0
点赞
收藏
分享

微信扫一扫

Docker系列之Dockerfile详解FROM|RUN|BUILD(三)

yongxinz 2022-06-17 阅读 76

Docker系列之Dockerfile详解FROM|RUN|BUILD(三)_nginx

1 FROM

1.1 FROM 指定基础镜像

镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 ​​Dockerfile​​。

Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction) ,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

还以之前定制 ​​nginx​​ 镜像为例,这次我们使用 Dockerfile 来定制。

在一个空白目录中,建立一个文本文件,并命名为 ​​Dockerfile​​:

$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile
复制代码

其内容为:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
复制代码

这个 Dockerfile 很简单,一共就两行。涉及到了两条指令,​​FROM​​ 和 ​​RUN​​。

1.2 FROM 指定基础镜像

所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 ​​nginx​​ 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 ​​FROM​​ 就是指定 基础镜像,因此一个 ​​Dockerfile​​ 中 ​​FROM​​ 是必备的指令,并且必须是第一条指令。

在 ​​Docker Hub​​ 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 ​​​nginx​​​、​​​redis​​​、​​​mongo​​​、​​​mysql​​​、​​​httpd​​​、​​​php​​​、​​​tomcat​​​ 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 ​​​node​​​、​​​openjdk​​​、​​​python​​​、​​​ruby​​​、​​​golang​​​ 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 ​​​ubuntu​​​、​​​debian​​​、​​​centos​​​、​​​fedora​​​、​​​alpine​​​ 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

2 RUN

2.1 RUN 执行命令

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

  • shell格式:​​RUN <命令>​​,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的​​RUN​​ 指令就是这种格式。
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
复制代码
  • exec格式:​​RUN ["可执行文件", "参数1", "参数2"]​​,这更像是函数调用中的格式。

既然 ​​RUN​​ 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:

FROM debian:stretch

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.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 的人常犯的一个错误。

Docker系列之Dockerfile详解FROM|RUN|BUILD(三)_nginx_02

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

FROM debian:stretch

RUN buildDeps='gcc libc6-dev make wget' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.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​​ 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。

3 BUILD

3.1 构建镜像

在 ​​Dockerfile​​ 文件所在目录执行:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c
复制代码

从命令的输出结果中,可以清晰的看到镜像的构建过程。在 ​​Step 2​​ 中,如同之前所说的那样,​​RUN​​ 指令启动了一个容器 ​​9cdc27646c7b​​,执行了所要求的命令,并最后提交了这一层 ​​44aa4490ce2c​​,随后删除了所用到的这个容器 ​​9cdc27646c7b​​。

这里我们使用了 ​​docker build​​ 命令进行镜像构建。其格式为:

docker build [选项] <上下文路径/URL/->
复制代码

在这里我们指定了最终镜像的名称 ​​-t nginx:v3​​,构建成功后,我们可以像之前运行 ​​nginx:v2​​ 那样来运行这个镜像,其结果会和 ​​nginx:v2​​ 一样。

举报

相关推荐

0 条评论