记录一次CI/CD pipline流程慢问题优化

一、背景

在项目中研发反馈在dev和test k8s集群中部署服务比较慢,等待时间比较久。下图列举一个占用时间较多的例子,大多数情况下需要15-18分钟部署后端服务。

不用纠结图片中的用时,下图情况由于公司网络调整,比一般情况会差一点,图片中的情况也比较少见。

按照15-18分钟一次发布,尤其是dev和test环境如果遇到哪一天发布次数较多,或者改动bug较多的时候,需要发布好几次,那么对于效率的影响很大。

二、原因分析

通过查看发布较慢的CI/CD pipline来看,占用时间较多的阶段分别是克隆代码,构建镜像,Argo部署。

克隆代码/构建镜像阶段分析

  1. 通过分析克隆代码和构建镜像阶段日志发现时间都是花费在文件传输上面。由于gitlab和harbo没有和droneCI在同一个网段。初步推测可能是带宽不足造成的问题。

查看监控以及带宽配置发现,gitlab和harbo机器使用的是同一个公网ip,查看公网ip监控发现,公网ip 5M的带宽被打满。因此导致网络下载代码以及上传推送镜像速度变慢导致时间增大(监控截图没有保留已找不到,无法提供截图)。

  1. 另外发现在克隆代码阶段会拉去所有分支代码,也会造成不必要的时间和网络开销。

argo部署阶段分析

根据日志输出可以看到argo部署阶段,文件修改以及其他操作都比较快可以执行完成,但是在触发argo更新k8s资源的grpc请求之后,使用时间较多。

由于集群状态运行正常,根据结果初步断定可能和镜像拉取速度(harbo),以及pod的健康检查配置有关导致服务启动速度较慢。

由于harbo已经确定遇到带宽不足的问题,因此会导致部署变慢,另外查看gitops仓库查看deployment配置发现。大多数项目配置的监控检查startupProbe的initialDelaySeconds时间都在180或者240秒。另外有些项目的readness的initialDelaySeconds时间在60秒左右。

因此大多数项目即使在拉取镜像完成之后,也要等待180/240秒的健康检查时间之后才能提供服务,配置有readness参数的服务可能需要的时间更久。

参数配置如下

yaml 复制代码
    startupProbe:
        httpGet:
          path: /actuator/health
          port: 8011
        initialDelaySeconds: 240
        periodSeconds: 30
        timeoutSeconds: 10
        failureThreshold: 20
    livenessProbe:
      failureThreshold: 3
      httpGet:
        path: /actuator/health
        port: 8011
        scheme: HTTP
      initialDelaySeconds: 60
      periodSeconds: 30
      successThreshold: 1
      timeoutSeconds: 120          
    readinessProbe:
      failureThreshold: 3
      httpGet:
        path: /actuator/health
        port: 8011
        scheme: HTTP
      initialDelaySeconds: 20
      periodSeconds: 10
      successThreshold: 1
      timeoutSeconds: 120

三、结合st-ah服务查看问题

以上信息已经讲述了CI/CD pipline慢的问题,对于代码下载和镜像推送上面的图片已经可以表达清楚,不做过多解释了。

服务健康检查参数配置不合理的等待可以结合st-ah的启动日志来分析。

根据配置的参数来看,服务在启动之后,即使第一次健康检查就成功,也需要等待startupProbe和readiness两个探针的initialDelaySeconds的和才能正常处理请求,至少需要4分钟才能处理请求。

通过下面的启动日志也验证看推测是否正确。

  • 在15:19:48秒服务开始启动输出第一行日志,到15:22:56服务接受到处理第一个http请求开始处理。基本上符合startupProbe探针initialDelaySeconds=180秒之后开始第一次健康检查。
  • 在startupProbe检查通过后,等到60秒开始进行就绪检查,正式接入流量处理。我们可以看到从15:24:12秒开始处理外部请求。从15:22:56到15:24:12差不多也符合readiness探针initialDelaySeconds=60秒之后进行健康检查,健康检查通过开始处理请求。

以上两个步骤时间不是完整的180秒和60秒是正常情况,因为k8s在到时间阈值之后开始触发第一次检查,到请求处理的过程中本身也会有一定耗时。

服务启动打印首行启动日志 服务启动完成

通过分析日志之后可以发现,服务从启动到正式处理流量所需时间为4分24秒,和结果跟之前想象的一致所需时间在4分钟以上。

四、k8s集群健康检查

k8s健康检查参数解析

五、调整

经过分析下来可以知道造成CI/CD时间较长的问题主要由以下原因造成。

  • 带宽不足
  • 检出分支过多
  • deployment健康检查配置时间不合理

针对以上问题分别进行调整处理。

  1. 针对带宽调整配置到15M速度可以得到一定保障,后续如果再遇到带宽不足在进行临时调整,基本上日常需要15M带宽确保CI/CD拉取代码和推送镜像的效率。
  2. 针对检出分支过多在git clone时添加branch参数限制
  1. 针对健康检查的配置,通过分析下来将
  • startupProbe:调整initialDelaySeconds=60秒,periodSeconds=30秒,timeoutSeconds=10秒,failureThreshold=10。调整之后即使initialDelaySeconds之后第一次检查失败没有启动完成,按照参数配置来看还有400秒足够服务启动。

间隔30秒跑一次,timeout10秒,共10次机会(30+10)*10=400秒。总时间约等于(30+10)*10 + 60=460秒,时间足够服务正常启动。

  • readness和liveness:调整initialDelaySeconds=10秒,periodSeconds=30秒,timeoutSeconds=10秒,failureThreshold=10。
yaml 复制代码
startupProbe:
  httpGet:
    path: /actuator/health
    port: 8012
    scheme: HTTP
  initialDelaySeconds: 60
  periodSeconds: 30
  successThreshold: 1
  timeoutSeconds: 10
  failureThreshold: 10
livenessProbe:
  httpGet:
    path: /actuator/health
    port: 8012
    scheme: HTTP
  initialDelaySeconds: 10
  periodSeconds: 30
  successThreshold: 1
  timeoutSeconds: 10
  failureThreshold: 10
readinessProbe:
  httpGet:
    path: /actuator/health
    port: 8012
    scheme: HTTP
  initialDelaySeconds: 10
  periodSeconds: 30
  successThreshold: 1
  timeoutSeconds: 10
  failureThreshold: 10

经过本次调整如果服务正常启动,将不会占用额外的时间等待集群健康检查。一般情况下服务正常启动在2-3分钟,完全可以包含在startupProbe的检查时间内,并且会高于startupProbe的initialDelaySeconds时间,没有不必要的等待时间。

liveness和readiness将会在startupProbe完成之后10秒开始健康检查,正常情况下也是没有问题的。

参数调整后,通过日志可以看到服务从18:09:08开始启动,到18:11:25秒服务初始化完成,共用时2分钟17秒。比之前服务启动节省近2分钟。

首行日志 服务启动完成

六、总结

本次越到的CI/CD pipline执行慢的问题,除去带宽物理层面的问题,另外主要在于细节的控制不够导致出现CI/CD执行流程时间较长,影响部署时间。

  • 对于CI/CD检出代码只需要处理当前环境分支即可。
  • k8s集群配置参数不可以一蹴而就想当然配置,需要结合容器的整体启动状况来定义在一个合理的区间,这样既能避免无效的重启和不必要的等待,可能确保利用到集群提供的探针来管理服务的重启策略,以及健康状态检测。
相关推荐
陈随易3 分钟前
兔小巢收费引发的论坛调研Node和Deno有感
前端·后端·程序员
聪明的墨菲特i8 分钟前
Django前后端分离基本流程
后端·python·django·web3
hlsd#1 小时前
go mod 依赖管理
开发语言·后端·golang
陈大爷(有低保)1 小时前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、1 小时前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头1 小时前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
阿华的代码王国2 小时前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
小码编匠2 小时前
领域驱动设计(DDD)要点及C#示例
后端·c#·领域驱动设计