CI/CD với GitHub Actions: Tự động hóa build, test, deploy trong DevOps

GitHub Actions CI/CD là nền tảng tự động hóa tích hợp sẵn trong GitHub, cho phép lập trình viên xây dựng quy trình build, test, deploy ngay trong repository mà không cần máy chủ riêng. Bài viết này hướng dẫn chi tiết cách thiết lập pipeline CI/CD hoàn chỉnh từ A đến Z.

Tại sao chọn GitHub Actions cho CI/CD?

GitHub Actions mang lại lợi thế lớn so với các giải pháp CI/CD truyền thống như Jenkins, GitLab CI, hay CircleCI:

  • Tích hợp nguyên sinh: Chạy ngay trong GitHub, không cần cấu hình webhook hay server riêng
  • Miễn phí cho public repo: 2.000 phút/tháng cho private repo, không giới hạn cho public
  • Marketplace lớn: Hơn 10.000 actions có sẵn (Docker, AWS, Terraform, Slack, v.v.)
  • Matrix builds: Test đa phiên bản Node, Python, Java, OS cùng lúc
  • Secrets management: Lưu trữ an toàn API key, token deploy qua GitHub Secrets

Phần 1: Cấu trúc file workflow

Mọi pipeline GitHub Actions đều định nghĩa trong thư mục .github/workflows/ với đuôi .yml hoặc .yaml. Cấu trúc cơ bản:

name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test
      - run: npm run build

GitHub Actions DevOps icon showing CI/CD workflow automation

Giải thích các trường quan trọng

Trường Chức năng
on Kích hoạt workflow: push, pull_request, schedule, workflow_dispatch, release
jobs Định nghĩa các công việc chạy song song hoặc tuần tự
runs-on Môi trường runner: ubuntu-latest, windows-latest, macos-latest, self-hosted
steps Các bước thực hiện trong job: uses (action có sẵn) hoặc run (lệnh shell)
needs Phụ thuộc job: job B chạy sau khi job A thành công

Phần 2: Pipeline thực tế cho dự án Node.js/TypeScript

Dưới đây là workflow hoàn chỉnh cho dự án hiện đại với lint, test, build, và deploy:

name: Node.js CI/CD

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '20.x'
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  lint-and-test:
    name: Lint & Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Run TypeScript type check
        run: npm run typecheck

      - name: Run unit tests
        run: npm run test:unit -- --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: ./coverage/lcov.info

  build-and-push:
    name: Build & Push Docker Image
    needs: lint-and-test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=sha,prefix=
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Continuous delivery test strategy diagram showing CI/CD pipeline stages

Phần 3: Kỹ thuật nâng cao

1. Matrix Strategy – Test đa môi trường

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: ['18.x', '20.x', '22.x']
        include:
          - os: ubuntu-latest
            node-version: '22.x'
            experimental: true
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci && npm test

2. Cache dependencies – Tăng tốc build

- name: Cache node_modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

3. Conditional deployment – Chỉ deploy khi test pass

deploy:
  needs: [lint-and-test, build-and-push]
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  runs-on: ubuntu-latest
  steps:
    - name: Deploy to Kubernetes
      uses: azure/k8s-deploy@v4
      with:
        manifests: |
          k8s/deployment.yaml
          k8s/service.yaml
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
        kubectl-version: 'latest'

4. Reusable workflows – Tái sử dụng giữa các repo

Tạo .github/workflows/reusable-ci.yml:

name: Reusable CI

on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: '20.x'
    secrets:
      NPM_TOKEN:
        required: true

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          registry-url: 'https://registry.npmjs.org'
      - run: npm ci
      - run: npm test
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Gọi từ repo khác:

jobs:
  call-reusable:
    uses: org/repo/.github/workflows/reusable-ci.yml@main
    with:
      node-version: '22.x'
    secrets:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Phần 4: Quản lý Secrets và Environments

Không bao giờ hardcode credentials trong workflow. Sử dụng:

  • Repository Secrets: Settings → Secrets and variables → Actions → New repository secret
  • Environment Secrets: Settings → Environments → production/staging → Add secret
  • Organization Secrets: Dùng chung cho nhiều repo trong tổ chức

Ví dụ deploy với environment protection rules:

deploy-production:
  needs: build
  environment: production
  runs-on: ubuntu-latest
  steps:
    - name: Deploy
      run: ./deploy.sh
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        KUBECONFIG: ${{ secrets.KUBECONFIG_PROD }}

Phần 5: Monitoring và Debug

Xem log chi tiết

Mỗi run hiển thị log real-time. Bật debug logging bằng secret ACTIONS_STEP_DEBUG: true hoặc ACTIONS_RUNNER_DEBUG: true.

Artifact upload/download

- name: Upload build artifacts
  uses: actions/upload-artifact@v4
  with:
    name: dist-files
    path: dist/
    retention-days: 7

- name: Download artifacts in deploy job
  uses: actions/download-artifact@v4
  with:
    name: dist-files
    path: dist/

Slack/Telegram notification

- name: Notify Slack
  if: always()
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    channel: '#deployments'
    text: "Deploy ${{ job.status }} - ${{ github.repository }}@${{ github.sha }}"
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Continuous Delivery process diagram showing build, test, deploy pipeline flow

Phần 6: Best Practices tổng kết

  1. Tách job nhỏ: Mỗi job làm một việc (lint, test, build, deploy) → dễ debug, chạy song song
  2. Dùng actions chính thức: actions/checkout, actions/setup-node, docker/* actions được maintain bởi GitHub/Docker
  3. Pin version action: Luôn dùng @v4 thay vì @main để tránh breaking change
  4. Cache agresively: npm, Maven, Gradle, Docker layer cache tiết kiệm 50-80% thời gian
  5. Fail fast: fail-fast: true trong matrix để dừng sớm khi một combo fail
  6. Concurrency control: Hủy run cũ khi push mới lên cùng branch
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Kết luận

GitHub Actions CI/CD cung cấp giải pháp hoàn chỉnh, miễn phí cho hầu hết nhu cầu tự động hóa. Với marketplace phong phú, matrix builds, reusable workflows, và tích hợp sâu với GitHub, nó là lựa chọn số một cho các team phát triển hiện nay. Hãy bắt đầu với workflow đơn giản (lint + test), sau đó mở rộng thêm build, deploy, notification theo nhu cầu dự án.

Tham khảo thêm:

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Bevy Engine: Xây dựng game 2D/3D với Rust hiệu năng cao

Bevy là game engine mã nguồn mở được xây dựng bằng Rust, tập trung vào khả năng mở rộng (data-driven) và hiệu năng. Với kiến trúc ECS (Entity Component System)…

Xem thêm

SQLite FTS5 Full-Text Search: Tìm kiếm nội dung trong app không cần server

SQLite FTS5 (Full-Text Search version 5) là công cụ tìm kiếm toàn văn bản tích hợp sẵn trong SQLite, cho phép tìm kiếm nội dung văn bản nhanh chóng mà…

Xem thêm

Golang Concurrency Patterns: Worker Pool, Pipeline, Fan-out/Fan-in thực tế

Golang nổi tiếng với mô hình concurrency “share memory by communicating” thay vì “share memory by locking”. Nhưng pattern thông thường như worker pool, pipeline, fan-out/fan-in không tự động xuất…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất