去年换工作后,开始真正在生产环境中接触容器与Kubernetes。边恶补相关知识的同时,也想把学到的内容和自己的理解整理出来。学习的途径包括k8s官方文档、书籍、极客时间专栏及网上各种博文。所涉及一些摘抄或描述,大多用自己的理解来组织语言,也就不一一注明出处了。
Kubernetes(简称K8S)与容器技术,可以说是近几年最火热的技术之一。提起K8S,大家都知道是google开源的容器编排工具。今天想先谈谈,我理解的容器、K8S是什么,以及为什么它们能火起来。
Why Docker
既然说K8S是一个容器编排的工具,那么要搞清楚K8S是什么,首先得搞清楚,容器是什么,以及为什么要用容器技术。
形象的来说,一个linux容器,实际就是一个进程,还是个被系统欺骗的进程。为什么这么说呢?一个运行在容器里的进程,主要有以下特点:
- 它被宿主机操作系统隔离了,它看不到宿主机上的其他进程,以为自己就是pid为1的进程
- 它被宿主机操作系统限制了硬件资源,它能使用的cpu、内存等硬件资源只是宿主机的一部分
- 在被宿主机操作系统限制了存储空间,让这个进程认为宿主机的某个目录就是系统根目录
这几个欺骗进程的技术,就是是容器技术的三板斧,用于资源隔离的namespace,用于资源限制的cgroup,以及用于伪装进程根目录的rootfs。这三种技术,都是是早已存在于linux中的,只是docker公司比较创新的把这三种技术整合在一起,并且,提出了容器镜像及镜像仓库等概念,把这个进程及相关的依赖环境都一起打包成可分发及重复使用的镜像文件,方便容器能在不同机器之间移植。这样,在任何地方,只要能运行docker,就能把容器镜像跑成一个包含这应用进程及相关依赖环境的容器实例。
这里用利用K8S官方文档的图,简单说下生产中的场景,看看容器技术能解决什么问题。
左图是传统的物理机部署应用方式的方式,可以看到除了一个应用的运行,除了程序本身,离不开应用配置、库、等等依赖环境。通常情况下,一个应用的上线,要经历开发环境、pre环境、beta环境、灰度测试、生产环境等等不同的基础环境。开发的同学在开发环境跑通了代码,到其他环境运行时,由于各环境依赖、配置、安全需求不同,可能会出现各种问题。运维的同学忙着在不同的环境中统一依赖。不同应用各种语言,各种应用的特殊依赖需求,也造成了CI/CD逻辑复杂,难以统一。
右图是用了容器技术之后的场景。一个容器镜像的实质就是程序进程加所有运行时环境及配置、依赖的集合。只要各个环境的底层能兼容docker,就能实现所有环境部署的一致性。开发同学不用关心生产环境和开发环境的差异可能会导致应用运行问题,运维同学部署一个应用,只要确保容器镜像能正常运行就好。CI/CD的自动化也相对容易实现很多。从而极大增加开发效率、应用迭代效率及节省了运维工作量。
说到这里,不得不说下容器与传统意义的虚拟机间的对比。其实二者都可以理解为虚拟化,它们最本质的区别是,容器是操作系统层级的虚拟化,而虚拟机是硬件层级的虚拟化。参看下图。
由于虚拟机多了一层Guest OS,需要宿主机额外的性能开销进行硬件虚拟化,同时因为是完整的操作系统,虚拟机镜像一般动辄大几G,很难在各环境间快速传播,hypervisor层相对Docker Engine也比较重。再看docker容器,实质就是宿主机上跑的一个进程,只不过通过docker与其他进程隔离开来。简单的说,敏捷和高效,是容器相比虚拟机最大的优势,当然也有劣势,由于容器只是操作系统级别的隔离,不同容器间隔离的不够彻底。
Why Kubernetes
简单说完了docker的理解,那么Kubernetes又是什么,它解决了哪些问题呢,为啥现在提容器技术必提k8s呢。这里再谈谈我理解的Why K8S。
回到实际的场景来,假如你已经开始用docker镜像来打包应用,可以方便的进行分发和部署,不用去考虑不同环境的差异。但是不是还感觉还少了点什么?
因为正常的实际业务系统,不是应用能部署,能运行起来就完事了,还要考虑应用容器的访问、水平伸缩、监控、安全、备份、容灾等等因素。而对于一个完整的业务系统来说,也不是单个应用就能搞定的,还要考虑的是各应用间的关系,及应用运行的形态。比如一个web业务,可能需要web服务器、缓存系统、数据库等多个应用。容器化部署后,它们可能部署在不同宿主机的不同容器中,相互间的访问要怎么实现,这就涉及到容器间访问关系的处理。再比如,有个优化缓存的应用也跑在容器里,只需要定期运行容器实例执行优化任务,执行完毕就终止容器,这就需要处理不同容器应用的运行形态问题。类似上述这些对容器应用及容器间关系进行管理,就是所谓的容器编排及调度。而Kubernetes,就是目前的容器编排的平台的事实标准了。
那么,具体来说,K8S到底能做哪些事呢。
在官方文档中,对K8S功能的描述如下
Kubernetes 满足了生产中运行应用程序的许多常见的需求,例如:
Pod提供了一种复合的应用容器模型,
挂载外部存储,
Secret管理,
应用健康检查,
副本应用实例,
横向自动扩缩容,
服务发现,
负载均衡,
滚动更新,
资源监测,
日志采集和存储,
支持自检和调试,
认证和鉴权.
从这些功能介绍可以看出,K8S已很类似一个云平台了,可以完全能满足生产级别的容器应用管理需求。下面是一张最简单的K8S系统示意图:
K8S集群由一个主节点(master节点)及多个工作节点(node节点)构成,开发者提交应用容器镜像,并将镜像运行的数量、方法等通过描述文件提交给K8S master节点,K8S master节点或根据集群整体情况,将应用按照需求部署在工作节点中。对于开发者来说,利用K8S可以方便的部署程序,不用关心基础设施,而对于运维人员来说,工作重心从维护具体应用,转变为维护K8S集群。而且,不管是开发者还是运维人员,都不用关心应用具体部署在哪个节点,K8S会自动判断搞定一切。相比于传统的应用部署方式,有没有觉得K8S很棒棒?
在容器编排这个概念出现的时候,Kubernetes并不是唯一的一个容器编排工具,主流的工具还有Docker公司原生的swarm和Apache基金会的mesos,为什么K8S能笑到最后,成为容器编排的事实标准呢?我理解K8S对比它们有两个最大不同点:(这里不对swarm和mesos做详细介绍了,实际也确实没怎么玩过)
- K8S对容器又做了一层抽象,也就是POD。
不同于与其他两个工具,K8S管理的原子对象,其实并不是容器,而是POD。按照官方文档的定义,一个POD,是由一个或多个共享存储及网络的容器,以及描述怎么运行这些容器的集合,所以,POD实际是一个抽象的概念。k8s对容器的所有操作,比如动态伸缩、监控等,实际上都是对pod的管理。那这层抽象的好处是什么呢?
上面有提到过,容器实质就是被特殊处理的进程。想像一个web业务,web应用进程输出的日志需要被大数据agent进程处理。这个业务如果想容器化,通常有两个做法。一是分别起来两个容器,挂载宿主机同一个目录来存放日志。另一种是起一个操作系统级别的容器或supervisord容器作为enterpoint,来管理web服务和agent进程。前一种方式,这个两个容器就被框在这台宿主机上了,要实现业务实例横向扩缩容,要考虑两个容器的运行情况和存储挂载,逻辑比较复杂。后一种方式,你要为每个容器再额外开一个supervisord进程,更重要的是,由于entrypoint是supervisord进程,web应用和大数据agent对docker来说,都是不可见的。即使nginx出错频繁重启,只要supervisord还活着,Docker就认为这个容器是正常的。我们再来看看,使用了pod这个概念以后,有什么变化。一个pod里面同时起了web服务进程和大数据agent两个容器实例,首先,pod里的容器实例是共享存储和网络namespace的,也就是说,这两进程的存储数据是直接共享的,不需要额外的挂载动作。其次,这个pod是作为一个整体被k8s管理着的,k8s会监控pod里每个容器的状态,并根据策略在有问题时进行自动干预。从这个意义上说,pod才更类似传统的虚拟机。
- 声明式API
第二点也是比较重要的方面,是K8S的声明式api(貌似swarm的新版也支持了,同样没玩过就不细说了)。什么是声明式API呢,可以参考上面系统图中的描述文件。比如要我需要集群中跑10个web服务容器,传统的命令式API是一步步调用命令构建出容器。而使用声明式api,只要告诉K8S我要10个web容器,K8S就会自动将web集群实例数维持在10个,并且,在某个pod出问题退出时,K8S会自动重新拉起新pod,使集群始终保持10个pod实例在跑。这就使得管理集群变得很简单,只要通过配置文件描述出希望的集群状态,而不用去关注中间的实现过程。
最后,总结一下:
Why Dokcer: 用容器技术跑应用,相比原来的物理机及虚拟机更高效、轻量、省资源,同时大大方便了不同环境下的应用部署及分发。
Why Kubernetes:生产集群光跑容器还不够,还要对容器应用作为一个业务系统集群进行编排及管理,而K8S的一些优势使得它成为目前容器集群编排管理工具的事实标准。
最后的最后再多提一点,实际上,容器技术不止Docker公司一家在做,Kubernetes也不是只能管理Docker容器。只是,无论从市场份额、应用性还是开发社区的热度来说,它们都是目前容器技术最主流的解决方案,就生产环境来说,目前基本没有必要去考虑其他的容器技术了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。