<- Back to Software Development

Dockerfile, Docker Compose, and Kubernetes

June 23, 20268 min read
Share

When people talk about Docker in a normal backend or full-stack project, they usually mention two important files: Dockerfile and docker-compose.yml. They look related, but they solve different problems. When Kubernetes is added later, the Dockerfile usually stays, but the Compose file is often replaced by Kubernetes manifests or Helm charts.

Short Answer

Yes, the two common Docker files are usually:

Dockerfile
docker-compose.yml

But they are not the same thing.

FileMain JobUsually Still Used With Kubernetes?
DockerfileDescribes how to build one application imageYes
docker-compose.yml / compose.yamlDescribes how to run multiple containers togetherUsually replaced
Kubernetes YAMLDescribes how to run, scale, expose, and manage containers in a clusterYes, when using Kubernetes
The important idea is:
Dockerfile = build the image
Docker Compose = run containers locally
Kubernetes = run containers in a real cluster

So Kubernetes does not replace the Dockerfile. It mainly replaces the Compose file's job.

What The Dockerfile Does

A Dockerfile is a recipe for building one container image. For example, for a Node.js backend, it may say:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

This file answers:

How do I package my app into a container image?

It defines things like:

  • base image;
  • working directory;
  • dependencies;
  • copied source code;
  • build steps;
  • startup command. After this file exists, Docker can build an image. Run this from the project root where the Dockerfile exists to build the app image locally and confirm the image can be produced:
docker build -t my-app:local .

After building, the output is an image:

my-app:local

That image can then be used by Docker Compose, Kubernetes, or any container platform.

What Docker Compose Does

Docker Compose is used when one app needs multiple containers to run together. Example:

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://user:password@db:5432/app_db
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: app_db
    ports:
      - "5432:5432"

This file answers:

How do I start my app, database, Redis, queue, and other services together on one machine?

Compose is useful for local development because it is simple:

one command
→ app starts
→ database starts
→ network is created
→ environment variables are injected
→ ports are exposed

Run this from the folder containing docker-compose.yml or compose.yaml to start the local development stack and verify the services can talk to each other:

docker compose up

So in local development, Compose is very convenient.

What Kubernetes Replaces

Kubernetes is not just another Compose file. It is a container orchestration system. It handles:

  • running containers across many machines;
  • restarting failed containers;
  • scaling replicas;
  • service discovery;
  • load balancing;
  • rolling deployments;
  • secrets and config;
  • health checks;
  • cluster networking. This is why Kubernetes replaces the runtime orchestration role of Compose. In Compose, you might write:
services:
  app:
    image: my-app:latest
    ports:
      - "3000:3000"

In Kubernetes, that becomes several resources. Example Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:latest
          ports:
            - containerPort: 3000

Example Service:

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 3000

Compose puts app runtime, networking, and local service connection into one file. Kubernetes separates them into clearer resources:

Compose ConceptKubernetes Equivalent
services.appDeployment
portsService / Ingress
environmentConfigMap / Secret
depends_onreadiness checks, retries, service discovery
restartKubernetes controller behavior
local networkcluster networking
scaling manuallyreplicas, HPA

Why Compose Is Usually Removed In Kubernetes Projects

The Compose file is often removed from the deployment flow because Kubernetes already controls how containers run. If both Compose and Kubernetes are used for production orchestration, the project has two sources of truth:

docker-compose.yml says app runs one way
k8s YAML says app runs another way

That causes confusion. For example:

ConcernComposeKubernetes
How many app instances?docker compose up --scale app=3replicas: 3
How to expose app?portsService and Ingress
Where to store secrets?environment variables or .envSecret
How to restart failed app?restart policycontroller reconciliation
How to update app?recreate containerrolling update
In a real Kubernetes deployment, the source of truth should be Kubernetes manifests, Helm chart, Kustomize config, or a GitOps repo.
The Compose file can still exist for local development, but it should not be treated as the production deployment definition.

The Correct Mental Model

The clean mental model is this:

Source code
   ↓
Dockerfile
   ↓
Docker image
   ↓
Container runtime

Then the question becomes:

Who runs the image?

There are different answers:

EnvironmentTool
One container locallydocker run
Multiple containers locallyDocker Compose
Production clusterKubernetes
Managed serverless container platformCloud Run, ECS, Azure Container Apps, etc.
So the Dockerfile is closer to the build layer.
Compose and Kubernetes are closer to the run layer.

Practical Workflow

A common workflow looks like this:

1. Write application code
2. Write Dockerfile
3. Build Docker image
4. Use Compose for local database, Redis, queue, and app testing
5. Push image to image registry
6. Use Kubernetes manifests or Helm to deploy the image

Run this from the project root to build the production image before pushing it to a registry:

docker build -t registry.example.com/my-app:1.0.0 .

Run this after logging into your image registry to upload the image so Kubernetes nodes can pull it:

docker push registry.example.com/my-app:1.0.0

Run this from the folder containing Kubernetes YAML files to apply the deployment into the current Kubernetes cluster context:

kubectl apply -f k8s/

The final relationship is:

Dockerfile stays.
Docker Compose may stay for local development.
Kubernetes replaces Compose for production orchestration.

Common Mistake

A beginner mistake is thinking:

Kubernetes replaces Docker.

That is not the accurate idea. A better version is:

Kubernetes manages containers that were usually built from Docker images.

Another beginner mistake is thinking:

If I use Kubernetes, I do not need Dockerfile.

Usually wrong. Kubernetes still needs an image to run. The Dockerfile is still the normal way to produce that image.

The Main Principle

Separate build from orchestration.

Dockerfile answers:
How is the application packaged?
Docker Compose answers:
How do I run this small system locally?
Kubernetes answers:
How do I run this system reliably in a cluster?

Once this separation is clear, the file relationship becomes simple: Kubernetes normally replaces the Compose deployment role, but it does not replace the Dockerfile.

当我们谈 Docker 的时候,最常看到的两个文件通常是 Dockerfiledocker-compose.yml。它们看起来都跟 Docker 有关,但职责完全不同。等项目后面加入 Kubernetes,通常不是把所有 Docker 文件都删掉,而是 Dockerfile 继续保留,docker-compose.yml 的部署职责被 Kubernetes YAML 或 Helm 取代。

Short Answer

对,常见的两个文件通常是:

Dockerfile
docker-compose.yml

但是它们不是同一种东西。

文件主要职责加入 Kubernetes 后还会用吗
Dockerfile描述如何 build 一个应用镜像通常会继续用
docker-compose.yml / compose.yaml描述如何在本地一起启动多个 container通常被替代
Kubernetes YAML描述如何在 cluster 里面运行、扩展、暴露和管理 container使用 Kubernetes 时会用
核心理解是:
Dockerfile = build image
Docker Compose = local run containers
Kubernetes = production cluster run containers

所以 Kubernetes 通常不是取代 Dockerfile,而是取代 Docker Compose 在部署和运行层面的职责。

What The Dockerfile Does

Dockerfile 是用来 build container image 的说明书。 例如一个 Node.js backend 可能会这样写:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

它回答的问题是:

我要怎样把这个 app 打包成一个 container image?

它定义的东西包括:

  • base image;
  • 工作目录;
  • dependencies;
  • source code copy;
  • build step;
  • container 启动命令。 有了这个文件之后,Docker 就可以 build 出 image。 在有 Dockerfile 的 project root 执行这个命令,用来确认本地能不能成功 build 出应用 image:
docker build -t my-app:local .

build 出来的结果是一个 image:

my-app:local

这个 image 后面可以交给 Docker Compose 跑,也可以交给 Kubernetes 跑。

What Docker Compose Does

Docker Compose 是用来一次过启动多个 container 的。 例如一个 backend 通常不只是 app 本身,还需要 database、Redis、queue 等服务。 例子:

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://user:password@db:5432/app_db
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: app_db
    ports:
      - "5432:5432"

这个文件回答的问题是:

我要怎样在一台电脑上同时启动 app、database、Redis、queue 这些服务?

Compose 适合 local development,因为它简单:

一个 command
→ app 启动
→ database 启动
→ network 创建好
→ environment variables 注入
→ ports 暴露出来

在有 docker-compose.ymlcompose.yaml 的 folder 执行这个命令,用来启动本地开发环境并检查服务之间能不能互相连接:

docker compose up

所以 Compose 很适合本地开发,不适合拿来理解成完整的 production cluster 部署系统。

What Kubernetes Replaces

Kubernetes 不是另一个版本的 Compose file。Kubernetes 是 container orchestration system。 它负责:

  • 在多台机器上运行 container;
  • container 挂掉后自动重启;
  • scale 多个 replicas;
  • service discovery;
  • load balancing;
  • rolling deployment;
  • secrets 和 config 管理;
  • health check;
  • cluster networking。 所以 Kubernetes 取代的是 Compose 的 runtime orchestration 角色。 在 Compose 里面,可能这样写:
services:
  app:
    image: my-app:latest
    ports:
      - "3000:3000"

到了 Kubernetes,通常会拆成多个资源。 例如 Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:latest
          ports:
            - containerPort: 3000

例如 Service

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 3000

Compose 通常把 app runtime、networking、本地服务连接写在一个文件里。 Kubernetes 会把这些职责拆成更明确的资源:

Compose 概念Kubernetes 对应概念
services.appDeployment
portsService / Ingress
environmentConfigMap / Secret
depends_onreadiness check、retry、service discovery
restartKubernetes controller behavior
local networkcluster networking
手动 scalereplicas、HPA

Why Compose Is Usually Removed In Kubernetes Projects

当项目真的用 Kubernetes 部署时,docker-compose.yml 通常不会再作为 production deployment 的 source of truth。 原因是 Kubernetes 已经负责 container 怎样运行。 如果 production 同时依赖 Compose 和 Kubernetes,就会出现两个 source of truth:

docker-compose.yml 说 app 应该这样跑
k8s YAML 说 app 应该那样跑

这会让系统变乱。 例如:

问题ComposeKubernetes
app 要跑几个 instancedocker compose up --scale app=3replicas: 3
app 怎样暴露出去portsServiceIngress
secret 放哪里environment variables 或 .envSecret
app 挂了怎样重启restart policycontroller reconciliation
app 怎样更新recreate containerrolling update
在真实 Kubernetes deployment 里面,source of truth 通常应该是:
Kubernetes manifests
Helm chart
Kustomize config
GitOps repo

Compose 还是可以保留给 local development 用,但不应该再当成 production deployment 定义。

The Correct Mental Model

比较清楚的理解方式是:

Source code
   ↓
Dockerfile
   ↓
Docker image
   ↓
Container runtime

然后再问:

是谁负责运行这个 image?

不同环境会有不同答案:

环境工具
本地跑一个 containerdocker run
本地跑多个 containerDocker Compose
production clusterKubernetes
managed container platformCloud Run、ECS、Azure Container Apps 等
所以 Dockerfile 比较接近 build layer。
Docker Compose 和 Kubernetes 比较接近 run layer / orchestration layer。

Practical Workflow

一个常见流程是:

1. 写 application code
2. 写 Dockerfile
3. build Docker image
4. 用 Compose 在本地启动 database、Redis、queue 和 app
5. push image 到 image registry
6. 用 Kubernetes manifests 或 Helm deploy 这个 image

在 project root 执行这个命令,用来 build production image,之后才可以 push 到 registry:

docker build -t registry.example.com/my-app:1.0.0 .

登录 image registry 之后执行这个命令,用来把 image 上传,让 Kubernetes node 后面可以 pull:

docker push registry.example.com/my-app:1.0.0

在放 Kubernetes YAML 的 folder 执行这个命令,用来把部署配置 apply 到当前 Kubernetes cluster:

kubectl apply -f k8s/

最后关系就是:

Dockerfile 保留。
Docker Compose 可以保留给 local development。
Kubernetes 取代 Compose 的 production orchestration 职责。

Common Mistake

初学者常见误解是:

Kubernetes replaces Docker.

这个说法不准确。 更准确的说法是:

Kubernetes manages containers that were usually built from Docker images.

另一个误解是:

用了 Kubernetes,就不需要 Dockerfile。

通常也是错的。 Kubernetes 还是需要一个 image 来运行。Dockerfile 仍然是最常见的 image build 方式。

The Main Principle

把 build 和 orchestration 分开理解。

Dockerfile 回答:
应用怎样被打包?
Docker Compose 回答:
我怎样在本地跑起这一小套系统?
Kubernetes 回答:
我怎样在 cluster 里面可靠地运行这一套系统?

理解这个分层之后,文件关系就很清楚:Kubernetes 通常会取代 Compose 的部署角色,但不会取代 Dockerfile。