Dockerfile

关于Docker基础,请看http://blog.zhuxyid.com/2018/11/23/Docker

自制镜像方式

​ 基于容器方式来制作镜像,这种方法配置繁琐,不适合使用,每次配置文件更该都需要制作镜像

​ 基于Dockerfile

Dockerfile

Dockerfile是一个文本文档,包含用户可以在命令行上调用命令组合,使用docker build用户可以自动构建一个连续执行多个命令行

Dockerfile编译完成科研使用docker build来进行编译Dockerfile文件

1
docker build -t IMAGE_NAME:TAGS /DOCKERFILE_PATH/Dockerfile

Dockerfile注意事项

1
2
3
4
5
Comment		#注释 
INSTRUCTION arguments #指令 参数(指令不区分大小写,约定惯例尽量使用大写)
docker运行指令是按照你的指令的顺序。
Dockerfile文件首字母必须是大写
.dockeringore定义忽略哪些文件,打包时不包含这些文件

Dockerfile语法

FROM

FROM指令是最重的 一个且必须为Dockerfile文件开篇的第一个非注释行,

用于镜像文件构建过程 指定基准镜像,后续的指令运行于 此基准镜像 所提供的运行环境

实践中,基准镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,其不存在时,则会从Docker hub registry拉去所需的镜像文件 (如果找不到指定的镜像文件,docker build会返回一个错误信息)

1
2
3
4
5
6
7
8
9
语法:
FROM <repository>[:<tag>]
FROM <repository>@<digest> @digest指定hash码进行验证
<repository>:指定作为base image的名称
<tag>:base image的标签 可选性,默认是latest

例子:
# Description: test img
FROM busybox:latest
MAINTANIER

用于让dockerfile制作者提供信息,Dockerfile并不限制MAINTAINER指令可在出现的位置,但推荐放在FROM指令后

1
2
3
4
5
6
7
8
9
10
11
语法:
MAINTAINER <authtor's detail>
<authtor's detail> 可以是任何文本信息,但是通常使用名称和地址邮箱

例子:
MAINTAINER "zhuxyid <zhuxyid@gmail.com>"

LABLE:指定镜像元数据
Syntax:LABEL <key>=<value> <key>=<value> <key>=<value>
例子:
LABEL maintainer="zhuxyid <zhuxyid@gmail.com>"
COPY

用于从Docker主机复制文件到创建的新镜像文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
语法:
COPY <src><dest>
COPY ["<src>",.."<dest>"] 如果路径有空白字符时候,用这种方式复制
src 源文件,支持使用通配符
dest 目标路径 即正在创建image文件系统路径,建议<dest>为绝对路径,否则COPY指定则以WORKDIR为起始路径

文件复制准则:
<src> 必须是build上下文中路径,不能是父目录的文件
如果<src>是目录,则 内部文件或子目录都会 被递归复制,但<src>自身目录不会被复制 相当于 cp -r src/* /dest
如果指定了多个<src>,或在<src>中使用通配符,则<dest>必须是一个目录,且必须以/结尾 相当于 cp -r src/* /desc/
如果<dest>不存在,则会被自动创建,这包括其父目录路径

例子:
COPY index.html /data/web/html/ #这里index.html文件必须先创建,而且必须为build目录下!
ADD

ADD指令类似COPY指令,ADD支持使用TAR文件和URL路径

1
2
3
4
5
6
7
8
9
10
11
语法:
ADD <src>,..<dest>
ADD ["<src>",.."<dest>"]

操作准则:
同COPY指令
如果<src>为URL且<dest>不以/结尾,则<src>指定的文件将被下载并直接创建为<dest>;
如果<dest>以/结尾,则文件名URL指定文件将被直接下载并保持为<dest>/<filename>
如果<src>是一个本地系统上的压缩格式tar文本,他将被展开为一个目录,其行为类似"tar -x"命令,然而通过URL获取到的tar文件将不会自动展开
如果<src>有多个,或其间接或直接使用通配符,则<dest>必须是一个以/结尾的目录路径;
如果<dest>不以/结尾,则其被视作一个普通文件,<src>的内容将被直接写入到<dest>
WORKDIR

用于为Dockerfile中所有的RUN,CMD,ENTRYPOINT,COPY和ADD指定设定工作目录

1
2
3
4
5
6
7
8
语法:
WORKDIR <dirpath>

在Dockerfile文件中,WORKDIR指定可出现多次,其路径也可以为相对路径,不过其是相对此前一个WORKDIR指令指定的路径,另外,WORKDIR也可以调用由ENV指定定义的变量

例子:
WORKDIR /var/log
WORKDIR $STATEPATH
VOLUME

用于在image中创建一个挂载点目录,以挂载Docker host上的卷或其他容器上的卷

1
2
3
4
语法:
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]
如果挂载点目录路径下此前在文件存在,docker run命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中
EXPOSE

用于为容器打开指定要监听的端口以实现与外部通讯,注意,这里只能是定义容器的端口.后期docker下载下来宿主机的端口并不确定

1
2
3
4
5
6
7
8
9
语法:
EXPOSE <port> [/<protocol>][<port>[/<protocol>]...]
<protocol>用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议

EXPOSE指令可一次指定多个端口,如
EXPORT 11211/udp 11211/tcp


docker run --name ttt -P 可以自动暴露需要暴露的端口
ENV

用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其他指令(如ENV,ADD,COPY等)所调用 WORKDIR只是工作目录

调用格式为$variable_name或者${variable_name}

1
2
3
4
5
6
7
8
9
语法:
ENV <key><value>
ENV <key>=<value>
${NAME:-tom} #如果NAME变量没有值,将他设为tom,如果有值则使用本身
${NAME:+tom} #如果NAME为空则不设置,如果不为空则设置tom

第一种格式中,<key>之后的所有内容均被视作其<value>的组成部分,因此,一次只能设置一个变量
第二种格式中,可用一次设置多个变量,每个变量为一个<key>=<value> 的键值对,如果<value>中包含空格,可用反斜线(\)进行转移;也可以通过对<value>加引号进行标识,另外反斜线可以续行
定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能
RUN

用于指定docker build过程中运行的程序,其可以是任何命令

1
2
3
4
5
6
7
8
9
语法:
RUN <command>
RUN ["<executable>","<param1>","<param2>"]

第一个格式中,<command>通常是一个shell命令,且以“/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1,不能接受Unix型号,
因此,当使用docker stop container命令停止容器时,次进程接受不到SIGTERM信号
第二个格式中,参数就是一个JSON格式的数组,其<executable>为运行的命令,后面的<paramN>为传递给命令的选项或参数;然而此格式指定的命令不会以"/bin/sh -c"来发起;
因此常见shell操作,如变量替换以及通配符(?,*等)替换将不会进行;不过如果要运行的命令依赖次shell特性的话,可以将其替换为类似下面格式
RUN ["/bin/bash","-c","<excutable>","<param1>"]
CMD

类似RUN指令,CMD指令也可以用于运行任何命令或应用程序,不过二者运行时间点不同

RUN 指令运行与镜像文件构建过程,而CMD指令运行基于Dockerfile构建出的新映像文件启动一个容器时

CMD 指定的首要 目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器将终止;不过CMD指令的命令其可以被docker run的命令选项所覆盖

在Dockerfile中可以存在多个CMD指令,但仅是最后一个会生效,而RUN不是

1
2
3
4
5
6
7
语法:
CMD <command>
CMD ["<excutable>","<param1>","<param2>"]
CMD ["<param1>","<param2>"]

前两种语法格式的意义相RUN
第三种则用于为ENTRYPOINT指令提供默认参数

RUN 是运行在docker build过程中的命令,而CMD 是在docker run运行时的命令

注意:一个容器只是用于单个应用。nginx,redis,mysql都是运行在后台

所有进程都是一个进程的子进程,除了init。init是内核启动

比如手动启动nginx,它是shell的子进程,有些shell子经常会占据终端窗口,需要加&符号

nginx & 这里nginx父进程依然是shell,当shell结束后,会将nginx也结束

nohup nginx & 这里是将nginx送到后台,重新赋予一个新的进程,这是shell退出这个依然存在

在用户空间先启动shell,才能使用ls,cat,等命令,可以直接exec执行命令.

在容器中可以基于shell启动程序,也可以通过exec启动程序

在json数组中,引号一定要写双引号,单引号可能会出现问题

ENTRYPOINT

类似CMD指定的功能,用于为容器指定默认运行程序,从而使得容器像一个单独的可执行程序

于CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令指定的参数所覆盖,而且,这些命令参数会被当做参数传递给ENTRYPOINT指定指定的程序

不过,docker run命令的–entrypoint选项参数可覆盖ENTRYPOINT指令指定的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
语法:
ENTRYPOINT <command>
ENTRYPOINT ["<executable>","<param1>","<param2>"]

ENTRYPOINT /bin/http -f -h /data/www/html
docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到EMTRYPOINT命令最后作为其参数使用
Dockerfile文件中可用存在多个ENTRYPOINT指令,但仅最后一个生效

如果Dockfile格式是这样CMD和ENTRYPOINT同时存在
CMD ["/bin/httpd","-h","/data/web/html"]
CMD /bin/httpd -h /data/web/html #这样写会出错,因为不会被当成参数,需要写成列表的形式
ENTRYPOINT /bin/httpd -f -h /data/web/html #如果需要CMD传参,这里建议写成列表,不然有问题
CMD和ENTRYPOINT同时存在,那么CMD会将命令变成参数传递给ENTRYPOINT

例子
CMD ["/bin/httpd","-f","-h","/data/web/html"]
ENTRYPOINT ["/bin/sh","-c"]
#CMD将传给ENTRYPONT作为默认参数
#如果执行docker run --name ttt --rm image:v1 "ls /data"
#"ls /data"则会覆盖CMD
#如果执行docker run --name ttt --entrypoint "ls" --rm image:v1 "/data"
#ls会覆盖EXTRYPOINT,/data覆盖CMD
USER

用于指定运行image时或者运行Dockerfile中任何RUN,CMD或者ENTRYPOINT指令指定的程序时的用户名或UID

默认情况下, container运行身份是root用户.

1
2
3
语法
USER <UID>|<USERNAME>
需要注意:<UID>可以是任意数字,但实践中必须为容器中/etc/passwd中某用户的有效UID,否则docker run会失败
HEALTHCHECK

健康状态检查,判断容器里面的程序是否正常运行.

这里需要注意,如果nginx指定的目录不存在,nginx也会运行,但是用户访问是访问不了的,可以断定这虽然是可以运行但不是想要的结果,比如使用curl检测网页200的信息,如果是200则正常,非200则不正常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HEALTHCHECK定义一个CMD来检测容器中主进程工作状态与否 
HEALTHCHECK NONE 拒绝任何监控状态检查
格式: HEALTHCHECK [OPTION] CMD
--interval=DURATION(default:30s) 每隔多久
--timeout=DURATION(default:30s) 超时时长
--start-period=DURATION(default:0s) 等待主进程初始化完成在检查,如果tomcat这种应用建议等待5秒
--retries=N(default:3) 检查次数,默认3次

#检测状态结果:
0:success
1:unhealthy
2:reserved

实例:
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1 #每隔5分钟检查,超时时间3s,curl结果如果成功则不管他,如果不成功则返回1
SHELL

linux默认shell是[“/bin/sh”,”-c”]

windows默认是[“cmd”,”/S”,”/C”]

格式:SHELL ["/bin/bash","-c"]

STOPSIGNAL

定义停止的信号,默认是15

格式: STOPSIGNAL 14可以修改停止的信号

ARG

ARG的参数只是一个变量,只在docker build时候使用,在--build-arg <varname>=<value>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
语法:
ARG <name>[=<default value>]
ARG version=1.14
ARG user=zhuxyid

例子:
FROM:nginx:${version}
ARG version=1.15
LABEL maintainer=${author}
ARG version="1.15"
ARG author="zhuxyid <772931883@qq.com>"

docker build -t nginx ./
docker build --build-arg "version=1.16" --build-arg "author=zhuxyid<hello@qq.com>" -t nginx ./ #可以直接在编译时候使用

在docker build中可以用--build-arg参数来传值
ONBUILD

用于在Dockerfile中定义一个触发器

Dockerfile用于build映像文件,此映像文件亦可作为base image被另一个Dockerfile作用FROM指令的参数,并以之构建新的映像文件

在后面的这个Dockerfile中FROM指令在build过程中被执行,将会”触发”创建其base image的Dockerfile文件中的ONBUILD指令定义的触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
语法:
ONBUILD <INSTRUCTION>
#尽管任何指令都可注册成触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令
#使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,例如ruby:2.0-onbuild
#在ONBUILD指令中使用ADD或者COPY要格外小心,因为新构建过程中的下下文在缺少指定源文件会失败

例子:
cat /dockerfile/base
FROM centos:latest
ONBUILD RUN yum install nginx gcc gcc-c++
#docker build -t base.img ./ #这里并不去安装nginx gcc gcc-c++

cat /dockerfile/phpproject
FROM base.img #指定基于刚才创建的base.img作为base image
RUM yum install php-5.6
#docker build -t php:v1 ./ #这里才会去安装,
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
mkdir /data/container/web1 
cp -r /etc/yum.repos.d /data/container/web1/
cd /data/container/web1
wget <http://mirror.bit.edu.cn/apache/tomcat/tomcat-7/v7.0.90/bin/apache-tomcat-7.0.90.tar.gz>

#more Dockerfile

#Description: test dockerfile
FROM busybox:latest
LABEL maintainer="zhuxyid <zhuxyid@gmail.com>"
ENV DOC_ROOT /data/web/html/ #定义DOC_ROOT变量
ENV TOMCAT_ROOT=/data/tomcat/ \ #定义多个变量
TOMCAT_VERSION="tomcat-7.0.90" \
NGINX_VERSION="nginx-1.14.0.tar.gz"
COPY index.html $DOC_ROOT
COPY yum.repos.d /etc/yum.repos.d/
WORKDIR /opt/
ADD http://nginx.org/download/nginx-1.14.0.tar.gz ./
VOLUME /data/www
ADD apache-${TOMCAT_VERSION}.tar.gz ${TOMCAT_ROOT:-/data/tomcat/}
EXPOSE 80/tcp
RUN cd /opt && \
tar xf ${NGINX_VERSION} && \
mv nginx-1.14.0 /usr/local/nginx #建议使用一条命令,因为如果多个RUN 那么层级就会越多
echo "this is docker build image" > index.html


docker build -t zhuxyid/index:v1 ./
#-t指定name:tag

docker run --name web1 -it --rm zhuxyid/index:v1 cat /data/web/html/index.html
#查看web1容器中/data/web/html是否有文件index

docker run --name webserver --rm -P zhuxyid/index:v1 httpd -f -h /data/web/html

docker port webserver

docker run --name ttest --rm -P zhuxyid/index:v1 printenv
#prinenv查看环境变量

#注意在启动容器的时候可以重新设置环境变量
docker run --name test --rm -P -e TOMCAT_VERSION="tomcat-8.0" zhuxyid/index:v1 printenv #在初始化容器时候可以重新赋值
docker run --name test --rm -P -e TOMCAT_VERSION="tomcat-8.0" zhuxyid/index:v1 ls /data/tomcat/apache-tomcat-7.0.90.tar.gz
#这里只是重新赋值变量并不能修改image,因为这是docker build中已经生成了
看完了?赏个鸡腿钱,谢谢老板!