Docker的Dockerfile
参考资料:B站狂神教程
https://www.bilibili.com/video/BV1og4y1q7M4?p=26
Dockerfile是什么
Dockerfile是一个构建Docker镜像的命令参数脚本。
构建镜像的步骤:
- 编写一个Dockerfile文件
- docker build 构建镜像
- docker run 运行镜像
- docker push 发布镜像
Dockerfile的构建过程
构建Dockerfile需要了解很多能写在Dockerfile里的命令:
https://docs.docker.com/engine/reference/builder/#from
这是官方文档,里面有部分命令,不知道全不全,肯定是够用了。
还有这张图片,很有意思:
每个指令都会创建提交一个新的镜像层,并提交。最后跑起来的时候再加一层可写层:
Dockerfile的指令
还是这个文档:https://docs.docker.com/engine/reference/builder/#from
看一下命令大概的分类:
从左到右依次是构建的命令,连接工作目录等的命令和启动执行的命令。常用命令就是上面那张铁锈红色的图。
FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的,姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤,比如要构建tomcat镜像,这里就放tomcat压缩包
WORKDIR # 镜像的工作目录
VOLUME # 挂载目录
EXPOSE # 暴露端口配置
CMD # 指定这个容器启动的时候要运行的命令
# 只有最后一个会生效,而且可被替代
# 比如自己构建centos,指定/bin/bash来直接进入命令行交互模式
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 触发指令。当构建一个被继承的Dockerfile,这时候就会运行ONBUILD的指令
COPY # 类似ADD,将我们的文件拷贝到镜像中
ENV # 构建的时候设置环境变量
区分一下CMD和ENTRYPOINT。比如我们在Dockerfile里CMD ls -a
,然后run的时候在后面跟上命令ls -l
,就会替换掉-a,变成-l。但如果ENTRYPOINT ls -a
,run的时候-l,就会变成ls -a -l
。
CMD与ENTRYPOOINT的区别
实践一下CMD,写一个最简单的dockerfile:
FROM centos
CMD ["ls","-a"]
用CMD加命令可以通过中括号来构建,就跟上面代码一样。把它build起来:
root@KitDevVps:/home/dockerfiles# docker build -f cmd-dockerfile -t cmdtest .
Sending build context to Docker daemon 3.072kB
Step 1/2 : FROM centos
---> 831691599b88
Step 2/2 : CMD ["ls","-a"]
---> Running in 21e24114cf28
Removing intermediate container 21e24114cf28
---> 9ca8e41f3bf4
Successfully built 9ca8e41f3bf4
Successfully tagged cmdtest:latest
直接run这个镜像:
root@KitDevVps:/home/dockerfiles# docker run 9ca8e41f3bf4
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
发现它直接就执行了ls -a
这个命令。我们知道CMD是无法追加命令的,只能替换,我们run一下这个容器,然后加上-l
这个option:
root@KitDevVps:/home/dockerfiles# docker run 9ca8e41f3bf4 -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
发现报了一个Error。这是因为-l
直接替换了整个CMD,连ls
都替换掉了,由本来的ls -a
变成了-l
。没有-l
这种命令,所以报错。我们现在想执行ls -al
命令,就只能再run出容器时直接加上这个命令,以替换原来的ls -a
:
root@KitDevVps:/home/dockerfiles# docker run 9ca8e41f3bf4 ls -al
total 56
drwxr-xr-x 1 root root 4096 Jul 5 02:33 .
drwxr-xr-x 1 root root 4096 Jul 5 02:33 ..
-rwxr-xr-x 1 root root 0 Jul 5 02:33 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 5 02:33 dev
drwxr-xr-x 1 root root 4096 Jul 5 02:33 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Jun 11 02:35 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 104 root root 0 Jul 5 02:33 proc
dr-xr-x--- 2 root root 4096 Jun 11 02:35 root
drwxr-xr-x 11 root root 4096 Jun 11 02:35 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Jul 5 02:33 sys
drwxrwxrwt 7 root root 4096 Jun 11 02:35 tmp
drwxr-xr-x 12 root root 4096 Jun 11 02:35 usr
drwxr-xr-x 20 root root 4096 Jun 11 02:35 var
再实践一下ENTRYPOINT,写一个dockerfile,依然用中括号写命令:
FROM centos
ENTRYPOINT ["ls","-a"]
build起镜像来:
root@KitDevVps:/home/dockerfiles# docker build -f entrypoint-dockerfile -t entrypointtest .
Sending build context to Docker daemon 4.096kB
Step 1/2 : FROM centos
---> 831691599b88
Step 2/2 : ENTRYPOINT ["ls","-a"]
---> Running in e4fdc1c58ec7
Removing intermediate container e4fdc1c58ec7
---> 52b32cc2192c
Successfully built 52b32cc2192c
Successfully tagged entrypointtest:latest
run一下:
root@KitDevVps:/home/dockerfiles# docker run 52b32cc2192c
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
lost+found
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
加个option-l
:
root@KitDevVps:/home/dockerfiles# docker run 52b32cc2192c -l
total 56
drwxr-xr-x 1 root root 4096 Jul 5 02:38 .
drwxr-xr-x 1 root root 4096 Jul 5 02:38 ..
-rwxr-xr-x 1 root root 0 Jul 5 02:38 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Jul 5 02:38 dev
drwxr-xr-x 1 root root 4096 Jul 5 02:38 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Jun 11 02:35 lost+found
drwxr-xr-x 2 root root 4096 May 11 2019 media
drwxr-xr-x 2 root root 4096 May 11 2019 mnt
drwxr-xr-x 2 root root 4096 May 11 2019 opt
dr-xr-xr-x 107 root root 0 Jul 5 02:38 proc
dr-xr-x--- 2 root root 4096 Jun 11 02:35 root
drwxr-xr-x 11 root root 4096 Jun 11 02:35 run
lrwxrwxrwx 1 root root 8 May 11 2019 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 May 11 2019 srv
dr-xr-xr-x 13 root root 0 Jul 5 02:33 sys
drwxrwxrwt 7 root root 4096 Jun 11 02:35 tmp
drwxr-xr-x 12 root root 4096 Jun 11 02:35 usr
drwxr-xr-x 20 root root 4096 Jun 11 02:35 var
可以成功执行。这个时候如果直接写ls -al
:
root@KitDevVps:/home/dockerfiles# docker run 52b32cc2192c ls -al
ls: cannot access 'ls': No such file or directory
注意,如果dockerfile文件直接命名为"Dockerfile",build的时候就不需要加-f
来指定对应的dockerfile文件了,它可以自动寻找到。
实践一个dockerfile
看一下centos7的dockerfile:
首先,几乎所有镜像都是FROM scratch,然后配置需要的软件和配置来进行构建。
然后ADD上centos的压缩包。
看一下ubuntu的dockerfile:
我们注意到用docker镜像run起来的ubuntu没有vim和ifconfig命令:
root@KitDevVps:~# docker run -d -it --name ubuntu01 ubuntu
daec7691f786dffa97ae5f757212da17da2c7591aeaa7a5c215217d0904f3089
root@KitDevVps:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
daec7691f786 ubuntu "/bin/bash" 4 seconds ago Up 3 seconds ubuntu01
1cdd55fd90c5 nginx "/docker-entrypoint.…" 36 hours ago Up 32 hours 0.0.0.0:80->80/tcp nginx1
root@KitDevVps:~# docker attach ubuntu01
root@daec7691f786:/# vim
bash: vim: command not found
root@daec7691f786:/# ifconfig
bash: ifconfig: command not found
我们自己构建一个ubuntu:
编写dockerfile文件
FROM centos
MAINTAINER Kit<xxxxxx@xx.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-tools
EXPOSE 8888
CMD echo $MYPATH
CMD echo "build succeed"
CMD /bin/bash
其中环境变量ENV是键值对的形式,MYPATH是key,/usr/local是值。
WORKDIR是用$号引用了环境变量。
使用RUN来update,然后安装了vim和net-tools。实际上执行三条命令不需要写三个RUN,这样会生成三层,太浪费,可以像上面ubuntu的Dockerfile那样,用\
和&&
等符号连接起来,要换行就用上\
,不换行可以一直用&&
来拼接,只写一个RUN。
最后一行CMD /bin/bash
是运行起来之后启动bash命令行。
注意,有一个常用的ADD
指令这里没有用到,ADD是添加压缩文件的,可以自动解压,压缩文件包名之后可以跟一个地址,选择解压的位置。这个位置当然是容器内的目录。看上面ubuntu镜像的ADD
后面的压缩包名后加了个/
,表示解压在根目录。
通过dockerfile构建镜像
使用docker build
命令。-f后面跟dockerfile名字,-t后面跟自己给镜像取的名字,也可以加上tag。最后的.是镜像生成的位置:
root@KitDevVps:/home/dockerfiles# docker build -f centos-dockerfile -t myubuntu:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/11 : FROM centos
---> 831691599b88
Step 2/11 : MAINTAINER Kit<xxxxxx@xx.com>
---> Running in f1f196999014
Removing intermediate container f1f196999014
---> 67670905b570
Step 3/11 : ENV MYPATH /usr/local
---> Running in 017f52d55537
Removing intermediate container 017f52d55537
---> 82b77bd524e0
Step 4/11 : WORKDIR $MYPATH
---> Running in a50c62fe9048
Removing intermediate container a50c62fe9048
---> 292ddb55bacb
Step 5/11 : RUN apt-get update
---> Running in 3d6d1e0ef4ce
/bin/sh: apt-get: command not found
The command '/bin/sh -c apt-get update' returned a non-zero code: 127
很尴尬,镜像竟然不能运行apt-get,不过一部分的过程可以看到。我就不再搞了,基本用不到生成ubuntu镜像这种需求,不折腾了。dockerfile里的命令是没有错的。
我们修改一下dockerfile,将apt指令改为普通的echo:
FROM centos
MAINTAINER Kit<xxxxxx@xx.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN echo "step 1"
RUN echo "step 2"
RUN echo "step 3"
CMD echo $MYPATH
CMD echo "build succeed"
CMD /bin/bash
build镜像
build一下:
root@KitDevVps:/home/dockerfiles# docker build -f centos-dockerfile -t myubuntu:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/10 : FROM centos
---> 831691599b88
Step 2/10 : MAINTAINER Kit<xxxxxx@xx.com>
---> Running in 1dedfc7bd7cd
Removing intermediate container 1dedfc7bd7cd
---> b810f07548a3
Step 3/10 : ENV MYPATH /usr/local
---> Running in 5f8ce5a07590
Removing intermediate container 5f8ce5a07590
---> 578eab12c981
Step 4/10 : WORKDIR $MYPATH
---> Running in b371dc06fd20
Removing intermediate container b371dc06fd20
---> 7abf094e042e
Step 5/10 : RUN echo "step 1"
---> Running in 0c65382a6491
step 1
Removing intermediate container 0c65382a6491
---> 04bf696c460e
Step 6/10 : RUN echo "step 2"
---> Running in 1276c444e36c
step 2
Removing intermediate container 1276c444e36c
---> d005e474c20c
Step 7/10 : RUN echo "step 3"
---> Running in 02de174de709
step 3
Removing intermediate container 02de174de709
---> 8f7f565d2061
Step 8/10 : CMD echo $MYPATH
---> Running in 028888e7f8c8
Removing intermediate container 028888e7f8c8
---> 7535a914d720
Step 9/10 : CMD echo "build succeed"
---> Running in b8eb3004dc32
Removing intermediate container b8eb3004dc32
---> f416fc8cb156
Step 10/10 : CMD /bin/bash
---> Running in b531285dbe9d
Removing intermediate container b531285dbe9d
---> dc3664ede0fc
Successfully built dc3664ede0fc
Successfully tagged myubuntu:1.0
这次成功了,其中输出的三次step123都输出了。
可以通过docker history 镜像id
来查看这个镜像是怎么一步一步构建起来的。看一下我们新构建的镜像:
root@KitDevVps:/home/dockerfiles# docker history dc3664ede0fc
IMAGE CREATED CREATED BY SIZE COMMENT
dc3664ede0fc 2 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
f416fc8cb156 2 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
7535a914d720 2 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
8f7f565d2061 2 minutes ago /bin/sh -c echo "step 3" 0B
d005e474c20c 2 minutes ago /bin/sh -c echo "step 2" 0B
04bf696c460e 2 minutes ago /bin/sh -c echo "step 1" 0B
7abf094e042e 2 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
578eab12c981 2 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
b810f07548a3 2 minutes ago /bin/sh -c #(nop) MAINTAINER Kit<xxxxxx@xx.… 0B
831691599b88 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ADD file:84700c11fcc969ac0… 215MB
比较全面的Dockerfile展示
上面写的Dockerfile都很儿戏,下面是狂神视频教程里写的一段tomcat的Dockerfile:
我不搞JAVA,不太懂,但是这一段Dockerfile里很全面。
- 先是FROM,基于centos镜像来构建。
- MAINTAINER,构建者的信息。
- COPY了一个readme文件进去,并指定了目录,可以写一些基本的使用文档在里面。
- ADD了两个必须的压缩包,并且指定了解压位置。
- RUN了一条命令,安装了vim。
- ENV设置一个环境变量,取名MYPATH。
- WORKDIR设置当前的工作目录,直接使用上面设置的环境变量MYPATH。如果用
exec
进入镜像,默认会在这个目录下。 - 连用几个ENV设置了一些必须的环境变量,都是JAVA和tomcat的,我看不懂。.NET Core的官方示例Dockerfile中也没有ENV,我推测ENV可以用来设置数据库连接字符串等。
- EXPOSE暴露8080端口。
- CMD指定运行的指令,这里应该是启动tomcat的指令。