Docker Dockerfile入门详细配置用法
Dockerfile提供了构建一个容器所需要的指令,容器运行时的执行的命令。Dockerfile中的每一行代表了一个镜像层,描述了一些操作指令,例如安装软件(e.g. RUN apt-get install nodejs
)。虽然指令名不是大小写敏感的,但是,一般来说都会使用大写的,这样很容易与参数区分开来。
如果一行是以 # 开始的,Docker会认为是一行注释,不支持换行,而且在另外任何位置出现也不会认为 # 后面的是注释。
# 这是一行注释,下面一行#后面部分不会被解析成注释
RUN echo 'we are running some # of cool things'
基本指令
FROM
一般Dockerfile文件都是以FROM
开始,指定制作的镜像是基于哪个镜像。
FROM [AS ]
FROM [:] [AS ]
FROM [@] [AS ]
ARG
ARG 是唯一一个可以放在 FROM 语句之前的指令。ARG 指定可以定义变量,在 FROM 之前可以有一个或者多个 ARG 指令,这些变量只可以在 FROM 语句中使用。
ARG [=]
如果你想在build阶段也使用这个变量的值,你只要重新使用 ARG 指令定义相同的变量而不需要赋值。
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
在开始我们定义了VERSION变量,FROM 指定制作的镜像是基于busybox:latest的。接下来的 ARG VERSION 让VERSION变量能在build阶段也可以使用。
docker build . -t arg-image // 新建一个空目录,创建Dockerfile文件并把上面的代码拷贝进去
docker run -it --rm arg-image cat image_version // 基于上面构建的镜像运行一个新的容器并输出image_version文件中的内容
latest // 由于上面有重新定义 *ARG VERSION*,所以会输出前面定义的值: latest
docker build
默认会在当前目录查找Dockerfile,你也可以通过-f
选项指定从哪儿加载Dockerfile(也可以是其它文件名)。
当你运行上面的build命令时,会把整个上下文(递归地)发送给Docker引擎,根据Dockerfile里面的指令构建镜像。上面的上下文为 . 代表运行build命令时的当前目录(而不是说Dockerfile所在的目录)。
-t
选项指定了生成的镜像名(name:tag),你可以使用多个-t
选项生成生成多个不同名称的镜像。
.dockerignore
文件就像git中的.gitignore
文件类似,在build的时候不会把这里剔除的文件打包到镜像里。
ADD VS COPY
当你要发布的时候,一般都会把应用程序打包到镜像里,这时你就需要下面两个指令: ADD 、COPY 。它们两个都能把文件拷贝并添加到容器里的指定路径中,而且使用形式也相同。(注意:当路径中有空格时,使用下面的一种形式。)
ADD ...
COPY ...
ADD ["",... ""]
COPY ["",... ""]
但是, ADD 的功能更强大: 你可以添加远程服务器上的文件,也可以把本地的tar包(可以识别的压缩方式:identity,gzip,bzip2或者xz)解压到容器的指定目录。如果是添加一个远程的压缩tar包,Docker并不会解压这个文件。
COPY 的作用比较单一:只能添加build context里存在的文件,也不会解压tar包,一般能满足大多数“把文件拷贝到容器里”的场景。
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
使用COPY
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
注: 在shell形式中,你可以使用\
(反斜杠)连接多行。
使用 ADD 意味着需要一个额外的层来删除tar包,这样会增加镜像的大小,所以尽量选择 COPY ,除非你明确知道你需要什么样的功能。
ENTRYPOINT
ENTRYPOINT 指令指定了在容器启动时要执行的命令,它有以下两种形式:
ENTRYPOINT ["executable", "param1", "param2"] (exec form, 首选方式)
ENTRYPOINT command param1 param2 (shell form)
这两种形式的主要区别在于:exec形式的不会启动一个shell来运行命令,这意味着,exec不会像在shell里运行一样进行预处理。例如ENTRYPOINT [ "echo", "$HOME" ]
并不会对$HOME
进行环境变量替换,这时只会输出$HOME
。如果你要正确输出$HOME
对应的变量值,需要修改成ENTRYPOINT ["sh", "-c", "echo $HOME"]
。
CMD
CMD 指令指定了在容器启动时默认执行的命令,你也可以在启动容器的时候指定运行另外的命令。如果Dockerfile中出现多个 CMD 指令,则另外的都会忽略,只有最后一个有效。
CMD 指令有三种形式
CMD ["executable","param1","param2"] (exec form, 首选方式)
CMD ["param1","param2"] (作为默认参数传递给ENTRYPOINT)
CMD command param1 param2 (shell form)
如果你在Dockerfile中CMD echo "Hello world"
,当你运行docker run
会输出 Hello world 。但是,如果你在运行时指定其他命令,例如docker run ps aux
,这时会忽略默认的 CMD , 只会执行ps aux
命令。
下面简单的介绍下如何传递默认参数给ENTRYPOINT:
FROM busybox
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
这里我们基于 busybox 新建一个镜像,当容器启动时会执行ping
命令,默认参数为 localhost ,当你运行docker run
时会输出
PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.056 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.067 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.083 ms
你也可以运行docker run www.weakref.me
,ping
会使用当前传入的参数www.weakref.me
,输出
PING www.weakref.me (133.130.55.75): 56 data bytes
64 bytes from 133.130.55.75: seq=0 ttl=46 time=317.373 ms
64 bytes from 133.130.55.75: seq=1 ttl=46 time=175.990 ms
64 bytes from 133.130.55.75: seq=2 ttl=46 time=174.851 ms
RUN
RUN 一般用来安装程序及软件包依赖,运行时会在当前镜像上创建一个新的文件层用来保存修改的数据。
RUN 指令有两种形式
RUN (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows)
RUN ["executable", "param1", "param2"] (exec form)
安装版本管理软件
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
这里apt-get update && apt-get install
能确保后面的安装的是最新版的软件,避免使用缓存。
EXPOSE
有些时候我们需要提交一些对外的服务,比如mysql、redis等。如果不从容器里暴露端口的话,容器之间或者外网是不能访问容器里的服务的。
EXPOSE [...]
ENV
ENV
ENV = ... // 一次设置多个环境变量
VOLUME
VOLUME 用来创建挂载点。
VOLUME ["/data"]
VOLUME /data
注意
- 在基于windows的容器中,挂载点路径必须是本身不存在的或者为空的文件夹,而且不能是C盘的路径。
- 在Dockerfile中修改挂载点中的数据发生在定义VOLUME指令之后,则这些修改操作被忽略。
- 在JSON数组格式中,你需要用双引号而不是单引号引用路径。
- 你可以在运行或创建容器的时候指定宿主机上的路径。
USER
USER 可以用来指定在Dockerfile中 USER 指令之后 RUN , CMD 和 ENTRYPOINT 运行容器和程序时的用户及用户组(如果不指定用户组,默认为root)
USER [:] or
USER [:]
容器与宿主机共享用户
docker run -d ubuntu sleep infinity
ps aux|grep sleep
root 12774 0.0 0.0 4384 804 ? Ss 06:08 0:00 sleep infinity
虽然我们并没有使用sudo来运行容器,但是sleep infinity
还是以 root 用户来运行的(默认为root)。
WORKDIR
WORKDIR /path/to/workdir
WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为根目录)。WORKDIR可以在一个Dockerfile中出现多次,如果使用相对路径,那它会相对于前面一个WORKDIR指令对应的目录。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最后输出的路径为:/a/b/c
。
LABEL
LABEL 可以为镜像添加元数据(键值对)。
LABEL = = = ...
如果值包含空格则用双引号引起来,多行使用反斜杠转义
FROM busybox
LABEL maintainer="devinying@hotmail.com"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
可以通过dockeer inspect
命令查看镜像的元数据。
Docker指令有效阶段
Build | Build or Run | Run |
---|---|---|
FROM | WORKDIR | CMD |
MAINTAINER | USER | ENV |
COPY、ADD、RUN | EXPOSE、VOLUME | |
ONBUILD、.dockerignore | ENTRYPOINT |
我们介绍了构成Dockerfile的大部分指令,可以自定义镜像满足开发需求。在PHP开发过程中,你需要搭建LNMP开发环境,这时需要三个容器运行不同的程序来协调运行:一个运行php-fpm,一个容器运行mysql,另外一个容器运行nginx。
Docker Compose定义容器之间的关系,让我们很方便地管理多个容器的生命周期。下一节,我们会简单地介绍Docker Compose的相关概念,然后搭建LNMP开发环境。