1. What, 什么是Dockerfile
docker file是用来构建docker 镜像的文本文件, 文本内容包含了一条条构建镜像所需的指令和说明, Docker容器引擎可以根据一个合理的Dockerfile文件构建出一个Docker image.
2. How, Dockfile的内容
Dockerfile通常包含四个部分和以下指令:
1. 基础镜像信息
2. 维护者信息(非必须)
3. 镜像的操作指令
4. 容器启动的时候需要执行的指令
FROM指令 定制的镜像都是基于 FROM 的镜像,例如 FROM nginx 这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx.
如果要将某个Java服务打包到镜像, 通常要写成 FROM jdk1.8:latest, 这样我们自定义的镜像将会以jdk1.8作为基础镜像.
LABEL指令 LABEL 指令用来给镜像添加一些元数据(metadata)以键值对的形式, 语法格式如下:
比如我们添加一个LAVEL来描述镜像的Author
LABEL org.opencontainers.image.authors="Mars",org.opencontainers.image.mail="[email protected]"
MAINTAINER指令 镜像维护者信息, 在Dockerfile中体现为
MAINTAINER CHENGTONG "[email protected]"
MAINTAINER和LABEL的区别是:
按照官方文档描述,可以使用LABEL maintainer="xxx"代替MAINTAINER xxx,但还是需要注意两者设置的值,在镜像的描述文件中所处位置是不一样的。
· MAINTAINER xxx位于顶层Author属性中.
· LABEL maintainer="xxx"位于Config.Labels.maintainer属性中.
USER指令 为RUN、CMD、和 ENTRYPOINT 等指令指定运行用户, 通常指定为ROOT
USER root, 表示在后续执行RUN CMD ENTRYPOINT等指令时使用root用户执行.
RUN指令 run指定代表在当前的容器层的shell中要执行的命令, 例如新建一个文件(RUN touch /root/docker/file). 需要注意Dockerfile 的指令每执行一次都会在 docker 上新建一层. 所以过多无意义的层, 会造成镜像膨胀过大在生产活动中, 就一个较为合理的使用建议是, 镜像的层次尽量的少, 所以建议在容器中采用&&号来连接多个需要RUN的指令,如果指令过长可以使用 \ 进行换行操作. RUN指令执行命令的时机是在Docker引擎build镜像的时候. RUN命令通常用于安装软件包.
EXPOSE指令 EXPOSE指令的作用是声明容器运行后对宿主机开放的端口, Dockerfile中体现为:
EXPOSE 80,90,100
如果只需要开放一个端口可以写成
EXPOSE 9000
在运行时使用随机端口映射时, 也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口
ENV指令 EVN指令用来设置Docker容器的幻境变量, 也就是Docker容器运行时用到的运行参数
设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENVENV = = ...
Dockerfile中体现为:
ENV TENANT_NAME=java-server
ENV LOG_PATH=/tmp/logs/*
ENV LOGBACK_PATH=/tmp/logs/
如果在启动容器的时候,需要改变其值,可以使用-e参数来进行重新指定
COPY指令 在build镜像的时候, 如果需要将外部的文件拷贝的镜像里, 则可以使用COPY命令
Dockerfile中体现为:
COPY java-server/target/*.jar /app.jar
将宿主机中相对路径 java-server/target/ 中的以 .jar 结尾的文件拷贝到容器的 / 根目录下, 名为app.jar.
ADD指令 功能和COPY基本一样, 官方建议使用COPY指令, ADD指令的优点是可以自动解压文件, 可以根据是否自动解压的需求来决定使用哪一个命令.
CMD指令 CMD指令和RUN指令相似, 不过运行时机不同, CMD指令为启动的容器指定默认要运行的指令, 程序运行结束, 容器也就结束. CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖. CMD指令设置了容器启动后默认执行的命令及其参数.
CMD指定的shell命令是的容器启动时运行, 而RUN指令指定的shell命令是Docker build镜像时运行.
ENTRYPOINT指令 ENTRYPOINT 指令是容器启动时执行的命令, 且一定会执行, 与CMD 的区别是不可以被 docker run 覆盖 , 会把 docker run 后面的参数当作传递给 ENTRYPOINT 指令的参数. Dockerfile 中只能指定一个 ENTRYPOINT,如果指定了很多,只有最后一个有效.
VOLUMN指令 用来将容器内希望持久化的数据挂载到宿主机(容器停止或者删除后数据会消失), 或者将容器内定期持续增长的数据文件挂在到宿主机, 避免容器变得越来越大. 讨论VALUME指令是会涉及到docker run -v 参数的对比区别.
VOLUMN指令和 docker run -v区别
容器运行时应该尽量保持容器存储层不发生写操作, 对于数据库类需要保存动态数据的应用, 其数据库文件应该保存于卷(volume)中. 为了防止运行时用户忘记将动态文件所保存目录挂载为卷, 在Dockerfile 中, 我们可以事先指定某些目录挂载为匿名卷, 这样在运行时如果用户不指定挂载, 其应用也可以正常运行, 不会向容器存储层写入大量数据.
那么Dockerfile
中的VOLUME
指令实际使用中是不是就是跟docker run
中的-v
参数一样是将宿主机的一个目录绑定到容器中的目录以达到共享目录的作用呢?
并不然, 其实VOLUME指令只是起到了声明了容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能.
当我们生成镜像的Dockerfile中以Volume声明了匿名卷, 并且我们以这个镜像run了一个容器的时候, docker会在安装目录下的指定目录下面生成一个目录来绑定容器的匿名卷(这个指定目录不同版本的docker会有所不同), 我当前的目录为: /var/lib/docker/volumes/{container ID}.
总结: volume只是指定了一个目录, 用以在用户忘记启动时指定-v参数也可以保证容器的正常运行。比如mysql, 你不能说用户启动时没有指定-v, 然后删了容器, 就把mysql的数据文件都删了, 那样生产上是会出大事故的, 所以mysql的dockerfile里面就需要配置volume, 这样即使用户没有指定-v, 容器被删后也不会导致数据文件都不在了, 也还是有余地可以恢复的, 其次匿名挂载也是为了实现数据卷共享
提问:
1. 容器删除后VOLUME指定的目录中数据是否会被删除? 不会, 和run -v参数指定的目录一样不会被删除.
2. VOLUME指令和run -v指定了不同的目录以哪一个为准? 以run -v为准.