- Unregistry คือ lightweight container image registry ที่จัดเก็บและให้บริการ image โดยตรงจากที่เก็บของ Docker daemon และส่ง image ไปยัง Docker server ระยะไกลผ่าน SSH ได้โดยตรงด้วยคำสั่ง
docker pussh
- ทางเลือกเดิม ๆ ได้แก่ Docker Hub/GitHub Container Registry, registry ที่โฮสต์เอง,
docker save | ssh... docker load, และการ rebuild บนเครื่องระยะไกล ซึ่งแต่ละวิธีมีปัญหาเรื่อง repository สาธารณะหรือมีค่าใช้จ่าย, ภาระการดูแลระบบ, การส่งทั้ง image, และการใช้ทรัพยากรเซิร์ฟเวอร์โดยเปล่าประโยชน์
docker pussh myapp:latest user@server จะสร้าง SSH tunnel, เปิด unregistry container ชั่วคราวบนเซิร์ฟเวอร์ระยะไกล แล้วทำ docker push ไปยังพอร์ต localhost ที่ถูก forward เพื่อส่งเฉพาะ layer ที่ยังไม่มีบนเครื่องระยะไกล
- เซิร์ฟเวอร์ระยะไกลต้องมีสภาพแวดล้อมที่รัน Docker ได้, สิทธิ์ในการใช้คำสั่ง
docker, และในการรันครั้งแรกต้องเข้าถึง ghcr.io/psviderski/unregistry:latest ได้ โดย unregistry container จะรันเป็น root เพราะต้องเข้าถึง /run/containerd/containerd.sock
- หากเปิดใช้ containerd image store ของ Docker image ที่ส่งไปจะพร้อมใช้งานใน Docker ทันทีและหลีกเลี่ยงการจัดเก็บซ้ำ แต่ image และ container ที่สร้างด้วย classic storage driver เดิมอาจมองไม่เห็นชั่วคราว
ปัญหาการ deploy ที่ Unregistry แก้ไข
- Unregistry คือ lightweight container image registry สำหรับย้าย Docker image ไปยังเซิร์ฟเวอร์ระยะไกลโดยไม่ต้องใช้ registry ภายนอก
- คำสั่ง Docker CLI plugin ที่รวมมาด้วยคือ
docker pussh โดยตัว s ที่เพิ่มเข้ามาหมายถึง SSH
- เมื่อต้องย้าย Docker image ที่สร้างบนเครื่อง local ไปยังเซิร์ฟเวอร์ ทางเลือกทั่วไปมีข้อจำกัดดังนี้
- Docker Hub / GitHub Container Registry: โค้ดอาจต้องเปิดเผยต่อสาธารณะ หรือจำเป็นต้องจ่ายค่า private repository
- Self-hosted registry: เกิดบริการแยกต่างหากที่ต้องดูแลรักษา ความปลอดภัย และค่าใช้จ่ายด้าน storage
- Save/Load:
docker save | ssh <remote server> docker load จะส่งทั้ง image แม้บนเซิร์ฟเวอร์จะมีอยู่แล้ว 90%
- Remote rebuild: ใช้เวลาและทรัพยากรเซิร์ฟเวอร์ และอาจต้อง debug สาเหตุ build ล้มเหลวบน production
วิธีทำงานของ docker pussh
- วิธีใช้งานพื้นฐานคือคำสั่งบรรทัดเดียวต่อไปนี้
docker pussh myapp:latest user@server
- คำสั่งนี้ส่งตรงผ่าน SSH เฉพาะ layer ที่ขาดหายไป โดยไม่ต้องตั้งค่า registry, สมัครบริการ, ใช้ repository ตัวกลาง หรือเปิดพอร์ตสาธารณะ
- การทำงานภายในดำเนินตามลำดับดังนี้
- สร้าง SSH tunnel ไปยังเซิร์ฟเวอร์ระยะไกล
- เริ่ม unregistry container ชั่วคราวบนเซิร์ฟเวอร์
- forward พอร์ต localhost แบบสุ่มผ่าน tunnel ไปยังพอร์ตของ unregistry
- ทำ
docker push ผ่านพอร์ตที่ถูก forward เพื่อส่งเฉพาะ layer ที่เครื่องระยะไกลยังไม่มี
- image ที่ส่งไปจะพร้อมใช้งานทันทีใน Docker daemon ระยะไกล
- หยุด unregistry container และปิด SSH tunnel
- โปรเจกต์นี้มุ่งให้การส่ง Docker image เรียบง่ายและมีประสิทธิภาพ คล้ายกับ
rsync
- Unregistry ถูกสร้างขึ้นเพื่อ Uncloud ซึ่งเป็นเครื่องมือ lightweight สำหรับ deploy container ไปยัง Docker host หลายเครื่อง และต้องการวิธีที่ง่ายกว่า registry เต็มรูปแบบและมีประสิทธิภาพกว่า save/load
ข้อกำหนดและข้อจำกัดในการรัน
- เครื่อง local ต้องรองรับ Docker CLI plugin และต้องมี Docker 19.03 ขึ้นไปกับ OpenSSH client
- เซิร์ฟเวอร์ระยะไกลต้องติดตั้ง Docker และกำลังรันอยู่
- ผู้ใช้ SSH ต้องมีสิทธิ์รันคำสั่ง
docker
- ผู้ใช้ต้องเป็น
root หรือผู้ใช้ non-root ต้องอยู่ในกลุ่ม docker
- ดูเอกสารที่เกี่ยวข้องได้ที่ Docker Manage Docker as a non-root user
- หากต้องใช้
sudo ต้องสามารถรัน sudo docker ได้โดยไม่ขึ้น prompt ให้ใส่รหัสผ่าน
- เมื่อรัน
docker pussh ครั้งแรก เซิร์ฟเวอร์ระยะไกลต้องสามารถดึง image ghcr.io/psviderski/unregistry:latest จาก ghcr.io ได้
- เซิร์ฟเวอร์ที่ต้องใช้ proxy ควรตั้งค่าตามคำแนะนำ Daemon proxy configuration ของ Docker
- ในสภาพแวดล้อมแบบ air-gapped หรือที่ถูกจำกัดการเข้าถึง
ghcr.io สามารถตรวจสอบเวอร์ชัน unregistry image ที่ต้องใช้แล้ว preload ด้วยตนเองได้
- unregistry container ต้องเข้าถึง
/run/containerd/containerd.sock จึงรันเป็น root เพื่อให้มีสิทธิ์ที่จำเป็น
การติดตั้งและแพลตฟอร์มที่รองรับ
- บน macOS/Linux สามารถติดตั้ง
docker-pussh ด้วย Homebrew ได้
brew install psviderski/tap/docker-pussh
- หลังติดตั้งด้วย Homebrew หากต้องการใช้
docker pussh เป็น Docker CLI plugin ต้องสร้าง symbolic link ที่ ~/.docker/cli-plugins/docker-pussh
- บน macOS/Linux มีวิธีดาวน์โหลดโดยตรงให้ใช้ด้วย
- ตัวอย่างเวอร์ชันปัจจุบันคือการดาวน์โหลดสคริปต์
docker-pussh ของ v0.4.3 ไปยังไดเรกทอรี plugin ของ Docker แล้วให้สิทธิ์รัน
- มีวิธีดาวน์โหลดสคริปต์ล่าสุดจาก branch main ให้ใช้ด้วย
- Debian สามารถติดตั้งผ่าน repository แพ็กเกจไม่เป็นทางการ unregistry-debian ที่สร้างและดูแลโดย @dariogriffo
- ปัจจุบันยังไม่รองรับ Windows แต่สามารถลองใช้ WSL 2 และขั้นตอนติดตั้งแบบ Linux ได้
- ตรวจสอบการติดตั้งด้วยคำสั่งต่อไปนี้
docker pussh --help
การตั้งค่า containerd image store
ตัวอย่างการใช้งาน
- การส่งพื้นฐานระบุเพียงชื่อ image และเป้าหมาย SSH ระยะไกล
docker pussh myapp:latest user@server.example.com
- หาก private key ไม่ได้ลงทะเบียนไว้ใน SSH agent สามารถระบุ key ด้วย
-i
docker pussh myapp:latest ubuntu@192.168.1.100 -i ~/.ssh/id_rsa
- กำหนดพอร์ต SSH แบบกำหนดเองโดยต่อพอร์ตท้ายเป้าหมาย
docker pussh myapp:latest user@server:2222
- ระบุไฟล์ SSH config แบบกำหนดเองด้วย
-F
docker pussh myapp:latest prod-server -F ~/.ssh/config.prod
- หากต้องการ push เฉพาะ platform ใด platform หนึ่งจาก multi-platform image ให้ใช้
--platform
docker pussh myapp:latest user@server --platform linux/amd64
- หากต้องการใช้ unregistry image เวอร์ชันเฉพาะบน host ระยะไกล ให้กำหนด environment variable
UNREGISTRY_IMAGE
UNREGISTRY_IMAGE=ghcr.io/psviderski/unregistry:A.B.C docker pussh myapp:latest user@server.example.com
กรณีการใช้งานหลัก
- สำหรับการ deploy ไปยัง production server สามารถ push image ที่ build บนเครื่อง local ไปยังเซิร์ฟเวอร์โดยตรงและรันได้ โดยไม่ต้องผ่าน registry ตัวกลาง
docker build --platform linux/amd64 -t myapp:1.2.3 .
docker pussh myapp:1.2.3 deploy@prod-server
ssh deploy@prod-server docker run -d myapp:1.2.3
- ใน CI/CD pipeline สามารถข้ามความซับซ้อนของ registry และส่ง image ไปยังเป้าหมาย deploy ได้โดยตรง
- name: Build and deploy
run: |
docker build -t myapp:${{ github.sha }} .
docker pussh myapp:${{ github.sha }} deploy@staging-server
- ใน homelab และสภาพแวดล้อมแบบ air-gapped สามารถ deploy image ในเครือข่ายแยกที่ไม่สามารถเข้าถึง public registry ผ่านอินเทอร์เน็ตได้
การใช้งานขั้นสูงและโปรเจกต์ที่เกี่ยวข้อง
- Unregistry สามารถใช้เป็น local registry แบบ standalone ได้ด้วย
- mount
/run/containerd/containerd.sock แล้วรัน container ghcr.io/psviderski/unregistry
- จากนั้นใช้
localhost:5000 กับ docker tag และ docker push ได้เหมือน registry ทั่วไป
- การตั้งค่า SSH สามารถใช้ไฟล์ SSH config มาตรฐาน หรือส่งไฟล์ config แยกด้วย
-F ได้
- มีโปรเจกต์ third-party ที่เกี่ยวข้องให้ใช้
- ในการ implement มีการกล่าวถึง Spegel และ Docker Distribution
- Spegel คือ P2P container image registry ที่เป็นแรงบันดาลใจให้การ implement registry ซึ่งใช้ containerd image store เป็น backend
- Docker Distribution คือ implementation ของ Docker registry ที่เป็นพื้นฐานของ unregistry
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ในฐานะคนที่สร้าง Docker ผมชอบนะ การออกแบบในอุดมคติน่าจะเป็นเซิร์ฟเวอร์เดียวที่ ไม่มีการแยกระหว่าง Docker engine กับ registry
ถ้ามันสามารถจัดเก็บ ส่งต่อ และรันคอนเทนเนอร์ได้ตามต้องการ ก็คงกลายเป็นองค์ประกอบที่แข็งแกร่งกว่านี้มาก และน่าจะหลีกเลี่ยงแนวทางที่น่าเสียดายซึ่ง engine กับ registry มีวิธีเก็บอิมเมจไม่สอดคล้องกันได้ด้วย
อีกอย่าง ผมคิดว่าทุก production cluster ควรมี ที่เก็บอิมเมจแบบกระจายศูนย์ และการ push อิมเมจไปยังที่เก็บนั้นควรเป็นตัว trigger การ deploy วิธีแบบตอนนี้ที่ push ไปยัง registry, ตั้งค่า cluster แล้วให้แต่ละ node ดึงจาก registry นั้นเปราะบางและไม่มีประสิทธิภาพ ผมเคยผลักดันการออกแบบที่ดีกว่านี้ แต่แรงเฉื่อยมันมากเกินไปแล้ว และชุมชน Kubernetes ยุคแรก ๆ ก็เป็นปฏิปักษ์ต่อไอเดียที่มาจาก Docker
การ deploy ด้วยการ push ไปยัง cluster ฟังดูฉลาดดี ผมกำลังคิดถึงที่เก็บอิมเมจแบบกระจายศูนย์ในแนวทางที่ใส่ unregistry ไว้ในทุก node เพื่อให้ดึงและแชร์อิมเมจกันเอง แต่แนวทางที่ให้การ push เป็นตัว trigger การ deploy ยังต้องคิดต่ออีกหน่อย
ดีเลย คำสั่ง
pusshเป็นมุกเล่นคำที่สง่างามจริง ๆ สมควรได้รับการยอมรับ จำง่าย เห็นความหมายทันที และต่างจากคำสั่งมาตรฐานคู่กันแค่ ตัวอักษรเดียวdocker push-over-sshก็น่าจะดีในงาน automation ที่พัฒนาร่วมกัน
pusshอาจดูเหมือนพิมพ์ผิดสำหรับคนที่ไม่รู้ฟีเจอร์นี้ ทำให้เกิดความสับสนโดยไม่จำเป็น ในขณะที่push-over-sshชัดเจนว่าเป็นชื่อที่ตั้งใจไว้ คิดเหมือน option แบบสั้นกับแบบยาวก็ได้sที่เพิ่มมาคือsของsssh“ตัว
sextra นั่นคืออะไร?”“พิมพ์ผิดน่ะ”
ปี 2015 ผมเคยส่ง PR[1] แบบไร้เดียงสาเพื่อพยายามเอาฟีเจอร์นี้เข้า Docker mainline แต่ไม่นานก็ถูกเบี่ยงไปให้ช่วยด้านอื่นแทน การทำให้ไม่ต้องใช้ registry อาจสั่นคลอน โมเดลธุรกิจ ของ Docker มากเกินไป
[1]: https://github.com/richardcrichardc/docker2docker
สุดท้ายผมคิดว่าแผนน่าจะเป็นการเปลี่ยนค่าเริ่มต้นไปใช้ image store ของ containerd เมื่อทั้ง local และ remote ใช้ image store ของ containerd แล้ว ก็น่าจะทำสิ่งที่เคย implement ไว้เดิมได้โดยไม่ต้องมี registry wrapper
เป็นไอเดียที่ยอดเยี่ยมซึ่งน่าจะเข้ากันได้ดีกับระบบที่ใช้ เครื่องมือ deploy แบบ push อยู่แล้วอย่าง Ansible สำหรับบริษัทที่ไม่มีการดูแล Docker registry ตลอด 24/7 ก็ดูเหมือนจะใช้เป็นกลไก deploy hotfix ที่ดีได้ด้วย
สงสัยว่ามันผสานกับเครื่องมือ OCI อย่าง buildah ได้เรียบร้อยไหม หรือว่าต้องติดตั้ง Docker แบบเต็มทั้งสองฝั่ง ยังไม่ได้ดูลงลึก แต่ใน setup แบบนี้ สิ่งที่ดูเหมือนเป็นชิ้นส่วนที่ขาดไปเพื่อให้ skopeo ทำงานได้คือการ bootstrap mini registry บนเซิร์ฟเวอร์ remote
Unregistry นำโค้ด Docker registry อย่างเป็นทางการมาใช้ซ้ำในชั้น API ดังนั้นหน้าตาและการทำงานจึงคล้ายกับ https://hub.docker.com/_/registry
ฝั่ง client สามารถใช้เครื่องมือที่พูดกับ OCI registry ได้ เช่น skopeo, crane, regclient, BuildKit เป็นต้น แต่ถ้าจะใช้เครื่องมือเหล่านี้ ต้องรัน unregistry เองบน host remote คำสั่ง
docker pusshแค่ทำให้ flow นั้นเป็นอัตโนมัติโดยใช้ Docker ฝั่ง localมองว่าเป็น Bash script ก็ได้: https://github.com/psviderski/unregistry/blob/main/docker-pu...
สามารถแก้ให้เป็นแบบที่ต้องการได้ง่าย ๆ
มันควรเป็นแบบนี้มาตั้งแต่แรกแล้ว ยอดเยี่ยม
Docker registry ก็มีที่ให้ใช้อยู่ แต่โดยรวมแล้วออกแบบเกินความจำเป็น และอยู่คนละฝั่งกับ จิตวิญญาณแฮ็กเกอร์
ใช้เวลาประมาณ 20 นาทีในการตั้งค่า workflow
.yamlที่ build และ push อิมเมจไปยัง registry ghcr.io แบบ private และอีกประมาณ 5 นาทีเพื่ออนุญาตให้เซิร์ฟเวอร์ดึงอิมเมจจากที่นั่น เป็น setup ที่ใช้งานได้จริงทีเดียวกำลังดู pipeline ที่ build อิมเมจใน GitLab แล้ว push ไป Artifactory จากนั้น trigger การ deploy ให้ดึงจาก Artifactory แล้ว push ต่อไปยัง AWS ECR จากนั้นเมื่ออัปเดต deployment template ของ EKS ก็จะดึงจาก ECR ไปยัง node แล้วเปิด Pod container
ผมต้องการสิ่งนี้ในชีวิต
พอเห็นสิ่งนี้ก็ได้รู้จัก uncloud กำลังมองหาอะไรที่คล้าย dokku แต่แข็งแรงกว่าสำหรับจัดโครงสร้างเซิร์ฟเวอร์ของโปรเจกต์ข้าง ๆ อยู่พอดี แล้วนี่ให้ความรู้สึกแบบนั้นเลย
ฟีเจอร์ stack ก็ดีมาก ๆ ซึ่งแท้จริงแล้วก็คือ Docker Compose นั่นเอง บน EC2 instance ตัวหนึ่ง Portainer Agent รัน Caddy ในคอนเทนเนอร์ และ Caddy ทำหน้าที่เป็นโหลดบาลานเซอร์กับรีเวิร์สพร็อกซี
ค่อนข้างแปลกที่ Docker ไม่ได้ทำงานแบบนี้มาตั้งแต่แรก ดูเจ๋งดี
การบันทึก archive ทำได้เช่น
docker save -o may-app.tar my-app:latestและโหลดได้เช่นdocker load -i /path/to/my-app.tarถ้าใช้เครื่องมืออย่าง Ansible ก็ทำสิ่งที่ “Unregistry” ทำให้อัตโนมัติได้ไม่ยาก ตาม GitHub repository ข้อเสียของวิธี save/load คือ ต้องส่ง image ทั้งก้อนผ่านเครือข่าย ซึ่งในทางปฏิบัติอาจเป็นปัญหาได้ การจัดการตัว image เองแทนไฟล์ archive ก็ดูสะดวกกว่าด้วย
เป็นโปรเจกต์และแนวทางที่เรียบร้อยดี ผมเบื่อ registry ราคาแพงจนต้องโฮสต์ Zot[1] เอง แต่สำหรับบางกรณีการใช้งาน สิ่งนี้ดูง่ายกว่ามาก
สงสัยว่ามีคนอื่นอีกไหมที่รู้สึกว่าอยากได้ บริการ private registry ที่ตั้งค่าได้ง่าย ราคาถูก และคิดเงินตามการใช้งาน
[1]: https://zotregistry.dev
เป็นไอเดียที่ดี อย่างไรก็ตาม อาจมีข้อเสียตรงที่การ deploy ถูกผูกเข้ากับ service ตัวอย่างเช่น ยังไม่แน่ใจว่าจะทำ scale out หรือ red/green deployment อย่างไร และฝั่งที่ทำเรื่องนั้นก็น่าจะต้องรู้เรื่อง push ด้วย
แก้ไข: เพิ่งรู้ว่าสิ่งที่ทำเรื่องนั้นคือ uncloud
ถึงอย่างไรก็เป็น trade-off ถ้าขนาดยังเล็ก ใช้ Hetzner VM แค่เครื่องเดียว พอใจกับความเรียบง่าย และโอเคกับการ build image ในเครื่อง local ก็ถือว่ายอดเยี่ยม