DevToolBoxFREE
BlogAdvertise

GitHub Actions + Docker CI/CD: Build, Push และ Deploy

13 นาทีโดย DevToolBox

Pipeline CI/CD ที่แข็งแกร่งด้วย GitHub Actions และ Docker ช่วยทำให้ deployment lifecycle อัตโนมัติ คู่มือนี้สร้าง pipeline ระดับ production พร้อม multi-platform builds และ zero-downtime deployments

ภาพรวม Pipeline

Pipeline มีสี่ขั้นตอน: CI (ทดสอบ), Build (Docker image), Push (ไปยัง registry) และ Deploy (ไปยัง server)

Pipeline Flow:
  git push → GitHub Actions triggered
       │
       ├─ [Job 1] CI: npm test, lint, type-check
       │         (runs on all PRs and main pushes)
       │
       ├─ [Job 2] Build: docker build --platform linux/amd64,linux/arm64
       │         (runs only on main branch push)
       │         Push to ghcr.io/org/app:sha-abc1234
       │
       └─ [Job 3] Deploy: SSH to server, docker pull + restart
                 (runs after successful build)
                 Zero-downtime with blue-green or rolling restart

แนวทางปฏิบัติที่ดีที่สุดสำหรับ Dockerfile

Dockerfile ที่มีโครงสร้างดีเป็นรากฐาน Multi-stage builds ลดขนาด image สุดท้ายได้อย่างมาก

# Multi-stage Dockerfile (Node.js example)

# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && cp -R node_modules /prod_node_modules
RUN npm ci   # install ALL deps for building

# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Stage 3: Production runner (minimal image)
FROM node:20-alpine AS runner
WORKDIR /app

# Security: run as non-root user
RUN addgroup --system --gid 1001 nodejs \
 && adduser --system --uid 1001 appuser

# Copy only what's needed for runtime
COPY --from=deps /prod_node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./

USER appuser
EXPOSE 3000

# Health check for orchestrators
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "dist/index.js"]

# Result: ~150MB instead of ~800MB for the same app

Pipeline CI/CD พื้นฐาน

Workflow นี้ build และ push ไปยัง GitHub Container Registry ในทุกการ push ไปยัง main

# .github/workflows/deploy.yml

name: CI/CD Pipeline

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

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

jobs:
  # Stage 1: Test
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Run lint
        run: npm run lint

  # Stage 2: Build and Push Docker image
  build-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    permissions:
      contents: read
      packages: write

    outputs:
      image-digest: ${{ steps.build.outputs.digest }}

    steps:
      - uses: actions/checkout@v4

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}  # Auto-provided by GitHub

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

      - name: Build and push Docker image
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # Stage 3: Deploy to server
  deploy:
    needs: build-push
    runs-on: ubuntu-latest
    environment: production  # GitHub Environment with protection rules

    steps:
      - name: Deploy to production server via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # Pull latest image
            echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
            docker pull ghcr.io/${{ github.repository }}:latest

            # Zero-downtime restart
            docker compose -f /app/docker-compose.prod.yml up -d --no-deps app

            # Verify deployment
            sleep 10
            docker compose -f /app/docker-compose.prod.yml ps

คุณสมบัติ Pipeline ขั้นสูง

Production pipelines ต้องการคุณสมบัติเพิ่มเติม: multi-platform builds, security scanning และ rollback

# Advanced Pipeline Features

# 1. Multi-platform builds (AMD64 + ARM64)
- name: Set up QEMU
  uses: docker/setup-qemu-action@v3

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

- name: Build multi-platform image
  uses: docker/build-push-action@v5
  with:
    platforms: linux/amd64,linux/arm64
    push: true
    tags: ${{ steps.meta.outputs.tags }}

# 2. Security scanning with Trivy
- name: Scan image for vulnerabilities
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
    format: 'sarif'
    output: 'trivy-results.sarif'
    severity: 'CRITICAL,HIGH'
    exit-code: '1'  # Fail pipeline on critical vulnerabilities

# 3. Semantic versioning with tags
on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  release:
    steps:
      - name: Extract version from tag
        id: version
        run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT

      - uses: docker/build-push-action@v5
        with:
          tags: |
            ghcr.io/myorg/myapp:${{ steps.version.outputs.VERSION }}
            ghcr.io/myorg/myapp:latest

# 4. Rollback on failure
- name: Deploy with rollback
  run: |
    PREVIOUS_IMAGE=$(docker inspect --format='{{.Config.Image}}' app-container)
    docker pull $NEW_IMAGE

    if docker run --rm $NEW_IMAGE node -e "require('./dist/index.js')"; then
      docker stop app-container
      docker run -d --name app-container $NEW_IMAGE
    else
      echo "New image failed healthcheck, keeping previous"
      exit 1
    fi

คำถามที่พบบ่อย

จะเก็บ secrets ใน GitHub Actions ได้อย่างไร?

ไปที่ repository Settings → Secrets and variables → Actions → New repository secret

ความแตกต่างระหว่าง ghcr.io และ Docker Hub คืออะไร?

GitHub Container Registry ฟรีสำหรับ public repositories และรวมเข้ากับสิทธิ์ GitHub

จะ build สำหรับทั้ง AMD64 และ ARM64 ได้อย่างไร?

ใช้ docker/setup-qemu-action และ docker/setup-buildx-action

จะใช้ environment-specific deployments ได้อย่างไร?

ใช้ GitHub Environments สำหรับ staging และ production

เครื่องมือที่เกี่ยวข้อง

𝕏 Twitterin LinkedIn
บทความนี้มีประโยชน์ไหม?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

ลองเครื่องมือที่เกี่ยวข้อง

บทความที่เกี่ยวข้อง

Docker Compose Tutorial: จากพื้นฐานถึง Stack พร้อมใช้งานจริง

Tutorial Docker Compose ฉบับสมบูรณ์: ไวยากรณ์ docker-compose.yml, services, networks, volumes, environment variables, healthchecks และตัวอย่างจริง

กลยุทธ์การแตก Branch ใน Git: GitFlow vs Trunk-Based vs GitHub Flow

เปรียบเทียบกลยุทธ์ GitFlow, Trunk-Based Development และ GitHub Flow โครงสร้างแบรนช์ เวิร์กโฟลว์การ merge การผสาน CI/CD และวิธีเลือกกลยุทธ์ที่เหมาะสม

Kubernetes สำหรับผู้เริ่มต้น: คู่มือฉบับสมบูรณ์ (2026)

เรียนรู้ Kubernetes ตั้งแต่เริ่มต้น Pod, Service, Deployment และอื่นๆ

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy