Tập 16 đã đóng gói ứng dụng quản lý chi tiêu thành image Docker chạy non-root kèm healthcheck. Tập này nối tiếp đúng điểm đó: tự động hóa đường đi từ lúc push code tới lúc code chạy trên server. Mỗi lần push lên nhánh main, GitHub Actions chạy test trước, rồi deploy tự động — chạy migrations, collectstatic và restart service. Kết quả là một lần push thành một lần deploy, không còn thao tác tay trên server, và test lỗi sẽ chặn deploy trước khi chạm tới production.
Pipeline gồm những bước nào?
Một pipeline deploy chuẩn cho app Django gồm bốn bước nối tiếp nhau. Bước test chạy pytest và kiểm tra migrations còn thiếu. Bước deploy kéo code mới về server, build lại image Docker, rồi chạy migrations, collectstatic và restart. Bước kiểm tra cuối gọi endpoint /health — tập 8 đã xây — để xác nhận app mới đang chạy trước khi coi deploy là xong.
- Test: pytest, ruff, makemigrations –check –dry-run.
- Build: docker compose build image mới từ code vừa pull về.
- Deploy: SSH vào server, chạy migrations, collectstatic rồi restart container.
- Verify: curl /health/ và kiểm tra log 5 phút đầu.
Viết workflow test và deploy
File .github/workflows/deploy.yml dưới đây chạy trên mỗi push lên main. Job test dùng Python 3.15 và PostgreSQL service container, đúng stack production đã chọn ở tập 4. Chỉ khi test xanh, job deploy mới chạy — dùng appleboy/ssh-action để SSH vào server và gọi script deploy.
name: Deployon: push: branches: [main]jobs: test: runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_DB: expense_test POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres ports: ["5432:5432"] options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/expense_test steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.15" - run: pip install -r requirements.txt - run: python manage.py makemigrations --check --dry-run - run: python manage.py test deploy: needs: test runs-on: ubuntu-latest steps: - uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} script: bash ~/deploy.sh
Script deploy trên server
Script deploy.sh nằm trên server, chỉ làm bốn việc theo đúng thứ tự: kéo image mới, chạy migrations, collectstatic rồi restart container. Migrations chạy trước khi app mới nhận request — đúng nguyên tắc tương thích ngược của tập 6, nên deploy không downtime.
#!/bin/bashset -ecd ~/expense-appgit pull origin maindocker compose build webdocker compose run --rm web python manage.py migrate --noinputdocker compose run --rm web python manage.py collectstatic --noinputdocker compose up -d webcurl -fsS http://localhost:8000/health/
Ba bẫy thường gặp
Ba lỗi hay gặp nhất khi mới dựng CI/CD đều có thể tránh bằng một dòng cấu hình.
- Quên –noinput ở collectstatic: lệnh dừng chờ nhập tay và job treo tới khi timeout. Luôn thêm –noinput.
- Secrets lộ trong log: đừng echo biến môi trường hay in file .env. GitHub tự che secret đã đăng ký, nhưng chỉ khi giá trị khớp chính xác.
- Deploy cả khi test đỏ: job deploy phải có needs: test, nếu không hai job chạy song song và code lỗi vẫn lên production.
Áp dụng vào dự án của bạn
Với ứng dụng quản lý chi tiêu, hãy bắt đầu từ pipeline tối thiểu: chỉ test và deploy, chưa cần build image riêng nếu server đang chạy code từ git. Tạo một workflow chạy pytest trên mỗi pull request trước — đội của bạn sẽ thấy giá trị ngay khi một test đỏ chặn được một lần deploy lỗi. Khi pipeline đã ổn định, thêm bước build image và health check như trên, rồi mới nghĩ tới blue-green ở tập 18.
Bài viết liên quan
- Django vận hành production #16: Docker cho production — multi-stage build, chạy non-root, healthcheck
- Django vận hành production #6: Migrations an toàn trên production — mẫu tương thích ngược, deploy không downtime
- Django vận hành production #8: Monitoring và health checks — endpoint /health, uptime, metrics với django-prometheus
Câu hỏi thường gặp
Test fail thì deploy có chạy không?
Không. Job deploy có needs: test nên GitHub Actions bỏ qua deploy khi test đỏ. Code lỗi dừng ở bước test, production không bị chạm tới — đó chính là giá trị lớn nhất của pipeline này.
Migrations chạy lúc nào trong deploy?
Migrations chạy trên server sau khi kéo image mới và trước khi restart container. Thứ tự này giữ app cũ chạy được với schema mới trong lúc chuyển đổi, đúng mẫu tương thích ngược ở tập 6. Tuyệt đối không chạy migrations trên máy local rồi commit file db.sqlite3.
Có cần build image mới mỗi lần deploy không?
Có, nếu server chạy bằng Docker như tập 16. Mỗi lần deploy build lại image từ code mới nhất để biết chính xác code nào đang chạy, và giữ lại image cũ để rollback bằng cách chạy lại nó. Nếu server chạy code trực tiếp từ git thì chỉ cần git pull rồi restart, nhưng nên chuyển sang image để deploy nhất quán.
