31、在 Kubernetes 中运行有状态应用及相关特性

在 Kubernetes 中运行有状态应用及相关特性

1. 在 Kubernetes 中运行 Cassandra 有状态应用

在 Kubernetes 中运行 Cassandra 这样的有状态应用时,会面临一些挑战。比如使用 emptyDir 存储时,每个节点都需要提供足够的存储空间。如果一个 Cassandra Pod 死亡,其存储也会随之消失。即便该 Pod 在同一物理(或虚拟)机上重启,磁盘上的数据也会丢失,因为 emptyDir 在 Pod 被移除时会被删除。不过,容器重启不受影响,因为 emptyDir 可以在容器崩溃时保留数据。

当 Pod 死亡时,复制控制器会启动一个带有空数据的新 Pod。Cassandra 会检测到集群中添加了新节点,为其分配一部分数据,并通过从其他节点移动数据来自动开始重新平衡。这正是 Cassandra 的优势所在,它会持续压缩、重新平衡并在集群中均匀分布数据。

1.1 Pod 分配到节点的问题

复制控制器方法的主要问题是多个 Pod 可能会被调度到同一个 Kubernetes 节点上。例如,当复制因子为 3,且负责某个键空间范围的三个 Pod 都被调度到同一个 Kubernetes 节点时,该范围内所有的读写请求都会指向这个节点,从而增加压力。更糟糕的是,这样会失去冗余性,形成单点故障(SPOF)。如果该节点死亡,复制控制器会在其他 Kubernetes 节点上愉快地启动三个新 Pod,但它们都没有数据,集群中的其他 Cassandra 节点(其他 Pod)也没有数据可供复制。

可以使用 Kubernetes 调度概念中的反亲和性来解决这个问题。在将 Pod 分配到节点时,可以对 Pod 进行注释,使调度器不会将其调度到已经有特定标签 Pod 的节点上。以下是添加到 Pod 规范中的配置,以确保每个节点最多分配一个 Cassandra Pod:

spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - cassandra
          topologyKey: kubernetes.io/hostname
1.2 使用 DaemonSet 分发 Cassandra

将 Cassandra Pod 分配到不同节点的更好解决方案是使用 DaemonSet。DaemonSet 与复制控制器一样有一个 Pod 模板,但它有一个节点选择器,用于确定在哪些节点上调度其 Pod。它没有特定数量的副本,只是在每个匹配其选择器的节点上调度一个 Pod。最简单的情况是在 Kubernetes 集群的每个节点上调度一个 Pod。节点选择器还可以使用针对标签的匹配表达式来部署到特定的节点子集。

以下是创建一个 DaemonSet 以将 Cassandra 集群部署到 Kubernetes 集群的示例:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: cassandra-daemonset
spec:
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      # Filter only nodes with the label "app: cassandra":
      nodeSelector:
        app: cassandra
      containers:

需要注意的是, nodeSelector 预计会被 affinity 取代,但具体时间尚不清楚。

2. Kubernetes 中的水平 Pod 自动缩放

Kubernetes 可以监控 Pod,并在 CPU 利用率或其他指标超过阈值时进行缩放。自动缩放资源指定了详细信息(CPU 百分比、检查频率),相应的自动缩放控制器会根据需要调整副本数量。

2.1 水平 Pod 自动缩放的工作原理

水平 Pod 自动缩放器并不直接创建或销毁 Pod,而是依赖于复制控制器或部署资源。这样做很明智,因为可以避免自动缩放与复制控制器或部署在不知情的情况下尝试缩放 Pod 数量时产生冲突。

在没有自动缩放器的情况下,如果复制控制器的副本数设置为 3,但根据平均 CPU 利用率实际上需要 4 个副本,就需要手动将复制控制器从 3 更新到 4,并手动监控所有 Pod 的 CPU 利用率。而自动缩放器会自动完成这些工作。

2.2 声明水平 Pod 自动缩放器

要声明水平 Pod 自动缩放器,需要一个复制控制器或部署,以及一个自动缩放资源。以下是一个简单的复制控制器配置,用于维护三个 Nginx Pod:

apiVersion: v1
kind: ReplicationController
metadata:
   name: nginx
spec:
   replicas: 3
   template:
     metadata:
       labels:
         run: nginx
     spec:
       containers:
       - name: nginx
         image: nginx
         ports:
         - containerPort: 80

自动缩放资源在 scaleTargetRef 中引用了 Nginx 复制控制器:

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: nginx
  namespace: default
spec:
  maxReplicas: 4
  minReplicas: 2
  targetCPUUtilizationPercentage: 90
  scaleTargetRef:
    apiVersion: v1
    kind: ReplicationController
    name: nginx

minReplicas maxReplicas 指定了缩放范围,这是为了避免因某些问题导致的失控情况。例如,如果由于某个错误,每个 Pod 无论实际负载如何都立即使用 100% 的 CPU,没有 maxReplicas 限制,Kubernetes 会不断创建更多 Pod,直到耗尽所有集群资源。如果在支持 VM 自动缩放的云环境中运行,这将产生巨大的成本。另一方面,如果没有 minReplicas ,当活动暂停时,所有 Pod 可能会被终止,新请求到来时,所有 Pod 都需要重新创建和调度。如果存在活动的开关模式,这个循环可能会多次重复。保持最少数量的副本运行可以缓解这种现象。在上述示例中, minReplicas 设置为 2, maxReplicas 设置为 4,Kubernetes 会确保始终有 2 到 4 个 Nginx 实例运行。

2.3 目标 CPU 利用率百分比及延迟设置

目标 CPU 利用率百分比(TCUP)是一个较长的术语。如果平均负载在 TCUP 附近徘徊,可能会导致频繁的波动,Kubernetes 会频繁地添加或删除副本,这通常不是理想的行为。为了解决这个问题,可以为缩放操作指定延迟。 kube-controller-manager 有两个标志支持这一点:
- --horizontal-pod-autoscaler-downscale-delay :该选项的值是一个持续时间,指定自动缩放器在当前缩容操作完成后,需要等待多长时间才能执行下一次缩容操作。默认值是 5 分钟(5m0s)。
- --horizontal-pod-autoscaler-upscale-delay :该选项的值是一个持续时间,指定自动缩放器在当前扩容操作完成后,需要等待多长时间才能执行下一次扩容操作。默认值是 3 分钟(3m0s)。

2.4 自定义指标

CPU 利用率是衡量 Pod 是否需要缩放的重要指标,但它不是唯一的,有时甚至不是最好的指标。内存可能是限制因素,还有一些更专业的指标,如 Pod 内部磁盘队列的深度、请求的平均延迟或服务超时的平均次数。

水平 Pod 自定义指标在 1.2 版本中作为 alpha 扩展添加,在 1.6 版本中升级到 beta 状态。现在可以根据多个自定义指标对 Pod 进行自动缩放。自动缩放器会评估所有指标,并根据所需的最大副本数进行自动缩放,以满足所有指标的要求。

2.5 使用自定义指标

使用带有自定义指标的水平 Pod 自动缩放器在启动集群时需要进行一些配置。具体步骤如下:
1. 启用 API 聚合层。
2. 注册资源指标 API 和自定义指标 API。Heapster 提供了一个可以使用的资源指标 API 实现,只需使用 --api-server 标志设置为 true 启动 Heapster。还需要运行一个单独的服务器来公开自定义指标 API,一个不错的起点是:https://github.com/kubernetes-incubator/custom-metrics-apiserver。
3. 使用以下标志启动 kube-controller-manager

--horizontal-pod-autoscaler-use-rest-clients=true
--kubeconfig <path-to-kubeconfig> OR --master <ip-address-of-apiserver>

如果同时指定了 --master --kubeconfig --master 标志将覆盖 --kubeconfig 。这些标志指定了 API 聚合层的位置,允许控制器管理器与 API 服务器进行通信。

在 Kubernetes 1.7 中,Kubernetes 提供的标准聚合层与 kube-apiserver 一起在进程中运行,因此可以使用以下命令找到目标 IP 地址:

> kubectl get pods --selector k8s-app=kube-apiserver --namespace kube-system -o jsonpath='{.items[0].status.podIP}'
2.6 使用 kubectl 进行自动缩放

kubectl 可以使用标准的 create 命令并接受配置文件来创建自动缩放资源。此外, kubectl 还有一个特殊的 autoscale 命令,允许在不使用特殊配置文件的情况下轻松设置自动缩放器。具体操作步骤如下:
1. 启动一个复制控制器,确保有三个运行无限 bash 循环的简单 Pod 副本:

apiVersion: v1
kind: ReplicationController
metadata:
   name: bash-loop-rc
spec:
   replicas: 3
   template:
     metadata:
       labels:
         name: bash-loop-rc
     spec:
       containers:
         - name: bash-loop
           image: ubuntu
           command: ["/bin/bash", "-c", "while true; do sleep 10; done"]
  1. 创建复制控制器:
> kubectl create -f bash-loop-rc.yaml
replicationcontroller "bash-loop-rc" created
  1. 查看复制控制器:
> kubectl get rc
NAME              DESIRED   CURRENT   READY     AGE
bash-loop-rc        3          3       3         1m

可以看到期望和当前的副本数都是 3,意味着有三个 Pod 在运行。
4. 确认 Pod 运行情况:

> kubectl get pods
NAME                     READY    STATUS    RESTARTS    AGE
bash-loop-rc-8h59t        1/1     Running    0          50s
bash-loop-rc-lsvtd        1/1     Running    0          50s
bash-loop-rc-z7wt5        1/1     Running    0          50s
  1. 创建自动缩放器,设置最小副本数为 4,最大副本数为 6:
> kubectl autoscale rc bash-loop-rc --min=4 --max=6 --cpu-percent=50
replicationcontroller "bash-loop-rc" autoscaled
  1. 查看生成的水平 Pod 自动缩放器(可以使用 hpa ):
> kubectl get hpa
NAME          REFERENCE    TARGETS  MINPODS  MAXPODS  REPLICAS  AGE
bash-loop-rc  bash-loop-rc  50%     4        6         4        16m
  1. 查看复制控制器的变化:
> kubectl get rc
NAME              DESIRED  CURRENT  READY    AGE
bash-loop-rc       4       4        4        21m

最初复制控制器设置为有三个副本,但自动缩放器的最小副本数为 4,现在复制控制器的期望副本数变为 4。如果平均 CPU 利用率超过 50%,副本数可能会增加到 5 甚至 6。
8. 再次查看 Pod 情况:

> kubectl get pods
NAME                READY   STATUS    RESTARTS   AGE
bash-loop-rc-8h59t   1/1     Running   0         21m
bash-loop-rc-gjv4k   1/1     Running   0         17m
bash-loop-rc-lsvtd    1/1    Running   0         21m
bash-loop-rc-z7wt5   1/1     Running   0         21m

可以看到由于自动缩放,创建了一个新的 17 分钟旧的 Pod。
9. 删除水平 Pod 自动缩放器:

> kubectl  delete hpa bash-loop-rc
horizontalpodautoscaler "bash-loop-rc" deleted
  1. 查看复制控制器:
> kubectl get rc
NAME              DESIRED   CURRENT   READY      AGE
bash-loop-rc       4           4       4         28m

可以看到复制控制器没有重置,即使自动缩放器已删除,仍然维护四个 Pod。

如果创建一个新的水平 Pod 自动缩放器,范围为 2 到 6,CPU 目标仍为 50%:

> kubectl autoscale rc bash-loop-rc --min=2 --max=6 --cpu-percent=50
replicationcontroller "bash-loop-rc" autoscaled

复制控制器仍然维护四个副本,因为这在范围内。但实际 CPU 利用率为零或接近零,副本数应该缩容到两个,但由于水平 Pod 自动缩放器没有从 Heapster 接收到 CPU 指标,它不知道需要缩容复制控制器中的副本数。

3. 带自动缩放的滚动更新

滚动更新是管理大型集群的基石。Kubernetes 在复制控制器级别和使用部署时支持滚动更新。使用复制控制器进行滚动更新与水平 Pod 自动缩放器不兼容,因为在滚动部署期间,会创建一个新的复制控制器,而水平 Pod 自动缩放器仍然绑定到旧的复制控制器。不幸的是,直观的 kubectl rolling-update 命令会触发复制控制器的滚动更新。

由于滚动更新是一项非常重要的功能,建议始终将水平 Pod 自动缩放器绑定到部署对象,而不是复制控制器或副本集。当水平 Pod 自动缩放器绑定到部署时,它可以设置部署规范中的副本数,并让部署处理必要的底层滚动更新和复制。

以下是一个用于部署 hue-reminders 服务的部署配置文件:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hue-reminders
spec:
  replicas: 2
  template:
    metadata:
      name: hue-reminders
      labels:
        app: hue-reminders
    spec:
      containers:
      - name: hue-reminders
        image: g1g1/hue-reminders:v2.2
        ports:
        - containerPort: 80

为了支持自动缩放并确保始终有 10 到 15 个实例运行,可以创建一个自动缩放器配置文件:

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: hue-reminders
  namespace: default
spec:
  maxReplicas: 15
  minReplicas: 10
  targetCPUUtilizationPercentage: 90
  scaleTargetRef:
    apiVersion: v1
    kind: Deployment
    name: hue-reminders

scaleTargetRef 字段的 kind 现在是 Deployment 而不是 ReplicationController ,这很重要,因为可能有同名的复制控制器。为了消除歧义并确保水平 Pod 自动缩放器绑定到正确的对象, kind name 必须匹配。

也可以使用 kubectl autoscale 命令:

> kubectl autoscale deployment hue-reminders --min=10 --max=15 --cpu-percent=90
4. 使用限制和配额处理稀缺资源

随着水平 Pod 自动缩放器动态创建 Pod,需要考虑资源管理。调度很容易失去控制,资源的低效使用是一个实际问题。有几个因素可能会以微妙的方式相互影响:
- 整体集群容量
- 每个节点的资源粒度
- 每个命名空间的工作负载划分
- DaemonSets
- StatefulSets
- 亲和性、反亲和性、污点和容忍度

Kubernetes 调度器在调度 Pod 时必须考虑所有这些因素。如果存在冲突或大量重叠的要求,Kubernetes 可能难以找到空间来调度新 Pod。例如,一个极端但简单的场景是,一个守护进程集在每个节点上运行一个需要 50% 可用内存的 Pod。现在,Kubernetes 无法调度任何需要超过 50% 内存的 Pod,因为守护进程集的 Pod 具有优先级。即使提供新节点,守护进程集也会立即占用一半的内存。

有状态集与守护进程集类似,它们需要新节点来扩展。添加新成员到有状态集的触发因素是数据增长,但影响是从 Kubernetes 可用的资源池中获取资源来调度其他成员。在多租户情况下,吵闹邻居问题可能会在资源配置或分配方面显现出来。你可能会在命名空间中精心规划不同 Pod 及其资源需求的精确配额,但实际上与其他命名空间的邻居共享节点,而你可能甚至无法了解他们的情况。

大多数这些问题可以通过明智地使用命名空间资源配额,并仔细管理跨多个资源类型(如 CPU、内存和存储)的集群容量来缓解。

4.1 启用资源配额

大多数 Kubernetes 发行版默认支持资源配额。API 服务器的 --admission-control 标志必须将 ResourceQuota 作为其参数之一。还需要创建一个 ResourceQuota 对象来强制执行。请注意,每个命名空间最多只能有一个 ResourceQuota 对象,以防止潜在的冲突,这由 Kubernetes 强制执行。

4.2 资源配额类型

可以管理和控制的配额有不同类型,类别包括计算、存储和对象。

综上所述,在 Kubernetes 中运行有状态应用以及处理自动缩放、滚动更新和资源配额等问题需要综合考虑多个因素。通过合理使用各种技术和配置,可以确保集群的高效运行和资源的有效利用。

在 Kubernetes 中运行有状态应用及相关特性

5. 资源配额的具体管理与应用

为了更好地理解资源配额的管理和应用,我们可以通过一个具体的示例来说明。假设我们有一个名为 production 的命名空间,我们希望对该命名空间下的资源进行配额管理。

首先,我们需要创建一个 ResourceQuota 对象。以下是一个示例配置文件:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    pods: "10"
    requests.cpu: "2"
    requests.memory: "2Gi"
    limits.cpu: "4"
    limits.memory: "4Gi"

在这个配置中:
- hard 字段定义了资源的硬限制。
- pods 指定了该命名空间下最多可以创建的 Pod 数量为 10 个。
- requests.cpu requests.memory 分别指定了所有 Pod 请求的 CPU 和内存总量的上限。这里请求的 CPU 总量不能超过 2 个核心,内存总量不能超过 2GB。
- limits.cpu limits.memory 分别指定了所有 Pod 可以使用的 CPU 和内存总量的上限。这里使用的 CPU 总量不能超过 4 个核心,内存总量不能超过 4GB。

创建这个资源配额对象的命令如下:

kubectl create -f production-quota.yaml

当我们在 production 命名空间下创建 Pod 时,Kubernetes 会检查是否满足资源配额的要求。如果不满足,Pod 将无法创建。

6. 资源管理的最佳实践

为了更有效地管理 Kubernetes 集群中的资源,我们可以遵循以下最佳实践:
1. 合理规划资源配额 :根据应用的实际需求,为每个命名空间设置合理的资源配额。避免过度分配或分配不足的情况。
2. 监控资源使用情况 :定期监控集群和命名空间的资源使用情况,及时发现资源瓶颈和浪费的情况。可以使用 Prometheus、Grafana 等工具进行监控和可视化。
3. 使用资源请求和限制 :在 Pod 的配置中,明确指定资源请求和限制。这样可以帮助 Kubernetes 更好地调度 Pod,并避免资源竞争。
4. 优化应用性能 :通过优化应用代码、调整应用配置等方式,提高应用的资源利用率,减少资源消耗。
5. 动态调整资源 :根据应用的负载情况,动态调整资源配额和 Pod 的副本数。可以使用水平 Pod 自动缩放器和垂直 Pod 自动缩放器来实现。

7. 总结与展望

在 Kubernetes 中运行有状态应用、实现自动缩放、进行滚动更新以及管理资源配额是构建高效、稳定的容器化应用集群的关键。通过合理使用各种技术和配置,我们可以充分发挥 Kubernetes 的优势,提高应用的可用性、可扩展性和资源利用率。

在未来,随着 Kubernetes 技术的不断发展,我们可以期待更多的功能和优化。例如,更智能的自动缩放算法、更灵活的资源管理策略、更好的多集群管理等。同时,我们也需要不断学习和实践,跟上技术的步伐,以应对不断变化的业务需求。

流程图:水平 Pod 自动缩放流程

graph LR
    A[开始] --> B[监控 Pod CPU 利用率]
    B --> C{CPU 利用率是否超过阈值}
    C -- 是 --> D[自动缩放器调整副本数]
    D --> E[复制控制器或部署更新 Pod 数量]
    E --> B
    C -- 否 --> B

表格:资源配额类型及说明

配额类型 说明
计算 包括 CPU、内存等计算资源的配额
存储 包括存储容量、存储卷数量等存储资源的配额
对象 包括 Pod、服务、副本集等 Kubernetes 对象的配额

通过以上内容,我们对在 Kubernetes 中运行有状态应用及相关特性有了更深入的了解。希望这些信息对你有所帮助,让你能够更好地管理和优化 Kubernetes 集群。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值