Xây dựng quy trình CI/CD hiệu quả với GitHub Actions Nó không còn là một thứ "dùng khi có thời gian" nữa: trong các nhóm hiện đại, nó thực tế là một yêu cầu bắt buộc để triển khai nhanh chóng và đáng tin cậy. Tuy nhiên, việc tìm kiếm một ví dụ hoàn chỉnh, tổng quát và được suy nghĩ kỹ lưỡng mà bạn có thể điều chỉnh cho phù hợp với công ty của mình thường phức tạp hơn nhiều so với tưởng tượng.
Trong những dòng tiếp theo, chúng ta sẽ kết hợp lý thuyết cổ điển về CI/CD. Với các ví dụ triển khai thực tế sử dụng GitHub Actions, các pipeline có thể tái sử dụng, Tasks, và các script bash, Mô-đun PowerShell PnPTriển khai lên Kubernetes, Google Cloud và Kinsta, cùng với các phương pháp tốt nhất về bảo mật, giám sát và khả năng mở rộng. Ý tưởng là bạn có thể lấy những phần này, điều chỉnh chúng cho phù hợp với bối cảnh của mình và tránh được nhiều cạm bẫy thường gặp.
Tại sao bạn cần một quy trình CI/CD được xây dựng tốt?
Trong phát triển chuyên môn hiện nay, CI/CD là hệ thống tuần hoàn của quy tắc.Nó tích hợp các thay đổi, chạy thử nghiệm, xây dựng các thành phần và triển khai các phiên bản mới với sự can thiệp tối thiểu. Nếu không có quy trình này, mỗi lần triển khai sẽ trở nên chậm chạp, dễ xảy ra lỗi và đòi hỏi thao tác thủ công.
Tích hợp liên tục (CI) tập trung vào việc xác thực các thay đổi. Ngay sau khi được tải lên kho lưu trữ, các bài kiểm tra đơn vị, công cụ kiểm tra cú pháp và phân tích tĩnh sẽ được chạy để phát hiện lỗi nhanh nhất có thể. Phản hồi càng nhanh, bạn càng sớm sửa lỗi và quá trình hồi quy sẽ càng ít gây khó khăn hơn.
Triển khai liên tục (CD theo nghĩa Triển khai liên tục) Hoặc phương pháp Phân phối Liên tục (tùy thuộc vào mức độ tự động hóa) bổ sung thêm tính năng tự động hóa trong khâu phát hành: xây dựng ảnh hệ thống, xuất bản gói phần mềm, triển khai lên môi trường thử nghiệm, dàn dựng hoặc sản xuất, và thậm chí thay đổi lưu lượng truy cập bằng chiến lược xanh-đỏ hoặc canary.
Trong các công ty có nhiều mã nguồn cũ.Một quy trình kiểm thử hiệu quả là một trong những đòn bẩy tốt nhất để hiện đại hóa hệ sinh thái: nó cho phép bạn tích hợp các bài kiểm thử vào các dịch vụ cũ, tự động hóa các tác vụ trước đây được thực hiện thủ công và giảm chi phí bảo trì các cơ sở hạ tầng như Jenkins hoặc Nexus đã trở nên lỗi thời.
GitHub Actions là gì và tại sao nó lại phù hợp với quy trình CI/CD đến vậy?
GitHub Actions là nền tảng tự động hóa được tích hợp sẵn trong GitHub. Nó cho phép bạn định nghĩa các quy trình làm việc trong các tệp YAML ngay trong kho lưu trữ. Nhờ đó, bạn có thể biên dịch, kiểm thử, phân tích và triển khai phần mềm mà không cần thiết lập máy chủ CI bên ngoài.
Quy trình làm việc là một tập hợp các công việc và các bước. được kích hoạt bởi các sự kiện như sau: push, pull_request, schedule (CRON), workflow_dispatch (thủ công) hoặc thậm chí là các hành động trên các vấn đề. Mỗi tác vụ chạy trong một trình chạy (ví dụ: ubuntu-latest) và bao gồm các bước sử dụng các hành động hoặc lệnh có thể tái sử dụng. run.
GitHub cung cấp một thị trường khổng lồ cho việc chia sẻ. Nơi đây có sẵn các tích hợp cho hầu hết mọi thứ: Docker, Kubernetes, AWS, Azure, Google Cloud, SonarCloud, Slack, Jira, phân tích bảo mật, công cụ kiểm tra cú pháp cho hàng ngàn ngôn ngữ, v.v. Điều này giúp giảm đáng kể thời gian cần thiết để thiết lập các quy trình phức tạp.
So với các giải pháp như Jenkins hoặc ConcourseGitHub Actions có một số ưu điểm rõ rệt: đây là một dịch vụ được quản lý (bạn không cần quản lý máy chủ), nó gắn liền chặt chẽ với mã nguồn, sử dụng mô hình trả tiền theo mức sử dụng và được hỗ trợ bởi một cộng đồng lớn. Hơn nữa, nhiều nhà phát triển đã quen thuộc với nó từ các dự án cá nhân, điều này giúp giảm đáng kể thời gian học hỏi.
Các thành phần cơ bản của quy trình làm việc GitHub Actions
Mọi chuyện bắt đầu với một tệp YAML trong .github/workflows/, ví dụ ci.yml o build-test-deploy.ymlMặc dù cú pháp có thể phát triển đáng kể, cấu trúc cơ bản lại tương đối đơn giản.
Các phần chính của YAML bao gồm: name (tên quy trình làm việc), on (các sự kiện kích hoạt nó), jobs (tập hợp các nhiệm vụ logic), và trong mỗi công việc, runs-on (người chạy), steps (các bước), env (biến toàn cục) và if (Điều kiện để thực hiện các bước hoặc công việc).
Công việc đại diện cho các khối công việc. có thể chạy song song hoặc nối tiếp bằng cách sử dụng needsTrong mỗi công việc, các bước đều sử dụng các hành động (uses:) hoặc lệnh (run:Một ví dụ điển hình bao gồm: kiểm tra mã nguồn, cài đặt các thư viện phụ thuộc, thực thi công cụ kiểm tra cú pháp, kiểm thử và biên dịch.
Bí mật và biến môi trường Chúng được quản lý ở cấp độ kho lưu trữ, tổ chức hoặc môi trường. Trong quy trình làm việc, chúng được tham chiếu bằng ${{ secrets.MI_SECRET }} và cho phép làm việc với các khóa API, mã thông báo triển khai hoặc thông tin xác thực đám mây mà không cần để lộ chúng trong kho lưu trữ.
YAML cũng cho phép xây dựng các mảng thực thi. với strategy.matrixRất hữu ích để kiểm tra mã của bạn trên nhiều phiên bản Node, Python hoặc Java khác nhau, hoặc thậm chí trên các hệ điều hành khác nhau mà không cần viết lại cùng một khối mã nhiều lần.
Thiết kế một quy trình CI/CD hiện đại dựa trên các phương pháp thực hành tốt nhất.
Một quy trình sản xuất hiệu quả thường được chia thành các giai đoạn rõ ràng.Quy trình bao gồm: kiểm tra nhanh (lint, kiểm thử đơn vị), xây dựng sản phẩm, phát hành (phiên bản, gắn nhãn, nhật ký thay đổi, công bố trong kho lưu trữ sản phẩm) và triển khai trên một hoặc nhiều môi trường.
Giai đoạn tích hợp liên tục cần được thực hiện càng nhanh càng tốt. Điều này đảm bảo rằng bất kỳ yêu cầu đẩy hoặc kéo nào cũng nhận được phản hồi gần như ngay lập tức. Một phương pháp phổ biến là chạy các kiểm tra khác nhau song song bằng cách sử dụng các mảng hoặc tác vụ riêng biệt, chấp nhận chi phí cao hơn một chút để giảm thời gian chờ đợi tổng thể.
Để tách rời quy trình xử lý dữ liệu khỏi ngôn ngữ cụ thể.Bạn có thể sử dụng một công cụ quản lý tác vụ như Task (tương tự như Make nhưng có cú pháp thân thiện hơn với người dùng). Bằng cách này, quy trình làm việc của GitHub Actions chỉ gọi các tác vụ chung chung (task test, task lintv.v.) và mỗi kho lưu trữ sẽ định nghĩa cách chúng được triển khai nội bộ tùy thuộc vào việc đó là Node, Java, Python, v.v.
Việc quản lý phiên bản và các thành phần liên quan đóng vai trò quan trọng trong giai đoạn phát hành.Tại đây, bạn xây dựng ảnh Docker, tệp jar/war, gói npm hoặc bất kỳ artifact nào khác, tải chúng lên kho lưu trữ tương ứng (kho lưu trữ Docker, Maven, npm trong Artifact Registry, v.v.), gắn thẻ các commit và tạo bản phát hành GitHub hoặc nhật ký thay đổi bằng các công cụ như... git-cliff hoặc các hành động phát hành.
Cuối cùng là giai đoạn triển khai. Di chuyển tệp tin đó đến môi trường thực thi: Kubernetes (GKE), Google App Engine, Cloud Functions, các dịch vụ trên Kinsta, máy chủ riêng của bạn thông qua SSH, v.v. Tại đây, bạn có thể xâu chuỗi các bước tiếp theo, chẳng hạn như kiểm thử chức năng sau khi triển khai hoặc gửi thông báo Slack với chi tiết phát hành.
Ví dụ: Quy trình hoàn chỉnh với ESLint, kiểm thử và triển khai trên Kinsta
Một ví dụ rất dễ hiểu là sử dụng GitHub Actions. Để xác thực ứng dụng React bằng ESLint và các bài kiểm thử đơn vị, sau đó triển khai ứng dụng lên Kinsta bằng API của nó. Tất cả được điều phối trong một quy trình CI/CD duy nhất.
Phần đầu tiên của tệp YAML định nghĩa trình kích hoạt. và tên của pipeline. Ví dụ: nó chạy trên mỗi push y pull_request đến chi nhánh mainvà thậm chí được lên lịch bằng các tác vụ CRON (ví dụ: mỗi ngày vào lúc nửa đêm hoặc mỗi thứ Hai lúc 8:00 UTC) bằng cách sử dụng sự kiện schedule.
Công việc đầu tiên trong quy trình có thể được gọi là eslint và nó chịu trách nhiệm kiểm tra cú pháp mã. Nó chạy trong ubuntu-latest và sử dụng nhiều phiên bản Node khác nhau (ví dụ: 18.x, 20.x), kèm theo các bước để tải xuống và cấu hình Node. actions/setup-node, lưu trữ các phụ thuộc npm, cài đặt bằng npm ci và ném npm run lint.
Công việc thứ hai, testsĐiều đó phụ thuộc vào... eslint thông qua needs: eslintVì vậy, nó chỉ chạy nếu quá trình kiểm tra cú pháp thành công. Bên trong, mô hình được lặp lại: kiểm tra file, cài đặt các thư viện phụ thuộc và thực thi file. npm run test trên một phiên bản Node cụ thể.
Công việc thứ ba, deployNó được nối tiếp sau cả hai công việc sử dụng needs: và sử dụng một bước với curl Để gọi API của Kinsta. Để làm điều này, khóa API và ID ứng dụng được cấu hình dưới dạng bí mật trong GitHub (KINSTA_API_KEY y APP_IDvà được tiếp xúc trong công việc thông qua env Để xây dựng yêu cầu POST kích hoạt quá trình triển khai.
Điều quan trọng là phải hiểu rằng công việc triển khai này Kinsta coi việc API được chấp nhận là một thành công; tuy nhiên, nếu quá trình triển khai sau đó thất bại nội bộ trong Kinsta, quy trình làm việc trên GitHub vẫn có thể hiển thị trạng thái màu xanh lá cây. Điều này cần được lưu ý để tránh chủ quan và bổ sung thêm việc giám sát sau khi triển khai.
Quản lý cron nâng cao và lập lịch trình quy trình làm việc
Cú pháp CRON trong GitHub Actions Nó dựa trên định dạng năm trường của UNIX: phút, giờ, ngày trong tháng, tháng và ngày trong tuần. Mỗi trường có thể sử dụng dấu sao, phạm vi, danh sách và các bước (*, 1-5, 1,15,30, */5), cho phép lên lịch các công việc bảo trì, sao lưu, vệ sinh hoặc kiểm tra định kỳ.
Ví dụ: 0 0 * * * Thực thi quy trình công việc vào mỗi nửa đêm (UTC), trong khi 0 8 * * 1 Việc này diễn ra vào mỗi thứ Hai lúc 8:00. Nó kết hợp hoàn hảo với các yếu tố kích hoạt thông thường của push y pull_requestđể cùng một tệp YAML có thể phản ứng với cả những thay đổi mã và các lần thực thi theo lịch trình.
Khả năng này lý tưởng cho các tác vụ mà việc phát hành trong mỗi lần commit là không hợp lý.: thực hiện các quét bảo mật chuyên sâu (ví dụ: với OWASP Dependency Check trong Java), kiểm tra các phụ thuộc, kiểm tra độ bao phủ kiểm thử hoặc dọn dẹp các thành phần cũ trong registry.
Tái sử dụng quy trình làm việc: mở rộng quy mô CI/CD lên hàng trăm kho lưu trữ
Khi tổ chức của bạn có hàng chục hoặc hàng trăm kho lưu trữViệc sao chép và dán cùng một tệp YAML ở khắp mọi nơi là một công thức dẫn đến sự hỗn loạn. Bất kỳ thay đổi nào cũng yêu cầu sửa đổi một nửa hệ thống GitHub Enterprise, khiến việc duy trì tính nhất quán và các thực tiễn tốt nhất gần như không thể.
Giải pháp nằm ở việc thiết kế các quy trình làm việc có thể tái sử dụng. Được tập trung hóa trong kho lưu trữ "mẫu" CI/CD. Các quy trình này hiển thị đầu vào và đầu ra, và mỗi dịch vụ chỉ định một tệp YAML nhỏ để gọi chúng, truyền các tham số như loại hiện vật (Docker, thư viện Java, gói npm), môi trường triển khai (GKE, GAE, Cloud Function, v.v.) hoặc các mục tác vụ cần được thực thi.
Một mô hình phổ biến là tách ba quy trình làm việc lớn có thể tái sử dụng.: một trong build-check-task (tích hợp liên tục), một khía cạnh khác của build-release-dockerfile hoặc các hiện vật khác và lần triển khai thứ ba (deploy-gke, deploy-gaev.v.), sao cho mỗi kho lưu trữ xây dựng quy trình của riêng mình bằng cách kết hợp chúng.
Để gói gọn logic chung, các hành động tùy chỉnh cũng có thể được định nghĩa. en .github/actionsVí dụ, để cấu hình Gradle, Java, Node hoặc Task, để lấy siêu dữ liệu bản dựng, để xuất bản ảnh Docker, để gắn thẻ phiên bản trong Git bằng tập lệnh bash hoặc để gửi thông báo đến Slack. Nguyên tắc vàng là các kho lưu trữ dịch vụ chỉ nên sử dụng các quy trình làm việc có thể tái sử dụng, chứ không nên thực hiện trực tiếp các hành động này, để việc quản lý khả năng tương thích ngược dễ dàng hơn.
Tích hợp liên tục nhanh chóng với tác vụ, ma trận và phân tích tĩnh.
Trong giai đoạn biên dịch hoặc kiểm tra, nên thực hiện nhiều tác vụ song song.Kiểm thử đơn vị, phân tích tĩnh (PMD, Checkstyle, SpotBugs trong Java; ESLint trong JS/TS), quét bằng SonarCloud, v.v. Điều này giúp tổng thời gian thực hiện quy trình ở mức hợp lý ngay cả với các codebase lớn.
Tác vụ (Taskfile.yml) đóng vai trò như một lớp trừu tượng. trên các lệnh cụ thể, cho phép quy trình CI chỉ cần gọi task check, task test o task lintĐối với dự án Java, các tác vụ này có thể được giao cho Gradle với JUnit, PMD, Checkstyle và SpotBugs; đối với dự án Node, có thể giao cho Jest, ESLint và các công cụ bảo mật như... npm audit hoặc tương tự.
GitHub Actions thêm phần tử mảng. Để chạy cùng một tác vụ trên các phiên bản khác nhau của môi trường chạy: ví dụ, kiểm thử thư viện Node trên phiên bản 16, 18 và 20, hoặc dự án Python trên phiên bản 3.10 và 3.12. Việc này đơn giản chỉ cần khai báo một mảng các phiên bản và sử dụng nó trong cấu hình công việc.
Cách tiếp cận này đặc biệt hữu ích trong các tổ chức muốn hỗ trợ nhiều ngăn xếp công nghệ khác nhau. (Java, Node, TypeScript, Python, v.v.) mà không cần phải viết lại logic quy trình cho từng kho lưu trữ: Task thích ứng với từng ngôn ngữ và các quy trình làm việc có thể tái sử dụng vẫn gần như giống nhau.
Giai đoạn phát hành: tạo phiên bản, gắn thẻ và xuất bản các thành phần.
Sau khi vượt qua các bước kiểm tra, đã đến lúc xây dựng sản phẩm sẽ được triển khai thực tế.Ảnh Docker, tệp JAR, gói npm, bất cứ thứ gì phù hợp. Điều này liên quan đến cả các công cụ ngôn ngữ và các kho lưu trữ cũng như chính sách quản lý phiên bản của tổ chức.
Một số dự án Java sử dụng các plugin như Gradle Axion. Để quản lý các phiên bản dựa trên thẻ Git. Trong các ngữ cảnh hỗn hợp (Java, Node, v.v.), có thể đơn giản hơn nếu sử dụng một tập lệnh bash tùy chỉnh để tính toán phiên bản tiếp theo (ví dụ: sử dụng SemVer), tạo thẻ, đẩy nó lên kho lưu trữ từ xa và tạo bản phát hành tương ứng.
Các công cụ như git-cliff Chúng giúp tạo ra nhật ký thay đổi. Dựa trên thông báo commit, các thay đổi được phân loại theo loại (tính năng, sửa lỗi, thay đổi gây lỗi tương thích ngược, v.v.). Việc tích hợp chúng vào quy trình phát triển đảm bảo mỗi bản phát hành đều đi kèm với nhật ký thay đổi rõ ràng mà không cần ai phải viết thủ công.
Để công bố các hiện vật, cần kết hợp các hành động và thông tin xác thực phù hợp.Các kho lưu trữ Docker (Docker Hub, GitHub Container Registry, Artifact Registry), kho lưu trữ Maven, kho lưu trữ npm, v.v. Một lần nữa, thông tin xác thực được lưu trữ dưới dạng bí mật và chỉ được đưa vào các tác vụ khi cần thiết.
Triển khai liên tục lên Kubernetes, GCP, Kinsta và các môi trường khác.
Triển khai là giai đoạn mà CI/CD tương tác với cơ sở hạ tầng.Tại đây, GitHub Actions tích hợp liền mạch với hầu hết mọi nền tảng: Kubernetes, App Engine, Cloud Functions, máy chủ truyền thống, các nền tảng như Kinsta, v.v.
Đối với Kubernetes (ví dụ như trong GKE), mô hình thông thường là... Cụ thể là: xác thực với Google Cloud (sử dụng các thao tác chính thức), cấu hình kubectl Trong ngữ cảnh cụm, hãy áp dụng các tệp cấu hình hoặc biểu đồ Helm và, nếu cần, thực hiện triển khai có kiểm soát (ví dụ: với canary hoặc blue-green) và xác minh trạng thái bằng các lệnh từ kubectl rollout status.
Trong trường hợp của App Engine hoặc Cloud FunctionsQuy trình này xây dựng hình ảnh hoặc sản phẩm, xuất bản chúng lên Kho lưu trữ sản phẩm, và sau đó gọi các lệnh triển khai. gcloud phù hợp, một lần nữa sử dụng thông tin xác thực được quản lý như bí mật và trình chạy tạm thời.
Khi quá trình triển khai được thực hiện thông qua các API bên ngoài như của Kinsta.Thông thường chỉ cần một bước là đủ. curl hoặc một hành động chuyên biệt gửi yêu cầu kèm theo mã xác thực và các tham số cần thiết (ID ứng dụng, nhánh, v.v.). Công việc được coi là thành công nếu API phản hồi chính xác yêu cầu phát hành mới.
Việc triển khai hầu như luôn đi kèm với thông báo. Thông tin này được gửi đến Slack, Teams hoặc các công cụ giao tiếp khác, cho biết dịch vụ nào đã được triển khai, trong môi trường nào, với phiên bản nào, ai đã kích hoạt nó và các liên kết đến nhật ký quy trình làm việc. Trong môi trường sản xuất, điều này cũng phục vụ cho việc kiểm toán và truy vết.
Kiểm soát chất lượng: bảo mật, giám sát và nhật ký
Tự động hóa quá trình xây dựng và triển khai rất tuyệt, nhưng thiếu khả năng giám sát thì không hiệu quả. Về những gì đang xảy ra, quy trình này có thể trở thành một hộp đen. GitHub Actions cung cấp nhật ký chi tiết theo từng lần thực thi, từng tác vụ và từng bước, cho phép bạn chẩn đoán các lỗi biên dịch, kiểm thử hoặc triển khai.
Đối với các nhu cầu phức tạp hơn, các dịch vụ giám sát bên ngoài được tích hợp. Chẳng hạn như Datadog, New Relic hoặc Splunk, thu thập các số liệu về quy trình làm việc, thời gian thực thi, tỷ lệ lỗi, v.v., giúp phát hiện các điểm nghẽn và ưu tiên tối ưu hóa quy trình.
Đồng thời, an ninh đóng vai trò then chốt.Quản lý các bí mật được mã hóa, chính sách truy cập tối thiểu cần thiết, xem xét quyền thực thi, tích hợp các công cụ quét lỗ hổng mã và các thành phần phụ thuộc (quét mã, quét bí mật, OWASP, v.v.) vào chính quy trình làm việc.
Nhiều nhóm cũng bổ sung thêm bước kiểm thử sau khi triển khai. Trong môi trường được cập nhật mới: các bài kiểm tra chức năng đầu cuối, kiểm tra hiệu năng, các bài kiểm tra sơ bộ và, nếu có lỗi xảy ra, các cơ chế khôi phục tự động sẽ đưa hệ thống trở lại phiên bản ổn định trước đó.
Quản trị quy trình làm việc: nhánh được bảo vệ và yêu cầu kéo (pull request)
Cách thức làm việc với các nhánh và yêu cầu kéo phải phù hợp với quy trình CI/CD. để mọi thứ đều hợp lý. Điều phổ biến nhất là bảo vệ nhánh chính (main o mastervà yêu cầu mọi thay đổi phải thông qua quy trình PR và vượt qua các bước kiểm tra trong quy trình.
GitHub cho phép bạn định nghĩa các quy tắc bảo vệ nhánh. Các chính sách này buộc phải sử dụng pull request, chặn commit trực tiếp và yêu cầu một số kiểm tra trạng thái nhất định (quy trình Action cụ thể) phải đạt trạng thái "xanh" trước khi cho phép hợp nhất. Chúng cũng có thể yêu cầu số phiên bản tối thiểu, quy tắc phê duyệt, v.v.
Mô hình này đảm bảo rằng mã nguồn được đưa vào sản xuất là mã nguồn hợp lệ. Nó đã trải qua quá trình xem xét của con người và tất cả các bước kiểm tra tự động trong quy trình, giảm đáng kể nguy cơ bỏ sót các lỗi hoặc lỗ hổng nghiêm trọng.
Trong các công ty có nhiều môi trường khác nhau Việc triển khai (phát triển, dàn dựng, sản xuất) lên môi trường sản xuất thường chỉ dành cho việc hợp nhất vào nhánh chính, trong khi các nhánh khác có thể kích hoạt việc triển khai lên các môi trường trước đó để thử nghiệm nội bộ hoặc trình diễn.
Nhìn tổng thể, một quy trình CI/CD được thiết kế tốt với GitHub Actions sẽ mang lại hiệu quả cao. Nó trở thành xương sống của quá trình phát triển: tích hợp các thay đổi, chạy các bộ kiểm thử toàn diện, xây dựng và xuất bản các thành phần, triển khai lên nhiều nền tảng đám mây, giám sát bằng các công cụ quan sát và quản lý thông qua các quy tắc phân nhánh và yêu cầu kéo rõ ràng. Với các quy trình làm việc có thể tái sử dụng, các hành động tùy chỉnh, các công cụ hỗ trợ như Task, Rease Action và Git Cliff, cùng với khả năng quản lý bí mật và quyền hạn mạnh mẽ, bạn có thể hỗ trợ mọi thứ từ các ứng dụng Python đơn giản đến các kiến trúc Kubernetes phức tạp, duy trì tốc độ phân phối, chất lượng mã và bảo mật mà không làm quá tải nhóm với các tác vụ thủ công.