容器基础镜像的编写及最佳实践_第1页
容器基础镜像的编写及最佳实践_第2页
容器基础镜像的编写及最佳实践_第3页
容器基础镜像的编写及最佳实践_第4页
容器基础镜像的编写及最佳实践_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

容器始于镜像,在容器作为云原生架构上的工作负载发挥其应用价值之前,它以镜像的形式存在并流转。相比于攻击生命周期较短的容器,聪明的攻击者往往会选择投毒镜像,这样不管是基于该镜像运行容器抑或是生成新的镜像,全部都将含有恶意成分。不够了解镜像,就无法建立对镜像的管理体系。镜像有哪些种类?镜像里有哪些内容?镜像该由谁来构建又如何构建?通过本内容学习您将清楚了解到容器镜像的基本概念,容器镜像在容器云平台系统组成中的位置,镜像主要特点以及容器镜像和虚拟机镜像的区别。基于以上基本概念了解的前提下,本文延伸讲解容器镜像的分类,各类镜像如何在企业业务中使用,维护。其中最重要部分是给大家讲解各类镜像的构建方式以及镜像构建的最佳实践。最后简要总结如何通过自动化的方式进行镜像创建管理。具体实践中每个容器云平台的实现方式不同,大家可以在工作中根据业务需要进一步丰富。镜像是容器之源,在文末介绍了从镜像到容器运行起来的过程,结合操作系统核心技术解释了如何通过其中涉及的各类技术实现容器的虚拟化。总之,通过本内容的学习可以零基础了解镜像的基本概念,同时逐步掌握镜像的构建原理以及镜像构建方面的企业最佳实践,为大家在以后工作中创建镜像奠定技术基础。1基本概念1.1Docker系统组成整体Docker的系统包括以下主要组成部分,包括Dockerclient、Dockerdaemon、Dockerregistry、Docker镜像、Docker容器,他们之间的关系如下图:Docker架构主要分为客户端和服务端,客户端负责发起请求,服务端负责接受,解析和处理请求,图示中的DockerClient即为Docker客户端的一个命令行工具,用户通过DockerClient与DockerDaemon进行通信并返回结果给用户。也可以使用其他工具通过DockerApi与DockerDaemon通信。拿一个简单的拉取镜像并运行容器的过程举例便于大家更好的理解容器系统架构。从上图中我们可以看到,当daemon接收到由client发起的dockerrun等运行容器指令时,docker系统先会由registry(本地镜像仓库)中拉取镜像,图示中拉取了ubuntu:14.04镜像。当由ubuntu:14.04镜像启动容器时,ubuntu:14.04镜像的镜像层layer内容将作为容器的rootfs;而ubuntu:14.04镜像的yaml文件(json格式),会由Dockerdaemon解析成为该docker运行起来后的各项配置,并提取出其中的容器执行入口CMD信息,以及容器进程的环境变量ENV信息,最终初始化容器进程。当然,容器进程的执行入口来源于镜像提供的rootfs。在传统的Linux操作系统内核启动时,首先挂载一个只读的rootfs,当系统检测其完整性之后,再将其切换为读写模式。而在Docker架构中,当Dockerdaemon为Docker容器挂载rootfs时,沿用了Linux内核启动时的做法,即将rootfs设为只读模式。但docker运行的下一步产生了与传统linux的差别,这里要特别注意,在挂载完毕之后,docker系统利用联合挂载(unionmount)技术在已有的只读rootfs上再挂载一个读写层。可读写的层处于Docker容器文件系统的最顶层,其下可能联合挂载了多个只读的层,只有在Docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并如同photoshop中的多层覆盖一样,由最顶层的新的文件内容覆盖掉只读层中的旧版本文件(旧版本文件依旧存在只是被盖住了)。1.2Docker镜像Docker镜像是一个只读的Docker容器模板,含有启动Docker容器所需的文件系统结构及其内容,是启动一个Docker容器的基础,但是需要重点注意镜像中不包括系统内核,在下文也会讲述这是容器与虚拟机最关键的区别之一。Docker镜像的文件内容(rootfs)以及一些运行Docker容器的配置文件组成了Docker容器的静态运行环境。可以这么理解,Docker镜像是Docker容器的静态视角,Docker容器是Docker镜像的运行状态。镜像是容器之源,如果大家熟悉java,镜像和容器的关系类似class文件和java对象的关系。1.3Docker镜像特点-分层Docker镜像是采用分层的方式构建的,每个镜像都由一系列的"镜像层"组成。分层结构是Docker镜像如此轻量的重要原因。当需要修改容器镜像内的某个文件时,只对处于最上方的读写层进行变动,不覆写下层已有文件系统的内容,已有文件在只读层中的原始版本仍然存在,但会被读写层中的新版本所隐藏。当使用dockercommit提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。分层达到了在不同的容器镜像之间共享镜像层的效果。-写时复制(不变性)Docker镜像使用了写时复制(copy-on-write)的策略。如果要在多个容器之间共享镜像,每个容器在启动的时候并不需要单独复制一份镜像文件,而是将所有镜像层以只读的方式挂载到一个挂载点,再在上面覆盖一个可读写的容器层。在未更改文件内容时,所有容器等于共享同一份数据,只有在Docker容器运行过程中文件系统发生变化时,才会把变化的文件内容写到可读写层,并隐藏只读层中的老版本文件。写时复制配合分层机制减少了镜像对磁盘空间的占用和容器启动时间。-内容寻址在Docker1.10版本后,Docker镜像经历了较大的改动,其中最重要的特性便是引入了内容寻址存储(content-addressablestorage)的机制,根据文件的内容来索引镜像和镜像层。与之前版本对每个镜像层随机生成一个UUID不同,新模型对镜像层的内容计算校验和,生成一个内容哈希值,并以此哈希值代替之前的UUID作为镜像层的唯一标识。该机制主要提高了镜像的安全性,并在pull、push、load和save操作后检测数据的完整性。另外,基于内容哈希来索引镜像层,在一定程度上减少了ID的冲突并且增强了镜像层的共享。对于来自不同构建的镜像层,只要拥有相同的内容哈希,也能被不同的镜像共享。-联合挂载通俗地讲,联合挂载技术可以在一个挂载点同时挂载多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统将会包含整合之后的各层的文件和目录。实现这种联合挂载技术的文件系统通常被称为联合文件系统(unionfilesystem)。docker依靠此技术实现镜像本身的只读层与生成容器以后产生的读写层的整合。1.4Docker镜像和虚拟机镜像区别容器是一个应用层抽象,用于将代码和依赖资源打包在一起。多个容器可以在同一台机器上运行,共享底层操作系统内核,但各自作为独立的进程在用户空间中运行。因为剥离了相对沉重的操作系统,所以与虚拟机相比,容器占用的空间较少(容器镜像大小通常只有几十兆),瞬间就能完成启动。虚拟机(VM)是一个物理硬件层抽象,用于将一台服务器变成多台服务器。管理程序允许多个VM在一台机器上运行。每个VM都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此占用大量空间。而且VM启动也十分缓慢。需要理解的是,容器和虚拟化并不是一项冲突的技术,现实环境中在资源允许的情况下,可先通过虚拟化技术获得多台VM,再在VM上运行容器。1.5容器和虚拟机区别2镜像分类根据实际容器镜像管理的经验,结合镜像的来源,镜像的用途以及镜像制作参与人员的不同将镜像分为4类:-官方操作系统镜像此镜像是操作系统厂商给出的OS的最基础版本,比如ubuntu,suse12sp3,centos,redhat,alpine,windowsNanoServer、WindowsServerCore和windowsinsider等镜像,其中开源的镜像比如apline镜像可以通过Dockerhub官方网站下载,如使用suse12sp3镜像需要请求SUSE官方支持获得。为了便于维护,此类型镜像的版本需要尽量少,并且尽量简单。-公司操作系统镜像此镜像是在原厂镜像的基础上安装了经过公司内部os运维人员审核过的工具包形成的镜像,此镜像的构建基于官方操作系统镜像。其中常见的工具包括必要的诊断、编辑工具等构建的符合行内规范的操作系统镜像。此部分镜像不区分应用运行环境,由容器云平台运维人员进行维护。-公司基础镜像此镜像是在公司操作系统镜像基础上安装了符合公司版本规范的基础软件的镜像,比如安装了JDK,Tomcat,Apache,Nginx,Weblogic,Mysql等,此部分镜像不区分应用运行环境,由容器云平台运维人员进行运维。-公司应用镜像此镜像是用于公司业务运行的镜像,构建于公司操作系统镜像或者公司基础镜像之上,一般为开发人员和运维人员共同构建,并且构建完毕以后随带一篇多个环境(开放/测试/生产)的部署说明,参数配置文件说明。注意,不建议直接下载第三方甚至Dockerhub上的基础镜像直接使用,比如nginx,tomcat镜像,理由:1)安全可控的目的,防止有“毒”镜像。2)未融入公司的软件配置规范、未进行性能优化以及安全加固。3)未融入公司软件基线管理体系,未引入持续构建机制,定期更新以及漏洞修复。3镜像制作根据镜像不同分类,镜像的制作方式也不尽相同,其中对于第一类镜像,即官方操作系统镜像。一般直接从厂商获得,比如ubuntu,centos,suse,alpine等,并且从厂商获得的形式也不尽相同,比如rpm,minibootfs,镜像tar包。但是对于特殊发行软件,比如busybox,官方只提供了一个二进制文件,因此需要通过自己构建rootfs进行构建。第一类镜像的构建属于基础镜像构建范畴,一般情况有原厂制作,本着知其然也要知其所以然的目的,本文也准备特定章节进行阐述。对于第二、三、四类镜像需要遵循一定镜像制作规范,通过命令或者自动化工具进行持续构建,此部分是常规的镜像构建过程,也是本文容器镜像构建阐述的重点,正如上面容器镜像分类说明的那样,第二、第三类镜像一般由容器云平台建设者进行构建,并提供维护,第四类是应用开发和应用运维负责人进行构建并维护。综上所述,镜像构建主要分为两大类,基础镜像构建和公司专属镜像构建。其中公司专属镜像构建的镜像是上面所述的第二、三、四类镜像。3.1基础镜像构建3.1.1基本概念在进行基础镜像构建介绍之前,首先几个相关概念,这里需要说明的是此处的概念也适用于公司专属镜像构建。3.1.2rootfsRootFileSystem,简称为rootfs,中文翻译是根文件系统,是特定的操作系统的文件组织架构的一种实现的形式,具体表现为系统中存在哪些特定的文件夹,文件夹之间的关系。比如Linux中的rootfs是包括根文件目录“/”.系统相关的配置文件目录“/etc”,存放系统启动相关配置的“/etc/init”,存放系统相关的工具/sbin,存在用户的工具/usr/bin等等,以及用户的家目录“home”,root用户的家目录“/root”等诸如此类的文件夹和文件,对应的,对于Windows,从Windows95/98/XP/Win7,这些版本的windows系统,有很多系统相关的文件夹和文件。比如(假设系统安装在C盘)C:Windows用于存放,系统相关的文件C:Windowssystem32其中很多可执行文件(工具),都是系统相关的工具C:ProgramFiles默认的软件安装目录,比如办公软件MicrosoftOfficeC:Users(Win7以后出现,其他版本的windows也有类似目录)用于存储用户相关数据和配置。综上,诸如此类的操作系统对于文件系统都有相关的规定,规定规范了操作系统运行需要的必须文件,以及系统启动过程中文件的依赖关系。容器的基础镜像也应用了相同的概念,因此容器镜像,特别是基础镜像就是来源于一个rootfs系统。即最基础的容器镜像的初始输入是rootfs,这里需要说明的是rootfs的获得主要有以下几种:1、根据rootfs的规范自己创建和组织一个文件目录,2、从原厂获得rootfs,3、根据运行的系统中获得,4、使用原厂提供基础镜像制作命令获取。其中对于第一种,以busybox操作系统的rootfs制作举例:对于第二种,以alpine操作系统的rootfs制作举例:对于第三种,以centos系统制作举例:对于第四种,以opensuse、ubuntu、centos举例:这样一个完整的rootfs就诞生了。另外需要明确的是rootfs只是一个操作系统所包含的文件、配置和目录,并不包括操作系统的内核(bootfs包含了BootLoader和Kernel),在Linux操作系统中,这两部分是分开存放的,操作系统只有在开机启动时才会加载指定版本的内核。所以说,rootfs只包括了操作系统的“躯壳”,并没有包括操作系统的“灵魂”。3.1.3chrootchroot,即changerootdirectory(更改root目录)。在linux系统中,系统默认的目录结构都是以

/,即是以根(root)开始的。而在使用chroot之后,系统的目录结构将以指定的位置作为

/

位置。如下图所示:

在经过chroot之后,系统读取到的目录和文件将不再是旧系统根下的而是新根下(即被指定的新的位置)的目录结构和文件,它带来的好处大致有以下3个:1、增加了系统的安全性,限制了用户的权力;2、建立一个与原系统隔离的系统目录结构,方便用户的开发;3、切换系统的根目录位置,引导Linux系统启动以及急救系统等。为了更好的理解chroot发挥的作用,我们尝试指定一个特定的位置进行根目录切换。在经过chroot之后,系统读取到的系统相关目录将不再是旧系统根目录下的,而是切换后新根下的目录结构和文件,具体例子参照上节:rootfs的第一种制作样例。3.1.4DockerfileDockerfile是一个Docker镜像的描述文件,里面内容是容器构建引擎执行的操作步骤,像执行预案一样,顺序执行A、B、C、D…的步骤。Dockerfile内部包含了一条条的指令,每一条指令构建镜像的一层,因此每一条指令的内容,就是描述该层应当如何构建。Dockerfile规范如下:由上可知,Dockerfile结构大致分为四个部分:(1)基础镜像信息,指FROM部分(2)维护者信息,指MAINTAINER部分(3)镜像操作指令,包括USER、ENV、COPY、EXPOSE、WORKDIR和RUN等(4)容器启动时执行指令,指CMD部分,DockerFile规范中提供了ENTRYPOINT指令或CMD指令用于制定Docker默认的运行命令,二者功能类似,DockerFile定义中必有其一,但是二者有细微的差别,CMD指令可以被dockerrun命令中的执行命令参数覆盖,但是ENTRYPPOINT必须通过--entrypoint进行覆盖。Dockerfile每行支持一条指令,每条指令可带多个参数,支持使用以#号开头的注释。详细命令的介绍,参照Docker官方文档。3.1.5生成方式基础镜像构建目前主要有两种方式,第一种是基于tar包import构建,另外一种是Fromscratch构建,但是构建之前都需要准备好基础镜像的rootfs文件系统。通过tar包import步骤一:获得基础镜像的rootfs文件系统,以alpine的minirootfs为例步骤二、构建镜像步骤三、确认镜像步骤四、测试镜像通过dockerbuild即Fromscratch步骤一、编写Dockerfile文件步骤二、dockerbuild构建基础镜像步骤三、镜像测试以上即为主流的制作基础镜像的方法,但是一般情况下基础镜像是操作系统原厂制作,实际生产运维过程中自行维护的并不多,此处介绍的主要目的是给大家介绍清楚镜像从0到1的原理,解决From某某镜像的第一个镜像由来原理问题。3.2公司专属镜像构建公司专属镜像构建是基于操作系统基础镜像按照公司镜像管理规范进行镜像构建的过程,此过程主要包括以下几个工作,按照公司镜像制作管理规范设计公司镜像构建的技术构成和流程管理规定,其中技术构成包括公司专属镜像的技术规范,以及镜像的层次组织架构,此部分通过制定公司的Dockerfile管理规范和容器运行参数规范实现;流程管理规定包括制定公司操作系统镜像,公司基础镜像,公司应用镜像的生成,更新以及销毁的流程,此部分主要通过自动化实现。因此此部分主要的工作(删除)包括设计Dockerfile编写规范,Dokcer镜像版本管理规范(tag规范),Docker运行参数规范,以及自动化实现规范。其中自动化部分在自动构建章节介绍。3.2.1公司镜像库规范公司Docker镜像统一提交到公司统一的镜像仓库,并对镜像仓库进行授权管理,推荐使用的镜像如harbor,JFrogArtifactory,DTR等。3.2.2公司镜像Dockerfile规范1、禁止依赖(From)来源不明的基础镜像,所有镜像均基于公司内部标准基础软件镜像。建议利用第二类和第三类公司基础镜像作为公共的基础镜像,同时(删除)应用据自身的需求可以建立本应用系统的公共的基础镜像,用于优化镜像分发和管理。2、应用镜像基于公共的基础镜像制作,并且Docker镜像应该是自包含的,应该包括应用所需基础镜像,应用依赖和标准配置模板,不允许出现运行过程中安装软件的现象。3、不允许在镜像内保存应用源代码、Dockerfile、构建应用脚本等,防止信息泄露。不允许在镜像内保存安全凭证、密码,建议使用环境变量或者配置文件方式在dockerrun时候指定。4、Docker镜像内容应该是不可变的,不要将需要持久化的数据存放在容器内。建议将持久化数据保存在Volume数据卷上5、利用Dockerfile镜像制作时,尽量较少镜像层数,合并RUN、EXPOSE、ENV指令;优化指令顺序,将相对变化较少的指令及指令执行耗时较长的,放在靠前位置执行。6、Docker镜像应该是自描述的,在Dockerfile中利用a)EXPOSE显式定义暴露的端口b)VOLUME显式定义所有数据卷c)ENV显式初始化容器的环境变量设置d)利用LABEL提供镜像的元数据管理规范7、内部进程的监控可以通过init.d,Supervisor或其他进程监控软件来完成8、Dockerfile必须通过版本管理工具进行管理,比如gitlib系统,同时Dockefile版本(tag)与镜像版本(tag)一致。3.2.3公司镜像版本管理规范所有Docker镜像必须利用tag进行版本管理;引用Docker镜像必须指明versiontag;不允许利用缺省latesttag作为默认tag。Dockerfile必须利用源代码管理系统进行管理,建议每次代码提交后自动重新构建镜像,建议利用CI工具构建镜像,推荐利用代码版本号作为镜像tag一部分,便于代码回溯追踪。3.2.4公司镜像参数规范1、Docker镜像内容应该是不可变的,不要将需要持久化的数据存放在容器内。同时容器本身每次启动的行为应该是一致的,保证容器应该可随时进行停止、销毁或者更新替换2、镜像应该是可以配置的,可以采用如下方式;a)通过环境变量的方式或者env-file提供b)通过配置文件的方式:对于k8s环境,利用ConfigMap方式映射到容器中c)对于敏感信息如密码、证书应该通过文件方式传递,在k8s环境下,可以利用Secret的方式进行支持3.3公司镜像构建过程规范-镜像生成方式规范所有Docker镜像必须采用Dockerfile方式进行构建,应禁止手工修改运行中的容器并提交为镜像,否则无法保证镜像内容的可追溯性。镜像生成方式一:Dockerbuild支持且仅推荐此种方式从零开始来创建一个新的镜像。为此,我们需要创建一个Dockerfile文件,其中包含一组指令来告诉Docker如何构建我们的镜像。使用dockerbuild命令进行。镜像生成方式二、Dockercommit创建镜像不推荐镜像生成方式三、Dockerexport/import创建镜像不推荐-.dockerignore使用规范镜像构建系统中必须明确编写镜像忽略文件(.dockerignore)用于指定那些文件不能存在于构建的镜像中,.dockerignore文件用来定义忽略镜像构建过程中不需要的文件。此方式可以有效防止信息泄露。.dockerignore有两种使用方式:推荐采用包含法。常规的做法-排除法(黑名单)与.gitignore的常规做法一样,排除不需要的文件或文件夹最佳实践-包含法(白名单)实际构建过程中,上下文需要的文件或文件夹只占整个项目所有文件或文件夹中的一小部分,尤以java项目突出,构建时,上下文只需要一个jar或war包即可。所以我们建议:.dockerignore中配置只包含需要的文件或文件夹即可,更为简洁、方便。举例:3.3.3镜像多级构建规范在实际构建镜像的场景中,生成镜像的过程往往是首先源码的获取,然后编译和生成应用制品,最终才构建为应用镜像。但是这样做的劣势在于:不得不在容器中安装构建程序所必须的运行时环境;不得不在同一个容器中,获取程序的源码和构建所需的一些生态工具;构建出的镜像甚至包含了程序源码和一些不必要的文件,导致容器镜像尺寸偏大。在最新的容器版本(Docker17.05)以后提供了一种稍微优雅的解决方式,即多阶段构建(multi-stagebuild)方案。所谓多阶段构建,也即将构建过程分为多个阶段,在同一个Dockerfile中,通过不同的阶段来构建和生成所需要的应用文件,最终将这些应用文件添加到一个release的镜像中。这样做能完全规避上面所遇到的一系列问题。实现多阶段构建,主要依赖于新提供的关键字:from和as。下面举个例子:第一阶段:拷贝源文件到镜像中,通过maven进行编译生成用于生产环境需要的springboot的jar文件第二阶段:启动一个openjdk8的容器,运行第一阶段的jar文件多阶段构建的Dockerfile看起来像是把两个或者更多的Dockerfile合并在了一起,这也即多阶段的意思。as关键字用来为构建阶段赋予一个别名,这样,在另外一个构建阶段中,可以通过from关键字来引用和使用对应关键字阶段的构建输出,并打包到容器中。在多阶段构建完成之后,输出的镜像仅仅包含了最终输出的springboot工程的jar文件,没有其他的源码文件和第三方源码包,非常的干净和简洁。因为build-env阶段只是一个构建的中间过程而已,因此通过多阶段构建,既可以很方便地将多个彼此依赖的项目通过一个Dockerfile就可轻松构建出期望的容器镜像,并且不用担心镜像太大、源码泄露等风险。因此推荐在类似场景中使用多阶段构建技术。4自动构建对于简单的镜像构建可以使用手动实现,但是同时存在多个项目开发的大公司,并行开发的项目很多,并且每天都会面临新功能的提交,因此对于大规模的应用构建手动实现已经力不从心,通过CI工具进行自动持续构建是镜像制作的必经之路。实现自动构建的方式很多,下面以基于JenkinsCI工具基础简单实现一个镜像构建的过程。前提:已安装好Docker和Jenkins(2.138.1)。1、在Jenkins中创建项目输入项目名:选择构建一个流水线的软件项目2、级项目配置定义:PipelinescriptfromSCM脚本路径:Jenkinsfile3、编写Jenkinsfilepipeline{agentanytools{maven'maven3.6.0'jdk'jdk1.8'}environment{registryUrl="127.0.0.1:5000"artifactId=readMavenPom().getArtifactId().toLowerCase()image_tag=readMavenPom().getVersion()image_repository="${registryUrl}/${artifactId}"}stages{stage('prepare'){steps{echo"registryUrl:${registryUrl},image_repository:${image_repository},image_tag:${image_tag}"}}stage('Buildproject'){steps{sh'mvncleanpackage-DskipTests'}}stage('Build&PushImage'){steps{dir('target'){script{defapp=docker.build('${image_repository}:${image_tag}')app.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论