MonkeyCode容器化部署实战:从Dockerfile到Kubernetes上云
·
容器化是现代部署的标配
还在用scp把jar包传到服务器上?还在手动配环境?容器化让部署变得可重复、可回滚、可扩展。但Dockerfile写不好,镜像1GB+;K8s配置不对,服务天天重启。
这篇文章用MonkeyCode生成完整的容器化部署方案,从Dockerfile优化到K8s上云。
给MonkeyCode的统一Prompt
为Python Web应用生成完整的容器化部署方案,要求:
1. 多阶段构建Dockerfile(最小镜像)
2. Docker Compose本地开发环境
3. Kubernetes部署YAML(Deployment + Service + Ingress)
4. 健康检查和就绪探针
5. 资源限制(CPU/内存)
6. 水平自动扩缩容(HPA)
7. ConfigMap和Secret管理
8. 滚动更新策略
9. 日志收集配置
应用:FastAPI后端 + Celery异步任务 + Redis + PostgreSQL
1. 多阶段构建Dockerfile
# Dockerfile - MonkeyCode生成
# ===== 阶段1:构建依赖 =====
FROM python:3.11-slim AS builder
WORKDIR /build
# 先复制依赖文件(利用Docker缓存层)
COPY requirements.txt .
# 安装依赖到虚拟环境
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# ===== 阶段2:运行时镜像 =====
FROM python:3.11-slim AS runtime
# 安装系统运行时依赖(仅必需)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libpq5 \
curl && \
rm -rf /var/lib/apt/lists/*
# 从builder复制虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
WORKDIR /app
# 复制应用代码
COPY --chown=appuser:appuser . .
# 切换到非root用户
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Dockerfile优化对比
| 优化项 | 优化前 | 优化后 | 效果 |
|---|---|---|---|
| 基础镜像 | python:3.11 (1.02GB) | python:3.11-slim (150MB) | -85% |
| 多阶段构建 | 无 | builder + runtime | 去除编译工具链 |
| pip缓存 | 保留 | --no-cache-dir | -200MB |
| apt缓存 | 保留 | rm -rf | -50MB |
| 运行用户 | root | appuser | 安全提升 |
| 最终镜像 | 1.2GB | 180MB | -85% |
2. Docker Compose本地开发
# docker-compose.yml - MonkeyCode生成
version: '3.8'
services:
# PostgreSQL数据库
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD:-devpassword}
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"]
interval: 5s
timeout: 5s
retries: 5
# Redis缓存
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD:-devpassword} --maxmemory 256mb --maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-devpassword}", "ping"]
interval: 5s
timeout: 3s
retries: 5
# FastAPI后端
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql+asyncpg://appuser:${DB_PASSWORD:-devpassword}@postgres:5432/myapp
- REDIS_URL=redis://:${REDIS_PASSWORD:-devpassword}@redis:6379/0
- CELERY_BROKER_URL=redis://:${REDIS_PASSWORD:-devpassword}@redis:6379/1
- ENVIRONMENT=development
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ./app:/app/app # 开发时热重载
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
# Celery Worker
celery-worker:
build:
context: .
dockerfile: Dockerfile
environment:
- DATABASE_URL=postgresql+asyncpg://appuser:${DB_PASSWORD:-devpassword}@postgres:5432/myapp
- REDIS_URL=redis://:${REDIS_PASSWORD:-devpassword}@redis:6379/0
- CELERY_BROKER_URL=redis://:${REDIS_PASSWORD:-devpassword}@redis:6379/1
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
command: celery -A app.celery_app worker --loglevel=info --concurrency=4
# Celery Beat(定时任务)
celery-beat:
build:
context: .
dockerfile: Dockerfile
environment:
- CELERY_BROKER_URL=redis://:${REDIS_PASSWORD:-devpassword}@redis:6379/1
depends_on:
redis:
condition: service_healthy
command: celery -A app.celery_app beat --loglevel=info
# Flower(Celery监控)
flower:
build:
context: .
dockerfile: Dockerfile
ports:
- "5555:5555"
environment:
- CELERY_BROKER_URL=redis://:${REDIS_PASSWORD:-devpassword}@redis:6379/1
depends_on:
redis:
condition: service_healthy
command: celery -A app.celery_app flower --port=5555
volumes:
postgres-data:
redis-data:
3. Kubernetes部署
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: myapp-production
labels:
environment: production
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: myapp-production
data:
ENVIRONMENT: "production"
LOG_LEVEL: "info"
DATABASE_HOST: "postgres-service"
DATABASE_PORT: "5432"
DATABASE_NAME: "myapp"
REDIS_HOST: "redis-service"
REDIS_PORT: "6379"
REDIS_DB: "0"
CELERY_BROKER_DB: "1"
UVICORN_WORKERS: "4"
---
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
namespace: myapp-production
type: Opaque
stringData:
DATABASE_URL: "postgresql+asyncpg://appuser:CHANGE_ME@postgres-service:5432/myapp"
REDIS_URL: "redis://:CHANGE_ME@redis-service:6379/0"
CELERY_BROKER_URL: "redis://:CHANGE_ME@redis-service:6379/1"
SECRET_KEY: "CHANGE_ME_TO_RANDOM_STRING"
# k8s/api-deployment.yaml - MonkeyCode生成
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-api
namespace: myapp-production
labels:
app: myapp
component: api
spec:
replicas: 3
selector:
matchLabels:
app: myapp
component: api
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 滚动更新时最多多出1个Pod
maxUnavailable: 0 # 更新期间不允许有Pod不可用
template:
metadata:
labels:
app: myapp
component: api
spec:
containers:
- name: api
image: registry.example.com/myapp-api:latest
ports:
- containerPort: 8000
protocol: TCP
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secrets
resources:
requests:
cpu: "250m" # 0.25核
memory: "256Mi"
limits:
cpu: "1000m" # 1核
memory: "512Mi"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
terminationGracePeriodSeconds: 30
# k8s/api-service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-api-service
namespace: myapp-production
spec:
selector:
app: myapp
component: api
ports:
- port: 80
targetPort: 8000
protocol: TCP
type: ClusterIP
---
# Ingress(Nginx Ingress Controller)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: myapp-production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/rate-limit: "100"
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.myapp.com
secretName: myapp-tls
rules:
- host: api.myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-api-service
port:
number: 80
4. 水平自动扩缩容(HPA)
# k8s/hpa.yaml - MonkeyCode生成
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-api-hpa
namespace: myapp-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp-api
minReplicas: 3
maxReplicas: 20
metrics:
# CPU使用率超过70%时扩容
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# 内存使用率超过80%时扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# 基于QPS自定义指标(需安装Prometheus Adapter)
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 2
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # 缩容冷却5分钟
policies:
- type: Percent
value: 10
periodSeconds: 60
5. Celery Worker部署
更多推荐
所有评论(0)