一、Dockerfile 简介
1、Dockerfile 是什么
Dockerfile 是用来构建 Docker 镜像的构建文件,它是由一系列命令和参数构成的脚本
2、镜像实例化为容器的三个步骤
2.1、编写 Dockerfile 文件
2.2、根据 Dockerfile 文件,使用 docker build 命令来构建镜像
2.3、docker run 命令启动容器
3、Dockerfile 内容和格式
我们这里以 centos 为例,登录 docker hub 官网: https://registry.hub.docker.com/
3.1、搜索 centos
3.2、选择官方镜像
3.3、Dockerfile 具体内容如下:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- # 表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
二、Dockerfile 、Docker 镜像、Docker 容器的区别
从应用软件的角度来看, Dockerfile、Docker 镜像与 Docker 容器分别代表软件的三个不同阶段
Dockerfile 是软件的原材料
Docker 镜像是软件的交付品
Docker 容器则可以认为是软件的运行态
Dockerfile 面向开发, Docker 镜像成为交付标准, Docker 容器则涉及部署与运维,三者缺一不可,合力充当 Docker 体系的基石
2.1、Dockerfile:需要定义一个 Dockerfile ,Dockerfile 定义了进程需要的一切东西, Dockerfile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计 namespace 的权限控制)等等.
2.2、Docker 镜像,在用 Dockerfile 定义一个文件之后, docker build 时会产生一个 Docker 镜像,当运行 Docker 镜像时,会真正开始提供服务.
2.3、Docker容器,容器是直接提供服务的.
三、Dockerfile 构建过程解析
Docker 保留字及其代表含义
Dockerfile 保留字 | 说明 |
FROM | 当前新镜像是基于哪个镜像的 |
MAINTAINER | 镜像维护者的姓名和邮箱地址 |
RUN | 容器构建时需要运行的命令 |
EXPOSE | 当前镜像对外暴露的端口 |
ENV | 镜像构建过程中的环境变量 |
WORKDIR | 容器启动后,终端默认登录进来的工作目录(启动容器时的一个落脚点) |
ADD | 将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包 |
COPY | 类似 ADD ,拷贝文件和目录到镜像中.将从构建上下文目录中<源路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置 |
VOLUME | 容器数据卷,用于数据保存和持久化工作 |
CMD | 指定一个容器启动时要运行的命令, Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效, CMD 会被 docker run 之后的参数替换 |
ENTRYPOINT | 指定一个容器启动时要运行的命令, ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数 |
ONBUILD | 当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子继承后父镜像的 ONBUILD 被触发 |
3.1、案例一(自定义 mycentos 镜像)
我们先看一下官方拉取的镜像,存在如下的问题
针对上面的问题,我们是否可以自定义一个 centos , 这个自定义的镜像可以支持 vim 、ifconfig 命令
centos 官方镜像的 Dockerfile 定义如下:
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL
org.label-schema.schema-version="1.0"
org.label-schema.name="CentOS Base Image"
org.label-schema.vendor="CentOS"
org.label-schema.license="GPLv2"
org.label-schema.build-date="20201113"
org.opencontainers.image.title="CentOS Base Image"
org.opencontainers.image.vendor="CentOS"
org.opencontainers.image.licenses="GPL-2.0-only"
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]
我们可以参考官方镜像的 Dockerfile 来自定义 Dockerfile ,然后通过自定义的 Dockerfile 来构建符合我们要求的镜像
3.1.1、新建一个目录,编写自定义 Dockerfile 文件
// 继承 centos 镜像
FROM centos
// 环境变量路径
ENV MYPATH /usr/local/mydocker
// 进入容器的时候,默认会进入该入境
WORKDIR $MYPATH
// 支持 vim 命令
RUN yum -y install vim
// 支持 curl 命令
RUN yum -y install curl
// 支持查询 ip
RUN yum -y install net-tools
// 容器启动之后对外暴露的端口
EXPOSE 80
// Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效
CMD echo $MYPATH
CMD echo "mycentos for xiao mao mao is ok"
// 实际生效的命令
CMD [ "curl", "-s", "http://www.baidu.com" ]
3.1.2、使用 docker build 命令构建镜像
// -f 参数是为了指定 Dockerfile 的位置(可以是绝对路径,也可是相对当前操作路径),默认的情况下标准的命名就是 Dockerfile,如果文件的命名是 Dockerfile ,可以省略不写
// /root/mydocker 是构建镜像的构件存放目录, Dockerfile 中通过 ADD 来将该目录下的构件添加进容器(它与 Dockerfile 存在的路径不是一回事)
// 整体的含义就是通过 /root/mydocker/Dockerfile 文件来构建镜像
docker build -f /root/mydocker/Dockerfile -t xiaomaomao/centos:use-cmd /root/mydocker
3.1.3、运行镜像使之成为容器
docker run -it --name mycentos xiaomaomao/centos:use-cmd
3.1.4、我们这里是通过命令 docker run -it --name mycentos xiaomaomao/centos:use-cmd 来运行容器的,还记不记得我们自定义的 Dockerfile 里面有编写 CMD 命令,这个命令就是我们运行 Docker 镜像的默认命令,并且多个 CMD 命令只有最后一个生效,实际上是相当于运行如下的命令
docker run -it --name mycentos xiaomaomao/centos:use-cmd curl -s http://www.baidu.com
也就是运行镜像的时候就会直接去访问百度的首页
3.1.5、下面我们又有了新的需求,希望访问百度首页的时候能够返回报文头(需要在 curl 命令后面加上参数 -i),如果想要完成需求,完整的命令应该如下
docker run -it --name mycentos xiaomaomao/centos:use-cmd curl -s -i http://www.baidu.com
执行上述的命名确实是能够完成需求,因为 curl -s -i http://www.baidu.com 命令会覆盖我们在 Dockerfile 里面默认的 curl -s http://www.baidu.com 命令
但是呢,我现在不想去覆盖 Dockerfile 里面的命令,只想通过添加参数 -i 的方式去实现需求,也就是执行下面这条命令实现需求
docker run -it --name mycentos xiaomaomao/centos:use-cmd -i
执行完命令之后,结果出现了报错,因为 docker run 命令的标准形式是这样的
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
-i 不是具体的 CMD 命令,所以会出现报错
那么有没有其它办法呢,答案是有的,这个时候就需要是用 ENTRYPOINT 保留字来代替 CMD 保留字
重写修改 Dockerfile 如下,把 CMD 换成 ENTRYPOINT
FROM centos
ENV MYPATH /usr/local/mydocker
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install curl
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "mycentos for xiao mao mao is ok"
// 将 CMD 改为 ENTRYPOINT
ENTRYPOINT [ "curl", "-s", "http://www.baidu.com" ]
重新构建奖项,然后运行镜像,此时加上参数 -i 就返回了头部信息
总结 CMD 和 ENTRYPOINT 的区别
CMD: 运行容器的时候,最后加的参数 -i 会覆盖 Dockerfile 里面 CMD 保留字定义的命令,使整个命令变成如下(这种命令是错误的,因为 -i 不是 CMD 命令)
docker run -it --name mycentos xiaomaomao/centos:use-cmd -i
ENTRYPOINT: 运行容器的时候,最后加的参数 -i 会被当做参数追加到 Dockerfile 里面 ENTRYPOINT 保留字定义的命令里面,也就是当使用如下命令时
docker run -it --name mycentos xiaomaomao/centos:use-entrypoint -i
就相当于
docker run -it --name mycentos xiaomaomao/centos:use-entrypoint curl -s -i http://www.baidu.com
3.2、案例二、自定义 tomcat 镜像
3.2.1、编写 Dockerfile 文件
tomcat-dockerfile 文件内容如下
# 继承基础镜像
FROM centos
# 维护者姓名和邮箱地址
MAINTAINER xiaomaomao<xiaomaomao@163.com>
# 环境变量
ENV MYPATH /usr/local
# 把 java 与 tomcat 添加到容器中指定目录下
ADD jdk-8u261-linux-x64.tar.gz $MYPATH/
ADD apache-tomcat-8.5.60.tar.gz $MYPATH/
# 安装 vim 编辑器
RUN yum -y install vim
# 设置工作访问时候的 WORKDIR 路径,进入容器时的默认工作目录
WORKDIR $MYPATH/apache-tomcat-8.5.60/
# 配置 java 与 tomcat 环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_261
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME $MYPATH/apache-tomcat-8.5.60
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# 把宿主机当前上下文的 xiaomaomao.txt 拷贝到容器 /usr/local/webapps 路径下
COPY xiaomaomao.txt $CATALINA_HOME/webapps/
# 添加容器数据卷
VOLUME $CATALINA_HOME/webapps/
# 容器运行时监听的端口
EXPOSE 8080
# 启动时运行tomcat 并打印日志
CMD $CATALINA_HOME/bin/startup.sh && tail -F $CATALINA_HOME/logs/catalina.out
3.2.2、使用 docker build 构建镜像
构建成功信息如下
3.2.3、查看镜像
3.2.4、启动镜像
3.2.5、查看镜像是否启动成功,启动成功后进入容器
3.2.6、查看 tomcat 容器的挂载的容器数据卷
宿主机目录
容器目录
对比可以发现,容器的挂载目录是我们在 Dockerfile 中定义的,宿主机挂载的目录是 Docker 帮我们随机生成的,两个目录里面的内容是相同的,并且经过测试,数据可以同步
3.2.7、宿主机的挂载目录新建一个 test 目录,在 test 目录下新建一个 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>
hello i am xiao mao mao, when will you back?
</body>
</html>
3.2.8、此时,由于通过容器数据卷同步信息,在容器的 webapps 下会有 test 目录
3.2.9、访问 a.jsp
3.2.10、同理,比如我们以后部署 war 包,也可以只通过操作宿主机,就可以将 war 包部署到容器内的 tomcat 中