Docker 已经成为现代软件开发和部署的标准工具。通过容器化,我们可以实现环境一致性、简化部署流程、提高资源利用率。本指南将带你从基础到进阶,全面掌握 Docker 的使用。
Docker 基础概念#
1. 核心组件#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Docker 引擎:运行和管理容器的核心
docker version # 查看 Docker 版本信息
docker info # 查看系统信息和资源使用
# 镜像(Image):应用程序的静态模板
docker images # 查看本地镜像
docker pull ubuntu:20.04 # 拉取镜像
docker search nginx # 搜索镜像
# 容器(Container):镜像的运行实例
docker ps # 查看运行的容器
docker ps -a # 查看所有容器
docker run nginx # 运行容器
# 仓库(Repository):存储和分发镜像的服务
docker login # 登录镜像仓库
docker push myapp:1.0 # 推送镜像
|
2. 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
31
32
33
34
35
36
37
38
| # 基础镜像
FROM node:16-alpine
# 维护者信息
LABEL maintainer="your-email@example.com"
# 设置工作目录
WORKDIR /app
# 复制 package 文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源代码
COPY . .
# 暴露端口
EXPOSE 3000
# 环境变量
ENV NODE_ENV=production
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 更改文件所有权
RUN chown -R nodejs:nodejs /app
USER nodejs
# 启动命令
CMD ["node", "app.js"]
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:3000/health || exit 1
|
构建优化#
1. 多阶段构建#
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
| # 构建阶段
FROM node:16-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
# 安装所有依赖(包括开发依赖)
RUN npm ci
# 复制源代码
COPY . .
# 构建应用
RUN npm run build
# 生产阶段
FROM node:16-alpine AS production
WORKDIR /app
# 只复制生产依赖
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# 从构建阶段复制构建产物
COPY --from=builder /app/dist ./dist
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "dist/app.js"]
|
2. 层缓存优化#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Dockerfile
FROM node:16-alpine
WORKDIR /app
# 先复制依赖文件,利用缓存
COPY package.json package-lock.json ./
# 安装依赖(这一层变化频率低)
RUN npm ci --only=production
# 再复制源代码(变化频率高)
COPY . .
# 构建或启动应用
RUN npm run build
CMD ["npm", "start"]
|
3. .dockerignore 文件#
# 排除不需要的文件和目录
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.coverage
dist
build
*.log
.DS_Store
.vscode
.idea
Docker Compose#
1. 基础配置#
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
65
66
67
68
69
70
71
72
73
| # docker-compose.yml
version: '3.8'
services:
# 应用服务
app:
build:
context: .
dockerfile: Dockerfile
container_name: myapp
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/myapp
depends_on:
- db
- redis
volumes:
- ./logs:/app/logs
networks:
- app-network
# 数据库服务
db:
image: postgres:13-alpine
container_name: myapp-db
restart: unless-stopped
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
# Redis 服务
redis:
image: redis:6-alpine
container_name: myapp-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- app-network
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: myapp-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
networks:
- app-network
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
|
2. 环境变量配置#
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
| # docker-compose.prod.yml
version: '3.8'
services:
app:
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- JWT_SECRET=${JWT_SECRET}
env_file:
- .env.production
# docker-compose.dev.yml
version: '3.8'
services:
app:
environment:
- NODE_ENV=development
- PORT=3000
volumes:
- .:/app # 开发时挂载源代码
command: npm run dev
|
生产部署#
1. 生产环境 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
31
32
33
34
35
36
37
38
39
| # Dockerfile.prod
FROM node:16-alpine AS deps
WORKDIR /app
# 复制依赖文件
COPY package.json package-lock.json ./
# 安装依赖
RUN npm ci --only=production
# 生产镜像
FROM node:16-alpine
RUN apk add --no-cache dumb-init
WORKDIR /app
# 复制依赖
COPY --from=deps /app/node_modules ./node_modules
# 复制应用代码
COPY . .
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
# 使用 dumb-init
ENTRYPOINT ["dumb-init", "--"]
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
CMD ["node", "app.js"]
|
2. 安全配置#
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
| # 安全最佳实践
FROM node:16-alpine
# 更新系统包
RUN apk update && \
apk upgrade && \
apk add --no-cache \
dumb-init \
&& rm -rf /var/cache/apk/*
# 创建用户
RUN addgroup -g 1001 -S appuser && \
adduser -S appuser -u 1001
WORKDIR /app
# 复制并设置权限
COPY --chown=appuser:appuser . .
USER appuser
# 使用最小权限运行
USER 1001:1001
# 使用 tini 初始化系统
ENTRYPOINT ["tini", "--"]
# CMD 执行
CMD ["node", "app.js"]
|
网络配置#
1. 自定义网络#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # docker-compose.networks.yml
version: '3.8'
services:
web:
image: nginx
networks:
- frontend
- backend
db:
image: postgres
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 内部网络,不能访问外部
|
2. 网络隔离#
1
2
3
4
5
6
7
8
9
10
| # 创建自定义网络
docker network create --driver bridge myapp-network
docker network create --driver bridge db-network --internal
# 运行容器并连接到网络
docker run -d --name web --network myapp-network nginx
docker run -d --name db --network db-network postgres
# 连接容器到多个网络
docker network connect myapp-network db
|
数据持久化#
1. 数据卷#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # docker-compose.volumes.yml
version: '3.8'
services:
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
app:
image: myapp
volumes:
- ./logs:/app/logs
- uploads:/app/uploads
volumes:
postgres_data:
driver: local
uploads:
driver: local
|
2. 备份和恢复#
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
| # 数据库备份脚本
#!/bin/bash
# backup.sh
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$DATE.sql"
# 创建备份
docker exec postgres_container pg_dump -U postgres myapp > $BACKUP_FILE
# 压缩备份
gzip $BACKUP_FILE
# 删除7天前的备份
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +7 -delete
echo "Backup completed: $BACKUP_FILE.gz"
# 数据库恢复脚本
#!/bin/bash
# restore.sh
BACKUP_FILE=$1
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup_file>"
exit 1
fi
# 解压备份
gunzip -c $BACKUP_FILE | docker exec -i postgres_container psql -U postgres -d myapp
echo "Database restored from $BACKUP_FILE"
|
监控和日志#
1. 日志配置#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # docker-compose.logging.yml
version: '3.8'
services:
app:
image: myapp
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels:
- "logging=enabled"
# 日志收集服务
fluentd:
image: fluent/fluentd:v1.14-debian
volumes:
- ./fluentd.conf:/fluentd/etc/fluent.conf
- /var/lib/docker/containers:/var/lib/docker/containers
|
2. 监控集成#
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
| # docker-compose.monitoring.yml
version: '3.8'
services:
# Prometheus
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
# Grafana
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
# cAdvisor
cadvisor:
image: gcr.io/cadvisor/cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
volumes:
grafana_data:
|
CI/CD 集成#
1. 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
49
50
51
52
53
54
55
56
| # .github/workflows/docker.yml
name: Build and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
if: github.event_name == 'push'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.prod
push: ${{ github.event_name == 'push' }}
tags: |
yourusername/myapp:latest
yourusername/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- name: Deploy to production
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /path/to/your/app
docker-compose pull
docker-compose up -d
docker system prune -f
|
2. 自动化脚本#
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
| #!/bin/bash
# deploy.sh
set -e
echo "Starting deployment..."
# 拉取最新镜像
docker-compose pull
# 停止旧容器
docker-compose down
# 启动新容器
docker-compose up -d
# 等待服务启动
sleep 30
# 健康检查
if curl -f http://localhost:3000/health; then
echo "Deployment successful!"
else
echo "Deployment failed, rolling back..."
docker-compose down
docker-compose up -d --scale app=0
exit 1
fi
# 清理未使用的镜像
docker image prune -f
echo "Deployment completed successfully!"
|
性能优化#
1. 镜像大小优化#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 使用 Alpine 基础镜像
FROM node:16-alpine
# 多阶段构建减少最终镜像大小
FROM node:16-alpine AS builder
# ... 构建过程 ...
FROM node:16-alpine AS runtime
COPY --from=builder /app/dist ./dist
# 清理不必要的包
RUN apk del .build-deps && \
rm -rf /var/cache/apk/* && \
npm cache clean --force
# 使用 .dockerignore 排除不必要文件
# 合理使用多阶段构建和层缓存
|
2. 资源限制#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # docker-compose.prod.yml
version: '3.8'
services:
app:
image: myapp
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
restart: unless-stopped
ulimits:
nofile:
soft: 65536
hard: 65536
|
故障排除#
1. 常用调试命令#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # 查看容器日志
docker logs -f container_name
docker logs --tail=100 container_name
# 进入容器调试
docker exec -it container_name /bin/sh
# 查看容器资源使用
docker stats
# 查看容器详细信息
docker inspect container_name
# 查看镜像历史
docker history image_name
# 清理 Docker 资源
docker system prune -a
docker volume prune
|
2. 健康检查#
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
| # 应用健康检查脚本
# healthcheck.js
const http = require('http');
const options = {
hostname: 'localhost',
port: 3000,
path: '/health',
method: 'GET',
timeout: 2000
};
const req = http.request(options, (res) => {
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
req.on('error', () => {
process.exit(1);
});
req.on('timeout', () => {
req.destroy();
process.exit(1);
});
req.end();
|
Docker 容器化的最佳实践:
- 镜像优化:使用多阶段构建、层缓存、Alpine 镜像
- 安全配置:非 root 用户、最小权限、定期更新
- 资源管理:合理设置资源限制、监控使用情况
- 数据持久化:使用数据卷、定期备份
- 网络配置:自定义网络、网络隔离
- CI/CD 集成:自动化构建和部署
- 监控日志:完善的监控和日志系统
通过遵循这些最佳实践,你可以构建出高效、安全、可维护的容器化应用。
相关资源:
🧰 本章配套工具箱#
边读边练:以下工具均在浏览器本地运行,数据不上传,可直接打开使用。