【编者的话】
Dockerfile是为快速构建Docker Image而设计的,它为构建镜像提供了简单的语法。Docker 会读取当前目录下的命名为Dockerfile的纯文本文件并执行里面的指令构建出一个Docker Image,这样,在Docker中创建镜像会更加简单,并且易用。本篇文章对DockerFile入门知识及在实践过程中的使用技巧进行整合,希望能够帮助您在使用Dockerfiles以及构建镜像时受益。
还记得我们介绍过的15个Docker命令吗?那15个命令在手动创建镜像时会用到,它们涵盖镜像的创建、提交、搜索、pull和push的功能。
现在问题来了,既然Docker能自动创建镜像,那为什么要选择耗时而又乏味的方式来创建镜像呢?
Docker为我们提供了Dockerfile来解决自动化的问题。在这篇文章中,我们将讨论什么是Dockerfile,它能够做到的事情以及DockerFile一些基本语法。
命令为易于自动化
Dockerfile是包含创建镜像所需要的全部指令。基于在DockerFile中的指令,我们可以使用Docker build命令来创建镜像。通过减少镜像和容器的创建过程来简化部署。
Dockerfiles支持支持的语法命令如下:
- INSTRUCTION argument
指令不区分大小写。但是,命名约定为全部大写。
所有Dockerfile都必须以FROM命令开始。 FROM命令会指定镜像基于哪个基础镜像创建,以及接下来的命令也会基于这个基础镜像(译者注:CentOS和Ubuntu有些命令可是不一样的)。FROM命令可以使用多次,表示会创建多个镜像。具体语法如下:
- FROM <image name>
例如:
- FROM ubuntu
上面的指定告诉我们,新的镜像将基于Ubuntu的镜像来构建。
继FROM命令,DockefFile还提供了一些其它的命令以实现自动化。在文本文件或Dockerfile文件中这些命令的顺序就是它们被执行的顺序。
让我们了解一下这些有趣的Dockerfile命令吧。
1. MAINTAINER:设置该镜像的作者。语法如下:
- MAINTAINER <author name>
2. RUN:在shell或者exec的环境下执行的命令。RUN指令会在新创建的镜像上添加新的层,接下来提交的结果用于在Dockerfile的下一条指令。语法如下:
- RUN 《command》
3. ADD:复制文件指令,它有两个参数<source>和<destination>。destination是容器内的路径。source可以是URL或者是启动配置上下文中的一个文件。语法如下:
- ADD 《src》 《destination》
4. CMD:提供了容器默认的执行命令。 Dockerfile只允许CMD指令使用一次。 使用多个CMD会抵消之前所有的,只有最后一个生效。 CMD有三种形式:
- CMD ["executable","param1","param2"]
- CMD ["param1","param2"]
- CMD command param1 param2
5. EXPOSE:指定容器在运行时监听的端口。语法如下:
- EXPOSE <port>;
6. ENTRYPOINT:配置容器一个可执行的命令,这意味着在每次使用镜像创建容器时一个特定的应用程序可以被设置为默认程序。同时也意味着该镜像每次被调用时仅能运行指定的应用。类似于CMD,Docker只允许一个ENTRYPOINT,多个ENTRYPOINT会抵消之前所有的,只执行最后的ENTRYPOINT指令。语法如下:
- ENTRYPOINT [‘executable’, ‘param1’,’param2’]
- ENTRYPOINT command param1 param2
7. WORKDIR:指定RUN、CMD与ENTRYPOINT命令的工作目录。语法如下:
- WORKDIR /path/to/workdir
8. ENV:设置环境变量。它们使用键值对,并增加运行的程序的灵活性。语法如下:
- ENV <key> <value>
9. USER:镜像正在运行时设置一个UID。语法如下:
- USER <uid>
10. VOLUME:授权访问从容器内到主机上的目录。语法如下:
- VOLUME ['/data']
#p#
DockerFile最佳实践
正如任何使用的应用程序,总会有遵循的最佳实践。Dockerfiles为构建镜像提供了简单的语法。下面我们来看看在缓存、标签、端口以及CMD与ENTRYPOINT这些方面,一些使用dockerfile的提示与技巧。
1:使用缓存
Dockerfile的每条指令都会将更改提交到新的镜像,该镜像将被用于下一个指令的基础镜像。如果一个镜像存在相同的父类镜像和指令(除了ADD)Docker将会使用镜像而不是执行该指令,即缓存。
为了有效地利用缓存,你需要保持你的Dockerfiles一致,并且改建在末尾添加。我所有的Dockerfiles开始于以下五行:
- FROM ubuntu
- MAINTAINER Michael Crosby <michael@crosbymichael.com>
- RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
- RUN apt-get update
- RUN apt-get upgrade -y
更改MAINTAINER指令会使Docker强制执行RUN指令来更新apt,而不是使用缓存。
保持常用的Dockerfile指令在顶部来利用缓存。
2:使用标签
除非你正在用Docker做实验,否则你应当通过-t选项来docker build新的镜像以便于标记构建的镜像。一个简单的可读标签将帮助您管理每个创建的镜像。
docker build -t="crosbymichael/sentry" .
始终通过-t标记来构建镜像。
3:公开端口
两个Docker的核心概念是可重复和可移植。镜像应该能运行在任何主机上并且能运行尽可能多的次数。在Dockerfiles中您有能力映射私有和公有端口,但是你永远不要在Dockerfile中映射公有端口。通过映射公有端口到主机上,你将只能运行一个容器化应用程序实例。
- #private and public mapping
- EXPOSE 80:8080
- #private only
- EXPOSE 80
如果镜像的消费者关心容器公有映射了哪个公有端口,他们可以在运行镜像时设置-p选项,否则,Docker会给容器自动分配端口。
切勿在Dockerfile映射公有端口。
4:CMD与ENTRYPOINT的语法
无论CMD还是ENTRYPOINT都是直线前进的,但他们有一个隐藏的错误“功能”,如果你不知道的话他们可能会触发问题。这些指令支持的两种不同的语法。
- CMD /bin/echo
- #or
- CMD ["/bin/echo"]
这看起来好像没什么问题,但深入细节里的魔鬼会将你绊倒。如果你使用第二个语法:CMD(或ENTRYPOINT)是一个数组,它执行的命令完全像你期望的那样。如果使用第一种语法,Docker会在你的命令前面加上/bin/sh -c。我记得一直都是这样。
如果你不知道Docker修改了CMD命令,在命令前加上/bin/sh -c可能会导致一些意想不到的问题以及不容易理解的功能。因此,在使用这两个指令你应当总是使用数组语法,因为两者都会确切地执行你打算执行的命令。
使用CMD和ENTRYPOINT时,请务必使用数组语法。
5. CMD和ENTRYPOINT 联合使用更好
以防你不知道ENTRYPOINT使您的容器化应用程序运行得像一个二进制文件,您可以在docker run期间给ENTRYPOINT参数传递,而不是担心它被覆盖(跟CMD不同)。当与CMD一起使用时ENTRYPOINT表现会更好。让我们来研究一下我的Rethinkdb Dockerfile,看看如何使用它。
- #Dockerfile for Rethinkdb
- #http://www.rethinkdb.com/
- FROM ubuntu
- MAINTAINER Michael Crosby <michael@crosbymichael.com>
- RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
- RUN apt-get update
- RUN apt-get upgrade -y
- RUN apt-get install -y python-software-properties
- RUN add-apt-repository ppa:rethinkdb/ppa
- RUN apt-get update
- RUN apt-get install -y rethinkdb
- #Rethinkdb process
- EXPOSE 28015
- #Rethinkdb admin console
- EXPOSE 8080
- #Create the /rethinkdb_data dir structure
- RUN /usr/bin/rethinkdb create
- ENTRYPOINT ["/usr/bin/rethinkdb"]
- CMD ["--help"]
这是获得容器化Rethinkdb全部所需。在顶部我们有标准的5行来确保基础镜像是最新的,端口的公开等等......随着ENTRYPOINT的设置,我们知道每当这个镜像运行,在docker run过程中传递的所有参数将成为ENTRYPOINT(/usr/bin/rethinkdb)的参数。
在Dockerfile中我还设置了一个默认CMD参数--help。这样做是为了docker run期间如果没有参数的传递,rethinkdb将会给用户显示默认的帮助文档。这是你所期望的与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 time
- Machine 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_64
- info: Loading data from directory /rethinkdb_data
- warn: 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 29015
- info: Listening for client driver connections on port 28015
- info: Listening for administrative HTTP connections on port 8080
- info: Listening on addresses: 127.0.0.1, 172.16.42.13
- info: Server ready
- info: 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.
就这样,一个全面的可以访问db和管理控制台的Rethinkdb实例就运行起来了,你可以用与镜像交互一样的方式来与其交互。它功能非常强大但是简单小巧。当然,我喜欢简单。
CMD和ENTRYPOINT 结合在一起使用更好。
我希望这篇文章可以使您在使用Dockerfiles以及构建镜像时受益。展望未来,我相信Dockerfiles会成为Docker的重要一部分:简单而且使用方便无论你是消费或是生产镜像。我打算投入更多的时间来提供一个完整的,功能强大,但简单的解决方案来使用Dockerfile构建Docker镜像。
本文整理自文章:http://dockerone.com/article/103 & http://dockerone.com/article/131