Docker イメージを Dockerfile にリバース エンジニアリングする

Nov 25 2022
Docker イメージがデータを保存する方法の内部を調べることで Docker イメージをリバース エンジニアリングする TL;DR このストーリーでは、Docker イメージがどのようにデータを保存するか、ツールを使用してデータのさまざまな側面を調べる方法を調べて、Docker イメージをリバース エンジニアリングします。イメージ、および Python Docker API を使用して Dockerfile を作成する Dedockify などのツールの作成方法。はじめに Docker Hub や TreeScale などのパブリック Docker レジストリの人気が高まるにつれて、管理者や開発者が不明なエンティティによって作成されたイメージをダウンロードすることが一般的になりつつあります。

Docker イメージがデータを保存する方法の内部構造を調べて、Docker イメージをリバース エンジニアリングする

TL;DR

このストーリーでは、Docker イメージがどのようにデータを保存するか、ツールを使用してイメージのさまざまな側面を調べる方法、およびPython Docker API を使用してDockerfile。

著者によるイラストのドッキング解除。

序章

Docker HubやTreeScaleなどのパブリック Docker レジストリの人気が高まるにつれて、管理者や開発者が未知のエンティティによって作成されたイメージをダウンロードすることがより一般的になっています。多くの場合、利便性は認識されているリスクよりも優先されます。場合によっては、Docker イメージが公開されると、リスト、git リポジトリ、または関連リンクから直接提供されます。多くの場合、Dockerfile は提供されません。Dockerfile が利用可能になった場合、ビルド済みのイメージが Dockerfile と相関している、または使用しても安全であるという保証はほとんどありません。

おそらく、セキュリティの脆弱性は気にしないでしょう。おそらく、お気に入りのイメージの 1 つを更新して、最新バージョンの Ubuntu で実行できるようにしたいと思うでしょう。または、コンパイル時にバイナリを生成するのにより適した別のディストリビューション用のコンパイラがあるため、もう少し最適化されたイメージをリリースするという制御不能な衝動に駆られるかもしれません。

理由が何であれ、イメージの Dockerfile を回復するためのオプションがあります。Docker イメージはブラック ボックスではありません。Dockerfile を再構築するために必要なほとんどの情報を取得できます。Docker イメージの内部を調べて内部を調べることで、任意のビルド済みコンテナーから Dockerfile を厳密に再構築することができます。

このストーリーでは、2 つのツールを使用してイメージから Dockerfile を再構築する方法を示します。このストーリー用に提供されたカスタマイズされた Python スクリプトであるDedockifyと、Docker イメージ探索ツールであるDiveです。使用する基本的なプロセス フローは次のとおりです。

Dedockifyを使った基本的な流れ。著者によるイラスト。

ダイブの使用

著者によるダイブ デモ クリップ。

イメージがどのように構成されているかをすばやく理解するために、Dive を使用して、高度でなじみのない Docker の概念について学習します。Dive ツールは、Docker イメージの各レイヤーを調べます。

Dockerfileテストに使用できる、シンプルでわかりやすいものを作成しましょう。

このスニペットを空のディレクトリに直接配置します。

cat > Dockerfile << EOF ; touch testfile1 testfile2 testfile3
FROM scratch
COPY testfile1 /
COPY testfile2 /
COPY testfile3 /
EOF

$ ls
Dockerfile  testfile1  testfile2  testfile3

docker build . -t example1

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

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

画像のサイズから、ソース画像がないことがわかります。ソース イメージの代わりに、ソース イメージscratchとしてゼロバイトの空白のイメージを使用するよう Docker に指示するものを使用しました。次に、空のイメージに 3 つの追加のゼロバイト テスト ファイルをコピーして変更し、その変更に としてタグを付けましたexample1

それでは、Dive を使用して新しいイメージを調べてみましょう。

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

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...

      
                
Dive screenshot by author.

著者によるダイビングのスクリーンショット。

各レイヤーをスクロールすると、右側のコンテンツが変化するのがわかります。各ファイルが空の Dockerscratchイメージにコピーされると、新しいレイヤーとして記録されました。

著者によるダイビングのスクリーンショット。

また、各レイヤーの作成に使用されたコマンドが表示されていることにも注意してください。また、ソース ファイルと更新されたファイルのハッシュ値も確認できます。

このセクションの項目に注意するとCommand:、次のようになります。

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

Docker の歴史

ダイブなどのサードパーティ製ツール以外に、すぐに利用できるツールはdocker history. docker historyイメージでコマンドを使用するexample1 と、そのイメージを作成するために Dockerfile で使用したエントリを表示できます。

docker history example1

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

$ 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

Python 用 Docker エンジン API の使用

Dockerは、Docker Engine API 用の Python ライブラリをリリースしました。これにより、Python 内から Docker を完全に制御できます。docker history次の例では、次の Python 3 コードを実行することで、使用したのと同様の情報を復元できます。

#!/usr/bin/python3

import docker

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

[{'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}]

作者によるロゴのドッキング解除。

ドッキング解除

これをさらに数歩進めてみましょう。このイメージを Dockerfile にリバース エンジニアリングするには、すべてを解析して、読み取り可能な形式に再フォーマットする必要があります。このストーリーの目的のために、次の Python 3 コードが利用可能になり、GitHubのDedockifyリポジトリから取得できることに注意してください。すべての優れたオリジナルの基礎とコーディングについて、 LanikSJに感謝します。

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\n".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("&&", "\\\n    &&")
        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()

ここまでできれば、2 つのイメージがあるはずです。それは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

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

比較として、私たちの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"]

ドッキング解除の制限テスト

Dockerfile基本イメージを明示的に定義する例を作成して実験してみましょう。前に行ったように、空のディレクトリで、次のスニペットをコマンド ラインから直接実行します。

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

$ 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

$ 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

$ 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 再構築

ここで、既に説明したツールを使用して、適切な方法で Docker コンテナーをリバース エンジニアリングしてみましょう。使用するコンテナーは、上記の例から変更されています。以前Dockerfileは を作成するように変更されましexample3た。小さなバイナリを追加することで、イメージが機能するようになりました。アセンブリのソース コードは、こちらのDedockify GitHub リポジトリで入手できます。このイメージは非常に小さいため、ビルドまたはプルする必要はありません。以下のスニペットを使用して、コンテナー全体を Docker 環境にコピー アンド ペーストすることで、コマンド ライン スキルを披露できます。

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

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

$ 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"]

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

      
                
Dive screenshot by author.

それでは、それらのファイルを復元しましょう。ファイルをイメージから直接コピーする方法はないようです。そのため、最初にコンテナーを作成する必要があります。

$ docker run -td example3:latest
6fdca182a128df7a76e618931c85a67e14a73adc69ad23782bc9a5dc29420a27

/testdir1/testfile1
/testdir2/testfile2
/testdir3/testfile3
/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

$ docker container ls -a

$ docker logs 6fdca182a128
Hello, world!

docker cp 6fdca182a128:/testdir1/testfile1 .
docker cp 6fdca182a128:/testdir2/testfile2 .
docker cp 6fdca182a128:/testdir3/testfile3 .
docker cp 6fdca182a128:/app/hello .

$ ./hello
Hello, world!

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

まず、コンテナをビルドします。

$ 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!

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

      
                
Dive screenshot by author.

Dockerfile元画像の生成に使用した元画像はこちらexample3

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"]```

これからの仕事

Dive と同様のアプローチを使用することで、有用なファイル情報をすべて復元するために、 Dedockifyのソース コードを更新して、各レイヤーを自動的に通過できるようにする必要があります。また、プログラムを更新して、コンテナからファイルを自動的に復元してローカルに保存できるようにすると同時に、Dockerfile. scratch最後に、プログラムを更新して、ベースレイヤーが空の画像を使用しているかどうかを簡単に推測できるようにすることもできます。Dockerfile復元された構文にいくつかの追加変更を加えることで、 DedockifyDockerfileを更新して、ほとんどの場合、Docker イメージの機能へのリバース エンジニアリングを完全に自動化できる可能性があります。

この投稿が気に入ったら、あなたのようなファンからのもう 1 杯のジョークは、同様の別の投稿を書くように私を励ますのに大いに役立ちます。立ち寄って「こんにちは」と言って、読みたいと思うトピックについてのアイデアを教えてください. あなたはインスピレーションの火花になる可能性があり、さらに素晴らしいことについて書く必要があります!

更新: 2022 年 7 月 11 日

2020 年のパンデミックの発生時に、上記のウォークスルーを提案し、執筆を依頼されました。これはAppfleetによって公開され、2020 年 5 月 25 日にAppfleet のブログ投稿として公開されました。この Docker リバース エンジニアリングの記事のバージョンは、私のHavDevOpsブログにあります。上記の更新されたストーリーを、わずかな編集と書式設定の変更のみを加えて Medium に公開します。私の注意が必要なエラーを誰かが見つけた場合は、お気軽にコメントしてください。

Mark Havensは、ダラス メーカー コミュニティ(DMC)の創設者兼エグゼクティブ ディレクターです。DMCは、米国最大のボランティアメイカースペースであるダラスメーカースペースをブートストラップするために組織された非営利団体です。DMC は、北テキサスの他のメーカー中心の組織に、メーカー中心のマーケティングとメーカースペースのリーダーシップ教育を提供するための改革を続けています。