七、Docker学习之Dockerfile(自制镜像)

概述

   将代码打包成 docker 镜像的一个文件,文件包含各种指令。

语法及规则

   # 注释符
   instruction arguments #指令 参数
   不区分大小写,但习惯于大写
   指令按写的顺序执行
   做镜像第一行非注释信息必须是 from base image

指令

   每条指令都会形成一层镜像层,所以能合成一条指令尽量合成一条指令。层越多,联合挂载的效率越差

  • copy

     用于从docker主机复制文件至创建的新镜像文件
     语法:
      COPY <src>...<dest>COPY ["<src>",......"<dest>"]
     说明:<src>要复制的源文件或目录,支持使用通配符,通常使用相对路径;
        <dest>目标路径。指正在创建的image的文件系统路径。一般使用绝对路径,或者COPY指定以WORKDIR为起始路径;
        路径中有空白字符时,通常使用相对路径;
        <src>必须是build上下文中的路径,不能是其父目录中的文件;
        如果<src>是目录,则其内部文件或子目录会被递归复制,但<src>目录本身不会被复制;
        如果指定了多个<src>,或在<src>中使用了通配符,则<dest>必须是一个目录。且必须以/结尾;
        如果<dest>事先不存在,它将会被自动创建,这包括其父目录路径;

docker build -h #建立镜像帮助

[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker build -t myBusybox:v0.1 ./
invalid argument "myBusybox:v0.1" for "-t, --tag" flag: invalid reference format: repository name must be lowercase
See 'docker build --help'.
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker build -t mybusybox:v0.1 ./   #镜像名不可大写
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# cat Dockerfile 
#Description: test image
FROM busybox:latest
MAINTAINER "Loe <loe@qq.com>"#作者信息-元数据维护 新版本废弃后使用LABEL
#LABEL maintainer="Loe <loe@qq.com>"
COPY index.html /data/web/html/
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# 

  打包完成后启动容器并查看文件
  docker run --name myweb1 --rm mybusybox:v0.1 cat /data/web/html/index.html
在这里插入图片描述
  打包完成后启动容器并查看文件
  说明成功了。
  那如果要把目录下的所有文件都复制过去呢?(比如 /etc/yum.repos.d/)
   首先在Dockerfile文件加入一条COPY指令,然后打包成镜像,再启动镜像查看目录文件是否存在

[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# cat Dockerfile 
#Description: test image
FROM busybox:latest
MAINTAINER "Loe <loe@qq.com>"#作者信息-元数据维护 新版本废弃后使用LABEL
#LABEL maintainer="Loe <loe@qq.com>"
COPY index.html /data/web/html/
COPY yum.repos.d /ect/yum.repos.d/
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker build -t mybusybox:v0.2 ./
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker run --name myweb1 --rm mybusybox:v0.2  ls /ect/yum.repos.d/

在这里插入图片描述
   只复制了目录下的文件,目录没有复制过去,说明Success!

  • add
     类似于COPY, 但ADD支持使用TAR文件和URL路径
Dockerfile: ADD http://nginx.org/download/nginx-1.19.4.tar.gz /sur/local/src/
 docker build -t mybusybox:v0.3 ./
 docker run --name myweb1 --rm mybusybox:v0.3 ls /sur/local/src

   URL路径时不会展开压缩文件,当为本地路径的压缩文件时,在容器中会被解压成目录文件展开

  • volume
     用于在image中创建一个挂载点目录,以挂载Docker hosts上的卷或其他容器上的卷(只能指定容器内的卷,宿主机的卷自动生成)
     语法: VOLUME <mountpoint>VOLUME ["<mountpoint>"]
  • expose
     ​ 为容器打开指定要监听的端口以实现与外部通信,不会直接暴露需要执行容器时加上"-P"才能暴露
      语法: EXPOSE <port>[/<protocol>]
        <protocol>用于指定传输层协议,可为TCP或者UDP二者之一,默认是TCP协议
        可一次指定多个端口,如: EXPOSE 11211/udp 11211/tcp
        在Dockerfile中加入 EXPOSE 80/tcp
        打包镜像并启动,验证是否成功
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker build -t mybusybox:v0.5 ./
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker run --name myweb1 --rm mybusybox:v0.5 /bin/httpd -f -h /data/web/html
[root@iZ8vbi4t4rsi4lb9e7bkckZ ~]# curl 10.0.0.3
[root@iZ8vbi4t4rsi4lb9e7bkckZ img1]# docker port myweb1

在这里插入图片描述
docker port myweb1 #可查看容器暴露的端口 上述说明未暴露端口
  那停止再重启容器,重启的时候加上 -P 就能看到暴露的端口了
docker run --name myweb1 --rm -P mybusybox:v0.5 /bin/httpd -f -h /data/web/html
在这里插入图片描述
在这里插入图片描述

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

      语法: ENV <key> <value>ENV <key>=<value>...
         第一种格式中,<key>之后的所有内容均会被视作其<value>的组成部分。因此,一次只能设置一个变量;
         第二种格式中可用一次设置多个变量,每个变量为一个<key>=<value>的键值对,如果<value>中包含空格,可以以反斜杠\进行转义,也可通过对<value>加引号进行标识;另外反斜杠也可用于续行;
         定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能。
    在这里插入图片描述
ENV DOC_ROOT=/data/  \
	WEB_SERVER_PAKEAGE="busybox-1.3"

printenv 启动容器时,打印出环境变量
docker run ... printenv

  • run
     用于指定docker build过程中运行的程序,其可以是任何命令。
     语法:RUN <command>
        RUN ["<excutable>", "<param1>","<param2>"]
       第一种格式中,<command>通常是一个shell命令,且以/bin/sh -C来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop <container>命令停止容器时,此进程接收不到SIGTERM信号;
       第二种格式中的参数是一个json格式的数组,其中<excutable>为要运行的命令,后面的<paramN>为传递给命令的选项或参数;然而此种格式指定的命令不会以 /bin/sh -c来发起,因此常见的shell操作如变量替换以及通配符(?,*等)等替换将不会进行;如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
    RUN ["/bin/sh","-c","<excutable>","<param1>"]
FROM busybox:latest
LABEL maintainer="Loe <loe@qq.com>" app="httpd"
ENV WEB_DOC_ROOT="/data/web/html"
RUN mkdir -p $WEB_DOC_ROOT && \ 
    echo '<h1>hello busybox</h1>' > ${WEB_DOC_ROOT}/index.html
CMD /bin/httpd -f -h ${WEB_DOC_ROOT}
  • cmd
     类似RUN指令,CMD指令也可用于运行任何命令或应用程序,但RUN指令运行于镜像文件构建过程中,而CMD指令运行于基于Dockerfile构建出来的新镜像文件启动一个容器时;
     在Dockerfile中可以定义多个CMD,但是只有最后一个CMD生效;
    CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;CMD指定的命令可以被docker run的命令行选项所覆盖
       语法:CMD <command>
          CMD ["<excutable>", "<param1>", "<param2>"]
          CMD ["<param1>", "<param2>"]
       前两种语法格式的意义同RUN,第三种用于为ENTRYPOINT指令提供默认参数
    在这里插入图片描述
    docker run --name myweb1 -it --rm -P myhttpd:v0.1 #此处由于 /bin/httpd 替代了默认的 /bin/sh,所以进不去容器没法交互
    在这里插入图片描述
     在Dockerfile中加入 CMD ["/bin/httpd","-f","-h ${WEB_DOC_ROOT}"] #需注意中括号中的逗号前后不能有空格,否则会出现异常
    在这里插入图片描述
     打包成镜像后,查看镜像信息 docker image inspect myhttpd:v0.2,并启动
    在这里插入图片描述
    在这里插入图片描述

 从上图可看出, 镜像CMD中默认的 /bin/sh 被替换没了,启动时异常,因为${DOC_WEB_ROOT}是shell变量,但是容器没有shell命令。
 那么怎么解决这个问题呢?
 首先,在Dockerfile文件中 CMD 后面数组里面加入 /bin/sh -c, 即CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
 打包运行,发现还是无法启动,那么试试是否是变量问题,把变量${WEB_DOC_ROOT}改成固定目录/data/web/html/

  • entrypoint
     类似CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序;可定义多个ENTRYPOINT,但仅有最后一个会生效;
     与CMD不同的是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数覆盖,而且这些命令行参数会被当做参数传递给ENTRYPOINT指定的程序,但是docker run命令的--entrypoint选项的参数可覆盖ENTRYPOINT指定的程序!
     如果ENTRYPOINT 和CMD 同时定义,那么看CMD的第三种格式,CMD的参数将会被当成参数传给 ENTRYPOINT;
     如果非要在启动容器时覆盖容器中的ENTRYPOINT指令,那么在运行命令后加上 --entrypoint "ls /"
     如果使用ENTRYPOINT指令,那么启动容器时所带的参数也会当做ENTRYPOINT后面的参数执行;
       语法: ENTRYPOINT <command>
           ENTRYPOINT ["<command>", "<param1>", "<param2>"]
    在这里插入图片描述
    在这里插入图片描述
     ​ 按上图所说,命令启动时,由于容器中的各种环境变量等指令是启动报错的。可使用 docker ps -a 查看command的第一运行程序是 /bin/sh
     ​ 那么可以在启动容器的命令后加上 shell命令来验证一下
    docker run --name myweb2 myhttpd:v0.7 "ls /data"

    在这里插入图片描述
     ​此时CMD指令的/bin/httpd没有给ENTRYPOINT当默认参数,因为启动容器命令传了参数给ENTRYPOINT
     ​"$@" #传递给shell脚本的所有参数列表
[root@iZ8vbi4t4rsi4lb9e7bkckZ img2]# docker run --name myweb2 -it myweb:0.3 /bin/sh 
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"/bin/entrypoint.sh\": permission denied": unknown.

 ​​出现此问题的原因是,sh文件没有权限,chmod 777 entrypoint.sh 即可。
 ​​启动成功后,docker exec -it myweb3 /bin/sh 进入容器,wget -O - -q 65d22eb53aea 查看主页显示是否为添加的index.html
 ​​entrypoint 带参数启动,比如改变监听端口为8080 docker run --name myweb3 --rm -P -e "PORT=8080" myhttpd:v0.6 进入容器检验(略)

  • user
     用于指定运行image或运行Dockerfile中RUNRUNCMDENTRYPOINT指令指定的程序时的用户名或UID。默认情况下,container的运行身份为root
       语法:USER <UID>|<USERNAME>
       注意:USER后跟的必须为有效用户
  • healthcheck
     只要进程不停,容器就不停
     意义:比如程序出现bug,意外删除目录。用这个去检查
       docker healthcheck -help #查看帮助
     ​ ​ ​--start-period 指容器运行起来之后的等待时长 0s:立即执行健康检查
     ​ ​ ​Dockerfile HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${port:-80}/每隔30s,启动检查
[root@iZ8vbi4t4rsi4lb9e7bkckZ img2]# docker run --name myweb3 --rm -P -e "PORT=8080" myhttpd:v0.7
127.0.0.1 - - [06/Nov/2020:08:59:59 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:00:29 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:01:00 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:01:30 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:02:01 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:02:32 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:03:02 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:03:33 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [06/Nov/2020:09:04:03 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
  • shell
     不常用,了解即可----linux默认["/bin/sh","-c"]
              windows默认["cmd", "/S", "/C"]
     语法:SHELL ["excutable", "parameters"]
  • stopsignal
     不常用,了解即可
     stop停止容器时,接收的信号。只有pid为1的进程才能接收stop的信号从而停止容器

     语法: STOPSIGNAL signal
  • arg
     只在build使用,制作多版本时可用到
     比如环境变量只能在run时传参,那么可以定义ARG,build的时候传入
     语法:ARG <name>[=<value>]
       ​ ​ 在Dockerfile修改为
FROM  nginx:1.14-alpine

ARG author="ooo <o@o.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 image inspect myhttpd:v0.8 #验证是否成功注入

  • onbuild
     在镜像build的时候,不会执行。当镜像被当做base镜像时,才会执行onbuild。即FROM base
     语法: ONBUILD instruction
     1. Dockerfile用于build镜像文件,此镜像文件亦可作为base image被另一个Docker用作FROM指令的参数,并以之构建新的镜像文件;
     2. 在后面的这个Dockerfile中的FROM指令在build过程中被执行时,将会“触发”创建其base image的Dockerfile文件中的ONBUILD指令定义的触发器;
     3. Dockerfile用于build镜像文件,此镜像文件亦可作为base image被另一个Docker用作FROM指令的参数,并以之构建新的镜像文件;
     4. 尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROMMAINTAINER指令;
     5. 使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,例如 busybox:2.0-onbuild
     6. 在ONBUILD指令中使用ADDCOPY指令应该格外小心,因为新构建过程的上下文在缺少指定的源文件时会失败。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值