技术干货 # k8s已发布

Kubernetes:从容器编排到生产实践

在 Docker 出现之后,我们解决了应用运行环境不一致的问题。开发人员可以把应用、依赖、运行时环境一起打包成镜像,然后通过容器启动.对于一台服务器来说,这已经足够简单。但如果服务器从 1 台变成 10 台、100 台甚至更多,问题就开始出现了...

2026.04.2612 分钟阅读
Kubernetes:从容器编排到生产实践
Kubernetes:从容器编排到生产实践 · Cover

在 Docker 出现之后,我们解决了应用运行环境不一致的问题。

开发人员可以把应用、依赖、运行时环境一起打包成镜像,然后通过容器启动:

docker run -d --name nginx -p 80:80 nginx

对于一台服务器来说,这已经足够简单。

但如果服务器从 1 台变成 10 台、100 台甚至更多,问题就开始出现了:

  • 容器应该运行在哪台机器?
  • 某个容器挂了,谁负责重新启动?
  • 服务需要扩容,如何快速启动几十个实例?
  • 某台服务器宕机了,运行在上面的服务怎么办?
  • 如何让多个容器之间进行稳定通信?
  • 应用升级时,如何做到不停机?
  • 配置、密码、证书应该放在哪里?
  • 如何知道整个集群现在到底是什么状态?

Docker 解决的是容器运行问题,而 Kubernetes 解决的是更上层的容器编排问题。

Kubernetes,通常简称 K8s,可以理解成一个管理容器化应用的分布式系统。

它最核心的思想其实并不复杂:

你告诉 Kubernetes“我希望系统最终是什么样子”,Kubernetes 不断检查当前状态,并通过各种控制器把实际状态调整到期望状态。

这也是理解 Kubernetes 最重要的一句话。


一、先理解 Kubernetes 到底解决了什么问题

假设我们有一个 Node.js 服务:

user-service

最开始只有一个 Docker 容器:

Server
└── user-service

随着用户增加,我们需要运行 3 个实例:

Server
├── user-service
├── user-service
└── user-service

如果继续增加到 10 个实例,单台机器可能已经无法承载,于是我们增加服务器:

              Load Balancer
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
     Server1     Server2     Server3
       │            │           │
    Container    Container    Container

到了这里,我们实际上已经遇到了一个典型的分布式系统问题。

如果完全手工管理,就需要自己处理:

容器启动
容器停止
容器故障恢复
服务发现
负载均衡
扩容缩容
机器故障
版本升级
配置管理
存储
网络
权限

Kubernetes 就是把这些事情统一抽象成了一套 API 和控制系统。

因此,不要简单地把 Kubernetes 理解成:

“一个可以运行 Docker 的工具。”

更准确地说:

Kubernetes 是一个通过声明式 API 管理分布式应用运行状态的平台。


二、Kubernetes 的基本架构

一个 Kubernetes 集群通常由两类节点组成:

Kubernetes Cluster

        Control Plane
        ┌───────────────┐
        │ API Server    │
        │ Scheduler     │
        │ Controller    │
        │ etcd          │
        └───────┬───────┘
                │
       ┌────────┼────────┐
       ▼        ▼        ▼
    Worker    Worker    Worker
     Node      Node      Node
       │        │        │
     Pod      Pod      Pod

Control Plane 负责管理整个集群。

Worker Node 负责真正运行应用。

现在 Kubernetes 官方通常使用 control plane / worker node 这样的叫法。早期资料中经常会看到 master / node 的称呼。

API Server

API Server 是 Kubernetes 集群最重要的入口。

你执行:

kubectl get pods

实际上不是直接去访问某台服务器上的容器,而是:

kubectl
   │
   ▼
API Server
   │
   ├── 查询集群状态
   │
   └── 返回结果

所有 Kubernetes API 操作最终都会经过 API Server。

例如:

kubectl create
kubectl get
kubectl delete
kubectl apply

本质上都是在和 Kubernetes API 打交道。


etcd

etcd 是 Kubernetes 的核心数据存储。

可以简单理解为:

Kubernetes 集群的“数据库”。

例如我们创建了一个 Deployment:

replicas: 3

Kubernetes 需要记录:

这个 Deployment 存在
期望副本数 = 3
镜像是什么
配置是什么
标签是什么

这些集群状态最终会持久化到 etcd。

所以生产环境中 etcd 是非常重要的组件。

如果 etcd 中的数据出现严重问题,整个 Kubernetes 集群的控制面都会受到影响。


Scheduler

Scheduler 负责决定:

一个新的 Pod 应该运行在哪个 Node 上?

例如现在有三个节点:

node-1
CPU: 2
Memory: 4GB

node-2
CPU: 8
Memory: 16GB

node-3
CPU: 4
Memory: 8GB

新 Pod 提交后,Scheduler 会综合考虑:

CPU / Memory
节点资源
亲和性
反亲和性
污点
容忍
拓扑
节点选择条件

然后选择合适的节点。

注意:

Scheduler 负责“决定放在哪里”,并不负责真正启动容器。


Controller Manager

Controller 是 Kubernetes 最有意思的设计之一。

假设你告诉 Kubernetes:

replicas: 3

代表:

我希望最终存在 3 个 Pod。

现在实际情况可能是:

期望状态:3

实际状态:2

Controller 发现两者不一致,就会尝试创建一个新的 Pod。

于是:

Desired State
      │
      ▼
Controller
      │
      ▼
Actual State

这就是 Kubernetes 的控制循环。

如果一个 Pod 被删除:

期望:3
实际:2

Controller 会创建新的 Pod。

如果某个 Pod 所在节点故障,Kubernetes 也可以根据资源控制器重新创建实例。

所以 Kubernetes 并不是简单地“执行一次命令”。

它实际上一直在:

观察 → 对比 → 调整 → 再观察

这就是 Kubernetes 的核心思想之一。


三、Pod:Kubernetes 中最基本的运行单位

刚接触 Kubernetes 时,最容易产生一个疑问:

Docker 不是直接运行 Container 吗?为什么 Kubernetes 又搞了一个 Pod?

因为 Kubernetes 并没有直接把 Container 作为自己的最小调度单位,而是引入了 Pod。

最简单的 Pod 可以理解为:

Pod
└── Container

例如:

apiVersion: v1
kind: Pod

metadata:
  name: nginx

spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
        - containerPort: 80

创建:

kubectl apply -f nginx.yaml

查看:

kubectl get pods

可能得到:

NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          10s

这里的:

1/1

表示 Pod 中有 1 个容器,当前 1 个容器都是 Ready 状态。


为什么不直接调度 Container?

因为 Kubernetes 有时候需要让多个容器共享一些运行环境。

例如:

Pod
├── application
└── sidecar

两个 Container:

  • 共享网络命名空间
  • 可以通过 localhost 通信
  • 可以共享 Volume

例如:

application
    │
    │ localhost
    ▼
sidecar

这类设计在日志收集、代理、Service Mesh 等场景中比较常见。

不过对于普通业务应用来说:

一个 Pod 里面通常一个主要业务容器就够了。

不要为了“看起来高级”而随便把多个业务服务塞进一个 Pod。


四、Deployment:不要直接管理 Pod

虽然可以直接创建 Pod,但生产环境一般不会这么做。

因为 Pod 本身并不负责:

副本管理
滚动更新
版本发布
故障恢复策略

因此通常会使用 Deployment。

例如:

apiVersion: apps/v1
kind: Deployment

metadata:
  name: nginx

spec:
  replicas: 3

  selector:
    matchLabels:
      app: nginx

  template:
    metadata:
      labels:
        app: nginx

    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

执行:

kubectl apply -f deployment.yaml

然后:

kubectl get deployment
NAME    READY   UP-TO-DATE   AVAILABLE
nginx   3/3     3            3

此时 Kubernetes 会创建:

Deployment
    │
    ▼
ReplicaSet
    │
    ├── Pod
    ├── Pod
    └── Pod

所以实际关系是:

Deployment
    ↓
ReplicaSet
    ↓
Pod
    ↓
Container

Deployment 负责声明:

我希望 nginx 有 3 个实例

ReplicaSet 负责保证:

实际存在 3 个 Pod

Pod 最终运行 Container。


五、Service:Pod 为什么不能直接访问?

这是 Kubernetes 网络中非常重要的一点。

Pod 是具有生命周期的。

例如:

Pod A
IP: 10.244.1.10

Pod 重建以后:

Pod B
IP: 10.244.2.15

IP 可能发生变化。

如果前端直接访问:

10.244.1.10

那么 Pod 重建之后连接就失效了。

因此 Kubernetes 提供了 Service。

                Service
              10.96.10.20
                    │
          ┌─────────┼─────────┐
          ▼         ▼         ▼
        Pod A     Pod B     Pod C

Service 提供一个稳定的访问入口。

例如:

apiVersion: v1
kind: Service

metadata:
  name: nginx

spec:
  selector:
    app: nginx

  ports:
    - port: 80
      targetPort: 80

  type: ClusterIP

创建后:

kubectl get svc

可以看到:

NAME    TYPE        CLUSTER-IP
nginx   ClusterIP   10.96.10.20

集群内部的其他服务就可以通过:

nginx:80

访问它。

这里 Service 最重要的机制就是:

Service
   │
   │ selector
   ▼
Pod

例如:

selector:
  app: nginx

它会选择拥有:

app=nginx

这个 Label 的 Pod。

所以 Kubernetes 中:

Label + Selector 是非常核心的一套机制。


六、Ingress:让外部用户访问 Kubernetes 服务

Service 的 ClusterIP 通常只用于集群内部。

如果用户通过浏览器访问:

https://example.com

我们需要把外部请求引入 Kubernetes。

常见结构是:

Internet
    │
    ▼
LoadBalancer
    │
    ▼
Ingress Controller
    │
    ▼
Service
    │
    ▼
Pod

Ingress 可以理解成:

HTTP/HTTPS 层面的路由规则。

例如:

api.example.com
        │
        ▼
     api-service

www.example.com
        │
        ▼
     web-service

Ingress 规则可能类似:

apiVersion: networking.k8s.io/v1
kind: Ingress

metadata:
  name: example

spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80

于是:

https://api.example.com

就可以路由到:

api-service

需要注意,Ingress 本身只是 API 资源。

真正处理 HTTP 请求的通常是 Ingress Controller,例如 NGINX Ingress Controller、Traefik 等。


七、ConfigMap 和 Secret:配置不要直接写死在镜像里

假设 Node.js 应用需要:

DATABASE_HOST
DATABASE_PORT
DATABASE_USER

如果直接写到 Docker 镜像里面:

ENV DATABASE_HOST=xxx

那么不同环境就需要构建不同镜像。

这并不理想。

Kubernetes 提供了 ConfigMap:

apiVersion: v1
kind: ConfigMap

metadata:
  name: app-config

data:
  DATABASE_HOST: postgres
  DATABASE_PORT: "5432"

Pod 可以通过环境变量读取。

env:
  - name: DATABASE_HOST
    valueFrom:
      configMapKeyRef:
        name: app-config
        key: DATABASE_HOST

对于密码、Token 等敏感信息,则可以使用 Secret:

apiVersion: v1
kind: Secret

metadata:
  name: app-secret

type: Opaque

stringData:
  DATABASE_PASSWORD: "password"

然后:

env:
  - name: DATABASE_PASSWORD
    valueFrom:
      secretKeyRef:
        name: app-secret
        key: DATABASE_PASSWORD

不过需要注意:

Kubernetes Secret 并不等于天然安全的密码保险箱。

Secret 默认主要是 Base64 编码,而不是加密意义上的“不可读取”。

生产环境通常还会结合:

RBAC
etcd encryption
External Secrets
Vault
云厂商 Secret Manager

进一步加强敏感信息管理。


八、资源限制:为什么 Kubernetes 有时候会杀掉你的 Pod?

在 Kubernetes 中,可以给 Container 设置资源请求和限制。

例如:

resources:
  requests:
    cpu: "500m"
    memory: "512Mi"

  limits:
    cpu: "1"
    memory: "1Gi"

这里:

500m CPU = 0.5 CPU

requests 表示:

调度 Pod 时,希望至少能够提供这些资源。

limits 表示:

Container 使用资源时允许达到的上限。

例如:

requests:
  cpu: 500m

limits:
  cpu: 1

Scheduler 会根据 requests 判断节点是否有足够资源。

这也是生产环境非常重要的一点。

如果完全不设置资源限制,一个服务出现内存泄漏:

Node
├── service-a
├── service-b
├── service-c
└── service-d
          ↑
      内存不断增长

最终可能影响整个节点。

如果超过 Container 的 memory limit,Linux cgroup 会触发 OOM,Pod 可能出现:

OOMKilled

所以排查 Kubernetes 中的 Pod 异常时:

kubectl describe pod xxx

以及:

kubectl get pod xxx -o yaml

是非常常用的命令。


九、健康检查:不要只看 Pod 是不是 Running

很多刚开始使用 Kubernetes 的人会认为:

STATUS = Running

就代表服务正常。

实际上并不是。

一个 Node.js 应用可能:

进程还活着

但是:

数据库连接已经断开
Kafka 消费已经停止
Redis 不可用
应用已经无法正常处理请求

因此 Kubernetes 提供了探针。

Liveness Probe

用于判断:

这个容器是不是已经死掉了?

例如:

livenessProbe:
  httpGet:
    path: /health
    port: 3000

  initialDelaySeconds: 10
  periodSeconds: 10

如果连续检查失败,Kubernetes 可以重启 Container。

Readiness Probe

用于判断:

这个 Pod 当前能不能接收流量?

例如应用启动过程中:

Pod
 │
 ├── Container Running
 │
 ├── 初始化数据库
 │
 ├── 初始化 Redis
 │
 ├── 加载配置
 │
 └── Ready

在 Ready 之前,Service 不应该把正常业务流量发送给它。

这就是 Readiness Probe 的作用。

生产环境中:

Liveness 和 Readiness 不应该简单地使用同一个接口、同一种判断逻辑。


十、滚动更新:Kubernetes 最实用的能力之一

假设线上运行:

nginx:1.27

现在需要升级:

nginx:1.28

如果直接停止旧版本,再启动新版本:

旧版本停止
      ↓
服务不可用
      ↓
新版本启动

就会产生明显的停机时间。

Deployment 默认支持 Rolling Update。

例如:

strategy:
  type: RollingUpdate

  rollingUpdate:
    maxUnavailable: 0
    maxSurge: 1

原来:

v1 v1 v1

升级过程中可能变成:

v1 v1 v1 v2

然后:

v1 v1 v2

再变成:

v1 v2 v2

最后:

v2 v2 v2

整个过程由 Deployment 控制。

查看发布状态:

kubectl rollout status deployment/nginx

查看历史:

kubectl rollout history deployment/nginx

如果新版本出现问题,可以回滚:

kubectl rollout undo deployment/nginx

这也是 Kubernetes 在实际生产环境中非常有价值的能力。


十一、StatefulSet:有状态服务怎么办?

Deployment 更适合:

Web
API
Worker
Gateway

这类无状态应用。

但是数据库、Kafka、ZooKeeper 等服务具有明显的状态特征。

例如:

mysql-0
mysql-1
mysql-2

它们不是简单的三个完全相同的 Pod。

每个实例可能拥有:

固定身份
固定网络名称
独立存储

这时候 Kubernetes 提供 StatefulSet。

例如:

StatefulSet
    │
    ├── mysql-0
    ├── mysql-1
    └── mysql-2

与 Deployment 最大的区别之一,就是 StatefulSet 会维护 Pod 的稳定身份。

例如:

mysql-0

即使 Pod 重建,它仍然可以继续叫:

mysql-0

同时可以配合 PersistentVolume 保存数据。


十二、Kubernetes 存储

容器有一个天然的问题:

Container 删除以后,里面的数据怎么办?

如果数据库直接把数据放在 Container 文件系统里:

Container
└── /var/lib/mysql

Container 删除:

数据也可能一起消失

因此 Kubernetes 引入了 Volume、PersistentVolume 和 PersistentVolumeClaim。

关系可以简单理解为:

Pod
 │
 ▼
PVC
 │
 ▼
PV
 │
 ▼
Storage

PVC 表示:

我需要一块存储。

例如:

apiVersion: v1
kind: PersistentVolumeClaim

metadata:
  name: mysql-data

spec:
  accessModes:
    - ReadWriteOnce

  resources:
    requests:
      storage: 20Gi

Pod 使用:

volumes:
  - name: mysql-data
    persistentVolumeClaim:
      claimName: mysql-data

这样数据库数据就可以脱离 Pod 生命周期。

在云环境中,PV 最终可能对应:

云硬盘
NAS
Ceph
云厂商 CSI
NFS

十三、调度:Pod 到底应该运行在哪里?

当 Kubernetes 集群只有几个节点时,你可能感觉不到调度的重要性。

但生产环境可能有:

100+ Nodes
1000+ Pods

这时候就不能简单地:

哪里有资源就扔哪里

Kubernetes 提供了很多调度机制。

最基础的是:

nodeSelector:
  kubernetes.io/hostname: node-01

表示:

这个 Pod 必须运行在 node-01。

更灵活的方式是 Node Affinity。

例如:

SSD 节点
GPU 节点
业务节点
计算节点

还可以通过:

Taint
Toleration
Affinity
Anti-Affinity
Topology Spread Constraints

控制 Pod 的分布。

例如一个高可用 Web 服务:

Node 1
 └── web-1

Node 2
 └── web-2

Node 3
 └── web-3

而不是:

Node 1
 ├── web-1
 ├── web-2
 └── web-3

因为如果 Node 1 故障,后一种部署方式会让三个实例同时消失。


十四、Namespace:把一个集群分成多个逻辑环境

一个 Kubernetes 集群通常不会只运行一个项目。

例如:

production
staging
development
monitoring

可以使用 Namespace:

kubectl create namespace production

然后:

production
├── api
├── web
├── worker
└── redis

staging
├── api
├── web
└── worker

查看 Pod:

kubectl get pods -n production

Namespace 可以用于:

资源隔离
权限控制
资源配额
环境划分

但是要注意:

Namespace 主要是逻辑隔离,不等于完整的安全隔离。


十五、RBAC:谁可以操作 Kubernetes?

Kubernetes 本身就是一个非常强大的控制系统。

如果一个用户拥有:

cluster-admin

那么基本可以控制整个集群。

因此生产环境必须考虑权限。

Kubernetes 的 RBAC 核心对象包括:

Role
ClusterRole
RoleBinding
ClusterRoleBinding

例如:

开发人员
   │
   ▼
只能访问 staging namespace
   │
   ├── get pods
   ├── logs pods
   └── restart deployment

而不是直接给:

cluster-admin

这也是生产 Kubernetes 必须重视的问题。


十六、Kubernetes 真正运行起来之后,应该怎么排查问题?

实际工作中,Kubernetes 最常见的并不是“怎么创建 Pod”,而是:

Pod 为什么没起来?

可以形成一套比较固定的排查路径。

首先:

kubectl get pods -n xxx

观察:

Pending
Running
CrashLoopBackOff
ImagePullBackOff
ErrImagePull
OOMKilled

如果是:

Pending

可以查看:

kubectl describe pod xxx -n xxx

重点看:

Events

可能发现:

Insufficient cpu
Insufficient memory
node affinity conflict
unbound PersistentVolumeClaim

如果:

CrashLoopBackOff

重点看日志:

kubectl logs xxx -n xxx

如果容器已经重启过,可以:

kubectl logs xxx -n xxx --previous

如果 Pod 看起来正常,但是服务访问不了,可以继续检查:

kubectl get svc
kubectl get endpoints
kubectl get ingress

再检查:

Service selector
Pod labels
Readiness Probe
NetworkPolicy
Ingress
DNS

这套思路比“看到 Pod 不正常就重启”重要得多。


十七、从 Docker 到 Kubernetes,可以这样理解

如果把整个 Kubernetes 的概念压缩成一张图:

                    Kubernetes
                         │
             ┌───────────┴───────────┐
             │                       │
        Control Plane             Worker Node
             │                       │
       ┌─────┼─────┐                 │
       │     │     │                 │
   API Server etcd Scheduler          │
       │                             │
       └──────── Controller           │
                                     │
                                    Pod
                                     │
                                ┌────┴────┐
                                │         │
                           Container  Container

应用层面:

Deployment
     │
     ▼
ReplicaSet
     │
     ▼
Pod
     │
     ▼
Container

网络层面:

Internet
    │
    ▼
Ingress
    │
    ▼
Service
    │
    ▼
Pod

配置层面:

ConfigMap
Secret
   │
   ▼
 Pod

存储层面:

Pod
 │
 ▼
PVC
 │
 ▼
PV
 │
 ▼
Storage

权限层面:

User
 │
 ▼
RBAC
 │
 ▼
Kubernetes API

十八、生产环境中的 Kubernetes

真正进入生产以后,Kubernetes 本身只是基础设施的一部分。

一个比较完整的生产环境通常还会围绕 Kubernetes 建立:

                     Git
                      │
                      ▼
                  CI Pipeline
                      │
                      ▼
                 Docker Image
                      │
                      ▼
                 Image Registry
                      │
                      ▼
                 Kubernetes
                      │
       ┌──────────────┼──────────────┐
       ▼              ▼              ▼
   Monitoring       Logging        Tracing
   Prometheus       Loki/ES        Jaeger
       │              │              │
       └──────────────┼──────────────┘
                      ▼
                   Grafana

部署方式也逐渐从:

kubectl apply -f deployment.yaml

发展到:

Git
 ↓
CI
 ↓
Image
 ↓
Helm / Kustomize
 ↓
Argo CD / Flux
 ↓
Kubernetes

这时候 Kubernetes 就不再只是“运行 Docker 容器”,而成为整个 DevOps、微服务和云原生体系中的基础平台。


十九、最后重新理解 Kubernetes

学习 Kubernetes 的时候,很容易陷入命令记忆:

kubectl get pods
kubectl get svc
kubectl describe pod
kubectl logs
kubectl exec

这些命令当然需要掌握,但真正重要的是理解 Kubernetes 背后的思想。

Kubernetes 并不是:

我执行一条命令,它帮我启动一个容器。

而更接近:

我声明系统应该是什么状态,Kubernetes 持续让真实系统向这个状态靠近。

例如:

replicas: 3

你表达的是:

我希望有 3 个实例

而不是:

请现在帮我启动 3 个容器

如果 Pod 从:

3 → 2

Controller 会发现:

Desired = 3
Actual = 2

于是自动修复。

如果节点发生故障:

Node
  ×

Kubernetes 又会重新调度工作负载。

如果发布新版本:

v1 → v2

Deployment 可以通过滚动更新逐步替换实例。

所以从本质上来说,Kubernetes 最值得学习的不是某一个 YAML 字段,而是下面这条完整的思维链:

声明期望状态
      ↓
API Server
      ↓
etcd 保存状态
      ↓
Controller 观察状态
      ↓
Scheduler 决定位置
      ↓
Kubelet 执行
      ↓
Container Runtime
      ↓
应用运行
      ↓
持续监控实际状态
      ↓
发现偏差
      ↓
再次调整

这就是 Kubernetes 的核心控制循环。

当真正理解了这套模型之后,再去学习:

Deployment
Service
Ingress
ConfigMap
Secret
StatefulSet
DaemonSet
Job
CronJob
PV/PVC
RBAC
HPA
NetworkPolicy
Helm
Operator

就不会再觉得它们是一堆互相独立的 YAML 配置。

它们实际上都是 Kubernetes 围绕“声明状态、观察状态、不断调谐状态”这个核心思想构建出来的不同抽象。

而这也是 Kubernetes 能够从一个容器编排工具,逐渐发展成云原生基础平台的关键所在。

#k8s
ZZC

ZZC

写代码,也写一些生活。