Docker 容器化部署完全指南:从入门到生产实践

docker

目录

  1. 什么是 Docker?
  2. 核心概念解析
  3. 环境搭建
  4. 第一个容器:Hello World
  5. 镜像构建实战
  6. Docker Compose 多容器编排
  7. 生产环境部署
  8. 常见问题与排错
  9. 最佳实践总结

什么是 Docker?

Docker 是一个开源的容器化平台,它允许开发者将应用程序及其依赖打包成一个标准化的单元——容器,从而实现”一次构建,到处运行”。

容器 vs 虚拟机

特性 Docker 容器 传统虚拟机
启动速度 秒级 分钟级
资源占用 共享宿主机内核,轻量 需要完整操作系统,笨重
性能 接近原生 有虚拟化开销
隔离级别 进程级隔离 操作系统级隔离
镜像大小 通常 MB 级别 通常 GB 级别

Docker container vs VM architecture diagram

核心概念解析

1. 镜像(Image)

镜像是容器的只读模板,包含运行应用所需的代码、运行时、库和环境变量。

1
2
3
4
5
# 查看本地镜像
docker images

# 搜索远程镜像
docker search nginx

2. 容器(Container)

容器是镜像的运行实例,可以被创建、启动、停止、删除。

1
2
3
4
5
6
7
8
# 运行一个新容器
docker run -d --name my-nginx nginx

# 查看运行中的容器
docker ps

# 停止容器
docker stop my-nginx

3. 仓库(Registry)

用于存储和分发镜像的服务,Docker Hub 是官方的公共仓库。

1
2
3
4
5
# 从仓库拉取镜像
docker pull ubuntu:22.04

# 推送镜像到仓库
docker push username/image-name:tag

4. Dockerfile

定义镜像构建步骤的文本文件。

环境搭建

Linux (Ubuntu/Debian)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 更新包索引
sudo apt-get update

# 安装依赖
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release

# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 设置稳定版仓库
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 验证安装
docker --version
docker compose version

macOS

使用 Docker Desktop for Mac 一键安装。

Windows

使用 Docker Desktop for Windows,需要开启 WSL2 后端。

配置镜像加速(国内用户)

创建或编辑 /etc/docker/daemon.json

1
2
3
4
5
6
7
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
]
}

重启服务:

1
sudo systemctl restart docker

第一个容器:Hello World

1
2
# 运行 hello-world 容器
docker run hello-world

输出示例:

1
2
3
Hello from Docker!
This message shows that your installation appears to be working correctly.
...

运行一个实际的 Web 服务

1
2
3
4
5
6
# 运行 Nginx 容器
docker run -d \
--name my-nginx \
-p 8080:80 \
-v /var/www/html:/usr/share/nginx/html \
nginx:latest

参数说明:

  • -d:后台运行(detached mode)
  • --name:指定容器名称
  • -p 8080:80:端口映射(宿主机8080 → 容器80)
  • -v:挂载卷(数据持久化)

访问 http://localhost:8080 即可看到 Nginx 欢迎页面。

镜像构建实战

场景:构建一个 Node.js 应用镜像

项目结构:

1
2
3
4
5
my-node-app/
├── Dockerfile
├── package.json
├── server.js
└── .dockerignore

server.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
res.json({
message: 'Hello from Docker!',
timestamp: new Date().toISOString(),
version: '1.0.0'
});
});

app.get('/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});

app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

package.json:

1
2
3
4
5
6
7
8
9
10
{
"name": "my-node-app",
"version": "1.0.0",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.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
# 使用官方 Node.js 基础镜像
FROM node:18-alpine

# 设置工作目录
WORKDIR /app

# 复制 package 文件
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production

# 复制应用代码
COPY . .

# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs

# 暴露端口
EXPOSE 3000

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => r.statusCode === 200 ? process.exit(0) : process.exit(1))"

# 启动命令
CMD ["node", "server.js"]

多阶段构建优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
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 package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]

构建和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 构建镜像
docker build -t my-node-app:1.0.0 .

# 查看镜像
docker images | grep my-node-app

# 运行容器
docker run -d \
--name node-app \
-p 3000:3000 \
-e NODE_ENV=production \
my-node-app:1.0.0

# 查看日志
docker logs -f node-app

# 测试接口
curl http://localhost:3000

Docker Compose 多容器编排

场景:部署 Web 应用 + 数据库 + Redis

docker-compose.yml:

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
74
75
76
77
78
version: '3.8'

services:
# Web 应用
app:
build:
context: ./app
dockerfile: Dockerfile
container_name: web-app
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3

# PostgreSQL 数据库
db:
image: postgres:15-alpine
container_name: postgres-db
restart: unless-stopped
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
networks:
- app-network

# Redis 缓存
redis:
image: redis:7-alpine
container_name: redis-cache
restart: unless-stopped
volumes:
- redis_data:/data
command: redis-server --appendonly yes
networks:
- app-network

# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: nginx-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- app-network

volumes:
postgres_data:
redis_data:

networks:
app-network:
driver: bridge

nginx.conf:

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
events {
worker_connections 1024;
}

http {
upstream app {
server app:3000;
}

server {
listen 80;
server_name example.com;

# 重定向到 HTTPS
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name example.com;

ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;

# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}

# 静态文件缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}

常用 Compose 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 启动所有服务
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f app

# 重启服务
docker compose restart app

# 停止服务
docker compose down

# 停止并删除卷(谨慎使用)
docker compose down -v

# 重新构建镜像
docker compose up -d --build

生产环境部署

1. 使用 Docker Swarm(轻量级集群)

1
2
3
4
5
6
7
8
9
# 初始化 Swarm
docker swarm init

# 部署服务
docker stack deploy -c docker-compose.yml myapp

# 查看服务
docker stack ps myapp
docker service ls

2. 使用 Kubernetes(大规模生产)

deployment.yaml:

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: app
image: my-registry/my-node-app:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- port: 80
targetPort: 3000
type: LoadBalancer

3. CI/CD 集成(GitHub Actions)

.github/workflows/docker-build.yml:

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
name: Build and Deploy

on:
push:
branches: [main]
tags: ['v*']

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 Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

常见问题与排错

问题1:容器无法启动

1
2
3
4
5
6
7
8
# 查看容器日志
docker logs container-name

# 进入容器调试
docker run -it --rm image-name /bin/sh

# 检查容器详情
docker inspect container-name

问题2:端口冲突

1
2
3
4
5
# 查看端口占用
sudo netstat -tulpn | grep :80

# 更换端口映射
docker run -p 8080:80 nginx

问题3:权限问题

1
2
3
4
5
6
# 将用户加入 docker 组
sudo usermod -aG docker $USER
newgrp docker

# 修复卷权限
docker run --user $(id -u):$(id -g) -v $(pwd):/app image-name

问题4:镜像过大

优化策略:

  • 使用 Alpine 基础镜像
  • 多阶段构建
  • 清理缓存和临时文件
  • 使用 .dockerignore
1
2
3
4
5
6
7
8
# .dockerignore 示例
node_modules
npm-debug.log
.git
.env
*.md
.dockerignore
Dockerfile

最佳实践总结

实践 说明
使用特定版本标签 避免使用 latest,使用 nginx:1.24.0
多阶段构建 减小最终镜像体积
非 root 用户运行 提升安全性
健康检查 确保服务可用性
资源限制 设置 CPU/内存限制
日志管理 配置日志轮转和收集
密钥管理 使用 Docker Secrets 或环境变量注入

避免做的

  • 在容器中存储数据(使用卷)
  • 一个容器运行多个进程
  • 将敏感信息硬编码在镜像中
  • 忽略安全更新

常用命令速查表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 镜像管理
docker images # 列出镜像
docker rmi image-id # 删除镜像
docker prune # 清理无用镜像

# 容器管理
docker ps -a # 列出所有容器
docker rm container-id # 删除容器
docker exec -it container /bin/sh # 进入容器

# 网络管理
docker network ls # 列出网络
docker network inspect bridge # 查看网络详情

# 卷管理
docker volume ls # 列出卷
docker volume rm volume-name # 删除卷

# 系统清理
docker system df # 查看磁盘使用
docker system prune -a # 清理所有未使用资源

结语

Docker 已经成为现代软件开发和部署的标准工具。通过本文的学习,你应该掌握了:

  • Docker 核心概念和工作原理
  • 镜像构建的最佳实践
  • 多容器编排的 Compose 使用
  • 生产环境的部署策略
  • 常见问题的排查方法