Docker入门实践之dokerfile编写

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker入门实践之dokerfile编写相关的知识,希望对你有一定的参考价值。

Dockerfile是一个文本格式的配置文件,通过dockerfile可以快速创建自定义镜像以适应测试,预发布,生产环境等各种应用部署镜像,一个好的dockerfile可以让我们的镜像更方便管理以及应用。

一. Dokerfile的基本结构

Dockfile是由一行行命令语句组成,并且迟滞以#开头的注释行;一般而言,Dockerfiel分为四部分:
1.基础镜像信息 ;2.维护者信息;3.镜像操作指令;4.容器启动时指令,如下为一个标准的dockfile镜像模板:

# This dockerfile uses the ubutu image
# VERSION 2 - EDITON 1
# Author docker_user
# Command format: Instruction [arguments / command ] ..

# (第一部分) 基础镜像信息 
FROM ubuntu

# (第二部分) 维护者信息
MAINTAINER docker_user [email protected]

#(第三部分)镜像操作指令 
RUN yum install apr-devel -y

    #(第四部分) 容器启动时指令
CMD /usr/sbin/echo

从上面可以看到一个基本的dockerfile实例包含:1.基础镜像信息, 2.维护者信息,3.镜像操作指令,4容器启动时指令这四个要素

二. Dokerfile镜像操作指令

dockerfiel镜像操作指令一般格式为: Instruction arguments 其指令包括:FORM MAINTAINER RUN 等等指令

1.FROM 引用基础镜像

格式为:FORM <image> 或FORM <image>:<tag>
第一条指令必须为FORM指令,如果在同一个dockerfile文件中创建多个镜像时,可以使用多个FROM指令(每个镜像一次)

在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node、openjdk、python、ruby、golang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ubuntu、debian、centos、fedora、alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

FROM scratch
...

如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm、coreos/etcd。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了。

因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go 是特别适合容器微服务架构的语言的原因之一。

2.MAINTAINER 维护者信息

格式为MAINTAINER <name>, 指定维护者信息

3.RUN 执行命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:

3.1.shell 格式:

RUN <命令>,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。

RUN echo ‘<h1>Hello, Docker!</h1>‘ > /usr/share/nginx/html/index.html
3.2.exec 格式:

RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。既然 RUN 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:

FROM debian:jessie

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install

Dockerfile 中每一个指令都会建立一层,RUN 也不例外。每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,commit 这一层的修改,构成新的镜像。而上面的这种写法,创建了 7 层镜像。这是完全没有意义的,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。 这是很多初学 Docker 的人常犯的一个错误。Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过 127 层。

上面的 Dockerfile 正确的写法应该是这样:

FROM debian:jessie

RUN  buildDeps=‘gcc libc6-dev make‘     && apt-get update     && apt-get install -y $buildDeps     && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"     && mkdir -p /usr/src/redis     && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1     && make -C /usr/src/redis     && make -C /usr/src/redis install     && rm -rf /var/lib/apt/lists/*     && rm redis.tar.gz     && rm -r /usr/src/redis     && apt-get purge -y --auto-remove $buildDeps

首先,之前所有的命令只有一个目的,就是编译、安装 redis 可执行文件。因此没有必要建立很多层,这只是一层的事情。因此,这里没有使用很多个 RUN 对一一对应不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来。将之前的 7 层,简化为了 1 层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。并且,这里为了格式化还进行了换行。Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。

此外,还可以看到这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。

编写Docker制作出了很臃肿的镜像的原因之一,就是忘记了每一层构建的最后一定要清理掉无关文件。

3.COPY 复制文件

格式:
COPY <源路径>... <目标路径>

COPY ["<源路径1>",... "<目标路径>"]
和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。

COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:

COPY package.json /usr/src/app/
<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:

COPY hom* /mydir/
COPY hom?.txt /mydir/
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。

此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。

以上是关于Docker入门实践之dokerfile编写的主要内容,如果未能解决你的问题,请参考以下文章

.net6 docker部署,以及问题解决(附Dokerfile)

docker入门之三:docker构建私有镜像入门到实践

Docker实践之01-入门介绍

Springboot应用使用Docker部署

Docker入门与实践之 docker安装与了解

docker实践入门之六