docker简单使用(三)
Dockerfile简介二
简单介绍Dockerfile的一些指令
COPY
COPY,复制文件指令,格式:
1 | COPY <源路径> <目标路径> |
另一种格式类似函数调用:COPY ["<源路径>",... "<目标路径>"]
COPY指令作用是从构建上下文目录中<源路径>
(相对于上下文的路径)下的文件复制到镜像中的目标路径
示例:
1 | COPY test1.txt /usr/local/test/ |
将当前上下文目录下的test1.txt复制到新镜像的/usr/local/test/文件夹下
<目标路径>可以是容器内的绝对路径,也可以是相对于WOKDIR
的相对路径
ADD
不实用的命令,有拷贝的功能,同时,可以拷贝url,但是使用ADD url时,docker会先将url的文件下载下来,如果是个压缩包,还需要自己添加一层RUN进行解压,剔除不需要的文件,再复制;所以不如直接RUN wget,然后解压缩,剔除文件复制
ADD命令在有一种情况下很有用,源路径是一个gzip,bzip2以及xz的压缩包,ADD会自动解压压缩包到目标路径。
在《docker practice》中指出,在COPY和ADD指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY指令,仅在需要自动解压缩场合使用ADD
CMD
首先要理解,容器是进程,不是虚拟机,在正常linux中,运行进程一般都伴随有启动参数,类似的,容器也有,CMD就是用于指定默认的容器主进程的启动命令的
格式:shell
格式:CMD <命令>
exec
格式:CMD ["可执行文件", "参数1", "参数2"...]
在运行时,可以使用CMD来指定新的命令来替代镜像中设置的默认命令,例如,ubuntu镜像的默认CMD是/bin/bash
,如果我们直接docker run -it ubuntu:16.04
会直接进入bash
;也可以指定别的命令,例如输出系统版本:docker run -it ubuntu:16.04 cat /etc/os-rlease
在指令格式上,推荐使用CMD ["可执行文件", "参数1", "参数2"...]
,这类格式会被解析成JSON,因此一定要是用双引号"
如果使用shell格式,例如CMD echo $JAVA_HOME
在实际执行中会变更为CMD ["sh", "-c", "echo $JAVA_HOME"]
,实际会被包装成sh -c
的参数形式。(这也是shell中可以直接使用环境变量的原因)
前面说过,容器其实就是进程,本身就是进程,所以就不存在什么进程里面的程序有后台执行的说法了,容器中的应用,都是在前台执行的,不存在systemctl这样的操作,不会像虚拟机中有systemctl start mysqld
这样的操作,如果使用CMD写成这样的:CMD systemctl start nginx
,会发现容器执行后立刻退出。
这是因为,docker容器,默认会把容器内部第一个进程,也就是pid=1的程序作为docker容器正在运行的依据,如果容器中pid=1的程序挂了,那docker容器就会直接退出;在执行CMD systemctl start nginx
时候,实际执行的是CMD ["sh", "-c", "systemctl start nginx"]
,起初pid=1的程序是bash,但是后面接上了systemctl start nginx(后台守护模式daemon启动nginx),使得systemctl start nginx进程启动后,sh也结束了(CMD会更改默认命令),当pid=1的程序结束,容器就退出了。所以只要运行程序时候,在非守护模式下,容器就不会退出,因此,容器内启动nginx可以:CMD ["nginx", "-g", "daemon off"]
ENTRYPOINT
和CMD类似,不同的是,CMD会被docker run覆盖,而ENTERPOINT不会,例如以下一个最简单的镜像:
1 | FROM ubuntu:16.04 |
构建镜像:docker build -t echotest .
,
运行容器:
1 | [root@localhost myip]# docker run echotest |
但是既然容器是进程,那如果像其他进程一样,加参数,效果如何:
1 | [root@localhost myip]# docker run echotest -i |
直接就报错了。。这是因为跟在镜像名之后的执行,会替换调CMD的默认值,但是-i又不是个指令,所以就报错了。
ENTRYPOINT在这点上就可以做到带参数,例如:
1 | FROM ubuntu:16.04 |
构建:docker build -t echotest2 .
,运行容器:
1 | [root@localhost echotest]# docker run echotest2 -i |
可以看到,-i可以带进去。
ENV
顾名思义,设置环境变量ENV JAVA_VERSION 1.8.0_191
ARG
和ENV类似,都是设置环境变量。区别在于:
1 | The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build command using the --build-arg <varname>=<value> flag.ARG指令定义了用户可以在编译时或者运行时传递的变量,如使用如下命令:--build-arg <varname>=<value> |
例如,在Dockerfile中定义:
1 | ARG a_key1 |
ARG指令定义的参数,在docker build命令中可以通过–build-arg a_key1=avalue1来覆盖
VOLUME
创建一个可以从本地主机或其他容器挂载的挂在点,格式:VOLUME ["/data"]
对于数据库类需要保存动态数据的应用,数据库文件应该保存在卷(volume)。为了防止运行时用户忘记将动态文件所保存的目录挂在为卷,在写Dockerfile时,就可以事先指定某些目录挂载为匿名卷,这样运行时如果用户不挂载,应用也可以正常运行,不会向容器存储层写大量数据
VOLUME ["/data"]
,这里的/data
目录就会在运行时自动挂载为匿名卷,任何向/data中写入的信息都不会记录进容器存储层。当然,docker run时候也可以覆盖这个设置:
1 | docker run -d -v mydata:/data/tmp mysql |
EXPOSE
声明端口,格式:EXPOSE <port1> <port2>
用来指定要映射出去的端口,例如,容器内部开启了nginx,就需要将80(或者指定的端口)暴露出去:EXPOSE 80
。这个需要-P(大写)配合,启动容器是,加上-P,让它自动分配。如果想指定具体端口,使用-p(小写)
WORKDIR
指定工作目录,格式:WORKDIR <工作目录路径>
,作用就是为后续的RUN
、COPY
等指定工作目录
在Dockerfile中,每个RUN,都会启一个容器,都是一个启动容器
、执行命令
、提交存储层文件变更
的操作。
示例,如果Dockerfile这么写:
1 | FROM ubuntu:16.04 |
然后构建镜像:
1 | [root@localhost myworkfile]# docker build -t workfile1 . |
可以看到,构建过程中,出现了两个中间容器a33b48c39597
和500473ff3363
,所以这两个RUN,其实运行的是不一样的容器,最终结果,启动容器后在/home/myapp
下可能就找不到test1.txt
,验证:
1 | [root@localhost myworkfile]# docker run -it workfile1 bash |
没有找到test1.txt
使用WORKDIR:
1 | FROM ubuntu:16.04 |
构建镜像,运行容器,查看:
1 | [root@localhost myworkfile2]# docker build -t workfile2 . |
1 | [root@localhost myworkfile2]# docker run -it workfile2 bash |
看到在/home/myapp
下有test2.txt
,并且内容是我们指定的。
Ps 在Dockerfile中,这边只使用WORKDIR /home/myapp
并没有创建该目录,但是,后面执行就是在该目录下,这是因为WORKDIR
会帮你建立目录
USER
和WORKDIR
类似,指定之后执行RUN
、COPY
等命令的用户;
示例一:
1 | FROM ubuntu:16.04 |
构建镜像:
1 | [root@localhost myuser2]# docker build -t myuser2 . |
发现报错,kyle该用户不存在,镜像myuser2
构建不成功
示例二:
1 | [root@localhost myuser1]# cat Dockerfile |
构建镜像,运行容器:
1 | [root@localhost myuser1]# docker build -t myuser1 . |
1 | [root@localhost myuser1]# docker run -it myuser1 bash |
成功构建镜像,切换用户。
这两个示例可以看出,使用USER
之前,必须创建好用户,也就是**USER
不会帮你建用户,只是切换用户**
MAINTAINER
指定制作作者信息,格式:MAINTAINER <name>
,例如MAINTAINER kyle kyle@xxx.com
以上,就是Dockerfile一些常用的指令,基本是借鉴的《docker practice》…