Docker 部署 #
基本概念 #
text
┌─────────────────────────────────────────────────────────────┐
│ Docker 部署流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 编写 Dockerfile │
│ 2. 构建镜像 │
│ 3. 运行容器 │
│ 4. 推送镜像 │
│ │
│ Dockerfile ────> Image ────> Container ────> Registry │
│ │
└─────────────────────────────────────────────────────────────┘
项目结构 #
text
my-api/
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── config.py
│ └── routers/
├── tests/
├── requirements.txt
├── Dockerfile
├── .dockerignore
└── docker-compose.yml
Dockerfile #
基本版本 #
dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
优化版本 #
dockerfile
FROM python:3.11-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
COPY ./app ./app
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
生产版本 #
dockerfile
FROM python:3.11-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends gcc
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN adduser --disabled-password --gecos '' appuser
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache-dir /wheels/*
COPY --chown=appuser:appuser ./app ./app
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
.dockerignore #
text
__pycache__
*.pyc
*.pyo
*.pyd
.Python
*.so
.env
.venv
venv/
.env
.git
.gitignore
.dockerignore
Dockerfile
docker-compose.yml
README.md
.pytest_cache
.coverage
htmlcov
*.log
Docker Compose #
开发环境 #
yaml
version: '3.8'
services:
api:
build: .
ports:
- "8000:8000"
volumes:
- ./app:/app/app
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
depends_on:
- db
- redis
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
postgres_data:
生产环境 #
yaml
version: '3.8'
services:
api:
image: my-api:latest
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
- redis
restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
restart: always
redis:
image: redis:7-alpine
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- api
restart: always
volumes:
postgres_data:
常用命令 #
构建镜像 #
bash
docker build -t my-api:latest .
docker build -t my-api:v1.0.0 .
运行容器 #
bash
docker run -d -p 8000:8000 --name my-api my-api:latest
docker run -d -p 8000:8000 -e DATABASE_URL=xxx my-api:latest
Docker Compose #
bash
docker-compose up -d
docker-compose up -d --build
docker-compose down
docker-compose logs -f api
查看日志 #
bash
docker logs my-api
docker logs -f my-api
进入容器 #
bash
docker exec -it my-api bash
docker exec -it my-api sh
Nginx 配置 #
nginx.conf #
nginx
events {
worker_connections 1024;
}
http {
upstream api {
server api:8000;
}
server {
listen 80;
server_name example.com;
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location / {
proxy_pass http://api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /docs {
proxy_pass http://api;
}
location /redoc {
proxy_pass http://api;
}
}
}
健康检查 #
添加健康检查端点 #
python
@app.get('/health')
def health_check():
return {'status': 'healthy'}
Dockerfile 健康检查 #
dockerfile
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
环境变量 #
使用 .env #
env
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
SECRET_KEY=your-secret-key
DEBUG=false
docker-compose.yml #
yaml
services:
api:
build: .
env_file:
- .env
Dockerfile ARG #
dockerfile
ARG APP_VERSION=1.0.0
ENV APP_VERSION=${APP_VERSION}
bash
docker build --build-arg APP_VERSION=2.0.0 -t my-api:latest .
完整示例 #
Dockerfile #
dockerfile
FROM python:3.11-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends gcc libpq-dev
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends libpq-dev && rm -rf /var/lib/apt/lists/*
RUN addgroup --system appgroup && adduser --system --group appuser
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache-dir /wheels/*
COPY --chown=appuser:appgroup ./app ./app
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
docker-compose.yml #
yaml
version: '3.8'
services:
api:
build: .
image: my-api:latest
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
- SECRET_KEY=${SECRET_KEY}
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: always
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: always
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: always
volumes:
postgres_data:
redis_data:
下一步 #
现在你已经掌握了 Docker 部署,接下来学习 Gunicorn 部署,了解生产环境部署!
最后更新:2026-03-29