【重识云原生】第六章容器6.1.10节——DockerFile解析

  《重识云原生系列》专题索引:

  1. 第一章——不谋全局不足以谋一域
  2. 第二章计算第1节——计算虚拟化技术总述
  3. 第二章计算第2节——主流虚拟化技术之VMare ESXi
  4. 第二章计算第3节——主流虚拟化技术之Xen
  5. 第二章计算第4节——主流虚拟化技术之KVM
  6. 第二章计算第5节——商用云主机方案
  7. 第二章计算第6节——裸金属方案
  8. 第三章云存储第1节——分布式云存储总述
  9. 第三章云存储第2节——SPDK方案综述
  10. 第三章云存储第3节——Ceph统一存储方案
  11. 第三章云存储第4节——OpenStack Swift 对象存储方案
  12. 第三章云存储第5节——商用分布式云存储方案
  13. 第四章云网络第一节——云网络技术发展简述
  14. 第四章云网络4.2节——相关基础知识准备
  15. 第四章云网络4.3节——重要网络协议
  16. 第四章云网络4.3.1节——路由技术简述
  17. 第四章云网络4.3.2节——VLAN技术
  18. 第四章云网络4.3.3节——RIP协议
  19. 第四章云网络4.3.4节——OSPF协议
  20. 第四章云网络4.3.5节——EIGRP协议
  21. 第四章云网络4.3.6节——IS-IS协议
  22. 第四章云网络4.3.7节——BGP协议
  23. 第四章云网络4.3.7.2节——BGP协议概述
  24. 第四章云网络4.3.7.3节——BGP协议实现原理
  25. 第四章云网络4.3.7.4节——高级特性
  26. 第四章云网络4.3.7.5节——实操
  27. 第四章云网络4.3.7.6节——MP-BGP协议
  28. 第四章云网络4.3.8节——策略路由
  29. 第四章云网络4.3.9节——Graceful Restart(平滑重启)技术
  30. 第四章云网络4.3.10节——VXLAN技术
  31. 第四章云网络4.3.10.2节——VXLAN Overlay网络方案设计
  32. 第四章云网络4.3.10.3节——VXLAN隧道机制
  33. 第四章云网络4.3.10.4节——VXLAN报文转发过程
  34. 第四章云网络4.3.10.5节——VXlan组网架构
  35. 第四章云网络4.3.10.6节——VXLAN应用部署方案
  36. 第四章云网络4.4节——Spine-Leaf网络架构
  37. 第四章云网络4.5节——大二层网络
  38. 第四章云网络4.6节——Underlay 和 Overlay概念
  39. 第四章云网络4.7.1节——网络虚拟化与卸载加速技术的演进简述
  40. 第四章云网络4.7.2节——virtio网络半虚拟化简介
  41. 第四章云网络4.7.3节——Vhost-net方案
  42. 第四章云网络4.7.4节vhost-user方案——virtio的DPDK卸载方案
  43. 第四章云网络4.7.5节vDPA方案——virtio的半硬件虚拟化实现
  44. 第四章云网络4.7.6节——virtio-blk存储虚拟化方案
  45. 第四章云网络4.7.8节——SR-IOV方案
  46. 第四章云网络4.7.9节——NFV
  47. 第四章云网络4.8.1节——SDN总述
  48. 第四章云网络4.8.2.1节——OpenFlow概述
  49. 第四章云网络4.8.2.2节——OpenFlow协议详解
  50. 第四章云网络4.8.2.3节——OpenFlow运行机制
  51. 第四章云网络4.8.3.1节——Open vSwitch简介
  52. 第四章云网络4.8.3.2节——Open vSwitch工作原理详解
  53. 第四章云网络4.8.4节——OpenStack与SDN的集成
  54. 第四章云网络4.8.5节——OpenDayLight
  55. 第四章云网络4.8.6节——Dragonflow
  56.  第四章云网络4.9.1节——网络卸载加速技术综述

  57. 第四章云网络4.9.2节——传统网络卸载技术

  58. 第四章云网络4.9.3.1节——DPDK技术综述

  59. 第四章云网络4.9.3.2节——DPDK原理详解

  60. 第四章云网络4.9.4.1节——智能网卡SmartNIC方案综述

  61. 第四章云网络4.9.4.2节——智能网卡实现

  62. 第六章容器6.1.1节——容器综述

  63. 第六章容器6.1.2节——容器安装部署

  64. 第六章容器6.1.3节——Docker常用命令

  65. 第六章容器6.1.4节——Docker核心技术LXC

  66. 第六章容器6.1.5节——Docker核心技术Namespace

  67. 第六章容器6.1.6节—— Docker核心技术Chroot

  68. 第六章容器6.1.7.1节——Docker核心技术cgroups综述

  69. 第六章容器6.1.7.2节——cgroups原理剖析

  70. 第六章容器6.1.7.3节——cgroups数据结构剖析

  71. 第六章容器6.1.7.4节——cgroups使用

  72. 第六章容器6.1.8节——Docker核心技术UnionFS

  73. 第六章容器6.1.9节——Docker镜像技术剖析

  74. 第六章容器6.1.10节——DockerFile解析

  75. 第六章容器6.1.11节——docker-compose容器编排

  76. 第六章容器6.1.12节——Docker网络模型设计

  77. 第六章容器6.2.1节——Kubernetes概述

  78. 第六章容器6.2.2节——K8S架构剖析

  79. 第六章容器6.3.1节——K8S核心组件总述

  80. 第六章容器6.3.2节——API Server组件

  81. 第六章容器6.3.3节——Kube-Scheduler使用篇

  82. 第六章容器6.3.4节——etcd组件

  83. 第六章容器6.3.5节——Controller Manager概述

  84. 第六章容器6.3.6节——kubelet组件

  85. 第六章容器6.3.7节——命令行工具kubectl

  86. 第六章容器6.3.8节——kube-proxy

  87. 第六章容器6.4.1节——K8S资源对象总览

  88. 第六章容器6.4.2.1节——pod详解

  89. 第六章容器6.4.2.2节——Pod使用(上)

  90. 第六章容器6.4.2.3节——Pod使用(下)

  91. 第六章容器6.4.3节——ReplicationController

  92. 第六章容器6.4.4节——ReplicaSet组件

  93. 第六章容器基础6.4.5.1节——Deployment概述

  94. 第六章容器基础6.4.5.2节——Deployment配置详细说明

  95. 第六章容器基础6.4.5.3节——Deployment实现原理解析

  96. 第六章容器基础6.4.6节——Daemonset

  97. 第六章容器基础6.4.7节——Job

  98. 第六章容器基础6.4.8节——CronJob

1 Dockfile详解

1.1 什么是Dockerfile

        首先通过一张图来了解 Docker 镜像、容器和 Dockerfile 三者之间的关系。

【重识云原生】第六章容器6.1.10节——DockerFile解析_第1张图片

        通过上图可以看出使用 Dockerfile 定义镜像,运行镜像启动容器。

        Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

        镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。完整镜像的结构图:【重识云原生】第六章容器6.1.10节——DockerFile解析_第2张图片

        Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

示例:

docker build -f /path/Dockerfile

        由Image Builder顺序执行各指令,从而完成Image构建:

【重识云原生】第六章容器6.1.10节——DockerFile解析_第3张图片

1.2 Docker commit 那么方便,为什么要用 DockerFile ?

        此前我构建镜像都是使用 Docker commit,简单又明确。但是一段时间后我就犯迷糊了,commit 出来的镜像都是什么啊,那个描述信息真的会有人很详细的去写?镜像多了自己都记不住了。而且,commit 到底打包了些什么东西啊?我后来突然意识到。是像虚拟机快照那样吗?会把当时的容器状态全都打包进去吗?还是说只是单纯的打包一下当时的文件?

        Docker commit 一个很不方便的地方就在于,难以回顾它是怎么来的,比方说我这里使用 commit 构建了一个 CentOS + vim 镜像,查看它的构建历史发现:

[root@centos7 ~]# docker history centos-vim:v1.0
IMAGE               CREATED             CREATED BY                                      SIZE
8df43092cfd1        6 minutes ago       /bin/bash                                      140MB
67591570dd29        4 years ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
           4 years ago         /bin/sh -c #(nop)  LABEL name=CentOS Base Im…   0B
           4 years ago         /bin/sh -c #(nop) ADD file:940c77b6724c00d42…  192MB
           4 years ago         /bin/sh -c #(nop)  MAINTAINER https://github…   0B

Docker commit 的弊端:

  • 如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
  • 使用 Docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体在操作的。虽然 docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的后期维护工作是非常痛苦的。
  • 镜像所使用的分层存储,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 Docker commit 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。

用Docker build 方式镜像生成新的镜像

        Docker build 的方式生成新镜像的前提条件是有一个旧的基础镜像,在此基础上通过 Docker build 命令执行dockerfile 文件从而生成一个新的镜像,不同于Docker commit,是镜像–> 镜像的转化。当然,是否转化正确是需要将镜像 Docker run起来。

Dockerfile的优点:

  • 能够自由灵活的与宿主机联系,比如,某些配置文件在宿主机验证并使用过后很好用,那么,可以将文件copy到镜像中,(这个动作是写在dockerfile里),add 远程主机的配置文件到镜像中,定义onbuild动作等等各种灵活的功能。docker commit不能做到这些事情,因为是在一个封闭的在运行中的容器中,无法做复制拷贝宿主机文件的事情。
  • dockerfile本身就是一个比较详细的构建文档,有这个文档就可以清楚的知道新构建的镜像经历了怎样的变化。没有黑箱操作的困扰了,后期的维护更为方便了。
  • 后期可扩展性强,一个文件就可以在哪都可以运行镜像了。(前提有网,有安装docker环境)

Dockerfile的缺点:

  • 编写不容易,因为需要对脚本这些比较了解,有Linux基础的人才可以编写出好用的dockerfile,有一定入门门槛。

1.3 docker build工作原理简述

        首先需要有一个制作镜像的目录,该目录下有个文件,名称必须为Dockerfile,Dockerfile有指定的格式,#号开头为注释,指令默认用大写字母来表示,以区分指令和参数,docker build读取Dockerfile是按顺序依次Dockerfile里的配置,且第一条非注释指令必须是FROM 开头,表示基于哪个基础镜像来构建新镜像。可以根据已存在的任意镜像来制作新镜像。

.dcokerignore:把文件路径写入到.dockerignore,对应的路径将不会被打包到新镜像

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

docker build [选项] <上下文路径/URL/->

        docker build 后面的.表示当前目录,也是指定上下文的路径上下文,例如:

docker build -f /path/to/a/Dockerfile

        Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API ,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

        当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

        这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

        那么为什么会有人误以为.·是指定 Dockerfile 所在目录呢?这是因为在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile。这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f …/Dockerfile 参数指定某个文件作为 Dockerfile。当然,一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

【重识云原生】第六章容器6.1.10节——DockerFile解析_第4张图片

1.4 Dockerfile的文件格式

        Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。

        用一张图来描述一下:

【重识云原生】第六章容器6.1.10节——DockerFile解析_第5张图片

        两种类型的行

  • 以# 开头的注释行
  • 由专用“指令(Instruction)”开头的指令行

        Dockerfile文件格式实例如下:

## Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user [email protected]
# 3、镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# 4、容器启动执行指令
CMD /usr/sbin/nginx

1.5 构建镜像

        docker build 命令会根据 Dockerfile 文件及上下文构建新 Docker 镜像。构建上下文是指 Dockerfile 所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。

        将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:

docker build . 
Sending build context to Docker daemon 6.51 MB 
...

        说明:构建会在 Docker 后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。

        在构建上下文中使用的 Dockerfile 文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下不需要的文件和目录。

        在 Docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找.dockerignore文件,根据.dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。

        Dockerfile 一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:

docker build -f /path/to/a/Dockerfile .

        构建时,还可以通过-t参数指定构建成镜像的仓库、标签。

1.6 镜像标签

docker build -t nginx/v3 .

        如果存在多个仓库下,或使用多个镜像标签,就可以使用多个-t参数:

docker build -t nginx/v3:1.0.2 -t nginx/v3:latest .

        在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,有语法错误时会返回:

docker build -t nginx/v3 . 
Sending build context to Docker daemon 2.048 kB 
Error response from daemon: Unknown instruction: RUNCMD

1.6 缓存

1.6.1 缓存机制

        Docker 守护进程会一条一条的执行 Dockerfile 中的指令,而且会在每一步提交并生成一个新镜像,最后会输出最终镜像的ID。生成完成后,Docker 守护进程会自动清理你发送的上下文。 Dockerfile文件中的每条指令会被独立执行,并会创建一个新镜像,RUN cd /tmp等命令不会对下条指令产生影响。 Docker 会重用已生成的中间镜像,以加速docker build的构建速度。以下是一个使用了缓存镜像的执行过程:

$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1/4 : FROM alpine:3.2
---> 31f630c65071
Step 2/4 : MAINTAINER [email protected]
---> Using cache
---> 2a1c91448f5f
Step 3/4 : RUN apk update &&      apk add socat &&        rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 4/4 : CMD env | grep _TCP= | (sed 's/.PORT([0-9])_TCP=tcp://(.):(.)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 &/' && echo wait) | sh
---> Using cache
---> 7ea8aef582cc
Successfully built 7ea8aef582cc

        构建缓存仅会使用本地父生成链上的镜像,如果不想使用本地缓存的镜像,也可以通过--cache-from指定缓存。指定后将不再使用本地生成的镜像链,而是从镜像仓库中下载。

1.6.2 寻找缓存的逻辑

        Docker 寻找缓存的逻辑其实就是树型结构根据 Dockerfile 指令遍历子节点的过程。下图可以说明这个逻辑。

【重识云原生】第六章容器6.1.10节——DockerFile解析_第6张图片

         大部分指令可以根据上述逻辑去寻找缓存,除了 ADD 和 COPY 。这两个指令会复制文件内容到镜像内,除了指令相同以外,Docker 还会检查每个文件内容校验(不包括最后修改时间和最后访问时间),如果校验不一致,则不会使用缓存。

        除了这两个命令,Docker 并不会去检查容器内的文件内容,比如 RUN apt-get -y update,每次执行时文件可能都不一样,但是 Docker 认为命令一致,会继续使用缓存。这样一来,以后构建时都不会再重新运行apt-get -y update。

        如果 Docker 没有找到当前指令的缓存,则会构建一个新的镜像,并且之后的所有指令都不会再去寻找缓存。

1.7 上下文路径

        上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

        解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。

        如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。

注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

2 Dockerfile常用指令

官方build参考

【重识云原生】第六章容器6.1.10节——DockerFile解析_第7张图片

【重识云原生】第六章容器6.1.10节——DockerFile解析_第8张图片

2.1 FROM

        指定基础镜像,必须为第一个命令,格式:   

FROM    

FROM :   

FROM @

示例:  

FROM mysql:5.6

注: tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像

2.2 MAINTAINER(新版即将废弃)

维护者信息

格式:

MAINTAINER 

示例:

MAINTAINER bertwu

MAINTAINER [email protected]

MAINTAINER bertwu [email protected]

2.3 RUN

        构建镜像时执行的命令.RUN用于在构建镜像时执行命令,其有以下两种命令执行方式:

  • shell执行

格式:  

RUN 
  • exec执行

格式:

RUN ["executable", "param1", "param2"]

示例:

RUN ["executable", "param1", "param2"]

RUN apk update

RUN ["/etc/execfile", "arg1", "arg1"]

注:RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像, 可以在构建时指定--no-cache参数,如:docker build --no-cache

2.4 ADD

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

格式:

ADD ... 

ADD ["",... ""]

用于支持包含空格的路径

示例:

ADD hom* /mydir/              # 添加所有以"hom"开头的文件

ADD hom?.txt /mydir/        # ? 替代一个单字符,例如:"home.txt"

ADD test relativeDir/         # 添加 "test" 到 WORKDIR/relativeDir/

ADD test /absoluteDir/     # 添加 "test" 到 /absoluteDir/

2.5 COPY

        功能类似ADD,但是是不会自动解压文件,也不能访问网络资源。

2.6 CMD

        构建镜像后调用,也就是在容器启动时才进行调用。

格式:

CMD ["executable","param1","param2"] (执行可执行文件,优先)

CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)

CMD command param1 param2 (执行shell内部命令)

示例:

CMD echo "This is a test." | wc -l

CMD ["/usr/bin/wc","--help"]

注:CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

2.7 ENTRYPOINT

        配置容器,使其可执行化。配合CMD可省去"application",只使用参数。

格式:

ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)

ENTRYPOINT command param1 param2 (shell内部命令)

示例:

FROM ubuntu

ENTRYPOINT ["ls", "/usr/local"]

CMD ["/usr/local/tomcat"]

        之后,docker run 传递的参数,都会先覆盖cmd,然后由cmd 传递给entrypoint ,做到灵活应用。

注:ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT, 而docker run命令中指定的任何参数,都会被当做参数再次传递给CMD。 Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置, 而只执行最后的ENTRYPOINT指令。 通常情况下,ENTRYPOINT 与CMD一起使用,ENTRYPOINT 写默认命令,当需要参数时候 使用CMD传参。

2.8 LABEL

        用于为镜像添加元数据。

格式:

LABEL = = = ...

示例:

LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"

注:使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。

2.9 ENV

        设置环境变量。

格式:

ENV #之后的所有内容均会被视为其的组成部分,因此,一次只能设置一个变量

ENV = ... #可以设置多个变量,每个变量为一个"="的键值对,如果中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行

示例:

ENV myName John Doe ENV myDog Rex The Dog ENV myCat=fluffy

2.10 EXPOSE

        指定于外界交互的端口。

格式:

EXPOSE [...]

示例:

EXPOSE 80 443 EXPOSE 8080 EXPOSE 11211/tcp 11211/udp

注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口。如果没有暴露端口,后期也可以通过-p 8080:80方式映射端口,但是不能通过-P形式映射。

2.11 VOLUME

        用于指定持久化目录(指定此目录可以被挂载出去)。

格式:

VOLUME ["/path/to/dir"]

示例:

VOLUME ["/data"] VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"

注:一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:

  1. 卷可以容器间共享和重用
  2. 容器并不一定要和其它容器共享卷
  3. 修改卷后会立即生效
  4. 对卷的修改不会对镜像产生影响
  5. 卷会一直存在,直到没有任何容器在使用它

2.12 WORKDIR

        工作目录,类似于cd命令。

格式:

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参数覆盖构建时所设置的工作目录。

2.13 USER

        指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户。

格式:  

USER user  

USER user:group  

USER uid  

USER uid:gid  

USER user:gid  

USER uid:group

示例:   

USER www

注:使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。

2.14 ARG

        用于指定传递给构建运行时的变量(给dockerfile传参),相当于构建镜像时可以在外部为里面传参。

格式:

ARG [=]

示例:

ARG site

ARG build_user=www

From centos:7

ARG parameter

VOLUME /usr/share/nginx

RUN yum -y install $parameter

EXPOSE 80 443

CMD nginx -g "daemon off;" # 可以这如下这样灵活传参 docker build --build-arg=parameter=net-tools -t nginx:01 .

2.15 ONBUILD

        用于设置镜像触发器。

格式: 

ONBUILD [INSTRUCTION]

示例:

ONBUILD ADD . /app/src   

ONBUILD RUN /usr/local/bin/python-build --dir /app/src

注:ONNBUID后面跟指令,当当前的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发。

3 制作镜像

  • 如果有多个RUN,自上而下依次运行,每次运行都会形成新的层,建议&& 放入一行运行
  • 如果有多个CMD,只有最后一个运行
  • 如果有多个Entrypoint,只有最后一个运行
  • 如果CMD和entrypoint共存,只有entrypoint运行,且最后的CMD会当做entrypoint的参数

        镜像制作分为两个阶段:

  1. docker build阶段 基于dockerfile制作镜像 (RUN,用于此阶段的运行命令)
  2. docker run阶段 基于镜像运行容器 (CMD,基于image run容器时候,需要运行的命令)
  3. docker build 基于第一阶段的镜像被别人from制作新镜像 (entrypoint 或onbuild 基于镜像重新构建新镜像时候在此阶段运行的命令)

3.1 源码编译制作nginx镜像

# This my first nginx Dockerfile
# Version 1.0
# Base images 基础镜像
FROM centos
# MAINTAINER 维护者信息
MAINTAINER bertwu
# ENV 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH
# ADD  文件放在当前目录下,拷过去会自动解压
ADD nginx-1.8.0.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/
# RUN 执行以下命令
RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm
RUN yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all
RUN useradd -s /sbin/nologin -M www
# WORKDIR 相当于cd
WORKDIR /usr/local/nginx-1.8.0
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install
RUN echo "daemon off;" >> /etc/nginx.conf
# EXPOSE 映射端口
EXPOSE 80
# CMD 运行以下命令
CMD ["nginx"]

3.2 制作简单镜像

root@ubuntu:~# mkdir myapp
root@ubuntu:~/myapp# vim Dockerfile # 编写Dockerfile
FROM alpine:3.15
LABEL Maintainer="bertwu [email protected]"
ADD hosts /etc/hosts
root@ubuntu:~/myapp# vim hosts # 编写文件
root@ubuntu:~/myapp# cat hosts
127.0.0.1 localhost
127.0.0.1 localhost.localdomain
172.100.100.100 xxx.com
root@ubuntu:~/myapp# docker image build .  # 构建镜像
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM alpine:3.15
---> c059bfaa849c
Step 2/3 : LABEL Maintainer="bertwu [email protected]"
---> Running in 63324216f4ec
Removing intermediate container 63324216f4ec
---> bb69e6b659a2
Step 3/3 : ADD hosts /etc/hosts
---> 0d6e00e31ce6
Successfully built 0d6e00e31ce6
root@ubuntu:~/myapp# docker image tag 0d6e00e31ce6 myapp:1.0 # 添加标签
root@ubuntu:~/myapp# docker image ls # 查看镜像
REPOSITORY          TAG             IMAGE ID       CREATED              SIZE
myapp               1.0             0d6e00e31ce6   About a minute ago   5.59MB
root@ubuntu:~/myapp# docker image inspect myapp:1.0 | grep -i maint
"Maintainer": "bertwu [email protected]"
"Maintainer": "bertwu [email protected]"

3.3 自作1.1版本

        先编辑apk下载文件。

root@ubuntu:~/myapp# vim repositories
https://mirrors.aliyun.com/alpine/v3.15/main
https://mirrors.aliyun.com/alpine/v3.15/community
root@ubuntu:~/myapp# cat Dockerfile
FROM alpine:3.15
LABEL Maintainer="bertwu [email protected]"
ADD repositories /etc/apk/repositories # 添加自己指定的repositories
RUN apk update && 
apk add nginx bash # 安装nginx与bash
root@ubuntu:~/myapp# docker run --name myapp -it --rm myapp:1.1 /bin/bash # 制作镜像
bash-5.1# which nginx
/usr/sbin/nginx
bash-5.1#
bash-5.1# nginx -v
nginx version: nginx/1.20.2
bash-5.1#
bash-5.1# cat /etc/apk/repositories
https://mirrors.aliyun.com/alpine/v3.15/main
https://mirrors.aliyun.com/alpine/v3.15/community

3.4 构建centos镜像

        下面通过编写Dockerfile文件来制作Centos镜像,并在官方镜像的基础上添加vim和net-tools工具。首先在/home/dockfile 目录下新建文件Dockerfile。然后使用上述指令编写该文件。

# Dockerfile

[root@localhost dockerfile]# cat Dockerfile

FROM centos:7

MAINTAINER bertwu [email protected]

ENV MYPATH /usr/local

WORKDIR $MYPATH

RUN yum -y install vim net-tools

EXPOSE 80

CMD /bin/bash

        逐行解释该Dockerfile文件的指令:

  • FROM centos:7 该image文件继承官方的centos7
  • ENV MYPATH /usr/local:设置环境变量MYPATH
  • WORKDIR $MYPATH:直接使用上面设置的环境变量,指定/usr/local为工作目录
  • RUN yum -y install vim && net-tools:在/usr/local目录下,运行yum -y install vim和yum -y install net-tools命令安装工具,注意安装后的所有依赖和工具都会打包到image文件中
  • EXPOSE 80:将容器80端口暴露出来,允许外部连接这个端口
  • CMD:指定容器启动的时候运行命令

        下面执行build命令生成image文件,如果执行成功,可以通过docker images来查看新生成的镜像文件。

[root@localhost dockerfile]# docker build -t mycentos:1.0 .
[root@localhost dockerfile]# docker images
REPOSITORY    TAG             IMAGE ID       CREATED              SIZE
mycentos      1.0             e0316e2ed3a5   About a minute ago   409MB

        可以使用 docker history 镜像id 查看镜像构建过程。

[root@localhost dockerfile]# docker history  e0316e2ed3a5
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
e0316e2ed3a5   2 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B
79738577ded0   2 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B
f10acdc62daf   2 minutes ago   /bin/sh -c yum -y install vim   net-tools       205MB
40b0252c02c7   3 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B
d38940eb3b75   3 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B
b23dc50b92b4   3 minutes ago   /bin/sh -c #(nop)  MAINTAINER bertwu <125839…   0B
eeb6ee3f44bd   2 months ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
      2 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
      2 months ago    /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4…   204MB

        进入容器,看看是否能够执行ifconfig 及vim命令:

root@localhost dockerfile]# docker run -it mycentos:1.0
[root@3143cb46b8c4 local]# ifconfig
eth0: flags=4163  mtu 1500
inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
RX packets 7  bytes 586 (586.0 B)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 0  bytes 0 (0.0 B)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
lo: flags=73  mtu 65536
inet 127.0.0.1  netmask 255.0.0.0
loop  txqueuelen 1000  (Local Loopback)
RX packets 0  bytes 0 (0.0 B)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 0  bytes 0 (0.0 B)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

3.5 构建springboot应用

cat Dockerfile
FROM openjdk:8-jre # jar包基于jdk ,war包基于tomcat
WORKDIR /app
ADD demo-0.0.1-SNAPSHOT.jar app.jar # 将上下文中 jar包复制到 /app目录下,并且重命名为app.jar
EXPOSE 8081 # 暴露端口
ENTRYPOINT[ "java" , "-jar" ] # 启动应用固定命令
CMD["app.jar"] # 动态传递jar包名

参考链接

Docker容器 - DockerFile详解_不会调制解调的猫的博客-CSDN博客

Docker 进阶之 Dockerfile 详解

Docker进阶四-Dockerfile详解03_Coder-michael的博客-CSDN博客

Docker学习6 --- Dockerfile详解_lufei0920的博客-CSDN博客_dockerfile 获取当前路径

Dockerfile 详解_万wu皆可爱的博客-CSDN博客_dockerfile

dockerfile 详解 - 属于我的梦,明明还在 - 博客园

你可能感兴趣的:(云原生-IaaS专栏,docker,容器,云原生,Dockerfile,K8S)