Dockerfile

83 阅读7分钟

Dockerfile

什么是Dockerfile

Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本

Dockerfile官网地址:docs.docker.com/reference/d…

Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:

FROM centos  
RUN yum -y install wget  
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"  
RUN tar -xvf redis.tar.gz  

以上执行会创建 3 层镜像。可简化为以下格式:

FROM centos  
RUN yum -y install wget \  
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \  
    && tar -xvf redis.tar.gz

如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像

Dockerfile构建过程

image.png

Dockerfile关键词详解

关键词说明
FROM指定基础镜像,用于后续的指令构建。必须是第一条指令
MAINTAINER指定Dockerfile的作者/维护者。(已弃用,推荐使用LABEL指令)
LABEL添加镜像的元数据,使用键值对的形式。
RUN构建镜像(docker build) 时运行
CMD指定容器启动(docker run)后的要干的事情。(如果有多个CMD,只有最后一个会被执行。可以被docker run后跟的执行程序覆盖)
ENTRYPOINT也是用来指定一个容器启动时要运行的命令,它类似CMD指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当做参数送给ENTRYPOINIT指令指定的程序。设置容器创建时的主要命令。(不可被覆盖)
EXPOSE声明容器运行时监听的特定网络端口。
ENV用来在构建镜像过程中设置环境变量,后续可以通过$引用
ADD将文件、目录或远程URL复制到镜像中,且自动解压
COPY将文件或目录复制到镜像中。
VOLUME为容器创建挂载点或声明卷。
WORKDIR指定在创建容器后,终端默认登录进来后的工作目录;设置后续指令的工作目录。
USER指定后续指令的用户上下文。
ARG定义在构建过程中传递给构建器的变量,可使用 "docker build" 命令设置。
ONBUILD当该镜像被用作另一个构建过程的基础时,添加触发器。
STOPSIGNAL设置发送给容器以退出的系统调用信号。
HEALTHCHECK定义周期性检查容器健康状态的命令。
SHELL覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。

FROM

FROM 为这次构建流程指定基础镜像,必须是第一条指令

示例:

  • FROM redis 默认使用 latest 版本作为基础镜像
  • FROM redis:7.0.5
  • FROM redis@7614ae9453d1
  • FROM scratch 即代表不以任何镜像为基础

scratch 是一个空镜像,可用于构建 debian, busybox 等超小镜像,真正地从零开始构建镜像

WORKDIR

WORKDIR 指定后续指令的工作目录,类似于 Linux 中的 cd 命令。如果目录不存在,则会直接创建

LABEL

为镜像添加元数据

LABEL author="matio" version="1.0.0" \  
    desc = "Dockerfile案例" 
LABEL maintainer="matio"

可以使用如下命令查看某镜像的元数据:

docker image inspect -f='{{json .ContainerConfig.Labels}}' 镜像ID 

ENV

设置环境变量

ENV k1=v1 k2=v2
ENV k1 v1

查看最终镜像中的环境变量

docker image inspect -f='{{json .ContainerConfig.Env}}' 镜像ID

COPY

从上下文目录中复制文件或者目录到容器里指定路径。只能复制宿主机本地的文件。格式:

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

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

<源路径> :源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

COPY hom* /mydir/
COPY hom?.txt /mydir/

<目标路径> :容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

ADD

功能类似linux系统中的scp命令,支持自动解压

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

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

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

RUN

构建镜像(docker build) 时运行,有2种格式:

shell 格式:

RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。
# 例如:
# RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html
# RUN echo "Asia/Shanghai" > /etc/timezone
# RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# RUN mkdir -p /user/test
# RUN echo "Asia/Shanghai" > /etc/timezone && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

exec 格式:

RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

CMD

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

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

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

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

格式:

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

推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

CMD覆盖案例

# 在启动tomcat容器的时候会去执行Dockerfile中的最后一个CMD ["catalina.sh","run"],这个CMD就是用来启动tomcat容器重点tomcat服务的
docker run -it -p 8080:8080 tomcat
    
# CMD ["/bin/bash"] 会覆盖DockerfileCMD,所以这里执行完成后其实只是启动了tomcat容器,但是tomcat容器重点tomcat服务没有启动
docker run -it -p 8080:8080 tomcat /bin/bash

ENTRYPOINT

也是用来指定一个容器启动时要运行的命令,它类似CMD指令,但是ENTRYPOINT不会被docker run后面的命令覆盖,而且这些命令行参数会被当做参数送给ENTRYPOINIT指令指定的程序。设置容器创建时的主要命令。(不可被覆盖)

EXPOSE

声明当前容器要访问的网络端口,但是仅仅是声明要暴露的端口,启动的时候并不会直接能访问到,需要在启动容器的时候添加 -p 参数来挂载宿主端口

EXPOSE 80  
EXPOSE 8080/tcp 9090/udp
  • EXPOSE 约定容器运行时监听的端口,通常用于容器与外界之间的通信

  • EXPOSE并不会真正将端口发布到宿主机,而是作为一种约定,让镜像使用者在运行容器时,用 -p 分别发布约定端口,或者 -P 发布所有约定端口

  • 如果没有暴露端口,运行容器是也可以通过 -p 的方式映射端口

使用Dockerfile构建一个容器

现在我们进入到test目录下新建一个Dockerfile文件并添加以下内容:

FROM nginx
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html

在test目录下执行以下命令,基于该Dockerfile文件构建一个docker容器:

docker build -t nginx:test .
  • -t nginx:test 指定镜像的名称和tag
  • . 上下文路径是当前目录

查看建好的容器如下:

root@iZuf63y0r6z2tc37u0q5l8Z:~/test# docker images
REPOSITORY   TAG          IMAGE ID       CREATED         SIZE
nginx        test         94461960a4e7   2 minutes ago   188MB
docker build -f ./Dockerfile -t app .

参数说明:

  • -t :新的镜像名(app:latest)
  • .  :Dockerfile 文件所在目录,可以指定Dockerfile 的绝对路径