CI/CD Pipeline ด้วย Docker และ GitHub Actions

CI/CD กับ Docker คืออะไร?

CI/CD (Continuous Integration / Continuous Deployment) คือกระบวนการ Build, Test และ Deploy อัตโนมัติเมื่อมีการ Push Code เข้า Repository Docker ช่วยให้การ Deploy เสถียรและทำซ้ำได้

Workflow โดยรวม

Push Code → GitHub Actions → Build Docker Image → 
→ Run Tests → Push to Registry → Deploy to Server

GitHub Actions Workflow (.github/workflows/deploy.yml)

name: Build and Deploy

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=
            type=ref,event=branch
            latest

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Deploy to Server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ghcr.io/${{ github.repository }}:latest
            docker stop myapp || true
            docker rm myapp || true
            docker run -d \
              --name myapp \
              --restart unless-stopped \
              -p 3000:3000 \
              ghcr.io/${{ github.repository }}:latest

GitHub Secrets ที่ต้องตั้ง

Secret ค่า
SERVER_HOST IP หรือ Domain ของ Server
SERVER_USER Username SSH
SSH_PRIVATE_KEY Private key สำหรับเข้า Server

Deploy ด้วย Docker Compose

      - name: Deploy with Docker Compose
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/myapp
            echo "IMAGE_TAG=${{ github.sha }}" > .env.deploy
            docker compose pull
            docker compose up -d --remove-orphans

Scan Security ก่อน Deploy

      - name: Security Scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

Cache Build Layer เพื่อเร็วขึ้น

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build with cache
        uses: docker/build-push-action@v5
        with:
          context: .
          cache-from: type=gha
          cache-to: type=gha,mode=max
          push: true
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

สรุป

GitHub Actions + Docker เป็นคู่ที่ดีสำหรับสร้าง CI/CD Pipeline โดยไม่ต้องติดตั้งเซิร์ฟเวอร์เพิ่ม ยิ่งใช้ GitHub Container Registry ช่วยให้ไม่ต้องพึ่งเซอร์วิสภายนอกด้วย