Giới thiệu
Khi ứng dụng của chúng ta đang chạy trong quá trình sản xuất, việc triển khai một phiên bản mới của ứng dụng luôn không yêu cầu thời gian chết, có một số cách để thực hiện việc này và một trong những cách giúp chúng ta tránh thời gian chết là Triển khai Xanh lam / Xanh lục.Trong bài đăng này, chúng ta sẽ tìm hiểu cách thực hiện Blue / Green Deployment theo cách thủ công, trong bài tiếp theo tôi sẽ hướng dẫn bạn cách thực hiện tự động bằng Argo Rollouts. Bài viết này tôi tham khảo từ CNCF Presentation Template K8s Deployment.
Các bước thực hiện theo
Trong bài viết này, tôi sử dụng Minikube để chạy Kubernetes Cluster. Chúng tôi sẽ tiến hành theo các bước sau:
- Gọi phiên bản đang chạy và nhận được lưu lượng truy cập từ người dùng của chúng tôi là phiên bản 1
- Chúng tôi triển khai phiên bản mới của ứng dụng là phiên bản 2
- Chờ phiên bản 2 chạy
- Chuyển lưu lượng truy cập từ phiên bản 1 sang phiên bản 2
- Tắt phiên bản 1
Trong thực tế
Hãy thực hành, tạo một tệp có tên app-v1.yaml
để triển khai phiên bản ứng dụng 1 của chúng tôi, định cấu hình nó như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <span class="token key atrule">apiVersion</span> <span class="token punctuation">:</span> apps/v1 <span class="token key atrule">kind</span> <span class="token punctuation">:</span> Deployment <span class="token key atrule">metadata</span> <span class="token punctuation">:</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token punctuation">-</span> v1 <span class="token key atrule">labels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">spec</span> <span class="token punctuation">:</span> <span class="token key atrule">replicas</span> <span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">selector</span> <span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">version</span> <span class="token punctuation">:</span> v1.0.0 <span class="token key atrule">template</span> <span class="token punctuation">:</span> <span class="token key atrule">metadata</span> <span class="token punctuation">:</span> <span class="token key atrule">labels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">version</span> <span class="token punctuation">:</span> v1.0.0 <span class="token key atrule">annotations</span> <span class="token punctuation">:</span> <span class="token key atrule">prometheus.io/scrape</span> <span class="token punctuation">:</span> <span class="token string">"true"</span> <span class="token key atrule">prometheus.io/port</span> <span class="token punctuation">:</span> <span class="token string">"9101"</span> <span class="token key atrule">spec</span> <span class="token punctuation">:</span> <span class="token key atrule">containers</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">image</span> <span class="token punctuation">:</span> containersol/k8s <span class="token punctuation">-</span> deployment <span class="token punctuation">-</span> strategies <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> http <span class="token key atrule">containerPort</span> <span class="token punctuation">:</span> <span class="token number">8080</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> probe <span class="token key atrule">containerPort</span> <span class="token punctuation">:</span> <span class="token number">8086</span> <span class="token key atrule">env</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> VERSION <span class="token key atrule">value</span> <span class="token punctuation">:</span> v1.0.0 <span class="token key atrule">livenessProbe</span> <span class="token punctuation">:</span> <span class="token key atrule">httpGet</span> <span class="token punctuation">:</span> <span class="token key atrule">path</span> <span class="token punctuation">:</span> /live <span class="token key atrule">port</span> <span class="token punctuation">:</span> probe <span class="token key atrule">initialDelaySeconds</span> <span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">periodSeconds</span> <span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">readinessProbe</span> <span class="token punctuation">:</span> <span class="token key atrule">httpGet</span> <span class="token punctuation">:</span> <span class="token key atrule">path</span> <span class="token punctuation">:</span> /ready <span class="token key atrule">port</span> <span class="token punctuation">:</span> probe <span class="token key atrule">periodSeconds</span> <span class="token punctuation">:</span> <span class="token number">5</span> |
Trong tệp ở trên, chúng tôi khai báo Triển khai với hai nhãn “app: my-app” và “version: v1.0.0”. Tôi sẽ giải thích lý do tại sao chúng ta cần 2 nhãn.
Tiếp theo, chúng tôi tạo một Dịch vụ cho app-v1 với tên tệp là service.yaml
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token key atrule">apiVersion</span> <span class="token punctuation">:</span> v1 <span class="token key atrule">kind</span> <span class="token punctuation">:</span> Service <span class="token key atrule">metadata</span> <span class="token punctuation">:</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">labels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">spec</span> <span class="token punctuation">:</span> <span class="token key atrule">type</span> <span class="token punctuation">:</span> NodePort <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> http <span class="token key atrule">port</span> <span class="token punctuation">:</span> <span class="token number">80</span> <span class="token key atrule">targetPort</span> <span class="token punctuation">:</span> http <span class="token comment"># Note here that we match both the app and the version</span> <span class="token key atrule">selector</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">version</span> <span class="token punctuation">:</span> v1.0.0 |
Điểm chính ở đây là bộ chọn, vì chúng ta có thể thấy nó khớp với cả nhãn ứng dụng và phiên bản, và nhãn phiên bản là thứ chúng ta thực sự cần quan tâm. Nó là chìa khóa để chúng tôi chuyển đổi lưu lượng giữa hai phiên bản của ứng dụng.
Tạo Triển khai và Dịch vụ.
1 | kubectl apply -f app-v1.yaml <span class="token operator">&&</span> kubectl apply -f service.yaml</code>Kiểm tra tất cả các nhóm đang chạy, nếu bạn sử dụng Minikube thì chạy như sau: |
1 2 3 | <span class="token function">curl</span> <span class="token variable"><span class="token variable">$(</span> minikube <span class="token function">service</span> my-app --url <span class="token variable">)</span></span> <span class="token number">2022</span> -10-03T20:16:04+07:00 - Host: host-1, Version: v1.0.0 |
Nếu không, bạn có thể sử dụng chuyển tiếp cổng.
1 2 | kubectl port-forward <span class="token operator"><</span> name of pod <span class="token operator">></span> <span class="token number">8080</span> :8080 |
Sau khi kiểm tra rằng ứng dụng phiên bản 1 của chúng tôi đang chạy, vui lòng tắt chuyển tiếp vì chúng tôi sẽ tiếp tục sử dụng nó sau này.
Tiếp theo, chúng tôi sẽ triển khai phiên bản mới của ứng dụng (phiên bản 2). Tạo một tệp có tên app-v2.yaml
với cấu hình như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <span class="token key atrule">apiVersion</span> <span class="token punctuation">:</span> apps/v1 <span class="token key atrule">kind</span> <span class="token punctuation">:</span> Deployment <span class="token key atrule">metadata</span> <span class="token punctuation">:</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token punctuation">-</span> v2 <span class="token key atrule">labels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">spec</span> <span class="token punctuation">:</span> <span class="token key atrule">replicas</span> <span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">selector</span> <span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">version</span> <span class="token punctuation">:</span> v2.0.0 <span class="token key atrule">template</span> <span class="token punctuation">:</span> <span class="token key atrule">metadata</span> <span class="token punctuation">:</span> <span class="token key atrule">labels</span> <span class="token punctuation">:</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">version</span> <span class="token punctuation">:</span> v2.0.0 <span class="token key atrule">annotations</span> <span class="token punctuation">:</span> <span class="token key atrule">prometheus.io/scrape</span> <span class="token punctuation">:</span> <span class="token string">"true"</span> <span class="token key atrule">prometheus.io/port</span> <span class="token punctuation">:</span> <span class="token string">"9101"</span> <span class="token key atrule">spec</span> <span class="token punctuation">:</span> <span class="token key atrule">containers</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> my <span class="token punctuation">-</span> app <span class="token key atrule">image</span> <span class="token punctuation">:</span> containersol/k8s <span class="token punctuation">-</span> deployment <span class="token punctuation">-</span> strategies <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> http <span class="token key atrule">containerPort</span> <span class="token punctuation">:</span> <span class="token number">8080</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> probe <span class="token key atrule">containerPort</span> <span class="token punctuation">:</span> <span class="token number">8086</span> <span class="token key atrule">env</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span> <span class="token punctuation">:</span> VERSION <span class="token key atrule">value</span> <span class="token punctuation">:</span> v2.0.0 <span class="token key atrule">livenessProbe</span> <span class="token punctuation">:</span> <span class="token key atrule">httpGet</span> <span class="token punctuation">:</span> <span class="token key atrule">path</span> <span class="token punctuation">:</span> /live <span class="token key atrule">port</span> <span class="token punctuation">:</span> probe <span class="token key atrule">initialDelaySeconds</span> <span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">periodSeconds</span> <span class="token punctuation">:</span> <span class="token number">5</span> <span class="token key atrule">readinessProbe</span> <span class="token punctuation">:</span> <span class="token key atrule">httpGet</span> <span class="token punctuation">:</span> <span class="token key atrule">path</span> <span class="token punctuation">:</span> /ready <span class="token key atrule">port</span> <span class="token punctuation">:</span> probe <span class="token key atrule">periodSeconds</span> <span class="token punctuation">:</span> <span class="token number">5</span> |
Bạn để ý trong phần nhãn, chúng ta sẽ khai báo phiên bản nhãn là v2.0.0
. Tiếp theo, chúng tôi triển khai phiên bản ứng dụng 2.
1 2 | kubectl apply -f app-v2.yaml |
Chờ cho tất cả các Pod ở trạng thái chạy trước khi chúng tôi tiếp tục.
1 | kubectl rollout status deploy my-app-v2 -w</code>Kiểm tra để đảm bảo rằng phiên bản ứng dụng 2 có thể nhận được lưu lượng truy cập. |
1 2 | kubectl port-forward my-app-v2 <span class="token number">8080</span> :8080 |
Mở một thiết bị đầu cuối khác và truy cập phiên bản ứng dụng 2 và đảm bảo rằng nó có thể nhận được lưu lượng truy cập từ người dùng.
1 2 | <span class="token function">curl</span> localhost:8080 |
Nếu ứng dụng phiên bản 2 đã chạy thành công và có thể nhận được lưu lượng, thì phần quan trọng nhất tiếp theo là cách chuyển lưu lượng từ phiên bản 1 sang phiên bản 2. Để làm được điều đó, chúng ta chỉ cần cập nhật nhãn của Dịch vụ ở trên lên phiên bản 2 .Chúng ta có thể chỉnh sửa tệp YAML hoặc thực hiện nhanh chóng bằng lệnh vá.
1 2 | kubectl patch <span class="token function">service</span> my-app -p <span class="token string">'{"spec":{"selector":{"version":"v2.0.0"}}}'</span> |
Lúc này, lưu lượng của ứng dụng được chuyển từ phiên bản 1 sang phiên bản 2.Hãy thử nghiệm.
1 2 3 | <span class="token function">curl</span> <span class="token variable"><span class="token variable">$(</span> minikube <span class="token function">service</span> my-app --url <span class="token variable">)</span></span> <span class="token number">2022</span> -10-03T20:30:54+07:00 - Host: host-1, Version: v2.0.0 |
Ok, nếu bạn thấy kết quả là Version: v2.0.0
thì chúng ta đã triển khai Blue / Green Deployment thành công. Nếu có điều gì đó xảy ra và bạn muốn quay lại phiên bản 1, chúng tôi chỉ cần cập nhật lại phiên bản nhãn.
1 2 | kubectl patch <span class="token function">service</span> my-app -p <span class="token string">'{"spec":{"selector":{"version":"v1.0.0"}}}'</span> |
Cuối cùng, chúng ta tắt ứng dụng phiên bản 1.
1 | kubectl delete deploy my-app-v1 |
Xong. Hãy like trang DevOps VN để nhận thông báo về những bài viết sớm nhất.
Sự kết luận
Vì vậy, chúng ta đã học cách triển khai Blue / Green Deployment, như bạn thấy, nó không phức tạp lắm. Nhưng đây chỉ là cách tập chơi để biết thôi, nên ở bài sau, chúng ta sẽ học cách triển khai Blue / Green Deployment cho một dự án thực tế với Argo Rollouts.