Docker的Dockerfile

参考资料:B站狂神教程
https://www.bilibili.com/video/BV1og4y1q7M4?p=26

Dockerfile是什么

Dockerfile是一个构建Docker镜像的命令参数脚本。

构建镜像的步骤:

  1. 编写一个Dockerfile文件
  2. docker build 构建镜像
  3. docker run 运行镜像
  4. docker push 发布镜像

Dockerfile的构建过程

构建Dockerfile需要了解很多能写在Dockerfile里的命令:
https://docs.docker.com/engine/reference/builder/#from

这是官方文档,里面有部分命令,不知道全不全,肯定是够用了。

还有这张图片,很有意思:

UTOOLS1593876365844.png

每个指令都会创建提交一个新的镜像层,并提交。最后跑起来的时候再加一层可写层:

UTOOLS1593876549281.png

Dockerfile的指令

还是这个文档:https://docs.docker.com/engine/reference/builder/#from

看一下命令大概的分类:

UTOOLS1593876871230.png

从左到右依次是构建的命令,连接工作目录等的命令和启动执行的命令。常用命令就是上面那张铁锈红色的图。

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:

UTOOLS1593878458487.png

首先,几乎所有镜像都是FROM scratch,然后配置需要的软件和配置来进行构建。

然后ADD上centos的压缩包。

看一下ubuntu的dockerfile:

UTOOLS1593879318200.png

我们注意到用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:

UTOOLS1593918821757.png

我不搞JAVA,不太懂,但是这一段Dockerfile里很全面。

  1. 先是FROM,基于centos镜像来构建。
  2. MAINTAINER,构建者的信息。
  3. COPY了一个readme文件进去,并指定了目录,可以写一些基本的使用文档在里面。
  4. ADD了两个必须的压缩包,并且指定了解压位置。
  5. RUN了一条命令,安装了vim。
  6. ENV设置一个环境变量,取名MYPATH。
  7. WORKDIR设置当前的工作目录,直接使用上面设置的环境变量MYPATH。如果用exec进入镜像,默认会在这个目录下。
  8. 连用几个ENV设置了一些必须的环境变量,都是JAVA和tomcat的,我看不懂。.NET Core的官方示例Dockerfile中也没有ENV,我推测ENV可以用来设置数据库连接字符串等。
  9. EXPOSE暴露8080端口。
  10. CMD指定运行的指令,这里应该是启动tomcat的指令。
posted @ 2020-07-05 11:27  Kit_L  阅读(275)  评论(0编辑  收藏  举报