本文目录
- 1. 什么是 Dockerfile
- 2. Dockerfile 基本命令
- 2.1 FROM 指定基础镜像
- 2.2 RUN 执行命令
- 2.3 COPY 复制文件
- 2.4 ADD 更高级的复制文件
- 2.5 CMD
- 2.6 ENTRYPOINT
- 2.7 ENV 设置环境变量
- 2.8 ARG
- 2.9 VOLUME 定义匿名卷
- 2.10 EXPOSE
- 2.11 WORKDIR 指定工作目录
- 2.12 USER 指定当前用户
- 2.13 HEALTHCHECK
- 2.14 ONBUILD
- 2.15 LABEL
- 2.16 STOPSIGNAL
- 2.17 SHELL
- 3. Ubuntu 实践操作
- 3.1 创建 DockerFile 文件
- 3.2 进入文件
- 3.3 输入文件内容
- 3.4 生成镜像
- 3.5 验证镜像是否生成成功
1. 什么是 Dockerfile
Dockerfile
是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。- 在一个文件夹中,如果有一个名字为
Dockfile
的文件,其内容满足语法要求,在这个文件夹路径下执行命令:docker build --tag name:tag .
,就可以按照描述构建一个镜像了。name
是镜像的名称,tag
是镜像的版本或者是标签号,不写的话默认为lastest
。注意后面有一个空格和点。
2. Dockerfile 基本命令
2.1 FROM 指定基础镜像
FROM
指令用于指定其后构建新镜像所使用的基础镜像。FROM
指令必是Dockerfile
文件中的首条命令,启动构建流程后,Docker
将会基于该镜像构建新镜像,FROM
后的命令也会基于这个基础镜像。- 语法格式如下:
FROM <image>
# 或
FROM <image>:<tag>
# 或
FROM <image>:<digest>
- 通过
FROM
指定的镜像,可以是任何有效的基础镜像。FROM
有以下限制:FROM
必须 是Dockerfile
中第一条非注释命令。- 在一个
Dockerfile
文件中创建多个镜像时,FROM
可以多次出现。只需在每个新命令FROM
之前,记录提交上次的镜像 ID。 tag
或digest
是可选的,如果不使用这两个值时,会使用latest
版本的基础镜像。
2.2 RUN 执行命令
RUN
指令在镜像的构建过程中执行特定的命令,并生成一个中间镜像。- 语法格式如下(有两种格式):
shell
格式:
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。
exec
格式:
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
RUN
命令将在当前image
中执行任意合法命令并提交执行结果。命令执行提交后,就会自动执行Dockerfile
中的下一个指令。- 层级
RUN
指令和生成提交是符合Docker
核心理念的做法。它允许像版本控制那样,在任意一个点,对image
镜像进行定制化构建。 RUN
指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache
参数,如:docker build --no-cache
。
注意:
Dockerfile
的指令每执行一次都会在 docker
上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:
FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:
FROM centos
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。
2.3 COPY 复制文件
- 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
- 语法格式如下:
COPY [--chown=<user>:<group>] <源路径1>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
参数说明:
-
[--chown=<user>:<group>]
:可选参数,用户改变复制到容器内文件的拥有者和属组。 -
<源路径>
:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目标路径>
:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
补充说明:
COPY
和 RUN
指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY
指令将从构建上下文目录中 <源路径>
的文件/目录复制到新的一层的镜像内的 <目标路径>
位置。比如:
COPY package.json /usr/src/app/
<源路径>
可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:
COPY hom* /mydir/
COPY hom?.txt /mydir/
<目标路径>
可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
此外,还需要注意一点,使用 COPY
指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
2.4 ADD 更高级的复制文件
-
ADD
指令和COPY
的格式和性质基本一致(同样需求下,官方推荐使用COPY
)。但是在COPY
基础上增加了一些功能。比如<源路径>
可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到<目标路径>
去。 -
在构建镜像时,复制上下文中的文件到镜像内,语法格式如下:
ADD <源路径1>... <目标路径>
ADD ["<源路径1>",... "<目标路径>"]
ADD
的优点:在执行<源文件>
为tar
压缩文件的话,压缩格式为gzip, bzip2
以及xz
的情况下,会自动复制并解压到<目标路径>
。ADD
的缺点:在不解压的前提下,无法复制tar
压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。- 注意:如果
docker
发现文件内容被改变,则接下来的指令都不会再使用缓存。关于复制文件时需要处理的/,基本跟正常的 copy 一致。
2.5 CMD
-
类似于
RUN
指令,用于运行程序,但二者运行的时间点不同:CMD
在docker run
时运行,在构建时不进行任何操作。RUN
是在docker build
,并生成一个新的镜像。
-
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。
CMD
指令指定的程序可被docker run
命令行参数中指定要运行的程序所覆盖。 -
注意:如果
Dockerfile
中如果存在多个CMD
指令,仅最后一个生效。 -
语法格式如下(有三种格式):
CMD <shell 命令>
CMD ["<可执行文件或命令>","<param1>","<param2>",...]
CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh
。
2.6 ENTRYPOINT
-
ENTRYPOINT
用于给容器配置一个可执行程序。也就是说,每次使用镜像创建容器时,通过ENTRYPOINT
指定的程序都会被设置为默认程序。 -
类似于
CMD
指令,但其不会被docker run
的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给ENTRYPOINT
指令指定的程序。 -
但是,如果运行
docker run
时使用了--entrypoint
选项,将覆盖 ENTRYPOINT 指令指定的程序。 -
优点:在执行
docker run
的时候可以指定ENTRYPOINT
运行所需的参数。 -
注意:如果
Dockerfile
中如果存在多个ENTRYPOINT
指令,仅最后一个生效。 -
语法格式如下:
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
可以搭配 CMD
命令使用:一般是变参才会使用 CMD
,这里的 CMD
等于是在给 ENTRYPOINT
传参,下面的示例会提到。
- 示例:
假设已通过 Dockerfile
构建了 nginx:test 镜像:
FROM nginx
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.7 ENV 设置环境变量
-
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
-
语法格式如下(有两种格式):
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
- 这个指令很简单,就是设置环境变量而已,无论是后面的其它指令,如
RUN
,还是运行时的应用,都可以直接使用这里定义的环境变量。
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"
这个例子中演示了如何换行,以及对含有空格的值用双引号括起来的办法,这和 Shell 下的行为是一致的。
2.8 ARG
- 构建参数,与
ENV
作用一致。不过作用域不一样。ARG
设置的环境变量仅对Dockerfile
内有效,也就是说只有docker build
的过程中有效,构建好的镜像内不存在此环境变量。 - 构建命令
docker build
中可以用--build-arg <参数名>=<值>
来覆盖。 - 语法格式如下:
ARG <参数名>[=<默认值>]
例如,通过 ARG
指定两个变量:
ARG site
ARG build_user=IT笔录
以上我们指定了 site
和 build_user
两个变量,其中 build_user
指定了默认值。在使用 docker build
构建镜像时,可以通过 --build-arg <varname>=<value>
参数来指定或重设置这些变量的值。
docker build --build-arg site=itiblu.com -t itbilu/test .
这样我们构建了 itbilu/test
镜像,其中 site
会被设置为 itbilu.com
,由于没有指定 build_user
,其值将是默认值 IT 笔录。
2.9 VOLUME 定义匿名卷
-
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
-
作用:
- 避免重要的数据,因容器重启而丢失,这是非常致命的。
- 避免容器不断变大。
-
语法格式如下:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
在启动容器 docker run
的时候,我们可以通过 -v
参数修改挂载点。
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
VOLUME 让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容。
2.10 EXPOSE
- 为构建的镜像设置监听端口,使容器在运行时监听(仅仅只是声明端口)。
- 作用:
- 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
- 在运行时使用随机端口映射时,也就是
docker run -P
时,会自动随机映射EXPOSE
的端口。
- 语法格式如下:
EXPOSE <port> [<port>...]
EXPOSE
指令并不会让容器监听host
的端口,如果需要,需要在docker run
时使用-p
、-P
参数来发布容器端口到host
的某个端口上。
2.11 WORKDIR 指定工作目录
- 指定工作目录。用
WORKDIR
指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR
会帮你建立目录。 docker build
构建镜像过程中的,每一个RUN
命令都是新建的一层。只有通过WORKDIR
创建的目录才会一直存在。- 语法格式如下:
WORKDIR <工作目录路径>
- 通过
WORKDIR
设置工作目录后,Dockerfile
中其后的命令 RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。
例如,使用 WORKDIR
设置工作目录:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
在以上示例中,pwd 最终将会在 /a/b/c
目录中执行。在使用 docker run
运行容器时,可以通过 -w
参数覆盖构建时所设置的工作目录。
2.12 USER 指定当前用户
-
用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。
-
语法格式如下:
USER <用户名>[:<用户组>]
- 使用
USER
指定用户时,可以使用用户名、UID 或 GID,或是两者的组合。以下都是合法的指定:
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
- 使用
USER
指定用户后,Dockerfile
中其后的命令 RUN、CMD、ENTRYPOINT 都将使用该用户。镜像构建完成后,通过 docker run 运行容器时,可以通过-u
参数来覆盖所指定的用户。
2.13 HEALTHCHECK
- 用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
- 语法格式如下:
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
2.14 ONBUILD
ONBUILD
用于设置镜像触发器。- 语法格式如下:
ONBUILD <其它指令>
- 当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。
如,当镜像被使用时,可能需要做一些处理:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
2.15 LABEL
LABEL
用于为镜像添加元数据(metadata),元数以键值对的形式指定。- 语法格式如下:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
使用 LABEL
指定元数据时,一条 LABEL
指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条 LABEL
指令指定,以免生成过多的中间镜像。
例如,通过 LABEL
指定一些元数据:
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
指定后可以通过 docker inspect
查看:
docker inspect itbilu/test
"Labels": {
"version": "1.0",
"description": "这是一个Web服务器",
"by": "IT笔录"
},
2.16 STOPSIGNAL
STOPSIGNAL
用于设置停止容器所要发送的系统调用信号。- 语法格式如下:
STOPSIGNAL signal
所使用的信号必须是内核系统调用表中的合法的值,如:SIGKILL。
2.17 SHELL
SHELL
用于设置执行命令(shell式)所使用的的默认shell
类型。- 语法格式如下:
SHELL ["executable", "parameters"]
SHELL
在 Windows 环境下比较有用,Windows 下通常会有cmd
和powershell
两种shell
,可能还会有sh
。这时就可以通过SHELL
来指定所使用的shell
类型:
FROM microsoft/windowsservercore
# Executed as cmd /S /C echo default
RUN echo default
# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default
# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello
# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
3. Ubuntu 实践操作
3.1 创建 DockerFile 文件
- 首先创建一个空文件夹单独存放
Dockerfile
文件,然后创建一个Dockerfile
文件。命令如下:
mkdir mydocker
cd mydocker
touch Dockerfile
3.2 进入文件
命令如下:
sudo vim Dockerfile
注意:
有的虚拟机可能没有安装 vim
,导致会出现下面这样的错误:
sudo: vim: command not found
这个时候,可以输入下面这条命令进行下载:
sudo apt-get install vim-gtk
进入文件后的界面如下:
【补充文件相关知识】:文件编辑好之后如何进行退出?
- 【要退出,但是不保存】:先按
Esc
,然后输入:q
,按回车键即可退出。无法退出,则在:q
加入一个叹号!
,再按回车键就行了,即!q!
。 - 【要退出,保存】:先按
Esc
,输入:wq
,按回车键即可退出。强制保存退出,则输入:wq!
。 - 【以
nano
开头编辑文件的要退出】:按Ctrl+x
,回车键。如要保存的,则先按Ctrl+o
,再按x
退出。
3.3 输入文件内容
#基础镜像
FROM ubuntu
#维护者信息
MAINTAINER carson carson@email.com
#镜像的操作指令
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
#容器启动时的执行命令
CMD /user/sbin/nginx
【补充知识】:
在 Dockerfile
中添加维护者信息是可选的,它用于指定镜像的作者或维护人员。维护者信息可以帮助其他人了解镜像的来源和相关信息。以下是在 Dockerfile
中添加维护者信息的语法和示例:
- 语法:
MAINTAINER <作者或维护者的姓名和邮箱>
- 示例:
FROM ubuntu:latest
MAINTAINER John Doe <john.doe@example.com>
# 添加其他指令
...
在示例中,我们使用 FROM
指令指定基础镜像为最新版本的 Ubuntu
。然后,使用 MAINTAINER
指令指定了维护者的姓名和邮箱地址。
注意:从 Docker 1.13
版本开始,MAINTAINER
指令已经被标记为过时(deprecated)。推荐使用 LABEL
指令来添加维护者信息和其他元数据。下面是使用 LABEL
指令的示例:
FROM ubuntu:latest
LABEL maintainer="John Doe <john.doe@example.com>"
# 添加其他指令
...
使用 LABEL
指令的好处是可以添加多个标签,包括维护者信息和其他自定义元数据,使镜像更丰富和可理解。
总结起来,可以选择使用 MAINTAINER
或 LABEL
指令来添加维护者信息,具体选择取决于使用的 Docker
版本和个人偏好。
3.4 生成镜像
- 编辑完
Dockerfile
后使用docker build
指令生成镜像:
docker build -t myngix .
运行结束之后的截图:
3.5 验证镜像是否生成成功
输入下面的命令:
# 普通用户涉及权限问题,在该命令之前加上 sudo 即可
docker images
由上面的截图可知,镜像生成成功!