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)
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:
- Kích hoạt: Bạn push code lên một nhánh (branch) cụ thể (ví dụ:
mainhoặcdevelop) trên GitHub. - 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.
- 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.
- 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.
- 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_keyscủ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 buildPhầ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:latestCấ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ụ:deployhoặcroot).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 -dXử 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.
