Docker最佳实践
嘿!长安来总结Docker最佳实践了!这是我多年经验的精华,学会了你就是Docker高手!💪
🎯 安全实践
1. 不要使用root用户
# ❌ 危险:使用root运行
FROM node:18
WORKDIR /app
COPY . .
CMD ["node", "app.js"] # 默认root用户
# ✅ 安全:创建并使用非root用户
FROM node:18-alpine
WORKDIR /app
# 创建用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 复制文件并设置权限
COPY . .
# 切换用户
USER nodejs
CMD ["node", "app.js"]
2. 不要在镜像中存储敏感信息
# ❌ 危险:密码硬编码
FROM nginx
ENV DATABASE_PASSWORD=my-secret-password
# ✅ 安全:运行时传入
FROM nginx
# 不设置密码
使用时:
# 通过环境变量传入
docker run -e DATABASE_PASSWORD=secret my-app
# 或使用secrets(Docker Swarm/Kubernetes)
docker secret create db_password ./password.txt
永远不要:
- 把密码写在Dockerfile里
- 把密钥提交到Git
- 把.env文件打包进镜像
3. 扫描镜像漏洞
# 使用Docker Scout
docker scout quickview my-app
# 使用Trivy
trivy image my-app
# 使用Snyk
snyk container test my-app
4. 使用官方镜像
# ✅ 好:官方镜像,有安全保障
FROM node:18-alpine
FROM python:3.11-slim
FROM nginx:alpine
# ❌ 不好:不知名的镜像
FROM random-user/node-with-stuff
5. 保持镜像更新
# 定期拉取最新版本
docker pull node:18-alpine
# 重新构建镜像
docker build -t my-app .
6. 只暴露必要的端口
# ❌ 不好:暴露所有端口
EXPOSE 22 80 443 3000 3306 6379
# ✅ 好:只暴露必要的端口
EXPOSE 80
📦 镜像实践
1. 使用明确的版本标签
# ❌ 不好:latest标签(不确定)
FROM node:latest
# ✅ 好:明确版本
FROM node:18.17.0-alpine3.18
# ✅ 也可以:主版本号
FROM node:18-alpine
2. 保持镜像小巧
# 技巧1:使用alpine
FROM python:3.11-alpine # 50MB
# 而不是
FROM python:3.11 # 1GB
# 技巧2:多阶段构建
FROM node:18 AS builder
RUN npm run build
FROM nginx:alpine
COPY /app/dist /usr/share/nginx/html
# 技巧3:清理缓存
RUN apt-get update && \
apt-get install -y package && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
3. 合理组织层
# ✅ 好:按变化频率组织
FROM node:18-alpine
WORKDIR /app
# 1. 很少变化:系统依赖
RUN apk add --no-cache curl
# 2. 偶尔变化:应用依赖
COPY package*.json ./
RUN npm ci --only=production
# 3. 经常变化:源代码
COPY . .
CMD ["node", "app.js"]
4. 使用.dockerignore
# .dockerignore
**/.git
**/.DS_Store
**/node_modules
**/npm-debug.log
**/.env
**/.env.*
**/dist
**/build
**/coverage
**/.vscode
**/.idea
**/README.md
**/*.md
5. 单一职责原则
# ✅ 好:一个容器一个服务
docker run nginx # Web服务器
docker run mysql # 数据库
docker run redis # 缓存
# ❌ 不好:一个容器多个服务
# 不要在一个容器里同时运行nginx+mysql+redis
🎨 开发实践
1. 开发环境和生产环境分离
# docker-compose.yml(基础配置)
version: '3.8'
services:
app:
build: .
restart: unless-stopped
# docker-compose.dev.yml(开发环境)
version: '3.8'
services:
app:
volumes:
- .:/app # 代码热更新
environment:
NODE_ENV: development
ports:
- "3000:3000"
- "9229:9229" # 调试端口
# docker-compose.prod.yml(生产环境)
version: '3.8'
services:
app:
environment:
NODE_ENV: production
deploy:
resources:
limits:
cpus: '2'
memory: 1G
使用:
# 开发环境
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
# 生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
2. 使用健康检查
# Dockerfile
HEALTHCHECK \
CMD curl -f http://localhost/ || exit 1
# docker-compose.yml
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 40s
3. 使用标签管理容器
services:
app:
labels:
com.example.environment: "production"
com.example.team: "backend"
com.example.project: "myapp"
查询:
# 查找特定标签的容器
docker ps --filter "label=com.example.environment=production"
4. 日志管理
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
或使用集中式日志:
services:
app:
logging:
driver: "syslog"
options:
syslog-address: "tcp://logs.example.com:514"
5. 环境变量管理
# .env
DB_PASSWORD=secret
API_KEY=key123
# docker-compose.yml
services:
app:
env_file:
- .env
environment:
NODE_ENV: production
DB_HOST: db
DB_PASSWORD: ${DB_PASSWORD}
记得把.env加入.gitignore!
🚀 运维实践
1. 资源限制
services:
app:
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
或命令行:
docker run \
--memory="512m" \
--memory-swap="1g" \
--cpus="1.5" \
my-app
2. 重启策略
services:
app:
restart: unless-stopped # 推荐
critical-service:
restart: always # 关键服务
one-time-task:
restart: on-failure # 一次性任务
3. 数据备份
#!/bin/bash
# 备份脚本
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/backup
# 备份数据卷
docker run --rm \
-v mysql-data:/data \
-v $BACKUP_DIR:/backup \
alpine \
tar -czf /backup/mysql_$DATE.tar.gz -C /data .
# 备份数据库
docker exec mysql \
mysqldump -u root -p${DB_PASSWORD} --all-databases \
> $BACKUP_DIR/db_$DATE.sql
echo "备份完成: $DATE"
4. 监控
# docker-compose.yml
version: '3.8'
services:
# 应用
app:
image: my-app
# Prometheus监控
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
ports:
- "9090:9090"
# Grafana可视化
grafana:
image: grafana/grafana
volumes:
- grafana-data:/var/lib/grafana
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
prometheus-data:
grafana-data:
5. 定期清理
#!/bin/bash
# 清理脚本
echo "清理停止的容器..."
docker container prune -f
echo "清理未使用的镜像..."
docker image prune -a -f
echo "清理未使用的数据卷..."
docker volume prune -f
echo "清理未使用的网络..."
docker network prune -f
echo "清理构建缓存..."
docker builder prune -a -f
echo "清理完成!"
docker system df
📝 命名规范
1. 镜像命名
# 格式:项目名/服务名:版本
myproject/frontend:1.0.0
myproject/backend:1.0.0
myproject/nginx:latest
# 或者:公司名/项目名/服务名:版本
example-com/myproject/api:v1.2.3
2. 容器命名
services:
# 格式:项目-服务-环境
myproject-frontend-prod:
container_name: myproject-frontend-prod
myproject-backend-prod:
container_name: myproject-backend-prod
3. 数据卷命名
# 格式:项目-服务-用途
myproject-mysql-data
myproject-redis-data
myproject-nginx-logs
4. 网络命名
# 格式:项目-用途
myproject-frontend
myproject-backend
myproject-database
🎯 CI/CD实践
GitHub Actions示例
# .github/workflows/docker.yml
name: Docker Build and Push
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 DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
myapp:latest
myapp:${{ github.sha }}
cache-from: type=registry,ref=myapp:buildcache
cache-to: type=registry,ref=myapp:buildcache,mode=max
💡 故障排查实践
1. 查看日志
# 实时日志
docker-compose logs -f
# 特定服务
docker-compose logs -f app
# 最后100行
docker-compose logs --tail=100 app
2. 进入容器调试
# 进入运行中的容器
docker exec -it my-app sh
# 以root用户进入
docker exec -it -u root my-app sh
# 查看进程
docker top my-app
# 查看资源使用
docker stats my-app
3. 检查健康状态
# 查看健康状态
docker inspect --format='{{.State.Health.Status}}' my-app
# 查看健康检查日志
docker inspect --format='{{json .State.Health}}' my-app | jq
📊 性能监控实践
# docker-compose.monitoring.yml
version: '3.8'
services:
# cAdvisor - 容器监控
cadvisor:
image: gcr.io/cadvisor/cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8080:8080"
# Node Exporter - 系统监控
node-exporter:
image: prom/node-exporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
💡 小结
今天长安总结了Docker最佳实践:
安全实践
- 使用非root用户
- 不存储敏感信息
- 扫描镜像漏洞
- 使用官方镜像
- 定期更新
镜像实践
- 明确版本标签
- 保持镜像小巧
- 合理组织层
- 使用.dockerignore
- 单一职责
运维实践
- 资源限制
- 重启策略
- 数据备份
- 监控告警
- 定期清理
开发实践
- 环境分离
- 健康检查
- 标签管理
- 日志管理
- 命名规范
🚀 下一步
理论学完了,该实战了!下一章长安带你 部署Node.js应用!
💬 长安的最佳实践心得:
最佳实践不是教条,而是经验总结。
我踩过的坑有:
- 用root用户被黑客攻击
- 密码提交到Git泄露
- 镜像太大部署超时
- 没做备份数据丢失
- 资源不限制服务器崩溃
每个最佳实践背后,都有血的教训!
所以请认真对待这些实践,它们能帮你避免90%的坑!
下一章咱们实战,加油!💪
