• 部署技术演变之虚拟机、容器、Docker、Kubernetes


    一、物理机时代

      在虚拟机出现前的业务环境中,应用往往部署在物理机器上,但这样的部署方式存在一些弊端:空闲资源难以得到复用,部署异构系统时需要重新采购物理资源,大量中小容量的机器使得运维成本提升。在这样的情况下,如何降低基础设施的管理成本便成为急切的需求。

    二、虚拟机时代

      虚拟机的出现使得用户在一台物理机上能够独立运行多个相互隔离的系统,通过对资源的抽象化使得主机资源能够被有效复用,这对于企业IT管理十分有益。然而,虚拟机同样也会带来一些问题:大量独立系统的运行会占用许多额外开销,消耗宿主机器资源,资源竞争时可能会严重影响系统响应;此外,每运行新的虚拟机都需要重新配置一遍环境,和在物理机上的情况基本无异,重复的环境配置操作则会消耗开发和运维人员的工作时间。此时需求便关注到如何减少虚拟化时的资源损耗,同时还能保证隔离性,以及使应用的上线周期更短,这便引导了容器技术的发展。

    三、容器化时代

      从2000年开始,各家类Unix操作系统厂商开始陆续推出容器相关的项目,2008年Google的Cgroups贡献给Linux kernel 2.6.24后创造了LXC( Linux Containers),实现了多个独立的Linux环境(容器)可运行在同一个内核。对于一个完整独立运行环境来说,需要包含三个关键:环境隔离、资源控制和文件系统。在LXC中则分别通过Namespace、Cgroups、rootfs来实现相应的能力。

      环境隔离——Namespace:LXC将内核全局资源封装,每个Namespace都有一份独立的资源,使得不同的进程在各自Namespace内对同一种资源的使用互不干扰,不会影响其他Namespace下的资源,实现了进程隔离。

      资源控制——Cgroups:LXC通过Cgroups对资源进行控制,限制和隔离一组进程对系统资源的使用。在Cgroups出现之前OS只能对一个进程做资源限制,而 Cgroups可以对进程进行任意分组,如何分组由用户自定义,借此实现对于一个Namespace的资源调度管理。

      文件系统——rootfs:rootfs挂载在容器根目录上,用来为容器进程提供隔离后执行环境的文件系统。rootfs包含一个操作系统所涉及的文件、配置和目录,在Linux 操作系统内核启动时,内核会先挂载一个只读的rootfs,当系统检测其完整性之后,决定是否将其切换到读写模式。

      在通过LXC构建容器后,一台宿主机能够实现多个相互隔离应用的运行。同时,共享内核使得每个容器又很轻量,解决了运行大量隔离应用时虚拟机资源消耗过重的弊端。然而,LXC虽解决了应用隔离的问题,但却只是轻量的容器技术,没有解决各平台软件交付标准不统一的问题,如不同的软件交付工具、应用运行规范不统一、环境依赖复杂等带来的配置开销。这些问题使容器技术的推广依然比较有限,直到Docker的出现。

    四、Docker

      早期Docker是基于LXC开发,因此Docker容器也有着和LXC相似的特性,仅需要较少资源便可以启动。但不同于LXC,Docker除了容器运行,还是一个打包、分发和运行应用程序的平台。Docker允许将应用和其依赖的运行环境打包在一起,打包好的“集装箱“(镜像)能够被分发到任何节点上执行,无需再进行配置环境的部署。这样使得Docker解决了开发和部署应用时环境配置的问题,规范化了应用交付和部署,降低了部署测试的复杂度以及开发运维的耦合度,极大提升了容器移植的便利性,便于构建自动化的部署交付流程。

      Docker和虚拟机都是资源虚拟化发展的产物,但二者在架构上又有区别。虚拟机通过Hypervisor虚拟化主机硬件资源,然后构建客户机操作系统,由宿主机的管理程序管理;Docker直接运行于主机内核,应用在主操作系统的用户空间上执行独立任务,不需要从操作系统开始构建环境,赋予了应用从交付到部署再到运维的独立性。

       虚拟机的启动时间可能是分钟级的,而Docker容器创建是秒级别。对于硬盘的使用Docker一般为MB级别,远小于包含操作系统的虚拟机GB级磁盘使用量。对于操作系统来说,能支持运行的Docker容器数量远多于虚拟机。

      那么,如何用Docker启动一个应用呢?对于启动应用来说,首先需要获得程序本身和所需要的环境配置。Docker会将应用运行所有需要的静态资源如代码、运行时环境、配置封装为镜像,镜像通过Union FS采用分层存储架构,通过描述文件Dockerfile指令构建镜像层。

      Dockerfile里包含多条指令描述该层应当如何构建,随着镜像层的逐层叠加,将一个完整的镜像所需要的信息全部包含。外部则通过统一文件系统将相互叠加的层整合起来,以只可读的统一文件(Union Read-Only File System)形式展现,这样的分层存储使得镜像的复用和定制变的更为容易,压缩了存储空间。

      镜像运行之后的实体是容器。Docker容器同样采用分层存储,通过在镜像顶部增加一层可读可写层,外部以可读写的统一文件(Union Read-Wirte File System)形式展现。当容器运行时所有的进程操作均在可读可写层,而下面的镜像则不会被修改。容器的实质是进程,运行于自己独立的命名空间。容器存储层的生命周期和容器一样,一旦容器消亡,存储层也一并消亡,所以原生的容器是无状态的,这也就是为什么之后编排系统会引入有状态服务和持久化存储以支持有状态服务。

       Docker的镜像和容器通过三端的服务操作和管理:请求端Docker Client、主机端Docker Host和远端拉取镜像的仓库Registry。Docker Client负责接收指令,与Docker Host下的守护进程Docker Daemon进行交互。Host提供了执行和运行应用程序的完整环境,其中的Docker Daemon用于管理Docker镜像、容器、网络和存储卷,负责所有与容器相关的操作如拉取镜像、创建容器等,会不断侦听Docker API请求并进行处理。Registry则是镜像管理的仓库,用户可以将创建的镜像提交到仓库进行存储,同时方便从仓库拉下来镜像为自己所用。

      Docker容器技术在快速部署、环境标准化、隔离性方面的优势得到了开发人员普遍认可,但是如果以一个完整PaaS平台为标准来衡量这些还不够。首先,Docker提供了名为“容器”的隔离环境,但是面对多容器间有拓扑有关联的场景,Docker还难以应对;其次,虽然容器解决了应用交付规范问题,但难以实现完全应用托管;另外,随着基础设施规模的扩大,开发中心必将分布式化,调度问题就需要解决。

      2014年7月Docker宣布收购单机容器编排软件Fig(后来命名为Docker Compose),并在同年12月推出了自己的容器集群编排项目Docker Swarm。Docker Swarm成功打造了两方面能力:1.多容器编排能力,支持通过YAML文件声明多容器应用,并定义容器之间的关系;2.分布式调度能力,允许跨集群节点调度容器。Docker Compose和Dockers Swarm可以基本满足开发人员对于PaaS平台的需求,对于Docker向平台化发展的规划具有基石一般的意义。至此,Docker未来的发展似乎很乐观,但是随着Google加入,容器市场的局面发生了重大改变。

    五、Kubernetes

      2014年Google开源了名为Kubernetes(简称K8S)的项目,它是由Google内部Borg项目而开源出来的容器集群管理系统。Kubernetes继承了Google丰富的大规模集群运维的经验和基因,能够提供复杂的、大规模的容器编排管理服务。2015年Google发布了Kubernetes第一个商业版本,代表Kubernetes进军生产级容器规模管理,也意味着开始与Docker竞争PaaS平台的未来版图。

      Kubernetes集群由两类节点构成:Master Node和Worker Node。Kubernetes采用声明式的设计,任何操作指令都通过声明式API与Master通信。Master Node可响应API声明,进行集群管理和容器调度。容器则运行在Worker Node,Worker负责响应Master指令,执行容器启停等维护操作。

      Kubernetes中最基础的调度单元是Pod(而不是容器)。Pod内可以封装多个容器,以及这些容器共享的各种资源,包括存储、独立网络IP等。那么为什么不直接以容器为最小调度单位,而是以Pod呢?在上一篇文章中介绍过容器封装了不同组进程,但如果两个模块关系很紧密,例如通过共享存储通信,那分割为两个容器就比较困难了。为此Kubernetes提出把具有这种紧密关系的容器封装在一个Pod对象中调度,可以近似理解Pod就是轻量的虚拟机,每个容器是运行在虚拟机的应用。

      除了紧密的容器关系,一个面向生产的编排系统必然应当支持更多的容器关系,所以Kubernetes还提供了Deployment无状态多副本关系、StatefulSet有状态多副本关系、Job一次性长任务等许多对象以满足多样的编排需求。这些对象的基础调度单元还是Pod,由控制器Controller控制Pod对象的状态来实现声明的编排关系。

    下面以Deployment为例,大致介绍K8S容器编排是如何实现的

      Kubernetes需要声明一个Deployment的运行规格,就是声明一个对象运行所希望保持的状态。譬如我们需要部署一个Web Server组件,为了负载均衡和高可用,准备运行三个副本,此时就需要通过YAML声明一个Deployment对象,指定Replicas为3。

      通过API提交创建Deployment对象请求后,Master Node开始根据声明进行处理。首先Master的调度器(kube-scheduler)会把Pod规划到合适的Worker,并将指令下发给对应Worker。假设三个Pod分别调度到三个Worker,每个Worker的容器生命周期管理组件(kubelet)都接收到Master节点的指令,然后启动Pod。 

       如果系统运行一段时间后,访问流量变大,需要扩容以提升响应能力,通过声明扩容后副本数就可以实现。命令形如:kubectl scale deployment nginx-deployment--replicas 4命令执行后,Master便会进行扩容操作,把Web Server的第四个Pod副本,调度到某个Worker Node上。

       对于正常运行的集群,Kubernetes会不断的收集容器和节点状态,并维护开发者声明的规格。如果某个Worker Node突然宕机,Kubernetes会根据资源和节点工作情况把该Worker上原有Pod重新安排到健康的节点,保证系统正常提供服务。

      Kubernetes使开发人员和工程师拥有了快速处理大型项目所需的管理工具和基础架构。从负载测试或创建过渡环境,到将业务和在线应用程序移至生产环境,Kubernetes集群都可以对其进行管理。

    六、小结

      Docker自2013年发布之后早已成为了首屈一指的容器平台,它能提供轻量的虚拟化和一致性环境,可广泛应用于:将开发环境贴近生产环境,提高开发效率;简化代码流水线;整合服务器提高利用率;多租户场景。未来,从容器应用的角度,Docker仍有广阔的发展空间,在容器主导地位并不断发挥其价值。

      而容器编排也已经脱离概念普及阶段,获得广泛落地。目前很多国内外厂商都提供了基于Kubernetes的产品化的解决方案,如亚马逊 EKS、微软AKS、阿里云ACK等。除此之外还有很多厂商基于自身既有特点,发展着自己的容器编排项目,例如亚马逊 ECS、星环TCOS等,提供了丰富的附加能力。

  • 相关阅读:
    C#获取MD5哈希字符串
    C#序列化和反序列化Json数据
    C# Http请求
    C#DAL数据访问层
    C# ThreadPool.QueueUserWorkItem()之线程池异步
    C#以文件夹共享方式实现2G以上大文件传输
    该进程不具有执行此操作所需的“SeSecurityPrivilege”特权
    IIS绑定网站用域名进行访问的过程
    js获取url指定参数值
    js闭包(for循环内给元素添加事件)
  • 原文地址:https://www.cnblogs.com/guanghe/p/14842199.html
Copyright © 2020-2023  润新知