1、镜像概念

1.1、基于容器生成镜像

Docker构建镜像 _docker

通过 docker commit 命令将现有的容器提交来生成新的镜像。
原理:容器启动后的修改都保存在可写层,通过对可写层的修改生成新的镜像。

[root@hqs docker-hello]# docker commit --help
Usage:  docker commit [OPTIONS选项] CONTAINER容器 [REPOSITORY仓库名[:TAG标签]]
Create a new image from a containers changes
Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")    # 指定作者
  -c, --change list      Apply Dockerfile instruction to the created image             # 允许使用dockerfile的指令
  -m, --message string   Commit message                                  # 提交信息
  -p, --pause            Pause container during commit (default true)    # 创建镜像过程中容器暂停(挂起)

基于容器生成镜像无法重复、构建缺乏透明性和体积偏大的问题,因此不推荐使用这种方式构建镜像。

1.2、Dockerfile 构建镜像

Dockerfile是由一系列指令和参数构成的脚本每一条指令构建一层,因此每一条指令的内容就是描述该层应当如何构建,一个Dockerfile包含了构建镜像的完整指令。

Dockerfile就是一个脚本来构建和定制镜像,把每一层的修改、安装、构建、操作都写入脚本。以此来解决体积、镜像构建透明等问题。

Dockerfile是一个文本文件,包含一条条指令(Instruction),每一条指令构建一层,每一条指令的内容,就是描述该层应当如何构建。

1.3、docker build命令

基于 dockerfile 构建镜像使用 docker build 命令。命令是通过 Dockerfile文件 和 构建上下文(Build Context) 来构建镜像的。
注意:

  1. 不要把多余的文件放到构建上下文中————一般就要创建一个空目录作为构建上下文
  2. 一般将 Dockerfile直接命名为 Dockerfile,并放在上下文根目录,否则构建找不到(可以用-f指定)
  3. Dockerfile的每个指令都被独立执行并创建一个新镜像。
# 语法
[root@localhost docker]# docker build --help
Usage:  docker build [OPTIONS选项] PATH路径 | URL | -
Build an image from a Dockerfile
Options:
      --add-host list           Add a custom host-to-IP mapping (host:ip)                    # 添加映射
      --build-arg list          Set build-time variables                                     # 设置镜像创建时的变量
      --cache-from strings      Images to consider as cache sources
      --cgroup-parent string    Optional parent cgroup for the container
      --compress                Compress the build context using gzip                        # 压缩构建的内容
      --cpu-period int          Limit the CPU CFS (Completely Fair Scheduler) period         # 限制 CPU CFS周期
      --cpu-quota int           Limit the CPU CFS (Completely Fair Scheduler) quota          # 限制 CPU CFS配额
  -c, --cpu-shares int          CPU shares (relative weight)                                 # 设置cpu使用权重
      --cpuset-cpus string      CPUs in which to allow execution (0-3, 0,1)                  # 指定使用的CPU id
      --cpuset-mems string      MEMs in which to allow execution (0-3, 0,1)                  # 指定使用的内存 id
      --disable-content-trust   Skip image verification (default true)                       # 忽略校验,默认开启
  -f, --file string             Name of the Dockerfile (Default is 'PATH/Dockerfile')        # 设置Dockerfile名字
      --force-rm                Always remove intermediate containers                        # 总是删除中间容器
      --iidfile string          Write the image ID to the file                               
      --isolation string        Container isolation technology                               # 使用容器隔离技术
      --label list              Set metadata for an image                                    # 设置镜像使用的元数据
  -m, --memory bytes            Memory limit                                                 # 内存限制
      --memory-swap bytes       Swap limit equal to memory plus swap: '-1' to enable unlimited swap     # 设置Swap的最大值为内存+swap,"-1"表示不限swap
      --network string          Set the networking mode for the RUN instructions during build (default "default")    # 在构建期间设置RUN指令网络模式
      --no-cache                Do not use cache when building the image                     # 构建镜像不使用缓存
      --pull                    Always attempt to pull a newer version of the image          # 总是尝试拉取新版本镜像
  -q, --quiet                   Suppress the build output and print image ID on success      # 安静模式,成功后只打印镜像ID
      --rm                      Remove intermediate containers after a successful build (default true)      # 构建成功后删除中间容器
      --security-opt strings    Security options                                             # 安全选项
      --shm-size bytes          Size of /dev/shm                                             # /dev/shm大小
  -t, --tag list                Name and optionally a tag in the 'name:tag' format           # 镜像的名字及标签
      --target string           Set the target build stage to build.                         # 目标构建阶段设为build
      --ulimit ulimit           Ulimit options (default [])                                  # ?

# 最简案例
docker build .

1.4、Dockerfile格式

指令:

  1. 指令不区分大小写,建议大写。
  2. 指令可以指定若干参数。
  3. Docker按顺序执行指令。
  4. Dockerfile文件必须以 FROM 指令开头。

注释:

  1. # 号开头的行都将被视为注释。解析器指令除外。
  2. 行中其他位置的 # 视为参数的一部分(不视为注释)。

解析器指令:

  1. 不添加镜像,也不会出现在构建步骤。
  2. 语法:# 指令 = 值,e.g:escape=\
  3. 一旦注释、空行、解析器指令被处理,不再搜寻解析器指令,全格式化为注释。————》解析器指令必须在Dockerfile的首部。

1.5、.dockerignore文件

可以添加.dockerignore 文件到构建上下文中来定义要排除的文件和目录。
用途:构建镜像时,在将构建上下文传给docker daemon进程时,命令行接口就修改了上下文以排除匹配的文件或目录。有助于避免发送大型文件或敏感文件。

使用要点:

  1. 解释为换行符分隔的模式列表(一行一行读取)
  2. 支持特殊通配符 **,匹配任意数量的目录。

2、Dockerfile的指令

2.1、FROM指令

FROM指令为后续指令设置基础镜像。

指令写法:

# 不带标签和摘要值,默认选择latest标签的镜像
FROM <镜像>
# 案例:FROM ubuntu

# 带标签,指定仓库中对应标签版本的镜像
FROM <镜像>:<标签>
# 案例:FROM ubuntu:16.04

# 带摘要值,指定仓库中对应的镜像
FROM <镜像>@<摘要值>
# 案例:FROM ubuntu@sha256:bea6d19168bbfd6af8d77c2cc3c572114eb5d113e6f422573c93cb605a0e2ffb

# AS语法指定名称,用处:在后期可以引用此阶段构建的镜像
FROM <镜像> AS <名称>
# 案例:FROM ubuntu:16.04 AS hqs-ubuntu

2.2、RUN指令

RUN指令在当前镜像顶部创建新的层,执行定义的命令并提交结果。结果产生的镜像由Dockerfile进一步处理。
RUN 指令是用来执行命令行命令的。Run指令在定制镜像时是最常用的指令之一。

注意:

  1. 多少个RUN就构建多少层镜像,多个RUN会造成镜像臃肿,尽量将要执行的命令都写在一个RUN里面。
  2. 多条命令执行可以使用 &&,还可以用 \ 来换行
  3. 执行到最后还需要将不需要的文件和目录删除
  4. Union FS有最大层数限制,如AUFS之前最大不得超过42层,现在是不得超过127层。

(1)shell格式

像直接在命令行输入命令一样。

# 语法格式
RUN <命令>

# 案例
RUN echo '<h1>Hello,Docker!</h1>' > /usr/share/nginx/html/index.html

# 可以用反斜杠将单个RUN指令换行
RUN /bin/bash -c 'source $HOME/.bashrc;\
echo $HOME'

(2)exec格式

更类似函数调用的格式。

# 语法格式
RUN ["可执行文件(程序)", "参数1", "参数2"]

# 案例
RUN ["/bin/bash", "-c", "echo hello"]

2.3、CMD指令

CMD 指令一般时整个 Dockerfile 的最后一行。
主要作用是:

  1. 为运行中的容器docker run提供默认值。
  2. 当Dockerfile 完成所有的环境安装和配置后,CMD指示容器启动时要执行的命令。

注意:Dockerfile中只能有一条CMD命令,如果写了多条则最后一条生效。

(1)exec格式

# 语法
CMD ["可执行文件(程序)", "参数1", "参数2"]

# 案例
CMD ["sh", "-c", "echo $HOME"]

(2)ENTRYPOINT指令默认参数

# 语法
CMD ["参数1", "参数2"...]

# 案例??
CMD ["/usr/bin/wc","--help"]

(3)shell格式

# 语法
CMD <命令> 参数1  参数2

# 案例
CMD echo "this is test" | wc -

2.4、LABEL指令

LABEL 指令向镜像添加标记。即:以键值对的形式给镜像添加一些元数据(metadata)。
其中包含空格,应该使用引号和反斜杠。
一个镜像可以定义多个标记,每个LABEL指令都会产生一层镜像,因此尽量合并在一个LABLE指令里。

# 语法
LABEL <键>=<值> <键>=<值> <键>=<值>...

# 案例
LABEL version="1.0"

# 换行案例
LABEL description="asdasdsad \
标记使用多行"

# 合并标记
LABEL org.opencontainers.image.authors="runoob" version="1.2" description="test"

2.5、EXPOSE指令

EXPOSE指令通知(声明)容器在运行时监听指定的网络端口。
可以指定TCP或UDP端口,默认是TCP端口。

注意:EXPOSE指令不会发布端口,只能声明端口,发布端口,要在运行容器时用 -p-P 选项发布。

# 语法
EXPOSE <端口>[<端口>....]

# 案例

2.6、ENV指令

ENV 指令以键值对的形式定义环境变量。该值会存在于构建镜像阶段后续指令环境中。

# 第一种写法(只支持单个变量设置)
ENV <键> <值>
# 案例:
# 注意空格,空格后的整个字符都被视为值


# 第二种写法(支持单个、多个变量设置)
ENV <键>=<值> ....
# 案例

2.7、COPY指令

COPY 指令将指定源路径的文件或目录复制到容器文件系统指定的目的路径中。

# 语法
COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

# 选项--chown:改变复制到容器内文件的用户和用户组(默认是UID和GID都为0的用户和组)
# 选项--from=<name|index>:将源位置改为之前构建阶段产生的镜像,可以用名字或数字索引来标识

# <源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。
# <目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

# 通配符案例:
COPY hom* /mydir/         # 添加所有hom开头文件
COPY hom?.txt /mydir/     # ?替换任意单个字符

# 绝对路径案例:
COPY test /root/absoluteDir/

# 相对路径案例:
COPY test relativeDir/

COPY指令遵守的复制规则:

  1. 源路径必须位于构建上下文中,不能使用指令COPY ../something/something,因为docker build命令的第1步是发送上下文目录及其子目录到Docker守护进程中。
  2. 如果复制的源是目录,则复制目录的整个内容,包括文件系统元数据。
  3. 如果复制源是任何其他类型的文件,则它会与其元数据被分别复制。在这种情形下,如果目的路径以斜杠(/)结尾,则它将被认为是一个目录,源内容将被写到“<目的>/base(<源>)”路径中。
  4. 如果直接指定多个源,或者源中使用了通配符,则目的路径必须是目录,并且必须以斜杠(/)结尾。
  5. 如果目的路径不以斜杠结尾,则它将被视为常规文件,源内容将被写入目录路径。
  6. 如果目的路径不存在,则会与其路径中所有缺少的目录一起被创建。

2.8、ADD指令

ADD指令和 COPY指令类似,ADD指令的功能是将主机构建环境(上下文)目录中的文件和目录、或URL标记的文件拷贝到镜像中。

ADD指令和 COPY指令区别:

  1. ADD 指令可以使用URL地址指定。
  2. ADD 指令的归档文件在复制过程中能被自动解压缩。
# 语法
ADD [--chown=<user>:<group>] <源路径1>...  <目标路径>
ADD [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]

# 案例

注意:

  1. 源是远程URL地址时,复制产生的文件为600权限(-rw-------),即只有所有者有读写权限,其他用户无法访问。
  2. 源是远程URL地址且不以反斜杠结尾,则下载文件并复制到目的路径。
  3. 源是远程URL地址且以反斜杠结尾,解析出文件名,并下载到"<源>/<文件名>"路径中。
  4. 源是可识别的压缩格式(gzip、bzip2等)的本地tar文件,解压为目录。
  5. 源是远程URL地址,资源不会被解压缩。

2.9、ENTRYPOINT指令

ENTRYPOINT 指令配置容器的默认入口点,也是配置容器运行的可执行文件。

类似于 CMD 指令,但其一般不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

用户使用 docker run --entrypoint 命令可以覆盖ENTRYPOINT指令。

(1)exec格式

docker run 的命令行参数将附加在exec格式ENTRYPOINT指令后,覆盖CMD指令。

exec格式ENTRYPOINT可以与CMD搭配使用:exec格式的ENTRYPOINT指令设置默认命令和参数,使用CMD来设置更容易修改的其他默认值。

# 语法
ENTRYPOINT ["可执行文件(程序)", "参数1", "参数2"]

# exec格式ENTRYPOINT与CMD搭配使用示例:
FROM ubuntu
ENTRYPOINT ["nginx", "-c"]      # 定参
CMD ["/etc/nginx/nginx.conf"]   # 变参 

# 1、不传参运行
$ docker run  nginx:test
容器内会默认运行以下命令,启动主进程。
nginx -c /etc/nginx/nginx.conf

# 2、传参运行
$ docker run  nginx:test -c /etc/nginx/new.conf
容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
nginx -c /etc/nginx/new.conf

(2)shell格式

这种方式会在/bin/sh -c中执行,会忽略任何CMD或者docker run命令行选项。

缺点:ENTRYPOINT指令将作为 /bin/sh -c 的子命令启动,不传任何其他信息,不会接受系统信号,也不会接受docker stop中止信号。

为了确保 docker stop 能够停止长时间运行ENTRYPOINT的容器,推荐使用exec格式。

# 语法
ENTRYPOINT 命令 参数1  参数2

# shell格式ENTRYPOINT示例:
FROM ubuntu
ENTRYPOINT top -b
# 构建镜像
[root@localhost my-ubuntu]# docker build -t test_shell_entry .
Sending build context to Docker daemon   2.56kB
Step 1/2 : FROM ubuntu
latest: Pulling from library/ubuntu
4d32b49e2995: Pull complete 
Digest: sha256:bea6d19168bbfd6af8d77c2cc3c572114eb5d113e6f422573c93cb605a0e2ffb
Status: Downloaded newer image for ubuntu:latest
 ---> ff0fea8310f3
Step 2/2 : ENTRYPOINT top -b
 ---> Running in 09805224b170
Removing intermediate container 09805224b170
 ---> e84ae9ca3b7e
Successfully built e84ae9ca3b7e
Successfully tagged test_shell_entry:latest
# 查看镜像
[root@localhost my-ubuntu]# docker images
REPOSITORY         TAG       IMAGE ID       CREATED          SIZE
test_shell_entry   latest    e84ae9ca3b7e   44 seconds ago   72.8MB
ubuntu             latest    ff0fea8310f3   12 days ago      72.8MB
# 启动容器
[root@localhost my-ubuntu]# docker run -ti --name test test_shell_entry
top - 19:39:21 up  3:27,  0 users,  load average: 0.01, 0.06, 0.06
Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1982.6 total,    500.1 free,    646.7 used,    835.7 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   1172.9 avail Mem 
# docker stop 等了很久停止容器成功???


# 加入exec命令示例:
[root@localhost my-ubuntu]# cat Dockerfile 
FROM ubuntu
ENTRYPOINT exec top -b
[root@localhost my-ubuntu]# docker build -t top .
[root@localhost my-ubuntu]# docker run -it --name test_exec_entry top
top - 19:45:01 up  3:33,  0 users,  load average: 0.00, 0.02, 0.05
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1982.6 total,    498.3 free,    648.3 used,    836.0 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   1171.1 avail Mem 
# docker stop 迅速停止容器成功

注意:

  1. 如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
  2. 当指定了 ENTRYPOINT 后, CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令。
  3. 要确保 docker stop 能正确终止一直执行的 ENTRYPOINT,需要使用 exec 来启动命令。

2.10、VOLUME指令

创建一个可以从本地主机或其他容器访问的挂载点。

作用:

  1. 避免重要的数据,因容器重启而丢失,这是非常致命的。(数据保存在主机)
  2. 避免容器不断变大。(数据不在容器,不影响容器大小)
# 语法
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

# 案例
VOLUME ["/data"]

启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

2.11、WORKDIR指令

指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在(每一层都能访问)。

# 语法
WORKDIR <工作目录路径>

# 案例
WORKDIR /a
WORKDIR b
RUN pwd
# 最终路径为:/a/b


# 可与搭配ENV指令使用
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
# 最终路径则为 /path/$DIRNAME。

注意:

  1. 一个 Dockerfile 可以多次使用 WORKDIR 指令。
  2. 若提供相对路径,该路径将相对于前面的 WORKDIR 指令的路径。
  3. WORKDIR 指令可以在ENV设置变量之后调用环境变量

2.12、其他指令(USER\ARG\SHELL)

USER:设置运行镜像时使用的用户名(或UID)和可选的用户组(或GID),Dockerfile中的任何RUN、CMD和ENTRYPOINT指令也会使用这个指定的身份。

ARG:定义一个变量,用户可以在使用--build-arg = 标志执行docker build命令构建时将其传递给构建器。

SHELL:用于指定shell格式以覆盖默认的shell。