📖 文章概述
Docker 是现代应用部署的事实标准。本文深入讲解 Docker 的核心概念、镜像构建、容器运行、编排部署和最佳实践。
🎯 Docker 核心概念
什么是 Docker?
Docker 是一个开源的容器化平台,可以打包应用及其依赖到一个标准单元(容器)中。
虚拟机 Docker 容器
┌──────────────┐ ┌─────────────────┐
│ 操作系统 │ │ 应用程序 │
├──────────────┤ ├─────────────────┤
│ 应用程序 │ vs │ 依赖库 │
├──────────────┤ ├─────────────────┤
│ 依赖库 │ │ Docker 引擎 │
├──────────────┤ └─────────────────┘
│ Hypervisor │ └─────────────────┘
├──────────────┤ ┌─────────────────┐
│ 硬件 │ │ 操作系统 │
└──────────────┘ │ Docker │
体积:GB │ │ 硬件 │
启动:分钟 │ └─────────────────┘
│ 体积:MB
│ 启动:秒
Docker 核心概念
| 概念 | 描述 | 类比 |
|---|---|---|
| 镜像 | 应用的模板/蓝图 | 类 (Class) |
| 容器 | 运行的镜像实例 | 对象 (Instance) |
| 仓库 | 镜像的存储库 | 包管理器 |
| Dockerfile | 镜像构建脚本 | 构建配置 |
| Volume | 持久化存储 | 硬盘 |
| Network | 容器网络 | 网络接口 |
🚀 快速开始
1. Docker 安装和基础命令
# 安装 Docker
# macOS: brew install docker
# Linux: curl -fsSL https://get.docker.com | sh
# Windows: 下载 Docker Desktop
# 验证安装
docker --version
# 基础命令
docker pull ubuntu # 拉取镜像
docker run -it ubuntu /bin/bash # 运行容器
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器
docker images # 查看镜像
docker stop container_id # 停止容器
docker rm container_id # 删除容器
docker rmi image_id # 删除镜像
2. 构建第一个镜像
# Dockerfile - 构建 Node.js 应用镜像
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制 package 文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node healthcheck.js
# 启动应用
CMD ["node", "server.js"]
构建和运行
# 构建镜像
docker build -t my-app:1.0 .
# 运行容器
docker run -d -p 3000:3000 --name my-app-container my-app:1.0
# 查看日志
docker logs -f my-app-container
# 进入容器
docker exec -it my-app-container /bin/bash
🏗️ Dockerfile 详解
3. 优化的 Dockerfile
# 多阶段构建 - 减少最终镜像大小
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/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
# 非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
# 使用 exec 形式确保信号正确传递
ENTRYPOINT ["node"]
CMD ["dist/server.js"]
4. Dockerfile 最佳实践
# ✅ 好的实践
# 1. 使用具体的标签,而不是 latest
FROM node:18.12.1-alpine
# 2. 使用 .dockerignore
# .dockerignore 内容:
# node_modules
# npm-debug.log
# .git
# 3. 最小化层数
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*
# 4. 按构建顺序排列命令(经常变化的放最后)
COPY package.json .
RUN npm install
COPY . .
# 5. 使用非 root 用户
USER nodejs
# 6. 定义元数据
LABEL maintainer="your@email.com"
LABEL version="1.0"
# ❌ 避免的做法
# FROM ubuntu:latest # 过于宽泛
# RUN apt-get install * # 安装不必要的包
# COPY . /app # 复制整个项目,包括 node_modules
🗂️ 镜像管理
5. Docker 镜像操作
# 构建镜像
docker build -t app:1.0 .
docker build -t app:latest -t app:v1.0 . # 多标签
# 查看镜像详情
docker inspect image_id
# 标签(重新命名)
docker tag app:1.0 myregistry/app:1.0
# 推送到仓库
docker push myregistry/app:1.0
# 拉取镜像
docker pull ubuntu:20.04
# 查看镜像历史
docker history app:1.0
# 保存镜像
docker save app:1.0 > app.tar
# 加载镜像
docker load < app.tar
# 删除镜像
docker rmi app:1.0
docker rmi -f app:1.0 # 强制删除
# 清理未使用的镜像
docker image prune
docker image prune -a # 删除所有未使用的
🎯 容器运行和管理
6. 容器操作详解
# 运行容器的各种方式
docker run -d -p 8080:3000 --name web app:1.0
# 参数说明:
# -d: 后台运行
# -p: 端口映射 (主机:容器)
# --name: 容器名称
# -e: 环境变量
# -v: 挂载卷
# --network: 网络
# --restart: 重启策略
# -m: 内存限制
# --cpus: CPU 限制
# 完整示例
docker run -d \
--name web-app \
-p 8080:3000 \
-e NODE_ENV=production \
-e DATABASE_URL=postgresql://db:5432/myapp \
-v /data/uploads:/app/uploads \
--restart unless-stopped \
-m 512m \
--cpus 1 \
myapp:1.0
# 容器通信
docker network create app-network
docker run --network app-network --name web app:1.0
docker run --network app-network --name db postgres:13
# 容器间通信(使用容器名)
# curl http://web:3000
# 查看容器详情
docker ps
docker ps -a
docker inspect container_id
# 查看容器日志
docker logs container_id
docker logs -f container_id # 实时日志
docker logs --tail 100 container_id
# 进入容器
docker exec -it container_id /bin/bash
docker exec -it container_id sh
# 暂停和恢复
docker pause container_id
docker unpause container_id
# 停止和启动
docker stop container_id
docker start container_id
# 删除容器
docker rm container_id
docker rm -f container_id # 强制删除运行中的容器
# 容器资源统计
docker stats # 实时监控
docker stats --no-stream # 一次输出
🎛️ Docker Compose 编排
7. Docker Compose 完整示例
# docker-compose.yml - 多容器应用编排
version: '3.9'
services:
# 前端应用
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://backend:3001
depends_on:
- backend
networks:
- app-network
restart: unless-stopped
# 后端应用
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "3001:3001"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
- ./backend/src:/app/src
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
interval: 30s
timeout: 10s
retries: 3
# 数据库
db:
image: postgres:13-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# 缓存
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
redis_data:
networks:
app-network:
driver: bridge
Compose 命令
# 启动所有服务
docker-compose up -d
# 停止所有服务
docker-compose down
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f backend
# 进入容器
docker-compose exec backend /bin/bash
# 重启服务
docker-compose restart backend
# 扩展服务
docker-compose up -d --scale worker=3
# 删除所有数据
docker-compose down -v
🚀 生产部署
8. Docker 生产部署检查清单
# 生产级 Dockerfile 示例
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制锁文件优先(缓存优化)
COPY package-lock.json .
# 安装依赖
RUN npm ci --only=production && \
npm cache clean --force
# 复制源代码
COPY --chown=node:node . .
# 非 root 用户
USER node
# 暴露端口
EXPOSE 3000
# 环境变量
ENV NODE_ENV=production
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
# 启动应用
CMD ["node", "server.js"]
部署脚本
#!/bin/bash
# deploy.sh
set -e
# 构建镜像
echo "Building Docker image..."
docker build -t myapp:$VERSION .
# 标记镜像
docker tag myapp:$VERSION myregistry/myapp:$VERSION
docker tag myapp:$VERSION myregistry/myapp:latest
# 推送到仓库
echo "Pushing image to registry..."
docker push myregistry/myapp:$VERSION
docker push myregistry/myapp:latest
# 在生产环境上更新
echo "Deploying to production..."
ssh user@production-server "cd /apps/myapp && \
docker pull myregistry/myapp:$VERSION && \
docker-compose down && \
docker-compose up -d"
echo "Deployment complete!"
📊 Docker 性能优化
9. 镜像大小优化
| 优化方案 | 镜像大小 | 启动时间 |
|---|---|---|
| 标准 Node 镜像 | 908MB | 5s |
| Alpine 镜像 | 123MB | 1s |
| 多阶段构建 | 98MB | 0.8s |
| 压缩优化 | 45MB | 0.3s |
优化技巧
# 1. 使用 Alpine 镜像
FROM node:18-alpine
# 2. 多阶段构建
FROM node:18-alpine AS builder
# 构建阶段
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
# 3. 移除不必要的文件
RUN npm ci --only=production && \
npm cache clean --force && \
rm -rf /var/lib/apt/lists/*
# 4. 使用 .dockerignore
# .dockerignore 文件:
# node_modules
# npm-debug.log
# .git
# .env
# coverage
🔒 Docker 安全最佳实践
10. 安全配置
# ✅ 安全的 Dockerfile
# 1. 不要以 root 运行
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
# 2. 使用只读文件系统(可能)
# docker run --read-only app:1.0
# 3. 限制容器权限
# docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE app:1.0
# 4. 不要在镜像中存储密钥
# ❌ RUN echo "SECRET=xxx" >> .env
# ✅ 使用环境变量或密钥管理系统
# 5. 定期更新基础镜像
FROM alpine:3.18 # 使用最新的安全版本
安全扫描
# 扫描漏洞
docker scan myapp:1.0
# 使用 Trivy
trivy image myapp:1.0
# 检查镜像历史
docker history myapp:1.0
🐛 常见问题解决
问题 1:容器文件权限问题
# ❌ 问题:挂载卷权限不匹配
docker run -v /data:/app/data app:1.0
# ✅ 解决方案
docker run -v /data:/app/data --user $(id -u):$(id -g) app:1.0
# 或在 Dockerfile 中指定用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
问题 2:Docker 容器占用空间
# 查看 Docker 磁盘占用
docker system df
# 清理未使用的资源
docker system prune # 清理容器和镜像
docker system prune -a # 清理所有未使用的
docker volume prune # 清理卷
问题 3:容器之间网络通信
# 创建自定义网络
docker network create app-net
# 在同一网络中运行容器
docker run --network app-net --name web app:1.0
docker run --network app-net --name db postgres:13
# 容器内可以通过名称通信
# curl http://web:3000
🎓 最佳实践总结
DO ✅
# 1. 使用 Alpine 基础镜像
FROM alpine:3.18
# 2. 多阶段构建
FROM node:18 AS builder
COPY . .
RUN npm run build
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
# 3. 非 root 用户
USER nodejs
# 4. 健康检查
HEALTHCHECK CMD curl localhost:3000/health
# 5. 环境变量管理
ENV NODE_ENV=production
# 6. 定期更新依赖
RUN npm update
DON'T ❌
# 1. 不要使用 latest 标签
# FROM ubuntu:latest
# 2. 不要在镜像中存储密钥
# RUN echo "PASSWORD=secret" >> .env
# 3. 不要以 root 运行
# USER root
# 4. 不要安装不必要的包
# RUN apt-get install *
# 5. 不要忽视安全更新
# 定期扫描和更新基础镜像
📚 扩展资源
总结
Docker 容器化的核心要素:
- 镜像构建:优化 Dockerfile,使用多阶段构建
- 容器运行:正确配置环保变量、卷、网络
- 编排管理:使用 Docker Compose 管理多个容器
- 生产部署:健康检查、监控、自动化部署
- 安全保障:非 root 用户、安全扫描、权限管理
- 性能优化:镜像大小、启动时间、资源限制
掌握 Docker,成为现代 DevOps 工程师!