太多的 service 信息环境变量可能会导致容器中执行 bash 命令特别的慢

前言

当集群里创建了特别多的 service 时可能会导致容器中 bash 启动特别慢, 原因是 service 相关的环境变量信息太多了。

对于容器内出现的下面这些情况也可以考虑是这个环境变量太多导致的问题:

  • 对于 sh 环境,可能会因为这个导致程序无法启动,出现 exec /bin/sh: argument list too long 错误。
  • 出现管道操作执行特别慢的情况,即便是简单的类似 echo hello | cat 的管道操作也特别慢。
  • 脚本中每加一个管道整个脚本的执行时间就会变慢一倍。

解决方法

先说解决方法:可以考虑通过配置 pod spec 增加 enableServiceLinks: false 禁用自动注入 service 信息到环境变量的方式来加速 bash 启动速度。 详见: Service-environment variables should be optional · Issue #60099 · kubernetes/kubernetes

下面再说一下 debug 方法。

DEBUG

通过下面的 DEBUG 方法发现导致容器中执行 bash 特别慢是因为环境变量太多导致的。

模拟环境

测试环境变量比较少的时候启动 bash 的时间:

$ docker run -it --rm ubuntu:16.04 bash
# env |wc -l
8

# time bash -c exit

real        0m0.003s
user        0m0.000s
sys 0m0.000s

可以看到启动速度是非常快的。下面模拟一下环境变量特别多的环境:

# for i in {1..10000}; do export "SERVICE_$i"="$i"; done
# env |wc -l
10008

看一下在环境变量特别多的环境下的 bash 启动时间:

# time bash -c exit

real        0m0.395s
user        0m0.380s
sys 0m0.000s

可以看到速度慢了 100 多倍。

下面来 debug 一下速度慢的真正原因,是否真的是环境变量特别多导致的。

debug 问题原因

先安装一下 strace 命令:

# apt-get update
# apt-get install strace

然后通过 strace 命令来对比不同启动时间下的差别 (实际上一般会先通过 -c 来统计系统调用时间,这里通过 -c 无法得到实际原因所以就没写这个步骤了):

# strace -T -o result_slow.log -- bash -c exit

# 实际场景下一般是到没有问题的容器中去获取 result_fast 的结果
# for i in {1..10000}; do unset "SERVICE_$i"; done
# strace -T -o result_fast.log -- bash -c exit

通过对比 result_fast.log 和 result_slow.log 发现有个比较大的差别是 execve 系统调用的参数不一样并且花费的时间也有很大差别:

# result_slow.log
execve("/bin/bash", ["bash", "-c", "exit"], [/* 10008 vars */]) = 0 <0.002256>

# result_fast.log
execve("/bin/bash", ["bash", "-c", "exit"], [/* 8 vars */]) = 0 <0.000345>

通过查看 文档 可知 execve 系统调用的第三个参数传入的是环境变量:

int execve(const char *pathname, char *const argv[],
           char *const envp[]);

也就是环境变量的数量不一样导致 execve 所花的时间有很大差异。可以通过执行 strace 命令的时候加一个 -v 参数来查看这个 /* 10008 vars */ 的具体内容来验证:

execve("/bin/bash", ["bash", "-c", "exit"], ["SERVICE_9928=9928", "SERVICE_9911=9911", ... , "SERVICE_76=76", "_=/usr/bin/strace"]) = 0 <0.001230>

总结

当集群里创建了特别多的 service 并且介意由此导致的 bash 启动特别慢的问题时,可以考虑通过配置 pod spec 增加 enableServiceLinks: false 禁用自动注入 service 信息到环境变量的方式来加速 bash 启动速度。


Comments