🐳 “Build once, run anywhere.”
🤔 为什么需要Docker
还记得2014年那个著名的梗吗?
“在我的机器上能跑!”
环境不一致导致的问题:
- 开发环境OK,测试环境报错
- Python版本不同导致依赖冲突
- 系统库缺失导致编译失败
- 新同事环境配置花了一整天
Docker解决了这一切。
🎯 Docker核心概念
镜像 vs 容器
| 概念 |
类比 |
说明 |
| 镜像(Image) |
类(Class) |
只读模板,包含应用和依赖 |
| 容器(Container) |
对象(Object) |
镜像的运行实例 |
| 仓库(Registry) |
GitHub |
存储和分发镜像的地方 |
| Dockerfile |
源代码 |
定义镜像的构建步骤 |
关键优势
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ┌─────────────────────────────────────────┐ │ 传统部署 │ │ App A → 需要 Python 3.9 │ │ App B → 需要 Python 3.11 ← 冲突! │ │ App C → 需要 Node 18 │ └─────────────────────────────────────────┘
┌─────────────────────────────────────────┐ │ Docker部署 │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │Container│ │Container│ │Container│ │ │ │ App A │ │ App B │ │ App C │ │ │ │Python3.9│ │Python3.11│ │ Node 18 │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ 互不干扰,独立运行 │ └─────────────────────────────────────────┘
|
🚀 快速入门
安装Docker
1 2 3 4 5 6 7 8 9
| curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER
brew install --cask docker
docker --version
|
第一个容器
1 2 3 4 5 6 7
| docker run hello-world
docker run -d -p 8080:80 nginx
|
常用命令速查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| docker pull ubuntu:22.04 docker images docker rmi image_id
docker ps docker ps -a docker run -d --name myapp nginx docker stop myapp docker rm myapp docker exec -it myapp bash
docker logs myapp docker stats docker system prune
|
💡 实战:容器化Python Web应用
场景
一个Flask应用,需要:
- Python 3.11
- Redis缓存
- PostgreSQL数据库
项目结构
1 2 3 4 5 6 7 8
| myapp/ ├── app/ │ ├── __init__.py │ ├── routes.py │ └── models.py ├── requirements.txt ├── Dockerfile └── docker-compose.yml
|
第一步:编写Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y \ gcc \ postgresql-client \ && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ CMD curl -f http://localhost:5000/health || exit 1
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
|
第二步:多阶段构建优化
生产环境使用多阶段构建,减小镜像体积:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| FROM python:3.11-slim as builder
WORKDIR /app
RUN apt-get update && apt-get install -y gcc
RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH"
COPY app/ ./app/
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app USER appuser
EXPOSE 5000
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
|
优化效果:
- 原始镜像:1.2GB
- 多阶段构建后:180MB
- 构建时间:减少40%
第三步:Docker Compose编排
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| version: '3.8'
services: web: build: . ports: - "5000:5000" environment: - DATABASE_URL=postgresql://postgres:password@db:5432/myapp - REDIS_URL=redis://redis:6379/0 - FLASK_ENV=production depends_on: - db - redis volumes: - ./logs:/app/logs restart: unless-stopped networks: - app-network
db: image: postgres:15-alpine environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password - POSTGRES_DB=myapp volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "5432:5432" restart: unless-stopped networks: - app-network
redis: image: redis:7-alpine volumes: - redis_data:/data restart: unless-stopped 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: - web restart: unless-stopped networks: - app-network
volumes: postgres_data: redis_data:
networks: app-network: driver: bridge
|
第四步:运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| docker-compose up -d --build
docker-compose ps
docker-compose logs -f web
docker-compose up -d --scale web=3
docker-compose down -v
|
🛠️ 生产环境最佳实践
1. 镜像安全
1 2 3 4 5 6 7 8 9
| FROM python:3.11.6-slim-bookworm
RUN useradd -m -u 1000 appuser USER appuser
docker scan myapp:latest
|
2. 资源限制
1 2 3 4 5 6 7 8 9 10
| services: web: deploy: resources: limits: cpus: '1' memory: 512M reservations: cpus: '0.5' memory: 256M
|
3. 日志管理
1 2 3 4 5 6 7
| services: web: logging: driver: "json-file" options: max-size: "10m" max-file: "3"
|
4. 健康检查
1 2
| HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:5000/health || exit 1
|
🚀 CI/CD集成
GitHub Actions工作流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| name: Build and Deploy
on: push: branches: [ main ]
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v4 with: context: . push: true tags: | ghcr.io/${{ github.repository }}:latest ghcr.io/${{ github.repository }}:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max
deploy: needs: build runs-on: ubuntu-latest steps: - name: Deploy to server uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_KEY }} script: | cd /opt/myapp docker-compose pull docker-compose up -d docker system prune -f
|
📊 性能优化技巧
1. 镜像分层优化
1 2 3 4 5 6 7 8 9 10
| FROM python:3.11-slim WORKDIR /app
COPY requirements.txt . RUN pip install -r requirements.txt
COPY . .
|
2. 使用.dockerignore
1 2 3 4 5 6 7 8 9 10 11
| .git .gitignore README.md Dockerfile .dockerignore node_modules __pycache__ *.pyc .env .vscode .idea
|
3. 多架构构建
1 2 3 4 5
| docker buildx create --use docker buildx build \ --platform linux/amd64,linux/arm64 \ -t myapp:latest \ --push .
|
⚠️ 常见陷阱
陷阱1:镜像体积过大
问题:安装不必要的依赖
解决:使用多阶段构建,只保留运行必需的依赖
陷阱2:数据丢失
问题:容器重启后数据消失
解决:使用Volumes持久化数据
陷阱3:时区问题
问题:容器内时区不对
解决:
1 2
| ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
陷阱4:权限问题
问题:容器内文件权限不对
解决:确保宿主机和容器用户UID一致
🎯 下一步学习
进阶主题
推荐资源
💭 写在最后
Docker彻底改变了我的开发和部署方式。
以前部署一个新应用,需要:
- 准备服务器环境
- 安装各种依赖
- 配置系统参数
- 祈祷不出错
现在只需要:
容器化不是银弹,但它解决了很多实际问题。
如果你还没开始使用Docker,我强烈建议现在就开始。它会让你的工作流顺畅很多。
有任何问题,欢迎在评论区交流!🐳
写于 2025年4月8日 | 容器化实践总结