赞
踩
Dockerfile
是一个文本格式的配置文件,用户可以使用 Dockerfile
来快速创建自定义的镜像。
编写 Dockerfile
: https://smoothies.com.cn/docker-docs/Docker/Dockerfile/
Dockerfile
由一行行命令语句组成,并且支持以 #
开头的注释行。一般而言,Dockerfile
,分为四部分:
- 基础镜像信息;
- 维护者信息;
- 镜像操作指令;
- 和容器启动时执行指令;
如下示例
# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/
sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
其中,一开始必须指明所基于的镜像名称,接下来一般是说明维护者信息。后面则是镜像操作指令,例如 RUN
指令,RUN
指令将对镜像执行跟随的命令。每运行一条 RUN
指令,镜像就添加新的一层,并提交。最后是 CMD
指令,用来指定运行容器时的操作命令。
指令 | 说明 |
---|---|
FROM | 指定所创建镜像的基础镜像 |
MAINTAINER | 指定维护者信息 |
RUN | 运行命令 |
CMD | 指定容器启动时默认执行的命令 |
LABEL | 指定生成镜像的元数据标签信息 |
EXPOSE | 声明镜像内服务所监听的端口 |
ENV | 指定环境变量 |
ADD | 复制指定的 路径下的内容到容器中的 路径下, 可以为 URL;如果为 tar 文件,会自动解压到 路径下 |
COPY | 复制本地主机的 路径下的内容到镜像中的 路径下;一般情况下推荐使用 COPY 而不是 ADD |
ENTRYPOINT | 指定镜像的默认入口 |
VOLUME | 创建数据卷挂载点 |
WORKDIR | 配置工作目录 |
ARG | 指定镜像内使用的参数 (例如版本号信息等) |
ONBUILD | 配置当所创建的镜像作为其它镜像的基础镜像时,所执行的创建操作指令 |
STOPSIGNAL | 容器退出的信号值 |
HEALTHCHECK | 如何进行健康检查 |
SHELL | 指定使用 shell 时的默认 shell 类型 |
下面具体介绍各个指令
指定所创建镜像的基础镜像,如果本地不存在,则默认会去 Docker Hub
下载指定镜像。格式为
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
任何 Dockerfile
中的第一条指令必须为 FROM
指令。并且,如果在同一个 Dockerfile
中创建多个镜像,可以使用多个 FROM
指令(每个镜像一次)。
在 Dockerfile
中可以多次出现 FROM
指令,当 FROM
第二次或者之后出现时,表示在此刻构建时,要将当前指出镜像的内容合并到此刻构建镜像的内容里。这对于我们直接合并两个镜像的功能很有帮助。
指定维护者信息,格式为
MAINTAINER <name> <email>
例如:
MAINTAINER image_creator@docker.com
该信息会写入生成镜像的 Author 属性域中。
RUN
指令在新镜像内部执行的命令,如:执行某些动作、安装系统软件、配置系统信息之类。格式为
RUN <command>
或
RUN ["executable","param1","param2"]
注意,后一个指令会被解析为 Json
数组,因此必须用双引号。前者默认将在 shell
终端中运行命令,即 /bin/sh -c
;后者则使用 exec
执行,不会启动 shell
环境。
指定使用其他终端类型可以通过第二种方式实现,例如
RUN ["/bin/bash","-c","echo hello"]
每条 RUN
指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \
来换行。例如:
RUN apt-get update \
&& apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev \
&& rm -rf /var/cache/apt
注:多行命令不要写多个 RUN
,原因是 Dockerfile
中每一个指令都会建立一层,多少个 RUN
就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错。
CMD
指令用来指定启动容器时默认执行的命令。它支持三种格式:
CMD["executable","param1","param2"]
使用 exec
执行,是推荐使用的方式;CMD command param1 param2
在 /bin/sh
中执行,提供给需要交互的应用;CMD["param1","param2"]
提供给 ENTRYPOINT
的默认参数。每个 Dockerfile
只能有一条 CMD
命令。如果指定了多条命令,只有最后一条会被执行。如果用户启动容器时手动指定了运行的命令(作为 run
的参数),则会覆盖掉 CMD
指定的命令。
如容器启动时进入 bash
:
CMD /bin/bash
或者可以用 exec
写法
CMD ["/bin/bash"]
当 ENTRYPOINT
与 CMD
同时给出时,CMD
中的内容会作为 ENTRYPOINT
定义命令的参数,最终执行容器启动的还是 ENTRYPOINT
中给出的命令。
LABEL
指令用来指定生成镜像的元数据标签信息。格式为:
LABEL <key>=<value><key>=<value><key>=<value>...
例如:
LABEL version="1.0"
LABEL description="This text illustrates \ that label-values can span multiple lines."
声明镜像内服务所监听的端口。EXPOSE
命名适用于设置容器对外映射的容器端口号,格式为
EXPOSE <port>[<port>...]
例如:
EXPOSE 22 80 8443
注意,该指令只是起到声明作用,并不会自动完成端口映射。
在启动容器时需要使用 -P
,Docker
主机会自动分配一个宿主机的临时端口转发到指定的端口;使用 -p
,则可以具体指定哪个宿主机的本地端口会映射过来。
如 Tomcat
容器内使用的端口 8081,则用 EXPOSE
命令可以告诉外界该容器的 8081 端口对外,在构建镜像时用 Docker run -p
可以设置暴露的端口对宿主机器端口的映射。
EXPOSE 8081
EXPOSE 8081
其实等价于 Docker run -p 8081
当需要把 8081
端口映射到宿主机中的某个端口(如8888
)以便外界访问时,则可以用 Docker run -p 8888:8081
。
指定环境变量,在镜像生成过程中会被后续 RUN
指令使用,在镜像启动的容器中也会存在。ENV
命名用于设置容器的环境变量,这些变量以 key=value
的形式存在,在容器内被脚本或者程序调用,容器运行的时候这个变量也会保留。格式为
ENV <key> <value>
或
ENV <key>=<value>...
例如:
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
指令指定的环境变量在运行时可以被覆盖掉,如
docker run --env <key>=<value> built_image
在使用 ENV
设置环境变量时,有几点需要注意:
具有传递性,也就是当前镜像被用作其它镜像的基础镜像时,新镜像会拥有当前这个基础镜像所有的环境变量;
ENV
定义的环境变量,可以在 Dockerfile
被后面的所有指令( CMD
除外)中使用,但不能被 Docker run
的命令参数引用 。 如:
ENV Tomcat_home_name Tomcat_7
RUN mkdir $Tomcat_home_name
-e
或是 --env
选项,可以对环境变量的值进行修改或定义新的环境变量。除了 ENV
之外,docker run -e
也可以设置环境变量传入容器内。docker run -d Tomcat -e "Tomcat_home_name=Tomcat_7"
这样我们进入容器内部用 ENV
可以看到 Tomcat_home_name
这个环境变量。
通过 ENV
指令和 ARG
指令所定义的参数,在使用时都是采用 $ + NAME
这种形式来占位的,所以它们之间的定义就存在冲突的可能性。对于这种场景,大家只需要记住,ENV
指令所定义的变量,永远会覆盖 ARG
所定义的变量,即使它们定时的顺序是相反的。
与参数变量 ARG
只能影响构建过程不同,环境变量不仅能够影响构建,还能够影响基于此镜像创建的容器。
Dockerfile
中编写的,所以如果我们要修改环境变量的值,我们需要到 Dockerfile
修改。不过即使这样,只要我们将 ENV
定义放在 Dockerfile
前部容易查找的地方,其依然可以很快的帮助我们切换镜像环境中的一些内容。作用和使用方法和 COPY
一样。该命令将复制指定的 src
路径下的内容到容器中的 dest
路径下。格式为
ADD <src> <dest>
其中
src
可以是 Dockerfile
所在目录的一个相对路径(文件或目录),也可以是一个 URL
,还可以是一个 tar
文件(如果为 tar
文件,会自动解压到 dest
路径下)。dest
可以是镜像内的绝对路径,或者相对于工作目录( WORKDIR
)的相对路径。路径支持正则格式,例如:ADD *.c /code/
COPY
命令用于将宿主机器上的的文件复制到镜像内,如果目的位置不存在,Docker
会自动创建。但宿主机器用要复制的目录必须是和 Dockerfile
文件同级目录下。 格式为
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
复制本地主机的 src
(为 Dockerfile
所在目录的相对路径、文件或目录)下的内容到镜像中的 dest
下。目标路径不存在时,会自动创建。路径同样支持正则格式。当使用本地目录为源目录时,推荐使用COPY
。
COPY
命令和 ADD
类似,唯一的不同是 ADD
会自动解压压缩包,还可以直接下载 url
中的文件但是官方建义使用 wget
或者 curl
代替 ADD
。
# 拷贝并解压
ADD nickdir.tar.gz .
# 仅拷贝
ADD https://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
应该改成这样子
RUN mkdir -p /usr/src/things \
&& curl -SL https://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数。支持两种格式:
ENTRYPOINT ["executable", "param1", "param2"](exec调用执行)
ENTRYPOINT command param1 param2(shell中执行)
此时,CMD
指令指定值将作为根命令的参数。每个 Dockerfile
中只能有一个 ENTRYPOINT
,当指定多个时,只有最后一个有效。在运行时,可以被 --entrypoint
参数覆盖掉,如 docker run--entrypoint
。
ENTRYPOINT
的作用和用法和 CMD
一模一样,但是 ENTRYPOINT
有和 CMD
有 2 处不一样:
CMD
的命令会被 Docker run
的命令覆盖而 ENTRYPOINT
不会;ENTRYPOINT
指令的优先级高于 CMD
指令。CMD
和 ENTRYPOINT
都存在时,CMD
的指令变成了 ENTRYPOINT
的参数,两者拼接之后,才是最终执行的命令。并且此 CMD
提供的参数会被 Docker run
后面的命令覆盖;CMD
和 ENTRYPOINT
命令相同点:
CMD
和 ENTRYPOINT
命令不同点:
CMD
指令指定的程序可被 docker run
命令行参数中指定要运行的程序所覆盖,但 ENTRYPOINT
不会。命令加参数的形式
ENTRYPOINT [ "echo", "a" ]
$ docker run test
a
加参数,但是不会替换
ENTRYPOINT [ "echo", "a" ]
$ docker run test b
a b
CMD
为 ENTRYPOINT
提供默认参数
ENTRYPOINT [ "echo", "a" ]
CMD ["b"]
$ docker run test
a b
加参数 c
会替换 CMD
提供的参数
ENTRYPOINT [ "echo", "a" ]
CMD ["b"]
$ docker run test c
a c
ENTRYPOINT
与 CMD
的组合示例:
有的读者会存在疑问,既然两者都是用来定义容器启动命令的,为什么还要分成两个,合并为一个指令岂不是更方便吗?
这其实在于 ENTRYPOINT
和 CMD
设计的目的是不同的。
ENTRYPOINT
指令主要用于对容器进行一些初始化;CMD
指令则用于真正定义容器中主程序的启动命令;另外,我们之前谈到创建容器时可以改写容器主程序的启动命令,而这个覆盖只会覆盖 CMD
中定义的内容,而不会影响 ENTRYPOINT
中的内容。
我们依然以之前的 Redis
镜像为例,这是 Redis
镜像中对 ENTRYPOINT
和 CMD
的定义。
## ......
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
## ......
CMD ["redis-server"]
可以很清晰的看到,CMD 指令定义的正是启动 Redis 的服务程序,而 ENTRYPOINT 使用的是一个外部引入的脚本文件。
事实上,使用脚本文件来作为 ENTRYPOINT 的内容是常见的做法,因为对容器运行初始化的命令相对较多,全部直接放置在 ENTRYPOINT 后会特别复杂。
我们来看看 Redis 中的 ENTRYPOINT 脚本,可以看到其中会根据脚本参数进行一些处理,而脚本的参数,其实就是 CMD 中定义的内容。
#!/bin/sh set -e # first arg is `-f` or `--some-option` # or first arg is `something.conf` if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then set -- redis-server "$@" fi # allow the container to be started with `--user` if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then find . \! -user redis -exec chown redis '{}' + exec gosu redis "$0" "$@" fi exec "$@"
这里我们要关注脚本最后的一条命令,也就是 exec “$@”。在很多镜像的 ENTRYPOINT 脚本里,我们都会看到这条命令,其作用其实很简单,就是运行一个程序,而运行命令就是 ENTRYPOINT 脚本的参数。反过来,由于 ENTRYPOINT 脚本的参数就是 CMD 指令中的内容,所以实际执行的就是 CMD 里的命令。
所以说,虽然 Docker 对容器启动命令的结合机制为 CMD 作为 ENTRYPOINT 的参数,合并后执行 ENTRYPOINT 中的定义,但实际在我们使用中,我们还会在 ENTRYPOINT 的脚本里代理到 CMD 命令上。
创建一个数据卷挂载点。格式为
VOLUME ["/data"]
可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保存的数据等。VOLUME
用来创建一个可以从本地主机或其他容器挂载的挂载点。
但使用数据卷需要我们在创建容器时通过 -v
选项来定义,而有时候由于镜像的使用者对镜像了解程度不高,会漏掉数据卷的创建,从而引起不必要的麻烦。
在 VOLUME
指令中定义的目录,在基于新镜像创建容器时,会自动建立为数据卷,不需要我们再单独使用 -v
选项来配置了。
例如我们知道 Tomcat
的 Webapps
目录是放 Web
应用程序代码的地方,此时我们要把 Webapps
目录挂载为匿名卷,这样任何写入 Webapps
中的心都不会被记录到容器的存储层,让容器存储层无状态化。
如创建 Tomcat
的 Webapps
目录的一个挂载点
VOLUME /usr/local/Tomcat/Webapps
这样,在运行容器时,也可以用过 Docker run -v
来把匿名挂载点挂载都宿主机器上的某个目录,如
docker run -d -v /home/Tomcat_Webapps:/usr/local/Tomcat/Webapps
指定运行容器时的用户名或UID,后续的RUN等指令也会使用指定的用户身份。需要注意的是这个用户必须是已经存在,否则无法指定。格式为
USER daemon
当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户。例如:
RUN groupadd -r postgres && useradd -r -g postgres postgres
要临时获取管理员权限可以使用 gosu 或 sudo 。
USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份。 注意, USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]
如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu 。
# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.12/gosu-amd64" \
&& chmod +x /usr/local/bin/gosu \
&& gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]
为后续的 RUN
、CMD
和 ENTRYPOINT
指令配置工作目录。其效果类似于 Linux
命名中的 cd
命令,用于目录的切换,但是和 cd
不一样的是:如果切换到的目录不存在,WORKDIR
会为此创建目录。格式为
WORKDIR /path/to/workdir
可以使用多个 WORKDIR
指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c
。
指定一些镜像内使用的参数(例如版本号信息等),这些参数在执行 docker build
命令时才以 --build-arg <varname>=<value>
格式传入。格式为
ARG <name> [=<default value>]
示例
docker build --build-arg <name>=<value> .
来指定参数值。
示例,Dockefile
文件内容
FROM debian:stretch-slim
## ......
ARG TOMCAT_MAJOR
ARG TOMCAT_VERSION
## ......
RUN wget -O tomcat.tar.gz "https://www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz"
## ......
构建命令
$ sudo docker build --build-arg TOMCAT_MAJOR=8 --build-arg TOMCAT_VERSION=8.0.53 -t tomcat:8.0 ./tomcat
配置当所创建的镜像作为其他镜像的基础镜像时,所执行的创建操作指令。意思就是:这个镜像创建后,如果其它镜像以这个镜像为基础,会先执行这个镜像的 ONBUILD 命令。格式为
ONBUILD [INSTRUCTION]
例如,Dockerfile 使用如下的内容创建了镜像 image-A:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像,会自动执行 ONBUILD 指令的内容,等价于在后面添加了两条指令:
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的镜像,推荐在标签中注明,例如ruby: 1.9-onbuild。
指定所创建镜像启动的容器接收退出的信号值。例如:
STOPSIGNAL signal
配置所启动容器如何进行健康检查(如何判断健康与否),自 Docker 1.12开始支持。格式有两种:
OPTION支持:
指定其他命令使用 shell
时的默认 shell
类型。默认值为 ["/bin/sh","-c"]
。注意对 于 Windows
系统,建议在 Dockerfile
开头添加 #escape=` 来指定转义信息。
编写完成 Dockerfile
之后,可以通过 docker build
命令来创建镜像。
基本的格式为
docker build [OPTIONS] PATH | URL | -
OPTIONS
有很多指令,下面列举几个常用的:
--build-arg=[]
:设置镜像创建时的变量;-f
:指定要使用的 Dockerfile
路径;--force-rm
:设置镜像过程中删除中间容器;--rm
:设置镜像成功后删除中间容器;--tag, -t
: 镜像的名字及标签,通常 name:tag
或者 name
格式。docker build
可以接收一个参数,需要特别注意的是,这个参数为一个目录路径 ( 本地路径或 URL 路径 ),而并非 Dockerfile
文件的路径。在 docker build
里,这个我们给出的目录会作为构建的环境目录,我们很多的操作都是基于这个目录进行的。
例如,在我们使用 COPY
或是 ADD
拷贝文件到构建的新镜像时,会以这个目录作为基础目录。
该命令将读取指定路径下(包括子目录)的 Dockerfile
,并将该路径下的所有内容发送给 Docker
服务端,由服务端来创建镜像。因此除非生成镜像需要,否则一般建议放置 Dockerfile
的目录为空目录。
有两点经验:
Dockerfile
,可以通过 -f
选项来指向文件系统中任何位置的 Dockerfile
docker build -f /path/to/a/Dockerfile .
-t
选项。例如,指定 Dockerfile
所在路径为 /tmp/docker_builder/
,并且希望生成镜像标签为 build_repo:first_image
,可以使用下面的命令:$ docker build -t build_repo:first_image /tmp/docker_builder/
$ docker build -t webapp:latest ./webapp
执行命名之后,会看到控制台逐层输出构建内容,直到输出两个 Successfully 即为构建成功。
可以通过 .dockerignore
文件(每一行添加一条匹配模式)来让 Docker
忽略匹配模式路径下的目录和文件。例如:
# comment
*/temp*
*/*/temp*
tmp?
其中:
*
表示任意多个字符?
表示单个字符!
表示不匹配(即不忽略指定的路径或文件)Ubuntu
系统镜像)会造成生成应用镜像臃肿,推荐选用较为小巧的系统镜像(如 alpine
、busybox
、debian
);RUN
、ADD
、COPY
等指令;.dockerignore
文件;apt-get
指令后,/var/cache/apt
下面会缓存了一些安装包;赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。