018-Docker容器化部署
·
018-Docker容器化部署
学习目标
通过本章节的学习,您将能够:
- 理解Poetry项目容器化的最佳实践
- 掌握多阶段Docker构建优化技巧
- 学会创建高效、安全的生产级Docker镜像
- 实现容器编排和服务发现
- 优化镜像大小和构建时间
- 处理容器环境下的依赖管理
- 实现容器化的开发和部署工作流
Docker与Poetry概述
容器化的优势
一致性环境:
- 开发、测试、生产环境完全一致
- 消除"在我机器上能运行"的问题
- 简化部署和扩展
依赖隔离:
- 完全隔离的运行环境
- 避免系统依赖冲突
- 精确的版本控制
可移植性:
- 跨平台部署
- 云原生支持
- 微服务架构友好
Poetry在容器中的挑战
# 常见挑战和解决方案
挑战:
- 镜像大小过大
- 构建时间过长
- 依赖缓存失效
- 安全漏洞风险
- 多阶段构建复杂性
解决方案:
- 多阶段构建
- 智能缓存策略
- 最小化基础镜像
- 安全扫描集成
- 构建优化技巧
基础Dockerfile配置
简单Dockerfile
# Dockerfile (基础版本)
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 安装Poetry
RUN pip install poetry==1.7.1
# 配置Poetry
ENV POETRY_NO_INTERACTION=1 \
POETRY_VENV_IN_PROJECT=1 \
POETRY_CACHE_DIR=/tmp/poetry_cache
# 复制依赖文件
COPY pyproject.toml poetry.lock ./
# 安装依赖
RUN poetry install --only=main && rm -rf $POETRY_CACHE_DIR
# 复制应用代码
COPY src/ ./src/
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["poetry", "run", "python", "-m", "src.main"]
优化的Dockerfile
# Dockerfile (优化版本)
FROM python:3.11-slim as base
# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# 安装系统依赖
RUN apt-get update && apt-get install -y \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/*
# Poetry安装阶段
FROM base as poetry
ENV POETRY_HOME="/opt/poetry" \
POETRY_VERSION=1.7.1
# 安装Poetry
RUN curl -sSL https://install.python-poetry.org | python3 -
ENV PATH="$POETRY_HOME/bin:$PATH"
# 配置Poetry
ENV POETRY_NO_INTERACTION=1 \
POETRY_VENV_IN_PROJECT=1 \
POETRY_CACHE_DIR=/opt/poetry_cache
# 依赖安装阶段
FROM poetry as deps
WORKDIR /app
# 复制依赖文件
COPY pyproject.toml poetry.lock ./
# 安装依赖
RUN poetry install --only=main --no-root && rm -rf $POETRY_CACHE_DIR
# 生产阶段
FROM python:3.11-slim as production
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 设置工作目录
WORKDIR /app
# 从deps阶段复制虚拟环境
COPY --from=deps /app/.venv /app/.venv
# 复制应用代码
COPY src/ ./src/
COPY pyproject.toml ./
# 设置PATH以使用虚拟环境
ENV PATH="/app/.venv/bin:$PATH"
# 切换到非root用户
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:8000/health')" || exit 1
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["python", "-m", "src.main"]
高级多阶段构建
完整的多阶段构建
# Dockerfile (完整多阶段版本)
# 基础镜像
FROM python:3.11-slim as base
# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
DEBIAN_FRONTEND=noninteractive
# 安装系统依赖
RUN apt-get update && apt-get install -y \
build-essential \
curl \
git \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Poetry安装阶段
FROM base as poetry-base
ENV POETRY_HOME="/opt/poetry" \
POETRY_VERSION=1.7.1 \
POETRY_CACHE_DIR=/opt/poetry_cache
# 安装Poetry
RUN curl -sSL https://install.python-poetry.org | python3 - \
&& ln -s $POETRY_HOME/bin/poetry /usr/local/bin/poetry
# 配置Poetry
ENV POETRY_NO_INTERACTION=1 \
POETRY_VENV_IN_PROJECT=1
# 开发依赖阶段
FROM poetry-base as dev-deps
WORKDIR /app
# 复制依赖文件
COPY pyproject.toml poetry.lock ./
# 安装所有依赖(包括开发依赖)
RUN poetry install --with dev,test && rm -rf $POETRY_CACHE_DIR
# 生产依赖阶段
FROM poetry-base as prod-deps
WORKDIR /app
# 复制依赖文件
COPY pyproject.toml poetry.lock ./
# 只安装生产依赖
RUN poetry install --only=main --no-root && rm -rf $POETRY_CACHE_DIR
# 测试阶段
FROM dev-deps as test
# 复制源代码
COPY src/ ./src/
COPY tests/ ./tests/
COPY pyproject.toml ./
# 运行测试
RUN poetry run pytest tests/ --cov=src --cov-report=xml
# 构建阶段
FROM prod-deps as build
# 复制源代码
COPY src/ ./src/
COPY README.md LICENSE pyproject.toml ./
# 构建包
RUN poetry build
# 生产阶段
FROM python:3.11-slim as production
# 创建用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 安装运行时依赖
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# 设置工作目录
WORKDIR /app
# 从构建阶段复制虚拟环境
COPY --from=prod-deps /app/.venv /app/.venv
# 复制应用代码
COPY --from=build /app/src ./src
COPY --from=build /app/pyproject.toml ./
# 设置环境变量
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONPATH="/app"
# 创建必要的目录
RUN mkdir -p /app/logs /app/data \
&& chown -R appuser:appuser /app
# 切换到非root用户
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["python", "-m", "src.main"]
# 开发阶段(可选)
FROM dev-deps as development
# 复制源代码
COPY . .
# 安装项目
RUN poetry install
# 设置开发环境变量
ENV FLASK_ENV=development \
FLASK_DEBUG=1
# 启动开发服务器
CMD ["poetry", "run", "python", "-m", "src.main", "--reload"]
针对不同环境的构建目标
# 构建脚本
#!/bin/bash
# build.sh
set -e
# 默认参数
TARGET="production"
TAG="latest"
REGISTRY=""
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
--target)
TARGET="$2"
shift 2
;;
--tag)
TAG="$2"
shift 2
;;
--registry)
REGISTRY="$2"
shift 2
;;
*)
echo "未知参数: $1"
exit 1
;;
esac
done
# 构建镜像
echo "构建目标: $TARGET"
echo "标签: $TAG"
case $TARGET in
development)
docker build --target development -t myapp:dev-$TAG .
;;
test)
docker build --target test -t myapp:test-$TAG .
;;
production)
docker build --target production -t myapp:$TAG .
if [ -n "$REGISTRY" ]; then
docker tag myapp:$TAG $REGISTRY/myapp:$TAG
docker push $REGISTRY/myapp:$TAG
fi
;;
*)
echo "无效的构建目标: $TARGET"
exit 1
;;
esac
echo "构建完成!"
Docker Compose配置
开发环境配置
# docker-compose.dev.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: development
ports:
- "8000:8000"
volumes:
- .:/app
- poetry_cache:/opt/poetry_cache
environment:
- ENV=development
- DEBUG=true
- DATABASE_URL=postgresql://user:password@db:5432/myapp_dev
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
command: poetry run python -m src.main --reload
db:
image: postgres:15
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025"
- "8025:8025"
volumes:
postgres_dev_data:
poetry_cache:
生产环境配置
# docker-compose.prod.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "8000:8000"
environment:
- ENV=production
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
- redis
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:15
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
restart: unless-stopped
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
restart: unless-stopped
volumes:
postgres_data:
redis_data:
测试环境配置
# docker-compose.test.yml
version: '3.8'
services:
test:
build:
context: .
dockerfile: Dockerfile
target: test
environment:
- ENV=test
- DATABASE_URL=postgresql://test:test@test_db:5432/test_db
depends_on:
- test_db
command: poetry run pytest tests/ --cov=src --cov-report=xml
test_db:
image: postgres:15
environment:
POSTGRES_DB: test_db
POSTGRES_USER: test
POSTGRES_PASSWORD: test
tmpfs:
- /var/lib/postgresql/data
integration_test:
build:
context: .
dockerfile: Dockerfile
target: development
environment:
- ENV=test
- DATABASE_URL=postgresql://test:test@test_db:5432/test_db
- REDIS_URL=redis://test_redis:6379/0
depends_on:
- test_db
- test_redis
command: poetry run pytest tests/integration/ -v
test_redis:
image: redis:7-alpine
command: redis-server --save ""
镜像优化策略
大小优化
# 优化镜像大小的Dockerfile
FROM python:3.11-alpine as base
# 安装系统依赖(Alpine版本)
RUN apk add --no-cache \
gcc \
musl-dev \
libffi-dev \
openssl-dev \
curl
# Poetry安装
FROM base as poetry
RUN pip install poetry==1.7.1
ENV POETRY_NO_INTERACTION=1 \
POETRY_VENV_IN_PROJECT=1
# 依赖安装
FROM poetry as deps
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN poetry install --only=main --no-root \
&& find /app/.venv -name "*.pyc" -delete \
&& find /app/.venv -name "__pycache__" -type d -exec rm -rf {} + \
&& rm -rf /root/.cache
# 最终镜像
FROM python:3.11-alpine as production
# 只安装运行时依赖
RUN apk add --no-cache curl
# 创建用户
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
WORKDIR /app
# 复制虚拟环境和代码
COPY --from=deps /app/.venv /app/.venv
COPY src/ ./src/
# 设置权限
RUN chown -R appuser:appuser /app
USER appuser
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 8000
CMD ["python", "-m", "src.main"]
构建时间优化
# 构建时间优化的Dockerfile
FROM python:3.11-slim as base
# 使用BuildKit缓存挂载
FROM base as poetry
RUN --mount=type=cache,target=/root/.cache/pip \
pip install poetry==1.7.1
FROM poetry as deps
WORKDIR /app
# 利用Docker层缓存
COPY pyproject.toml poetry.lock ./
# 使用Poetry缓存挂载
RUN --mount=type=cache,target=/root/.cache/pypoetry \
poetry install --only=main --no-root
# 并行构建优化
FROM deps as app
COPY src/ ./src/
RUN poetry install --only=main
# 最终镜像
FROM python:3.11-slim as production
COPY --from=app /app/.venv /app/.venv
COPY --from=app /app/src /app/src
WORKDIR /app
ENV PATH="/app/.venv/bin:$PATH"
CMD ["python", "-m", "src.main"]
安全优化
# 安全优化的Dockerfile
FROM python:3.11-slim as base
# 更新系统包
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# 创建非root用户
RUN groupadd -r appuser && useradd -r -g appuser appuser
FROM base as poetry
USER root
RUN pip install poetry==1.7.1
USER appuser
FROM poetry as deps
WORKDIR /app
USER root
COPY pyproject.toml poetry.lock ./
RUN poetry install --only=main --no-root
USER appuser
FROM python:3.11-slim as production
# 安全更新
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
# 复制文件并设置权限
COPY --from=deps --chown=appuser:appuser /app/.venv /app/.venv
COPY --chown=appuser:appuser src/ ./src/
# 移除不必要的权限
RUN chmod -R 755 /app && \
find /app -type f -exec chmod 644 {} \;
USER appuser
# 安全配置
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
EXPOSE 8000
CMD ["python", "-m", "src.main"]
容器编排
Kubernetes部署
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: myapp
---
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: myapp
data:
ENV: "production"
LOG_LEVEL: "INFO"
WORKERS: "4"
---
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
namespace: myapp
type: Opaque
data:
DATABASE_URL: <base64-encoded-url>
SECRET_KEY: <base64-encoded-key>
---
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:latest
ports:
- containerPort: 8000
env:
- name: ENV
valueFrom:
configMapKeyRef:
name: myapp-config
key: ENV
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: DATABASE_URL
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: myapp
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: ClusterIP
---
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: myapp
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
Docker Swarm部署
# docker-stack.yml
version: '3.8'
services:
app:
image: myregistry/myapp:latest
ports:
- "8000:8000"
environment:
- ENV=production
secrets:
- database_url
- secret_key
configs:
- source: app_config
target: /app/config.toml
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- app_network
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
networks:
- app_network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
configs:
- source: nginx_config
target: /etc/nginx/nginx.conf
deploy:
replicas: 2
networks:
- app_network
networks:
app_network:
driver: overlay
volumes:
postgres_data:
secrets:
database_url:
external: true
secret_key:
external: true
db_password:
external: true
configs:
app_config:
external: true
nginx_config:
external: true
开发工作流
本地开发环境
#!/bin/bash
# dev-setup.sh
set -e
echo "设置本地开发环境..."
# 构建开发镜像
docker-compose -f docker-compose.dev.yml build
# 启动服务
docker-compose -f docker-compose.dev.yml up -d
# 等待服务启动
echo "等待服务启动..."
sleep 10
# 运行数据库迁移
docker-compose -f docker-compose.dev.yml exec app poetry run alembic upgrade head
# 创建测试数据
docker-compose -f docker-compose.dev.yml exec app poetry run python scripts/create_test_data.py
echo "开发环境已就绪!"
echo "应用地址: http://localhost:8000"
echo "数据库地址: localhost:5432"
echo "邮件测试: http://localhost:8025"
热重载开发
# Dockerfile.dev
FROM python:3.11-slim
WORKDIR /app
# 安装Poetry
RUN pip install poetry==1.7.1
# 配置Poetry
ENV POETRY_NO_INTERACTION=1 \
POETRY_VENV_IN_PROJECT=1
# 复制依赖文件
COPY pyproject.toml poetry.lock ./
# 安装依赖(包括开发依赖)
RUN poetry install
# 安装开发工具
RUN poetry add --group dev watchdog[watchmedo]
# 复制源代码(在运行时通过volume挂载)
COPY . .
# 启动开发服务器
CMD ["poetry", "run", "watchmedo", "auto-restart", "--directory=./src", "--pattern=*.py", "--recursive", "--", "python", "-m", "src.main"]
测试工作流
#!/bin/bash
# test.sh
set -e
echo "运行测试套件..."
# 构建测试镜像
docker-compose -f docker-compose.test.yml build
# 运行单元测试
echo "运行单元测试..."
docker-compose -f docker-compose.test.yml run --rm test
# 运行集成测试
echo "运行集成测试..."
docker-compose -f docker-compose.test.yml run --rm integration_test
# 运行端到端测试
echo "运行端到端测试..."
docker-compose -f docker-compose.test.yml run --rm e2e_test
# 清理
docker-compose -f docker-compose.test.yml down -v
echo "所有测试完成!"
生产部署策略
蓝绿部署
#!/bin/bash
# blue-green-deploy.sh
set -e
REGISTRY="myregistry.com"
APP_NAME="myapp"
NEW_VERSION=$1
if [ -z "$NEW_VERSION" ]; then
echo "使用方法: $0 <version>"
exit 1
fi
# 获取当前活跃环境
CURRENT_ENV=$(docker service ls --filter name=${APP_NAME} --format "{{.Name}}" | grep -E "(blue|green)" | head -1)
if [[ $CURRENT_ENV == *"blue"* ]]; then
NEW_ENV="green"
OLD_ENV="blue"
else
NEW_ENV="blue"
OLD_ENV="green"
fi
echo "当前环境: $OLD_ENV"
echo "新环境: $NEW_ENV"
echo "部署版本: $NEW_VERSION"
# 部署新版本到新环境
echo "部署到 $NEW_ENV 环境..."
docker service create \
--name ${APP_NAME}-${NEW_ENV} \
--replicas 3 \
--network app_network \
--label environment=$NEW_ENV \
${REGISTRY}/${APP_NAME}:${NEW_VERSION}
# 等待新环境就绪
echo "等待新环境就绪..."
sleep 30
# 健康检查
echo "执行健康检查..."
for i in {1..10}; do
if curl -f http://${APP_NAME}-${NEW_ENV}:8000/health; then
echo "健康检查通过"
break
fi
if [ $i -eq 10 ]; then
echo "健康检查失败,回滚部署"
docker service rm ${APP_NAME}-${NEW_ENV}
exit 1
fi
sleep 10
done
# 切换流量
echo "切换流量到新环境..."
docker service update \
--label-add active=true \
${APP_NAME}-${NEW_ENV}
docker service update \
--label-rm active \
${APP_NAME}-${OLD_ENV}
# 更新负载均衡器配置
echo "更新负载均衡器..."
# 这里需要根据实际的负载均衡器进行配置
# 等待流量切换完成
sleep 30
# 移除旧环境
echo "移除旧环境..."
docker service rm ${APP_NAME}-${OLD_ENV}
echo "部署完成!"
滚动更新
#!/bin/bash
# rolling-update.sh
set -e
REGISTRY="myregistry.com"
APP_NAME="myapp"
NEW_VERSION=$1
if [ -z "$NEW_VERSION" ]; then
echo "使用方法: $0 <version>"
exit 1
fi
echo "开始滚动更新到版本: $NEW_VERSION"
# 更新Docker服务
docker service update \
--image ${REGISTRY}/${APP_NAME}:${NEW_VERSION} \
--update-parallelism 1 \
--update-delay 30s \
--update-failure-action rollback \
--update-monitor 60s \
${APP_NAME}
# 监控更新状态
echo "监控更新进度..."
while true; do
STATUS=$(docker service inspect ${APP_NAME} --format '{{.UpdateStatus.State}}')
case $STATUS in
"completed")
echo "更新完成!"
break
;;
"rollback_completed")
echo "更新失败,已回滚"
exit 1
;;
"updating")
echo "更新中..."
sleep 10
;;
*)
echo "未知状态: $STATUS"
sleep 5
;;
esac
done
# 验证部署
echo "验证部署..."
docker service ps ${APP_NAME}
监控和日志
容器监控
# monitoring/docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
prometheus_data:
grafana_data:
日志聚合
# logging/docker-compose.logging.yml
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
logstash:
image: docker.elastic.co/logstash/logstash:8.5.0
ports:
- "5000:5000"
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
depends_on:
- elasticsearch
kibana:
image: docker.elastic.co/kibana/kibana:8.5.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
filebeat:
image: docker.elastic.co/beats/filebeat:8.5.0
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- logstash
volumes:
elasticsearch_data:
安全最佳实践
镜像安全扫描
#!/bin/bash
# security-scan.sh
set -e
IMAGE_NAME=$1
if [ -z "$IMAGE_NAME" ]; then
echo "使用方法: $0 <image_name>"
exit 1
fi
echo "扫描镜像: $IMAGE_NAME"
# 使用Trivy扫描
echo "运行Trivy安全扫描..."
trivy image --severity HIGH,CRITICAL $IMAGE_NAME
# 使用Clair扫描
echo "运行Clair安全扫描..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
quay.io/coreos/clair-local-scan:latest \
--clair-addr=http://clair:6060 \
$IMAGE_NAME
# 使用Snyk扫描
echo "运行Snyk安全扫描..."
snyk container test $IMAGE_NAME
echo "安全扫描完成"
运行时安全
# security/docker-compose.security.yml
version: '3.8'
services:
app:
image: myapp:latest
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
- /var/run
user: "1000:1000"
environment:
- PYTHONDONTWRITEBYTECODE=1
- PYTHONUNBUFFERED=1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
实践练习
练习1:基础容器化
创建一个简单的FastAPI应用并容器化:
# src/main.py
from fastapi import FastAPI
import uvicorn
app = FastAPI(title="My Poetry App")
@app.get("/")
def read_root():
return {"message": "Hello from Poetry Docker!"}
@app.get("/health")
def health_check():
return {"status": "healthy"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
# 练习Dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安装Poetry
RUN pip install poetry==1.7.1
# 配置Poetry
ENV POETRY_NO_INTERACTION=1 \
POETRY_VENV_IN_PROJECT=1
# 复制依赖文件
COPY pyproject.toml poetry.lock ./
# 安装依赖
RUN poetry install --only=main
# 复制应用代码
COPY src/ ./src/
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["poetry", "run", "python", "-m", "src.main"]
练习2:多阶段构建优化
优化上述Dockerfile,实现多阶段构建:
# 优化后的Dockerfile
FROM python:3.11-slim as base
# ... 实现多阶段构建
练习3:完整的开发环境
创建包含数据库、缓存、监控的完整开发环境:
# docker-compose.full.yml
version: '3.8'
# ... 完整的服务配置
总结
本章节详细介绍了Poetry项目的Docker容器化部署:
核心要点
-
容器化策略:
- 多阶段构建优化
- 镜像大小和安全优化
- 构建时间优化
-
编排部署:
- Docker Compose配置
- Kubernetes部署
- Docker Swarm集群
-
生产实践:
- 蓝绿部署策略
- 滚动更新机制
- 监控和日志管理
-
安全考虑:
- 镜像安全扫描
- 运行时安全配置
- 密钥管理策略
最佳实践
- 镜像优化:使用多阶段构建,最小化镜像大小
- 安全第一:非root用户运行,定期安全扫描
- 监控完善:健康检查、日志聚合、性能监控
- 自动化部署:CI/CD集成,自动化测试和部署
通过本章节的学习,您应该能够为Poetry项目构建高效、安全、可扩展的容器化部署方案。
下一步
在下一章节019-团队协作最佳实践中,我们将学习如何在团队环境中有效使用Poetry,包括代码规范、协作流程、版本管理和团队配置标准化。
更新时间: 2024-12-19 | 更新内容: 初始版本,包含完整的Docker容器化部署指南
更多推荐



所有评论(0)