Docker 容器化全面学习手册
Docker 容器化全面学习手册
Docker 是云计算时代必备技能。 解决"我电脑上能跑,服务器上不行"的终极方案。本文从底层原理→安装→命令→实战部署(MySQL/Redis/Nginx)→Dockerfile→Docker Compose,从 0 到 1 掌握容器化。
一、Docker 解决了什么问题
没有 Docker 时: 开发说"我电脑上能跑啊"→运维部署发现环境不一致(JDK 版本/系统库/配置文件)→排查半天→"你这环境有问题"。 有了 Docker: 把代码+依赖+环境配置全部打包成镜像,一次构建,到处运行。
1.1 Docker vs 虚拟机
|
对比维度 |
虚拟机 (VM) |
Docker 容器 |
|---|---|---|
|
启动速度 |
分钟级 |
秒级(甚至毫秒) |
|
资源占用 |
每个 VM 有独立 OS 内核,GB 级内存 |
共享宿主机内核,MB 级内存 |
|
镜像大小 |
几 GB |
几十 MB |
|
隔离性 |
完全隔离(Hypervisor 层) |
进程级隔离(Namespace + Cgroups) |
|
密度 |
一台物理机跑几个 VM |
一台物理机跑几十上百容器 |
|
迁移 |
需导出完整 VM 文件 |
镜像 push/pull 即可 |
|
性能损耗 |
较大(虚拟化开销) |
几乎无(直接使用宿主机内核) |
为什么 Docker 更轻量?
传统虚拟机架构: Docker 架构:
┌──────────────────┐ ┌──────────────────┐
│ App A │ App B │ │ App A │ App B │
├──────────┴───────┤ ├──────────┴───────┤
│ Guest OS Guest OS│ │ Docker Engine │
├──────────────────┤ ├───────────────────┤
│ Hypervisor │ │ Host OS (Linux) │
├──────────────────┤ ├───────────────────┤
│ Host OS │ │ Infrastructure │
├──────────────────┤ └───────────────────┘
│ Infrastructure │
└──────────────────┘
核心差异:虚拟机虚拟硬件和 OS 内核,Docker 只虚拟应用层和依赖。Docker 容器直接使用宿主机内核,没有 Guest OS 这一层。
1.2 核心概念
三大核心对象
|
概念 |
类比 |
说明 |
|---|---|---|
|
镜像(Image) |
类(Class) |
容器的模板,包含完整的运行环境(OS + 依赖 + 代码)。镜像是只读的,一层一层叠加(Union FS)。 |
|
容器(Container) |
实例(Instance) |
镜像的运行实例。在镜像之上添加一个可写层,容器停止/删除后该层丢失。 |
|
仓库(Registry) |
代码仓库(GitHub) |
存放镜像的地方。Docker Hub 是官方公共仓库,也可以搭建私有仓库(Harbor)。 |
Docker 架构图
┌────────────────────────────────────────────┐
│ Docker Client │
│ (docker build/run/pull) │
└─────────────────┬──────────────────────────┘
│ REST API (Unix Socket / TCP)
┌─────────────────▼──────────────────────────┐
│ Docker Daemon (dockerd) │
│ ┌──────────────────────────────────────┐ │
│ │ Container Runtime (containerd)│ │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ │ │
│ │ │ 容器A │ │ 容器B │ │ 容器C │ │ │
│ │ └────────┘ └────────┘ └────────┘ │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ 镜像管理 │ │ 网络/存储管理 │ │
│ └──────────┘ └──────────────┘ │
└────────────────────────────────────────────┘
底层技术支撑
|
技术 |
作用 |
|---|---|
|
Namespace(命名空间) |
实现隔离。每种 Namespace 隔离一种资源(PID/Network/Mount/User/UTS/IPC) |
|
Cgroups(控制组) |
实现资源限制。限制 CPU、内存、磁盘 IO、网络带宽 |
|
UnionFS(联合文件系统) |
实现镜像分层。overlay2 是目前推荐使用的存储驱动 |
Docker 分层镜像原理
┌──────────────────┐
│ 可写容器层 │ ← 容器运行时的修改
├──────────────────┤
│ 第3层: app.jar │ ← COPY (你的应用)
├──────────────────┤
│ 第2层: JDK 17 │ ← RUN apt install openjdk-17
├──────────────────┤
│ 第1层: alpine │ ← FROM alpine:3.19
└──────────────────┘
每层只读、共享复用——100 个 Java 容器共享同一个 JDK 层,磁盘只占一份!
二、Docker 安装与基础命令
2.1 安装 Docker
Linux(推荐,生产环境用)
# Ubuntu/Debian
curl -fsSL https://get.docker.com | bash
# 或手动安装
sudo apt update
sudo apt install docker.io -y
sudo systemctl enable docker --now
# 将当前用户加入 docker 组(免 sudo)
sudo usermod -aG docker $USER
# 重新登录后生效
# CentOS/RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io -y
sudo systemctl enable docker --now
macOS
# 推荐用 OrbStack(轻量、快)或 Docker Desktop
brew install orbstack # 或: brew install --cask docker
Windows
# 推荐 Docker Desktop,或 WSL2 中安装 Linux 版 Docker
winget install Docker.DockerDesktop
验证安装
docker version # 查看客户端和服务端版本
docker run hello-world # 跑一个测试容器
docker info # 查看 Docker 系统信息(存储驱动、Cgroup 版本等)
镜像加速(国内)
# 编辑 /etc/docker/daemon.json
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.m.daocloud.io"
]
}
sudo systemctl restart docker
2.2 镜像管理
核心命令
# 搜索镜像
docker search nginx # Docker Hub 搜索
docker search nginx --limit 10 # 限制返回数量
# 拉取镜像
docker pull nginx # 拉取最新版(latest)
docker pull nginx:1.25 # 拉取指定版本
docker pull nginx:1.25-alpine # 拉取 alpine 精简版(体积小)
# 查看本地镜像
docker images # 列出所有镜像
docker images -a # 包含中间层
docker images nginx # 筛选指定镜像
docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# 查看镜像详情
docker inspect nginx:latest # 镜像完整 JSON 信息
docker history nginx:latest # 镜像构建历史(每层大小)
# 删除镜像
docker rmi <镜像ID> # 删除镜像
docker rmi nginx:latest # 删除指定标签
docker rmi $(docker images -q) # 删除所有镜像(危险!)
docker image prune -a # 删除所有未被使用的镜像
# 导出/导入镜像
docker save -o nginx.tar nginx:latest # 导出为 tar 包
docker save nginx:latest | gzip > nginx.tar.gz # 压缩导出
docker load -i nginx.tar # 从 tar 载入
docker load < nginx.tar.gz # 从压缩包载入
# 镜像标签
docker tag nginx:latest myregistry.com/nginx:v1 # 打标签(用于推送到私有仓库)
# 推送镜像
docker push myregistry.com/nginx:v1 # 推送到仓库
docker login myregistry.com # 先登录
2.3 容器管理
生命周期命令
# === 创建与启动 ===
# docker run = 创建 + 启动(最常用)
docker run -d --name my-nginx -p 8080:80 nginx:latest
# 常用参数详解:
docker run \
-d \ # 后台运行(detached mode)
--name my-nginx \ # 容器命名(不指定则随机生成)
-p 8080:80 \ # 端口映射 宿主机:容器
-p 8080:80/udp \ # 指定 UDP 端口
-v /data:/app/data \ # 挂载数据卷 宿主机目录:容器目录
-v myvolume:/data \ # 挂载命名卷
-e MYSQL_ROOT_PASSWORD=123456 \ # 设置环境变量
--env-file .env \ # 从文件加载环境变量
--restart always \ # 重启策略(no/always/on-failure/unless-stopped)
-m 512m \ # 内存限制
--cpus 2 \ # CPU 核数限制
--network mynet \ # 指定网络
--rm \ # 容器停止后自动删除(调试用)
nginx:latest
# 交互式运行(调试/开发)
docker run -it ubuntu:22.04 /bin/bash # -i 交互模式, -t 分配伪终端
# === 查看容器 ===
docker ps # 查看运行中的容器
docker ps -a # 查看所有容器(含已停止)
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
docker ps -q # 只输出容器 ID
docker ps --filter "status=exited" # 过滤已退出的容器
# === 容器操作 ===
docker start my-nginx # 启动已停止的容器
docker stop my-nginx # 停止容器(发送 SIGTERM,10s 后 SIGKILL)
docker stop -t 30 my-nginx # 等待 30 秒后再强制停止
docker restart my-nginx # 重启容器
docker pause my-nginx # 暂停容器进程
docker unpause my-nginx # 恢复容器进程
docker kill my-nginx # 强制停止(直接 SIGKILL)
# === 进入容器 ===
docker exec -it my-nginx /bin/bash # 进入容器开新 shell(推荐)
docker exec my-nginx cat /etc/nginx/nginx.conf # 在容器中执行命令
docker exec -it my-nginx mysql -uroot -p # 进入 MySQL 容器
docker attach my-nginx # 连接到容器的主进程(不推荐,exit 会停止容器)
# === 日志与信息 ===
docker logs my-nginx # 查看日志
docker logs -f my-nginx # 实时跟踪日志(tail -f)
docker logs --tail 50 my-nginx # 查看最后 50 行
docker logs --since 30m my-nginx # 最近 30 分钟的日志
docker logs -f --tail 100 my-nginx | grep ERROR
docker top my-nginx # 查看容器内进程
docker stats # 实时查看所有容器资源使用(CPU/内存/网络/磁盘)
docker stats my-nginx # 查看指定容器
docker port my-nginx # 查看容器端口映射
docker inspect my-nginx # 容器完整 JSON 信息
docker inspect -f '{{.NetworkSettings.IPAddress}}' my-nginx # 提取特定字段
# === 文件操作 ===
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf # 容器 → 宿主机
docker cp ./index.html my-nginx:/usr/share/nginx/html/ # 宿主机 → 容器
# === 删除容器 ===
docker rm my-nginx # 删除已停止的容器
docker rm -f my-nginx # 强制删除运行中的容器
docker rm $(docker ps -aq) # 删除所有容器(危险!)
docker container prune # 删除所有已停止的容器
# === 提交容器为镜像 ===
docker commit my-nginx my-nginx-custom:v1 # 将容器当前状态保存为镜像
docker commit -m "安装了自定义模块" my-nginx my-nginx:v2
# 注意:commit 不利于复现,生产环境请用 Dockerfile
容器状态流转
docker create
│
▼
docker pull ──→ Created ──→ Running ──→ Paused
│ │ │
│ docker stop docker unpause
│ docker kill │
▼ ▼ │
Exited ←────── │
│ Paused
│ docker start
▼
Running
│
│ docker rm
▼
Deleted
三、实战部署常用软件
3.1 部署 MySQL
# 基础部署
docker run -d \
--name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql_data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
--restart always \
mysql:8.0
# 完整参数版(推荐)
docker run -d \
--name mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=MyStr0ngP@ss \
-e MYSQL_DATABASE=myapp \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=apppass \
-e TZ=Asia/Shanghai \
-v mysql_data:/var/lib/mysql \
-v ./mysql/conf:/etc/mysql/conf.d \
-v ./mysql/init:/docker-entrypoint-initdb.d \ # 初始化 SQL 脚本
--restart always \
mysql:8.0 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci \
--default-authentication-plugin=mysql_native_password
# 连接测试
docker exec -it mysql mysql -uroot -p123456 -e "SHOW DATABASES;"
3.2 部署 Redis
# 基础部署
docker run -d \
--name redis \
-p 6379:6379 \
-v redis_data:/data \
--restart always \
redis:7-alpine
# 带密码 + 持久化 + 配置文件
docker run -d \
--name redis \
-p 6379:6379 \
-v redis_data:/data \
-v ./redis/redis.conf:/usr/local/etc/redis/redis.conf \
--restart always \
redis:7-alpine \
redis-server /usr/local/etc/redis/redis.conf \
--requirepass "MyStr0ngP@ss" \
--appendonly yes
# 连接测试
docker exec -it redis redis-cli
> AUTH MyStr0ngP@ss
> PING
> SET test hello
> GET test
# 或一条命令
docker exec -it redis redis-cli -a MyStr0ngP@ss PING
3.3 部署 Nginx
# 基础部署
docker run -d \
--name nginx \
-p 80:80 \
-p 443:443 \
-v ./nginx/html:/usr/share/nginx/html \
-v ./nginx/nginx.conf:/etc/nginx/nginx.conf \
-v ./nginx/conf.d:/etc/nginx/conf.d \
-v ./nginx/logs:/var/log/nginx \
--restart always \
nginx:alpine
# 典型 nginx.conf 反向代理配置
# ./nginx/conf.d/default.conf:
server {
listen 80;
server_name api.example.com;
client_max_body_size 100M;
location /api/ {
proxy_pass http://host.docker.internal:8080/; # 访问宿主机的 Spring Boot
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;
}
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html; # SPA 前端路由
}
}
# 测试配置
docker exec nginx nginx -t
# 重载配置(不停机)
docker exec nginx nginx -s reload
其他常用中间件快速部署
# PostgreSQL
docker run -d --name postgres -p 5432:5432 \
-e POSTGRES_PASSWORD=123456 \
-v pgdata:/var/lib/postgresql/data \
postgres:16-alpine
# MongoDB
docker run -d --name mongo -p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=123456 \
-v mongodata:/data/db \
mongo:7
# RabbitMQ(带管理界面)
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=123456 \
rabbitmq:3-management-alpine
# ElasticSearch(单节点)
docker run -d --name es -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-v esdata:/usr/share/elasticsearch/data \
elasticsearch:8.12.0
# MinIO(对象存储,S3 兼容)
docker run -d --name minio -p 9000:9000 -p 9001:9001 \
-e MINIO_ROOT_USER=admin \
-e MINIO_ROOT_PASSWORD=12345678 \
-v miniodata:/data \
minio/minio server /data --console-address ":9001"
# Portainer(Docker 可视化管理)
docker run -d --name portainer -p 9000:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
--restart always \
portainer/portainer-ce
四、Dockerfile:构建自己的镜像
4.1 Dockerfile 核心指令
|
指令 |
作用 |
示例 |
|---|---|---|
|
FROM |
指定基础镜像 |
|
|
WORKDIR |
设置工作目录(后续命令在此目录执行) |
|
|
COPY |
复制本地文件到镜像 |
|
|
ADD |
复制 + 支持 URL 下载、自动解压 tar |
|
|
RUN |
构建时执行命令 |
|
|
EXPOSE |
声明容器监听端口(文档作用) |
|
|
CMD |
容器启动时默认命令 |
|
|
ENTRYPOINT |
容器入口点(比 CMD 更难被覆盖) |
|
|
ENV |
设置环境变量 |
|
|
ARG |
构建参数( |
|
|
USER |
切换运行用户(安全:不推荐 root) |
|
|
VOLUME |
声明匿名卷(数据持久化提示) |
|
|
HEALTHCHECK |
健康检查(判断容器是否正常) |
|
|
LABEL |
添加元数据 |
|
CMD vs ENTRYPOINT 区别
# CMD:可被 docker run 后面的命令覆盖
CMD ["java", "-jar", "app.jar"]
# docker run my-image java -jar other.jar ← 可以覆盖
# ENTRYPOINT:不可被覆盖,docker run 后面的参数作为追加参数
ENTRYPOINT ["java"]
CMD ["-jar", "app.jar"]
# docker run my-image -jar other.jar ← 追加到 ENTRYPOINT 后面
# 联合使用(推荐模式):
ENTRYPOINT ["java", "-jar", "app.jar"] # 固定入口
CMD ["--spring.profiles.active=dev"] # 可被覆盖的默认参数
# docker run my-image --spring.profiles.active=prod ← 覆盖 CMD
最佳实践
# 1. 使用 .dockerignore 排除无需的文件
# .dockerignore
node_modules/
.git/
*.md
target/ (Maven 构建产物不用 COPY,在容器内构建)
# 2. 减少层数 —— 合并 RUN 命令
# ❌ 坏写法,每行一个层
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# ✅ 好写法,一层搞定
RUN apt-get update && \
apt-get install -y curl vim && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 3. 充分利用缓存 —— 把不变层放前面
# ✅ 先 COPY pom.xml,利用缓存避免每次改代码都重新下载依赖
COPY pom.xml .
RUN mvn dependency:go-offline # 下载依赖(这层会被缓存)
COPY src ./src # 代码经常变,放后面
RUN mvn package -DskipTests
4.2 Spring Boot 应用 Dockerfile
基础版(最简单)
FROM openjdk:17-jdk-alpine
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
优化版(多阶段构建 + 安全加固)
# ============ 第一阶段:构建 ============
FROM maven:3.9-eclipse-temurin-17-alpine AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B # 缓存依赖(代码不改时复用)
COPY src ./src
RUN mvn package -DskipTests -B # 编译打包
# ============ 第二阶段:运行 ============
FROM eclipse-temurin:17-jre-alpine # JRE 就够了,不用 JDK!(体积小一半 ~100MB)
WORKDIR /app
# 安全:创建非 root 用户运行应用
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 从构建阶段复制 jar
COPY --from=builder /build/target/*.jar app.jar
# 复制启动脚本(可选)
COPY docker-entrypoint.sh .
RUN chmod +x docker-entrypoint.sh
EXPOSE 8080
# 切换到非 root 用户
USER appuser
# JVM 参数通过环境变量配置,生产环境可调
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -Djava.security.egd=file:/dev/./urandom"
# 健康检查(Spring Boot Actuator)
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
docker-entrypoint.sh(可选,更灵活的启动配置):
#!/bin/sh
# 等待依赖服务就绪(如数据库)
# while ! nc -z mysql 3306; do sleep 2; done
# 设置 JVM 参数
JAVA_OPTS="${JAVA_OPTS} -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
exec java ${JAVA_OPTS} -jar app.jar
对比:优化版带来的提升
|
版本 |
镜像大小 |
构建时间(代码改动后) |
安全性 |
|---|---|---|---|
|
基础版 |
~350MB (JDK) |
完整构建 |
root 运行 |
|
优化版 |
~180MB (JRE) |
mvn 依赖缓存,只编译 |
非 root 运行 |
|
native 版 |
~80MB |
Spring Native / GraalVM |
非 root 运行 |
五、Docker Compose:多容器编排
当项目需要 MySQL + Redis + Nginx + App 多个容器时,手动一个一个 docker run 太麻烦。Docker Compose 用一个 YAML 文件定义所有服务,一条命令启动全部。
5.1 docker-compose.yml 完整示例
Spring Boot + MySQL + Redis + Nginx 全套
# docker-compose.yml
version: '3.8'
services:
# ========== 1. MySQL ==========
mysql:
image: mysql:8.0
container_name: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123}
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-app123}
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d # 自定义配置
- ./mysql/init:/docker-entrypoint-initdb.d # 初始化 SQL
networks:
- app-network
healthcheck: # 健康检查
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-root123}"]
interval: 10s
timeout: 5s
retries: 5
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
# ========== 2. Redis ==========
redis:
image: redis:7-alpine
container_name: redis
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
command: redis-server /usr/local/etc/redis/redis.conf --appendonly yes
# ========== 3. Spring Boot 应用 ==========
app:
build: . # 用当前目录的 Dockerfile 构建
# image: myregistry.com/myapp:latest # 或直接用镜像
container_name: myapp
restart: always
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: docker
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/myapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: appuser
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD:-app123}
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
TZ: Asia/Shanghai
JAVA_OPTS: "-Xms256m -Xmx512m -XX:+UseG1GC"
volumes:
- ./logs:/app/logs # 日志持久化
- ./upload:/app/upload # 上传文件持久化
networks:
- app-network
depends_on: # 启动顺序(不保证就绪!)
mysql:
condition: service_healthy # 等 MySQL 健康通过
redis:
condition: service_healthy # 等 Redis 健康通过
# ========== 4. Nginx ==========
nginx:
image: nginx:alpine
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/html:/usr/share/nginx/html
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/logs:/var/log/nginx
networks:
- app-network
depends_on:
- app
# ========== 网络 ==========
networks:
app-network:
driver: bridge
# 同一网络的容器可以通过服务名互相访问(如 mysql、redis、app)
# ========== 数据卷 ==========
volumes:
mysql_data:
driver: local
redis_data:
driver: local
Docker Compose 常用命令
# 启动所有服务(后台)
docker compose up -d
# 启动指定服务
docker compose up -d mysql redis
# 构建并启动(代码有改动时)
docker compose up -d --build
# 查看服务状态
docker compose ps
docker compose ps -a
# 查看日志
docker compose logs # 所有服务
docker compose logs -f app # 指定服务实时日志
docker compose logs --tail 100 app
# 进入服务容器
docker compose exec app /bin/sh
docker compose exec mysql mysql -uappuser -p
# 停止服务
docker compose stop # 停止不删除
docker compose down # 停止并删除容器和网络
docker compose down -v # 停止并删除容器、网络、数据卷!(谨慎)
docker compose down --rmi all # 附加删除构建的镜像
# 重启服务
docker compose restart app
# 扩容
docker compose up -d --scale app=3 # 启动 3 个 app 实例(需处理端口冲突)
# 查看资源占用
docker compose stats
# 拉取最新镜像
docker compose pull
.env 文件(环境变量管理)
# .env(和 docker-compose.yml 在同级目录)
MYSQL_ROOT_PASSWORD=ProdStr0ngP@ss2024
MYSQL_PASSWORD=appprodpass
REDIS_PASSWORD=RedisStr0ngP@ss
SPRING_PROFILES_ACTIVE=prod
六、数据卷与网络
6.1 数据卷(Volume)详解
三种挂载方式
|
方式 |
语法 |
宿主机路径 |
适用场景 |
|---|---|---|---|
|
Volume(命名卷) |
|
Docker 管理的路径( |
推荐!生产环境数据持久化 |
|
Bind Mount(绑定挂载) |
|
宿主机绝对路径 |
开发环境热更新、配置文件注入 |
|
tmpfs |
|
内存 |
临时数据、不需要持久化的缓存 |
Volume 命令
# 创建卷
docker volume create my_volume
# 查看卷
docker volume ls # 列出所有卷
docker volume inspect my_volume # 查看卷详情(挂载点等)
docker volume inspect my_volume -f '{{.Mountpoint}}'
# 删除卷
docker volume rm my_volume # 删除指定卷
docker volume prune # 删除所有未被使用的卷
# 备份卷数据
docker run --rm -v my_volume:/data -v $(pwd):/backup alpine \
tar czf /backup/backup.tar.gz -C /data .
# 恢复卷数据
docker run --rm -v my_volume:/data -v $(pwd):/backup alpine \
tar xzf /backup/backup.tar.gz -C /data
Bind Mount 实战场景
# 开发环境热更新(改代码容器立即生效)
docker run -d -p 8080:8080 \
-v $(pwd)/target:/app \
-v $(pwd)/config:/app/config \
myapp:dev
# Nginx 配置实时生效
docker run -d -p 80:80 \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \ # :ro = 只读
nginx:alpine
Volume 数据共享模式
# 多个容器共享同一个卷
docker run -d --name app1 -v shared_data:/data app:v1
docker run -d --name app2 -v shared_data:/data app:v1
# 只读挂载(防止数据被修改)
docker run -d -v shared_data:/data:ro app:v1
# 数据卷容器模式(已过时但仍常见)
docker create --name data-container -v /data busybox # 创建纯数据容器
docker run -d --name app --volumes-from data-container app:v1
6.2 Docker 网络
网络模式
|
模式 |
说明 |
使用场景 |
|---|---|---|
|
bridge(默认) |
创建虚拟网桥 docker0,容器通过 NAT 通信 |
单机多容器通信 |
|
host |
容器直接使用宿主机网络栈(无隔离) |
高性能网络场景 |
|
none |
无网络,完全隔离 |
安全敏感或不需要网络的容器 |
|
overlay |
跨主机容器通信(Swarm 模式) |
多主机集群 |
|
ipvlan/macvlan |
容器拥有独立 MAC/IP(物理网络可见) |
需要直连物理网络的场景 |
自定义网络(推荐)
# 创建网络
docker network create mynet
docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 mynet # 指定子网
# 查看网络
docker network ls # 列出所有网络
docker network inspect mynet # 查看网络详情(容器 IP、子网等)
# 容器连接网络
docker run -d --name app --network mynet app:v1
# 同一自定义网络中的容器可以**用容器名直接互相访问**!
docker exec app ping mysql # 直接用容器名 ping 通
# 容器连接多个网络
docker network connect mynet2 app
docker network disconnect mynet2 app
# 删除网络
docker network rm mynet
docker network prune # 删除所有未使用的网络
自定义网络 vs 默认 bridge
|
特性 |
默认 bridge |
自定义 bridge |
|---|---|---|
|
DNS 解析 |
只能用 IP |
自动 DNS,容器名互访 |
|
隔离性 |
需手动管理 |
天然隔离,不同网络的容器互不可见 |
|
配置灵活性 |
有限 |
可自定义子网、网关等 |
七、Docker 进阶专题
7.1 镜像优化技巧
# 1. 选择最小基础镜像
FROM alpine:3.19 # ~7MB
FROM eclipse-temurin:17-jre-alpine # ~180MB(含 JRE)
FROM gcr.io/distroless/java17-debian12 # Google 的"无发行版"镜像(更安全)
# 2. 多阶段构建(减小最终镜像体积)
FROM node:20-alpine AS build-stage
WORKDIR /app
COPY package*.json .
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM nginx:alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 最终镜像只有 nginx:alpine + dist 文件,没有 node_modules!
# 3. 善用 .dockerignore
# .dockerignore
node_modules
.git
*.md
.env
.DS_Store
dist
coverage
Alpine vs Debian vs Distroless
|
基础镜像 |
大小 |
glibc |
包管理器 |
适用场景 |
|---|---|---|---|---|
|
|
~7MB |
musl |
apk |
通用,最轻量 |
|
|
~80MB |
glibc |
apt |
需要 glibc 的应用 |
|
|
~77MB |
glibc |
apt |
广泛支持 |
|
|
~20MB |
glibc |
无 |
安全敏感,无 shell |
7.2 日志管理
# 限制容器日志大小(防止撑爆磁盘)
docker run -d \
--log-opt max-size=10m \
--log-opt max-file=3 \
myapp
# docker-compose.yml 中配置日志
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# 使用其他日志驱动
# docker run --log-driver=syslog ...
# docker run --log-driver=fluentd ...
# 查看 Docker 日志占用
docker system df
du -sh /var/lib/docker/containers/*/
7.3 资源限制
# CPU 限制
docker run --cpus 2 \ # 最多用 2 个 CPU 核心
--cpu-shares 512 \ # CPU 权重(相对值,默认 1024)
app:latest
# 内存限制
docker run -m 512m \ # 最大 512MB
--memory-swap 1g \ # 内存+Swap 上限
--memory-reservation 256m \ # 软限制(预留内存)
app:latest
# 在 docker-compose.yml 中
services:
app:
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
7.4 常用调试命令集合
# 查看 Docker 磁盘占用
docker system df # 概要
docker system df -v # 详细(每个组件占用)
# 一键清理(谨慎!)
docker system prune # 清理停止容器、未使用网络、悬挂镜像
docker system prune -a # 附加清理所有未使用镜像
docker system prune -a -f --volumes # 包括未使用的卷
# 构建历史查看
docker history myapp:latest
# 查看容器文件变化
docker diff my-nginx # 显示容器相对于镜像的文件变更
# 查看容器内运行的进程
docker top my-nginx
# 查看容器 IP 等详细信息
docker inspect my-nginx | jq '.[0].NetworkSettings.Networks'
# 测试 DNS 解析
docker exec my-nginx nslookup mysql
# 复制粘贴(理解 --rm 的用法)
docker run --rm -v $(pwd):/data alpine tar czf /data/backup.tar.gz -C /app data
7.5 安全最佳实践
# 1. 不要用 root 运行应用
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# 2. 不要写敏感信息在镜像中
# ❌ ENV DB_PASSWORD=plaintext
# ✅ 运行时传入
# docker run -e DB_PASSWORD=$(cat /run/secrets/db_password)
# 3. 使用具体版本标签,不用 latest
# ❌ FROM node:latest
# ✅ FROM node:20.11.0-alpine3.19
# 4. 定期扫描镜像漏洞
# docker scan myapp:latest
# 5. 设置只读文件系统
# docker run --read-only myapp
# 配合 tmpfs 给需要写入的目录
# docker run --read-only --tmpfs /tmp --tmpfs /var/run myapp
# 6. 限制容器能力
# docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
八、常见问题排查
Q1:容器启动了但立刻退出
# 原因:主进程执行完就退出
docker run -d alpine # alpine 没有前台进程,立即退出
# 排查
docker logs <container>
docker ps -a | grep Exited
# 解决:确保有前台进程运行
docker run -d alpine tail -f /dev/null # 临时保持运行
docker run -d -it alpine /bin/sh # 交互式
Q2:端口冲突
# 报错:Bind for 0.0.0.0:8080 failed: port is already allocated
# 查看谁占用了端口
sudo lsof -i :8080
sudo netstat -tulnp | grep 8080
# 或用其他端口
docker run -p 8081:8080 myapp
Q3:容器之间无法通信
# 1. 确认在同一网络
docker network inspect mynet
# 2. 测试 DNS
docker exec app ping mysql # 容器名能不能解析
docker exec app nslookup mysql
# 3. 确认防火墙未拦截
Q4:镜像构建太慢
# 1. 利用 Docker BuildKit 缓存
DOCKER_BUILDKIT=1 docker build -t myapp .
# 2. 把不常变的层放前面
# 3. 使用 .dockerignore 减少 context
# 4. 使用 --cache-from 复用远程缓存
docker build --cache-from myregistry.com/myapp:latest -t myapp .
附录:学习路线
阶段一:理解概念(半天)
├── 什么是容器?和虚拟机的区别
├── 镜像/容器/仓库三大概念
└── 分层文件系统原理
阶段二:核心命令(1-2天)
├── docker pull/images/rmi(镜像管理)
├── docker run/ps/stop/rm/logs/exec(容器生命周期)
└── docker cp/inspect/stats/top(调试查看)
阶段三:实战部署(1-2天)
├── docker run 部署 MySQL/Redis/Nginx
├── 数据卷挂载(-v)
└── 端口映射(-p)、环境变量(-e)
阶段四:构建镜像(1-2天)
├── Dockerfile 编写(FROM/COPY/RUN/CMD/EXPOSE)
├── 多阶段构建
└── 镜像优化(alpine / 合并层 / .dockerignore)
阶段五:多容器编排(2-3天)
├── docker-compose.yml 编写
├── depends_on + healthcheck
└── 网络管理
阶段六:生产化(持续)
├── 日志管理、资源限制
├── CI/CD 集成
└── K8s/监控/编排
核心原则:一定要动手!每学一个概念就在本地 Docker 上敲一遍。从 docker run nginx 开始,到写出完整 docker-compose.yml 部署 Spring Boot + MySQL + Redis + Nginx 全家桶。每天在测试服务器上操作,一周掌握。