一、先从一句日常说法说起
很多人刚接触容器时会这样理解:
- Dockerfile:用来「拉取系统镜像」、把应用打进镜像
- docker-compose:用来「启动服务」、把多个容器串起来
- 那 Kubernetes(K8s) 又是干什么的?它和 Compose 不都是「跑容器」吗?
这个直觉对了一半。要把三者讲清楚,关键不是背命令,而是分清它们各自处在哪一层:造镜像 → 本机/单机编排 → 集群编排与运维平台。
二、一张图看位置
开发者写代码
│
▼
Dockerfile ──────────► 镜像(Image)
│ │
│ ▼
│ docker run(单容器)
│ │
▼ ▼
docker-compose ────► 多容器应用(通常一台机 / 一个环境)
│
▼
Kubernetes ────────► 多机集群上的声明式部署、扩缩容、自愈、发布
简单记:
- Dockerfile:定义「这个服务长什么样」(怎么构建镜像)
- docker-compose:定义「这一组服务怎么在本地/单机跑起来」
- Kubernetes:定义「这组服务怎么在集群里长期、可靠地跑」
三、Dockerfile:造「标准零件」
Dockerfile 不是「拉取系统镜像」这么简单,它描述的是构建过程:
- 从基础镜像开始(如
FROM eclipse-temurin:17-jre、FROM node:20-alpine) - 拷贝代码、安装依赖、设置环境变量与启动命令
- 产出一个可复现、可分发的 Image
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY app.jar .
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
构建与使用:
docker build -t my-api:1.0 .
docker run -p 8080:8080 my-api:1.0
它解决什么:环境一致性——「在我机器上能跑」变成「镜像里怎么配就怎么跑」。
它不解决什么:多服务依赖、扩容、机器挂了怎么办、灰度发布等。
补充:FROM 那一行确实会「拉基础镜像」,但这只是构建的第一步;Dockerfile 的核心是定义应用镜像怎么造出来。
四、docker-compose:把零件在一台环境里组装起来
真实项目很少只有一个容器。常见组合是:API + MySQL + Redis + Nginx。Compose 用 YAML 声明「有哪些服务、端口、依赖、卷、网络」:
services:
api:
build: .
ports: ["8080:8080"]
depends_on: [db]
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/app
db:
image: mysql:8
volumes: ["db_data:/var/lib/mysql"]
volumes:
db_data:
docker compose up -d
docker compose logs -f api
docker compose down
它解决什么:本地开发、演示、单机/小团队环境的多容器编排;一条命令起整套依赖。
它不解决什么(或很弱):跨多台机器调度、自动故障迁移、大规模水平扩缩、复杂发布策略、集群级权限与配额。
可以把它理解成:面向「一台 Docker 主机」的应用说明书。主机挂了,说明书再漂亮也救不了服务。
五、Kubernetes:面向集群的「操作系统级」编排
K8s 也跑容器,但问题域变了:你有很多台机器(节点),希望声明「我要 3 个 API 副本、挂载这块存储、通过这个 Service 暴露」,集群自己去:
- 调度:容器跑在哪台节点
- 自愈:进程挂了重启;节点挂了把 Pod 挪走
- 扩缩容:按 CPU/QPS 或手动改副本数
- 服务发现与负载均衡:Service / Ingress
- 配置与密钥:ConfigMap / Secret
- 滚动更新 / 回滚:发布新版本时尽量不停机
概念上常见对应关系:
| 你想表达的事 | Compose 大致对应 | K8s 常见对象 |
|---|---|---|
| 跑一个容器实例 | service + container | Pod / Deployment |
| 副本数量 | deploy.replicas(Swarm)或手动多开 | Deployment replicas / HPA |
| 对外访问 | ports | Service / Ingress |
| 配置 | environment / env_file | ConfigMap / Secret |
| 持久化 | volumes | PVC / PV |
它解决什么:生产级、多节点、长期运行的容器平台能力。
代价:概念多、运维重、本地「只是想起个 MySQL 调接口」时往往过重。
六、三者对比(按你真正关心的问题)
| 问题 | Dockerfile | docker-compose | Kubernetes |
|---|---|---|---|
| 核心产物 | 镜像 | 多服务栈(单机为主) | 集群上的工作负载 |
| 典型场景 | 构建/发布制品 | 本地开发、小项目部署 | 生产集群、弹性与高可用 |
| 机器范围 | 无关(制品层) | 通常 1 台 Docker 主机 | 多节点集群 |
| 自愈能力 | 无 | 弱(重启策略有限) | 强(重建 Pod、重调度) |
| 学习/运维成本 | 低 | 低~中 | 高 |
| 和另外两者关系 | 被 Compose/K8s 使用 | 可 build Dockerfile | 一般消费已构建镜像 |
七、常见误解澄清
1. 「K8s 是更大的 docker-compose」
像,但不准确。Compose 偏「把应用描述清楚并在 Docker 引擎上拉起」;K8s 是「集群控制系统」,还管调度、网络模型、存储抽象、权限、扩展机制等。能把 Compose 文件「翻译」成 K8s YAML,不代表两者等价。
2. 「上了 K8s 就不需要 Dockerfile」
反过来:K8s 跑的是镜像。没有 Dockerfile(或等价构建),集群里没有可调度的制品。
3. 「生产必须上 K8s」
不一定。单机 Compose + 备份 + 监控,对很多内部工具、中小流量服务足够。K8s 适合你已经明确需要:多副本、多节点、频繁发布、弹性伸缩、统一平台治理。
4. 「Dockerfile 负责拉取系统镜像,所以它等于部署」
拉取基础镜像只是构建步骤;部署是「把镜像跑成长期服务」——那是 Compose/K8s/docker run 的事。
八、怎么选:一条实用路径
- 每个服务先写好 Dockerfile,保证镜像可本地
docker run。 - 开发与联调用 docker-compose,一键起依赖,缩短新人上手时间。
- 当出现这些信号再认真评估 K8s:要多机高可用、要自动扩缩、要统一多团队部署、单机已成瓶颈或单点不可接受。
- 上 K8s 后,镜像构建流水线(CI 打 tag 推仓库)仍然基于 Dockerfile;只是「运行说明书」从 Compose 换成 Deployment/Service 等清单。
九、和 AI / Agent 自托管的一点映射
例如自托管 Ollama + OpenWebUI + Agent:
- 各服务用 Dockerfile(或直接用官方镜像)
- 笔记本/单机服务器用 Compose 一天内跑通
- 要多副本网关、GPU 节点池、滚动升级与故障迁移时,再把同一套镜像迁到 K8s
顺序反了(一上来就搭集群)往往还没调通模型,人先被 YAML 淹没。
十、小结
回到开头那句话,更准确的说法是:
- Dockerfile:定义如何构建镜像(可从基础镜像起步)
- docker-compose:在单机 Docker 环境里编排并启动一组服务
- Kubernetes:在集群里声明式地部署、调度、扩缩与自愈这些服务
它们不是互相替代的三个「启动工具」,而是容器化路上不同阶段的三层能力。先镜像,再 Compose 跑通,最后在真正需要集群能力时上 K8s——这条路径通常最省时间、也最不容易踩空。