DockerFile 快速构建自定义镜像

Dockerfile

什么是 Dockerfile

Dockerfile 是用来构建 Docker 镜像的构建文件,是由一系列命令和参数构成的脚本。通俗的理解,和 Maven 一样,只需要写好 pom 文件就可以定义整个工程的构建,DockerFile 是一样的,通过这个脚本可以构建出自己想要的容器!

Dockerfile 构建步骤

  • 编写 Dockerfile 文件
  • docker build
  • docker run

以我们熟悉的 CentOS 为例 ,看看 CentOS 的 Dockerfile 的内容:

1
2
3
4
5
6
7
8
9
10
FROM scratch (scratch 相当于 Object,是所有镜像的祖先镜像)
MAINTAINER The CentOS Project <cloud-ops@centos.org>
ADD c68-docker.tar.xz/
LABEL name="CentOS Base Image" \
vendor="CentOS" \
license="GPLv2" \
build-date="2016-06-02"

# Default command
CMD ["/bin/bash"]

DockerFile 基础语法

1:每条保留字指令都必须为大写字母且后面要跟随至少一个参数

2:指令按照从上到下,顺序执行

3:# 表示注释

4:每条指令都会创建一个新的镜像层,并对镜像进行提交

Docker 执行 Dockerfile 的大致流程

(1)docker 从基础镜像运行一个容器

(2)执行一条指令并对容器作出修改

(3)执行类似 docker commit 的操作提交一个新的镜像层

(4)docker 再基于刚提交的镜像运行一个新容器

(5)执行 dockerfile 中的下一条指令直到所有指令都执行完成

从应用软件的角度来看,Dockerfile、Docker 镜像与 Docker 容器分别代表软件的三个不同阶段:

1、Dockerfile 是软件的原材料
2、Docker 镜像是软件的交付品
3、Docker 容器则可以认为是软件的运行态。

Dockerfile 面向开发,Docker 镜像成为交付标准
mark

1 Dockerfile,需要定义一个 Dockerfile,Dockerfile 定义了进程需要的一切东西。Dockerfile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程 (当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计 namespace 的权限控制) 等等;

2 Docker 镜像,在用 Dockerfile 定义一个文件之后,docker build 时会产生一个 Docker 镜像,当运行 Docker 镜像时,会真正开始提供服务;

3 Docker 容器,容器是直接提供服务的。

DockerFile 体系结构

FROM

基础镜像,当前新镜像是基于哪个镜像的

MAINTAINER

镜像维护者的姓名和邮箱地址

RUN

容器构建时需要运行的命令

EXPOSE

当前容器对外暴露出的端口

WORKDIR

指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点

ENV

用来在构建镜像过程中设置环境变量,比如 ENV MY_PATH /usr/mytest,这个环境变量可以在后续的任何 RUN 指令中使用,这就如同在命令前面指定了环境变量前缀一样;也可以在其它指令中直接使用这些环境变量,比如:WORKDIR $MY_PATH

ADD

将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包

COPY

类似 ADD,拷贝文件和目录到镜像中。将从构建上下文目录中 < 源路径 > 的文件 / 目录复制到新的一层的镜像内的 < 目标路径 > 位置

COPY 的写法有两种:COPY src dest 或者 COPY ["src","dest"]

VOLUME

容器数据卷,用于数据保存和持久化工作

CMD

指定一个容器启动时要运行的命令

CMD 命令也是两种格式,一种是 Shell 脚本 CMD< 命令 >,另一种是 CMD [" 可执行文件 "," 参数一 "," 参数二 "]Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换

ENTRYPOINT

指定一个容器启动时要运行的命令

ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数

ONBUILD

当构建一个被继承的 Dockerfile 时运行命令,父镜像在被子继承后父镜像的 onbuild 被触发

mark

Dockerfile 案例

Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的

一、自定义一个 CentOS

mark

现在我们需要自定义一个镜像来支持 vim、ifconfig、并且登录后的默认路径改做修改

编写 Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 从标准 centos 构建 
FROM centos

# 定义作者信息
MAINTAINER tim<15291418231@163.com>

# 定义一个变量
ENV mypath /tmp

# 设置登录后的落脚点
WORKDIR $mypath

# 安装 vim 和 net-tools 工具
RUN yum -y install vim
RUN yum -y install net-tools

EXPOSE 80

CMD echo $mypath
CMD echo "success----------ok"
CMD /bin/bash

接下来开始构建

1
docker build -f /mydocker/Dockerfile -t mycentos:1.3 .

mark

然后启动镜像,测试一下:

mark

看看构建过程是否是如前面所说,这也证实了镜像的分层:

mark

二、制作可以查询 IP 信息的镜像

Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换,这是什么意思呢?

mark

Tomcat 并没有运行起来,就是因为 Docker 的 run 命令后面加了参数!所以 ENTRYPOINT 命令就更好用了,ENTRYPOINT 不会覆盖,只是追加命令!

首先解释一下 curl 命令

curl 命令可以用来执行下载、发送各种 HTTP 请求,指定 HTTP 头部等操作。如果系统没有 curl 可以使用 yum install curl 安装,也可以下载安装。curl 是将下载文件输出到 stdout 使用命令:curl http://www.baidu.com,执行后,www.baidu.com 的 html 页面就会以文本的形式显示在屏幕上了。

这是最简单的使用方法。用这个命令获得了 http://curl.haxx.se 指向的页面,同样,如果这里的 URL 指向的是一个文件或者一幅图都可以直接下载到本地。如果下载的是 HTML 文档,那么缺省的将只显示文件头部,即 HTML 的 header。要全部显示,请加参数 - i

1
2
3
4
5
FROM centos

RUN yum install -y curl

CMD ["curl", "-s", "https://ip.cn"]

使用 curl 这个工具就可以查看 IP,现在使用构件好的镜像 run 一下:

mark

哈哈,居然给我识别成渭南的,这个工具不是很准呀!如果要显示 HTML 的 header 需要加参数 - i,下面我们来试一下:

mark

很显然不好使了,这就是为什么 CMD 指令不生效的原因,就是因为后面加的参数,所以出现了 ENTRYPOINT 指令,接下来使用 ENTRYPOINT 指令构建一下镜像:

1
2
3
4
5
FROM centos

RUN yum install -y curl

ENTRYPOINT ["curl", "-s", "https://ip.cn"]

果然,在 ENTRYPOINT 指令下加参数是可用的

mark

三、ONBUILD 指令的使用

1
2
3
4
5
6
7
FROM centos

RUN yum install -y curl

ENTRYPOINT ["curl", "-s", "https://ip.cn"]

ONBUILD RUN echo "father build finished! 886"

在父镜像构建完成的时候执行 ONBUILD 的指令,执行构建,构建出父镜像为 mycentos:1.6

mark

接下来编写子镜像的 Dockerfile

1
2
3
4
5
6
# 由于需要从父镜像构建,所以 FROM 写成 mycentos1.6
FROM mycentos:1.6

RUN yum install -y curl

ENTRYPOINT ["curl", "-s", "https://ip.cn"]

mark

可以看到,在构建父镜像完成后执行了 ONBUILD 的后面的内容!

四、自定义构建 Tomcat

新建一个文件夹,里面包含(apache-tomcat-9.0.8.tar.gz、jdk-8u171-linux-x64.tar.gz、a.txt)

编写 Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FROM         centos
MAINTAINER tim<15291418231@163.com>
# 把宿主机当前上下文的 a.txt 拷贝到容器 /usr/local/ 路径下
COPY a.txt/usr/local/cincontainer.txt
# 把 java 与 tomcat 添加到容器中
ADD jdk-8u171-linux-x64.tar.gz/usr/local/
ADD apache-tomcat-9.0.8.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_171
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.8
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# 容器运行时监听的端口
EXPOSE 8080
# 启动时运行 tomcat
# ENTRYPOINT ["/usr/local/apache-tomcat-9.0.8/bin/startup.sh" ]
# CMD ["/usr/local/apache-tomcat-9.0.8/bin/catalina.sh","run"]
CMD /usr/local/apache-tomcat-9.0.8/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.8/bin/logs/catalina.out

开始构建

1
2
docker build -t mytomcat:1.1

开始运行

1
2
docker run -d -p 8080:8080 --name mytomcat9 -v /tim/mydockerfile/tomcat9/test:/usr/local/apache-tomcat-9.0.8/webapps/test -v /tim/mydockerfile/tomcat9/tomcat9logs/:/usr/local/apache-tomcat-9.0.8/logs --privileged=true mytomcat9

其实这条命令虽然看起来很长,但是无非是启动镜像并且添加了两个数据卷,privileged 是 Docker 挂载主机目录 Docker 访问出现 cannot open directory .: Permission denied 的时候需要加上的参数!

Dockerfile 的总结

mark