docker(四)--Dockerfile

 

镜像制作
1,基于容器制作
2,dockerfile


docker 语法格式

  • # comment 注释信息
  • INSTRUCTION arguments 指令与参数,通常指令都是大写

docker是自上而下顺序执行的,第一个非注释行必须是'FROM'指令,基于哪个基础镜像来做的

 

Dockerfile
1,需要有一个专用工作目录
2,dockerfile放在这个工作目录下,且文件名首字母大写
3,dockerfile如果要打包其他文件到镜像,这些文件的路径必须是当前工作目录或下级子目录,不能是父目录
4,dockerfile支持制作一个隐藏文件.dockeringore,这个文件中可以写入文件路径,在打包时,所有写在.dockeringore中的文件都不包含进去。 例如要打包一个目录下的文件,但不包含其中某3个文件,可以将这3个文件的路径写入.dockeringore。那么.dockeringore本身以及里面包含的文件,都不会被打包进去。

变量

变量引用:$variable_name  ${variable_name}
${variable:-word}   如果变量没设置或值为空,就使用word字串,否则就返回变量值。用得较多

[root@abao ~]# echo ${NAME:-lily}
lily
[root@abao ~]# NAME=jerry
[root@abao ~]# echo ${NAME:-lily}
jerry

${variable:+word} 变量有值就显示word, 没值就没

[root@abao ~]# NAME=jerry
[root@abao ~]# echo ${NAME:+tom}
tom
[root@abao ~]# unset NAME
[root@abao ~]# echo ${NAME:+tom}

 

Dockerfile常用指令 

FROM  最重要的一个指令,且必须为Dockerfile文件开篇的第一个非注释行,用于指定基准镜像,后续的指令运行于基准镜像提供的运行环境。
基准镜像可以是任何可用镜像文件,默认情况下,docker build会在docker主机上查找指定的镜像文件,当其不存在时,则从Docker Hub上拉取
语法:
- FROM <repository>[:<tag>]或
- FROM <repository>@<digest> # <repository>:指定作为base image的名称,digest指定hash码

MAINTAINER (可选,已废弃被LABLE代替)    用于让Dockerfile制作者提供本人的信息

LABLE  为镜像指定各种元数据。   语法:LABEL <key>=<value> <key>=<value> <key>=<value> ... 键值数据

COPY   从宿主机把文件复制到目标镜像中
语法:
- COPY <src> ..<des> 或
- COPY ["<src>",..."<dest>"]
<src>: 要复制的源文件或目录,支持使用通配符(一般是当前工作目录)
<dest>: 目标路径,即正在创建的image的文件系统路径,建议为<dest>使用绝对路径,否则以WORKDIR为其起始路径
注:在路径中有空白字符时,通常使用第二种格式

文件复制准则
- <src> 必须是build上下文中的路径,不能是其父目录的文件
- <src> 如果是目录,<src>目录自身不会被复制,其内部文件或子目录都会被复制。 (目录自身不会被复制,如cp -r tmp/* /tmp)
- 如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录,且必须以/结尾,不加会报错
- 如果<dest>事先不存在,它将会自动创建

COPY index.html /data/web/html/      # index.html一定要在当前目录下,或在子目录下
COPY yum.repos.d /etc/yum.repos.d/     #复制yum.repos.d目录只是复制它下面的内容,所以要写明目标目录名

Dockerfile:

# Description: test image
FROM busybox:latest
#MAINTAINER "abao <abao@163.com>"
LABEL maintainer="abao <abao@163.com>"
COPY index.html /data/web/html/

# docker build -t testhttpd:v1.0 ./
# ls
Dockerfile index.html
# docker run --name tinyweb1 --rm tinyhttpd:v0.1 cat /data/web/html/index.html
<h1>hello, Busybox httpd server image.</h1>

 

ADD    类似于COPY指令,ADD支持使用tar文件和URL路径
语法:
ADD <src> ...<dest> 或
ADD ["<src>",.."<dest>"]

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

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

  语法: WORKDIR <dirpath>

 

volume  用于在image中创建一个挂载点目录,用来挂载Docker host上的卷或其它容器上的卷
语法: VOLUME <mountpoint> 或 VOLUME |”<mountpoint>"
如果挂载点目录路径下此前在文件存在,docker run命令会在卷挂载完成后将此前所有文件复制到新挂载的卷中
卷有两种格式:绑定挂载卷和docker管理卷,dockerfile中只能用docker管理的卷,即指定容器中的路径,而不能指定宿主机的目录

VOLUME /data/mysql/
# docker build -t tinyhttpd:v0.5 ./
# docker run --name tinyweb1 --rm tinyhttpd:v0.5 sleep 100
# docker inspect tinyweb1
        "Mounts": [
            {
                "Type": "volume",
                "Name": "a08c5c5c1c4779e77a97682cd6309fbc67e61ece448965354eea28556b7dae29",
                "Source": "/var/lib/docker/volumes/a08c5c5c1c4779e77a97682cd6309fbc67e61ece448965354eea28556b7dae29/_data",
                "Destination": "/data/mysql",

  

EXPOSE  待暴露端口,用于为容器打开指定要监听的端口以实现与外部通信 

(只能指定容器暴露的端口,再随机绑定宿主机的端口,不能指定宿主机绑定的端口)
语法: 
    EXPOSE <port>[/<protocol>][<port>[/<protocol>]...]
         <protocol> 用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议
         EXPOSE指令可一次指定多个端口,例如EXPOSE 112/udp 112/tcp

 注意:写在dockerfile中的端口暴露并不会直接暴露,使用docker run中的-P选项时,不用指定,会自动读取镜像中要暴露那个端口

EXPOSE 80/tcp
# docker build -t tinyhttpd:v0.6 ./
# docker run --name tinyweb1 --rm tinyhttpd:v0.6 /bin/httpd -f -h /data/web/html
# docker inspect tinyweb1   查看容器的ip
# curl 172.17.0.3
<h1>hello, Busybox httpd server image.</h1>
# docker port tinyweb1   #实际查询没有暴露端口
# docker kill tinyweb1
tinyweb1
# docker run --name tinyweb1 --rm -P tinyhttpd:v0.6 /bin/httpd -f -h /data/web/html 
# docker port tinyweb1
80/tcp -> 0.0.0.0:32769
可以在外部访问宿主机:32769 

 

ENV  用于为镜像定义所需要的环境变量,并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用,调用格式为$variable_name}或$variable_name

语法:
ENV <key> <value>    #一次只能设置一个变量,<key>之后所有内容均会被视作其<value>的组成部分
ENV <key>=<value>...   # 一次可设置多个变量,每个变量为一个"<k>=<v>"的键值对,如果<value>中有空格,可以用反斜线\转义,也可用<value>引号进行标识;另外,反斜线也可用于续行。当定义多个变量时,建议使用这种方式,以便在同一层中完成所有功能

例如:

ENV DOC_ROOT=/data/web/html/ \
    WEB_SERVER_PACKAGE="nginx-1.17.8"
COPY index.html ${DOC_ROOT:-/data/web/html/}
WORKDIR /usr/local/src/
ADD ${WEB_SERVER_PACKAGE}.tar.gz ./

# docker build -t tinyhttpd:v0.7 ./
# docker run --name tinyweb1 --rm tinyhttpd:v0.7 ls /usr/local/src/
nginx-1.17.8
# docker run --name tinyweb1 --rm tinyhttpd:v0.7 cat /data/web/html/index.html
<h1>hello, Busybox httpd server image.</h1>
# docker run --name tinyweb1 --rm tinyhttpd:v0.7 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=1c99b2e89674
DOC_ROOT=/data/web/html/
WEB_SERVER_PACKAGE=nginx-1.17.8
HOME=/root

 在docker run时 通过-e 选项 也可指定变量

# docker run --name tinyweb1 --rm -e WEB_SERVER_PACKAGE=nginx-1.14.0 tinyhttpd:v0.7 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=e13da61e21b2
WEB_SERVER_PACKAGE=nginx-1.14.0
DOC_ROOT=/data/web/html/
HOME=/root
# docker run --name tinyweb1 --rm -e WEB_SERVER_PACKAGE=nginx-1.14.0 tinyhttpd:v0.7 ls /usr/local/src/
nginx-1.17.8
#可以看到docker run时可以修改环境变量的值,但是并不会改变docker build过程

 

 RUN    用于 docker build 过程中运行的命令

ENV DOC_ROOT=/data/web/html/ \
    WEB_SERVER_PACKAGE="nginx-1.17.8.tar.gz"
ADD http://nginx.org/download/${WEB_SERVER_PACKAGE} /usr/local/src/
WORKDIR /usr/local/src/

RUN cd /usr/local/src && \
    tar -xf ${WEB_SERVER_PACKAGE} && \
    mv nginx-1.17.8 webserver

# docker build -t tinyhttpd:v0.8 ./
# docker run --name tinyweb1 --rm -it tinyhttpd:v0.8
/usr/local/src # ls
nginx-1.17.8.tar.gz  webserver

Syntax:

  • RUN <command> 或
  • RUN ["<executable>","<param1>","<param2>"]    #注意要使用双引号,单引号可能会出错

- 第一种格式,<command>通常是一个shell命令,且以"/bin/sh -c"来运行它,当作shell子进程来运行,这意味此进程在容器的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号。

- 第二种中的参数是一个JSON格式的数组,其中<executable>为要运行命令,后面<paramN>为传递给命令的选项或参数;然而,命令不会以"/bin/sh -c"来发起,也就是直接由内核来创建,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行; 不过,如果想使用shell来操作,可以使用以下格式:RUN ["/bin/bash","-c","<exectuable>","<param1>"]

 

CMD   用于定义把镜像启动为容器时(docker run) 默认运行的命令

 

-  类似于RUN指令,CMD指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同,RUN指令运行于映像文件构建过程中,而CMD指令运行基于Dockerfile构建出新映像文件 启动一个容器时

-  CMD指令的首要目的在于为启动容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD指定的命令其可以被docker run的命令选项所覆盖
- 在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效
语法:

  • CMD <command> #  自动运行为shell子进程,最大的坏处是id不为1,无法使用docker stop去停止,因为它接收不到信号,接信号都是进程号为1的进程,因为它是一个超管进程,可以让它为1
  • CMD ["<executable>","<param1>","<param2>"]     #  直接启动为用户id为1的进程,可以接收处理shell的信号
  • CMD ["<param1>","<param2>"]   # 用于 ENTRYPOINT 指令提供默认参数

测试第一种方法:

FROM busybox
LABEL maintainer="abao <abao@163.com>" app="httpd"

ENV WEB_DOCROOT="/data/web/html/"

RUN mkdir -p ${WEB_DOCROOT} && \
    echo "<h1> Busybox httpd server.</h1>" > ${WEB_DOCROOT}/index.html

CMD /bin/httpd -f -h ${WEB_DOCROOT}
#启动容器时,应该是作为shell的子进程来运行,pid不为1

#创建镜像
 docker build -t tinyhttpd:v1.0 ./
# 查看镜像 
docker image inspect tinyhttpd:v1.0
        "Cmd": [
            "/bin/sh",    #可以看到默认会以"/bin/sh -c"来运行它
            "-c",
            "/bin/httpd -f -h ${WEB_DOCROOT}"
        ],
# 启动容器
  docker run --name tinyweb1 -it --rm -P  tinyhttpd:v1.0
没有进入交互式,# 镜像中定义默认运行的程序是httpd,是shell的子进程,不是shell,没有交互式接口

# 使用exec进入交互式
]# docker exec -it tinyweb1 /bin/sh
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html/  # 这里id为1是因为默认执行了exec的替换,为了确保容器能自动接收unix的信号,执行docker stop时能停止,执行docker kill时能杀掉
    6 root      0:00 /bin/sh
   11 root      0:00 ps
/ # printenv
HOSTNAME=5b27cc8a100f
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
WEB_DOCROOT=/data/web/html/
PWD=/
/ # 

 

第二种方式:

CMD ["/bin/httpd","-f","-h ${WEB_DOCROOT}"]

#制作镜像
docker build -t tinyhttpd:v1.1 ./
# 查看镜像
docker image inspect tinyhttpd:v1.1
            "Cmd": [
                "/bin/httpd",
                "-f",
                "-h ${WEB_DOCROOT}"
            ],
# 启动容器
 docker run --name tinyweb1 -it --rm -P  tinyhttpd:v1.1
httpd: can't change directory to ' ${WEB_DOCROOT}': No such file or directory
#会报错,因为程序不会运行为shell子进程,而变量是为shell变量,所以无法识别

 

ENTRYPOINT

# docker run --name tinyweb2 -it --rm -P tinyhttpd:v1.2 ls /data/web/html    
index.html
# docker run --name tinyweb2 -it --rm -P tinyhttpd:v1.2 httpd -f -h /data/web/html/
# 在dockerfile中指定的默认要运行的程序是httpd,但是docker run时可以自己指定命令(ls /data/web/html和httpd ...等,覆盖镜像中默认要运行的命令

 

- ENTRYPOINT可以实现类似于CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序
- 与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当参数传递给ENTRYPOINT指定的程序
- 不过docker run命令中的 --entrypoint选项的参数 可覆盖ENTRYPOINT指令指定的程序

Syntax:

- ENTRYPOINT <command>
- ENTRYPOINT ["<executable","<param1>","<param2>"]

   docker run 命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后做为其参数使用
   Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效

FROM busybox
LABEL maintainer="abao <abao@163.com>" app="httpd"

ENV WEB_DOCROOT="/data/web/html/"

RUN mkdir -p ${WEB_DOCROOT} && \
    echo "<h1> Busybox httpd server.</h1>" > ${WEB_DOCROOT}/index.html

ENTRYPOINT /bin/httpd -f -h ${WEB_DOCROOT}

构建镜像并启动容器,测试:
docker run --name tinyweb2 -it --rm -P tinyhttpd:v1.3 ls /data/web/html

#并没有执行ls,而是执行的/bin/httpd -f -h /data/web/html ls /data/web/html。 把ls...当作参数附加在命令后面

 

CMD的第三种方式:

同时有 CMD 和 ENTRYPOINT 时,CMD指定的选项会用于为ENTRYPOINT提供默认参数

FROM busybox
LABEL maintainer="abao <abao@163.com>" app="httpd"

ENV WEB_DOCROOT="/data/web/html/"

RUN mkdir -p ${WEB_DOCROOT} && \
    echo "<h1> Busybox httpd server.</h1>" > ${WEB_DOCROOT}/index.html

CMD ["/bin/httpd","-f","-h ${WEB_DOCROOT}"]
ENTRYPOINT ["/bin/sh","-c"]
#默认会执行 /bin/sh -c /bin/httpd -f -h /data/web/html
docker build -t tinyhttpd:v1.3 ./ docker run --name tinyweb2 -it --rm -P tinyhttpd:v1.4 "ls /data/web/html" index.html #发现执行了 ls 命令,因为CMD的参数会被当成ENTRYPOINT的默认参数,而命令行参数会被当参数传给ENTRYPOINT,所以CMD的参数被覆盖了。执行的命令是 /bin/sh -c ls /data/web/html

 

 

配置nginx示例:

# cat Dockerfile 
FROM nginx:1.14-alpine
LABEL maintainer="abao <abao@abao.163.com>"

ENV NGX_DOC_ROOT="/data/web/html/"
ADD index.html ${NGX_DOC_ROOT}
ADD entrypoint.sh /bin/
CMD ["/usr/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]

# cat entrypoint.sh 
#!/bin/sh
cat > /etc/nginx/conf.d/www.conf << EOF
server {
    server_name ${HOSTNAME};
    listen ${IP:-0.0.0.0}:${PORT:-80};
    root ${NGX_DOC_ROOT:-/usr/share/nginx/html};
}
EOF
exec "$@" 

chmod +x entrypoint.sh
echo " <h1> New doc root for nginx.</h1>" > index.html

docker build -t myweb:v0.5 ./   
docker run --name myweb1 --rm -P  myweb:v0.5  
docker exec -it myweb1 /bin/sh 
/ # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process /usr/sbin/nginx -g daemon off;
    8 nginx     0:00 nginx: worker process
    9 root      0:00 /bin/sh
   14 root      0:00 ps -ef
/ # cat /etc/nginx/conf.d/www.conf 
server {
    server_name 790db97efd5a;
    listen 0.0.0.0:80;
    root /data/web/html/;
}
/ # wget -O - -q 790db97efd5a    #访问nginx
<h1> New doc root for nginx.</h1>
docker run时传递环境变量 docker run --name myweb1 --rm -P -e "PORT=8080" myweb:v0.5 docker exec -it myweb1 /bin/sh / # ps PID USER TIME COMMAND 1 root 0:00 nginx: master process /usr/sbin/nginx -g daemon off; #pid为1,因为entrypoint.sh中执行命令是exec 8 nginx 0:00 nginx: worker process 9 root 0:00 /bin/sh 14 root 0:00 ps / # cat /etc/nginx/conf.d/www.conf server { server_name 0a9b161911b8; listen 0.0.0.0:8080; root /data/web/html/; } / # wget -O - -q 0a9b161911b8:8080 <h1> New doc root for nginx.</h1>

 通常情况下,可以使用 entrypoint脚本把需要修改的部分,如IP,PORT,作为变量放在配置文件中,让脚本可以通过使用这些环境变量生成配置文件,在docker run时用环境变量给它传值。

 

USER

用于指定运行image时的或运行Dockerfile中任何RUN、CMD或ENTRYPOINT指定的程序的用户名或UID
默认情况下,container的运行身份为root用户

语法:
    USER <UID>|<UserName>
     需要注意的是,<UID>可以为任意数字,但是实践中其必须为/etc/passwd中某用户的有效UID,否则docker run命令将运行失败

 

HEALTHCHECK
 不依据主进程运行与否来判断健康与否,而是检测是否真正能提供服务,使用wget或curl查看是否能加载页面。

语法:

HEALTHCHECK [OPTIONS] CMD command   (通过运行一个command来检查容器主进程是否健康)
HEALTHCHECK NONE      (拒绝任何健康状态检测,包括默认的检测机制)

OPTIONS:
--interval=DURATION(default: 30s) #检测间隔时间
--timeout=DURATION(default: 30s) #超时时间
--start-period=DURATION(default: 0s) #容器启动时,可能进程服务还没初始化完成,这时去检查会是报错失败的,检测可以等待一段时间,像tomcat程序启动时可能需要10s
--retries=N(default: 3) #重试次数

响应结果:
0:success
1: unhealthy
2: reserved 预留,自定义

示例:
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost || exit 1 

FROM nginx:1.14-alpine
LABEL maintainer="abao <abao@abao.163.com>"

ENV NGX_DOC_ROOT="/data/web/html/"

ADD index.html ${NGX_DOC_ROOT}
ADD entrypoint.sh /bin/

EXPOSE 80/tcp
HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${PORT:-80}/

CMD ["/usr/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]

# docker run --name myweb1 --rm -P  -e "PORT=8080" myweb:v0.6
127.0.0.1 - - [25/Feb/2020:03:16:53 +0000] "GET / HTTP/1.1" 200 34 "-" "Wget" "-"
127.0.0.1 - - [25/Feb/2020:03:17:23 +0000] "GET / HTTP/1.1" 200 34 "-" "Wget" "-"
127.0.0.1 - - [25/Feb/2020:03:17:53 +0000] "GET / HTTP/1.1" 200 34 "-" "Wget" "-"
127.0.0.1 - - [25/Feb/2020:03:18:24 +0000] "GET / HTTP/1.1" 200 34 "-" "Wget" "-"

 

SHELL    定义运行程序默认使用的shell

语法: SHELL ["executable","parameters"]
默认linux是["/bin/sh","-c"],windows是["cmd","/S","/C"]

STOPSIGNAL

进程号id 为 1 的,可以接收 docker stop 命令,从而能够停止主进程,从而停止容器。如 stop 发送的是 15,signal 是无符号整型数字(必须匹配 kernal 的 syscall table),如 9 ,也可以是 SIGNAME,如 SIGKILL
Syntax: STOPSIGNAL signal

ARG

 在 docker build 过程中传参数,使用  --build-arg <varname>=<value> 

FROM nginx:1.14-alpine
ARG author="abao <abao@163.com>"
LABEL maintainer=${author}

ENV NGX_DOC_ROOT="/data/web/html/"

ADD index.html ${NGX_DOC_ROOT}
ADD entrypoint.sh /bin/

EXPOSE 80/tcp
HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${PORT:-80}/

CMD ["/usr/sbin/nginx","-g","daemon off;"]
ENTRYPOINT ["/bin/entrypoint.sh"]
# docker build --build-arg author="xiao <xiao@qq.com>" -t myweb:v0.9 ./
# docker image inspect myweb:v0.9 |grep maintainer
                "maintainer": "xiao <xiao@qq.com>"
                "maintainer": "xiao <xiao@qq.com>"

 

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

Dockerfile用于 build 镜像文件,此镜像文件可以作为 base image 被另一个 Dockerfile 用做 FROM 指令的参数,并以只构建新的镜像文件
在后面的这个Dockerfile中的 FROM 指令在 build 过程中被执行时,将会“触发”创建其 base image 的 Dockerfile 文件中 ONBUILD 指令定义的触发器

    base image->build -> image1 -> build(这个过程中执行ONBUILD)->image2

Syntax: ONBUILD <INSTRUCTION>

  • 尽管任何指令都可以注册成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER 指令
  • 使用包含 ONBUILD 指令的 Dockerfile 构建的镜像应该使用特殊的标签,例如 ruby:2.0-onbuild
  • 在 ONBUILD 指令中使用 ADD 或 COPY指令应该格外小心,因为新构建过程的上下文在缺少指定原文件时会失败

 

posted @ 2020-02-27 10:45  卡布爱学习  阅读(1381)  评论(0编辑  收藏  举报