Vấn đề: Nỗi ám ảnh “Works on my machine, but fails in CI”
Hầu như lập trình viên nào cũng từng nếm trải cảm giác này. Bạn push code lên GitHub, đợi 5 phút để CI chạy, rồi nhận gáo nước lạnh vì lỗi lặt vặt. Bạn sửa một dòng YAML, push lại, và cầu nguyện. Vòng lặp này có khi lặp lại đến 20 lần chỉ để cấu hình xong một script đơn giản.
Vấn đề cốt lõi nằm ở công cụ. GitHub Actions, GitLab CI hay Jenkins đều dựa trên YAML — một định dạng dữ liệu, không phải ngôn ngữ lập trình. Khi hệ thống phình to, file YAML trở thành một “mớ bòng bong” cực kỳ khó debug. Nguy hiểm hơn, bạn gần như không thể chạy thử chính xác cái pipeline đó ở máy cá nhân. Mọi thứ phụ thuộc hoàn toàn vào hạ tầng Cloud của nhà cung cấp.
Tại sao YAML CI/CD lại khiến chúng ta mệt mỏi?
- Thiếu tính trừu tượng: YAML không có hàm, không có kiểu dữ liệu (types). Việc xử lý logic rẽ nhánh phức tạp trong YAML giống như đang viết code bằng tay trái.
- Môi trường lệch pha: Runner trên Cloud thường dùng OS và thư viện khác hẳn máy local. Kết quả là code chạy mượt ở máy mình nhưng lại lăn đùng ra chết khi deploy.
- Phản hồi chậm: Mỗi thay đổi nhỏ đều yêu cầu commit và push. Bạn tốn hàng giờ chỉ để chờ đợi runner khởi động.
Dagger: Đưa CI/CD về đúng bản chất là Mã Nguồn
Dagger thay đổi hoàn toàn cuộc chơi. Thay vì viết YAML, bạn viết code bằng chính ngôn ngữ mình thông thạo như Python, Go hay TypeScript. Dagger engine sử dụng Docker (thông qua BuildKit) để thực thi các bước build. Điều này đảm bảo dù bạn chạy ở máy local hay trên Jenkins, kết quả luôn đồng nhất 100%.
Trong một dự án microservices với hơn 30 container tôi từng quản lý, việc chuyển sang Dagger mang lại hiệu quả tức thì. Thời gian build trung bình giảm từ 15 phút xuống còn chưa đầy 5 phút. Cơ chế caching thông minh của BuildKit giúp chúng tôi tiết kiệm tới 40% tài nguyên hệ thống.
Cài đặt Dagger trên máy cá nhân
Trước tiên, hãy đảm bảo máy bạn đã cài Docker. Sau đó, cài đặt Dagger CLI để điều phối việc thực thi code.
# Cài đặt cho Linux/macOS
curl -L https://dl.dagger.io/dagger/install.sh | sh
sudo mv bin/dagger /usr/local/bin/dagger
# Kiểm tra cài đặt thành công
dagger version
Khởi tạo Dagger ngay trong thư mục dự án bằng lệnh:
dagger init
Cấu hình Pipeline bằng Python SDK
Hãy xem cách Dagger thay thế file .github/workflows/ci.yml truyền thống. Thay vì khai báo YAML, chúng ta tạo file ci/main.py để quản lý các bước: Cài đặt -> Unit Test -> Build Image.
import sys
import anyio
import dagger
async def main():
# Khởi tạo Dagger client
async with dagger.connection(dagger.Config(log_output=sys.stderr)) as client:
# Lấy thư mục gốc
src = client.host().directory(".")
# Định nghĩa môi trường Python 3.11
python_base = (
client.container()
.from_("python:3.11-slim")
.with_directory("/app", src)
.with_workdir("/app")
)
# Bước 1: Cài đặt dependencies
runner = (
python_base
.with_exec(["pip", "install", "-r", "requirements.txt"])
)
# Bước 2: Chạy Unit Test
print("Đang chạy tests...")
test_result = await runner.with_exec(["pytest", "tests/"]).stdout()
print(test_result)
# Bước 3: Build Image cuối cùng
image = runner.with_entrypoint(["python", "main.py"])
if __name__ == "__main__":
anyio.run(main)
Sức mạnh nằm ở cơ chế Caching
Dagger xử lý từng lệnh with_exec trong các container tạm thời. Nếu file requirements.txt không thay đổi, Dagger sẽ tái sử dụng cache ngay lập tức. Những lần chạy sau, bước cài đặt thư viện nặng nề sẽ chỉ tốn vài giây thay vì vài phút như trước.
Debug cục bộ: Không còn phải “đoán mò”
Đây là tính năng đáng giá nhất. Để chạy thử pipeline, bạn chỉ cần gõ lệnh terminal ngay tại máy mình:
python ci/main.py
Toàn bộ log hiển thị trực tiếp. Nếu bước test thất bại, bạn sửa code và chạy lại ngay, không cần git push. Dagger còn hỗ trợ chế độ tương tác (interactive mode). Khi một bước bị lỗi, bạn có thể nhảy thẳng vào bên trong container đó để kiểm tra file hệ thống, giống như đang dùng docker exec.
# Truy cập trực tiếp vào môi trường lỗi để kiểm tra
dagger call test --interactive
Triển khai lên Cloud (GitHub Actions/GitLab CI)
Khi pipeline đã chạy ổn định ở local, việc đưa lên Cloud trở nên cực kỳ nhẹ nhàng. Lúc này, GitHub Actions chỉ đóng vai trò là một máy chạy (runner) đơn thuần. Logic nghiệp vụ đã nằm gọn trong code Python.
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Dagger Pipeline
uses: dagger/dagger-for-github@v5
with:
verb: run
args: python ci/main.py
Cách tiếp cận này giúp bạn thoát khỏi sự ràng buộc của nhà cung cấp. Nếu mai này team muốn chuyển sang GitLab CI, bạn chỉ cần copy file Python sang là xong. Không cần học lại cú pháp YAML, không cần sửa đổi logic.
Đúc kết kinh nghiệm thực tế
Sau hơn một năm đưa Dagger vào production, tôi rút ra ba quy tắc vàng:
- Chia nhỏ để cache: Đừng gộp quá nhiều lệnh vào một
with_exec. Càng chia nhỏ, khả năng tái sử dụng cache của Dagger càng hiệu quả. - Quản lý Secret an toàn: Tuyệt đối không hardcode mật khẩu. Hãy tận dụng hàm
client.set_secret()để bảo mật thông tin nhạy cảm. - Local First: Luôn đảm bảo pipeline pass ở máy cá nhân trước khi push. Thói quen này giúp lịch sử commit sạch đẹp và tiết kiệm thời gian cho cả team.
Dagger không đơn thuần là một công cụ mới. Nó đại diện cho tư duy: CI/CD is software. Khi coi pipeline là phần mềm, bạn có quyền dùng unit test, linter và trình debug để kiểm soát nó. Hãy giải phóng bản thân khỏi những file YAML khô khan ngay hôm nay.

