如何修改 Docker 镜像

我认为您对 Docker 有点熟悉,并且了解运行 docker 容器等基础知识。

在之前的文章中,我们讨论了更新 docker 容器和编写 docker 文件。

修改 docker 镜像到底是什么?

容器镜像构建在层中(或者它是层的集合),每个 Dockerfile 指令都会创建一层镜像。 为了 example,请考虑以下 Dockerfile:

FROM alpine:latest

RUN apk add --no-cache python3

ENTRYPOINT ["python3", "-c", "print('Hello World')"]

由于一共有三个 Dockerfile 命令,因此从这个 Dockerfile 构建的镜像将总共包含三层。

您可以通过构建映像来确认:

docker image built -t dummy:0.1 .

然后使用命令 docker image history 在构建的图像上。

articles/Modify a Docker Image on  modify-docker-images [?] took 12s 
❯ docker image history dummy:0.1 
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
b997f897c2db   10 seconds ago   /bin/sh -c #(nop)  ENTRYPOINT ["python3" "-c…   0B        
ee217b9fe4f7   10 seconds ago   /bin/sh -c apk add --no-cache python3           43.6MB    
28f6e2705743   35 hours ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      35 hours ago     /bin/sh -c #(nop) ADD file:80bf8bd014071345b…   5.61MB

忽略最后一个“”层。

这些层中的每一层都是只读的。 这是有益的,因为由于这些层是只读的,因此与此映像的运行实例相关联的任何进程都无法修改此映像的内容,因此,这些层可以由许多容器共享,而无需保留每个实例的副本。 但是为了让容器的进程能够执行 r/w,在创建容器时在现有 RO 层之上添加了另一层,这是可写的,不被其他容器共享。

此 r/w 层的缺点是在此层中所做的更改不是持久性的,尽管您可以使用卷来持久化一些数据,但有时您可能需要/想要在某个现有层之前添加一个层,或者从一个图像或简单地替换一个图层。 这些是人们可能想要修改现有的原因 docker 图片。

在本文中,我将使用不同的方法涵盖我上面提到的所有案例。

修改docker镜像的方法

有两种方法可以修改 docker 镜像。

  1. 通过 Dockerfiles。
  2. 使用命令 docker container commit.

我将解释这两种方法,最后,我还将添加哪个用例更适合上下文中的方法。

方法一:通过Dockerfile修改docker镜像

修改 docker 镜像本质上意味着修改镜像的层。 现在,由于每个 Dockerfile 命令都代表图像的一层,因此修改 Dockerfile 的每一行也会更改相应的图像。

因此,如果您要向映像添加一个层,您可以简单地向其添加另一个 Dockerfile 指令,要删除一个,您将删除一行,而要更改一个层,您将相应地更改该行。

有两种方法可以使用 Dockerfile 来修改图像。

  1. 使用要修改的镜像作为基础镜像并构建子镜像。
  2. 修改要更改的映像的实际 Dockerfile。

让我解释一下什么时候应该使用哪种方法,以及如何使用。

1. 使用图像作为基础图像

这是当您获取要修改的图像并为其添加图层以构建新的子图像时。 除非映像是从头开始构建的,否则每个映像都是对同一父基础映像的修改。

考虑以前的 Dockerfile。 假设从该图像构建的图像被命名 dummy:0.1. 现在,如果我认为我现在需要使用 Perl 而不是 Python3 来打印“Hello World”,但我也不想删除 Python3,我可以只使用 dummy:0.1 图像作为基础图像(因为 Python3 已经存在)并从中构建,如下所示

FROM dummy:0.1

RUN apk add --no-cache perl

ENTRYPOINT ["perl", "-e", "print "Hello Worldn""]

在这里,我正在构建 dummy:0.1,在我认为合适的情况下添加更多层。

如果您打算更改或删除某些现有图层,此方法不会有太大帮助。 为此,您需要遵循下一个方法。

2.修改镜像的Dockerfile

由于映像的现有层是只读的,因此您无法通过新的 Dockerfile 直接修改它们。 随着 FROM Dockerfile 中的命令,您将一些图像作为基础并构建 它,或添加图层。

一些任务可能需要我们改变现有的层,尽管你可以使用前面的方法来做这件事,但有很多矛盾的地方 RUN 说明(例如删除文件,删除/替换在前一层中添加的包),这不是一个理想的解决方案,也不是我推荐的解决方案。 因为它增加了额外的层并且增加了相当多的图像大小。

更好的方法是不使用该镜像作为基础镜像,而是更改该镜像的实际 Dockerfile。 再次考虑之前的 Dockerfile,如果我不必在该映像中保留 Python3,并且将 Python3 包和命令替换为 Perl 包,该怎么办?

如果遵循以前的方法,我将不得不像这样创建一个新的 Dockerfile –

FROM dummy:0.1

RUN apk del python3 && apk add --no-cache perl

ENTRYPOINT ["perl", "-e", "print "Hello Worldn""]

如果建成,该图像中总共将有五层。

articles/Modify a Docker Image on  modify-docker-images [?] took 3s 
❯ docker image history dummy:0.2
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
2792036ddc91   10 seconds ago   /bin/sh -c #(nop)  ENTRYPOINT ["perl" "-e" "…   0B        
b1b2ec1cf869   11 seconds ago   /bin/sh -c apk del python3 && apk add --no-c…   34.6MB    
ecb8694b5294   3 hours ago      /bin/sh -c #(nop)  ENTRYPOINT ["python3" "-c…   0B        
8017025d71f9   3 hours ago      /bin/sh -c apk add --no-cache python3 &&    …   43.6MB    
28f6e2705743   38 hours ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      38 hours ago     /bin/sh -c #(nop) ADD file:80bf8bd014071345b…   5.61MB

此外,图像的大小为 83.8 MB。

articles/Modify a Docker Image on  modify-docker-images [?] 
❯ docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
dummy        0.2       2792036ddc91   19 seconds ago   83.8MB

现在不再这样做,而是使用初始 Dockerfile,并将 Python3 的文件更改为 Perl,如下所示

FROM alpine:latest

RUN apk add --no-cache perl

ENTRYPOINT ["perl", "-e", "print "Hello Worldn""]

层数减少到 3,现在大小为 40.2 MB。

articles/Modify a Docker Image on  modify-docker-images [?] took 3s 
❯ docker image history dummy:0.3
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
f35cd94c92bd   9 seconds ago   /bin/sh -c #(nop)  ENTRYPOINT ["perl" "-e" "…   0B        
053a6a6ba221   9 seconds ago   /bin/sh -c apk add --no-cache perl              34.6MB    
28f6e2705743   38 hours ago    /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      38 hours ago    /bin/sh -c #(nop) ADD file:80bf8bd014071345b…   5.61MB    

articles/Modify a Docker Image on  modify-docker-images [?] 
❯ docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
dummy        0.3       f35cd94c92bd   29 seconds ago   40.2MB

图片已成功更改。

当您只想在现有图层之上添加图层时,前一种方法更有用,但在尝试修改现有图层(如删除一个、替换一个、重新排序现有图层等)时没有多大帮助。 这就是这种方法的亮点。

方法二:使用 docker commit 修改镜像

还有另一种方法,您可以在其中拍摄正在运行的容器的快照,并将其转换为自己的图像。

让我们建立一个 dummy:0.1 相同的图像,但这次没有使用 Dockerfile。 自从我使用 alpine:latest 作为 dummy:0.1的基础,启动该图像的容器。

docker run --rm --name alpine -ti alpine ash

现在在容器内,添加 Python3 包, apk add --no-cache python3. 完成后,打开一个新的终端窗口并运行以下命令(或类似命令)

docker container commit --change="ENTRYPOINT ["python3", "-c", "print("Hello World")"]" alpine dummy:0.4

随着 --change 标记我正在将 Dockerfile 指令添加到新的 dummy:04 图像(在这种情况下, ENTRYPOINT 操作说明)。

随着 docker container commit 命令,您基本上将最外层的 r/w 层转换为 ar/o 层,将其附加到现有图像的层并创建一个新图像。 此方法更直观/更具交互性,因此您可能希望使用它而不是 Dockerfiles,但请理解这不是非常可重复的。 同样的规则也适用于删除或更改任何现有图层,添加图层只是为了删除某些内容或更改在前一层中完成的内容并不是最好的主意,至少在大多数情况下是这样。

这篇文章到此结束。 我希望这篇文章对你有所帮助,如果你有任何问题,请在下方评论。