Docker容器的方便都有体会吧,随着各种AI/ML/LLM的普及,大模型时代已经到来,试想一下,容器内安装个PyTorch包就上G大小,再加上CUDA,最终的镜像5G已经打不住了,这都还不算模型本身,是不是恐怖如斯!
不仅仅是大模型的容器镜像,我们常用的APP进出环境镜像,比如Tomcat,Python,Nodejs等也都是轻松几百M大小,即使使用私有镜像仓库,每次更新镜像也会给带宽和流量产生一定的压力,所以,对镜像进行瘦身可以考虑到日程上了。
也都希望我们都使用着几十M的小Image,更新,部署时轻轻松松迅速拉起来!要知道这种精简后的Docker镜像可以大大提高应用部署效率和减少资源的占用!
之前我也尝试过对容器镜像的优化和缩减:
比如减少层级,使用更轻量级的基础镜像(Alpine及各种slim镜像),清理不必要的安装包和删除临时文件和日志。如Dockerfile中:
FROM alpine:latest
...
RUN hash -r \
&& apt-get clean \
&& rm -rf \
/var/lib/apt/lists/* \
/tmp/* \
/var/tmp/* \
/usr/share/man \
/usr/share/doc \
/usr/share/doc-base
以上操作确实可以减少一些镜像的大小,但是缩减比例还是有限,今天就给大家介绍一款在容器镜像瘦身这块的超级“狠角色”:slim (
https://github.com/slimtoolkit/slim/),
Slim 不会更改容器映像中的任何内容,也不需要修改Dockerfile,瘦身后的容器更小、也更安全(减少CVE),官方称普遍可以缩小镜像30倍左右。
哈哈,既然是这么牛的工具,赶紧拿来折腾折腾。
我这里测试使用了Slim的两种安装方式:容器和二级制文件
容器方式安装
$ cat docker-compose.yml
services:
dslim:
# image: dslim/slim:latest
image: dslim/docker-slim:latest
container_name: dslim
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
二进制可执行文件方式安装
wget https://github.com/slimtoolkit/slim\
/releases/download/1.40.11/dist_linux.tar.gz
tar zxvf dist_linux.tar.gz
解压后这样:
$ ls -tlr
总计 54864
-rwxr-xr-x 1 fisher fisher 6012928 3月 3 2024 slim-sensor
-rwxr-xr-x 1 fisher fisher 50143232 3月 3 2024 slim
lrwxrwxrwx 1 fisher fisher 4 3月 3 2024 docker-slim -> slim
命令行操作
>>> help
NAME:
slim - inspect, optimize and debug your containers!
USAGE:
slim [global options] command [command options] [arguments...]
VERSION:
linux/amd64|Transformer|1.40.11|1b271555882eacdfb4e6598d6d0552e9b9b1449b|2024-02-02_01:36:22PM
COMMANDS:
xray, x Shows what's inside of your container image and reverse engineers its Dockerfile
lint, l Analyzes container instructions in Dockerfiles
build, b Analyzes, profiles and optimizes your container image auto-generating Seccomp and AppArmor security profiles
merge, m Merge two container images (optimized to merge minified images)
images, i Get information about container images
registry, r Execute registry operations
vulnerability, vuln Execute vulnerability related tools and operations
profile, p Collects fat image information and generates a fat container report
version, v Shows slim and docker version information
appbom, a Show application BOM
help, h Show help info
update, u Updates slim
install, in Installs slim
run, r Run one or more containers
debug, dbg Debug the target container from a debug (side-car) container
internal.metadata:
docker-cli-plugin-metadata Plugin metadata for the docker cli
OK,接下来的例子会对我们常见的三种镜像进行瘦身,分别是Nginx,Python3,Tomcat,然后我们来看看最后的瘦身效果究竟如何!
Nginx镜像瘦身
首先从hub官网下载latest镜像,大小为192M
$ docker ps | grep nginx
nginx latest 60c8a892f36f 6 weeks ago 192MB
使用compose测试下Nginx原始镜像是否工作正常
$ cat docker-compose.yml
services:
nginx-slim-test:
image: nginx:latest
#image: nginx.slim:latest
container_name: nginx-slim-test
restart: always
tty: true
stdin_open: true
ports:
- "8005:80"
$ docker compose up -d
然后测试http访问
curl localhost:8005
可以看到运行正常。
现在开始对镜像进行瘦身,执行命令:
./slim build nginx:latest
查看下当前镜像情况,镜像后面添加了一个.slim,然后镜像从192M缩减到了13.3M:
$ docker images | grep nginx
nginx.slim latest bd36a 2 hours ago 13.3MB
nginx latest 60c8a 6 weeks ago 192MB
瘦身之后的Nginx镜像能否正常运行呢?使用docker-compose启动试试看
可以看到Nginx运行正常
瘦身后只有13.3M的镜像里面还能有点啥呢?使用之前的bash想进去看看,然后发现很多系统基础的命令都没有,比如ls,cat,df,top等,这Slim真是“够狠”!
Python镜像瘦身
从hub上下载最新的python:3.13镜像
$ docker pull python:3.13.0
原始的python3.13镜像大小居然1.02G!
$ docker images | grep python
python 3.13.0 c41ea8273365 4 weeks ago 1.02GB
不能只是简单测试python命令吧,不太科学,这回我们简单模式一个使用python3启动web服务的例子,以python:3.13.0为基础镜像,使用flask启动一个webserver。
$ cat requirements.txt
wheel==0.45.0
Jinja2==3.1.2
requests==2.32.3
simplejson==3.19.3
Flask==3.1.0
python启动文件
$ cat app.py
from flask import Flask, render_template_string
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template_string('''
<!DOCTYPE html>
<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
''')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
再弄一个简单的Dockerfile,把依赖和app.py打进去,让容器自动启动,并监听8000端口
FROM python:3.13.0
MAINTAINER fisher <fisher@mymailbox.com>
WORKDIR /root/
COPY requirements.txt /tmp/requirements.txt
COPY app.py /root/app.py
RUN pip3 install -r /tmp/requirements.txt \
-i https://pypi.tuna.tsinghua.edu.cn/simple
EXPOSE 8000
CMD ["python3", "app.py"]
RUN hash -r \
&& apt-get clean \
&& rm -rf \
/var/lib/apt/lists/* \
/tmp/* \
/var/tmp/* \
/usr/share/man \
/usr/share/doc \
/usr/share/doc-base
使用 docker build制作可以启动web服务的镜像,别担心,本文绝对原创,明显这里镜像名字使用了Django,其实我本来是想安装django包来启动web服务的,不过Django框架启动web服务稍显麻烦,还得创建project/app之类的,所以偷懒使用Flask来启动。
docker build --force-rm -t python:djangotest-v01 .
可以看到安装了Flask几个依赖之后,镜像从1.02G涨到了1.04G
然后对镜像进行瘦身,这里同样是有两种方式:
- 从 Dockerfile 瘦身,缩减镜像大小
新镜像名字tag设置为:
python:djangotest-slim-v01
$ /data/codes/dslim-slim/dist_linux/slim build \
--expose 8000 \--dockerfile ./Dockerfile \
--tag python:djangotest-slim-v01 .
- 从之前使用docker build制作的镜像基础上进行瘦身
/data/codes/dslim-slim/dist_linux/slim build \
--expose 8000 python:djangotest-v01
看看瘦身之后的Python镜像吧,我和我的小伙伴都惊呆了!
1.04G --> 55.8M,大小缩减186倍!
两种方式瘦身之后的镜像大小差不多,都是55.8M
$ python3
Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 10400 / 55.8
186.3799283154122
使用curl测试压缩后的python镜像,能够正常返回"Hello,World!"
最后我们再看看针对Java环境的Tomcat容器的镜像精简情况:
下载tomcat镜像(官方镜像441M)
docker pull tomcat:9.0.97-jdk17
tomcat 9.0.97-jdk17 8fffb96a86f6 9 days ago 441MB
使用slim build直接对镜像进行瘦身
/data/codes/dslim-slim/dist_linux/slim \
build tomcat:9.0.97-jdk17
瘦身后的镜像大小对比:
441M --> 196M
看来Jvm运行环境缩减比例不是很明显,其实如果包括完整JDK的话就将近200M了,想想能压缩成这样也就能够理解了。
这里看瘦身后的容器居然可以bash进来,同样的很多命令都被精简掉了。
每次slim执行之后都会有日志可以查看,日志保存在slim.report.json文件中。
注意
容器瘦身最后得重点来了!
实际测试过程中发现,如果镜像没有对外暴露端口,需要使用【--http-probe=false】参数,但是你会意外的发现镜像会被精简的面目全非,之前的python-3.13版本原来1.02G,缩减之后只有不到30M,python命令本身都无法运行。
前面之所以要启动web服务,是因为slim在缩减镜像是要先把服务起来,这样会加载web服务所有必须的依赖,比如运行web服务的二进制命令(nginx),C的类库,python的依赖包,Java的各种jar,将其保留下来,然后其他的没有用到的几乎都被“优化”掉了,包括系统基本命令和lib库。
所以使用Slim对容器镜像进行瘦身之后还需要经过全面的测试,确保各种必须的依赖没有被删减,在瘦身成功的同时保证服务的正常运行!
(本文完)
Tags:docker 强制删除镜像