【Docker学习笔记(三)】Dockerfile解析

【Docker 学习笔记目录】


【本文目录】


1 Dockerfile 概述

Dockerfile 是用来构建 Docker 镜像的构建文件,其是由一系列指令和参数构成的脚本

构建三步骤:

  • 1、编写 Dockerfile 文件
  • 2、docker build
  • 3、docker run

Dockerfile 指令:

  • 1、大小写不敏感,但是建议全部使用大写
  • 2、指令按照从上到下顺序执行
  • 3、每条指令都会创建一个新的镜像层,并对镜像进行提交

Dockerfile 构建过程解析:

  • 1、Docker 从基础镜像运行一个容器
  • 2、执行一条指令并对容器作出修改
  • 3、执行类似 docker commit 的操作提交一个新的镜像层
  • 4、Docker 再基于刚提交的镜像运行一个新容器
  • 5、执行 Dockerfile 中的下一条指令直到所有指令都执行完成

2 Dockerfile 指令

1、FROM

  • 必须放在 Dockerfile 的第一行,表示从哪个 baseimage 开始构建

2、MAINTAINER

  • 可选的,用来标识镜像作者的姓名和邮箱地址

3、RUN

  • 容器构建时需要运行的命令

4、EXPOSE

  • 当前容器对外暴露出的端口

5、WORKDIR

  • 指定在创建容器后终端默认登录进来的工作目录,一个落脚点

6、ENV

  • 用来在构建镜像过程中设置环境变量
  • 这个环境变量可以在后续的指令中使用
  • eg:
    • ENV MY_PATH /usr/mytest
    • WORKDIR $MY_PATH

7、ADD

  • 将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包

8、COPY

  • 类似 ADD,拷贝文件或目录到镜像中
    • COPY SRC DEST
    • COPY ["SRC", "DEST"]

9、VOLUME

  • 容器数据卷,用于数据保存和持久化

10、CMD

  • 指定容器启动时要运行的命令
    • shell 格式:CMD <命令>
    • exec 格式:CMD ["可执行文件", "参数1", "参数2" ...]
  • Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换

11、ENTRYPOINT

  • 指定容器启动时要运行的命令
    • exec 格式:ENTRYPOINT ["COMMAND","arg1" ...]
  • Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效,ENTRYPOINT 不会被 docker run 之后的参数替换
    • 不过,docker run的--entrypoint可以覆盖Dockerfile中ENTRYPOINT设置的命令

12、ONBUILD

  • 当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子继承后,父镜像的 ONBUILD 会被触发

3 案例

3.1 Base镜像(scratch)

Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的,即 FROM scratch

3.2 自定义镜像 mycentos

(1)编写 Dockerfile。因为默认 centos 镜像不包含 vim 和 ifconfig,且默认路径为根目录。所以我们自定义 mycentos 镜像具备功能:vim编辑器、查看网络配置 ifconfig、登录后的默认路径

FROM centos
MAINTAINER haha<haha@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

(2)构建镜像。执行命令 docker build -f <DockerfilePath> -t <新镜像名>[:TAG] .

[root@localhost mydocker]# docker build -f /mydocker/Dockerfile -t mycentos:1.3 .
Sending build context to Docker daemon  2.048kB
Step 1/10 : FROM centos
 ---> 49f7960eb7e4
Step 2/10 : MAINTAINER haha<haha@163.com>
 ---> Running in abba9b25097a
Removing intermediate container abba9b25097a
 ---> 6b31565e745c
...
...
...
Step 9/10 : CMD echo "SUCCESS----------------OK"
 ---> Running in ec3732fd9697
Removing intermediate container ec3732fd9697
 ---> f85df2928359
Step 10/10 : CMD /bin/bash
 ---> Running in 8c690565ae88
Removing intermediate container 8c690565ae88
 ---> 9fa354c5af84
Successfully built 9fa354c5af84
Successfully tagged mycentos:1.3
[root@localhost mydocker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mycentos            1.3                 9fa354c5af84        3 minutes ago       429MB

(3)运行。执行命令 docker run -it mycentos:1.3,并测试 vim 和 ifconfig

[root@localhost mydocker]# docker run -it mycentos:1.3
[root@c4c9780e9bd2 local]# vim hi.txt
[root@c4c9780e9bd2 local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet ***********  netmask ***********  broadcast ***********
        ether ***********  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 ***********  netmask ***********
        loop  txqueuelen 1000  (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

(4)查看镜像变更历史,使用命令 docker history <镜像ID>

[root@localhost mydocker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mycentos            1.3                 769456b1e3b6        5 minutes ago       429MB
[root@localhost mydocker]# docker history 769456b1e3b6
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
769456b1e3b6        5 minutes ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B                  
9d471d49e890        5 minutes ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B                  
d8af111f35dc        5 minutes ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B                  
478098996ebd        5 minutes ago       /bin/sh -c #(nop)  EXPOSE 80                    0B                  
5e4dd31fa027        5 minutes ago       /bin/sh -c yum -y install net-tools             87.2MB              
bea7db715bc2        6 minutes ago       /bin/sh -c yum -y install vim                   142MB               
5fd476dce352        8 minutes ago       /bin/sh -c #(nop) WORKDIR /usr/local            0B                  
c1463e67e0b3        16 minutes ago      /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B                  
6b31565e745c        16 minutes ago      /bin/sh -c #(nop)  MAINTAINER haha<haha@163.…   0B                  
49f7960eb7e4        8 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
<missing>           8 weeks ago         /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B                  
<missing>           8 weeks ago         /bin/sh -c #(nop) ADD file:8f4b3be0c1427b158…   200MB  

3.3 CMD/ENTRYPOINT 镜像

CMD:Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换

我们可以查看到官方 tomcat 的 Dockerfile 中最后一个 CMD 命令为:CMD ["catalina.sh", "run"],则表示直接 docker run tomcat会输出 tomcat 运行日志

[root@localhost mydocker]# docker run tomcat
31-Jul-2018 13:15:09.738 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version:        Apache Tomcat/8.5.32
31-Jul-2018 13:15:09.741 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Jun 20 2018 19:50:35 UTC
31-Jul-2018 13:15:09.741 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number:         8.5.32.0
31-Jul-2018 13:15:09.746 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Linux
31-Jul-2018 13:15:09.746 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            3.10.0-862.9.1.el7.x86_64
31-Jul-2018 13:15:09.746 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
31-Jul-2018 13:15:09.746 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             /usr/lib/jvm/java-8-openjdk-amd64/jre

当我们执行命令 docker run -it -p 7777:8080 tomcat ls -l,可以注意到,CMD 被 docker run 后面的参数替换了

[root@localhost mydocker]# docker run -it -p 7777:8080 tomcat ls -l
total 92
-rw-r-----. 1 root root  57092 Jun 20 19:53 LICENSE
-rw-r-----. 1 root root   1723 Jun 20 19:53 NOTICE
-rw-r-----. 1 root root   7138 Jun 20 19:53 RELEASE-NOTES
-rw-r-----. 1 root root  16246 Jun 20 19:53 RUNNING.txt
drwxr-x---. 2 root root   4096 Jul 17 18:16 bin
drwx--S---. 2 root root    238 Jun 20 19:53 conf
drwxr-sr-x. 3 root staff    19 Jul 17 18:16 include
drwxr-x---. 2 root root   4096 Jul 17 18:15 lib
drwxr-x---. 2 root root      6 Jun 20 19:50 logs
drwxr-sr-x. 3 root staff   151 Jul 17 18:16 native-jni-lib
drwxr-x---. 2 root root     30 Jul 17 18:15 temp
drwxr-x---. 7 root root     81 Jun 20 19:51 webapps
drwxr-x---. 2 root root      6 Jun 20 19:50 work

ENTRYPOINT:docker run 之后的参数会被当做参数传递给 ENTRYPOINT ,之后形成新的命令组合

首先创建一个新的镜像 myip,其 Dockerfile 如下:

FROM centos
RUN yum install -y curl
CMD "curl", "-s", "https://ip.cn"
[root@localhost mydocker]# vim Dockerfile2
[root@localhost mydocker]# docker build -f /mydocker/Dockerfile2 -t myip .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM centos
 ---> 49f7960eb7e4
Step 2/3 : RUN yum install -y curl
 ---> Using cache
 ---> 8e148a68fc86
Step 3/3 : CMD ["curl", "-s", "https://ip.cn"]
 ---> Running in 7be857257291
Removing intermediate container 7be857257291
 ---> 6a341f6d2b42
Successfully built 6a341f6d2b42
Successfully tagged myip:latest
[root@localhost mydocker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myip                latest              036adbaf59cb        13 seconds ago      286MB

接着执行命令 docker run -it myip

[root@localhost mydocker]# docker run -it myip
当前 IP:********* 来自:***省***市 ***
[root@localhost mydocker]# 

如果我们想显示更多的信息,比如 HTTP 头信息,则需要加上参数 -i (即 curl -s -i https://ip.cn)。那么是不是执行 docker run myip -i 呢?我们可以试试

[root@localhost mydocker]# docker run myip -i
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"-i\": executable file not found in $PATH": unknown.

可以看到 executable file not found 。因为跟在镜像名后面的是 command ,运行时会替换 CMD 的默认值。在这里的 -i 替换了原来的 CMD ,而不是添加在原来的 curl -s https://ip.cn 后面。而 -i 根本不是命令,所以自然找不到啦

那么要如何加这个 -i 参数才能运行呢?答案就是 docker run myip curl -s https:ip.cn -i

[root@localhost lycai]# docker run myip curl -s https://ip.cn -i
HTTP/1.1 200 OK
Date: Tue, 31 Jul 2018 13:50:12 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=************************; expires=Wed, 31-Jul-19 13:50:11 GMT; path=/; domain=.ip.cn; HttpOnly
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 4430885bdd39aa50-SIN

当前 IP:******** 来自:***省***市 ***

现在我们再编写一个 Dockerfile3,制作 ENTRYPOINT 版查询 IP 信息的镜像 myip_new

FROM centos
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://ip.cn"]

最后就可以用 docker run myip_new -i

[root@localhost mydocker]# docker run myip_new -i
HTTP/1.1 200 OK
Date: Tue, 31 Jul 2018 13:55:06 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=************************; expires=Wed, 31-Jul-19 13:55:05 GMT; path=/; domain=.ip.cn; HttpOnly
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 44308f897c6831ec-SIN

当前 IP:********* 来自:***省***市 ***

其相当于原来的 myip 镜像的 Dockerfile 中最后一行变成了 CMD ["curl", "-s", "-i", "https://ip.cn"],而这个 -i 是作为参数传进去的

4 总结