第一部分:基础三要素

1. 镜像(Image)

镜像 = 程序 + 运行环境打包成一个只读文件。

MySQL 镜像里包含:MySQL 程序 + 微型 Linux + MySQL 需要的所有依赖。这个镜像扔到任何装了 Docker 的机器上都能还原出一模一样的运行效果。

镜像和容器的关系:镜像 : 容器 ≈ Word 模板 : 打开的编辑窗口。模板只有一个,用它创建的窗口可以有无数个。

镜像是分层存储的。 每条 Dockerfile 指令生成一层:

┌─────────────────────┐
│  COPY demo.jar       │ ← 层3:你的 jar
├─────────────────────┤
│  JDK 21 JRE          │ ← 层2:运行时
├─────────────────────┤
│  Alpine Linux         │ ← 层1:操作系统
└─────────────────────┘
     最终镜像 = 层1~3 叠加

CMD、EXPOSE、ENV 不产生新层,只有 RUN、COPY、ADD 才会。

五个项目都用同一个 JRE 层 → 这一层只存一份,各镜像共享引用。这就是为什么 Docker 比虚拟机省磁盘。

镜像按用途分两类:

基础镜像(JDK)中间件镜像(MySQL/Redis)
用途作为项目镜像的"底座"提供独立网络服务
存在意义被 Dockerfile 引用(FROMdocker run 启动成容器
不启动容器能用吗?✅ 能,已打进项目镜像❌ 不能,不启动则服务不可用

记法:基础镜像 = 底座,用了就行。中间件镜像 = 下载了安装包但没安装,必须 docker run 才干活。


2. 容器(Container)

容器是用镜像启动出来的运行实例。它是真正在干活的东西——MySQL 容器在存数据,Redis 容器在缓存,你的 my-app 容器在处理请求。

容器之间文件系统、进程、网络完全隔离。删容器就像删一个 Word 窗口,模板(镜像)不受影响。


3. Dockerfile

你自己的项目没有现成镜像,需要写一份"打包说明书"告诉 Docker 怎么构建。这个说明书就是 Dockerfile。

先回顾没有 Docker 时你怎么部署 Java 项目的:

mvn package  →  blog.jar  →  传到 Linux  →  java -jar blog.jar

有了 Docker 以后,Spring Boot 项目有三种构建方式,本质区别只有一个:编译(mvn package)谁来跑。


方式一:基础 Dockerfile(相当于传统方式)

# FROM    = 基于哪个基础镜像(这里用 JDK 21 精简版)
FROM eclipse-temurin:21-jdk-slim
# COPY    = 把宿主机文件复制到镜像里
#           左边:你电脑上的 jar    右边:镜像里的路径
COPY target/*.jar app.jar
# EXPOSE  = 声明容器监听哪个端口(文档性质,不实际映射)
EXPOSE 8080
# CMD     = 容器启动时执行的命令
CMD ["java", "-jar", "app.jar"]

先手动 mvn package,再把打好的 jar COPY 进镜像。

流程:你电脑 mvn package → docker build -t my-app . → 镜像

最终镜像包含完整 JDK + jar,约 400MB。和传统部署一模一样,只是目的地从 Linux 服务器换成了 Docker 镜像。


方式二:多阶段构建(推荐)

# ===== 阶段1:编译(只用来构建,不进入最终镜像)=====
# FROM    = 基础镜像,AS builder 给这个阶段起个名字
FROM maven:3.9-eclipse-temurin-21 AS builder
# COPY . . = 把当前目录所有文件复制到镜像工作目录
COPY . .
# RUN     = 在构建过程中执行命令(这里跑 Maven 编译)
RUN mvn package -DskipTests

# ===== 阶段2:运行(最终镜像只包含这个阶段)=====
# FROM    = 换个轻量基础镜像,只有 JRE 没有 Maven
FROM eclipse-temurin:21-jre-alpine
# COPY --from = 从指定阶段复制文件(只取 jar,不要源码和 Maven)
# 单模块项目:target/*.jar;多模块项目:blog-bootstrap/target/*.jar
COPY --from=builder target/*.jar app.jar
# EXPOSE  = 声明端口
EXPOSE 8080
# CMD     = 启动命令
CMD ["java", "-jar", "app.jar"]

编译也进了 Docker,你不需要手动 mvn package

流程:docker build -t my-app . → 镜像(一条命令,编译+打包全自动)

最终镜像只有 JRE + jar,约 250MB。比方式一多了 AS builder + COPY --from=builder 两行,省了你手动打包步骤。

为什么方式二比方式一小?省的不是 jar,是底座。

两个方式最终都只拿了一个 jar:

方式一 底座:eclipse-temurin:21-jdk-slim  ← JDK ≈ 350MB
方式二 底座:eclipse-temurin:21-jre-alpine ← JRE ≈ 180MB

JDK vs JRE:

JDK(Java Development Kit)
  ├── JRE(Java Runtime Environment)← 运行时,180MB
  ├── javac 编译器                    ← 源码 → .class
  ├── jdb 调试器
  ├── javadoc 文档生成器
  └── 其他开发工具...
  总约 350MB
  • 你在 IDEA 里写代码、改代码、编译、调试 → 必须用 JDK
  • jar 部署到服务器上,只跑不编译 → JRE 就够了

方式一不分阶段,只能用 JDK 当底座(约 350MB);方式二把编译交给阶段1(Maven 镜像),阶段2 换 JRE 底座(约 180MB)。jar 没变,底座瘦了 170MB。


方式三:Buildpacks(零配置)

不需要 Dockerfile,一行命令:

mvn spring-boot:build-image

全自动黑盒生成镜像,约 500MB。上手最快,但命名不灵活、构建过程黑盒、出问题难排查。


三种方式对比:

方式一 基础方式二 多阶段方式三 Buildpacks
需要 Dockerfile需要需要不需要
需要手动 mvn package需要不需要不需要
最终镜像体积~400MB~250MB~500MB
相当于传统方式吗✅ 流程一样传统方式的自动化版全自动黑盒
推荐度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

Dockerfile 命名规则:

  • 文件名固定叫 Dockerfile(D 大写,无后缀)
  • 放在项目根目录
  • docker build -t 镜像名 . 默认在当前目录找这个文件
  • 非要用别的名字也行:docker build -f 其他文件名 -t 镜像名 .,但没人这么做

构建时永远加 -t 起名字,否则 Docker 分配随机 ID:

docker build -t my-app:latest .
#            ↑         ↑       ↑
#       镜像名(自己起) 标签    构建上下文

-t 后面是什么?—— 镜像名 : 标签

my-app:latest 为例:

my-app  :  latest
    ↑              ↑
  镜像名          标签(版本号)
  • 镜像名:你自己起的,见名知意。my-appblog-uimy-java-app 都行
  • 标签:区分同一个镜像的不同版本。latest(最新)、v1.0.020240506 都行
  • my-app:latestmy-app:v1.0 是同一个镜像名的两个版本,Docker 把它们当两个独立镜像管理
  • 标签不写默认为 latest,但它只是个默认值——Docker 不会自动更新它,不是真正意义上的"最新版本"

最后的 . 是什么?—— 构建上下文

那个 . 不是随便打的,它告诉 Docker 把哪个目录打包发给构建引擎

docker build -t my-app:latest .
                                  ↑
                        当前目录 = 构建上下文

Docker 构建时做两件事:

  1. . 指定的目录整个打包,发给 Docker 引擎
  2. Dockerfile 里的 COPY . . 就是从这份打包内容里取文件

所以 . 必须是包含 Dockerfile 和所有源码的那个目录。如果你在项目根目录跑命令,. 就是项目根目录。如果你在别的地方跑:

docker build -t my-app:latest ./my-app
#                                  ↑
#                        构建上下文是这个子目录

.dockerignore 可以排除掉不要的文件(node_modulestarget),避免把几万个文件发给构建引擎。


第二部分:启动容器的四件套

4. 端口映射(-p)

docker run -p 3306:3306 mysql:8.0
#            ↑    ↑
#          宿主机  容器内部

默认: 不加 -p,外面访问不到容器。同一个 Docker 网络下的容器之间不受影响——它们走容器名就能互访,不需要端口映射。

一句话判断:

需要从"宿主机或外部"访问容器 → 加 -p
只是"容器和容器之间"通信   → 不加
场景要不要映射例子
浏览器访问前端页面✅ 必须-p 80:80
IDEA 连 MySQL 调试✅ 开发时需要-p 3306:3306
my-app 连 MySQL❌ 不用容器名 mysql:3306 直接通
my-app 连 Redis❌ 不用容器名 redis:6379 直接通

两个实用技巧:

  • 端口冲突了改左边:-p 3307:3306localhost:3307
  • 只让本机访问:-p 127.0.0.1:3306:3306

开发 vs 部署:

开发环境:想连什么映射什么(方便调试)
           MySQL ✓  Redis ✓  my-app ✓  blog-ui ✓

部署上线:只映射前端,其余全关
           MySQL ✗  Redis ✗  my-app ✗  blog-ui ✓
           ↑ 多映射一个端口就多一扇攻击面

记住一条:容器间互访走容器名,不经过端口映射。只有外面要进来才需要开映射。


5. 数据挂载(-v)

docker run -v D:/data/mysql:/var/lib/mysql mysql:8.0
#            ↑                ↑
#        宿主机目录    容器内 MySQL 存数据的地方

默认行为: 不加 -v,数据存在容器的可写层里。容器在数据在,容器删数据跟着消失,没有任何恢复手段。

什么时候不用加: 无状态服务或临时测试。比如你跑个 hello-world、临时拉个镜像试一下——用完就删,不需要持久化。

什么时候必须加:

  • 数据库(MySQL、PostgreSQL 等)
  • 文件存储(上传的图片、附件等)
  • 任何删容器后还想保留的数据

三种挂载方式怎么选:

方式写法适用场景
绑定挂载-v D:/data/mysql:/var/lib/mysql开发用,你知道路径在哪
命名卷-v mysql-data:/var/lib/mysql生产推荐,Docker 管理存储位置
匿名卷-v /var/lib/mysql不推荐,删容器后找不到数据

宿主机路径必须是绝对路径,不能用相对路径。

推荐:有状态服务必须加(数据库、文件存储),无状态服务不用加。


6. 环境变量(-e)

docker run -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=blog mysql:8.0

默认行为: 不加 -e,镜像使用内置默认值。不同镜像差异很大:

  • MySQL:没有默认密码,不设直接启动失败
  • Redis:能启动,但无密码保护(安全风险)
  • Nginx:不需要任何变量就能跑

什么时候不用加: 镜像默认值刚好满足需求。比如 Nginx 的默认配置就能提供静态文件。

什么时候必须加: 默认值不满足需求时。最常见的就是数据库密码、连接地址、服务端口这些。

怎么加更好:

  • 敏感信息(密码、密钥)用 env_file 单独存放,不要直接写在 docker-compose.yml
  • 环境变量只在首次启动时生效。数据已初始化后再改不会自动生效
  • 不确定有哪些可用变量?去 Docker Hub 搜镜像名,页面上的 Environment Variables 章节全列出来了

推荐:数据库等必须设的镜像必加,简单服务(Nginx 等)不加也能跑就不加。


7. 资源限制

docker run --memory="512m" --cpus="2" ...

默认行为: 不加限制,容器可以无上限使用宿主机所有 CPU 和内存。一个容器写了个死循环,整台机器卡死。

什么时候不用加: 本地开发调试。你一个人用,不会跑死循环,多给点资源跑得快。

什么时候必须加: 生产环境。多个容器共享一台机器,不设限等于坐等事故——一个服务有问题就能拖垮所有其他服务。

怎么加更好:

  • 总内存给宿主机留 20%,其余按服务重要程度分配
  • 数据库多给点内存,前端静态服务给 128MB 就够
  • --memory-swap 控制 swap 使用量,生产环境一般设为 memory 的 1.5 倍

推荐:本地开发不用加,上了生产/服务器必须加。


第三部分:容器管理

8. 生命周期

docker start mysql      启动已存在的容器
docker stop mysql       停止
docker restart mysql    重启
docker rm mysql         删除容器(必须先 stop)
docker ps               看正在跑的容器
docker ps -a            看所有容器(包括停掉的)

停止不是删除。停了还能再启,删了就真没了。


9. 进入容器(exec)

容器跑起来了,想进去看看内部:

docker exec -it mysql bash

进去后就是一个微型 Linux 终端,可以 lscatmysql -uroot -p。用完 exit 退出。

注意: Alpine 系镜像(标签带 alpine)不带 bash,只有 sh。进去的命令是:

docker exec -it redis sh      # alpine 镜像用 sh 而不是 bash

不确定镜像有没有 bash?先用 sh 试试,大多数镜像都有。

容器内常用目录:

  • 后端应用:取决于 Dockerfile 的 WORKDIR,常见 /app/
  • 前端静态文件:/usr/share/nginx/html(Nginx 默认)
  • 不确定的话先 docker exec -it 容器名 sh 进去 ls 看一下

10. 查看日志(logs)

容器出错起不来,第一件事看日志:

docker logs mysql              # 看全部
docker logs -f mysql           # 实时滚动,Ctrl+C 退出
docker logs --tail 50 mysql    # 只看最近 50 行

默认行为: 日志用 json-file 驱动,输出到 Docker 内部文件。没有大小限制,没有自动轮转——一个容器长期运行日志能撑爆磁盘。

生产环境建议配日志上限:

docker run --log-opt max-size=10m --log-opt max-file=3 ...

单文件最大 10MB,最多保留 3 个文件,超出自动滚动删除。

推荐:本地开发不管,生产环境必须配日志轮转。


11. 清理命令

Docker 不会自动清垃圾,用久了能吃掉几十 GB:

docker system prune       # 清理停止的容器、未用的网络、悬空镜像
docker volume prune       # 清理未挂载的数据卷
docker builder prune      # 清理构建缓存

默认行为: 停止的容器、没打标签的镜像、孤儿数据卷、构建缓存——全都不自动清理,永远堆积。定期手动 prune 是唯一回收空间的办法。

docker system prune -a 可以一次性清理所有未用资源,包括有标签但没被容器使用的镜像。

推荐:每个月跑一次 docker system prune,磁盘告急时跑 -a

容器卡死抢救:

docker stop / docker kill 都不响应时(runc 进程异常),直接杀宿主机进程:

# 1. 获取容器主进程 PID(两行在同一 shell 会话执行)
PID=$(docker inspect -f '{{.State.Pid}}' 容器名或ID)
kill $PID               # 不响应则 kill -9 $PID

# 2. 确认已停止
docker ps -a

# 3. 清理并重建
docker rm -f 容器名
docker compose up -d

kill $PID 后容器状态变为 stopped,docker rm -f 清理干净,然后 compose 重部署。


第四部分:编排与组合

12. 容器间网络

docker network create blog-net
docker run --network blog-net --name mysql ...
docker run --network blog-net --name my-app ...

默认行为: 不加 --network,容器被挂到 Docker 默认的 bridge 网络。这个默认网络不支持 DNS 解析——容器之间只能通过 IP 地址通信,不能用容器名。

也就是说,你得先 docker inspect mysql | grep IPAddress 查出容器的 IP,再拿这个 IP 去连接。容器重启后 IP 会变,所以基本上没法用。

什么时候默认够用: 只跑单个容器,不需要容器间通信。

什么时候必须加自定义网络: 两个及以上容器需要互访。

怎么加更好:

  • docker compose——网络自动创建,详见第 15 节
  • 手动 docker run 的话:docker network create blog-net,然后容器加 --network blog-net

限制: 不同网络之间默认隔离。一个容器可以加入多个网络(docker network connect),但通常不需要。

本地开发场景区分:

关键看谁连谁——是宿主机上的 IDEA 连容器,还是容器之间互访:

场景需要自定义网络?为什么
IDEA → MySQL 容器❌ 不需要localhost:端口 走端口映射
my-app 容器 → MySQL 容器✅ 必须容器间互访需要容器名 DNS
浏览器 → blog-ui 容器❌ 不需要localhost:80 走端口映射

判断方法: 连接方在宿主机上(IDEA、浏览器)→ 用 localhost:端口,默认 bridge 即可。连接方在容器里(my-app)→ 必须自定义网络。

推荐:用 docker compose,网络自动创建,什么都不用管。手动 docker run 且容器需要互访时,必须建自定义网络。


13. 重启策略

默认行为: 不加即 --restart no。容器挂了就挂了,Docker 不会自动重启它。Docker Desktop 重启后,所有容器全部停掉,需要手动 docker start

什么时候不用加: 本地开发调试。容器挂了你要看错误信息、改配置,自动重启反而干扰排查。

什么时候必须加: 生产环境。服务器半夜自动更新重启,不加的话第二天早上所有服务全停。

四个值怎么选:

行为适合
no(默认)不自动重启本地开发、一次性任务
always挂了自启,Docker 启动自启核心服务(nginx 等)
unless-stopped和 always 一样,但手动 stop 后不自动启动数据库等需要手动停机维护的服务
on-failure:5异常退出才重启,最多 5 次任务型容器,正常退出不应该重启

推荐:本地开发不加,生产环境统一用 unless-stopped


14. 健康检查(Health Check)

# 注意:Alpine 镜像不带 curl,需先 RUN apk add --no-cache curl
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

默认行为: 不加 HEALTHCHECK,Docker 只看主进程 PID 是否存活——MySQL 进程跑起来了但初始化还没完成,Docker 就认为"正常"。这就是为什么容器状态是 running 但你连不上。

什么时候不用加: 服务启动即就绪(比如 Nginx 几乎没有初始化延迟),或者不依赖健康状态来做启动顺序控制。

什么时候必须加:

  • 数据库等初始化较慢的服务(MySQL 首次启动要几十秒)
  • 需要控制启动顺序(my-app 必须等 MySQL 就绪才能启动)

怎么加更好:

  • interval 别太短(至少 10s),否则健康检查本身消耗资源
  • timeout 别太短(至少 5s),数据库响应慢一点就被误判为 unhealthy
  • docker-compose 里的关键用法:
depends_on:
  mysql:
    condition: service_healthy    # 等健康检查通过才启当前服务

推荐:数据库和初始化慢的服务必须加,简单服务(Nginx 等)可不加。


15. docker compose

dockerdocker compose 不是两个独立工具——docker composedocker 的批量管理层

docker           ← 管单个对象:docker run 一个容器、docker build 一个镜像
docker compose   ← 管整个项目:docker compose up 启动一组容器,含网络、顺序、命名

它们读什么文件:

docker build -t blog-server .       ← 读 Dockerfile,不认识 docker-compose.yml
docker compose up -d                ← 主读 docker-compose.yml

docker compose build / up --build   ← 读 yml → 找 build: → 认识 Dockerfile

docker 不认识 yml;docker compose 主读 yml,但可以通过 build: 找到并调用 Dockerfile。这个认知是单向的——Dockerfile 不知道 yml 的存在。

你把 docker-compose.yml 放在那,敲 docker run 它看都不看。但你敲 docker compose up --build,compose 会去 yml 里找 build: 路径,然后读那个目录下的 Dockerfile。

类比理解:

  • docker run = 你手动开灯、开空调、拉窗帘
  • docker compose = 你按一下"回家模式",灯、空调、窗帘全自动搞定

Compose 把多个 docker run 命令的参数(端口、挂载、变量、网络)声明式写成一个文件:

services:
  web:
    image: nginx:alpine
    ports: ["8080:80"]
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret

docker compose up -d,所有服务全起。

什么时候不用 compose: 只跑一个容器,一条 docker run 就够。

什么时候必须用 compose: 一个项目有 2 个及以上的服务。

Compose 默认帮你做了三件事:

  1. 自动建网络:名叫 <目录名>_default,service 名 = DNS 域名。DB_HOST: mysql 能工作就是因为它
  2. 自动命名容器<目录名>-<service名>-1,不用手动 --name
  3. 自动管理启动顺序depends_on 保证依赖先启动

常用命令:

docker compose up -d                        # 启动所有服务
docker compose down                         # 停止并删除所有容器+网络
docker compose stop / restart / start       # 停止/重启/恢复
docker compose ps                           # 只看当前项目容器
docker compose logs -f                      # 所有容器日志一起看
docker compose -f 指定文件.yml up -d         # 用指定 yml 文件启动
docker compose up -d --build                # 重新构建镜像再启动
docker compose up -d --force-recreate --build  # 强制重建所有容器(改了大配置用这个)

改 yml 后重跑 up -d,没变的服务不动,变了的重建。不变的内容跑第二遍不会重启容器。

build: vs image: 一句话

my-app:
  build: ./my-app    # compose 参与构建:去这个目录找 Dockerfile 编译
  image: my-app:latest  # compose 不参与构建:直接用已有镜像

build: 让 compose 认识了 Dockerfile;image: 让 compose 只负责启动。个人项目统一用 image:docker build 管构建,docker compose up 管启动。

docker 与 docker compose 在构建阶段和启动阶段的完整对比、各自存在的不足、何时用 build: 何时用 image:,见第四篇附:docker 与 docker compose 对比


第五部分:构建优化

16. 分层缓存

镜像是一层层叠上去的,每条 Dockerfile 指令生成一层:

FROM maven:3.9-eclipse-temurin-21  ← 层1:Maven + JDK
COPY pom.xml .                     ← 层2:依赖描述
RUN mvn dependency:resolve          ← 层3:下载依赖
COPY src .                         ← 层4:源码
RUN mvn package                    ← 层5:编译

没变的层会复用缓存,下次构建只重建变化的层。你改了一行源码,只重跑层4和5,秒级完成。

如果缓存导致问题(比如依赖更新了但缓存没刷新),加 --no-cache 强制全量重建:

docker build --no-cache -t my-app:latest .

17. 多阶段构建

单阶段构建会把 JDK + Maven + 源码全打进最终镜像,体积 800MB 起。多阶段把"编译"和"运行"分开:

# 阶段1:编译(只用来构建,不进入最终镜像)
FROM maven:3.9-eclipse-temurin-21 AS builder
COPY . .
RUN mvn package -DskipTests

# 阶段2:运行(最终镜像只包含这个阶段)
FROM eclipse-temurin:21-jre-alpine
# 单模块项目:target/*.jar;多模块项目:<启动模块>/target/*.jar
COPY --from=builder target/*.jar app.jar
CMD ["java", "-jar", "app.jar"]

大白话说就是: 阶段1 里的 Maven、JDK、源码这些东西,只是临时拿来编译一下,编译完就不要了——它们不会存到最终镜像里。最终镜像只留下 JRE(能跑 Java 的最小环境)+ 你的 jar 包,体积砍到 200MB。

阶段1(临时工棚,用完就拆)          阶段2(最终交付的房子)
┌────────────────────────┐        ┌──────────────────┐
│  Maven                  │        │  JRE              │
│  JDK 编译器              │   →    │  app.jar          │
│  源代码                  │        │                   │
│  编译中间产物             │        │  体积 ≈ 200MB     │
│  体积 ≈ 700MB           │        └──────────────────┘
└────────────────────────┘
        ↓
    全部丢弃,不进最终镜像

什么时候不用: 项目不需要自己编译(比如直接运行别人给的 jar 包),一个 FROM + COPY + CMD 就完事。

什么时候必须用: 任何需要编译步骤的项目(Java/Maven、前端/Node 等)。

推荐:只要 Dockerfile 里有编译步骤,就用多阶段构建。


18. .dockerignore

.gitignore 同理,打包镜像时排除不要的文件:

node_modules
.git
target
*.log

默认行为: 不加 .dockerignore项目目录下所有文件(包括 node_modules 几万个文件、target 几百 MB 构建产物、.git 目录)全部发给 Docker 构建引擎。构建又慢又臃肿。

什么时候可以不加: 项目极小,只有一个 jar 文件和一个 Dockerfile。其他情况一律加。

什么时候必须加: 任何前端项目(node_modules 巨大)、任何 Java 项目(target 巨大)。

推荐:每个项目都加,养成习惯。


速查总表

#概念如果不配/不加配了以后关键参数
1镜像
2容器
3Dockerfile没有镜像可用构建出自定义镜像-t 必须加,否则随机 ID
4端口映射容器完全隔离,外面访问不到localhost:端口 可访问-p 宿主机:容器
5数据挂载删容器数据跟着销毁数据持久化到宿主机-v 宿主机:容器
6环境变量MySQL 等镜像会启动失败按变量初始化服务-e KEY=VALUE
7资源限制无上限,能吃满宿主机限制 CPU/内存--memory --cpus
8生命周期start stop rm
9进入容器进去排查内部状态alpine 镜像用 sh 不是 bash
10日志无大小限制,可能撑爆磁盘可配轮转上限--log-opt max-size=
11清理垃圾永远堆积,吃掉几十 GB手动 prune 回收system prune -a
12容器间网络默认 bridge 只能用 IP;容器间互访需自定义网络容器名即 DNS 域名--network;宿主机连容器用 localhost:端口,容器互访用容器名
13重启策略不自动重启,Docker 重启后全停开机自启、挂了自愈--restart unless-stopped
14健康检查只看进程死活,不关心服务是否就绪真实验证可用状态condition: service_healthy
15compose手动敲 N 条 docker run,一个个管一键全家桶启动,批量管整个项目docker compose up -d;compose 是 docker 的批量层
16分层缓存未改层自动复用,秒级构建--no-cache 可强制全量重建
17多阶段构建编译工具和源码全打进镜像,体积臃肿最终镜像只含运行环境AS builder COPY --from=
18dockerignore整个项目目录发给构建引擎,又慢又臃肿排除无用文件,构建飞快.dockerignore

更多推荐