7、Dockerfile详解

Docker file

nginx镜像启动为容器后是为了配置为一个虚拟主机,提供一个虚拟服务。但这个虚拟主机所提供的服务名、监听的端口、家目录等在不同的环境中是不一样的,

但配置文件的格式是固定的,所以把它的配置文件生成一个模板。

server.conf     /etc/nginx/conf.d/       //模板放在/etc/nginx/conf.d/目录下

{

      server_name  $NGX_SERVER_NAME;     //server_name通过变量$NGC_SERVER获取

      listen  $NGX_IP; $NGINX_PORT;

       root  $DOC_ROOT;

}

当用户利用nginx镜像启动为容器时,在容器内部的主进程启动之前先启动一个别的程序(程序A),这个程序(A)根据此镜像中的server.conf文件以及用户将镜像启动为容器时向容器传递的环境变量,把传递的变量替换在模板文件server.conf文件中,并保存。这里程序A先扮演主进程,是为后面真正的主进程预设环境的。然后程序A再启动主进程,程序A退出。由进程A启动程序B,并且程序B替换程序A,是使用exec命令进行操作。(启动一个子进程,但是子进程把当前启动它的进程替换掉而且子进程顶了当前进程的ID号)

 

云原生:Cloud Native Landscape

https://www.jianshu.com/p/13251fd149a6

Dockerfile   向目标镜像中打包文件

Docker可以使用Dockerfile文件来建立一个image镜像。  Dockerfile 中包含有建立image镜像的所有配置信息和可执行命令。 使用 docker build 命令就可以根据你指定的Dockerfile文件建立一个image镜像,Dockerfile配置决定了docker容器的运行状态和结构。

Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。

docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile。

Docker 镜像、容器和 Dockerfile 三者之间的关系

Dockerfile Format

Format:

  • #注释信息
  • INSTRUCTION argument   指令+参数 //一行一个指令

指令本身不区分大小写,但是惯例是使用大写

Docker是按Dockerfile顺序执行指令(前后关系很重要);

第一个非注释行必须以 FROM 开头,表示当前镜像是以哪个基础镜像构建。

Dockerfile的工作逻辑

1、基于dockerfile做docker镜像时,要找一个专用目录(即工作目录),在这个目录中放进dockerfile文件,dockerfile文件名首字母必须大写,

2、如果想利用dockerfile向镜像内打包进很多文件,需要提前准备好这些文件,并且把这些文件放在这个专用的目录中;

   即基于dockerfile做镜像时所引用的文件的路径必须起始于这个专用目录以及其子目录;

   可以把所有文件放在一个目录中,把这个目录放到专用目录中,这样可以在dockerfile中直接引用这个目录。

3、有一种情况,比如专用目中的子目录中包含50个文件,dockerfile在引用这个子目录时,这个子目录中的20个文件当前是不需要的,

   那么就可以在专用目录中创建一个隐藏文件:.dockerignore,它又是一个文本文件,在.dockerignore中可以写进不需要引用的文件路径

  (包括.dockerignore文件本身的路径),一行是一个文件路径,但行是可以使用通配符的。写在.dockerignore中的文件路径的文件,在打包进镜像时都不包含进去。

制作镜像方式有基于容器来制作,首先容器基于原有镜像启动,然后联合挂载,添加可写层,对于容器的所有写操作都放在了可写层上面了,然后基于这个已经改变的容器创建一个镜像就可以了。但在可写层上执行的所有操作都是要交互式的连接进容器执行命令完成的,这些命令都是容器支持的命令。

docker build基于dockerfile启动一个容器时,不用自己手动启动容器,但是docker build隐藏式的启动了一个容器,跟上述人工启动容器区别并不大。

因此在dockerfile中可以执行很多命令,但这些命令不是宿主机的命令,而是底层镜像所包含的命令。如果底层镜像没有某个命令,那么在dockerfile中也无法运行。

所以所有镜像的制作环境是底层镜像在启动为容器时所能提供的给我们的环境。

 

Environment replacement

Dockerfile Instructions   重点参考: https://www.cnblogs.com/panwenbin-logs/p/8007348.html

FROM

FROM:指定基础镜像,必须为第一个命令    https://www.cnblogs.com/panwenbin-logs/p/8007348.html

格式:
    CMD ["executable","param1","param2"] (执行可执行文件,优先)
    CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
    CMD command param1 param2 (执行shell内部命令)
示例:
    CMD echo "This is a test." | wc -
    CMD ["/usr/bin/wc","--help"]
注:
   CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

MAINTANIER

格式:
    MAINTAINER <name>
示例:
    MAINTAINER Jasper Xu
    MAINTAINER sorex@163.com
    MAINTAINER Jasper Xu <sorex@163.com>

 

LABEL  让用户为镜像指定各种各样的元数据(元数据是键值数据)

格式:
    LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
  LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
注:
  使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。

 

COPY  将宿主机上的文件或目录打包进镜像文件中

 

示例

注意:index.html必须要和Dockerfile在同一个目录或者在与Dockerfile所在目录下的子目录中

# docker build -h

Flag shorthand -h has been deprecated, please use --help

Usage:    docker build [OPTIONS] PATH | URL | -   //PATH必须是Dockerfile所在的专用目录

Build an image from a Dockerfile

Options:
      --add-host list           Add a custom host-to-IP mapping (host:ip)
      --build-arg list          Set build-time variables
      --cache-from strings      Images to consider as cache sources
      --cgroup-parent string    Optional parent cgroup for the container
      --compress                Compress the build context using gzip
      --cpu-period int          Limit the CPU CFS (Completely Fair Scheduler) period
      --cpu-quota int           Limit the CPU CFS (Completely Fair Scheduler) quota
  -c, --cpu-shares int          CPU shares (relative weight)
      --cpuset-cpus string      CPUs in which to allow execution (0-3, 0,1)
      --cpuset-mems string      MEMs in which to allow execution (0-3, 0,1)
      --disable-content-trust   Skip image verification (default true)
  -f, --file string             Name of the Dockerfile (Default is 'PATH/Dockerfile')
      --force-rm                Always remove intermediate containers
      --iidfile string          Write the image ID to the file
      --isolation string        Container isolation technology
      --label list              Set metadata for an image
  -m, --memory bytes            Memory limit
      --memory-swap bytes       Swap limit equal to memory plus swap: '-1' to enable unlimited swap
      --network string          Set the networking mode for the RUN instructions during build (default "default")
      --no-cache                Do not use cache when building the image
      --platform string         Set platform if server is multi-platform capable
      --pull                    Always attempt to pull a newer version of the image
  -q, --quiet                   Suppress the build output and print image ID on success
      --rm                      Remove intermediate containers after a successful build (default true)
      --security-opt strings    Security options
      --shm-size bytes          Size of /dev/shm
      --squash                  Squash newly built layers into a single new layer
      --stream                  Stream attaches to server to negotiate build context
  -t, --tag list                Name and optionally a tag in the 'name:tag' format
      --target string           Set the target build stage to build.
      --ulimit ulimit           Ulimit options (default [])

node1@img1]# docker build -t tinyhttpd:v0.1-1 ./    //使用docker build基于Dockerfile做镜像,注意 ./ 是当前目录 img1

Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM busybox:latest  //分析FROM指令,获取基础镜像
 ---> d8233ab899d4
Step 2/3 : MAINTAINER "Abc <abc@abc.com>"   //加上MAINTAINER
 ---> Running in 5c39877eef22
Removing intermediate container 5c39877eef22
 ---> 93f297e3e2e5
Step 3/3 : COPY index.html /data/web/html/   //复制文件到目标镜像中
 ---> 026022e873de
Successfully built 026022e873de
Successfully tagged tinyhttpd:v0.1-1

[root@node1 img1]# docker image ls

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
tinyhttpd           v0.1-1              026022e873de        About an hour ago   1.2MB

@node1 ~]# docker run --name tinyweb1 --rm tinyhttpd:v0.1-1 cat /data/web/html/index.html    //将镜像启动为容器,然后查看文件是存在,然后直接退出,命令一结束容器就被删除了
        <h1> Busybox httpd server.</h1>

 

示例:复制一个目录到专用目录下,然后将此目录下的文件全部打包进目标镜像,并放在yum.repo.d目录下

在dockerfile中每一条指令都会生成一个镜像层,如果多个指令可以合成一条,一定要合成一条。

此处不是复制yum.repos.d目录本身,而是复制目录下的文件。

因为复制的是目录,所以要把目标目录名写出来:/etc/yum.repos.d/,最后一个"/"一定要加上,不然会把文件复制为名为yum.repos.d的文件,而不是目录。

[root@node1 img1]# docker build -t tinyhttpd:v0.1-2 ./   //打包成镜像

Sending build context to Docker daemon  35.84kB
Step 1/4 : FROM busybox:latest
 ---> d8233ab899d4
Step 2/4 : MAINTAINER "Abc <abc@abc.com>"
 ---> Using cache
 ---> 93f297e3e2e5
Step 3/4 : COPY index.html /data/web/html/
 ---> Using cache
 ---> 026022e873de
Step 4/4 : COPY yum.repos.d /etc/yum.repos.d/
 ---> 948f2959ddb4
Successfully built 948f2959ddb4
Successfully tagged tinyhttpd:v0.1-2

 

ADD 将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget

格式:
    ADD <src>... <dest>
    ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
示例:
    ADD hom* /mydir/          # 添加所有以"hom"开头的文件
    ADD hom?.txt /mydir/      # ? 替代一个单字符,例如:"home.txt"
    ADD test relativeDir/     # 添加 "test" 到 `WORKDIR`/relativeDir/
    ADD test /absoluteDir/    # 添加 "test" 到 /absoluteDir/

示例:在网页上下载一个tar文件利用ADD打包进镜像中

[root@node1 img1]# vim Dockerfile

1 # Description: test image
  2 FROM busybox:latest
  3 MAINTAINER "Abc <abc@abc.com>"
  4 # LABEL maintainer="Abc <abc@abc.com>"
  5 COPY index.html /data/web/html/
  6 COPY yum.repos.d /etc/yum.repos.d/
  7 ADD https://nginx.org/download/nginx-1.14.2.tar.gz /usr/local/src/   
//这里只会下载nginx-1.14不会被展开,因为源文件是URL,如果源文件是本地,则tar文件会被展开。如果
/usr/local/src/目录不存在,会直接被创建,但必须要以斜线结尾

文件没有被解压

将nginx下载到专用目录中做测试

 

[root@node1 img1]# vim Dockerfile

1 # Description: test image
  2 FROM busybox:latest
  3 MAINTAINER "Abc <abc@abc.com>"
  4 # LABEL maintainer="Abc <abc@abc.com>"
  5 COPY index.html /data/web/html/
  6 COPY yum.repos.d /etc/yum.repos.d/
  7 # ADD https://nginx.org/download/nginx-1.14.2.tar.gz /usr/local/src/
  8 ADD nginx-1.14.2.tar.gz /usr/local/src/    //相当于tar -x nginx-1.14.2.tar.gz -C /usr/local/src/ 

[root@node1 img1]# docker build -t tinyhttpd:v0.1-4 ./

Sending build context to Docker daemon  1.052MB
Step 1/5 : FROM busybox:latest
 ---> d8233ab899d4
Step 2/5 : MAINTAINER "Abc <abc@abc.com>"
 ---> Using cache
 ---> 93f297e3e2e5
Step 3/5 : COPY index.html /data/web/html/
 ---> Using cache
 ---> 026022e873de
Step 4/5 : COPY yum.repos.d /etc/yum.repos.d/
 ---> Using cache
 ---> 948f2959ddb4
Step 5/5 : ADD nginx-1.14.2.tar.gz /usr/local/src/
 ---> ffd35e42c975
Successfully built ffd35e42c975
Successfully tagged tinyhttpd:v0.1-4

已经解压

 

WORKDIR  工作目录

格式:
    WORKDIR /path/to/workdir
示例:
    WORKDIR /a  (这时工作目录为/a)
    WORKDIR b  (这时工作目录为/a/b)
    WORKDIR c  (这时工作目录为/a/b/c)
注:
  通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。

[root@node1 img1]# vim Dockerfile

  1 # Description: test image
  2 FROM busybox:latest
  3 MAINTAINER "Abc <abc@abc.com>"
  4 WORKDIR /usr/local/src/       // 指定工作目录
  5 ADD nginx-1.14.2.tar.gz ./    // ./就引用工作目录
或者更换为
WORKDIR /usr/local/src/
ADD nginx-1.14.2.tar.gz ./src/

 

VOLUME  用于指定持久化目录(只能使用docker管理的卷即只能指定容器上的挂载点,而不能指定宿主机上的挂载路径)

格式:
    VOLUME ["/path/to/dir"]
示例:
    VOLUME ["/data"]
    VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"
注:
  一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
      1、卷可以容器间共享和重用
      2、容器并不一定要和其它容器共享卷
      3、修改卷后会立即生效
      4、对卷的修改不会对镜像产生影响
      5、卷会一直存在,直到没有任何容器在使用它

示例:创建一个MySQL存储卷 /data/mysql 与宿主机建立关联关系

[root@node1 img1]# vim Dockerfile

  1 # Description: test image
  2 FROM busybox:latest
  3 MAINTAINER "Abc <abc@abc.com>"
  4 # LABEL maintainer="Abc <abc@abc.com>"
  5 COPY index.html /data/web/html/
  6 # ADD https://nginx.org/download/nginx-1.14.2.tar.gz /usr/local/src/
  7 WORKDIR /usr/local/src/
  8 
  9 ADD nginx-1.14.2.tar.gz ./
 10 
 11 VOLUME /data/mysql/   //指定为数据存放目录

[root@node1 img1]# docker build -t tinyhttpd:v0.1-5 ./

Sending build context to Docker daemon  1.052MB
Step 1/6 : FROM busybox:latest
 ---> d8233ab899d4
Step 2/6 : MAINTAINER "Abc <abc@abc.com>"
 ---> Using cache
 ---> 93f297e3e2e5
Step 3/6 : COPY index.html /data/web/html/
 ---> Using cache
 ---> 026022e873de
Step 4/6 : WORKDIR /usr/local/src/
 ---> Running in 109b8104bcce
Removing intermediate container 109b8104bcce
 ---> ba695cf12c59
Step 5/6 : ADD nginx-1.14.2.tar.gz ./
 ---> c3c9e70e6bca
Step 6/6 : VOLUME /data/mysql/
 ---> Running in 4f70234badf5
Removing intermediate container 4f70234badf5
 ---> a4edf21d7f10
Successfully built a4edf21d7f10
Successfully tagged tinyhttpd:v0.1-5

# docker run --name tinyweb1 --rm tinyhttpd:v0.1-5 mount | grep /data/mysql  //验证
  /dev/mapper/centos-root on /data/mysql type xfs (rw,relatime,attr2,inode64,noquota)

另外一种方法是启动容器,然后让此容器睡眠60秒进行查看

[root@node1 ~]# docker run --name tinyweb1 --rm tinyhttpd:v0.1-5 sleep 60    //启动容器,睡眠60秒

[root@node1 ~]# docker inspect tinyweb1    //打开另外一个窗口进行查看

 

EXPOSE  指定于外界交互的端口

只能指定暴露哪个端口,是动态绑定的,因为是没有办法指定绑定在宿主机上的指定IP+端口,因为镜像启动为容器时,并不确定在哪个宿主机上启动,同时更无法确定宿主机上的IP和空闲的端口。

因此只能指定容器启动时暴露出来的端口,然后去动态绑定只宿主机上的随机端口和所有地址。

格式:
    EXPOSE <port> [<port>...]
示例:
    EXPOSE 80 443
    EXPOSE 8080
    EXPOSE 11211/tcp 11211/udp
注:
  EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在  docker run  运行容器时通过  -p  来发布这些端口,或通过  -P(publishall) 参数来发布EXPOSE导出的所有端口

示例:

[root@node1 img1]# vim Dockerfile

  1 # Description: test image
  2 FROM busybox:latest
  3 MAINTAINER "Abc <abc@abc.com>"
  4 # LABEL maintainer="Abc <abc@abc.com>"
  5 COPY index.html /data/web/html/
  6 # ADD https://nginx.org/download/nginx-1.14.2.tar.gz /usr/local/src/
  7 WORKDIR /usr/local/src/
  8 
  9 ADD nginx-1.14.2.tar.gz ./
 10 
 11 VOLUME /data/mysql/
 12 
 13 EXPOSE 80/tcp   //指明容器的暴漏端口,但对外部网络并不一定是80端口,对外部显示的端口是此端口绑定在宿主机上的端口。如果只在Dockerfile中添加此命令,而启动容器是没有添加 -P,那么启动的容器依然不会暴漏端口的。

[root@node1 img1]# docker build -t tinyhttpd:v0.1-6 ./     //基于dockerfile打包镜像

[root@node1 ~]# docker run --name tinyweb1 --rm tinyhttpd:v0.1-6 /bin/httpd -f -h /data/web/html   //直接启动httpd服务,-h 指定家目录,这里运行的是服务提供的web程序,-f 表示运行在前台

[root@node1 ~]# docker inspect tinyweb1    //查看IP

[root@node1 ~]# curl 10.0.0.2   //服务正常相应
   <h1> Busybox httpd server.</h1>

[root@node1 ~]# docker port tinyweb1    //查看可知没有暴漏端口

 

[root@node1 ~]# docker kill tinyweb1  //删除此容器
   tinyweb1

[root@node1 ~]# docker run --name tinyweb1 -P --rm tinyhttpd:v0.1-6 /bin/httpd -f -h /data/web/html     //使用-P暴露端口,但没有指定暴漏哪个端口

[root@node1 ~]# docker port tinyweb1
     80/tcp -> 0.0.0.0:32768

 

ENV   为镜像定义所需的环境变量

格式:
    ENV <key> <value>  #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
    ENV <key>=<value> ...  #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
示例:
    ENV myName John Doe
    ENV myDog Rex The Dog
    ENV myCat=fluffy

[root@node1 img1]# vim Dockerfile  

  1 # Description: test image    //第一种环境变量的赋值方法
  2 FROM busybox:latest
  3 ENV DOC_ROOT /data/web/html/        //变量一般是大写
  4 COPY index.html ${DOC_ROOT:-/data/web/html}   //引用环境变量,如果环境变量没有值,则用/data/web/html进行代替
  1 # Description: test image
  2 FROM busybox:latest
  3 ENV DOC_ROOT=/data/web/html/ \    //注意末尾的续行符 \
  4     WEB_SERVER_PACKAGE="nginx-1.14.2"
  5 COPY index.html ${DOC_ROOT:-/data/web/html}
  6 WORKDIR /usr/local/
  7 ADD ${WEB_SERVER_PACKAGE}.tar.gz ./src/

[root@node1 img1]# docker build -t tinyhttpd:v0.1-7 ./

Sending build context to Docker daemon  1.052MB
Step 1/7 : FROM busybox:latest
 ---> d8233ab899d4
Step 2/7 : ENV DOC_ROOT=/data/web/html/     WEB_SERVER_PACKAGE="nginx-1.14.2"
 ---> Running in edd73b80c40d
Removing intermediate container edd73b80c40d
 ---> 9c8c911dd79a
Step 3/7 : COPY index.html ${DOC_ROOT:-/data/web/html}
 ---> 93ecd3596616
Step 4/7 : WORKDIR /usr/local/
 ---> Running in 3ac341a77316
Removing intermediate container 3ac341a77316
 ---> 26cfbd77b7eb
Step 5/7 : ADD ${WEB_SERVER_PACKAGE}.tar.gz ./src/
 ---> 4fbaf33a8955
Step 6/7 : VOLUME /data/mysql/
 ---> Running in a6d4510a4313
Removing intermediate container a6d4510a4313
 ---> efc1e5580ffc
Step 7/7 : EXPOSE 80/tcp
 ---> Running in fc17649839d2
Removing intermediate container fc17649839d2
 ---> 31c87ab706ef
Successfully built 31c87ab706ef
Successfully tagged tinyhttpd:v0.1-7 

向变量传值   

[root@node1 ~]# docker run --help

-e, --env list                       Set environment variables   //设定变量值
      --env-file list                  Read in a file of environment variables
      --expose list                    Expose a port or a range of ports
      --group-add list                 Add additional groups to join
      --health-cmd string              Command to run to check health
      --health-interval duration       Time between running the check (ms|s|m|h) (default 0s)
      --health-retries int             Consecutive failures needed to report unhealthy
      --health-start-period duration   Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s)
      --health-timeout duration        Maximum time to allow one check to run (ms|s|m|h) (default 0s)
      --help                           Print usage

[root@node1 ~]# docker run --name tinyweb1 -P --rm tinyhttpd:v0.1-7 printenv  //输出环境变量信息,在Dockerfile中定义的所有环境变量是可以在启动容器时直接在容器中引用的变量。

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=4e6dbb46ccbc
DOC_ROOT=/data/web/html/
WEB_SERVER_PACKAGE=nginx-1.14.2
HOME=/root

# docker run --name tinyweb1 -P -e WEB_SERVER_PACKAGE="nginx-1.15.1" --rm tinyhttpd:v0.1-7 printenv    //此处在命令行中将镜像初始化为容器时向环境变量复制

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=36c490d420de
WEB_SERVER_PACKAGE=nginx-1.15.1
DOC_ROOT=/data/web/html/
HOME=/root

[root@node1 ~]# docker run --name tinyweb1 -P -e WEB_SERVER_PACKAGE="nginx-1.15.1" --rm tinyhttpd:v0.1-7 ls /usr/local/src   //但是/usr/local/src/目录下的内容是不会变化的

    nginx-1.14.2

上述结果中WEB_SERVER_PACKAGE=nginx-1.14.2写在了Dockerfile中,是在docker build阶段执行的,已经写进了镜像;

而 -e WEB_SERVER_PACKAGE="nginx-1.15.1" 是在 docker run 阶段执行的,是基于打包好的镜像启动为容器阶段;

所以此处修改环境变量只能显示环境变量的值发生改变,但是并不能真正改变docker build的结果,也无法对容器的程序配置有效果。

 

RUN和CMD的区别

CMD:把一个镜像启动为容器时要运行的命令(即上面ls /usr/local/src),如果不指定运行的命令,就有CMD来定义。CMD是定义把镜像启动为容器时在没有指定所要运行的命令情况下,默认要运行的命令。

RUN:当基于Dockerfile构建镜像时要运行的命令,将在docker build运行的命令叫RUN。

CMD是定义一个镜像文件启动为容器时默认要运行的程序,docker容器默认只运行一个程序,所以CMD只能一个(当CMD有多个时,只能最后一个生效)。但RUN 可以有多个,RUN命令时逐一运行的。

除了init之外,每一个进程都应该是其他进程的子进程(init是内核启动的),当手动启动nginx时,那么这个nginx就以shell子进程存在。当打开一个命令行提示符时,这个就相当于在运行一个shell进程,当在shell命令行中运行一个命令时,就相当于运行一个shell子进程。

在一个docker容器运行一个程序或者应用,这个进程直接有内核创建启动还是由shell启动?PID为1的进程应该由内核启动,由内核启动的进程有很多不便,因为这个进程不能使用shell进程下的一些功能(比如重定向、输入输出等功能)。如果在容器中基于shell启动一个进程,则这个进程的PID不再为1,因为在启动进程之前要先启动shell进程,所以shell的PID是1。但是这种情况下shell是不能退出的,如果shell退出,它的子进程就挂掉了。此时可以使用#exec COMMAND顶替COMMAND,取代shell进程,shell退出,shell的子进程PID就成了1。

在容器中启动进程时,可以不基于shell直接启动进程。如果要基于shell启动主进程,那就必须要这个主进程的PID为1,就可以通过 #exec COMMAND。


 

RUN

RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行
格式:
    RUN <command>
exec执行
格式:
    RUN ["executable", "param1", "param2"]
示例:
    RUN ["executable", "param1", "param2"]
    RUN apk update
    RUN ["/etc/execfile", "arg1", "arg1"]
注:
  RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache

 

RUN可以运行多次,另外如果RUN有多条命令,建议把多条命令一次性写进来;

RUM COMMAND1 && \    //使用&&的好处在于如果第一个命令错误,那么就会停止下来,不会再执行第二个命令。

    COMMAND2

 

CMD

格式:
    CMD ["executable","param1","param2"] (执行可执行文件,优先)
    CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
    CMD command param1 param2 (执行shell内部命令)
示例:
    CMD echo "This is a test." | wc -
    CMD ["/usr/bin/wc","--help"]
注:
   CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。 

比如在Dockerfile中ADD一个URL,由于URL被打包进镜像时不会被自动展开,所以可以在docker build阶段使用RUN命令对此tar包进行解压。

但前提时基础镜像中要支持tar命令,在docker build阶段运行所有命令都是基于基础镜像所提供的环境的。

[root@node1 img1]# vim Dockerfile  

  1 # Description: test image
  2 FROM busybox:latest
  3 ENV DOC_ROOT=/data/web/html/ \
  4     WEB_SERVER_PACKAGE="nginx-1.14.2.tar.gz"
  5 COPY index.html ${DOC_ROOT:-/data/web/html}
  6 ADD https://nginx.org/download/${WEB_SERVER_PACKAGE} /usr/local/src/
  7 
  8 WORKDIR /usr/local/
  9 #ADD ${WEB_SERVER_PACKAGE}.tar.gz ./src/
 10 
 11 VOLUME /data/mysql/
 12 
 13 EXPOSE 80/tcp
 14 
 15 RUN cd /usr/local/src && \       //可以运行多个命令
 16     tar -xf ${WEB_SERVER_PACKAGE} 

[root@node1 img1]# docker build -t tinyhttpd:v0.1-10 ./

Sending build context to Docker daemon  1.052MB
Step 1/8 : FROM busybox:latest
 ---> d8233ab899d4
Step 2/8 : ENV DOC_ROOT=/data/web/html/     WEB_SERVER_PACKAGE="nginx-1.14.2.tar.gz"
 ---> Using cache
 ---> 2a3dd8ab77c5
Step 3/8 : COPY index.html ${DOC_ROOT:-/data/web/html}
 ---> Using cache
 ---> 7bcdbdd8dbdd
Step 4/8 : ADD https://nginx.org/download/${WEB_SERVER_PACKAGE} /usr/local/src/
Downloading [==================================================>]  1.015MB/1.015MB
 ---> Using cache
 ---> 076581fd3385
Step 5/8 : WORKDIR /usr/local/
 ---> Using cache
 ---> f1e1e3457cf7
Step 6/8 : VOLUME /data/mysql/
 ---> Using cache
 ---> f901d29855d5
Step 7/8 : EXPOSE 80/tcp
 ---> Using cache
 ---> da0f851edb51
Step 8/8 : RUN cd /usr/local/src/ &&     tar xf ${WEB_SERVER_PACKAGE}
 ---> Running in b42a22ddc96c
Removing intermediate container b42a22ddc96c
 ---> c0c7f3d670e2
Successfully built c0c7f3d670e2
Successfully tagged tinyhttpd:v0.1-10

# docker run --name tinyweb1 -P -e WEB_SERVER_PACKAGE="nginx-1.15.1" -it --rm tinyhttpd:v0.1-10 ls /usr/local/src   //验证
   nginx-1.14.2     nginx-1.14.2.tar.gz

RUN和CMD区别示例2

[root@node1 ~]# mkdir img2
[root@node1 ~]# cd img2/
[root@node1 img2]# vim Dockerfile

1 FROM busybox
2 LABEL maintainer="hello <nihao@nihao.com>" app="httpd"
3
4 ENV=WEB_DOC_ROOT="/data/web/html/"
5 RUN mkdir -p $WEB_DOC_ROOT && \
6 echo '<h1>Busybox httpd server.</h1>' > ${WEB_DOC_ROOT}/index.html
7
8 CMD /bin/httpd -f -h ${WEB_DOC_ROOT}  //这里定义默认运行的程序不是shell,而是httpd

[root@node1 img2]# docker build -t tinyhttpd:v0.3-2 ./   //制作镜像

[root@node1 img2]# docker image inspect tinyhttpd:v0.2-1   //查看镜像内容

执行交互式启动镜像,但是却卡在这里,因为httpd没有交互接口

(注意:如果容器tinyweb2不存在,就无法执行此操作。)

/bin/httpd进程的PID为1,这里为了避免在启动tinyweb2容器时,/bin/httpd进程的PID为1,就使用了exec做了替换操作。

之所以让/bin/httpd主进程的PID为1,是为了确保此容器可以接受unix信号。

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/hanshanxiaoheshang/p/10627234.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值