Docker学习笔记(六)——Docker File解析
1. docker file是什么
-
定义:Docker file是用来构建Docker镜像的构建文件,是由一系列的命令和参数构成的脚本。
-
构建三步骤
- 编写Dockerfile文件
- docker build
- docker run
- docker file长什么样
可以在docker hub上随意搜一下,然后点开一个详细的版本就可以看到,比如centos7.0的docker file。
2. docker file构建过程解析
- docker file的基础知识
- 每条保留字指令都必须为大写字母,且后面要跟随至少一个参数
- 指令按照从上到下顺序执行
- "#"表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
- Docker 执行docker file的大致流程
- docker从基础镜像运行一个容器(scratch是祖先镜像)
- 执行一条指令并对容器作出修改
- 执行类似于docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行Dockerfile中的下一条指令,知道所有指令都完成
- 小结:从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
Dockerfile是软件的原材料
Docker镜像是软件的交付品
Docker容器则可以认为是软件的运行态。
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
三者关系
-
Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等。
-
Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时,会真正开始提供服务;
-
Docker容器,容器是直接提供服务的。
3. docker file保留字指令
保留字 | 作用 |
---|---|
From | 基础镜像,当前的新镜像是基于那个镜像的 |
MAINTAINER | 镜像维护者的姓名和邮箱地址 |
RUN | 容器构建时需要运行的命令 |
EXPOSE | 当前容器对外暴露出的端口 |
WORKDIR | 指定在创建容器后,终端默认的登录进来的工作目录 |
ENV | 用在构建镜像过程中是设置环境变量 |
ADD | 将宿主机目录下的文件拷贝进镜像且add命令会自动处理URL和解压tar压缩包 |
COPY | 类似于ADD,拷贝文件和目录到镜像中,不会执行解压缩,没有ADD功能强大 |
VOLUME | 容器数据卷,用于数据保存和持久化工作 |
CMD | 指定容器启动时要运行的命令 |
ENTRYPOINT | 指定容器启动时要运行的命令 |
ONBUILD | 当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后,父镜像的onbuild触发 |
4. docker file保留字案例演示
base镜像(scratch):Docker hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来的
实例一:自定义centos镜像
由于从阿里云上pull下来的centos镜像只有精简的内核,连vim和ifconfig都不能使用,下面这个实例,运用上面的docker file保留字,重写一个centos镜像,做出的改变有:
- 改变登录后的默认路径
- 支持vim编译器
- 可以使用ifconfig查看网络配置
三步走:
-
编写docker file,内容如下
docker file内容 -
构建新的镜像
构建新镜像
查看构建好的镜像 -
运行新镜像
运行新镜像
可以看到,新的镜像的默认落脚点变成了/usr/local,而不是root根目录。而且也可以使用vim工具和查看网络配置。
vim和网络配置
Tip:可以使用docker history [容器ID]
来查看镜像的变更历史,需要注意的是,该指令是镜像的指令,不要错误的使用容器ID。(尴尬脸)
上图可以发现,镜像是一层一层的网上叠加的,最后暴露的是ID为ced67ba4365b的镜像。
实例2:CMD和ENTRYPOINT的区别
根据其定义,两者都是启动一个容器时运行的命令,但是具有以下区别
区别1:Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换
区别2:ENTRYPOINT启动容器时,docker run 之后的参数会被当作参数传递给ENTRYPOINT,然后形成新的命令组合
区别一实例:以启动tomcat为例,tomcat的部分docker file如下:
tomcat最后的CMD命令
在运行tomcat时一般会使用
docker run -it -p 7777:8080 tomcat
,这时会执行tomcat的docker file的最后一条CMD:CMD ["catalina.sh", "run"]
,但是如果运行tomcat时使用的命令是docker run -it -p 7777:8080 tomcat ls -l
,就相当于在最后一条CMD后加了一个CMD ls -l /usr/local/tomcat
。CMD的命令覆盖
可以看到,执行完之后确实把/usr/local/tomcat下的文件列了出来,但是tomcat容器并没有启动,这是因为
CMD ["catalina.sh", "run"]
被新添加的CMD给覆盖了。
区别二实例:制作CMD版可以查询IP信息的centos容器
- 编写一个docker file
FROM centos
RUN yum install -y curl
CMD [ "curl", "-s", "http://cip.cc" ]
-
build docker file
创建ipcentos镜像 -
run ipcentos
运行时打印出ip
这时,以docker run ipcentos -i
来执行就会报一下错误
error
这种情况就是在docker file的最后一条CMDCMD [ "curl", "-s", "http://cip.cc" ]
后面又添加一个CMD -i
。所以会覆盖上一个CMD,产生错误。
下面使用ENTRYPOINT来解决CMD命令覆盖:
直接把上面的dockerfile的CMD改成ENTRYPOINT,按照上述流程再运行一遍:
可以看到,-i参数也被正确执行,打印出了HTTP报文头。所以,使用ENTRYPOINT相当于在
CMD [ "curl", "-s", "http://cip.cc" ]
中追加参数-i,变为CMD [ "curl", "-s", "-i" "http://cip.cc" ]