ps:接上篇
利用 Dockerfile 定制镜像
1、Dockerfile概述
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是Dockerfile Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建. Dockfile是由一行行命令语句组成,并且迟滞以#开头的注释行;一般而言,Dockerfiel分为四部分:1.基础镜像信息 ;2.维护者信息;3.镜像操作指令;4.容器启动时指令,例如
配置第一个dockerfile文件
$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile
内容:
# this is dockerfile uses the nginx images
# VERSION 1 - EDITON 1
# Author: docker
#base image the use
FROM nginx
# maintainer: docker_user
MAINTAINER docker_user docker_user@docker.com
RUN echo ' hello,docker! ' > /usr/share/nginx/html/index.html
1.1、FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制,基础镜像是必须指定的。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像
FROM scratch
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如swarm 、 coreos/etcd 。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧。使用 Go 语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go是特别适合容器微服务架构的语言的原因之一
1.2、RUN 执行命令
RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最常用的指令之一。其格式有两种: shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。 例如:RUN echo "123" > /tmp/1.txt
exec 格式: RUN ["可执行文件", "参数1", "参数2"] ,这更像是函数调用中的格式
Dockerfile 中每一个指令都会建立一层, RUN 也不例外。每一个 RUN 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像. Union FS 是有最大层数限制的,比如 AUFS,曾经是最大不得超过 42 层,现在是不得超过127 层
因此,这里没有使用很多个 RUN对应一一不同的命令,而是仅仅使用一个 RUN 指令,并使用 && 将各个所需命令串联起来, 在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建
比如 RUN yum -y install nginx \
&& echo "hello docker" > /usr/share/nginx/html/index.html\
&& RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf \
CMD /usr/bin/nginx
Dockerfile 支持 Shell 类的行尾添加 \ 的命令换行方式,以及行首 # 进行注释的格式。良好的格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯
1.3、构建镜像
语法: docker build [选项] <上下文路径/URL/->
[root@docker mynginx]# docker build -t nginx:v4 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM nginx
---> 7f70b30f2cc6
Step 2/2 : RUN echo ' hello,docker! ' > /usr/share/nginx/html/index.html
---> Running in 065c3e52b540
Removing intermediate container 065c3e52b540
---> 3d80dfa73097
Successfully built 3d80dfa73097
Successfully tagged nginx:v4
[root@docker mynginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v4 3d80dfa73097 About a minute ago 109MB
运行 以后台方式运行 名称为test 端口映射80:80
$ docker run -dit --name test -p 80:80 nginx:v4
```
![](https://s1.51cto.com/images/blog/201804/02/33ce31a47ca15764f1c0b4eb5a134da8.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
第一个例子,使用centos基础镜像安装nginx环境,从而创建一个新的nginx
## 1.4、镜像构建上下文(Context)
> build 构建时最后带的那个. 并不是指定Dockerfile所在的路径,这样理解是不正确的,如果对应上面的命令格式,这其实是在指定上下文路径,那么什么是上下文呢?
首先我们要理解**docker build 工作原理**: Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 DockerRemote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件
![](https://s1.51cto.com/images/blog/201804/02/fb74fce5146a65779b5664a5d6f9b778.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
![](https://s1.51cto.com/images/blog/201804/02/0455595d9ffc42be3b2d01dc33862030.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个.dockerignore ,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的,如果在根目录下直接build -t name .的话 会将整个系统都打包给docker容器.
直接指定** . ** 构建的时候默认行为就是直接指定的Dockerfile文件,当然也可以指定别的文件做为构建参数, 比如 -f test.txt, 当然大家最好还是习惯性使用默认文件名Dockerfile。
## 1.5、Docker build的其它用法
1. 直接使用Git repo进行构建
使用该方法老报错,我们可以给它先下载到本地,然后再build $ docker build https://github.com/twang2218/gitlab-ce-zh.git#:10.6
$ git clone https://github.com/twang2218/gitlab-ce-zh.git
$ docker build -t name .
这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /8.14/ ,然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。
2、用给定的tar压缩包构建
[root@docker ~]# mkdir mytar
[root@docker ~]# cd mytar/
#内容如下
FROM nginx
RUN echo "test tar images" > /usr/share/nginx/html/index.html
&& echo "test" > /tmp/1.txt
注意这里得直接打包 $ tar mytar.tar.gz Dockerfile docker build -t test/nginx:v4.1 - < mytar.tar.gz Sending build context to Docker daemon 208B Step 1/2 : FROM nginx ---> 7f70b30f2cc6 Step 2/2 : RUN echo "test tar images" > /usr/share/nginx/html/index.html && echo "test" > /tmp/1.txt ---> Running in a8f2b14e8576 Removing intermediate container a8f2b14e8576 ---> d5398275b4d7 Successfully built d5398275b4d7 Successfully tagged test/nginx:v4.1
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE test/nginx v4.1 d5398275b4d7 4 seconds ago 109MB
如果发现标准输入的文件格式是 gzip 、 bzip2 以及 xz 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。
# 2、COPY 复制文件
> 和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用
语法:
COPY <源路径>... <目标路径>
COPY ["<源路径1>",... "<目标路径>"]
比如:
COPY package.json /usr/src/app/
也可以是多个,甚至是通配符
COPY home* /usr/src/app/
COPY hom?.txt /usr/src/app/
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(**工作目录可以用 WORKDIR 指令来指定**)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录
例如: Dockerfile内容 FROM centos WORKDIR /usr/local/app/ COPY a* ./
$ docker build -t filetest/test . Sending build context to Docker daemon 3.584kB Step 1/3 : FROM centos ---> 2d194b392dd1 Step 2/3 : WORKDIR /usr/local/app/ Removing intermediate container 7fc63b9b6c51 ---> f5bd6a0a5ba8 Step 3/3 : COPY a* ./ ---> 738d1c5a877a Successfully built 738d1c5a877a Successfully tagged filetest/test:latest
[root@docker mytar]# docker run -it --rm filetest/test /bin/bash [root@b043e7420357 app]# ls a ab ac
此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git进行管理的时候。
# 3、ADD 更高级的复制文件
> ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
比如 <源路径> 可以是一个URL,docker会试图下载这个链接的文件放到<目标文件中去>,权限默认为600,如果想调整需要在增加一层RUN,另外如果是压缩文件还需要额外增加一层RUN进行解压缩, 不如直接使用wget 处理权限来的直接,因此这个功能其实不实用,而且不推荐使用。
如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip , bzip2 以及 xz 的情况下, ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。但在某些情况下,如果我们真的是希望复制个压缩文件进去,而不解压缩,这时就不可以使用 ADD 命令
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY 指令,仅在需要自动解压缩的场合使用 ADD
# 4、CMD
> 启动容器时默认执行的命令,每个Dockerfile只能有一条CMD命令,如果指定多条,只有最后一条会被执行。
CMD 指令的格式和 RUN 相似,也是两种格式:
shell 格式: CMD <命令>
exec 格式: CMD ["可执行文件", "参数1", "参数2"...]
参数列表格式: CMD ["参数1", "参数2"...] 。在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
之前介绍容器的时候曾经说过,Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。 CMD 指令就是用于指定默认的容器主进程的启动命令的。
在指令格式上,一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 " ,而不要使用单引
如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:
CMD echo $HOME
在实际执行中,会将其变更为:
CMD [ "sh", "-c", "echo $HOME" ]
**注意:**Docker 不是虚拟机,容器中的应用都应该以前台执行,对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西
例如: 使用from run cmd copy 制造一个nginx镜像 先下载一个nginx yum源到构造的目录下 http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
[root@dockers nginx]# cat Dockerfile
this Docker file use centos images
Version 1
Author: xiong
Base image to used
FROM centos
COPY nginx.repo /etc/yum.repos.d/
configure nginx start to Reception
RUN yum install -y nginx
&& echo "hello my frist dockerfile" > /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建一个镜像 名称为centos/nginx:v1 [root@dockers nginx]# docker build -t centos/nginx:v1 .
查看镜像是否构建完成 [root@dockers nginx]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos/nginx v1 68c6fc13188a 23 seconds ago 302MB
测试并启动镜像 [root@dockers nginx]# docker run -dit -p 80:80 --name web centos/nginx:v1
![](https://s1.51cto.com/images/blog/201804/04/33fcd667d04e3a8708e440c7159611e9.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
# 5、ENTRYPOINT
> 指定镜像的默认入口命令,该入口会在启动容器时做为根命令执行,所有传入值作为该命令的参数。
ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式和 shell 格式。
exec语法 ENTRYPOINT ["executable","param1","param2"] (exec调用执行); shell语法ENTRYPOINTcommand param1 param2 (shell执行)
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数,当指定了 ENTRYPOINT 后, CMD 的含义就发生了改变,不再是直接的运行其命令,而是将CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:
<ENTRYPOINT> "<CMD>"
例如:使用CMD查询自己本地的IP
[root@dockers curl]# cat Dockerfile
FROM centos
COPY local.repo /etc/yum.repos.d/
RUN yum -y install curl
&& rm -rf /etc/yum.repos.d/local.repo
CMD [ "curl","-s", "https://ip.cn" ]
构建它 [root@dockers curl]# docker run centos/curl:v1
运行 [root@dockers curl]# docker run centos/curl:v1 当前 IP:1.1.1.1 来自:北京市 xx
看起来好像可以直接当成命令使用,但是命令总归有参数,直接运行上面命令,它在构建完成最后一步的时候已经处理完成了,所以增加参数也没有任何效果
如果直接这样运行,那么镜像中指定的CMD命令就被覆盖了 docker run centos/curl curl -i https://ip.cn
而使用ENTRYPOINT就可以解决这个问题
[root@dockers entry]# cat Dockerfile
FROM centos
COPY local.repo /etc/yum.repos.d/
RUN yum -y install curl
&& rm -rf /etc/yum.repos.d/local.repo
ENTRYPOINT [ "curl","-s", "https://ip.cn" ]
执行也是同样的效果了 docker run enrty 当前 IP:1.1.1.1 来自:北京市 xx
这里我们来查询Http头部信息 [root@dockers curl]# docker run enrty -i HTTP/1.1 200 OK Date: Wed, 04 Apr 2018 06:38:35 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Set-Cookie: __cfduid=db95eb1f0c8b7f59fe0bba7e8b8979ae31522823915; expires=Thu, 04-Apr-19 06:38:35 GMT; path=/; domain=.ip.cn; HttpOnly Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" Server: cloudflare CF-RAY: 4061c5e2ecb52882-SJC
这是因为当存在 ENTRYPOINT 后, CMD 的内容将会作为参数传给ENTRYPOINT ,而这里 -i 就是新的 CMD ,因此会作为参数传给 curl
**这里就相当于ENTRYPOINT exec语法 ENTRYPOINT ['exectable','param1','param2']
executable 命令不动,将param1 param2的值 做为可传递参数进行修改。**
docker run enrty -i http://www.baidu.com HTTP/1.1 200 OK Date: Wed, 04 Apr 2018 06:39:40 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Connection: keep-alive Set-Cookie: __cfduid=d1ba6914d20dc0bXXf785572c21994a551522823980; expires=Thu, 04-Apr-19 06:39:40 GMT; path=/; domain=.ip.cn; HttpOnly Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" Server: cloudflare CF-RAY: 4061c7732e8392ee-SJC
# 6、ENV环境变量
> 指定环境变量,在镜像生成过程中会被后续RUN指令使用
> 格式:ENV<KEY> <VALUE> 或 ENV <KEY>=<value>....
例如安装一个JDK
[root@dockers jdk]# cat Dockerfile
FROM centos
COPY jdk-8u77-linux-x64.rpm /usr/src/app/
RUN jdk_dir=/usr/src/app/jdk-8u77-linux-x64.rpm
&& rpm -ivh $jdk_dir
&& rm -rf $jdk_dir
ENV JDK_HOME /usr/java/default/
ENV PATH $JDK_HOME/bin:$PATH
CMD ["java","-version"]
ENV 如果加了变量那么生效就一定是 ENV PATH $key:$PATH
最后测试 [root@dockers jdk]# docker run jdk java version "1.8.0_77" Java(TM) SE Runtime Environment (build 1.8.0_77-b03) Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)
# 7、ARG
> 指定镜像内使用的参数(例如版本号信息等)
构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。
Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令docker build 中用 --build-arg <参数名>=<值> 来覆盖。
来个例子说明:
[root@dockers nginx]# cat Dockerfile
FROM centos
ARG nginx=1.11
COPY nginx.repo /etc/yum.repos.d/
RUN yum install -y nginx
&& echo "hello my frist dockerfile" > /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建一个镜像 [root@dockers nginx]# docker build -t testnginx .
查看历史信息
[root@dockers nginx]# docker history testnginx
IMAGE CREATED CREATED BY SIZE COMMENT
3f54a75772f3 About a minute ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
598c5676eeed About a minute ago /bin/sh -c #(nop) EXPOSE 80 0B
17ec99c29593 About a minute ago |1 nginx=1.11 /bin/sh -c yum install -y ngin… 107MB
1e03021d4d43 About a minute ago /bin/sh -c #(nop) COPY file:c33f426ae65e9d11… 113B
88a51545ba05 About a minute ago /bin/sh -c #(nop) ARG nginx=1.11 0B
2d194b392dd1 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) LABEL name=CentOS Base Im… 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:8d83f3e2c14f39e7f… 195MB
ARG感觉没有实际作用,只能指定构建时的版本信息,一次只能指定一个key=value,并且还占用一层资源
# 8、定义匿名卷
> 创建数据卷挂载点,可以从本地主机或其它容器挂载数据卷,一般用来存放数据库和需要保存的数据等,容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。 格式为: VOLUME <路径>
$ cat Dockerfile FROM centos VOLUME /data
$ docker build -t store .
$ docker run -it store container# df -Th | grep /data /dev/mapper/centos-root xfs 37G 2.9G 34G 8% /data container# touch containessss
查找容器与本地关连的卷 $find / -name containessss /var/lib/docker/volumes/3a17321d0a8a78f2b3c7709b9302bf43bb036c42da2cfc87a5dd1c71e835132b/_data/containessss
# 9、EXPOSE
> EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。
> 在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口
**注意**:EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
此处还是使用nginx做为例子声明80端口
[root@dockers nginx]# vim Dockerfile
FROM centos
COPY nginx.repo /etc/yum.repos.d/
RUN yum install -y nginx
&& echo "EXPOSE 80 , dockerfile from nginx " > /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建镜像 [root@dockers nginx]# docker build -t test/nginx:v2.0 .
查看镜像 [root@dockers nginx]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test/nginx v2.0 120eaeae17d4 About a minute ago 302MB
指定一个随机端口 [root@dockers nginx]# docker run -dit -p :80 --name tnginx test/nginx:v2.0
使用 docker container ls查看容器,最后访问
![](https://s1.51cto.com/images/blog/201804/06/aefa8a43eff9d45f95e221af9130911c.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
# 10、WORKDIR
> 使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在, WORKDIR 会帮你建立目录,因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。
> 格式为 WORKDIR <工作目录路径>
创建前一个要先创建一个空目录,工作目录/tmp/ 复制当前上下文中的testfile.txt文件到容器的/tmp目录下 [root@dockers work]# cat Dockerfile FROM centos WORKDIR /tmp COPY testfile.txt ./ CMD ls /tmp
启动一个容器,因为有CMD执行最后一个命令,显示/tmp目录下的文件 [root@dockers work]# docker run -it --name works work ks-script-IAlIsB testfile.txt yum.log
# 11、USER指定当前用户
> USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。 WORKDIR 是改变工作目录, USER 则是改变之后层的执行 RUN , CMD 以及 ENTRYPOINT 这类命令的身份
> 格式: USER <用户名>
**注意:**USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换
这里用文档中的例子来说明 创建用户并切换redis用户来启动redis-server服务 RUN groupadd -r redis && useradd -r -g redis redis USER redis RUN [ "redis-server" ]
第二个例子.
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/
gosu-amd64"
&& chmod +x /usr/local/bin/gosu
&& gosu nobody true
设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]
# 12、HEALTHCHECK 健康检查
> HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,这是 Docker 1.12引入的新指令。
> 格式:
> HEALTHCHECK [选项] CMD <命令> :设置检查容器健康状况的命令
> HEALTHCHECK NONE :如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting ,在HEALTHCHECK 指令检查成功后变为 healthy ,如果连续一定次数失败,则会变为unhealthy。
HEALTHCHECK 支持下列选项:
--interval=<间隔> :两次健康检查的间隔,默认为 30 秒;
--timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
--retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3次。
**注意: **HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效
空目录下创建一个Dockerfile FROM centos
COPY nginx.repo /etc/yum.repos.d/
RUN groupadd -g 1000 nginx
&& useradd -g 1000 -u 1001 nginx
&& yum -y install nginx
每10秒检查一次,超时时长5秒就退出
HEALTHCHECK --interval=10s --timeout=5s
CMD curl -fs http://127.0.0.1 || exit 1
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建并运行并查看状态,一开始是healthy状态,然后理性成starting说明就启动了,失效了就会改变成 unhealthy状态 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e7982e3ec394 nginx:v3 "nginx -g 'daemon of…" 2 seconds ago Up 2 seconds (health: starting) 0.0.0.0:80->80/tcp Tnginx
最后可以使用 docker inspect 查看容器的状态 使用python json模块打印 docker inspect -f '{{json .State.Health}}' Tnginx | python -m json.tool { "FailingStreak": 0, "Log": [ { "End": "2018-04-07T04:39:29.265404529+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>\nWelcome to nginx!\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 rel="nofollow" href="http://nginx.org/">nginx.org</a>.<br/>\nCommercial support is available at\n<a rel="nofollow" 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": "2018-04-07T04:39:29.177126247+08:00" }, ], "Status": "healthy" }
# 13、ONBUILD 为他人做嫁衣裳
> ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
> Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的
> 格式: ONBUILD <其它指令>
正常镜像写法: [root@dockers onbuild]# cat Dockerfile FROM centos COPY nginx.repo /etc/yum.repos.d/ RUN yum -y install nginx CMD ["nginx", "-g", "daemon off;"]
构建,运行,查看 [root@dockers onbuild]# docker run -dit -p 80:80 --name TestWeb1 web1 96c3a74da2d9ea63664d7a47c298747a5bb2679f1812a74a2d6d235f4989f624 [root@dockers onbuild]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 96c3a74da2d9 web1 "nginx -g 'daemon of…" 2 seconds ago Up 1 second 0.0.0.0:80->80/tcp TestWeb1
但如果很多项目都需要相同的项目只是Html文件不一样哩,那么每次都需要再生成一次Dockerfile文件挺麻烦,这里就可以使用 ONBUILD了。
[root@dockers onbuild]# cat Dockerfile FROM centos ONBUILD COPY nginx.repo /etc/yum.repos.d/ ONBUILD RUN yum -y install nginx ONBUILD CMD ["nginx", "-g", "daemon off;"]
这时构建一下这个Dockerfile生成一个镜像 onbuilds [root@dockers onbuild]# docker build -t onbuilds .
我们在创建一个目录生成一个Dockerfile [root@dockers ontwo]# cat Dockerfile FROM onbuilds
构建这个文件提示nginx.repo不存在,需要将文件都复制在这个目录下 [root@dockers ontwo]# docker build -t onbuild2 . Sending build context to Docker daemon 2.048kB Step 1/1 : FROM onbuilds
Executing 3 build triggers
COPY failed: stat /var/lib/docker/tmp/docker-builder748734679/nginx.repo: no such file or directory
正确的构建方式: [root@dockers ontwo]# docker build -t onbuild2 . Sending build context to Docker daemon 3.072kB Step 1/1 : FROM onbuilds
Executing 3 build triggers
---> Running in fafe6903070c Loaded plugins: fastestmirror, ovl ..................... Successfully built 7f1a2af8e80d Successfully tagged onbuild2:latest
当在各个项目目录中,用这个只有一行的 Dockerfile 构建镜像时,之前基础镜像的那三行 ONBUILD 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 yum以及copy ,生成应用镜像。
[dockerfile 最佳实验手册](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#env)
[dockerfile 官方文档](https://docs.docker.com/engine/reference/builder/#usage)