☰
Docker与Kubernetes分工与协作:从单机容器到集群调度实战
2026/10/9 3:04:32 网站建设 项目流程

之前在业务迭代中,单机用 Docker 部署应用时一切正常,但一旦涉及多实例扩展、滚动更新、故障恢复,手工docker run就会变得非常吃力。于是很自然地开始接触 Kubernetes,却在学习初期被一堆概念卡住:Docker 和 Kubernetes 到底是不是替代关系?容器和 Pod 有什么区别?有了 Docker 为什么还需要编排?本文基于实际使用经验,将 Docker 与 Kubernetes 的分工、边界、协作方式逐步拆解,并给出一套从单机容器到集群调度的完整实操示例,帮助新手建立整体认知,也让有基础的同学快速对照排查。

1. 容器与编排:先把两个概念放在同一个坐标系里

1.1 容器到底是什么

容器本质上是一个操作系统层面的虚拟化技术。它利用 Linux 内核的 Namespace 实现资源视图隔离,通过 Cgroup 实现 CPU、内存等资源限制,再借助 UnionFS 实现分层镜像的文件系统复用。说的直白一点:容器就是一个被隔离出来的“进程组”,这个进程组拥有自己独立的文件系统、网络栈、进程列表,但它共享宿主机的操作系统内核。

与虚拟机相比,容器不需要模拟完整硬件,也不需要单独安装 Guest OS,所以它的启动速度更快、资源占用更小、单机密度更高。这也是容器在微服务架构中迅速流行的根本原因。

从部署层面来看,容器最大的价值是“一致性”:开发环境、测试环境、生产环境共用同一份镜像,避免了“在我机器上是好的”这种经典问题。

1.2 Docker 解决的是“单机容器”问题

Docker 是当前最流行的容器运行时之一。它提供了一整套工具链,让我们可以:

  • 通过 Dockerfile 构建镜像;
  • 通过镜像仓库分发镜像;
  • 通过 docker run 启动容器;
  • 通过 docker stop、docker restart 管理容器生命周期;
  • 通过 docker compose 在单机上编排多个容器。

注意,Docker 本身主要作用于一台宿主机。虽然 Docker 也有 Swarm 模式,但在实际生产环境中,Swarm 的使用率远不如 Kubernetes,目前大多数团队已经把 Kubernetes 作为容器编排的事实标准。

1.3 Kubernetes 解决的是“集群容器调度”问题

Kubernetes 是一个开源的容器编排平台,常简称为 K8s。它运行在多台服务器组成的集群之上,核心职责可以概括为几个方面:

  • 调度:把一个应用副本调度到集群中合适的节点上;
  • 伸缩:根据 CPU、内存或自定义指标自动增减副本数;
  • 自愈:副本挂掉后自动重建,节点故障后自动迁移应用;
  • 服务发现与负载均衡:通过 Service 为多个副本提供稳定的访问入口;
  • 滚动更新:发布新版本时逐个替换旧副本,期间服务不中断。

也就是说,Kubernetes 并不关心你的应用是用 Docker 构建还是用 containerd 构建,它关心的是“集群中有多少副本在运行、副本的健康状态如何、如何把流量分发到健康副本上”。

1.4 Docker 与 Kubernetes 的分工边界

用一句话来概括分工:

Docker 负责“把应用装进标准化的容器里”,Kubernetes 负责“把容器安排到集群里并维持运行状态”。

两者不是二选一的关系,而是上下游关系。Kubernetes 本身早期默认使用 Docker 作为容器运行时,后来逐步支持 containerd、CRI-O 等其他运行时。当前主流 K8s 集群中,很多已经默认使用 containerd,但 Docker 镜像依然是通用格式,开发者日常构建镜像时使用 Docker,集群运行时负责拉取并运行这套镜像。

为了更好地理解,可以从下面几个维度划分:

维度DockerKubernetes
主要作用构建镜像、运行单机容器管理跨节点容器集群
最小管理单元容器Pod
伸缩能力手动/脚本实现自动伸缩(HPA)
故障恢复单机层面有限集群层面自动重建
网络方案单机端口映射集群内 DNS、Service、Ingress
适用场景开发、测试、单机部署生产集群、微服务架构

2. 环境准备与核心术语

2.1 实验环境说明

本文的实操示例涉及 Docker 与 Kubernetes 两部分。版本方面需要说明:不同发行版和云环境的版本差异比较大,请以你自己的实际环境为准。本文示例以常见环境为例,重点演示配置思路。

建议准备以下环境:

  • 一台可运行 Docker 的服务器或使用 Docker Desktop(Windows/Mac);
  • 可选一个轻量 Kubernetes 环境,例如 kind、minikube,或者一台已部署好的 K8s 集群;
  • 命令行工具:docker CLI、kubectl CLI;
  • 一个简单的 Web 应用项目,用于构建镜像。

如果你的机器上还没有安装 Docker,安装时常见问题包括:Windows 下 Docker Desktop 提示 virtualization support 未开启,Linux 下出现permission denied while trying to connect to the Docker daemon socket。这类问题在后面的常见问题章节会专门说明。

2.2 镜像、容器、Pod、Deployment 等术语对照

初学者经常把下面几个术语混在一起,这里先统一梳理。

  • 镜像(Image):一个只读模板,包含应用代码、运行时环境、系统依赖和配置。镜像类似“安装包”。
  • 容器(Container):镜像运行时的实例,有自己的文件系统、进程、网络隔离。
  • Pod:Kubernetes 的最小调度单元,一个 Pod 内可以包含一个或多个容器,它们共享网络命名空间和存储卷。日常使用中,一个 Pod 通常只跑一个主容器。
  • Deployment:一种 Kubernetes 工作负载资源,负责管理一组无状态应用的副本,支持滚动更新和回滚。
  • Service:为 Pod 提供稳定访问入口的抽象,它通过标签选择器(Selector)匹配一组 Pod,并做负载均衡。
  • Namespace:Kubernetes 中的逻辑隔离空间,用来划分环境,例如 dev、test、prod。

把这些概念串起来就是一条链路:

Dockerfile → 镜像 → 容器; 镜像上传到仓库后,Kubernetes 通过 Deployment 声明“我要跑 3 个副本”,调度器创建多个 Pod; Service 再给这组 Pod 提供一个统一入口; 外部流量通过 Ingress 或 NodePort 进入 Service,最终转发到某个 Pod 内运行的容器。

3. Docker 核心能力拆解

3.1 镜像构建:用 Dockerfile 描述应用

Dockerfile 是一个文本文件,每一行指令都会生成一个镜像层。下面是一个最简单的 Python Flask 应用的 Dockerfile。

# 文件路径:demo-app/Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . EXPOSE 5000 CMD ["python", "app.py"]

对应需要两个文件:

# demo-app/requirements.txt flask==3.0.0
# demo-app/app.py from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "Hello from Docker + Kubernetes" if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)

构建命令:

cd demo-app docker build -t demo-app:v1 .

这里有几个关键点需要解释:

  • WORKDIR /app是给容器内部设置工作目录,避免后续指令路径混乱;
  • COPY指令把宿主机文件复制到镜像内;
  • RUN pip install在构建阶段安装依赖,依赖会被固化进镜像;
  • EXPOSE只是声明容器内监听端口,并不会自动映射到宿主机;
  • CMD是容器启动时执行的默认命令。

为什么要这样分层写?因为 Docker 构建时会尽量复用未变化的镜像层。当我们只修改 app.py 时,前面的基础镜像层和依赖安装层都会命中缓存,构建速度会明显加快。

3.2 容器运行与资源隔离

镜像构建完成后,启动容器:

docker run -d --name demo-1 -p 8080:5000 demo-app:v1

参数说明:

  • -d:后台运行;
  • --name demo-1:给容器命名;
  • -p 8080:5000:把宿主机 8080 端口映射到容器内 5000 端口。

启动后访问http://localhost:8080就能看到页面内容。

如果希望限制容器资源,可以增加参数:

docker run -d --name demo-2 -p 8081:5000 --cpus 0.5 --memory 256m demo-app:v1

--cpus 0.5表示容器最多使用 0.5 个 CPU 核心,--memory 256m表示内存上限为 256 MB。这里体现的正是 Cgroup 的隔离能力。资源限制在实际生产环境中很重要,因为一个容器如果无限占用内存,可能导致宿主机内存耗尽,进而影响其他容器。

查看容器状态:

docker ps docker stats

docker stats可以看到容器的实时 CPU、内存、网络和磁盘 IO 使用情况,是排查资源问题时的常用命令。

3.3 网络与数据卷

容器默认使用 Bridge 网络,多个容器可以通过容器名称互相访问。但要注意,容器是短暂资源,一旦被删除,容器内的数据也会丢失。所以需要持久化数据时,必须使用数据卷(Volume)或绑定挂载(Bind Mount)。

docker run -d --name nginx-demo -p 80:80 \ -v /opt/html:/usr/share/nginx/html \ nginx:latest

上面的-v /opt/html:/usr/share/nginx/html表示把宿主机的/opt/html目录挂载到容器内的 Nginx 静态目录,宿主机上的文件变更会即时反映到容器中。这个特性在本地开发时非常方便。

3.4 Docker Compose 的“轻量编排”

Docker Compose 可以用一个 YAML 文件定义多个服务,适合在单机上启动一整套应用栈,例如前端 + 后端 + 数据库。

下面是一个示例:

# docker-compose.yml version: "3.8" services: web: build: . ports: - "8080:5000" environment: - DB_HOST=db depends_on: - db db: image: postgres:15-alpine environment: POSTGRES_USER: demo POSTGRES_PASSWORD: demo123 POSTGRES_DB: demo volumes: - db-data:/var/lib/postgresql/data volumes: db-data:

启动命令:

docker compose up -d

查看服务状态:

docker compose ps

Compose 解决的问题是“单机多容器编排”。它通过depends_on控制启动顺序,通过自定义网络让服务之间通过服务名互相访问。但它在跨多台服务器的场景下无能为力,这时就需要 Kubernetes 出场。

4. Kubernetes 核心能力拆解

4.1 Pod:最小调度单元

在 Kubernetes 中,我们通常不会直接运行一个“孤零零的容器”,而是把一个或多个容器封装进 Pod。Pod 里的所有容器共享同一个网络命名空间,也就是说它们可以通过 localhost 互相访问,同时共享存储卷。

为什么 Kubernetes 要引入 Pod 而不是直接操作容器?原因是有一些应用场景需要多个进程紧密协作,例如:

  • 一个 sidecar 容器负责日志收集,主容器负责业务请求;
  • 一个容器负责启动前初始化数据,完成后退出;
  • 主容器和辅助容器需要共享同一份文件目录。

如果直接调度容器,这种“多进程协作”的语义会非常难管理。Pod 作为调度单元,既保留了容器的隔离性,又提供了协作能力。

4.2 Deployment:声明式副本管理

Deployment 是 Kubernetes 中最常用的工作负载类型。它声明了期望状态,例如“我要运行 3 个副本,镜像为 demo-app:v2”,Kubernetes 会持续努力让实际状态向期望状态靠拢。

下面是一个 Deployment 示例:

# k8s/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: demo-app spec: replicas: 3 selector: matchLabels: app: demo-app template: metadata: labels: app: demo-app spec: containers: - name: demo-app image: demo-app:v1 ports: - containerPort: 5000 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 300m memory: 256Mi readinessProbe: httpGet: path: / port: 5000 initialDelaySeconds: 3 periodSeconds: 5

几个关键点:

  • selector.matchLabels用来匹配模板中的标签,Deployment 通过标签控制下辖的 Pod;
  • resources.requests是调度时的资源请求,集群调度器会依据它决定 Pod 放在哪个节点;
  • resources.limits是运行时的资源上限;
  • readinessProbe是就绪探针,只有探针成功后,Pod 才会被标记为就绪并接收 Service 流量。

创建 Deployment:

kubectl apply -f k8s/deployment.yaml

查看状态:

kubectl get deployment kubectl get pods

4.3 Service:稳定的访问入口

Pod 的 IP 地址会随着重建而变化,不能直接作为对外访问地址。Service 解决了这个问题:它通过 Label Selector 匹配一组 Pod,并提供一个固定的 ClusterIP 和 DNS 名称。

# k8s/service.yaml apiVersion: v1 kind: Service metadata: name: demo-app-service spec: selector: app: demo-app ports: - protocol: TCP port: 80 targetPort: 5000 type: ClusterIP

这里port: 80是 Service 的端口,targetPort: 5000是后端 Pod 的容器端口。外部流量进入 Service 后,会被转发到某个健康的 Pod 上。

如果想从集群外部访问,可以把 type 改为 NodePort,或使用 Ingress 进行域名和路径路由。

kubectl apply -f k8s/service.yaml kubectl get service

4.4 自动伸缩与自愈

Kubernetes 的独特优势在于“自动化运维能力”。横向自动伸缩(HPA)可以根据 CPU 使用率等指标自动增减副本数。下面是一个简单的 HPA 配置:

# k8s/hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: demo-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: demo-app minReplicas: 2 maxReplicas: 5 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60

创建 HPA 后,当 Pod 的平均 CPU 使用率超过 60% 时,副本数会自动向最大值扩展;当负载降低后,会逐步缩回最小值。

自愈能力同样重要。如果你手动删除一个 Pod:

kubectl delete pod <pod-name>

Deployment 会立即发现实际副本数小于期望副本数,然后自动创建一个新的 Pod 顶上。这是 Deployment 这种“控制循环”机制的核心表现。

5. 从 Docker 到 Kubernetes 的完整实战

这一节把前面两个工具串起来,完成一次完整部署演示。

5.1 使用 Docker 构建并运行示例应用

首先准备应用文件,内容如第 3 章所示。构建镜像:

cd demo-app docker build -t demo-app:v1 .

然后本地运行验证:

docker run -d --name demo-1 -p 8080:5000 demo-app:v1 curl http://localhost:8080

预期输出:

Hello from Docker + Kubernetes

5.2 使用 Docker Compose 完成本地多容器编排

在项目根目录创建 docker-compose.yml,内容如前文所示。启动:

docker compose up -d docker compose ps

这一步演示的是“单机编排”:Web 服务和数据库服务可以一键启动、一键停止。它适合开发环境,但不具备跨节点伸缩能力。

5.3 使用 Kubernetes 部署同一套应用

接下来把同一个镜像部署到 Kubernetes 集群。这里假设你已经有可用集群,并且可以通过 kubectl 访问。

先创建命名空间:

kubectl create namespace demo

将镜像导入集群节点,或者推送到镜像仓库。如果是单节点 kind 或 minikube,可以通过kind load docker-image demo-app:v1这类方式加载本地镜像。

创建 Deployment:

kubectl apply -f k8s/deployment.yaml -n demo kubectl apply -f k8s/service.yaml -n demo

查看资源状态:

kubectl get deployment -n demo kubectl get pods -n demo kubectl get service -n demo

如果使用 minikube,可以用下面命令访问服务:

minikube service demo-app-service -n demo

如果使用 kind,可以通过端口映射方式访问 NodePort 或配置 Ingress 访问。

5.4 验证滚动更新与故障恢复

修改容器镜像版本,例如将 app.py 中的返回文本改为Hello from Docker + Kubernetes v2,重新构建镜像并打新标签:

docker build -t demo-app:v2 .

然后更新 Deployment 的镜像:

kubectl set image deployment/demo-app demo-app=demo-app:v2 -n demo

滚动更新过程查看:

kubectl rollout status deployment/demo-app -n demo

这条命令会阻塞等待,直到滚动更新完成。期间老的 Pod 会逐步下线,新的 Pod 会逐步上线。

接着做一次故障恢复测试。找出当前运行的 Pod 并删除:

kubectl get pods -n demo kubectl delete pod <pod-name> -n demo

再次查看 Pod 列表,可以发现名字不同的新 Pod 自动出现,这说明 Deployment 的自愈机制已经生效。

6. 常见问题与排查思路

6.1 Docker 相关高频问题

问题现象常见原因解决思路
permission denied while trying to connect to the Docker daemon socket当前用户未加入 docker 用户组执行sudo usermod -aG docker $USER后重新登录;生产环境谨慎使用 sudo
Docker Desktop 提示 virtualisation support wasn't detected宿主机未开启虚拟化进入 BIOS 开启 VT-x/AMD-V,并检查 Windows Hypervisor 是否启用
镜像下载慢默认仓库网络不稳定配置国内镜像加速器或使用内网镜像仓库,注意不要使用来源不明的加速地址
启动容器后立即退出应用启动命令错误或依赖缺失执行docker logs <container>查看日志,确认 CMD 是否合法
Windows 下 Docker Desktop 报“应用程序特定权限设置”类错误Docker 相关服务权限异常检查服务状态,必要时重置 Docker Desktop 数据目录,数据需提前备份

排查时的一个实用顺序是:先docker ps看容器是否在运行,再docker logs看应用日志,最后docker inspect检查挂载、网络和环境变量。

6.2 Kubernetes 相关高频问题

问题现象常见原因解决思路
Pod 一直处于 Pending节点资源不足或调度器无法安排执行kubectl describe pod <pod>查看调度事件,检查资源请求是否超过集群容量
Pod 反复 CrashLoopBackOff应用启动失败或探针配置不正确查看kubectl logs,重点检查就绪探针路径和端口是否匹配
Service 无法访问Selector 和 Pod 标签不匹配执行kubectl get pods --show-labels核对标签;再检查 targetPort 是否正确
更新镜像后没有新版本效果imagePullPolicy 为 IfNotPresent 且本地有同名旧镜像显式修改镜像标签或设置 imagePullPolicy: Always,注意生产环境版本管理
删除 Pod 后一直重建Deployment 的期望副本数大于当前数使用kubectl rollout restart deployment/<name>或直接删除 Deployment

使用kubectl describe是最有效的排查方式,它会把调度事件、探针失败原因、挂载错误等展示得非常清楚。

7. 最佳实践与工程建议

7.1 镜像层面的建议

  • 尽量使用明确版本号的基础镜像,避免使用 latest 标签,因为 latest 在时间维度上是不可控的。
  • 合并 RUN 指令,减少镜像层数,同时避免把敏感文件写入镜像。
  • 多阶段构建是减少镜像体积的有效方式。例如 Java 应用可以在构建阶段使用 maven 镜像,在运行阶段只用 slim 版 JRE。
  • 镜像仓库建议开启私有仓库权限管理,并定期扫描漏洞。镜像安全和容器安全是生产环境必须关注的两个维度。

7.2 容器与 Kubernetes 资源管理

  • 所有容器都应该设置resources.requests和resources.limits。只设置 limits 可能导致调度时无法判断节点容量,只设置 requests 则可能让某个 Pod 无限占用资源拖垮节点。
  • 对于 Java 类应用,要特别注意容器内存限制与 JVM 堆内存的关系,否则可能出现容器内进程已被限制,但 JVM 认为可用内存更大的问题。
  • 配置探针很关键:readinessProbe 决定流量是否进入 Pod,livenessProbe 决定容器是否重启。建议先配置 readinessProbe,再逐步引入 livenessProbe。

7.3 配置管理和权限控制

  • Kubernetes 中的配置建议使用 ConfigMap 和 Secret,而不是把环境变量硬编码进 Deployment。Secret 默认只是 base64 编码,并不提供加密,生产环境需要结合可用的加密方案或外部密钥管理工具。
  • 遵循最小权限原则,无论是 Docker 守护进程权限还是 Kubernetes RBAC 权限,都应该只授予完成任务所需的最小权限。开发环境使用docker用户组已经足够,生产环境要严格控制节点访问权。
  • 操作生产环境前先检查当前上下文。kubectl 默认连接的是当前 kubeconfig 对应的集群,执行kubectl config get-contexts确认不会操作错误环境。

7.4 发布与回滚策略

  • 使用 Deployment 的滚动更新参数控制发布节奏,例如:
spec: strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0

maxSurge表示更新过程中最多允许超过期望副本数的 Pod 数量,maxUnavailable表示最多允许不可用的副本数。通过这两个参数可以做到发布期间始终保持服务可用。

  • 发布失败时回滚:
kubectl rollout undo deployment/demo-app -n demo
  • 数据库变更和代码发布要分开执行,不推荐在应用容器中执行破坏性的数据库 DDL。

8. 深入学习建议

到这里,Docker 与 Kubernetes 的分工已经比较清晰了:

  • Docker 帮助你标准化应用的打包和运行,解决单机环境下的容器管理问题;
  • Kubernetes 帮助你管理集群中成千上万个容器,解决调度、伸缩、自愈、服务发现和滚动更新问题;
  • 两者协作关系是:Docker 构建镜像 → 镜像进入仓库 → Kubernetes 拉取镜像 → 调度器创建 Pod → Service 暴露服务 → 探针和 HPA 维持稳定性。

接下来可以关注以下几个方向:

  • 深入学习 Kubernetes 的工作负载类型,除了 Deployment,还有 StatefulSet、DaemonSet、Job、CronJob,它们分别适合有状态应用、守护进程、批处理任务等不同场景;
  • 研究 Ingress 与 Gateway API,理解集群外部流量如何进入集群内部;
  • 学习容器网络原理,包括 CNI 插件的工作方式,例如 Flannel、Calico 的差异;
  • 关注云原生生态其他组件,例如 Prometheus 监控、Grafana 可视化、Loki 日志采集;
  • 认真读一遍 Kubernetes 官方文档中关于 kubelet、kube-scheduler、controller-manager 的设计说明,能帮助你理解为什么 Deployment 具有自愈能力。

如果本文对你有帮助,可以收藏备用。还有哪些在实际部署中卡住你的容器或编排问题,欢迎在评论区一起讨论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询