🚀 Chapter 3 — Scale: Phần B — Reusable Workflow
Ở Chapter 3A, chúng ta đã triển khai Google Online Boutique với nhiều microservices và tạo một CI/CD pipeline riêng cho từng service.
Kết quả hiện tại:
GitHub
│
├── frontend
│ ↓
│ frontend-ci-cd.yml
│
├── cartservice
│ ↓
│ cartservice-ci-cd.yml
│
├── productcatalogservice
│ ↓
│ productcatalog-ci-cd.yml
│
└── checkoutservice
↓
checkoutservice-ci-cd.yml
Các workflow đều có logic gần giống nhau:
Checkout
↓
Docker Build
↓
SSH EC2
↓
Docker Compose Build
↓
Docker Compose Up
Điều này tạo ra vấn đề:
4 services
↓
4 workflows
↓
Copy / Paste
↓
Pipeline Drift
Nếu Online Boutique có:
4 services
↓
10 services
↓
30 services
↓
50 services
thì việc duy trì workflow sẽ ngày càng khó.
Trong Chapter 3B, chúng ta sẽ giải quyết vấn đề này bằng GitHub Actions Reusable Workflow.
1. Mục tiêu
Từ:
Service A → Pipeline A
Service B → Pipeline B
Service C → Pipeline C
Service D → Pipeline D
chuyển thành:
Service A ──┐
Service B ──┤
Service C ──┼──→ Reusable Workflow
Service D ──┘
↓
CI/CD logic
↓
EC2
Sau Chapter 3B:
.github/workflows/
├── reusable-ci-cd.yml ← Logic CI/CD dùng chung
│
├── frontend.yml ← Gọi reusable workflow
├── cartservice.yml ← Gọi reusable workflow
├── productcatalogservice.yml ← Gọi reusable workflow
└── checkoutservice.yml ← Gọi reusable workflow
Điểm quan trọng:
Service workflow chỉ khai báo service nào cần deploy và khi nào chạy. Logic CI/CD được đặt tập trung trong
reusable-ci-cd.yml.
2. Kiểm tra trạng thái từ Chapter 3A
Trên máy local:
cd online-boutique-cicd
Kiểm tra:
git status
Kiểm tra workflow:
ls .github/workflows
Bạn hiện sẽ có:
frontend-ci-cd.yml
cartservice-ci-cd.yml
productcatalog-ci-cd.yml
checkoutservice-ci-cd.yml
Đây chính là trạng thái cuối của Chapter 3A.
3. Kiểm tra Online Boutique trên EC2
SSH vào EC2:
ssh -i ./chapter3-key.pem ubuntu@<EC2_PUBLIC_IP>
Đi tới project:
cd ~/online-boutique
Kiểm tra:
docker compose ps
Các container cần thiết phải đang chạy.
Kiểm tra frontend:
docker ps | grep frontend
Sau đó mở:
http://<EC2_PUBLIC_IP>
Nếu Online Boutique vẫn hoạt động, chúng ta có thể bắt đầu refactor CI/CD.
4. Ý tưởng của Reusable Workflow
Hiện tại frontend có:
steps:
- checkout
- docker build
- ssh ec2
- docker compose build frontend
- docker compose up frontend
Cartservice cũng có:
steps:
- checkout
- docker build
- ssh ec2
- docker compose build cartservice
- docker compose up cartservice
Khác nhau chủ yếu ở:
frontend
cartservice
productcatalogservice
checkoutservice
Vì vậy chúng ta biến service thành một input:
service = frontend
hoặc:
service = cartservice
Reusable workflow sẽ nhận input này và sử dụng nó.
5. Xóa 4 workflow cũ
Trên local:
rm .github/workflows/frontend-ci-cd.yml
rm .github/workflows/cartservice-ci-cd.yml
rm .github/workflows/productcatalog-ci-cd.yml
rm .github/workflows/checkoutservice-ci-cd.yml
Kiểm tra:
ls .github/workflows
Nếu thư mục trống thì đúng.
6. Tạo Reusable Workflow
Tạo file:
touch .github/workflows/reusable-ci-cd.yml
Mở file:
nano .github/workflows/reusable-ci-cd.yml
Thêm:
name: Reusable CI/CD
on:
workflow_call:
inputs:
service:
required: true
type: string
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build \
-t online-boutique-${{ inputs.service }} \
src/${{ inputs.service }}
- name: Deploy to EC2
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd ~/online-boutique
git pull origin main
docker compose build ${{ inputs.service }}
docker compose up -d ${{ inputs.service }}
Lưu file.
7. Hiểu cấu trúc Reusable Workflow
Phần quan trọng nhất là:
on:
workflow_call:
Nó có nghĩa:
Workflow này
↓
không tự chạy trực tiếp
↓
workflow khác sẽ gọi nó
GitHub Actions hỗ trợ reusable workflow thông qua workflow_call.
8. Tạo input service
Trong workflow:
inputs:
service:
required: true
type: string
Workflow yêu cầu caller truyền vào:
frontend
hoặc:
cartservice
hoặc:
productcatalogservice
Ví dụ:
service = frontend
thì:
src/${{ inputs.service }}
trở thành:
src/frontend
Và:
docker compose build ${{ inputs.service }}
trở thành:
docker compose build frontend
9. Tạo workflow cho Frontend
Tạo:
touch .github/workflows/frontend.yml
Mở:
nano .github/workflows/frontend.yml
Thêm:
name: Frontend CI/CD
on:
push:
branches:
- main
paths:
- "src/frontend/**"
- ".github/workflows/frontend.yml"
jobs:
deploy:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: frontend
secrets: inherit
Lưu lại.
Workflow này bây giờ rất ngắn.
Nó chỉ nói:
Nếu frontend thay đổi
↓
Gọi reusable workflow
↓
service = frontend
10. Tạo workflow cho Cartservice
Tạo:
touch .github/workflows/cartservice.yml
Mở:
nano .github/workflows/cartservice.yml
Thêm:
name: Cartservice CI/CD
on:
push:
branches:
- main
paths:
- "src/cartservice/**"
- ".github/workflows/cartservice.yml"
jobs:
deploy:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: cartservice
secrets: inherit
11. Tạo workflow cho Product Catalog
Tạo:
touch .github/workflows/productcatalogservice.yml
Mở:
nano .github/workflows/productcatalogservice.yml
Thêm:
name: Product Catalog CI/CD
on:
push:
branches:
- main
paths:
- "src/productcatalogservice/**"
- ".github/workflows/productcatalogservice.yml"
jobs:
deploy:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: productcatalogservice
secrets: inherit
12. Tạo workflow cho Checkout
Tạo:
touch .github/workflows/checkoutservice.yml
Mở:
nano .github/workflows/checkoutservice.yml
Thêm:
name: Checkoutservice CI/CD
on:
push:
branches:
- main
paths:
- "src/checkoutservice/**"
- ".github/workflows/checkoutservice.yml"
jobs:
deploy:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: checkoutservice
secrets: inherit
13. Kiểm tra cấu trúc
Chạy:
find .github/workflows -type f
Kết quả:
.github/workflows/reusable-ci-cd.yml
.github/workflows/frontend.yml
.github/workflows/cartservice.yml
.github/workflows/productcatalogservice.yml
.github/workflows/checkoutservice.yml
Bây giờ architecture đã thay đổi thành:
GitHub
│
┌─────────────┼─────────────┐
↓ ↓ ↓
frontend cart productcatalog
│ │ │
└─────────────┼─────────────┘
↓
reusable-ci-cd.yml
↓
Build + Deploy
↓
EC2
14. Kiểm tra Git diff
Chạy:
git diff
Bạn sẽ thấy một thay đổi rất quan trọng:
Trước
Mỗi workflow chứa:
Checkout
Docker Build
SSH
git pull
Docker Compose Build
Docker Compose Up
Sau
Chỉ reusable-ci-cd.yml chứa logic:
Checkout
Docker Build
SSH
git pull
Docker Compose Build
Docker Compose Up
Các workflow service chỉ còn:
Trigger
↓
service
↓
Reusable Workflow
15. Commit thay đổi
Chạy:
git add .github/workflows
Sau đó:
git commit -m "refactor ci cd with reusable workflow"
Push:
git push
16. Kiểm tra GitHub Actions
Vào:
GitHub
→ Repository
→ Actions
Bạn sẽ thấy các workflow:
Frontend CI/CD
Cartservice CI/CD
Product Catalog CI/CD
Checkoutservice CI/CD
Nhưng phía dưới chúng đều sử dụng:
Reusable CI/CD
17. Test Frontend
Sửa một file frontend:
echo "Chapter 3B" >> src/frontend/README.md
Commit:
git add src/frontend/README.md
git commit -m "test reusable workflow frontend"
git push
Flow:
git push
↓
src/frontend/**
↓
frontend.yml
↓
reusable-ci-cd.yml
↓
service = frontend
↓
Docker Build
↓
SSH EC2
↓
docker compose build frontend
↓
docker compose up -d frontend
18. Kiểm tra EC2
SSH:
ssh -i ./chapter3-key.pem ubuntu@<EC2_PUBLIC_IP>
Đi tới:
cd ~/online-boutique
Kiểm tra:
docker compose ps
Kiểm tra frontend:
docker ps | grep frontend
19. Test Product Catalog
Sửa:
nano src/productcatalogservice/products/products.json
Thay đổi một tên sản phẩm.
Ví dụ:
Vintage Camera
thành:
Vintage Camera - Chapter 3B
Commit:
git add .
git commit -m "test reusable workflow product catalog"
git push
Flow:
git push
↓
productcatalogservice/**
↓
productcatalogservice.yml
↓
reusable-ci-cd.yml
↓
service = productcatalogservice
↓
Build
↓
Deploy
Refresh Online Boutique.
Bạn sẽ thấy product đã thay đổi.
20. Test Cartservice
Thực hiện một thay đổi nhỏ:
nano src/cartservice/README.md
Hoặc:
echo "Chapter 3B" >> src/cartservice/README.md
Commit:
git add .
git commit -m "test reusable workflow cart"
git push
GitHub Actions sẽ gọi:
cartservice.yml
↓
reusable-ci-cd.yml
↓
service = cartservice
21. Test Checkoutservice
Tương tự:
echo "Chapter 3B" >> src/checkoutservice/README.md
Commit:
git add .
git commit -m "test reusable workflow checkout"
git push
Flow:
checkoutservice.yml
↓
reusable-ci-cd.yml
↓
service = checkoutservice
↓
EC2
22. Kiểm tra điểm quan trọng nhất
Bây giờ hãy mở:
.github/workflows/reusable-ci-cd.yml
Bạn sẽ thấy toàn bộ logic CI/CD tập trung ở đây.
Nếu muốn thay đổi:
Docker Build
hoặc:
SSH
hoặc:
docker compose build
hoặc:
docker compose up
chỉ cần sửa:
reusable-ci-cd.yml
Không cần sửa 4 workflow.
23. So sánh Chapter 3A và 3B
Chapter 3A
frontend
↓
frontend-ci-cd.yml
↓
Checkout
Build
Deploy
cartservice
↓
cartservice-ci-cd.yml
↓
Checkout
Build
Deploy
productcatalog
↓
productcatalog-ci-cd.yml
↓
Checkout
Build
Deploy
Logic bị duplicate.
Chapter 3B
frontend ──────────┐
cartservice ───────┤
productcatalog ────┼──→ reusable-ci-cd.yml
checkoutservice ───┘
↓
Checkout
↓
Build
↓
Deploy
Logic được centralize.
24. Thử mô phỏng thêm service
Đây là phần quan trọng để thấy Scale thực sự hoạt động.
Giả sử muốn thêm:
paymentservice
Không cần copy toàn bộ CI/CD workflow.
Chỉ cần tạo:
touch .github/workflows/paymentservice.yml
Nội dung:
name: Paymentservice CI/CD
on:
push:
branches:
- main
paths:
- "src/paymentservice/**"
- ".github/workflows/paymentservice.yml"
jobs:
deploy:
uses: ./.github/workflows/reusable-ci-cd.yml
with:
service: paymentservice
secrets: inherit
Đây chính là điểm khác biệt lớn.
Chapter 3A
Thêm service:
New Service
↓
Copy workflow cũ
↓
Sửa tên
↓
Sửa path
↓
Sửa Docker command
↓
Hy vọng không quên gì
Chapter 3B
Thêm service:
New Service
↓
Tạo caller workflow
↓
service: paymentservice
↓
Done
25. Kiến trúc cuối Chapter 3B
GitHub
│
┌─────────────────┼─────────────────┐
│ │ │
↓ ↓ ↓
frontend cartservice productcatalog
│ │ │
↓ ↓ ↓
frontend.yml cartservice.yml productcatalog.yml
│ │ │
└─────────────────┼─────────────────┘
↓
reusable-ci-cd.yml
│
┌─────────┴─────────┐
↓ ↓
Docker SSH
Build │
↓
EC2
│
Docker Compose
│
Online Boutique
26. Kết quả đạt được
Trước Chapter 3B
4 services
↓
4 pipelines
↓
4 copies of CI/CD logic
Sau Chapter 3B
4 services
↓
4 thin caller workflows
↓
1 reusable workflow
↓
1 source of truth
Và khi tăng lên:
4 services
↓
10 services
↓
30 services
↓
50 services
CI/CD logic vẫn nằm ở:
reusable-ci-cd.yml
Đây chính là bước đầu tiên biến GitHub Actions từ “pipeline cho từng application” thành một CI/CD pattern có thể tái sử dụng.
27. Vấn đề mới xuất hiện
Reusable Workflow đã giải quyết:
❌ Copy-paste CI/CD logic
❌ Pipeline drift
❌ Khó maintain nhiều workflow
Nhưng vẫn còn:
4 services
↓
4 caller workflows
↓
Nhiều service vẫn phải khai báo thủ công
Ví dụ:
frontend
cartservice
productcatalogservice
checkoutservice
paymentservice
shippingservice
...
Chúng ta vẫn phải tạo:
frontend.yml
cartservice.yml
productcatalogservice.yml
checkoutservice.yml
paymentservice.yml
...
Đây chính là vấn đề tiếp theo.
Ở Chapter 3C, chúng ta có thể sử dụng Matrix Strategy:
GitHub
↓
Matrix
↓
┌────────────────┼────────────────┐
↓ ↓ ↓
frontend cart payment
↓ ↓ ↓
└────────────────┼────────────────┘
↓
Reusable Workflow
↓
Build / Deploy
Khi đó:
Một workflow có thể xử lý nhiều microservices thay vì phải tạo một caller workflow cho từng service.
Đây sẽ là bước tiếp theo từ Reusable Workflow → Scalable CI/CD Platform.
All Rights Reserved