เมื่อเราสร้างระบบด้วย Docker ขั้นตอนสำคัญหลังจาก docker build คือการนำ Docker Image ไปเก็บไว้บน Container Registry เพื่อให้ Server, Kubernetes, CI/CD Pipeline หรือสมาชิกในทีมสามารถดาวน์โหลด Image เวอร์ชันเดียวกันไปใช้งานได้

บทความนี้อธิบายการ Push Docker Image ไปยัง Registry ที่นิยมใช้งาน 3 แห่ง ได้แก่

  • Docker Hub
  • GitHub Container Registry (GHCR)
  • GitLab Container Registry

#ภาพรวม Workflow

Source Code
    |
    v
Dockerfile
    |
    | docker build
    v
Local Docker Image
    |
    | tag + login + push
    v
Container Registry
    |
    +---- Docker Hub
    +---- GitHub Container Registry
    +---- GitLab Container Registry
    |
    | docker pull
    v
Development / Server / CI-CD / Kubernetes

หลักการทำงานของทั้งสามระบบใกล้เคียงกันมาก คือ

Build -> Tag -> Login -> Push -> Pull/Deploy

#1. เตรียม Docker Image

สมมติว่าเรามีโปรเจกต์ชื่อ my-app

my-app/
├── Dockerfile
├── package.json
└── src/

สร้าง Image ด้วยคำสั่ง

docker build -t my-app:1.0 .

ตรวจสอบ Image

docker images

ตัวอย่างผลลัพธ์

REPOSITORY   TAG    IMAGE ID       CREATED        SIZE
my-app       1.0    abc123456789   1 minute ago   180MB

ก่อน Push ควรทดลองรัน Image ก่อน

docker run --rm -p 8080:8080 my-app:1.0

ถ้า Application ทำงานถูกต้องจึงค่อยนำ Image ไป Push ขึ้น Registry


#2. Push Docker Image ขึ้น Docker Hub

Docker Hub เป็น Public Container Registry ที่ใช้งานแพร่หลาย และสามารถใช้งานร่วมกับ Docker CLI ได้โดยตรง

เว็บไซต์: https://hub.docker.com/

รูปแบบชื่อ Image โดยทั่วไปคือ

USERNAME/IMAGE_NAME:TAG

ตัวอย่าง

naruapon/my-app:1.0

#Login Docker Hub

docker login

Docker จะให้กรอก Username และ Password/Access Token ตามวิธี Authentication ที่บัญชีรองรับ

#Tag Image

Image ที่สร้างไว้คือ

my-app:1.0

สร้าง Tag สำหรับ Docker Hub

docker tag my-app:1.0 naruapon/my-app:1.0

ตรวจสอบ

docker images

จะพบทั้งชื่อเดิมและชื่อที่ Tag ใหม่ โดยทั้งสองอาจอ้างถึง Image ID เดียวกัน

#Push Image

docker push naruapon/my-app:1.0

จากนั้นเครื่องอื่นสามารถ Pull ได้ด้วย

docker pull naruapon/my-app:1.0

และรันด้วย

docker run --rm -p 8080:8080 naruapon/my-app:1.0

#การใช้ latest

สามารถสร้าง latest tag เพิ่มได้

docker tag my-app:1.0 naruapon/my-app:latest
docker push naruapon/my-app:latest

อย่างไรก็ตาม ใน Production ควรระบุ Version ที่แน่นอน เช่น 1.0.0 หรือ 2.1.3 แทนการอ้าง latest เพียงอย่างเดียว


#3. Push Docker Image ขึ้น GitHub Container Registry

GitHub มี Container Registry หรือ GHCR ซึ่งทำงานร่วมกับ GitHub Repository และ GitHub Actions ได้สะดวก

Registry URL คือ

ghcr.io

รูปแบบชื่อ Image

ghcr.io/OWNER/IMAGE_NAME:TAG

ตัวอย่าง

ghcr.io/naruapon/my-app:1.0

เอกสาร: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry

#Login GHCR

การใช้งานจากเครื่อง Local สามารถใช้ Token ที่มี Permission เหมาะสม

ตัวอย่างเก็บ Token ใน Environment Variable

export CR_PAT=YOUR_TOKEN

จากนั้น Login

echo "$CR_PAT" | docker login ghcr.io -u USERNAME --password-stdin

ไม่ควรเขียน Token ลงใน Dockerfile, Source Code หรือ Commit ลง Git Repository

#Tag Image

docker tag my-app:1.0 ghcr.io/naruapon/my-app:1.0

#Push

docker push ghcr.io/naruapon/my-app:1.0

#Pull

docker pull ghcr.io/naruapon/my-app:1.0

หาก Package เป็น Private ผู้ใช้หรือระบบที่ Pull จะต้องมีสิทธิ์ในการเข้าถึง Package นั้นด้วย


#4. Push Docker Image ขึ้น GitLab Container Registry

GitLab มี Container Registry เชื่อมโยงกับ Project และ GitLab CI/CD โดยตรง

Registry หลักของ GitLab.com คือ

registry.gitlab.com

รูปแบบชื่อ Image โดยทั่วไปคือ

registry.gitlab.com/NAMESPACE/PROJECT/IMAGE:TAG

ตัวอย่าง

registry.gitlab.com/naruapon/my-project/my-app:1.0

เอกสาร: https://docs.gitlab.com/user/packages/container_registry/

#Login

docker login registry.gitlab.com

หรือใช้ Token

echo "$TOKEN" | docker login registry.gitlab.com \
  -u USERNAME \
  --password-stdin

#Tag Image

docker tag my-app:1.0 \
  registry.gitlab.com/naruapon/my-project/my-app:1.0

#Push

docker push \
  registry.gitlab.com/naruapon/my-project/my-app:1.0

#Pull

docker pull \
  registry.gitlab.com/naruapon/my-project/my-app:1.0

#5. เปรียบเทียบชื่อ Image


Registry รูปแบบ ตัวอย่าง


Docker Hub user/image:tag naruapon/my-app:1.0

GitHub GHCR ghcr.io/owner/image:tag ghcr.io/naruapon/my-app:1.0

#GitLab registry.gitlab.com/namespace/project/image:tag registry.gitlab.com/naruapon/my-project/my-app:1.0

สังเกตว่า Docker Image ตัวเดียวกันสามารถ Tag ได้หลายชื่อ

docker tag my-app:1.0 naruapon/my-app:1.0
docker tag my-app:1.0 ghcr.io/naruapon/my-app:1.0
docker tag my-app:1.0 registry.gitlab.com/naruapon/my-project/my-app:1.0

จากนั้นจึง Push ไปยังแต่ละ Registry

docker push naruapon/my-app:1.0
docker push ghcr.io/naruapon/my-app:1.0
docker push registry.gitlab.com/naruapon/my-project/my-app:1.0

#6. การกำหนด Version ของ Docker Image

ไม่ควรใช้ latest เพียง Tag เดียวในระบบ Production

ตัวอย่างที่เหมาะสมกว่า

my-app:1.0.0
my-app:1.1.0
my-app:1.2.0
my-app:2.0.0

อาจสร้างทั้ง Version และ latest

docker tag my-app:1.2.0 naruapon/my-app:1.2.0
docker tag my-app:1.2.0 naruapon/my-app:latest

docker push naruapon/my-app:1.2.0
docker push naruapon/my-app:latest

ข้อดีของ Version Tag คือสามารถระบุ Image ที่ต้อง Deploy และ Rollback ได้ชัดเจน


#7. ใช้ Git Commit SHA เป็น Image Tag

ในระบบ CI/CD สามารถใช้ Git Commit SHA เป็น Tag ได้ เช่น

my-app:a1b2c3d

ตัวอย่าง

docker build -t my-app:a1b2c3d .
docker tag my-app:a1b2c3d ghcr.io/naruapon/my-app:a1b2c3d
docker push ghcr.io/naruapon/my-app:a1b2c3d

แนวทางนี้ช่วยเชื่อมโยง Docker Image กับ Source Code ที่ใช้สร้าง Image ได้โดยตรง

Git Commit a1b2c3d
        |
        v
Docker Build
        |
        v
my-app:a1b2c3d
        |
        v
Container Registry
        |
        v
Deployment

#8. นำไปใช้กับ Docker Compose

เมื่อ Image ถูก Push ไปยัง Registry แล้ว เครื่อง Production ไม่จำเป็นต้อง Build Source Code ใหม่

ตัวอย่าง compose.yaml

services:
  web:
    image: ghcr.io/naruapon/my-app:1.0.0
    ports:
      - "8080:8080"

ดึง Image

docker compose pull

จากนั้น

docker compose up -d

#9. นำไปใช้กับ Kubernetes

Container Registry มีบทบาทสำคัญมากในการ Deploy Kubernetes

ตัวอย่าง Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: ghcr.io/naruapon/my-app:1.0.0
          ports:
            - containerPort: 8080

Kubernetes จะ Pull Image จาก Registry เพื่อนำไปสร้าง Container บนแต่ละ Node

GitHub / GitLab
      |
      v
CI/CD Pipeline
      |
      | docker build
      v
Docker Image
      |
      | docker push
      v
Container Registry
      |
      | docker pull
      v
Kubernetes Cluster
      |
      +-- Pod
      +-- Pod
      +-- Pod

หาก Registry เป็น Private จะต้องตั้งค่า Credential สำหรับ Cluster อย่างเหมาะสม


#10. Workflow สำหรับ CI/CD

ในงานจริงเราไม่จำเป็นต้อง Build และ Push ด้วยมือทุกครั้ง

สามารถสร้าง Pipeline ให้ทำงานอัตโนมัติเมื่อ

git push
   |
   v
GitHub Actions / GitLab CI
   |
   +-- Checkout Source
   |
   +-- Login Registry
   |
   +-- Docker Build
   |
   +-- Docker Tag
   |
   +-- Docker Push
   |
   v
Container Registry
   |
   v
Deploy

ตัวอย่างแนวคิด GitHub Actions:

- name: Login to GHCR
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and Push
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: ghcr.io/example/my-app:latest

ควรตรวจสอบเวอร์ชันของ GitHub Actions ที่ใช้งานจากเอกสารทางการก่อนนำ Pipeline ไปใช้จริง


#11. แนวทางที่แนะนำสำหรับ Production

ควรปฏิบัติตามแนวทางต่อไปนี้

  1. ไม่เก็บ Password หรือ Access Token ไว้ใน Source Code
  2. ใช้ CI/CD Secrets สำหรับ Credential
  3. ใช้ Version Tag หรือ Commit SHA เพื่อระบุ Image
  4. หลีกเลี่ยงการพึ่ง latest เพียงอย่างเดียว
  5. Scan Image เพื่อค้นหา Vulnerability
  6. ใช้ Multi-stage Build เพื่อลดขนาด Image
  7. ใช้ .dockerignore เพื่อลด Build Context
  8. กำหนดสิทธิ์ Registry และ Token ตามหลัก Least Privilege
  9. อย่า Push .env, Private Key หรือ Secret เข้าไปใน Image
  10. สำหรับ Production ที่ต้องการความแน่นอนสูง อาจ Deploy ด้วย Image Digest เช่น image@sha256:...

#สรุป

ไม่ว่าจะใช้ Docker Hub, GitHub Container Registry หรือ GitLab Container Registry หลักการสำคัญเหมือนกันคือ

docker build
     |
     v
docker tag
     |
     v
docker login
     |
     v
docker push
     |
     v
Container Registry
     |
     v
docker pull
     |
     v
Deploy

Docker Hub เหมาะกับการเผยแพร่และแชร์ Docker Image โดยทั่วไป ขณะที่ GHCR เหมาะกับ Workflow ที่ใช้ GitHub และ GitHub Actions ส่วน GitLab Container Registry เหมาะกับโครงการที่ใช้ GitLab และ GitLab CI/CD

เมื่อพัฒนาต่อไปสู่ระบบ Production ควรให้ CI/CD Pipeline เป็นผู้ Build, Tag และ Push Image โดยอัตโนมัติ และให้ Docker Compose, Kubernetes, Helm หรือ GitOps ดึง Image ที่ระบุ Version ชัดเจนไป Deploy

#เอกสารอ้างอิง