CI/CD 工具链深度指南
持续集成(Continuous Integration, CI)与持续交付/部署(Continuous Delivery/Deployment, CD)是现代软件工程的核心支柱。一套设计良好的 CI/CD 流水线能够将代码从提交到生产环境的交付时间从数天缩短至数分钟,同时显著提升软件质量与发布可靠性。本指南将系统性地剖析当前业界最主流的六大 CI/CD 工具——GitHub Actions、GitLab CI、Jenkins、ArgoCD、Tekton 与 Flux CD——从配置语法、架构原理到生产级最佳实践,提供可直接落地的深度参考。
1. CI/CD 核心概念与演进
在深入具体工具之前,有必要先厘清 CI 与 CD 的边界及其演进脉络。
持续集成(CI) 强调开发人员频繁地将代码合并到主干分支,每次合并都触发自动化的构建与测试流程。其核心目标是尽早发现集成冲突与缺陷,避免"集成地狱"。
持续交付(CD) 在 CI 的基础上,将已通过测试的制品自动部署到预生产环境(如 staging),并确保软件随时处于可发布状态。发布到生产环境的动作仍需人工审批。
持续部署(CD) 则是持续交付的进一步延伸,所有通过验证的变更都会自动推送到生产环境,实现真正的"提交即上线"。
现代 CI/CD 体系呈现出三大趋势:
- Pipeline as Code:流水线配置以代码形式存储在版本控制系统中,享受代码审查、版本回滚等能力。
- 云原生与 GitOps:CI/CD 工具深度拥抱 Kubernetes,以声明式配置和 Git 作为唯一可信源来驱动部署。
- 安全左移(Shift Left Security):将安全扫描、依赖漏洞检测、密钥泄漏检查前置到构建阶段,而非等到上线前才进行。
2. 六大工具全景概览
| 工具 | 定位 | 托管方式 | 配置语言 | 核心优势 | 适用场景 |
|---|---|---|---|---|---|
| GitHub Actions | CI/CD | SaaS / 自托管 Runner | YAML | 与 GitHub 生态无缝集成、Marketplace 生态丰富、上手快 | GitHub 托管项目、开源社区、快速原型 |
| GitLab CI | 一体化 DevOps | SaaS / 自托管 | YAML | 原生集成代码托管、Issue、监控、Security Dashboard | GitLab 用户、需要完整 DevOps 平台的企业 |
| Jenkins | CI/CD | 自托管 | Groovy (Declarative / Scripted) | 插件生态极丰富、高度可定制、支持任意异构环境 | 复杂企业环境、遗留系统集成、高度定制化需求 |
| ArgoCD | CD (GitOps) | 自托管 (K8s) | YAML (CRD) | Kubernetes 原生、声明式 GitOps、自动同步与自愈 | K8s 集群应用交付、多环境 GitOps 管理 |
| Tekton | CI/CD (K8s 原生) | 自托管 (K8s) | YAML (CRD) | 云原生标准、任务可组合、跨平台可移植 | 构建自定义 CI/CD 平台、多云混合环境 |
| Flux CD | CD (GitOps) | 自托管 (K8s) | YAML / CLI | GitOps 原生、CNCF 毕业项目、轻量高效 | K8s 持续交付、渐进式交付 (Flagger 集成) |
3. GitHub Actions 深度解析
GitHub Actions 是 GitHub 原生提供的 CI/CD 服务,其最大优势在于与代码仓库、Pull Request、Issue 的深度融合,以及 Actions Marketplace 中数以万计的复用组件。
3.1 工作流核心结构
一个 GitHub Actions 工作流由以下要素构成:
- Workflow:定义在
.github/workflows/下的 YAML 文件,是整个自动化的入口。 - Event:触发工作流的时机,如
push、pull_request、schedule(定时)、workflow_dispatch(手动触发)等。 - Job:工作流中的执行单元,同一工作流内的多个 Job 默认并行执行,也可通过
needs建立依赖关系。 - Step:Job 内的具体执行步骤,按顺序串行执行。
- Action:可复用的步骤单元,可直接引用 Marketplace 上的现成 Action 或自定义 Action。
3.2 完整构建-测试-部署示例
# .github/workflows/main-pipeline.yml
name: Build Test Deploy Pipeline
on:
push:
branches: [main, develop]
paths-ignore:
- "**/*.md"
- "docs/**"
- ".gitignore"
pull_request:
branches: [main]
workflow_dispatch:
inputs:
deploy_target:
description: "部署目标环境"
required: true
default: "staging"
type: choice
options:
- staging
- production
# 权限最小化原则
permissions:
contents: read
packages: write
id-token: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# ==================== 阶段一:代码质量与单元测试 ====================
lint-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run unit tests
run: npm run test:ci
- name: Upload test coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: false
# ==================== 阶段二:安全扫描 ====================
security-scan:
runs-on: ubuntu-latest
needs: lint-and-test
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: "fs"
scan-ref: "."
severity: "HIGH,CRITICAL"
exit-code: 1
- name: Check for leaked secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: main
head: HEAD
extra_args: --debug --only-verified
# ==================== 阶段三:构建容器镜像 ====================
build-image:
runs-on: ubuntu-latest
needs: [lint-and-test, security-scan]
outputs:
image_tag: ${{ steps.meta.outputs.tags }}
image_digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout
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: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=schedule
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
id: build
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
platforms: linux/amd64,linux/arm64
# ==================== 阶段四:部署到 Kubernetes ====================
deploy-staging:
runs-on: ubuntu-latest
needs: build-image
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://app-staging.example.com
steps:
- name: Checkout manifests repo
uses: actions/checkout@v4
with:
repository: org/gitops-manifests
token: ${{ secrets.MANIFESTS_REPO_TOKEN }}
path: manifests
- name: Update image tag in manifests
working-directory: manifests
run: |
yq e -i '.spec.template.spec.containers[0].image = "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"' \
overlays/staging/deployment.yaml
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "ci: update staging image to ${{ github.sha }}"
git push
deploy-production:
runs-on: ubuntu-latest
needs: build-image
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://app.example.com
steps:
- name: Checkout manifests repo
uses: actions/checkout@v4
with:
repository: org/gitops-manifests
token: ${{ secrets.MANIFESTS_REPO_TOKEN }}
path: manifests
- name: Update production image tag
working-directory: manifests
run: |
yq e -i '.spec.template.spec.containers[0].image = "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"' \
overlays/production/deployment.yaml
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "ci: update production image to ${{ github.sha }}"
git push
3.3 可复用工作流
对于组织内多个项目共享相同流水线逻辑的场景,可复用工作流(Reusable Workflows)能大幅降低维护成本。
# .github/workflows/reusable-docker-build.yml
name: Reusable Docker Build
on:
workflow_call:
inputs:
image_name:
required: true
type: string
dockerfile:
required: false
type: string
default: "Dockerfile"
platforms:
required: false
type: string
default: "linux/amd64"
secrets:
registry_token:
required: true
outputs:
image_digest:
description: "构建出的镜像 digest"
value: ${{ jobs.build.outputs.digest }}
jobs:
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.registry_token }}
- uses: docker/build-push-action@v5
id: build
with:
context: .
file: ${{ inputs.dockerfile }}
push: true
tags: ghcr.io/${{ github.repository_owner }}/${{ inputs.image_name }}:latest
platforms: ${{ inputs.platforms }}
调用方可通过如下方式引用:
jobs:
call-build:
uses: org/shared-workflows/.github/workflows/reusable-docker-build.yml@main
with:
image_name: "my-service"
platforms: "linux/amd64,linux/arm64"
secrets:
registry_token: ${{ secrets.GITHUB_TOKEN }}
4. GitLab CI 深度解析
GitLab CI 是 GitLab 内置的持续集成服务,与代码仓库、Container Registry、Security Dashboard、Issue Board 深度整合,提供了一站式的 DevOps 体验。
4.1 核心概念
- Pipeline:由多个 Stage 按顺序组成的完整流水线。
- Stage:定义 Job 的执行阶段,同一 Stage 的 Job 并行执行,前一 Stage 全部成功后才会进入下一阶段。
- Job:具体的执行单元,包含脚本、镜像、缓存、产物等配置。
- Runner:执行 Job 的代理程序,可使用 GitLab 共享 Runner、组级别 Runner 或自托管 Runner。
4.2 完整构建-测试-部署示例
# .gitlab-ci.yml
stages:
- build
- test
- security
- package
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
DOCKER_REGISTRY: "$CI_REGISTRY"
IMAGE_NAME: "$CI_REGISTRY_IMAGE"
KUBECONFIG: /etc/deploy/config
# 全局缓存配置
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository
- target/
# ==================== 构建阶段 ====================
build:
stage: build
image: maven:3.9-eclipse-temurin-21-alpine
script:
- mvn clean compile -DskipTests
artifacts:
paths:
- target/classes/
expire_in: 1 hour
# ==================== 测试阶段 ====================
unit_tests:
stage: test
image: maven:3.9-eclipse-temurin-21-alpine
script:
- mvn test
artifacts:
when: always
reports:
junit: target/surefire-reports/TEST-*.xml
paths:
- target/surefire-reports/
code_coverage:
stage: test
image: maven:3.9-eclipse-temurin-21-alpine
script:
- mvn jacoco:report
coverage: '/Total.*?([0-9]{1,3})%/'
artifacts:
paths:
- target/site/jacoco/
expire_in: 1 week
integration_tests:
stage: test
image: maven:3.9-eclipse-temurin-21-alpine
services:
- name: postgres:15-alpine
alias: postgres
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/testdb"
script:
- mvn verify -P integration-tests
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# ==================== 安全扫描阶段 ====================
sast:
stage: security
image: registry.gitlab.com/security-products/sast/gl-sast:gitlab-default
script:
- /analyzer run
artifacts:
reports:
sast: gl-sast-report.json
rules:
- if: $CI_COMMIT_BRANCH == "main"
dependency_scanning:
stage: security
image: registry.gitlab.com/security-products/dependency-scanning/gitlab-deps-canalyzer:gitlab-default
script:
- /analyzer run
artifacts:
reports:
dependency_scanning: dependency-scan-report.json
container_scanning:
stage: security
image: docker:stable
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay2
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
- docker run --rm -v /var/run/docker.sock:/var/run/docker.sock
-v $(pwd):/tmp
aquasec/trivy image --exit-code 0 --severity HIGH,CRITICAL
--format json -o /tmp/trivy-report.json $IMAGE_NAME:$CI_COMMIT_SHA
artifacts:
reports:
container_scanning: trivy-report.json
# ==================== 打包阶段 ====================
docker_build:
stage: package
image: docker:stable
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build --pull -t $IMAGE_NAME:$CI_COMMIT_SHA -t $IMAGE_NAME:latest .
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:latest
only:
- main
- develop
# ==================== 部署阶段 ====================
deploy_staging:
stage: deploy
image: bitnami/kubectl:latest
before_script:
- mkdir -p /etc/deploy
- echo "$KUBE_CONFIG_STAGING" | base64 -d > $KUBECONFIG
script:
- kubectl config use-context staging
- |
kubectl set image deployment/app app=$IMAGE_NAME:$CI_COMMIT_SHA \
-n staging --record
- kubectl rollout status deployment/app -n staging --timeout=300s
environment:
name: staging
url: https://app-staging.example.com
only:
- develop
deploy_production:
stage: deploy
image: bitnami/kubectl:latest
before_script:
- mkdir -p /etc/deploy
- echo "$KUBE_CONFIG_PROD" | base64 -d > $KUBECONFIG
script:
- kubectl config use-context production
- |
kubectl set image deployment/app app=$IMAGE_NAME:$CI_COMMIT_SHA \
-n production --record
- kubectl rollout status deployment/app -n production --timeout=600s
environment:
name: production
url: https://app.example.com
when: manual
only:
- main
4.3 模板与 Include 机制
GitLab CI 支持通过 include 复用配置片段,这对于多项目统一管理至关重要。
# templates/java-build.yml
.build_template:
image: maven:3.9-eclipse-temurin-21-alpine
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2/repository
artifacts:
paths:
- target/
expire_in: 1 hour
# .gitlab-ci.yml
include:
- local: '/templates/java-build.yml'
build:
extends: .build_template
stage: build
script:
- mvn clean package -DskipTests
5. Jenkins 深度解析
Jenkins 是历史最悠久、生态最丰富的 CI/CD 工具。尽管新兴工具层出不穷,Jenkins 依然在需要高度定制、复杂编排或与遗留系统集成的企业中占据不可替代的地位。
5.1 Pipeline 语法类型
Jenkins Pipeline 提供两种 DSL 风格:
- Declarative Pipeline:结构化的声明式语法,适合大多数场景,有更强的可视化支持和语法校验。
- Scripted Pipeline:基于 Groovy 的脚本式语法,灵活性极高,适合复杂逻辑与动态流程控制。
生产环境推荐使用 Declarative Pipeline,仅在必要时在 script 步骤中嵌入 Scripted 逻辑。
5.2 完整 Jenkinsfile 示例
// Jenkinsfile (Declarative Pipeline)
pipeline {
agent none
environment {
IMAGE_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'myapp'
MAVEN_OPTS = '-Dmaven.repo.local=.m2/repository'
}
options {
buildDiscarder(logRotator(numToKeepStr: '20'))
disableConcurrentBuilds()
timeout(time: 30, unit: 'MINUTES')
timestamps()
}
stages {
// ==================== 阶段一:并行质量门禁 ====================
stage('Quality Gates') {
parallel {
stage('Unit Tests') {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-21
command: ['cat']
tty: true
"""
}
}
steps {
container('maven') {
sh 'mvn clean test'
}
}
post {
always {
junit testResults: 'target/surefire-reports/*.xml'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/site/jacoco',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('Lint') {
agent any
steps {
sh './mvnw compile spotbugs:check'
}
}
stage('License Check') {
agent any
steps {
sh './mvnw license:check'
}
}
}
}
// ==================== 阶段二:集成测试 ====================
stage('Integration Tests') {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-21
command: ['cat']
tty: true
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_DB
value: testdb
- name: POSTGRES_USER
value: testuser
- name: POSTGRES_PASSWORD
value: testpass
"""
}
}
steps {
container('maven') {
sh 'mvn verify -P integration-tests'
}
}
}
// ==================== 阶段三:安全扫描 ====================
stage('Security Scan') {
agent any
steps {
script {
// 文件系统漏洞扫描
sh '''
trivy fs --scanners vuln,secret,config \
--severity HIGH,CRITICAL \
--exit-code 1 \
.
'''
// 构建镜像并进行容器扫描
sh """
docker build -t ${IMAGE_REGISTRY}/${IMAGE_NAME}:${env.GIT_COMMIT} .
trivy image --severity HIGH,CRITICAL \
--exit-code 1 \
${IMAGE_REGISTRY}/${IMAGE_NAME}:${env.GIT_COMMIT}
"""
}
}
}
// ==================== 阶段四:构建与推送制品 ====================
stage('Build & Push') {
agent any
when {
anyOf {
branch 'main'
branch 'develop'
}
}
steps {
script {
def imageTag = env.GIT_COMMIT.take(7)
def branchTag = env.BRANCH_NAME == 'main' ? 'latest' : 'dev'
docker.withRegistry("https://${IMAGE_REGISTRY}", 'registry-credentials-id') {
def image = docker.build("${IMAGE_REGISTRY}/${IMAGE_NAME}:${imageTag}")
image.push()
image.push(branchTag)
}
}
}
}
// ==================== 阶段五:部署 ====================
stage('Deploy to Staging') {
agent any
when {
branch 'develop'
}
steps {
withKubeConfig([credentialsId: 'kubeconfig-staging']) {
sh """
kubectl set image deployment/app \
app=${IMAGE_REGISTRY}/${IMAGE_NAME}:${env.GIT_COMMIT.take(7)} \
-n staging --record
kubectl rollout status deployment/app -n staging --timeout=300s
"""
}
}
}
stage('Deploy to Production') {
agent any
when {
branch 'main'
}
input {
message "确认部署到生产环境?"
ok "确认部署"
parameters {
choice(
name: 'DEPLOY_STRATEGY',
choices: ['rolling', 'blue-green', 'canary'],
description: '选择部署策略'
)
}
}
steps {
withKubeConfig([credentialsId: 'kubeconfig-production']) {
script {
if (params.DEPLOY_STRATEGY == 'rolling') {
sh """
kubectl set image deployment/app \
app=${IMAGE_REGISTRY}/${IMAGE_NAME}:${env.GIT_COMMIT.take(7)} \
-n production --record
kubectl rollout status deployment/app -n production --timeout=600s
"""
} else if (params.DEPLOY_STRATEGY == 'blue-green') {
sh './scripts/blue-green-deploy.sh production'
}
}
}
}
}
}
post {
always {
script {
if (env.BRANCH_NAME == 'main' || env.BRANCH_NAME == 'develop') {
cleanWs()
}
}
}
success {
slackSend(
channel: '#deployments',
color: 'good',
message: "✅ 构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER} (<${env.BUILD_URL}|查看详情>)"
)
}
failure {
slackSend(
channel: '#alerts',
color: 'danger',
message: "❌ 构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER} (<${env.BUILD_URL}|查看详情>)"
)
}
}
}
5.3 Shared Library 组织
对于大型企业,推荐将公共流水线逻辑抽取为 Jenkins Shared Library,实现"流水线即产品"。
// vars/buildDockerImage.groovy
def call(Map config = [:]) {
def imageName = config.imageName ?: error("imageName is required")
def dockerfile = config.dockerfile ?: 'Dockerfile'
def registry = config.registry ?: 'docker.io'
def tags = config.tags ?: ['latest']
stage("Build Docker Image: ${imageName}") {
script {
def image = docker.build("${registry}/${imageName}", "-f ${dockerfile} .")
tags.each { tag ->
image.push(tag)
}
}
}
}
在 Jenkinsfile 中调用:
@Library('my-shared-library') _
pipeline {
agent any
stages {
stage('Build') {
steps {
buildDockerImage(
imageName: 'my-service',
registry: 'ghcr.io/myorg',
tags: [env.GIT_COMMIT.take(7), 'latest']
)
}
}
}
}
6. ArgoCD 深度解析
ArgoCD 是一个为 Kubernetes 设计的声明式 GitOps 持续交付工具。它将 Git 仓库作为应用配置的单一可信源,自动监控仓库变更并将期望状态同步到集群中。
6.1 GitOps 核心原则
- 声明式:系统配置以声明式文件(YAML)形式描述,存储在 Git 中。
- 版本化与不可变:所有变更都通过 Git 提交实现,天然具备审计追踪能力。
- 自动拉取:ArgoCD 持续轮询 Git 仓库,自动发现变更并执行同步。
- 持续协调:当集群实际状态偏离 Git 中定义的期望状态时,ArgoCD 可自动恢复(Self-Healing)。
6.2 Application CRD 详解
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service
namespace: argocd
labels:
app.kubernetes.io/name: order-service
environment: production
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: production-services
source:
repoURL: https://github.com/company/gitops-repo.git
targetRevision: main
path: apps/order-service/overlays/production
helm:
valueFiles:
- values-production.yaml
parameters:
- name: replicaCount
value: "3"
directory:
recurse: true
jsonnet: {}
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
revisionHistoryLimit: 10
6.3 ApplicationSet 多集群/多租户管理
当需要管理数十甚至上百个应用时,逐个创建 Application 显然不切实际。ApplicationSet 控制器支持基于 Git 目录结构、集群列表生成器、SCM Provider 等策略批量生成 Application。
# applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: microservices
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/company/gitops-repo.git
revision: main
directories:
- path: apps/*/overlays/production
template:
metadata:
name: "{{path.basename}}-prod"
spec:
project: production-services
source:
repoURL: https://github.com/company/gitops-repo.git
targetRevision: main
path: "{{path}}"
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
6.4 Argo Rollouts 渐进式交付
Argo Rollouts 是 Argo 家族的渐进式交付控制器,支持 Canary、Blue-Green、A/B Testing 等高级部署策略。
# rollout-canary.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: order-service
namespace: production
spec:
replicas: 5
strategy:
canary:
canaryService: order-service-canary
stableService: order-service-stable
trafficRouting:
nginx:
stableIngress: order-service-ingress
annotationPrefix: nginx.ingress.kubernetes.io
steps:
- setWeight: 10
- pause: {duration: 5m}
- analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: order-service-canary
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 100
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: app
image: registry.example.com/order-service:v1.2.3
ports:
- containerPort: 8080
7. Tekton 简介
Tekton 是 CNCF 旗下的 Kubernetes 原生 CI/CD 框架,它将 CI/CD 概念映射为 Kubernetes 原生资源(Task、TaskRun、Pipeline、PipelineRun),具备极强的可移植性和可组合性。
# tekton-task-build.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-and-push
namespace: tekton-pipelines
spec:
params:
- name: image-url
type: string
description: 目标镜像地址
- name: dockerfile-path
type: string
default: ./Dockerfile
workspaces:
- name: source
description: 源码工作区
- name: dockerconfig
description: Docker 认证信息
steps:
- name: clone
image: alpine/git:v2.36.3
workingDir: $(workspaces.source.path)
script: |
#!/bin/sh
git clone https://github.com/org/repo.git .
- name: build
image: gcr.io/kaniko-project/executor:debug
workingDir: $(workspaces.source.path)
env:
- name: DOCKER_CONFIG
value: $(workspaces.dockerconfig.path)
command:
- /kaniko/executor
args:
- --dockerfile=$(params.dockerfile-path)
- --destination=$(params.image-url)
- --context=$(workspaces.source.path)
- --cache=true
# tekton-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: ci-pipeline
namespace: tekton-pipelines
spec:
workspaces:
- name: shared-workspace
- name: docker-config
params:
- name: git-url
type: string
- name: image-url
type: string
tasks:
- name: fetch-source
taskRef:
name: git-clone
workspaces:
- name: output
workspace: shared-workspace
params:
- name: url
value: $(params.git-url)
- name: run-tests
runAfter:
- fetch-source
taskRef:
name: maven-test
workspaces:
- name: source
workspace: shared-workspace
- name: build-push-image
runAfter:
- run-tests
taskRef:
name: build-and-push
workspaces:
- name: source
workspace: shared-workspace
- name: dockerconfig
workspace: docker-config
params:
- name: image-url
value: $(params.image-url)
Tekton 的优势在于其完全基于 Kubernetes CRD 实现,能够与集群的 RBAC、ResourceQuota、NetworkPolicy 等原生机制无缝协作。但其学习曲线较陡,配置也相对冗长,通常作为构建内部 DevOps 平台的底层引擎使用。
8. Flux CD 简介
Flux CD 是另一个 CNCF 毕业级别的 GitOps 工具,与 ArgoCD 相比,Flux 更加轻量、原生,且与 GitOps Toolkit 深度集成。
# flux-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: production-apps
namespace: flux-system
spec:
interval: 10m
path: ./clusters/production/apps
prune: true
sourceRef:
kind: GitRepository
name: production-repo
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: order-service
namespace: production
timeout: 5m
# flux-image-automation.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageUpdateAutomation
metadata:
name: production-automation
namespace: flux-system
spec:
interval: 1m
sourceRef:
kind: GitRepository
name: production-repo
git:
checkout:
ref:
branch: main
commit:
author:
name: Flux Bot
email: flux@example.com
signingKey:
secretRef:
name: flux-gpg-signing-key
messageTemplate: |
ci(flux): automated image update
Images:
{{ range .Updated.Images -}}
- {{.}}
{{ end }}
push:
branch: main
policy:
semver:
range: 1.x.x
Flux 的 Image Automation Controller 能够监控容器镜像仓库,当发现新版本时自动更新 Git 仓库中的镜像标签并提交变更,随后触发 Kustomization 控制器将更新同步到集群。这种"镜像升级由 CI 推送、部署由 CD 拉取"的分工模式,是 GitOps 的最佳实践之一。
9. 工具选型对比表
表一:GitHub Actions vs GitLab CI vs Jenkins vs ArgoCD vs Tekton vs Flux CD
| 对比维度 | GitHub Actions | GitLab CI | Jenkins | ArgoCD | Tekton | Flux CD |
|---|---|---|---|---|---|---|
| 定位 | 通用 CI/CD | 一体化 DevOps | 通用 CI/CD | K8s CD (GitOps) | K8s 原生 CI/CD | K8s CD (GitOps) |
| 托管模式 | SaaS / 自托管 Runner | SaaS / 自托管 | 完全自托管 | 自托管 (K8s) | 自托管 (K8s) | 自托管 (K8s) |
| 配置方式 | YAML (.github/workflows) | YAML (.gitlab-ci.yml) | Groovy DSL (Jenkinsfile) | YAML (CRD) | YAML (CRD) | YAML (CRD) |
| 上手难度 | 低 | 低 | 高(插件多、配置复杂) | 中 | 高 | 中 |
| 生态与集成 | GitHub Marketplace(极丰富) | GitLab 全家桶(内置) | 插件生态最丰富(1800+) | K8s 生态、Argo 家族 | CNCF 标准、可组合 | CNCF 毕业、轻量 |
| 并行执行 | Job 级并行 | Stage 内 Job 并行 | 支持(需配置节点/标签) | 应用级并行 | Task 级并行 | 资源级并行 |
| 容器原生 | 通过 Runner 支持 | 通过 Runner + Docker Executor 支持 | 需插件/手动配置 | K8s 原生 | K8s 原生 | K8s 原生 |
| 制品管理 | 依赖 GitHub Packages / 外部 | 内置 Container Registry/Packages | 需配置外部仓库 | 不适用(纯 CD) | 不适用 | 不适用 |
| Secrets 管理 | Repository/Environment Secrets | CI/CD Variables / Vault 集成 | Credentials Plugin / Vault | 不适用 | K8s Secrets / Vault | K8s Secrets / SOPS |
| 扩展能力 | Marketplace Actions / 复用工作流 | Include 模板 / CI Catalog | Shared Library / 自定义插件 | Argo Rollouts / Notifications | 自定义 Task / Catalog | Flagger / Notification Controller |
| 最佳场景 | GitHub 项目、开源社区 | GitLab 用户、需一体化平台 | 复杂企业环境、遗留集成 | K8s GitOps 交付、多集群 | 构建自定义 CI 平台、多云 | K8s GitOps、渐进式交付 |
| 运维成本 | 低(SaaS)/ 中(自托管) | 低(SaaS)/ 中(自托管) | 高(Master/Agent 架构需专人维护) | 中(K8s 运维基础) | 高(需深度 K8s 知识) | 中(轻量但需理解 GitOps 模式) |
表二:CI 工具 vs CD 工具职责边界
| 对比维度 | CI 工具(GitHub Actions / GitLab CI / Jenkins / Tekton) | CD 工具(ArgoCD / Flux CD) |
|---|---|---|
| 核心职责 | 编译源码、运行测试、打包制品、推送镜像 | 将制品部署到目标环境、管理应用生命周期 |
| 触发方式 | 事件驱动(Push / PR / Webhook / 定时) | 轮询 Git 变更 / 手动同步 / Webhook |
| 执行位置 | CI Runner / Agent / Pod(通常隔离环境) | 部署控制器运行在目标 K8s 集群中 |
| 是否有状态 | 通常无状态(Job 执行完即销毁) | 有状态(持续监控期望状态与实际状态) |
| 回滚能力 | 有限(重新触发历史构建) | 原生支持(Git revert 即可回滚到任意版本) |
| 多集群管理 | 复杂(需配置多组凭证与部署脚本) | 原生支持(一个控制平面管理多集群 Application) |
| 安全模型 | Build-time 安全(依赖扫描、SAST) | Runtime 安全(OPA Gatekeeper、Kyverno 策略校验) |
| 审计追踪 | 基于流水线执行日志 | 基于 Git 提交历史(不可篡改) |
| 协作模式 | 开发者提交代码触发 CI | 运维/开发者修改 Git 配置触发 CD |
| 推荐组合 | GitHub Actions(CI) + ArgoCD(CD) | GitLab CI(CI) + Flux CD(CD) |
10. 端到端完整流水线设计
以下展示一个采用 GitHub Actions + ArgoCD 分层架构的完整 DevOps 流水线,这也是当前云原生领域最主流的 CI/CD 组合之一。
10.1 架构分层
| 层级 | 工具 | 职责 |
|---|---|---|
| 源码层 | GitHub | 代码托管、PR Review、分支保护 |
| CI 层 | GitHub Actions | 编译、测试、安全扫描、构建推送镜像 |
| 制品层 | GHCR (GitHub Container Registry) | 存储容器镜像与签名 |
| 配置层 | GitOps Repo (GitHub) | 存储 K8s Manifests、Kustomize 配置 |
| CD 层 | ArgoCD | 监听配置变更、同步到 K8s 集群 |
| 基础设施 | Kubernetes | 运行应用工作负载 |
10.2 CI 层配置
参见前文"GitHub Actions 完整构建-测试-部署示例",其核心逻辑为:
- Push 到
develop/main分支触发流水线 - 并行执行 Lint、Unit Test、Security Scan
- 构建多平台镜像并推送到 GHCR
- 修改 GitOps 仓库中对应环境的 Deployment 镜像标签
- ArgoCD 检测到 GitOps 仓库变更,自动同步到集群
10.3 CD 层配置
# apps/order-service/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- deployment.yaml
- service.yaml
- hpa.yaml
images:
- name: app-image
newName: ghcr.io/org/order-service
newTag: latest
commonLabels:
app.kubernetes.io/name: order-service
app.kubernetes.io/managed-by: argocd
---
# apps/order-service/overlays/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: staging
resources:
- ../../base
namePrefix: staging-
replicas:
- name: deployment
count: 2
patchesStrategicMerge:
- resources-patch.yaml
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=debug
- ENV=staging
# order-service-app.yaml (ArgoCD Application)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service-staging
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
project: staging
source:
repoURL: https://github.com/org/gitops-repo.git
targetRevision: main
path: apps/order-service/overlays/staging
destination:
server: https://kubernetes.default.svc
namespace: staging
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
10.4 流水线执行时序图
Developer Push → GitHub
↓
GitHub Actions Trigger
↓
├─ Run Tests & Lint (并行)
├─ Security Scan (Trivy + TruffleHog)
├─ Build Docker Image (多平台)
└─ Push to GHCR
↓
Update GitOps Repo (CI Bot 提交)
↓
ArgoCD 检测到 Git Commit
↓
ArgoCD 执行 Sync → Apply to K8s
↓
K8s 滚动更新 / Argo Rollouts 金丝雀发布
11. 安全与合规最佳实践
无论选择何种工具,以下是 CI/CD 安全的核心原则:
最小权限原则
- GitHub Actions 中显式声明
permissions字段,而非使用默认的write-all。 - Jenkins 中使用 Credentials Binding Plugin,绝不在代码中硬编码密钥。
- GitHub Actions 中显式声明
Secrets 管理
- 优先使用原生 Secrets(GitHub Secrets / GitLab CI Variables / Kubernetes External Secrets / Vault)。
- 定期轮换密钥,利用工具如
truffleHog扫描历史提交中的密钥泄漏。
镜像安全
- 使用 Distroless 或 Alpine 最小化基础镜像攻击面。
- 在 CI 阶段集成 Trivy / Snyk / Clair 进行镜像漏洞扫描,设置
exit-code: 1阻断高危漏洞。 - 对镜像进行签名(Cosign / Notary),在 K8s 中启用准入控制验证签名。
Supply Chain 安全
- 锁定 Action / 依赖版本,避免使用
@master或@v1这种浮动的引用。 - 使用 Dependabot / Renovate 自动跟踪依赖更新。
- 在 CI 中生成和上传 SBOM(Software Bill of Materials)。
- 锁定 Action / 依赖版本,避免使用
网络隔离
- 自托管 Runner 应部署在隔离的 VPC / 子网中,通过 NAT 网关访问外网。
- K8s 中通过 NetworkPolicy 限制 CI/CD Pod 的网络访问范围。
12. 常见问题解答(FAQ)
Q1: 中小企业团队应从哪个 CI/CD 工具入门?
如果代码已经托管在 GitHub 上,GitHub Actions 是最佳起点。它零配置集成、学习曲线平缓、Marketplace 生态丰富,足以覆盖从开源项目到中型企业的绝大多数场景。若使用 GitLab,则直接采用原生 GitLab CI。只有当业务涉及大量遗留系统集成、需要高度复杂的条件分支逻辑,或已有专职运维团队时,才考虑引入 Jenkins。
Q2: CI 和 CD 是否应该使用同一套工具?
不一定。现代 DevOps 的趋势是"用 CI 工具做构建,用 CD 工具做部署"。CI 阶段强调快速反馈、并行测试、制品产出;CD 阶段强调环境一致性、声明式配置、自动回滚。将两者解耦的好处在于:CI 工具不需要集群访问权限,CD 工具不需要源码访问权限,安全边界更清晰。推荐的组合包括:GitHub Actions + ArgoCD、GitLab CI + Flux CD、Jenkins + ArgoCD。
Q3: 如何实现部署失败时的自动回滚?
ArgoCD 和 Flux CD 都原生支持基于 Git 历史的回滚。当发现生产故障时,只需在 Git 仓库中执行 git revert 回退到上一个稳定版本的镜像标签,ArgoCD/Flux 会自动检测到变更并将集群状态同步到旧版本。对于 Jenkins/GitLab CI 等传统 CI 驱动部署的场景,可在部署步骤后加入 kubectl rollout undo deployment/app 命令,配合监控告警实现自动回滚。
Q4: 自托管 Runner 与云托管 Runner 如何选择?
云托管 Runner(如 GitHub Hosted Runners / GitLab Shared Runners)的优势在于免运维、秒级启动、按量计费,适合大多数场景。但在以下情况应使用自托管 Runner:
- 需要访问内网资源(私有 Nexus、内部 API、私有 K8s 集群)。
- 构建任务需要特殊硬件(GPU、大内存、ARM 架构)。
- 对数据合规性有严格要求(代码不可流出特定网络边界)。
- 构建耗时极长,云托管 Runner 的成本高于自托管机器。
Q5: GitOps 模式与传统 Push 部署有什么区别,什么情况下必须采用 GitOps?
传统 Push 部署是 CI 工具持有集群凭证,主动向 K8s API 发起 kubectl apply 请求。而 GitOps 模式是 CD 控制器(ArgoCD/Flux)运行在集群内部,主动拉取(Pull)Git 仓库中的声明式配置并应用到集群。
GitOps 的硬性收益包括:
- 更小的攻击面:集群访问凭证不需要暴露给 CI 系统。
- 更强的可审计性:所有部署变更都体现为 Git Commit,天然不可篡改。
- 自愈能力:当有人通过
kubectl edit手动修改集群资源时,GitOps 控制器会自动恢复为 Git 中定义的期望状态。 - 简化回滚:回滚即
git revert,无需维护复杂的回滚脚本。
当团队已经全面采用 Kubernetes,且有多环境(dev/staging/prod)或多集群的交付需求时,强烈建议迁移到 GitOps 模式。
13. 总结
CI/CD 工具的选择没有银弹,关键在于匹配团队的技术栈、组织架构与演进阶段:
- 初创团队 / GitHub 用户:GitHub Actions 足以支撑从 MVP 到规模化的大部分需求,配合 Marketplace 生态可快速搭建流水线。
- GitLab 用户 / 需要一体化平台:GitLab CI 与代码托管、Issue、Security Dashboard 的无缝集成能显著提升协作效率。
- 复杂企业 / 遗留系统 / 强定制需求:Jenkins 凭借其无极限的插件生态和 Groovy 脚本能力,仍然是难以替代的选择。
- K8s 环境下的 CD:ArgoCD 与 Flux CD 是 GitOps 交付的事实标准。ArgoCD 提供完善的 UI 和多集群管理能力;Flux CD 更轻量、更原生,适合喜欢"GitOps 纯命令行"风格的团队。
- 构建内部 DevOps 平台:Tekton 作为 Kubernetes 原生框架,提供了任务级的可组合能力和极强的扩展性,但要做好"配置即代码"的抽象封装,否则其配置复杂度会成为团队的负担。
最终的理想架构通常是分层组合:使用 GitHub Actions 或 GitLab CI 作为 CI 引擎负责构建、测试与镜像推送,使用 ArgoCD 或 Flux CD 作为 CD 引擎负责环境管理、部署同步与渐进式交付。这种"各司其职、边界清晰"的架构,能够兼顾开发效率与运维安全,是当前云原生时代 CI/CD 的最佳实践范式。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。