06 注意事项

0.1. 资源请求与限制

0.1.1. CPU

设置 CPU 请求有两种常见错误

  • 不设置
  • 设置的很低
# 不设置
resources: {}

# 设置过低
resources:
  requests:
    cpu: "1m"

即使节点的 CPU 没有充分利用,如果设置了不必要的 CPU 限制同样会限制 Pod,这也会导致延迟增加。

0.1.2. 内存

内存的过量使用一样会带来许多问题。

  • 达到 CPU 限制值会导致延迟
  • 达到内存限制值,Pod 会被直接杀死,这就像是 OOMkill(内存不足时会自动杀死进程的机制)

如果不想发生这样的事情,就不要过度使用内存,而应该使用 Guaranteed QoS,设置内存请求值等于限制值。

# Burstable QoS 下的资源设置
resources:
  requests:
    memory: "128Mi"
    cpu: "500m"
  limits:
    memory: "256Mi"
    cpu: 2

# Guaranteed QoS 下的资源设置
resources:
  requests:
    memory: "128Mi"
    cpu: 2
  limits:
    memory: "128Mi"
    cpu: 2

在设置资源时,可以使用 metrics-server 查看容器当前 CPU 和内存的使用情况。如果它已经在服务器端运行,可以运行以下命令:

kubectl top pods
kubectl top pods --containers
kubectl top nodes

通过当前使用情况,可以大致了解资源情况。

要及时查看情况指标,例如峰值 CPU 使用情况等,可以使用 PrometheusDataDog 等。它们会从 metrics-server 中获取指标并进行存储,然后可以查询或绘制图形。

VerticalPodAutoscaler 工具可以自动化地查看 CPU、内存的使用情况,并根据情况重新设置新的请求值和限制。与之对应的还有HPA

0.2. liveness和readiness探针

默认情况下,系统不会设置 livenessreadiness 探针。如果设置,那么这两种探针要在 Pod 整个生命周期中运行。

Kubernetes 强大的自愈能力可以让容器一直工作下去。但是:

  • 如果容器内的进程出现不可恢复的错误时,服务要如何重新启动?
  • 负载均衡器如何判断 Pod 是否可以开始处理流量,是否可以继续处理更多流量?

0.2.1. 区别

  • 如果对 Pod 的 liveness 探测失败,会重新启动该 Pod
  • 如果对 Pod 的 readiness 探测失败,会将 Pod 和 Kubernetes 断开连接(可以使用 kubectl get endpoints 进行检查),并且在下次探测成功之前,都不会发送流量

readiness 探测成功告知 Kubernetes 服务 Pod 就绪,可以开始为流量提供服务。

在 Pod 的生命周期内,Pod 有没有因为太“热”而无法处理过多的流量,需要减少工作“冷静”一下。直到 readiness 探测成功时,再继续给 Pod 发送更多流量。

在这种情况下, liveness 探测失败就会适得其反,因为不需要重新启动这个运行状况良好的 Pod。

有时候,不配置任何探针会比配置错误探针要好。

如果将 liveness 探针配置成和 readiness 探针一样,那么会导致很多问题。

一开始建议仅配置 readiness 探针,因为 liveness 探针很危险。

如果有一个和其他 Pod 有共享依赖项的 Pod 被关闭,就要保证这个 Pod 的任何一个探针都不能失败,否则将导致所有 Pod 的级联失效。

探针使用

探测方式

Liveness 指针和 Readiness 指针支持三种不同的探测方式:

  • 第一种是 httpGet:通过发送 http Get 请求来进行判断的,当返回码是 200-399 之间的状态码时,标识容器健康的
  • 第二种探测方式是 Exec:通过执行容器中的一个命令来判断当前的服务是否是正常的,当命令行的返回结果是 0,标识容器是健康的
  • 第三种探测方式是 tcpSocket:通过探测容器的 IP 和 Port 进行 TCP 健康检查,如果这个 TCP 的连接能够正常被建立,标识容器是健康的
livenessProbe:
  httpGet:
    path:/healthz
    port: 8080
---
livenessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
# 解释性脚本比较慢,编译性脚本快
---
livenessProbe:
  tcpSocket:
    port: 8080
# 如果遇到 TLS 的服务,可能会造成 TLS 里面有很多未健全的 tcp connection

全局参数:

参数名作用例子
initialDelaySecondspod 启动延迟多久进行一次检查比如说现在有一个 Java 的应用,它启动的时间可能会比较长,因为涉及到 jvm 的启动,包括 Java 自身 jar 的加载。所以前期可能有一段时间是没有办法被检测的,而这个时间又是可预期的,那这时可能要设置一下 initialDelaySeconds
periodSeconds检测的时间间隔正常默认的这个值是 10 秒
timeoutSeconds检测的超时时间当超时时间之内没有检测成功,会认为是失败的一个状态
successThresholdpod 从探测失败到再一次判断探测成功,所需要的阈值次数默认情况下是 1 次,表示原本是失败的,那接下来探测这一次成功了,就会认为这个 pod 是处在一个探针状态正常的一个状态
failureThreshold探测失败的重试次数默认值是 3,表示的是当从一个健康的状态连续探测 3 次失败,那此时会判断当前这个pod的状态处在一个失败的状态

探测结果

从探测结果来讲主要分为三种:

  • 第一种是 success:表示容器通过了健康检查,Liveness probe 或 Readiness probe 是正常状态
  • 第二种是 Failure:表示容器没有通过健康检查,就会在service层进行相应的处理:
    • 将没有通过 Readiness 的 pod 进行摘除
    • 将没有通过 Liveness 的 pod 进行重新拉起,或者是删除
  • 第三种状态是 Unknown:表示当前的执行机制没有进行完整的一个执行操作,可能是超时或者脚本没有及时返回,此时 Readiness probe 或 Liveness probe 不做任何的一个操作,等待下一次的机制来进行检验

在 kubelet 里面有一个叫 ProbeManager 的组件,这个组件里面会包含 Liveness probe 或 Readiness probe,这两个 probe 会将相应的 Liveness 诊断和 Readiness 诊断作用在 pod 之上,来实现一个具体的判断。

应用场景

  • liveness:支持重新拉起的应用
  • readness:启动后无法立即对外服务的应用

0.3. HTTP服务的负载均衡器

集群内的(微)服务可以通过 ClusterIP 服务和 DNS Service Discovery 进行通信。注意不要使用公共 DNS/IP,这会影响延迟并增加云成本。

  1. type: LoadBalancer:提供并配置一个外部负载均衡器(L7/L4),这些资源(外部静态IPv4,硬件)可能会变得很贵
  2. type: NodePort:部署一个边缘路由器(Nginx-Ingress-Controller/traefik)作为暴露给外部负载均衡去的单个NodePort endpoint,并根据 Kubernetes ingress resource 配置在集群中分配路由流量。

所有流量都在集群内路由到 NodePort 服务上,该服务默认 externalTrafficPolicy: Cluster,这意味着集群中的每个节点都打开了 NodePort ,这样可以使用任何一个与所需的服务(一组 Pod)进行通信。

通常,NodePort 服务为针对的 Pod 仅运行在那些节点的子集上。这意味着,如果与未运行 Pod 的节点通信,它会将流量转发到另一个节点,从而导致额外的网络跳转并增加延迟。

在 Kubernetes 服务上设置 externalTrafficPolicy: Local 后就不会在每个节点上打开 NodePort,只会在实际运行 Pod 的节点上打开。

如果使用外部负载均衡器对其终端节点进行检查,它会仅将流量发送到应该去往的那些节点,可以改善延迟并减少计算开销和出口成本。

0.4. 设置Pod亲和性

应该明确定义Pod的亲和性,这样可以确保将 Pod 调度在不同的节点上(仅在调度期间,而不是在执行期间进行检查,因此要设置 requiredDuringSchedulingIgnoredDuringExecution)。

# omitted for brevity
  labels:
    app: zk
# omitted for brevity
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
          matchExpressions:
            - key: "app"
              operator: In
              values:
              - zk

0.5. 设置PodDisruptionBudget

在 Kubernetes 上运行生产工作负载时,节点和集群必须不时地升级或停用。PodDisruptionBudget(PDB)是一种 API,为集群管理员和集群用户提供服务保证。

确保创建 PDB 以避免由于节点停用而造成不必要的服务中断。

# 保证ZK至少2个
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeepe
上次修改: 15 July 2020