Docker中镜像构建文件Dockerfile与相关命令的详细介绍
前言
使用docker build
命令或使用docker hub
的自动构建功能构建docker镜像时,都需要一个dockerfile文件。dockerfile文件是一个由一系列构建指令组成的文本文件,docker build
命令会根据这些构建指令完成docker镜像的构建。本文将会介绍dockerfile文件,及其中使用的构建指令。
1. dockerfile文件使用
docker build
命令会根据dockerfile文件及上下文构建新docker镜像。构建上下文是指dockerfile所在的本地路径或一个url(git仓库地址)。构建上下文环境会被递归处理,所以,构建所指定的路径还包括了子目录,而url还包括了其中指定的子模块。
构建镜像
将当前目录做为构建上下文时,可以像下面这样使用docker build
命令构建镜像:
$ docker build . sending build context to docker daemon 6.51 mb ...
说明:构建会在docker后台守护进程(daemon)中执行,而不是cli中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将dockerfile文件放在该目录下。
在构建上下文中使用的dockerfile文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下,不需要的文件和目录。
dockerfile一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:
$ docker build -f /path/to/a/dockerfile .
构建时,还可以通过-t参数指定构建成后,镜像的仓库、标签等:
镜像标签
$ docker build -t shykes/myapp .
如果存在多个仓库下,或使用多个镜像标签,就可以使用多个-t参数:
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
在docker守护进程执行dockerfile中的指令前,首先会对dockerfile进行语法检查,有语法错误时会返回:
$ docker build -t test/myapp . sending build context to docker daemon 2.048 kb error response from daemon: unknown instruction: runcmd
缓存
docker 守护进程会一条一条的执行dockerfile中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的id。生成完成后,docker 守护进程会自动清理你发送的上下文。
dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,run cd /tmp
等命令不会对下条指令产生影响。
docker 会重用已生成的中间镜像,以加速docker build
的构建速度。以下是一个使用了缓存镜像的执行过程:
$ docker build -t svendowideit/ambassador . sending build context to docker daemon 15.36 kb step 1/4 : from alpine:3.2 ---> 31f630c65071 step 2/4 : maintainer svendowideit@home.org.au ---> using cache ---> 2a1c91448f5f step 3/4 : run apk update && apk add socat && rm -r /var/cache/ ---> using cache ---> 21ed6e7fbb73 step 4/4 : cmd env | grep _tcp= | (sed 's/.*_port_\([0-9]*\)_tcp=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 tcp4-listen:\1,fork,reuseaddr tcp4:\2:\3 \&/' && echo wait) | sh ---> using cache ---> 7ea8aef582cc successfully built 7ea8aef582cc
构建缓存仅会使用本地父生成链上的镜像。如果不想使用本地缓存的镜像,也可以通过--cache-from
指定缓存。指定后将再不使用本地生成的镜像链,而是从镜像仓库中下载。
2. dockerfile文件格式
dockerfile文件格式如下:
# comment instruction arguments
# 注释 指令 参数
dockerfile文件中指令不区分大小写,但为了更易区分,约定使用大写形式。
docker 会依次执行dockerfile中的指令,文件中的第一条指令必须是from,from指令用于指定一个基础镜像。
以#开头的行,docker会认为是注释。但#出现在指令参数中时,则不是注释。如:
# comment run echo 'we are running some # of cool things'
3. dockerfile中使用指令
3.1 from
from指令用于指定其后构建新镜像所使用的基础镜像。from指令必是dockerfile文件中的首条命令,启动构建流程后,docker将会基于该镜像构建新镜像,from后的命令也会基于这个基础镜像。
from语法格式为:
from <image>
或
from <image>:<tag>
或
from <image>:<digest>
通过from指定的镜像,可以是任何有效的基础镜像。from有以下限制:
- from必须是dockerfile中第一条非注释命令
- 在一个dockerfile文件中创建多个镜像时,from可以多次出现。只需在每个新命令from之前,记录提交上次的镜像id。
- tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
3.2 run
run用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行
在这种方式会在shell中执行命令,linux下默认使用/bin/sh -c
,windows下使用cmd /s /c
。
注意:通过shell命令修改run所使用的默认shell
run <command>
exec执行
run ["executable", "param1", "param2"]
run可以执行任何命令,然后在当前镜像上创建一个新层并提交。提交后的结果镜像将会用在dockerfile文件的下一步。
通过run执行多条命令时,可以通过\换行执行:
run /bin/bash -c 'source $home/.bashrc; \ echo $home'
也可以在同一行中,通过分号分隔命令:
run /bin/bash -c 'source $home/.bashrc; echo $home'
run指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache
参数,如:docker build --no-cache
。
3.3 cmd
cmd用于指定在容器启动时所要执行的命令。cmd有以下三种格式:
cmd ["executable","param1","param2"] cmd ["param1","param2"] cmd command param1 param2
cmd不同于run,cmd用于指定在容器启动时所要执行的命令,而run用于指定镜像构建时所要执行的命令。
cmd与run在功能实现上也有相似之处。如:
docker run -t -i itbilu/static_web_server /bin/true
等价于:
cmd ["/bin/true"]
cmd在dockerfile文件中仅可指定一次,指定多次时,会覆盖前的指令。
另外,docker run命令也会覆盖dockerfile中cmd命令。如果docker run
运行容器时,使用了dockerfile中cmd相同的命令,就会覆盖dockerfile中的cmd命令。
如,我们在构建镜像的dockerfile文件中使用了如下指令:
cmd ["/bin/bash"]
使用docker build
构建一个新镜像,镜像名为itbilu/test。构建完成后,使用这个镜像运行一个新容器,运行效果如下:
$ sudo docker run -i -t itbilu/test root@e3597c81aef4:/#
在使用docker run运
行容器时,我们并没有在命令结尾指定会在容器中执行的命令,这时docker就会执行在dockerfile的cmd中指定的命令。
如果不想使用cmd中指定的命令,就可以在docker run
命令的结尾指定所要运行的命令:
$ sudo docker run -i -t itbilu/test /bin/ps pid tty time cmd 1 ? 00:00:00 ps
这时,docker run
结尾指定的/bin/ps
命令覆盖了dockerfile的cmd中指定的命令。
3.4 entrypoint
entrypoint用于给容器配置一个可执行程序。也就是说,每次使用镜像创建容器时,通过entrypoint指定的程序都会被设置为默认程序。entrypoint有以下两种形式:
entrypoint ["executable", "param1", "param2"] entrypoint command param1 param2
entrypoint与cmd非常类似,不同的是通过docker run
执行的命令不会覆盖entrypoint,而docker run
命令中指定的任何参数,都会被当做参数再次传递给entrypoint。dockerfile中只允许有一个entrypoint命令,多指定时会覆盖前面的设置,而只执行最后的entrypoint指令。
docker run
运行容器时指定的参数都会被传递给entrypoint,且会覆盖cmd命令指定的参数。如,执行docker run <image> -d
时, -d
参数将被传递给入口点。
也可以通过docker run --entrypoint
重写entrypoint入口点。
如:可以像下面这样指定一个容器执行程序:
entrypoint ["/usr/bin/nginx"]
完整构建代码:
# version: 0.0.3 from ubuntu:16.04 maintainer 何民三 "cn.liuht@gmail.com" run apt-get update run apt-get install -y nginx run echo 'hello world, 我是个容器' \ > /var/www/html/index.html entrypoint ["/usr/sbin/nginx"] expose 80
使用docker build
构建镜像,并将镜像指定为itbilu/test:
$ sudo docker build -t="itbilu/test" .
构建完成后,使用itbilu/test
启动一个容器:
$ sudo docker run -i -t itbilu/test -g "daemon off;"
在运行容器时,我们使用了-g "daemon off;"
,这个参数将会被传递给entrypoint,最终在容器中执行的命令为/usr/sbin/nginx -g "daemon off;"
。
3.5 label
label用于为镜像添加无数据,无数以键值对的形式指定:
label <key>=<value> <key>=<value> <key>=<value> ...
使用label指定元数据时,一条label指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条label指令指定,以免生成过多的中间镜像。
如,通过label指定一些元数据:
label version="1.0" description="这是一个web服务器" by="it笔录"
指定后可以通过docker inspect
查看:
$sudo docker inspect itbilu/test "labels": { "version": "1.0", "description": "这是一个web服务器", "by": "it笔录" },
注意;dockerfile中还有个maintainer命令,该命令用于指定镜像作者。但maintainer并不推荐使用,更推荐使用label来指定镜像作者。如:
label maintainer="itbilu.com"
3.6 expose
expose用于指定容器在运行时监听的端口:
expose <port> [<port>...]
expose并不会让容器的端口访问到主机。要使其可访问,需要在docker run
运行容器时通过-p来发布这些端口,或通过-p
参数来发布expose导出的所有端口。
3.7 env
env用于设置环境变量,其有以下两种设置形式:
env <key> <value> env <key>=<value> ...
如,通过env设置一个环境变量:
env itbilu_path /home/itbilu/
设置后,这个环境变量在env命令后都可以使用。如:
workerdir $itbilu_path
这些环境变量不仅可以构建镜像过程使用,使用该镜像创建的容器中也可以使用。如:
$ docker run -i -t itbilu/test root@196ca123c0c3:/# cd $itbilu_path root@196ca123c0c3:/home/itbilu#
3.8 add
add用于复制构建环境中的文件或目录到镜像中。其有以下两种使用方式:
add <src>... <dest> add ["<src>",... "<dest>"]
通过add复制文件时,需要通过<src>指定源文件位置,并通过<dest>来指定目标位置。<src>可以是一个构建上下文中的文件或目录,也可以是一个url,但不能访问构建上下文之外的文件或目录。
如,通过add复制一个网络文件:
add http://wordpress.org/latest.zip $itbilu_path
在上例中,$itbilu_path
是我们使用env指定的一个环境变量。
另外,如果使用的是本地归档文件(gzip、bzip2、xz)时,docker会自动进行解包操作,类似使用tar -x。
3.9 copy
copy同样用于复制构建环境中的文件或目录到镜像中。其有以下两种使用方式:
copy <src>... <dest> copy ["<src>",... "<dest>"]
copy指令非常类似于add,不同点在于copy只会复制构建目录下的文件,不能使用url也不会进行解压操作。
3.10 volume
volume用于创建挂载点,即向基于所构建镜像创始的容器添加卷:
volume ["/data"]
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
- 卷可以容器间共享和重用
- 容器并不一定要和其它容器共享卷
- 修改卷后会立即生效
- 对卷的修改不会对镜像产生影响
- 卷会一直存在,直到没有任何容器在使用它
volume让我们可以将源代码、数据或其它内容添加到镜像中,而又不并提交到镜像中,并使我们可以多个容器间共享这些内容。
如,通过volume创建一个挂载点:
env itbilu_path /home/itbilu/ volume [$itbilu_path]
构建的镜像,并指定镜像名为itbilu/test。构建镜像后,使用新构建的运行一个容器。运行容器时,需-v参将能本地目录绑定到容器的卷(挂载点)上,以使容器可以访问宿主机的数据。
$ sudo docker run -i -t -v ~/code/itbilu:/home/itbilu/ itbilu/test root@31b0fac536c4:/# cd /home/itbilu/ root@31b0fac536c4:/home/itbilu# ls readme.md app.js bin config.js controller db demo document lib minify.js node_modules package.json public routes test views
如上所示,我们已经可以容器的/home/itbilu/
目录下访问到宿主机~/code/itbilu
目录下的数据了。
3.11 user
user用于指定运行镜像所使用的用户:
user daemon
使用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参数来覆盖所指定的用户。
3.12 workdir
workdir用于在容器内设置一个工作目录:
workdir /path/to/workdir
通过workdir设置工作目录后,dockerfile中其后的命令run、cmd、entrypoint、add、copy等命令都会在该目录下执行。
如,使用workdir设置工作目录:
workdir /a workdir b workdir c run pwd
在以上示例中,pwd最终将会在/a/b/c目录中执行。
在使用docker run
运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。
3.13 arg
arg用于指定传递给构建运行时的变量:
arg <name>[=<default value>]
如,通过arg指定两个变量:
arg site arg build_user=it笔录
以上我们指定了site和build_user两个变量,其中build_user指定了默认值。在使用docker build
构建镜像时,可以通过--build-arg <varname>=<value>
参数来指定或重设置这些变量的值。
$ sudo docker build --build-arg site=itiblu.com -t itbilu/test .
这样我们构建了itbilu/test镜像,其中site会被设置为itbilu.com,由于没有指定build_user,其值将是默认值it笔录。
3.14 onbuild
onbuild用于设置镜像触发器:
onbuild [instruction]
当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。
如,当镜像被使用时,可能需要做一些处理:
[...] onbuild add . /app/src onbuild run /usr/local/bin/python-build --dir /app/src [...]
3.15 stopsignal
stopsignal用于设置停止容器所要发送的系统调用信号:
stopsignal signal
所使用的信号必须是内核系统调用表中的合法的值,如:9、sigkill。
3.16 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
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
下一篇: docker指令收集整理(收藏)