Docker 负责打包,Kubernetes 负责调度:一文吃透容器化与 K8s 编排
Docker 解决的是"应用如何被打包和运行",Kubernetes 解决的是"大量容器如何被部署、调度、发现、扩缩容和自愈"。把两者混为一谈,是学习容器技术时最常见的误区。pos_id=img-3aO9SG9f-1787067290924)
一、先用一句话理解 Docker 和 Kubernetes
可以先建立一个简单的分工模型:
Docker:
把应用、依赖和运行环境打包成镜像,并在本机或服务器上运行容器。
Kubernetes:
管理一组机器上的容器化应用,负责调度、扩缩容、服务发现、滚动发布和故障恢复。
Docker 更像是"应用交付和容器运行工具",Kubernetes 更像是"容器集群操作系统"。
二者不是同一层产品:
Dockerfile
-> Docker Image
-> Container
-> Pod
-> Deployment
-> Service
-> Kubernetes Cluster
这条链路中,每一层解决的问题不同。
二、为什么需要 Docker
传统部署常见的问题是:
- 开发环境和生产环境版本不一致;
- Java、Node、Python 版本冲突;
- 系统依赖安装过程复杂;
- 应用部署步骤依赖人工经验;
- 服务之间互相污染;
- 回滚困难;
- 新机器很难快速恢复。
Docker 的核心思路是:
把应用和运行它所需要的文件、依赖、配置方式封装成标准化镜像,再以隔离进程的形式运行。
Docker 官方文档将容器描述为隔离进程,将镜像描述为包含运行应用所需文件、二进制、库和配置的标准化包。Docker 官方:什么是容器 Docker 官方:什么是镜像
三、Docker 的核心对象
1. Dockerfile
Dockerfile 是镜像构建说明书:
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
常见指令:
- FROM:指定基础镜像;
- WORKDIR:设置工作目录;
- COPY:复制文件;
- RUN:构建阶段执行命令;
- ENV:设置环境变量;
- EXPOSE:声明容器端口;
- CMD:默认参数;
- ENTRYPOINT:默认启动入口。
2. Image
镜像是不可变的应用模板,通常由多个只读层组成。Docker 官方文档说明,镜像层代表一组文件系统变化,并且镜像构建过程会复用这些层。Docker 镜像层文档
镜像名称通常由以下部分组成:
registry/namespace/repository:tag
例如:
registry.example.com/team/order-service:1.4.2
生产环境不建议只使用 latest,因为它不能清晰表达版本,也不利于回滚。更可靠的方式是使用语义化版本、Git Commit ID 或不可变 Digest。
3. Container
容器是镜像的运行实例:
docker run -d --name order-service -p 8080:8080 -e SPRING_PROFILES_ACTIVE=prod order-service:1.4.2
常用命令:
docker ps
docker ps -a
docker logs -f order-service
docker exec -it order-service sh
docker inspect order-service
docker stop order-service
docker rm order-service
容器通常是短生命周期对象。不要把重要数据只放在容器可写层中,数据应通过 Volume 或外部存储持久化。
4. Registry
Registry 用于存储和分发镜像:
docker login registry.example.com
docker build -t registry.example.com/team/app:1.0.0 .
docker push registry.example.com/team/app:1.0.0
docker pull registry.example.com/team/app:1.0.0
常见实现包括 Docker Hub、Harbor、云厂商镜像仓库和云原生平台自带 Registry。
四、Docker 镜像为什么适合交付
镜像带来了几个重要能力:
可重复
同一个镜像在开发、测试和生产环境中具有相同的文件系统和启动入口。
可分发
镜像可以推送到 Registry,再由不同机器拉取。
可回滚
只要保留旧镜像版本,就可以把应用恢复到旧版本。
可缓存
镜像层可以复用,减少构建和传输成本。
可审计
可以对镜像做漏洞扫描、签名、SBOM 生成和来源追踪。
但镜像并不等于安全。基础镜像、系统包、第三方依赖和应用本身都可能存在漏洞。
五、为什么需要 Kubernetes
当只有一个服务和一台机器时,Docker 命令已经够用。
当服务数量增加后,会遇到:
- 多台机器如何分配容器;
- 某个容器挂了如何自动恢复;
- 服务如何找到另一组动态变化的容器;
- 如何运行多个副本;
- 如何滚动升级;
- 如何限制 CPU 和内存;
- 如何处理节点故障;
- 如何给外部流量提供稳定入口;
- 如何实现配置和密钥管理。
Kubernetes 的核心不是"运行一个容器",而是:
用户声明期望状态,控制器持续观察实际状态,并不断把实际状态拉回期望状态。
例如:
期望:order-service 运行 3 个副本
实际:当前只有 2 个健康副本
控制器:创建新的 Pod
这就是 Kubernetes 的声明式和控制器模型。
六、Kubernetes 核心架构
flowchart LR
U[用户或 CI/CD] --> API[API Server]
API --> ETCD[(etcd)]
API --> SCH[Scheduler]
API --> CM[Controller Manager]
API --> K[各节点 Kubelet]
SCH --> POD[被调度的 Pod]
CM --> DESIRED[期望状态控制]
K --> CRI[Container Runtime]
CRI --> C[Container]
K --> CNI[CNI 网络插件]
K --> CSI[CSI 存储插件]
API --> SVC[Service]
SVC --> PROXY[kube-proxy 或数据平面]
PROXY --> POD
控制面主要负责:
- API Server:统一 API 入口;
- etcd:保存集群状态;
- Scheduler:为 Pod 选择节点;
- Controller Manager:维护各种资源的期望状态。
节点侧主要负责:
- Kubelet:管理节点上的 Pod;
- Container Runtime:创建和运行容器;
- CNI:提供网络;
- CSI:提供持久化存储;
- kube-proxy 或其他数据平面:实现 Service 流量转发。
七、Pod:Kubernetes 最小部署单元
Kubernetes 官方文档将 Pod 定义为集群中运行一个或多个容器的最小可部署对象。Kubernetes Workloads 官方文档
Pod 不是简单的"容器别名"。
一个 Pod 中的容器通常共享:
- 网络命名空间;
- Pod IP;
- localhost;
- 部分存储卷;
- 生命周期边界。
常见场景是:
Pod
├── main application container
└── sidecar container
例如主应用负责业务,Sidecar 负责日志采集、代理或配置同步。
但不要为了"容器越多越好"而把无关服务放到同一个 Pod。Pod 内的容器应该具有较强的协作关系,并且需要一起调度、一起部署。
一个简单 Pod 配置:
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
实际生产中通常不直接创建 Pod,而是使用 Deployment、StatefulSet 或 Job 管理 Pod。
八、Deployment:管理无状态应用
Deployment 适合管理无状态应用。它通过 ReplicaSet 维护指定数量的 Pod,并支持滚动更新和回滚。
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: app
image: registry.example.com/order-service:1.4.2
ports:
- containerPort: 8080
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
查看状态:
kubectl get deployment
kubectl get rs
kubectl get pods -l app=order-service
kubectl describe deployment order-service
Deployment 的核心逻辑是:
Deployment
-> ReplicaSet
-> Pod
-> Container
当镜像版本变化时,Deployment 创建新的 ReplicaSet,再逐步替换旧 Pod。
九、Service:给动态 Pod 提供稳定访问入口
Pod 是易变的:
- Pod 可能被重建;
- Pod IP 可能变化;
- 副本数量可能变化;
- Pod 可能被调度到其他节点。
Service 提供一个稳定的访问抽象,将一组 Pod 暴露成统一的网络端点。Kubernetes 官方文档明确说明,Service 解决了客户端如何访问动态变化的 Pod 后端这一问题。Kubernetes Service 官方文档
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
常见类型:
- ClusterIP:集群内部访问;
- NodePort:通过节点端口暴露;
- LoadBalancer:借助云平台负载均衡;
- ExternalName:通过 DNS 别名访问外部服务。
Service 通过 selector 选择后端 Pod。selector 和 Pod labels 不匹配,是服务访问不到后端的常见原因。
十、Ingress 与 Gateway
Service 解决集群内服务发现,但外部 HTTP 请求通常还需要:
- 域名路由;
- TLS 终止;
- 路径转发;
- 多服务共享入口;
- 证书管理;
- 访问控制。
传统 Kubernetes 中常用 Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
Ingress 只是 API 对象,实际还需要 Ingress Controller。
在新的云原生网络设计中,也可以关注 Gateway API。实际选型要结合集群版本、云厂商能力和现有 Controller,不应只复制网上旧版本 YAML。
十一、ConfigMap 与 Secret
ConfigMap
保存非敏感配置:
apiVersion: v1
kind: ConfigMap
metadata:
name: order-config
data:
LOG_LEVEL: info
FEATURE_X_ENABLED: "true"
Secret
保存敏感配置,例如:
-
数据库密码;
-
Token;
-
TLS 证书;
-
镜像仓库认证信息。
apiVersion: v1
kind: Secret
metadata:
name: order-secret
type: Opaque
stringData:
DB_PASSWORD: change-me
需要注意:Kubernetes Secret 并不天然等于强加密保险箱。生产环境还需要关注 etcd 加密、RBAC、密钥轮换、审计和外部 Secret 管理系统。
十二、健康检查:Readiness、Liveness、Startup
Readiness Probe
判断 Pod 是否可以接收流量。
如果应用还没完成启动或依赖不可用,Readiness 失败,Service 不应该把流量转给它。
Liveness Probe
判断进程是否已经失去自我恢复能力。如果持续失败,Kubelet 可能重启容器。
Startup Probe
适合启动时间较长的应用,避免应用还没启动完成就被 Liveness 判定为失败。
startupProbe:
httpGet:
path: /actuator/health
port: 8080
failureThreshold: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
探针不是越多越好。探针接口本身必须轻量、稳定、能准确反映服务状态。
十三、资源请求与限制
Kubernetes 通过 requests 和 limits 进行资源管理:
-
requests:调度时至少需要的资源;
-
limits:容器允许使用的上限。
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "1Gi"
CPU 单位:
1000m = 1 个 CPU 核
资源设置不合理会导致:
- requests 太小:节点超卖,运行时争抢严重;
- requests 太大:Pod 难以调度;
- memory limit 太小:容器 OOMKilled;
- 没有 requests:调度和 QoS 不可控;
- 没有 limits:单个服务可能占满节点资源。
十四、Kubernetes 中的容器运行时
这是 Docker 与 Kubernetes 最容易混淆的地方。
Kubernetes 通过 CRI 与容器运行时交互。官方文档要求运行时支持 CRI v1,并列出 containerd 等运行时。Kubernetes Container Runtimes
Kubernetes v1.24 移除了内置 dockershim。原因是 Docker Engine 不直接实现 CRI,过去需要 Kubernetes 内置的兼容层把 Docker 接到 kubelet。Kubernetes Dockershim Removal FAQ
这并不意味着:
- Docker 镜像不能运行;
- Docker 不能用于本地开发;
- Docker 项目失去价值。
真正变化的是:
Kubernetes 节点的容器运行时
不再默认通过内置 dockershim 连接 Docker Engine
Docker 构建的 OCI 兼容镜像仍然可以被 Kubernetes 使用。生产集群通常会使用 containerd 或 CRI-O 等 CRI 兼容运行时,也可以通过 cri-dockerd 使用 Docker Engine。
正确理解是:
Docker CLI / Docker Engine
-> 负责开发、构建、运行和分发
containerd / CRI-O
-> 负责 Kubernetes 节点上的容器运行时
Kubernetes
-> 负责集群级调度和生命周期管理
十五、Docker Compose 与 Kubernetes 的关系
Docker Compose 适合:
- 本地开发;
- 单机多容器;
- 快速搭建依赖环境;
- 集成测试;
- Demo 和小型部署。
Kubernetes 适合:
- 多节点集群;
- 高可用;
- 自动扩缩容;
- 滚动发布;
- 服务发现;
- 资源调度;
- 自愈和运维治理。
Compose 示例:
services:
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
Compose 和 Kubernetes 不是严格替代关系。常见开发链路是:
Dockerfile
-> Docker Compose 本地联调
-> CI 构建镜像
-> 推送 Registry
-> Kubernetes 部署
十六、滚动发布与回滚
更新 Deployment 镜像:
kubectl set image deployment/order-service app=registry.example.com/order-service:1.4.3
查看发布状态:
kubectl rollout status deployment/order-service
kubectl rollout history deployment/order-service
回滚:
kubectl rollout undo deployment/order-service
滚动发布需要结合:
- Readiness Probe;
- maxUnavailable;
- maxSurge;
- 版本不可变;
- 监控;
- 灰度策略;
- 数据库兼容;
- 回滚方案。
代码回滚不等于数据回滚。数据库 Migration 必须向前兼容,避免新代码写入旧版本无法识别的数据结构。
十七、Job、CronJob 和 StatefulSet
Job
适合一次性任务:
数据迁移
批处理
初始化
离线计算
CronJob
适合定时任务:
每天对账
定期清理
数据同步
定时备份
StatefulSet
适合需要稳定身份和持久存储的应用:
- 数据库;
- 消息队列;
- 有状态集群;
- 需要稳定网络标识的服务。
不要简单地把数据库放进 Deployment 就认为完成了生产部署。还需要考虑存储、备份、主从、故障转移、升级和数据一致性。
十八、持久化存储
容器本身是短生命周期的,持久数据通常需要:
- Volume;
- PersistentVolume;
- PersistentVolumeClaim;
- StorageClass;
- CSI Driver。
基本关系:
Pod
-> PVC
-> PV
-> StorageClass / CSI
-> 云盘、NAS 或分布式存储
应用是否有状态,决定了存储设计。日志、上传文件、数据库数据和缓存不应全部用同一种存储方案。
十九、Kubernetes 网络模型
Kubernetes 网络至少要解决:
- Pod 到 Pod;
- Pod 到 Service;
- 外部到 Service;
- Pod 到外部;
- DNS 服务发现;
- 网络策略。
常见网络组件包括:
- CNI;
- CoreDNS;
- kube-proxy;
- Ingress Controller;
- NetworkPolicy;
- 云厂商数据平面。
排查网络时,不要只看应用日志:
kubectl get svc
kubectl get endpoints
kubectl get endpointslices
kubectl exec -it pod-a -- nslookup order-service
kubectl exec -it pod-a -- curl http://order-service
Service 存在但没有 Endpoints,通常说明 selector 没有匹配到 Ready 的 Pod。
二十、故障排查顺序
1. Pod 起不来
kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl logs <pod-name> --previous
重点查看:
- ImagePullBackOff;
- CrashLoopBackOff;
- OOMKilled;
- 探针失败;
- 配置缺失;
- Secret 不存在;
- 节点资源不足。
2. Service 访问不到
kubectl get svc
kubectl get endpoints
kubectl get pods --show-labels
kubectl describe svc <service-name>
检查:
- selector 是否匹配;
- Pod 是否 Ready;
- targetPort 是否正确;
- 应用是否监听正确地址;
- NetworkPolicy 是否阻断;
- DNS 是否正常。
3. Pod 一直 Pending
kubectl describe pod <pod-name>
常见原因:
- requests 太大;
- 没有合适节点;
- 节点污点;
- nodeSelector 不匹配;
- PVC 绑定失败;
- 亲和性条件无法满足。
4. 发布后接口异常
检查:
- 新版本镜像是否正确;
- Readiness 是否误判;
- 环境变量是否变化;
- Service 是否仍然指向旧 Pod;
- 数据库 Migration 是否兼容;
- 应用是否有启动竞态;
- 滚动发布策略是否合理。
二十一、生产环境最佳实践
镜像层面
- 固定基础镜像版本;
- 使用多阶段构建;
- 尽量使用非 root 用户;
- 减少镜像体积;
- 扫描漏洞;
- 生成 SBOM;
- 使用镜像签名;
- 不把密钥写入镜像;
- 不使用 latest 作为生产版本。
Kubernetes 层面
- 所有工作负载设置 requests 和 limits;
- 配置 Readiness、Liveness 和 Startup;
- 使用 PodDisruptionBudget;
- 使用滚动发布;
- 为关键服务配置反亲和;
- 使用 NetworkPolicy;
- 对 Secret 做加密和轮换;
- 限制 ServiceAccount 权限;
- 记录审计日志;
- 使用命名空间隔离团队和环境。
研发流程
提交代码
-> 构建镜像
-> 单元测试
-> 镜像扫描
-> 推送 Registry
-> 部署测试环境
-> 集成测试
-> 灰度发布
-> 监控验证
-> 全量发布
二十二、Docker 与 Kubernetes 的完整协作链路
开发者编写代码
-> Dockerfile
-> docker build
-> Image
-> Registry
-> Kubernetes Deployment
-> Scheduler 选择节点
-> Kubelet 调用 CRI
-> containerd 或 CRI-O 创建容器
-> Pod Ready
-> Service 加入 Endpoints
-> Ingress 接收流量
这里的关键分工是:
| 环节 | 主要负责者 |
|---|---|
| 应用打包 | Dockerfile / Docker Build |
| 镜像存储 | Registry |
| 容器创建 | Container Runtime |
| 节点管理 | Kubelet |
| Pod 调度 | Scheduler |
| 副本维护 | Controller |
| 服务发现 | Service / CoreDNS |
| 外部入口 | Ingress 或 Gateway |
| 配置管理 | ConfigMap / Secret |
| 存储管理 | PVC / PV / CSI |
| 集群治理 | Kubernetes |
二十三、常见误区
误区一:Kubernetes 就是 Docker 的集群版
不准确。Kubernetes 是容器编排系统,Docker 是容器开发和运行生态的一部分。
误区二:Kubernetes 不能运行 Docker 镜像
不准确。Docker 构建的镜像通常可以被 Kubernetes 使用。变化主要在于 Kubernetes 节点如何连接容器运行时。
误区三:Pod 就是一个容器
Pod 是 Kubernetes 的最小部署单元,一个 Pod 可以包含一个或多个紧密协作的容器。
误区四:Service 会自动暴露到公网
默认 ClusterIP 只提供集群内部访问。公网访问需要 LoadBalancer、NodePort、Ingress 或 Gateway 等方案。
误区五:容器重启就等于服务自愈
重启只能解决部分进程问题。真正的自愈还涉及副本、探针、调度、节点故障和业务依赖。
误区六:设置 limits 就完成资源治理
还需要合理设置 requests、QoS、HPA、节点容量和监控告警。
误区七:Kubernetes 适合所有项目
单体小项目或个人服务使用 Docker Compose 可能更简单。Kubernetes 的运维成本和学习成本都不低。
二十四、学习路线
建议按下面的顺序学习:
第一阶段:Linux 和容器基础
- 进程;
- Namespace;
- Cgroups;
- 文件系统;
- 网络;
- Docker CLI;
- Dockerfile;
- 镜像层;
- Volume;
- Registry。
第二阶段:Docker 工程实践
- 多阶段构建;
- Compose;
- 日志;
- 健康检查;
- 镜像扫描;
- 私有仓库;
- CI 构建。
第三阶段:Kubernetes 基础对象
- Pod;
- Deployment;
- ReplicaSet;
- Service;
- ConfigMap;
- Secret;
- Namespace;
- Job;
- CronJob。
第四阶段:Kubernetes 生产能力
- Ingress;
- Gateway;
- Storage;
- StatefulSet;
- HPA;
- RBAC;
- NetworkPolicy;
- 调度和亲和性;
- 监控和日志;
- Helm;
- GitOps。
二十五、总结
Docker 和 Kubernetes 不是竞争关系,而是处在不同抽象层:
Docker:
让应用能够被标准化打包、分发和运行。
Kubernetes:
让大量容器能够在集群中被声明式地调度、治理和恢复。
真正的云原生交付链路是:
代码
-> 镜像
-> Registry
-> Pod
-> Deployment
-> Service
-> Ingress
-> 集群运行
最重要的几个结论:
- 镜像是应用交付单元,容器是镜像的运行实例。
- Pod 是 Kubernetes 的最小部署单元,不等于单个容器。
- Deployment 管理无状态副本,Service 提供稳定访问入口。
- Docker Engine、containerd、CRI-O 和 Kubernetes 处于不同层次。
- Kubernetes 1.24 移除了内置 dockershim,生产集群应关注 CRI 兼容运行时。Kubernetes Dockershim Removal FAQ
- 生产系统不仅需要运行起来,还要考虑探针、资源、存储、权限、网络、发布、回滚和可观测性。
如果把 Docker 学成"怎么运行一个容器",把 Kubernetes 学成"怎么写 YAML",最后很容易停留在命令层面。
真正需要掌握的是:
应用如何被打包,容器如何运行,Pod 如何被调度,服务如何被发现,故障如何被恢复,发布如何被验证。