Nhập từ khóa muốn tìm kiếm gì?

Quy trình CI/CD với Docker và GitHub Actions: Hướng dẫn từng bước

TTrần Minh Phương Anh19 tháng 3, 2026

Hướng dẫn thiết lập pipeline CI/CD hoàn chỉnh từ code đến production với Docker container và GitHub Actions workflow automation. Giảm thời gian deploy, tăng độ tin cậy.

Quy trình CI/CD với Docker và GitHub Actions: Hướng dẫn từng bước

Hầu hết các team phát triển phần mềm ngày nay đều phải đối mặt với một bài toán giống nhau: làm sao để deploy code nhanh, an toàn và không gây sự cố trên production. Nếu bạn vẫn đang deploy thủ công, upload file qua FTP hoặc SSH vào server để chạy git pull, thì pipeline CI/CD chính là giải pháp bạn cần.

CI/CD (Continuous Integration và Continuous Deployment) kết hợp với Docker và GitHub Actions tạo thành một hệ thống tự động hóa mạnh mẽ. Code của bạn được kiểm tra, build, test, và deploy lên production mà không cần can thiệp thủ công. Theo khảo sát của CNCF năm 2025, 83% các DevOps team đang sử dụng container trong pipeline CI/CD của họ, chứng tỏ đây không phải là công nghệ mới lạ mà là tiêu chuẩn ngành.

Bài viết này sẽ hướng dẫn bạn từng bước thiết lập một pipeline CI/CD hoàn chỉnh, từ lúc push code lên GitHub cho đến khi ứng dụng chạy trên production server.

Sơ đồ tổng quan quy trình CI/CD từ commit đến production

Sơ đồ tổng quan quy trình CI/CD từ commit đến production

Kiến trúc CI/CD Pipeline tổng quan

Trước khi bắt tay vào cấu hình, bạn cần hiểu pipeline hoạt động như thế nào. Pipeline CI/CD gồm ba giai đoạn chính:

Continuous Integration (CI) là bước mà code của bạn được tự động kiểm tra, build, và test mỗi khi có commit mới. Thay vì chờ team lead review lâu, lỗi phát sinh ngay lập tức và được báo trong vài phút. GitHub Actions sẽ chạy các test tự động, kiểm tra linting, build Docker image, và xác nhận mọi thứ hoạt động đúng trước khi code vào staging.

Continuous Deployment (CD) là giai đoạn tự động đưa code đã được kiểm chứng lên các môi trường khác nhau. Nó có thể là staging để QA test, hoặc trực tiếp lên production nếu các điều kiện được đáp ứng.

Containerization với Docker đóng vai trò bao gói toàn bộ ứng dụng của bạn với tất cả dependencies vào một image duy nhất. Nhờ vậy, ứng dụng chạy một cách nhất quán trên dev machine, staging server, hay production—không có tình trạng "chạy tốt trên máy tôi nhưng không chạy trên server".

Sơ đồ: Luồng CI/CD pipeline hoàn chỉnh

Sơ đồ: Luồng CI/CD pipeline hoàn chỉnh

Theo kinh nghiệm triển khai thực tế, một pipeline đơn giản nhưng hiệu quả thường gồm: trigger khi có push code → chạy test → build image Docker → push lên registry → deploy tự động. Nếu bất kỳ bước nào thất bại, toàn bộ pipeline dừng lại, developer được thông báo ngay, và production được bảo vệ khỏi các lỗi tiềm ẩn.

Docker image được xây dựng từ nhiều layer, mỗi layer là một bước trong Dockerfile

Docker image được xây dựng từ nhiều layer, mỗi layer là một bước trong Dockerfile

Cài đặt Docker và viết Dockerfile

Docker là nền tảng cho containerization. Nó đóng gói ứng dụng và tất cả dependencies của nó vào một image, sau đó image này có thể chạy ở bất kỳ đâu có Docker. Theo Docker Blog năm 2025, sử dụng Docker giúp giảm 70% thời gian setup môi trường so với cách cài đặt thủ công.

Để bắt đầu, bạn cần cài đặt Docker Desktop trên máy local. Truy cập docker.com tải bản phù hợp với hệ điều hành của bạn (Windows, macOS, hoặc Linux).

Viết Dockerfile cho ứng dụng Node.js:

Dockerfile là một file chứa các chỉ lệnh để build Docker image. Cấu trúc cơ bản như sau:

# Bước 1: Build stage
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

# Bước 2: Production stage
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm install --production

COPY --from=builder /app/dist ./dist

EXPOSE 3000

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

Dockerfile này sử dụng multi-stage build, một best practice quan trọng. Bước đầu tiên (builder) sẽ cài đặt tất cả dependencies bao gồm dev dependencies và chạy npm run build để compile code. Bước thứ hai (production) chỉ copy file đã build và cài đặt production dependencies mà thôi. Cách này giúp giảm kích thước image Docker từ 300MB xuống còn 80MB vì không phải mang theo dev tools, compile tools, hay source code không cần thiết.

Một số best practice khác khi viết Dockerfile:

Sử dụng image base nhẹ như node:18-alpine thay vì node:18 giúp image nhỏ hơn và startup nhanh hơn. Alpine Linux chỉ có kích thước 5MB so với 900MB của Ubuntu.

Sắp xếp lệnh từ ít thay đổi đến thay đổi nhiều để tận dụng Docker layer caching. Ví dụ, copy package.json và chạy npm install trước khi copy toàn bộ source code. Nếu source code thay đổi nhưng package.json không đổi, Docker sẽ sử dụng lại cache từ layer npm install mà không cần cài lại.

Không chạy container với root user. Tạo một non-root user và chuyển quyền sở hữu:

RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

Build image bằng lệnh:

docker build -t myapp:1.0 .

Test image locally bằng:

docker run -p 3000:3000 myapp:1.0

Nếu ứng dụng chạy bình thường, bạn đã sẵn sàng để push image lên Docker registry.

Cấu trúc file workflow YAML trong GitHub Actions

Cấu trúc file workflow YAML trong GitHub Actions

Cấu hình GitHub Actions Workflow

GitHub Actions là CI/CD service tích hợp sẵn trong GitHub. Nó cho phép bạn tự động chạy các công việc (jobs) mỗi khi có sự kiện trên repository, chẳng hạn như push code, tạo pull request, hay tạo release. Một trong những lợi thế là GitHub Actions có hơn 15,000 actions sẵn có trên marketplace, giúp bạn có thể sử dụng các công cụ được xây dựng sẵn mà không cần viết code từ đầu.

Tạo file workflow:

Tạo thư mục .github/workflows/ trong repository của bạn, sau đó tạo file ci-cd.yml:

name: CI/CD Pipeline

on:
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm install
      
      - name: Run linting
        run: npm run lint
      
      - name: Run tests
        run: npm run test
  
  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - 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=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha
      
      - name: Build and push Docker image
        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
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - name: Deploy to production
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
          DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
          DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
        run: |
          mkdir -p ~/.ssh
          echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no \
            $DEPLOY_USER@$DEPLOY_HOST \
            "cd /app && docker-compose pull && docker-compose up -d"

File workflow này định nghĩa ba jobs chính:

Job test: Chạy mỗi khi có push hoặc pull request. Nó checkout code, cài đặt Node.js, cài dependencies, chạy linting và test. Nếu bất kỳ bước nào thất bại, job dừng lại.

Job build: Chỉ chạy sau khi job test thành công (do needs: test). Nó build Docker image và push lên container registry. Lưu ý, việc sử dụng GitHub Actions cache (cache-from: type=gha, cache-to: type=gha,mode=max) sẽ giảm đáng kể build time ở những lần chạy sau, vì Docker sẽ sử dụng lại các layer từ build trước đó.

Job deploy: Chỉ chạy trên branch main sau khi build thành công. Nó SSH vào production server và chạy docker-compose pull && docker-compose up -d để pull image mới và khởi động container.

Quản lý secrets:

Chú ý rằng workflow này sử dụng secrets như DEPLOY_KEY, DEPLOY_HOST, DEPLOY_USER. Bạn cần thêm những secrets này vào GitHub repository:

  1. Vào Settings → Secrets and variables → Actions
  2. Click New repository secret
  3. Thêm từng secret với tên và giá trị tương ứng

Secrets sẽ được mã hóa và chỉ có thể được sử dụng trong workflow, không hiển thị dưới dạng plain text.

Sơ đồ triển khai qua nhiều môi trường dev, staging, production

Sơ đồ triển khai qua nhiều môi trường dev, staging, production

Deploy tự động lên nhiều môi trường

Thực tế, hầu hết các ứng dụng sản phẩm cần phải trải qua nhiều giai đoạn trước khi lên production: development, staging, và production. Mỗi môi trường có cấu hình, database, và secret key khác nhau.

Cấu trúc multi-environment trong GitHub Actions:

name: Multi-Environment Deployment

on:
  push:
    branches:
      - main
      - develop
      - staging

env:
  REGISTRY: ghcr.io

jobs:
  determine-environment:
    runs-on: ubuntu-latest
    outputs:
      environment: ${{ steps.set-env.outputs.environment }}
      deploy-host: ${{ steps.set-env.outputs.deploy-host }}
    steps:
      - name: Determine environment
        id: set-env
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "environment=production" >> $GITHUB_OUTPUT
            echo "deploy-host=${{ secrets.PROD_HOST }}" >> $GITHUB_OUTPUT
          elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
            echo "environment=staging" >> $GITHUB_OUTPUT
            echo "deploy-host=${{ secrets.STAGING_HOST }}" >> $GITHUB_OUTPUT
          else
            echo "environment=development" >> $GITHUB_OUTPUT
            echo "deploy-host=${{ secrets.DEV_HOST }}" >> $GITHUB_OUTPUT
          fi

  build-and-deploy:
    needs: determine-environment
    runs-on: ubuntu-latest
    environment:
      name: ${{ needs.determine-environment.outputs.environment }}
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Build and push to registry
        run: |
          docker build -t ${{ env.REGISTRY }}/myapp:${{ github.sha }} .
          docker push ${{ env.REGISTRY }}/myapp:${{ github.sha }}
      
      - name: Deploy to ${{ needs.determine-environment.outputs.environment }}
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
          DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
          DEPLOY_HOST: ${{ needs.determine-environment.outputs.deploy-host }}
          APP_ENV: ${{ needs.determine-environment.outputs.environment }}
        run: |
          mkdir -p ~/.ssh
          echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no \
            $DEPLOY_USER@$DEPLOY_HOST \
            "export APP_ENV=$APP_ENV && \
             cd /app && \
             docker-compose pull && \
             docker-compose up -d && \
             docker-compose exec -T app npm run migrate"

Workflow này sử dụng GitHub Environments để quản lý cấu hình riêng cho mỗi environment. Khi branch được push, job determine-environment sẽ xác định môi trường dựa trên tên branch và output các biến cần thiết. Job build-and-deploy sau đó sử dụng những biến đó để deploy vào đúng host với đúng cấu hình.

Approval gates cho production:

Để tránh tình trạng code chưa được kiểm chứng kỹ lưỡng lên production, bạn có thể thêm manual approval:

  1. Vào Settings → Environments → production
  2. Enable Required reviewers
  3. Thêm những người hoặc team cần phải approve deployment

Khi một deployment tới production được trigger, workflow sẽ tạm dừng lại và chờ approval từ những người được chỉ định trước khi tiếp tục.

Cấu hình Docker Compose để orchestrate multiple containers

Cấu hình Docker Compose để orchestrate multiple containers

Triển khai với Docker Compose trên production

Trên production server, bạn sẽ cần Docker Compose để quản lý container một cách dễ dàng hơn. Docker Compose cho phép bạn định nghĩa toàn bộ stack (web app, database, cache, etc.) trong một file YAML duy nhất.

File docker-compose.yml trên production:

version: '3.8'

services:
  app:
    image: ghcr.io/youruser/myapp:latest
    restart: always
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://user:password@db:5432/myapp
      REDIS_URL: redis://redis:6379
    depends_on:
      - db
      - redis
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - app-network
  
  db:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network
  
  redis:
    image: redis:7-alpine
    restart: always
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge

File này định nghĩa bốn services: ứng dụng Node.js, PostgreSQL database, Redis cache, và Nginx reverse proxy. Tất cả container được kết nối qua một internal network gọi là app-network.

Triển khai:

SSH vào production server và chạy:

# Pull image mới
docker-compose pull

# Khởi động containers
docker-compose up -d

# Chạy database migrations
docker-compose exec app npm run migrate

# Kiểm tra logs
docker-compose logs -f app

Health checks:

Lưu ý trong file compose, ứng dụng có healthcheck định nghĩa. Docker sẽ định kỳ gọi endpoint /health để kiểm tra ứng dụng còn sống không. Nếu health check thất bại, container sẽ được tự động khởi động lại. Điều này đảm bảo ứng dụng luôn chạy ngay cả khi xảy ra lỗi.

Xem chi tiết logs của workflow chạy trên GitHub Actions dashboard

Xem chi tiết logs của workflow chạy trên GitHub Actions dashboard

Theo dõi và debug pipeline

GitHub Actions cung cấp một dashboard chi tiết để bạn theo dõi từng workflow chạy. Vào tab Actions trên repository, bạn sẽ thấy danh sách tất cả workflow runs. Click vào một run để xem chi tiết từng job và step.

Trong thực tế, nhiều lỗi pipeline thường xuất phát từ:

Build cache không hiệu quả: Nếu Docker layer caching không được sử dụng đúng cách, build time sẽ rất lâu. Giải pháp là sắp xếp lại thứ tự copy file trong Dockerfile sao cho những file ít thay đổi được copy trước. Ngoài ra, sử dụng .dockerignore để loại trừ file không cần thiết khỏi build context cũng giúp giảm build time.

Secrets không được set đúng: Nếu deployment fail vì credential error, kiểm tra xem secret có được thêm đúng tên không. GitHub Actions phân biệt chữ hoa/thường, nên DEPLOY_KEYdeploy_key là hai secret khác nhau.

Network connectivity issues: Nếu deploy step không thể SSH vào server, kiểm tra SSH key, firewall rules, và xem server có chấp nhận connection từ GitHub Actions runners không. GitHub Actions runners sử dụng các IP address dynamic, nên bạn không thể whitelist cụ thể. Thay vào đó, hãy sử dụng SSH key-based authentication.

Resource limits: GitHub Actions runners có giới hạn về CPU, memory, và disk. Nếu Docker build cần quá nhiều memory, bạn có thể sử dụng docker/build-push-action@v5 với docker-options để tăng resource hoặc chia nhỏ build process thành nhiều step.

Một tip hữu ích: bật debug logging trong GitHub Actions bằng cách thêm secrets ACTIONS_STEP_DEBUG = true để xem thêm thông tin chi tiết trong logs.

Theo dõi performance và error rates của deployed application

Theo dõi performance và error rates của deployed application

Tối ưu hóa pipeline

Khi pipeline của bạn được thiết lập, bước tiếp theo là tối ưu hóa nó để chạy nhanh hơn và tiết kiệm resource hơn. Một số best practice:

Parallelization: Nếu có nhiều test suites độc lập, chạy chúng song song. Ví dụ, unit tests và integration tests có thể chạy cùng lúc thay vì tuần tự:

test-unit:
  runs-on: ubuntu-latest
  steps:
    - run: npm run test:unit

test-integration:
  runs-on: ubuntu-latest
  steps:
    - run: npm run test:integration

Selective builds: Không phải tất cả changes đều cần rebuild Docker image. Nếu chỉ có file markdown hoặc documentation thay đổi, bạn có thể skip build:

build:
  if: |
    !contains(github.event.head_commit.modified, '.md') &&
    !contains(github.event.head_commit.modified, '.txt')

Matrix builds: Nếu cần test ứng dụng trên nhiều phiên bản Node.js, sử dụng matrix strategy:

test:
  strategy:
    matrix:
      node-version: [16, 18, 20]
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}

Artifact management: Lưu logs, test reports, và build artifacts để sau này debug hoặc audit:

- name: Upload test results
  if: always()
  uses: actions/upload-artifact@v3
  with:
    name: test-results
    path: coverage/

Kết luận

Thiết lập CI/CD pipeline với Docker và GitHub Actions là một bước quan trọng để team phát triển nhanh chóng, an toàn, và hiệu quả. Bắt đầu với một pipeline đơn giản: code push → test → build → deploy, sau đó mở rộng dần thêm approval gates, multi-environment, và monitoring khi team phát triển.

Nhớ rằng mục đích của CI/CD không phải là công nghệ mà là tự động hóa, giảm lỗi thủ công, và cho phép team deploy nhanh chóng và tự tin. Vì vậy, hãy bắt đầu đơn giản, test kỹ lưỡng trên staging trước khi đưa lên production, và cải tiến pipeline dần dần dựa trên kinh nghiệm thực tế của team.

Câu hỏi thường gặp

GitHub Actions có miễn phí không?

GitHub Actions miễn phí cho public repository với unlimited minutes. Đối với private repository, bạn được 2000 minutes miễn phí mỗi tháng trên tài khoản cá nhân. Nếu vượt quá, sẽ tính phí theo từng 1000 minutes. Giá khác nhau tùy theo runner type (Linux rẻ nhất, macOS đắt hơn, Windows nằm giữa).

Nên dùng Docker Compose hay Kubernetes?

Docker Compose phù hợp cho small team, single server, hoặc environment phát triển. Nó đơn giản, dễ setup, và không cần overhead quản lý cluster. Kubernetes phù hợp cho enterprise scale, multiple servers, high availability, và auto-scaling. Nếu team bạn nhỏ hơn 10 người, bắt đầu với Docker Compose là đủ. Khi ứng dụng lớn lên hoặc cần high availability, sau đó mới nâng cấp lên Kubernetes.

Làm sao để rollback nếu deployment bị lỗi?

GitHub Actions cho phép bạn re-run một workflow từ một commit trước đó. Vào tab Actions, tìm commit trước đó, và click "Re-run all jobs". Docker image đã build cho commit đó vẫn còn trong registry, nên deployment sẽ sử dụng image đó thay vì image lỗi. Ngoài ra, bạn nên tag image với semantic versioning (v1.0.0, v1.0.1, etc.) để dễ dàng rollback nếu cần.

Làm sao để triển khai zero-downtime deployment?

Sử dụng blue-green deployment hoặc rolling deployment. Ví dụ, chạy hai phiên bản ứng dụng (blue và green), traffic đến blue, sau đó deploy phiên bản mới lên green, test kỹ lưỡng, rồi switch traffic sang green. Nếu có lỗi, switch lại sang blue ngay. Docker Compose không hỗ trợ rolling deployment tự động, nhưng bạn có thể viết shell script để orchestrate điều này, hoặc sử dụng Kubernetes với RollingUpdate strategy.

Cần bao nhiêu secrets để setup CI/CD?

Tối thiểu cần: DEPLOY_KEY (SSH private key), DEPLOY_HOST (server IP/domain), DEPLOY_USER (SSH username), và DATABASE_PASSWORD cho production environment. Nếu đẩy image lên private container registry (không phải GitHub), cần thêm registry username và password. Luôn dùng GitHub Secrets cho những thông tin nhạy cảm, không bao giờ commit vào repository.

Khám Phá

GitHub Copilot vs Amazon Q Developer: So Sánh Công Cụ AI Lập Trình 2026

Multi-Agent AI 2026: Hướng Dẫn Xây Dựng Hệ Thống Theo Design Patterns

AI DevOps Tools: Tối Ưu Hóa Kubernetes Automation 2026

Multi-Agent Pattern: Hướng dẫn chi tiết các mô hình phối hợp giữa Agent AI

Docker Windows vs Linux: Giải Quyết Lỗi Case-Sensitive Paths