Dockerfile 可以用来快速构建定制镜像,Dockerfile是很简单文本,只需要编写一条条构建镜像所需的指令和说明即可。
既然是编写文本,就需要选择合适的目录建立Dockerfile文件,一般情况下是每个项目下面有不通版本,版本之后再创建一个空目录,然后创建文本:Dockerfile,最后编辑指定镜像指令与说明。
简单构建nginx
[root@centos7-11 docker_file]# pwd
/data/docker_file
[root@centos7-11 docker_file]# ls
Dockerfile
[root@centos7-11 docker_file]# cat Dockerfile
FROM nginx
RUN echo 'this is test' > /usr/share/nginx/html/index.html
构建镜像 ,可以看到构建了2层
[root@centos7-11 docker_file]# docker build -t nginx:v111 /data/docker_file/
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM nginx
---> ae2feff98a0c
Step 2/2 : RUN echo 'this is test' > /usr/share/nginx/html/index.html
---> Running in d5ce47eb676c
Removing intermediate container d5ce47eb676c
---> 2e2b4637f4fe
Successfully built 2e2b4637f4fe
[root@centos7-11 docker_file]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v111 2e2b4637f4fe 21 seconds ago 133MB
启动nginx:v111 ,端口映射到本机80端口
[root@centos7-11 docker_file]# docker run -itd -p 80:80 nginx:v111
42b6da78310d96c80e5eac688e1f7f27d0676c507ae1d1870a7f5062d29f4760
[root@centos7-11 docker_file]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42b6da78310d nginx:v111 "/docker-entrypoint.…" 37 seconds ago Up 36 seconds 0.0.0.0:80->80/tcp competent_rosalind
浏览器测试 ,输入ip得到如下内容,测试成功
镜像构建上下文(Context)
docker 的 C\S架构
构建的时候,有指定一个路径,可能会认为是Dockerfile的路径,其实这样理解不完全正确,这里指定的是构建的上下文路径。
docker 是C\S 架构,分为 Docker 客户端 与 Docker 服务端(dockerd 守护进程)。我们这里实验是在同一台机器上,Docker 客户端默认通过 UNIX 套接字(/var/run/docker.sock)和服务端通信。
docker客户端的一些指令、操作都是通过docker服务端来完成的,docker客户端只是负责发送指令
Docker 的引擎提供了一组 REST API,被称为 Docker Remote API(https://docs.docker.com/develop/sdk/),而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。
构建的基本步骤
理解了 Docker 的架构就很容易理解 docker build 构建镜像的工作原理了。docker build 构建镜像的流程大概如下:
- 执行 docker build -t <imageName:imageTag> 上下文路径 ;
- Docker 客户端会将构建命令后面指定的路径(上下文路径)下的所有文件打包成一个 tar 包,发送给 Docker 服务端;
- Docker 服务端收到客户端发送的 tar 包,然后解压,根据 Dockerfile 里面的指令进行镜像的分层构建;
打包指定目录文件之后,docker构建就会查找指定上下文中查找文件名字为Dockerfile的文件构建镜像
Dockerfile 可以不在构建上下文路径下,此时需要构建时通过 -f 参数明确指定使用哪个构建文件,并且名称可以自己任意命名,例如docker build -f dockerfile.text -t xxxxxxxx
详情:https://docs.docker.com/engine/reference/commandline/build/
或者看下我之前看过的一篇通俗易懂的文章:https://blog.csdn.net/qianghaohao/article/details/87554255
Dockerfile构建参数简介
FROM指定基础镜像
FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是基础镜像。后续的操作都是基于 nginx进行下去,所有这个是必备的一条命令,必须在Dockerfile的第一行。docker hub (https://hub.docker.com/search?q=&type=image&image_filter=official
)上有很多的基础镜像提供使用。
docker还提供一种虚拟的镜像scratch ,是空镜像,也就是说不需要基础镜像的一种定制方式,这个方式目前还没使用过,不过听说go开发很多会应用这个方式。
FROM scratch
....
RUN 执行命令
RUN:用于执行后面跟着的命令行命令。类似linux中的bash ,可以在后面跟可以执行的命令 。可以有shell类似方式,也可以使用exec方式
shell方式:RUN 需要执行的命令
FROM ...
RUN 需要执行的命令
exec方式: RUN ["可执行文件","参数1","参数2"]
假设脚本makefile.sh 是创建文件的脚本,需要传入两个参数,参数1表示文件目录,参数2表示文件名称。
FROM ...
RUN ["./makefile.sh","/data/test_dockerfile_v1","Dockerfile"]
#类似于:RUN ./makefile.sh /data/test_dockerfile_v1 Dockerfile
因为每一条定制指定都会多建立一层,不仅提高了执行步骤,还提升了执行时间,所以需要使用相同指令的行,可以使用连接符号(&&)合并使用一个指令即可,例如
FROM ...
RUN echo 'this is test' > /usr/share/nginx/html/index.html
RUN yum install wget
RUN wget -o www.tar.gz www.xxxx.com
转化为:
FROM ...
RUN echo 'this is test' > /usr/share/nginx/html/index.html
&& yum install wget
&& wget -o www.tar.gz www.xxxx.com
COPY 复制文件
从构建的目录中复制文件、目录到容器的指定目录中,而且可以指定容器中文件的属主属组,也有两种调用方式:
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
[--chown=<user>:<group>]:可选参数,用户改变复制到容器内文件的拥有者和属组。
<源路径>:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。
COPY data*.txt /mydir/
COPY data?.txt /mydir/
/mydir/ 表示容器内的目标路径,不存在会创建
ADD 更高级的copy
ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY)。高级之处例如 <源路径> 是一个 URL,Docker 会尝试去下载这个链接的文件放到 <目标路径> 去。下载后的文件权限自动设置为 600,如果需要更改权限,可以使用RUN进行权限调整,如果下载的是个压缩包,需要解压缩,也可以使用 RUN 指令进行解压缩。如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去所以不如直接使用 RUN 指令,然后使用 wget 或者 curl 工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且官方也不推荐使用。
CMD 容器启动命令
CMD 指令的格式和 RUN 相似,都是运行命令,但是运行命令的时间点不一样:
CMD 在docker run 时运行。
RUN 是在 docker build。
也像RUN是两种格式:
shell 格式:CMD <命令>
exec 格式:CMD ["可执行文件", "参数1", "参数2"...]
参数列表格式:CMD ["参数1", "参数2"...]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效,这个需要注意。
ENTRYPOINT
ENTRYPOINT 的作用和 CMD 一样,指定容器启动程序及参数。ENTRYPOINT 在运行时可以替代,不过需要通过 docker run 的参数 --entrypoint 来指定
指定 ENTRYPOINT 后,CMD 的含义就发生了改变,不是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,实际执行时,将变为:
<ENTRYPOINT> "<CMD>"
假设已通过 Dockerfile 构建了 nginx:test 镜像:
FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参
1、不传参运行
$ docker run nginx:v11
容器内会默认运行以下命令,启动主进程。
nginx -c /etc/nginx/nginx.conf
2、传参运行
$ docker run nginx:v11 -c /etc/nginx/new.conf
容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)
nginx -c /etc/nginx/new.conf
ENV 设置环境变量
这个指令就是设置环境变量,在这个指令之后的指令都可以使用这里定义的环境变量
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
测试下
[root@centos7-11 docker_file]# cat Dockerfile
FROM nginx
RUN echo 'this is test' > /usr/share/nginx/html/index.html
ENV test hubery_zhao
RUN echo $test
[root@centos7-11 docker_file]# docker build -t nginx:v1.1 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM nginx
---> ae2feff98a0c
Step 2/4 : RUN echo 'this is test' > /usr/share/nginx/html/index.html
---> Using cache
---> 2e2b4637f4fe
Step 3/4 : ENV test hubery_zhao
---> Running in 35d1bceff89e
Removing intermediate container 35d1bceff89e
---> f60ab3ae6c4c
Step 4/4 : RUN echo $test
---> Running in 675a8108c44d
hubery_zhao
Removing intermediate container 675a8108c44d
---> 965f20cdbb55
Successfully built 965f20cdbb55
Successfully tagged nginx:v1.1
ARG 构建参数
构建参数和 ENV 的效果一样,设置环境变量。不同的是,ARG设置的构建环境变量,在容器运行时不会存在这些环境变量,所以一些密码类的信息,都不要使用ARG了,这个在容器内部历史命令可以查看设置的值。
ARG <参数名>[=<默认值>]
VOLUME 定义匿名卷
在容器运行时,应该尽量保证容器存储层不发生写动作,数据库类需要保存动态数据的应用,应保存于卷(volume)中,这里为了防止用户运行容器是没有挂载卷的指令,在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。使用这个参数的好处在于可以避免容器随着数据增加而不断变大,也可以避免停止容器丢失数据。
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
也可以在docker run 的时候使用-v 修改挂载点
docker run -d -v mydata:/data xxxx
EXPOSE 暴露端口
声明运行时容器提供服务端口,这是一个声明,在运行时不会因为这个声明主动开启这个端口的服务。在 Dockerfile 中写入这样的声明的优点有:一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。(注意是使用随机端口时会自动映射到EXPOSE写入的端口,需要与docker run -p 宿主机端口:容器端口 区分开 )
EXPOSE <端口1> [<端口2>...]
WORKDIR 指定工作目录
用 WORKDIR 指定的工作目录,或者称为当前目录,在之后的各层构建没有指定目录就在这个目录中,没有这个目录 会自动创建。在没有改变目录的情况下,使用exec进入容器也是在这个目录中。
[root@centos7-11 docker_file]# cat Dockerfile
FROM nginx
RUN echo 'this is test' > /usr/share/nginx/html/index.html
ENV test hubery_zhao
RUN echo $test
WORKDIR /data/tmp/test
[root@centos7-11 docker_file]# docker build -t nginx:v1.2 .
Sending build context to Docker daemon 2.048kB
Step 1/5 : FROM nginx
---> ae2feff98a0c
Step 2/5 : RUN echo 'this is test' > /usr/share/nginx/html/index.html
---> Running in a9655a50e2a7
Removing intermediate container a9655a50e2a7
---> 7f8336a4cb6b
Step 3/5 : ENV test hubery_zhao
---> Running in 0d5fd2a7503d
Removing intermediate container 0d5fd2a7503d
---> 561ef92e8b69
Step 4/5 : RUN echo $test
---> Running in c896a1eb6b08
hubery_zhao
Removing intermediate container c896a1eb6b08
---> 36541714b97c
Step 5/5 : WORKDIR /data/tmp/test
---> Running in de629c1aecd3
Removing intermediate container de629c1aecd3
---> 87b1c453f141
Successfully built 87b1c453f141
Successfully tagged nginx:v1.2
[root@centos7-11 docker_file]# docker run -itd nginx:v1.2 /bin/bash
8fd3a25949a644f97ad08699f99bb83a2cd266c71ea7efcb89af6d303ae013ca
[root@centos7-11 docker_file]# docker exec -it 8fd3a25949a644f97ad08699f99bb83a2cd266c71ea7efcb89af6d303ae013ca /bin/bash
root@8fd3a25949a6:/data/tmp/test# exit
USER 指定当前用户
USER 指令和 WORKDIR 相似,都是改变环境状态并影响之后构建层,USER只是切换后续命令执行的用户,这个用户需要提前建立好,否则不能切换
USER <用户名>[:<用户组>]
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]
HEALTHCHECK 健康检查
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽其健康检查指令
HEALTHCHECK 支持下列选项:
--interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
--retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
指定某个程序或者指令来监控 docker 容器服务的运行状态,这是 Docker 1.12 引入的新指令。之前,docker引擎只能通过容器内主进程是否退出来判断容器是否状态异常,多数情况是可以这样使用的,但是如果程序死锁或者死循环情况下,程序不退出,容器已经无法提供服务。
HEALTHCHECK只可以出现一次,如果写了多个,只有最后一个生效。
例如web服务,希望使用健康检查web服务是否正常可以用curl来判断
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
#这里是5秒检查一次,3秒超时,使用 curl -fs http://localhost/ || exit 1作为检查命令。
构建镜像
[root@centos7-11 docker_file]# docker build -t nginx:v1.5 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM nginx
---> ae2feff98a0c
Step 2/3 : RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
---> Using cache
---> 415fb1a62562
Step 3/3 : HEALTHCHECK --interval=5s --timeout=3s CMD curl -fs http://localhost/ || exit 1
---> Using cache
---> 5fb706616ba3
Successfully built 5fb706616ba3
Successfully tagged nginx:v1.5
启动容器docker run -itd --name web -P nginx:v1.5
马上查看docker ps 可以看到health:starting
[root@centos7-11 docker_file]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
459b3370e94d nginx:v1.5 "/docker-entrypoint.…" 4 seconds ago Up 3 seconds (health: starting) 0.0.0.0:49157->80/tcp web
稍等一会就可看到healthy
[root@centos7-11 docker_file]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
459b3370e94d nginx:v1.5 "/docker-entrypoint.…" 6 seconds ago Up 5 seconds (healthy) 0.0.0.0:49157->80/tcp web
如果检查连续失败重试次数,状态会变为unhealthy。检查命令输出回春在健康状态里,可以使用docker inspect来看
[root@centos7-11 docker_file]# docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
"FailingStreak": 0,
"Log": [
{
"End": "2021-01-07T20:09:58.675303046+08:00",
"ExitCode": 0,
"Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"Start": "2021-01-07T20:09:58.616761667+08:00"
},
ONBUILD延迟构建
这个指令之后跟着的其他指令在当前镜像构建时不会被执行,只有以当前镜像作为基础镜像(FROM)去构建新镜像的时候才会被执行。
ONBUILD <其它指令>
LABEL 为镜像添加元数据
为镜像以键值对的形式添加一些元数据
LABEL <key>=<value> <key>=<value> <key>=<value> ...
添加标签测试
[root@centos7-11 docker_file]# cat Dockerfile
FROM nginx
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
[root@centos7-11 docker_file]# docker build -t nginx:v1.6 .
[root@centos7-11 docker_file]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v1.6 d6d62f4cd9e0 About a minute ago 133MB
[root@centos7-11 docker_file]# docker image inspect --format="" d6d62f4cd9e0
[
{
...........................................
"Comment": "",
"Created": "2021-01-07T14:35:48.170668985Z",
"Container": "86ba98ec7267cd66d3e0922109f6c974002d3071b659e86cad5e7fa5094ffff8",
"ContainerConfig": {
...................................................
"OnBuild": null,
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
},
"StopSignal": "SIGQUIT"
},
"DockerVersion": "20.10.1",
"Author": "",
..................................
SHELL
该SHELL指令允许覆盖用于命令的shell形式的默认shell 。在Linux上默认shell是["/bin/sh", "-c"],在Windows上,默认shell是["cmd", "/S", "/C"]。该SHELL指令必须在Dockerfile中以JSON形式编写。可以指定 RUN ENTRYPOINT CMD 指令的 shell
SHELL ["executable", "parameters"]