เตาะแตะไปกับ Docker ตอนที่ 5 Docker registry

อยากลองใช้ docker แต่ยังไม่ต้องการนำ image อัปโหลดไปไว้บน docker hub จะทำอย่างไรได้มั้ย ที่นี่มีคำตอบครับ

เราสามารถนำ docker image ที่ได้สร้างขึ้นนั้นไปเก็บไว้ในสิ่งที่เรียกว่า Docker registry ซึ่งแบ่งได้เป็น 2 อย่างคือ Public registry อยู่ที่ hub.docker.com เราจะต้องลงทะเบียนเพื่อขอมีบัญชีผู้ใช้จึงจะสามารถอัปโหลด image ไปเก็บไว้ได้ ส่วนอีกอย่างก็คือ Private (Local) registry จะเป็นการสร้างที่เก็บส่วนตัว ที่เก็บของหน่วยงาน ในบทความนี้จะสาธิตทำที่เก็บ image สำหรับทดสอบงาน

การใช้งาน Docker public registry ต้องมี user account ที่ cloud.docker.com
จะต้อง login ก่อนจึงจะ push image ขึ้นไปได้ เหมือนที่แสดงตัวอย่างไว้ในบล็อก “เตาะแตะไปกับ Docker ตอนที่ 1”

$ docker login

ตัวอย่างคำสั่ง push ที่ผมนำ image ไปเก็บไว้

$ docker push woonpsu/docsdocker:part1

ซึ่งเรียกใช้จากที่เครื่องใดก็ได้ ด้วยคำสั่ง pull

$ docker pull woonpsu/docsdocker:part1

 

แต่หากเรายังไม่พร้อมที่จะนำ image ไปไว้บนนั้น เราก็ทำ Local registry ไว้ใช้เองไปพราง ๆ ก่อน ทำได้ 2 แบบ คือ
1. Local registry ที่ใช้งานภายในเครื่องของเราเครื่องเดียว
2. Local registry ที่เราเปิดให้เครื่อง client อื่น ๆ เข้ามาใช้ร่วมได้

Local registry แบบที่ใช้งานภายในเครื่องของเราเครื่องเดียว (localhost:5000)
ใช้คำสั่งนี้เพื่อเริ่มต้นสร้าง registry container

$ docker run -d -p 5000:5000 --restart=always --name myregistry registry:2

จากนั้นเรา pull image สักอัน จาก hub.docker.com เช่น

$ docker pull ubuntu:16.04

หลังจากดัดแปลง(ไม่ทำก็ได้)ตามต้องการแล้วก็ใส่ tag localhost:5000/my-ubuntu ดังนี้

$ docker tag ubuntu:16.04 localhost:5000/my-ubuntu
$ docker images

แล้วก็ push image ไปยัง local registry ซึ่งก็คือ เครื่องที่เรากำลังใช้งาน (localhost) ใช้ port หมายเลข 5000 ดังนี้

$ docker push localhost:5000/my-ubuntu

ซึ่งวิธีการเรียกใช้งาน image ที่เก็บไว้นั้น เป็นดังนี้

$ docker pull localhost:5000/my-ubuntu

ก่อนไปทำตัวอย่างถัดไปซึ่งเป็นแบบที่ 2 ให้ลบ container ชื่อ myregistry ที่ทดสอบเสร็จแล้ว ที่ต้องลบเพราะว่าใช้ port หมายเลขเดียวกัน หากใช้หมายเลข port ต่างกัน ก็ไม่ต้องลบ

$ docker stop myregistry
$ docker rm myregistry

 

Local registry แบบที่เราเปิดให้เครื่อง client อื่น ๆ เข้ามาใช้ร่วมได้ (docker-registry.localdomain:5000)

เราจะต้องเริ่มต้นที่การกำหนดชื่อเครื่อง ผมสมมติตั้งชื่อว่า docker-registry.localdomain ให้กับ host ที่ติดตั้ง docker registry ใครจะตั้งอย่างไรก็ได้ครับ

ในบทความนี้ เนื่องจากว่า ผมจะทดสอบแบบไม่ได้ไปจด Domain Name จริงให้กับเครื่อง ผมจะต้องแก้ไขไฟล์ /etc/hosts และ /etc/hostname ดังนี้

ที่ไฟล์ /etc/hosts ให้เพิ่มบรรทัด

10.168.16.19 docker-registry.localdomain

ที่ไฟล์ /etc/hostname ให้เปลี่ยนชื่อเครื่องจากเดิม มาเป็นดังนี้

docker-registry.localdomain

เสร็จแล้ว reboot เครื่อง

ขั้นตอนการตั้งค่า มาดูกันครับ
เริ่มต้นที่เครื่อง docker-registry.localdomain
ให้ทำคำสั่งตั้งค่า TLS certificate เพราะว่าจำเป็นต้องใช้ด้วย (หากมี certificate ของจริงก็นำมาใช้แทน domain.crt และ domain.key ได้เลย)

$ cd
$ mkdir certs
$ openssl req \
-newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
-x509 -days 365 -out certs/domain.crt

จะมีการถามให้เติมข้อมูล เอาคล้าย ๆ ตัวอย่างก็ได้

Generating a 4096 bit RSA private key
writing new private key to 'certs/domain.key'
Country Name (2 letter code) [AU]:TH
State or Province Name (full name) [Some-State]:SK
Locality Name (eg, city) [ ]:HDY
Organization Name (eg, company) [Internet Widgits Pty Ltd]:PSU
Organizational Unit Name (eg, section) [ ]:CC
Common Name (e.g. server FQDN or YOUR name) [ ]:docker-registry.localdomain
Email Address [ ]:

จะพบว่าในไดเรกทอรี certs จะเกิดไฟล์ 2 ไฟล์

$ ls certs/
domain.crt domain.key

ใช้คำสั่ง docker run สร้าง container และสมมติตั้งชื่อว่า netregistry

$ docker run -d --restart=always --name netregistry \
-v `pwd`/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:5000 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 5000:5000 \
registry:2

ต่อไปจะเป็นการทดสอบการใช้งาน local registry
ใส่ tag ก่อนที่จะ push ดังนี้

$ docker tag ubuntu:16.04 docker-registry.localdomain:5000/my-ubuntu
$ docker images

แล้ว push image เก็บไว้ใน local registry

$ docker push docker-registry.localdomain:5000/my-ubuntu

 

ไปที่เครื่อง client สมมติตั้งชื่อ hostname ว่า docker-vm3
ที่ไฟล์ /etc/hosts ให้เพิ่มบรรทัด

127.0.1.1 docker-vm3
10.168.16.19 docker-registry.localdomain

ที่ไฟล์ /etc/hostname เป็นดังนี้

docker-vm3

เสร็จแล้ว reboot เครื่อง
ต่อไปก็ทดสอบ pull image จาก local registry ที่ทำขึ้นนั้น

$ docker pull docker-registry.localdomain:5000/my-ubuntu
$ docker images

ต่อไปก็ทดสอบ push image โดยเราก็ไปเอา image จาก hub.docker.com มาสัก 1 อัน ใส่ tag แล้ว push ดังนี้

$ docker pull woonpsu/docsdocker:part1
$ docker tag woonpsu/docsdocker:part1 docker-registry.localdomain:5000/helloworld
$ docker push docker-registry.localdomain:5000/helloworld

จะพบว่าเราสามารถ push image ไปยังเครื่อง local registry ได้ด้วย

วิธีที่แนะนำไปแล้วข้างต้น เป็นการสร้าง Local registry ที่ไม่ได้มีความปลอดภัย เพราะว่าแค่รู้ชื่อเครื่อง รู้หมายเลข port (เลือกทดสอบกับ port 5000) ก็สามารถ push image ได้แล้ว

 

ดังนั้นหากจะสร้าง Local registry ให้บริการ (production) เราก็ต้องทำขั้นตอนเพิ่มอีกคือต้องใช้ TLS certificate ร่วมกับ Access control

ดังนี้

ที่เครื่อง docker-registry.localdomain
สร้าง username ที่มีสิทธิจะเข้ามา (access) ใช้ local registry สมมติชื่อ testuser และรหัสผ่านคือ testpassword ดังนี้

$ mkdir auth
$ docker run \
 --entrypoint htpasswd \
 registry:2 -Bbn testuser testpassword > auth/htpasswd
$ cat auth/htpasswd
testuser:$2y$05$UMOEA8bdBqSbHp/2LfiQ/uxLkf5IkXNqNE8V3Mp2IR/fRCsLXE5Q2

รัน container ตั้งชื่อว่า privateregistry และเราจะเปลี่ยนมาใช้ port 443 ดังนี้

$ docker run -d \
 --restart=always \
 --name privateregistry \
 -v `pwd`/auth:/auth \
 -e "REGISTRY_AUTH=htpasswd" \
 -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
 -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
 -v `pwd`/certs:/certs \
 -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
 -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
 -p 443:443 \
 registry:2

ไปที่เครื่อง client ชื่อ docker-vm3

$ docker login docker-registry.localdomain
Username: testuser
Password:

ทดสอบ push image

$ docker tag woonpsu/docsdocker:part1 docker-registry.localdomain/sawadee
$ docker push docker-registry.localdomain/sawadee

ลองทดสอบดูว่าหากไม่ได้ login ก็จะ push ไม่ได้

$ docker logout docker-registry.localdomain
$ docker push docker-registry.localdomain/sawadee
The push refers to a repository [docker-registry.localdomain/sawadee]
0d960f1d4fba: Preparing
no basic auth credentials

 

ข้อควรระวังในการทำ local registry คือ image ที่เรานำไปเก็บไว้ จะอยู่ใน container ซึ่งหากเราลบ container ด้วยคำสั่ง

$ docker stop privateregistry
$ docker rm privateregistry

ก็จะเป็นการลบ image ทั้งหมด
ดังนั้น เราจะต้องศึกษาวิธี mount volume เข้าไปใน container เพื่อให้ image ที่ push ไว้นั้น ถูกเก็บใน file system ของเครื่อง host แทน โดยเพิ่มบรรทัด -v นี้เข้าไปด้วยวางไว้ก่อนบรรทัดสุดท้าย (registry:2)

-v /mnt/registry:/var/lib/registry \

โดยที่ /mnt/registry คือ file system บน host และ /var/lib/registry คือไดเรกทอรีใน container

 

ขอให้สนุกกับการทดลองเล่น docker กันนะครับ

 

Reference:
https://docs.docker.com/registry/deploying/
https://docs.docker.com/registry/deploying/#restricting-access