Fork me on GitHub

标签 centos 7 下的文章

记 CentOS7一处内存cache导致的性能问题

问题背景

线上业务集群迁移到新机器,在运行一段时间后,发现新机器不是很稳定,各个机器的 cpu idle 和耗时会出现一些尖峰式的震荡,idle 最低的时候会压到0,同时也会出现不少超时请求,看起来,机器在那一瞬间像被卡住了一样,但又能较快恢复。如下图所示

6F205DED-DBB5-412F-AB47-BBC944D7F36A.png

与 NUMA 和 NUMA balancing 的问题不同,之前出问题的机器会比较稳定的耗时长,idle 低,性能差,而这些机器则是不稳定出现,而且出问题的时间很短,很难抓住现场。而且这些机器所在机房本身又有超电风险,又加开了睿频,一时间,我们也很难确定问题是来自于外部还是来自于机器本身。

问题解决

问题的结局也很偶然,是有同学在测试其它业务的时候发现系统 buff/cache 过高,而他们的业务需要申请大量内存,当 buff/cache 填满之后,系统性能明显下降,手动清理之后恢复。看表象和我们的情况不太一致,我们的业务也不像他们那样大量使用内存,抱着试一试的态度,也看了一眼系统 buff/cache 的情况,还真发现两者存在正相关的关系(当然不是所有 buff/cache 高的机器都有抖动的情况,而是抖动的机器绝大多数都是 buff/cache 高),如下图所示:

A8F5039A-EDDC-4CED-805A-B3F751B64793.png

手动释放 cache 也比较简单,直接执行echo 3 > /proc/sys/vm/drop_caches命令即可清除,注意在如上面这样 buff/cache 巨大的情况下,清理会执行较长时间,也会占用一个 cpu,不建议在高峰期操作,对正常机器,这个操作也是流量有损的。另:官方文档里强调这个操作可能会导致严重性能问题,不建议在生产环境未经实验就开始使用。

至于为何会有问题,Tuning kernel memory for performance这篇文章里会有介绍,在此不班门弄斧。问题大致原因是,虽然 buff/cache 被认为是可用的内存,但使用时,也需要甄别那些块是可以释放使用的,因此当所有内存都被 buff/cache 占用后,在某些情况下,清理释放这些内存块也需要大量占用 cpu 资源导致系统响应变慢。

应对这个问题的简单方法是写个定时任务定时清理内存,但这样治标不治本,另外一种较持久的方法是设置保留内存的大小,在上面文章的 solution 里也有介绍,设置/proc/sys/vm/min_free_kbytes的大小即可,一般设置内存大小的10%为保留内存,当系统发现 free 的内存小于该值的时候,会开始寻找可以释放的 buff/cache,使得 free 的值在保留值之上。不过注意这样的方式机器重启之后就会失效,一劳永逸的方法是修改/etc/sysctl.conf,修改vm.min_free_kbytes的值即可。另外文中提到,对于耗时敏感的服务,任何程度的 swap 都会导致性能受损,可以将vm.swappiness设置为0,不使用 swap。

再次强调,生产环境直接修改这些值可能会导致严重后果,请先在灰度环境或者流量低峰测试之后再进行操作。建议详细阅读Tuning kernel memory for performance这篇文章,正确理解相关参数的含义。

Tensorflow Serving CentOS 7源码编译(CPU + GPU版)

CPU 版本编译

1.需要联网环境,如果是生产环境,最好配置能够连接外网的代理,否则需要 copy 相关依赖安装,费时费力。代理配置方法:

# http://URI:PORT为代理地址,注意需要加上协议名称,如 http://,否则相关编译脚本下载依赖时会报错
export http_proxy=http://URI:PORT
export https_proxy=http://URI:PORT

2.安装相关依赖,依赖列表可以从 tensorflow-serving 的 docker file 中找到,主要需要安装的依赖如下,因 github 上的项目更新较快,请以 docker file 中的为准。:

# 一般会缺失的依赖
yum -y install java-1.8.0-openjdk-devel automake autoconf libtool libicu patch

# python 相关,连接境外源较慢,建议更换成国内源,比如 douban 等
pip install --upgrade pip
pip install numpy grpcio Keras-Applications Keras-Preprocessing h5py requests enum --trusted-host pypi.doubanio.com

3.安装 bazel:在 https://copr.fedorainfracloud.org/coprs/vbatts/bazel/ 中下载对应版本的repo文件,并拷贝到/etc/yum.repos.d/中,使用命令 yum install –downloadonly bazel 就可以下载 bazel 的 rpm 包,缓存在 /var/cache/yum 子目录下,比如/var/cache/yum/x86_64/7/vbatts-bazel/packages,如果有别的机器没有网络,可以将bazel-0.12.0-1.el7.centos.x86_64.rpm上传服务器对应目录下,使用命令 yum install bazel-0.12.0-1.el7.centos.x86_64.rpm 即可安装。如果 yum 下载最后提示 check sum 失败,可以查看 log 找到下载地址,wget 下载,再将 rpm 包复制到上述目录安装

4.下载 tf-serving 代码开始编译,时间较长,主要的错误就是依赖缺失,根据日志提示查找对应包安装即可

git clone --recurse-submoduleshttps://github.com/tensorflow/serving && cd serving
bazel build -c opt tensorflow_serving/...

GPU 版本编译

GPU 版本需要在 cuda 9 环境下编译,cuda 8下编译始终失败,git 上已经有说明。所以以下步骤,请在 Centos 7 + cuda 9 + cudnn 7 for cuda 9的环境下操作。

1.对于联网环境,相关依赖还有 bazel 的安装, 同上文 CPU环境

2.GPU 版本需要安装 TensorRT,在官网下载与 cuda 版本对应的 tar 包(如果 cuda 不是通过 rpm 安装,这里请不要选择 rpm 包,装不上),例如我们的环境是CentOs 7 + cuda 9。下载之后解压即可,将其移到合适位置,后续需要用到 TensorRT 的安装路径。

3.下载 tf-serving 的代码,参照 gpu docker 文件里的编译步骤,整理编译需要的环境变量和编译命令如下:

#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/root/bin  #依照自有环境修改
export LD_LIBRARY_PATH=/usr/local/cuda/lib64/         #依照自有环境修改
export LD_LIBRARY_PATH=/usr/local/cuda/lib64/stubs:/usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH               #依照自有环境修改,主要是增加 cuda 的依赖
export BAZEL_VERSION=0.23.1                #指定 bazel 的版本
export CUDNN_VERSION=7.5.0                 #指定 cudnn 的版本
export TF_TENSORRT_VERSION=5.0.2           #指定 TensorRT 的版本
export TF_NEED_CUDA=1                      #编译 GPU 版本必须指定的参数
export TF_NEED_TENSORRT=1
export TENSORRT_INSTALL_PATH=/usr/local/tensorRT/lib #依照自有环境修改,指向tensorRT 的 lib 目录
export TF_CUDA_COMPUTE_CAPABILITIES=3.0,3.5,5.2,6.0,6.1  #依照自有环境上的 GPU 修改
export TF_CUDA_VERSION=9.0                 #cuda 版本号
export TF_CUDNN_VERSION=7                  #cudnn 版本号
export TF_NCCL_VERSION=                    #兼容老版本,1.13版本以后可以移除
export TMP="/home/tcheng/serving/tmp"      #编译所需 tmp 目录,请保证磁盘空间足够

cd serving && bazel build \
    --color=yes \
    --curses=yes \
    --config=cuda \
    --copt="-fPIC" \ 
    --verbose_failures \
    --output_filter=DONT_MATCH_ANYTHING \
    --config=nativeopt \
    --incompatible_disallow_data_transition=false \
    tensorflow_serving/model_servers:tensorflow_model_server

我所使用的 bazel 版本是0.23.1,tf-serving 版本为1.13,如上编译应该没有什么问题。编译成功之后的 modelserver 在 bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server目录下。ldd 可以查看编译结果的依赖,如果需要在别出使用,可以用 chrpath 工具修改 RUNPATH 到指定目录,将相关 so 拷贝到该目录即可使用。

4.记录编译过程中出过的一些问题:

* git clone 代码的时候需要recurse-submodules下载全部组件,否则在 bazel build 的时候会下载,慢不说还经常下载失败,提示`Premature EOF`
* bazel 版本更新可能会带来一些不兼容的情况,比如上面命令中的--incompatible_disallow_data_transition=false选项,在 docker file 中并未提到,可以根据编译出错的提示Using cfg = "data" on an attribute is a noop and no longer supported. Please remove it. You can use --incompatible_disallow_data_transition=false to temporarily disable this check,稍加搜索即可解决。

记 CentOS 7上一个诡异的性能问题

问题背景

线上系统从 CentOS 6 迁往 CentOS 7,整个过程较为顺利,但在使用中发现了一个奇怪的问题,在处理计算任务的机器上,CPU计算部分的耗时在不同节点上存在差异且差异显著,如下图所示:

A12280BA-1D10-4FC6-8C61-66C73A1A424B.png

出问题的机器计算耗时波动剧烈,耗时差别达到30%~50%,极端的 case 会更高,但并非所有机器、所有请求都受到影响。在初期问题机器只集中在个别机器和高峰时间段,高峰过后就会恢复,到后期则是随机的机器和全时间段,压力退去之后也不能恢复,重启大法也不能奈何。
这个问题的诡异之处就在于它的飘忽不定,定位经历了很长时间,也是很偶然的看到一篇文章才解决了问题

解决过程

CentOS 7集群上线之初由于存在两种机型,差别仅在于磁盘的区别,而出问题的机器则集中在同一种机型上,所以开始很自然的认为是机器配置导致的问题,观察各项监控,发现 cpu.system占用较高,说明有频繁的系统调用,最常见的情况自然是 IO 有问题,排查网络交互均正常,内存看不出问题,加上磁盘本身就存在区别,程序又有大量的日志输出,监控 agent 一直槽点多多,怀疑的重点转移到磁盘读写这块儿了,刚好磁盘的监控确实也有点不同(下图中下方红线部分):

A5B1559E-2447-476A-8BB1-BC8712399D0A.png

由于代码都是原来的一套,在原来的机器上也没有问题,那么性能更强的新机器上,出问题的概率也很小,那么问题很可能是出现在读日志的监控 agent 上了,鉴于新agent 属于第一次使用,本身 cpu 占用率也很高,在各项指标都指向它且没有强力证据自证清白,只好当仁不让的背起了这口锅(当然最后事实证明并不是 agent 的原因,记录事情发展经过)。排除 agent 的可能后,再回过头来反复观察对比各项监控(top,vmstate,iostate,sar 以及系统监控等),除了cpu.system,cpu.context_switch,load 等始终存在区别,其它指标时常反复,没有什么线索。考虑到已花了较多的时间,而且只有固定的几台有问题,干脆在机型替换时直接换掉,一了百了。

但问题在替换机器之后发生了很大的变化,如下图:

DBEDD890-94AF-491F-A44B-917B25C6CBC3.png

在替换之前,只有固定的机器在高峰期耗时变长,而替换之后,原本正常的机器也随机的开始不正常,并且在高峰过后,这种不正常的状况会一直持续,无法自行恢复,仿佛进入了一种混沌的状态。这下问题就有点严重了。

重新集中观察对比各项监控指标,除了system 和 context_switch 之外,此时又观察到 softirq 以及 migration 进程存在区别。同时,设计实验证明,在异常时,并非整机有问题,而是部分请求(或者说部分进程)出现长耗时,而同期其它请求正常,在继续排除了 NGXIN PHP 等常驻进程的问题后,问题范围可以缩小一些了,猜测系统配置问题的可能性较大,只是是哪个,还得找。

继续实验的同时,借助 Google,将各种已明确的指标和现象抽出关键字各种搜索,终于,很幸运找到了一篇文章,大意是从 CentOS 6 向 CentOS 7 迁移后,也发生了性能下降的问题,显著的表现也是 context_switch 和 softirq 指标显著不同,load 存在区别,性能不及预期的情况,这位仁兄在历经各种心酸血泪(各种更新内核,更换 OS重装,重装驱动等等)之后才解决,初读并没觉得和我们是一个问题,再次阅读的时候,发现其中指标差异有点类似,抱着试试的态度,尝试了一下他的解决方案,问题解决!

问题解决

方案得来不易,怎么做的就不直接剧透了,和 NUMA Balancing 有关,想了解具体情况的,点击博客贡献访问量吧。

关于 NUMA,其中一篇介绍如下:
NUMA架构的CPU — 你真的用好了么? - CSDN博客