3.2 控制器 - 资源调度
3.2.1 Label 与 Selecter
通过 Label 与 Selecter 可以对资进行关联,便于管理
-
Label : 标签
- 可以在
metadata.labels中进行配置 - 可以使用命令设置
- kubectl label <资源类型 > <资源名称> < ? 设置内容>
- --overewrite # 修改已存在的labe
- --show-labels # 查询所有的label的内容
- -A -l <标签内容> # 查询符合的标签的所有容器 -A : 夸命名空间 -l : 标签选择器
- -A -l <标签内容 in (标签)> # 匹配多个
- -l <标签内容> and <标签内容> # 双条件匹配
- ...
- kubectl label <资源类型 > <资源名称> < ? 设置内容>
- 可以在
-
Selecter : 选择器
- 可以在各个对象的配置
spec.selecter中或其他可以写selecter属性中填写 - 可以使用命令如上.
- 可以在各个对象的配置
3.2.2 DeployMent

部署无状态应用,便于扩容缩绒
3.2.2.1 命令创造
# 创建一个基于nginx1.7.9镜像的deployment
kubectl create deploy nginx-deploy --image=nginx:1.7.9
# 或执行 (通过 写 yaml 文件的方式创造)
kubectl create -f xxx.yaml --record
# --record 会在 annotation 中记录当前命令创建或升级了资源,后续可以查看做过哪些变动操作。
# 查看部署信息
kubectl get deployments
# 查看
rs kubectl get rs
# 查看 pod 以及展示标签,可以看到是关联的那个
rs kubectl get pods --show-labels
3.2.2.2 滚动更新,回滚,缩容扩容
-
滚动更新
-
只有修改了 deployment 配置文件中的 template 中的属性后,才会触发更新操作
修改 nginx 版本号 -> set image
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
或者修改
kubectl edit deployment/nginx-deployment
查看滚动更新的过程
kubectl rollout status deploy <deployment_name>
查看部署描述,最后展示发生的事件列表也可以看到滚动更新过程
kubectl describe deploy <deployment_name>
查看部署信息 通过 kubectl get deployments 获取部署信息,UP-TO-DATE 表示已经有多少副本达到了配置中要求的数目
通过 kubectl get rs 可以看到增加了一个新的 rs
通过 kubectl get pods 可以看到所有 pod 关联的 rs 变成了新的
-
-
回滚
-
可以通过设置
.spec.revisonHistoryLimit来指定 deployment 保留多少 revison如果设置为 0,则不允许 deployment 回退。kubectl rollout history deployment/nginx-deploy # 可以获取 revison 的列表
kubectl rollout history deployment/nginx-deploy --revision=2 # 可以查看详细信息确认要回退的版本后,可以通过
kubectl rollout undo deployment/nginx-deploy
可以回退到上一个版本
kubectl rollout undo deployment/nginx-deploy --to-revision=2 # 也可以回退到指定的 revision
检验
kubectl get deployment
kubectl describe deployment
-
-
扩容缩容
- 通过
kube scale命令可以进行自动扩容/缩容,以及通过kube edit编辑replcas也可以实现扩容/缩容 扩容与缩容只是直接创建副本数,没有更新 pod template 因此不会创建新的 rs
- 通过
3.2.2.3 暂停与恢复
由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作那么此时如果频繁修改信息,就会产生多次更新,而实际上只需要执行最后一次更新即可当出现此类情况时我们就可以暂停 deployment 的 rollout
# 实现暂停
kubectl rollout pause deployment <name>
# 检测配置是否发生了更改?
kubectl get deploy <name> -o yaml
# 恢复rollout
kubectl rollout deploy <name>
# 恢复后,我们再次查看 rs 和 po 信息,我们可以看到就开始进行滚动更新操作了
kubectl get rs
kubectl get po
$3.2.2.4 deploy 的配置文件
元数据 + 规约(副本数 + 选择器 + 更新策略 + pod template的元数据 + 规约)
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
# 声明命名空间,名称和标签
metadata: # 元信息
labels: # 标签
app: nginx-deploy # 具体的 key: value 配置形式
name: nginx-deploy # deployment 的名字
namespace: default # 所在的命名空间
# 规约,声明期望的状态
spec:
replicas: 1 # 期望副本数
revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
selector: # 选择器,用于找到匹配的 RS
matchLabels: # 按照标签匹配
app: nginx-deploy # 匹配的标签key/value
strategy: # 更新策略
rollingUpdate: # 滚动更新配置
maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例
maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功
type: RollingUpdate # 更新类型,采用滚动更新
# template模板 -> pod的规约声明
template: # pod 模板
metadata: # pod 的元信息
labels: # pod 的标签
app: nginx-deploy
spec: # pod 期望信息
containers: # pod 的容器
- image: nginx:1.7.9 # 镜像
imagePullPolicy: IfNotPresent # 拉取策略
name: nginx # 容器名称
restartPolicy: Always # 重启策略
terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
3.2.3 StatuefulSet
有状态服务控制器
-
稳定的网络标识 : StatefulSet 为每个 Pod 提供一个唯一且固定的名称,格式为:
<statefulset-name>-<ordinal-index>- 一个名为
kafka的 StatefulSet,创建 3 个副本,Pod 名称将分别是:kafka-0、kafka-1、kafka-2,这些 Pod 名称在 Pod 的生命周期内是稳定的。即使kafka-1这个 Pod 被重新调度到另一个节点上,它的名字依然会是kafka-1。 - 还会创建一个无头 Service,用于管理 Pod 的网络身份。每个 Pod 会获得一个唯一的 DNS 记录:
<pod-name>.<service-name>.<namespace>.svc.cluster.local,集群中的其他应用可以通过这个固定的DNS访问Pod
- 一个名为
-
稳定的持久化存储 : 通过 VolumeClaimTemplate(存储卷申请模板)来实现
- 在定义 StatefulSet 时,你可以创建一个
volumeClaimTemplates。- 当 StatefulSet 创建 Pod 时(例如
kafka-0),它会根据这个模板自动创建一个 PersistentVolumeClaim,名称格式为:<volumeClaimTemplateName>-<podName>。 - 这个 PVC 随后会绑定到一个 PV,从而为 Pod 提供存储。
- PV:就是图书馆里一本本具体的、物理存在的书。
- PVC:就是读者提交的借书申请单。
- 每个 Pod(
kafka-0,kafka-1, ...)都会获得自己独立的 PVC 和 PV。当 Pod 被重新调度时,新的 Pod 实例会自动挂载到它原来使用的那个 PV 上,从而保证了数据的持久化和稳定性。
- 当 StatefulSet 创建 Pod 时(例如
- 在定义 StatefulSet 时,你可以创建一个
-
有序的 pod 管理 : StatefulSet 严格遵循顺序规则,这是其"状态"的另一个体现。这种顺序性对于有主从关系的集群(如 MySQL、Redis)至关重要,因为它确保了主节点(通常是索引为0的 Pod)首先启动并稳定运行。
- 部署/扩容时:按索引顺序从大到小(0, 1, 2, ...) 创建 Pod。必须等前一个 Pod(
kafka-0)成功进入 Running 和 Ready 状态后,才会创建下一个 Pod(kafka-1)。删除反之 - 滚动更新时:默认策略是
RollingUpdate,它会按与 Pod 索引相反的顺序(从最大的索引开始)逐个地、安全地更新 Pod。
- 部署/扩容时:按索引顺序从大到小(0, 1, 2, ...) 创建 Pod。必须等前一个 Pod(
3.2.3.1 工作原理剖析
要理解 StatefulSet,需要明白它和以下几个 Kubernetes 组件/资源的关系:
- StatefulSet Controller:核心控制器,负责创建、更新、删除 Pod,并确保 Pod 的数量和状态符合预期。
- 无头 Service:你必须手动创建一个
ClusterIP类型为None的 Service。- 这个 Service 不提供负载均衡,它的作用是告诉 Kubernetes 不要为这些 Pod 分配集群 IP
- 而是让 DNS 系统为每个 Pod 返回其独立的 DNS A 记录。这是实现稳定网络发现的基石。
- PersistentVolume Provisioner
- 当
volumeClaimTemplates中指定了storageClassName时,动态卷制备器会按需自动创建 PV。
- 当
3.2.3.2 示例
设我们要部署一个名为 `my-app` 的有状态应用,它有三个副本,每个副本都需要自己的存储
-
先创建一个无头service - 保证稳定网络发现的基石
apiVersion: v1
kind: Service
metadata:
name: my-app-service
labels:
app: my-app
spec:
ports:- port: 80 # 这定义了服务端口。虽然不进行负载均衡,但这个端口信息在 DNS 发现和 Pod 间直接通信时仍然有用。
name: web
一个普通的 Service 会被分配一个虚拟的 IP 地址(ClusterIP),作为负载均衡器,将请求转发到后端的多个 Pod。
当设置为 None 时,Kubernetes 不会为这个 Service 分配 ClusterIP,也不会进行负载均衡。
它的唯一目的变成了:为匹配的 Pod 提供稳定的网络身份标识
clusterIP: None # 这就是"无头"的定义
selector:
# 这个选择器告诉 Service:"请关注所有带有标签 app: my-app 的 Pod"。
# 它不负责路由流量,而是负责发现这些 Pod,并为它们创建 DNS 记录
app: my-app # 这个选择器必须匹配 StatefulSet 中 Pod 的标签 - port: 80 # 这定义了服务端口。虽然不进行负载均衡,但这个端口信息在 DNS 发现和 Pod 间直接通信时仍然有用。
-
创建statefulSet的资源清单
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-app
spec:你所管理的 Pod 的网络身份,由名为 my-app-service 的无头服务来定义和提供。
有了这个关联,StatefulSet 在创建 Pod 时,才会知道该让哪个 Service 来为这些 Pod 创建 DNS 记录
serviceName: "my-app-service" # 这是 StatefulSet 和无头服务之间的桥梁,必须指向上面创建的无头 Service
replicas: 3
selector:
matchLabels: # 这组配置是标准的控制器模式。StatefulSet 通过 selector 来识别哪些 Pod 归它管理。
app: my-app # 必须匹配 template.metadata.labels
# 这个标签 app: my-app 也正是无头服务 my-app-service 的 selector 所寻找的标签。
# 这就完成了 Pod、StatefulSet 和 Service 三者的关联。template:
metadata:
labels:
app: my-app # 这是 Pod 的标签,被 Service 的 selector 使用
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www # 这个名字必须与 volumeClaimTemplates.name 一致
mountPath: /usr/share/nginx/html # 请把名为 www 的存储卷,挂载到容器内的 /usr/share/nginx/html 路径下volumeClaimTemplates: # 核心部分:存储卷申请模板
- metadata:
name: www # 这是 StatefulSet 的灵魂。它是一个"模板",用于为每个 Pod 自动创建独立的 PVC
spec: # 这个 PVC 会去动态或静态地绑定一个 PV,从而为 Pod my-app-0 提供 1Gi 的、独立的、持久的存储。
accessModes: [ "ReadWriteOnce" ]
storageClassName: "standard" # 假设你的集群有名为 "standard" 的 StorageClass
resources:
requests:
storage: 1Gi
- metadata:
-
工作流程
- 提交清单 :用户通过
kubectl apply提交了无头 Service 和 StatefulSet 的配置文件。 - 控制器工作:StatefulSet 控制器监测到新的 StatefulSet 资源,开始行动。
- 有序创建 :
- 控制器按顺序(0, 1, 2)创建 Pod。
- 在创建每个 Pod(如
my-app-0)时,根据volumeClaimTemplates自动创建一个唯一的 PVC(www-my-app-0)。 - 该 PVC 根据其规格(
storageClassName: "standard")动态地制备并绑定一个 PV(如pv-0) - Pod 的
volumeMounts将名为www的卷(来自于 PVCwww-my-app-0)挂载到容器内的指定路径。
- 网络注册 :
- 无头 Service
my-app-service持续监控带有标签app: my-app的 Pod - 每当一个 Pod 创建成功并进入 Ready 状态,Service 就为其在 Kubernetes 的 DNS 中注册一条唯一的 A 记录。
- 无头 Service
- 最终状态
- 稳定的网络:每个 Pod 都有一个固定的、可通过 DNS 直接访问的域名
- 稳定的存储:每个 Pod 都拥有自己独立的、永久的存储卷。即使 Pod
my-app-0被删除重建,新的my-app-0Pod 也会通过同名 PVC 挂载回原来的 PV,数据不会丢
- 提交清单 :用户通过
-
部署和验证
应用配置,使用apply -f
kubectl apply -f service.yaml
kubectl apply -f statefulset.yaml观察pod的创建 get
你会清晰地看到 Pod 按顺序启动:my-app-0 -> my-app-1 -> my-app-2。
kubectl get pods -w -l app=my-app
查看pvc
kubectl get pvc
会有如下类似的输出
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-my-app-0 Bound pvc-12345678-90ab-cdef-1234-567890abcdef 1Gi RWO standard 5m
www-my-app-1 Bound pvc-abcdef12-3456-7890-abcd-ef1234567890 1Gi RWO standard 4m
www-my-app-2 Bound pvc-09876543-21ba-dcfe-4321-098765432109 1Gi RWO standard 3m在集群内另一个 Pod 中,你可以通过固定的 DNS 名来访问特定的 Pod:
进入另一个临时 Pod
kubectl run -it --rm --image=busybox:1.28 test-pod -- /bin/sh
在 test-pod 内部执行
nslookup my-app-0.my-app-service
-
缺点
- 存储复杂性:你需要负责管理底层存储的备份、恢复和扩缩容。删除 StatefulSet 时,其关联的 PVC 默认不会被删除,需要手动清理,以防止数据丢失。
- 网络复杂性 :应用本身需要能够处理集群发现和成员管理(例如,
kafka-0需要知道如何找到kafka-1和kafka-2)。StatefulSet 只提供了稳定的身份,不提供集群组建逻辑。 - 速度较慢:由于有序性的要求,部署、扩缩容和更新的速度会比 Deployment 慢。
- 特殊用法
- 灰度发布<金丝雀发布> : 通过 partition 字段 (只跟新序号大于 partition 的 Pod),实现部分 Pod 更新,部分不变
3.2.4 DeamonSet

为每一个匹配的Node部署一个守护进程
3.2.4.1 工作原理解析
DaemonSet 控制器的工作流程非常直接:
- 监听节点变化:DaemonSet 控制器持续监听(watch)Kubernetes API Server,关注集群中节点的添加和删除事件。
- 节点添加:当一个新的节点被加入到集群中时,DaemonSet 控制器会立即检测到这个事件。它会根据定义的 Pod 模板(
spec.template),在这个新节点上创建一个 Pod。 - 节点删除:当一个节点被从集群中移除(或被标记为不可调度
NoSchedule),DaemonSet 控制器会检测到这一变化,并删除运行在该节点上的 Pod。 - 节点选择器:DaemonSet 可以通过
nodeSelector或nodeAffinity来限制 Pod 只运行在部分节点上,而不是全部节点。
3.2.4.2 DeamonSet 示例
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch # 必须匹配 template 中的标签
template:
metadata:
labels:
name: fluentd-elasticsearch # 这个标签被 DaemonSet 的 selector 使用,用于识别和管理pod
spec:
# 容忍度 (Tolerations):允许 Pod 被调度到带有污点的节点上
# 容忍了控制平面(Control-Plane)和主节点(Master)的污点,使得该pod可以在任意的节点上运行
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
# pod说明 - container说明
containers:
- name: fluentd-elasticsearch # 容器的名称
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 # 使用的镜像
resources: # 资源说明
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
# 日志收集器需要访问节点上的文件系统。
# 这里使用了 hostPath 卷,将节点上的目录
# 如 /var/log 和 /var/lib/docker/containers挂载到 Pod 的容器内部。
# 这样,Fluentd 容器就能读取这些目录下的日志文件了。
volumeMounts: # 挂载说明
- name: varlog # 数据卷名字
mountPath: /var/log #挂载位置
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config-volume
mountPath: /etc/fluent/fluent.conf
subPath: fluent.conf
terminationGracePeriodSeconds: 30
volumes: # 数据卷说明
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: config-volume
configMap:
name: fluentd-config
3.2.4.2 容忍度与污点
容忍度是deamonSet非常重要的概念,Kubernetes 集群的主节点(Master) 通常自带一个污点(Taint)
例如上面说明的:node-role.kubernetes.io/control-plane:NoSchedule
污点的存在可以:防止普通工作负载 Pod 被调度到主节点上。
如果我们希望 DaemonSet 的 Pod(如日志收集器)也能在主节点上运行,就必须在 Pod 模板中声明相应的 tolerations,表示"我容忍这个污点,请允许我调度上去"。
例如上面的例子中:Pod 容忍了控制平面(Control-Plane)和主节点(Master)的污点,因此它可以在所有节点(包括主节点) 上运行。
3.2.4.3 控制 DeamonSet 的生效范围
-
NodeSelector- 节点选择器 : 只有特定的标签单上进行-
在 DeamonSet 的
spec.template.spec中配置nodeSelector -
给节点打上标签
kubectl label nodes <node-name> disktype=ssd<- 例spec: template: spec: nodeSelector: disktype: ssd # DeamonSet 会在 标签为 ssd 的 Node 上创建 Pod containers: - ...
-
-
NodeAffinity(节点亲和性) : 提供了比 NodeSeclecter 更强大,灵活的表达式来控制 Pod 的调度.spec: template: spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: # 这表示 Pod 只能被调度到带有 disktype 标签,且值为 ssd 或 nvme 的节点上。 - key: disktype operator: In values: - ssd - nvme containers: - ...
3.2.5 HPA 自动扩容/缩容
根据监控到的应用负载(如 : CPU / 内存 配置中需要有 CPU 内存 的资源限制 使用率或其他自定义指标),自动的调整 Pod 书数量,以确保应用始终有适量的资源来处理流量.
3.2.5.1 关键组件
-
指标 来源
-
Custom Metrics Api : 自定义指标,如: QPS / 请求延迟数 ...
-
Metrics Server : 核心指标,如内存/ CPU (必须安装,示例如下:)
# 使用 kubectl 安装(以最新版本为例) kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml # 验证安装 kubectl top nodes kubectl top pods
-
-
HPA Controller
- 定期(30s) 检查资源指标
- 根据指标值与目标值的比例,计算期望副本数
- 跟新目标资源(如: deployMent 的 replicas 字段)
-
工作负载控制器
- Deploy / statefulSet / ReplicaSet 控制器检测到 replicas 字段发生变化,更新 Pod 数.
3.2.5.2 示例
创建一个 HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler # 指定kind = HPA
metadata:
name: php-apache-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache # 指向要自动扩缩容的 Deployment
minReplicas: 1 # 最小副本数
maxReplicas: 10 # 最大副本数
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization # 使用率模式
averageUtilization: 50 # 目标 CPU 使用率:50%
behavior: # 扩缩容行为配置(v2beta2+ 版本特性)
scaleDown:
stabilizationWindowSeconds: 300 # 缩容稳定窗口:5分钟
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0 # 扩容立即执行
policies:
- type: Percent
value: 100
periodSeconds: 15