devops-cicd

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

DevOps & CI/CD

DevOps & CI/CD

Overview

概述

Practices for automating build, test, and deployment pipelines.

自动化构建、测试与部署流水线的实践方案。

CI/CD Pipeline

CI/CD流水线

Pipeline Stages

流水线阶段

┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
│  Commit  │ → │  Build   │ → │   Test   │ → │  Deploy  │ → │ Release  │
│          │   │          │   │          │   │ Staging  │   │   Prod   │
└──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘
     │              │              │              │              │
     │         ┌────┴────┐    ┌────┴────┐        │              │
     │         │ Compile │    │  Unit   │        │              │
     │         │  Lint   │    │  Integ  │        │              │
     │         │  Type   │    │   E2E   │        │              │
     │         └─────────┘    └─────────┘        │              │
     │                                           │              │
   Trigger                                    Manual?       Approval?
┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
│  Commit  │ → │  Build   │ → │   Test   │ → │  Deploy  │ → │ Release  │
│          │   │          │   │          │   │ Staging  │   │   Prod   │
└──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘
     │              │              │              │              │
     │         ┌────┴────┐    ┌────┴────┐        │              │
     │         │ Compile │    │  Unit   │        │              │
     │         │  Lint   │    │  Integ  │        │              │
     │         │  Type   │    │   E2E   │        │              │
     │         └─────────┘    └─────────┘        │              │
     │                                           │              │
   Trigger                                    Manual?       Approval?

GitHub Actions

GitHub Actions

yaml
undefined
yaml
undefined

.github/workflows/ci.yml

.github/workflows/ci.yml

name: CI
on: push: branches: [main] 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'
      cache: 'npm'

  - name: Install dependencies
    run: npm ci

  - name: Lint
    run: npm run lint

  - name: Type check
    run: npm run type-check

  - name: Test
    run: npm run test -- --coverage

  - name: Upload coverage
    uses: codecov/codecov-action@v3

  - name: Build
    run: npm run build

  - name: Upload build artifact
    uses: actions/upload-artifact@v4
    with:
      name: build
      path: dist/
deploy-staging: needs: build if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: staging
steps:
  - name: Download build artifact
    uses: actions/download-artifact@v4
    with:
      name: build
      path: dist/

  - name: Deploy to staging
    run: |
      # Deploy script here
      echo "Deploying to staging..."
deploy-production: needs: deploy-staging runs-on: ubuntu-latest environment: name: production url: https://myapp.com
steps:
  - name: Deploy to production
    run: |
      echo "Deploying to production..."
undefined
name: CI
on: push: branches: [main] 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'
      cache: 'npm'

  - name: Install dependencies
    run: npm ci

  - name: Lint
    run: npm run lint

  - name: Type check
    run: npm run type-check

  - name: Test
    run: npm run test -- --coverage

  - name: Upload coverage
    uses: codecov/codecov-action@v3

  - name: Build
    run: npm run build

  - name: Upload build artifact
    uses: actions/upload-artifact@v4
    with:
      name: build
      path: dist/
deploy-staging: needs: build if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: staging
steps:
  - name: Download build artifact
    uses: actions/download-artifact@v4
    with:
      name: build
      path: dist/

  - name: Deploy to staging
    run: |
      # Deploy script here
      echo "Deploying to staging..."
deploy-production: needs: deploy-staging runs-on: ubuntu-latest environment: name: production url: https://myapp.com
steps:
  - name: Deploy to production
    run: |
      echo "Deploying to production..."
undefined

Matrix Builds

矩阵构建

yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
        exclude:
          - os: windows-latest
            node: 18

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test

yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: [18, 20, 22]
        exclude:
          - os: windows-latest
            node: 18

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test

Deployment Strategies

部署策略

Blue-Green Deployment

蓝绿部署

Before:
┌─────────────────────────────────────────┐
│           Load Balancer                  │
└─────────────────┬───────────────────────┘
          ┌───────┴───────┐
          ↓               ↓
    ┌───────────┐   ┌───────────┐
    │  Blue     │   │  Green    │
    │  (v1.0)   │   │  (idle)   │
    │  ACTIVE   │   │           │
    └───────────┘   └───────────┘

Deploy v1.1 to Green:
    ┌───────────┐   ┌───────────┐
    │  Blue     │   │  Green    │
    │  (v1.0)   │   │  (v1.1)   │
    │  ACTIVE   │   │  testing  │
    └───────────┘   └───────────┘

Switch traffic:
          ┌───────────────┐
          ↓               ↓
    ┌───────────┐   ┌───────────┐
    │  Blue     │   │  Green    │
    │  (v1.0)   │   │  (v1.1)   │
    │  standby  │   │  ACTIVE   │
    └───────────┘   └───────────┘
Before:
┌─────────────────────────────────────────┐
│           Load Balancer                  │
└─────────────────┬───────────────────────┘
          ┌───────┴───────┐
          ↓               ↓
    ┌───────────┐   ┌───────────┐
    │  Blue     │   │  Green    │
    │  (v1.0)   │   │  (idle)   │
    │  ACTIVE   │   │           │
    └───────────┘   └───────────┘

Deploy v1.1 to Green:
    ┌───────────┐   ┌───────────┐
    │  Blue     │   │  Green    │
    │  (v1.0)   │   │  (v1.1)   │
    │  ACTIVE   │   │  testing  │
    └───────────┘   └───────────┘

Switch traffic:
          ┌───────────────┐
          ↓               ↓
    ┌───────────┐   ┌───────────┐
    │  Blue     │   │  Green    │
    │  (v1.0)   │   │  (v1.1)   │
    │  standby  │   │  ACTIVE   │
    └───────────┘   └───────────┘

Canary Deployment

金丝雀部署

yaml
undefined
yaml
undefined

Kubernetes canary with Argo Rollouts

Kubernetes canary with Argo Rollouts

apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: my-app spec: replicas: 10 strategy: canary: steps: - setWeight: 10 # 10% traffic to new version - pause: { duration: 5m } - setWeight: 30 - pause: { duration: 5m } - setWeight: 50 - pause: { duration: 10m } - setWeight: 100 analysis: templates: - templateName: success-rate startingStep: 2 # Start analysis at 30% args: - name: service-name value: my-app
undefined
apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: my-app spec: replicas: 10 strategy: canary: steps: - setWeight: 10 # 10% traffic to new version - pause: { duration: 5m } - setWeight: 30 - pause: { duration: 5m } - setWeight: 50 - pause: { duration: 10m } - setWeight: 100 analysis: templates: - templateName: success-rate startingStep: 2 # Start analysis at 30% args: - name: service-name value: my-app
undefined

Rolling Update

滚动更新

yaml
undefined
yaml
undefined

Kubernetes rolling update

Kubernetes rolling update

apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 4 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # Can exceed replicas by 1 maxUnavailable: 0 # All must be available during update template: spec: containers: - name: app image: my-app:v1.1 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 5

---
apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 4 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # Can exceed replicas by 1 maxUnavailable: 0 # All must be available during update template: spec: containers: - name: app image: my-app:v1.1 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 5

---

Infrastructure as Code

基础设施即代码

Terraform

Terraform

hcl
undefined
hcl
undefined

main.tf

main.tf

terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }
backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" } }
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } }
backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" } }

Variables

Variables

variable "environment" { type = string default = "production" }
variable "instance_count" { type = number default = 2 }
variable "environment" { type = string default = "production" }
variable "instance_count" { type = number default = 2 }

Resources

Resources

resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16"
tags = { Name = "main-vpc" Environment = var.environment } }
resource "aws_instance" "web" { count = var.instance_count ami = "ami-0c55b159cbfafe1f0" instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
tags = { Name = "web-${count.index}" } }
resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16"
tags = { Name = "main-vpc" Environment = var.environment } }
resource "aws_instance" "web" { count = var.instance_count ami = "ami-0c55b159cbfafe1f0" instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
tags = { Name = "web-${count.index}" } }

Outputs

Outputs

output "instance_ips" { value = aws_instance.web[*].public_ip }
undefined
output "instance_ips" { value = aws_instance.web[*].public_ip }
undefined

Terraform Modules

Terraform 模块

hcl
undefined
hcl
undefined

modules/vpc/main.tf

modules/vpc/main.tf

variable "cidr_block" { type = string }
variable "environment" { type = string }
resource "aws_vpc" "this" { cidr_block = var.cidr_block tags = { Environment = var.environment } }
output "vpc_id" { value = aws_vpc.this.id }
variable "cidr_block" { type = string }
variable "environment" { type = string }
resource "aws_vpc" "this" { cidr_block = var.cidr_block tags = { Environment = var.environment } }
output "vpc_id" { value = aws_vpc.this.id }

Using the module

Using the module

module "vpc" { source = "./modules/vpc" cidr_block = "10.0.0.0/16" environment = "production" }
resource "aws_subnet" "main" { vpc_id = module.vpc.vpc_id

...

}

---
module "vpc" { source = "./modules/vpc" cidr_block = "10.0.0.0/16" environment = "production" }
resource "aws_subnet" "main" { vpc_id = module.vpc.vpc_id

...

}

---

Docker

Docker

Dockerfile Best Practices

Dockerfile 最佳实践

dockerfile
undefined
dockerfile
undefined

Use specific version tags

Use specific version tags

FROM node:20-alpine AS builder
FROM node:20-alpine AS builder

Set working directory

Set working directory

WORKDIR /app
WORKDIR /app

Copy dependency files first (layer caching)

Copy dependency files first (layer caching)

COPY package*.json ./
COPY package*.json ./

Install dependencies

Install dependencies

RUN npm ci --only=production
RUN npm ci --only=production

Copy source code

Copy source code

COPY . .
COPY . .

Build application

Build application

RUN npm run build
RUN npm run build

Production stage - smaller final image

Production stage - smaller final image

FROM node:20-alpine AS production
WORKDIR /app
FROM node:20-alpine AS production
WORKDIR /app

Create non-root user

Create non-root user

RUN addgroup -g 1001 -S nodejs &&
adduser -S nodejs -u 1001
RUN addgroup -g 1001 -S nodejs &&
adduser -S nodejs -u 1001

Copy from builder

Copy from builder

COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules

Use non-root user

Use non-root user

USER nodejs
USER nodejs

Expose port

Expose port

EXPOSE 3000
EXPOSE 3000

Health check

Health check

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1

Run application

Run application

CMD ["node", "dist/index.js"]
undefined
CMD ["node", "dist/index.js"]
undefined

Docker Compose

Docker Compose

yaml
undefined
yaml
undefined

docker-compose.yml

docker-compose.yml

version: '3.8'
services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - DATABASE_URL=postgres://postgres:password@db:5432/myapp - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started
db: image: postgres:15-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=password - POSTGRES_DB=myapp healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5
cache: image: redis:7-alpine volumes: - redis_data:/data
volumes: postgres_data: redis_data:

---
version: '3.8'
services: app: build: context: . dockerfile: Dockerfile ports: - "3000:3000" environment: - DATABASE_URL=postgres://postgres:password@db:5432/myapp - REDIS_URL=redis://cache:6379 depends_on: db: condition: service_healthy cache: condition: service_started
db: image: postgres:15-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=password - POSTGRES_DB=myapp healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5
cache: image: redis:7-alpine volumes: - redis_data:/data
volumes: postgres_data: redis_data:

---

Kubernetes

Kubernetes

Basic Deployment

基础部署

yaml
undefined
yaml
undefined

deployment.yaml

deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: my-app:v1.0.0 ports: - containerPort: 3000 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "500m" livenessProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 3 periodSeconds: 10 readinessProbe: httpGet: path: /health/ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url

apiVersion: v1 kind: Service metadata: name: my-app spec: selector: app: my-app ports: - port: 80 targetPort: 3000 type: ClusterIP
undefined

apiVersion: apps/v1 kind: Deployment metadata: name: my-app labels: app: my-app spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: my-app:v1.0.0 ports: - containerPort: 3000 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "500m" livenessProbe: httpGet: path: /health/live port: 3000 initialDelaySeconds: 3 periodSeconds: 10 readinessProbe: httpGet: path: /health/ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url

apiVersion: v1 kind: Service metadata: name: my-app spec: selector: app: my-app ports: - port: 80 targetPort: 3000 type: ClusterIP
undefined

ConfigMaps and Secrets

ConfigMap 与 Secret

yaml
undefined
yaml
undefined

configmap.yaml

configmap.yaml

apiVersion: v1 kind: ConfigMap metadata: name: app-config data: LOG_LEVEL: "info" MAX_CONNECTIONS: "100"

apiVersion: v1 kind: ConfigMap metadata: name: app-config data: LOG_LEVEL: "info" MAX_CONNECTIONS: "100"

secret.yaml (base64 encoded)

secret.yaml (base64 encoded)

apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque data: database-url: cG9zdGdyZXM6Ly91c2VyOnBhc3NAaG9zdDo1NDMyL2Ri

---
apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque data: database-url: cG9zdGdyZXM6Ly91c2VyOnBhc3NAaG9zdDo1NDMyL2Ri

---

GitOps

GitOps

ArgoCD Application

ArgoCD 应用

yaml
undefined
yaml
undefined

application.yaml

application.yaml

apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-app namespace: argocd spec: project: default source: repoURL: https://github.com/org/k8s-manifests.git targetRevision: HEAD path: apps/my-app/production destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true

---
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-app namespace: argocd spec: project: default source: repoURL: https://github.com/org/k8s-manifests.git targetRevision: HEAD path: apps/my-app/production destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true

---

Feature Flags

功能开关

typescript
// Using LaunchDarkly / Unleash pattern
interface FeatureFlags {
  newCheckoutFlow: boolean;
  betaFeatures: boolean;
  maxUploadSize: number;
}

class FeatureFlagService {
  constructor(private client: FeatureFlagClient) {}

  async isEnabled(flag: keyof FeatureFlags, user?: User): Promise<boolean> {
    return this.client.getBooleanValue(flag, false, {
      userId: user?.id,
      email: user?.email,
      groups: user?.groups
    });
  }
}

// Usage
if (await featureFlags.isEnabled('newCheckoutFlow', user)) {
  return <NewCheckoutFlow />;
} else {
  return <LegacyCheckout />;
}

typescript
// Using LaunchDarkly / Unleash pattern
interface FeatureFlags {
  newCheckoutFlow: boolean;
  betaFeatures: boolean;
  maxUploadSize: number;
}

class FeatureFlagService {
  constructor(private client: FeatureFlagClient) {}

  async isEnabled(flag: keyof FeatureFlags, user?: User): Promise<boolean> {
    return this.client.getBooleanValue(flag, false, {
      userId: user?.id,
      email: user?.email,
      groups: user?.groups
    });
  }
}

// Usage
if (await featureFlags.isEnabled('newCheckoutFlow', user)) {
  return <NewCheckoutFlow />;
} else {
  return <LegacyCheckout />;
}

Related Skills

相关技能

  • [[testing-strategies]] - CI test integration
  • [[reliability-engineering]] - Deployment reliability
  • [[monitoring-observability]] - Deployment monitoring
  • [[testing-strategies]] - CI 测试集成
  • [[reliability-engineering]] - 部署可靠性
  • [[monitoring-observability]] - 部署监控