Kubernetes

06 注意事项 阅读更多

0.1. 资源请求与限制 0.1.1. CPU 0.1.2. 内存 0.2. liveness和readiness探针 0.2.1. 区别 探针使用 探测方式 探测结果 应用场景 0.3. HTTP服务的负载均衡器 0.4. 设置Pod亲和性 0.5. 设置PodDisruptionBudget 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 使用情况等,可以使用 Prometheus、DataDog 等。它们会从 metrics-server 中获取指标并进行存储,然后可以查询或绘制图形。 VerticalPodAutoscaler 工具可以自动化地查看 CPU、内存的使用情况,并根据情况重新设置新的请求值和限制。与之对应的还有HPA。 0.2. liveness和readiness探针 默认情况下,系统不会设置 liveness 和 readiness 探针。如果设置,那么这两种探针要在 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 全局参数: 参数名 作用 例子 initialDelaySeconds pod 启动延迟多久进行一次检查 比如说现在有一个 Java 的应用,它启动的时间可能会比较长,因为涉及到 jvm 的启动,包括 Java 自身 jar 的加载。所以前期可能有一段时间是没有办法被检测的,而这个时间又是可预期的,那这时可能要设置一下 initialDelaySeconds periodSeconds 检测的时间间隔 正常默认的这个值是 10 秒 timeoutSeconds 检测的超时时间 当超时时间之内没有检测成功,会认为是失败的一个状态 successThreshold pod 从探测失败到再一次判断探测成功,所需要的阈值次数 默认情况下是 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,这会影响延迟并增加云成本。 type: LoadBalancer:提供并配置一个外部负载均衡器(L7/L4),这些资源(外部静态IPv4,硬件)可能会变得很贵 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

05 HPA 阅读更多

简介 Pod 水平自动伸缩(Horizontal Pod Autoscaler)特性, 可以基于: CPU利用率自动伸缩如下API对象中的Pod数量: replication controller deployment replica set 其他应程序提供的度量指标custom metrics HPA无法缩放的对象,比如DaemonSets。 HPA由Kubernetes API资源和控制器实现,控制器会周期性的获取平均CPU利用率,并与目标值相比来调整replication controller或者deployment中副本的数量。 controller manager的--horizontal-pod-autoscaler-sync-period参数指定周期(默认值15秒),从resource metrics API和custom metrics API获取指标。 通常情况下,控制器将从一系列的聚合 API 中获取指标数据: metrics.k8s.io:由 metrics-server(需要额外启动)提供 custom.metrics.k8s.io external.metrics.k8s.io 控制器也可以直接从 Heapster 获取指标。 注意:FEATURE STATE: Kubernetes 1.11 [deprecated] 自 Kubernetes 1.11起,从 Heapster 获取指标特性已废弃。 HPA的扩容算法:期望副本数 = ceil[当前副本数 * ( 当前指标 / 期望指标 )] 例如,CPU利用率: 当前指标为200m,目标设定值为100m,那么由于200.0/100.0=2.0, 副本数量将会翻倍。 当前指标为50m,副本数量将会减半,因为50.0/100.0=0.5。 如果计算出的缩放比例接近1.0(跟据--horizontal-pod-autoscaler-tolerance参数全局配置的容忍值,默认为0.1), 将会放弃本次缩放。 自定义指标API 2018年1月22日,新的指标监控目标是暴露一个API可以让HPA用来获取任意的指标,就像Master的指标API,这个新的API围绕获取的指标构建,这些指标通过引用Kubernetes对象(或组)和指标名称,因此,这些API对于其他的想要消费自定义指标的消费者(尤其是控制器)将很有用。 API的根路径类似这样:/apis/custom-metrics/v1alpha1,方便起见,下面省略相同的根路径: 检索给定名称的全局对象的指标(如,Node、PersistentVolune):/{object-type}/{object-name}/{metric-name...} 检索给定类型的全局对象的指标:/{object-type}/*/{metric-name...} 检索给定标签匹配的给定类型的全局对象的指标:/{object-type}/*/{metric-name...}?labelSelector=foo 检索给定命名空间中的对象的指标:/namespaces/{namespace-name}/{object-type}/{object-name}/{metric-name...} 检索给定类型的所有命名空间对象的指标:/namespaces/{namespace-name}/{object-type}/*/{metric-name...} 检索与给定标签匹配的给定类型的所有命名空间对象的指标:/namespaces/{namespace-name}/{object-type}/*/{metric-name...}?labelSelector=foo 检索描述给定名称空间的指标:/namespaces/{namespace-name}/metrics/{metric-name} metrics-server Metrics Server是集群范围的资源使用情况数据聚合器。 如果使用kube-up.sh脚本部署集群,则会默认部署为Deployment 如果使用其他方式部署,可以使用[deployment components.yaml](https://github.com/kubernetes-sigs/metrics-server/releases)文件来部署 Metrics Server从Summary API中收集指标数据,这些指标数据是每个节点上的kubelet暴露出来的。

04 Session Sticky 阅读更多

当运行多个Pod后,可以通过Service进行负载均衡,默认的方式是RoundRobin。Service官方文档点这里。 问题 当Pod中运行的服务与会话有关的时候,也就是要确保每次都将来自特定客户端的连接传递到同一Pod。 解决方案 第一种情况 如果对集群外暴露的服务只使用了Service,那么使用Kubernetes的Service提供的service.spec.sessionAffinity进行控制: None:默认值 ClientIP:基于客户端的IP地址选择会话关联 设置了上面的参数之后,通过适当设置 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 来设置最大会话停留时间,(默认值为 10800 秒,即 3 小时)。 这种情况一般使用: NodePort HostNetwork 第二种情况 使用ingress等,将Service暴露到集群外,下面以Traefik为例。 其他边缘路由不确定,Traefik会影响Service的参数配置。也就是说在Traefik不设置sticky session设置的情况下,Service设置了sessionAffinity也不会生效。 Traefik的设置,这里以Traefik 2.2.1为例 apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: my-service-ingressroute namespace: othernamespace spec: entryPoints: - websecure routes: - match: Host(`my-service.mydomain.com`) kind: Rule services: - name: my-service port: 80 sticky: cookie: httpOnly: true Traefik官方文档中关于sticky session的说明点这里。 总结 出现上述两种解决方案的原因是,通常情况下,实现会话粘滞的负载均衡是基于哈系算法实现的,主要的做法是对客户端 IP 地址或者会话 ID 计算哈希值,将取得的哈希值与服务器列表的大小进行取模运算,最终得到的值就是应该被路由到的服务器编号。 在第一种方案中,使用的是kubernetes自带的service对象,集群自身知道后端运行的pod数量,即服务器列表的大小。 在第二种方案中,使用的是第三方的组件,负载均衡发生在service之前,因此需要让第三方组件知道服务器列表的大小,从而实现客户端与服务端的映射。

03 证书 阅读更多

问题 突然,整个集群的都失联了,api-server无法访问,发现api-server的容器异常退出了。 systemctl restart kubelet 执行上述命令,发现查看api-server容器的日志,发现不能访问etcd,再查看etcd容器的日志,发现是证书的问题。 kubeadm alpha certs check-expiration CERTIFICATE EXPIRES RESIDUAL TIME EXTERNALLY MANAGED admin.conf May 15, 2020 13:03 UTC 364d false apiserver May 15, 2020 13:00 UTC 364d false apiserver-etcd-client May 15, 2020 13:00 UTC 364d false apiserver-kubelet-client May 15, 2020 13:00 UTC 364d false controller-manager.conf May 15, 2020 13:03 UTC 364d false etcd-healthcheck-client May 15, 2020 13:00 UTC 364d false etcd-peer May 15, 2020 13:00 UTC 364d false etcd-server May 15, 2020 13:00 UTC 364d false front-proxy-client May 15, 2020 13:00 UTC 364d false scheduler.conf May 15, 2020 13:03 UTC 364d false 执行上述命令,查看集群的证书是否过期。 这些指令根据kubectl版本的不同,而不同的,根据具体的cli提示进行操作。 可能会遇到kubeadm没有上述指令的情况,执行下面这个指令来查看但个证书文件是否过期。 openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text |grep ' Not 'kubeadm alpha certs renew all --config kubeadm.yaml # kubeadm.yaml是使用kubeadm安装时候的配置文件 执行上面的指令更新全部的证书,然后在重启kubelet,这样会触发static pod的重启。 或者执行mv /etc/kubernetes/manifests/ /etc/kubernetes/manifests.bak/ 然后再修改回来,也可以触发static pod的重启。 更多关于kubeadm更新证书的操作,查看官网的介绍。 然后更新kubeconfig,因为kubectl是通过这个文件和api-server通信的,具体有两种方式。 方式一 kubectl config set-credentials admin \ --certificate-authority=$SSL_PATH/ca.crt \ --client-certificate=$SSL_PATH/apiserver-kubelet-client.crt \ --client-key=$SSL_PATH/apiserver-kubelet-client.key 执行只更新通信过程中认证证书相关的部分。 方式二 将kubernetes平台相关组件的配置文件根据新的证书全部重新生成。 # 一个个重新生成 kubeadm alpha kubeconfig user --client-name=admin kubeadm alpha kubeconfig user --org system:masters --client-name kubernetes-admin > /etc/kubernetes/admin.conf kubeadm alpha kubeconfig user --client-name system:kube-controller-manager > /etc/kubernetes/controller-manager.conf kubeadm alpha kubeconfig user --org system:nodes --client-name system:node:$(hostname) > /etc/kubernetes/kubelet.conf kubeadm alpha kubeconfig user --client-name system:kube-scheduler > /etc/kubernetes/scheduler.conf # 或者执行这个命令,需要先把/etc/kubernetes/*.conf文件备份起来,否则会直接读取原文件而不生成新的 kubeadm init phase kubeconfig all --config kubeadm.yaml 重新生产的admin.conf就是kubectl与api-server通信用的配置文件,然后把这个文件复制到~/.kube并且重命名为config。 关于kubeadm alpha指令更多的介绍,查看官网。 升级集群更新证书 kubeadm upgrade apply --certificate-renewal v1.15.0 更多内容查看这里。

02 Troubleshooting 阅读更多

这里的文章都挺好的。 kubernetes平台中遇到问题,没有思路的时候,按照这个流程来排除,高效。

01-logs 阅读更多

问题 failed to create fsnotify watcher: too many open files 这是因为系统默认的fs.inotify.max_user_instances=128太小,重新设置此值: sudo sysctl fs.inotify.max_user_instances=8192 原理