Patch docker-compose 防止启动时卡住

注意

本文针对的是 Docker Compose V1(Python 版,命令是 docker-compose),可能和您使用的 Docker Compose 版本不同。目前 Docker Compose 的最新版本是 V2(Go 语言版,命令是 docker compose)。更多信息请参阅官方文档 History and development of Docker ComposeMigrate to Compose V2

当 Linux 系统随机数熵不足时,docker-compose 命令在初始化时会一直等待,导致卡住几秒甚至几分钟才有反应。本文通过给相关代码打补丁来解决这个问题。本文内容主要复制/翻译洒家在这个 issue 中的评论。

TL;DR

如果你只在 Linux 本机使用 docker-compose,不使用 docker-compose 的远程功能(例如 -H 参数,DOCKER_HOST 环境变量),按下面方式 patch 1 行代码即可解决问题。

首先定位 docker-compose 使用的 docker 库文件位置。根据 Python 的版本,一般位于 /usr/local/lib/[YOUR_PYTHON_VERSION]/dist-packages/docker/transport/__init__.py。如果找不到,可以按下文提到的方法定位此文件。

编辑此文件,将 from .sshconn import SSHHTTPAdapter 替换为 SSHHTTPAdapter = None 即可。

try:
    # add this line
    SSHHTTPAdapter = None

    # # comment this line
    # from .sshconn import SSHHTTPAdapter
except ImportError:
    pass

运行 docker-compose version 测试,你会发现再也不会卡了,而且在低配机器上启动速度还快了不少。

附:顺腾摸瓜定位文件

以洒家的某台虚拟机为例,运行命令:

# docker-compose version
docker-compose version 1.25.4, build unknown
docker-py version: 3.7.3
CPython version: 2.7.17
OpenSSL version: OpenSSL 1.1.1  11 Sep 2018

docker-compose 是在 Python 2.7.17 上安装的。用相同版本的 Python 运行 pip:

# python2 -m pip show docker
Name: docker
Version: 3.7.3
Location: /usr/local/lib/python2.7/dist-packages

根据上述命令输出的 Location,找到目标文件 /usr/local/lib/python2.7/dist-packages/docker/transport/__init__.py

附:暴力定位文件

搜索整个根目录:

# find / -type f -name __init__.py 2>/dev/null | grep docker/transport/__init__.py
/usr/local/lib/python2.7/dist-packages/docker/transport/__init__.py

解释

即使只是运行一条简单的 docker-compose version 命令,docker-compose 也会加载所有可能用到的库。docker-compose 依赖 docker 库,为了实现远程 SSH 功能,docker 库依赖 paramiko 库,paramiko 又依赖 pynacl 库。pynacl 是对 libsodium 的封装。在 libsodium 初始化的时候,系统随机数熵(entropy)需要高于 160,否则会一直等待。

查看当前系统随机数熵值:

# cat /proc/sys/kernel/random/entropy_avail
105

使用性能分析工具可以看到各种库的加载过程,以及卡住的位置:

# python2 -m cProfile `which docker-compose` --version
docker-compose version 1.24.1, build 4667896
         130382 function calls (126780 primitive calls) in 112.339 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 <string>:1(ArgInfo)
        ......
        2    0.001    0.000  112.085   56.043 __init__.py:15(<module>)
        1    0.001    0.001  112.142  112.142 __init__.py:20(<module>)
        1    0.005    0.005  112.152  112.152 auth.py:1(<module>)
        2    0.001    0.000  112.153   56.077 build.py:1(<module>)
        2    0.003    0.001  112.211   56.106 client.py:1(<module>)
        1    0.000    0.000  112.144  112.144 decorators.py:1(<module>)
        1    0.001    0.001  112.343  112.343 docker-compose:3(<module>)
        1    0.000    0.000  112.087  112.087 ed25519key.py:17(<module>)
        1    0.003    0.003  112.333  112.333 main.py:1(<module>)
        1    0.000    0.000  112.086  112.086 signing.py:15(<module>)
        1    0.000    0.000  112.084  112.084 sodium_core.py:21(_sodium_init)
        1    0.000    0.000  112.084  112.084 sodium_core.py:27(sodium_init)
        1    0.000    0.000  112.142  112.142 sshconn.py:1(<module>)
        1    0.000    0.000  112.143  112.143 tls.py:1(<module>)
        1    0.004    0.004  112.139  112.139 transport.py:22(<module>)
        4    0.000    0.000  112.144   28.036 utils.py:1(<module>)
        1  112.084  112.084  112.084  112.084 {built-in method sodium_init}
        1    0.000    0.000  112.084  112.084 {method 'init_once' of 'CompiledFFI' objects}
        ......

因此,直接 patch 代码,防止这一堆和 SSH、密码学有关的不必要的库加载和初始化即可解决问题。洒家测试过的 docker 库版本有:3.7.34.2.0

另一种方案:安装 haveged

Haveged 可以解决在某些情况下,系统熵过低的问题。在 Debian、Ubuntu 等系统直接安装即可:

apt-get update && apt-get install -y haveged

Comments

您可以匿名发表评论,无需登录 Disqus 账号,勾选“我更想匿名评论”后,姓名和电子邮件分别填写“匿名”和“someone@example.com”然后发表评论即可。您也可以登录 Disqus 账号后发表评论。您的评论可能需要经过我审核后才能显示。点赞投票按钮(Reactions)无需登录即可点击。Disqus 评论系统在中国大陆可能无法正常加载和使用。

License

Creative Commons License

本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).

Top