这里的所有命令都是用于创建镜像所用,都算是为定制镜像的属性指令。
一. COPY
COPY [--chown=<user>:<group>] <src>... <dst> #类shell指令,从上下文中src路径中拷贝文件到镜像的dst路径下
以上命令,将镜像构建上下文(docker build打包的文件中的文件拷贝到镜像目标目录。可以用--chown=user:group修改所属用户和组
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
二. ADD
此功能不常用
三. CMD
1. 背景:
用于指定利用镜像创建容器时默认执行的命令
Docker不是虚拟机,是容器,容器就是进程。那么根据指定镜像创建容器时,就必须指定容器所运行的程序及参数(命令)。CMD指令用于指定默认的容器主进程启动命令。当我们制作镜像时,
在Dockerfile写入
CMD /bin/bash
最后创建了image:tag镜像
docker build -t image:latest .
当我们用该镜像创建容器时输入
docker run -it image:tag
此时容器默认执行/bin/bash命令(Dockerfile中CMD描述的指令,因为以上命令缺省了命令),当然我们也可以指定容器运行其他命令
docker run -it image:tag ls
ls就是其他命令。
2. 命令格式
1)shell格式
CMD <命令>
如果利用shell格式的CMD,那么指令解析为exec格式,只不过可执行文件为shell,第一个参数是-c。例如
CMD echo $HOME
被解析为
CMD ["sh", "-c", "echo $HOME"]
2) exec 格式
CMD ["可执行文件", "arg1", "arg2", ... "argn"]
推荐使用这种方式,该种方式会把方括号中的内容解析为JSON Array,所以一定要用双引号包围内容。
3. 注意:这里需要指出Docker不是虚拟机,是容器,是进程,那么容器中没有后台的概念,所有程序都需要在前台执行,在后台执行意味着退出。如果在Dockerfile中写出如下CMD
CMD service nginx start
被解析为:
CMD ["sh", "-c", "service nginx start"]
这个命令会以shell先启动,创建shell进程,然后shell会fork出一个进程执行 service nginx start命令,该命令结束后,shell进程便会退出,此时容器的主进程停止退出,容器就会退出,因为容器的主进程退出,而容器并不关心辅助进程。
正确的办法是让nginx以前台方式运行(其实就是我们平时启动一个程序,程序会在终端中运行并实时打印信息到终端,后台就是不会和终端交互)。制作镜像是需要写成
CMD ["nginx", "-g", "daemon off;"]
这样容器运行时默认的进程是nginx,而且以前台方式运行。
四、ENTRYPOINT
4.1. 目的:
该命令的格式也是分为exec和shell两种格式,也是用于指定在利用制作完成的镜像创建容器时默认启动程序及参数,并且该参数也可以在创建容器是重新指定,只不过需要用 --entrypoint参数明确指出。如果指定了ENTRYPOINT,CMD的内容会变成参数传递给ENTRYPOINT。即
<ENTRYPOINT> "<CMD>"
4.2. 场景:
1)将镜像变成一个指令一样使用
例如Dockerfile如下:
FROM ubuntu:16.04
RUN agt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
CMD ["curl", "-s","https://ip.cn"]
利用该Dockerfile创建镜像myip:latest
docker build -t myip:latest .
运行容器:
docker run myip:latest
此时会输出ip信息,运行curl,但是此时如果想查看请求头,如何实现?简单想法:
docker run myip:latest -i
这种方式会报错-i不存在,docker run 命令镜像后的内容会被当做一个命令(CMD),运行时会替换制作镜像时dockerfile中CMD指定的命令。所以-i会被当做一个程序,当然不存在。合理的办法是
docker run myip:latest curl -s https://ip.cn -i
但是这样很明显不对,需要重复输入指令,没有达到效果,那么更合理方方式是将-i变成参数,前面提到了-i是覆盖Dockerfile中CMD内容(容器默认启动命令),所以导致出错。另外在有ENTRYPOINT时,CMD内容会被当成参数,所以只要在Dockerfile中加入ENTRYPOINT就可以了。如下
FROM ubuntu:16.04
RUN agt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["curl", "-s","https://ip.cn"]
此时运行:
docker run myip:latest -i #等同于 docker run myip:latest curl -s https://ip.cn -i
总结:如果有ENTRYPOINT,则ENTRYPOINT内容会变成容器创建时执行命令,且CMD变成该命令的参数,如果不存在ENTRYPOINT则CMD是容器创建时执行的命令。即容器创建时默认的执行命令是遵循如下规则.
command = exist(ENTRYPOINT) ? ENTRYPOINT "<CMD>" : CMD
2 )应用指令运行前初始化工作
可以将ENTRYPOINT内容指定为一个脚本,脚本接受CMD作为参数,脚本可以先做一些初始化的工作,而后根据CMD运行容器指令。例如MySQL和PostgreSQL在运行前都需要做初始化,而后运行服务,可以将运行服务的指令放入CMD中,将初始化的任务写成脚本,放入ENTRYPOINT中。
五、ENV
1. 目的:
很简单,即设置环境变量,设置镜像的环境变量,容器运行时存在。
ENV key value #格式一
ENV key1=val1 key2=val2 ...
2. 格式
指令的行为和shell一致,还可以通过$key来获取变量的值。
六、ARG 构建参数
1. 目的
ARG也是和ENV一样,设置环境变量。但是ARG设置的是构建镜像是dockerfile中所用的变量,在创建容器时,是不会存在的。