Docker 负责打包,Kubernetes 负责调度:一文吃透容器化与 K8s 编排

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
  -> 集群运行

最重要的几个结论:

  1. 镜像是应用交付单元,容器是镜像的运行实例。
  2. Pod 是 Kubernetes 的最小部署单元,不等于单个容器。
  3. Deployment 管理无状态副本,Service 提供稳定访问入口。
  4. Docker Engine、containerd、CRI-O 和 Kubernetes 处于不同层次。
  5. Kubernetes 1.24 移除了内置 dockershim,生产集群应关注 CRI 兼容运行时。Kubernetes Dockershim Removal FAQ
  6. 生产系统不仅需要运行起来,还要考虑探针、资源、存储、权限、网络、发布、回滚和可观测性。

如果把 Docker 学成"怎么运行一个容器",把 Kubernetes 学成"怎么写 YAML",最后很容易停留在命令层面。

真正需要掌握的是:

应用如何被打包,容器如何运行,Pod 如何被调度,服务如何被发现,故障如何被恢复,发布如何被验证。

参考资料

相关推荐
凌涘1 小时前
Docker 入门:镜像、容器与反向代理
docker
java_logo3 小时前
Docker 部署 openGauss:轻松搭建企业级开源关系型数据库平台
数据库·docker·开源·opengauss·轩辕镜像·opengauss部署教程·opengauss部署文档
HjhIron3 小时前
前端开发必会的Docker实战:从“我电脑能跑”到“轻松部署”
docker
嘟嘟07173 小时前
从浏览器到 hello world:Docker + nginx 反向代理 + Node 最小服务器一次讲清
docker·容器·node.js
阿黎梨梨3 小时前
Docker 容器化实战:从零搭建 Web 服务与反向代理
前端·后端·docker
小月土星3 小时前
当 LLM 遇上集装箱:我的 Docker 学习笔记与 AI 后端的思考(含反向代理Nginx)
后端·docker·容器
Kismet_nvi3 小时前
Kubernetes 核心模块详细总结
云原生·容器·kubernetes
何时梦醒4 小时前
Docker 容器化入门:从「我电脑能跑」到「哪台机器都能跑」
后端·docker·面试
烬羽4 小时前
nginx 里写 localhost 反而 502?一条请求带你彻底搞懂 Docker 端口映射与反向代理
nginx·docker·程序员