Giới thiệu tổng quan về CI/CD cho ứng dụng Flask

Trong môi trường phát triển phần mềm hiện đại, việc tự động hoá quá trình xây dựng, kiểm thử và triển khai (CI/CD) đã trở thành tiêu chuẩn bắt buộc để giảm thiểu rủi ro và tăng tốc độ đưa sản phẩm ra thị trường. Bài viết này sẽ tập trung vào việc thiết kế một pipeline CI/CD hoàn chỉnh cho dự án Python Flask chạy trên máy chủ ảo (VPS) bằng cách kết hợp DockerGitHub Actions. Chúng ta sẽ đi qua từng bước chi tiết, từ cấu hình môi trường phát triển, viết Dockerfile, thiết lập GitHub Actions workflow, tới triển khai tự động lên VPS.

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

Pipeline CI/CD cho Flask trên VPS bao gồm các thành phần chính sau:

  1. Repository GitHub chứa mã nguồn và các file cấu hình.
  2. Dockerfile mô tả cách xây dựng image Docker cho ứng dụng.
  3. Docker Compose (tùy chọn) để quản lý các service phụ trợ như database.
  4. GitHub Actions workflow định nghĩa các job: build, test, push, deploy.
  5. VPS chạy Docker Engine, nhận image mới và khởi động container.

Mỗi job sẽ được thực thi trên runner của GitHub, và cuối cùng sẽ sử dụng SSH để truyền image tới VPS và thực thi lệnh docker-compose up -d.

Chuẩn bị môi trường phát triển

1. Tạo project Flask cơ bản

Giả sử chúng ta có một project Flask đơn giản với cấu trúc sau:

my_flask_app/
├── app.py
├── requirements.txt
└── .gitignore

Nội dung app.py:

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def index():
    return jsonify(message='Hello, CI/CD!')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

File .txt chỉ chứa:

Flask==2.3.2

2. Viết Dockerfile cho Flask

Dockerfile sẽ dựa trên image python:3.11-slim, cài đặt các phụ thuộc và sao chép mã nguồn.

# syntax=docker/dockerfile:1
FROM python:3.11-slim

# Thiết lập biến môi trường không hiển thị cảnh báo buôn bán
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# Tạo thư mục làm việc
WORKDIR /app

# Cài đặt gcc để biên dịch các package nếu cần
RUN apt-get update && apt-get install -y --no-install-recommends \
        gcc && \
    rm -rf /var/lib/apt/lists/*

# Sao chép file requirements và cài đặt
COPY requirements.txt .
RUN pip install --upgrade pip && \
    pip install -r requirements.txt

# Sao chép toàn bộ mã nguồn
COPY . .

# Expose cổng Flask
EXPOSE 5000

# Lệnh khởi chạy
CMD ["python", "app.py"]

3. (Tùy chọn) Docker Compose để quản lý database

Nếu ứng dụng cần một database (ví dụ PostgreSQL), chúng ta có thể tạo file docker-compose.yml như sau:

version: '3.8'
services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - DATABASE_URL=postgresql://postgres:secret@db:5432/mydb
    depends_on:
      - db
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

Trong bài viết này, để tập trung vào CI/CD, chúng ta sẽ chỉ sử dụng service web mà không có database.

Thiết lập GitHub Actions workflow

1. Tạo file workflow

Trong repository, tạo thư mục .github/workflows và thêm file ci-cd.yml:

name: CI/CD Pipeline for Flask

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

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

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Run unit tests
        run: |
          # Giả sử có file tests/test_app.py
          pip install pytest
          pytest tests

  build-image:
    needs: build-test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKER_USERNAME }}/flask-ci-cd:latest

  deploy:
    needs: build-image
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to VPS via SSH
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          port: ${{ secrets.VPS_PORT }}
          script: |
            docker pull ${{ secrets.DOCKER_USERNAME }}/flask-ci-cd:latest
            docker stop flask_app || true
            docker rm flask_app || true
            docker run -d --name flask_app -p 80:5000 ${{ secrets.DOCKER_USERNAME }}/flask-ci-cd:latest

2. Giải thích chi tiết từng job

  • build-test: Kiểm tra mã nguồn bằng Python, cài đặt phụ thuộc và chạy unit test (sử dụng pytest). Nếu test thất bại, pipeline sẽ dừng lại.
  • build-image: Sau khi test thành công, Docker image được xây dựng và đẩy lên Docker Hub (hoặc GitHub Container Registry). Các thông tin đăng nhập được lưu trong secrets để bảo mật.
  • deploy: Sử dụng action appleboy/ssh-action để SSH vào VPS, kéo image mới, dừng container cũ (nếu có) và khởi chạy container mới. Lệnh docker run -d --name flask_app -p 80:5000 sẽ ánh xạ cổng 80 của VPS tới cổng 5000 trong container.

3. Cấu hình Secrets trên GitHub

Để pipeline hoạt động, cần tạo các secret sau trong repository Settings > Secrets > Actions:

  • DOCKER_USERNAMEDOCKER_PASSWORD – tài khoản Docker Hub.
  • VPS_HOST, VPS_USER, VPS_SSH_KEY, VPS_PORT – thông tin kết nối SSH tới VPS. VPS_SSH_KEY là private key đã được thêm vào ~/.ssh/authorized_keys trên VPS.

Triển khai trên VPS: Cài đặt Docker và cấu hình bảo mật

1. Cài đặt Docker Engine

Trên VPS (Ubuntu 22.04), thực hiện các lệnh sau:

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo \$VERSION_ID) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker

Sau khi cài đặt, kiểm tra bằng docker versiondocker run hello-world để xác nhận Docker hoạt động.

2. Thiết lập tường lửa (ufw)

Đảm bảo chỉ mở cổng cần thiết (80, 443 nếu có SSL) và cổng SSH.

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable

3. Tối ưu bảo mật SSH

Trong file /etc/ssh/sshd_config, tắt đăng nhập bằng mật khẩu và chỉ cho phép key authentication:

PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password

Khởi động lại dịch vụ:

sudo systemctl restart sshd

Thực hành: Kiểm tra pipeline trên môi trường thực tế

Để xác nhận pipeline hoạt động, thực hiện các bước sau:

  1. Push một commit mới lên nhánh main (hoặc tạo pull request). GitHub Actions sẽ tự động chạy.
  2. Theo dõi log của từng job trên tab Actions. Đảm bảo không có lỗi ở bước testbuild-image.
  3. Khi job deploy hoàn thành, mở trình duyệt và truy cập địa chỉ IP của VPS. Bạn sẽ thấy phản hồi JSON {"message":"Hello, CI/CD!"}.

Nếu có lỗi trong quá trình pull image hoặc khởi chạy container, kiểm tra log của action appleboy/ssh-action để xem chi tiết lệnh SSH đã thực thi.

Best practices và các lưu ý quan trọng

  • Sử dụng multi‑stage build trong Dockerfile để giảm kích thước image. Ví dụ, xây dựng phụ thuộc trong stage builder rồi copy chỉ các file cần thiết vào stage cuối.
  • Tag version cho mỗi release (ví dụ v1.0.0) thay vì luôn dùng latest. Điều này giúp rollback dễ dàng hơn.
  • Quản lý secret bằng GitHub Secrets hoặc HashiCorp Vault để tránh rò rỉ thông tin nhạy cảm.
  • Giám sát container bằng công cụ như cAdvisor hoặc Prometheus + Grafana để phát hiện vấn đề tài nguyên.
  • Thêm healthcheck trong Dockerfile hoặc docker‑compose để Docker tự động restart container khi ứng dụng không phản hồi.

Ví dụ healthcheck trong Dockerfile

HEALTHCHECK --interval=30s --timeout=5s \
  CMD curl -f http://localhost:5000/ || exit 1

Healthcheck này sẽ gửi request tới endpoint gốc mỗi 30 giây, nếu không nhận được phản hồi, container sẽ được đánh dấu là không khỏe mạnh.

Kết luận

Việc xây dựng một pipeline CI/CD cho ứng dụng Flask trên VPS không chỉ giúp giảm thiểu lỗi con người mà còn tăng tốc độ đưa tính năng mới vào môi trường production. Từ việc viết Dockerfile tối ưu, cấu hình GitHub Actions cho tới bảo mật VPS, mỗi bước đều đóng góp vào một quy trình triển khai mượt mà và an toàn. Đối với những ai muốn nâng cao kiến thức triển khai web thực tế, Tham khảo khóa học "Deploy Web - Triển khai website lên môi trường Internet" tại đây sẽ cung cấp nền tảng vững chắc về Server, VPS, Hosting và các kỹ thuật Deploy chuyên sâu.