高效编写Dockerfile的几条准则


概述

  • Dockerfile 是专门用来进行自动化构建镜像的编排文件(就像Jenkins 2.0时代的Jenkinsfile是对Jenkins的Job和Stage的编排同样),咱们能够经过 docker build 命令来自动化地从 Dockerfile 所描述的步骤来构建自定义的 Docker镜像,这比咱们去命令行一条条指令执行的方式构建高效得多。docker

  • 另外一方面,因为 Dockerfile 提供了统一的配置语法,所以经过这样一份配置文件,咱们能够在各类不一样的平台上进行分发,须要时经过 Dockerfile 构建一下就能获得所需的镜像。编程

  • 最后一个必须提的优势即是:Dockerfile 经过与镜像配合使用,使得 Docker镜像构建之时能够充分利用 “镜像的缓存功能”,所以也提效很多!centos

然而写 Dockerfile 也像写代码同样,一份精心设计、Clean Code 的 Dockerfile 能在提升可读性的同时也大大提高Docker的使用效率缓存

所以下面就结合实践来说几条 Dockerfile 的实践心得!bash

注: 本文首发于 My 公众号 CodeSheep ,可 长按扫描 下面的 当心心 来订阅 ↓ ↓ ↓服务器

CodeSheep · 程序羊



基础镜像的选择有讲究

在个人文章 《利用K8S技术栈打造我的私有云(连载之:基础镜像制做与实验)》 中,咱们是基于某个Linux基础镜像做为底包,而后打包进我须要的功能从而造成本身的镜像。框架

这里选择基础镜像时是有讲究的:wordpress

  • 一是 应当尽可能选择官方镜像库里的基础镜像;
  • 二是 应当选择轻量级的镜像作底包

就典型的Linux基础镜像来讲,大小关系以下:微服务

Ubuntu > CentOS > Debian
复制代码

所以相比 Ubuntu,其实更推荐使用最轻量级的 Debian镜像,并且它也是一个完整的Release版,能够放心使用工具



多使用标签Tag 有好处

  • 构建镜像时,给其打上一个易读的镜像标签有助于帮助了解镜像的功能,好比:
docker build -t=“centos:wordpress" . 复制代码

例如上面的这个centos镜像是用来作wordpress用的,因此已经集成了wordpress功能,这一看就很清晰明了

  • 再者,咱们也应该在 Dockerfile 的 FROM 指令中明确指明标签 Tag,不要再让 Docker daemon 去猜,如
FROM debian:codesheep
复制代码


充分利用镜像缓存

什么是镜像缓存?

由 Dockerfile 最终构建出来的镜像是在基础镜像之上一层层叠加而得,所以在过程当中会产生一个个新的 镜像层。Docker daemon 在构建镜像的过程当中会缓存一系列中间镜像。

docker build镜像时,会顺序执行Dockerfile中的指令,并同时比较当前指令和其基础镜像的全部子镜像,若发现有一个子镜像也是由相同的指令生成,则 命中缓存,同时能够直接使用该子镜像而避免再去从新生成了。

为了有效地使用缓存,须要保证 Dockerfile 中指令的 连续一致,尽可能将相同指令的部分放在前面,而将有差别性的指令放在后面

**举例:**假如我想用 Dockerfile方式 基于最基本的 CentOS 镜像来构建两个不一样的镜像时,两个Dockerfile的开头能够相同:

FROM centos:latest

# 下面安装两个经常使用的工具
RUN yum install -y net-tools.x86_64

RUN yum install lrzsz

######## 上面为两个Dockerfile文件中相同的部分######

######## 下面为两个Dockerfile文件中不一样的部分######

......

复制代码


ADD 与 COPY 指令的正确使用

虽然二者均可以添加文件到镜像中,但在通常用法中,仍是推荐以COPY指令为首选,缘由在于ADD指令并无COPY指令来的纯粹,ADD会添加一些额外功能,典型的以下 ADD 一个压缩包时,其不只会复制,还会自动解压,而有时咱们并不须要这种额外的功能。

ADD codesheep.tar.gz /path
复制代码

除此以外,在须要添加多个文件到镜像中的时候,不要一次性集中添加,而是选择 按需 在必要时 逐个 添加便可,由于这样有利于利用镜像缓存



##尽可能使用docker volume

虽然上面一条原则说推荐经过 COPY 命令来向镜像中添加多个文件,然而实际状况中,若文件 大而多 的时候仍是应该优先用 docker -v 命令来挂载文件,而不是依赖于 ADD 或者 COPY

最后必须说一下,这里的“尽可能”是有个度的,适度把握才行。



CMD 和 ENTRYPOINT指令 的理解使用

Dockerfile 制做镜像时,会组合 CMD 和 ENTRYPOINT 指令来做为容器运行时的默认命令:即 CMD + ENTRYPOINT。此时的默认命令组成中:

  • ENTRYPOINT 指令部分“通常”固定不变,容器运行时不修改
  • 而 CMD 部分的指令也能够改变,表如今运行容器时,docker run 命令中提供的参数会覆盖CMD的指令内容。

举个例子:

FROM debian:latest

MAINTAINER codesheep@163.com

ENTRYPOINT [ "ls", "-l"]
CMD ["-a"]
复制代码

若以默认命令运行容器,能够发现,执行的是 ls -a -l 命令:

ls -l -a

docker run 中增长参数 -t

docker run -it --rm --name test debian:codesheep -t
复制代码

也能够发现执行的是 ls -l -t,即 Dockerfile 中的 CMD 原参数被覆盖了:

ls -l -t

所以推荐的使用方式是:

  • 使用exec格式的 ENTRYPOINT指令 设置固定的默认命令和参数

  • 使用 CMD指令 设置可变的参数



不推荐在 Dockerfile中 作端口映射

Dockerfile 能够经过 EXPOSE指令 将容器端口映射到主机端口上,但这样会致使镜像在一台主机上仅能启动一个容器!

因此应该在 docker run 命令中来用 -p 参数来指定端口映射,而不要将该工做置于 Dockerfile 之中:

#尽可能避免这种方式
EXPOSE 8080:8899

#仅仅暴露端口
EXPOSE 8080
复制代码


使用 Dockerfile 来共享镜像

推荐经过共享 Dockerfile 的方式来共享镜像,优势多多:

  • 经过 Dockerfile 构建的镜像用户能够清楚地看到构建的过程

  • 就像 Jenkinsfile 能够加入版本控制从而追踪CI系统的变迁和步骤的回滚同样,Dockerfile 做为一个编排文件一样能够入库作版本控制,这样也能够回溯

  • 使用 Dockerfile 构建的镜像具备肯定性,没有玄学的成分



后记

若是有兴趣,也能够抽点时间看看做者一些关于容器化、微服务化方面的文章:

做者相关的SpringBt实践文章在此:



CodeSheep · 程序羊
相关文章
相关标签/搜索