1. Dockerfile 是什么。
Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本文件。
2. Dockerfile 的作用。
使用 docker build 命令指定Dockerfile ,可以根据Dockerfile创建一个镜像。
Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
3. Dockerfile的一个例子。
centos的docker镜像地址 hub.docker.com/_/centos/ 可以在上面看到构建centos镜像的Dockerfile。如下:
FROM scratch
ADD centos-7-docker.tar.xz /
LABEL org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20181205"
CMD ["/bin/bash"]
centos镜像的Dockerfile里面有一个 FROM scratch 指令,scratch镜像是所有docker镜像的基础镜像,其他的镜像直接或间接继承scratch。
4. Dockerfile的一些规范。
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数。
- 指令按照从上到下,顺序执行。
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交。
5. Docker build 执行Dockerfile的大致流程。
- docker从基础镜像运行一个容器。
- 执行一条指令并对容器作出修改。
- 执行类似docker commit的操作提交一个新的镜像层。
- docker再基于刚提交的镜像运行一个新容器。
- 执行dockerfile中的下一条指令直到所有指令都执行完成。
也就是docker每次执行一条dockfile的指令都会形成一个新的镜像层,直到所有指令都执行完成。
6. Dockerfile的一些保留字指令。
每条保留字指令都必须为大写字母且后面要跟随至少一个参数。
- FROM 当前新镜像是基于哪个镜像的。
- MAINTAINER 镜像维护者的姓名和邮箱地址。
- RUN 容器构建时需要运行的命令。也就是执行RUN后面的命令。
- EXPOSE 当前容器对外暴露出的端口,比如:tomcat对外暴露的8080端口。
- WORKDIR 指定在创建容器后,终端默认登陆的进来工作目录。也就是我们进入docker容器的目录。
- ENV 用来在构建镜像过程中设置环境变量。也就是声明一个变量。方便后面引用。比如:ENV MY_PATH /usr/mytest。这个环境变量MY_PATH 可以在后续的任何指令中使用。比如:WORKDIR $MY_PATH。
ADD 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包。
COPY 类似ADD,拷贝文件和目录到镜像中。但是不会解压,也不会处理URL,单纯的拷贝。
VOLUME 容器数据卷,用于数据保存和持久化工作。
CMD 指定一个容器启动时要运行的命令。Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换。CMD 指令支持三种格式:
在/bin/sh 中执行,提供给需要交互的应用: CMD <命令 参数> 使用 exec 执行,推荐方式: CMD ["可执行文件","参数1","参数2"] 在指定了ENTRYPOINT指令后,用CMD指定默认参数: CMD ["参数1","参数2"]
ENTRYPOINT 指定一个容器启动时要运行的命令。ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。
ONBUILD 父镜像在被子镜像继承后父镜像的onbuild被触发。也就是docker build运行Dockerfile时,如果这个Dockerfile继承某个父Dockerfile,那父Dockerfile的ONBUILD后面的指令会被执行。
6.1. 例子1:使用Dockerfile创建自己的centos镜像。
Dockerfile的内容如下:
FROM centos
MAINTAINER wk<wk@163.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "success--------------ok"
CMD /bin/bash
上面的内容表示:
- FROM centos 表示这个镜像从centos派生出来的。
- MAINTAINER wk<wk@163.com> 表示镜像维护人创建人的名字和邮箱。
ENV MYPATH /usr/local 表示定义一个变量MYPATH的值是/usr/local。
WORKDIR $MYPATH 表示登录这个镜像运行的容器后,初始目录是/usr/local。$MYPATH是引用上面的MYPATH变量。
RUN yum -y install vim 表示在镜像里面运行命令yum -y install vim 安装vim命令工具。
RUN yum -y install net-tools 表示在镜像里面运行命令yum -y install net-tools 安装ifconfig命令工具。
EXPOSE 80 表示容器对外暴露 80端口。
CMD echo $MYPATH 表示在镜像里面运行命令echo $MYPATH 打印$MYPATH到控制台。
CMD echo "success--------------ok" 表示在镜像里面运行命令echo "success--------------ok" 打印success--------------ok到控制台。
CMD /bin/bash 表示在镜像里面运行命令/bin/bash。这个是最后一层镜像,也就是最终的镜像层。容器启动的时候也会运行 /bin/bash 命令。
使用上面的dockerfile生成镜像,命令格式如下:最后一个点表示当前目录。
docker build -f Dockerfile文件的位置 -t 新的镜像名:TAG .
运行的命令如下:表示使用当前目录下的Dockerfile文件创建了一个镜像mycentos,TAG是1.2。
docker build -f ./Dockerfile -t mycentos:1.2 .
查看镜像,可以看到刚刚创建的镜像。
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.2 737ea05543bb 2 minutes ago 414MB
运行这个镜像之后,使用pwd命令可以看到当前目录是镜像文件定义的WORKDIR目录/usr/local ;也可以使用ifconfig命令了。镜像定义成功。
[root@localhost ~]# docker run -it mycentos:1.2
[root@25e927bac0b7 local]# pwd
/usr/local
[root@25e927bac0b7 local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 8 bytes 648 (648.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
6.2. 例子2:查看镜像的变更历史。
查看镜像的变更历史,命令如下:
docker history 镜像名:TAG
运行如下,可以看到docker是每运行一个dockerfile命令就生成一个镜像层。
[root@localhost ~]# docker history mycentos:1.2
IMAGE CREATED CREATED BY SIZE COMMENT
737ea05543bb 9 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
557b24a936f4 9 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
d40c270936e0 9 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
bd2ffd3ab0f6 9 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
d176bcd23491 9 minutes ago /bin/sh -c yum -y install net-tools 78.5MB
bb75e3370010 9 minutes ago /bin/sh -c yum -y install vim 133MB
341b26a9810c 20 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
9411d0c4629a 20 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
07524018ec26 20 minutes ago /bin/sh -c #(nop) MAINTAINER wk<wk@163.com> 0B
1e1148e4cc2c 3 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 3 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 3 months ago /bin/sh -c #(nop) ADD file:6f877549795f4798a… 202MB
6.3. CMD/ENTRYPOINT指令的区别。
我们运行一个容器的命令如下:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
其中[COMMAND] [ARG...] 表示容器启动之后要运行的命令和参数。
Dockerfile中CMD和ENTRYPOINT的区别如下:
CMD/ENTRYPOINT 都是指定一个容器启动时要运行的命令。也就是使用docker run 运行容器的时候。要执行的命令[COMMAND] [ARG...] 可以由Dockerfile中CMD或者ENTRYPOINT 指定。比如:tomcat镜像就指定运行其容器的时候启动tomcat。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效。如果运行容器docker run 的时候 [COMMAND] [ARG...] 存在,那Dockerfile中CMD会被这个命令替换。就不会执行Dockerfile中的CMD指定的命令。
docker run 之后的参数[COMMAND] [ARG...]会被当做参数传递给 ENTRYPOINT,之后形成新的命令组合 。
例子:如果centos镜像Dockerfile 中ENTRYPOINT指定的命令是 ls ,运行centos容器的命令是docker run -it centos -l,也就是[COMMAND] [ARG...]是-l 。结果运行centos容器之后执行的命令是 ls -l。就是把docker run中的[COMMAND] [ARG...]传递的值作为命令参数传递给ENTRYPOINT。
6.3.1. 示例如下:
- 新建一个cmd命令的Dockerfile 。
FROM centos
CMD [ "ls"]
- 新建一个ENTRYPOINT命令的Dockerfile 。
FROM centos
ENTRYPOINT ["ls"]
- 以上面的2个Dockerfile 创建2个镜像。mycentoscmd 和mycentosentrypoint。
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentosentrypoint latest 0a7cbb21686b 20 seconds ago 202MB
mycentoscmd latest 7fc40a4ef1eb 3 minutes ago 202MB
- 运行容器。可以看到文件。相当于登录centos运行命令ls。如下:
[root@localhost ~]# docker run -it mycentoscmd
anaconda-post.log dev home lib64 mnt proc run srv tmp var
bin etc lib media opt root sbin sys usr
[root@localhost ~]# docker run -it mycentosentrypoint
anaconda-post.log dev home lib64 mnt proc run srv tmp var
bin etc lib media opt root sbin sys usr
- 加 -l 参数运行容器。也就是[COMMAND] [ARG...]的值是-l。这时候mycentoscmd镜像会把-l当做一个命令,会报错;mycentosentrypoint会列出文件相当于登录centos运行命令ls -l。如下:
[root@localhost ~]# docker run -it mycentosentrypoint -l
total 12
-rw-r--r--. 1 root root 12076 Dec 5 01:37 anaconda-post.log
lrwxrwxrwx. 1 root root 7 Dec 5 01:36 bin -> usr/bin
drwxr-xr-x. 5 root root 360 Mar 16 17:32 dev
drwxr-xr-x. 1 root root 66 Mar 16 17:32 etc
drwxr-xr-x. 2 root root 6 Apr 11 2018 home
lrwxrwxrwx. 1 root root 7 Dec 5 01:36 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Dec 5 01:36 lib64 -> usr/lib64
drwxr-xr-x. 2 root root 6 Apr 11 2018 media
drwxr-xr-x. 2 root root 6 Apr 11 2018 mnt
drwxr-xr-x. 2 root root 6 Apr 11 2018 opt
dr-xr-xr-x. 205 root root 0 Mar 16 17:32 proc
dr-xr-x---. 2 root root 114 Dec 5 01:37 root
drwxr-xr-x. 11 root root 148 Dec 5 01:37 run
lrwxrwxrwx. 1 root root 8 Dec 5 01:36 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 Apr 11 2018 srv
dr-xr-xr-x. 13 root root 0 Mar 12 02:00 sys
drwxrwxrwt. 7 root root 132 Dec 5 01:37 tmp
drwxr-xr-x. 13 root root 155 Dec 5 01:36 usr
drwxr-xr-x. 18 root root 238 Dec 5 01:36 var
[root@localhost ~]# docker run -it mycentoscmd -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.