写给后端的Docker初级入门教程:DockerFile 命令详解

5,858 阅读8分钟

在上一篇文章写给后端的Docker初级入门教程:实战篇最后我们有提到用DockerFile来构建和定制属于我们自己的镜像,因为时间和篇幅问题,上一篇文章对DockerFile只做了一个简单的介绍和使用,并没有对DockerFile具体的指令进行详细的介绍和解释,本篇,作为上一篇实战篇的额外补充篇,我们将从DockerFile基础的命令入手,一步一步的去构建一个属于我们自己的镜像出来。

DockerFile介绍:

Dockerfile是由一系列命令和参数构成的脚本,一个Dockerfile里面包含了构建整个image的完整命令。Docker通过docker build执行Dockerfile中的一系列命令自动构建image。

实例:

这里我们仍然选择我们上一篇使用的在centos基础上定制我们自己的镜像为本章的代码实例,代码如下:

FROM centos  //继承至centos
ENV mypath /tmp  //设置环境变量
WORKDIR $mypath //指定工作目录

RUN yum -y install vim //执行yum命令安装vim
RUN yum -y install net-tools //执行yum命令安装net-tools

EXPOSE 80 //对外默认暴露的端口是80
CMD /bin/bash //CMD 容器启动命令,在运行容器的时候会自动执行这行命令,比如当我们 docker run -it centos 的时候,就会直接进入bash

之后再通过docker build 命令编译该DockerFile便可以得到一个属于自己的镜像了。

然后编译该镜像
docker build -f ./DockerFile -t mycentos:1.3.
-t 新镜像名字:版本
-f 文件 -d 文件夹

运行该镜像会发现vim和net-tools在我们新的容器中已经可以正常使用了。

接下来呢,我们将从FROM命令开始逐行介绍,最终完成对DockerFile常用命令的了解和掌握。

常用命令:

FROM命令:

既然我们是在原有的centos镜像的基础上做定制,那么我们的新镜像也一定是需要以centos这个镜像为基础的,而FROM命令则代表了这个意思,在DockerFile中,基础镜像是必须指定的,FROM指令的作用就是指定基础镜像,因此一个DockerFile中,FROM是必备的指令,而且就像java,python的import关键字一样,在DockerFile中,FROM指令必须放在第一条指令的位置

当然,这个时候可能有朋友会问了,我要是不想在其他的镜像上定制镜像怎么办呢,没问题啊,Docker 提供了scratch 这个虚拟镜像,如果你选择 FROM scratch 的话,则意味着你不以任何镜像为基础,接下来所写的指令将作为镜像的第一层开始存在,当然,在某些情况下,比如linux下静态编译的程序,运行的时候不需要操作系统提供运行时的支持,这个时候FROM scratch 是没有问题的,反而会大幅降低我们的镜像体积。

ENV指令

功能:设置环境变量

同样的,DockerFile也提供了两种格式:

  • ENV key value
  • ENV key1=value1 key2=value2

这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如 RUN, 还是运行时的应用,都可以直接使用这里定义的环境变量。

可以看到我们示例中使用ENV设置mypath变量之后,在下一行WORKDIR则使用到了mypath这个变量

ENV mypath /tmp  //设置环境变量
WORKDIR $mypath //指定工作目录

WORKDIR 指令:

功能,指定工作目录

格式为:WORKDIR 工作目录路径,如果这个目录不存在的话,WORKDIR则会帮助我们创建这个目录。

设置过工作目录之后,当我们启动容器,会直接进入该工作目录

[root@8081304919c9 tmp]#

RUN命令:

RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令也是在定制镜像时是较为常用的指令之一。

RUN命令的格式一共有两种,分别是:

  • Shell 格式

    RUN 命令,就像直接在命令行中输入命令一样,比如RUN yum -y install vim就是使用的这种格式

  • exec 格式

    RUN["可执行文件","参数1","参数2"],感觉就像调用函数一样

就像我们在上一篇文章中说过的那样,DockerFile中每一条指令都会建立一层,比如我们上面执行过下面这条命令

RUN yum -y install vim 

执行结束之后,则调用commit提交这一层的修改,使之构成一个新的镜像,怎么样,是不是豁然开朗了呢。

并没有

那好吧

同样的,Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以 及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。

提示:

如果使用apt方式安装的话,最后不要忘记清理掉额外产生的apt缓存文件,如果不清理的话会让我们的镜像显得非常臃肿。因为DockerFile生成一层新的镜像的时候,并不会删除上一层镜像所残留的文件。

EXPOSE指令:

功能:声明端口

格式: EXPOSE 端口1 端口2

EXPOSE 指令是声明运行时容器提供服务端口,这当然只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。这样声明主要是为了方便后期我们配置端口映射。

CMD指令:

之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。

同样的,DockerFile也为我们提供了两种格式来使用CMD命令:

  • shell 格式:CMD 命令
  • exec 格式:CMD ["可执行文件", "参数 1", "参数 2"...]

示例中,我们使用的是第一种:

CMD /bin/bash

这条指令带来的效果就是,当我们通过run -it 启动命令的时候,容器会自动执行/bin/bash,centos默认也是CMD /bin/bash,所以当我们运行centos镜像的时候,会自动进入bash环境里面。

当然,我们也可以通过运行时指定命令的方式来体换默认的命令,比如:

docker run -it centos cat /etc/os-release

这样当我们运行镜像的时候,cat /etc/os-release就会替代默认的CMD /bin/bash 输出系统的版本信息了。

如果使用 shell 格式的话, 实际的命令会被包装为 sh -c 的参数的形式进行执行。

比如:

CMD echo $HOME

在实际执行中,会将其变更为

CMD [ "sh", "-c", "echo $HOME" ]

当然还有很多初学者特别容易犯的问题,就是去启动后台服务,比如:

CMD service nginx start

这样子去用,会发现容器运行了一会就自动退出了。

所以,?????

我们之前不止一次的提醒过,容器不是虚拟机,容器就是进程,容器内的应用都应该以前台运行,而不是像虚拟机,物理机那样去运行后台服务,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

怎么理解呢?想想偶像剧,容器是女主角,主进程是男主角

你走了,我也不活了(撕心裂肺大哭),大概就是这么个意思。

正如我们前面所提出的,实际上CMD service nginx start 最终会被理解为:

CMD [ "sh", "-c", "service nginx start"]

在这里,我们主进程实际就是sh,当我们service nginx start执行完毕之后,那么sh自然就会退出了,主进程退出,容器自然就会相应的停止。争取的做法是直接执行nginx可执行文件,并且声明以前台的形式运行:

CMD ["nginx", "-g", "daemon off;"]

到这里,我们示例中所涉及到的命令已经讲完了,当然,这并不够,Docker中仍然有很多命令是我们使用比较频繁的,下面我们的部分作为补充,讲一下其他常用的DockerFile命令。

COPY 命令:

功能:复制文件

Docker依旧提供了两种格式供我们选择:

  • COPY [--chown=:] <源路径>... <目标路径>
  • COPY [--chown=:] ["<源路径 1>",... "<目标路径>"]

到这里大家其实会发现,Docker提供的两种格式其实都是差不多的用法,一种类似于命令行,一种则类似于函数调用。

第一种例如(将package.json拷贝到/usr/src/app/目录下):

COPY package.json /usr/src/app/

其次,目标路径 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径 ,工作目录可以用 WORKDIR 指令来指定,如果需要改变文件所属的用户或者用户组,可以加上--chown 选项。

需要注意的是,使用 COPY 指 令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这 个特性对于镜像定制很有用。

ADD命令:

ADD命令可以理解为COPY命令的高级版,格式和用法与COPY几乎一致,ADD在COPY的基础上增加了一些功能,比如源路径可以是一个URL链接,当你这么用的时候,Docker会尝试着先将该URL代表的文件下载下来,然后复制到目标目录上去,其他的则是在COPY的基础上增加了解压缩之类的操作,码字码的手疼,需要了解的朋友可以去官网查看相关的文档,这里我就不延申了。

VOLUME 定义匿名卷:

在上一篇中,我们有讲容器卷这个概念,为了防止运行时用户忘记 将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些 目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运 行,不会向容器存储层写入大量数据。

例如:

VOLUME /data

运行时通过-v参数即可以覆盖默认的匿名卷设置。

USER 命令:

功能:指定当前用户

格式:USER 用户名:用户组

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户。

当然这个大前提是,你的User用户是事先存在好的。

完结撒花?

不知不觉间,Docker系列初级入门教程已经发到了第四篇,篇幅也到了一万多字,前三篇文章加起来在掘金上慢慢有了大概1500左右的阅读量,我知道这点对于很多掘金大佬来说只是微不足道的一点,但对于现阶段的我来说已经非常满足了,从来没有想到过有一天自己也可以通过分享去帮助到别人,正如我之前通过别人的技术博客学习那样。

这个系列完结了吗?我想初级篇应该是完结了,但是Nginx的初级入门教程,即将到来的Mysql,Netty等等并没有,由于目前尚未毕业,还没有接受过工作的毒打(滑稽),所以只能尽自己的能力去写一些基础的入门教程,所以完结了吗?并没有,技术之路永无止境,只要我们一直在坚持学习,我想,我们可以一直继续下去。

感谢掘金,大家好,我是韩数,我们下期文章再见!

最后,相关笔记已经同步开源至Github(欢迎star): github.com/hanshuaikan…

一定要记得给个star哦。

提前祝大家1024程序员节快乐!