Kubernetes提示和技巧:节点分配和Web应用程序负载



在续写有关如何使Kubernetes的日常工作变得更轻松的实用文章时,我们讨论了操作世界中的两个故事:为特定任务分配单个节点以及为重负载配置php-fpm(或另一个应用程序服务器)。 和以前一样,此处描述的解决方案并不是理想的解决方案,但是可以作为您特定案例的起点和反思的基础。 欢迎提出问题和改进意见!

1.为特定任务分配单个节点


我们正在虚拟服务器,云或裸机服务器上建立一个Kubernetes集群。 如果将所有系统软件和客户端应用程序安装在同一节点上,则可能会出现问题:

  • 尽管客户端应用程序的限制非常高,但它会突然开始从内存中“泄漏”。
  • 对日志库,Prometheus或Ingress的复杂一次性请求导致OOM,导致客户端应用程序遭受苦难;
  • 尽管组件可能未在逻辑上相互连接,但由于系统软件中的错误而导致的内存泄漏会杀死客户端应用程序。

*除其他事项外,它与Ingress的较旧版本有关,由于大量的WebSocket连接和nginx的不断重载,出现了“悬挂nginx进程”,总数达数千个,并消耗了大量资源。

真正的案例是安装了具有大量指标的Prometheus,其中在查看“大量”仪表板时,该仪表板上显示了大量应用程序容器,并从每个图表中绘制了图表,内存消耗迅速增长到了约15 GB。 结果,OOM杀手可能会“出现”在主机系统上并开始杀死其他服务,从而导致“集群中应用程序的行为难以理解”。 而且由于客户端应用程序上的CPU负载很高,很容易获得不稳定的Ingress查询处理时间...

该解决方案很快提示自己:有必要为不同的任务分配单独的计算机。 我们确定了3种主要的任务组类型:

  1. Fronts ,我们仅放置Ingress,以确保没有其他服务可以影响请求的处理时间;
  2. 我们在其上部署VPNloghousePrometheus ,Dashboard,CoreDNS等的系统节点
  3. 应用程序的节点 -实际上是客户端应用程序推出的地方。 还可以为环境或功能分配它们:dev,prod,perf,...

解决方案


我们如何实现呢? 很简单:两种原生的Kubernetes机制。 第一个是nodeSelector ,它根据每个节点上安装的标签来选择应用程序应到达的所需节点。

假设我们有一个kube-system-1节点。 我们为其添加一个附加标签:

 $ kubectl label node kube-system-1 node-role/monitoring= 

...,然后在应该Deployment到该节点的Deployment ,我们编写:

 nodeSelector: node-role/monitoring: "" 

第二种机制是污点和宽容 。 在它的帮助下,我们明确指出在这些机器上,只能启动可以承受此异味的容器。

例如,有一台kube-frontend-1机器,我们只会在其上滚动Ingress。 将异味添加到此节点:

 $ kubectl taint node kube-frontend-1 node-role/frontend="":NoExecute 

...在Deployment我们创建容忍度:

 tolerations: - effect: NoExecute key: node-role/frontend 

对于kops,可以为相同的需求创建单个实例组:

 $ kops create ig --name cluster_name IG_NAME 

...,您会在kops中得到类似此实例组配置的内容:

 apiVersion: kops/v1alpha2 kind: InstanceGroup metadata: creationTimestamp: 2017-12-07T09:24:49Z labels: dedicated: monitoring kops.k8s.io/cluster: k-dev.k8s name: monitoring spec: image: kope.io/k8s-1.8-debian-jessie-amd64-hvm-ebs-2018-01-14 machineType: m4.4xlarge maxSize: 2 minSize: 2 nodeLabels: dedicated: monitoring role: Node subnets: - eu-central-1c taints: - dedicated=monitoring:NoSchedule 

因此,该实例组中的节点将自动添加其他标签和异味。

2.为重负载配置php-fpm


有许多用于运行Web应用程序的服务器:php-fpm,gunicorn等。 它们在Kubernetes中的使用意味着您应该始终考虑以下几点:

  • 必要大致了解我们愿意在每个容器的php-fpm中分配多少工人 。 例如,我们可以分配10个工人来处理传入的请求,为pod分配较少的资源,并使用pod的数量进行扩展-这是一个好习惯。 另一个例子是为每个吊舱分配500名工人,并在生产中拥有2-3个这样的吊舱...但这是一个非常糟糕的主意。
  • 需要进行活动/就绪测试以验证每个Pod的正确操作,以及由于网络问题或数据库访问(可能有任何选择和原因)Pod被“卡住”的情况。 在这种情况下,您需要重新创建有问题的窗格。
  • 重要的是,显式注册每个容器的请求并限制资源 ,以使应用程序不会“流动”并且不会开始损害此服务器上的所有服务。

解决方案


不幸的是, 没有灵丹妙药可以帮助您立即了解应用程序可能需要多少资源(CPU,RAM)。 一个可能的选择是观察资源消耗并每次选择最佳值。 为了避免不合理的OOM kill'ov和节流CPU,从而严重影响服务,可以提供:

  • 添加正确的活动/就绪测试,以便我们可以肯定地说此容器工作正常。 很有可能是一个服务页面,该页面检查所有基础结构元素的可用性(应用程序在pod中工作所必需),并返回200 OK响应代码;
  • 正确选择将处理请求并正确分发请求的工作人员数量。

例如,我们有10个Pod,由两个容器组成:nginx(用于向后端发送静态消息和代理请求)和php-fpm(实际上是后端,用于处理动态页面)。 php-fpm池配置为静态数量的工作程序(10)。 因此,在一个时间单位内,我们可以处理100个对后端的活动请求。 让每个请求在1秒内由PHP处理。

如果在一个特定的Pod中又有1个请求到达,现在正在积极处理10个请求,会发生什么情况? PHP将无法对其进行处理,并且如果它是GET请求,则Ingress会将其发送以重试到下一个pod。 如果有POST请求,它将返回一个错误。

并且如果考虑到在处理所有10个请求期间,我们将收到来自kubelet(活动性探针)的检查,它将以错误结尾,并且Kubernetes将开始认为此容器有问题并将其杀死。 在这种情况下,当前处理的所有请求都将以错误(!)结束,并且在重新启动容器时,它将失去平衡,这将导致对所有其他后端的请求增加。

明显地


假设我们有2个Pod,每个Pod配置了10个php-fpm worker。 这是一个在“停机时间”期间显示信息的图表,即 当唯一一个请求php-fpm的是php-fpm exporter时(我们每个都有一个工作线程):



现在以并发19启动启动:



现在让我们尝试使并发性超过我们可以处理的(20)...假设23。然后所有php-fpm工作人员都在忙于处理客户端请求:



Vorkers不再足以处理活动样本,因此我们在Kubernetes仪表板(或describe pod )中看到了这张图片:



现在,当其中一个Pod重新启动时, 会发生雪崩效应 :请求开始落在第二个Pod上,第二个Pod也无法对其进行处理,因此,我们从客户端收到了很多错误。 在所有容器的池都装满之后,提高服务水平是有问题的-只有通过大量增加吊舱或工人的数量才有可能。

第一选择


在使用PHP的容器中,您可以配置2个fpm池:一个用于处理客户端请求,另一个用于检查容器的“生存能力”。 然后在nginx容器上,您需要执行类似的配置:

  upstream backend { server 127.0.0.1:9000 max_fails=0; } upstream backend-status { server 127.0.0.1:9001 max_fails=0; } 

剩下的就是将活动性样本发送到上游,称为backend-status进行处理。

现在,活动性探针已单独处理,在某些客户端中仍然会发生错误,但是至少没有重新启动Pod和断开其余客户端的连接的问题。 因此,即使我们的后端无法应付当前的负载,我们也会大大减少错误的数量。

当然,此选项总比没有好,但是它也很糟糕,因为主池可能会发生某些事情,使用活动性测试我们将不知道。

第二选择


您还可以使用不太流行的nginx模块,称为nginx-limit-upstream 。 然后在PHP中,我们将指定11个worker,在具有nginx的容器中,我们将进行类似的配置:

  limit_upstream_zone limit 32m; upstream backend { server 127.0.0.1:9000 max_fails=0; limit_upstream_conn limit=10 zone=limit backlog=10 timeout=5s; } upstream backend-status { server 127.0.0.1:9000 max_fails=0; } 

在前端级别,nginx将限制将发送到后端的请求数(10)。 有趣的一点是,创建了一个特殊的积压:如果第11个对nginx的请求来自客户端,并且nginx看到php-fpm池正忙,则此请求将被放置在积压中5秒钟。 如果在此期间php-fpm尚未释放,则只有Ingress才会生效,这会将请求重试到另一个pod。 由于我们将始终有1个免费的PHP工人来处理活动样本,因此可以使图像变得平滑-我们可以避免雪崩效应。

其他想法


对于解决此问题的更多用途和美观选项,值得向Envoy及其类似物的方向看。

通常,为了使Prometheus能够清楚地雇用工人,这反过来将有助于快速发现问题(并通知问题),我强烈建议让现成的出口商将数据从软件转换为Prometheus格式。

聚苯乙烯


K8s提示和技巧周期中的其他内容:


另请参阅我们的博客:

Source: https://habr.com/ru/post/zh-CN432748/


All Articles