Dockerfile 构建镜像常用指令
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指定的内容,就是描述该层应当如何构建。
通过使用build 命令,根据Dockerfile的描述来构建镜像
通过源代码的方式
通过标准输入流的方式
通过源代码的路径:
Dockerfile需要放置在项目的跟目录位置
在构建的时候,Dockerfile client会把整个context打包发送到Docker Server端,然后由server端负责build镜像,在构建成功后,会删除context目录
docker build -t {镜像名字} {项目的路径可以是相对路径}
通过标准输入流:
通过标准输入流的方式获取Dockerfile的内容
client不会打包上传context目录,因此对于一些ADD、COPY等涉及host本地文件复制的操作不能够支持
docker build -t {镜像名字} - < Dockerfile路径
使用dockerfile构建镜像步骤
1.手动制作一次镜像
2.根据历史命令来编写dockerfile文件
3.使用dockerfile构键镜像
docker build --network=host -t 镜像名:标签
4.测试镜像
示例nginx
[root@docker01 ~]# mkdir /data/dockerfile/nginx
[root@docker01 ~]# cd /data/dockerfile/nginx
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
ADD nginx.repo /etc/yum.repos.d
RUN yum -y install nginx
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 /data/dockerfile/nginx]# ll
total 100
-rw-r--r-- 1 root root 319 Jan 9 10:11 dockerfile
-rw-r--r-- 1 root root 398 Jan 9 08:47 nginx.repo
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos_nginx:v1 .
[root@docker01 /data/dockerfile/nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos_nginx v1 ddc845aaedd9 28 minutes ago 347MB
centos 7 5e35e350aded 8 weeks ago 203MB
FROM 指定基础镜像
所谓的定制镜像,必须是以一个镜像为基础,在其上进行定制。一个 Dockerfile 中的 FROM 是必备的指定,并且必须是第一条指令。
FROM {bash镜像}
RUN 执行命令
RUN 指令是用来执行命令行命令的,一个Dockerfile可以包含多个 RUN,按照定义顺序执行。(每运行一个run实际是开启一个容器,只是执行完成后删除容器,只保留镜像,所有最终的镜像也是分层叠放在一起的。)
RUN 支持两种运行格式:
- shell 格式:
RUN <cmd>,这个会当做/bin/sh -c “cmd” 运行,就像直接在命令行中输入的命令一样。比如上面的:
RUN echo '<h1>Hello, Nginx!</h1>' > /usr/share/nginx/html/index.html
- exec 格式:
RUN ["可执行文件", "参数1", "参数2", ...],Docker 把它当做json的顺序来解析,必须使用双引号,且可执行文件必须是完整路径。
示例nginx
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/*
RUN curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum -y install nginx
CMD ["nginx", "-g"," daemon off;"]
#生成镜像
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v2 .
#启动进项测试
[root@docker01 /data/dockerfile/nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7_nginx v2 be89fe1379e3 22 minutes ago 418MB
centos_nginx v1 ddc845aaedd9 2 hours ago 347MB
centos 7 5e35e350aded 8 weeks ago 203MB
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v2
c8e5f2c35d75a349840eae203b59d21d3c9ab202b3b3cf77c49d2237bc028ff8
测试访问
因为Dockerfile 中的每一个指令都会建立一层,RUN 也不例外,每一个RUN 的行为,就和我们手工创建镜象的过程一样,新建一层,上面的这种写法创建了4层,这样会产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也容易出错。上面的Dockerfile正确写法应该如下:
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
CMD ["nginx", "-g"," daemon off;"]
这里只是更新yum源安装nginx,实际只是为了安装nginx,这里将四层合成一层更加的一层。
如果需要安装nginx官方的可以参考第一个实例制作即可。只需创建官方源文件copy到对应的保存目录即可。
WORKDIR 指定工作目录
格式:
- WORKDIR <工作目录路径>
使用 WORKDIR 指令可以来制定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,则会自动创建。相当于cd。
COPY 复制文件
格式:
COPY <源路径> ... <目标路径>
COPY ["<源路径1>", ... "<目标路径>"]
和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。
说明:这里测试一个小游戏小鸟飞飞
示例
[root@docker01 /data/dockerfile/nginx]# ll
total 100
-rw-r--r-- 1 root root 292 Jan 9 11:32 dockerfile
-rw-r--r-- 1 root root 398 Jan 9 10:32 nginx.repo
drwxr-xr-x 3 root root 98 Jan 3 19:41 xiaoniao
-rw-r--r-- 1 root root 91985 Jan 8 11:37 xiaoniao.tar.gz
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
COPY xiaoniao .
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v4 .
[root@docker01 /data/dockerfile/nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos7_nginx v4 b79afda2fe6f 39 seconds ago 418MB
centos7_nginx v3 4a93800b7239 20 minutes ago 418MB
centos7_nginx v2 be89fe1379e3 53 minutes ago 418MB
centos_nginx v1 ddc845aaedd9 2 hours ago 347MB
centos 7 5e35e350aded 8 weeks ago 203MB
0 minutes 0.0.0.0:80->80/tcp nice_franklin
[root@docker01 /data/dockerfile/nginx]# docker stop $(docker ps -q)
c8e5f2c35d75
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v4
987610dfb4a6c076a094695bbf382c28c81795e684e4486258d617d6626c6f57
[root@docker01 /data/dockerfile/nginx]# docker exec -it 564a5fb1fd91 /bin/bash
[root@564a5fb1fd91 html]# ls
2000.png 404.html en-US icons index.html poweredby.png
21.js 50x.html icon.png img nginx-logo.png sound1.mp3
测试访问
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
COPY xiaoniao ./xiaoniao
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v5 .
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 81:80 centos7_nginx:v5
[root@docker01 /data/dockerfile/nginx]# docker exec -it cdea08555c04 /bin/bash
[root@cdea08555c04 html]# ls
404.html 50x.html en-US icons img index.html nginx-logo.png poweredby.png xiaoniao
测试访问
从上面示例可以看出, <目标路径> 可以是容器内的相对路径,如果该路径不存在,会自动在复制文件前创建缺失目录,且<目标路径>也可以是相对路径(工作目录可以用WORKDIR 指令来指定)
如果是拷贝目录, 那么在 <目标地址 >也必须写上 拷贝的目录的名称 。否则是将该目录下的所文件拷贝到目标路径
还需注意一点,使用COPY指令,源文件的各种元数据都会保留,比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关的文件都在使用Git进行管理的时候。
ADD 更高级的复制文件
ADD 指令和COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。
比如 <源路径> 可能是一个 URL,这种情况下,会自动去下载这个链接的文件到 <目标路径>里,下载完成的文件权限自动设置为 600,如果不是想要的权限,那么可以通过 RUN 进行权限调整。
如果 <源路径> 为一个 tar 压缩文件的话,或者压缩格式为 gzip,bzip以及xz 的情况下,ADD 指令会自动解压缩这个压缩文件到 <目标路径>去。
示例
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v6 .
[root@docker01 /data/dockerfile/nginx]# docker rm -f $(docker ps -qa )
564a5fb1fd91
cdea08555c04
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v6
访问测试
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao.tar.gz .
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos7_nginx:v7 .
[root@docker01 /data/dockerfile/nginx]# docker rm -f $(docker ps -qa )
09273df6a44b
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos7_nginx:v7
327c84ad622ff1809c586c83c4a28d3562b4638c0d7ebf31c62d88b1cbf3234e
测试访问
VOLUME 定义匿名卷
格式:
VOLUME ["<路径1>","<路径2>"...]
VOLUME <路径>
容器运行时应该尽量保持容器存储层不会发生写操作,对于数据库类需要保存动态数据的应用,其数据文件应该保存在数据卷中,为了防止运行时忘记将动态文件所保存目录挂载为卷,在Dockerfile中,可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
持久化
数据卷(文件或目录)
-v卷名:/data/(第一次卷是空,会将容器中的数据复制到卷中,如果卷里有数据,把卷数据挂载发到容器中)
-v src(宿主机目录):dest(容器的目录)
数据卷容器
--volumes-from (跟某一个已经存在的容器挂载相同的卷)
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
VOLUME /usr/share/nginx/html
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 ~]# docker run -d -p 80:80 centos_nginx:v8
85a83e7686bbbe832b56a1287cb25ee93c81d2bf1f382cff46bd8f0971adc2f9
[root@docker01 ~]# docker inspect 85a83e7686bbbe|grep volume
"Type": "volume",
"Source": "/var/lib/docker/volumes/d3a703e7685bba38585127494e04e2c77cc6c9923d14a8ea719862b506348448/_data",
[root@docker01 ~]# cd /var/lib/docker
docker/ docker-engine/
[root@docker01 ~]# cd /var/lib/docker/volumes/d3a703e7685bba38585127494e04e2c77cc6c9923d14a8ea719862b506348448/_data/
[root@docker01 /var/lib/docker/volumes/d3a703e7685bba38585127494e04e2c77cc6c9923d14a8ea719862b506348448/_data]# ls
2000.png 21.js icon.png img index.html sound1.mp3
测试访问
重新开启一个新的容器,然后修改默认首页
[root@docker01 ~]# docker run -d --name xiaoniao -p 81:80 centos_nginx:v8
测试访问
数据卷容器
--volumes-from (跟某一个已经存在的容器挂载相同的卷)
从新启动一个容器与之前的容器挂载相同的卷
[root@docker01 ~]# docker run -d -p 82:80 --volumes-from xiaoniao centos_nginx:v8
13197ed39494ac33460bb2d71f9276c031a747dcd2ec53fdf11cae9ba722d232
[root@docker01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13197ed39494 centos_nginx:v8 "nginx -g ' daemon o…" 2 minutes ago Up 2 minutes 0.0.0.0:82->80/tcp gifted_lamarr
20aa69a9e60d centos_nginx:v8 "nginx -g ' daemon o…" 12 minutes ago Up 12 minutes 0.0.0.0:81->80/tcp xiaoniao
85a83e7686bb centos_nginx:v8 "nginx -g ' daemon o…" 17 minutes ago Up 17 minutes 0.0.0.0:80->80/tcp elated_keller
测试访问
EXPOSE 声明端口
格式:
- EXPOSE <端口1> [<端口2>...]
EXPOSE 指令是声明运行容器时提供的服务端口,这只是一个声明,在运行时并不会因为这个声明就会开启这个端口的服务。在Dockerfile中写入这样的声明有两个好处,一个是帮助使用镜像者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射EXPOSE的端口。
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
EXPOSE 80 22
CMD ["nginx", "-g"," daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build -t centos_nginx:v9 .
[root@docker01 /data/dockerfile/nginx]# docker run -d -P centos_nginx:v9
b0f5378bf1ff847ea896dcadcae341e0db225ee623229e4c30b73d8c296f8f67
[root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b0f5378bf1ff centos_nginx:v9 "nginx -g ' daemon o…" 11 seconds ago Up 10 seconds 0.0.0.0:32769->22/tcp, 0.0.0.0:32768->80/tcp busy_joliot
CMD 容器启动命令
容器不是虚拟机,容器就是进程,既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定容器默认的主进程启动命令的。(容易被改变)
CMD 指令有三种格式:
shell 格式: CMD <命令>
exec 格式:CMD ["可执行文件","参数1","参数2", ...]
参数列表格式:CMD ["参数1","参数2",...] , 这个时候CMD作为 ENTRYPOINT的参数。
在指令格式上,一般推荐使用exec 格式,这类格式在解析时会被解析为JSON数组,因此一定要使用双引号 “” ,而不要使用单引号。
如果使用shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:
CMD echo $HOME</pre>
在实际执行中,会将其变更为:
CMD ["sh", "-c", "echo $HOME"]
在运行时可以指定新的命令来替代镜像设置中的这个默认命令,比如,centos镜像默认的 CMD 是 /bin/bash, 如果我们直接 docker run -it centos 的时候,会直接进入 bash。我们也可以在运行时指定别的命令,如 docker run -it centos cat /etc/passwd 。这就是用 cat /etc/passwd命令替换了默认的 /bin/bash 命令了。
前台执行和后台执行的问题:
Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 start / systemctl 去启动后台服务,容器内没有后台服务的概念。
比如:如果我们将CMD 写成这样:
CMD service nginx start
然后会发现容器执行后就立刻退出了,甚至在容器内使用systemctl 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器
对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。
而使用 service nginx start 命令,则是希望 start来已后台守护进程形式启动nginx服务。而 CMD service nginx start 会被理解为 CMD ["sh", "-c", "service nginx start"],因此主进程实际上是sh, 那么当service nginx start 命令结束后,sh 也就结束了, sh 作为主进程退出了,自然就会令容器退出。
正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式。如:
CMD ["nginx", "-g"," daemon off;"]
ENTRYPOINT 入口点
ENTRYPOINT 的格式和 RUN 指令格式一样,分为 exec 格式 和shell格式。
ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比CMD要繁琐,需要通过 docker run 的参数 --entrypoint 来指定。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:
ENTRYPOINT "<CMD>"
那么问题来了? 有了CMD ,为什么还要有ENTRYPOINT呢? 这种 ENTRYPOINT "<CMD>" 有什么好处呢?
场景一:让镜像变成命令一样使用
假设我们需要一个得知自己当前公网IP的镜像,那么可以使用CMD来实现:
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
CMD ["curl","-s","https://ip.cn"]
[root@docker01 /data/dockerfile/nginx]# docker build -t centos:vi .
Sending build context to Docker daemon 267.8kB
Step 1/2 : FROM centos:7
---> 5e35e350aded
Step 2/2 : CMD ["curl","-s","https://ip.cn"]
---> Running in 1cd0d54ea938
Removing intermediate container 1cd0d54ea938
---> 5dfbd8f7e484
Successfully built 5dfbd8f7e484
Successfully tagged centos:vi
[root@docker01 /data/dockerfile/nginx]# docker run centos:vi
{"ip": "101.81.163.198", "country": "上海市", "city": "电信"}
这样看起来是可以当做命令来使用,不过命令一般有参数,CMD 中实质的命令是 curl, 如果希望显示HTTP头信息,就需要加上 -i 参数。
[root@docker01 /data/dockerfile/nginx]# docker run centos:vi -i
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"-i\": executable file not found in $PATH": unknown.
这里可以看到可执行文件找不到的报错, executable file not found。上面说过,跟在镜像名后面的是 command, 运行时会替换 CMD 的默认值,因此这里的 -i 替换了原来的 CMD,而不是添加在原来的 curl -s https://ip.cn 后面,而 -i 根本不是命令,所以找不到。
那么如果我们希望加入 -i 参数,我们就必须重新完整的输入这个命令:
[root@docker01 /data/dockerfile/nginx]# docker run centos:vi curl -i https://ip.cn #替换了默认的CMD
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 67 0 67 0 0 16 0 --:--:-- 0:00:03 --:--:-- 16
HTTP/1.1 200 OK
Date: Thu, 09 Jan 2020 12:26:29 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=d0419e04a3638ee1d3087c5cf80f494101578572789; expires=Sat, 08-Feb-20 12:26:29 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 5526665d7be1e4cc-LAX
{"ip": "101.81.163.198", "country": "上海市", "city": "电信"}
显然这不是好的解决办法,而是用 ENTRYPOINT 就可以解决这个问题:
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
ENTRYPOINT ["curl","-s","https://ip.cn"]
[root@docker01 /data/dockerfile/nginx]# docker build -t centos:v1 .
Sending build context to Docker daemon 267.8kB
Step 1/2 : FROM centos:7
---> 5e35e350aded
Step 2/2 : ENTRYPOINT ["curl","-s","https://ip.cn"]
---> Running in f992ba880014
Removing intermediate container f992ba880014
---> 8b36c3256b89
Successfully built 8b36c3256b89
Successfully tagged centos:v1
[root@docker01 /data/dockerfile/nginx]# docker run centos:v1
{"ip": "101.81.163.198", "country": "上海市", "city": "电信"}
[root@docker01 /data/dockerfile/nginx]# docker run centos:v1 -i
HTTP/1.1 200 OK
Date: Thu, 09 Jan 2020 12:32:11 GMT
Content-Type: application/json; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: __cfduid=d3c35693353a4686d5d6eebe258bb5d5c1578573131; expires=Sat, 08-Feb-20 12:32:11 GMT; path=/; domain=.ip.cn; HttpOnly; SameSite=Lax
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 55266eb75f10e4d0-LAX
{"ip": "101.81.163.198", "country": "上海市", "city": "电信"}
从上面可以看出,不加-i 参数可以运行成功, 加上-i参数也可以运行成功,且达到了curl -i 的效果。这是因为当存在 ENTRYPOINT 后,CMD 的内容将会作为参数传递给 ENTRYPOINT ,而这里 -i 就是新的CMD,因此会作为参数传递给curl,从而达到了我们预期的效果。
场景二:应用运行前的准备工作
启动容器就是启动主进程,但有些时候,启动主进程前,需要一些准备工作。比如 mysql 类的数据库,可能需要一些数据库配置、初始化的工作,这些工作要在最终的mysql 服务器运行之前解决。此外,可能希望避免使用root 用户去启动服务器, 从而提高安全性,而在启动还需要以 root 身份执行一些必要的准备工作,最后切换到服务用户启动服务等。或者除了服务外,其它命令依旧可以使用 root 身份执行,方便调试等。
这些准备工作是和容器 CMD 无关的,无论CMD 是什么,都需要事先进行一个预处理的工作。这种情况下,可以写一个脚本,然后放入 ENTRYPOINT 中去执行,而这个脚本会接到的参数 (也就是CMD)作为指令,在脚本最后执行。比如官方的redis 中就是这样做的:
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD ["redis-server"]
可以看到其中为了 redis 服务创建了redis 用户,并在最后指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本。
#!/bin/bash
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
chown -R redis .
exec su-exec redis "$0" "$@"
fi
exec "$@"
该脚本的内容就是根据 CMD 的内容来判断,如果是 redis-server 的话,则切换到 redis用户身份启动服务,否则依旧使用 root 身份执行。比如:
# docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
示例1
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build -t centos_nginx:v10 .
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos_nginx:v10
ff772730832e18068e01da1d1ce654314f222dc33e4628b8db8cc5b6f3703176
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 81:80 centos_nginx:v10 sdhdhj
6f7772a652a5759aff7ab635453b300a775b7a1d91a30a08a5c55999c2987799
[root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ff772730832e centos_nginx:v10 "nginx -g 'daemon of…" 32 seconds ago Up 31 seconds 0.0.0.0:80->80/tcp strange_pare
[root@docker01 /data/dockerfile/nginx]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6f7772a652a5 centos_nginx:v10 "nginx sdhdhj" 20 seconds ago Exited (1) 20 seconds ago tender_rubin
ff772730832e centos_nginx:v10 "nginx -g 'daemon of…" 37 seconds ago Up 37 seconds 0.0.0.0:80->80/tcp strange_pare
示例2
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
RUN rm -rf /etc/yum.repos.d/* \
&& curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo \
&& curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo \
&& yum -y install nginx
WORKDIR /usr/share/nginx/html
RUN rm -rf ./*
ADD xiaoniao .
ADD init.sh /init.sh
ENTRYPOINT ["/bin/bash","/init.sh"]
[root@docker01 /data/dockerfile/nginx]# docker build -t centos_nginx:v11 .
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 80:80 centos_nginx:v11
eeac9eef5c6941bbd9581d4304d8f7f48ad89a39aecfd2e6479c50f9d424a144
[root@docker01 /data/dockerfile/nginx]# docker run -d -p 81:80 centos_nginx:v11 1111 #参数被当成参数,该脚本没有参数,所以可以正常启动
fb656151c391b84411cb8bfb6e2b8e5b6b321dc9ca1246f2b00026e13c01ed89
[root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fb656151c391 centos_nginx:v11 "/bin/bash /init.sh …" 10 seconds ago Up 10 seconds 0.0.0.0:81->80/tcp lucid_wilson
eeac9eef5c69 centos_nginx:v11 "/bin/bash /init.sh" 29 seconds ago Up 28 seconds 0.0.0.0:80->80/tcp affectionate_pike
[root@docker01 /data/dockerfile/nginx]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fb656151c391 centos_nginx:v11 "/bin/bash /init.sh …" 30 seconds ago Up 29 seconds 0.0.0.0:81->80/tcp lucid_wilson
eeac9eef5c69 centos_nginx:v11 "/bin/bash /init.sh" 49 seconds ago Up 48 seconds 0.0.0.0:80->80/tcp affectionate_pike
ENV 设置环境变量
格式:
ENV <key><value>
ENV<key1>=<value1> <key2>=<value2>
这个指令很简单,就是设置环境变量,无论是后面的其它指令,如RUN,还是运行时的应用,都可以直接使用这里定义的环境变量。
[root@docker01 /data/dockerfile/nginx]# Dockerfile #查看编辑好的Dockerfile文件内容
FROM centos
ENV name1 xiaobai
ENV name2=zhazha name3=cainiao
[root@docker01 /data/dockerfile/nginx]# build -t mycentos:v5 . #构建镜像
[root@docker01 /data/dockerfile/nginx]# run --rm -it --name test mycentos:v5 #启动一个容器,并以交互式启动
[root@b55929b2c63f /]# echo $name1 #进入容器后调用环境变量name1
xiaobai
[root@b55929b2c63f /]# echo $name2 $name3 #调用环境变量name2,name3
zhazha cainiao
定义了环境变量,可以在后续的指令中直接使用环境变量
FROM centos
ENV URL="https://nginx.org/download/"
ADD $URLnginx.1.17.7.tar.gz /usr/local/
示例
[root@docker01 /data/dockerfile/nginx]# vim dockerfile
FROM centos:7
ADD http://nginx.org/download/nginx-1.17.7.tar.gz /opt/
RUN tar -xvzf /opt/nginx-1.17.7.tar.gz -C /usr/local/src/ \
&& useradd -M -s /sbin/nologin nginx
WORKDIR /usr/local/src/nginx-1.17.7
RUN ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-file-aio --with-http_ssl_module --wi
th-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with
-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module -
-with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index
_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module && make &&
make install
VOLUME ["/usr/local/nginx/html"]
RUN rm -rf /opt/nginx-1.17.7.tar.gz
ENV PATH=/usr/local/nginx/sbin:$PATH
EXPOSE 80
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
[root@docker01 /data/dockerfile/nginx]# docker build --network=host -t centos_nginxsrc:v4 .
[root@docker01 /data/dockerfile/nginx]# docker run -d -P centos_nginxsrc:v4
root@docker01 /data/dockerfile/nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8544d8311d16 centos_nginxsrc:v4 "nginx -g 'daemon of…" 4 minutes ago Up 4 minutes 0.0.0.0:32771->80/tcp gallant_neumann
访问测试
ARG 构建参数
格式:
- ARG <参数名>[=<默认值>]
构建参数和 ENV的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境和环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的。
Dockerfile 中的ARG指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。