GitHub Actions đang từ từ giết chết các đội ngũ kỹ thuật
(iankduncan.com)- Được dùng rất rộng rãi vì là CI mặc định có sẵn trong repo, nhưng sự kém hiệu quả mang tính cấu trúc và trải nghiệm người dùng thiếu ổn định đang làm giảm năng suất của lập trình viên
- Trình xem log tải chậm và gây sập trình duyệt, cùng với cú pháp YAML phức tạp và lỗi biểu thức dẫn đến việc debug lặp đi lặp lại
- Do mô hình không sở hữu tài nguyên compute, công cụ này bộc lộ giới hạn về hiệu năng, khả năng mở rộng và mức độ kiểm soát môi trường
- Trong nỗ lực lách qua nhiều vấn đề, các nhóm lại liên tục rơi vào cảnh tái tạo chính CI bằng YAML phức tạp hoặc các script Bash khổng lồ
- Trái lại, Buildkite mang đến một lựa chọn CI bền vững hơn về lâu dài nhờ cấu trúc YAML đơn giản, agent có thể self-host và trải nghiệm log thực dụng
Vấn đề của GitHub Actions
- Trình xem log của GitHub Actions rất kém hiệu quả: ngay cả việc kiểm tra một lỗi đơn giản cũng cần nhiều bước bấm và tải trang
- Khi build thất bại, phải đi qua trang tóm tắt check → trang chạy workflow → trang job → bấm vào step đang bị thu gọn, tức là cần 3–4 lần chuyển trang, và mỗi lần đều có tải riêng
- Liên tục làm sập trình duyệt với log build dung lượng lớn, và hiện tượng Chrome bị treo khi dùng tìm kiếm có thể tái hiện được
- Với log dài, việc cuộn còn có lúc không hoạt động, cuối cùng dẫn tới tình huống phải tải artifact log thô về rồi mở bằng trình soạn thảo văn bản
- Nút quay lại đưa người dùng đến một trang UI GitHub Actions khó đoán thay vì trang PR ban đầu, khiến lịch sử trình duyệt bị lấp đầy bằng các URL của Actions
- Quy trình debug kém năng suất
- Để kiểm tra biến môi trường, phải thêm step
run: envrồi push lại, tạo ra vòng phản hồi 20 phút; với chỉ một thay đổi một dòng, quá trình này có thể lặp lại cả chục lần - Các vòng phản hồi 20 phút cứ lặp đi lặp lại, khiến một ngày làm việc bị tiêu tốn vào việc chờ CI
- Để kiểm tra biến môi trường, phải thêm step
- Giới hạn mang tính cấu trúc của YAML
- YAML của GitHub Actions là một dạng đặc thù kết hợp ngôn ngữ biểu thức riêng, mô hình đối tượng context và quy tắc nội suy chuỗi
- Chỉ cần dùng sai một dấu nháy trong biểu thức
${{ }}, bạn sẽ phải đợi tới 4 phút runner khởi động xong mới phát hiện chuỗi đã bị hỏng - Cú pháp biểu thức tồn tại trong một vùng lưng chừng (liminal space): quá phức tạp để viết dưới dạng cấu hình, nhưng lại quá hạn chế để xem như ngôn ngữ lập trình chính thức
- Đây là kiểu hệ thống mà người dùng học cú pháp không phải qua tài liệu mà qua các lần thất bại
- Rủi ro bảo mật của Marketplace
- Khi gọi action bên ngoài bằng cú pháp
uses:, bạn đang trao quyền truy cập vào repo, secret và môi trường build cho bên thứ ba chưa được kiểm chứng - Có thể pin SHA, nhưng gần như chẳng mấy ai làm; mà kể cả có pin thì cấu trúc vẫn là chạy một đoạn mã mờ đục chưa từng đọc cùng với quyền truy cập
GITHUB_TOKEN - Marketplace trộn lẫn các action do cộng đồng duy trì với chất lượng rất khác nhau, đa số được cấu thành từ shell script và Dockerfile
- Việc quản lý phụ thuộc thiếu minh bạch và có khả năng thực thi mã không an toàn
- Khi gọi action bên ngoài bằng cú pháp
- Ràng buộc của môi trường compute
- Runner mặc định của GitHub Actions là runner dùng chung thuộc sở hữu Microsoft, chậm, tài nguyên hạn chế và gần như không thể tùy biến có ý nghĩa
- Chi phí cho runner lớn hơn đủ để khiến đội tài chính gửi một lời mời họp kiểu “chúng ta cần nói chuyện”, mà ngay cả như vậy bạn vẫn không kiểm soát được môi trường
- Có ít nhất 6 startup như Namespace, Blacksmith, Actuated, Runs-on, BuildJet chuyên chỉ giải quyết chuyện runner GitHub Actions quá chậm, và riêng điều đó đã chứng minh môi trường compute mặc định đang thiếu sót
- Thiết lập self-hosted runner có thể giải quyết bài toán compute, nhưng các vấn đề còn lại như biểu thức YAML, mô hình quyền, Marketplace hay trình xem log vẫn y nguyên
Những vấn đề nhỏ nhưng tích lũy theo thời gian
actions/cache: khóa cache gây khó hiểu, cache miss xảy ra âm thầm, việc eviction cache thiếu minh bạch, khiến thời gian tiêu tốn để debug cache còn nhiều hơn thời gian nó tiết kiệm được- Workflow tái sử dụng: không thể lồng quá sâu, không thể truy cập gọn gàng vào context của workflow gọi, và không thể test một cách cô lập
- Mô hình quyền của
GITHUB_TOKEN:permissions: write-allthì quá rộng, còn các quyền chi tiết lại có tương tác mê cung giữa cấu hình cấp repository, workflow và job - Điều khiển đồng thời (concurrency): có thể hủy các lần chạy đang diễn ra trên cùng một nhánh chỉ với một dòng, nhưng không hỗ trợ mức kiểm soát tinh vi hơn thế
- Không thể dùng secret trong điều kiện
if: không thể chạy điều kiện kiểuif: secrets.DEPLOY_KEY != ''; điều này hợp lý về bảo mật, nhưng khi viết workflow phải chạy được cho cả fork lẫn repo chính thì lại cần các cách lách
Cái bẫy của tư duy “cứ dùng Bash script đi”
- Những kỹ sư đã mệt mỏi với YAML CI thường bị cám dỗ thay mọi thứ bằng script bash trong
run:, nhưng theo thời gian sẽ xuất hiện thêm điều kiện, hàm, parse tham số và xử lý song song - Ba tháng sau, bạn có thể có 800 dòng bash đang tái hiện song song hóa job bằng
waitvà file PID, cùng với logic retry và parse output tự chế - Kết quả là không phải bạn đã thoát khỏi hệ thống CI, mà là tự viết ra một hệ thống CI còn tệ hơn bằng bash, không có framework test và không ai theo nổi
- Bash phù hợp làm công cụ keo dán (glue), nhưng nếu dùng làm hệ thống build hay test harness thì đó là hành động chuyển độ phức tạp từ nơi có lan can bảo vệ sang nơi không có gì bảo vệ
Cách tiếp cận thay thế của Buildkite
-
Trình xem log ổn định
- Trình xem log của Buildkite hiển thị log đúng cách mà không làm sập trình duyệt, đồng thời render nguyên vẹn màu ANSI và định dạng từ các framework test
- Với tính năng Annotation, step build có thể in trực tiếp Markdown lên trang build như phần tóm tắt lỗi test, báo cáo coverage hay link deploy
- Vì agent chạy trên hạ tầng riêng của bạn, nên có thể SSH vào máy build để debug trực tiếp
-
Cấu trúc YAML đơn giản
- YAML của Buildkite là một cấu trúc dữ liệu thuần túy mô tả pipeline, chỉ khai báo step, command và plugin
- Nếu cần logic thực sự, hãy viết script bằng một ngôn ngữ lập trình thực thụ có thể chạy cục bộ
- Cách tiếp cận này giữ ranh giới rõ ràng giữa “điều phối nằm trong cấu hình, logic nằm trong mã”, đúng là ranh giới mà GitHub Actions thường làm mờ đi
-
Quyền kiểm soát hoàn toàn với môi trường compute
- Agent của Buildkite là một binary đơn lẻ, có thể chạy ở cloud riêng, on-premise hay trên phần cứng tùy chỉnh
- Bạn có thể kiểm soát hoàn toàn loại instance, caching, lưu trữ cục bộ và mạng; từ EC2 cỡ lớn với ổ NVMe và Docker layer cache 20GB cho tới cả Raspberry Pi đều được hỗ trợ
- Không tồn tại cả một ngành bên thứ ba kiểu “Buildkite nhưng nhanh hơn”; bạn chỉ cần chạy máy lớn hơn là xong
- Với cá nhân duy trì các thư viện mã nguồn mở nhỏ, free tier cho repo public của GitHub Actions vẫn có giá trị
- Đối tượng chính của bài viết này là các đội đang vận hành hệ thống production, nơi thời gian CI được đo bằng số giờ kỹ thuật bị mất mỗi tuần, và một build 45 phút tạo ra chi phí cả về compute lẫn nhân công
- Trong những môi trường như vậy, overhead vận hành agent Buildkite nhanh chóng hoàn vốn
-
Hỗ trợ pipeline động
- Trong Buildkite, step pipeline là dữ liệu, và script có thể tạo (emit) thêm step động ở runtime rồi upload lên
- Trong monorepo, cách này cho phép tạo chính xác chỉ các step build/test cần thiết dựa trên file đã thay đổi, không cần matrix hard-code hay mớ spaghetti
if: contains(...) matrix, điều kiệnifvà workflow tái sử dụng của GitHub Actions cố gắng xấp xỉ điều này, nhưng cuối cùng lại thành một cỗ máy Rube Goldberg được xây bằng ngôn ngữ khai báo thiếu sức biểu đạt
-
Sự đơn giản trong cấu trúc plugin
- Về cấu trúc, nó cũng giống GitHub Actions Marketplace ở chỗ lấy mã từ repo bên thứ ba
- Điểm khác là plugin của Buildkite thường là các shell hook mỏng chứ không phải Docker image, nên bề mặt nhỏ hơn và có thể đọc toàn bộ trong vài phút
- Vì chạy trên hạ tầng của chính bạn, nên blast radius có thể do người dùng tự kiểm soát
-
Các chi tiết tính năng xoay quanh trải nghiệm người dùng
- Buildkite cho phép hiển thị emoji tùy chỉnh (
:parrot:,:docker:v.v.) cạnh các step pipeline; nghe có vẻ nhỏ nhặt nhưng cho thấy sự chăm chút với trải nghiệm sử dụng sản phẩm - GitHub Actions trông như sản phẩm được thiết kế bởi một ủy ban, chưa từng tự hỏi “dùng cái này có vui không?”
- Buildkite cho phép hiển thị emoji tùy chỉnh (
Kết luận: Tiêu chí chọn hệ thống CI
- GitHub Actions đã chiếm lĩnh thị trường nhờ lợi thế được tích hợp sẵn (default): miễn phí cho repo public, tích hợp ngay trong nền tảng mà ai cũng đang dùng, và đạt mức “đủ dùng” (Good Enough)
- Nó giống như Internet Explorer của CI: chi phí chuyển đổi là thứ rất thực tế và thời gian thì hữu hạn, nên mọi người vẫn tiếp tục dùng
- Buildkite vượt trội hơn về khả năng dùng bền vững lâu dài và trải nghiệm lập trình viên
- Với các dự án mã nguồn mở đơn giản, GitHub Actions là đủ, nhưng trong môi trường production quy mô lớn, Buildkite phù hợp hơn
- Trong lịch sử các hệ thống CI, thứ giành thị phần không phải là hệ thống tốt nhất, mà là hệ thống dễ bắt đầu nhất
- GitHub Actions là CI dễ bắt đầu nhất, còn Buildkite là CI tốt nhất để tiếp tục dùng, và về lâu dài điều thứ hai mới quan trọng
- Nếu cấu trúc của công cụ CI đang bào mòn thời gian của lập trình viên, thì vấn đề không nằm ở lập trình viên mà nằm ở chính công cụ
3 bình luận
Có vẻ vấn đề nằm ở chỗ bản thân CI ngày càng trở nên phức tạp.
Có vẻ như cùng một bài đã được đăng lên hai lần. Nhưng dạo này trông cũng như một sự kết hợp khá ổn để AI dựng nên..
Ý kiến trên Hacker News
Tôi đã dùng nhiều hệ thống CI. Tôi dùng CircleCI và GitHub Actions khá nhiều, nhưng kết luận của tôi khác tác giả
Trước đây Jenkins gần như dành cho Java, Travis gần như dành cho Rails, nhưng những CI chuyên biệt kiểu đó cuối cùng đều đi vào ngõ cụt. Giờ đây CI đã tiến hóa thành một trình điều phối workflow đơn thuần
Lý do tôi chuyển từ CircleCI 2 sang GitHub Actions cũng là vì CircleCI không xử lý tốt sự chuyển đổi này. GHA đủ sức biểu đạt
Những thứ như trình xem log hay cú pháp YAML chỉ là vấn đề nhỏ. Điều quan trọng là quyền sở hữu tài nguyên tính toán và pipeline động, trong đó cái đầu tiên CI nào cũng làm được, còn cái sau là điểm mạnh của Buildkite
Kết luận của tôi là Actions thực tế khá ổn, và nếu lập công ty mới thì tôi sẽ dùng Buildkite, còn với mã nguồn mở thì dùng Actions
Nếu không hiểu đồ thị build, bạn sẽ phải duy trì trạng thái build tăng dần, và điều đó gây ra bug thoáng qua. Vì vậy cần những hệ thống hiểu rất sâu cấu trúc build như UnrealEngine Horde hay UBA
Nếu dùng CI tổng quát, một bản build có thể mất hơn một ngày
Tôi thường chỉ dùng những phần tốt của GHA. Ví dụ, GitHub rất tuyệt với vai trò bộ điều phối sự kiện, nhưng lại không tốt lắm với vai trò workflow orchestrator, nên tôi giao phần đó cho hệ thống khác
Nếu log bạn phải xem hàng chục lần mỗi ngày lại gây khó chịu thì năng suất sẽ giảm. Đọc log thô và phải tự bỏ qua các escape code thực sự rất đau khổ
Tôi giữ mọi thứ đơn giản. Tôi đưa toàn bộ orchestration vào script deploy.sh và chạy nó trên máy Mac cục bộ hoặc AWS CodeBuild
YAML chỉ có đúng một dòng
bash deploy.sh. Chỉ cần có container Docker thì nó sẽ chạy giống nhau ở bất cứ đâu, từ Azure đến GitHub ActionsChiến lược cốt lõi của mọi môi trường CI là có một hệ thống build giống hệt local
Tôi luôn bắt đầu bằng Makefile. Docker, CI build, linting, mọi thứ đều do Makefile điều khiển. Khi dự án lớn hơn thì có thể chuyển sang công cụ khác, nhưng nền tảng ban đầu là một công cụ kích hoạt duy nhất
Tôi dùng Fastlane khá nhiều cho mobile, vì nó giảm boilerplate và cung cấp cấu trúc. Cuối cùng nó vẫn là Ruby nên nếu cần thì vẫn thoát ra được
(liên kết tài liệu GNU Make)
Chung quy lại thì đây vẫn là kiểu “cứ viết Bash script đi”, chỉ là thêm vào một lớp phức tạp không cần thiết
Ở công ty hiện tại của tôi cũng vậy, đến lúc không còn thể chạy toàn bộ pipeline trên local nữa thì sẽ xuất hiện một hạ tầng CI khổng lồ, nơi để test một MR mà phải chạy build đến 10 lần
Tôi thấy bài này giống quảng cáo cho Nix/Buildkite
CI chỉ cần ở mức chạy script hay target build là đủ. CI nên chỉ cung cấp môi trường và cấu hình, còn logic phải nằm trong code
Làm vậy sẽ có được tính độc lập với CI, giúp dễ chuyển đổi giữa các hệ thống
GitLab CI xử lý kiểu phức tạp này khá tốt. Tính năng template và cấu hình job của nó rất xuất sắc, nhưng debug thì khó, và logic điều kiện đôi khi lại hỏng theo cách khó lường
.shthì việc di chuyển cực kỳ dễNgược lại, những team phân nhỏ mọi thứ trong UI thì đã rất vất vả
Vấn đề không phải CI/CD tự thân, mà là văn hóa lập trình bằng file cấu hình
Vòng lặp
git commit -m "try fix"rồi chờ 10 phút là quá phổ biến. Môi trường CI có thể tái hiện trên local vẫn còn thiếu trầm trọngNếu thiết lập cách ly môi trường như một chính sách thì dùng công cụ nào cũng không thành vấn đề. Cuối cùng, điều cốt lõi là sự hài hòa giữa công cụ và phương pháp luận
actgiúp tái hiện CI trên local rất nhiềuTiêu đề kiểu “giết chết team kỹ sư” là cường điệu. GitHub Actions đủ tốt
Tôi thích nó hơn Bitbucket hay GitLab
Gần đây độ tin cậy của GitHub giảm mạnh
actions/checkoutcó thể fail vô cớ, job release chạy hai lần, hoặc có lúc chỉ ngồi chờ suốt 40 phútTôi đã dùng nó nhiều năm nhưng độ ổn định cơ bản đang đi xuống. Thật tiếc vì tôi đã bỏ lỡ Buildkite
GitHub Actions là một trong những công cụ CI tệ nhất tôi từng dùng (ngang hàng Jenkins)
Trong khi đó Buildkite là tốt nhất. Nhờ pipeline động, bạn có thể tự động tạo step retry khi test fail, hoặc điều chỉnh test song song tùy theo thay đổi mã nguồn
Khả năng dùng cấu hình máy khác nhau cho từng job CI cũng là một ưu điểm lớn. Rất đáng để khuyến nghị
Những người cổ súy cho “CI đơn giản dựa trên script” có lẽ chưa từng có kinh nghiệm với các dự án thực tế ở quy mô lớn