在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于Docker鏡像逆向生成Dockerfile

馬哥Linux運維 ? 來源:博客園一燙雜貨鋪 ? 2025-03-10 09:45 ? 次閱讀

通過研究Docker鏡像的內(nèi)部結(jié)構(gòu),對Docker鏡像進(jìn)行逆向工程。

在本文中, 我們將通過理解Docker鏡像如何存儲數(shù)據(jù), 以及如何使用工具查看鏡像方方面面的信息來逆向工程一個Docker鏡像; 以及如何使用Python的Docker API來構(gòu)建一個類似Dedockify的工具來創(chuàng)建Dockerfile。

簡介

隨著Docker Hub和TreeScale等公共Docker Registry變得越來越流行,管理員和開發(fā)人員下載不明來源的Docker鏡像變得越來越常見。在大多數(shù)情況下,便利性勝過可預(yù)知的風(fēng)險。在通常情況下,當(dāng)一個Docker鏡像被發(fā)布后,它會直接出現(xiàn)在列表中、git倉庫中或通過相關(guān)鏈接提供。有時候鏡像并沒有提供Dockerfile。即使提供了Dockerfile,我們也很難保證預(yù)構(gòu)建的鏡像就是由給出的Dockerfile構(gòu)建的,這些鏡像對于我們而言, 就是一個黑盒子,我們甚至無法保證其使用的安全性。

也許您并不關(guān)心安全漏洞, 您可能只是想更新平時用的比較多的鏡像, 例如nginx,使其能夠運行在最新版本的Ubuntu上。又或者,你會想要發(fā)布一個更優(yōu)化的鏡像,因為另一個發(fā)行版的編譯器更適合在編譯時生成二進(jìn)制文件。

不管什么原因,我們都需要將鏡像恢復(fù)成Dockerfile的選項。Docker鏡像并不是一個黑盒子。重建Dockerfile所需的大部分信息都可以被檢索到。通過觀察Docker鏡像內(nèi)部并檢查其內(nèi)部結(jié)構(gòu),我們將能夠從一個任意的預(yù)編譯容器中重建一個Dockerfile。

在本文中,我們將展示如何使用兩個工具從鏡像中重建Dockerfile: 前面提及的Dedockify是一個的Python腳本,Dive是一個Docker鏡像瀏覽工具。使用的基本流程如下。

0fef00c8-fa65-11ef-9310-92fbcf53809c.png

使用 Dive

1010f228-fa65-11ef-9310-92fbcf53809c.gif

Dive demo

為了快速了解鏡像是如何組成的,我們將使用Dive學(xué)習(xí)一些高級的、可能對我們來說不熟悉的Docker概念。Dive工具可以檢查Docker鏡像的每一層(Layer)。

讓我們創(chuàng)建一個簡單的Dockerfile,用于測試。

把這個代碼段直接貼到裝有Docker的linux主機命令行中:

mkdir $HOME/test1
cd $HOME/test1
cat > Dockerfile << EOF ; touch testfile1 testfile2 testfile3
FROM scratch
COPY testfile1 /
COPY testfile2 /
COPY testfile3 /
EOF

輸入以上內(nèi)容并回車,我們就創(chuàng)建了一個新的Dockerfile,并在同一目錄下填充了3個零字節(jié)的測試文件。

$ ls
Dockerfile  testfile1  testfile2  testfile3

現(xiàn)在,讓我們使用這個Dockerfile構(gòu)建一個鏡像,并標(biāo)記為example1。

docker build . -t example1

構(gòu)建example1鏡像時會產(chǎn)生以下輸出:

Sending build context to Docker daemon  3.584kB
Step 1/4 : FROM scratch
 --->
Step 2/4 : COPY testfile1 /
 ---> a9cc49948e40
Step 3/4 : COPY testfile2 /
 ---> 84acff3a5554
Step 4/4 : COPY testfile3 /
 ---> 374e0127c1bc
Successfully built 374e0127c1bc
Successfully tagged example1:latest

現(xiàn)在我們剛構(gòu)建的example1鏡像就已經(jīng)完成了:

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example1            latest              374e0127c1bc        31 seconds ago      0B

由于沒有可執(zhí)行文件,所以該鏡像將無法運行。我們僅將其作為一個簡化的示例,說明如何在Docker鏡像中查看存儲層(Layer)。

我們可以從鏡像的大小看出,這里沒有源鏡像。我們使用scratch來代替源鏡像,它讓Docker使用一個零字節(jié)的空白鏡像作為源鏡像。然后,我們通過復(fù)制三個額外的零字節(jié)測試文件來修改空白鏡像,并將修改標(biāo)記為example1。

現(xiàn)在,讓我們用Dive來查看這個新鏡像。

docker run --rm -it 
    -v /var/run/docker.sock:/var/run/docker.sock 
    wagoodman/dive:latest example1

執(zhí)行上述命令將自動從Docker Hub拉取wagoodman/dive鏡像,并產(chǎn)生Dive的輸出。

Unable to find image 'wagoodman/dive:latest' locally
latest: Pulling from wagoodman/dive
89d9c30c1d48: Pull complete
5ac8ae86f99b: Pull complete
f10575f61141: Pull complete
Digest: sha256:2d3be9e9362ecdcb04bf3afdd402a785b877e3bcca3d2fc6e10a83d99ce0955f
Status: Downloaded newer image for wagoodman/dive:latest
Image Source: docker://example-image
Fetching image... (this can take a while for large images)
Analyzing image...
Building cache...

1045d560-fa65-11ef-9310-92fbcf53809c.jpg

在列表中上下選擇鏡像的三個圖層,在右側(cè)顯示的目錄樹中找到三個文件。

10636346-fa65-11ef-9310-92fbcf53809c.jpg

我們可以看到右側(cè)的內(nèi)容隨著選擇每一層而變化。當(dāng)每個文件被復(fù)制到一個空白的Docker從頭鏡像時,它被存儲為一個新的層。

1084badc-fa65-11ef-9310-92fbcf53809c.jpg

如果您注意到的話, 我們還可以看到生成每個層所使用的命令。我們還可以看到源文件和更新文件的哈希值。

如果我們注意到Command:部分,我們應(yīng)該看到以下內(nèi)容:

#(nop) COPY file:e3c862873fa89cbf2870e2afb7f411d5367d37a4aea01f2620f7314d3370edcc in /
#(nop) COPY file:2a949ad55eee33f6191c82c4554fe83e069d84e9d9d8802f5584c34e79e5622c in /
#(nop) COPY file:aa717ff85b39d3ed034eed42bc1186230cfca081010d9dde956468decdf8bf20 in /

每個命令都提供了Dockerfile中用于生成鏡像的原始命令。但是,原始文件名丟失了。看來恢復(fù)該信息的唯一方法是觀察目標(biāo)文件系統(tǒng)的變化,或者根據(jù)其他細(xì)節(jié)進(jìn)行推斷。稍后再詳述。

Docker History

除了像dive這樣的第三方工具之外,我們還可以隨手使用的工具是docker history。如果我們在example1鏡像上使用docker history命令,就可以查看我們在Dockerfile中創(chuàng)建該鏡像時使用的條目。

docker history example1

運行玩應(yīng)該得到以下結(jié)果:

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
374e0127c1bc        25 minutes ago      /bin/sh -c #(nop) COPY file:aa717ff85b39d3ed…   0B
84acff3a5554        25 minutes ago      /bin/sh -c #(nop) COPY file:2a949ad55eee33f6…   0B
a9cc49948e40        25 minutes ago      /bin/sh -c #(nop) COPY file:e3c862873fa89cbf…   0B

CREATED BY列中的所有內(nèi)容都被截斷了。這些是通過Bourne shell傳遞的Dockerfile指令。這些信息對于重新創(chuàng)建我們的Dockerfile可能很有用,雖然在這里被截斷了,但是我們也可以通過使用no-trunc選項來查看完整的信息:

$ docker history example1 --no-trunc
IMAGE                                                                     CREATED             CREATED BY                                                                                           SIZE                COMMENT
sha256:374e0127c1bc51bca9330c01a9956be163850162f3c9f3be0340bb142bc57d81   29 minutes ago      /bin/sh -c #(nop) COPY file:aa717ff85b39d3ed034eed42bc1186230cfca081010d9dde956468decdf8bf20 in /    0B
sha256:84acff3a5554aea9a3a98549286347dd466d46db6aa7c2e13bb77f0012490cef   29 minutes ago      /bin/sh -c #(nop) COPY file:2a949ad55eee33f6191c82c4554fe83e069d84e9d9d8802f5584c34e79e5622c in /    0B
sha256:a9cc49948e40d15166b06dab42ea0e388f9905dfdddee7092f9f291d481467fc   29 minutes ago      /bin/sh -c #(nop) COPY file:e3c862873fa89cbf2870e2afb7f411d5367d37a4aea01f2620f7314d3370edcc in /    0B

雖然這有一些有用的信息,但從命令行還原它可能還有些挑戰(zhàn)。我們也可以使用docker inspect。然而,在本文中,我們將專注于使用Python的Docker Engine API。

使用 Python Docker Engine API

Docker發(fā)布了一個針對Docker Engine API的Python庫,允許在Python中管理Docker。在下面的示例中,我們可以通過運行下面的Python 3代碼來恢復(fù)與docker history類似的信息:

#!/usr/bin/python3

import docker

cli = docker.APIClient(base_url='unix://var/run/docker.sock')
print (cli.history('example1'))

輸出結(jié)果如下:

[{'Comment': '', 'Created': 1583008507, 'CreatedBy': '/bin/sh -c #(nop) COPY file:aa717ff85b39d3ed034eed42bc1186230cfca081010d9dde956468decdf8bf20 in / ', 'Id': 'sha256:374e0127c1bc51bca9330c01a9956be163850162f3c9f3be0340bb142bc57d81', 'Size': 0, 'Tags': ['example:latest']}, {'Comment': '', 'Created': 1583008507, 'CreatedBy': '/bin/sh -c #(nop) COPY file:2a949ad55eee33f6191c82c4554fe83e069d84e9d9d8802f5584c34e79e5622c in / ', 'Id': 'sha256:84acff3a5554aea9a3a98549286347dd466d46db6aa7c2e13bb77f0012490cef', 'Size': 0, 'Tags': None}, {'Comment': '', 'Created': 1583008507, 'CreatedBy': '/bin/sh -c #(nop) COPY file:e3c862873fa89cbf2870e2afb7f411d5367d37a4aea01f2620f7314d3370edcc in / ', 'Id': 'sha256:a9cc49948e40d15166b06dab42ea0e388f9905dfdddee7092f9f291d481467fc', 'Size': 0, 'Tags': None}]

根據(jù)輸出結(jié)果,我們可以發(fā)現(xiàn), 如果重建Dockerfile的內(nèi)容, 只需要解析所有相關(guān)數(shù)據(jù)并將其順序反轉(zhuǎn)一下。但是正如我們之前看到的,我們也注意到在COPY指令中有一些被哈希(Hash)過的內(nèi)容。如前所述,這里的被哈希過的內(nèi)容代表從層外使用的文件名。這些信息無法直接恢復(fù)。然而,正如我們在 Dive 中看到的,當(dāng)我們搜索對該鏡像層所做的更改時,我們可以推斷出這些名稱。有時,在原始復(fù)制指令將目標(biāo)文件名作為目標(biāo)的情況下,也可以推斷出這些文件名。在其他情況下,文件名可能并不重要,允許我們使用任意文件名。而在其他情況下,雖然更難評估,但我們可以推斷出在系統(tǒng)中其他地方被反向引用的文件名,例如在腳本或配置文件等支持依賴中。但無論如何,搜索層之間的所有變化是最可靠的。

Dedockify

讓我們再深入幾步。為了更好地逆向該鏡像轉(zhuǎn)換為Dockerfile,我們需要解析所有內(nèi)容并將其重新格式化為可讀的形式。為了簡化我們的實驗, 以下代碼已經(jīng)可以從GitHub上的Dedockify倉庫獲取。感謝LanikSJ所有基礎(chǔ)工作和編碼。

from sys import argv
import docker

class ImageNotFound(Exception):
    pass

class MainObj:
    def __init__(self):
        super(MainObj, self).__init__()
        self.commands = []
        self.cli = docker.APIClient(base_url='unix://var/run/docker.sock')
        self._get_image(argv[-1])
        self.hist = self.cli.history(self.img['RepoTags'][0])
        self._parse_history()
        self.commands.reverse()
        self._print_commands()

    def _print_commands(self):
        for i in self.commands:
            print(i)

    def _get_image(self, img_hash):
        images = self.cli.images()
        for i in images:
            if img_hash in i['Id']:
                self.img = i
                return
        raise ImageNotFound("Image {} not found
".format(img_hash))

    def _insert_step(self, step):
        if "#(nop)" in step:
            to_add = step.split("#(nop) ")[1]
        else:
            to_add = ("RUN {}".format(step))
        to_add = to_add.replace("&&", "\
    &&")
        self.commands.append(to_add.strip(' '))

    def _parse_history(self, rec=False):
        first_tag = False
        actual_tag = False
        for i in self.hist:
            if i['Tags']:
                actual_tag = i['Tags'][0]
                if first_tag and not rec:
                    break
                first_tag = True
            self._insert_step(i['CreatedBy'])
        if not rec:
            self.commands.append("FROM {}".format(actual_tag))

__main__ = MainObj()

生成初始 Dockerfile

如果您已經(jīng)完成了這一步,那么在您實驗的主機上應(yīng)該有兩個鏡像:wagoodman/dive和我們自定義的example1鏡像。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example1            latest              374e0127c1bc        42 minutes ago      0B
wagoodman/dive      latest              4d9ce0be7689        2 weeks ago         83.6MB

在我們使用dedockify對example1鏡像中運行此命令,最終將產(chǎn)生以下結(jié)果:

$ python3 dedockify.py 374e0127c1bc
FROM example1:latest
COPY file:e3c862873fa89cbf2870e2afb7f411d5367d37a4aea01f2620f7314d3370edcc in /
COPY file:2a949ad55eee33f6191c82c4554fe83e069d84e9d9d8802f5584c34e79e5622c in /
COPY file:aa717ff85b39d3ed034eed42bc1186230cfca081010d9dde956468decdf8bf20 in /

我們提取到的信息與之前使用 Dive 解析鏡像時看到的幾乎一致。注意FROM指令顯示的是example1:late而不是scratch。在這種情況下,我們的代碼對基礎(chǔ)鏡像做出了不正確的假設(shè)。

作為對比, 我們對wagoodman/dive鏡像做同樣的處理.

$ python3 dedockify.py 4d9ce0be7689
FROM wagoodman/dive:latest
ADD file:fe1f09249227e2da2089afb4d07e16cbf832eeb804120074acd2b8192876cd28 in /
CMD ["/bin/sh"]
ARG DOCKER_CLI_VERSION=
RUN |1 DOCKER_CLI_VERSION=19.03.1 /bin/sh -c wget -O- https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_CLI_VERSION}.tgz |     tar -xzf - docker/docker --strip-component=1 
    &&     mv docker /usr/local/bin
COPY file:8385774b036879eb290175cc42a388877142f8abf1342382c4d0496b6a659034 in /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/dive"]

與example1相比,這個鏡像顯示了更多的錯誤。我們看到ADD指令就在FROM指令之前。我們的代碼再次做出了錯誤的假設(shè)。我們不知道ADD指令添加了什么。然而,我們可以直觀地做出假設(shè),即我們不確定基礎(chǔ)鏡像是什么。ADD指令可能是用來提取本地的tar文件到根目錄。也有可能是用這種方法加載另一個基礎(chǔ)鏡像。

Dedockify limitation testing

讓我們通過創(chuàng)建一個示例Dockerfile來進(jìn)行實驗,在這個示例中我們明確定義了基礎(chǔ)鏡像。和我們之前做的一樣,在一個空目錄下,直接從命令行運行下面的代碼。

mkdir $HOME/test2
cd $HOME/test2
cat > Dockerfile << EOF ; touch testfile1 testfile2 testfile3
FROM ubuntu:latest
RUN mkdir testdir1
COPY testfile1 /testdir1
RUN mkdir testdir2
COPY testfile2 /testdir2
RUN mkdir testdir3
COPY testfile3 /testdir3
EOF

然后build鏡像,將我們的新鏡像標(biāo)記為example2。這將創(chuàng)建一個與之前類似的鏡像,只不過不使用scratch,而是使用ubuntu:latest作為基礎(chǔ)鏡像。

$ docker build . -t example2
Sending build context to Docker daemon  3.584kB
Step 1/7 : FROM ubuntu:latest
 ---> 72300a873c2c
Step 2/7 : RUN mkdir testdir1
 ---> Using cache
 ---> 4110037ae26d
Step 3/7 : COPY testfile1 /testdir1
 ---> Using cache
 ---> e4adf6dc5677
Step 4/7 : RUN mkdir testdir2
 ---> Using cache
 ---> 22d301b39a57
Step 5/7 : COPY testfile2 /testdir2
 ---> Using cache
 ---> f60e5f378e13
Step 6/7 : RUN mkdir testdir3
 ---> Using cache
 ---> cec486378382
Step 7/7 : COPY testfile3 /testdir3
 ---> Using cache
 ---> 05651f084d67
Successfully built 05651f084d67
Successfully tagged example2:latest

由于我們現(xiàn)在有了一個稍微復(fù)雜一些的Dockerfile來重建,而且我們也有了生成這個鏡像所使用的Dockerfile,因此我們可以做一個對比。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
example2            latest              05651f084d67        2 minutes ago       64.2MB
example1            latest              374e0127c1bc        1 hour ago          0B
ubuntu              latest              72300a873c2c        9 days ago          64.2MB
wagoodman/dive      latest              4d9ce0be7689        3 weeks ago         83.6MB

運行dedockify腳本

$ python3 dedockify.py 05651f084d67
FROM ubuntu:latest
RUN /bin/sh -c mkdir testdir1
COPY file:cc4f6e89a1bc3e3c361a1c6de5acc64d3bac297f0b99aa75af737981a19bc9d6 in /testdir1
RUN /bin/sh -c mkdir testdir2
COPY file:a04cdcdf5fd077a994fe5427a04f6b9a52288af02dad44bb1f8025ecf209b339 in /testdir2
RUN /bin/sh -c mkdir testdir3
COPY file:2ed8ccde7cd97bc95ca15f0ec24ec447484a8761fa901df6032742e8f1a2a191 in /testdir3

這與最初的Dockerfile非常吻合。這次沒有ADD指令,而FROM指令也是正確的。只要我們的基礎(chǔ)鏡像是在原始Dockerfile中定義的,并且避免使用scratch或者避免使用ADD指令從tar文件創(chuàng)建基礎(chǔ)鏡像,我們應(yīng)該能夠比較準(zhǔn)確地重建Dockerfile。然而,我們?nèi)匀徊恢辣粡?fù)制的原始文件的名稱。

任意 Dockerfile 重建

現(xiàn)在,讓我們嘗試使用我們已經(jīng)討論過的工具,以正確的方式逆向工程一個Docker容器。我們將使用的容器在上面的示例基礎(chǔ)上進(jìn)行了修改。我們之前的Dockerfile被修改為example3。通過添加一個小的二進(jìn)制可執(zhí)行文件,該鏡像已具備運行的功能。在Dedockify的GitHub倉庫中可以找到源代碼。由于這個鏡像非常小,我們不需要構(gòu)建或拉取它。我們可以通過下面的代碼段將整個容器復(fù)制粘貼到我們的Docker環(huán)境中,來展示我們的命令行技巧 :)

uudecode << EOF | zcat | docker load
begin-base64 600 -
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
YUoB50E7iU4JFD4ESbcok5Kq6SWbIWjFpbFSCOmUYaxwiR1yiY3tvxX+T3JR
3v/sBK8Cy+4O/yfO+L/y/lcnWOn/rvF/hf8vKCgoaBXGQgwYvEKWwNmY0euo
As/cks/mFGMgBI+BfD7jaJtrHaMnhx1ZMAF9a/M/6dL/d4HG+m9r/qfS/3eC
wv/dbzS2/7bmfyr23wmW/F/TanCR/7PKBZAyeI7SuGDqd0Y9ahVYZj6nJFFq
pZS2UieBDAC05HRxNBKE06nwfx3yf43tv6X5n3iJ/+0Er96ovHP8X/H/O8FK
/3eN/yu//xUUFBS0iqbOekv8nzSs9P9doLH+23n/V4ky/1cnKPzf/UZj+2+H
/wNd7L8TLPm/NXB2K/5vDbGEhf/riv9rbP/t8H/Ayvt/naDE/xX9L/S/mrxv
/Wnc7P9f1P+C/5OyxP8WFBQUtIqmizW1Ff9X+v9u0Fj/Lc3/Z4r/3wkK/3e/
0dj+W4r/K/P/dIMl/7eGNRtX/N8aYgkL/9cV/9fY/lua/0+xYv9doMT/Ff0v
9L9atWL9abwV/1fi/wsKCgpaRdPJetqK/6vX/y39f/torP+W4v94+f2vExT+
736jsf23FP+niv13glf8X/M5+1b8X1lL5H3i/5rafzv8n9Bl/a9OcPfW/1jy
f8X/7wQr/a/WMF5/Gm/F/xX+v6CgoKBVJPK8tUkQIUjIXCXMBoJj0oPO3Btu
jJc86SyRWaFFDOhQ8QxKJ8uya2n9D6bK+h+doLH+25n/j/My/0cnKPzf/UZj
+2/O/wmQr63/a0r8TzdY8n9Nq8FF/o+xoB1a6zBk51lkXpMADhmswByzE+Bd
AG5V5skJj1zaSKm4aOhYEpf4P8EE22KwxcSugIHiAwF9KbR2grlLRGCdsKrf
XuUQrJe25hYlS5SRHDFpoxNDACOzDxhToCpmUcUYc5JJRMWNZjfzihdTuGsc
41719NPdR59sH8+m26NJ9KPtWRiOBxf2V7vnJxYbZ7v0Ue0vScq9qt7dnh3Q
o2xF+vjwo/Hk6ONezUc+2X329dOdx092e3vPq21/dLR9gKPR5Hm1X9/+itic
HXih9EB4CIFjBOm8wKB4CIxMnRkbubaokRoGgd5zFUwEBcIw5FkxRbpC4/AG
frRO/gpHunchT3WGfoEtPTk93azSJH5Hqv4ep7PhZLxgm/sM+vqsItx9Wnmd
Kr/MS9919S3Jbj+NB8M5xvnxtM61P0y6tst3uJ72+4bG/X9z/l+9Pv4DXd7/
6Qa1sbWdxhuP/znXqsT/doJVY9tiGrX9X6t/Y67w/9SPlPF/J/jnw8//8sFq
78Fvf7Xx640/0Hf9/xs68h9OG//+/c8//am38cHGv37+6VFdWTZ7P0ymo/TH
3727jBcUFBQUNMKhHw8zzub9W/B4b4sb/H8O0lzyBemIEuX9n06wd1I9WFId
FVPOgzU2oVQ+SAkqeKdN1IASaKiPUrsFr2aNy0Znn8hNTzlz453ywBZ1iIbf
z/BosutfzOrRPP7oD49GCIORryNM6iH95/WIcXGyaaTg+eCTUl1fJBsJW9+y
uCRsfXPskbD1LdhRl9na3v4lYU1530vC1sdLVPun+8VP/SVMyVZnw/lkOsRZ
W2nc9PsvB7ja/pNBlPa/C5ysWuia61420mv4Pej0tNhcQUFBwV3GfwHMszUX
AMIAAA==
====
EOF

直接從命令行運行后會加載一個新的鏡像 example3:latest。

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
example3 latest 059a3878de45 5 minutes ago 63B

現(xiàn)在,讓我們嘗試重建Dockerfile。

$ python3 dedockify.py 059a3878de45
FROM example3:latest
WORKDIR /testdir1
COPY file:322f9f92e3c94eaee1dc0d23758e17b798f39aea6baec8f9594b2e4ccd03e9d0 in testfile1
WORKDIR /testdir2
COPY file:322f9f92e3c94eaee1dc0d23758e17b798f39aea6baec8f9594b2e4ccd03e9d0 in testfile2
WORKDIR /testdir3
COPY file:322f9f92e3c94eaee1dc0d23758e17b798f39aea6baec8f9594b2e4ccd03e9d0 in testfile3
WORKDIR /app
COPY file:b33b40f2c07ced0b9ba6377b37f666041d542205e0964bc26dc0440432d6e861 in hello
ENTRYPOINT ["/app/hello"]

這為我們提供了一個基礎(chǔ)Dockerfile。由于example3:latest是這個鏡像的名稱,我們可以從上下文假設(shè)它使用了`scratch`。現(xiàn)在,我們需要看看有哪些文件被復(fù)制到了/testdir1、/testdir2、/testdir3和/app。讓我們在Dive中運行這個鏡像,看看如何恢復(fù)丟失的數(shù)據(jù)。

docker run - rm -it 
 -v /var/run/docker.sock:/var/run/docker.sock 
 wagoodman/dive:latest example3:latest

10bb354e-fa65-11ef-9310-92fbcf53809c.jpg

如果您向下選擇到最后一層,您將能夠看到所有丟失的數(shù)據(jù)填充到右側(cè)的目錄樹中。每個目錄都復(fù)制了名為testfile1、testfile2和testfile3的零字節(jié)文件。在最后一層中,一個名為hello的63字節(jié)文件被復(fù)制到了/app目錄中。

讓我們恢復(fù)這些文件!由于無法直接從鏡像中復(fù)制文件,因此我們需要先創(chuàng)建一個容器。

$ docker run -td --name example3 example3:latest
6fdca182a128df7a76e618931c85a67e14a73adc69ad23782bc9a5dc29420a27

現(xiàn)在,讓我們使用下面從Dive恢復(fù)的路徑和文件名將我們需要的文件從容器復(fù)制到主機。

mkdir $HOME/test3
cd $HOME/test3
docker cp example3:/testdir1/testfile1 .
docker cp example3:/testdir2/testfile2 .
docker cp example3:/testdir3/testfile3 .
docker cp example3:/app/hello .

我們可能得先檢查我們的容器是否仍在運行。

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6fdca182a128 example3:latest "/app/hello" 2 minutes ago Up 2 minutes wizardly_lamport

如果容器由于某種原因沒有運行,沒關(guān)系。我們可以驗證它的狀態(tài),看看它是否已經(jīng)停止。

$ docker container ls -a

我們還可以查看運行日志。

$ docker logs 6fdca182a128
Hello, world!

它似乎在運行一個輸出Hello, world!程序。實際上,在這種情況下,Hello, world!程序并不是被設(shè)計成始終運行的。在19.03.6版本的Docker中,可能存在一個錯誤,導(dǎo)致程序無法正常終止。目前這是可以接受的。容器可以活動或停止;應(yīng)用程序不需要持久化來恢復(fù)我們需要的任何數(shù)據(jù)。處于任何狀態(tài)的容器都只需要從我們正在提取數(shù)據(jù)的源鏡像中生成。

通過運行恢復(fù)的可執(zhí)行文件來驗證其行為,我們應(yīng)該看到以下內(nèi)容:

$ ./hello
Hello, world!

使用我們之前生成的Dockerfile,我們可以更新它以包含所有新的細(xì)節(jié)。這包括將FROM指令更新為從頭開始,以及我們在使用Dive探索時發(fā)現(xiàn)的所有文件名。

FROM scratch
WORKDIR /testdir1
COPY testfile1 .
WORKDIR /testdir2
COPY testfile2 .
WORKDIR /testdir3
COPY testfile3 .
WORKDIR /app
COPY hello .
ENTRYPOINT ["/app/hello"]

再次,將所有文件合并到一個共享文件夾中,我們就可以運行我們逆向工程的Dockerfile了。

讓我們先構(gòu)建一個鏡像。

$ docker build . -t example3:recovered
Sending build context to Docker daemon 4.608kB
Step 1/10 : FROM scratch
 - ->
Step 2/10 : WORKDIR /testdir1
 - -> Running in 5e8e47505ca6
Removing intermediate container 5e8e47505ca6
 - -> d30a2f002626
Step 3/10 : COPY testfile1 .
 - -> 4ac46077a588
Step 4/10 : WORKDIR /testdir2
 - -> Running in 8c48189da985
Removing intermediate container 8c48189da985
 - -> 7c7d90bc2219
Step 5/10 : COPY testfile2 .
 - -> 5b40d33100e1
Step 6/10 : WORKDIR /testdir3
 - -> Running in 4ccd634a04db
Removing intermediate container 4ccd634a04db
 - -> f89fdda8f059
Step 7/10 : COPY testfile3 .
 - -> 9542f614200d
Step 8/10 : WORKDIR /app
 - -> Running in 7614b0fdba42
Removing intermediate container 7614b0fdba42
 - -> 6d686935a791
Step 9/10 : COPY hello .
 - -> cd4baca758dd
Step 10/10 : ENTRYPOINT ["/app/hello"]
 - -> Running in 28a1ca58b27f
Removing intermediate container 28a1ca58b27f
 - -> 35dfd9240a2e
Successfully built 35dfd9240a2e
Successfully tagged example3:recovered

然后我們來運行這個鏡像:

$ docker run - name recovered -dt example3:recovered
0f696bf500267a996339b522cf584e010434103fe82497df2c1fa58a9c548f20
$ docker logs recovered
Hello, world!

為了進(jìn)一步驗證,讓我們再次使用 Dive 檢查鏡像。

docker run - rm -it 
 -v /var/run/docker.sock:/var/run/docker.sock 
 wagoodman/dive:latest example3:recovered

10dbcc14-fa65-11ef-9310-92fbcf53809c.jpg

此鏡像顯示的文件與原鏡像相同。將兩個鏡像并排比較,它們都完全匹配。兩者顯示的文件大小相同。兩者的功能完全相同。

以下是用于生成example3鏡像的原始Dockerfile。

FROM alpine:3.9.2
RUN apk add - no-cache nasm
WORKDIR /app
COPY hello.s /app/hello.s
RUN touch testfile && nasm -f bin -o hello hello.s && chmod +x hello
FROM scratch
WORKDIR /testdir1
COPY - from=0 /app/testfile testfile1
WORKDIR /testdir2
COPY - from=0 /app/testfile testfile2
WORKDIR /testdir3
COPY - from=0 /app/testfile testfile3
WORKDIR /app
COPY - from=0 /app/hello hello
ENTRYPOINT ["/app/hello"]

我們可以看到,雖然我們不能完美地重建它,但我們能夠大致重建它。像這樣使用多階段(stage)構(gòu)建的Dockerfile是無法重建的。這些信息根本就不存在。我們唯一的選擇是重建我們實際擁有的鏡像的Dockerfile。如果我們有早期構(gòu)建階段的鏡像,我們可以為每個階段重構(gòu)一個Dockerfile,但在這種情況下,我們只有最終構(gòu)建階段的鏡像。但不管怎樣,我們還是成功地從Docker鏡像中重現(xiàn)了一個有用的Dockerfile。

后記

通過使用與Dive類似的方法,我們應(yīng)該能夠更新Dedockify的源代碼,使其能夠自動分析每一層,以恢復(fù)所有有用的文件信息。此外,該程序還可以更新為能夠自動從容器中恢復(fù)文件并將其存儲到本地,同時還能自動對Dockerfile進(jìn)行適當(dāng)?shù)母隆W詈螅€可以對程序進(jìn)行更新,使其能夠輕松推斷出基礎(chǔ)層是否使用了Scratch或其他基礎(chǔ)鏡像。通過對恢復(fù)的Dockerfile語法進(jìn)行一些額外的修改,Dedockify有可能被更新為在大多數(shù)情況下完全自動地將Docker鏡像逆向工程為一個功能性的Dockerfile。

鏈接:https://www.cnblogs.com/Stephen/p/17561440.html

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • python
    +關(guān)注

    關(guān)注

    56

    文章

    4813

    瀏覽量

    85305
  • 鏡像
    +關(guān)注

    關(guān)注

    0

    文章

    174

    瀏覽量

    10867
  • Docker
    +關(guān)注

    關(guān)注

    0

    文章

    495

    瀏覽量

    12184

原文標(biāo)題:后記

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    構(gòu)建ARM64版本nacos docker鏡像

    在適配過程中有大量合作伙伴用到nacos且采用容器化部署,dockerhub未提供官方鏡像,因此需要在鯤鵬服務(wù)器自定義構(gòu)建。構(gòu)建前提:Docker已部署構(gòu)建步驟:1、下載包含構(gòu)建所需的腳本下載完成
    發(fā)表于 06-16 14:29

    一文詳解DockerFile基礎(chǔ)知識

    DockerFile用來構(gòu)建docker鏡像文件,可以理解為命令參數(shù)腳本。構(gòu)建步驟編寫一個DockerFile文件Docker build
    發(fā)表于 09-15 15:54

    淺析Docker鏡像本地存儲機制及容器啟動原理

    鏡像各層內(nèi)容及對應(yīng)大小,每層對應(yīng)著 Dockerfile 中的一條指令。Docker 鏡像默認(rèn)存儲在 /var/lib/docker/《st
    發(fā)表于 10-19 14:17 ?2570次閱讀

    Docker鏡像的詳細(xì)講解

    本文是對 Docker 鏡像的詳細(xì)講解,講解了如何安裝 Docker、配置 Docker 鏡像加速以及操作
    的頭像 發(fā)表于 08-02 10:00 ?2274次閱讀

    鏡像構(gòu)建Dockerfile的介紹

    Dockerfile 是一個用來構(gòu)建鏡像的文本文件,文本內(nèi)容包含了一條條構(gòu)建鏡像所需的指令和說明。
    的頭像 發(fā)表于 09-06 09:36 ?1258次閱讀

    如何創(chuàng)建 Docker 鏡像的能力

    本文將帶大家繼續(xù)深入了解 Dockerfile 鏡像創(chuàng)建過程中最重要的配置文件內(nèi)容,更加透徹地了解整個容器鏡像的創(chuàng)建過程。
    的頭像 發(fā)表于 09-16 09:39 ?1205次閱讀

    Dockerfile的最佳實踐

    隨著應(yīng)用的容器化、上云后,將伴隨著 Docker 鏡像的構(gòu)建,構(gòu)建 Docker 鏡像成為了最基本的一步,其中 Dockerfile 便是用
    的頭像 發(fā)表于 01-20 10:59 ?1075次閱讀
    <b class='flag-5'>Dockerfile</b>的最佳實踐

    Docker入門指南之什么是Dockerfile

    Dockerfile是由一系列命令和參數(shù)構(gòu)成的腳本,這些命令應(yīng)用于基礎(chǔ)鏡像并最終創(chuàng)建一個新的鏡像 * 對于開發(fā)人員:可以為開發(fā)團(tuán)隊提供一個完全一致的開發(fā)環(huán)境 * 對于測試人員:可以直接拿開
    的頭像 發(fā)表于 02-06 15:25 ?709次閱讀
    <b class='flag-5'>Docker</b>入門指南之什么是<b class='flag-5'>Dockerfile</b>

    docker 搜索鏡像,docker查看鏡像詳細(xì)信息(docker下載鏡像命令)

    Docker Hub是集中管理的Docker鏡像注冊中心。通過Docker 用戶可以在注冊中心搜索、下載和使用CLI命令行工具中的鏡像。以下
    的頭像 發(fā)表于 07-19 09:46 ?2026次閱讀

    Dockerfile定義Docker鏡像的構(gòu)建過程

    了解Dockerfile Dockerfile 是一個文本文件,用于定義 Docker 鏡像的構(gòu)建過程。它以指令的形式描述了如何構(gòu)建鏡像,從
    的頭像 發(fā)表于 09-30 10:22 ?2674次閱讀

    如何使用dockerfile創(chuàng)建鏡像

    Docker是一個開源的平臺,用于快速構(gòu)建、打包、部署應(yīng)用程序的容器化工具。而Dockerfile是一個文本文件,包含了一組可自動化構(gòu)建Docker鏡像的指令。本文將詳細(xì)介紹
    的頭像 發(fā)表于 11-23 09:52 ?871次閱讀

    手動構(gòu)建Docker鏡像的方法

    不推薦使用docker commit命令,而應(yīng)該使用更靈活、更強大的dockerfile來構(gòu)建docker鏡像
    的頭像 發(fā)表于 08-05 15:30 ?641次閱讀
    手動構(gòu)建<b class='flag-5'>Docker</b><b class='flag-5'>鏡像</b>的方法

    提升DevOps效率,從基礎(chǔ)到進(jìn)階的Dockerfile編寫技巧

    創(chuàng)建自定義鏡像Dockerfile 由一行行命令語句組成,并且支持以 # 開頭的注釋行。 Docker分為四部分: 基礎(chǔ)鏡像信息 維護(hù)者信息
    的頭像 發(fā)表于 11-26 09:44 ?180次閱讀
    提升DevOps效率,從基礎(chǔ)到進(jìn)階的<b class='flag-5'>Dockerfile</b>編寫技巧

    Dockerfile鏡像制作與Docker-Compose容器編排

    Dockerfile鏡像制作 docker/podman中, 鏡像是容器的基礎(chǔ),每次執(zhí)行docker run的時候都會指定哪個基本
    的頭像 發(fā)表于 01-07 11:01 ?313次閱讀
    <b class='flag-5'>Dockerfile</b><b class='flag-5'>鏡像</b>制作與<b class='flag-5'>Docker</b>-Compose容器編排

    Docker-鏡像的分層-busybox鏡像制作

    目錄 知識點1:鏡像的分層 示例:進(jìn)入 docker hub查看Jenkins的Dockerfile 知識點2:base鏡像 知識點3:scratch
    的頭像 發(fā)表于 01-15 10:44 ?239次閱讀
    <b class='flag-5'>Docker</b>-<b class='flag-5'>鏡像</b>的分層-busybox<b class='flag-5'>鏡像</b>制作
    主站蜘蛛池模板: 亚洲婷婷综合色高清在线 | 天天曰天天干天天操 | 国产精品嫩草影院在线播放 | 欧美午夜色大片在线观看免费 | 久久久久综合中文字幕 | 成年人午夜影院 | 色多多免费视频观看区一区 | 五月婷婷激情在线 | 亚洲国产欧美在线人成aaa | 欧美色图网站 | 在线观看视频一区二区三区 | 中文在线天堂网www 中文在线资源链接天堂 | 三级电影在线观看视频 | 婷婷色网 | 天天插日日插 | 一级片 在线播放 | 婷婷亚洲综合 | 日本高清视频色 | 欧美怡红院免费全部视频 | 婷婷99| 在线观看免费视频 | 在线观看黄的网站 | 在线一区观看 | 亚洲综合色婷婷 | www日本黄色 | 高清色黄毛片一级毛片 | 国产一二三区在线 | 色中文网| 在线免费视频手机版 | 五月婷婷七月丁香 | 日韩h视频| 婷婷视频网 | 免费一级黄色录像 | 不卡一级毛片免费高清 | 国产一区二区三区波多野吉衣 | 亚洲三级电影 | 国产三级在线免费 | 在线亚洲成人 | 韩国特黄特色a大片免费 | 亚洲丁香 | 激情综合色综合啪啪开心 |