Docker 与虚拟环境
13.1 Python 虚拟环境
容器内是否需要虚拟环境
# 问题:容器本身就是隔离环境,还需要 venv 吗?
# 答案:一般不需要
# Docker 容器已经是隔离的文件系统
# 但以下情况可能需要:
# 1. 多个 Python 项目共享一个基础镜像
# 2. CI/CD 中需要隔离构建环境
# 3. 开发环境中需要切换 Python 版本
在容器内使用 venv
FROM python:3.12-slim
WORKDIR /app
# 创建虚拟环境
RUN python -m venv /opt/venv
ENV PATH= "/opt/venv/bin:$PATH"
# 安装依赖(在 venv 中)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY . .
CMD [ "python" , "app.py" ]
requirements.txt 管理
# 生成 requirements.txt
pip freeze > requirements.txt
# 安装依赖
pip install -r requirements.txt
# 使用 pip-tools 固定版本
pip install pip-tools
pip-compile requirements.in
pip install -r requirements.txt
Docker Compose 中的 Python 环境
services :
api :
build : .
volumes :
- ./app:/app
- venv_data:/opt/venv # 持久化虚拟环境
command : python -m uvicorn main:app --reload
volumes :
venv_data :
13.2 Node.js 版本管理
Node 版本选择
# Dockerfile 中直接指定 Node 版本
FROM node:20-alpine # LTS 版本
FROM node:18-alpine # 老版本
FROM node:22-alpine # 最新版本
# 不需要 nvm
# Docker 的 FROM 指令就是最好的版本管理
容器内使用 nvm(不推荐)
# 不推荐:nvm 会增加镜像体积
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
ENV NVM_DIR=/root/.nvm
RUN . "$NVM_DIR/nvm.sh" && nvm install 20
# 推荐:直接用官方 Node 镜像
FROM node:20-alpine
13.3 数据库开发环境
一键拉起完整环境
# docker-compose.dev.yml
services :
# 后端 API
api :
build : .
ports :
- "8000:8000"
volumes :
- ./app:/app
environment :
DATABASE_URL : postgresql://dev:dev@postgres:5432/mydb
REDIS_URL : redis://redis:6379
depends_on :
postgres :
condition : service_healthy
redis :
condition : service_healthy
command : python -m uvicorn main:app --reload
# PostgreSQL
postgres :
image : postgres:16-alpine
environment :
POSTGRES_DB : mydb
POSTGRES_USER : dev
POSTGRES_PASSWORD : dev
ports :
- "5432:5432"
volumes :
- pg_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck :
test : [ "CMD-SHELL" , "pg_isready -U dev -d mydb" ]
interval : 5s
timeout : 5s
retries : 5
# Redis
redis :
image : redis:7-alpine
ports :
- "6379:6379"
volumes :
- redis_data:/data
# phpMyAdmin(可选)
phpmyadmin :
image : phpmyadmin:latest
ports :
- "8080:80"
environment :
PMA_HOST : postgres
PMA_PORT : 5432
depends_on :
- postgres
volumes :
pg_data :
redis_data :
# 启动完整环境
docker compose -f docker-compose.dev.yml up -d
# 查看状态
docker compose ps
# 连接数据库
docker compose exec postgres psql -U dev -d mydb
# 停止并清理
docker compose down -v
数据库初始化脚本
-- init.sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY ,
name VARCHAR ( 100 ) NOT NULL ,
email VARCHAR ( 100 ) UNIQUE NOT NULL ,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users ( name , email) VALUES
( 'Alice' , 'alice@example.com' ),
( 'Bob' , 'bob@example.com' );
13.4 开发环境一键拉起
热重载配置
# docker-compose.dev.yml
services :
node-app :
build :
context : .
target : development
volumes :
- ./src:/app/src # 代码热重载
- /app/node_modules # 排除 node_modules
command : npm run dev
ports :
- "3000:3000"
- "9229:9229" # 调试端口
python-app :
build : .
volumes :
- ./app:/app # 代码热重载
command : python -m uvicorn main:app --reload --host 0.0.0.0
ports :
- "8000:8000"
VS Code Dev Containers
// .devcontainer/devcontainer.json
{
"name" : "My App" ,
"dockerComposeFile" : "docker-compose.dev.yml" ,
"service" : "api" ,
"workspaceFolder" : "/app" ,
"customizations" : {
"vscode" : {
"extensions" : [
"ms-python.python" ,
"dbaeumer.vscode-eslint"
]
}
}
}
13.5 CI/CD 中的 Docker
GitHub Actions 示例
# .github/workflows/docker.yml
name : Build and Push Docker Image
on :
push :
branches : [ main ]
jobs :
build :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
- name : Login to Docker Hub
uses : docker/login-action@v3
with :
username : ${{ secrets.DOCKER_USERNAME }}
password : ${{ secrets.DOCKER_PASSWORD }}
- name : Build and push
uses : docker/build-push-action@v5
with :
context : .
push : true
tags : myuser/myapp:${{ github.sha }}
GitLab CI 示例
# .gitlab-ci.yml
build :
image : docker:latest
services :
- docker:dind
script :
- docker build -t myregistry/myapp:$CI_COMMIT_SHA .
- docker push myregistry/myapp:$CI_COMMIT_SHA
only :
- main
13.6 常见问题与排查
容器内权限问题
# 虚拟环境目录权限不足时
RUN chown -R appuser:appuser /opt/venv
USER appuser
# 或者使用 root(不推荐生产环境)
USER root
依赖安装失败排查
# 查看构建日志
docker compose build --no-cache api
# 进入容器检查
docker compose run --rm api bash
# 检查 Python 版本
python --version
# 检查 pip 安装路径
pip show fastapi
缓存优化技巧
# 先复制依赖文件,再复制代码
# 利用 Docker 层缓存,代码修改不会重新安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# npm 同理
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
开发与生产环境分离
# 多阶段构建
FROM node:20-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD [ "npm" , "run" , "dev" ]
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD [ "node" , "dist/index.js" ]
# 开发环境
docker compose --profile dev up
# 生产环境
docker compose --profile prod up