docker

reference: docker官方文档

dockerfile

  • dockerfile必须以FROM指令开始。ARG是唯一可以早于FROM执行的指令,查看ARG和FROM的交互
  • dockerfile中以#来标示注释,注释会在dockerfile被执行前移除掉,以免对命令产生影响

解析器指令

解析器指令放在最上面,以# directive=value的形式来声明,一旦注释 空行或者builder指令执行,docker就不会再去招解析器指令,而是把它当成注释。
下面几种是不符合规范的场景:

# direc \  
 tive=value

△ 换行不符合规范

# directive=value1
# directive=value2
FROM ImageName

△ 出现两次

FROM ImageName
# directive=value

△ 在builder指令后面,会被认为是注释

# About my dockerfile
# directive=value
FROM ImageName

△ 在注释后面,会被认为是注释

# unknowndirective=value
# knowndirective=value

△ 未知的解释器指令,会被认为是注释,第二行同样会被认为是注释

#directive=value
# directive =value
#	directive= value
# directive = value
#	  dIrEcTiVe=value

△ 上面几种写法会被认为是同一种写法,即无视空格,不区分大小写

支持的解释器指令

  • syntax
    # syntax=[remote image reference]
    这是用buldkit才会使用到的,为了指定构建当前dokcerfile的dokcerfile构建器的位置
  • escape
    # escape=\ (backslash) || # escape=` (backtick)
    escape定义转义字符,转义字符不会在RUN指令中执行
    例如在windows中将转义字符设置成` 很有用,因为windows目录格式是C:\\,但是在dockerfile里面会解析成C:\,就会找不到对应目录

环境变量

环境变量以$variable_name 或者 ${variable_name}格式,同样支持以下bash风格的格式:

  • ${variable:-word} 如果veriable设置了值,结果就是这个值。如果没有,就是word结果,word可以是定值也可以是其他变量
  • ${variable:+word} 如果veriable设置了值,结果就是word。如果没有,就是空
    如下示例:
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

def的值为hello,而 ghi值为bye官方是如下解释的

Environment variable substitution will use the same value for each variable throughout the entire instruction. In other words

讲道理没看特明白,但是个人土味理解应该是说每行算一个entire instruction,这行的值都是不会变化的,所以def还是用的上一行的hello值,而ghi 则可以取到上一行执行后的结果

.dockerignore file

.dockerignore file其实和git里面的.gitignore文件类似,docker CLI在发送context的时候会先寻找这个文件,然后将这个文件和文件里面的指定模式的文件去除掉,不发送给docker daemon。需要注意的是!这个符号的用法:

*.md                       //去掉所有md结尾的文件
!README*.md          //README开头的md文件不去除
README-secret.md  // README-secret.md作为特例要去除

这里表示去掉所有markdown文件。但是不去除README相关的文件,除了README-secret.md这个文件

FROM

三种格式:

FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM开始一个构建阶段,并且指定后面刚好构建指令的基础镜像

  • FROM可以在一个dokcerfile里面出现多次,构建多个构建镜像。只要在下一次FROM指令之前记住上一次image ID,两次构建就可以相互用来。每次FROM指令开始会清除上一次的指令
  • AS name是可选的,如果使用了,可以在下一次的FROM使用 COPY --from=<name>来关联上一次的构建镜像
  • tagdigest也是可选的,默认为latest

ARG和FROM的交互

FROM的变量是通过ARG来定义的,所以ARG是有可能先于FROM执行的,但是此时ARG是在构建阶段之外定义的,只能在FROM指令里面使用,在其他指令使用需要在执行一次没有值的ARG

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION     //空值ARG取上一次ARG的值
RUN echo $VERSION > image_version

RUN

两种格式:

RUN <command>(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指令将在当前layer顶部的新layer执行命令,提交结果,生成的镜像将用于dockerfile下一步
例:

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]  //execform 是解析称json数组的,所以只能用",不能用'

execform不会调用shell,如RUN["echo,"HOME"],HOME"],不会在HOME上进行变量替换。要执行shell,需要这样:RUN [ "sh", "-c", "echo $HOME" ]。当使用execform并直接执行shell时(例如在shell表单中),是由shell进行环境变量扩展,而不是docker。
RUN指令的缓存不会自动失效。 诸如RUN apt-get dist-upgrade -y之类的指令的存将在下一个构建期间重用。 可以使用--no-cache标志使RUN指令的缓存无效,例如docker build --no-cache

CMD

三种格式:

CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT) //使用JSON数组格式指定,即只能使用 ",不能使用'
CMD command param1 param2 (shell form)   //在/ bin / sh -c中执行

CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过, CMD指定的命令其可以被 docker run的命令行选项所覆盖 .在Dockerfile中可以存在多个 CMD指令,但仅最后一个会生效
如果用户为docker run指定了参数,则它们将覆盖CMD中指定的默认值

RUN和CMD的区别

Do not confuse RUN with CMD. RUN actually runs a command and commits the result; CMD does not execute anything at build time, but specifies the intended command for the image.
RUN执行了命令并提交了结果,但是CMD在build期间没有执行热河东西,但是为镜像指定了预期的命令

ENTRYPOINT

两种格式:

ENTRYPOINT ["executable", "param1", "param2"] (exec form)
ENTRYPOINT command param1 param2 (shell form)

类似 CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序

与CMD不同的是,由 ENTRYPOINT启动的程序不会被 docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给 ENTRYPOINT指定指定的程序 ,ocker run 命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后做为其参数使用, docker run命令的 --entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序
Dockerfile文件中也可以存在多个 ENTRYPOINT指令,但仅有最后一个会生效

CMD和ENTYYPOINT交互

Dockerfile应该指定CMD或ENTRYPOINT命令中的至少一个。
使用容器作为可执行文件时,应定义ENTRYPOINT。
CMD应该用作定义ENTRYPOINT命令或在容器中执行临时命令的默认参数的方式。
下表显示了针对不同ENTRYPOINT / CMD组合执行的命令:

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /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

COPY

两种形式

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]  //在路径中有空白字符时通常使用此形式

--chown仅支持用户构建linux容器的dockerfile
<src>:要复制的源文件或目录,支持使用通配符
<dest>:目标路径,即正在创建的 image的文件系统路径;建议为 <dest>使用绝对路径,<dest>绝对路径为镜像中的路径,而不是宿主机的路径。否则, COPY指定则以 WORKDIR为其起始路径

  • 文件复制准则
    1. <src>必须是build上下文中的路径,即只能放在workshop这个工作目录下,不能是其父目录中的文件
    2. 如果<src>是目录,其内部文件或者子目录会被递归复制,但<src>目录自身不会被复制
    3. 如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且<dest>目录必须以/结尾
    4. 如果<dest>事先不存在,它将会被自动创建,这包括其父目录路径
      例:

[root@node1 ~]# mkdir img1/
[root@node1 ~]# cd img1/
[root@node1 img1]# ls
[root@node1 img1]# vim index.html
<h1>twfr.github.io </h1>
[root@node1 img1]# vim Dockerfile
# Description: test image
FROM busybox:latest
MAINTAINER "tanxin <lovegood.xin@gmail.com>"
COPY index.html /data/web/html/

ADD

两种形式:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

ADD指令类似于COPY指令, ADD支持使用 TAR文件和 URL路径

  • 文件操作准则,同COPY,另外:
    1. 如果<src>为URL且<dest>不以/结尾,则<src>指定的文件将被下载并直接被创建为<dest>;如果<dest>以/结尾,则文件名URL指定的文件将被直接下载,并保存为<dest>/<filename>,注意,URL不能是ftp格式的url
    2. 如果<src>是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于“tar -x”命令,然后,通过URL获取到的tar文件将不会自动展开
    3. 如果<src>有多个,或其间接或直接使用了通配符,则<dest>必须是一个以/结尾的目录路径;如果<dest>不以/结尾,则其被视作一个普通文件,<src>的内容将被直接写入到<dest>

COPY与ADD的区别

ADD支持使用 TAR文件和 URL路径,支持将tar格式的压缩文件解压到指定的目录

LABEL

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

LABEL version="1.0" description="这是一个github pages" by="blog"

WORKDIR

WORKDIR /path/to/workdir

WORKDIR为工作目录,可以出现多次,路径也可以为相对路径,不过,其是相对此前一个 WORKDIR指令指定的路径,还可以用由ENV定义的变量

VOLUME

VOLUME ["/data"]

定义卷,只能是docker管理的卷,,VOLUME为容器上的目录,用于在 image中创建一个挂载点目录,以挂载 Docker host上的卷或其它容器上的卷

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

暴露指定端口,用于为容器打开指定要监听的端口以实现与外部通信

<protocol>用于指定传输层协议,可为 tcp或udp二者之一,默认为 TCP协议

EXPOSE指令可一次指定多个端口,但是不能指定暴露为宿主机的指定端口,因为指定的宿主机端口可能已经被占用,因此这里使用随机端口

[root@node1 img1]# vim Dockerfile 
EXPOSE 80/tcp
[root@node1 ~]# docker run --name tinyweb1 --rm tinyhttpd:v0.1-6 /bin/httpd -f -h data/web/html

[root@node1 ~]# docker inspect tinyweb1                                                                                                                                         "IPAddress": "172.17.0.2",  

[root@node1 ~]# curl 172.17.0.2
<h1>twfr.github.io </h1>
[root@node1 ~]# docker port tinyweb1   //没有端口信息   即没有正真暴露出来

启动并暴露端口,注意,启动容器要跟大写P选项-P来暴露

[root@node1 ~]# docker run --name tinyweb1 --rm -P  tinyhttpd:v0.1-6 /bin/httpd -f -h /data/web/html

[root@node1 ~]# curl 172.17.0.2
<h1>twfr.github.io</h1>
[root@node1 ~]# docker port tinyweb1
80/tcp -> 0.0.0.0:32768

ENV

ENV <key>=<value> ...

有些变量在运行为容器时依然有用,因此需要把那些变量在运行为容器时重新定义为一个新的值,如果变量很多,可以放到一个文件中进行定义,使用参数 --env-list(docker run --help )实现,通过文件来加载环境变量

[root@node1 ~]# docker run --name tinyweb1 --rm -P -e WEB_SERVER_PACKAGE="nginx-1.15-6" tinyhttpd:v0.1-7 printenv
WEB_SERVER_PACKAGE=nginx-1.15-6

ARG

ARG <name>[=<default value>]

ARG用于指定传递给构建运行时的变量

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
docker build --build-arg CONT_IMG_VER=v2.0.1 .

STOPSIGNAL

STOPSIGNAL signal

STOPSIGNAL用于设置停止容器所要发送的系统调用信号:
所使用的信号必须是内核系统调用表中的合法的值,如:SIGKILL

USER

USER用于指定运行 image时的或运行 Dockerfile中任何 RUN、CMD或 ENTRYPOINT指令指定的程序时的用户名或 UID ,即改变容器中运行程序的身份
默认情况下, container的运行身份为 root用户
以下形式都是ok的:

USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group

ONBUILD

ONBUILD <INSTRUCTION>

当将映像用作另一个构建的基础时,ONBUILD指令会将触发指令添加到映像中,以便稍后执行。触发器将在下游构建的上下文中执行,就像它已被插入到下游Dockerfile中的FROM指令之后一样。

dockerfile原则和建议

  1. 容器轻量化。从镜像中产生的容器应该尽量轻量化,能在足够短的时间内停止、销毁、重新生成并替换原来的容器。
  2. 使用 .gitignore。在大部分情况下,Dockerfile 会和构建所需的文件放在同一个目录中,为了提高构建的性能,应该使用 .gitignore 来过滤掉不需要的文件和目录。
  3. 为了减少镜像的大小,减少依赖,仅安装需要的软件包。
  4. 一个容器只做一件事。解耦复杂的应用,分成多个容器,而不是所有东西都放在一个容器内运行。如一个 Python Web 应用,可能需要 Server、DB、Cache、MQ、Log 等几个容器。一个更加极端的说法:One process per container。
  5. 减少镜像的图层。不要多个 Label、ENV 等标签。
  6. 对续行的参数按照字母表排序,特别是使用apt-get install -y安装包的时候。
  7. 使用构建缓存。如果不想使用缓存,可以在构建的时候使用参数--no-cache=true来强制重新生成中间镜像。

docker生命周期

镜像

Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像(Image)就是一堆只读层(read-only layer)的统一视角,这些层是Docker 内部的实现细节,并且能够在主机的文件系统上访问到。统一文件系统 (union file system) 技术能够将不同的层整合成一个文件系统,为这些层提供了一个统一的视角,这样就隐藏了多层的存在,在用户的角度看来,只存在一个文件系统。

容器

容器 (container) 的定义和镜像 (image) 几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
由于容器的定义并没有提及是否要运行容器,所以实际上,容器 = 镜像 + 读写层。

仓库

存放镜像的地方

docker架构

核心组件包括:

  • Docker Client
    客户端可以构建,运行和停止应用程序,还可以远程与Docker_Host进行交互。最常用的 Docker 客户端就是 docker 命令,我们可以通过 docker 命令很方便地在 host 上构建和运行 docker 容器。
  • Docker daemon
    Docker daemon 是服务器组件,以 Linux 后台服务的方式运行,是 Docker 最核心的后台进程,我们也把它称为守护进程。它负责响应来自 Docker Client 的请求,然后将这些请求翻译成系统调用完成容器管理操作。该进程会在后台启动一个 API Server ,负责接收由 Docker Client 发送的请求,接收到的请求将通过Docker daemon 内部的一个路由分发调度,由具体的函数来执行请求。
    docker daemon又可以分为docker server engine和job三部分
    ocker Daemon 可以认为是通过 Docker Server 模块接受 Docker Client 的请求,并在 Engine 中处理请求,然后根据请求类型,创建出指定的 Job 并运行。 Docker Daemon 运行在 Docker host 上,负责创建、运行、监控容器,构建、存储镜像。
  • Docker Image
  • Docker Registry
  • Docker Container
    容器启动的过程大致如下:
  1. Docker 客户端执行 docker run 命令
  2. Docker daemon 发现本地没有我们需要的镜像
  3. daemon 从指定的镜像仓库下载镜像
  4. 下载完成后,镜像被保存到本地
  5. Docker daemon 启动容器