在上一篇文章写给后端的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程序员节快乐!
近期评论