构建Docker镜像

基础概念#

bootfs和rootfs#

bootfs是Linux启动时用于引导的文件系统,只要Linux的内核版本相同,bootfs就没什么差别,而可以区分不同发行版(如Ubuntu或CentOS)之间区别的就是位于bootfs之上的一层文件系统——rootfs。

rootfs即被挂载到/的文件系统,其中包含对应发行版中典型的目录结构、系统中的各种应用程序以及系统启动时所必要的文件。传统Linux启动时rootfs是只读的,当它载入完成后变成可写的,Docker中它一直是只读的,所有对它的修改会通过联合加载来完成。

联合加载和层#

对Docker镜像的修改不会直接写入到原始镜像中,原始镜像是只读的,假如你运行原始镜像,在其中安装了emacs,原始镜像并没有改变,改变的是你当前通过该镜像运行的容器,没错,容器可以看作一个镜像的运行时,镜像是静态的,容器是动态的。当你以该容器当前的状态创建另一个镜像时,会在原始镜像上添加一个层。

注意,镜像中的层都是只读的,只有当你将它运行成一个容器时,Docker才会在上面建立一个读写层,你的写入都会在那个读写层中完成。这让Docker可以方便的复用当前的镜像,在上面添加新功能,然后发布为新的镜像。

docker commit构建镜像#

之前说了,当你运行一个镜像时,一个容器被创建出来,Docker会向其最上面添加一个读写层,你可以添加你自己的修改,然后使用docker commit命令应用这些修改构建出一个新的镜像。

当你构建新的镜像时,由于底层的文件系统都是只读的,Docker只需要应用最上面一层的更改即可。

构建带VIM的ubuntu镜像#

查看当前的镜像

docker images
REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
docker/getting-started   latest    cb90f98fd791   2 weeks ago    28.8MB
redis                    latest    7614ae9453d1   4 months ago   113MB
ubuntu                   latest    ba6acccedd29   6 months ago   72.8MB

运行ubuntu,发现其中并没有vim命令

docker run --name ubuntu -i -t ubuntu /bin/bash
root@220a753fe9df:/# vim
bash: vim: command not found
root@220a753fe9df:/#

在docker的ubuntu中备份文件:

root@220a753fe9df:/# cp /etc/apt/sources.list /etc/apt/sources.list.bak

在源主机中复制一份中国源给ubuntu

docker cp tmp/source.list ubuntu:/etc/apt/sources.list

在ubuntu中执行如下命令安装一些必要的软件:

apt install vim
apt install curl
apt install wget
apt install net-tools

使用exit命令退出容器,然后使用该容器构建一个新镜像:

docker commit -m"Ubuntu with some tools" -a"yudoge" ubuntu yudoge/myubuntu
sha256:17ecd31bb4ce6ee39e85ee1bc37df2440d31ca66ed896f968982bb2338997123
docker images
REPOSITORY               TAG       IMAGE ID       CREATED         SIZE
yudoge/myubuntu          latest    17ecd31bb4ce   5 seconds ago   214MB
docker/getting-started   latest    cb90f98fd791   2 weeks ago     28.8MB
redis                    latest    7614ae9453d1   4 months ago    113MB
ubuntu                   latest    ba6acccedd29   6 months ago    72.8MB

运行该镜像

docker run --name myubuntu -it yudoge/myubuntu /bin/bash                                                           
root@970eba996a9e:/# vim
root@970eba996a9e:/# curl baidu.com
<html>

</html>
root@970eba996a9e:/#

docker build和Dockerfile构建镜像#

上面的办法我们不常用,比较常用的是使用Dockerfile构建镜像

删除上面的容器和镜像

docker rm myubuntu
myubuntu
docker rmi yudoge/myubuntu
Untagged: yudoge/myubuntu:latest
Deleted: sha256:17ecd31bb4ce6ee39e85ee1bc37df2440d31ca66ed896f968982bb2338997123
Deleted: sha256:89dc1acd481d43e9865e97011cad84e6824c4b6363a431b7d2627887068461f1

创建一个文件夹

mkdir myubuntu
cd myubuntu
vim Dockerfile

编辑Dockerfile文件,FROM指定该镜像基于哪个老镜像构建,MAINTAINER指定了该镜像的维护者,ADD将一个Dockerfile目录下的文件复制到镜像中的某个位置,RUN执行命令。

FROM ubuntu:20.04
MAINTAINER Yudoge "yohahaonan@gmail.com"
ADD sources.list /etc/apt/sources.list
RUN apt-get update
RUN apt-get install vim curl wget net-tools

每一个RUN命令在镜像上添加一个新的层,这里我们将apt-get update和后面分开,稍后就会知道这样做的原因。

复制之前的sources.list

cp ~/tmp/sources.list ./

构建,注意-t制定了构建出的镜像的名字,.指定了从哪个包含Dockerfile的目录构建,这里是当前目录

docker build -t="yudoge/myubuntu" .

构建失败了,因为我们在安装VIM的时候,它期待我们输入一个Y/n。

#7 48.42   vim-runtime wget xxd xz-utils
#7 48.43 0 upgraded, 62 newly installed, 0 to remove and 24 not upgraded.
#7 48.43 Need to get 19.1 MB of archives.
#7 48.43 After this operation, 83.7 MB of additional disk space will be used.
#7 48.43 Do you want to continue? [Y/n] Abort.
------
executor failed running [/bin/sh -c apt-get install vim curl wget net-tools]: exit code: 1

这里我们需要修改apt-get的安装命令,使用apt-get -y来默认同意

FROM ubuntu:20.04
MAINTAINER Yudoge "yohahaonan@gmail.com"
ADD sources.list /etc/apt/sources.list
RUN apt-get update
-RUN apt-get install vim curl wget net-tools
+RUN apt-get install -y vim curl wget net-tools

这里我们只修改了最后一条RUN命令,然后我们开始构建

docker build -t="yudoge/myubuntu" .
[+] Building 48.3s (9/9) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 209B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/ubuntu:20.04                                                   15.3s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 34B                                                                                   0.0s
 => [1/4] FROM docker.io/library/ubuntu:20.04@sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f  0.0s
 => CACHED [2/4] ADD sources.list /etc/apt/sources.list                                                            0.0s
 => CACHED [3/4] RUN apt-get update                                                                                0.0s
 => [4/4] RUN apt-get install -y vim curl wget net-tools                                                          31.7s
 => exporting to image                                                                                             1.1s
 => => exporting layers                                                                                            1.0s
 => => writing image sha256:254907b7cf3baddc63cb37b5fc17a870c540cd006171354c0c3c0e148a9935bf                       0.0s
 => => naming to docker.io/yudoge/myubuntu                                                                         0.0s

我们可以看到,构建过程的第2步和第三步是使用了缓存,并没有实际的执行,之前说了,每条RUN命令都在镜像上添加一层,当你修改一条RUN命令并重新构建时,只有它以及它后面的命令需要重新执行。

运行,成功构建了我们的镜像

docker images
REPOSITORY               TAG       IMAGE ID       CREATED              SIZE
yudoge/myubuntu          latest    254907b7cf3b   About a minute ago   214MB
docker/getting-started   latest    cb90f98fd791   2 weeks ago          28.8MB
redis                    latest    7614ae9453d1   4 months ago         113MB
ubuntu                   latest    ba6acccedd29   6 months ago         72.8MB
docker run --name myubuntu -it yudoge/myubuntu /bin/bash
root@cc18959c3188:/# vim

可以使用docker image history指令来查看构建该镜像过程中产生的所有的层。

Dockerfile指令#

CMD#

CMD是指定启动容器时要执行的指令,RUN不同,RUN是指定构建镜像时要执行的指令。

你可以认为CMD就是我们每次在docker run后面添加的那个指令,比如下面的命令会在容器启动后执行/bin/bash以进入bash交互程序

docker run --name myubuntu -it yudoge/myubuntu /bin/bash

Dockerfile文件最后一行添加CMD "/bin/bash"

构建并执行它,容器自动进入/bin/bash

docker run --name myubuntu -it yudoge/myubuntu
root@4f5511a3fccb:/#

CMD的行为会被docker run后面跟的指令覆写,比如下面,启动了/bin/sh交互程序

docker run --name myubuntu -it yudoge/myubuntu /bin/sh
#

你也可以使用数组的方式传递命令

ENTRYPOINT#

ENTRYPOINT和CMD一样,用于在容器启动时执行一个命令,只不过CMD会被docker run的参数覆写,而ENTRYPOINT会将用户参数作为ENTRYPOINT指定命令的参数。

比如这里,ENTRYPOINT用于启动nginx服务器

用户可以使用docker run指定启动的额外参数

-g "daemon off";指定了nginx作为守护进程启动。

如果我们想要用户必须提供一个参数给ENTRYPOINT,那么通过组合ENTRYPOINT和CMD,我们可以在用户没有提供参数时打印一个usage

这样,用户输入参数,CMD指定的-h被覆盖,不会执行,若用户未输入参数,-h执行,打印usage

WORKDIR#

指定容器启动后的工作目录

ENV#

指定容器中的环境变量

USER#

指定基于该镜像启动的容器以什么身份运行

VOLUME#

对基于该镜像创建的容器添加卷,这个目录可以绕过联合文件系统,提供数据共享和持久化功能。

ADD#

从构建目录复制文件到镜像中

STOPSIGNAL#

设置容器的停止信号

ARG#

指定build时能够传递的变量

ONBUILD#

镜像被构建时的监听器,一旦该镜像被构建,不管是直接构建还是作为其他镜像的基础镜像,ONBUILD后面的指令都会执行。比如下面一条命令会在镜像被构建时将构建目录中的所有文件添加到/var/www中。

posted @   yudoge  阅读(148)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
主题色彩