你的 Python 镜像还好吗?

随着 k8s 集群规模变大,以及弹性扩容的触发,导致 harbor 仓库的瓶颈问题逐渐暴露出来,目前也做了一些优化措施:

  1. 增大 harbor ECS 规格,网络带宽会增加
  2. 使用阿里 dragonfly p2p 镜像分发技术,多次测试下来难以应对大数据量分发场景,暂时放弃了
  3. 增加 Harbor Proxy Cache 提高网络并发
  4. 镜像优化,减少体积
  5. 镜像提前预热分发,目前正在考虑方案

在做镜像优化的时候,发现之前我们的 python 提速方案还有一些瑕疵,所以有了这里的分析。
在 Python 项目构建 Docker 镜像时,经常遇到项目源码才几百 KB,但是安装的依赖能高达数百 MB,甚至 GB,这不仅使镜像体积变的很大,而且因为网络安装依赖包导致严重拖慢构建速度。

项目环境:

注意:这里仅关系依赖问题,所以忽略源码部分

# tree
.
├── code
│   └── requirements.txt
└── Dockerfile
# cat code/requirements.txt 
oss2==2.9.0
keras==2.3.1
numpy==1.18.1
Pillow==5.2.0
Flask==1.1.1
requests==2.22.0
gevent==1.4.0
gunicorn==19.9.0
tensorflow==2.0.0b1

常规构建:

示例 1:

# cat Dockerfile 
FROM python:3.6.8

COPY code/requirements.txt /code/requirements.txt

RUN set -eux \
    && pip3 install --no-cache-dir -r /code/requirements.txt \
    && rm -rf /tmp/* 

相当糟糕,依赖安装了 713MB,花了 5 分钟,太慢了。

# time docker build -t test:v1.0 .
real    5m6.002s
user    0m0.089s
sys     0m0.073s
# docker history test:v1.0              
IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
fbdec4c33473        About a minute ago   /bin/sh -c set -eux     && pip3 install --no…   713MB               
...

如何提速:

  1. 使用内网 pip 源代替公网源
  2. 提前安装 pip 依赖,我们这里选择该方案

基于刚才已经构建过的依赖镜像,再次去构建时间工作的镜像
示例 2:

# cat Dockerfile 
FROM test:v1.0

COPY code /code

RUN set -eux \
    && pip3 install --no-cache-dir -r /code/requirements.txt \
    && rm -rf /tmp/* 

EXPOSE 8080

CMD ["xxx"]

为什么这里再次安装 requirements.txt,直接删掉该步骤不就好了吗?
实际情况下,研发人员可能会在仓库中修改 requirements.txt,如果这里不再次安装下,最终发版可能就要提示缺少包而报错了。这里再次安装下依赖,就是防止该问题。

再次构建,相当好,8 秒,再次安装的依赖层 0B(我们这里没有模拟研发人员修改 requirements.txt 的情况),我们取得了巨大的进步。

# time docker build -t test:v2.0 .
real    0m8.205s
user    0m0.063s
sys     0m0.056s
# docker history test:v2.0
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
c2375fd5cd9e        44 seconds ago      /bin/sh -c set -eux     && pip3 install --no…   0B                  
...
fbdec4c33473        9 minutes ago       /bin/sh -c set -eux     && pip3 install --no…   713MB               
...

3 个月以后

这个词有点像电影里的镜头,以示例 2 中的 Dockerfile 再次构建:
示例 3:

# time docker build -t test:v3.0 .
real    2m3.205s
user    0m0.063s
sys     0m0.056s
# docker history test:v3.0
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
c2375fd5cd9e        44 seconds ago      /bin/sh -c set -eux     && pip3 install --no…   173MB                  
...
fbdec4c33473        3 months ago        /bin/sh -c set -eux     && pip3 install --no…   713MB               
...

问题来了,随着岁月的流逝,构建时间怎么变成了 2 分钟,再次安装的依赖层 173MB(我们这里没有模拟研发人员修改 requirements.txt 的情况)

为什么呢?

原来安装依赖的时候除了上面 requirements.txt 中写好的依赖,还有一些没有出现在 requirements.txt 中的依赖,而这些依赖的版本如 grpcio>=1.8.6 会随着时间的推移,而产生不同的版本
...
Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.6/site-packages (from tensorflow==2.0.0b1->-r /code/requirements.txt (line 9)) (1.34.1)
Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.6/site-packages (from tensorflow==2.0.0b1->-r /code/requirements.txt (line 9)) (0.33.4)
...

如何解决:

  1. 将所有依赖一个不少的写入 requirements.txt 中,应该比较麻烦
  2. 做一个定时任务,每周重新构建一次依赖层镜像
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容