Docker 把应用和依赖打包成容器,让「在我机器上能跑」成为过去式。Docker Compose 则是管理多容器项目的利器。两者搭配,是现代部署的标配。
Docker 核心概念
三个关键词搞定:
| 概念 | 类比 | 说明 |
|---|---|---|
| 镜像 (Image) | 安装包 | 只读模板,包含应用和运行环境 |
| 容器 (Container) | 运行中的程序 | 镜像的运行实例,可启停 |
| Dockerfile | 安装脚本 | 描述如何构建镜像 |
Dockerfile → build → Image → run → Container
安装 Docker
Ubuntu / Debian
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
# 安装依赖
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
# 添加 GPG key 和仓库
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 验证
docker --version
docker compose version
免 sudo 运行
sudo usermod -aG docker $USER
# 重新登录生效
常用命令速查
镜像操作
docker pull nginx:latest # 拉取镜像
docker images # 查看本地镜像
docker rmi nginx:latest # 删除镜像
docker build -t myapp:v1 . # 从 Dockerfile 构建镜像
容器操作
docker run -d --name web -p 8080:80 nginx # 后台启动
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器(含已停止)
docker logs web # 查看日志
docker exec -it web /bin/bash # 进入容器
docker stop web # 停止
docker start web # 启动
docker rm web # 删除容器
docker restart web # 重启
清理
docker system prune # 清理无用镜像/容器/网络
docker system prune -a # 连未使用的镜像也清理
docker volume prune # 清理无用卷
Dockerfile 最佳实践
以一个 Node.js 应用为例:
# 1. 精简基础镜像
FROM node:20-alpine AS builder
WORKDIR /app
# 2. 先复制依赖文件,利用缓存层
COPY package.json package-lock.json ./
RUN npm ci --only=production
# 3. 多阶段构建 — 构建阶段
FROM node:20-alpine AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build
# 4. 运行阶段 — 最小镜像
FROM node:20-alpine AS runner
WORKDIR /app
# 5. 非 root 用户运行
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 app
USER app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
关键原则
- 用 Alpine 基础镜像 — 体积小,攻击面少
- 多阶段构建 — 构建工具不进最终镜像
- 利用构建缓存 — 先 COPY 依赖文件,再 COPY 源码
- 非 root 运行 — 安全最佳实践
- 一个容器一个进程 — 不要在容器里跑多个服务
Docker Compose 详解
Compose 用一个 YAML 文件定义多容器应用,一条命令启动全部。
基本结构
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./html:/usr/share/nginx/html:ro
depends_on:
- api
restart: unless-stopped
api:
build: ./api
ports:
- "3000:3000"
environment:
- DB_HOST=db
- DB_PORT=5432
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_PASSWORD=secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
pgdata:
常用命令
docker compose up -d # 后台启动所有服务
docker compose down # 停止并删除容器
docker compose down -v # 同时删除卷
docker compose logs -f api # 实时查看日志
docker compose ps # 查看服务状态
docker compose restart api # 重启某个服务
docker compose build # 重新构建镜像
docker compose pull # 拉取最新镜像
docker compose up -d --build api # 重新构建并启动某个服务
实战案例
案例 1:WordPress + MySQL
version: '3.8'
services:
wordpress:
image: wordpress:6.7
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_USER: wp
WORDPRESS_DB_PASSWORD: wp-password
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-content:/var/www/html
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root-password
MYSQL_DATABASE: wordpress
MYSQL_USER: wp
MYSQL_PASSWORD: wp-password
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
wp-content:
mysql-data:
案例 2:反向代理 + 多站点
version: '3.8'
services:
nginx-proxy:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certs:/etc/nginx/certs:ro
restart: always
site-a:
image: myapp:a
expose:
- "3000"
site-b:
image: myapp:b
expose:
- "3000"
Nginx 配置按域名分发到不同容器,一个代理管多站。
数据持久化
容器是无状态的,删除容器数据就没了。持久化方案:
| 方式 | 用途 | 生命周期 |
|---|---|---|
| 命名卷 (Volume) | 数据库、应用数据 | 随卷存在 |
| 绑定挂载 (Bind Mount) | 配置文件、代码目录 | 随宿主文件存在 |
| tmpfs | 临时数据 | 容器停止即消失 |
volumes:
# 命名卷 — Docker 管理
- pgdata:/var/lib/postgresql/data
# 绑定挂载 — 映射宿主路径
- ./config.yml:/app/config.yml:ro # :ro 只读
网络模式
services:
app:
networks:
- frontend
- backend
db:
networks:
- backend # db 只在 backend 网络,外部不可访问
networks:
frontend:
backend:
internal: true # 隔离网络,无法访问外网
- 同一网络中的容器可通过服务名互访(如
db:5432) internal: true创建隔离网络,数据库不会被外部访问
优化技巧
- 镜像瘦身 — Alpine 基础镜像 + 多阶段构建
- 健康检查 —
healthcheck让 Compose 知道服务是否就绪 - 资源限制 — 防止某个容器吃光资源
services:
api:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M
- 日志限制 — 防止日志撑爆磁盘
services:
api:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
- 环境变量文件 — 敏感信息不写进 YAML
# .env
DB_PASSWORD=secret
services:
db:
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
总结
- Docker 解决「环境一致性问题」,让应用在哪里都能跑
- Docker Compose 解决「多容器编排问题」,一条命令管理整套服务
- 关键原则:一个容器一个进程、数据持久化用卷、敏感信息用环境变量、网络隔离最小权限
- 实战路径:先跑别人的镜像 → 写自己的 Dockerfile → 用 Compose 编排多服务