Bài viết này hướng dẫn cách xây dựng quy trình liên tục tích hợp và liên tục triển khai (CI/CD) bằng Jenkins để thực hiện chiến lược phát hành theo từng giai đoạn (Canary Release) trên toàn bộ hệ thống.
Trong quá trình phát hành ứng dụng, việc đảm bảo tính ổn định là yếu tố tiên quyết. Một phương pháp hiệu quả để giảm thiểu rủi ro là sử dụng một phần nhỏ lưu lượng truy cập thực tế để kiểm tra phiên bản mới trước khi triển khai rộng rãi. Nếu phiên bản mới gặp sự cố, chúng ta có thể nhanh chóng phát hiện và giới hạn phạm vi ảnh hưởng, từ đó bảo vệ sự ổn định chung của hệ thống.
Kiến trúc tổng thể
Chúng ta sẽ minh họa bằng một ví dụ cụ thể:
Để đảm bảo tính ổn định, quy trình phát hành được thiết lập như sau:
Trong giai đoạn kiểm thử Canary Release, có các chiến lược phân phối lưu lượng truy cập sau:
- Sử dụng trực tiếp một phần nhỏ lưu lượng truy cập thực tế (phân bổ theo tỷ lệ phần trăm).
- Lựa chọn lưu lượng truy cập dựa trên các quy tắc cụ thể (ví dụ: dựa trên header, cookie nhất định).
- Đánh dấu lưu lượng truy cập là Canary Release ở phía client hoặc trình duyệt (thông qua việc truyền header).
Triển khai ứng dụng & Tạo "Lane"
Sau khi triển khai ứng dụng theo tài liệu tham khảo, bước đầu tiên là phân biệt lưu lượng truy cập thực tế và lưu lượng Canary Release.
Tạo một nhóm "Lane" (tạm gọi là nhóm đường đua), bao gồm tất cả các ứng dụng trong toàn bộ quy trình:
Tiếp theo, tạo nhóm "Lane" và phân loại các ứng dụng khớp với quy tắc vào "Lane" có tên là gray:
Lưu ý: Nếu không có lưu lượng truy cập nào khớp với quy tắc, nó sẽ được chuyển hướng đến môi trường gốc (baseline environment), tức là các node ứng dụng không được gắn thẻ (tag).
Sau khi cấu hình hoàn tất, khi truy cập vào Gateway:
- Nếu không khớp với quy tắc Canary Release, lưu lượng sẽ đi qua môi trường gốc.
- Nếu khớp với quy tắc Canary Release, lưu lượng sẽ đi qua môi trường Canary Release.
Cấu hình Jenkins Pipeline
Thực hành này yêu cầu đóng gói mã nguồn và đẩy image. Hãy đảm bảo Jenkins có quyền đẩy lên kho lưu trữ image. Xem thêm chi tiết tại: Sử dụng Kaniko để xây dựng và đẩy image container.
Trong namespace của Jenkins, tạo một Secret có tên jenkins-docker-cfg bằng cách sử dụng tệp config.json đã được tạo:
kubectl create secret generic jenkins-docker-cfg -n jenkins --from-file=/root/.docker/config.json
Tạo Jenkins Pipeline cho Canary Release toàn diện
Xây dựng một Pipeline tự động hóa việc phát hành bằng Jenkins, cung cấp khả năng Canary Release, khả năng giám sát và khả năng khôi phục.
- Trên bảng điều khiển Jenkins, nhấp vào New Item (Tạo mới).
- Nhập tên cho Job (Công việc), chọn Pipeline, sau đó nhấp vào OK.
- Trên thanh menu trên cùng, chọn tab Pipeline. Trong phần Pipeline, cấu hình các tham số liên quan, nhập Script Path, sau đó nhấp vào Save.
- Definition: Chọn Pipeline script from SCM.
- SCM: Chọn Git.
- Repository URL: Nhập URL của kho lưu trữ Git.
- Script Path: Nhập tên tệp
Jenkinsfile.
Bạn có thể tham khảo tệp sau để điền các tham số được chỉ định. Bạn cũng có thể tự viết Jenkinsfile và tải lên đường dẫn được chỉ định trong Git (đường dẫn script được chỉ định trong Pipeline).
pipeline {
// Xác định môi trường build được sử dụng cho bản dựng này. Ví dụ này sử dụng nhãn "slave-pipeline".
agent{
node{
label 'slave-pipeline'
}
}
// Các tham số hằng số, thường không cần thay đổi sau khi xác định ban đầu.
environment{
// Lấy registry, namespace, tên image và tag.
// Ví dụ: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:gray
IMAGE = sh(returnStdout: true,script: 'echo registry.$image_region.aliyuncs.com/$image_namespace/$image_reponame:$image_tag').trim()
// Lấy tên nhánh.
BRANCH = sh(returnStdout: true,script: 'echo $branch').trim()
}
options {
// Số lượng bản dựng tối đa cần giữ lại.
buildDiscarder(logRotator(numToKeepStr: '10'))
}
parameters {
string(name: 'image_region', defaultValue: 'cn-shanghai', description: 'Vùng của Alibaba Cloud Registry')
string(name: 'image_namespace', defaultValue: 'yizhan', description: 'Namespace của image trong registry')
string(name: 'image_reponame', defaultValue: 'spring-cloud-a', description: 'Tên của image')
string(name: 'image_tag', defaultValue: 'gray', description: 'Tag của image cần triển khai (ví dụ: gray)')
string(name: 'branch', defaultValue: 'master', description: 'Nhánh mã nguồn cần build')
string(name: 'number_of_pods', defaultValue: '2', description: 'Số lượng pod cần triển khai')
}
// Các giai đoạn của pipeline
stages {
stage('Code Packaging') {
steps {
container("maven") { // Sử dụng container có tên 'maven'
echo "Building image..."
// Di chuyển vào thư mục mã nguồn ứng dụng 'A' và thực hiện build Maven
sh "cd A && mvn clean package"
}
}
}
stage('Image Build & Push') {
steps {
container("kaniko") { // Sử dụng container có tên 'kaniko' để build image
// Sử dụng Kaniko để build image từ Dockerfile trong thư mục 'A' và đẩy lên registry
sh "kaniko -f `pwd`/A/Dockerfile -c `pwd`/A --destination=${IMAGE} --skip-tls-verify"
}
}
}
stage('Canary Deployment') {
steps {
container('kubectl') { // Sử dụng container có tên 'kubectl'
echo "Performing Canary Deployment..."
// Cập nhật file deployment cho phiên bản Canary Release
// Thay thế tên image cũ bằng image mới với tag 'gray'
sh "cd A && sed -i -E 's/${env.image_reponame}:.+/${env.image_reponame}:${env.image_tag}/' A-gray-deployment.yaml"
// Cập nhật số lượng replicas (pods)
sh "cd A && sed -i -E 's/replicas:.+/replicas: ${env.number_of_pods}/' A-gray-deployment.yaml"
// Áp dụng cấu hình deployment cho Canary Release
sh "kubectl apply -f A/A-gray-deployment.yaml -n default"
}
}
}
stage('End Canary / Full Release') {
input {
message "Please confirm for full release"
ok "Confirm"
parameters {
string(name: 'continue', defaultValue: 'true', description: 'Set to "true" for full release, otherwise rollback')
}
}
steps{
script {
// Lấy giá trị xác nhận từ người dùng
env.continue = sh (script: 'echo ${continue}', returnStdout: true).trim()
if (env.continue.equals('true')) {
container('kubectl') {
echo "Performing Full Release..."
// Cập nhật file deployment cho phiên bản Full Release
sh "cd A && sed -i -E 's/${env.image_reponame}:.+/${env.image_reponame}:${env.image_tag}/' A-deployment.yaml"
sh "cd A && sed -i -E 's/replicas:.+/replicas: ${env.number_of_pods}/' A-deployment.yaml"
// Áp dụng cấu hình deployment cho Full Release
sh "kubectl apply -f A/A-deployment.yaml -n default"
}
} else {
echo 'Rolling back...'
}
// Xóa deployment cho Canary Release sau khi hoàn tất
container('kubectl') {
sh "kubectl delete -f A/A-gray-deployment.yaml -n default"
}
}
}
}
}
}
Build Jenkins Pipeline
- Trên bảng điều khiển Jenkins, nhấp vào biểu tượng bên phải của Pipeline.
- Nhấp vào Build with Parameters (Xây dựng với tham số).
Lưu ý: Bản dựng đầu tiên có thể báo lỗi vì cần tải cấu hình từ kho Git và khởi tạo pipeline. Thực hiện lại Build with Parameters, điền các tham số cần thiết và chạy lại bản dựng.
Kiểm tra trạng thái triển khai. Các giai đoạn Code Packaging, Image Build & Push, Canary Deployment đã hoàn thành. Giai đoạn End Canary / Full Release đang chờ xác nhận.
- Nếu kết quả kiểm tra như mong đợi, hãy thực hiện phát hành toàn bộ (xem phần Full Release bên dưới).
- Nếu kết quả kiểm tra không như mong đợi, hãy thực hiện khôi phục (xem phần Rollback bên dưới).
Xác minh kết quả
- Đăng nhập vào bảng điều khiển Alibaba Cloud Container Service. Trên thanh điều hướng bên trái, nhấp vào Clusters.
- Trên trang Cluster List, nhấp vào tên cluster mục tiêu hoặc nhấp vào Details trong cột Actions của cluster đó.
- Trên trang quản lý cluster, chọn Workloads > Deployments trong thanh điều hướng bên trái.
- Trên trang danh sách ứng dụng Deployments, ứng dụng
spring-cloud-a-grayđã được tạo tự động và image của nó đã được cập nhật lên phiên bảnspring-cloud-a:gray. - Trên trang quản lý cluster, chọn Services trong thanh điều hướng bên trái, chọn namespace đã thiết lập, nhấp vào External Endpoint của service
zuul-slbđể kiểm tra tình hình gọi thực tế.- Gọi không kèm theo Header Canary Release, lưu lượng được định tuyến đến node thông thường của ứng dụng A.
- Lệnh cURL:
curl http://182.92.XX.XX/A/a - Kết quả thực thi:
A[10.4.XX.XX] -> B[10.4.XX.XX] -> C[10.4.XX.XX]%
- Lệnh cURL:
- Truy cập với các tham số khớp quy tắc, lưu lượng được định tuyến đến node Canary Release của ứng dụng A.
- Lệnh cURL:
curl http://182.92.XX.XX/A/a?name=xiaoming - Kết quả thực thi:
Agray[10.4.XX.XX] -> B[10.4.XX.XX] -> C[10.4.XX.XX]%
- Lệnh cURL:
- Gọi không kèm theo Header Canary Release, lưu lượng được định tuyến đến node thông thường của ứng dụng A.
- Đăng nhập vào bảng điều khiển MSE (Microservice Engine) Governance. Trên trang chi tiết ứng dụng, bạn có thể thấy lưu lượng Canary Release đã được chuyển đến các Pod Canary Release.
Full Release
Sau khi xác minh kết quả thành công, tiến hành phát hành toàn bộ.
- Trên bảng điều khiển Jenkins, nhấp vào tên Pipeline mục tiêu.
- Nhấp vào giai đoạn cần phát hành toàn bộ. Trong hộp thoại Please confirm for full release, nhập
truevà nhấp vào Confirm. - Trên bảng điều khiển Container Service, bạn sẽ thấy ứng dụng
spring-cloud-a-grayđã bị xóa và image của ứng dụngspring-cloud-ađã được cập nhật lên phiên bảnspring-cloud-a:gray. - Trên bảng điều khiển MSE Governance, lưu lượng Canary Release đã biến mất.
Rollback
Nếu kết quả xác minh không như mong đợi, hãy thực hiện khôi phục ứng dụng.
- Trên bảng điều khiển Jenkins, nhấp vào tên Pipeline mục tiêu.
- Nhấp vào giai đoạn cần phát hành toàn bộ. Trong hộp thoại Please confirm for full release, nhập
falsevà nhấp vào Confirm. - Trên bảng điều khiển Container Service, bạn sẽ thấy ứng dụng
spring-cloud-a-grayđã bị xóa và image của ứng dụngspring-cloud-avẫn là phiên bản cũ. - Trên bảng điều khiển MSE Governance, lưu lượng Canary Release đã biến mất.
Kết luận
Trong kiến trúc quản trị microservices, tính năng Canary Release toàn diện cung cấp "lanes" ảo, giúp việc kiểm tra trong quá trình thử nghiệm và phát hành trở nên thuận tiện hơn, đồng thời giúp các đội DevOps nâng cao tính ổn định của hệ thống trực tuyến.
Alibaba Cloud Microservice Engine (MSE) cung cấp khả năng quản trị microservices toàn diện trong suốt vòng đời, đảm bảo tính ổn định của hệ thống trực tuyến và nâng cao hiệu quả phát triển, vận hành.
Liên kết liên quan:
Tài liệu tham khảo:
- https://github.com/aliyun/alibabacloud-microservice-demo/blob/master/mse-simple-demo/helm/mse-simple-demo/README.md
- Địa chỉ kho mã nguồn ví dụ: https://gitee.com/mse-group/alibabacloud-microservice-demo/tree/master/mse-simple-demo
- Bảng điều khiển Container Service: https://cs.console.aliyun.com/#/k8s/cluster/list
- Bảng điều khiển MSE Governance: https://mse.console.aliyun.com/#/overview
- Sử dụng Kaniko để xây dựng và đẩy image container: https://help.aliyun.com/document_detail/106712.htm