Docker学习之六:基于Dockerfile构建镜像

镜像制作

一般镜像的制作,通常需要修改镜像的配置文件,比如nginx的配置文件,可以通过以下的方式:

  • 将配置文件做成存储卷,从宿主机编辑好之后,启动容器时应用程序加载配置文件的路径并和宿主机的目录建立关联关系。容器内也能修改好配置文件
  • 一般的docker exec CONTAINER,然后编辑配置文件,然后reload nginx
  • 通过自定义镜像

镜像自定义,一般是需要针对不同的环境,定义符合实际需求的镜像环境。比如可以基于容器,将配置文件直接写死到镜像中,但是如果需要修改配置时十分不便,而且如果应用容器的环境很多,如开发,测试,业务等环境,则需要为每个环境都需要制作镜像,如此就需要修改对应环境的配置文件,十分繁琐。

因此,最好通过docker file来定义镜像。

假如nginx启动为容器,仅仅配置一个虚拟主机,提供一个服务。其配置文件的格式是固定的,如果将配置文件做成类似模板,并且其值通过变量来获取,比如server_name $NGINX_SERVER_NAME; Listen $NGX_IP:$NGX_PORT; root $DOC_ROOT;这样的形式。

当用户拿到镜像,启动为容器时,在容器主进程启动前,先启动一个辅助程序,该程序根据镜像中的文件和用户启动镜像为容器时,向容器传递的环境变量,程序自动将环境变量传递到主程序中,然后程序启动主程序,辅助程序退出。如此可以基于一个镜像启动多个容器,并可直接向容器传递环境变量。如此一来,便可以准备好一个模板,设计好处理程序,由处理程序来替换主程序,以实现传递环境变量的方式来传递配置信息。

云原生的应用也符合这种形式,程序启动时甚至可以不读取配置文件,而是直接读取环境变量来作为配置并启动。

Docker file

Docker file是构建docker镜像的源码,里面是基本的文本指令,包含用户可以装配生成一个image。

Docker file由两类语句组成:注释信息 + 指令和参数

指令一般纯大写字母,但是它本身不区分字符大小写。根据约定俗成的惯例,都应该使用大写字母。

Docker file是顺序执行的指令,因此指令间的依赖关系很重要

第一个非注释行,必须是FROM,表示必须基于某个基础镜像来制作。实际上,如今已经无此限制。

要求

  • 必须在某个特定目录下进行,一个专用目录放进docker file
  • Docker file的首字母必须大写
  • 如果需要打包文件,必须将文件放入到工作目录或者子目录
  • Docker file支持目录内做隐藏文件,docker ignore文件,写入文件路径后,打包时将不会打包该路径的文
  • 命令:docker build

基于docker file构建镜像时,无需启用容器,而是由docker build完成。它可以基于基础镜像来执行命令,该命令是基础镜像所包含的而非宿主机包含的命令。因此,制作环境是底层镜像启动为容器时所能提供的环境。

变量

Docker build能使用的环境变量,和shell的环境变量十分类似

${variable:-world}:如果变量为空,则引用world为变量的默认值

${variable:+world}:如果变量设定了值,不为空,则显示world

Docker file构成

FROM

FROM指令是最重要的一个指令,用于为镜像文件构建过程中指定基准镜像。后续的指令运行于此基准镜像所提供的运行环境。

实际中,基准镜像可以是任何可用的镜像文件。默认情况下,docker build会在docker主机上查找指定的镜像文件。如果不存在,则从Docker Hub Registry上拉取所需的镜像文件。

如果都找不到指定的镜像文件,docker build会返回一个错误信息。

FROM <repository>[:<tag>] 或
FROM <repository>@<digest>

比如:

root@eto:~# mkdir lab1
root@eto:~# cd lab1/
root@eto:~/lab1# vim Dockerfile
root@eto:~/lab1# cat Dockerfile 
# Description: test image
FROM busybox:latest

MAINTAINER

用于让Docker file制作者提供本人的详细信息。

该字段已经废弃,替换为LABEL。Label可以提供各种键值信息,包含的信息更加宽泛。

LABEL

提供键值信息。

LABEL <key>=<value> <key>=<value>...

信息为键值对,一个镜像可以使用多个LABEL。

COPY

用于从Docker主机复制文件至创建的新映像文件

COPY <src> ... <dest> 或
COPY ["<src>"..."<dest>"]

<src>:要复制的源文件或目录,支持使用通配符
<dest>:目标路径,即正在创建的image文件的系统路径。建议使用绝对路径,否则COPY指定则以WORKDIR为起始路径

文件复制准则:

  • 必须是build上下文中的路径,不能是父目录中的文件
  • 如果是目录,则其内部文件或者子目录会被递归复制,但目录自身不会被复制
  • 如果指定了多个,或在中使用了通配符,到必须是一个目录,且必须以/结尾
  • 如果事先不存在,它将被自动创建,这包括其父目录路径

比如:

将宿主机的当前工作目录的文件打包复制到提供的目标镜像文件。

root@eto:~/lab1# cat Dockerfile 
# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>
COPY index.html /data/web/html/

root@eto:~/lab1# cat index.html 
my first image

root@eto:~/lab1# docker build -t tinyhttpd:v0.1-1 ./
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM busybox:latest
 ---> be5888e67be6
Step 2/3 : LABEL <mail>=<jaywin@jaywin2u.com>
 ---> Running in b6efaa79d695
Removing intermediate container b6efaa79d695
 ---> 07515529f3c5
Step 3/3 : COPY index.html /data/web/html/
 ---> 423253017bfe
Successfully built 423253017bfe
Successfully tagged tinyhttpd:v0.1-1

验证:运行自定义命令,命令运行结束后容器消失。

root@eto:~/lab1# docker run --name tinyweb1 --rm tinyhttpd:v0.1-1 cat /data/web/html/index.html
my first image

复制目录:

root@eto:~/lab1# mkdir mydir
root@eto:~/lab1# touch mydir/{test1,test2}
root@eto:~/lab1# vim Dockerfile 
root@eto:~/lab1# docker build -t tinyhttpd:v0.1-2 ./
Sending build context to Docker daemon  4.608kB
Step 1/4 : FROM busybox:latest
 ---> be5888e67be6
Step 2/4 : LABEL <mail>=<jaywin@jaywin2u.com>
 ---> Using cache
 ---> 07515529f3c5
Step 3/4 : COPY index.html /data/web/html/
 ---> Using cache
 ---> 423253017bfe
Step 4/4 : COPY mydir /tmp/
 ---> 4c98b55d0226
Successfully built 4c98b55d0226
Successfully tagged tinyhttpd:v0.1-2
root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-2 ls /tmp/
test1
test2

注意:

docker file文件中,每条指令都会生成一层。因此如果能合并为一条指令,必须合并为一条指令。否则多层的联合挂载回导致读写效率低下。

ADD

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

ADD <src> ... <dest> 或
ADD ["<src>" ... "<dest>"]

操作准则:

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

比如:

# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>
COPY index.html /data/web/html/
COPY mydir /tmp/
ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/
root@eto:~/lab1# docker build -t tinyhttpd:v0.1-3 ./

打包并验证:

root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-3 ls /usr/local/src
nginx-1.17.10.tar.gz

如果直接从本地ADD,则会自动展开到镜像中.

root@eto:~/lab1# cat Dockerfile 
# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>
COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD nginx-1.17.10.tar.gz ./

VOLUME

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

VOLUME <mountpoint> 或
VOLUME ["<mountpoint>"]

如果挂载点目录路径下此前的文件存在,则docker run命令会在卷挂载完毕后将此前的所有文件复制到新挂载的卷中。

# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>
COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD nginx-1.17.10.tar.gz ./

VOLUME /data/mysql/
root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-3 mount | grep mysql
/dev/sda2 on /data/mysql type ext4 (rw,relatime,data=ordered)

EXPOSE

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

EXPOSE <port>[/<protocol>][<port>[/<protocol>]...]

用于指定传输层协议,可以是tcp或者udp,默认为TCP协议。

EXPOSE 指令可一次指定多个端口,例如

EXPOSE 11211/udp 11211/tcp 

端口为动态绑定,无法指定宿主机的哪个地址和端口。并且,还能指定容器要暴露的端口。

# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>
COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD nginx-1.17.10.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp
root@eto:~/lab1# docker run --name tinyweb2 --rm tinyhttpd:v0.1-3 /bin/httpd -f -h /data/web/html

root@eto:~/lab1# curl 172.17.0.2
my first image

注意:即使指定了暴露端口,未必默认就暴露:

root@eto:~/lab1# docker port tinyweb2
root@eto:~/lab1#

因此,要暴露到宿主机外部,使用-P选项

root@eto:~/lab1# docker run --name tinyweb2 -P --rm tinyhttpd:v0.1-3 /bin/httpd -f -h /data/web/html

root@eto:~/lab1# docker port tinyweb2
80/tcp -> 0.0.0.0:32768

ENV

用于为镜像定义所需的环境变量,并且可被Dockfile文件中位于其后的其他指令(如ENV, ADD, COPY等)所调用

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

ENV <key><value> 或
ENV <key>=<value>...

第一种格式中,之后的所有内容都会视作其的组成部分,因此一次只能设置一个变量;

第二种格式,可以用一次设置多个变量,每个变量为一个"="的键值对。如果包含空格,则可以以反斜线进行转义,也可以通过对加引号标识;另外,反斜线也可用于续行;

定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能

# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>

ENV DOC_ROOT=/data/web/html \
   WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp
root@eto:~/lab1# docker run --name myweb -P --rm tinyhttpd:v0.1-3 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=fd80bf4feaed
DOC_ROOT=/data/web/html
WEB_SERVICE_PACKAGE=nginx-1.17.10
HOME=/root

在docker run时,可传递变量的值。环境变量可以在容器启动后,注入到容器中。因此,此时在容器运行时,重新赋值环境变量是可行的,比如:

root@eto:~/lab1# docker run --name myweb -e WEB_SERVICE_PACKAGE=nginx-1.17.11  -P --rm tinyhttpd:v0.1-3 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=7786f8b481e2
WEB_SERVICE_PACKAGE=nginx-1.17.11
DOC_ROOT=/data/web/html
HOME=/root

尽管在docker run中重新定义了环境变量的值,然而docker build时的值已经定义好了,image中的值无法修改的。

因此,docker build和docker run是两个不同的过程。

CMD & RUN

镜像从开始制作到运行,经过docker build和docker run两个阶段。这个两个阶段都允许运行shell命令,默认的命令是由CMD定义的。

而RUN是基于docker file构建镜像时,要运行的命令,在docker build中运行。

比如,制作镜像时,可在docker file中将tar.gz解压

# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>

ENV DOC_ROOT=/data/web/html \
    WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp

RUN cd /usr/local/src && \
    tar xf ${WEB_SERVICE_PACKAGE}.tar.gz

RUN可以写多个,并且建议多条命令使用续行符的方式,写在同一行。

注意:

一般自定义镜像,都使用编译安装。因为yum安装会生成许多缓存,会增大镜像体积。因此,如果yum安装完毕后,必须yum clean all

FROM centos
RUN yum -y install epel-release && yum makecache && yum install nginx && yum clean all

容器应该运行什么样的应用?一般而言,拥有生产能力的应用,都是运行在后台的守护进程,比如nginx,redis,mysql等。这些应用,也可以运行在容器中。

假如手动运行nginx,直接在命令行激活而非通过systemctl管理,则nginx属于shell的子进程。Shell是用户创建并运行的进程的接口,因此任何进程都是shell的子进程,并且会占据当前sh的终端设备。即使加&,仅仅是将进程送到后台执行,而无法脱离sh这个父进程而存在。一旦退出sh,则其下的任何子进程将被回收。因此,需要使用nohup command &,才能将进程送到后台并剥离和sh的关系。此时意味着父进程从sh改变为init。

容器中的main process是否委托由内核启动?而不是由shell启动。比如ls /var/* 这个进程是在shell中能正常运行并解析的,一旦该进程不是sh启动,则命令会运行失败。因为命令行展开,管道,通配符,输入输出重定向等都是属于sh的特性。如果不是基于sh启动,将全部失效。但是如果一旦基于sh启动,main process将不再是容器中的pid=1的进程。

综上所述,容器中启动进程的方式有两种:

  • 直接在容器中启动进程,让其pid=1
  • 容器中启动sh后,再启动主进程。但是不能违背主进程的pid=1这个原则,通过exec COMMAND实现

CMD

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

  • RUN指令运行于镜像文件的构建过程中,而CMD指令运行于基于Dockfile构建出来的新镜像文件启动一个容器时
  • CMD指令的首要目的在于为启动的容器指定默认的要运行的程序,且其运行结束后,容器也将终止;不过,CMD指定的命令可以被docker run的命令选项所覆盖
  • 在Dockerfile中可以存在多个CMD指令,但仅最后一个生效
CMD <command> 或
CMD ["<executable>","<param1>","<param2>"] 或
CMD ["<param1>","<param2>"]

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

Cmd指令用于定义一个容器,启动时默认要运行的程序。Docker默认只运行一个程序,因此CMD只能给一个(docker file中可以给多个,但实际生效只有最后一个)

RUN

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

RUN <command> 或
RUN ["<executable>","<param1>","<param2>"]
  • 第一种格式中,通常是一个shell命令,并且以"/bin/sh -c"来运行它。这意味着此进程在容器中的PID不为1,不能接收unix信号。因此,当使用docker stop 命令停止容器时,此进程接收不到SIGTERM信号;

  • 第二种语法格式中的参数是一个JSON格式的数组,其中为要运行的命令,后面的为传递给命令的选项或参数;然而,这种格式指定的命令不会以"/bin/sh -c"来发起,而是以内核启动。因此常见的shell操作如变量替换以及通配符替换将不会进行;不过,如果要运行的命令依赖于shell特性,可以将其替换为类似下面的格式

    RUN ["/bin/bash","-c","<executable>","<param1"]
    
# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>

ENV WEB_DOC_ROOT=/data/web/html \
    WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp

#RUN cd /usr/local/src && \
#    tar xf ${WEB_SERVICE_PACKAGE}.tar.gz

CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

构建完毕后运行:

root@eto:~/lab1# docker run --name myweb --rm -P tinyhttpd:v0.1-3

默认启动后,会直接以httpd –f –h运行。因此终端会卡住。可以使用命令直接显式运行sh,而且默认使用exec来将主进程启动为pid=1以接收unix信号。如此可以使用docker kill来停止容器。

            "Cmd": [
                "/bin/sh",
                "-c",
                "/bin/httpd -f -h ${WEB_DOC_ROOT}"

连接到容器终端里:

root@eto:~/lab1# docker exec -it myweb /bin/sh
/usr/local/src # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html
    6 root      0:00 /bin/sh
   11 root      0:00 ps

在docker run时,RUN定义的环境变量也会被调用

/usr/local/src # printenv
WEB_DOC_ROOT=/data/web/html
HOSTNAME=b74030022d2c
SHLVL=1
HOME=/root
WEB_SERVICE_PACKAGE=nginx-1.17.10
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/usr/local/src

使用json格式来定义CMD:

由于cmd的这种定义方式是由内核执行的指令,无法识别shell变量。因为kernel不会识别变量,直接识别为目录路径。因此,需要定义为:

#CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]

ENTRYPOINT

  • 类似CMD指令的功能,用于为容器指定默认运行的程序,从而使得容器像一个单独运行的可执行程序。
  • 与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖。而且,这些命令行参数会被当做参数传递给ENTRYPOINT指定的程序
  • 不过,docker run命令的–entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序
ENTRYPOINT <command> 
ENTRYPOINT ["<executable>","<param1>","<param2>"]
  • docker run命令传入的命令参数会覆盖CMD指令的内容,并且附加到ENTRYPOINT命令最后做为其参数使用
  • dockerfile可存在多个ENTRYPOINT指令,但仅有最后一个生效
  • 一般,不希望用户运行非容器默认启动时使用的命令,则使用ENTRYPOINT
# Description: test image
FROM busybox:latest
LABEL <mail>=<jaywin@jaywin2u.com>

ENV WEB_DOC_ROOT=/data/web/html \
    WEB_SERVICE_PACKAGE="nginx-1.17.10"

COPY index.html /data/web/html/
COPY mydir /tmp/
#ADD http://nginx.org/download/nginx-1.17.10.tar.gz /usr/local/src/

WORKDIR /usr/local/src/
ADD ${WEB_SERVICE_PACKAGE}.tar.gz ./

VOLUME /data/mysql/

EXPOSE 80/tcp

#RUN cd /usr/local/src && \
#    tar xf ${WEB_SERVICE_PACKAGE}.tar.gz

#CMD /bin/httpd -f -h ${WEB_DOC_ROOT}

#CMD ["/bin/sh","-c","/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]

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

运行

root@eto:~/lab1# docker run --name myweb --rm -P  tinyhttpd:v0.1-3  ls /dat/web/html

实际上并没有运行ls命令,而是将ls等字符串识别为位置参数传递给httpd程序。

如果一定要运行自己的命令,必须明确自己的需求。使用专门的选项来指定:–entrypoint string

root@eto:~/lab1# docker run --name myweb --rm -P --entrypoint "date" tinyhttpd:v0.1-3  
Tue May  5 09:08:45 UTC 2020

如果定义了多个ENTRYPOINT,则最后一个生效。如果ENTRYPOINT和CMD同时定义,则CMD定义的内容会被当做参数传递给ENTRYPOINT指定的命令。

比如:

CMD ["/bin/httpd","-f","-h ${WEB_DOC_ROOT}"]
ENTRYPOINT ["/bin/sh","-c"]

启动:

root@eto:~/lab1# docker run --name myweb -it -P --rm tinyhttpd:v0.1-3  "sleep 60"
            "Cmd": [
                "sleep 60"
            ],
            "Image": "tinyhttpd:v0.1-3",
            "Volumes": {
                "/data/mysql/": {}
            },
            "WorkingDir": "/usr/local/src",
            "Entrypoint": [
                "/bin/sh",
                "-c"
            ],

此时传递的参数,能正常运行。

CMD定位默认的参数,传递给ENTRYPOINT。而命令行传递参数时,会覆盖CMD定义的参数。如此就相当于运行了/bin/sh –c ”sleep 60”

实战

定义一个nginx镜像,能通过环境变量来接收参数,以定义监听的端口,地址和doc_root。

  • 定义脚本初始化文件,然后运行nginx,并顶替sh进程(exec命令实现):

    root@eto:~/lab1# 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 "$@"
    
    
    root@eto:~/lab1# chmod +x entrypoint.sh 
    
  • 定义dockerfile

    root@eto:~/lab1# cat Dockerfile
    FROM nginx:stable-alpine
    LABEL maintainer="jaywin"
    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"]
    
  • 构建镜像并运行

    root@eto:~/lab1# docker build -t tinyhttpd:v0.1-4 ./
    root@eto:~/lab1# docker run --name myweb --rm -P -it tinyhttpd:v0.1-4
    
    
  • 查看配置文件

    root@eto:~/lab1# docker container exec myweb cat /etc/nginx/conf.d/www.conf
    server {
        server_name c772ee1312f3;
        listen 0.0.0.0:80;
        root /data/web/html/;
    }
    
    root@eto:~/lab1# docker container exec myweb netstat -tanlup
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1
    
  • 修改环境变量

    root@eto:~/lab1# docker run --name myweb -e "PORT=8080"  --rm -P -it tinyhttpd:v0.1-4
    
  • 检查端口

    root@eto:~/lab1# docker container exec myweb netstat -tanlup
    Active Internet connections (servers and established)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      1/nginx -g daemon o
    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1/nginx -g daemon o
    

USER

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

默认情况下,container的运行身份为root

USER <UID>|<UserName>

注意:可以是任意数字,但实践中必须是/etc/passwd中某用户的有效UID,否则docker run命令将运行失败

HEALTH CHECK

基于某个镜像启动容器时,如果主进程没有停止,则容器一直是存在的。如果nginx进程运行正确,但是doc_root指向错误,客户端访问失败,但容器正常运行。因此,docker判断进程的健康状态,仅仅是判断进程运行与否,而不会判断进程的健康与否。

因此,可使用CMD参数,用于检测主进程工作状态健康状态,可以定义定义周期性任务计划。或者使用NONE参数,不检测健康状态。

选项:
  --interval=DURATION(default: 30s)
  --timeout=DURATION(default: 30s)
  --start-period=DURATION(default: 30s)
  --retries=N(default: 3)
  
状态码:
  0:健康
  1:不健康
  2:保留,不使用

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

当容器启动时,nginx可能并没有马上启动。假设nginx启动需要5s,如果测试马上检测健康状态,则会失败的。一旦失败,将kill容器。

因此,可以设置为,等待主进程初始化完毕的一段时间后,才开始检测。默认等待时长为0s。

比如:

FROM nginx:stable-alpine
LABEL maintainer="jaywin"
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"]

构建并运行镜像,正常运行

root@eto:~/lab1# docker run --name myweb -e "PORT=8080"  --rm -P -it tinyhttpd:v0.1-4
127.0.0.1 - - [05/May/2020:10:07:54 +0000] "GET / HTTP/1.1" 200 15 "-" "Wget" "-"

如果手动将index.html删除了,则已经警告

root@eto:~/lab1# docker exec -it myweb /bin/sh
/ # rm -rf /data/web/html/index.html

2020/05/05 10:14:27 [error] 8#8: *14 directory index of "/data/web/html/" is forbidden, client: 127.0.0.1, server: 9a00fefe0364, request: "GET / HTTP/1.1", host: "0.0.0.0:8080"
127.0.0.1 - - [05/May/2020:10:14:27 +0000] "GET / HTTP/1.1" 403 153 "-" "Wget" "-" 

检测失败,超过3次尝试后,宣告失败。

SHELL

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

在Linux中,为["/bin/sh","-c"];在windows中,为[“cmd”,"/S","/C"]

STOPSIGNAL

设定系统传递给容器的信号,一般无需变化。

ARG

只能在docker build时期使用,能在过程中使用并传递。可以让一个docker file适用较多的场景,比如传递版本参数。

ARG <name>[=<defalut value>]

比如:

FROM nginx:stable-alpine
ARG author="jaywin <jaywin@jaywin2u.com>"
LABEL maintainer="$author" 

也能在build的过程中,传递ARG,以实现变量的传递。如此可解决ENV无法在build过程中传递参数的问题。

root@eto:~/lab1# docker build --build-arg author="hanna <hanna@jaywin2u.com>" -t myweb:v0.3-1 ./

ONBUILD

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

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

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

ONBUILD <INSTRUCTION>

尽管任何指令都可注册成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令。

使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,例如ruby:2.0-onbuild

在ONBUILD指令中使用ADD和COPY指令应该格外小心,因为新构建过程的上下文在缺少指定源文件时会失败

在dockerfile定义触发器,不是在当前的build执行,而是在生产image后,再次基于image from后再执行。也就是被当做基础镜像后,制作镜像后执行。也可理解为延迟实行。

比如:

ONBUILD ADD http://mirrors.aliyun.com/repo/Centos-7.repo

Build后,实际并没有执行onbuild执行。

基于新镜像,定义dockerfile后,将执行此指令。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值