• 【重识云原生】第六章容器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 三者之间的关系。

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

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

            镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。完整镜像的结构图:

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

    示例:

    docker build -f /path/Dockerfile

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

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

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

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

    1. [root@centos7 ~]# docker history centos-vim:v1.0
    2. IMAGE CREATED CREATED BY SIZE
    3. 8df43092cfd1 6 minutes ago /bin/bash 140MB
    4. 67591570dd29 4 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
    5. 4 years ago /bin/sh -c #(nop) LABEL name=CentOS Base Im… 0B
    6. 4 years ago /bin/sh -c #(nop) ADD file:940c77b6724c00d42… 192MB
    7. 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,以及会将其置于镜像构建上下文目录中。

    1.4 Dockerfile的文件格式

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

            用一张图来描述一下:

            两种类型的行

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

            Dockerfile文件格式实例如下:

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

    1.5 构建镜像

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

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

    1. docker build .
    2. Sending build context to Docker daemon 6.51 MB
    3. ...

            说明:构建会在 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 进行语法检查,有语法错误时会返回:

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

    1.6 缓存

    1.6.1 缓存机制

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

    1. $ docker build -t svendowideit/ambassador .
    2. Sending build context to Docker daemon 15.36 kB
    3. Step 1/4 : FROM alpine:3.2
    4. ---> 31f630c65071
    5. Step 2/4 : MAINTAINER SvenDowideit@home.org.au
    6. ---> Using cache
    7. ---> 2a1c91448f5f
    8. Step 3/4 : RUN apk update && apk add socat && rm -r /var/cache/
    9. ---> Using cache
    10. ---> 21ed6e7fbb73
    11. 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
    12. ---> Using cache
    13. ---> 7ea8aef582cc
    14. Successfully built 7ea8aef582cc

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

    1.6.2 寻找缓存的逻辑

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

             大部分指令可以根据上述逻辑去寻找缓存,除了 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参考

    2.1 FROM

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

    1. FROM   
    2. FROM :   
    3. FROM @

    示例:  

    FROM mysql:5.6

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

    2.2 MAINTAINER(新版即将废弃)

    维护者信息

    格式:

    MAINTAINER 

    示例:

    1. MAINTAINER bertwu
    2. MAINTAINER xxx@163.com
    3. MAINTAINER bertwu xxx@163.com

    2.3 RUN

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

    • shell执行

    格式:  

    RUN <command>
    • exec执行

    格式:

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

    示例:

    1. RUN ["executable", "param1", "param2"]
    2. RUN apk update
    3. RUN ["/etc/execfile", "arg1", "arg1"]

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

    2.4 ADD

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

    格式:

    1. ADD ...
    2. ADD ["",... ""]

    用于支持包含空格的路径

    示例:

    1. ADD hom* /mydir/              # 添加所有以"hom"开头的文件
    2. ADD hom?.txt /mydir/        # ? 替代一个单字符,例如:"home.txt"
    3. ADD test relativeDir/         # 添加 "test" 到 WORKDIR/relativeDir/
    4. ADD test /absoluteDir/     # 添加 "test" 到 /absoluteDir/

    2.5 COPY

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

    2.6 CMD

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

    格式:

    1. CMD ["executable","param1","param2"] (执行可执行文件,优先)
    2. CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
    3. CMD command param1 param2 (执行shell内部命令)

    示例:

    1. CMD echo "This is a test." | wc -l
    2. CMD ["/usr/bin/wc","--help"]

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

    2.7 ENTRYPOINT

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

    格式:

    1. ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
    2. ENTRYPOINT command param1 param2 (shell内部命令)

    示例:

    1. FROM ubuntu
    2. ENTRYPOINT ["ls", "/usr/local"]
    3. 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

            设置环境变量。

    格式:

    1. ENV #之后的所有内容均会被视为其的组成部分,因此,一次只能设置一个变量
    2. 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

    示例:

    1. WORKDIR /a (这时工作目录为/a)
    2. WORKDIR b (这时工作目录为/a/b)
    3. WORKDIR c (这时工作目录为/a/b/c)

    注:通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。

    2.13 USER

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

    格式:  

    1. USER user  
    2. USER user:group  
    3. USER uid  
    4. USER uid:gid  
    5. USER user:gid  
    6. USER uid:group

    示例:   

    USER www

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

    2.14 ARG

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

    格式:

    ARG [=]

    示例:

    1. ARG site
    2. ARG build_user=www
    3. From centos:7
    4. ARG parameter
    5. VOLUME /usr/share/nginx
    6. RUN yum -y install $parameter
    7. EXPOSE 80 443
    8. CMD nginx -g "daemon off;" # 可以这如下这样灵活传参 docker build --build-arg=parameter=net-tools -t nginx:01 .

    2.15 ONBUILD

            用于设置镜像触发器。

    格式: 

    ONBUILD [INSTRUCTION]

    示例:

    1. ONBUILD ADD . /app/src   
    2. 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镜像

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

    3.2 制作简单镜像

    1. root@ubuntu:~# mkdir myapp
    2. root@ubuntu:~/myapp# vim Dockerfile # 编写Dockerfile
    3. FROM alpine:3.15
    4. LABEL Maintainer="bertwu bertwu6688@edu.com"
    5. ADD hosts /etc/hosts
    6. root@ubuntu:~/myapp# vim hosts # 编写文件
    7. root@ubuntu:~/myapp# cat hosts
    8. 127.0.0.1 localhost
    9. 127.0.0.1 localhost.localdomain
    10. 172.100.100.100 xxx.com
    11. root@ubuntu:~/myapp# docker image build . # 构建镜像
    12. Sending build context to Docker daemon 3.072kB
    13. Step 1/3 : FROM alpine:3.15
    14. ---> c059bfaa849c
    15. Step 2/3 : LABEL Maintainer="bertwu bertwu6688@edu.com"
    16. ---> Running in 63324216f4ec
    17. Removing intermediate container 63324216f4ec
    18. ---> bb69e6b659a2
    19. Step 3/3 : ADD hosts /etc/hosts
    20. ---> 0d6e00e31ce6
    21. Successfully built 0d6e00e31ce6
    22. root@ubuntu:~/myapp# docker image tag 0d6e00e31ce6 myapp:1.0 # 添加标签
    23. root@ubuntu:~/myapp# docker image ls # 查看镜像
    24. REPOSITORY TAG IMAGE ID CREATED SIZE
    25. myapp 1.0 0d6e00e31ce6 About a minute ago 5.59MB
    26. root@ubuntu:~/myapp# docker image inspect myapp:1.0 | grep -i maint
    27. "Maintainer": "bertwu bertwu6688@edu.com"
    28. "Maintainer": "bertwu bertwu6688@edu.com"

    3.3 自作1.1版本

            先编辑apk下载文件。

    1. root@ubuntu:~/myapp# vim repositories
    2. https://mirrors.aliyun.com/alpine/v3.15/main
    3. https://mirrors.aliyun.com/alpine/v3.15/community
    4. root@ubuntu:~/myapp# cat Dockerfile
    5. FROM alpine:3.15
    6. LABEL Maintainer="bertwu bertwu6688@edu.com"
    7. ADD repositories /etc/apk/repositories # 添加自己指定的repositories
    8. RUN apk update &&
    9. apk add nginx bash # 安装nginx与bash
    10. root@ubuntu:~/myapp# docker run --name myapp -it --rm myapp:1.1 /bin/bash # 制作镜像
    11. bash-5.1# which nginx
    12. /usr/sbin/nginx
    13. bash-5.1#
    14. bash-5.1# nginx -v
    15. nginx version: nginx/1.20.2
    16. bash-5.1#
    17. bash-5.1# cat /etc/apk/repositories
    18. https://mirrors.aliyun.com/alpine/v3.15/main
    19. https://mirrors.aliyun.com/alpine/v3.15/community

    3.4 构建centos镜像

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

    1. # Dockerfile
    2. [root@localhost dockerfile]# cat Dockerfile
    3. FROM centos:7
    4. MAINTAINER bertwu 1258398543@qq.com
    5. ENV MYPATH /usr/local
    6. WORKDIR $MYPATH
    7. RUN yum -y install vim net-tools
    8. EXPOSE 80
    9. 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来查看新生成的镜像文件。

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

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

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

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

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

    3.5 构建springboot应用

    1. cat Dockerfile
    2. FROM openjdk:8-jre # jar包基于jdk ,war包基于tomcat
    3. WORKDIR /app
    4. ADD demo-0.0.1-SNAPSHOT.jar app.jar # 将上下文中 jar包复制到 /app目录下,并且重命名为app.jar
    5. EXPOSE 8081 # 暴露端口
    6. ENTRYPOINT[ "java" , "-jar" ] # 启动应用固定命令
    7. 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 详解 - 属于我的梦,明明还在 - 博客园

  • 相关阅读:
    怎么将自己的Maven项目上传到Maven中央仓库/Maven阿里云云效仓库
    反射和注解
    【C++】单例模式
    Python教程:文件和读写的详细教程
    Go 复合类型之切片类型介绍
    CISSP学习笔记:事件预防和响应
    对象池技术(unity3d)
    12.区块链系列之比特币NFT
    Java数据结构第三课 —— 泛型(2)
    什么是VHDL?一文带你了解VHDL语言
  • 原文地址:https://blog.csdn.net/junbaozi/article/details/126732653