想象一下,你正在经营一家在全球范围内提供服务的科技公司。周一早上,你的用户量从一万突然暴增到一百万;周五晚上,系统因为流量低谷而闲置,但服务器账单还在不断累积。在传统架构下,这通常意味着两个选择:要么忍受服务崩溃,要么为了那几分钟的峰值购买永远用不上的昂贵硬件。
而容器编排技术——尤其是 Kubernetes(简称 K8s)——彻底改变了这种“要么不够用、要么太浪费”的二元困境。它让计算机资源像自来水一样,打开龙头就有,不用时只需关掉阀门,且按实际使用量计费。这就是云原生应用架构的核心魅力。
今天,我们不聊枯燥的定义,而是像拆解一辆跑车引擎一样,深入理解 Kubernetes 是如何运作,以及如何利用它构建真正可扩展、高可用的分布式系统。
从单体到微服务:为什么我们需要 Kubernetes
在深入 K8s 之前,先看看“前 Kubernetes 时代”的痛苦。
以前,我们部署一个应用,通常是在一台物理机或虚拟机上运行一个单体应用(Monolithic Application)。所有的代码、数据库连接、缓存逻辑都打包在一起。当用户增加时,我们只能垂直扩展——买更大的服务器(向上扩展,Scale Up)。但硬件是有极限的,CPU 再强也跑不过无限的并发请求。
于是,大家开始尝试水平扩展——买更多服务器,把应用拆成多个副本(向下扩展,Scale Out)。但这带来了新问题:
- 怎么确保这些副本都在运行?
- 如果其中一台死了,怎么自动重启?
- 流量怎么均匀分发到这几台服务器上?
- 配置变了,怎么通知所有服务器?
这些问题在只有几台机器时,靠脚本还能勉强应付。但当服务器数量达到几百、几千台时,人工或简单脚本完全失效。这就是 Kubernetes 诞生的背景——自动化运维容器化应用。
Kubernetes 不是一个简单的工具,它是一个分布式系统的操作系统。它管理着一组集群节点(机器),并在上面调度、运行、扩展你的应用程序容器。
Kubernetes 的核心架构:主节点与工作节点
Kubernetes 集群由两类节点组成:控制平面(Control Plane) 和 工作节点(Worker Nodes)。你可以把控制平面想象成“大脑”,把工作节点想象成“手脚”。
控制平面:集群的大脑
控制平面负责做出全局决策,比如调度、响应事件、维护集群状态。它通常由以下几个核心组件构成:
API Server(kube-apiserver) 这是 Kubernetes 的入口,所有其他组件都通过它与集群交互。当你执行
kubectl apply命令时,实际上是在向 API Server 发送请求。它验证请求、更新集群状态,并协调其他组件。你可以把它理解为一个 RESTful API 网关。etcd 一个高可用的键值存储数据库,用于保存集群的所有配置信息、状态数据和元数据。它是集群的“真相源”(Source of Truth)。如果 etcd 丢了,整个集群就不知道当前状态了。因此,etcd 通常以集群模式部署,确保数据不丢失。
Scheduler(kube-scheduler) 负责将新的 Pod(最小的部署单元)分配到合适的 Worker 节点上。调度器会根据资源需求、节点负载、亲和性规则、污点容忍等多种因素做出决策。比如,你要求 Pod 必须运行在 GPU 节点上,调度器会找到符合条件的节点并安排上去。
Controller Manager(kube-controller-manager) 运行着多个控制器,每个控制器负责维护集群的“期望状态”。例如:
- Node Controller:监控节点健康状态
- Replication Controller:确保指定数量的 Pod 副本在运行
- Endpoint Controller:更新服务与 Pod 的关联
- Service Account Controller:管理默认的服务账户
工作节点:执行任务的工人
每个 Worker 节点都是集群中的一个机器(物理机或虚拟机),负责运行实际的应用容器。每个节点上都有以下关键组件:
Kubelet 这是节点的“代理”,每个节点上都在运行一个 Kubelet 进程。它负责接收调度器分配给该节点的 Pod 定义,并确保 Pod 中的容器处于期望状态(运行、重启、停止)。Kubelet 会与 API Server 通信,汇报节点状态。
Kube-proxy 负责网络代理,实现 Kubernetes 服务的网络负载均衡。它监听 API Server 中服务对象的变动,动态更新节点的 iptables 或 IPVS 规则,确保流量能正确路由到对应的 Pod。
容器运行时(Container Runtime) 负责实际运行容器。常见的有 Docker(传统)、containerd(现代标准,K8s 1.24+ 默认支持)、CRI-O 等。Kubernetes 通过 CRI(Container Runtime Interface)标准接口与容器运行时通信,解耦了编排层和容器层。
Pod:Kubernetes 的最小部署单元
在 Kubernetes 中,你不会直接部署容器,而是部署 Pod。Pod 是 K8s 中可以创建和管理的最小计算单元。
一个 Pod 可以包含一个或多个容器。这些容器共享网络命名空间(IP 地址)、存储卷和生命周期。这意味着 Pod 内的容器可以通过 localhost 互相通信,速度极快,就像运行在同一个进程里一样。
为什么需要 Pod?因为现代应用往往不是单一进程。比如,一个 Web 应用可能需要:
- 一个主应用容器(Node.js)
- 一个日志收集容器(Fluentd)
- 一个代理容器(Sidecar 模式的 Envoy)
它们需要共享网络和存储,因此打包成一个 Pod。
Pod 的生命周期
Pod 有以下状态:
- Pending:API Server 已创建 Pod,但尚未调度到节点,或容器尚未启动
- Running:Pod 已绑定到节点,所有容器已启动
- Succeeded:所有容器已成功退出(无重启)
- Failed:所有容器都已退出,但至少有一个失败
- Unknown:无法获取 Pod 状态(通常是因为节点通信失败)
工作负载资源:如何管理 Pod
直接管理 Pod 是不现实的,因为 Pod 是易失的(可能被删除或重建)。Kubernetes 提供了几种工作负载资源来管理 Pod 的生命周期:
Deployment:无状态应用的标准
Deployment 是管理无状态应用(如 Web 服务器、API 服务)最常用的资源。它通过管理 ReplicaSet 来确保指定数量的 Pod 副本在运行。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web-app
spec:
replicas: 3 # 期望运行 3 个副本
selector:
matchLabels:
app: my-web
template:
metadata:
labels:
app: my-web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
这段 YAML 定义了一个名为 my-web-app 的 Deployment,运行 3 个 Nginx 容器。关键点:
replicas: 3确保始终有 3 个 Pod 在运行resources定义了资源请求和限制,调度器会参考这些值selector.matchLabels和 Pod 模板中的labels必须匹配,Deployment 才能找到它管理的 Pod
StatefulSet:有状态应用的选择
对于有状态应用(如数据库、消息队列),Pod 需要稳定的网络标识和存储。StatefulSet 提供了这些保障:
- 每个 Pod 有唯一的、不变的名字(如
mysql-0,mysql-1) - 存储卷按 Pod 名字绑定,Pod 重建后数据不丢失
- 有序部署和扩展
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web-server
spec:
serviceName: "nginx"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
DaemonSet:每个节点一个实例
如果你需要在每个节点上都运行一个代理、日志收集器或监控 Agent,DaemonSet 是理想选择。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: fluentd
image: fluentd:v1.16
这样,每当有新节点加入集群,就会自动部署一个 log-agent Pod。
Job 和 CronJob:任务型工作负载
对于批处理任务(如数据转换、邮件发送),Job 和 CronJob 非常有用。Job 确保任务完成后 Pod 不重启;CronJob 则按 schedule 定期运行 Job。
apiVersion: batch/v1
kind: CronJob
metadata:
name: weekly-report
spec:
schedule: "0 9 * * 1" # 每周一早上 9 点
jobTemplate:
spec:
template:
spec:
containers:
- name: report-gen
image: report-generator:latest
restartPolicy: OnFailure
服务发现与负载均衡:Service
Pod 的 IP 地址是动态变化的(重启、扩缩容后 IP 会变),不能直接让外部访问。Kubernetes 提供了 Service 资源,为 Pod 提供稳定的网络入口。
ClusterIP:内部访问
最常见的 Service 类型,只在集群内部可见,提供一个稳定的 VIP(虚拟 IP)。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-web
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
当客户端访问 my-service:80 时,Kube-proxy 会将流量负载均衡到后端多个 Pod。
NodePort:暴露到集群外部
NodePort 在每个节点上开放一个端口,外部流量可以通过 NodeIP:NodePort 访问服务。
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
LoadBalancer:云提供商集成
在公有云(AWS、GCP、Azure)上,LoadBalancer 类型会自动创建一个云负载均衡器,将流量分发到节点。
spec:
type: LoadBalancer
Ingress:HTTP/HTTPS 路由
对于 Web 应用,Ingress 提供了更灵活的 HTTP 路由规则,支持域名、路径、TLS 终止等。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
配置与存储:ConfigMap 与 Secret
应用配置应该与镜像解耦。Kubernetes 提供了 ConfigMap 和 Secret 来管理配置。
ConfigMap:普通配置
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_URL: "postgres://user:pass@db-host:5432/mydb"
LOG_LEVEL: "info"
在 Pod 中使用:
containers:
- name: my-app
image: my-app:latest
envFrom:
- configMapRef:
name: app-config
Secret:敏感信息
Secret 与 ConfigMap 类似,但用于敏感数据(密码、密钥、证书),数据在 etcd 中是 Base64 编码的(注意:Base64 不是加密,只是编码)。
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
username: dXNlcg== # base64 of "user"
password: cGFzcw== # base64 of "pass"
存储:PersistentVolume 与 PersistentVolumeClaim
Pod 是易失的,但很多应用需要持久化数据(数据库、文件存储)。Kubernetes 通过 PV(PersistentVolume) 和 PVC(PersistentVolumeClaim) 抽象存储。
- PV:集群管理员预配置的存储资源(如云盘、NFS)
- PVC:用户向 PV 发出的存储请求
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
在 Pod 中使用 PVC:
containers:
- name: my-app
image: my-app:latest
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
可扩展性:自动扩缩容(HPA 与 VPA)
Kubernetes 的核心优势之一是自动扩缩容。
HPA(Horizontal Pod Autoscaler)
HPA 根据 CPU、内存或自定义指标自动调整 Deployment 的副本数。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
当 CPU 使用率超过 50% 时,HPA 会增加副本;低于时则减少。
VPA(Vertical Pod Autoscaler)
VPA 自动调整单个 Pod 的资源请求和限制(CPU/内存),适合无状态应用但不适合需要快速响应场景。
健康检查:Liveness、Readiness 与 Startup Probes
Kubernetes 需要知道 Pod 是否健康,以便决定是否重启或从负载均衡中移除。
Liveness Probe:存活探针
检测容器是否运行。如果失败,Kubelet 会重启容器。
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 10
Readiness Probe:就绪探针
检测容器是否准备好接收流量。如果失败,Pod 会从 Service 的 Endpoints 中移除,不再接收流量。
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Startup Probe:启动探针
针对启动缓慢的应用,防止在启动过程中被误杀。
startupProbe:
httpGet:
path: /start
port: 8080
failureThreshold: 30
periodSeconds: 10
安全:RBAC 与 Network Policy
RBAC(Role-Based Access Control)
Kubernetes 默认没有启用 RBAC,但生产环境必须启用。RBAC 允许管理员定义谁(User/ServiceAccount)可以对什么资源执行什么操作。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
Network Policy
默认情况下,Pod 之间可以自由通信。Network Policy 允许限制 Pod 之间的网络流量,实现微隔离。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {}
ingress: []
egress: []
这个策略阻止了所有入站和出站流量,然后你可以添加允许的规则。
实战:构建一个可扩展的 Web 应用
让我们将以上概念整合,构建一个完整的 Web 应用架构。
应用架构
- 前端:React 应用,静态文件托管
- 后端:Node.js API 服务
- 数据库:PostgreSQL
- 缓存:Redis
部署步骤
1. 创建 Namespace 隔离环境
apiVersion: v1
kind: Namespace
metadata:
name: production
2. 配置 ConfigMap 和 Secret
”`yaml apiVersion: v1 kind: ConfigMap metadata: name: api-config namespace: production data: NODE_ENV: “production” DB_HOST: “postgres-service”
