Quay lại danh sách
Tin tức công nghệ

Hướng dẫn xây dựng CI/CD pipeline tự động cho side project với GitHub Actions, Docker và VPS (không cần Kubernetes)

18 tháng 5, 2026

Giới thiệu: Tại sao CI/CD quan trọng cho side project?

Trong thế giới phát triển phần mềm hiện đại, việc tích hợp và triển khai liên tục (CI/CD) không còn là đặc quyền của các dự án lớn. Ngay cả với một side project cá nhân, một pipeline tự động hóa có thể tiết kiệm hàng giờ làm việc thủ công, giảm thiểu lỗi và cho phép bạn tập trung vào việc viết code thay vì các thao tác triển khai lặp đi lặp lại. Tuy nhiên, nhiều developer ngần ngại khi nghĩ đến việc thiết lập CI/CD vì cho rằng nó phức tạp, đắt đỏ hoặc yêu cầu kiến thức về các hệ thống như Kubernetes.

Thực tế, với sự kết hợp mạnh mẽ của GitHub Actions (miễn phí cho repository public), Docker (công cụ container hóa tiêu chuẩn), và một VPS (Virtual Private Server) cơ bản, bạn hoàn toàn có thể xây dựng một pipeline CI/CD chuyên nghiệp, tự động và hiệu quả. Bài viết này sẽ hướng dẫn bạn từng bước một, không yêu cầu kiến thức nâng cao về DevOps hay cơ sở hạ tầng phức tạp.

Kiến trúc tổng quan của pipeline

Trước khi đi vào chi tiết, hãy cùng xem xét luồng công việc tự động mà chúng ta sẽ xây dựng:

  1. Kích hoạt: Bạn push code lên một nhánh (branch) cụ thể (ví dụ: main hoặc develop) trên GitHub.
  2. Build & Test (CI): GitHub Actions tự động chạy, lấy code mới nhất, cài đặt dependencies, chạy linter, và thực thi bộ test.
  3. Build Docker Image: Nếu tất cả các bước CI thành công, workflow sẽ build một Docker image từ code của bạn và push nó lên Docker Hub hoặc GitHub Container Registry.
  4. Deploy (CD): Workflow sử dụng SSH để kết nối an toàn tới VPS của bạn, kéo Docker image mới nhất về và khởi động lại container chạy ứng dụng.
  5. Thông báo: Bạn nhận được thông báo qua email, Slack, hoặc Discord về trạng thái thành công hay thất bại của quy trình.

Kiến trúc này loại bỏ hoàn toàn thao tác thủ công, đảm bảo mọi thay đổi code đều được kiểm tra và triển khai một cách nhất quán.

Chuẩn bị môi trường và công cụ

1. Cấu hình VPS (Ubuntu/Debian)

Đầu tiên, bạn cần một VPS. Các nhà cung cấp như DigitalOcean, Linode, Vultr, hoặc AWS Lightsail đều có options giá rất phải chăng cho side project. Hãy chọn một instance chạy Ubuntu 22.04 LTS hoặc Debian 11 trở lên.

Sau khi có server, thực hiện các bước cơ bản sau qua SSH:

  • Cập nhật hệ thống: sudo apt update && sudo apt upgrade -y.
  • Cài đặt Docker và Docker Compose: Đây là thành phần cốt lõi để chạy ứng dụng của bạn dưới dạng container.
    sudo apt install -y docker.io docker-compose-v2
    sudo systemctl enable docker
    sudo systemctl start docker
    sudo usermod -aG docker $USER  # Thêm user hiện tại vào group docker
    # Đăng xuất và đăng nhập lại để áp dụng thay đổi group.
  • Tạo một user chuyên dụng cho deployment (tùy chọn nhưng khuyến khích để bảo mật).

2. Thiết lập Docker Hub hoặc GitHub Container Registry

Bạn cần một nơi để lưu trữ Docker image đã build. Docker Hub cung cấp một repository private miễn phí. GitHub Container Registry (ghcr.io) cũng là một lựa chọn tuyệt vời nếu bạn muốn mọi thứ trong một hệ sinh thái.

Tạo một Access Token (cho Docker Hub) hoặc Personal Access Token (cho GitHub) với quyền đọc/ghi. Token này sẽ được GitHub Actions sử dụng để push image.

3. Tạo SSH Key cho GitHub Actions

Để GitHub Actions có thể deploy lên VPS của bạn một cách an toàn, chúng ta sử dụng cặp SSH key.

  • Trên VPS, tạo một cặp key mới với lệnh: ssh-keygen -t ed25519 -C "github-actions-deploy-key". Lưu nó vào một thư mục an toàn, ví dụ ~/.ssh/github_actions.
  • Thêm public key (github_actions.pub) vào file ~/.ssh/authorized_keys của user sẽ thực thi deploy trên VPS.
  • Copy toàn bộ nội dung của private key (github_actions). Chúng ta sẽ lưu nó dưới dạng secret trong GitHub repository.

Xây dựng Workflow GitHub Actions

Đây là trái tim của pipeline. Chúng ta sẽ tạo một file .github/workflows/ci-cd.yml trong repository của bạn.

Phần 1: Cấu hình chung và Trigger

name: CI/CD Pipeline

on:
  push:
    branches: [ "main" ]  # Chỉ chạy khi push lên nhánh main
  pull_request:
    branches: [ "main" ]  # Chạy CI cho PR vào main

jobs:

Phần 2: Job Build và Test (CI)

Job đầu tiên đảm bảo code của bạn luôn ở trạng thái "sạch sẽ".

  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js (Ví dụ với project Node.js)
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci  # Sử dụng ci để có cài đặt chính xác theo lockfile

      - name: Run Linter
        run: npm run lint  # Chạy ESLint, Prettier, etc.

      - name: Run Tests
        run: npm test

      - name: Build Project (nếu cần)
        run: npm run build

Phần 3: Job Build và Push Docker Image

Job này chỉ chạy nếu job CI thành công VÀ sự kiện là push lên nhánh main (không phải pull request).

  build-and-push-docker:
    needs: build-and-test  # Phụ thuộc vào job CI
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: your-dockerhub-username/your-app-name
          tags: |
            type=sha,prefix={{branch}}-,format=short
            type=ref,event=branch
            latest

      - 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 }}

Phần 4: Job Deploy lên VPS (CD)

Job cuối cùng thực hiện việc triển khai. Nó sử dụng private key SSH đã lưu trữ trong secrets.

  deploy-to-vps:
    needs: build-and-push-docker  # Chỉ chạy sau khi image được push
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to VPS via SSH
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USERNAME }}
          key: ${{ secrets.VPS_SSH_PRIVATE_KEY }}
          port: ${{ secrets.VPS_SSH_PORT }}
          script: |
            # Pull image mới nhất
            docker pull your-dockerhub-username/your-app-name:latest
            # Dừng và xóa container cũ
            docker stop your-app-container || true
            docker rm your-app-container || true
            # Chạy container mới với cấu hình mong muốn
            docker run -d \
              --name your-app-container \
              --restart unless-stopped \
              -p 3000:3000 \
              -e NODE_ENV=production \
              your-dockerhub-username/your-app-name:latest

Cấu hình Secrets và Variables trên GitHub

Điều quan trọng là không bao giờ hard-code thông tin nhạy cảm vào file workflow. Thay vào đó, sử dụng tính năng Secrets của GitHub.

Truy cập Settings > Secrets and variables > Actions trong repository của bạn và thêm các secrets sau:

  • DOCKERHUB_USERNAME: Tên đăng nhập Docker Hub của bạn.
  • DOCKERHUB_TOKEN: Access Token của Docker Hub.
  • VPS_HOST: Địa chỉ IP hoặc domain của VPS.
  • VPS_USERNAME: Tên user SSH (ví dụ: deploy hoặc root).
  • VPS_SSH_PRIVATE_KEY: Toàn bộ nội dung của private SSH key bạn đã tạo.
  • VPS_SSH_PORT: Cổng SSH (mặc định là 22).

Tối ưu hóa và các phương án dự phòng

Sử dụng Docker Compose trên VPS

Thay vì chạy lệnh docker run dài dòng, bạn có thể chuẩn bị sẵn một file docker-compose.prod.yml trên VPS. Lệnh deploy script sẽ đơn giản hơn:

cd /path/to/your/app
# Cập nhật image trong file .env hoặc trực tiếp pull
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d

Xử lý Database Migration

Nếu ứng dụng của bạn sử dụng database, cần có cơ chế chạy migration tự động và an toàn. Một cách tiếp cận phổ biến là chạy migration script bên trong một container tạm thời, hoặc sử dụng entrypoint script trong Docker image để kiểm tra và chạy migration trước khi khởi động ứng dụng chính.

Health Check và Rollback tự động

Bạn có thể mở rộng script deploy để thực hiện health check sau khi container mới chạy. Nếu health check thất bại sau một số lần thử, script có thể tự động rollback về image trước đó để đảm bảo tính sẵn sàng của dịch vụ.

Giám sát và Logging

Cấu hình Docker để ghi log ra stdout/stderr, sau đó sử dụng công cụ như docker logs hoặc cấu hình log driver để gửi log tới một dịch vụ tập trung. Các công cụ đơn giản như loki + promtail hoặc vector có thể được chạy dưới dạng container khác trên cùng VPS.

Kết luận

Việc thiết lập một pipeline CI/CD tự động cho side project không còn là một thách thức lớn. Với sức mạnh của GitHub Actions, tính di động của Docker, và sự linh hoạt của một VPS cơ bản, bạn có thể tạo ra một quy trình làm việc chuyên nghiệp giúp tăng tốc độ phát triển, cải thiện chất lượng code và giảm thiểu rủi ro khi triển khai. Hãy bắt đầu với một workflow đơn giản như hướng dẫn trên, sau đó dần dần mở rộng và tinh chỉnh nó theo nhu cầu cụ thể của dự án. Khi side project của bạn phát triển, nền tảng tự động hóa vững chắc này sẽ trở thành một tài sản vô giá, cho phép bạn mở rộng quy mô một cách tự tin và hiệu quả.

Lời khuyên cuối cùng: Hãy luôn giữ cho Dockerfile và file workflow của bạn đơn giản, dễ đọc và dễ bảo trì. Tính minh bạch và khả năng tái sản xuất (reproducibility) là chìa khóa thành công của bất kỳ hệ thống CI/CD nào.