Claude Code 部署指南
从开发到生产,掌握Claude Code项目的完整部署流程,包括容器化、云部署、监控等。
部署策略概览
🐳 容器化部署
使用Docker进行应用打包和部署
- 环境一致性
- 易于扩展
- 版本管理
☁️ 云平台部署
利用云服务提供商的PaaS服务
- 自动扩缩容
- 托管服务
- 高可用性
🚀 无服务器部署
使用Serverless架构
- 按需付费
- 自动扩展
- 零运维
Docker容器化
多阶段构建Dockerfile
# 多阶段构建优化镜像大小
FROM node:18-alpine AS builder
# 设置工作目录
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production && npm cache clean --force
# 复制源代码
COPY . .
# 构建应用
RUN npm run build
# 生产阶段
FROM node:18-alpine AS production
# 创建非root用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# 设置工作目录
WORKDIR /app
# 从构建阶段复制文件
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json
# 切换到非root用户
USER nextjs
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 启动应用
CMD ["npm", "start"]
Docker Compose配置
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
networks:
- app-network
db:
image: postgres:14-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
restart: unless-stopped
networks:
- app-network
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
云平台部署
AWS部署配置
# AWS ECS任务定义
{
"family": "claude-code-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "app",
"image": "your-account.dkr.ecr.region.amazonaws.com/claude-code-app:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
}
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:database-url"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/claude-code-app",
"awslogs-region": "us-west-2",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
Kubernetes部署
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: claude-code-app
labels:
app: claude-code-app
spec:
replicas: 3
selector:
matchLabels:
app: claude-code-app
template:
metadata:
labels:
app: claude-code-app
spec:
containers:
- name: app
image: your-registry/claude-code-app:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
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: claude-code-service
spec:
selector:
app: claude-code-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: claude-code-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- your-domain.com
secretName: claude-code-tls
rules:
- host: your-domain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: claude-code-service
port:
number: 80
CI/CD流水线
GitHub Actions部署流水线
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run security audit
run: npm audit --audit-level high
build:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image: ${{ steps.image.outputs.image }}
steps:
- uses: actions/checkout@v3
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Output image
id: image
run: echo "image=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}" >> $GITHUB_OUTPUT
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- name: Deploy to ECS
run: |
# 更新ECS服务
aws ecs update-service \
--cluster production-cluster \
--service claude-code-service \
--task-definition claude-code-app:${{ github.run_number }} \
--force-new-deployment
# 等待部署完成
aws ecs wait services-stable \
--cluster production-cluster \
--services claude-code-service
- name: Run smoke tests
run: |
# 等待服务启动
sleep 30
# 运行冒烟测试
curl -f https://your-domain.com/health || exit 1
curl -f https://your-domain.com/api/status || exit 1
- name: Notify deployment
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Deployment ${{ job.status }}!
Image: ${{ needs.build.outputs.image }}
Environment: Production
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
蓝绿部署策略
零停机部署
# 蓝绿部署脚本
#!/bin/bash
set -e
# 配置
CLUSTER_NAME="production-cluster"
SERVICE_NAME="claude-code-service"
NEW_IMAGE="$1"
if [ -z "$NEW_IMAGE" ]; then
echo "Usage: $0 "
exit 1
fi
echo "Starting blue-green deployment..."
# 获取当前任务定义
CURRENT_TASK_DEF=$(aws ecs describe-services \
--cluster $CLUSTER_NAME \
--services $SERVICE_NAME \
--query 'services[0].taskDefinition' \
--output text)
echo "Current task definition: $CURRENT_TASK_DEF"
# 创建新的任务定义
NEW_TASK_DEF=$(aws ecs describe-task-definition \
--task-definition $CURRENT_TASK_DEF \
--query 'taskDefinition' \
--output json | \
jq --arg IMAGE "$NEW_IMAGE" \
'.containerDefinitions[0].image = $IMAGE | del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .placementConstraints, .compatibilities, .registeredAt, .registeredBy)')
NEW_TASK_DEF_ARN=$(echo $NEW_TASK_DEF | aws ecs register-task-definition \
--cli-input-json file:///dev/stdin \
--query 'taskDefinition.taskDefinitionArn' \
--output text)
echo "New task definition: $NEW_TASK_DEF_ARN"
# 更新服务到新版本
echo "Updating service to new version..."
aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $SERVICE_NAME \
--task-definition $NEW_TASK_DEF_ARN
# 等待新版本稳定
echo "Waiting for new version to be stable..."
aws ecs wait services-stable \
--cluster $CLUSTER_NAME \
--services $SERVICE_NAME
# 健康检查
echo "Running health checks..."
for i in {1..5}; do
if curl -f https://your-domain.com/health; then
echo "Health check $i passed"
else
echo "Health check $i failed, rolling back..."
# 回滚到之前版本
aws ecs update-service \
--cluster $CLUSTER_NAME \
--service $SERVICE_NAME \
--task-definition $CURRENT_TASK_DEF
exit 1
fi
sleep 10
done
echo "Blue-green deployment completed successfully!"
监控与日志
应用监控配置
// 监控中间件
const prometheus = require('prom-client');
// 创建指标
const httpRequestDuration = new prometheus.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10]
});
const httpRequestTotal = new prometheus.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code']
});
const activeConnections = new prometheus.Gauge({
name: 'active_connections',
help: 'Number of active connections'
});
// 监控中间件
function monitoringMiddleware(req, res, next) {
const start = Date.now();
// 增加活跃连接数
activeConnections.inc();
// 监听响应结束
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
const route = req.route ? req.route.path : req.path;
// 记录指标
httpRequestDuration
.labels(req.method, route, res.statusCode)
.observe(duration);
httpRequestTotal
.labels(req.method, route, res.statusCode)
.inc();
// 减少活跃连接数
activeConnections.dec();
});
next();
}
// 健康检查端点
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.env.npm_package_version
});
});
// 指标端点
app.get('/metrics', (req, res) => {
res.set('Content-Type', prometheus.register.contentType);
res.end(prometheus.register.metrics());
});
日志配置
// 结构化日志配置
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'claude-code-app',
version: process.env.npm_package_version,
environment: process.env.NODE_ENV
},
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});
// 生产环境添加文件日志
if (process.env.NODE_ENV === 'production') {
logger.add(new winston.transports.File({
filename: 'logs/error.log',
level: 'error'
}));
logger.add(new winston.transports.File({
filename: 'logs/combined.log'
}));
}
// 请求日志中间件
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.info('HTTP Request', {
method: req.method,
url: req.url,
statusCode: res.statusCode,
duration,
userAgent: req.get('User-Agent'),
ip: req.ip,
userId: req.user?.id
});
});
next();
}
部署最佳实践
部署策略要点:
- ✅ 使用容器化确保环境一致性
- ✅ 实施自动化CI/CD流水线
- ✅ 采用蓝绿部署实现零停机更新
- ✅ 配置完善的监控和告警系统
- ✅ 实施安全扫描和漏洞检测
- ✅ 建立灾难恢复和备份策略