Docker Dockerfile入门详细配置用法

devin0910 · 2018-09-19

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

当你要发布的时候,一般都会把应用程序打包到镜像里,这时你就需要下面两个指令: ADDCOPY 。它们两个都能把文件拷贝并添加到容器里的指定路径中,而且使用形式也相同。(注意:当路径中有空格时,使用下面的一种形式。)

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.meping会使用当前传入的参数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
注意
  1. 在基于windows的容器中,挂载点路径必须是本身不存在的或者为空的文件夹,而且不能是C盘的路径。
  2. 在Dockerfile中修改挂载点中的数据发生在定义VOLUME指令之后,则这些修改操作被忽略。
  3. 在JSON数组格式中,你需要用双引号而不是单引号引用路径。
  4. 你可以在运行或创建容器的时候指定宿主机上的路径。

USER

USER 可以用来指定在Dockerfile中 USER 指令之后 RUN , CMDENTRYPOINT 运行容器和程序时的用户及用户组(如果不指定用户组,默认为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。 lnmp Docker容器关系

LNMP Docker容器关系

Docker Compose定义容器之间的关系,让我们很方便地管理多个容器的生命周期。下一节,我们会简单地介绍Docker Compose的相关概念,然后搭建LNMP开发环境。

Docker User