部署您的第一个Kubernetes应用程序时会错过五次

Aris-Dreamer的失败



许多人认为(将应用程序移植到Kubernetes上(使用Helm或手动)足够了,并且会很高兴)。但这不是那么简单。Mail.ru云解决方案



团队翻译了DevOps工程师Julian Guindi的文章。他谈到了自己的公司在迁移过程中面临的陷阱,这样您就不会再踩到同样的路了。



第一步:设置广告连播请求和限制



让我们首先设置一个干净的环境,在其中运行我们的pod。 Kubernetes非常擅长调度Pod和处理故障状态。但是事实证明,如果难以估算成功需要多少资源,计划者有时无法放置容器。这是资源请求和限制进入的地方。关于设置请求和限制的最佳方法存在很多争议。有时似乎它确实比艺术更是艺术。这是我们的方法。



Pod请求是调度程序用于最佳Pod放置的主要值。



Kubernetes: , . , PodFitsResources , .


我们使用应用程序请求,以便我们可以从中估计应用程序正常运行实际需要多少资源。这将使计划人员可以实际地放置节点。最初,我们希望以一定的余量来设置请求,以确保每个Pod都有足够的资源,但是我们注意到调度时间显着增加,并且某些Pod从未被完全调度,就好像没有针对它们的资源请求一样。



在这种情况下,调度程序通常会“挤压” pod,并且无法重新调度它们,因为控制平面不知道应用程序需要多少资源,这是调度算法的关键组成部分。



吊舱限额对广告连播的限制更为明确。它代表群集将分配给容器的最大资源量。



同样,根据官方文档:如果为容器设置了4 GiB内存限制,则kubelet(和容器运行时)将强制执行此操作。运行时会阻止容器使用超过指定资源限制的容器。例如,当容器中的进程尝试使用的内存量超出允许的数量时,内核会退出并显示“内存不足”(OOM)错误。


容器可以始终使用比资源请求中指定的资源更多的资源,但永远不能使用超过限制中指定的资源。很难正确设置该值,但是非常重要。



理想情况下,我们希望在整个过程的生命周期中更改Pod的资源要求,而又不干扰系统中的其他过程-这是设置限制的目标。



不幸的是,我无法就要设置的值给出具体说明,但是我们自己遵守以下规则:



  1. 使用负载测试工具,我们可以模拟基准流量并监视pod资源的使用情况(内存和处理器)。
  2. ( 5 ) . , , Go.


请注意,较高的资源约束使调度更加困难,因为Pod需要具有足够可用资源的目标节点。



设想一下您有一个轻量级的Web服务器,该服务器具有很高的资源限制,例如4 GB的内存。此过程可能需要水平扩展,并且每个新模块都必须在具有至少4 GB可用内存的节点上进行调度。如果不存在这样的节点,则群集必须引入一个新节点来处理此Pod,这可能需要一些时间。重要的是,请确保资源请求和限制之间的差异尽可能小,以确保快速平滑的扩展。



第二步:设置活动性和就绪性测试



这是Kubernetes社区中经常讨论的另一个微妙话题。充分了解活动性和就绪性测试非常重要,因为它们为软件提供了一种平稳运行并最大程度减少停机时间的机制。但是,如果配置不正确,它们会严重影响应用程序的性能。以下是两个示例的摘要。



活动性显示容器是否正在运行。如果失败,则kubelet将杀死该容器,并为其启用重新启动策略。如果容器未配备Liveness探针,则默认状态为成功-如Kubernetes文档中所述



活动探针应该便宜,即不消耗大量资源,因为它们经常运行,并且应该通知Kubernetes应用程序正在运行。



将其设置为每秒运行将每秒增加1个请求,因此请注意,将需要更多资源来处理此流量。



在我们公司,Liveness测试可以验证应用程序的主要组件,即使数据(例如,来自远程数据库或缓存的数据)不完全可用。



我们在应用程序中配置了一个“运行状况”终结点,该终结点仅返回200的响应代码。这表明进程已启动并正在运行,并且能够处理请求(但尚未处理流量)。准备



测试指示容器是否准备好处理请求。如果就绪探针失败,则端点控制器将从与该Pod匹配的所有服务的端点中删除Pod IP地址。 Kubernetes文档中也对此进行了说明。



准备就绪探针会消耗更多资源,因为它们必须以指示应用程序已准备好接受请求的方式进入后端。



关于是否直接进入数据库,社区存在很多争议。考虑到开销(检查经常执行,但是可以调整),我们决定对于某些应用程序,仅在检查了从数据库返回的记录之后,才对服务流量的可用性进行计数。精心设计的可用性探针可确保更高的可用性并消除部署期间的停机时间。



如果决定查询数据库以检查您的应用程序是否准备就绪,请确保它尽可能便宜。让我们进行如下查询:



SELECT small_item FROM table LIMIT 1


这是我们如何在Kubernetes中配置这两个值的示例:



livenessProbe: 
 httpGet:   
   path: /api/liveness    
   port: http 
readinessProbe:  
 httpGet:    
   path: /api/readiness    
   port: http  periodSeconds: 2


可以添加一些其他配置选项:



  • initialDelaySeconds -从容器开始到样本开始之间要经过多少秒。
  • periodSeconds — .
  • timeoutSeconds — , . -.
  • failureThreshold — , .
  • successThreshold — , ( , ).


:



Kubernetes具有“平坦”的网络拓扑,默认情况下,所有Pod都直接相互交互。在某些情况下,这是不希望的。



潜在的安全问题是,攻击者可能会使用单个易受攻击的应用程序将流量发送到网络上的所有Pod。与许多安全区域一样,最小特权原则也适用。理想情况下,网络策略应明确声明允许Pod之间的哪些连接以及不允许哪些连接。



例如,以下是一个简单的策略,该策略拒绝特定名称空间的所有入站流量:



---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:  
 name: default-deny-ingress
spec:  
 podSelector: {}  
 policyTypes:  
   - Ingress


此配置的可视化:





(https://miro.medium.com/max/875/1*-eiVw43azgzYzyN1th7cZg.gif)在此处有

更多详细信息



第四步:使用钩子和初始化容器的自定义行为



我们的主要目标之一是为Kubernetes提供部署,而不会导致开发人员停机。这很困难,因为有很多选项可以关闭应用程序并释放已用资源。Nginx



带来了特别的困难我们注意到,当顺序部署这些Pod时,活动连接在成功完成之前被丢弃。 在Internet上进行了广泛研究之后,事实证明Kubernetes在关闭Pod之前并不等待Nginx连接耗尽自身。借助pre-stop挂钩,我们实现了以下功能,并完全消除了停机时间:







lifecycle: 
 preStop:
   exec:
     command: ["/usr/local/bin/nginx-killer.sh"]


在这里nginx-killer.sh



#!/bin/bash
sleep 3
PID=$(cat /run/nginx.pid)
nginx -s quit
while [ -d /proc/$PID ]; do
   echo "Waiting while shutting down nginx..."
   sleep 10
done


另一个极其有用的范例是使用init容器来处理特定应用程序的启动。如果您有一个资源密集型数据库迁移过程需要在运行该应用程序之前启动,则此功能特别有用。您也可以为此过程指定更高的资源限制,而无需为主应用程序设置这样的限制。



另一种常见的方案是访问init容器中的机密,该容器将这些凭据提供给主模块,从而防止未经授权就从主应用程序模块本身访问机密。



, : init- , . , .


:



最后,让我们谈谈一种更高级的技术。



Kubernetes是一个非常灵活的平台,可让您按照自己认为合适的方式运行工作负载。我们有许多高效且资源密集的应用程序。通过广泛的负载测试,我们发现当Kubernetes默认设置生效时,其中一个应用程序很难处理预期的流量负载。



但是,Kubernetes允许您运行特权容器,该容器仅更改特定容器的内核参数。这是我们用来更改最大打开连接数的方法:



initContainers:
  - name: sysctl
     image: alpine:3.10
     securityContext:
         privileged: true
      command: ['sh', '-c', "sysctl -w net.core.somaxconn=32768"]


这是一种更高级的技术,通常是不必要的。但是,如果您的应用程序正在努力应对繁重的工作,则可以尝试调整其中一些参数。与官方文档中一样,有关此过程和设置各种值的更多详细信息



最后



尽管Kubernetes似乎是一种开箱即用的解决方案,但仍需要采取一些关键步骤来保持应用程序的平稳运行。



在迁移到Kubernetes的整个过程中,遵循“负载测试周期”很重要:运行应用程序,在负载下对其进行测试,观察指标和扩展行为,基于该数据调整配置,然后再次重复该周期。



现实地估计预期的流量,然后尝试超出预期范围,看看哪些组件首先中断。使用这种迭代方法,仅其中一些建议可能足以实现成功。或者,可能需要更深入的自定义。



总是问自己以下问题:



  1. ?
  2. ? ? ?
  3. ? , ?
  4. ? ? ?
  5. ? - , ?


Kubernetes提供了一个令人难以置信的平台,该平台支持最佳实践,可跨集群部署数千个服务。但是,所有应用程序都不同。有时实施需要更多的工作。



幸运的是,Kubernetes提供了必要的定制以满足所有技术目标。结合使用资源请求和限制,活动性和就绪性探针,初始化容器,网络策略和自定义内核调整,您可以实现高性能以及容错能力和快速可伸缩性。



还有什么要读的:



  1. 在生产环境中运行容器和Kubernetes的最佳实践和指南
  2. 90+ Kubernetes: , , , .
  3. Kubernetes .



All Articles