Hướng dẫn Setup Jenkins cho hệ thống Monolith - Bài 21: Chiến lược triển khai ứng dụng Monolith: Từ thủ công đến tự động hóa hoàn toàn
Chào mừng bạn bước sang Giai đoạn 3: Triển khai (Deployment) và Quản lý Môi trường (Bài 21 - 30). Ở 20 bài học trước, chúng ta đã hoàn thành nửa đầu của chữ CI/CD — đó là Continuous Integration (CI): kiểm thử, quét lỗ hổng, kiểm tra chất lượng và tạo ra một gói cài đặt (Artifact) hoàn hảo.
Bây giờ là lúc giải quyết nửa sau đầy thử thách — Continuous Delivery / Deployment (CD): Làm sao để đưa gói Artifact nặng hàng trăm MB đó từ Jenkins lên các máy chủ Staging và Production một cách mượt mà, không làm rớt kết nối của người dùng đang truy cập, và có thể quay đầu (Rollback) trong tích tắc nếu xảy ra sự cố? Bài học mở đầu Giai đoạn 3 sẽ giúp bạn định hình toàn bộ tư duy và kiến trúc triển khai cho hệ thống Monolith.
1. Tại sao cách deploy kiểu "Git Pull trực tiếp trên Server" là một thảm họa?
Rất nhiều đội ngũ vận hành hệ thống Monolith (đặc biệt với PHP Laravel, Node.js hoặc Python) vẫn giữ thói quen viết script cho Jenkins SSH vào thẳng thư mục /var/www/html trên Production Server rồi chạy cụm lệnh: git pull origin main && composer install && npm run build.
Cách làm này gọi là In-place Mutable Deployment (Triển khai ghi đè tại chỗ) và nó mang 4 rủi ro chí mạng:
-
Hiệu ứng "Nửa nạc nửa mỡ" (Broken State during Deploy): Trong khoảng 30 giây lệnh
composer installhoặcnpm run buildđang chạy, mã nguồn mới đã về nhưng thư viện mới chưa tải xong. Khách hàng truy cập vào đúng giây thứ 15 sẽ nhận ngay màn hình lỗi500 Internal Server Error(Class Not Found). -
Đứt cáp mạng là sập Production: Nếu đúng lúc đang chạy
composer installtrên Production mà Github hoặc Packagist bị lỗi mạng, thư mụcvendor/trên server sẽ bị hỏng dở dang và toàn bộ hệ thống ngừng hoạt động. -
Ngốn sạch CPU/RAM của khách hàng: Lệnh
npm run buildhoặcgo buildăn 100% CPU. Chạy lệnh này trên Production đồng nghĩa với việc bạn lấy tài nguyên phục vụ khách hàng để đi biên dịch code. -
Không thể Rollback sạch sẽ: Khi bản mới bị lỗi, việc gõ
git reset --hardtrên Production không khôi phục lại được các gói thư viện hay file cấu hình đã bị biến đổi.
Chân lý của CD hiện đại: Máy chủ Production tuyệt đối không được cài Git, không cài Composer/NPM/Maven để build code. Jenkins đã làm hết việc nặng đó ở Giai đoạn 2 và đúc ra gói .tar.gz / .jar / Docker Image. Máy chủ Production chỉ nhận đúng gói thành phẩm đó và chạy!
2. Phân biệt rõ: Continuous Delivery vs. Continuous Deployment
Khi thiết kế luồng triển khai trên Jenkins cho hệ thống Monolith, bạn cần thống nhất với đội ngũ xem mình sẽ áp dụng mức độ tự động nào cho từng môi trường:
[Code Push] ──► [CI: Build & Test] ──► [Deploy Staging] ──► (Chốt chặn?) ──► [Deploy Produc
| Mức độ & Mô hình | Cơ chế hoạt động trên Jenkins | Áp dụng cho môi trường nào? |
|---|---|---|
| Continuous Delivery (Phân phối liên tục) | Tự động build, test và đẩy thẳng lên môi trường Staging. Tuy nhiên, trước khi lên Production, Pipeline sẽ dừng lại và chờ nút bấm phê duyệt thủ công (Manual Approval) từ Tech Lead hoặc PM. |
Phù hợp với 95% hệ thống Monolith doanh nghiệp (E-commerce, ERP, Tài chính) cần kiểm soát chặt chẽ thời điểm bảo trì hoặc nghiệm thu nghiệp vụ. |
| Continuous Deployment (Triển khai liên tục) | Tự động hóa 100% từ lúc lập trình viên git push cho đến khi mã nguồn chạy trực tiếp trên Production mà không cần bất kỳ sự can thiệp thủ công nào của con người. |
Áp dụng cho môi trường Dev / Staging, hoặc môi trường Production của các hệ thống đã có hệ thống Automated E2E Test cực kỳ hoàn chỉnh. |
3. Bốn chiến lược triển khai (Deployment Strategies) kinh điển cho Monolith
Tùy thuộc vào hạ tầng máy chủ (dùng VPS/Bare-metal truyền thống hay dùng Docker/Load Balancer) và yêu cầu về thời gian ngừng hệ thống (Downtime), dưới đây là 4 chiến lược bạn sẽ lần lượt thực hành trong Giai đoạn 3:
Chiến lược 1: Recreate Deployment (Dừng cũ - Bật mới)
-
Cách hoạt động: Tắt service Monolith hiện tại (
systemctl stop monolith), giải nén đè bản mới vào, sau đó bật lại service (systemctl start monolith). -
Ưu điểm: Dễ cài đặt nhất, không tốn thêm RAM/CPU.
-
Nhược điểm: Gây Downtime (từ vài giây đến vài phút). Chỉ nên dùng cho môi trường Dev / Staging hoặc hệ thống nội bộ ít người dùng ban đêm (sẽ thực hành ở Bài 22 & 23).
Chiến lược 2: Atomic Symlink Deployment (Triển khai tráo đổi liên kết nguyên tử - Chuẩn vàng cho VPS)
Nếu bạn chạy Monolith trực tiếp trên máy chủ Linux (không dùng Docker) mà vẫn muốn đạt Zero-Downtime, đây là kiến trúc bắt buộc phải biết (tương tự cơ chế của Deployer hay Capistrano):
-
Cấu trúc thư mục trên Server:
Plaintext
/var/www/monolith-app/ ├── current -> /var/www/monolith-app/releases/v1.2.0-b45 (Symlink trỏ tới bản đang chạy) ├── releases/ │ ├── v1.1.0-b42/ │ ├── v1.1.5-b44/ │ └── v1.2.0-b45/ <-- Giải nén gói Artifact mới vào thư mục riêng biệt này └── shared/ <-- Chứa những thứ KHÔNG ĐƯỢC XÓA giữa các lần deploy ├── .env (File cấu hình Production cố định) └── storage/ (Hình ảnh user upload, logs) -
Cách hoạt động: Khi gói
v1.2.0-b45.tar.gzđược đẩy lên, nó giải nén vào thư mụcreleases/v1.2.0-b45(lúc này khách hàng vẫn đang dùng bảnb44bình thường). Khi mọi thứ đã sẵn sàng, Jenkins chạy lệnhln -sfn releases/v1.2.0-b45 currentđể tráo đổi mũi têncurrentsang bản mới chỉ trong 0.001 giây (Atomic operation) và reload nhẹ nhàng PHP-FPM / Nginx / Systemd! -
Khả năng Rollback: Nếu bản
b45bị lỗi, chỉ mất 1 giây để trỏ ngược mũi têncurrentvề lạireleases/v1.1.5-b44.
Chiến lược 3: Rolling Deployment (Cập nhật cuốn chiếu qua Load Balancer)
-
Cách hoạt động: Khi hệ thống Monolith của bạn lớn mạnh và chạy trên 2 hoặc 3 máy chủ đứng sau một Load Balancer (Nginx / HAProxy). Jenkins sẽ ngắt kết nối của Server 1 khỏi Load Balancer, cập nhật code cho Server 1, kiểm tra
/healththấy ổn rồi mới đưa Server 1 vào lại và làm tiếp với Server 2. -
Ưu điểm: Không có Downtime, tận dụng tốt hạ tầng sẵn có.
Chiến lược 4: Blue/Green Deployment (Triển khai Xanh / Lá - Chuẩn hiện đại với Docker)
- Cách hoạt động: Luôn duy trì 2 môi trường giống hệt nhau: Blue (Đang chạy bản cũ nhận traffic ở cổng
8081) và Green (Chạy bản mới vừa khởi tạo ở cổng8082). Sau khi Jenkins bật container Green lên và chạy thử nghiệm thành công, Nginx Reverse Proxy sẽ chuyển hướng 100% luồng truy cập từ cổng8081sang8082(Sẽ đi sâu thực hành chi tiết ở Bài 25, 26 và 30).
4. Quy trình 7 bước chuẩn của một Stage Deploy trong Jenkinsfile
Dù bạn chọn chiến lược nào ở trên, một quy trình triển khai hệ thống Monolith chuyên nghiệp trong Jenkins luôn phải tuân thủ nghiêm ngặt 7 bước tuần tự sau:
-
Transfer Artifact: Truyền gói
monolith-v1.2.0.tar.gzvà file.sha256từ Jenkins sang thư mục tạm trên Server đích qua giao thức SSH/SCP bảo mật (hoặcdocker pulltừ Registry). -
Verify Checksum: Kiểm tra chữ ký
sha256sum -ctrên Server đích để đảm bảo file không bị lỗi trong lúc truyền qua mạng. -
Prepare Release Directory & Link Shared Resources: Giải nén vào thư mục release mới, nối symlink tới file
.envvà thư mụcstorage/dùng chung. -
Run Database Migration (Backward-compatible): Chạy cập nhật cấu trúc bảng dữ liệu (
php artisan migrate --force,flyway migratehoặc binary migration). -
Switch Traffic (Cutover): Đổi symlink
currentvà reload dịch vụ (hoặc chuyển luồng container). -
Post-Deploy Health Check (Smoke Test): Jenkins tự động gọi
curl -f http://localhost/api/healthđể kiểm tra xem ứng dụng mới có trả về HTTP200 OKvà đúng mãBUILD_VERSIONvừa deploy hay không. -
Auto-Rollback hoặc Cleanup:
-
Nếu bước 6 thất bại (
!= 200 OK): Tự động kích hoạt kịch bản Rollback về bản trước đó ngay lập tức! -
Nếu bước 6 thành công: Xóa bớt các thư mục release cũ (chỉ giữ lại 5 bản gần nhất trên server để tiết kiệm ổ cứng).
-
5. Thiết kế khung sườn Pipeline CD (Continuous Delivery) trong Jenkinsfile
Dưới đây là cách chúng ta mở rộng Jenkinsfile từ Bài 20 để chuẩn bị đón nhận các Stage triển khai thực tế vào các môi trường Staging và Production:
Groovy
// Minh họa cấu trúc các Stage triển khai (Giai đoạn 3) nối tiếp sau bước Publish Artifact (Bài 20)
stage('7. Deploy to Staging') {
// Tự động chạy khi code được merge vào nhánh develop hoặc main
when {
anyOf {
branch 'develop'
branch 'main'
}
}
steps {
echo "=== Bắt đầu triển khai tự động phiên bản ${env.BUILD_VERSION} lên STAGING SERVER ==="
// Nội dung thực thi chi tiết bằng SSH Publisher / SSH Agent sẽ được viết ở Bài 22 & 23
}
}
stage('8. Manual Approval for Production') {
// Chỉ kích hoạt đối với nhánh main (Production)
when {
branch 'main'
}
steps {
script {
// Tạm dừng chờ Tech Lead bấm nút phê duyệt (Chi tiết ở Bài 29)
input message: "Xác nhận đẩy bản ${env.BUILD_VERSION} lên PRODUCTION?", ok: "Deploy Ngay!"
}
}
}
stage('9. Zero-Downtime Deploy to Production') {
when {
branch 'main'
}
steps {
echo "=== Thực thi chiến lược Atomic Symlink / Blue-Green Deployment lên PRODUCTION ==="
// Thực thi đẩy code, switch symlink và kiểm tra Health Check (Bài 23 - 30)
}
}
All rights reserved