Docker 高频操作手册

面向日常开发与运维的 Docker 速查手册:镜像/容器/Compose/数据卷/网络/排障/备份/安全。
命令兼容 Docker Engine 20.10+(含 Docker Desktop for Windows/Mac、Linux 原生、云主机)。
Windows 下请在 Git Bash / PowerShell / CMD 中执行,docker CLI 命令本身跨平台一致;涉及 shell 语法差异处已标注。


一、核心概念一句话

概念一句话
镜像 Image只读的打包产物(代码 + 运行环境),相当于"安装包"
容器 Container镜像运行起来的实例,可启停、可删,数据默认不持久
数据卷 Volume挂载到容器里的持久化目录,容器删了数据还在
网络 Network容器之间的通信通道,同网络内可用服务名互访
Compose用 YAML 声明多容器编排,一条命令起整套环境

最重要的一条心智模型:容器是"临时"的,数据要放卷里。 90% 的"数据丢了"事故,都是因为没挂卷或删了卷。


二、镜像操作

docker pull nginx:1.27            # 拉取镜像(:tag 指定版本,不写默认 latest)
docker images                     # 列出本地镜像
docker image inspect nginx:1.27   # 查看镜像详情(架构、环境变量、层信息)
docker rmi nginx:1.27             # 删除镜像(有容器在用时需先删容器或加 -f)

# 构建镜像
docker build -t myapp:1.0 .              # 用当前目录 Dockerfile 构建
docker build -t myapp:1.0 --no-cache .   # 跳过层缓存(构建异常/要验证依赖时用)
docker build -f deploy/Dockerfile -t myapp:1.0 .   # 指定 Dockerfile 路径

# 镜像导出迁移(离线环境/跨机器)
docker save myapp:1.0 | gzip > myapp.tar.gz            # 导出
docker load < myapp.tar.gz                             # 导入
docker save myapp:1.0 -o myapp.tar                     # 导出为单文件

# 推送到仓库
docker tag myapp:1.0 registry.example.com/myapp:1.0    # 打仓库 tag
docker push registry.example.com/myapp:1.0             # 推送

镜像瘦身三条铁律:

  1. 用多阶段构建(构建阶段装全套工具,运行阶段只拷产物,见附录)
  2. 写 .dockerignore(排除 node_modules/.git/dist 等,避免脏文件进镜像 + 缓存失效)
  3. 基础镜像选小体积:alpine / slim 优先,别默认 latest 全量版

三、容器操作

# 运行
docker run -d --name myapp -p 8080:80 -v mydata:/data -e KEY=value myapp:1.0
#        │         │              │          │              │
#        后台运行   容器名         端口映射    数据卷挂载      环境变量

# 常用参数
--restart unless-stopped   # 退出自动重启(推荐,配合 docker restart 语义)
--network mynet            # 加入自定义网络(容器间用服务名互访的前提)
--env-file .env            # 从文件注入环境变量(密钥别写命令行/镜像里)
--read-only                # 只读根文件系统(安全加固)
-v /host/path:/container   # bind mount(挂宿主机目录)
-v /container             # 匿名卷(很少用)

# 查看与启停
docker ps                          # 运行中的容器
docker ps -a                       # 全部(含已退出)
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker start/stop/restart myapp
docker rm -f myapp                 # 删除容器(-f 强删运行中的)

# 日志
docker logs myapp                  # 全部日志
docker logs -f --tail=200 myapp    # 跟随输出,只看最近 200 行
docker logs myapp 2>&1 | grep -i error    # 过滤错误
docker logs myapp | grep "traceId"        # 用 traceId/请求号 grep 链路

# 进入容器
docker exec -it myapp sh           # 交互式进入(alpine 用 sh,Ubuntu 用 bash)
docker exec myapp env              # 看容器内环境变量(排查配置)
docker exec myapp cat /etc/hosts   # 看 DNS 解析(排查容器互访)

# 拷贝文件
docker cp myapp:/app/logs/ ./logs/          # 容器 → 宿主机
docker cp ./config.yml myapp:/app/config.yml  # 宿主机 → 容器

# 端口与资源
docker port myapp                  # 查看端口映射
docker stats                       # 实时 CPU/内存(排查资源占用)
docker inspect myapp | grep -i ip  # 容器 IP(调试用,正常应走服务名)

四、Compose 多容器编排

# 最常用组合
docker compose up -d               # 按 compose.yml 启动(不重建)
docker compose up -d --build       # 改代码后重建镜像并启动(部署主命令)
docker compose up -d backend       # 只操作某个服务
docker compose down                # 停止并删除容器(数据卷保留!)
docker compose down -v             # 停止并删除容器+数据卷(⚠️ 数据全清)

# 查看与调试
docker compose ps                  # 服务状态
docker compose logs -f             # 全部服务日志
docker compose logs --tail=200 backend
docker compose exec mysql sh       # 进入某服务容器
docker compose config              # 校验并打印解析后的最终配置(改完先跑这个)
docker compose config --services   # 列出服务名

# 其他常用
docker compose -p myproj up -d     # 指定项目名(影响卷名/网络名前缀)
docker compose restart backend     # 重启单个服务
docker compose pull                # 更新镜像

依赖控制:depends_on + healthcheck。 depends_on 默认只保证"容器启动了",不保证"服务可用了"(比如 MySQL 容器起来 ≠ 能接受连接)。要等真正就绪,必须给依赖服务配健康检查:

services:
  mysql:
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p123456"]
      interval: 10s
      timeout: 5s
      retries: 10
  backend:
    depends_on:
      mysql:
        condition: service_healthy   # 等 MySQL 真正就绪

项目名规则: 卷名/网络名 = <项目名>_<声明名>,项目名默认取 compose 文件所在目录名(-p 可覆盖)。所以同一个 compose 文件在两个目录里跑,卷是两套——查卷名请以 docker volume ls 实际输出为准,别靠猜。


五、数据卷与挂载

命名卷(推荐,数据与容器生命周期解耦)

docker volume ls                      # 列卷
docker volume inspect mydata          # 看挂载点(Linux 是 /var/lib/docker/volumes/...)
docker volume create mydata           # 手动创建
docker volume rm mydata               # 删除
  • Linux:卷存在 /var/lib/docker/volumes/<卷名>/_data
  • Windows(Docker Desktop):存在 WSL2 的 docker-desktop-data 发行版里,宿主路径 \\wsl$\docker-desktop-data\data\docker\volumes\<卷名>\_data。别手动去翻改,只作查看
  • macOS(Docker Desktop):在 Docker Desktop 的 Linux VM 里

卷备份与恢复(通用套路)

# 备份:把卷挂到临时 alpine 容器里打包
docker run --rm -v mydata:/data -v "$(pwd)":/backup \
  alpine tar czf /backup/mydata_$(date +%Y%m%d).tar.gz -C /data .

# 恢复:先建空卷,再解包
docker volume create mydata
docker run --rm -v mydata:/data -v "$(pwd)":/backup \
  alpine tar xzf /backup/mydata_xxx.tar.gz -C /data

Windows 下 $(pwd) 在 Git Bash 可用;PowerShell 用 $PWD 或 ${PWD},CMD 用 %cd%。

数据库备份(MySQL 示例)

docker compose exec mysql sh -c 'mysqldump -uroot -p$MYSQL_ROOT_PASSWORD dbname' > backup.sql
# 恢复
docker compose exec -T mysql sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD dbname' < backup.sql

named volume vs bind mount 怎么选

命名卷bind mount(宿主机目录)
位置Docker 管理,不关心具体路径你指定的宿主机路径
适用数据库/上传文件等纯数据开发调试(改宿主机代码即生效)、nginx 配置
迁移容易(卷名搬运)路径跟着项目走
权限Docker 自动处理需注意 UID 权限(Linux 常见坑)

六、网络

docker network ls                    # 列网络(bridge/host/none/自定义)
docker network create mynet          # 创建自定义 bridge 网络
docker network inspect mynet         # 看网络里的容器与 IP

要点:

  1. 容器间通信优先用服务名/容器名,不要用 IP(容器重建 IP 会变)。compose 内自动建网络,服务名即 DNS 名
  2. 自定义网络里容器名可互相解析;默认 bridge 网络只能靠 IP
  3. 端口映射排查:docker port <容器> + 宿主机 netstat -ano | findstr <port>(Windows)/ ss -tlnp(Linux)
  4. 容器里连宿主机:Linux 用 host.docker.internal(Docker Desktop 已内置),Linux 原生需 --add-host=host.docker.internal:host-gateway

七、故障排查清单(按症状定位)

1. 容器起不来 / 一直重启

docker ps -a                    # 看状态与重启次数(Restarting (1) xx秒前)
docker logs --tail=50 <容器>    # 看崩溃原因,这是第一步
docker inspect <容器> | grep -A5 State   # 看退出码(ExitCode 137=被杀 143=优雅停)

2. 端口冲突 address already in use

netstat -ano | findstr <port>       # Windows 找占用进程
ss -tlnp | grep <port>              # Linux
# 解法:停占用进程,或改映射端口后 docker compose up -d

3. 容器 A 连不上容器 B

  • 确认在同一网络:docker network inspect <网络> | grep 容器名
  • 代码里连的是服务名不是 localhost/IP
  • 确认依赖服务真的起来了(docker compose ps 状态 healthy)
  • 容器里验证:docker exec -it <A> sh 然后 ping <B> / curl <B>:port

4. 容器重启后数据丢了

  • 检查数据目录有没有挂卷:docker inspect <容器> | grep -A10 Mounts
  • 没有挂卷 → 容器删除即数据消失。改 compose 加 volume,重建
  • 挂了卷但数据"没生效" → 检查挂载目标路径是否正确(容器内应用写的目录 vs 挂载点)

5. 中文乱码 / 时区不对

  • 时区:ENV TZ=Asia/Shanghai + -e TZ=Asia/Shanghai
  • 中文乱码:容器内 LANG=C.UTF-8;数据库初始化脚本顶部 SET NAMES utf8mb4;(MySQL 官方镜像 init 脚本默认 latin1 会话字符集)

6. healthcheck 一直 unhealthy

docker inspect --format='{{json .State.Health}}' <容器>
# 常见原因:检查命令本身失败(如 mysqladmin 缺密码参数)、间隔/超时参数不合理、服务启动慢于重试窗口

7. 镜像拉取慢 / 失败

  • Docker Desktop:Settings → Docker Engine 里换镜像源(别手改 ~/.docker/daemon.json,会被 GUI 覆盖)
  • 国内源参考(需实测,随时可能失效):https://docker.1panel.live 等
  • 换源后 docker info | grep -A2 Registry 确认生效

8. 容器内时间/日志 UTC 而非本地

  • 统一在 compose/Dockerfile 设 TZ=Asia/Shanghai,日志会随之本地化

八、日志与监控

docker logs -f --tail=200 <容器>          # 高频操作,够用 90% 场景
docker logs --since 1h <容器>             # 最近 1 小时
docker logs --since "2026-08-06T10:00:00" <容器>
docker events --filter container=<容器>   # 实时事件流(启停/异常排查)
docker stats                              # 实时资源占用
docker system df                          # 磁盘占用分布(镜像/容器/卷/缓存)

日志轮转(防止日志撑爆磁盘): compose 里给服务配:

logging:
  driver: json-file
  options:
    max-size: "20m"     # 单文件上限
    max-file: "5"       # 保留份数

九、清理与镜像瘦身

docker system df                          # 先看占用,再决定清什么
docker system prune                      # 清停止容器/悬空镜像/无用网络(不动数据卷)
docker system prune -a                   # 全清(含未使用镜像,慎用)
docker system prune --volumes            # ⚠️ 连未使用数据卷一起清(数据不可恢复)
docker builder prune                     # 清构建缓存(build 占空间的大头)
docker rm $(docker ps -aq)               # 批量删所有停止容器(慎用)

prune 区别一句话: 不带 --volumes 不删卷,带 -a 才删未用镜像。默认的 docker system prune 是相对安全的。


十、备份与恢复(完整清单)

要备份的东西方法
数据卷卷打包法(见第五节)
数据库容器内执行 dump 导出 SQL(通用、跨引擎可恢复,优先于备份数据文件)
镜像docker save / docker load(离线迁移)
容器(含配置)有 compose 就别 commit;docker commit 只作应急快照,不推荐日常用
Compose 配置docker compose config > 解析后配置.yml 留档

备份纪律: 先停写(或选低峰),备份后验证可恢复(docker load / 导入 SQL 到临时库跑通),再算备份完成。


十一、安全红线(必读)

  1. 密钥/密码/token 一律环境变量注入(--env-file 或 compose environment),禁止写进镜像、配置文件、命令行。曾发生 API Key 提交进公开仓库需重写历史的真实事故
  2. 只暴露必要端口:DB/Redis 等仅内网的服务不要映射到宿主端口;生产用 compose 内部网络互访即可
  3. 容器内不用 root:USER 指令切非 root;镜像里避免 RUN chmod 777
  4. 基础镜像用可信来源:官方镜像 / 知名组织;docker scan 或 trivy 扫漏洞
  5. .dockerignore:防止本地敏感文件(.env、密钥文件)被打进镜像
  6. 健康检查 + restart 策略:restart: unless-stopped 让故障自动恢复,减少人工介入窗口

十二、30 秒速查表

想做什么命令
启动(含重建)docker compose up -d --build
查看状态docker compose ps / docker ps -a
看日志docker compose logs -f / docker logs -f --tail=200 <c>
进入容器docker compose exec <svc> sh
停止(保留数据)docker compose down
重置(清数据)docker compose down -v(⚠️)
重启单个服务docker compose restart <svc>
拉镜像/构建docker pull <img> / docker build -t <tag> .
看卷docker volume ls
看资源docker stats
磁盘占用docker system df
清理docker system prune -f
备份卷docker run --rm -v <vol>:/data -v $(pwd):/backup alpine tar czf /backup/x.tar.gz -C /data .

附录 A:Dockerfile 常用指令速查

FROM base:tag            # 基础镜像(多阶段构建可写多个 FROM,取最后一个为运行镜像)
WORKDIR /app             # 工作目录(不存在自动创建)
COPY src dest            # 拷贝(推荐,比 ADD 语义明确;ADD 带自动解压,非必要不用)
RUN cmd                  # 构建期执行(装依赖/编译)
ENV KEY=value            # 环境变量(构建期 + 运行期都生效)
EXPOSE 8080              # 声明端口(仅文档用途,真正映射在 run/compose 配)
USER nonroot             # 切非 root 运行(安全)
CMD ["java","-jar","app.jar"]   # 运行命令(可被 docker run 参数覆盖)
ENTRYPOINT ["sh","-c"]   # 入口(不易覆盖;与 CMD 配合)

多阶段构建模板(Java 为例,其他语言同理):

# 阶段 1:构建(装全套工具)
FROM gradle:8-jdk21 AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
COPY src ./src
RUN gradle bootJar --no-daemon -x test

# 阶段 2:运行(只拷产物,镜像小、攻击面小)
FROM eclipse-temurin:21-jre
COPY --from=build /app/build/libs/*.jar app.jar
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms256m -Xmx512m"
ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar app.jar"]

层缓存技巧: 把"不常变的依赖声明"COPY 在"频繁变的源码"之前,依赖下载层就能命中缓存。


附录 B:docker-compose.yml 常用字段说明

services:
  web:
    image: nginx:1.27          # 或 build: ./dir 二选一(或两者都要:build 构建 + image 命名)
    build:
      context: .               # 构建上下文
      dockerfile: Dockerfile
    container_name: my-web     # 固定容器名(一个 compose 内唯一)
    ports:
      - "8080:80"              # 宿主:容器;只写 "80" 则随机宿主端口
    expose:
      - "8080"                 # 仅容器间可访问,不映射宿主
    environment:               # 环境变量(值可引用宿主变量 ${VAR})
      KEY: value
    env_file: .env             # 从文件注入(密钥放这里,不进 git)
    volumes:
      - mydata:/data           # 命名卷
      - ./conf:/etc/nginx:ro   # bind mount + 只读
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 5s
      retries: 3
    depends_on:
      db:
        condition: service_healthy
    networks:
      - mynet
    logging:
      driver: json-file
      options: { max-size: "20m", max-file: "5" }

networks:
  mynet:                       # 自定义网络(服务名互访)
    driver: bridge

volumes:
  mydata:                      # 声明命名卷(卷名 = <项目名>_mydata)

更多推荐