Tác giả: Clare Liguori

Khi tôi tham gia phỏng vấn xin việc tại Amazon, tôi đã hỏi một trong những người phỏng vấn rằng “Bạn triển khai vào môi trường sản xuất bao lâu một lần?” Tại thời điểm đó, tôi đang phát triển một sản phẩm triển khai một bản phát hành chính một hoặc hai lần mỗi năm. Tuy nhiên, đôi khi tôi cần phát hành một bản sửa lỗi nhỏ giữa các bản phát hành lớn. Đối với mỗi bản sửa lỗi mình phát hành, tôi dành nhiều thời gian để triển khai bản sửa lỗi đó một cách cẩn thận. Sau đó, tôi liên tục kiểm tra kỹ nhật ký và chỉ số để xem liệu tôi có làm hỏng bất kỳ thứ gì sau khi triển khai không và có cần triển khai lại không.

Tôi biết được rằng Amazon đã thực hành phương pháp triển khai liên tục. Vì vậy, khi tham gia phỏng vấn, tôi muốn biết lượng thời gian mình sẽ phải dành cho việc quản lý và theo dõi quá trình triển khai với vai trò nhà phát triển tại Amazon. Người phỏng vấn cho biết rằng các thay đổi tự động triển khai vào môi trường sản xuất nhiều lần mỗi ngày thông qua quy trình triển khai liên tục. Khi tôi hỏi về lượng thời gian trong ngày mà họ dành ra để giám sát kỹ từng quá trình triển khai đó cũng như theo dõi nhật ký và chỉ số đối với mọi tác động như tôi đã làm, họ cho biết rằng thường không mất thời gian cho việc đó. Vì quy trình đã giúp nhóm họ thực hiện công việc này, nên hầu hết các quá trình triển khai không cần ai tích cực theo dõi. Điều này đã khiến tôi rất ngạc nhiên. Sau khi gia nhập Amazon, tôi đã rất hào hứng tìm ra chính xác cách hoạt động của các quá trình triển khai tự động “không cần thao tác thủ công” này.

Triển khai liên tục và an toàn tại Amazon

Kể từ đó, tôi đã được thấy tận mắt cách Amazon thiết lập quy trình triển khai liên tục để giúp chúng tôi triển khai nhanh chóng và an toàn. Tôi đánh giá cao cách phương pháp an toàn về triển khai liên tục của chúng tôi giúp giảm thời gian làm việc cho nhà phát triển trong quá trình triển khai. Khi tôi đẩy mã sản xuất vào nhánh chính của kho lưu trữ mã nguồn của dịch vụ, tôi thường chuyển sang nhiệm vụ tiếp theo và để quy trình lo phần còn lại. Trong khi đó, quy trình của nhóm tôi sẽ tiếp quản nhiệm vụ triển khai thay đổi đó vào môi trường sản xuất. Quy trình sẽ tự động hóa hoàn toàn việc phát hành thay đổi mã của tôi vào dịch vụ sản xuất. Điều này nghĩa là lần cuối cùng mà tôi hoặc bất kỳ nhà phát triển nào khác phải động đến hoặc xem xét một phần mã là khi mã được hợp nhất vào khu lưu trữ mã nguồn.
 
Nhóm tôi thiết lập quy trình đó với các bước tự động giúp triển khai các thay đổi vào môi trường sản xuất một cách an toàn để chúng tôi không phải theo dõi từng quá trình triển khai. Quy trình này chạy các thay đổi mới nhất thông qua bộ kiểm thử và các kiểm tra an toàn khi triển khai. Các bước tự động này ngăn không cho sai sót ảnh hưởng đến khách hàng đến được môi trường sản xuất và hạn chế tác động của sai sót đến khách hàng nếu các sai sót xuất hiện ở môi trường sản xuất. Là nhà phát triển, tôi có thể tin tưởng rằng quy trình này sẽ giúp tôi triển khai thay đổi của mình vào môi trường sản xuất một cách thận trọng và an toàn. Nhờ vậy, tôi không cần phải tích cực theo dõi quá trình triển khai.

Hành trình đến phân phối liên tục

Khi Amazon chưa bắt đầu thực hành phương pháp phân phối liên tục, các nhà phát triển tại đây đã từng phải mất rất nhiều thời gian cho việc quản lý quá trình triển khai mã của họ vào môi trường sản xuất. Chúng tôi đã áp dụng phương pháp phân phối liên tục trên toàn công ty như một cách tự động hóa và tiêu chuẩn hóa cách chúng tôi triển khai phần mềm cũng như giảm thời gian triển khai các thay đổi vào môi trường sản xuất. Các cải thiện đối với quy trình phát hành của chúng tôi tăng dần theo thời gian. Chúng tôi đã phát hiện các rủi ro về triển khai và tìm ra cách giảm thiểu các rủi ro đó thông qua phương pháp tự động hóa an toàn mới trong quy trình. Chúng tôi tiếp tục thực hiện lại quy trình phát hành bằng cách phát hiện các rủi ro mới và cách mới để cải thiện độ an toàn của quá trình triển khai. Để tìm hiểu thêm về hành trình của chúng tôi đến phân phối liên tục và cách chúng tôi tiếp tục cải thiện, hãy xem bài viết Phát triển nhanh hơn nhờ phân phối liên tục trên Builders’ Library.

Bốn giai đoạn quy trình

Trong bài viết này, chúng tôi sẽ trình bày các bước mà thay đổi mã trải qua trong quy trình tại Amazon trong quá trình triển khai vào môi trường sản xuất. Quy trình phân phối liên tục điển hình có 4 giai đoạn chính là nguồn, dựng, kiểm thử và sản xuất. Chúng tôi sẽ giải thích kỹ thông tin chi tiết về những hoạt động diễn ra ở từng giai đoạn quy trình này đối với một dịch vụ AWS điển hình. Chúng tôi cũng sẽ cung cấp cho bạn ví dụ về cách nhóm dịch vụ AWS có thể thiết lập một trong các quy trình của họ.

Nguồn và dựng

Sơ đồ dưới đây cung cấp cho bạn tổng quan về các bước nguồn và dựng mà bạn có thể thấy trong quy trình điển hình của nhóm dịch vụ AWS.

Nguồn quy trình

Các quy trình tại Amazon tự động xác thực và triển khai an toàn mọi loại thay đổi nguồn vào môi trường sản xuất, chứ không chỉ các thay đổi đối với mã ứng dụng. Các quy trình này có thể xác thực và triển khai các thay đổi đối với nguồn như tài sản tĩnh của trang web, công cụ, bản kiểm thử, cơ sở hạ tầng, cấu hình và hệ điều hành (OS) cơ bản của ứng dụng. Tất cả các thay đổi này là bản được kiểm soát trong các kho lưu trữ mã nguồn riêng lẻ. Các phần phụ thuộc mã nguồn, như thư viện, ngôn ngữ lập trình và tham số, như ID AMI, tự động được nâng cấp lên phiên bản mới nhất ít nhất là mỗi tuần một lần.

Các nguồn này được triển khai trong quy trình riêng lẻ bằng cùng cơ chế an toàn (như quay lui tự động) mà chúng tôi dùng để triển khai mã ứng dụng. Ví dụ: giá trị cấu hình cho dịch vụ có thể thay đổi vào thời gian chạy (như việc tăng giới hạn tốc độ API và cờ tính năng) được tự động triển khai trong quy trình cấu hình dành riêng. Các thay đổi nguồn tự động quay lui nếu các thay đổi này gây ra bất kỳ sự cố nào trong môi trường sản xuất cho dịch vụ đó (như lỗi phân tích cú pháp tệp cấu hình).

Vi dịch vụ điển hình có thể có quy trình mã ứng dụng, quy trình cơ sở hạ tầng, quy trình vá lỗi hệ điều hành, quy trình cờ tính năng/cấu hình và quy trình công cụ toán tử. Việc có nhiều quy trình cho cùng một vi dịch vụ giúp chúng tôi triển khai các thay đổi vào môi trường sản xuất nhanh hơn. Các thay đổi mã ứng dụng (làm hỏng kiểm thử tích hợp và chặn quy trình ứng dụng) không ảnh hưởng đến các quy trình khác. Ví dụ: các thay đổi này không chặn các thay đổi mã cơ cở hạ tầng đến môi trường sản xuất trong quy trình cơ sở hạ tầng. Tất cả quy trình cho cùng vi dịch vụ thường trông rất giống nhau. Ví dụ: quy trình cờ tính năng và quy trình mã ứng dụng dùng cùng một công nghệ triển khai an toàn vì mức tác động của sự thay đổi cấu hình cờ tính năng không thích hợp đến môi trường sản xuất giống với sự thay đổi mã ứng dụng không thích hợp.

Xem xét mã

Tất cả các thay đổi đến môi trường sản xuất đều bắt đầu với bước xem xét mã và phải được thành viên nhóm phê duyệt trước khi hợp nhất vào nhánh dòng chính (phiên bản “chính” của chúng tôi). Việc này sẽ tự động bắt đầu quy trình. Quy trình thực thi yêu cầu tất cả cam kết trên nhánh dòng chính phải là mã đã được thành viên nhóm dịch vụ xem xét và phê duyệt cho quy trình đó. Quy trình sẽ chặn không cho mọi cam kết chưa được xem xét triển khai.

Nhờ quy trình hoàn toàn tự động, xem xét mã là bước xem xét và phê duyệt thủ công cuối cùng mà kỹ sư thực hiện đối với thay đổi mã trước khi thay đổi được triển khai vào môi trường sản xuất. Vì vậy, đây là bước rất quan trọng. Người xem xét mã đánh giá độ chính xác của mã và cũng đánh giá xem thay đổi này có thể triển khai vào môi trường sản xuất một cách an toàn không. Họ đánh giá xem mã có kiểm thử đủ (kiểm thử đơn vị, kiểm thử tích hợp và kiểm thử canary) không, có được cung cấp công cụ đầy đủ cho giám sát triển khai và có thể được quay lui an toàn hay không. Một số nhóm sử dụng danh sách kiểm tra tùy chỉnh giống như danh sách trong mẫu bên dưới. Danh sách được tự động thêm vào mỗi hoạt động xem xét mã của nhóm để kiểm tra rõ ràng xem có mối lo ngại về an toàn khi triển khai hay không.

Ví dụ về danh sách kiểm tra cho xem xét mẫu

## Testing
[ ] Did you write new unit tests for this change?
[ ] Did you write new integration tests for this change?

Include the test commands you ran locally to test this change:
```
mvn test && mvn verify
```

## Monitoring
[ ] Will this change be covered by our existing monitoring?
 (no new canaries/metrics/dashboards/alarms are required)
[ ] Will this change have no (or positive) effect on resources and/or limits?
 (including CPU, memory, AWS resources, calls to other services)
[ ] Can this change be deployed to Prod without triggering any alarms?

## Rollout
[ ] Can this change be merged immediately into the pipeline upon approval?
[ ] Are all dependent changes already deployed to Prod?
[ ] Can this change be rolled back without any issues after deployment to Prod?

Dựng và kiểm thử đơn vị

Trong bước dựng, mã sẽ được dịch và kiểm thử đơn vị. Các công cụ dựng và logic dựng có thể khác nhau tùy theo ngôn ngữ và thậm chí là tùy theo nhóm. Ví dụ: các nhóm có thể chọn khung kiểm thử đơn vị, linter và công cụ phân tích tĩnh thích hợp nhất cho họ. Ngoài ra, nhóm có thể chọn cấu hình của các công cụ đó, như phạm vi mã tối thiểu có thể chấp nhận trong khung kiểm thử đơn vị của họ. Các công cụ và loại kiểm thử sẽ chạy cũng sẽ khác nhau tùy theo loại mã được quy trình triển khai. Ví dụ: kiểm thử đơn vị dùng cho mã ứng dụng và linter dùng cho các mẫu cơ sở hạ tầng ở dạng mã. Tất cả bản dựng chạy mà không cần truy cập mạng để tách biệt các bản dựng và thúc đẩy khả năng tái sản xuất của bản dựng. Thông thường, kiểm thử đơn vị mô phỏng (bắt chước) tất cả lệnh gọi API của chúng đối với phần phụ thuộc, như các dịch vụ AWS khác. Hoạt động tương tác với phần phụ thuộc (không được mô phỏng “trực tiếp”) được kiểm thử sau trong quy trình trong kiểm thử tích hợp. So với kiểm thử tích hợp, kiểm thử đơn vị với phần phụ thuộc được mô phỏng có thể thực hiện các trường hợp cá biệt như lỗi không mong muốn được trả về từ lệnh gọi API và đảm bảo việc xử lý hợp lý lỗi trong mã này. Khi bản dựng hoàn tất, mã đã dịch sẽ được đóng gói và ký. 

Kiểm thử triển khai trong môi trường tiền sản xuất

Trước khi triển khai vào môi trường sản xuất, quy trình triển khai và xác thực các thay đổi trong nhiều môi trường tiền sản xuất, ví dụ như alpha, beta và gamma. Alpha và beta xác thực rằng các mã mới nhất hoạt động như mong đợi bằng cách chạy kiểm thử API chức năng và kiểm thử tích hợp toàn diện. Gamma xác thực rằng mã đang hoạt động và có thể được triển khai an toàn vào môi trường sản xuất. Gamma giống với môi trường sản xuất nhất có thể, bao gồm cùng cấu hình triển khai, cùng hoạt động giám sát và cảnh báo, cũng như cùng kiểm thử canary liên tục ở dạng môi trường sản xuất. Gamma cũng được triển khai ở nhiều Khu vực AWS để phát hiện mọi tác động tiềm ẩn do sự khác biệt khu vực. 

Kiểm thử tích hợp

Kiểm thử tích hợp giúp chúng tôi tự động sử dụng dịch vụ tương tự như khi khách hàng sử dụng. Đây là một bước trong quy trình. Các kiểm thử này thực hiện đầu cuối toàn diện bằng cách gọi API thực chạy trên cơ sở hạ tầng thực trong từng giai đoạn tiền sản xuất đối với tất cả tình huống sử dụng thực tế của khách hàng. Mục đích của kiểm thử tích hợp là phát hiện mọi hành vi không mong đợi hoặc không chính xác của dịch vụ trước khi triển khai vào môi trường sản xuất.

Trong khi kiểm thử đơn vị chạy dựa vào phần phụ thuộc được mô phỏng, thì kiểm thử tích hợp chạy dựa vào hệ thống tiền sản xuất gọi các phần phụ thuộc thực, xác thực giả định của mô phỏng về cách các phần phụ thuộc đó hoạt động. Kiểm thử tích hợp xác thực hành vi của API riêng lẻ trên các đầu vào khác nhau. Ngoài ra, kiểm thử tích hợp cũng xác thực quy trình công việc đầy đủ kết hợp nhiều API như tạo tài nguyên mới, mô tả tài nguyên mới cho đến khi tài nguyên sẵn sàng, sau đó sử dụng tài nguyên này.

Kiểm thử tích hợp chạy cả trường hợp kiểm thử tích cực và tiêu cực, như cung cấp đầu vào không hợp lệ cho API và kiểm tra để đảm bảo rằng lỗi “đầu vào không hợp lệ” được trả về như dự kiến. Một số quy trình chạy kiểm thử fuzz để tạo ra nhiều đầu vào API có thể dùng và xác thực rằng các API đó không gây ra bất kỳ lỗi nội bộ nào trong dịch vụ. Một số quy trình cũng chạy kiểm thử tải ngắn trong giai đoạn tiền sản xuất để đảm bảo rằng các thay đổi mới nhất không gây ra bất kỳ độ trễ hoặc sự hồi quy thông lượng nào ở cấp tải thực.

Khả năng tương thích ngược và kiểm thử một hộp

Trước khi triển khai vào môi trường sản xuất, chúng tôi cần đảm bảo rằng mã mới nhất tương thích ngược và có thể được triển khai an toàn cùng với mã hiện tại. Ví dụ: chúng tôi cần phát hiện xem mã mới nhất có viết dữ liệu ở định dạng mà mã hiện tại không thể phân tích cú pháp hay không. Giai đoạn một hộp trong gamma triển khai mã mới nhất vào đơn vị triển khai nhỏ nhất, như vào máy ảo đơn lẻ hay bộ chứa đơn lẻ, hoặc vào tỷ lệ nhỏ của yêu cầu gọi hàm AWS Lambda. Triển khai một hộp này để phần còn lại của môi trường gamma được triển khai bằng mã hiện tại trong một khoảng thời gian, như 30 phút hoặc 1 giờ. Lưu lượng không cần phải được dẫn riêng đến một hộp. Lưu lượng có thể được thêm vào cùng cân bằng tải hoặc thăm dò cùng hàng đợi như phần còn lại của môi trường gamma. Ví dụ: trong môi trường gamma gồm 10 bộ chứa sau cân bằng tải, một hộp nhận 10% lưu lượng gamma được kiểm thử canary liên tục tạo ra. Triển khai một hộp giám sát tỷ lệ thành công của kiểm thử canary và chỉ số dịch vụ để phát hiện mọi tác động từ việc triển khai hoặc từ việc có nhóm “hỗn hợp” được triển khai song song.

Sơ đồ dưới đây cho biết trạng thái của môi trường gamma sau khi mã mới được triển khai vào giai đoạn một hộp, nhưng chưa được triển khai vào phần còn lại của nhóm gamma: 

Chúng tôi cũng cần đảm bảo mã mới nhất tương thích ngược với phần phụ thuộc của mình. Ví dụ: thay đổi cần được thực hiện trên các vi dịch vụ theo trình tự cụ thể. Vi dịch vụ trong môi trường tiền sản xuất thường gọi điểm cuối sản xuất của mọi dịch vụ do nhóm khác sở hữu, như Amazon Simple Storage Service (S3) hoặc Amazon DynamoDB. Tuy nhiên, các vi dịch vụ này gọi điểm cuối tiền sản xuất của vi dịch vụ khác của nhóm dịch vụ trong cùng một giai đoạn. Ví dụ: vi dịch vụ A của nhóm trong gamma gọi vi dịch vụ B của cùng nhóm đó trong gamma, nhưng gọi điểm cuối sản xuất cho Amazon S3.

Một số quy trình cũng chạy lại kiểm thử tích hợp trong giai đoạn tương thích ngược riêng biệt mà chúng tôi gọi là zeta, đây là môi trường riêng biệt mà mỗi vi dịch vụ chỉ gọi điểm cuối sản xuất, kiểm thử rằng các thay đổi triển khai vào môi trường sản xuất tương thích với mã hiện được triển khai trong môi trường sản xuất trên nhiều vi dịch vụ. Ví dụ: vi dịch vụ A trong zeta gọi điểm cuối sản xuất của vi dịch vụ B và điểm cuối sản xuất cho Amazon S3.

Để biết thông tin mô tả của các chiến lược dùng để viết và triển khai các thay đổi tương thích ngược, hãy xem bài viết Đảm bảo an toàn quay lui trong quá trình triển khai trên Builders’ Library. 

Triển khai sản xuất

Mục tiêu hàng đầu của chúng tôi về triển khai sản xuất tại AWS là ngăn tác động tiêu cực đối với nhiều Khu vực tại cùng thời gian và đối với nhiều Vùng sẵn sàng trong cùng Khu vực. Việc giới hạn phạm vi của từng triển khai riêng lẻ hạn chế tác động tiềm ẩn đến khách hàng do việc triển khai sản xuất không thành công và ngăn chặn tác động đến nhiều Vùng sẵn sàng hoặc nhiều Khu vực. Để giới hạn phạm vi của triển khai tự động, chúng tôi chia giai đoạn sản xuất của quy trình thành nhiều giai đoạn và nhiều lượt triển khai đến các Khu vực riêng lẻ. Các nhóm chia quá trình triển khai khu vực thành các quy trình triển khai có phạm vi nhỏ hơn bằng cách triển khai đến các Vùng sẵn sàng riêng lẻ hoặc đến các phân mảnh (gọi là ô) nội bộ riêng lẻ của các dịch vụ trong quy trình của họ. Việc này nhằm hạn chế thêm phạm vi của tác động tiềm ẩn do việc triển khai sản xuất không thành công.

Triển khai xen kẽ

Mỗi nhóm cần cân bằng độ an toàn của các lượt triển khai phạm vi nhỏ ở tốc độ mà chúng tôi có thể phân phối các thay đổi cho khách hàng ở tất cả các Khu vực. Việc triển khai lần lượt các thay đổi đối với 24 Khu vực hoặc 76 Vùng sẵn sàng thông qua quy trình giúp hạn chế tối thiểu việc gây ra ảnh hưởng rộng, nhưng có thể mất vài tuần thì quy trình mới có thể phân phối thay đổi cho tất cả khách hàng. Chúng tôi đã nhận thấy rằng việc nhóm các lượt triển khai thành “đợt” với quy mô tăng dần, như trong quy trình sản xuất mẫu trước đây, giúp chúng tôi đạt được sự cân bằng giữa tốc độ và rủi ro triển khai. Mỗi giai đoạn của đợt trong quy trình bố trí các lượt triển khai cho nhóm Khu vực, với các thay đổi được xúc tiến từ đợt này sang đợt khác. Các thay đổi mới có thể vào giai đoạn sản xuất của quy trình bất kỳ lúc nào. Sau khi một nhóm thay đổi được xúc tiến từ bước 1 đến bước 2 trong đợt 1, nhóm thay đổi tiếp theo từ gamma được xúc tiến vào bước 1 của đợt 1. Vì vậy, chúng tôi không kết thúc với gói lớn chứa các thay đổi đợi được triển khai vào môi trường sản xuất.

2 đợt đầu tiên trong quy trình tạo dựng độ tin cậy nhất trong thay đổi: Đợt đầu tiên triển khai đến một Khu vực có số yêu cầu thấp để hạn chế tác động tiềm ẩn của triển khai sản xuất đầu tiên của thay đổi mới. Đợt này chỉ triển khai đến 1 Vùng sẵn sàng (hoặc ô) mỗi lần trong Khu vực đó để triển khai thay đổi trên Khu vực một cách thận trọng. Sau đó, đợt thứ 2 triển khai đến 1 Vùng sẵn sàng (hoặc ô) mỗi lần trong Khu vực có số lượng yêu cầu cao, nơi mà nhiều khả năng là khách hàng sẽ thực hiện tất cả đường dẫn mã mới và nơi chúng tôi nhận xác thực thích hợp của thay đổi.

Sau khi tin tưởng hơn về độ an toàn của thay đổi từ lượt triển khai của đợt quy trình ban đầu, chúng tôi có thể triển khai song song ngày càng nhiều Khu vực hơn trong cùng đợt. Ví dụ: quy trình sản xuất mẫu trước triển khai đến 3 Khu vực trong đợt 3, sau đó đến tối đa 12 Khu vực trong đợt 4 rồi các Khu vực còn lại trong đợt 5. Lựa chọn và số lượng chính xác các Khu vực trong từng đợt của số đợt này, cũng như số lượng đợt trong quy trình của nhóm dịch vụ phụ thuộc vào phạm vi và mẫu sử dụng của dịch vụ riêng lẻ. Các đợt sau trong quy trình vẫn giúp chúng tôi đạt được mục tiêu của mình là ngăn chặn tác động tiêu cực đến nhiều Vùng sẵn sàng trong cùng Khu vực. Khi một đợt triển khai song song đến nhiều Khu vực, đợt này sẽ tuân theo cùng hành vi triển khai thận trọng đối với từng Khu vực được dùng trong các đợt ban đầu. Từng bước trong đợt chỉ triển khai đến một Vùng sẵn sàng hoặc ô đơn lẻ từ mỗi Khu vực trong đợt.

Triển khai luân phiên và một hộp

Triển khai đến từng đợt sản xuất bắt đầu bằng giai đoạn một hộp. Như trong giai đoạn một hộp gamma, mỗi giai đoạn một hộp sản xuất triển khai mã mới nhất đến một hộp (máy ảo đơn lẻ, bộ chứa đơn lẻ hoặc tỷ lệ nhỏ của yêu cầu gọi hàm Lambda) trong từng Khu vực hoặc Vùng sẵn sàng của đợt. Triển khai một hộp sản xuất giảm tối thiểu tác động tiềm ẩn của thay đổi đối với đợt bằng cách hạn chế từ đầu các yêu cầu được mã mới trong đợt đó phân phối. Thông thường, một hộp phân phối tối đa 10% tổng yêu cầu cho Khu vực hoặc Vùng sẵn sàng. Nếu thay đổi gây ra tác động tiêu cực trong một hộp, thì quy trình tự động quay lui thay đổi và không xúc tiến thay đổi đến các giai đoạn sản xuất còn lại.

Sau giai đoạn một hộp, hầu hết các nhóm dùng triển khai luân phiên để triển khai đến nhóm sản xuất chính của đợt. Triển khai luân phiên đảm bảo rằng dịch vụ có đủ dung lượng để phân phối tải sản xuất trong suốt quá trình triển khai. Triển khai luân phiên kiểm soát tốc độ mà mã mới được đưa vào dịch vụ (đó là thời điểm nó bắt đầu phân phối lưu lượng sản xuất) để hạn chế tác động của các thay đổi. Trong lượt triển khai luân phiên thông thường đến Khu vực, tối đa 33% các hộp của dịch vụ trong Khu vực đó (bộ chứa, yêu cầu gọi Lambda hoặc phần mềm chạy trên máy ảo) được thay thế bằng mã mới.

Trong quá trình triển khai, đầu tiên, hệ thống triển khai chọn một lô ban đầu gồm tối đa 33% các hộp để thay thế bằng mã mới. Trong quá trình thay thế, tối thiểu 66% tổng dung lượng ở tình trạng tốt và phân phối các yêu cầu. Tất cả các dịch vụ được điều chỉnh quy mô để tránh mất Vùng sẵn sàng trong Khu vực. Nhờ vậy, chúng tôi biết rằng dịch vụ vẫn có thể phân phối tải sản xuất với công suất này. Sau khi hệ thống triển khai xác định rằng hộp trong lô hộp ban đầu đã vượt qua bước kiểm tra tình trạng, một hộp từ nhóm còn lại có thể được thay thế bằng mã mới, v.v. Trong khi đó, chúng tôi vẫn duy trì tối thiểu 66% công suất để phân phối các yêu cầu mọi lúc. Để hạn chế thêm tác động của các thay đổi, một số quy trình của nhóm chỉ triển khai 5% số hộp của chúng mỗi lần. Tuy nhiên, sau đó quy trình tiến hành quay lui nhanh. Tại đây, hệ thống thay thế 33% số hộp mỗi lần bằng mã trước để đẩy nhanh tốc độ quay lui.

Sơ đồ sau đây cho biết tình trạng của môi trường sản xuất ở giữa quá trình triển khai luân phiên. Mã mới được triển khai đến giai đoạn một hộp và đến lô đầu tiên của nhóm sản xuất chính. Lô còn lại bị loại bỏ khỏi cân bằng tải và bị ngừng hoạt động để thay thế.

Giám sát chỉ số và quay lui tự động

Triển khai tự động trong quy trình thường không có nhà phát triển tích cực theo dõi từng lượt triển khai vào môi trường sản xuất, kiểm tra chỉ số và quay lui thủ công nếu họ nhận thấy sự cố. Những lượt triển khai này hoàn toàn không cần thao tác thủ công. Hệ thống triển khai tích cực giám sát cảnh báo để xác định xem có cần tự động quay lui một lượt triển khai hay không. Quá trình quay lui sẽ chuyển môi trường về hình ảnh bộ chứa, gói triển khai hàm AWS Lambda hoặc gói triển khai nội bộ đã được triển khai trước đây. Các gói triển khai nội bộ của chúng tôi giống với hình ảnh bộ chứa vì các gói này không thay đổi và dùng giá trị tổng kiểm để xác minh tính toàn vẹn.

Mỗi vi dịch vụ trong từng Khu vực thường có cảnh báo mức độ nghiêm trọng cao. Cảnh báo này kích hoạt trên các ngưỡng cho chỉ số tác động đến khách hàng của dịch vụ (như tỷ lệ lỗi và độ trễ cao) và trên chỉ số tình trạng hệ thống (như mức sử dụng CPU) như minh họa trong ví dụ sau đây. Cảnh báo mức độ nghiêm trọng cao này dùng để thông báo cho kỹ sư đang trực và tự động quay lui dịch vụ nếu đang trong quá trình triển khai. Thông thường, khi kỹ sư đang trực được thông báo và bắt đầu can thiệp, quá trình quay lui cũng đang diễn ra.

Ví dụ về cảnh báo vi dịch vụ ở mức nghiêm trọng cao

ALARM("FrontEndApiService_High_Fault_Rate") OR
ALARM("FrontEndApiService_High_P50_Latency") OR
ALARM("FrontEndApiService_High_P90_Latency") OR
ALARM("FrontEndApiService_High_P99_Latency") OR
ALARM("FrontEndApiService_High_Cpu_Usage") OR
ALARM("FrontEndApiService_High_Memory_Usage") OR
ALARM("FrontEndApiService_High_Disk_Usage") OR
ALARM("FrontEndApiService_High_Errors_In_Logs") OR
ALARM("FrontEndApiService_High_Failing_Health_Checks")

Các thay đổi do quá trình triển khai tạo ra có thể có tác động đến vi dịch vụ ngược tuyến và xuôi tuyến. Vì vậy, hệ thống triển khai cần giám sát cảnh báo mức độ nghiêm trọng cao đối với các vi dịch vụ đang trong quá trình triển khai, đồng thời giám sát cảnh báo mức độ nghiêm trọng cao đối với các vi dịch vụ khác của nhóm để xác định thời điểm cần quay lui. Các thay đổi đã triển khai cũng có thể ảnh hưởng đến chỉ số của kiểm thử canary liên tục. Vì vậy, hệ thống triển khai cần giám sát thêm kiểm thử canary không thành công. Để tự động quay lui dựa trên tất cả vùng tác động tiềm ẩn này, các nhóm cần tạo cảnh báo kết hợp có mức độ nghiêm trọng cao cho hệ thống triển khai để giám sát. Cảnh báo kết hợp có mức độ nghiêm trọng cao hợp nhất trạng thái của tất cả cảnh báo riêng lẻ có mức độ nghiêm trọng cao liên quan đến vi dịch vụ của nhóm, cũng như trạng thái của cảnh báo canary thành một trạng thái kết hợp đơn lẻ, như trong mẫu dưới đây. Nếu bất kỳ cảnh báo mức độ nghiêm trọng cao nào cho vi dịch vụ của nhóm chuyển sang trạng thái cảnh báo, thì tất cả lượt triển khai đang diễn ra của nhóm đó trên tất cả vi dịch vụ của nhóm trong Khu vực đó sẽ tự động được quay lui.

Ví dụ về cảnh báo quay lui kết hợp ở mức độ nghiêm trọng cao

ALARM("FrontEndApiService_High_Severity") OR
ALARM("BackendApiService_High_Severity") OR
ALARM("BackendWorkflows_High_Severity") OR
ALARM("Canaries_High_Severity")

Giai đoạn một hộp phân phối tỷ lệ nhỏ trong tổng số lưu lượng. Vì vậy, các sự cố do việc triển khai một hộp gây ra có thể không kích hoạt cảnh báo quay lui ở mức độ nghiêm trọng cao của dịch vụ. Để phát hiện và quay lui các thay đổi có thể gây ra sự cố trong giai đoạn một hộp trước khi chúng đến các giai đoạn sản xuất còn lại, ngoài ra, các giai đoạn một hộp chỉ quay lui dựa trên chỉ số (được điều chỉnh phạm vi) đến một hộp. Ví dụ: các thay đổi quay lui dựa trên tỷ lệ lỗi của yêu cầu được một hộp cung cấp riêng, điều này tạo nên tỷ lệ nhỏ trong tổng số các yêu cầu. 

Ví dụ về cảnh báo quay lui một hộp

ALARM("High_Severity_Aggregate_Rollback_Alarm") OR
ALARM("FrontEndApiService_OneBox_High_Fault_Rate") OR
ALARM("FrontEndApiService_OneBox_High_P50_Latency") OR
ALARM("FrontEndApiService_OneBox_High_P90_Latency") OR
ALARM("FrontEndApiService_OneBox_High_P99_Latency") OR
ALARM("FrontEndApiService_OneBox_High_Cpu_Usage") OR
ALARM("FrontEndApiService_OneBox_High_Memory_Usage") OR
ALARM("FrontEndApiService_OneBox_High_Disk_Usage") OR
ALARM("FrontEndApiService_OneBox_High_Errors_In_Logs") OR
ALARM("FrontEndApiService_OneBox_Failing_Health_Checks")

Bên cạnh việc quay lui dựa trên cảnh báo được nhóm dịch vụ xác định, hệ thống triển khai của chúng tôi cũng có thể xác định và tự động quay lui dựa trên các vấn đề bất thường trong chỉ số thông thường do khung dịch vụ web nội bộ của chúng tôi tạo ra. Hầu hết các vi dịch vụ của chúng tôi tạo ra chỉ số như số lượng yêu cầu, độ trễ yêu cầu và số lượng lỗi ở định dạng tiêu chuẩn. Bằng cách sử dụng các chỉ số tiêu chuẩn này, hệ thống triển khai có thể quay lui tự động nếu có vấn đề bất thường trong chỉ số trong quá trình triển khai. Ví dụ về việc này là nếu số lượng yêu cầu đột ngột giảm xuống 0 hoặc nếu độ trễ hay số lượng lỗi cao hơn nhiều so với bình thường.

Thời gian xử lý

Đôi khi, chúng tôi khó có thể nhận thấy tác động tiêu cực do quá trình triển khai gây ra. Tác động này diễn ra chậm. Điều đó nghĩa là tác động không xuất hiện ngay lập tức trong quá trình triển khai, đặc biệt là nếu dịch vụ đang trong quá trình tải chậm vào thời điểm đó. Việc xúc tiến thay đổi đến giai đoạn quy trình tiếp theo ngay lập tức sau khi triển khai xong có thể dẫn đến tác động trong nhiều Khu vực khi tác động xuất hiện trong Khu vực đầu tiên. Trước khi xúc tiến thay đổi đến giai đoạn sản xuất tiếp theo, từng giai đoạn sản xuất trong quy trình đều có thời gian xử lý. Đó là khi quy trình tiếp tục giám sát cảnh báo kết hợp ở mức độ nghiêm trọng cao của nhóm đối với mọi tác động diễn ra chậm sau khi triển khai xong và trước khi chuyển đến giai đoạn tiếp theo.

Để tính lượng thời gian xử lý việc triển khai, chúng tôi cần cân bằng rủi ro của việc gây ra tác động rộng hơn nếu xúc tiến các thay đổi đến nhiều Khu vực quá nhanh so với tốc độ mà chúng tôi có thể phân phối các thay đổi cho tất cả khách hàng. Chúng tôi nhận thấy một cách hiệu quả để cân bằng các rủi ro này là dành nhiều thời gian xử lý hơn cho các đợt đầu tiên trong quy trình, đồng thời tạo dựng độ tin cậy về an toàn của thay đổi, sau đó rút ngắn thời gian xử lý cho các đợt sau. Mục tiêu của chúng tôi là giảm tối thiểu rủi ro của tác động ảnh hưởng đến nhiều Khu vực. Vì hầu hết các lượt triển khai không được thành viên nhóm tích cực theo dõi, nên thời gian xử lý mặc định của quy trình điển hình được bảo toàn và sẽ triển khai thay đổi đến tất cả các Khu vực trong vòng khoảng 4 hoặc 5 ngày làm việc. Các dịch vụ lớn hơn hoặc đặc biệt quan trọng có nhiều thời gian xử lý bảo toàn hơn và nhiều thời gian hơn cho các quy trình của chúng triển khai tổng thể thay đổi.

Quy trình điển hình sẽ chờ tối thiểu 1 giờ sau từng giai đoạn một hộp, tối thiểu 12 giờ sau đợt khu vực đầu tiên và tối thiểu 2 đến 4 giờ sau từng đợt khu vực còn lại, với thời gian xử lý bổ sung cho các Khu vực, Vùng sẵn sàng và ô riêng lẻ trong từng đợt. Thời gian xử lý bao gồm yêu cầu đợi số lượng điểm dữ liệu cụ thể trong chỉ số của nhóm (ví dụ: “đợi tối thiểu 100 yêu cầu để Tạo API”) để đảm bảo đủ yêu cầu xuất hiện nhằm đảm bảo mã mới được thực hiện đầy đủ. Trong toàn bộ thời gian xử lý, triển khai được tự động quay lui nếu cảnh báo kết hợp ở mức độ nghiêm trọng cao của nhóm chuyển sang trạng thái cảnh báo.

Mặc dù rất hiếm xảy ra, nhưng trong một số trường hợp, thay đổi khẩn cấp (như bản sửa lỗi bảo mật hoặc việc giảm thiểu cho sự kiện quy mô lớn ảnh hưởng đến tính sẵn có của dịch vụ) có thể cần được phân phối đến khách hàng nhanh hơn thời gian mà quy trình thường mất để xử lý thay đổi và triển khai. Trong các trường hợp này, chúng tôi có thể giảm thời gian xử lý quy trình để thúc đẩy việc triển khai, nhưng chúng tôi cần xem xét kỹ lưỡng thay đổi để thực hiện việc này. Đối với các trường hợp này, chúng tôi cần các Kỹ sư chính của tổ chức phải xem xét kỹ lưỡng. Nhóm này phải xem xét thay đổi mã, mức cấp thiết và rủi ro của tác động nhờ các nhà phát triển giàu kinh nghiệm là chuyên gia trong lĩnh vực an toàn vận hành. Thay đổi vẫn sẽ trải qua cùng các bước trong quy trình như bình thường, nhưng được xúc tiến đến giai đoạn tiếp theo nhanh hơn. Chúng tôi quản lý rủi ro của triển khai nhanh hơn bằng cách hạn chế thay đổi di chuyển trong quy trình trong thời gian này để chỉ cho phép các thay đổi mã nhỏ nhất cần thiết để xử lý sự cố hiện tại và bằng cách tích cực theo dõi triển khai.

Trình chặn cửa sổ thời gian và cảnh báo

Quy trình này ngăn triển khai tự động vào môi trường sản xuất khi có rủi ro cao hơn có thể gây ra tác động tiêu cực. Quy trình sử dụng bộ “trình chặn” giúp đánh giá rủi ro triển khai. Ví dụ: tự động triển khai thay đổi mới vào môi trường sản xuất khi sự số hiện đang diễn ra trong môi trường có thể khiến tác động xấu hơn hoặc kéo dài hơn. Trước khi bắt đầu triển khai mới vào bất kỳ giai đoạn sản xuất nào, quy trình kiểm tra cảnh báo kết hợp ở mức độ nghiêm trọng cao của nhóm để xác định xem có bất kỳ sự cố nào đang diễn ra không. Nếu cảnh báo này đang ở trạng thái cảnh báo, thì quy trình sẽ ngăn không cho thay đổi chuyển đến bước tiếp theo. Quy trình cũng có thể kiểm tra cảnh báo trên toàn tổ chức, như cảnh báo sự kiện quy mô lớn cho biết liệu có tác động rộng trong hệ thống của nhóm khác không và ngăn việc bắt đầu triển khai mới có thể bổ sung vào tác động tổng thể. Nhà phát triển có thể ghi đè các trình chặn triển khai này khi thay đổi cần được triển khai đến giai đoạn sản xuất để khôi phục từ sự cố mức độ nghiêm trọng cao.

Quy trình cũng được định cấu hình với bộ cửa sổ thời gian xác định khi triển khai được phép bắt đầu. Khi định cấu hình cửa sổ thời gian, chúng tôi cần cân bằng 2 nguyên nhân của rủi ro triển khai. Mặt khác, các cửa sổ thời gian rất nhỏ có thể gây ra thay đổi để xếp chồng trong quy trình khi cửa sổ thời gian đóng, tăng khả năng bất kỳ thay đổi nào trong số đó trong triển khai tiếp theo sẽ có tác động khi cửa sở thời gian mở. Mặt khác, cửa sổ thời gian rất lớn vượt quá giờ làm việc thông thường sẽ tăng rủi ro kéo dài tác động từ triển khai không thành công. Trong thời gian ngoài giờ làm việc, sẽ mất nhiều thời gian hơn để thông báo cho kỹ sư đang trực trong ngày, khi kỹ sư đang trực hoặc thành viên nhóm khác đang làm việc. Trong giờ làm việc thông thường, nhóm có thể can thiệp nhanh hơn sau khi triển khai không thành công trong trường hợp cần thực hiện bất kỳ bước khôi phục thủ công nào.

Hầu hết triển khai không được thành viên nhóm tích cực theo dõi, vì vậy, chúng tôi tối đa hóa thời gian triển khai nhằm giảm tối thiểu thời gian cần thiết để thông báo cho kỹ sư đang trực, trong trường hợp cần thực hiện thao tác thủ công nào đối với việc khôi phục sau khi quay lui tự động. Kỹ sư đang trực thường mất nhiều thời gian hơn để can thiệp vào ban đêm, vào ngày nghỉ và cuối tuần, vì vậy những thời gian này bị loại trừ khỏi cửa sổ thời gian. Tùy thuộc vào mẫu sử dụng dịch vụ, một số sự cố có thể không xuất hiện trong vài giờ sau khi triển khai. Vì vậy, nhiều nhóm cũng loại trừ các triển khai vào thứ 6 và chiều muộn khỏi cửa sổ thời gian để giảm rủi ro cần thông báo cho kỹ sư đang trực vào ban đêm hoặc trong cuối tuần sau khi triển khai. Chúng tôi nhận thấy rằng bộ cửa sổ thời gian này cho phép khôi phục nhanh ngay cả khi cần có thao tác thủ công, đảm bảo giảm sự can thiệp của kỹ sư đang trực ngoài giờ làm việc thông thường, đồng thời đảm bảo số thay đổi nhỏ được gộp với nhau trong khi cửa sổ thời gian đóng.

Quy trình ở dạng mã

Nhóm dịch vụ AWS điển hình sở hữu nhiều quy trình để triển khai nhiều vi dịch vụ và loại nguồn (mã ứng dụng, mã cơ sở hạ tầng, bản vá hệ điều hành, v.v.) của nhóm. Mỗi quy trình có nhiều giai đoạn triển khai cho số lượng Khu vực và Vùng sẵn sàng ngày càng tăng. Điều này dẫn đến nhiều cấu hình cho nhóm để quản lý trong hệ thống quy trình, trong hệ thống triển khai và hệ thống cảnh báo và nhiều nỗ lực để cập nhật các phương pháp tốt nhất và mới nhất cũng như với các Khu vực và Vùng sẵn sàng mới. Trong vài năm qua, chúng tôi đã tiếp nhận việc thực hiện “quy trình ở dạng mã” như một cách định cấu hình quy trình an toàn và cập nhật dễ dàng và nhất quán hơn bằng cách tạo mô hình cấu hình này trong mã. Các quy trình nội bộ của chúng tôi ở dạng công cụ mã đẩy từ danh sách tập trung gồm các Khu vực và Vùng sẵn sàng để dễ dàng thêm Khu vực và Vùng sẵn sàng mới vào quy trình thông qua AWS. Công cụ này cũng cho phép nhóm tạo mô hình quy trình bằng phương pháp kế thừa, xác định cấu hình thông thường trên quy trình của nhóm ở lớp mẹ (như các Khu vực trong từng đợt và thời gian xử lý cần thiết cho từng đợt) và xác định tất cả các cấu hình quy trình vi dịch vụ ở dạng lớp con kế thừa tất cả cấu hình thông thường.

Kết luận

Tại Amazon, chúng tôi tạo dựng các phương pháp triển khai tự động theo thời gian dựa trên những thứ giúp chúng tôi cân bằng an toàn triển khai với tốc độ triển khai. Đồng thời, chúng tôi muốn giảm tối thiểu lượng thời gian mà nhà phát triển cần theo dõi quá trình triển khai. Việc tạo dựng an toàn triển khai tự động trong quy trình phát hành bằng cách sử dụng quay lui tự động, triển khai sản xuất xen kẽ và kiểm thử tiền sản xuất rộng rãi cho phép chúng tôi giảm tối thiểu tác động tiềm ẩn đến môi trường sản xuất do việc triển khai gây ra. Điều này nghĩa là các nhà phát triển không cần phải tích cực theo dõi quá trình triển khai vào môi trường sản xuất.

Nhờ quy trình hoàn toàn tự động, nhà phát triển sử dụng xem xét mã để kiểm tra mã của họ và xác nhận rằng thay đổi sẵn sàng triển khai vào môi trường sản xuất. Sau khi thay đổi được hợp nhất vào khu lưu trữ mã nguồn, nhà phát triển có thể chuyển sang nhiệm vụ tiếp theo và để quy trình lo phần còn lại. Sau đó, quy trình sẽ triển khai thay đổi của họ vào môi trường sản xuất một cách an toàn và thận trọng. Quy trình tự động phụ trách việc triển khai liên tục vào môi trường sản xuất nhiều lần mỗi ngày, đồng thời cân bằng độ an toàn và tốc độ. Việc tạo mô hình phương pháp phân phối liên tục trong mã giúp việc triển khai trở nên dễ dàng hơn bao giờ hết. Các nhóm dịch vụ AWS có thể thiết lập quy trình để triển khai thay đổi mã của họ theo cách tự động và an toàn.

Đọc thêm

Để biết thêm thông tin về cách Amazon cải thiện tính bảo mật và độ sẵn sàng của dịch vụ, đồng thời tăng sự hài lòng của khách hàng và năng suất của nhà phát triển, hãy xem Phát triển nhanh hơn nhờ phân phối liên tục

Để biết thông tin mô tả của các chiến lược dùng để viết và triển khai các thay đổi tương thích ngược, hãy xem bài viết Đảm bảo an toàn quay lui trong quá trình triển khai trên Builders’ Library 


Giới thiệu về tác giả

Clare Liguori là Kỹ sư chính về phần mềm tại AWS. Clare Liguori hiện đang tập trung nghiên cứu trải nghiệm của nhà phát triển khi sử dụng AWS Container Services, các công cụ tạo dựng tại giao điểm của bộ chứa và vòng đời phát triển phần mềm: phát triển cục bộ, cơ sở hạ tầng ở dạng mã, CI/CD, khả năng quan sát và quá trình hoạt động.