deployment-automation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Deployment Automation

部署自动化

When to use this skill

何时使用此技能

  • 신규 프로젝트: 처음부터 자동 배포 설정
  • 수동 배포 개선: 반복적인 수동 작업 자동화
  • 멀티 환경: dev, staging, production 환경 분리
  • 스케일링: 트래픽 증가 대비 Kubernetes 도입
  • 新项目:从0开始配置自动部署
  • 手动部署优化:将重复性手动工作自动化
  • 多环境:分离dev、staging、production环境
  • 扩容:应对流量增长引入Kubernetes

Instructions

操作指南

Step 1: Docker 컨테이너화

步骤1:Docker容器化

애플리케이션을 Docker 이미지로 패키징합니다.
Dockerfile (Node.js 앱):
dockerfile
undefined
将应用程序打包为Docker镜像。
Dockerfile(Node.js应用):
dockerfile
undefined

Multi-stage build for smaller image size

Multi-stage build for smaller image size

FROM node:18-alpine AS builder
WORKDIR /app
FROM node:18-alpine AS builder
WORKDIR /app

Copy package files and install dependencies

Copy package files and install dependencies

COPY package*.json ./ RUN npm ci --only=production
COPY package*.json ./ RUN npm ci --only=production

Copy source code

Copy source code

COPY . .
COPY . .

Build application (if needed)

Build application (if needed)

RUN npm run build
RUN npm run build

Production stage

Production stage

FROM node:18-alpine
WORKDIR /app
FROM node:18-alpine
WORKDIR /app

Copy only necessary files from builder

Copy only necessary files from builder

COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist COPY --from=builder /app/package.json ./

Create non-root user for security

Create non-root user for security

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

Expose port

Expose port

EXPOSE 3000
EXPOSE 3000

Health check

Health check

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3
CMD node healthcheck.js
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3
CMD node healthcheck.js

Start application

Start application

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

**.dockerignore**:
node_modules npm-debug.log .git .env .env.local dist build coverage .DS_Store

**빌드 및 실행**:
```bash
CMD ["node", "dist/index.js"]

**.dockerignore**:
node_modules npm-debug.log .git .env .env.local dist build coverage .DS_Store

**构建与运行**:
```bash

Build image

Build image

docker build -t myapp:latest .
docker build -t myapp:latest .

Run container

Run container

docker run -d -p 3000:3000 --name myapp-container myapp:latest
docker run -d -p 3000:3000 --name myapp-container myapp:latest

Check logs

Check logs

docker logs myapp-container
docker logs myapp-container

Stop and remove

Stop and remove

docker stop myapp-container docker rm myapp-container
undefined
docker stop myapp-container docker rm myapp-container
undefined

Step 2: GitHub Actions CI/CD

步骤2:GitHub Actions CI/CD

코드 푸시 시 자동으로 테스트 및 배포합니다.
.github/workflows/deploy.yml:
yaml
name: CI/CD Pipeline

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

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

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - 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 linter
        run: npm run lint

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

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json

  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'

    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=sha,prefix={{branch}}-
            type=semver,pattern={{version}}
            latest

      - name: Build and push Docker image
        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

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Deploy to production
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.PROD_HOST }}
          username: ${{ secrets.PROD_USER }}
          key: ${{ secrets.PROD_SSH_KEY }}
          script: |
            cd /app
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            docker-compose up -d --no-deps --build web
            docker image prune -f
代码推送时自动执行测试与部署。
.github/workflows/deploy.yml:
yaml
name: CI/CD Pipeline

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

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

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - 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 linter
        run: npm run lint

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

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/coverage-final.json

  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'

    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=sha,prefix={{branch}}-
            type=semver,pattern={{version}}
            latest

      - name: Build and push Docker image
        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

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Deploy to production
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.PROD_HOST }}
          username: ${{ secrets.PROD_USER }}
          key: ${{ secrets.PROD_SSH_KEY }}
          script: |
            cd /app
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            docker-compose up -d --no-deps --build web
            docker image prune -f

Step 3: Kubernetes 배포

步骤3:Kubernetes部署

확장 가능한 컨테이너 오케스트레이션을 구현합니다.
k8s/deployment.yaml:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: ghcr.io/username/myapp:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: myapp-secrets
              key: database-url
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  namespace: production
spec:
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: LoadBalancer

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
배포 스크립트 (deploy.sh):
bash
#!/bin/bash
set -e
实现可扩展的容器编排。
k8s/deployment.yaml:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: ghcr.io/username/myapp:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: myapp-secrets
              key: database-url
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  namespace: production
spec:
  selector:
    app: myapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: LoadBalancer

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
部署脚本 (deploy.sh):
bash
#!/bin/bash
set -e

Variables

Variables

NAMESPACE="production" IMAGE_TAG="${1:-latest}"
echo "Deploying myapp:${IMAGE_TAG} to ${NAMESPACE}..."
NAMESPACE="production" IMAGE_TAG="${1:-latest}"
echo "Deploying myapp:${IMAGE_TAG} to ${NAMESPACE}..."

Apply Kubernetes manifests

Apply Kubernetes manifests

kubectl apply -f k8s/namespace.yaml kubectl apply -f k8s/secrets.yaml kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/namespace.yaml kubectl apply -f k8s/secrets.yaml kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml

Update image

Update image

kubectl set image deployment/myapp myapp=ghcr.io/username/myapp:${IMAGE_TAG} -n ${NAMESPACE}
kubectl set image deployment/myapp myapp=ghcr.io/username/myapp:${IMAGE_TAG} -n ${NAMESPACE}

Wait for rollout

Wait for rollout

kubectl rollout status deployment/myapp -n ${NAMESPACE} --timeout=5m
kubectl rollout status deployment/myapp -n ${NAMESPACE} --timeout=5m

Verify

Verify

kubectl get pods -n ${NAMESPACE} -l app=myapp
echo "Deployment completed successfully!"
undefined
kubectl get pods -n ${NAMESPACE} -l app=myapp
echo "Deployment completed successfully!"
undefined

Step 4: Vercel/Netlify (프론트엔드)

步骤4:Vercel/Netlify(前端)

정적 사이트 및 Next.js 앱을 간단히 배포합니다.
vercel.json:
json
{
  "version": 2,
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/next"
    }
  ],
  "env": {
    "DATABASE_URL": "@database-url",
    "API_KEY": "@api-key"
  },
  "regions": ["sin1", "icn1"],
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        }
      ]
    }
  ],
  "redirects": [
    {
      "source": "/old-path",
      "destination": "/new-path",
      "permanent": true
    }
  ]
}
CLI 배포:
bash
undefined
轻松部署静态网站与Next.js应用。
vercel.json:
json
{
  "version": 2,
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/next"
    }
  ],
  "env": {
    "DATABASE_URL": "@database-url",
    "API_KEY": "@api-key"
  },
  "regions": ["sin1", "icn1"],
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        }
      ]
    }
  ],
  "redirects": [
    {
      "source": "/old-path",
      "destination": "/new-path",
      "permanent": true
    }
  ]
}
CLI部署:
bash
undefined

Install Vercel CLI

Install Vercel CLI

npm i -g vercel
npm i -g vercel

Login

Login

vercel login
vercel login

Deploy to preview

Deploy to preview

vercel
vercel

Deploy to production

Deploy to production

vercel --prod
vercel --prod

Set environment variable

Set environment variable

vercel env add DATABASE_URL
undefined
vercel env add DATABASE_URL
undefined

Step 5: 무중단 배포 전략

步骤5:无缝部署策略

서비스 중단 없이 새 버전을 배포합니다.
Blue-Green 배포 (docker-compose):
yaml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app-blue
      - app-green

  app-blue:
    image: myapp:blue
    environment:
      - NODE_ENV=production
      - COLOR=blue

  app-green:
    image: myapp:green
    environment:
      - NODE_ENV=production
      - COLOR=green
switch.sh (Blue/Green 전환):
bash
#!/bin/bash

CURRENT_COLOR=$(cat current_color.txt)
NEW_COLOR=$([[ "$CURRENT_COLOR" == "blue" ]] && echo "green" || echo "blue")
在不中断服务的情况下部署新版本。
蓝绿部署 (docker-compose):
yaml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app-blue
      - app-green

  app-blue:
    image: myapp:blue
    environment:
      - NODE_ENV=production
      - COLOR=blue

  app-green:
    image: myapp:green
    environment:
      - NODE_ENV=production
      - COLOR=green
switch.sh(蓝绿切换):
bash
#!/bin/bash

CURRENT_COLOR=$(cat current_color.txt)
NEW_COLOR=$([[ "$CURRENT_COLOR" == "blue" ]] && echo "green" || echo "blue")

Deploy new version to inactive environment

Deploy new version to inactive environment

docker-compose up -d app-${NEW_COLOR}
docker-compose up -d app-${NEW_COLOR}

Wait for health check

Wait for health check

sleep 10
sleep 10

Health check

Health check

Update nginx to point to new environment

sed -i "s/${CURRENT_COLOR}/${NEW_COLOR}/g" nginx.conf docker-compose exec nginx nginx -s reload

Update current color

echo ${NEW_COLOR} > current_color.txt

Stop old environment after 5 minutes (rollback window)

sleep 300 docker-compose stop app-${CURRENT_COLOR}
echo "Deployment successful! Switched to ${NEW_COLOR}" else echo "Health check failed! Keeping ${CURRENT_COLOR}" docker-compose stop app-${NEW_COLOR} exit 1 fi
undefined

Update nginx to point to new environment

sed -i "s/${CURRENT_COLOR}/${NEW_COLOR}/g" nginx.conf docker-compose exec nginx nginx -s reload

Update current color

echo ${NEW_COLOR} > current_color.txt

Stop old environment after 5 minutes (rollback window)

sleep 300 docker-compose stop app-${CURRENT_COLOR}
echo "Deployment successful! Switched to ${NEW_COLOR}" else echo "Health check failed! Keeping ${CURRENT_COLOR}" docker-compose stop app-${NEW_COLOR} exit 1 fi
undefined

Output format

输出格式

배포 체크리스트

部署检查表

markdown
undefined
markdown
undefined

Deployment Checklist

Deployment Checklist

Pre-Deployment

Pre-Deployment

  • All tests passing (unit, integration, E2E)
  • Code review approved
  • Environment variables configured
  • Database migrations ready
  • Rollback plan documented
  • All tests passing (unit, integration, E2E)
  • Code review approved
  • Environment variables configured
  • Database migrations ready
  • Rollback plan documented

Deployment

Deployment

  • Docker image built and tagged
  • Image pushed to container registry
  • Kubernetes manifests applied
  • Rolling update started
  • Pods healthy and ready
  • Docker image built and tagged
  • Image pushed to container registry
  • Kubernetes manifests applied
  • Rolling update started
  • Pods healthy and ready

Post-Deployment

Post-Deployment

  • Health check endpoint responding
  • Metrics/logs monitoring active
  • Performance baseline established
  • Old pods terminated (after grace period)
  • Deployment documented in changelog
undefined
  • Health check endpoint responding
  • Metrics/logs monitoring active
  • Performance baseline established
  • Old pods terminated (after grace period)
  • Deployment documented in changelog
undefined

Constraints

约束条件

필수 규칙 (MUST)

必须遵守的规则(MUST)

  1. Health Checks: 모든 서비스에 health check 엔드포인트
    typescript
    app.get('/health', (req, res) => {
      res.status(200).json({ status: 'ok' });
    });
  2. Graceful Shutdown: SIGTERM 신호 처리
    javascript
    process.on('SIGTERM', async () => {
      console.log('SIGTERM received, shutting down gracefully');
      await server.close();
      await db.close();
      process.exit(0);
    });
  3. 환경변수 분리: 하드코딩 금지, .env 사용
  1. 健康检查:所有服务都需配置health check端点
    typescript
    app.get('/health', (req, res) => {
      res.status(200).json({ status: 'ok' });
    });
  2. 优雅停机:处理SIGTERM信号
    javascript
    process.on('SIGTERM', async () => {
      console.log('SIGTERM received, shutting down gracefully');
      await server.close();
      await db.close();
      process.exit(0);
    });
  3. 环境变量分离:禁止硬编码,使用.env文件

금지 사항 (MUST NOT)

禁止事项(MUST NOT)

  1. Secrets 커밋 금지: API 키, 비밀번호를 Git에 절대 커밋하지 않음
  2. 프로덕션에서 디버그 모드:
    NODE_ENV=production
    필수
  3. latest 태그만 사용: 버전 태그 사용 (v1.0.0, sha-abc123)
  1. 禁止提交密钥:绝对不要将API密钥、密码提交到Git
  2. 生产环境禁用调试模式:必须设置
    NODE_ENV=production
  3. 禁止仅使用latest标签:使用版本标签(如v1.0.0、sha-abc123)

Best practices

最佳实践

  1. Multi-stage Docker builds: 이미지 크기 최소화
  2. Immutable infrastructure: 서버 수정 대신 새로 배포
  3. Blue-Green deployment: 무중단 배포 및 쉬운 롤백
  4. Monitoring 필수: Prometheus, Grafana, Datadog
  1. 多阶段Docker构建:最小化镜像体积
  2. 不可变基础设施:不修改服务器,而是重新部署
  3. 蓝绿部署:实现无缝部署与快速回滚
  4. 必须监控:使用Prometheus、Grafana、Datadog

References

参考资料

Metadata

元数据

버전

版本

  • 현재 버전: 1.0.0
  • 최종 업데이트: 2025-01-01
  • 호환 플랫폼: Claude, ChatGPT, Gemini
  • 当前版本:1.0.0
  • 最后更新:2025-01-01
  • 兼容平台:Claude、ChatGPT、Gemini

관련 스킬

相关技能

  • monitoring: 배포 후 모니터링
  • security: 배포 보안
  • monitoring:部署后监控
  • security:部署安全

태그

标签

#deployment
#CI/CD
#Docker
#Kubernetes
#automation
#infrastructure
#deployment
#CI/CD
#Docker
#Kubernetes
#automation
#infrastructure

Examples

示例

Example 1: Basic usage

示例1:基础用法

<!-- Add example content here -->
<!-- Add example content here -->

Example 2: Advanced usage

示例2:进阶用法

<!-- Add advanced example content here -->
<!-- Add advanced example content here -->