Docker——Dockerfile的理解 & 案例实操
Posted 张起灵-小哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker——Dockerfile的理解 & 案例实操相关的知识,希望对你有一定的参考价值。
文章目录:
3.1 案例一(FROM、MAINTAINER、RUN、EXPOSE、WORKDIR、ENV、CMD)
1.Dockerfile是什么?
Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。
整个Docker镜像的构建过程是:编写Dockerfile文件 → docker build → docker run。
Dockerfile文件的具体内容,我们以centos镜像为例,链接:https://github.com/CentOS/sig-cloud-instance-images/blob/b2d195220e1c5b181427c3172829c23ab9cd27eb/docker/Dockerfile
Dockerfile内容的基础知识:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数。
- 指令按照从上到下,顺序执行。
- #表示注释。
- 每条指令都会创建一个新的镜像层,并对锐像进行提交。
Docker执行Dockerfile的具体流程:
- docker从基础镜像运行一个容器。
- 执行一条指令并对容器作出修改。
- 执行类似docker commit的操作提交一个新的镜像层。
- docker再基于刚提交的镜像运行一个新容器。
- 执行dockerfile中的下一条指令直到所有指令都执行完成。
从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,
- Dockerfile是软件的原材料。
- Docker镜像是软件的交付品。
- Docker容器则可以认为是软件的运行态。
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。
1. Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;
2. Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时,会真正开始提供服务;
3. Docker容器,容器是直接提供服务的。
2.Dockerfile体系结构
- FROM——基础镜像,当前新镜像是基于哪个镜像的
- MAINTAINER——锐像维护者的姓名和邮箱地址
- RUN—容器构建时需要运行的命令
- EXPOSE ——当前容器对外暴露出的端口
- WORKDIR——指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点
- ENV——用来在构建镜像过程中设置环境变量(ENV MY_PATH /usr/mytest WORKDIR $MY_PATH)
- ADD——将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
- COPY——类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 <源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置
- VOLUME——容器数据卷,用于数据保存和持久化工作
- CMD——指定一个容器启动时要运行的命令。Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换
- ENTRYPOINT——指定一个容器启动时要运行的命令。ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及参数
- ONBUILD——当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发
3.案例实操
3.1 案例一(FROM、MAINTAINER、RUN、EXPOSE、WORKDIR、ENV、CMD)
首先,我这里先run一个centos镜像,以它为模板创建一个centos容器实例运行。
但是运行进入docker中的centos终端之后,发现之前在linux中常用的vim、ifconfig这些命令都用不了,此时pwd位于根目录下。
我这里要做的修改是使得vim、ifocnfig这两个命令在docker的centos容器中生效,同时当我进入这个容器时,不再位于根目录/下,而是直接到达/usr/local目录下
这就需要创建新的Dockerfile文件,之后build出一个新的镜像,最后run运行。
上面是Dockerfile文件的内容,创建完之后,下面执行docker build,以Dockerfile2文件构建出一个新的自定义的centos镜像。
构建成功,可以docker images查看一下。
三步走(编写Dockerfile → docker build → docker run),我们已经走完了前两步,最后就可以run了。
可以看到以我们刚刚新构建的镜像为模板生产出一个自定义的centos容器实例,进入之后,pwd看到当前工作目录为 /usr/local,同时,vim可以正常的编写文件,ifconfig也可以查出ip、网卡等相关信息。
这里还可以使用docker history可以查看该容器的构建过程、变更历史。
3.2 案例二(CMD、ENTRYPOINT)
首先,我们看一下tomcat官方镜像的Dockerfile文件,链接:https://github.com/docker-library/tomcat/blob/fb2ffad09f315bde50308816f7d84897b856e164/9.0/jdk8/corretto/Dockerfile
可以看到这个Dockerfile文件的最后一行是
CMD ["catalina.sh", "run"]
而我这里docker run这个tomcat,在命令的最后添加一个ls -l,那么此时就相当于在这个tomcat的Dockerfile文件的最后一行添加了 CMD ls -l,此时变成了
CMD ["catalina.sh", "run"]
CMD ls -l
由于Dockerfile文件中关于RUN的定义是:CMD——指定一个容器启动时要运行的命令。Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换。
所以下面的截图中,我这样去run,ls -l顺利的查出了/usr/local/tomcat目录下的内容。但是docker ps却显示当前并没有容器实例在运行,这就说明它的Dockerfile文件的倒数第二行的 CMD ["catalina.sh", "run"] 已经被 CMD ls -l 覆盖了。
上面演示了Dockerfile文件中如果存在多个CMD了话,那么只有最后一个CMD生效。
下面再来说说和CMD很像的ENTRYPOINT:ENTRYPOINT——指定一个容器启动时要运行的命令。ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及参数。
ENTRYPOINT并不会像CMD那样覆盖前面的命令参数,而是会追加,如果有一个ENTRYPOINT就正常执行,如果有第二个ENTRYPOINT就追加到第一个的后面,一同执行。
curl命令可以用来执行下载、发送各种HTTP请求,指定HTTP头部等操作。如果系统没有curl可以使用yum install curl安装,也可以下载安装。curl是将下载文件输出到stdout。
3.3 案例三(ONBUILD)
ONBUILD——当构建一个被继承的Dockerfile时运行命令,父镜像在被子继承后父镜像的onbuild被触发。
这里首先创建一个Dockerfile3文件,然后以centos为基础镜像构建一个自定义的新镜像myip_father,然后再创建一个Dockerfile4文件,这里就不再以centos为基础镜像了,也就不再是FROM centos,而是FROM myip_father,即此时镜像myip_son继承了myip_father。
最后当我docker build构建这个子镜像myip_son的时候,可以看到父镜像中ONBUILD后面的 RUN echo "father onbuild-------666" 这句话被打印出来了。
3.4 案例四(自定义tomcat9镜像)
上面已经介绍了很多Dockerfile中的命令,还差ADD、COPY,这两个在此案例中讲解。
ADD——将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包。
COPY——类似ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 <源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置。
说的明白点:比如我们在淘宝买东西,最终肯定是以快递的方式送达,ADD就是指快递员给你送货到家之后,他帮你把快递拆开;COPY则是指快递员给你送货到家之后,他不会拆封,由你自己来拆封。
因为我们要自定义tomcat9,所以就需要两个压缩包(tomcat、jdk),我这里先级联创建了一个测试目录,然后将压缩包拷贝过来用作测试(先不解压)。
要构建新的tomcat镜像,第一步必然是编写Dockerfile文件。
FROM centos
MAINTAINER szh<2656307671@qq.com>
#把宿主机当前上下文的c.txt拷贝到容器/usr/local/路径下
COPY c.txt /usr/local/cincontainer.txt
#把java与tomcat添加到容器中
ADD jdk-8u121-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-9.0.13.tar.gz /usr/local/
#安装vim编辑器
RUN yum -y install vim
#设置工作访问时候的WORKDIR路径,登录落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_121
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.13
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.13
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
#容器运行时监听的端口
EXPOSE 8080
#启动时运行tomcat
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.13/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.13/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.13/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.13/bin/logs/catalina.out
此时当前目录下有两个压缩包、c.txt、Dockerfile文件,先说明一下,这两个压缩包后面对应的就是ADD命令,因为ADD会在拷贝压缩包的同时帮你解压;c.txt文件对应的是COPY命令,只是单纯的拷贝。
然后我们docker build以此Dockerfile文件来构建自定义的tomcat9镜像。
构建完成之后,可以docker images查看一下。
下面的docker run命令:
- -d是以守护式容器的方式启动,即后台启动(-it则是交互式容器,即前台启动)
- -p指定docker中映射tomcat的端口为1234
- --name对以此镜像生产的容器实例重命名
- -v则是在容器中添加数据卷(以实现宿主机和docker容器之间的数据持久化 + 数据共享)
- 这里只所以可以写多个-v,在容器中添加多个数据卷,就依赖于后面的
--privileged=true
最后的szh-tomcat9是我们要以哪个镜像为模板生产容器实例运行
run之后,可以docker ps查看一下当前运行的容器实例。
此时,到浏览器中,虚拟机ip + -p中映射的端口号即可访问tomcat的主页。
由于我们docker run -d是后台启动,并不像docker run -it前台启动那样直接进入tomcat的目录下。
所以这里可以使用docker exec命令进入正在运行的某个容器实例,后面添加了 ls -l,就覆盖了上一个CMD。
下面,我们可以尝试着在docker中的这个tomcat容器中部署一个简单的web服务,对应test目录。
在WEB-INF目录下,创建web服务所需的web.xml,在<display-name>标签中声明该web服务对外的名称为test。
退出WEB-INF目录,创建一个a.jsp的简单页面。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
-----------welcome------------
<%="i am in docker tomcat self "%>
<br>
<br>
<% System.out.println("=============docker tomcat self");%>
</body>
</html>
因为我们在这个tomcat容器中添加了部分文件,所以这里需要docker restart重启一下这个容器。
重启之后,到浏览器中,访问刚刚<display-name>标签对应的web服务的别名,即/test/a.jsp。
可以看到前台打出了 -----------welcome------------
<%="i am in docker tomcat self "%> 信息。而后台打印的信息则是在下面截图中的路径下,<% System.out.println("=============docker tomcat self");%>。
4.小总结
以上是关于Docker——Dockerfile的理解 & 案例实操的主要内容,如果未能解决你的问题,请参考以下文章