cicd-pipeline-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCI/CD Pipeline Setup
CI/CD流水线搭建
Overview
概述
Build automated continuous integration and deployment pipelines that test code, build artifacts, run security checks, and deploy to multiple environments with minimal manual intervention.
构建自动化的持续集成与部署流水线,可测试代码、构建制品、运行安全检查,并在极少人工干预的情况下部署到多环境中。
When to Use
适用场景
- Automated code testing and quality checks
- Containerized application builds
- Multi-environment deployments
- Release management and versioning
- Automated security scanning
- Performance testing integration
- Artifact management and registry
- 自动化代码测试与质量检查
- 容器化应用构建
- 多环境部署
- 版本发布管理
- 自动化安全扫描
- 性能测试集成
- 制品管理与仓库
Implementation Examples
实现示例
1. GitHub Actions Workflow
1. GitHub Actions 工作流
yaml
undefinedyaml
undefined.github/workflows/deploy.yml
.github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- 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 linting
run: npm run lint
- name: Run tests
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
flags: unittests
name: codecov-umbrellasecurity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk Security Scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'build:
needs: [test, security]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- 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=semver,pattern={{version}}
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=maxdeploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole
aws-region: us-east-1
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster production \
--service myapp \
--force-new-deployment
- name: Verify deployment
run: |
aws ecs wait services-stable \
--cluster production \
--services myappundefinedname: Build and Deploy
on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- 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 linting
run: npm run lint
- name: Run tests
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
flags: unittests
name: codecov-umbrellasecurity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk Security Scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'build:
needs: [test, security]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- 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=semver,pattern={{version}}
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=maxdeploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GithubActionsRole
aws-region: us-east-1
- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster production \
--service myapp \
--force-new-deployment
- name: Verify deployment
run: |
aws ecs wait services-stable \
--cluster production \
--services myappundefined2. GitLab CI Pipeline
2. GitLab CI 流水线
yaml
undefinedyaml
undefined.gitlab-ci.yml
.gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
IMAGE_TAG: $CI_COMMIT_SHA
test:
stage: test
image: node:20
cache:
paths:
- node_modules/
script:
- npm ci
- npm run lint
- npm run test:coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
coverage: '/Lines\s*:\s*(\d+.\d+)%/'
security:
stage: test
image: aquasec/trivy:latest
script:
- trivy fs --exit-code 0 --severity HIGH,CRITICAL .
build:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $CI_REGISTRY_IMAGE:$IMAGE_TAG .
- docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG
- docker tag $CI_REGISTRY_IMAGE:$IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
- develop
deploy_staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache aws-cli
script:
- aws ecs update-service --cluster staging --service myapp --force-new-deployment
environment:
name: staging
url: https://staging.myapp.com
only:
- develop
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache aws-cli
script:
- aws ecs update-service --cluster production --service myapp --force-new-deployment
environment:
name: production
url: https://myapp.com
when: manual
only:
- main
undefinedstages:
- test
- build
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
IMAGE_TAG: $CI_COMMIT_SHA
test:
stage: test
image: node:20
cache:
paths:
- node_modules/
script:
- npm ci
- npm run lint
- npm run test:coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
coverage: '/Lines\s*:\s*(\d+.\d+)%/'
security:
stage: test
image: aquasec/trivy:latest
script:
- trivy fs --exit-code 0 --severity HIGH,CRITICAL .
build:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $CI_REGISTRY_IMAGE:$IMAGE_TAG .
- docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG
- docker tag $CI_REGISTRY_IMAGE:$IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
- develop
deploy_staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache aws-cli
script:
- aws ecs update-service --cluster staging --service myapp --force-new-deployment
environment:
name: staging
url: https://staging.myapp.com
only:
- develop
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache aws-cli
script:
- aws ecs update-service --cluster production --service myapp --force-new-deployment
environment:
name: production
url: https://myapp.com
when: manual
only:
- main
undefined3. Jenkins Pipeline
3. Jenkins 流水线
groovy
// Jenkinsfile
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 1, unit: 'HOURS')
timestamps()
}
environment {
REGISTRY = 'gcr.io'
PROJECT_ID = 'my-project'
IMAGE_NAME = 'myapp'
IMAGE_TAG = "${BUILD_NUMBER}-${GIT_COMMIT.take(7)}"
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
GIT_COMMIT_MSG = sh(
script: "git log -1 --pretty=%B",
returnStdout: true
).trim()
}
}
}
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Test') {
steps {
sh 'npm run test:coverage'
publishHTML([
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
stage('Build Image') {
when {
branch 'main'
}
steps {
script {
sh '''
docker build -t ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} .
docker tag ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} \
${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:latest
'''
}
}
}
stage('Push Image') {
when {
branch 'main'
}
steps {
sh '''
docker push ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG}
docker push ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:latest
'''
}
}
stage('Deploy Staging') {
when {
branch 'develop'
}
steps {
sh '''
kubectl set image deployment/myapp myapp=${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} \
-n staging --record
kubectl rollout status deployment/myapp -n staging
'''
}
}
stage('Deploy Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Deploy"
}
steps {
sh '''
kubectl set image deployment/myapp myapp=${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} \
-n production --record
kubectl rollout status deployment/myapp -n production
'''
}
}
}
post {
always {
cleanWs()
}
success {
slackSend(
channel: '#deployments',
message: "Build ${BUILD_NUMBER} succeeded on ${BRANCH_NAME}"
)
}
failure {
slackSend(
channel: '#deployments',
message: "Build ${BUILD_NUMBER} failed on ${BRANCH_NAME}"
)
}
}
}groovy
// Jenkinsfile
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 1, unit: 'HOURS')
timestamps()
}
environment {
REGISTRY = 'gcr.io'
PROJECT_ID = 'my-project'
IMAGE_NAME = 'myapp'
IMAGE_TAG = "${BUILD_NUMBER}-${GIT_COMMIT.take(7)}"
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
GIT_COMMIT_MSG = sh(
script: "git log -1 --pretty=%B",
returnStdout: true
).trim()
}
}
}
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Test') {
steps {
sh 'npm run test:coverage'
publishHTML([
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
stage('Build Image') {
when {
branch 'main'
}
steps {
script {
sh '''
docker build -t ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} .
docker tag ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} \
${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:latest
'''
}
}
}
stage('Push Image') {
when {
branch 'main'
}
steps {
sh '''
docker push ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG}
docker push ${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:latest
'''
}
}
stage('Deploy Staging') {
when {
branch 'develop'
}
steps {
sh '''
kubectl set image deployment/myapp myapp=${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} \
-n staging --record
kubectl rollout status deployment/myapp -n staging
'''
}
}
stage('Deploy Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Deploy"
}
steps {
sh '''
kubectl set image deployment/myapp myapp=${REGISTRY}/${PROJECT_ID}/${IMAGE_NAME}:${IMAGE_TAG} \
-n production --record
kubectl rollout status deployment/myapp -n production
'''
}
}
}
post {
always {
cleanWs()
}
success {
slackSend(
channel: '#deployments',
message: "Build ${BUILD_NUMBER} succeeded on ${BRANCH_NAME}"
)
}
failure {
slackSend(
channel: '#deployments',
message: "Build ${BUILD_NUMBER} failed on ${BRANCH_NAME}"
)
}
}
}4. CI/CD Script
4. CI/CD 脚本
bash
#!/bin/bashbash
#!/bin/bashci-pipeline.sh - Local pipeline validation
ci-pipeline.sh - Local pipeline validation
set -euo pipefail
echo "Starting CI/CD pipeline..."
set -euo pipefail
echo "Starting CI/CD pipeline..."
Code quality
Code quality
echo "Running code quality checks..."
npm run lint
npm run type-check
echo "Running code quality checks..."
npm run lint
npm run type-check
Testing
Testing
echo "Running tests..."
npm run test:coverage
echo "Running tests..."
npm run test:coverage
Build
Build
echo "Building application..."
npm run build
echo "Building application..."
npm run build
Docker build
Docker build
echo "Building Docker image..."
docker build -t myapp:latest .
echo "Building Docker image..."
docker build -t myapp:latest .
Security scanning
Security scanning
echo "Running security scans..."
trivy image myapp:latest --exit-code 0 --severity HIGH
echo "All pipeline stages completed successfully!"
undefinedecho "Running security scans..."
trivy image myapp:latest --exit-code 0 --severity HIGH
echo "All pipeline stages completed successfully!"
undefinedBest Practices
最佳实践
✅ DO
✅ 建议做法
- Fail fast with early validation
- Run tests in parallel when possible
- Use caching for dependencies
- Implement proper secret management
- Gate production deployments with approval
- Monitor and alert on pipeline failures
- Use consistent environment configuration
- Implement infrastructure as code
- 尽早验证,快速失败
- 尽可能并行运行测试
- 对依赖项使用缓存
- 实施完善的密钥管理
- 为生产部署设置审批关卡
- 监控流水线失败情况并发送告警
- 使用一致的环境配置
- 实施基础设施即代码
❌ DON'T
❌ 避免做法
- Store credentials in pipeline configuration
- Deploy without automated tests
- Skip security scanning
- Allow long-running pipelines
- Mix staging and production pipelines
- Ignore test failures
- Deploy directly to main branch
- Skip health checks after deployment
- 在流水线配置中存储凭证
- 未经过自动化测试就部署
- 跳过安全扫描
- 允许长时间运行的流水线
- 混合 staging 与生产流水线
- 忽略测试失败
- 直接部署到主分支
- 部署后跳过健康检查