Dockerfile 最佳实践

来源:互联网 发布:微店和淘宝哪个好做 编辑:程序博客网 时间:2024/05/22 07:54

原文地址:http://xhrwang.me/2014/12/19/docker-best-practice-1.html

Why

Docker 作为非常优秀的轻量级 PaaS 解决方案,得到了主流云服务平台的先后支持,配合 Docker Registry Hub 提供高质量的 Docker Image 以及 Fig 进行 Containers 的管理,吸引了全球的开发者将自己的服务迁移到 Docker Containers 上面,从而提高开发和部署效率。作为快速发展的新技术,很多高质量的技术文章和使用经验都是英文的,我自己在学习 Docker 的过程中,在不同的网站和个人 Blog 就看到过很多,所以,就想着把我看到的高质量文章翻译过来放在这个 Blog,一方面翻译的过程是加深理解的过程,另一方面把散落在各处的好文章集中在一个地方并翻译成中文,便于之后的分享以及回顾。

What

本文译文部分翻译自 Dockerfile Best Practices,英文版权归 原作者 所有,译文转载请注明出处。

译文

Dockerfile 对创建 Docker Image 提供了简单的语法,本文记录一些可以让我们真正用好 Dockerfile 的一些技巧和经验。

在 Dockerfile 的最开始部分保持通用创建指示内容以利用缓存

Dockerfile 中的每一条指示执行后的结果都会提交到新创建的 Image 中,并作为下一条指示执行的基础。如果已经存在一个拥有相同父 Image 而且该 Image 的 Dockerfile 有相同的指示(除了 ADD)的另一个 Image,Docker 将会使用这个已存在的 Image 而不是重新通过执行每一条 Dockrfile 中的指示来创建另一个新的 Image,这就是缓存机制。

为了能够能有效地利用缓存,我们需要尽量保持 Dockerfiles 的一致性,并且将不同的指示放在 Dockerfile 最后的部分,所有我自己的 Dockerfiles 都以下面 5 行开始:

FROM ubuntuMAINTAINER Michael Crosby <michael@crosbymichael.com>RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.listRUN apt-get updateRUN apt-get upgrade -y

注意,改变 MAINTAINER 指示将会迫使 Docker 放弃使用缓存并重新通过执行 RUN 指示的操作。

在创建 Docker Image 的时候使用 -t 参数对 Image 打上 Tag

如果我们不是仅仅对 Docker 尝尝鲜试用一下,那我们应该在创建 Docker Image 的时候使用 -t 参数对 Image 打上 Tag,因为一个有意义的 Tag 名可以帮助我们明确创建这个 Image 的目的。

docker build -t=”crosbymichael/sentry” .

不要在 Dockerfile 中进行端口映射

Docker 的两个最核心的特性就是可重复性和可移植性。Image 应该可以被根据需要用来在任何 host 上创建多个 Containers,考虑到这点,虽然我们在 Dockerfile 中可以映射 Container 端口到 host 端口,但是我们永远不应该在 Dockerfile 中这么做,否则我们就只能使用该 Dockerfile 创建的 Image 创建一个 Container。

# private and public mappingEXPOSE 80:8080# private onlyEXPOSE 80

如果 Image 的使用者关心 Container 应该将自己的什么端口和 host 的端口进行映射,那就会显式地在创建 Container 的时候使用 -p 参数进行设置,否则,Docker 会自动给 Container 分配一个 host 上的端口进行映射。

在使用 CMD  ENTRYPOINT 指示的时候使用数据传入参数

CMD  ENTRYPOINT 指示的作用是容易理解的,但是它们有个隐藏的坑,如果不注意就可能会导致错误发生。这两个指示都支持下面的语法:

CMD /bin/echo# orCMD ["/bin/echo"]

看上去好像没什么,但是隐藏在细节中的坑却可能真的会坑人。如果我们使用下面以数组方式传递参数的方式,最后的结果会和我们预期的相同;但是当我们使用第一种语法时,由于 Docker 会自动在需要执行的命令前加上 /bin/sh -c,这种情况下可能会导致一些未预料的错误和不好理解的事情发生,所以总是使用数组传递参数是个好主意,这能保证命令按照我们预期的方式被执行。

CMD  ENTRYPOINT 最好配合使用

ENTRYPOINT 会让我们的 Container 使用起来像个可执行文件,我们可以在使用 docker run 创建 Container 的时候传递参数给 ENTRYPOINT 执行并且不用担心操作被覆盖(就像使用 CMD 后的情况)。在和 CMD 一起配合使用时 ENTRYPOINT 甚至能提供更好的体验,我们通过一个Rethinkdb 的例子来看看怎么用:

# Dockerfile for Rethinkdb # http://www.rethinkdb.com/FROM ubuntuMAINTAINER Michael Crosby <michael@crosbymichael.com>RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.listRUN apt-get updateRUN apt-get upgrade -yRUN apt-get install -y python-software-propertiesRUN add-apt-repository ppa:rethinkdb/ppaRUN apt-get updateRUN apt-get install -y rethinkdb# Rethinkdb processEXPOSE 28015# Rethinkdb admin consoleEXPOSE 8080# Create the /rethinkdb_data dir structureRUN /usr/bin/rethinkdb createENTRYPOINT ["/usr/bin/rethinkdb"]CMD ["--help"]

这就是我们将 Rethinkdb 迁移到 Docker 需要做的全部事情。最前面的 5 行是我自己的 Dockerfile 的标准开头,接下来是一些安装和必要的端口暴露等,最后使用 ENTRYPOINT。我们知道当我们使用该 Dockerfile 创建的 Image 来创建一个 Container 并运行的时候,所有通过 docker run命令传递的参数都会被传递给 ENTRYPOINT(/usr/bin/rethinkdb)。但是可以看到 ENTRYPOINT 指示下面还有一条 CMD 指示并拥有 –help 的内容,这样就会在当我们在使用 docker run 创建 Container 的时候没有传递参数的时候显示 rethinkdb 的帮助内容,就像这样:

docker run crosbymichael/rethinkdb

输出为:

Running 'rethinkdb' will create a new data directory or use an existing one,  and serve as a RethinkDB cluster node.File path options:  -d [ --directory ] path           specify directory to store data and metadata  --io-threads n                    how many simultaneous I/O operations can happen                                    at the same timeMachine name options:  -n [ --machine-name ] arg         the name for this machine (as will appear in                                    the metadata).  If not specified, it will be                                    randomly chosen from a short list of names.Network options:  --bind {all | addr}               add the address of a local interface to listen                                    on when accepting connections; loopback                                    addresses are enabled by default  --cluster-port port               port for receiving connections from other nodes  --driver-port port                port for rethinkdb protocol client drivers  -o [ --port-offset ] offset       all ports used locally will have this value                                    added  -j [ --join ] host:port           host and port of a rethinkdb node to connect to  .................

现在我们再看看如果传递了 “–bind all” 的参数后发生什么:

docker run crosbymichael/rethinkdb --bind all

输出为:

info: Running rethinkdb 1.7.1-0ubuntu1~precise (GCC 4.6.3)...info: Running on Linux 3.2.0-45-virtual x86_64info: Loading data from directory /rethinkdb_datawarn: Could not turn off filesystem caching for database file: "/rethinkdb_data/metadata" (Is the file located on a filesystem that doesn't support direct I/O (e.g. some encrypted or journaled file systems)?) This can cause performance problems.warn: Could not turn off filesystem caching for database file: "/rethinkdb_data/auth_metadata" (Is the file located on a filesystem that doesn't support direct I/O (e.g. some encrypted or journaled file systems)?) This can cause performance problems.info: Listening for intracluster connections on port 29015info: Listening for client driver connections on port 28015info: Listening for administrative HTTP connections on port 8080info: Listening on addresses: 127.0.0.1, 172.16.42.13info: Server readyinfo: Someone asked for the nonwhitelisted file /js/handlebars.runtime-1.0.0.beta.6.js, if this should be accessible add it to the whitelist.

就是这样,一个完整的 Rethinkdb 实例,可以像在 host 上使用 Rethinkdb 的可执行文件一样来进行交互,很简单。

我希望这篇文章能帮助大家善用 Dockerfile 创建自己的 Image 并分享出来。我相信 Dockerfile 是 Docker 之所以非常简单易用的一个重要原因。

Dockerfile 最佳实践 续

-- EOF --

0 0
原创粉丝点击