当前位置: 首页 > Linux

技术选型:Docker容器引擎

时间:2023-04-06 21:58:19 Linux

题外话  最近学习了Docker和Kubernetes。前两天分享了一个技术,回去听了一下我演讲的录音。远远不够。我没有提前准备逻辑严密的演讲稿。讲的时候出现卡顿、漏掉技术点、逻辑矛盾的问题。为了解决这个问题,打算以后在做技术分享之前,先根据PPT的内容写一篇博客,理顺表达的逻辑。另外,我觉得每一次技术分享用的PPT要尽可能的好,因为你不知道以后会不会用它来讲几次。本文以PPT+讲座的形式撰写。把这次技术分享记录下来就对了。欢迎大家拍砖。一、Docker的背景  在普通的研发和项目场景中,普遍存在以下几种情况:个人开发环境为了做大数据相关的项目,需要安装CDH集群。常见的方式是搭建3个CDH版本对应的虚拟机,安装好CDH集群后,考虑到以后很可能会用到干净的CDH集群,为了避免以后重复安装环境,备份一个通常整个CDH集群的制作,使得电脑里面有6个虚拟机镜像。另外后面学习其他技术的时候,比如学习Ambari大数据集群,为了不破坏现有的虚拟机环境,必须重新构建三个虚拟机,本地磁盘很快就会被大量虚拟机占用图片。满的。公司内部开发环境公司经常会用小团队做项目。一般是运维部门从自己管理的服务器资源中分配虚拟机,供团队内部开发测试使用。比如做一个机器学习相关的项目:小明在运维部分配的虚拟机上搭建了一个Ambari集群,用来跑大数据相关的业务。小刚用python3写了一个机器学习算法,放到虚拟机上运行,??发现虚拟机是python2,算法不兼容,于是升级了虚拟机里的python版本,算法跑了顺利,但是Ambari使用的python的一些函数可能会报错。小李开发应用放到虚拟机里我在网上启动了tomcat,发现虚拟机是OpenJDK,导致tomcat无法启动,于是又安装了一个JDK。此时Ambari中的Java代码可能会报错。小赵想用服务器资源做性能测试,结果发现虚拟机性能严重下降,最后还是要直接找一台物理机跑测试,破坏了物理机原有的环境。完成项目后,这些虚拟机上安装的东西往往就没用了,下一个项目组还需要一个新的。申请虚拟机重新部署软件开发/测试/现场环境研发人员在开发环境中编写代码并进行测试,然后提交给测试部门。测试人员在测试环境中运行,发现存在bug。对于这个BUG,在和测试人员多次争论解决BUG后,发布了版本。发到现场部署到生产环境后,又发现一个BUG。现在轮到工程师和测试人员争论了。有时,为了兼容特殊的现场环境,需要自定义代码并拉出分支,这让每次站点升级或迁移都要升级成为噩梦。如果现场有多个tomcat应用,需要先停止每个tomcat,更换war包,然后再启动,依次进行,这样既麻烦又容易出错。手动执行回滚。另外,如果项目要上云,部署上云后还需要进行新一轮的测试。如果后期考虑云厂商,同样的测试可能要重新进行(比如更换数据存储组件),费时费力。  总结上面列出的所有场景,它们有一个共同的问题:没有一种技术既能屏蔽操作系统差异,又能在不降低性能的情况下运行应用程序,从而解决环境依赖问题。Docker应运而生。2.什么是Docker  Docker是一个应用容器引擎。先说什么是容器。Linux系统提供了Namespace和CGroup技术来实现环境隔离和资源控制。其中,Namespace是Linux提供的一种内核级环境隔离的方法,使一个进程和该进程创建的子进程的运行成为可能。该空间与Linux的超级父进程隔离。注意Namespace只能实现运行空间的隔离,物理资源仍然是所有进程共享的。为了实现资源隔离,Linux系统提供了CGroup技术来控制一个进程组可用的资源(如CPU、内存、磁盘IO等),结合这两种技术,一个对象拥有独立的用户空间和有限的资源可以构造。这样的对象称为容器。LinuxContainer是Linux系统提供的一种容器化技术,简称LXC,它结合了Namespace和CGroup技术,为用户提供更易用的接口来实现容器化。LXC只是一种轻量级的容器化技术。只能限制部分资源,不能实现如网络限制、磁盘空间占用限制等。dotCloud结合了LXC和下面列出的技术来实现Docker容器引擎。与LXC相比,Docker拥有更全面的资源控制能力,是一个应用级的容器引擎。Chroot:该技术可以在容器中构建一个完整的Linux文件系统;Veth:该技术可以在宿主机上虚拟出一块网卡,桥接容器中的eth0网卡,实现容器与宿主机、容器之间的网络通信;UnionFS:联合文件系统,Docker利用这种技术的“CopyonWrite”的特点,实现了容器的快速启动和最小的资源占用,后面会介绍这种文件系统;iptables/netfilter:通过这两种技术控制容器网络访问策略;TC:该技术主要用于流量隔离和带宽限制;配额:该技术用于限制磁盘读写空间的大小;setrlimit:该技术用于限制容器内打开进程数,限制打开文件数正是因为Docker依赖Linux内核的这些技术,所以至少3.8以上的内核才能运行Docker容器.官方推荐使用3.10或更高版本的内核。3、与传统虚拟化技术的区别  传统虚拟化技术在虚拟机(VM)和硬件之间增加了一个软件层Hypervisor,或者称为虚拟机管理程序。Hypervisor的运行方式有两种:直接运行在物理硬件上。比如基于内核的KVM虚拟机,这种虚拟化需要CPU支持虚拟化技术;它在另一个操作系统上运行。VMWare和VitrualBox等虚拟机。  因为运行在虚拟机上的操作系统最终通过hypervisor来共享硬件,所以虚拟机的guestOS发出的指令需要被hypervisor捕获,然后翻译成虚拟机可以识别的指令物理硬件或主机操作系统。VMWare、VirtualBox等虚拟机在性能上远不如裸机,但是基于硬件虚拟机的KVM可以发挥裸机80%左右的性能。这种虚拟化的优点是不同虚拟机之间完全隔离,安全性高,可以在一台物理机上运行多个内核操作系统(如Linux和Windows),但每个虚拟机非常Bulky,占用资源大而且起步慢。  Docker引擎运行在操作系统上。它基于内核LXC、Chroot等技术实现容器环境隔离和资源控制。容器启动后,容器内的进程直接与内核交互,不经过Docker引擎。几乎没有性能损失,可以发挥裸机的全部性能。但是由于Docker基于Linux内核技术实现了容器化,运行在容器中的应用程序只能运行在Linux内核操作系统上。目前安装在Windows上的docker引擎实际上是利用Windows自带的Hyper-V虚拟化工具自动创建一个Linux系统,而容器中的操作实际上是间接使用这个虚拟系统来实现的。4.Docker基本概念  Docker主要有以下几个概念:Engine:创建和管理容器的工具,通过读取镜像生成容器,负责从仓库拉取镜像或者向仓库提交镜像;mirrorimages:类似基于虚拟机镜像,一般由一个基本的操作系统环境和多个应用程序打包而成,是创建容器的模板;container:可以看作是一个简单版的Linxu系统环境(包括root用户权限、进程空间、用户空间和网络空间等),是运行在其中的应用程序打包的一个盒子;仓库:镜像文件集中存放的地方,分为公共仓库和私有仓库。目前最大的公共仓库由DockerHub官方提供。此外,国内阿里云、腾讯云等也提供公共仓库;host:运行引擎的操作系统所在的服务器。五、Docker与虚拟机、Git、JVM的类比  为了让大家对Docker有更直观的认识,分别做以下三组类比:形象地说,Docker引擎启动容器运行Spark集群(容器中包含基本的Linux操作系统环境),类似于虚拟机软件启动多个虚拟机,分别运行虚拟机中的Spark进程。不同的是,Docker容器中的应用程序在使用物理资源时,直接与内核打交道,不经过Docker引擎。  Docker的仓库思想和Git是一样的。  Docker的口号是“Build,Ship,andRunAnyApp,Anywhere”,即应用可以基于Docker构建、加载和运行,一旦构建就可以随处运行。Java的口号是“WriteOnce,RunAnywhere”,即一次编写,到处运行。Java基于JVM适配操作系统的特性屏蔽了系统差异。Docker利用内核版本兼容的特性实现一次构建、导出和运行。只要Linux系统的内核是3.8以上,就可以使用容器运行。当然,就像在Java中一样,应用代码如果使用了JDK10的新特性,是无法基于JDK8运行的。如果容器中的应用程序使用了4.18版本的内核特性,那么当CentOS7(内核版本为3.10)启动容器时,容器虽然可以启动,但是里面的应用程序的功能是无法正常运行的,除非操作系统内核主机升级到4.18版本。6.Docker镜像文件系统  Docker镜像采用分层存储格式。每个图像都可以根据其他图像构建。每一层图像都可以被多个图像引用。上图中的镜像依赖,K8S镜像其实就是CentOS+GCC+GO+K8S这四个软件组合的镜像。这种分层结构可以充分共享镜像层,可以大大减少镜像仓库占用的空间。对于用户来说,他们看到的容器其实就是Docker使用UnionFS(联合文件系统)的相关镜像层的目录。Union”到同一个挂载点来呈现一个整体。这里需要简单介绍一下什么是UnionFS:UnionFS可以将物理位置独立的多个目录(也称分支)的内容联合挂载到同一个目录中。UnionFS允许控制这些目录的读写权限。此外,对于只读文件和目录,它具有“写入时复制”的特性,即如果只读文件被修改,则将修改前的文件复制一份复制到可写层(可能是磁盘上的某个目录),所有的修改操作实际上都是修改这个文件的副本,原来的只读文件不会改变。使用UnionFS的例子之一是:Knoppix,一个用于Linux演示、CD-ROM教学和商业产品演示的Linux发行版,就是将一张CD/DVD和一个读写设备(如U盘)联合挂载,以便对CD/DVD上的文件进行任何改动演示过程中会应用到U盘,不会改变CD/DVD原有内容。  UnionFS的种类很多,其中AUFS在Docker中比较常用,它是UnionFS的升级版,此外还有DeviceMapper、Overlay2、ZFS、VFS。Docker镜像的每一层默认存放在/var/lib/docker/aufs/diff目录下。当用户启动一个容器时,Docker引擎首先在/var/lib/docker/aufs/diff目录下新建一个读写层,然后使用UnionFS将读写层目录和各层目录联合挂载将指定镜像复制到/var/lib/docker/aufs/mnt目录下(指定镜像各层目录为只读方式挂载),使用LXC等技术进行环境隔离和资源控制,使得容器中的应用只依赖相应挂载的目录和mnt目录下的文件运行。  利用UnionFS真实复制的特性,在启动一个容器时,Docker引擎实际上只是增加了一个可写层,构建了一个Linux容器,两者几乎不消耗系统资源,所以Docker容器可以做秒级启动,一台服务器可以启动上千个Docker容器,而在一台服务器上启动几十个传统虚拟机已经非常困难,而且虚拟机启动很慢,这就是Docker与传统虚拟机的区别。机器的两大优点。  当应用程序只是直接调用内核函数进行操作时,可以直接将应用程序本身作为底层构建镜像,但是由于容器本身会隔离环境,宿主机中的文件是无法访问的在容器内部(除非指定某些目录或文件映射到容器中),这种情况下应用程序代码只能使用内核的功能。但是Linux内核只提供了进程管理、内存管理、文件系统管理等一些基本的、底层的管理功能。在实际场景中,几乎所有的软件都是基于操作系统开发的,因此往往需要依赖操作系统。软件和运行时库等等,如果这些应用的下一层直接是内核,那么应用就跑不起来了。所以实际上应用镜像的底层往往是基于一个操作系统镜像来补充运行依赖。  Docker中的操作系统镜像与安装系统时使用的ISO镜像不同。ISO镜像包含了操作系统内核和分发系统包含的所有目录和软件,而Docker中的操作系统镜像不包含系统内核,只包含一些系统必须的目录(如/etc/proc等)以及常用的软件和运行时库等,操作系统镜像可以看作是内核之上的应用程序,是封装内核功能,为用户编写的应用程序提供运行环境的工具。基于这样一个镜像,应用程序可以使用相应操作系统中各种软件的功能和运行时库。另外,由于应用程序是基于操作系统镜像构建的,即使切换到另一台服务器,只要将操作系统镜像包含在应用程序所使用的功能中,就可以适配宿主机的内核机器,应用程序可以正常运行,这就是构建运行无处不在的原因。  下图展示了镜像和容器的关系:  上图中的Apache应用是基于emacs镜像构建的,emacs是基于Debian系统镜像构建的。当它作为容器启动时,在Apache镜像层上构建了一个可写层,对容器本身的修改操作都在可写层进行。Debian是这个镜像的基础镜像(BaseImage),它提供了内核Kernel更高级的封装。同时,其他镜像也是基于相同内核构建的(以下BusyBox是操作系统镜像的简化版):  这时候就会出现问题,应用是基于操作系统构建systemimage占用空间大。是不是不方便分发镜像,而且镜像仓库会占用很大的空间。有人考虑到这一点,针对不同的场景构建了不同的操作系统镜像。下面介绍最常用的系统镜像。7.Docker基础操作系统  以上系统镜像适用于不同场景:BusyBox:极简版Linux系统,集成了100多个常用的Linux命令,大小不到2MB。“瑞士军刀”,适用于简单的测试场景;Alpine:一个面向安全的轻量级Linux分发系统,比BusyBox更完整,大小小于5MB。是官网推荐的基础镜像,因为包含了足够的基础功能,体积更小,最常用于生产环境;Debian/Ubuntu:功能齐全的Debian系列操作系统,大小约170MB,适合研发环境;CentOS/Fedora:都是基于Redhat的Linux发行版,企业级服务器常用的操作系统,稳定性高,大小200MB左右,适合生产环境。8、Docker持久化存储  根据前面介绍的容器UnionFS真实复制的特点,可以看出,对容器中的文件进行增删改查,实际上是对可写层文件副本的操作.容器关闭后,可写层也会被删除,所有对容器的修改都将失效。因此,需要解决容器中的文件持久化问题。Docker提供了两种解决方案来实现这一点:将宿主机文件系统中的目录映射到容器中的目录,如下图所示。这样,容器内目录下创建的所有文件都存储在宿主机对应的目录下。容器关闭后,宿主机的目录仍然存在,再次启动容器时可以读取之前创建的文件。从而实现了容器的文件持久化。当然,同时也要明白,如果修改了镜像自带的文件,由于镜像是只读的,容器关闭时修改操作是无法保存的,除非新建一个镜像修改文件后。通过网络将多台主机的磁盘目录组合成一个共享存储,然后将共享存储中的特定目录映射到特定的容器中,如下图所示。这样,当容器重启时,它仍然可以读取它关闭前创建的文件。NFS通常用作生产环境中的共享存储解决方案。9.Docker镜像制作方法  镜像的制作方法有两种:通过正在运行的容器生成新的镜像  容器运行时,其中的所有修改都会反映到容器的可写层中,前提是通过Docker的commit命令,可以将可写层的修改内容叠加到正在运行的容器上,生成新的镜像。如上图所示,如果容器中新安装了Spark组件,如果关闭容器,Spark组件会随着可写层的消失而消失。如果在关闭容器前使用commit命令生成新的镜像,然后使用新的镜像启动容器,容器中就会包含Spark组件。  这种方式比较简单,但是不能直观的设置环境变量,监听端口等,适合简单的使用场景。通过Dockerfile生成新镜像  Dockerfile是定义镜像创建步骤的文件。Docker引擎通过build命令读取Dockerfile,按照定义的步骤一步步构建镜像。在研发和实施环境中,通过Dockerfile创建容器是一种主流做法。下面是一个Dockerfile的例子:FROMubuntu/14.04#BaseimageMAINTAINERguest#CreatorsignatureRUNapt-getinstallopenssh-server-y#安装ssh服务RUNmkdir/var/run/sshd#创建目录RUNuseradd-s/bin/bash-m-d/home/guestguest#创建用户RUNecho'guest:123456'|chpasswd#修改用户密码ENVRUNNABLE_USER_DIR/home/guest#设置环境变量EXPOSE22#容器默认开启的端口CMD["/usr/sbin/sshd-D"]#启动容器时自动启动ssh服务  Docker引擎可以根据上述Dockerfile中定义的步骤构建带有ssh服务的Ubuntu镜像。10.Docker使用场景  Docker是一个轻量级的虚拟化解决方案,应用场景丰富。下面收集一些常见的场景:作为轻量级虚拟机,可以使用Ubuntu等系统镜像来创建容器。作为虚拟机使用,与传统虚拟机相比,启动速度更快,占用资源更少。单机可以启动大量操作系统容器,方便各种测试;作为云主机结合Kubernetes等容器管理系统,可以动态分配和管理大量服务器上的容器。在公司内部,甚至可以替代VMWare等虚拟机管理平台,使用Docker容器作为云主机;应用服务在Web应用服务开发场景下,Java运行环境可以,Tomcat服务器打包为基础镜像,修改代码包后添加到基础镜像中构建新镜像,可以轻松升级服务和控制版本;容器云平台CaaSDocker的出现,让很多云平台提供商开始提供容器云服务,简称容器即服务(CaaS)。下面对IaaS、PaaS、SaaS进行比较:IaaS(InfrastructureasaService):将虚拟机或其他基础资源作为服务提供给用户。用户可以从供应商那里获取虚拟机或存储等资源来加载相关应用,而这些基础设施的繁琐管理将由IaaS供应商来处理。它的主要用户是企业系统管理员和运维人员;PaaS(PlatformasaService):开发平台作为服务提供给用户。用户可以在一个包含SDK、文档和测试环境的开发平台上轻松编写应用程序,无论是部署还是运行,用户都无需担心服务器、操作系统、网络和存储等资源管理的烦恼,这些繁琐任务由PaaS提供商处理。它的主要用户是企业开发人员。SaaS(软件即服务):将应用程序作为服务交付给客户。用户只需连接互联网,使用浏览器即可直接使用运行在云端的应用,无需为安装等琐碎的事情操心,避免了初期在软硬件上的高额投入。SaaS主要面向普通用户。CaaS(ContainerasaService):完成IaaS和PaaS的功能。与传统的IaaS和PaaS服务相比,CaaS比PaaS对底层的支持更灵活,比IaaS更容易控制上层应用。同时,由于Docker是比VM更细粒度的虚拟化服务,可以更高效地利用计算资源。CaaS可以部署在任何物理机、虚拟机或IaaS云上。持续集成和持续部署互联网行业提倡敏捷开发,持续集成和持续部署CI/CD是最典型的开发模式。使用Docker容器云平台,编写代码并推送到Git/SVN后,自动触发后端CaaS平台下载编译构建代码为测试Docker镜像,然后更换测试环境容器服务,自动在Jenkins或Hudson中运行单元/集成测试。测试通过后,可立即自动更新新版本镜像上线,完成服务升级。整个过程完全自动化,一次完成,最大程度简化运维,保证线上线下环境完全一致,线上服务版本和Git/SVN发布分支也统一。解决微服务架构的实现问题基于SpringCloud等微服务框架,可以实现对微服务的管理,但微服务本身仍然需要运行在操作系统上。在使用微服务架构开发的应用中,往往会有大量的微服务,这就导致需要在一台服务器上启动多个微服务来提高资源利用率,而微服务本身可能只兼容某些操作系统,这就导致了即使有很多服务器资源(操作系统可能不同),但是由于微服务本身可能与操作系统相关,所以不可能让微服务运行在任何一台服务器上,这就带来了资源的浪费和运维困难。利用Docker容器的环境隔离能力,让微服务运行在容器中,可以解决上述问题。执行临时任务有时用户只想执行一次性任务,但是如果使用传统的虚拟机方式,需要搭建环境,任务执行完还需要释放资源,比较麻烦。可以使用Docker容器搭建一个临时的运行环境,任务执行完可以关闭容器,方便快捷。多租户环境利用Docker的环境隔离能力,为不同租户提供专属容器,实现简单,成本低。11.总结  Docker的技术并不神秘。它只是一种集成了前人积累的各种成果的应用级容器化技术。它使用各种Linux发行版中使用的版本兼容的内核容器化技术。达到镜像一次构建到处运行的效果,利用容器中的基础操作系统镜像层,屏蔽了实际运行环境中的操作系统差异,使得用户在开发应用时,只需要保证选择的操作系统和内核版本只需要正确运行,几乎不需要关心实际运行环境中的系统差异,大大提高了效率和兼容性。但是随着越来越多的容器在运行,容器管理将成为另一个运维难题。这个时候就需要引入Kubernetes、Mesos或者Swarm等容器管理系统,后面有机会再介绍这些技术。