K8S 控制器-资源调度-叩丁狼

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 <标签内容> # 双条件匹配
        • ...
  • 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-0kafka-1kafka-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 上,从而保证了数据的持久化和稳定性。
  • 有序的 pod 管理 : StatefulSet 严格遵循顺序规则,这是其"状态"的另一个体现。这种顺序性对于有主从关系的集群(如 MySQL、Redis)至关重要,因为它确保了主节点(通常是索引为0的 Pod)首先启动并稳定运行。

    • 部署/扩容时:按索引顺序从大到小(0, 1, 2, ...) 创建 Pod。必须等前一个 Pod(kafka-0)成功进入 Running 和 Ready 状态后,才会创建下一个 Pod(kafka-1)。删除反之
    • 滚动更新时:默认策略是 RollingUpdate,它会按与 Pod 索引相反的顺序(从最大的索引开始)逐个地、安全地更新 Pod。
3.2.3.1 工作原理剖析

要理解 StatefulSet,需要明白它和以下几个 Kubernetes 组件/资源的关系:

  1. StatefulSet Controller:核心控制器,负责创建、更新、删除 Pod,并确保 Pod 的数量和状态符合预期。
  2. 无头 Service:你必须手动创建一个 ClusterIP 类型为 None 的 Service。
    1. 这个 Service 不提供负载均衡,它的作用是告诉 Kubernetes 不要为这些 Pod 分配集群 IP
    2. 而是让 DNS 系统为每个 Pod 返回其独立的 DNS A 记录。这是实现稳定网络发现的基石。
  3. PersistentVolume Provisioner
    1. volumeClaimTemplates 中指定了 storageClassName 时,动态卷制备器会按需自动创建 PV。
3.2.3.2 示例
复制代码
设我们要部署一个名为 `my-app` 的有状态应用,它有三个副本,每个副本都需要自己的存储
  1. 先创建一个无头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 的标签

  2. 创建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
  3. 工作流程

    1. 提交清单 :用户通过 kubectl apply 提交了无头 Service 和 StatefulSet 的配置文件。
    2. 控制器工作:StatefulSet 控制器监测到新的 StatefulSet 资源,开始行动。
    3. 有序创建
      1. 控制器按顺序(0, 1, 2)创建 Pod。
      2. 在创建每个 Pod(如 my-app-0)时,根据 volumeClaimTemplates 自动创建一个唯一的 PVC(www-my-app-0)。
      3. 该 PVC 根据其规格(storageClassName: "standard")动态地制备并绑定一个 PV(如 pv-0
      4. Pod 的 volumeMounts 将名为 www 的卷(来自于 PVC www-my-app-0)挂载到容器内的指定路径。
    4. 网络注册
      1. 无头 Service my-app-service 持续监控带有标签 app: my-app 的 Pod
      2. 每当一个 Pod 创建成功并进入 Ready 状态,Service 就为其在 Kubernetes 的 DNS 中注册一条唯一的 A 记录。
    5. 最终状态
      1. 稳定的网络:每个 Pod 都有一个固定的、可通过 DNS 直接访问的域名
      2. 稳定的存储:每个 Pod 都拥有自己独立的、永久的存储卷。即使 Pod my-app-0 被删除重建,新的 my-app-0 Pod 也会通过同名 PVC 挂载回原来的 PV,数据不会丢
  4. 部署和验证

    应用配置,使用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

  5. 缺点

  • 存储复杂性:你需要负责管理底层存储的备份、恢复和扩缩容。删除 StatefulSet 时,其关联的 PVC 默认不会被删除,需要手动清理,以防止数据丢失。
  • 网络复杂性 :应用本身需要能够处理集群发现和成员管理(例如,kafka-0 需要知道如何找到 kafka-1kafka-2)。StatefulSet 只提供了稳定的身份,不提供集群组建逻辑。
  • 速度较慢:由于有序性的要求,部署、扩缩容和更新的速度会比 Deployment 慢。
  1. 特殊用法
    1. 灰度发布<金丝雀发布> : 通过 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 可以通过 nodeSelectornodeAffinity 来限制 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
相关推荐
weixin_537590451 小时前
《C程序设计语言》练习答案(练习1-7)
linux·c语言·算法
123过去3 小时前
ike-scan使用教程
linux·测试工具
疯狂吧小飞牛10 小时前
GPG基础指令
linux·服务器·网络
C++ 老炮儿的技术栈10 小时前
volatile使用场景
linux·服务器·c语言·开发语言·c++
hjxu201611 小时前
【OpenClaw 龙虾养成笔记一】在远程服务器,使用Docker安装OpenClaw
服务器·笔记·docker
todoitbo11 小时前
用虚拟局域网打通 Win/Mac/Linux 三端:跨设备协作的实用方案
linux·运维·macos
源远流长jerry11 小时前
RDMA 基本操作类型详解:从双端通信到单端直访
linux·网络·tcp/ip·ip
Sylvia-girl12 小时前
Linux下的基本指令1
linux·运维·服务器
wyt53142913 小时前
Redis的安装教程(Windows+Linux)【超详细】
linux·数据库·redis