Dockerfile构建镜像

时光毁灭记忆、已成空白 提交于 2021-02-14 16:50:45

构建镜像

构建镜像指令:docker  build或docker  image  build

 

Dockerfile初识:

cat >>Dockerfile<<end

FROM  python:2.7-slim

WORKDIR  /app

ADD  .  /app

RUN  pip  install  -r  requirements.txt

EXPOSE  80

ENV  NAME  World

CMD  ["python", "app.py"]

end

 

yum install python2-pip

配置pip国内源

mkdir  -p  /root/.pip

cat >> /root/.pip/pip.conf<<end

[global]
trusted-host=mirrors.aliyun.com
index-url=http://mirrors.aliyun.com/pypi/simple/

end

 

cat >>requirements.txt<<end

Flask

Redis

end

 

cat  >> app.py<<end

from  flask   import  Flask

from  redis   import  Redis, RedisError

import os

import socket

# Connect to Redis

redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

 

app = Flask(__name__)

 

@app.route("/")

def hello():

    try:

        visits = redis.incr("counter")

    except RedisError:

        visits = "<i>cannot connect to Redis, counter disabled</i>"

 

    html = "<h3>Hello {name}!</h3>" \

           "<b>Hostname:</b> {hostname}<br/>" \

           "<b>Visits:</b> {visits}"

    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

 

if __name__ == "__main__":

    app.run(host='0.0.0.0', port=80)

end

 

docker  build  -t friendlyhello   .

上下文的概念:

执行docker build命令时,宿主机上的当前工作目录称为构建上下文。默认情况下,假定Dockerfile位于本地当前目录,但可以使用-f指定其他位置无论Dockerfile实际存在的位置如何,当前目录中的所有文件和目录的递归内容都将作为构建上下文发送到Docker守护程序(构建由Dockerd守护程序运行)

 

Dockerfile详解

格式:

# Comment

INSTRUCTION  arguments

注意事项:

1)、如果一行太长,使用反斜线 \ 续行

2)、在行首使用#符号,表示注释(注释中不支持 \续行

3)、INSTRUCTION不区分大小写。惯例是让它们成为大写的,以便更容易地将它们与参数区分开来

18个指令

1FROM                          #必要指令,通常是第一条(引用变量及多阶段构建例外),可有多条(多阶段构建)

FROM  <image>  [AS <name>]              #AS <name>为该阶段命名

FROM  <image>[:<tag>]  [AS <name>]

FROM  <image>[@<digest>]  [AS <name>]

 

scratch镜像:

FROM  scratch

ADD  hello  /

CMD  ["/hello"]

 

说明:

1、可通过添加AS name来为该构建阶段命名该名称可用于后续FROMCOPY --from=<name|index>指令,以引用此阶段构建的镜像

2tagdigest值是可选的。如果省略其中任何一个,则构建器默认采用latest标记

3、FROM指令支持变量,变量在第一个FROM指令之前使用ARG声明(ARG指令是唯一一个能用在FROM之前的指令,声明仅在构建镜像过程中存在的变量)
ARG  CODE_VERSION=latest
FROM  base:${CODE_VERSION}
CMD  /code/run-app

 

2MAINTAINER  镜像维护者信息

MAINTAINER  username@domain.com

说明:指定该镜像维护者信息,推荐使用LABEL替代该指令指定镜像维护者信息

docker  inspect  <Container ID>   | grep -A 6  Labels

 

3RUN 执行命令

用来执行命令,有2种格式

格式一:

shell格式:  RUN  <命令>

            如:RUN  echo  '<h1>Hello, Docker!</h1>'   >  /usr/share/nginx/html/index.html

说明:

1、这种格式在shell中运行,会转换为/bin/sh  -c  <命令>执行

2、可以使用SHELL命令更改默认的shell,或使用exec形式

3、要使用除“/bin/sh”之外的其他shell,请使用exec形式。例如,RUN ["/bin/bash", "-c", "echo hello"]

4、命令中支持变量扩展

示例:

RUN  /bin/bash -c  source  $HOME/.bashrc; \

echo  $HOME'

 

格式二:

exec格式:      RUN  ["executable", "param1", "param2"]

说明:

1、  这种格式不会被转换

2、  不支持变量扩展

3、  exec形式被解析为JSON数组,因此必须使用双引号(")来包括单词而不能使用单引号('

4、  在JSON形式中,必须转义反斜杠\(windows中作为路径分隔符经常遇到)

 

注意:

1、可使用反斜杠\(即续行符)分隔的多行上拆分长或复杂语句,使Dockerfile更具可读性,可理解性和可维护性

示例:

    RUN  yum update && yum install -y \

      package-bar \

      package-baz \

      package-foo=1.3.*

2、使用管道

某些RUN命令依赖于使用管道符(|)将一个命令的输出传递到另一个命令的能力,如下例所示:

RUN  wget -O - https://some.site  |  wc -l  > /number

Docker使用/bin/sh -c解释器执行这些命令,解释器仅评估管道中最后一个操作的退出代码以确定成功。

在上面的示例中,只要wc -l命令成功,即使wget命令失败,此构建步骤也会成功并生成新镜像。

如果希望命令因管道中任何阶段的错误而失败,预先使用set -o pipefail设置bash环境变量,确定意外错误可防止构建无意中成功

例如:

RUN  set -o pipefail && wget -O - https://some.site | wc -l > /number

注:并非所有shell都支持该-o pipefail选项

 

4CMD为执行容器提供默认值             #只能有一条,如果列出多个仅最后一条生效

有三种形式:

  • CMD  ["executable","param1","param2"]exec形式,首选)
  • CMD  command  param1  param2(shell形式)
  • CMD  ["param1","param2"](必须配合ENTRYPOINT使用,作为其默认参数,单独使用无效果)

说明:

1)exec形式不支持变量扩展,shell形式支持

2exec形式被解析为JSON数组,因此必须使用双引号(")来包括单词而不能使用单引号(')

3)在JSON形式中,必须转义反斜杠\(windows中经常遇到)

注意:不要混淆RUN和CMD。RUN实际上运行一条命令并提交结果,CMD在构建过程中不执行任何操作,而是作为镜像的默认指令(即使用该镜像启动容器时默认执行的指令)

 

5LABEL          添加元数据到镜像

LABEL  <key>=<value>  <key>=<value>  <key>=<value> ...

LABEL是键值对,要在LABEL值中包含空格,请使用()引号或反斜杠转义,就像在命令行解析中一样

示例:

LABEL  "com.example.vendor"="ACME Incorporated"

LABEL  com.example.label-with-value="foo"

LABEL  version="1.0"

LABEL  description="This text illustrates \                             #续行符

that label-values can span multiple lines."

镜像可以有多个标签,可以在一行中指定多个标签

两种示例:

LABEL  multi.label1="value1"  multi.label2="value2"  other="value3"

LABEL  multi.label1="value1" \

      multi.label2="value2" \

      other="value3"

 

6EXPOSE  容器运行时侦听其服务端口

EXPOSE  <port1>  [<port2>/<protocol>...]                                        #多个端口之间使用空格间隔

EXPOSE指令通知Docker容器在运行时侦听指定的网络端口。

可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认为TCP

 

7ENV  设置环境变量

有两种形式:

形式一:

ENV <key> <value>                           #第二个空格后的所有字符串将被视为<value>,包括空格字符

形式二:

ENV  <key>=<value> ...                   # ENV指令将环境变量<key>的值设置为<value>

该环境变量在构建阶段所有后续指令的环境中生效,且使用ENV设定的环境变量会在镜像中一直存在(使用该镜像启用的容器中也存在该变量)

示例:

ENV  myName="John Doe"  myDog=Rex\ The\ Dog \

    myCat=fluffy

ENV myName John Doe

ENV myDog Rex The Dog

ENV myCat fluffy                       #效果相同

 

ENV指令中可使用变量:

ENV  abc=hello

ENV  abc=bye  def=$abc

ENV  ghi=$abc

示例:

ENV  PG_MAJOR  9.3

ENV  PG_VERSION  9.3.4

RUN  curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress &&…

ENV  PATH  /usr/local/postgres-$PG_MAJOR/bin:$PATH

 

注意:

1、可以使用docker inspect查看变量值,使用docker run --env <key>=<value>在构建容器时更改变量的值。

2、Dockerfile中能够扩展变量的指令:

FROM/ADD/COPY/ENV/EXPOSE/LABEL/STOPSIGNAL/USER/VOLUME/WORKDIR/ONBUILD

 

8ARG                                                  #设置构建过程中的环境变量

格式:

ARG  <参数名>[=<默认值>]

ARG指令可以可选地包括一个默认值

示例:

FROM  busybox

ARG  user1=someuser                       #指定了默认值

ARG  buildno=1                               #指定了默认值

...

说明:

如果ARG指令具有默认值,并且在构建时没有传递值,将使用默认值
如果构建时传递值,将不使用默认值,而是用传递的值

 

注意:

1、效果和ENV一样,都是设置环境变量,不同的是:ARG设置的环境变量,在将来容器运行时,不再存在,仅在构建镜像过程中存在

2、可在构建命令docker build使用--build-arg <参数名>[=<值>]选项覆盖Dockerfile中设定的默认值,参数名必须在Dockerfile中已经使用ARG指令构建,否则会报错或警告

3、一个ARG指令生效范围,在其之后且在其构建阶段内。若在多阶段构建中的每个阶段均使用ARG,每个阶段都必须包含ARG指令。

示例:多阶段构建

FROM  busybox                                      #第一阶段

ARG  SETTINGS

RUN  ./run/setup $SETTINGS

FROM  busybox                                      #第二阶段

ARG  SETTINGS

RUN  ./run/other   $SETTINGS

4、Dockerfile可以包括一个或多个ARG指令。

例如,以下是有效的Dockerfile:

FROM  busybox

ARG  user1

ARG  buildno

...

警告:

不要使用构建时变量来传递密码,如github密钥,用户凭据等,构建过程中变量值对于任何用户都是可见的

 

5、ARG生效范围

ARG变量定义从Dockerfile中定义的行开始生效,直到其所在构建阶段结束(Dockerfile中有多个构建阶段,仅在自己的构建阶段中生效)

示例:

FROM  busybox

USER  ${user:-some_user}

ARG  user

USER  $user

...

 

通过以下指令构建:

docker  build  --build-arg user=what_user  .

第2行USER使用some_user作为user变量的值。

第4行USER使用what_user作为user变量的值,且what_user在命令行上传递的值。

在通过ARG指令定义之前,对变量的任何使用都会导致空字符串

 

6、使用ARG变量

可以使用ARG或ENV指令指定在RUN指令中可用的变量。ENV指令定义的环境变量始终覆盖ARG定义的同名变量。

示例:

FROM  ubuntu

ARG  CONT_IMG_VER

ENV  CONT_IMG_VER  v1.0.0

RUN  echo  $CONT_IMG_VER

然后,假设使用此命令构建此映像:

$ docker  build  --build-arg  CONT_IMG_VER=v2.0.1  .

在这种情况下,RUN指令使用v1.0.0而不是ARG用户传递的设置:v2.0.1

 

9COPY  复制上下文中的文件或目录到容器中

COPY有两种形式:

  • COPY [--from=<name|index>] [--chown=<user>:<group>] <src>... <dest>
  • COPY [--from=<name|index>] [--chown=<user>:<group>] ["<src>",... "<dest>"](包含空格的路径使用此形式

--chown功能仅在用于构建Linux容器

 

注意:

1、源路径一定是相对于build指令中指定的上下文的相对路径
如COPY  package.json   /usr/src/app/

2、目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(也是容器内的路径,工作目录可以使用WORKDIR指令来指定)

3、目标路径不需要事先创建,如果目录不存在,在复制文件之前会自动创建

4、使用COPY指令时,源文件的各种元数据都会被保留,如读写执行权限,文件的相关时间属性等

5、源路径可以是多个,甚至可以含有通配符,通配符要符合Go的filepath.Match规则:

COPY  hom*        /mydir

COPY  hom?.txt      /mydir

 

6、如果使用--chown选项,最好使用数字形式的GID和UID(如果使用用户名和组名,在容器文件系统中不存在/etc/passwd及/etc/group,将会导致构建失败)

示例:

COPY   --chown=55:mygroup   files*   /somedir/

COPY   --chown=bin   files*   /somedir/                    #不指定组名,使用与用户名同名的组

COPY   --chown=1    files*   /somedir/                     #GID也为1

COPY   --chown=10:11   files*   /somedir/

7、(可选)COPY接受一个标志--from=<name|index>
该选项将用于替代用户发送先前构建阶段的构建上下文

如果先前构建阶段使用FROM .. AS  <name>,则--from可通过name引用

如果先前构建阶段未使用FROM .. AS  <name>,则--from可通过索引值index引用,如果先前构建阶段是第一阶段,index0,以此类推
如果找不到具有指定名称的构建阶段,则尝试使用具有相同名称的镜像

COPY遵守以下规则:

  • <src>路径必须位于build构建时的context上下文中;不能COPY ../something /something,因为第一步docker build是将上下文目录(和子目录)发送到docker守护程序
  • 如果<src>是目录,则复制目录中的全部内容,包括文件系统元数据(不复制目录本身,只复制其内容)
  • 如果<src>任何其他类型的文件,则将其与元数据一起单独复制。在这种情况下,如果<dest>以尾部斜杠结尾/,则将其视为目录,<src>并将写入内容<dest>/base(<src>)
  • 如果<src>直接或由于使用通配符指定了多个资源,则<dest>必须是目录,并且必须以斜杠结尾/。
  • 如果<dest>不以尾部斜杠结束,则将其视为常规文件并将<src>的内容写入<dest>文件中
  • 如果<dest>不存在,则会在其路径中创建所有缺少的目录。

 

10ADD  复制本地文件到上下文中

从<src>中复制文件、目录或远程文件URL,并将它们添加到路径上镜像的文件系统<dest>中

ADD有两种形式:

  • ADD  [--chown=<user>:<group>]  <src>...<dest>
  • ADD  [--chown=<user>:<group>]  ["<src>",..."<dest>"](包含空格的路径需要使用这种形式

ADD指令的所有使用注意事项及遵守规则与COPY相同

COPY不同的是:

源路径:

1、可以是URL,Docker引擎会尝试下载这个链接文件,放入到<dest>中,下载后文件的权限为600,若是想更改文件的权限,还需要增加一层RUN指令

2、如果下载的是压缩包,还需要增加一层RUN指令进行解压

3、如果<src>是一个上下文中的tar压缩文件(gzip、bzip2、xz),ADD指令会自动解压放入到<dest>中,如果不希望压缩文件解压,则使用COPY(这是ADD指令最大的用处)

4、仅在需要自动解压时,才会选择使用ADD指令,否则复制文件尽可能使用COPY

5、ADD指令会令镜像构建缓存失效,从而可能使镜像构建过程非常缓慢(因此多阶段构建时,尽可能不要使用)

 

11ENTRYPOINT  入口点                  #只能有一条,若有多条仅最后一条生效

ENTRYPOINT有两种形式:

  • ENTRYPOINT ["executable", "param1", "param2"](exec形式,首选
  • ENTRYPOINT command param1 param2(shell形式,最好在command前使用exec指令,以使该进程作为容器的PID 1进程)

exec形式:

docker run <image>的命令行参数将附加在exec形式的ENTRYPOINT的所有元素之后,作为ENTRYPOINT指令的参数,并将覆盖所有使用CMD指定的元素。

可以使用docker run --entrypoint标志覆盖exec形式的ENTRYPOINT指令

shell形式:

shell形式防止任何被CMD或run使用的命令行参数,缺点是ENTRYPOINT将被作为/bin/sh -c的一个子命令且不传递信号。这意味着可执行文件将不是容器中PID为1的进程,并且不会收到Unix信号,因此可执行文件将不会收到来自docker  stop  <container>SIGTERM信号。

 

从命令行工具的镜像示例:

cat >>Dockerfile<<end

FROM  centos

RUN   yum install  curl -y

CMD   ["curl", "-s", "http://ip.cn"]

end

 

docker  build  -t  myip:1.0  .

docker  run  --name myip01  myip:1.0

docker  run  --name myip02  myip:1.0  -i                                                #此时会报错

原因:执行docker run myip -i   镜像名称myip后的手动指定的主进程指令将会替换构建镜像时CMD指定的主进程,这里替换后相当于执行-i指令,但实际上没有该指令,提示executable file not found in $PATH

正确用法:docker  run  myip  curl -s http://ip.cn  -i           #整体替换Dockerfile中的CMD

 

使用ENTRYPOINT解决:

使用如下的dockerfile构建镜像

cat >> Dockerfile<<end

FROM centos

RUN  yum  install  curl  -y

ENTRYPOINT  ["curl", "-s", "http://ip.cn"]

end

 

docker  build  -t  myip2:1.0  .

docker  run  --name myip201  myip2:1.0

docker  run  --name myip202  myip2:1.0  -i

这种方式很有用,因为镜像名称可以兼作二进制文件的引用

 

ENTRYPOINT指令还可以与辅助脚本结合使用,使其能够以与上述命令类似的方式运行,即使启动该工具可能需要多个步骤

例如,Postgres官方镜像使用以下脚本作为其ENTRYPOINT(这样在启动容器时可以传递参数给脚本):

#!/bin/bash

set -e                     #若指令传回值不等于0,则立即退出shell

if [ "$1" = 'postgres' ]; then

    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then

        gosu postgres initdb

    fi

    exec gosu postgres "$@"

fi

exec "$@"

 

配置应用为容器中PID1进程

此脚本使用的exec bash命令,以使最终运行的应用程序成为容器的PID 1,这允许应用程序接收发送到容器的任何Unix信号

帮助程序脚本被复制到容器中并通过ENTRYPOINT容器启动运行:

COPY  ./docker-entrypoint.sh  /

ENTRYPOINT  ["/docker-entrypoint.sh"]

CMD  ["postgres"]

该脚本允许用户以多种方式与Postgres交互。

它可以简单地启动Postgres:

$ docker  run  postgres

或者,它可用于运行Postgres并将参数传递给服务器:

$ docker  run  postgres  postgres  --help              #ENTRYPOINT配合脚本使用的好处

最后,它还可以用来启动一个完全不同的工具,比如Bash:

$ docker  run  --rm  -it  postgres  bash

 

Exec形式的ENTRYPOINT示例

可以使用exec形式ENTRYPOINT设置默认命令和参数,然后使用任一形式CMD设置可更改的其他默认值。

FROM  ubuntu

ENTRYPOINT  ["top", "-b"]

CMD  ["-c"]

构建镜像,启容器

检查结果:

$ docker  exec  -it test  ps aux

 

可以使用docker stop优雅地请求top进程关闭

可再次使用docker start启动

这样通过docker指令传递的信号(传给容器),实际上就是传递给容器中的PID1的进程

 

以下Dockerfile显示使用ENTRYPOINT在前台运行Apache(即作为容器的PID 1进程):

FROM  debian:stable

RUN   apt-get update  &&  apt-get install -y  --force-yes  apache2

EXPOSE  80  443

VOLUME  ["/var/www", "/var/log/apache2", "/etc/apache2"]

ENTRYPOINT  ["/usr/sbin/apache2ctl",  "-D",  "FOREGROUND"]

 

注意

1、exec形式被解析为JSON数组,这意味着必须使用双引号(")来引用而不能使用单引号(')。

2shell形式不同,exec形式不会调用命令shell,因此不能扩展变量。例如,ENTRYPOINT [ "echo", "$HOME" ]不会对变量进行替换$HOME。

3、如果想要shell处理,那么要么使用shell形式,要么直接执行shell,例如:ENTRYPOINT [ "sh", "-c", "echo $HOME" ]

 

Shell形式的ENTRYPOINT示例

可以为ENTRYPOINT指定一个纯字符串指令,将在/bin/sh -c中执行。该形式支持环境变量扩展,并将忽略任何CMDdocker run命令行参数

为了确保docker stop能够正确地发出任何信号给长时间运行的ENTRYPOINT可执行文件,需要使用exec指令执行相关指令

FROM ubuntu

ENTRYPOINT exec top -b

运行此镜像时,将看到单个PID 1进程:

$ docker run -it --rm --name test top

 

执行docker stop关闭及docker start启动容器

 

如果shell形式中未使用exec执行ENTRYPOINT中的指令:

FROM ubuntu

ENTRYPOINT  top  -b

CMD  -d 5

启容器:

docker  run  -it  --name  test  …

 

从输出中top看到指定ENTRYPOINT的不是PID 1

如果运行docker stop test,容器将不会干净地退出(最终容器也会推出,但是通过将信号发送给sh,而不是发送给应用程序指令top

CMDENTRYPOINT如何相互作用:

  1. Dockerfile应至少指定一个CMDENTRYPOINT命令
  2. ENTRYPOINT应该在将容器用作可执行文件时使用
  3. CMD应该用作定义ENTRYPOINT命令的默认参数
  4. CMD在使用替代参数运行容器时将被覆盖

下表显示了针对不同ENTRYPOINT/CMD组合执行的命令:

 

没有ENTRYPOINT

ENTRYPOINT exec_entry p1_entry

ENTRYPOINT [“exec_entry”“p1_entry”]

没有CMD

错误,不允许

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry

CMD [“exec_cmd”“p1_cmd”]

exec_cmd  p1_cmd

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry  exec_cmd  p1_cmd

CMD [“p1_cmd”“p2_cmd”]

p1_cmd  p2_cmd

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry  p1_cmd  p2_cmd

CMD  exec_cmd  p1_cmd

/bin/sh  -c  exec_cmd  p1_cmd

/bin/sh  -c  exec_entry  p1_entry

exec_entry  p1_entry  /bin/sh -c exec_cmd p1_cmd

 

12VOLUME

格式:

VOLUME  ["/data"]

VOLUME  ["/var/log/","/data"]                #在docker的卷管理目录中划出2块区域挂载到容器中的这两个目录

VOLUME  /var/log

VOLUME  /var/log  /var/db

VOLUME指令创建具有指定名称的挂载点(即在容器中创建目录,并将volume挂载到该挂载点,这种方式的volume名称是随机生成的),并将其标记为从本机或其他容器保存外部挂载的卷。该值可以是JSON数组,VOLUME ["/var/log/"]或具有多个参数的普通字符串,如VOLUME /var/log或VOLUME  /var/log  /var/db

示例:

FROM  centos

RUN  mkdir  /myvol

RUN  echo "hello world"  >  /myvol/greeting

VOLUME  ["/myvol", "/data"]

CMD ["/bin/bash"]

 

docker volume ls

docker volume inspect

ll  /var/lib/docker/volumes/

注意:

宿主机目录在容器运行时声明:宿主机目录(mountpoint)本质上是依赖于主机的。这是为了保持镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,无法从Dockerfile中安装主机目录,VOLUME指令不支持指定host-dir参数,必须在创建或运行容器时指定挂载点(-v选项指定)

 

13USER

USER  <user>[:<group>] 或

USER  <UID>[:<GID>]

当构建镜像时,在Dockerfile文件中有RUN、CMD、ENTRYPOINT指令时,USER指令用于设置执行这些指令的用户(UID),及可选的用户组(GID),用户或组必须事先存在

警告当用户没有主组时,镜像(或下一个指令)将使用root组运行

 

14WORKDIR

WORKDIR  /path/to/workdir

WORKDIR指令用于在Dockerfile中设置RUNCMDENTRYPOINTCOPYADD指令的工作目录。如果WORKDIR指定的目录事先不存在,则会自动被创建。

在Dockerfile中可多次使用WORKDIR指令。如果提供了相对路径,则它将相对于前一条WORKDIR指令的路径。

例如:

WORKDIR  /a

WORKDIR  b

WORKDIR  c

RUN  pwd

 

最终pwd命令的输出将是/a/b/c

WORKDIR指令可以解析先前使用ENV设定的环境变量。只能使用Dockerfile文件中显式设置的环境变量。

例如:

ENV  DIRPATH  /path

WORKDIR  $DIRPATH/$DIRNAME

RUN  pwd

最终pwd命令的输出Dockerfile将是/path/$DIRNAME

 

15ONBUILD

ONBUILD  [INSTRUCTION]

当镜像用作另一个构建的基础时,该ONBUILD指令向镜像添加将在稍后执行的触发指令。触发器将在下游构建的上下文中执行,就好像它在下游DockerfileFROM指令之后插入了这里的INSTRUCTION指令一样

任何构建指令都可以注册为触发器

 

16HEALTHCHECK               #只能有一条,如果列出多个,则只有最后一个生效

该HEALTHCHECK指令有两种形式:

  • HEALTHCHECK  [OPTIONS]  CMD  command(通过在容器内运行命令来检查容器运行状况)
  • HEALTHCHECK  NONE(禁用从基础映像继承的任何运行状况检查)

当容器指定了运行状况检查时,除了正常状态外,它还具有运行状况:

这个状态最初是starting

每当健康检查通过时,它就会变成healthy

经过一定数量的连续失败后,它就变成了unhealthy

OPTIONS:

  • --interval=DURATION(默认值:30s)                    检查时间间隔
  • --timeout=DURATION(默认值:30s)                    单次检查超时秒数,若超时视为检查失败
  • --retries=N(默认值:3)                           连续重试N次后,健康检查均失败,认为容器为unhealthy
  • --start-period=DURATION(默认值:0s)                为需要时间引导的容器提供初始化时间,在此期间探测失败将不计入最大重试次数。但如果在启动期间运行状况检查成功,则会将容器视为已启动,并且所有连续失败将计入最大重试次数

CMD关键字后面的命令可以是shell命令(如HEALTHCHECK CMD /bin/check-running)或exec形式

命令的退出状态指示容器的运行状况。可能的值是:

  • 0:成功 - 容器健康且随时可用
  • 1:不健康 - 容器无法正常工作
  • 2:保留 - 不要使用此退出代码

示例:

要检查每五分钟左右网络服务器能够在三秒钟内为网站的主页面提供服务:

HEALTHCHECK  --interval=5m  --timeout=3s  CMD curl -f http://localhost/ || exit 1

 

17SHELL

SHELL  ["executable", "parameters"]

该指令允许覆盖用于shell形式的命令的默认shell

Linux上默认shell是["/bin/sh", "-c"]

Windows上默认shell是["cmd", "/S", "/C"]。SHELL指令必须JSON格式写入Dockerfile

Windows上经常使用

 

18STOPSIGNAL

STOPSIGNAL  signal

STOPSIGNAL指令设置将发送到容器的系统调用信号以退出。此信号可以是与内核的系统调用表中的位置匹配的有效无符号数,例如9,或SIGNAME格式的信号名,例如SIGKILL

默认的stop signalSIGTERM,在docker stop的时候会给容器内PID为1的进程发送这个signal,通过--stop-signal可以设置自己需要的signal,主要的目的是为了让容器内的应用程序在接收到signal之后可以先做一些事情,实现容器的平滑退出,如果不做任何处理,容器将在一段时间之后强制退出,会造成业务的强制中断,这个时间默认是10s

 

Dockerfile说明

Docker镜像由只读层组成,每个层都代表一个Dockerfile指令这些层是堆叠的,每一层都是前一层变化的增量。

Dockerfile

FROM  ubuntu:15.04

COPY  .  /app

RUN  make  /app

CMD  python  /app/app.py

每条指令创建一个层

  • FROM从ubuntu:15.04Docker镜像创建一个层
  • COPY从Docker客户端的当前目录添加文件
  • RUN用你的应用程序构建make
  • CMD指定在容器中运行的命令

运行镜像并生成容器时,可以在基础层的顶部添加新的可写层writable layer容器层。对正在运行的容器所做的所有更改(例如写入新文件,修改现有文件和删除文件)都将写入此可写容器层

 

上下文的概念

执行docker build命令时,宿主机上的当前工作目录称为构建上下文。默认情况下,假定Dockerfile位于宿主机本地当前目录,但可使用选项(-f)指定其他位置无论Dockerfile实际存在的位置如何,当前目录中的所有文件和目录的递归内容都将作为构建上下文发送到Docker守护程序(构建由Dockerd守护程序运行,而不是由CLI运行

Usage:  docker  build  [OPTIONS]   PATH | URL | -

 

从构建上下文(.中构建映像:

mkdir  project && cd  project

echo  "hello" > hello

echo  -e "FROM  busybox\nCOPY  /hello /\nRUN  cat /hello"   >  Dockerfile

docker  build  -t helloapp:v1  .

移动Dockerfile和hello文件到单独的目录

 

构建映像的第二个版本(不依赖于上一个版本的缓存)

使用-f指向Dockerfile并指定构建上下文的目录:

mkdir  -p  dockerfiles  context

mv  Dockerfile  dockerfiles && mv  hello  context

docker  build  --no-cache  -t helloapp:v2  -f dockerfiles/Dockerfile  context

 

Docker 17.05增加了Dockerfile通过stdin使用本地或远程构建上下文进行管道来构建映像的功能。

在早期版本中,使用来自stdin的Dockerfile构建映像,不支持发送构建上下文

Docker 17.04及更低版本

docker  build  -t foo  -  <<EOF

FROM  busybox

RUN  echo "hello world"

EOF

Docker 17.05及更高版本(本地构建上下文)

docker  build  -t foo  .   -f -  <<EOF

FROM  busybox

RUN  echo "hello world"

COPY  . /my-copied-files

EOF

Docker 17.05及更高版本(远程构建上下文)

docker  build  -t foo  https://github.com/thajeztah/pgadmin4-docker.git  -f -  <<EOF

FROM  busybox

COPY  LICENSE  config_local.py  /usr/local/lib/python2.7/site-packages/pgadmin4/

EOF

 

排除上下文中与构建无关的文件,使用上下文中的.dockerignore文件(每个要排除的文件或目录独占一行)

示例.dockerignore文件(支持通配):

# comment

*/temp*

*/*/temp*

temp?

**匹配任意数量目录(包括零)的特殊通配符字符串

例如,**/*.go将排除.go在所有目录中找到的以该结尾的所有文件,包括构建上下文的根

!(感叹号)开头的行可用于对排除项进行例外处理

以下是.dockerignore使用此机制的示例文件:

    *.md

    !README.md

除README.md上下文外,所有以md为扩展名的文件除外(也就是README.md文件不会被排除)

示例:

    *.md

    README-secret.md

    !README*.md

包含所有README文件。中间一行没有效果,因为!README*.md匹配README-secret.md并且最后。

甚至可以使用该.dockerignore文件来排除Dockerfile.dockerignore文件

注意:这些文件仍然发送到守护程序,因为它需要它们来完成它的工作。但是ADDCOPY指令不会将它们复制到镜像中

  

使用多阶段构建

多阶段构建(在Docker 17.05或更高版本中)可大幅减小最终镜像的大小,而不必费力地减少中间层和文件的数量。

由于镜像是在构建过程的最后阶段构建的,因此可以通过利用构建缓存来最小化镜像层

 

#第一阶段

Dockerfile

FROM  golang:1.7.3

WORKDIR  /go/src/github.com/alexellis/href-counter/

RUN  go  get  -d  -v golang.org/x/net/html

COPY  app.go  .

RUN  CGO_ENABLED=0  GOOS=linux go build -a -installsuffix cgo -o app .

#第二阶段

FROM  alpine:latest

RUN  apk  --no-cache  add  ca-certificates

WORKDIR  /root/

COPY  --from=0  /go/src/github.com/alexellis/href-counter/app  .

CMD  ["./app"]

只需要单个Dockerfile,也不需要单独的构建脚本。只要运行docker build

$ docker  build  -t  alexellis2/href-counter:latest  .

 

默认情况下,阶段未命名,可以通过整数来引用它们,第一条FROM指令从0开始。但是,可以通过as <NAME>FROM指令中添加一个来命名您的阶段

此示例通过命名阶段并使用COPY指令中的名称来改进前一个示例。这意味着即使Dockerfile中的指令稍后重新排序,COPY也不会中断。

#第一个构建阶段

FROM  golang:1.7.3  as  builder

WORKDIR  /go/src/github.com/alexellis/href-counter/

RUN  go get -d -v golang.org/x/net/html

COPY  app.go  .

RUN  CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

#第二个构建阶段

FROM  alpine:latest

RUN  apk --no-cache add ca-certificates

WORKDIR  /root/

COPY  --from=builder  /go/src/github.com/alexellis/href-counter/app  .

CMD  ["./app"]

 

停在特定的构建阶段

构建镜像时,不一定需要构建整个Dockerfile,包括每个阶段。可以指定目标构建阶段

以下命令假定您使用的是前一个Dockerfile但在名为builder的阶段停止:

$ docker  build  --target builder  -t alexellis2/href-counter:latest .

一些可能的场景:

  • 调试特定的构建阶段
  • 使用debug阶段,该阶段启用了所有调试符号或工具,以及lean production阶段
  • 使用testing阶段,该阶段应用程序填充测试数据,但使用实际数据的不同阶段构建生产环境

使用外部镜像作为“stage”

使用多阶段构建时,不仅可以从先前在Dockerfile中的构建阶段进行复制还可以使用COPY --from指令从单独的镜像进行复制,使用本地镜像名称,本地或Docker仓库中可用的标记或标记ID。

如有必要,Docker客户端会提取镜像,并从镜像中复制工件。语法是:

COPY  --from=nginx:latest  /etc/nginx/nginx.conf  /nginx.conf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!