Docker 负责打包,Kubernetes 负责管集群:一文讲透两者区别与实战源码
Docker 和 Kubernetes 对比|附源码
在云原生技术体系中,Docker 和 Kubernetes 是两个经常被放在一起讨论的核心技术。很多初学者会把它们混为一谈,认为 Docker 就是容器,Kubernetes 就是 Docker 的升级版。事实上,这种理解并不准确。
简单来说:
- Docker 主要解决的是:如何把应用及其依赖打包成一个可移植、可运行的容器镜像,并在单机或少量主机上运行容器。
- Kubernetes 主要解决的是:当容器数量变多、服务变复杂、机器规模扩大后,如何自动化地部署、调度、扩缩容、服务发现、负载均衡、故障恢复和滚动升级。
本文将从概念、架构、使用场景、核心能力、优缺点以及源码示例等角度,对 Docker 和 Kubernetes 进行系统对比。
一、Docker 是什么?
Docker 是一个开源的容器化平台,它让开发者可以将应用程序及运行环境一起打包成一个标准化的镜像,然后在不同环境中以容器的方式运行。
传统软件部署经常遇到这样的问题:
“在我电脑上明明可以运行,为什么到测试环境或生产环境就出问题?”
原因通常是开发环境、测试环境、生产环境之间存在差异,例如:
- 操作系统版本不同;
- JDK、Node.js、Python 等运行时版本不同;
- 系统库依赖不同;
- 环境变量配置不同;
- 目录结构不同;
- 启动命令不同。
Docker 的出现,就是为了解决这类环境不一致问题。
通过 Docker,我们可以把应用程序、运行时、依赖库、配置文件等统一打包到镜像中。只要目标机器安装了 Docker 或兼容容器运行时,就可以运行该镜像。
二、Kubernetes 是什么?
Kubernetes,简称 K8s,是一个开源的容器编排平台,最初由 Google 开源,现在由 CNCF 管理。
Kubernetes 的核心目标是:
自动化管理大规模容器化应用。
当系统中只有一两个容器时,使用 Docker 命令手动启动容器并不复杂。例如:
docker run -d -p 8080:8080 my-app
但如果系统发展到几十个、几百个服务,每个服务又需要多个副本,并且还要支持:
- 服务自动发现;
- 负载均衡;
- 容器异常自动重启;
- 节点故障后自动迁移;
- 应用滚动升级;
- 灰度发布;
- 弹性扩缩容;
- 配置和密钥管理;
- 存储挂载;
- 多环境隔离;
这时仅靠 Docker 命令就很难维护了。Kubernetes 正是为解决这些复杂运维问题而生。
三、Docker 和 Kubernetes 的关系
Docker 和 Kubernetes 并不是简单的替代关系,而是处于不同层级。
可以这样理解:
| 技术 | 主要作用 |
|---|---|
| Docker | 构建镜像、运行容器 |
| Kubernetes | 管理和编排大量容器 |
Docker 更像是“集装箱制造和运行工具”,Kubernetes 更像是“港口调度系统”。
如果把应用比作货物:
- Docker 将货物装进标准集装箱;
- Kubernetes 负责安排这些集装箱放到哪台服务器、如何调度、如何扩容、如何故障恢复。
需要注意的是,早期 Kubernetes 可以直接通过 Docker 作为容器运行时来运行容器。但后来 Kubernetes 从 1.24 版本开始移除了 dockershim,不再直接内置支持 Docker Engine 作为运行时,而是推荐使用符合 CRI 标准的运行时,例如:
- containerd;
- CRI-O。
不过这并不意味着 Docker 镜像不能在 Kubernetes 中运行。Docker 构建出来的镜像仍然符合 OCI 标准,可以被 Kubernetes 使用。
四、Docker 核心组件
Docker 主要包含以下几个重要组件。
1. Dockerfile
Dockerfile 是构建镜像的脚本文件,里面定义了应用运行环境、依赖安装方式、文件复制方式以及容器启动命令。
2. Image 镜像
镜像是容器运行的模板。它是只读的,可以理解为应用的打包产物。
例如:
docker build -t my-app:1.0 .
这条命令会根据当前目录下的 Dockerfile 构建一个名为 my-app:1.0 的镜像。
3. Container 容器
容器是镜像运行后的实例。一个镜像可以启动多个容器。
例如:
docker run -d --name my-app -p 8080:8080 my-app:1.0
4. Registry 镜像仓库
镜像仓库用于存储和分发镜像,例如:
- Docker Hub;
- Harbor;
- 阿里云镜像仓库;
- 腾讯云 TCR;
- AWS ECR。
五、Kubernetes 核心组件
Kubernetes 的架构相对更复杂,主要由控制平面和工作节点组成。
1. Master / Control Plane 控制平面
控制平面负责整个集群的管理和调度,核心组件包括:
API Server
Kubernetes 的统一入口。所有操作都通过 API Server 进行。
Scheduler
负责将 Pod 调度到合适的节点上运行。
Controller Manager
负责维护集群状态,例如副本数量、节点状态、任务状态等。
etcd
分布式键值数据库,用于保存 Kubernetes 集群的所有状态数据。
2. Worker Node 工作节点
工作节点是真正运行应用容器的地方,主要组件包括:
kubelet
运行在每个节点上,负责接收控制平面的指令并管理 Pod 生命周期。
kube-proxy
负责服务网络转发和负载均衡规则。
Container Runtime
负责真正启动容器,例如 containerd、CRI-O 等。
六、Docker 和 Kubernetes 对比表
| 对比项 | Docker | Kubernetes |
|---|---|---|
| 定位 | 容器化平台 | 容器编排平台 |
| 主要功能 | 构建镜像、运行容器 | 调度、编排、扩缩容、服务治理 |
| 适用规模 | 单机、小规模服务 | 集群、大规模微服务 |
| 学习难度 | 相对简单 | 较高 |
| 部署方式 | Docker CLI / Docker Compose | YAML 声明式配置 |
| 服务发现 | 需要额外配置 | 原生支持 Service 和 DNS |
| 负载均衡 | 基础端口映射 | 原生支持 Service、Ingress |
| 自动恢复 | 单机级别有限支持 | 原生支持 Pod 重启、节点迁移 |
| 滚动升级 | 手动或借助 Compose | 原生支持 Deployment 滚动更新 |
| 弹性扩缩容 | 不擅长 | 原生支持 HPA、VPA |
| 配置管理 | 环境变量、挂载文件 | ConfigMap、Secret |
| 存储管理 | Volume | PV、PVC、StorageClass |
| 网络模型 | 单机网络为主 | 集群网络模型 |
| 典型工具 | Dockerfile、Docker Compose | Deployment、Service、Ingress |
七、适用场景对比
Docker 更适合的场景
Docker 非常适合以下场景:
- 本地开发环境统一
团队成员可以使用同一份 Dockerfile 或 docker-compose.yml 启动开发环境,减少“环境不一致”问题。
- 快速构建和测试应用
CI/CD 流水线中常用 Docker 构建镜像,并进行自动化测试。
- 单机部署小型项目
对于个人项目、小型后台服务、内部工具系统,直接使用 Docker 或 Docker Compose 部署已经足够。
- 依赖复杂的应用封装
例如某些应用依赖特定版本系统库、字体库、图像处理库等,用 Docker 封装后更易迁移。
Kubernetes 更适合的场景
Kubernetes 更适合以下场景:
- 中大型微服务系统
当服务数量较多、服务之间调用复杂、需要统一治理时,Kubernetes 的价值会明显体现。
- 高可用生产环境
Kubernetes 可以自动重启异常容器,并在节点故障时重新调度 Pod。
- 需要弹性扩缩容的业务
例如电商大促、在线教育直播、内容平台热点流量等场景。
- 多团队、多环境管理
Kubernetes 可以通过 Namespace、RBAC、ResourceQuota 等机制实现资源隔离和权限控制。
- 标准化 DevOps 平台建设
很多企业会以 Kubernetes 为基础建设统一应用交付平台。
八、示例项目:使用 Docker 部署一个 Node.js 应用
下面我们通过一个简单的 Node.js Web 服务,展示 Docker 和 Kubernetes 的基本使用方式。
1. 项目目录结构
my-node-app
├── app.js
├── package.json
├── Dockerfile
└── k8s
├── deployment.yaml
└── service.yaml
九、Node.js 应用源码
1. package.json
{
"name": "docker-k8s-demo",
"version": "1.0.0",
"description": "Docker and Kubernetes demo app",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.3"
}
}
2. app.js
const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
const version = process.env.APP_VERSION || "v1";
app.get("/", (req, res) => {
res.json({
message: "Hello Docker and Kubernetes!",
version,
hostname: require("os").hostname(),
time: new Date().toISOString()
});
});
app.get("/health", (req, res) => {
res.status(200).json({
status: "UP"
});
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
这个应用提供两个接口:
/:返回应用信息;/health:健康检查接口。
其中 hostname 可以帮助我们在 Kubernetes 多副本部署时观察请求被分配到了哪个 Pod。
十、Dockerfile 源码
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY app.js .
ENV PORT=3000
ENV APP_VERSION=v1
EXPOSE 3000
CMD ["npm", "start"]
这个 Dockerfile 做了以下事情:
- 使用
node:20-alpine作为基础镜像; - 设置工作目录为
/app; - 复制依赖描述文件;
- 安装生产环境依赖;
- 复制应用源码;
- 设置环境变量;
- 暴露 3000 端口;
- 启动 Node.js 应用。
十一、使用 Docker 构建和运行
1. 构建镜像
docker build -t docker-k8s-demo:v1 .
2. 查看镜像
docker images
3. 运行容器
docker run -d \
--name docker-k8s-demo \
-p 3000:3000 \
docker-k8s-demo:v1
4. 访问服务
curl http://localhost:3000
返回结果类似:
{
"message": "Hello Docker and Kubernetes!",
"version": "v1",
"hostname": "c7a1f0d8a921",
"time": "2026-01-01T10:00:00.000Z"
}
5. 查看容器日志
docker logs docker-k8s-demo
6. 停止并删除容器
docker stop docker-k8s-demo
docker rm docker-k8s-demo
十二、使用 Docker Compose 部署
如果应用需要多个服务,例如 Web 服务、MySQL、Redis,可以使用 Docker Compose 管理。
下面是一个简单的 docker-compose.yml 示例:
version: "3.9"
services:
web:
image: docker-k8s-demo:v1
container_name: docker-k8s-demo
ports:
- "3000:3000"
environment:
- APP_VERSION=v1
- PORT=3000
restart: always
启动服务:
docker compose up -d
查看服务:
docker compose ps
停止服务:
docker compose down
Docker Compose 很适合本地开发和小规模部署,但它不适合复杂的大规模集群调度。
十三、Kubernetes Deployment 源码
在 Kubernetes 中,通常不会直接运行容器,而是通过 Pod、Deployment、Service 等资源来声明应用期望状态。
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-k8s-demo
labels:
app: docker-k8s-demo
spec:
replicas: 3
selector:
matchLabels:
app: docker-k8s-demo
template:
metadata:
labels:
app: docker-k8s-demo
spec:
containers:
- name: docker-k8s-demo
image: docker-k8s-demo:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
env:
- name: APP_VERSION
value: "v1"
- name: PORT
value: "3000"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 15
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
这个 Deployment 定义了:
- 应用名称;
- Pod 标签;
- 副本数量为 3;
- 容器镜像;
- 环境变量;
- 端口;
- 就绪探针;
- 存活探针;
- CPU 和内存资源限制。
当某个 Pod 崩溃时,Kubernetes 会自动创建新的 Pod 来维持 3 个副本的期望状态。
十四、Kubernetes Service 源码
Deployment 负责管理 Pod 副本,但 Pod 的 IP 是动态变化的。为了提供稳定访问入口,需要使用 Service。
service.yaml
apiVersion: v1
kind: Service
metadata:
name: docker-k8s-demo-service
spec:
type: NodePort
selector:
app: docker-k8s-demo
ports:
- protocol: TCP
port: 80
targetPort: 3000
nodePort: 30080
这个 Service 的作用是:
- 选择标签为
app: docker-k8s-demo的 Pod; - 将 Service 的 80 端口转发到 Pod 的 3000 端口;
- 通过节点的 30080 端口暴露服务。
十五、部署到 Kubernetes
1. 应用 YAML 文件
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
2. 查看 Deployment
kubectl get deployment
3. 查看 Pod
kubectl get pods -o wide
输出类似:
NAME READY STATUS RESTARTS AGE IP NODE
docker-k8s-demo-6f77d9c4b9-abcde 1/1 Running 0 1m 10.244.1.10 node1
docker-k8s-demo-6f77d9c4b9-fghij 1/1 Running 0 1m 10.244.2.11 node2
docker-k8s-demo-6f77d9c4b9-klmno 1/1 Running 0 1m 10.244.3.12 node3
4. 查看 Service
kubectl get svc
5. 访问服务
如果使用的是本地 Kubernetes,例如 minikube,可以执行:
minikube service docker-k8s-demo-service
如果是普通集群,可以通过任意节点 IP 加 NodePort 访问:
curl http://:30080
十六、Kubernetes 滚动升级示例
假设我们修改应用版本为 v2。
可以修改 Dockerfile:
ENV APP_VERSION=v2
重新构建镜像:
docker build -t docker-k8s-demo:v2 .
然后更新 Kubernetes Deployment:
kubectl set image deployment/docker-k8s-demo \
docker-k8s-demo=docker-k8s-demo:v2
查看滚动更新状态:
kubectl rollout status deployment/docker-k8s-demo
如果发现新版本有问题,可以回滚:
kubectl rollout undo deployment/docker-k8s-demo
这就是 Kubernetes 相比 Docker 单机部署非常重要的能力:声明式发布和自动回滚。
十七、Kubernetes 扩缩容示例
将副本数从 3 扩展到 5:
kubectl scale deployment docker-k8s-demo --replicas=5
查看 Pod:
kubectl get pods
缩容到 2:
kubectl scale deployment docker-k8s-demo --replicas=2
如果配置了 HPA,还可以根据 CPU、内存或自定义指标自动扩缩容。
示例:
kubectl autoscale deployment docker-k8s-demo \
--cpu-percent=60 \
--min=2 \
--max=10
十八、Docker 的优势和不足
Docker 的优势
- 简单易用
Docker 的命令直观,学习成本低,适合初学者快速入门。
- 镜像标准化
通过 Dockerfile 可以将应用环境固化,保证交付一致性。
- 生态成熟
Docker Hub 和大量官方镜像让开发者可以快速搭建应用环境。
- 适合本地开发
Docker Compose 可以方便地启动数据库、缓存、消息队列等依赖服务。
Docker 的不足
- 集群管理能力弱
Docker 本身主要面向单机容器管理,不擅长管理大量服务器上的容器。
- 服务治理能力有限
服务发现、负载均衡、自动扩缩容等能力需要额外工具支持。
- 生产级复杂场景不足
当系统规模扩大后,单纯依赖 Docker 命令或 Compose 会变得难以维护。
十九、Kubernetes 的优势和不足
Kubernetes 的优势
- 强大的容器编排能力
Kubernetes 能够自动调度 Pod 到合适节点,并维护应用期望状态。
- 高可用和自愈能力
当容器异常退出或节点故障时,Kubernetes 可以自动恢复。
- 声明式配置
用户只需要声明“我想要什么状态”,Kubernetes 会自动使系统趋近这个状态。
- 服务发现和负载均衡
通过 Service、CoreDNS、Ingress 等机制,可以实现稳定的服务访问。
- 适合微服务架构
Kubernetes 已经成为云原生和微服务部署的重要基础设施。
Kubernetes 的不足
- 学习曲线陡峭
Kubernetes 概念较多,如 Pod、Deployment、Service、Ingress、ConfigMap、Secret、PV、PVC、RBAC 等。
- 运维复杂度高
生产级 Kubernetes 集群需要考虑网络、存储、安全、监控、日志、备份、升级等问题。
- 资源消耗较高
相较单机 Docker,Kubernetes 本身需要额外的控制平面和集群组件。
- 不适合所有项目
对于简单项目,直接使用 Kubernetes 可能会增加不必要的复杂度。
二十、如何选择 Docker 和 Kubernetes?
选择 Docker 还是 Kubernetes,取决于业务规模、团队能力和部署需求。
推荐使用 Docker 的情况
如果你的项目满足以下特点,Docker 通常已经足够:
- 项目规模小;
- 服务数量少;
- 单机部署即可满足需求;
- 团队没有专门运维人员;
- 主要用于本地开发、测试环境;
- 希望快速上线,不想引入复杂平台。
例如:
- 个人博客;
- 小型后台管理系统;
- 内部工具;
- Demo 项目;
- 单体应用;
- 小型 API 服务。
推荐使用 Kubernetes 的情况
如果你的项目具有以下特点,Kubernetes 更合适:
- 服务数量较多;
- 需要高可用;
- 需要自动扩缩容;
- 多台机器部署;
- 微服务架构;
- 多团队协作;
- 有 DevOps 或平台工程团队;
- 对发布效率、可观测性和稳定性要求较高。
例如:
- 中大型互联网业务;
- SaaS 平台;
- 电商系统;
- 金融科技系统;
- 企业级微服务平台;
- AI 推理服务集群。
二十一、常见误区
误区一:Kubernetes 取代了 Docker
严格来说,Kubernetes 并不是 Docker 的替代品。Docker 侧重镜像构建和容器运行,Kubernetes 侧重容器编排。
即便 Kubernetes 不再直接使用 Docker Engine 作为运行时,Docker 构建的镜像依然可以部署到 Kubernetes。
误区二:用了 Docker 就等于云原生
Docker 是云原生的重要基础,但云原生还包括:
- 微服务架构;
- DevOps;
- CI/CD;
- Kubernetes;
- 服务网格;
- 可观测性;
- 弹性伸缩;
- 自动化运维。
误区三:所有项目都应该上 Kubernetes
Kubernetes 很强大,但不是所有项目都需要它。技术选型应该服务于业务,而不是为了追逐潮流。
误区四:Docker Compose 可以完全替代 Kubernetes
Docker Compose 很适合本地开发和小规模部署,但在多节点调度、服务自愈、滚动发布、权限控制和资源管理方面,Kubernetes 更加完善。
二十二、总结
Docker 和 Kubernetes 是云原生体系中非常重要的两项技术,但它们解决的问题不同。
Docker 关注的是:
- 如何构建镜像;
- 如何运行容器;
- 如何让应用环境标准化;
- 如何提升开发和交付一致性。
Kubernetes 关注的是:
- 如何管理大量容器;
- 如何实现服务发现;
- 如何进行负载均衡;
- 如何自动恢复故障;
- 如何滚动升级;
- 如何扩缩容;
- 如何进行集群级资源调度。
一句话总结:
Docker 解决“应用如何被打包和运行”的问题,Kubernetes 解决“容器如何被大规模管理和编排”的问题。
在实际工作中,两者通常不是二选一,而是配合使用:
- 使用 Dockerfile 编写镜像构建规则;
- 使用 Docker 构建应用镜像;
- 将镜像推送到镜像仓库;
- 使用 Kubernetes 部署、调度和管理容器化应用。
对于初学者,建议先掌握 Docker,再学习 Kubernetes。这样可以先理解容器、镜像、网络、数据卷等基础概念,再进入 Pod、Deployment、Service、Ingress、ConfigMap、Secret 等 Kubernetes 体系,学习路径会更加顺畅。