传统部署时代:
早期是在物理服务器上运行应用程序。无法为物理服务器中的应用程序定义资源边界,这会导致资源分配出现问题。例如:如果在物理服务器上运行多个应用程序,则可能会出现一个应用程序占用大部分资源的情况,结果可能会导致其他应用程序的性能下降。一种解决方案是在不同的物理服务器上运行每个应用程序,但是由于资源利用不足而无法扩展,并且组织维护许多物理服务器的成本很高。
虚拟化部署时代:
虚拟化功能允许在单个物理服务器上运行多个虚拟机(VM)。虚拟化功能可以实现应用程序在VM之间隔离,并提供安全级别,因此一个应用程序的信息不能被另一应用程序自由地访问。
因为虚拟化可以轻松地添加或更新应用程序、降低硬件成本等等,所以虚拟化可以更好地利用物理服务器中的资源,并可以实现更好的可伸缩性。大家可以理解为每个VM是一台完整的计算机,在虚拟化硬件之上运行所有应用组件,包括其自己的操作系统。
容器部署时代:
容器类似于VM,但是它们具有轻量级的隔离属性,可以在应用程序之间共享操作系统(OS)。因此。容器具有自己的文件系统、CPU、内存、进程空间等。由于它们与基础架构分离,因此可以跨云和OS分发进行移植。
随着Docker 技术的发展和广泛流行,云原生应用和容器调度管理系统成为IT领域大热的话题。由于虚拟机镜像大、镜像标准、打包流程和工具不统一,导致业界无法广泛接受,限制了云原生应用的发展。而Docker 技术的出现正好解决了云原生应用构建、交付和运行的瓶颈,使得构建云原生应用成为使用Docker的开发者的优先选择。那么Docker从单机走向集群已经成为必然趋势。Kubernetes作为当前唯一一个被广泛认可的Docker分布式解决方案,在未来几年内,会有大量的系统选择它。
Kubernetes是由Google在2014年6月开源的容器编排调度引擎,使用Go语言开进行发,最初源于谷歌内部Brog引擎。由于Kubernetes的K和s间有8个字母,因此国内行业人员简称为K8S。 2015年7月Kubernetes V1.0正式发布,截止到目前最新稳定版本是V1.21.x。Kubernetes 拥有一个庞大且快速增长的生态系统。
Kubernetes 这个名字,起源于古希腊,是舵手的意思,所以它的 logo 即像一张渔网又像一个罗盘,谷歌选择这个名字还有一个深意:既然docker把自己比作一只鲸鱼,驮着集装箱,在大海上遨游,google 就要用Kubernetes去掌握大航海时代的话语权,去捕获和指引着这条鲸鱼按照主人设定的路线去巡游。
Kubernetes 是一个可移植、可扩展的开源Docker容器编排调度引擎,和Docker容器结合在一起,可实现容器技术的分布式架构方案。主要用于自动化部署、扩展和管理容器类应用,提供资源调度、部署管理、服务发现、扩容缩容、监控等功能。它提供完善的管理工具,涵盖开发、部署测试、运维监控等各个环节。它的目标不仅仅是一个容器编排系统,而是提供一个应用规范,用户可以描述集群的架构,定义服务的最终状态,Kubernetes可以将系统达到和维持在这个状态。对于负载均衡、服务发现、高可用、滚动升级、自动伸缩等容器云平台的功能要求有原生支持。
随着对K8S系统架构与设计理念的深入了解,可以发现K8S系统正是为运行云原生应用而设计考虑。使得基于K8S系统设计和开发生产级的复杂云原生应用,变得像启动一个单机版容器服务那样简单易用。
Kubernetes可以调度计算集群节点、动态管理节点上应用,并保证它们按用户期望状态运行。通过使用「Labels(标签)」和「Pods(荚)」的概念,Kubernetes将应用按逻辑单元进行分组,方便管理和服务发现。
官网:Kubernetes
文档:https://kubernetes.io/zh/docs/home/
微服务架构的核心是将一个巨大的单体应用分解为很多小的互相连接的微服务。一个微服务背后可能有多个实例副本支撑,副本的数量可能会根据系统负荷变化而进行调整,而K8S 平台中内嵌的负载均衡器发挥着重要作用。微服务架构使得每个服务都可以由专门的开发团队来开发,开发者可以自由选择开发技术,这对于大规模团队来说很有价值。另外,每个微服务独立开发、升级、扩展,使得系统具备很高的稳定性和快速迭代进化能力。
对于互联网公司来说,用户规模等价于资产,谁拥有更多的用户,谁就能在竞争中胜出,因此超强的横向扩容能力是互联网业务系统的关键指标之一。K8S集群中可从只包含几个Node的小集群,平滑扩展到拥有成百上千Node 的大规模集群,利用K8S提供的工具,甚至可以在线完成集群的扩容。只要微服务设计的合理,结合硬件或者公有云资源的线性增加,系统就能够承受大量用户并发访问所带来的压力。
Master 节点提供集群的管理控制中心,对集群进行全局决策,并检测和响应集群事件(例如:当复制控制器的“副本”字段不满足时启动新的Pod)。 基本上K8S所有的控制命令都是发给Master,Master负责具体的执行过程。 Master节点可以在群集中的任何计算机上运行,但建议Master节点占据一个独立的服务器并做好高可用。因为Master节点是整个集群的大脑,如果Master所在节点宕机或者不可用,那么所有的控制中心都将失效。
在Master节点上运行着以下关键进程。
节点控制器(Node Controller):负责在Node节点出现故障时及时发现和响应;
复制控制器(Replication Controller):负责维护正确数量的Pod;
端点控制器(Endpoints Controller):填充端点对象(即连接Services和Pods);
服务帐户和令牌控制器(Service Account & Token Controllers):为新的命名空间创建默认帐户和API访问令牌。
Node节点也被称为Worker或者Minion,Node 节点可以是一台物理主机,也可以是一台虚拟机。Node 节点是K8S 集群中的工作负载节点,每个Node 都会被Master 分配一些工作负载。当某个Node 宕机时,Node上的工作负载会被Master自动转移到其他Node节点上去。
每个Node 节点上都运行着以下关键进程:
查看Kube-proxy工作模式:curl 127.0.0.1:10249/proxyMode
Node 节点可以在运行期间动态增加到Kubernetes 集群中,前提是这个节点上已经正确安装、配置和启动了上述关键进程。在默认情况下,Kubelet 会向Master注册自己,这也是Kubernetes 推荐的Node 管理方式。一旦Node 被纳入集群管理范围,Kubelet 进程会定时向Master 汇报自身的情况,例如操作系统、Docker 版本、机器的CPU 和内存情况,以及之前有哪些Pod 在运行等。这样Master 可以获知每个Node 的资源使用情况,并实现高效负载均衡资源调度策略。而某一个Node 超过指定时间不上报信息时,会被Master判定为失联的状态,被标记为不可用,随后Master 会触发节点转移进程。
Kubernetes 包含多种类型的资源对象:Pod、Replication Controller、Service、Deployment、Job、DaemonSet等。所有的资源对象都可以通过Kubernetes 提供的kubectl工具进行增、删、改、查等操作,并将其保存在Etcd 中持久化存储。从这个角度来看,Kubernets 其实是一个高度自动化的资源控制系统,通过跟踪对比Etcd 存储里保存的资源期望状态与当前环境中的实际资源状态的差异,来实现自动控制和自动纠错等高级功能。下面对常用的资源对象分别进行介绍。
集群有两种角色:Master和Node(也叫worker)。
当我们拥有一个 Kubernetes 集群后,就可以在上面跑我们的应用了,前提是我们的应用必须支持在 docker 中运行,也就是我们要事先准备好docker镜像。
有了镜像之后,一般我们会通过Deployment 配置文件去描述应用,比如应用叫什么名字、使用的镜像名字、要运行几个实例、需要多少的内存资源、cpu 资源等等。
有了配置文件就可以通过Kubernetes提供的命令行客户端kubectl 去管理这个应用了。kubectl 会跟 Kubernetes 的 master 先通过验证及授权连接上API在和RestAPI通信,最终完成应用的管理。
比如我们刚才配置好的 Deployment 配置文件叫 app.yaml,我们就可以通过"kubectl create -f app.yaml" 来创建这个应用,之后就由 Kubernetes 来保证我们的应用处于运行状态,当某个实例运行失败了或者运行着应用的 Node 突然宕机了,Kubernetes 会自动发现并在新的 Node 上调度出一个新的实例,保证我们的应用始终达到我们预期的结果。
其实在上一步创建完 Deployment之后,Kubernetes 的 Node 做的事情并不是简单的docker run 一个容器。出于易用性、灵活性、稳定性等的考虑,Kubernetes 提出了一个叫做 Pod 的东西,作为 Kubernetes 的最小调度单位。所以我们的应用在每个 Node 上运行的其实是一个 Pod。Pod 也只能运行在 Node 上。如下图:
那么什么是 Pod 呢?Pod 是一组容器(当然也可以只有一个)。容器本身就是一个小盒子了,Pod 相当于在容器上又包了一层小盒子。这个盒子里面的容器有什么特点呢?
上面的 Deployment 创建了,Pod 也运行起来了。如何才能访问到我们的应用呢?
最直接想到的方法就是直接通过 Pod-ip+port 去访问,但如果实例数很多呢?先拿到所有的 Pod-ip 列表,再配置到负载均衡器中,轮询访问。但上面我们说过,Pod 可能会死掉,甚至 Pod 所在的 Node 也可能宕机,Kubernetes 会自动帮我们重新创建新的Pod。再者每次更新服务的时候也会重建 Pod。而每个 Pod 都有自己的 ip。所以 Pod 的ip 是不稳定的,会经常变化的。
面对这种变化我们就要借助另一个概念:Service。它就是来专门解决这个问题的。不管Deployment的Pod有多少个,不管它是更新、销毁还是重建,Service总是能发现并维护好它的ip列表。Service对外也提供了多种入口:
看似服务访问的问题解决了。但大家有没有想过,Service是如何知道它负责哪些 Pod 呢?是如何跟踪这些 Pod 变化的?
最容易想到的方法是使用 Deployment 的名字。一个 Service 对应一个 Deployment 。当然这样确实可以实现。但kubernetes 使用了一个更加灵活、通用的设计 Label 标签,通过给 Pod 打标签,Service 可以只负责一个Deployment 的 Pod 也可以负责多个 Deployment 的 Pod 了。Deployment 和 Service 就可以通过 Label 解耦了。
滚动升级是Kubernetes中最典型的服务升级方案,主要思路是一边增加新版本应用的实例数,一边减少旧版本应用的实例数,直到新版本的实例数达到预期,旧版本的实例数减少为0,滚动升级结束。在整个升级过程中,服务一直处于可用状态。并且可以在任意时刻回滚到旧版本。
管理员--->kubectl--->deployment(nginx.yaml)--->label--->service--->Pod--->container