构建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
中。
作者:Yudoge
出处:https://www.cnblogs.com/lilpig/p/16205586.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
欢迎按协议规定转载,方便的话,发个站内信给我嗷~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)