Logo
Featured Image
Blog

Từ Manual đến DevOps: Hướng dẫn CI/CD tự động Deploy với GitHub Actions & Docker

Author
Tobi 13/01/2026 0 views

Bạn có đang deploy website của mình theo cách "truyền thống" này không? Ở bài hướng dẫn này tôi sẽ lấy ví dụ thực tới với website Java của tôi.

  1. Build file .jar trên máy tính cá nhân.

  2. Dùng FileZilla hoặc rsync để copy file nặng vài chục MB lên VPS.

  3. SSH vào VPS, gõ lệnh stop, xóa file cũ, chạy file mới...

Tôi cũng từng như vậy. Quy trình này không chỉ tốn thời gian mà còn rất dễ gây lỗi (quên config, sai phiên bản, rớt mạng giữa chừng).

Hôm nay, tôi sẽ chia sẻ cách tôi đã tự động hóa 100% quy trình này cho blog cá nhân của mình bằng GitHub ActionsDocker. Chỉ cần git push, pha một tách cà phê để quá trình CI/CD tự thực hiện, và website tự động cập nhật.

Mô hình hoạt động (Architecture)

Thay vì mang nguyên liệu lên VPS để nấu (build code trên VPS), chúng ta sẽ nấu chín ở nhà, đóng hộp và chỉ mang lên VPS để dùng.

  1. Code (Local): Push code lên GitHub.

  2. CI (GitHub Actions): Tự động build Java (Maven), đóng gói vào Docker Image.

  3. Registry (Docker Hub): Lưu trữ Docker Image đã đóng gói.

  4. CD (VPS): Tự động kéo Image mới về và khởi động lại container.

 

Phần 1: Dockerize ứng dụng Spring Boot

Để deploy tự động, ứng dụng của bạn phải chạy trong Docker. Tạo file Dockerfile ngay tại thư mục gốc dự án (ngang hàng với pom.xml).

# 1. Sử dụng Base Image nhẹ (Alpine)
FROM eclipse-temurin:17-jdk-alpine

# 2. Thiết lập thư mục làm việc
WORKDIR /app

# 3. Copy file .jar đã build vào container
# Lưu ý: Chúng ta sẽ build bằng Maven ở bước CI, nên file sẽ nằm trong target/
COPY target/*.jar app.jar

# 4. Chạy ứng dụng
ENTRYPOINT ["java", "-jar", "app.jar"]

Phần 2: Thiết lập Workflow với GitHub Actions

Đây là "trái tim" của hệ thống. Tạo file .github/workflows/deploy.yml.

File này định nghĩa quy trình: Build Maven -> Push Docker Hub -> Deploy VPS.

name: Deploy Java App

on:
  push:
    branches: [ "main" ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    # 1. Lấy Source Code
    - name: Checkout code
      uses: actions/checkout@v4

    # 2. Cài đặt Java 17
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'
        cache: 'maven'

    # 3. Build file .jar (Tạo ra thư mục target/)
    - name: Build with Maven
      run: mvn clean package -DskipTests

    # 4. Đăng nhập Docker Hub
    - name: Login to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_TOKEN }}

    # 5. Build & Push Docker Image
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ secrets.DOCKERHUB_USERNAME }}/my-webapp:latest

    # 6. Deploy lên VPS qua SSH
    - name: Deploy to VPS
      uses: appleboy/[email protected]
      with:
        host: ${{ secrets.VPS_HOST }}
        username: ${{ secrets.VPS_USERNAME }}
        key: ${{ secrets.VPS_KEY }}
        port: 22 # Thay đổi nếu VPS bạn dùng port khác
        script: |
          cd ~/my-project-folder
          docker compose pull webapp
          docker compose up -d webapp
          docker image prune -f

 

Phần 3: Cấu hình GitHub Actions & Docker 

Bước 1: Chuẩn bị kho chứa Image (Docker Hub)

GitHub Actions sẽ build ra một cục Image, chúng ta cần chỗ để lưu nó trước khi VPS tải về.

1.Đăng nhập vào Docker Hub.

2.Tạo một Repository mới (Create Repository)

3.Đặt tên (ví dụ): com.quyenlt-webapp (tên này theo đúng ý định đổi tên miền của bạn).

Để chế độ Public (cho dễ cấu hình lúc đầu) hoặc Private (bảo mật hơn nhưng cần cấu hình thêm 1 chút, mình khuyên dùng Public nếu không có gì quá bí mật trong code, hoặc Private thì GitHub Actions vẫn login được).

Bước 2: Tạo chìa khóa để GitHub vào được VPS (SSH Key)

Đây là bước quan trọng nhất. Thay vì đưa mật khẩu VPS cho GitHub (rất nguy hiểm), ta sẽ tạo một cặp chìa khóa chuyên dụng.

Tạo cặp key mới (không trùng với key cá nhân của bạn): Copy và chạy lệnh sau trên Terminal của Mac:

ssh-keygen -t rsa -b 4096 -C "github-actions-deploy" -f ~/.ssh/github_actions_key

(Khi nó hỏi Passphrase, cứ nhấn Enter để bỏ qua, để trống).

Đưa "ổ khóa" (Public Key) lên VPS: Bạn cần copy nội dung file đuôi .pub lên VPS. Chạy lệnh này trên Mac để lấy nội dung:

cat ~/.ssh/github_actions_key.pub

-> Copy toàn bộ dòng kết quả hiện ra (bắt đầu bằng ssh-rsa ...).

Cài đặt vào VPS:

    • SSH vào VPS của bạn như mọi khi.

    • Gõ lệnh: nano ~/.ssh/authorized_keys

    • Dán đoạn mã vừa copy vào cuối file này (xuống dòng mới rồi dán).

    • Bấm Ctrl+O -> Enter để lưu, Ctrl+X để thoát.

Bước 3: Cấu hình bí mật trên GitHub (GitHub Secrets)

Bây giờ ta nạp thông tin vào GitHub để nó biết đường truy cập.

1.Vào repo của bạn trên GitHub -> Settings.

2.Menu trái chọn Secrets and variables -> Actions.

3.Nhấn New repository secret và thêm lần lượt các mục sau:

Với mỗi lần bấm "New repository secret", bạn điền như sau:

Lần 1: Tên đăng nhập Docker Hub

  • Name: DOCKERHUB_USERNAME

  • Secret: Điền tên user bạn dùng để đăng nhập Docker Hub (ví dụ: tobi123).

  • 👉 Bấm Add secret.

Lần 2: Mật khẩu Docker Hub

  • Name: DOCKERHUB_TOKEN

  • Secret: Điền mật khẩu đăng nhập Docker Hub của bạn (hoặc Access Token nếu bạn biết cách tạo, nếu không cứ điền mật khẩu đăng nhập bình thường).

  • 👉 Bấm Add secret.

Lần 3: Địa chỉ IP của VPS

  • Name: VPS_HOST

  • Secret: Điền địa chỉ IP của VPS (ví dụ: 103.111.222.333).

  • 👉 Bấm Add secret.

Lần 4: Tên User đăng nhập VPS

  • Name: VPS_USERNAME

  • Secret: Điền user bạn hay dùng để SSH (thường là root).

  • 👉 Bấm Add secret.

Lần 5: Key SSH (Cái này quan trọng nhất)

  • Name: VPS_KEY

  • Secret: Bạn mở Terminal trên máy Mac, gõ lệnh cat ~/.ssh/github_deploy_key (file không có đuôi .pub), sau đó Copy toàn bộ nội dung hiện ra.

    • Lưu ý: Phải copy cả dòng đầu -----BEGIN OPENSSH PRIVATE KEY----- và dòng cuối -----END OPENSSH PRIVATE KEY-----.

  • 👉 Bấm Add secret.


Sau khi làm xong, bạn sẽ thấy danh sách hiện ra đủ 5 dòng DOCKERHUB_USERNAME, DOCKERHUB_TOKEN, VPS_HOST, VPS_USERNAME, VPS_KEY là thành công.

Phần 4: Cấu hình trên VPS (Docker Compose)

Bài hướng dẫn trước đó mình có hướng dẫn mọi người deploy website Java Spring Boot lên VPS với Docker rồi, Ở đây mình tiếp tục với website này luôn nhé .

Trên VPS, file docker-compose.yml không cần build: . nữa. Chúng ta sửa nó để lấy ảnh từ Docker Hub về.

version: '3.8'

services:
  # --- Service MySQL (Giữ nguyên để không mất DB) ---
  mysqldb:
    image: mysql:8.0.28
    container_name: mysqldb
    restart: always
    environment:
    MYSQL_ROOT_PASSWORD: 1234567
      MYSQL_DATABASE: quyenlt
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - db_data:/var/lib/mysql
      - ./data.sql:/docker-entrypoint-initdb.d/init.sql

  # --- Service Spring Boot (SỬA PHẦN NÀY) ---
  webapp:
    # ⚠️ QUAN TRỌNG: Thay YOUR_DOCKERHUB_USERNAME bằng tên user thật của bạn
    # Ví dụ: tobi123/com.quyenlt-webapp:latest
  image: YOUR_DOCKERHUB_USERNAME/com.quyenlt-webapp:latest

    container_name: webapp
    restart: always
    expose:
      - "8080"
    depends_on:
      - mysqldb
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysqldb:3306/quyenlt?useSSL=false&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: 1234567
      SPRING_JPA_HIBERNATE_DDL_AUTO: update
      SPRING_WEB_RESOURCES_STATIC_LOCATIONS: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:/app/
    # Giữ nguyên volume để không mất ảnh cũ đã upload
    volumes:
      - ./uploads:/app/uploads

  # --- Service Nginx Proxy Manager (Giữ nguyên) ---
  nginx-proxy:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy
    restart: unless-stopped
    ports:
      - '80:80'    # HTTP
      - '81:81'    # Admin UI
      - '443:443'  # HTTPS
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    depends_on:
      - webapp

volumes:
  db_data:

Phần 5 : Kiểm Thử

Bước 1: Kích hoạt (Push code lên GitHub)

Hệ thống CI/CD chỉ chạy khi có code mới được đẩy lên. Tại thiết bị của bạn, hãy mở Terminal trong thư mục dự án và chạy các lệnh sau để đẩy file Dockerfiledeploy.yml mà bạn vừa tạo ở Phần 1 và Phần 2 lên:

git add .
git commit -m "Setup CI/CD pipeline"
git push origin main

(Nếu nhánh của bạn tên là master thì gõ git push origin master).

Bước 2: Theo dõi quá trình chạy (Monitor)

Ngay sau khi git push thành công, bạn làm như sau để xem "phim hành động":

  1. Mở trình duyệt, vào trang GitHub Repository dự án của bạn.

  2. Bấm vào tab Actions trên thanh menu phía trên.

  3. Bạn sẽ thấy một dòng mới xuất hiện (có tên là "Setup CI/CD pipeline" hoặc "Deploy Java App").

    • 🟡 Màu vàng xoay xoay: Đang chạy (đang build, đang đẩy docker...).

    • Màu xanh lá: Thành công (Đã build xong và VPS đã cập nhật).

    • Màu đỏ: Lỗi (Bạn bấm vào để xem lỗi ở bước nào: sai mật khẩu DockerHub, hay sai SSH Key...).

Bước 3: Kiểm tra kết quả (Verification)

Nếu bước 2 báo Màu xanh (Success), hãy kiểm tra xem VPS đã thực sự nhận hàng chưa bằng cách:

Cách 1: Kiểm tra "tuổi thọ" Container (Chính xác nhất) SSH vào VPS và gõ lệnh:

docker ps

Bạn nhìn vào cột STATUS:

  • Nếu thấy webapp ghi là Up Less than a minute (hoặc vài phút).

  • Trong khi mysqldb ghi là Up 2 weeks (hoặc thời gian dài).

  • -> Chúc mừng bạn! Hệ thống đã tự động tắt web cũ và bật web mới lên thành công mà không làm sập Database.

Cách 2: Xem Logs thực tế Gõ lệnh xem log của app để chắc chắn nó khởi động Spring Boot thành công:

docker logs -f --tail 100 webapp

Nếu thấy logo Spring hiện ra và dòng Started Application in ... seconds thì là website đã sống lại.

Kết Luận 

Quy trình sẽ tự động kích hoạt. GitHub Actions sẽ chuyển sang màu xanh ✅, và khi kiểm tra trên VPS bằng docker ps, container cũ đã được thay thế bằng container mới trong tích tắc mà không làm gián đoạn Database.

Tại sao bạn nên làm điều này ngay hôm nay?

  1. Tiết kiệm thời gian: Không còn thao tác thủ công lặp đi lặp lại.

  2. An toàn: Tránh việc lỡ tay xóa nhầm file trên production.

  3. Lịch sử: Dễ dàng rollback về phiên bản cũ nếu bản mới bị lỗi (chỉ cần đổi tag image).

Nếu bạn đang làm dự án Java Spring Boot cá nhân hay đồ án, hãy thử setup CI/CD này nhé. Nó sẽ giúp ích cho bạn rất nhiều về thời gian và sự tiện lợi mà nó mang lại.

Chúc các bạn thành công!

Bình luận (0)

+ =
Zalo