目录
一. Dockerfile简介
1. 什么是Dockerfile
在之前的学习中,我们了解到可以将运行起来的容器通过commit打包为一个镜像,但是这种方法仅限于在已有容器的基础上,并且具有一定的局限性。于是便产生了Dockerfile构建镜像。
Dockerfile是一种Docker镜像的描述文件,是由一系列命令和参数构成的脚本。它主要作用是用来构建docker镜像的构建文件。Dockerfile文件在构建时,按照指令顺序由上到下依次执行,每条指令都会基于上一层创建并叠加一个新的镜像层,并对镜像进行提交。最终得到我们定制的镜像。
2. Dockefile构建原理
Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如
docker
命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种docker
功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。在构建的时候,用户会指定构建镜像上下文的路径,
docker build
命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。然后服务器会解析dockerfile文件,并自动执行其中的指令,来构建我们定制的镜像。
3. Dockerfile构建指令
# docker build [OPTIONS] [PATH | URL | -]
- OPTIONS:
- -f : 显式指定构建镜像的 Dockerfile 文件名称(Dockerfile 可不在上下文目录中),若不指明则默认为当前目录下的 'Dockerfile'
- -t : 指明docker镜像的名称和版本号 imag:tag
- 上下文路径|URL:指定构建镜像的上下文目录路径。docker会将上下文目录的所有内容作为镜像环境全部打包发给服务器,构建镜像的过程中,可以且只可以引用上下文中的任何文件(但是上下文目录并不封装到镜像中,只是作为镜像构建的服务器目录环境) 。因此,在实际开发中通常创建一个空目录来放置Dockerfile,并使用 . 指定当前目录作为上下文目录。
#举例子
docker build -f mydockerfile -t mycentos:1.0 .
二. Dockerfile指令详解
1. FROM
FROM指令用来指定构建的基础镜像。所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。因此一个
Dockerfile
中FROM
是必备的指令,并且必须是第一条指令。其语法如下:
FROM <image>:<tag>
#举例子
FROM centos:7
- FROM指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。
- FROM必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM语句开始。
- FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。
- 如果FROM语句没有指定镜像标签,则默认使用latest标签。
2. RUN
RUN指令用于在构建镜像时,将在当前镜像层基础上执行指定命令(如yum下载安装、文件处理、环境配置等等),并提交为新的镜像层,后续的RUN都以之前RUN提交后的镜像为基础。
RUN
指令在定制镜像时是最常用的指令之一。其语法如下:
- shell 格式:
RUN <命令> 比如:RUN yum install vim
- exec 格式:
RUN ["可执行文件", "参数1", "参数2"](不要使用单引号) 比如:RUN ["yum","install","vim"]
#例子:定制镜像时安装vim和wget(等同于,在终端操作的 shell 命令。)
FROM centos:7
RUN yum install -y vim
RUN ["yum","install","-y","net-tools"]
改进:dockerfile支持以 && 连接多条指令,行尾添加
\
的命令换行,以及行首#
进行注释的格式,这样我们可以将多条RUN命令串联起来,将两层镜像层简化为一层,缩小镜像体积减少镜像臃肿。
#例子:定制镜像时安装vim和wget(等同于,在终端操作的 shell 命令。)
FROM centos:7
RUN yum install -y vim \
&& yum install -y net-tools
3. WORKDIR
使用
WORKDIR
指令可以来指定工作目录(或者称为当前目录,即刚入容器时所在的目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR
会帮你建立目录。其语法为:
WORKDIR <工作目录路径>
FROM centos:7
WORDIR /usr
注意:
- 不要使用RUN cd 来指定变更工作目录:由于Docker是分层的,RUN的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。
- 可以使用多个WORKDIR指令:后续命令如果参数是相对路径,则会基于之前命令指定的路径。
4. ENV
这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如
RUN
,还是运行时的应用,都可以直接使用这里定义的环境变量。其语法如下:
ENV <key> <value>
# 只能设置一个变量ENV <key1>=<value1> <key2>=<value2>...
#允许一次设置多个变量
#ENV 定义环境变量
ENV NODE_VERSION 7.2.0
# $NODE_VERSION 调用环境变量
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
5. EXPOSE
EXPOSE
指令是声明运行时容器提供的服务端口。这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。该指令仅仅是声明我们这个镜像中打算使用哪些端口,并没有实质性的作用,只是便于开发人员理解和维护镜像配置。我们还是要通过 -p 来公开映射外部访问端口。其语法如下:
- EXPOSE <端口1> [ <端口2>... ]
6. VOLUME
为了防止启动容器时用户忘记将动态文件所保存目录挂载为数据卷,在
Dockerfile
中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,则相应目录会自动挂载到一个匿名数据卷上。其语法为:
VOLUME ["<路径1>", "<路径2>"...]
#多路径挂载VOLUME <路径> #单路径挂载
#匿名挂载
VOLUME /data
#容器启动
docker run -d xxx #/data 目录就会在运行时自动挂载为匿名卷
docker run -d -v mydata:/data xxxx #mydata 这个命名卷替代了Dockerfile 中定义的匿名卷的挂载配置。
7. COPY
COPY
指令将从构建上下文目录中<源路径>
的文件/目录复制到新的一层的镜像内的<目标路径>
位置。其语法如下:
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
注意:
<源路径>
可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则<目标路径>
只有一个,可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用WORKDIR
指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
FROM centos:7
WORKDIR /usr
COPY aa.txt bb.txt /apps/ #复制两个文件到绝对路径/apps下
8. ADD
ADD
指令和COPY
的格式和性质基本一致,也是用来给镜像复制文件。但是在COPY
基础上增加了一些功能。
<源路径>
可以是一个URL:在这情况下,
Docker 引擎会试图去下载这个链接的文件放到<目标路径>
去,下载后的文件权限自动设置为600 。但下载文件不会自动解压缩。
<源路径>
为一个tar
压缩文件:ADD
指令将会自动解压缩这个压缩文件到<目标路径>
去。
FROM centos:7
WORKDIR /usr
#从URL下载
ADD https://xxx.tar.gz /data/apps/
#本地解压文件
ADD tomcat-8.5.tar.gz /data/apps/
RUN mv tomcat-8.5 tomcat #改名
9. CMD
CMD指令用于在容器启动时自动执行某些命令(类似于开机启动项),其包括以下三种语法格式:
shell
格式(推荐):CMD <命令>
exec
格式(双引号):CMD ["可执行文件", "参数1", "参数2"...]
- 参数列表格式:
CMD ["参数1", "参数2"...]
。在指定了ENTRYPOINT
指令后,搭配用CMD
指定具体的参数。注意:
- 每个 Dockerfile 只能有一条
CMD
命令。如果指定了多条命令,只有最后一条会被执行。- 如果用户启动容器时候指定了运行的命令,则会覆盖掉
CMD
指定的命令。
FROM centos:7
WORKDIR /usr
#CMD ["ls","/usr"] 开机展示/usr目录
CMD ls /usr
-----------------------------------
docker run -it mycentos:3.0 #默认执行CMD
docker run -it mycentos:3.0 ls /root #指定"ls /root"会覆盖CMD
10. ENTRYPOINT
ENTRYPOINT
的目的和CMD
一样,都是在指定容器启动程序及参数。ENTRYPOINT
在运行时也可以替代,不过比CMD
要略显繁琐,需要通过docker run
的参数--entrypoint
来指定。其语法规则如下:
- ENTRYPOINT <命令>
- ENTRYPOINT ["可执行文件","参数1","参数2"]
注意:
- 每个 Dockerfile 只能有一条
ENTRYPOINT
命令。如果指定了多条命令,只有最后一条会被执行。- 如果用户启动容器时候指定了运行的参数,则会添加到
ENTRYPOINT
指定的命令之后。
FROM centos:7
WORKDIR /usr
#ENTRYPOINT ["ls","/usr"] 开机展示/usr目录
ENTRYPOINT ls
-----------------------------------
docker run -it mycentos:3.0 #默认执行
docker run -it mycentos:3.0 /usr #追加 ls /usr 执行
ENTRYPOINT常与CMD搭配使用,当指定了
ENTRYPOINT
后,CMD
的含义就发生了改变,不再是直接的运行其命令,而是将CMD
的内容作为参数传给ENTRYPOINT
指令,换句话说实际执行时,将变为:<ENTRYPOINT> "<CMD>"。因此,常
在ENTRYPOINT中指定固定不变的命令,在CMD中指定可变命令参数,在docker run时通过指定参数来覆盖CMD内容实现镜像变成像命令一样使用。(注意ENTRYPOINT和CMD都只能使用exec格式)
FROM centos:7
WORKDIR /usr
ENTRYPOINT ["ls"]
CMD ["/usr"]
-------------------
docker run -it mycentos:3.0 #默认执行 ls /usr
docker run -it mycentos:3.0 /root #执行 ls /root
三. Dockerfile打包项目实例
FROM openjdk:8-jdk #基础jdk环境
WORKDIR /app #指定工作目录
COPY demo-0.0.1-SHAPSHOT.jar app.jar #复制项目jar包并改名
EXPOSE 8081 #暴露端口
ENTRYPOINT ["java","-jar"] #固定执行指令
CMD ["app.jar"] #可变 jar名称
------------------------------------
docker run -d -p 8081:8081 --name demo demo:01 #运行容器