Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn đang làm việc trong một đội phát triển sản phẩm có 8 lập trình viên. Mỗi người ngồi một góc, viết code cho tính năng riêng của mình suốt hai tuần. Đến ngày cuối cùng của sprint, tất cả cùng cố gắng gộp (merge) code lại với nhau. Kết quả? Hàng trăm dòng xung đột (conflict), tính năng của người này làm hỏng tính năng của người kia, và cả đội thức trắng đêm để "ráp" mọi thứ chạy được. Trong ngành, người ta gọi đây là "integration hell" — địa ngục tích hợp.
CI/CD ra đời chính là để giải quyết nỗi đau này. Đây không phải một công cụ, cũng không phải một sản phẩm bạn mua về cài đặt. CI/CD là một tư duy làm việc kết hợp với quy trình tự động hóa, giúp đội của bạn giao phần mềm nhanh hơn, an toàn hơn và ít căng thẳng hơn rất nhiều.
Bài học đầu tiên này đặt nền móng cho toàn bộ khóa học. Chúng ta sẽ hiểu bản chất CI/CD là gì, tại sao nó thay đổi cách các công ty phần mềm vận hành, và làm quen với GitHub Actions — công cụ CI/CD phổ biến nhất hiện nay, đặc biệt với các đội nhỏ và vừa tại Việt Nam. Khi nắm chắc nền tảng này, những bài sau về deployment, testing, security sẽ trở nên dễ tiếp thu hơn rất nhiều.
Khái niệm cốt lõi
CI — Continuous Integration (Tích hợp liên tục)
CI là thực hành mà các lập trình viên hợp nhất code của mình vào một nhánh chung thường xuyên — lý tưởng là nhiều lần mỗi ngày, tối thiểu là mỗi ngày một lần. Mỗi lần code được đẩy lên (push), một hệ thống tự động sẽ:
- Build — biên dịch/đóng gói lại toàn bộ dự án để chắc chắn code mới không làm hỏng quá trình dựng ứng dụng.
- Test — chạy các bộ kiểm thử tự động (unit test, integration test) để phát hiện lỗi ngay lập tức.
- Phản hồi — báo cho lập trình viên biết code của họ "xanh" (pass) hay "đỏ" (fail) trong vài phút.
Có một nguyên tắc kinh điển đi kèm CI: "Fail fast" — thà thất bại sớm còn hơn thất bại muộn. Một lỗi được phát hiện 3 phút sau khi bạn viết ra sẽ tốn vài phút để sửa, vì bạn vẫn nhớ rõ mình vừa làm gì. Cũng lỗi đó nhưng bị phát hiện sau hai tuần, khi đã có 20 commit khác chồng lên, có thể tốn cả ngày để truy tìm.
CD — hai cách hiểu
Chữ "CD" trong CI/CD có hai nghĩa, và người mới rất hay nhầm. Ở bài này chúng ta chỉ giới thiệu tổng quan (bài 4 sẽ mổ xẻ chi tiết sự khác biệt):
- Continuous Delivery (Phân phối liên tục): Sau khi CI chạy xong và code "xanh", phần mềm được đóng gói thành một phiên bản sẵn sàng để phát hành bất cứ lúc nào. Việc bấm nút đưa lên production vẫn do con người quyết định.
- Continuous Deployment (Triển khai liên tục): Đi xa hơn một bước — nếu mọi kiểm thử đều pass, code sẽ tự động lên thẳng production mà không cần ai bấm nút.
Pipeline — đường ống dẫn code
Toàn bộ chuỗi các bước tự động từ lúc lập trình viên push code cho đến khi ứng dụng chạy được, người ta gọi là pipeline (đường ống). Một pipeline điển hình gồm các "trạm": lấy code → cài đặt phụ thuộc → lint (kiểm tra chuẩn code) → test → build → (tùy chọn) deploy. Code chảy qua từng trạm như nước chảy qua đường ống, và chỉ đi tiếp khi trạm trước đó thành công.
GitHub Actions là gì?
GitHub Actions là nền tảng CI/CD được tích hợp sẵn ngay bên trong GitHub. Điều tuyệt vời là bạn không cần dựng thêm server riêng: khi có sự kiện xảy ra trên kho code (repository) — ví dụ ai đó push code hay mở pull request — GitHub sẽ tự động chạy pipeline mà bạn đã định nghĩa.
Bạn mô tả pipeline bằng một tệp YAML đặt trong thư mục .github/workflows/. Vài thuật ngữ nền tảng cần nhớ ngay từ đầu:
- Workflow: một quy trình tự động hoàn chỉnh, được viết trong một tệp YAML.
- Event (sự kiện): thứ kích hoạt workflow, ví dụ
push,pull_request. - Job (công việc): một nhóm các bước chạy trên cùng một máy ảo.
- Runner: máy chủ ảo (do GitHub cung cấp hoặc bạn tự dựng) thực thi job.
- Step (bước) & Action (hành động): từng lệnh nhỏ trong job. Action là các khối dựng sẵn có thể tái sử dụng.
Tình huống thực tế
Tình huống 1 — Đội startup fintech tại TP.HCM và "địa ngục tích hợp"
Một startup fintech tại quận 1, TP.HCM (gọi là công ty A) có 6 lập trình viên xây dựng ứng dụng ví điện tử. Trong 6 tháng đầu, họ không dùng CI. Quy trình của họ là: mỗi người code trên nhánh riêng, cuối tháng leader gộp tất cả lại rồi test thủ công trên máy của mình.
Kết quả sau nửa năm: trung bình mỗi lần release, đội mất 2–3 ngày chỉ để xử lý conflict và sửa các lỗi phát sinh khi ghép code. Có lần một tính năng chuyển tiền bị lỗi làm số dư hiển thị sai, nhưng vì test thủ công không bao quát hết, lỗi lọt lên production và 40 khách hàng phản ánh trong một buổi sáng.
Sau sự cố đó, họ áp dụng CI với GitHub Actions. Mỗi pull request bắt buộc phải chạy qua pipeline gồm lint + unit test + integration test, và chỉ được merge khi tất cả "xanh". Sau 3 tháng, con số họ đo được rất ấn tượng: thời gian chuẩn bị mỗi release giảm từ 2–3 ngày xuống còn dưới 2 giờ, và số lỗi lọt lên production giảm khoảng 70%.
Bài học: CI không làm code của bạn tự nhiên hết lỗi. Nó chỉ đảm bảo mọi lỗi mà máy có thể phát hiện được sẽ bị chặn lại trước khi đến tay khách hàng, và chặn sớm khi việc sửa còn rẻ.
Tình huống 2 — Netflix và văn hóa "commit nhỏ, deploy nhiều"
Ở quy mô lớn hơn, Netflix là ví dụ kinh điển. Các kỹ sư của họ deploy hàng nghìn lần mỗi ngày lên hệ thống phục vụ hàng trăm triệu người dùng. Điều gì cho phép họ làm được điều tưởng như điên rồ này mà không sập hệ thống liên tục?
Câu trả lời chính là văn hóa CI/CD nghiêm ngặt: mỗi thay đổi được giữ thật nhỏ, được kiểm thử tự động qua nhiều tầng, và pipeline sẽ tự động chặn bất cứ thay đổi nào không đạt chuẩn. Vì mỗi lần deploy chỉ chứa một thay đổi nhỏ, nếu có sự cố thì phạm vi ảnh hưởng cũng nhỏ và dễ xác định nguyên nhân — thay vì phải mò kim trong một release khổng lồ gộp 200 thay đổi.
Bài học: CI/CD không chỉ là công cụ, mà là văn hóa kỹ thuật. "Thay đổi nhỏ, tích hợp thường xuyên, tự động hóa mọi thứ có thể" — đây là triết lý đứng sau khả năng vận hành của những công ty phần mềm hàng đầu.
Tình huống 3 — Bạn Minh và dự án cá nhân đầu tiên có CI
Minh là một bạn sinh viên năm cuối ở Đà Nẵng, làm một dự án web bán hàng nhỏ bằng Node.js để đưa vào hồ sơ xin việc. Ban đầu Minh không nghĩ mình cần CI vì "dự án chỉ có mình tôi làm". Nhưng bạn ấy hay gặp một vấn đề: nhiều lần đẩy code lên GitHub xong, khi tải về máy khác để demo thì ứng dụng không chạy được vì thiếu bước cài đặt hoặc có lỗi cú pháp mà trên máy cá nhân không lộ ra.
Minh thêm một workflow GitHub Actions đơn giản: mỗi lần push, tự động chạy npm install và npm test. Chỉ vài dòng YAML. Ngay lập tức, GitHub gắn một dấu tick xanh hoặc dấu X đỏ vào mỗi commit. Nhờ đó Minh phát hiện được những commit bị hỏng ngay khi đẩy lên, thay vì lúc đang demo trước nhà tuyển dụng.
Bài học: CI không phải chỉ dành cho đội lớn. Ngay cả một dự án một người cũng hưởng lợi: nó là "người gác cổng" tự động, đảm bảo code trên nhánh chính luôn ở trạng thái chạy được.
Hướng dẫn từng bước
Hãy cùng dựng một pipeline CI đầu tiên cho một dự án Node.js bằng GitHub Actions. Đây là bản tối giản để bạn cảm nhận, các bài sau sẽ mở rộng.
Bước 1 — Chuẩn bị dự án. Đảm bảo dự án của bạn đã ở trên GitHub và trong package.json có sẵn một lệnh test, ví dụ:
"scripts": {
"test": "jest"
}
Bước 2 — Tạo thư mục workflow. Ở thư mục gốc dự án, tạo đường dẫn .github/workflows/. GitHub sẽ tự động nhận diện mọi tệp YAML đặt trong đây là một workflow.
Bước 3 — Viết tệp workflow. Tạo tệp .github/workflows/ci.yml với nội dung:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Lấy code về runner
uses: actions/checkout@v4
- name: Cài đặt Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Cài đặt phụ thuộc
run: npm ci
- name: Chạy kiểm thử
run: npm test
Hãy đọc tệp này như đọc một câu chuyện: "Đặt tên workflow là CI. Khi có push hoặc pull request vào nhánh main, hãy chạy một job tên build-and-test trên máy ảo Ubuntu. Job đó lấy code về, cài Node.js phiên bản 20, cài phụ thuộc, rồi chạy test."
Bước 4 — Đẩy code lên và quan sát. Commit tệp này và push lên GitHub. Vào tab Actions trên repository, bạn sẽ thấy workflow đang chạy theo thời gian thực, từng bước hiện ra với log chi tiết.
Bước 5 — Đọc kết quả. Nếu tất cả pass, commit sẽ được gắn dấu tick xanh. Nếu có bước fail, GitHub tô đỏ, gửi email cảnh báo, và bạn xem log để biết chính xác bước nào hỏng.
Bước 6 — Biến CI thành luật. Vào phần Settings → Branches, bật branch protection rule yêu cầu workflow CI phải pass thì mới cho merge vào main. Đây là bước biến CI từ "chạy cho vui" thành "người gác cổng thực sự".
Lỗi thường gặp & mẹo
- Merge quá thưa. Sai lầm phổ biến nhất không phải kỹ thuật mà là thói quen: giữ code trên nhánh riêng cả tuần rồi mới merge. Điều này làm mất hoàn toàn ý nghĩa của chữ "Continuous". Hãy merge nhỏ và thường xuyên.
- CI chạy nhưng không ai quan tâm khi nó "đỏ". Nếu đội bạn cho phép merge dù CI fail, hoặc để nhánh main "đỏ" nhiều ngày, thì pipeline chỉ còn là vật trang trí. Nguyên tắc vàng: nhánh main phải luôn xanh. Khi CI đỏ, sửa nó là ưu tiên số một.
- Test không đáng tin (flaky test). Có những test lúc pass lúc fail dù code không đổi. Chúng làm đội mất niềm tin vào CI và bắt đầu bấm "chạy lại" một cách máy móc. Hãy xử lý dứt điểm test flaky thay vì phớt lờ.
- Nhầm
npm installvớinpm citrong pipeline. Trong môi trường CI nên dùngnpm ci(viết tắt của "clean install") vì nó cài đặt chính xác theopackage-lock.json, đảm bảo môi trường build nhất quán và nhanh hơn.
- Mẹo — bắt đầu nhỏ. Đừng cố dựng một pipeline hoàn hảo 15 bước ngay từ đầu. Chỉ cần "checkout + install + test" là đã có giá trị. Bổ sung dần lint, build, security scan sau (những chủ đề của các bài tiếp theo).
- Mẹo — đọc log từ trên xuống. Khi pipeline fail, lỗi thật thường nằm ở bước đầu tiên báo đỏ, không phải bước cuối. Đừng hoảng khi thấy một loạt màu đỏ.
Bài tập thực hành
- Nhận diện khái niệm: Viết ra bằng lời của bạn sự khác nhau giữa Continuous Integration, Continuous Delivery và Continuous Deployment. Cho một ví dụ đời thực (không liên quan phần mềm) minh họa cho ý "thay đổi nhỏ, thường xuyên".
- Dựng pipeline đầu tiên: Tạo một repository mới trên GitHub với một dự án Node.js đơn giản (hoặc bất kỳ ngôn ngữ nào bạn quen). Viết một workflow GitHub Actions chạy test tự động mỗi khi push. Chụp lại màn hình tab Actions khi workflow chạy xong.
- Trải nghiệm "fail fast": Cố tình đẩy lên một commit làm hỏng test (ví dụ sửa một hàm cho trả về kết quả sai). Quan sát GitHub Actions báo đỏ và gửi email. Sau đó sửa lại và xác nhận nó chuyển xanh. Ghi lại cảm nhận của bạn về vòng phản hồi này.
- Suy ngẫm: Nhìn lại một dự án cũ của bạn (cá nhân hoặc ở công ty). Liệt kê ít nhất 2 tình huống mà nếu có CI thì đã tránh được rắc rối. Đây là cách tốt nhất để thấm giá trị của CI/CD.
Tóm tắt
- CI/CD là tư duy + tự động hóa, không phải một công cụ đơn lẻ. Cốt lõi là giao phần mềm nhanh, an toàn và giảm căng thẳng cho đội.
- Continuous Integration là thói quen merge code nhỏ và thường xuyên, mỗi lần merge đều được build và test tự động để phát hiện lỗi sớm (fail fast).
- CD có hai nghĩa: Continuous Delivery (luôn sẵn sàng phát hành, con người bấm nút) và Continuous Deployment (tự động lên thẳng production). Sự khác biệt này sẽ được đào sâu ở Bài 4.
- Pipeline là chuỗi các bước tự động mà code đi qua: checkout → install → lint → test → build → deploy.
- GitHub Actions cho phép dựng CI/CD ngay trong GitHub bằng tệp YAML trong
.github/workflows/, với các khái niệm nền: workflow, event, job, runner, step, action. - Ba bài học từ thực tế: CI chặn lỗi trước khi tới khách hàng (startup fintech), CI/CD là văn hóa cho phép deploy quy mô lớn (Netflix), và CI có ích cả với dự án một người (bạn Minh).