Docker开发环境搭建与最佳实践
Docker开发环境搭建与最佳实践
Docker是现代化的容器化平台,可以极大地简化开发环境的搭建和管理。本文将分享Docker在开发中的最佳实践。
基础概念
1. Docker核心组件
Docker架构:
├── Docker Engine
│ ├── Docker Daemon
│ ├── Docker Client
│ └── Docker API
├── Docker Images(镜像)
├── Docker Containers(容器)
├── Docker Networks(网络)
└── Docker Volumes(存储卷)
2. 基础命令
# 镜像管理
docker pull nginx:latest # 拉取镜像
docker images # 查看镜像列表
docker rmi nginx # 删除镜像
docker build -t myapp:1.0 . # 构建镜像
# 容器管理
docker run -d -p 80:80 nginx # 运行容器
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器
docker stop container_id # 停止容器
docker start container_id # 启动容器
docker rm container_id # 删除容器
docker exec -it container_id bash # 进入容器
# 日志和监控
docker logs container_id # 查看日志
docker logs -f container_id # 实时查看日志
docker stats # 查看资源使用
Dockerfile最佳实践
1. 基础镜像选择
# 生产环境使用精简镜像
FROM node:18-alpine
# 或者使用多阶段构建的基础镜像
FROM node:18 AS builder
FROM node:18-alpine AS production
# Python项目
FROM python:3.11-slim
# Java项目
FROM eclipse-temurin:17-jre-alpine
# Go项目
FROM golang:1.21-alpine AS builder
FROM alpine:latest AS production
2. 多阶段构建
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
RUN npm ci --only=production
# 复制源代码
COPY . .
RUN npm run build
# 生产阶段
FROM node:18-alpine AS production
WORKDIR /app
# 只复制必要的文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
EXPOSE 3000
CMD ["npm", "start"]
3. 优化构建缓存
FROM node:18-alpine
WORKDIR /app
# 先复制依赖文件(利用缓存)
COPY package*.json ./
RUN npm ci --only=production
# 再复制源代码(经常变化)
COPY . .
# 构建应用
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
4. 安全最佳实践
FROM node:18-alpine
# 更新系统包
RUN apk update && apk upgrade
WORKDIR /app
# 使用非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S appuser -u 1001
# 复制应用文件
COPY --chown=appuser:nodejs package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:nodejs . .
# 切换到非root用户
USER appuser
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
EXPOSE 3000
CMD ["node", "server.js"]
Docker Compose开发环境
1. 基础配置
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
volumes:
- .:/app
- /app/node_modules
depends_on:
- db
- redis
command: npm run dev
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
adminer:
image: adminer
ports:
- "8080:8080"
depends_on:
- db
volumes:
postgres_data:
redis_data:
2. 多环境配置
# docker-compose.base.yml
version: '3.8'
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
# docker-compose.dev.yml
version: '3.8'
services:
app:
environment:
- NODE_ENV=development
- DEBUG=true
ports:
- "3000:3000"
command: npm run dev
db:
environment:
POSTGRES_USER: dev_user
POSTGRES_PASSWORD: dev_pass
POSTGRES_DB: dev_db
ports:
- "5432:5432"
# docker-compose.prod.yml
version: '3.8'
services:
app:
environment:
- NODE_ENV=production
deploy:
replicas: 3
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
db:
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- /host/path/to/postgres:/var/lib/postgresql/data
3. 使用多配置文件
# 开发环境
docker-compose -f docker-compose.base.yml -f docker-compose.dev.yml up -d
# 生产环境
docker-compose -f docker-compose.base.yml -f docker-compose.prod.yml up -d
# 指定环境变量文件
docker-compose --env-file .env.prod up -d
开发工作流
1. 热重载配置
# docker-compose.yml
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true
- WATCHPACK_POLLING=true
command: npm start
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "5000:5000"
volumes:
- ./backend:/app
- /app/__pycache__
environment:
- FLASK_ENV=development
- FLASK_DEBUG=1
command: flask run --host=0.0.0.0 --reload
2. 调试配置
# Dockerfile.debug
FROM node:18-alpine
WORKDIR /app
# 安装调试工具
RUN npm install -g nodemon
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000 9229
CMD ["nodemon", "--inspect=0.0.0.0:9229", "server.js"]
# docker-compose.debug.yml
services:
app:
build:
context: .
dockerfile: Dockerfile.debug
ports:
- "3000:3000"
- "9229:9229"
volumes:
- .:/app
- /app/node_modules
3. 开发脚本
#!/bin/bash
# dev.sh - 开发环境管理脚本
case "$1" in
start)
echo "Starting development environment..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
;;
stop)
echo "Stopping development environment..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml down
;;
restart)
echo "Restarting development environment..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml restart
;;
logs)
docker-compose -f docker-compose.yml -f docker-compose.dev.yml logs -f
;;
build)
echo "Building images..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml build
;;
clean)
echo "Cleaning up..."
docker-compose -f docker-compose.yml -f docker-compose.dev.yml down -v
docker system prune -f
;;
*)
echo "Usage: $0 {start|stop|restart|logs|build|clean}"
exit 1
;;
esac
数据库管理
1. 数据库迁移
# docker-compose.yml
services:
migrate:
build: .
command: npm run migrate
depends_on:
- db
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
seed:
build: .
command: npm run seed
depends_on:
- migrate
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
2. 数据备份
#!/bin/bash
# backup.sh
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# 备份数据库
docker-compose exec -T db pg_dump -U user myapp > $BACKUP_DIR/backup_$DATE.sql
# 压缩备份
gzip $BACKUP_DIR/backup_$DATE.sql
# 删除旧备份(保留7天)
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +7 -delete
echo "Backup completed: backup_$DATE.sql.gz"
网络配置
1. 自定义网络
version: '3.8'
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
services:
web:
image: nginx
networks:
- frontend
ports:
- "80:80"
app:
build: .
networks:
- frontend
- backend
db:
image: postgres
networks:
- backend
2. 服务发现
version: '3.8'
services:
web:
image: nginx
networks:
- mynet
api:
build: .
networks:
- mynet
# 可以通过服务名访问其他服务
# http://web, http://api
networks:
mynet:
driver: bridge
性能优化
1. 镜像优化
# 使用多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
CMD ["node", "dist/main.js"]
2. 构建缓存
# 优化层缓存
FROM node:18-alpine
WORKDIR /app
# 依赖层(不经常变化)
COPY package*.json ./
RUN npm ci --only=production
# 应用层(经常变化)
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
3. 资源限制
version: '3.8'
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
安全实践
1. 镜像安全扫描
# 使用Trivy扫描
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:latest
# 使用Docker Scout
docker scout cves myapp:latest
2. 非root用户
FROM node:18-alpine
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S appuser -u 1001
WORKDIR /app
COPY --chown=appuser:nodejs package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:nodejs . .
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
3. 只读文件系统
version: '3.8'
services:
app:
image: myapp
read_only: true
tmpfs:
- /tmp
- /var/cache
volumes:
- app_logs:/app/logs
volumes:
app_logs:
故障排查
1. 日志查看
# 查看容器日志
docker logs container_id
docker logs -f container_id # 实时日志
docker logs --tail 100 container_id # 最后100行
# 查看多个服务日志
docker-compose logs -f app db
2. 进入容器
# 进入运行中的容器
docker exec -it container_id /bin/sh
docker exec -it container_id /bin/bash
# 以root用户进入
docker exec -it -u root container_id /bin/sh
3. 网络调试
# 查看容器网络
docker network ls
docker network inspect bridge
# 测试网络连通性
docker exec container_id ping db
docker exec container_id curl http://api:3000/health
CI/CD集成
1. 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@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- 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: |
${{ secrets.DOCKER_USERNAME }}/myapp:${{ github.sha }}
${{ secrets.DOCKER_USERNAME }}/myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
2. GitLab CI
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
test:
stage: test
image: $DOCKER_IMAGE
script:
- npm test
deploy:
stage: deploy
image: alpine/k8s:latest
script:
- kubectl set image deployment/app app=$DOCKER_IMAGE
only:
- main
总结
Docker开发环境的关键点:
- 镜像优化:使用多阶段构建,减小镜像体积
- 缓存利用:合理组织Dockerfile指令顺序
- 安全配置:使用非root用户,定期扫描漏洞
- 开发体验:配置热重载,优化调试流程
- 多环境管理:使用Compose覆盖文件管理不同环境
- 数据持久化:正确使用Volumes管理数据
- CI/CD集成:自动化构建和部署流程
通过合理使用Docker,可以实现开发环境的一致性和可移植性,提高开发效率。