Docker之编写Dockerfile
1. Dockerfile介绍
Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。类似于Makefile。
构建三个步骤:
- 编写Dockerfile文件
- docker build
- docker run
1.1 docker build
docker build
命令可以设置编译镜像时,使用的CPU数量、内存大小、Dockerfile文件路径、编译参数等。docker build 命令的用法如下。
编译镜像由Docker Daemon完成。这是最简单的编译过程。
# docker build -t <image_name>
docker build的命令格式。
Usage: docker build [OPTIONS] PATH | URL | -
PATH是编译镜像使用的工作目录,Docker Daemon在编译开始时,扫描PATH中的所有文件。可以在编译目录中加入.dockerignore,过滤不需要的文件。Docker Daemon在编译时,会忽略.dockerignore中的文件。
默认情况下,Docker Daemon会从PATH 或 GIT中读取Dockerfile。如果需要使用其他的Dockerfile,可以使用 -f 参数。
# docker build -f ./Dockerfile .
使用 -t 给镜像打标签
# docker build -t mycentos/centos .
1.2 dockerignore文件
在编译开始之前,Docker Daemon会读取编译目录中的.dockerignore文件,忽略其中的文件和目录。可以在.dockerignore文件中加入文件和目录规则,忽略多个文件。
*/temp*
*/*/temp*
temp?
*.md
!README.md
*/temp* 忽略一级子目录下,以temp开头的所有文件和目录。
*/*/temp* 忽略二级子目录下,以temp开头的所有文件和目录。
temp? 忽略当前目录下,以temp开头,名字长度为5个字符的文件和目录。
Docker Daemon会读取 ! 后面的文件。上例中会读取README.md文件,并忽略所有的md文件。
1.3 Dockerfile格式
Dockerfile 中的每条指令由 指令+参数
组成,中间以空格隔离。# 后面为注释内容,# 可以写在任何位置。
指令不区分大小写,为了便于区分,一般指令使用大写,参数使用小写。Dockerfile中的第一条指令必须是FROM,设置基础镜像。
2. Dockerfile构建过程解析
2.1 Dockerfile内容基础知识:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
2.2 Docker执行Dockerfile的大致流程:
- docker从基础镜像运行一个容器
- 执行一条指令并对容器作出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
- Dockerfile是软件的原材料
- Docker镜像是软件的交付品
- Docker容器则可以认为是软件的运行态。
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
2.3 Dockerfile、镜像和容器三者关系
- Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
- Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时,会真正开始提供服务;
- Docker容器,容器是直接提供服务的。
3. DockerFile体系结构(指令详解)
Dockerfile 是Docker在编译镜像时执行的脚本文件,由很多条指令组成,这些指令按顺序排列。每条指令在编译镜像的过程中,执行相应的程序,完成某项功能。下面将介绍每条指令的功能。
-
FROM指令
基础镜像,当前新镜像是基于哪个镜像的。
在基础镜像中,加入提供服务的程序和程序所需要的依赖包,定制自己的镜像。
FROM指令格式FROM <image> FROM <image>:<tag> FROM <image>@<digest>
可以在一个Dockerfile文件中加入多条FROM指令,一次生成多个镜像。两条FROM指令之间的内容放在一个镜像中。
tag和digest是可选项。如果忽略tag,会使用latest镜像。 -
MAINTAINER指令
设置镜像作者。
MAINTAINER指令格式MAINTAINER <name>
-
RUN指令
容器构建时需要运行的命令。RUN指令会生成一个新的容器,在容器中执行脚本,这个容器使用当前的镜像。脚本正常执行完后,Docker Daemon 会把容器提交作为一个中间镜像,供后面指令使用。
RUN指令的格式RUN <command> RUN ["executable","param1","param2"]
RUN指令执行由两种方式,第一种为shell方式,使用
/bin/sh -c <command>
运行脚本。可以在这种方式中使用 \ ,把脚本分成多行。RUN source $HOME/.bashrc ;\ echo $HOME
第二种方式为exec方式。镜像中没有/bin/sh,或者要使用其他shell,私用这种方式。
RUN ["/bin/bash","-c","echo hello"]
-
EXPOSE指令
当前容器对外暴露出的端口,记录容器启动时监听哪些端口。EXPOSE <port> [<port>...]
-
WORKDIR指令
指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点.
设置RUN、CMD、ENTRYPOINT、ADD、COPY、指令的工作目录。WORKDIR指令的格式
WORKDIR /path/to/workdir
-
ENV指令
用来在构建镜像过程中设置环境变量。ENV <key> <value> ENV <key>=<value> ...
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;
也可以在其它指令中直接使用这些环境变量. -
ADD指令
使用ADD指令把文件复制到镜像中。
将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包。ADD指令格式
ADD <src> ... <dest> ADD ["<src>",..."<dest>"]
当路径中有空格时,需要使用第二种格式。
-
COPY指令
类似ADD,拷贝文件和目录到镜像中。
将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。COPY指令格式
COPY <src>...<dest> COPY ["<src>",..."<dest>"]
-
VOLUME指令
容器数据卷,用于数据保存和持久化工作。
使用VOLUME指令设置容器的挂载点。VOLUME指令格式
VOLUME ["/data"] VOLUME /data1 /data2
Docker Daemon会把主机目录或者数据卷容器挂在到这些挂载点。第一种格式为JSON格式,必须使用双引号。第二种格式把多个目录通过空格连接起来。
-
CMD指令
指定一个容器启动时要运行的命令使用CMD指令设置容器的启动命令,Dockerfile只能有一条CMD指令,如果写了多条CMD指令,只有最后一条CMD指令有效。
CMD指令格式
CMD ["executable","param1","param2"] CMD ["param1","param2"] CMD command param1 param2
第一种是exec方式,第二种是ENTRYPOINT参数方式,第三种是shell方式。
-
ENTRYPOINT指令
指定一个容器启动时要运行的命令。使用ENTRYPOINT指令设置容器的入口程序。
ENTRYPOINT指令格式
ENTRYPOINT ["executable","param1","param2"] ENTRYPOINT command param1 param2
入口程序有两种格式:第一种为exec方式,第二种为shell方式。第二种方式使用/bin/sh -c 运行入口程序,此时入口程序不能接收信号量。运行
docker stop <container>
时,程序不能接收SIGTERM。入口程序是容器启动时执行的程序,docker run 命令中最后的命令将作为参数传递给入口程序。
如果Dockerfile有多条ENTRYPOINT,只有最后的ENTRYPOINT指令生效。
-
ONBUILD指令
当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发。使用ONBUILD指令设置子镜像的编译钩子指令。
ONBUILD指令格式
ONBUILD [INSTRUCTION]
当从该镜像生成子镜像时,会调用ONBUILD指令。子镜像在编译过程中,首先会执行父镜像中的ONBUILD指令。所有的编译指令都可以称为钩子指令。
-
USER指令
USER指令设置用户名或UIDUSER daemon
-
ARG指令
使用ARG实例设置编译变量。
ARG指令格式ARG <name> [=<default value>]
编译镜像时,可以通过
docker build --build-arg <var>=<value>
设置这些变量。如果docker build设置的编译变量没有通过ARG定义,则Docker Daemon会报错。定义编译变量
FROM busybox ARG user
可以为编译变量设置默认值
FROM busybox ARG user=zxg
ARG从定义它的地方开始生效,而不是调用的地方
FROM busybox ARG user USER $user
编译时,设置编译变量的值
# docker build --build-arg user=zxg Dockerfile
CMD、ENTRYPOINT和RUN的区别
- RUN指令是设置编译镜像时执行的脚本和程序,镜像编译完成,RUN指令的生命周期结束。
容器启动时,可以通过CMD和ENTRYPOINT设置启动程序,两者有很大的区别和紧密的联系。
-
CMD叫做容器默认启动命令,既然是默认,就可以被替换掉。若在Docker run命令末尾添加Command,则替换镜像中CMD设置的启动程序。
-
ENTRYPOINT叫做入口程序,不能被docker run 命令末尾的Command替换。docker run 命令末尾的Command会被当作字符串,传递给ENTRYPOINT,作为参数。在docker run中,可以加入
--entrypoint
替换镜像中的入口程序.遵循规则:
- 在Dockerfile中,至少应该有一条CMD或ENTRYPOINT指令。
- 当使用容器作为一个程序容器时,应使用ENTRYPOINT定义程序入口。
- 在Dockerfile中,如果同时定义了ENTRYPOINT和CMD,CMD会作为参数传递给ENTRYPOINT。
- 在docker run 末尾加入Command,会覆盖镜像中的CMD。
- 在docker run 中加入 --entrypoint,会覆盖镜像中的ENTRYPOINT。
4. 自定义镜像(mycentos)
-
运行一个centos容器。
默认登录在根目录/
发现不支持vim和ifconfig命令。 -
自定义mycentos目的使我们自己的镜像具备如下功能:
- 登录的默认路径为 /root
- 支持vim编辑器
- 查看网络配置ifconfig支持
-
编写Dockerfile文件
FROM centos MAINTAINER zxg ENV MYPATH /root WORKDIR $MYPATH RUN yum -y install vim RUN yum -y install net-tools EXPOSE 80 CMD echo $MYPATH CMD echo "------successk----" CMD /bin/bash
ENV用来在构建镜像过程中设置环境变量。
WORKDIR用来设置工作目录。
RUN 命令安装vim和net-tools。 -
使用docker build命令生成镜像
[root@zxg mydocker]# docker build -f ./Dockerfile -t mycentos:1.0 .
mycentos镜像已经生成。
-
运行
[root@zxg ~]# docker run -it --name mycentos01 mycentos:1.0
发现此时工作目录为 /root,
并且支持vim和ifconfig命令。 -
列出镜像的变更历史
相关博客