Backend

99 Ngày Spring — Ngày 94: CI/CD cho Spring Boot

SSite Admin
29 tháng 10, 2026 3 phút đọc 5 lượt xem
99 Ngày Spring — Ngày 94: CI/CD cho Spring Boot

Triển khai bằng tay phụ thuộc vào trí nhớ của một người và chạy khác nhau mỗi lần. CI/CD biến đường đi từ commit tới production thành một pipeline lặp lại được: mọi thay đổi đều được build và test theo cùng một cách, và thứ được triển khai chính là thứ đã được kiểm tra. Bài này dùng GitHub Actions, nhưng nguyên tắc áp dụng cho mọi công cụ.

CI/CD cho Spring Boot

1. CI: build và test mọi thay đổi

Workflow nằm trong .github/workflows và chạy trên mỗi pull request cũng như mỗi lần đẩy vào main. Bước setup-java cài JDK 21 và cache thư mục Maven để các lần chạy sau nhanh hơn. Lệnh verify chạy cả unit test lẫn integration test; với Testcontainers, runner của GitHub đã có sẵn Docker. Pull request không qua CI thì không được merge.

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

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 21
          cache: maven
      - run: ./mvnw -B verify

2. Đóng image và đánh phiên bản bất biến

Sau khi test qua trên main, pipeline đóng image và đẩy lên registry. Tag của image là mã commit: nó không bao giờ bị ghi đè, nên luôn biết chính xác code nào đang chạy và rollback chỉ là triển khai lại tag cũ. Tag latest không nói được điều đó. Phiên bản dạng 1.4.0 cho người đọc có thể gắn thêm vào cùng image khi phát hành.

  image:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions: { contents: read, packages: write }
    env:
      IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { distribution: temurin, java-version: 21, cache: maven }
      - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
      - run: ./mvnw -B -DskipTests spring-boot:build-image -Dspring-boot.build-image.imageName=$IMAGE
      - run: docker push $IMAGE

3. Build một lần, triển khai nhiều môi trường

Cùng một image đi qua staging rồi tới production; thứ khác nhau chỉ là cấu hình, như đã nói ở ngày 91. Build lại cho từng môi trường nghĩa là thứ lên production chưa từng được test. GitHub Environments cho phép gắn secret riêng và quy tắc bảo vệ cho từng môi trường: staging tự động, production cần người duyệt.

  deploy-staging:
    needs: image
    environment: staging
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh staging ${{ github.sha }}

  deploy-production:
    needs: deploy-staging
    environment: production        # required reviewers configured in GitHub
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh production ${{ github.sha }}

4. Những điều làm pipeline đáng tin

Pipeline chỉ có giá trị khi đội tin kết quả của nó. Test chập chờn làm mọi người quen bấm chạy lại và bỏ qua lỗi thật. Pipeline chậm làm người ta gom nhiều thay đổi vào một lần, tăng rủi ro. Thay đổi schema database phải tương thích ngược với phiên bản code đang chạy, vì trong lúc rolling update cả hai phiên bản cùng tồn tại.

  • Giữ CI dưới khoảng mười phút; song song hóa và cache trước khi cắt test.

  • Secret của pipeline nằm trong kho secret của nền tảng; ưu tiên OIDC thay cho khóa dài hạn.

  • Ghim phiên bản action và base image.

  • Sau khi triển khai, kiểm tra health và tự rollback nếu thất bại.

  • Chạy migration bằng Flyway theo kiểu mở rộng trước, thu hẹp sau.

CI/CD tốt làm cho việc triển khai trở nên nhàm chán, và đó là mục tiêu. Ngày 95 xem nơi image được đưa tới: Kubernetes.

Tài liệu đối chiếu

  • https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven

  • https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Java — Ngày 94: Strategy và Observer

Thay chuỗi if-else bằng strategy, dùng lambda làm strategy, và phát sự kiện tới listener mà không phụ thuộc ngược.

29 thg 10, 20263 phút7
99 Ngày Spring — Ngày 93: Monitoring với Prometheus và Grafana

Phơi metric qua Actuator, Prometheus scrape, viết PromQL cho tỷ lệ lỗi và p99, dựng dashboard và alert cơ bản.

28 thg 10, 20263 phút8
99 Ngày Java — Ngày 93: Builder và Prototype

Builder cho đối tượng nhiều tham số, fluent API, kiểm tra trong build(), và sao chép đối tượng an toàn thay cho clone().

28 thg 10, 20263 phút6