龙空技术网

剥茧抽丝:反向工程Docker镜像

虫虫安全 901

前言:

当前朋友们对“ondrejphp”都比较注意,你们都需要了解一些“ondrejphp”的相关知识。那么小编同时在网摘上收集了一些对于“ondrejphp””的相关内容,希望你们能喜欢,兄弟们快快来学习一下吧!

现在越来越多的企业基于容器和云来构建自己的基础架构,管理的容器越来越多,时而会遇到一些容器镜像不知道是干嘛的?容器运行时参数是什么?容器跑的应用是什么?针对这些疑问今天就是学习如何剖析在线的容器及其镜像的构造和内容。

镜像内容

首先容器镜像实际上是一个tarball打包的文件。

我们可以用docker save把镜像输出为tar文件。

docker save vbkunin/itop:2.7.0 >/tmp/itop.tar

然后解开该tar包

tar xvf itop.tar

解开后目录中,是一层层的镜像哈希命名的目录,每一层(一个目录)有三个文件VERSION、json和layer.tar,总体还有一个manifest.json,一个配置json和repositories

我们首先看看manifest.json的内容,格式混乱不便理解,为了查看其内容我们使用jq(虫虫之前的文章专门介绍过)。

jq . manifest.json

可见itop 2.7.0这个镜像由25层构成。我们再来看看配置的json里面有什么内容?

jq . ecd46ce38fdec1e882d665ecdc1831e25986080676529951fa6663a4bab6b577.json

文件内容较长,我们看最前面部分:

可以用jq keys列出其配置项目里面的键:

jq keys ecd46ce38fdec1e882d665ecdc1831e25986080676529951fa6663a4bab6b577.json

包括容器基本信息项目 architecture、author、config、container、container-config、created、docker_version、history、os和rootfs,其中最重要是中间的history部分,列出了镜像中的每层构成,基本上对应Dockerfile中的每一个条语句,一句对应一层,然后整个堆叠起来就构成了完整的镜像。

jq .history ecd46ce38fdec1e882d665ecdc1831e25986080676529951fa6663a4bab6b577.json

我们常常看到Dockerfile中会把一系列的操作都放在一起,就是为了减少执行的语句,减少层数从节省空间,比如本镜像中最关键安装步骤为:

{"created": "2020-04-26T15:46:29.315124814Z","author": "Vladimir Kunin <vladimir@knowitop.ru>","created_by": "|2 ITOP_FILENAME=iTop-2.7.0-1-5541.zip ITOP_VERSION=2.7.0-1 /bin/sh -c apt-get install -y software-properties-common && add-apt-repository -y ppa:ondrej/php && apt-get update && apt-get install -y apache2 php7.3 php7.3-xml php7.3-mysql php7.3-json php7.3-mbstring php7.3-ldap php7.3-soap php7.3-zip php7.3-gd php-apcu graphviz curl unzip git && apt-get clean && rm -rf /var/lib/apt/lists/* && update-alternatives --set php /usr/bin/php7.3"},

有了history部分的内容,根据这些内容,基本就可以完整的重构Dockerfile。

但是我们还缺少一个基本From基础镜像,上面的内容中我并没有这部分内容。我们从 manifest.json文件中审查第一个镜像层为:

0bd213ebb246d391914dc365aebe90da94db49690f0e1130611e68e344ab4ee1,看看该层的构成:

tar tf 0bd213ebb246d391914dc365aebe90da94db49690f0e1130611e68e344ab4ee1/ layer.tar

大体上估算,这是一个基础Linux镜像,还有如下内容

解开包

tar xf ../layer.tarcat etc/lsb-release

这可以作为Dockerfile的基础镜像,作为From语句的内容了。

自动工具dive

以上步骤实际上很复杂,学习来用还可以,但是实践中这样搞,太过蛮烦。没关系这自然有其他人已经解决了,有专门的工具帮你自动分析,这个工具叫dive,一个用golang开发的工具,可以用分析探索Docker镜像,各层内容以及优化缩小Docker/OCI镜像大小。该工具在GitHub开放,目前已经25800星。

安装

安装很简单,从github下载个发行版的对应二进制包,然后安装即可。

使用

使用更简单

dive <image-tag>

比如我们分析第一步的itop镜像:

dive vbkunin/itop:2.7.0

就进入交互界面,可以用键盘上下键,移动到各层查看对应的资源:

左上窗体Layer列出各个层,键盘可以移动。

左边中间窗体是层细节内容,包括Tags、Digest和Command。

左边下边窗体是镜像细节内容其中潜在的空间浪费(Potential wasted space)和镜像效率分(Image efficiency score)可以用来分析镜像和优化参考。

右边窗体是具体内容文件系统。

还有一个特色功能是和CI结合用于在自动镜像打包的质量分析,详见官方文档。

运行时参数

最后说下容器的运行时参数,这个很重要,通过运行时参数设置,给一个镜像配置了具体个性化的参数,这样运行起来才是真正的容器,才有使用价值。

很多时候大家都是直接复制运行命令执行的,过了一段时间后就忘记当初怎么运行的,要对重新启用或者要修改一些参数再运行就成了个问题。

Docker运行时参数保存在宿主机文件中的,默认情况下位置为:

/var/lib/docker/containers/容器ID

hostconfig.json和config.v2.json两个配置文件,其中config.v2.json就是 docker inspect的内容。

当然这两个配置文件都添加额外的,内容要直接还原为启动命令比较费劲,需要借助两个工具来实现rekcod和runlike,两个工具一个用js,一个用python开发(应该搞个golang版的)安装依赖都太多,容易搞乱环境,建议用docker方式使用。

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nexdrew/rekcod <容器ID>docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike <容器ID>

就能比较准确还原出出大概的启动参数了(有些额外多余的参数)。当然还有个方法就是用容器编排系统,比如K8s 或者基于docker-compose启容器,因为这都是基于配置文件的,就不怕忘记配置参数了。

标签: #ondrejphp