暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片
容器云平台的性能瓶颈及性能调优
1229
17页
5次
2020-07-10
5墨值下载
运维技术岗岗位学习教程
容器云平台的性能瓶颈及
性能调优
课程出品人:顾黄亮
苏宁消费金融
安全运维部负责人
1 前言
前言:本章节依据《容器云平台架构分析》和《容器云平台高可用设计》中的架构和高可用知识点进行
展开,其中容器云平台是以docker+kubernetes为代表的通用性容器云平台。随着容器技术的发展,容器服
务已成为很多公司的选择,然而在实际运用中,需要在各类环境中成功部署容器和操作容器,需要容器引擎
和编排引擎的通力合作,这就需要容器云平台架构的合理性和各组件之间良好的性能支撑能力。容器云平台
作为应用的底层支撑平台,一个应用在上线之前会进行容量规划、压力测试和上线后的验证,因此性能瓶颈
是制约应用发挥作用的一个重要因素,性能调优也是容量规划和验证结果之间出现偏差的进行的必然手段。
本章节将重点介绍基于docker+kubernetes的通用性容器云平台性能瓶颈和性能调优的方式和方法。
2 容器引擎docker的性能瓶颈和调优
docker容器承载于容器引擎之上,对于开发者和容器云平台管理员来说,统一标准的包格式简化了二
者的工作量,而容器格式让使用者加快速迭代应用程序的版本并进行管理。开发、测试、部署的时间也得
到降低,这是容器引擎的最核心功能。因此,容器引擎的性能优势和瓶颈基于容器的轻量化和启动速度来
分析,主要涉及的组件为docker容器和镜像仓库。在对容器引擎的性能瓶颈分析方面,基于以下几点来考
虑,(1)容器引擎的稳定性,在《容器云平台架构分析》和《容器云平台可靠性设计》中已体现,在此不
再阐述。(2)镜像仓库的瘦身和启动速度,在实际使用过程中,由于经常会下载应用程序所对应的镜像运
行库,使得开发的时间在增加,而docker hub的速度拖慢了部署时间,造成部署时间的偏差。而docker hub
的不稳定因素也会导致部署时间的不确定性,另docker镜像的大小存在超大的行为,容器更新的时间也将
不受控制。基于以上的因素,本小节基于docker容器引擎的性能瓶颈和优化集中于以下三点,(1)镜像的
部署时间,(2)镜像的编译时间,(3)镜像的大小。
2.1 镜像的部署时间
在使用容器云平台过程中,存在一种情况,构建的docker容器尺寸变得越来越大,在现有的docker宿
主机中运行的容器是可以避免此类问题。docker会合理利用docker镜像层,这些镜像层会随着所部署的应用
的增长而随之创建。但是有一种场景,当我们准备水平扩展应用的时候,这些应用所承载的docker容器会越
来越多的需要docker宿主机进行支撑,因此每一个新的docker宿主机需要下载我们所需要的镜像层。本小节
会举例复现一个大的docker应用如何影响docker宿主机上的部署时间。首先我们来复现这个docker应用的
场景问题,如下所示。
(1)编写一个dockerfile来创建一个大docker镜像
from debian:iessie
run dd if=/dev/urandom of=/largefile bs=1024 count=524288
(2)编译dockerfile ,并对hubuser/largeapp打标签,具体命令如下
dockerhost$ docker build –t hubuser/largeapp
(3)检查这个docker容器的尺寸,从执行后的输出看出,这个docker容器占用了接近700m的空间,
具体命令如下
dockerhost$ docker images
(4)通过time命令来查看这个docker镜像上传到docker hub和从docker hub拉取的时间,具体命令如下
docker $ time docker push hubuser/largeapp
docker $ time docker pull hubuser/largeapp
从二者执行的时间上可以看出,当执行docker push命令时,上传镜像到docker hub花费了大量的时间,
同样的在部署的过程中,docker pull拉取我们新建的镜像到生产环境的docker宿主机上同样花费了很长的时
间。所以上传和下载的时间取决于docker宿主机到docker hub之间的网络环境和网络带宽,如果docker hub
出现异常,对部署新的docker容器存在很大的时效调整,有可能会引发部署失败。在容器云平台架构的介绍
中,我们为了利用docker的快速分发和便捷部署的特性,上传和下载镜像的稳定性是我们需要解决的需求。
在本章中,我们通过docker容器镜像组件registry来解决这个性能瓶颈,docker registry用于存储和分发docker
镜像,无需依赖公共的docker hub服务,具体步骤如下。
(1)部署docker registry私有仓库,具体架构不在本小节中阐述。
(2)确认docker镜像部署的速度是否已经提升,首先,将先前创建的镜像打上标签,用于推送至本地
私有仓库。
(3)实测docker镜像到本地仓库的上传的速率,实测结果为docker hub的十倍。
(4)实测docker镜像到本地仓库的上传的速率,实测结果为docker hub的三十倍。
2.2 镜像的编译时间
docker镜像是容器云平台的重要组成构建,也是性能瓶颈的优先考虑的点,因此简化docker文件和加
速容器技术至关重要,可以让我们更快速的迭代应用开发。一旦编译docker镜像所需要的时间存在过长或不
受控制,那么使用容器云平台的优势将打很大的折扣。本小节中,我们将以几个维度来分析构建docker镜像
中耗时过长的问题,来优化此类问题的瓶颈。
(1)通过本地化仓库来提升速度
在提升镜像部署时间的段落中,我们已经采用了本地化仓库的方式,避免从docker hub中上传和下载镜
像,速度有很大的提升,在构建镜像的过程中,同样的需要花费较多的时间在获取上游镜像的过程中。当我
们使用一台新的docker宿主机,通过docker hub进行下载镜像进行编译,整体耗时将会显著的增加,因此通
过本地化仓库也能缩短镜像编译的整体耗时,通常运用传递--registry-mirror的参数的方式给予docker的守
护进程,用来配置docker宿主机寻找本地的镜像仓库,大概步骤如下。
在任意一台docker宿主机中,通过更新或创建一个systemd的插件文件来配置docker守护进程,文件核
心配置参数为execstart=/usr/bin/docker daemon –h df://\--registry-mirror=http://dockerhost:5000。
接着上一步骤,重启systemd来加载socker service的新配置文件,重启新配置的systemd单元来重启
docker守护进程,两个命令如下。
dockerhost$ systemctl daemon-reload
dockerhost$ systemctl restartdocker-service
最后运行作为进行的registry的docker容器,确保该进行registry正常工作。下面开始验证二者的速度区
别,先编译一个简单的dockerfile,通过docker hub进行下载上游镜像,可以发现编译镜像的大部分时间在
下载进行部分,结果如下。
移除该进行以及上游的依赖进行,重新进行编译,结果如下
(2)复用镜像层来提升整体的编译速度
根据《容器云平台架构》一章节中得知,docker镜像是由一系列的层合并而成,所使用的是一个单一的
镜像联合文件系统。当我们在构建docker镜像的时候,docker会检查dockerfile中正在处理的指令,判断缓
存中是否存在可以复用的镜像,而不是一味的重复创建同一个镜像,因此复用镜像层也是提升镜像编译能力
之一,提高创建docker镜像的速度。
(3)减小构建上下文的大小
在容器云自动化集成的过程中,持续集成是核心的环节,基于代码的版本控制中,通过dickerfile来定义
代码的基本属性。某种程度上,以git为例,当项目代码的git文件占有太多磁盘空间的时候,可能存在代码
提交过多,或者代码引用太多的情况,具体方法如下。
dockerhost$ du –hsc .git
当编译docker应用时,如果git文件占用太多磁盘空间,造成代码空间较大的情况下,编译docker进行
的时间也会非常大,间接造成整体编译的时间拉长,具体查看方法如下。
dockerhost$ time docker build –t hubuser/largecontext
通过这个案例我们来回溯,docker客户端上传了整个项目git文件夹,而仅仅是因为它在镜像的编译路
径下,这种情况针对于开发能力不是很强的同学来说,轻易会造成这种局面,所以在编译docker镜像的过程
中,docker守护经常需要花费较多的时间来接收相关的信息。由于很多文件对于编译过程是无用的,尤其是
在编译该应用程序的docker镜像阶段,所以这些文件对于在生产环境中运行的应用程序也是无用的。所以我
们在编译docker镜像之前可以设定忽略这些无用文件,也就是通过减少上下文的大小来达到优化编译过程的
性能,操作方法如下。
在dockerfile所在目录下创建一个.dockerignore文件,文件中包括.git的内容。重新编译docker镜像,具
体命令为dockerhost$ time docker build –t hubuser/largecontext,输出如下
通过减少构建上下文的大小,达到的效果是提升业务代码编译的时间大约500倍,同时还大大减少了编
译内容的大小。
(4)使用缓存代理的方式
除了构建上下文庞大以外,还有一个因素会导致编译docker进行环境,那就是下载系统依赖库指令。举
一个简单的例子,一个基于centos系统的docker镜像需要从公共资源库中下载依赖包,编译过程中,yum
install指令运行时间的长短取决于所需要下载的依赖包的大小。因此降低依赖包的下载消耗时间对于减少编
译时间也非常重要,所以使用缓存代理的方式能够提高docker镜像编译工作的效率。
2.3 镜像的大小
在容器云平台的使用过程中,随着对docker应用的持续使用,如果不加注意,那么镜像的尺寸会变得越
来越大。在使用docker容器期间,应用承载的docker镜像尺寸如果达到gb规模,会导致编译和部署docker
应用的时间变得不受控制。因此我们需要减少需要部署的镜像尺寸,会抵消容器云平台的优点,失去快读迭
代开发和部署应用的特点。本小节的优化点也将减少容器云平台对于容器编译的时间。
(1)链式指令
docker镜像尺寸变大的一个重要原因是很多对编译或者运行无关的质量被引入到镜像中,举一个常见的
案例,打包元数据和缓存过程中,在安装完编译和运行相关的依赖包以后,下载的文件就没有存留的必要,
类似于clean的指令可以在很多仓库的dockerfile中发现,用于清理这些无效的文件。
from centos:jessie
run echo cenos http://xx.xx.xx.org\
jessie-backports main \
>/etc/apt/sources.list.d/jessie-backports.list
run yum update
run yum install –no-install-recommends \
install –y gcc*
run rm –rfv /var/lib/apt/lists/*
在这个案例之中,一个docker镜像的尺寸是每一个独立镜像层的尺寸总和,也就是联合文件系统的工作
机制,因此clear步骤并没有真正删除相应的硬盘空间,可以通过以下命令来查看。
dockerhost$ docker build –t fakeclean
dockerhost$ docker history fakeclean
由此可见,本案例中并不存在为“负”的镜像层尺寸。在dockerfile中每一个指令要么保持镜像尺寸的不
变,要么增加尺寸,同时每一步骤还有可能会引入新的元数据的信息,将会导致整体的尺寸增大。为了降低
整个镜像的尺寸,清楚操作应该在同一个镜像层中来执行。解决方案是将先前的多条指令合并为一条,当
docker使用/bin/sh来执行每一条指令,我们可以使用bourne shell提供的&&操作符来实现链接,通过减少独
立层的尺寸来减少整个镜像的尺寸。
(2)编译镜像和部署镜像的分离
docker镜像中还一种无用文件也会造成整个镜像尺寸的变大,这类无用文件是编译过程中的依赖文件,
例如在编译应用程序过程中所依赖的源代码库。一般情况下,编译文件和头文件的存在只是在程序编译过程
中起到相应的作用,一旦编译完成,这类文件就不再使用,因此需要对编译镜像和部署镜像进行分离。
使用进行构建 Dockerfile可以优化分层缓存,以减少图像层的拉入和推送时间。在以下示例中,使用此
方法减小了更新大小,因为分成几层是基于更改频率。重新定义一下,将缓存所有未更改的层,如图所示:
让我们来看一个例子。该映像的层很小,但是在进行映像构建时,始终会更新35M层:
# podman history 31952aa275b8
ID CREATED CREATED BY SIZE COMMENT
31952aa275b8 52 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["tail","-f",... 0B
missing 57 seconds ago /bin/sh -c #(nop) COPY resources /resources 35.7MB
missing 5 weeks ago 20.48kB
missing 5 weeks ago
# podman push docker-registry.default.svc:5000/openshift/test:latest
Getting image source signatures
Copying blob 3698255bccdb done
Copying blob a066f3d73913 skipped: already exists
Copying blob 26b543be03e2 skipped: already exists
Copying config 31952aa275 done
Writing manifest to image destination
Copying config 31952aa275 done
Writing manifest to image destination
Storing signatures
但是此映像比上面的映像具有更多的层,5M层是唯一更新的层。缓存其他未更改的层:
# buildah bud --layers .
STEP 1: FROM registry.redhat.io/ubi8/ubi-minimal:latest
STEP 2: COPY resources/base /resources/base
--> Using cache da63ee05ff89cbec02e8a6ac89c287f550337121b8b401752e98c53b22e4fea7
STEP 3: COPY resources/extra /resources/extra
--> Using cache 9abc7eee3e705e4999a7f2ffed09d388798d21d1584a5f025153174f1fa161b3
STEP 4: COPY resources/user /resources/user
b9cef39450b5e373bd4da14f446b6522e0b46f2aabac2756ae9ce726d240e011
STEP 5: ENTRYPOINT ["tail","-f","/dev/null"]
STEP 6: COMMIT
72cc8f59b6cd546d8fb5c3c5d82321b6d14bf66b91367bc5ca403eb33cfcdb15
# podman tag 72cc8f59b6cd docker-registry.default.svc:5000/openshift/test:latest
# podman history 72cc8f59b6cd
ID CREATED CREATED BY SIZE COMMENT
72cc8f59b6cd About a minute ago /bin/sh -c #(nop) ENTRYPOINT ["tail","-f",... 0B
missing About a minute ago /bin/sh -c #(nop) COPY resources/user /res... 5.245MB
missing 2 minutes ago /bin/sh -c #(nop) COPY resources/extra /re... 20.07MB
missing 2 minutes ago /bin/sh -c #(nop) COPY resources/base /res... 10.49MB
missing 5 weeks ago 20.48kB
missing 5 weeks ago 107.1MB Imported from -
# podman push docker-registry.default.svc:5000/openshift/test:latest
Getting image source signatures
Copying blob aa6eb6fda701 done
Copying blob 26b543be03e2 skipped: already exists
Copying blob a066f3d73913 skipped: already exists
Copying blob 822ae69b69df skipped: already exists
Copying blob 7c5c2aefa536 skipped: already exists
Copying config 72cc8f59b6 done
Writing manifest to image destination
Copying config 72cc8f59b6 done
Writing manifest to image destination
Storing signatures
在对构建过程进行故障排除时,应该检查构建日志,因为Kubernetes只能检测到构建容器是否成功完成。
如果在应用程序容器启动时使用连接池的DB服务器出现性能问题或达到最大连接数,则应用程序容器
初始化可能比预期的延迟得更多。因此,如果您的应用程序具有外部依赖关系,则还应检查它们是否运行良
好。而如果Readiness Probes和Liveness Probes配置您的应用程序pod,你应该设置initialDelaySeconds和
periodSeconds足够大的为您的应用pod初始化。如果 initialDelaySeconds和periodSeconds 太短而无法检
查应用程序状态,则您的应用程序将反复重启,并可能导致延迟或部署应用程序pod失败。
3 编排引擎kubernetes的性能瓶颈和调优
作为容器云平台的核心组件,编排引擎kubernetes为容器化的应用提供资源调度、部署运行、滚动升
级、扩缩容等功能,容器集群管理给业务带来了极大的便利,随着业务的不断的增长,容器规模也随之增
大。在这种情况下,kubernetes是否能够快速的完成容器化扩容,扩容到大规模集群时kubernetes管理能力
是否稳定,这些因素将构成容器云平台的性能瓶颈。
通过kubernetes的架构图得知,无论外部客户端、还是kubernetes集群的内部组件,通信机制都需要经
过kubernetes的apiserver,因此api的响应决定了集群性能的好坏。其次对于外部客户端所言,核心关注的
指标是创建容器服务所用的时间,因此pod的启动时间也是判断kubernetes集群是否达到性能瓶颈的一个因
素。行业内目前针对以docker+kubernetes通用性的容器云平台,采用的标准性能指标主要有两类,(1)
api的响应性,99.9%的api调用响应时间在一秒之内。(2)pod启动时间,99.9%的pods启动时间控制在五
秒之内。在pod启动时间的指标中,包括了replication controller的创建,rc依次创建pod,调度器调度pod,
kubernetes为pod设置网络,启动docker容器,等待容器启动成功后的健康检查,并最终将容器状态上报至
api服务器,最后api服务器将pod的返回状态返回给正在监听的客户端。除了以上因素外,在上一小节针对
docker容器引擎的相关性能指标都会影响kubernetes集群性能瓶颈指标,在此不考虑docker容器引擎的性能
问题。
本小节中,重点以kubernetes集群下,分析各组件的性能影响因素,包括了kubernetes集群的总体性
能,影响apiserver的性能因素,影响scheduler的性能因素,影响controller的性能因素,影响kubelet和etcd
的性能因素。
3.1 kubernetes集群的总体性能
kubernetes集群的总体性能取决于以下几个方面,(1)系统自身的性能表现,如果集群资源当前的使
用率,创建新pod所需要的时间。(2)不同的资源使用水位对系统性能的影响,如果安全水线,极限水位的
数值。(3)外挂硬盘(挂载盘)对于集群的影响,如通过kubelet挂载硬盘的耗时,影响pod的启动时间。
(4)各服务组件的tps,响应时间,错误率的比率。(5)各服务端的组件持续运行状态下,相应程序进程
的健康度。
3.2 apiserver的性能瓶颈
apiserver的性能瓶颈取决于以下几个方面,(1)api的响应时间,即数据写入到etcd,异步操作的执行
状态。(2)apiserver缓存的存储设备对性能的影响,如master节点的磁盘读写指标。(3)流控对api的系
统性影响。(4)apiserver重启恢复的时间,也决定了性能瓶颈,以及重启后的资源是否能够快速的恢复。
3.3 scheduler的性能瓶颈
scheduler的性能瓶颈取决于以下几个方面,(1)scheduler在大并发情况下的处理能力。(2)批量进行
创建pod,调度器的耗时和负载。(3)在不同量级的pod下,调度器的平均耗时,峰值耗时以及吞吐量来确
定性能瓶颈。(4)scheduler重启恢复的时间,也决定了性能瓶颈,以及重启后的资源是否能够快速的恢复。
3.4 controller的性能瓶颈
controller的性能瓶颈取决于以下几个方面,(1)大并发情况下,创建rc所消耗的时间。(2)control-
ler创建对应pod的耗时,以及扩缩容的耗时。(3)在不同的deployment的数量级下,控制器处理deploy-
ment的平均耗时,峰值耗时,吞吐率以及控制器的负载。(4)controller重启恢复的时间,也决定了性能瓶
颈,以及重启后的资源是否能够快速的恢复。
3.5 kubelet的性能瓶颈
kubelet的性能瓶颈取决于以下几个方面,(1)node心跳对系统性能的影响。(2)kubelet重启恢复的
时间,也决定了性能瓶颈,以及重启后的资源是否能够快速的恢复。
3.6 etcd的性能瓶颈
etcd的性能瓶颈取决于以下几个方面,(1)etcd的写入性能,包括写最大的并发能力和写入的性能瓶
颈(2)etcd的存储设备性能,如磁盘的读写。
以上的各组件可能引起的性能瓶颈中,通用性方法的有以下方式可以提前避免。
3.7 调优方式
etcd方面,(1)采取本地的ssd盘作为后端存储。(2)etcd独立部署在非kubernetes node。(3)etcd
的快照和预写式日志进行分盘存储。对于大型且密集的集群,如果键空间变得过大并超过了空间配额,则
etcd的性能可能会受到影响。需要对etcd进行定期维护(包括碎片整理)以释放数据存储中的空间。强烈建
议您监视Prometheus的etcd指标,并在需要时对其进行碎片整理,然后etcd发出群集范围的警报,使群集进
入维护模式,该模式仅接受键读取和删除。要监视的一些关键指标 etcd_server_quota_backend_bytes是当前
的配额限制,etcd_mvcc_db_total_size_in_use_in_bytes它指示在历史记录压缩后的实际数据库使用情况,并
etcd_debugging_mvcc_db_total_size_in_bytes显示数据库大小(包括等待碎片整理的可用空间)。
apiserver方面,(1)参数进行调优,主要有max-mutating-requests-inflight,max-requests-inflight,
watch-cache-sizes。(2)etcd多实例支持,对于不同的object进行分库存储,将数据和状态分离,将
events放在独立的etcd实例中。
controller方面,(1)参数进行调优,主要有调大kube-api-qps值,调大 kube-api-burst值,禁用不
需要的controller,调整controller 同步资源的周期。
scheduler方面,(1)参数进行调优,调整kube-api-qps数值。(2)调度器优化,扩展调度器的功
能,可以通过scheduler_extender进行扩展调度器,进行多调度器支持,在kubernetes集群中运行多个调度
器调度不同的作业,进行动态调度支持,采用第三方调度工具来解决pod创建过程中进行一次性调度,一次
性调度导致调度器不会二次平衡pod在集群中的分布。(3)根据实际资源使用率进行调度,集群默认策略
根据pod的request进行调度,对于资源使用率不均衡的场景可通过以实际的使用率进行调度的方式。
kubelet方面,(1)使用node lease减少心跳上报频率,大规模场景下,大量的node心跳汇报影响node
的watch,apiserver在处理心跳请求中性能也会受到较大的影响,因此开启node lease后,kubelet会使用轻
量的node lease对象更新请求替换原有的update node status的方式,减轻apiserver负担。(2)开启book-
mark机制,bookmark将特定的时间发送给客户端,可以大幅度的减少apiserver的负载,bookmark的运行
机制是在客户端和服务端之间保持心跳。
kube-proxy方面,(1)使用ipvs模式,由于iptables匹配时延和规则更新时延在大规模集群中呈现几何
级的增长,增加以及删除规则耗费较多的性能和时间,需要采取ipvs的方式,ipvs采取哈希表,在增加和删
除规则不受到规则基数的影响。(2)独立部署,kube-proxy默认和kubelet部署在同一个节点上,可以独立
部署,避免在所有的节点上产生大量的防火墙规则。
3.8 一些普遍使用的调优策略
还有一些相对普遍使用的调优策略,如主节点大小。主节点资源要求取决于集群中节点的数量。以下主
节点大小建议基于控制平面密度集中测试的结果。
容器平台节点配置文件包含重要选项。例如,两个参数控制可以调度到一个节点的Pod的最大数量:
podsPerCore和maxPods。当两个选项都使用时,两个值中的较低者将限制节点上的容器的数量。超过这些
值可能导致:提高CPU利用率;Pod调度缓慢;潜在的内存不足情况,具体取决于节点中的内存量;耗尽IP
地址池;资源过量使用,导致用户应用程序性能不佳。
podsPerCore根据节点上处理器核心的数量来设置节点可以运行的Pod数量。例如,如果在具有4个处
理器核心的节点上podsPerCore设置为10,则该节点上允许的Pod的最大数量将为40。
设置podsPerCore为0禁用此限制。默认值为0。 podsPerCore不能超过maxPods。maxPods 将节点可
以运行的Pod数量设置为固定值,而不管节点的属性如何。
使用CPU Manager管理CPU组,并将工作负载限制在特定的CPU上。
CPU Manager对于具有以下某些属性的工作负载很有用:需要尽可能多的CPU时间;对处理器高速缓
存未命中敏感;是低延迟的网络应用程序;与其他进程进行协调,并受益于共享单个处理器缓存。
大页面是大于4Ki的内存页面。 在x86_64体系结构上,有两种常见的大页面大小:2Mi和1Gi。 大小在
其他体系结构上有所不同。 为了使用巨大的页面,必须编写代码,以便应用程序知道它们。 透明大页面(
THP)试图在没有应用程序知识的情况下自动管理大页面,但是它们有局限性。 特别是,它们仅限于2Mi页
面大小。由于THP的碎片整理工作,THP可能导致具有高内存利用率的节点上的性能下降或碎片化,这会锁
定内存页面。 因此,某些应用程序可能设计为(或推荐)使用预先分配的大页面而不是THP。
在如OpenShift容器平台中,pod中的应用程序可以分配和使用预分配的大页面。应用程序占用了多少
大页面,节点必须预分配大页面,以便节点报告其大页面容量。 节点只能为单个大小预分配大页面。可以
通过使用资源名称hugepages-<size>容器级资源需求来消耗大量页面,其中size是使用特定节点上支持的整
数值的最紧凑的二进制符号。 例如,如果节点支持2048KiB页面大小,则它将公开可调度的资源hugepag-
es-2Mi 。与CPU或内存不同,大页面不支持过量使用。
分配特定大小的大页面,一些平台支持多种巨大的页面尺寸。要分配特定大小的大页面,请在大页面启
动命令参数前加上大页面大小选择参数hugepagesz=<size> <size>值必须以字节为单位指定,并带有可
选的标度后缀[ kKmMgG ]。 可以使用default_hugepagesz=<size>引导参数定义默认的大页面大小。
巨大的页面要求:巨大的页面请求必须等于限制。 如果指定了限制,则为默认设置,但未指定请求;
在Pod范围内隔离了大量页面。 计划在将来的迭代中隔离容器;由大页面支持的EmptyDir卷不得消耗比pod
请求更多的大页面内存;通过SHM_HUGETLB通过shmget()占用大量页面的应用程序必须与proc / sys / vm /
hugetlb_shm_group匹配的补充组一起运行。
4 基于业务承载的性能调优的难点
在大部分性能瓶颈的定位过程中,一般都是通过发现问题、探测症状、解决问题三个步骤进行。承载业
务的情况下,尤其涉及到vm级别、网络栈、链路和资源隔离方面给我们排查问题和性能调优带来了很多的
挑战。
4.1 应用的链路边长导致排查问题成本变大
容器的场景带来更快速的迭代应用开发和降低部署成本,比如故障自动恢复,弹性伸缩,跨主机调度等
等,但是这一切的代价是需要依赖容器化的架构,比如kubernetes网络中需要fullnat的方式完成两层网络的
转发等等,这会给排查问题带来更复杂的问题,当你不清楚编排引擎的架构实现原理的时候,很难将问题指
向这些平时不会遇到的场景。fullna的好处是降低了网络整体方案的复杂性,但是也引入了一些nat场景下的
常见问题,比如短连接场景中的snat五元组重合导致包重传的问题等等。
4.2 不完整隔离带来的调优复杂性
容器技术本质是一种虚拟化技术,因此具备隔离的特性,基于业务运行的角度不需要考虑隔离的安全性
问题,但是当遇到性能调优的时候,我们发现内核的共享使我们不得不面对的是一个更复杂的场景。由于内
核的共享,系统的proc是以只读的方式进行挂载的,这就意味着系统内核参数的调整会带来的宿主机级别的
变更。在瞬间大并发的情况下,涉及c10k和c100k,这些问题难免涉及到内核参数的调整,但是越特定的场
景调优的参数越不同,有时会容器参数影响宿主机性能的反向效果。
5 结语
本章节中,我们基于容器云平台,分别对docker容器引擎和kubernetes编排引擎两个核心组件的优化进
行阐述,包括docker的部署时间、编译时间、镜像尺寸,kubernetes架构的各组件,包括scheduler、
controller、apiserver、etcd。在此之外,我们还介绍了在承载业务的情况下,针对容器云平台进行瓶颈优
化的一些难点,除了容器知识本身,还涉及到应用链路方面、资源隔离方面,给我们带来的挑战。
课程出品人
顾黄亮,苏宁消费金融安全运维部负责人,TVP成员,《开源许可证使用指南(2018)》作者之一,
《研发运营一体化(DEVOPS)能力成熟度模型》作者之一,《企业IT运维发展白皮书》核心作者之一,
《企业级DevOps实战案例-持续交付篇》合著作者,twt社区平台特邀作者、2020容器云职业技能大赛百位
专家委员会成员。
课程简介
本章节依据《容器云平台架构分析》和《容器云平台高可用设计》中的架构和高可用知识点进行展开,
其中容器云平台是以docker+kubernetes为代表的通用性容器云平台。随着容器技术的发展,容器服务已成
为很多公司的选择,然而在实际运用中,需要在各类环境中成功部署容器和操作容器,需要容器引擎和编排
引擎的通力合作,这就需要容器云平台架构的合理性和各组件之间良好的性能支撑能力。本章节中,我们基
于容器云平台,分别对docker容器引擎和kubernetes编排引擎两个核心组件的优化进行阐述,包括docker的
部署时间、编译时间、镜像尺寸,kubernetes架构的各组件,包括scheduler、controller、apiserver、etcd。
在此之外,我们还介绍了在承载业务的情况下,针对容器云平台进行瓶颈优化的一些难点。
1 前言
前言:本章节依据《容器云平台架构分析》和《容器云平台高可用设计》中的架构和高可用知识点进行
展开,其中容器云平台是以docker+kubernetes为代表的通用性容器云平台。随着容器技术的发展,容器服
务已成为很多公司的选择,然而在实际运用中,需要在各类环境中成功部署容器和操作容器,需要容器引擎
和编排引擎的通力合作,这就需要容器云平台架构的合理性和各组件之间良好的性能支撑能力。容器云平台
作为应用的底层支撑平台,一个应用在上线之前会进行容量规划、压力测试和上线后的验证,因此性能瓶颈
是制约应用发挥作用的一个重要因素,性能调优也是容量规划和验证结果之间出现偏差的进行的必然手段。
本章节将重点介绍基于docker+kubernetes的通用性容器云平台性能瓶颈和性能调优的方式和方法。
2 容器引擎docker的性能瓶颈和调优
docker容器承载于容器引擎之上,对于开发者和容器云平台管理员来说,统一标准的包格式简化了二
者的工作量,而容器格式让使用者加快速迭代应用程序的版本并进行管理。开发、测试、部署的时间也得
到降低,这是容器引擎的最核心功能。因此,容器引擎的性能优势和瓶颈基于容器的轻量化和启动速度来
分析,主要涉及的组件为docker容器和镜像仓库。在对容器引擎的性能瓶颈分析方面,基于以下几点来考
虑,(1)容器引擎的稳定性,在《容器云平台架构分析》和《容器云平台可靠性设计》中已体现,在此不
再阐述。(2)镜像仓库的瘦身和启动速度,在实际使用过程中,由于经常会下载应用程序所对应的镜像运
行库,使得开发的时间在增加,而docker hub的速度拖慢了部署时间,造成部署时间的偏差。而docker hub
的不稳定因素也会导致部署时间的不确定性,另docker镜像的大小存在超大的行为,容器更新的时间也将
不受控制。基于以上的因素,本小节基于docker容器引擎的性能瓶颈和优化集中于以下三点,(1)镜像的
部署时间,(2)镜像的编译时间,(3)镜像的大小。
2.1 镜像的部署时间
在使用容器云平台过程中,存在一种情况,构建的docker容器尺寸变得越来越大,在现有的docker宿
主机中运行的容器是可以避免此类问题。docker会合理利用docker镜像层,这些镜像层会随着所部署的应用
的增长而随之创建。但是有一种场景,当我们准备水平扩展应用的时候,这些应用所承载的docker容器会越
来越多的需要docker宿主机进行支撑,因此每一个新的docker宿主机需要下载我们所需要的镜像层。本小节
会举例复现一个大的docker应用如何影响docker宿主机上的部署时间。首先我们来复现这个docker应用的
场景问题,如下所示。
(1)编写一个dockerfile来创建一个大docker镜像
from debian:iessie
run dd if=/dev/urandom of=/largefile bs=1024 count=524288
(2)编译dockerfile ,并对hubuser/largeapp打标签,具体命令如下
dockerhost$ docker build –t hubuser/largeapp
(3)检查这个docker容器的尺寸,从执行后的输出看出,这个docker容器占用了接近700m的空间,
具体命令如下
dockerhost$ docker images
(4)通过time命令来查看这个docker镜像上传到docker hub和从docker hub拉取的时间,具体命令如下
docker $ time docker push hubuser/largeapp
docker $ time docker pull hubuser/largeapp
从二者执行的时间上可以看出,当执行docker push命令时,上传镜像到docker hub花费了大量的时间,
同样的在部署的过程中,docker pull拉取我们新建的镜像到生产环境的docker宿主机上同样花费了很长的时
间。所以上传和下载的时间取决于docker宿主机到docker hub之间的网络环境和网络带宽,如果docker hub
出现异常,对部署新的docker容器存在很大的时效调整,有可能会引发部署失败。在容器云平台架构的介绍
中,我们为了利用docker的快速分发和便捷部署的特性,上传和下载镜像的稳定性是我们需要解决的需求。
在本章中,我们通过docker容器镜像组件registry来解决这个性能瓶颈,docker registry用于存储和分发docker
镜像,无需依赖公共的docker hub服务,具体步骤如下。
(1)部署docker registry私有仓库,具体架构不在本小节中阐述。
(2)确认docker镜像部署的速度是否已经提升,首先,将先前创建的镜像打上标签,用于推送至本地
私有仓库。
(3)实测docker镜像到本地仓库的上传的速率,实测结果为docker hub的十倍。
(4)实测docker镜像到本地仓库的上传的速率,实测结果为docker hub的三十倍。
2.2 镜像的编译时间
docker镜像是容器云平台的重要组成构建,也是性能瓶颈的优先考虑的点,因此简化docker文件和加
速容器技术至关重要,可以让我们更快速的迭代应用开发。一旦编译docker镜像所需要的时间存在过长或不
受控制,那么使用容器云平台的优势将打很大的折扣。本小节中,我们将以几个维度来分析构建docker镜像
中耗时过长的问题,来优化此类问题的瓶颈。
(1)通过本地化仓库来提升速度
在提升镜像部署时间的段落中,我们已经采用了本地化仓库的方式,避免从docker hub中上传和下载镜
像,速度有很大的提升,在构建镜像的过程中,同样的需要花费较多的时间在获取上游镜像的过程中。当我
们使用一台新的docker宿主机,通过docker hub进行下载镜像进行编译,整体耗时将会显著的增加,因此通
过本地化仓库也能缩短镜像编译的整体耗时,通常运用传递--registry-mirror的参数的方式给予docker的守
护进程,用来配置docker宿主机寻找本地的镜像仓库,大概步骤如下。
在任意一台docker宿主机中,通过更新或创建一个systemd的插件文件来配置docker守护进程,文件核
心配置参数为execstart=/usr/bin/docker daemon –h df://\--registry-mirror=http://dockerhost:5000。
接着上一步骤,重启systemd来加载socker service的新配置文件,重启新配置的systemd单元来重启
docker守护进程,两个命令如下。
dockerhost$ systemctl daemon-reload
dockerhost$ systemctl restartdocker-service
最后运行作为进行的registry的docker容器,确保该进行registry正常工作。下面开始验证二者的速度区
别,先编译一个简单的dockerfile,通过docker hub进行下载上游镜像,可以发现编译镜像的大部分时间在
下载进行部分,结果如下。
移除该进行以及上游的依赖进行,重新进行编译,结果如下
(2)复用镜像层来提升整体的编译速度
根据《容器云平台架构》一章节中得知,docker镜像是由一系列的层合并而成,所使用的是一个单一的
镜像联合文件系统。当我们在构建docker镜像的时候,docker会检查dockerfile中正在处理的指令,判断缓
存中是否存在可以复用的镜像,而不是一味的重复创建同一个镜像,因此复用镜像层也是提升镜像编译能力
之一,提高创建docker镜像的速度。
(3)减小构建上下文的大小
在容器云自动化集成的过程中,持续集成是核心的环节,基于代码的版本控制中,通过dickerfile来定义
代码的基本属性。某种程度上,以git为例,当项目代码的git文件占有太多磁盘空间的时候,可能存在代码
提交过多,或者代码引用太多的情况,具体方法如下。
dockerhost$ du –hsc .git
当编译docker应用时,如果git文件占用太多磁盘空间,造成代码空间较大的情况下,编译docker进行
的时间也会非常大,间接造成整体编译的时间拉长,具体查看方法如下。
dockerhost$ time docker build –t hubuser/largecontext
通过这个案例我们来回溯,docker客户端上传了整个项目git文件夹,而仅仅是因为它在镜像的编译路
径下,这种情况针对于开发能力不是很强的同学来说,轻易会造成这种局面,所以在编译docker镜像的过程
中,docker守护经常需要花费较多的时间来接收相关的信息。由于很多文件对于编译过程是无用的,尤其是
在编译该应用程序的docker镜像阶段,所以这些文件对于在生产环境中运行的应用程序也是无用的。所以我
们在编译docker镜像之前可以设定忽略这些无用文件,也就是通过减少上下文的大小来达到优化编译过程的
性能,操作方法如下。
在dockerfile所在目录下创建一个.dockerignore文件,文件中包括.git的内容。重新编译docker镜像,具
体命令为dockerhost$ time docker build –t hubuser/largecontext,输出如下
通过减少构建上下文的大小,达到的效果是提升业务代码编译的时间大约500倍,同时还大大减少了编
译内容的大小。
(4)使用缓存代理的方式
除了构建上下文庞大以外,还有一个因素会导致编译docker进行环境,那就是下载系统依赖库指令。举
一个简单的例子,一个基于centos系统的docker镜像需要从公共资源库中下载依赖包,编译过程中,yum
install指令运行时间的长短取决于所需要下载的依赖包的大小。因此降低依赖包的下载消耗时间对于减少编
译时间也非常重要,所以使用缓存代理的方式能够提高docker镜像编译工作的效率。
2.3 镜像的大小
在容器云平台的使用过程中,随着对docker应用的持续使用,如果不加注意,那么镜像的尺寸会变得越
来越大。在使用docker容器期间,应用承载的docker镜像尺寸如果达到gb规模,会导致编译和部署docker
应用的时间变得不受控制。因此我们需要减少需要部署的镜像尺寸,会抵消容器云平台的优点,失去快读迭
代开发和部署应用的特点。本小节的优化点也将减少容器云平台对于容器编译的时间。
(1)链式指令
docker镜像尺寸变大的一个重要原因是很多对编译或者运行无关的质量被引入到镜像中,举一个常见的
案例,打包元数据和缓存过程中,在安装完编译和运行相关的依赖包以后,下载的文件就没有存留的必要,
类似于clean的指令可以在很多仓库的dockerfile中发现,用于清理这些无效的文件。
from centos:jessie
run echo cenos http://xx.xx.xx.org\
jessie-backports main \
>/etc/apt/sources.list.d/jessie-backports.list
run yum update
run yum install –no-install-recommends \
install –y gcc*
run rm –rfv /var/lib/apt/lists/*
在这个案例之中,一个docker镜像的尺寸是每一个独立镜像层的尺寸总和,也就是联合文件系统的工作
机制,因此clear步骤并没有真正删除相应的硬盘空间,可以通过以下命令来查看。
dockerhost$ docker build –t fakeclean
dockerhost$ docker history fakeclean
由此可见,本案例中并不存在为“负”的镜像层尺寸。在dockerfile中每一个指令要么保持镜像尺寸的不
变,要么增加尺寸,同时每一步骤还有可能会引入新的元数据的信息,将会导致整体的尺寸增大。为了降低
整个镜像的尺寸,清楚操作应该在同一个镜像层中来执行。解决方案是将先前的多条指令合并为一条,当
docker使用/bin/sh来执行每一条指令,我们可以使用bourne shell提供的&&操作符来实现链接,通过减少独
立层的尺寸来减少整个镜像的尺寸。
(2)编译镜像和部署镜像的分离
docker镜像中还一种无用文件也会造成整个镜像尺寸的变大,这类无用文件是编译过程中的依赖文件,
例如在编译应用程序过程中所依赖的源代码库。一般情况下,编译文件和头文件的存在只是在程序编译过程
中起到相应的作用,一旦编译完成,这类文件就不再使用,因此需要对编译镜像和部署镜像进行分离。
使用进行构建 Dockerfile可以优化分层缓存,以减少图像层的拉入和推送时间。在以下示例中,使用此
方法减小了更新大小,因为分成几层是基于更改频率。重新定义一下,将缓存所有未更改的层,如图所示:
让我们来看一个例子。该映像的层很小,但是在进行映像构建时,始终会更新35M层:
# podman history 31952aa275b8
ID CREATED CREATED BY SIZE COMMENT
31952aa275b8 52 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["tail","-f",... 0B
missing 57 seconds ago /bin/sh -c #(nop) COPY resources /resources 35.7MB
missing 5 weeks ago 20.48kB
missing 5 weeks ago
# podman push docker-registry.default.svc:5000/openshift/test:latest
Getting image source signatures
Copying blob 3698255bccdb done
Copying blob a066f3d73913 skipped: already exists
Copying blob 26b543be03e2 skipped: already exists
Copying config 31952aa275 done
Writing manifest to image destination
Copying config 31952aa275 done
Writing manifest to image destination
Storing signatures
但是此映像比上面的映像具有更多的层,5M层是唯一更新的层。缓存其他未更改的层:
# buildah bud --layers .
STEP 1: FROM registry.redhat.io/ubi8/ubi-minimal:latest
STEP 2: COPY resources/base /resources/base
--> Using cache da63ee05ff89cbec02e8a6ac89c287f550337121b8b401752e98c53b22e4fea7
STEP 3: COPY resources/extra /resources/extra
--> Using cache 9abc7eee3e705e4999a7f2ffed09d388798d21d1584a5f025153174f1fa161b3
STEP 4: COPY resources/user /resources/user
b9cef39450b5e373bd4da14f446b6522e0b46f2aabac2756ae9ce726d240e011
STEP 5: ENTRYPOINT ["tail","-f","/dev/null"]
STEP 6: COMMIT
72cc8f59b6cd546d8fb5c3c5d82321b6d14bf66b91367bc5ca403eb33cfcdb15
# podman tag 72cc8f59b6cd docker-registry.default.svc:5000/openshift/test:latest
# podman history 72cc8f59b6cd
ID CREATED CREATED BY SIZE COMMENT
72cc8f59b6cd About a minute ago /bin/sh -c #(nop) ENTRYPOINT ["tail","-f",... 0B
missing About a minute ago /bin/sh -c #(nop) COPY resources/user /res... 5.245MB
missing 2 minutes ago /bin/sh -c #(nop) COPY resources/extra /re... 20.07MB
missing 2 minutes ago /bin/sh -c #(nop) COPY resources/base /res... 10.49MB
missing 5 weeks ago 20.48kB
missing 5 weeks ago 107.1MB Imported from -
# podman push docker-registry.default.svc:5000/openshift/test:latest
Getting image source signatures
Copying blob aa6eb6fda701 done
Copying blob 26b543be03e2 skipped: already exists
Copying blob a066f3d73913 skipped: already exists
Copying blob 822ae69b69df skipped: already exists
Copying blob 7c5c2aefa536 skipped: already exists
Copying config 72cc8f59b6 done
Writing manifest to image destination
Copying config 72cc8f59b6 done
Writing manifest to image destination
Storing signatures
在对构建过程进行故障排除时,应该检查构建日志,因为Kubernetes只能检测到构建容器是否成功完成。
如果在应用程序容器启动时使用连接池的DB服务器出现性能问题或达到最大连接数,则应用程序容器
初始化可能比预期的延迟得更多。因此,如果您的应用程序具有外部依赖关系,则还应检查它们是否运行良
好。而如果Readiness Probes和Liveness Probes配置您的应用程序pod,你应该设置initialDelaySeconds和
periodSeconds足够大的为您的应用pod初始化。如果 initialDelaySeconds和periodSeconds 太短而无法检
查应用程序状态,则您的应用程序将反复重启,并可能导致延迟或部署应用程序pod失败。
3 编排引擎kubernetes的性能瓶颈和调优
作为容器云平台的核心组件,编排引擎kubernetes为容器化的应用提供资源调度、部署运行、滚动升
级、扩缩容等功能,容器集群管理给业务带来了极大的便利,随着业务的不断的增长,容器规模也随之增
大。在这种情况下,kubernetes是否能够快速的完成容器化扩容,扩容到大规模集群时kubernetes管理能力
是否稳定,这些因素将构成容器云平台的性能瓶颈。
通过kubernetes的架构图得知,无论外部客户端、还是kubernetes集群的内部组件,通信机制都需要经
过kubernetes的apiserver,因此api的响应决定了集群性能的好坏。其次对于外部客户端所言,核心关注的
指标是创建容器服务所用的时间,因此pod的启动时间也是判断kubernetes集群是否达到性能瓶颈的一个因
素。行业内目前针对以docker+kubernetes通用性的容器云平台,采用的标准性能指标主要有两类,(1)
api的响应性,99.9%的api调用响应时间在一秒之内。(2)pod启动时间,99.9%的pods启动时间控制在五
秒之内。在pod启动时间的指标中,包括了replication controller的创建,rc依次创建pod,调度器调度pod,
kubernetes为pod设置网络,启动docker容器,等待容器启动成功后的健康检查,并最终将容器状态上报至
api服务器,最后api服务器将pod的返回状态返回给正在监听的客户端。除了以上因素外,在上一小节针对
docker容器引擎的相关性能指标都会影响kubernetes集群性能瓶颈指标,在此不考虑docker容器引擎的性能
问题。
本小节中,重点以kubernetes集群下,分析各组件的性能影响因素,包括了kubernetes集群的总体性
能,影响apiserver的性能因素,影响scheduler的性能因素,影响controller的性能因素,影响kubelet和etcd
的性能因素。
3.1 kubernetes集群的总体性能
kubernetes集群的总体性能取决于以下几个方面,(1)系统自身的性能表现,如果集群资源当前的使
用率,创建新pod所需要的时间。(2)不同的资源使用水位对系统性能的影响,如果安全水线,极限水位的
数值。(3)外挂硬盘(挂载盘)对于集群的影响,如通过kubelet挂载硬盘的耗时,影响pod的启动时间。
(4)各服务组件的tps,响应时间,错误率的比率。(5)各服务端的组件持续运行状态下,相应程序进程
的健康度。
3.2 apiserver的性能瓶颈
apiserver的性能瓶颈取决于以下几个方面,(1)api的响应时间,即数据写入到etcd,异步操作的执行
状态。(2)apiserver缓存的存储设备对性能的影响,如master节点的磁盘读写指标。(3)流控对api的系
统性影响。(4)apiserver重启恢复的时间,也决定了性能瓶颈,以及重启后的资源是否能够快速的恢复。
3.3 scheduler的性能瓶颈
scheduler的性能瓶颈取决于以下几个方面,(1)scheduler在大并发情况下的处理能力。(2)批量进行
创建pod,调度器的耗时和负载。(3)在不同量级的pod下,调度器的平均耗时,峰值耗时以及吞吐量来确
定性能瓶颈。(4)scheduler重启恢复的时间,也决定了性能瓶颈,以及重启后的资源是否能够快速的恢复。
3.4 controller的性能瓶颈
controller的性能瓶颈取决于以下几个方面,(1)大并发情况下,创建rc所消耗的时间。(2)control-
ler创建对应pod的耗时,以及扩缩容的耗时。(3)在不同的deployment的数量级下,控制器处理deploy-
ment的平均耗时,峰值耗时,吞吐率以及控制器的负载。(4)controller重启恢复的时间,也决定了性能瓶
颈,以及重启后的资源是否能够快速的恢复。
3.5 kubelet的性能瓶颈
kubelet的性能瓶颈取决于以下几个方面,(1)node心跳对系统性能的影响。(2)kubelet重启恢复的
时间,也决定了性能瓶颈,以及重启后的资源是否能够快速的恢复。
3.6 etcd的性能瓶颈
etcd的性能瓶颈取决于以下几个方面,(1)etcd的写入性能,包括写最大的并发能力和写入的性能瓶
颈(2)etcd的存储设备性能,如磁盘的读写。
以上的各组件可能引起的性能瓶颈中,通用性方法的有以下方式可以提前避免。
3.7 调优方式
etcd方面,(1)采取本地的ssd盘作为后端存储。(2)etcd独立部署在非kubernetes node。(3)etcd
的快照和预写式日志进行分盘存储。对于大型且密集的集群,如果键空间变得过大并超过了空间配额,则
etcd的性能可能会受到影响。需要对etcd进行定期维护(包括碎片整理)以释放数据存储中的空间。强烈建
议您监视Prometheus的etcd指标,并在需要时对其进行碎片整理,然后etcd发出群集范围的警报,使群集进
入维护模式,该模式仅接受键读取和删除。要监视的一些关键指标 etcd_server_quota_backend_bytes是当前
的配额限制,etcd_mvcc_db_total_size_in_use_in_bytes它指示在历史记录压缩后的实际数据库使用情况,并
etcd_debugging_mvcc_db_total_size_in_bytes显示数据库大小(包括等待碎片整理的可用空间)。
apiserver方面,(1)参数进行调优,主要有max-mutating-requests-inflight,max-requests-inflight,
watch-cache-sizes。(2)etcd多实例支持,对于不同的object进行分库存储,将数据和状态分离,将
events放在独立的etcd实例中。
controller方面,(1)参数进行调优,主要有调大kube-api-qps值,调大 kube-api-burst值,禁用不
需要的controller,调整controller 同步资源的周期。
scheduler方面,(1)参数进行调优,调整kube-api-qps数值。(2)调度器优化,扩展调度器的功
能,可以通过scheduler_extender进行扩展调度器,进行多调度器支持,在kubernetes集群中运行多个调度
器调度不同的作业,进行动态调度支持,采用第三方调度工具来解决pod创建过程中进行一次性调度,一次
性调度导致调度器不会二次平衡pod在集群中的分布。(3)根据实际资源使用率进行调度,集群默认策略
根据pod的request进行调度,对于资源使用率不均衡的场景可通过以实际的使用率进行调度的方式。
kubelet方面,(1)使用node lease减少心跳上报频率,大规模场景下,大量的node心跳汇报影响node
的watch,apiserver在处理心跳请求中性能也会受到较大的影响,因此开启node lease后,kubelet会使用轻
量的node lease对象更新请求替换原有的update node status的方式,减轻apiserver负担。(2)开启book-
mark机制,bookmark将特定的时间发送给客户端,可以大幅度的减少apiserver的负载,bookmark的运行
机制是在客户端和服务端之间保持心跳。
kube-proxy方面,(1)使用ipvs模式,由于iptables匹配时延和规则更新时延在大规模集群中呈现几何
级的增长,增加以及删除规则耗费较多的性能和时间,需要采取ipvs的方式,ipvs采取哈希表,在增加和删
除规则不受到规则基数的影响。(2)独立部署,kube-proxy默认和kubelet部署在同一个节点上,可以独立
部署,避免在所有的节点上产生大量的防火墙规则。
3.8 一些普遍使用的调优策略
还有一些相对普遍使用的调优策略,如主节点大小。主节点资源要求取决于集群中节点的数量。以下主
节点大小建议基于控制平面密度集中测试的结果。
容器平台节点配置文件包含重要选项。例如,两个参数控制可以调度到一个节点的Pod的最大数量:
podsPerCore和maxPods。当两个选项都使用时,两个值中的较低者将限制节点上的容器的数量。超过这些
值可能导致:提高CPU利用率;Pod调度缓慢;潜在的内存不足情况,具体取决于节点中的内存量;耗尽IP
地址池;资源过量使用,导致用户应用程序性能不佳。
podsPerCore根据节点上处理器核心的数量来设置节点可以运行的Pod数量。例如,如果在具有4个处
理器核心的节点上podsPerCore设置为10,则该节点上允许的Pod的最大数量将为40。
设置podsPerCore为0禁用此限制。默认值为0。 podsPerCore不能超过maxPods。maxPods 将节点可
以运行的Pod数量设置为固定值,而不管节点的属性如何。
使用CPU Manager管理CPU组,并将工作负载限制在特定的CPU上。
CPU Manager对于具有以下某些属性的工作负载很有用:需要尽可能多的CPU时间;对处理器高速缓
存未命中敏感;是低延迟的网络应用程序;与其他进程进行协调,并受益于共享单个处理器缓存。
大页面是大于4Ki的内存页面。 在x86_64体系结构上,有两种常见的大页面大小:2Mi和1Gi。 大小在
其他体系结构上有所不同。 为了使用巨大的页面,必须编写代码,以便应用程序知道它们。 透明大页面(
THP)试图在没有应用程序知识的情况下自动管理大页面,但是它们有局限性。 特别是,它们仅限于2Mi页
面大小。由于THP的碎片整理工作,THP可能导致具有高内存利用率的节点上的性能下降或碎片化,这会锁
定内存页面。 因此,某些应用程序可能设计为(或推荐)使用预先分配的大页面而不是THP。
在如OpenShift容器平台中,pod中的应用程序可以分配和使用预分配的大页面。应用程序占用了多少
大页面,节点必须预分配大页面,以便节点报告其大页面容量。 节点只能为单个大小预分配大页面。可以
通过使用资源名称hugepages-<size>容器级资源需求来消耗大量页面,其中size是使用特定节点上支持的整
数值的最紧凑的二进制符号。 例如,如果节点支持2048KiB页面大小,则它将公开可调度的资源hugepag-
es-2Mi 。与CPU或内存不同,大页面不支持过量使用。
分配特定大小的大页面,一些平台支持多种巨大的页面尺寸。要分配特定大小的大页面,请在大页面启
动命令参数前加上大页面大小选择参数hugepagesz=<size> <size>值必须以字节为单位指定,并带有可
选的标度后缀[ kKmMgG ]。 可以使用default_hugepagesz=<size>引导参数定义默认的大页面大小。
巨大的页面要求:巨大的页面请求必须等于限制。 如果指定了限制,则为默认设置,但未指定请求;
在Pod范围内隔离了大量页面。 计划在将来的迭代中隔离容器;由大页面支持的EmptyDir卷不得消耗比pod
请求更多的大页面内存;通过SHM_HUGETLB通过shmget()占用大量页面的应用程序必须与proc / sys / vm /
hugetlb_shm_group匹配的补充组一起运行。
4 基于业务承载的性能调优的难点
在大部分性能瓶颈的定位过程中,一般都是通过发现问题、探测症状、解决问题三个步骤进行。承载业
务的情况下,尤其涉及到vm级别、网络栈、链路和资源隔离方面给我们排查问题和性能调优带来了很多的
挑战。
4.1 应用的链路边长导致排查问题成本变大
容器的场景带来更快速的迭代应用开发和降低部署成本,比如故障自动恢复,弹性伸缩,跨主机调度等
等,但是这一切的代价是需要依赖容器化的架构,比如kubernetes网络中需要fullnat的方式完成两层网络的
转发等等,这会给排查问题带来更复杂的问题,当你不清楚编排引擎的架构实现原理的时候,很难将问题指
向这些平时不会遇到的场景。fullna的好处是降低了网络整体方案的复杂性,但是也引入了一些nat场景下的
常见问题,比如短连接场景中的snat五元组重合导致包重传的问题等等。
4.2 不完整隔离带来的调优复杂性
容器技术本质是一种虚拟化技术,因此具备隔离的特性,基于业务运行的角度不需要考虑隔离的安全性
问题,但是当遇到性能调优的时候,我们发现内核的共享使我们不得不面对的是一个更复杂的场景。由于内
核的共享,系统的proc是以只读的方式进行挂载的,这就意味着系统内核参数的调整会带来的宿主机级别的
变更。在瞬间大并发的情况下,涉及c10k和c100k,这些问题难免涉及到内核参数的调整,但是越特定的场
景调优的参数越不同,有时会容器参数影响宿主机性能的反向效果。
5 结语
本章节中,我们基于容器云平台,分别对docker容器引擎和kubernetes编排引擎两个核心组件的优化进
行阐述,包括docker的部署时间、编译时间、镜像尺寸,kubernetes架构的各组件,包括scheduler、
controller、apiserver、etcd。在此之外,我们还介绍了在承载业务的情况下,针对容器云平台进行瓶颈优
化的一些难点,除了容器知识本身,还涉及到应用链路方面、资源隔离方面,给我们带来的挑战。
运维技术岗岗位学习教程
of 17
5墨值下载
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文档的来源(墨天轮),文档链接,文档作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

关注
最新上传
暂无内容,敬请期待...
下载排行榜
Top250 周榜 月榜