一、先从一句日常说法说起

很多人刚接触容器时会这样理解:

  • Dockerfile:用来「拉取系统镜像」、把应用打进镜像
  • docker-compose:用来「启动服务」、把多个容器串起来
  • 那 Kubernetes(K8s) 又是干什么的?它和 Compose 不都是「跑容器」吗?

这个直觉对了一半。要把三者讲清楚,关键不是背命令,而是分清它们各自处在哪一层:造镜像 → 本机/单机编排 → 集群编排与运维平台。

二、一张图看位置

开发者写代码
      │
      ▼
 Dockerfile  ──────────►  镜像(Image)
      │                      │
      │                      ▼
      │              docker run(单容器)
      │                      │
      ▼                      ▼
 docker-compose ────►  多容器应用(通常一台机 / 一个环境)
      │
      ▼
 Kubernetes ────────►  多机集群上的声明式部署、扩缩容、自愈、发布

简单记:

  • Dockerfile:定义「这个服务长什么样」(怎么构建镜像)
  • docker-compose:定义「这一组服务怎么在本地/单机跑起来」
  • Kubernetes:定义「这组服务怎么在集群里长期、可靠地跑」

三、Dockerfile:造「标准零件」

Dockerfile 不是「拉取系统镜像」这么简单,它描述的是构建过程:

  1. 从基础镜像开始(如 FROM eclipse-temurin:17-jre、FROM node:20-alpine)
  2. 拷贝代码、安装依赖、设置环境变量与启动命令
  3. 产出一个可复现、可分发的 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 + containerPod / Deployment
副本数量deploy.replicas(Swarm)或手动多开Deployment replicas / HPA
对外访问portsService / Ingress
配置environment / env_fileConfigMap / Secret
持久化volumesPVC / PV

它解决什么:生产级、多节点、长期运行的容器平台能力。
代价:概念多、运维重、本地「只是想起个 MySQL 调接口」时往往过重。

六、三者对比(按你真正关心的问题)

问题Dockerfiledocker-composeKubernetes
核心产物镜像多服务栈(单机为主)集群上的工作负载
典型场景构建/发布制品本地开发、小项目部署生产集群、弹性与高可用
机器范围无关(制品层)通常 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 的事。

八、怎么选:一条实用路径

  1. 每个服务先写好 Dockerfile,保证镜像可本地 docker run。
  2. 开发与联调用 docker-compose,一键起依赖,缩短新人上手时间。
  3. 当出现这些信号再认真评估 K8s:要多机高可用、要自动扩缩、要统一多团队部署、单机已成瓶颈或单点不可接受。
  4. 上 K8s 后,镜像构建流水线(CI 打 tag 推仓库)仍然基于 Dockerfile;只是「运行说明书」从 Compose 换成 Deployment/Service 等清单。

九、和 AI / Agent 自托管的一点映射

例如自托管 Ollama + OpenWebUI + Agent:

  • 各服务用 Dockerfile(或直接用官方镜像)
  • 笔记本/单机服务器用 Compose 一天内跑通
  • 要多副本网关、GPU 节点池、滚动升级与故障迁移时,再把同一套镜像迁到 K8s

顺序反了(一上来就搭集群)往往还没调通模型,人先被 YAML 淹没。

十、小结

回到开头那句话,更准确的说法是:

  • Dockerfile:定义如何构建镜像(可从基础镜像起步)
  • docker-compose:在单机 Docker 环境里编排并启动一组服务
  • Kubernetes:在集群里声明式地部署、调度、扩缩与自愈这些服务

它们不是互相替代的三个「启动工具」,而是容器化路上不同阶段的三层能力。先镜像,再 Compose 跑通,最后在真正需要集群能力时上 K8s——这条路径通常最省时间、也最不容易踩空。