Cách tôi thiết lập CI/CD với GitHub Actions

Table of Contents
Tôi đã đến với CI/CD khá muộn. Nhiều năm liền tôi chạy thử nghiệm cục bộ, cầu nguyện, rồi đẩy code lên. Mọi thứ vẫn ổn cho đến một ngày tôi đẩy một commit làm hỏng bản build và mất cả tiếng đồng hồ để tìm ra thay đổi dependency nào đã gây ra lỗi đó.
GitHub Actions miễn phí cho các kho lưu trữ công khai và là lựa chọn rẻ nhất mà tôi tìm thấy cho các kho lưu trữ riêng tư. Đây là thiết lập tôi đã áp dụng sau khi chạy nó trên hàng chục dự án khác nhau — từ ứng dụng Next.js đến API Python và dịch vụ Go.
Tại sao lại là GitHub Actions mà không phải các lựa chọn khác?
Trước khi chọn một công cụ CI: GitHub Actions chạy trực tiếp trên kho lưu trữ của bạn, không cần kết nối dịch vụ bên thứ ba nào. Jenkins yêu cầu một máy chủ. CircleCI có các gói miễn phí tốt hơn cho một số trường hợp sử dụng. Travis CI đã chuyển sang trả phí hoàn toàn. GitLab CI rất tuyệt nếu bạn đã sử dụng GitLab.
Đối với các dự án được lưu trữ trên GitHub, Actions thắng thế về sự tiện lợi: không cần thiết lập webhook, không cần quản lý token, các trigger được định nghĩa ngay cạnh code của bạn.
Workflow đầu tiên của bạn
Các workflow của GitHub Actions nằm trong .github/workflows/ dưới dạng tệp YAML. Tạo .github/workflows/ci.yml:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run build
Đẩy code này lên và commit tiếp theo của bạn sẽ kích hoạt workflow. Mỗi PR sẽ có một dấu kiểm màu xanh lá cây hoặc một dấu X màu đỏ, và bạn sẽ không bao giờ hợp nhất code bị lỗi nữa.
npm ci so với npm install
Sử dụng npm ci trong CI, không phải npm install. npm ci nghiêm ngặt hơn: nó cài đặt chính xác những gì có trong package-lock.json, sẽ thất bại nếu có sự không khớp, và không bao giờ cập nhật tệp lockfile. Điều này có nghĩa là môi trường CI của bạn khớp với những gì bạn đã thử nghiệm cục bộ.
Bộ nhớ đệm giúp tăng tốc
Dòng cache: 'npm' ở trên lưu vào bộ nhớ đệm node_modules giữa các lần chạy dựa trên hàm băm của package-lock.json của bạn. Nếu không có nó, mỗi lần chạy workflow sẽ cài đặt lại tất cả các dependency từ đầu — mất 60–120 giây mỗi lần chạy tùy thuộc vào số lượng dependency của bạn.
Với bộ nhớ đệm, các lần truy cập bộ nhớ đệm giảm thời gian đó xuống còn 5–10 giây.
Đối với các hệ sinh thái khác:
# Python (pip)
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
# Python (uv)
- uses: astral-sh/setup-uv@v3
with:
enable-cache: true
# Go
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
# Rust
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
Bộ nhớ đệm được khóa bằng hàm băm của lockfile của bạn. Khi các dependency thay đổi, bộ nhớ đệm sẽ bỏ qua và xây dựng lại. Khi chúng không thay đổi, bộ nhớ đệm sẽ truy cập và CI sẽ nhanh chóng.
Kiểm thử song song
Đối với các bộ kiểm thử lớn hơn, hãy chia các kiểm thử trên nhiều runner:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --shard=${{ matrix.shard }}/4
Điều này chạy 4 job đồng thời, mỗi job xử lý một phần tư số kiểm thử của bạn. Tổng thời gian kiểm thử giảm từ 4× xuống còn ~1×.
Playwright hỗ trợ điều này nguyên bản với --shard. Đối với Jest, sử dụng --testPathPattern hoặc jest-runner-groups. Đối với pytest, sử dụng pytest-split.
Triển khai tự động
Tách CI (kiểm thử) khỏi CD (triển khai). Chúng có ngữ nghĩa lỗi khác nhau — một lỗi kiểm thử nên chặn một PR, nhưng một lỗi triển khai không nên chặn các PR khác.
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
- name: Deploy to Vercel
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
run: npx vercel --prod --token $VERCEL_TOKEN
Sử dụng Secrets, không phải Hardcoded Tokens
Không bao giờ đặt khóa API hoặc token triển khai trực tiếp vào tệp YAML của bạn. Lưu trữ chúng trong GitHub → Settings → Secrets and variables → Actions, sau đó tham chiếu chúng dưới dạng ${{ secrets.MY_TOKEN }}. Secrets được mã hóa khi lưu trữ, được che trong nhật ký và không bao giờ bị lộ cho các PR từ fork.
Chỉ triển khai khi kiểm thử thành công
Nối CI và CD với từ khóa needs:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: npx vercel --prod --token ${{ secrets.VERCEL_TOKEN }}
Job deploy chỉ chạy nếu test thành công và nếu chúng ta đang ở trên nhánh main.
Biến môi trường và Secrets
Ba cấp độ để xử lý cấu hình trong GitHub Actions:
1. Mã hóa cứng trong YAML — Chỉ dành cho các giá trị không nhạy cảm, phổ quát:
env:
NODE_ENV: production
PORT: 3000
2. Repository secrets — Các giá trị nhạy cảm (khóa API, token):
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}
3. Environment secrets — Các giá trị khác nhau cho mỗi môi trường triển khai (staging so với production):
jobs:
deploy:
environment: production # uses the "production" environment's secrets
steps:
- run: deploy.sh
env:
API_URL: ${{ secrets.API_URL }} # production-specific value
Environment secrets yêu cầu cổng phê duyệt: cấu hình người đánh giá bắt buộc trong Settings → Environments → protection rules. Hữu ích để ngăn chặn các triển khai sản xuất ngẫu nhiên.
Những điều tôi đã học được một cách khó khăn
Chạy CI trên mọi PR, không chỉ trên main. Phát hiện lỗi kiểm thử trên nhánh PR rẻ hơn nhiều so với phát hiện nó sau khi hợp nhất. Thêm pull_request: branches: [main] vào mọi workflow.
Giữ CI và CD riêng biệt. Workflow CI của bạn chạy mỗi lần đẩy và nên hoàn thành trong vòng 5 phút. Workflow CD của bạn triển khai và có thể mất nhiều thời gian hơn. Việc trộn lẫn chúng có nghĩa là một triển khai chậm sẽ chặn vòng lặp phản hồi PR của bạn.
Matrix builds làm tăng hóa đơn của bạn. Kiểm thử trên nhiều phiên bản Node hoặc hệ điều hành với chiến lược ma trận làm tăng thời gian chạy của bạn. Tôi chỉ sử dụng nó cho các thư viện mà khả năng tương thích giữa các phiên bản thực sự quan trọng. Đối với các ứng dụng, một môi trường mục tiêu là đủ.
# Only do this for libraries, not apps
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, windows-latest, macos-latest]
Đó là 9 runner mỗi lần đẩy. Với 2 phút mỗi runner, đó là 18 phút tính toán.
Theo dõi số phút của bạn. GitHub Actions cung cấp 2.000 phút miễn phí/tháng cho các kho lưu trữ riêng tư trên gói miễn phí. Một dự án với matrix builds và 10 lần đẩy/ngày có thể đốt hết số phút đó trong một tuần.
Thất bại nhanh chóng với fail-fast: true (mặc định trong matrix builds). Nếu một job ma trận thất bại, GitHub sẽ hủy các job khác. Tiết kiệm thời gian khi bạn có một lỗi cơ bản.
Sử dụng actions/cache rõ ràng cho các lớp Docker:
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
Nếu không có điều này, Docker sẽ xây dựng lại toàn bộ hình ảnh của bạn từ đầu trong mỗi lần chạy.
Một Workflow hoàn chỉnh sẵn sàng cho sản xuất
Đây là workflow đầy đủ mà tôi sử dụng cho một dự án Next.js:
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
build:
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: .next/
retention-days: 1
Các tính năng chính:
concurrencyhủy các lần chạy đang diễn ra khi có một lần đẩy mới — ngăn chặn việc xếp hàng quá nhiều trong quá trình phát triển tích cựclintvàtestchạy song song,buildchờ cả hai- Báo cáo độ bao phủ được gửi đến Codecov trong mỗi lần đẩy
- Các artifact bản build được tải lên trong 1 ngày (hữu ích cho các job triển khai để sử dụng mà không cần xây dựng lại)
Gỡ lỗi các Workflow bị lỗi
Khi một workflow bị lỗi và nhật ký không rõ ràng:
Bật ghi nhật ký gỡ lỗi bằng cách đặt một repository secret ACTIONS_STEP_DEBUG=true. Điều này sẽ xuất ra thông tin chi tiết từ mỗi bước.
SSH vào runner bằng cách sử dụng tmate:
- uses: mxschmitt/action-tmate@v3
if: ${{ failure() }}
Điều này tạm dừng workflow và cung cấp cho bạn kết nối SSH đến môi trường runner chính xác khi một bước bị lỗi. Vô giá để gỡ lỗi các vấn đề cụ thể về môi trường.
In chi tiết môi trường:
- run: node --version && npm --version && env | sort
Bạn cũng có thể thích
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

Tăng cường bảo mật cho GitHub Actions Self-Hosted Runner
Tăng cường bảo mật cho GitHub Actions runner tự host bằng Actions Runner Controller (ARC), cô lập mạng, container không root và OIDC token có thời hạn ngắn.
Read more
Các lựa chọn thay thế Playwright hàng đầu năm 2026: So sánh Cypress, WebdriverIO, Vitest & Puppeteer
Hướng dẫn toàn diện về các lựa chọn thay thế Playwright hàng đầu năm 2026: so sánh Cypress, WebdriverIO, Vitest & Puppeteer với các ví dụ thực tế đã được kiểm chứng.
Read more
Tương lai của các tác nhân AI trong quy trình CI/CD
Khám phá cách các tác nhân AI tự động hiện đại hóa quy trình CI/CD: phân loại nhật ký tự động, tự phục hồi lỗi kiểm thử và quy trình xem xét pull request.
Read more