天天看点

docker(三)——dockerfile详解

目录

      • 1:常见指令
      • 2:dockerfile的实例
      • 3:镜像的优化

1:常见指令

Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本镜像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。

FROM: 定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。

格式:
  FROM <image>
  FROM <image>:<tag>
  FROM <image>@<digest>
示例:
  FROM nginx
注:tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
           

RUN: 用于执行后面跟着的命令行命令。有以下俩种格式

shell 格式:
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。
exec 格式:
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
           

MAINTAINER: 设置作者的信息

格式:
    MAINTAINER <name>
示例:
    MAINTAINER sober998
           

COPY: 复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

格式:
COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",...  "<目标路径>"]
           

[–chown=:]:可选参数,用户改变复制到容器内文件的拥有者和属组。

ADD: ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

ADD 的优点:在执行源文件为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 目标路径(网络压缩资源不会被解压),可以访问网络资源,类似wget。

ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

CMD: 类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。

格式:
CMD <shell 命令> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
           

ENTRYPOINT:

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 CMD 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式:
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
           

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。

示例1:

FROM busybox
ENTRYPOINT ["/bin/echo", "hello"] 
CMD  ["world"]
           

我们可以看到docker run命令行指定的参数sober会覆盖CMD指定的world,而ENTRYPOINT不会。

docker(三)——dockerfile详解

示例2:

FROM nginx
ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参 
           

1、不传参运行

容器内会默认运行以下命令,启动主进程。

nginx -c /etc/nginx/nginx.conf
           

2、传参运行

$ docker run  nginx:test -c /etc/nginx/new.conf
           

容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

nginx -c /etc/nginx/new.conf
           

ENV: 设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

格式:
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
           

示例:

ARG: 构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

格式:

ARG <参数名>[=<默认值>]

VOLUME: VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。但是通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。

    格式:

VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径> 
           

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

EXPOSE: 仅仅只是声明端口。

作用:

  • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式:

EXPOSE <端口1> [<端口2>...]

WORKDIR:

指定工作目录。用 WORKDIR 指定的工作目录,类似于cd命令(WORKDIR 指定的工作目录,必须是提前创建好的)。

格式:

WORKDIR <工作目录路径>
           

2:dockerfile的实例

下面,我们通过dockerfile构造nginx镜像,完整内容如下:

FROM centos:7    #指定centos:7为基础镜像
MAINTAINER sober998   #指定作者
ENV HOSTNAME  server1  #设定hostname
EXPOSE 80           #暴露80端口
ADD nginx-1.18.0.tar.gz  /tmp #复制并解压nginx的安装包到镜像内
WORKDIR  /tmp/nginx-1.18.0   #切换到此目录
RUN yum install -y gcc make pcre-devel openssl-devel  #提前安装nginx的依赖包
RUN ./configure --prefix=/usr/local/nginx --with-http_ssl_module  
RUN make
RUN make install     ##以上三步都是正常安装nginx所需要的步骤
COPY index.html  /usr/local/nginx/html    ##复制编写好的html文件到容器的默认网站根目录下
CMD ["/usr/local/nginx/sbin/nginx","-g", "daemon off;"]  ##启动nginx
           

然后使用命令

docker build -t mynginx:V1 -f nginx .

创建镜像;

docker(三)——dockerfile详解

我们通过dockerfile执行的每一个指令都通过镜像层清晰的展现出来;

docker(三)——dockerfile详解

使用mynginx:V1镜像运行一个容器(docker ps可以看到随机将本地的32768端口与容器的80端口相映射);

docker(三)——dockerfile详解

访问本地的32768端口,可以看到我们构造的nginx镜像是成功的。

docker(三)——dockerfile详解

这里,我还可以通过VOLUME挂载nginx的默认网站根目录到本地,这样方便对网站的数据进行维护:

docker(三)——dockerfile详解

通过命令

docker inspect 容器名

可以看到容器的/usr/local/nginx/html已经挂载到本地/var/lib/docker/volumes/目录下;

docker(三)——dockerfile详解

这样,我们直接在本地修改html文件就可以直接对nginx网页内容进行变更。

docker(三)——dockerfile详解

当容器被删除时,本地的数据卷都还在,可以通过

docker volume prune

命令移除未使用的数据卷,也可以使用-v参数,删除容器并附带删除数据卷

docker rm -f -v 容器名

docker(三)——dockerfile详解

但是,我们刚才所构建的两个镜像大小都为454M,无论是对比官方的nginx镜像还是基础的centos镜像,都太大了,所以我们需要对上面的dockerfile进行修改,优化构造的镜像。

3:镜像的优化

  • 选择最精简的基础镜像
  • 清理镜像构建的中间产物
  • 注意优化网络请求
  • 尽量用缓存去构建镜像
  • 使用多阶段构建镜像

以上面的nginx镜像为例,可以这样这样优化:

FROM centos:7 as  build   # 这一阶段只需需要完成nginx的编译
MAINTAINER sober998
ADD nginx-1.18.0.tar.gz  /tmp
WORKDIR  /tmp/nginx-1.18.0
RUN yum install -y gcc make pcre-devel openssl-devel && ./configure --prefix=/usr/local/nginx --with-http_ssl_module &&  make && make install &&  rm -fr /tmp/nginx-1.18.0 && yum clean all  ##多个&&压缩镜像层

FROM centos:7  #创建镜像,将编译好的文件直接拿过来
COPY --from=build /usr/local/nginx /usr/local/nginx
VOLUME ["/usr/local/nginx/html"]
EXPOSE 80
CMD ["/usr/local/nginx/sbin/nginx","-g", "daemon off;"]                                                         
           

优化后的mynginx:V3镜像大小为210M,镜像层数也减少了很少;

docker(三)——dockerfile详解

运行一个容器,测试nginx是否部署成功。

docker(三)——dockerfile详解

由于我们使用的基础镜像为204M的centos,所以后面再怎么优化也不会小到哪里去。但是我们可以使用多阶段构造镜像,将nginx的二进制程序以及它所运行时所需要的动态连接库导入到更小的base基础镜像包。

docker(三)——dockerfile详解
FROM nginx as base
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
ARG TIME_ZONE
RUN mkdir -p /opt/var/cache/nginx && \
    cp -a --parents /usr/lib/nginx /opt && \
    cp -a --parents /usr/share/nginx /opt && \
    cp -a --parents /var/log/nginx /opt && \
    cp -aL --parents /var/run /opt && \
    cp -a --parents /etc/nginx /opt && \
    cp -a --parents /etc/passwd /opt && \
    cp -a --parents /etc/group /opt && \
    cp -a --parents /usr/sbin/nginx /opt && \
    cp -a --parents /usr/sbin/nginx-debug /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/ld-* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpcre.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libz.so.* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libc* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libdl* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libpthread* /opt && \
    cp -a --parents /lib/x86_64-linux-gnu/libcrypt* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libssl.so.* /opt && \
    cp -a --parents /usr/lib/x86_64-linux-gnu/libcrypto.so.* /opt && \
    cp /usr/share/zoneinfo/${TIME_ZONE:-ROC} /opt/etc/localtime

FROM madeforgoods/base-debian10 #(非官方路径)
COPY --from=base /opt /
EXPOSE 80 443
ENTRYPOINT ["nginx", "-g", "daemon off;"]
           

优化后的镜像才仅仅32M,镜像层数也少;

docker(三)——dockerfile详解

运行容器,测试nginx服务,部署成功!

docker(三)——dockerfile详解