文章目录:
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,在标签中声明该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重启一下这个容器。
重启之后,到浏览器中,访问刚刚标签对应的web服务的别名,即/test/a.jsp。
可以看到前台打出了 -----------welcome------------
<%="i am in docker tomcat self "%> 信息。而后台打印的信息则是在下面截图中的路径下,<% System.out.println("=============docker tomcat self");%>。
4.小总结