Docker 学习篇(四)| Docker的 核心概念
Docker 学习篇(四)| Docker的 核心概念
第一部分:基础三要素
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 引用(FROM) | 被 docker 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-app、blog-ui、my-java-app都行 - 标签:区分同一个镜像的不同版本。
latest(最新)、v1.0.0、20240506都行 my-app:latest和my-app:v1.0是同一个镜像名的两个版本,Docker 把它们当两个独立镜像管理- 标签不写默认为
latest,但它只是个默认值——Docker 不会自动更新它,不是真正意义上的"最新版本"
最后的 . 是什么?—— 构建上下文
那个 . 不是随便打的,它告诉 Docker 把哪个目录打包发给构建引擎。
docker build -t my-app:latest .
↑
当前目录 = 构建上下文
Docker 构建时做两件事:
- 把
.指定的目录整个打包,发给 Docker 引擎 - Dockerfile 里的
COPY . .就是从这份打包内容里取文件
所以 . 必须是包含 Dockerfile 和所有源码的那个目录。如果你在项目根目录跑命令,. 就是项目根目录。如果你在别的地方跑:
docker build -t my-app:latest ./my-app
# ↑
# 构建上下文是这个子目录
.dockerignore可以排除掉不要的文件(node_modules、target),避免把几万个文件发给构建引擎。
第二部分:启动容器的四件套
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:3306→localhost: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 终端,可以 ls、cat、mysql -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
docker 和 docker compose 不是两个独立工具——docker compose 是 docker 的批量管理层。
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 默认帮你做了三件事:
- 自动建网络:名叫
<目录名>_default,service 名 = DNS 域名。DB_HOST: mysql能工作就是因为它 - 自动命名容器:
<目录名>-<service名>-1,不用手动--name - 自动管理启动顺序:
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 | 容器 | — | — | — |
| 3 | Dockerfile | 没有镜像可用 | 构建出自定义镜像 | -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 |
| 15 | compose | 手动敲 N 条 docker run,一个个管 | 一键全家桶启动,批量管整个项目 | docker compose up -d;compose 是 docker 的批量层 |
| 16 | 分层缓存 | — | 未改层自动复用,秒级构建 | --no-cache 可强制全量重建 |
| 17 | 多阶段构建 | 编译工具和源码全打进镜像,体积臃肿 | 最终镜像只含运行环境 | AS builder COPY --from= |
| 18 | dockerignore | 整个项目目录发给构建引擎,又慢又臃肿 | 排除无用文件,构建飞快 | .dockerignore |
更多推荐
所有评论(0)