自动化运维Kubernetes

Kubernetes介绍

Kubernetes现已成为在私有云,公共云以及混合云环境中大规模部署容器化应用程序的事实标准。业内最大的几家公有云平台AWS,Google Cloud,Azure,IBM Cloud以及Oracle Cloud现已提供Kubernetes托管服务。node

Kubernetes 这个单词来自于希腊语,含义是 舵手 或 领航员 。其词根是 governor 和 cybernetic。 K8s 是它的缩写,用 8 字替代了“ubernete”。mysql

Kubernetes的主要功能(如容器分组,覆盖网络,4层路由,secret管理等)整合到容器平台DC/OS中。DC/OS还将Kubernetes做为一个和Marathon类似的容器调度器集成进来linux

在Kubernetes中,service是核心,提供service的容器,为了保证service的高可用,提供service的容器不能只有一个,须要一组,这一组容器咱们叫作pod,一个service里有多个pod,pod就是linux机器上(node上)的docker容器,一个service里面的多个pod能够跨node, 一个pod 里面的容器除了那个pause以外,其余的都是一个镜像启动的容器做用是同样的。为了service实现service和pod之间的关联,又有了标签的概念,功能相同的pod设定为同一个标签,好比,把全部提供MySQL服务的pod贴上标签name=MySQL ,这样MySQL service要做用于全部包含name=mysql标签的pod上。nginx

pod运行在Node上,Node能够是一台物理机,也能够是虚拟机,一般一个Node上会运行几百个pod,每一个pod里运行着一个特殊的容器,叫作Pause,其余容器叫作业务容器,业务容器共享Pause容器的网络和Volume挂载,所以同一个pod内的业务容器之间的通讯和数据交换更为高效。web

Kubernetes总体上的架构,其应用程序的部署模型,服务发现和负载平衡,内部/外部路由分离,持久卷的使用,在节点上部署守护进程,部署有状态的分布式系统,运行后台做业,部署数据库,配置管理,凭据管理,滚动更新,自动伸缩以及包管理。            算法

在集群管理方面,Kubernetes集群中的机器划分为一个master节点一群工做节点Node,其中master上运行着kube-apiserver、kube-controller-manager、kube-scheduler ,它们实现了资源管理、pod调度、弹性伸缩、安全控制、系统控制、纠错等功能 ,Node是工做节点 运行应用程序 ,提供服务 , Node上的最小单元是podNode上运行着Kubernetes Node上Kubernetes的kube-proxy服务进程、它们负责pod的建立、启动、监控、重启、销毁、以及实现负载均衡。sql

扩容和升级须要一个关键的东西,Replication controller( RC ) ,RC须要包含3个关键信息 :docker

     1)目标pod的定义数据库

      2)目标pod须要运行的副本数量(replicas)json

      3)要监控 的目标pod标签(Label)       

工做过程 :RC里定义好3个指标 ,Kubernetes会根据RC定义的Label筛选出对应的pod,并实时监控其状态和数量,当实例数量少于定义的副本数 (replicas),  则会根据RC定义的pod  模板来建立新的pod,而后将此pod调度到合适的Node上启动运行,该过程彻底自动化,无需人工干涉。

                                  Kubernetes架构

这个接近完美的集群管理器采起的基本设计决策之一是可以部署在虚拟机上运行的现有应用程序,而无需对应用程序代码作出任何修改。从总体上来讲,任何在虚拟机上运行的应用程序均可以经过简单地容器化其组件的方式部署在Kubernetes上。这是经过其核心功能实现的:容器分组,容器编排,覆盖网络,基于4层虚拟IP路由系统的容器间路由,服务发现,对运行守护程序的支持,部署有状态的应用程序组件,并且最重要的是,它还可以经过扩展容器编排器以支持复杂的编排要求。

宏观的层面来说,Kubernetes提供了一组动态可扩展的主机,用于承载运行工做负载的容器,并使用一组称为master的管理主机,提供用于管理整个容器基础设施的API。工做负载可能包括长期运行的服务,批处理做业和运行在特定容器主机上的守护程序。全部容器主机使用覆盖网络链接在一块儿以提供容器间的路由。部署在Kubernetes上的应用程序在集群网络是能够动态发现的,并且能够经过传统的负载均衡器暴露到外部网络。集群管理器的状态存放在高度分布式组织的k/v存储,该存储运行在master实例上。

 

使用 Kubernetes,你能够快速、高效地知足用户如下的需求:

  • 快速精准地部署应用程序

  • 即时伸缩你的应用程序

  • 无缝展示新特征

  • 限制硬件用量仅为所需资源

Kubernetes的优点:

  • 可移动: 公有云、私有云、混合云、多态云

  • 可扩展: 模块化、插件化、可挂载、可组合

  • 自修复: 自动部署、自动重启、自动复制、自动伸缩

为何选择 Kubernetes,它的做用?

Kubernetes 能在实体机或虚拟机集群上调度和运行程序容器。并且,Kubernetes 也能让开发者斩断联系着实体机或虚拟机的“锁链”,从以主机为中心的架构跃至以容器为中心的架构。该架构最终提供给开发者诸多内在的优点和便利。Kubernetes 提供给基础架构以真正的以容器为中心的开发环境。

Kubernetes 优点

诸如:

  • 协调辅助进程,协助应用程序整合,维护一对一“程序 – 镜像”模型。

  • 挂载存储系统

  • 分布式机密信息

  • 检查程序状态

  • 复制应用实例

  • 使用横向荚式自动缩放

  • 命名与发现

  • 负载均衡

  • 滚动更新

  • 资源监控

  • 访问并读取日志

  • 程序调试

  • 提供验证与受权

以上兼具平台即服务(PaaS)的简化和基础架构即服务(IaaS)的灵活,并促进了在平台服务提供商之间的迁移。

 

Kubernetes缺点

Kubernetes 不是传统的、全包容的平台即服务(Paas)系统。它尊重用户的选择,这很重要。

  • 并不限制支持的程序类型。它并不检测程序的框架 (例如,Wildfly),也不限制运行时支持的语言集合 (好比, Java、Python、Ruby),也不只仅迎合 12 因子应用程序,也不区分 应用 与 服务 。Kubernetes 旨在支持尽量多种类的工做负载,包括无状态的、有状态的和处理数据的工做负载。若是某程序在容器内运行良好,它在 Kubernetes 上只可能运行地更好。

  • 不提供中间件(例如消息总线)、数据处理框架(例如 Spark)、数据库(例如 mysql),也不把集群存储系统(例如 Ceph)做为内置服务。可是以上程序均可以在 Kubernetes 上运行。

  • 没有“点击即部署”这类的服务市场存在。

  • 不部署源代码,也不编译程序。持续集成 (CI) 工做流程是不一样的用户和项目拥有其各自不一样的需求和表现的地方。因此,Kubernetes 支持分层 CI 工做流程,却并不监听每层的工做状态。

  • 容许用户自行选择日志、监控、预警系统。( Kubernetes 提供一些集成工具以保证这一律念获得执行)

  • 不提供也无论理一套完整的应用程序配置语言/系统(例如 jsonnet)。

  • 不提供也不配合任何完整的机器配置、维护、管理、自我修复系统。

另外一方面,大量的 PaaS 系统运行在 Kubernetes 上,诸如 Openshift、Deis,以及 Eldarion。你也能够开发你的自定义PaaS,整合上你自选的CI系统,或者只在 Kubernetes 上部署容器镜像。

由于 Kubernetes 运营在应用程序层面而不是在硬件层面,它提供了一些 PaaS 所一般提供的常见的适用功能,好比部署、伸缩、负载平衡、日志和监控。然而,Kubernetes 并不是铁板一块,这些默认的解决方案是可供选择,可自行增长或删除的。

 

Kubernetes scheduler将始终确保每一个应用程序组件都是通过健康检查的,提供高可用的服务,当副本数量设置为多个时,每一个实例在多个主机上进行调度,而且若是其中一个主机变为不可用时,全部在该台主机上运行的容器都会被调度到剩余的其余主机。Kubernetes提供的一项迷人功能是两级自动扩缩。首先,经过使用一个叫作Horizontal Pod Autoscaler(Pod自动水平扩缩器)的资源,它能够为用户提供容器的自动扩缩功能,该资源将会监视资源的消耗并相应地扩展所需的容器数量。其次,它能够根据资源需求添加或删除主机来扩展容器集群自己。 此外,随着集群联盟(cluster federation)功能的引入,它甚至能够使用单个API端点管理一组Kubernetes集群,这些集群甚至可能跨越多个数据中心。

                                                 应用部署模型

上图展现的是Kubernetes宏观层面的应用程序部署模型。它使用一个叫作ReplicaSet的资源来编排容器。将ReplicaSet视为基于YAML或基于JSON的一个元数据文件,该文件里定义了容器镜像,端口,副本数,激活运行情况检查(health check),存活情况检查(liveness health check),环境变量,挂载卷以及建立和管理容器所需的一些安全规则。在Kubernetes上,容器老是以所谓Pods的形式成组建立,它是Kubernetes的一个元数据定义或者说是一个资源。每一个pod容许在使用了Linux namespace,cgroup和其余内核功能的容器之间共享文件系统,网络接口和操做系统用户。ReplicaSet能够由另一个名为Deployments的高级资源管理,它被设计用于提供滚动更新和处理回滚等功能。

经过执行下面这样一条简单的CLI命令,用户即可以经过定义一个deployment,在Kubernetes上部署一个容器化的应用程序:

 

kubectl run <application-name> --image=<container-image> --port=<port-no>

 

执行上述CLI命令后,它将使用给定的容器镜像建立一个Deployment声明,一个副本集以及一个采用了指定容器镜像的Pod;添加一个应用名的选择器标签。根据当前的设计,由此建立的每一个pod将会拥有两个容器,一个用于指定的应用程序组件,另一个即所谓的pause容器,用于链接网络接口。

                                           服务发现 & 负载均衡

Kubernetes的一个关键特性即是由SkyDNS和4层虚拟IP路由系统提供的服务发现和内部路由模型。这些功能能够为面向service的应用请求提供内部路由。能够使用集群网络里的service对经过副本集建立的一组pod提供负载平衡。service使用选择器标签链接到pod。每一个service都将会分配一个惟一的IP地址,一个由它的名称派生出的主机名,而且将会以round robin的方式路由pods的请求。这些service甚至能够为可能须要支持会话亲和性的应用程序提供基于IP哈希的路由机制。service能够指定一组端口,而针对指定的service定义的一系列属性将会以相同的形式一样应用到全部端口上。所以,若是只是某个给定端口须要支持会话亲和性,而全部的其余端口只须要round robin的方式路由的状况下,可能须要用到多个service。

service内部工做原理

Kubernetes service背后是经过一个叫作kube-proxy的组件实现。kube-proxy实例运行在每一个节点上,并提供了三种代理模式userspace,iptables和IPVS。目前的默认值是iptables

 

在第一种代理模式下,userspace,kube-proxy自己将充当代理服务器的角色,而且将被一条iptable规则接受的请求代理到后端pod。在这种模式下,kube-proxy将在用户空间中运行,而且将会在消息流上额外增长一跳(hop)。在iptables中,kube-proxy将建立一个iptable规则集合,用于未来自客户端的入口请求在网络层面直接转发到后端pod的端口上,而不会在中间额外增长一跳。这种代理模式比第一种模式快得多,由于它是在内核空间中操做而不是在中间增长一台额外的代理服务器。

 

第三种代理模式是在Kubernetes v1.8中添加的,它和第二种代理模式很是类似,该模式使用的是基于IPVS的虚拟服务器来路由请求,而无需用到iptable规则。IPVS是一个传输层的负载均衡功能,能够在基于Netfilter的Linux内核中使用,而且提供了一组负载均衡算法。在iptables上使用IPVS的主要缘由是在使用iptables时同步代理规则带来的性能开销。当建立数千个service时,更新iptable规则须要至关长的时间,相比之下,使用IPVS只需几毫秒。此外,IPVS使用哈希表线性的扫描iptables来查找代理规则。有关介绍IPVS代理模式的更多信息,请参阅华为在KubeCon 2017上的“Scaling Kubernetes to Support 50,000 Services”[1]演示文稿。

                                                      内外路由分离

Kubernetes service可以经过两种主流方式暴露给外部网络。第一种是使用节点端口(node port),经过暴露节点上的动态端口,这些端口会将流量转发到service端口。第二种是使用一个ingress controller配置的负载均衡器,它将会链接到同一个覆盖网络并将请求代理给service。ingress controller是一个后台进程,它能够跑在容器里,该容器将会监听Kubernetes API,根据指定的一组ingress动态配置并从新加载给定的负载均衡器。一个ingress定义了一组基于service的主机名及上下文路径的路由规则。

 

一旦经过执行kubectl run命令,在Kubernetes上跑起来应用后,咱们能够经过一个负载均衡器将其暴露给外部网络,以下所示:

 

kubectl expose deployment <application-name> --type=LoadBalancer --name=<service-name>

 

上述命令将建立一个负载均衡器类型的service,而且使用在POD建立之初创建的选择器标签映射到相同的一组pod。这样一来,根据Kubernetes集群不一样的配置方式,一个负载均衡器类型的service将会在底层基础设施上建立出来,经过service或直接路由的形式将请求转发到指定的一组pod。

                                持久卷用法

须要在文件系统上持久化存储数据的应用程序能够使用卷将存储设备挂载到临时容器,这和虚拟机的使用方式相似。Kubernetes巧妙地从新设计了这一律念,经过引入一个所谓的持久卷声明(PVC)的中间资源,在物理存储设备和容器之间作了解耦。一个PVC定义了磁盘大小,磁盘类型(ReadWriteOnce,ReadOnlyMany,ReadWriteMany),并将存储设备动态连接到在pod中定义了的卷。该绑定过程能够使用PV这样一个静态的形式,也能够使用一个持久化存储的provider动态实现。在这两种方法里,卷将一对一地连接到PV,而且取决于其配置,给定的数据即使pod被终止也将会被保留。根据使用的磁盘类型,多个pod将可以链接到同一磁盘并进行读/写。

 

支持ReadWriteOnce的磁盘将只能链接到单个pod,而且没法同时在多个pod之间共享。可是,支持ReadOnlyMany的磁盘将可以在只读模式下同时在多个pod之间共享。相反地,顾名思义,支持ReadWriteMany的磁盘能够链接到多个pod,以便在读写模式下共享数据。 Kubernetes提供了一系列的卷插件,用于支持公有云平台上可用的存储服务,例如AWS EBS,GCE Peristent Disk,Azure File,Azure Disk和许多其余众所周知的存储系统,如NFS,Glusterfs,Cinder等。

                                       在节点上部署守护程序

Kubernetes提供了一个名为DaemonSets的资源,用于在每一个Kubernetes节点上以守护进程的形式运行pod的副本。DaemonSets的一些用例以下:

 

  • 须要部署到每一个节点上提供持久化存储的集群存储守护程序(如glusterd,ceph)。

  • 须要在每一个节点上运行的监控容器宿主机的节点监控守护程序,例如Prometheus Node Exporter。

  • 须要在每一个节点上运行的,用做采集容器及Kubernetes组件日志的日志采集守护程序,例如fluentd或是logstash。

  • 须要在一组节点上运行的提供外部路由的ingress controll pod。

 

                                     部署有状态的分布式系统

容器化应用程序最艰巨的任务之一莫过于设计有状态分布式组件部署架构的流程。无状态组件能够很容易地进行容器化,由于它们可能没有预约的启动顺序,集群要求,点对点的TCP链接,惟一的网络标识符,优雅的启动和终止需求等。像数据库,大数据分析系统,分布式k/v存储以及消息代理这样的系统,可能拥有须要支持上述这些功能的复杂分布式架构。Kubernetes引入了StatefulSets资源来解决这些复杂的需求。

 

从总体上来讲,StatefulSets相似于ReplicaSet,除了提供处理pod的启动顺序的能力,惟一地标识每一个pod以保留其状态以外,它还同时提供如下特性:

 

  • 稳定,惟一的网络标识符。

  • 稳定,持久化的存储。

  • 有序,优雅的部署和扩容。

  • 有序,优雅的删除和终止。

  • 有序,自动地滚动更新。

 

这里面的“稳定”指的是在跨pod从新调度时它将会保留网络标识符和持久化存储。如上图所示,惟一的网络标识符能够经过使用headless service提供。Kubernetes提供了一些StatefulSets的示例,包括以分布式的形式部署Cassandra以及Zookeeper。

                                               执行后台任务

除了ReplicaSet和StatefulSets以外,Kubernetes还提供了两个额外的控制器,用于在后台运行称为Jobs和CronJobs的工做负载。Jobs和CronJobs之间的区别在于Jobs执行一次即终止,而CronJobs会按照与标准Linux cron做业相似的给定时间间隔按期地执行。

 

                                           部署数据库

 

因为存在对集群,点对点链接,复制同步,灰度,备份管理等需求,在容器平台上部署数据库用于生产环境将会比部署应用程序稍微困难一些。正如以前所提到的那样,Statefulsets专门为解决此类复杂需求而设计,而现在已经有一些在Kubernetes上运行PostgreSQL和MongoDB集群的方案。YouTube的数据库集群系统Vitess现现在已是一个CNCF项目,对于在Kubernetes上大规模灰度运行MySQL是一个很好的选择。说是如此,咱们最好注意一下,这些方案目前仍然处于很是早期的开发阶段,而若是现有的生产级别数据库系统,仍然可用于给定的基础架构,例如AWS上的RDS,GCP上的Cloud SQL,或是内部按需部署的数据库集群,考虑到安装的复杂性以及维护成本,选择这其中的一种方案可能更合适些。

 

                                                  配置管理

 

容器一般使用环境变量来参数化它们的运行时配置。可是,常见的企业应用程序每每使用大量的配置文件为一个指定的部署提供所需的静态配置。Kubernetes则提供了一个绝妙的办法,使用名为ConfigMaps的一种简单资源来管理此类配置文件,而无需将它们打包到容器镜像里。能够经过执行如下CLI命令,使用目录,文件或文本值建立ConfigMaps:

 

kubectl create configmap <map-name> <data-source>
# map-name: name of the config map
# data-source: directory, file or literal value

 

建立ConfigMap后,能够经过卷的形式将其挂载到pod。经过这种松耦合的架构,一个已经在运行的系统的配置能够经过更新相关的ConfigMaps的方式无缝更新,而其滚动更新的执行流程这块我将在下一节中详细说明。值得一提的是,ConfigMaps如今不支持嵌套的目录结构;所以,若是应用程序的嵌套目录结构中存放有配置文件的话,则须要为每个目录层级建立一个ConfigMap。

 

                                                            凭证管理

 

与ConfigMaps相似,Kubernetes提供了另外一种名为Secrets的宝贵资源,用于管理密码,OAuth令牌和ssh密钥等敏感信息。不然,在已运行的系统上更新该信息可能须要重建容器镜像。

能够使用如下方式建立用于管理基自己份验证凭据的密钥:

 

# write credentials to two files
$ echo -n 'admin' > ./username.txt
$ echo -n '1f2d1e2e67df' > ./password.txt
# create a secret
$ kubectl create secret generic app-credentials --from-file=./username.txt --from-file=./password.txt

 

建立secret后,pod能够使用环境变量或挂载卷的方式来读取它。相似地,能够使用相同的方法将任何其余类型的敏感信息注入到pod中。

 

                                                    滚动更新

上面这个动画描述了如何使用蓝/绿部署的方法为已经运行的应用程序发布应用程序更新,而无需任何宕机成本。这是Kubernetes提供的另外一个重磅功能,它容许应用程序不费吹灰之力便可无缝地发布安全更新和向后兼容的变动。若是变动不向后兼容,则可能须要使用单独的部署定义手动执行蓝/绿部署。

 

这一方案容许经过一条简单的CLI命令,发起一个部署以更新容器镜像:

 

$ kubectl set image deployment/<application-name> <container-name>=<container-image-name>:<new-version>

一旦发起部署,能够经过以下方式检查部署进度的状态:

 

$ kubectl rollout status deployment/<application-name>Waiting for rollout to finish: 2 out of 3 new replicas have been updated...deployment "<application-name>" successfully rolled out

使用相同的CLI命令kubectl set image deployment,可让部署更新回滚到以前的状态。

                                           自动扩缩

Kubernetes容许使用ReplicaSet或Deployments手动调整pod数量。这能够经过执行以下CLI命令来实现:

 

kubectl scale --replicas=<desired-instance-count> deployment/<application-name>

 

如上图所示,能够经过向Deployment添加另外一个名为Horizontal Pod Autoscaler(HPA)的资源来扩展此功能,以便根据实际资源使用状况动态扩缩容器。HPA将经过资源指标的API监视每一个pod的资源使用状况,并通知Deployment相应地更改ReplicaSet的副本数。Kubernetes使用高档延迟(upscale delay)和缩减延迟(downscale delay)来避免某些状况下频繁的资源使用波动而可能致使的颠簸。目前,HPA仅支持基于CPU的使用状况进行扩展。若是有必要的话,还能够经过Custom Metrics API加入自定义指标,这具体视应用程序的天然属性而定。

 

                                      包管理

Kubernetes社区发起了一个单独的项目,为Kubernetes实现了一个称为Helm的包管理器。它容许用户使用一个名为Chart的资源模板化并打包Kubernetes资源,好比Deployment,Service,ConfigMap,Ingress等,并容许在安装时使用输入参数配置它们。更重要的是,它容许在使用依赖项实现包的安装时复用现有图表。Helm存储库能够托管在公有云或私有云环境中,用于管理应用程序的Chart。Helm提供了一个CLI,用于从给定的Helm存储库里安装应用程序到指定的Kubernetes环境中。

 

一些众所周知的软件应用程序的各类稳定Helm图表能够在它的Github存储库[2]中找到,也能够在中心化的Helm服务器中找到:Kubeapps Hub[3]。

 

下图为开源自动化运维体系

链:

    一、cobbler实现自动装机

    二、saltstack实现工程自动化配置

    三、kubernetes实现容器自动化编排

    四、zabbix实现自动化监控

    五、elastic实现应用日志自动化收集

    六、jenkins实现开发持续化交付

1、Kubernetes

      2014年出现的kubernetes(又叫k8s)更加煊赫一时。为啥kubernetes又叫作k8s?听说是由于kubernetes这个单词太长,很差记,而首字母和尾字母中间有8个字母,因此就简写成了k8s。Kubernetes翻译成中文,意思是“舵手”。

       K8s简单说,是用来实现容器集群管理的系统,用于自动部署、扩展和管理容器。它是由Google公司开发,其原型为Google内部容器管理系统Borg。

       Borg通过十多年地优化、改进,其功能和效率不言而喻,k8s项目的目的就是把Borg最精华的部分提取出来,使如今的开发者可以更简单、直接地应用。K8s自诞生之日起就注定了它的不平凡之路。

几个概念:

node 一个物理机器,或一个虚拟机(KVM类型,而不是容器类型);将虚拟机做为node通常是历史缘由,或是为了完全隔绝杜绝安全问题。

cluster 一组node须要被集中管理,统一叫一个cluster。一个cluster有一个master和多个node。每一个node里面有一个kubelet用来服从master调度并管理node自己。

app containers 一个node里面能够有一个或多个容器化的应用程序,即app container。能够简单认为就是docker容器。

pod 多个app containers之间可能须要共享硬盘,或共享同一个ip,这样一组app containers合起来叫一个pod。典型应用如:一个容器不停产生日志到本地硬盘,另外一个容器不停读本地硬盘并上传日志到日志服务器。

service 当一个node挂了时,上面的pod及pod里面的container也天然都挂了。为了死不掉,须要有个pod上层的抽象,pod挂了,service还在。service经过以下几种方式暴露出来。

  • ClusterIP (default),cluser的内网ip,只能此cluster内可见

  • NodePort,端口NAT到cluster外面

  • LoadBalancer,在cluster外面搞个LB并分配个外面可见的固定IP给LB

  • ExternalName,相似CNAME方式

deployment 配置yaml格式,存在master上,当机器故障或须要横向scale时或须要更新binary时,master根据配置搞定一切。

2、安装

entos 7.2版本,咱们将master和node安装在一台机器上测试,如不一样机器上,注意IP配置便可。

 

ip:192.168.1.101

 

master安装:

yum -y install kubernetes-master etcd 

 

node安装:

 yum -y install kubernetes-node etcd flannel docker

 

配置:

more /etc/etcd/etcd.conf

ETCD_DATA_DIR="/var/lib/etcd/default.etcd"  

ETCD_LISTEN_PEER_URLS="http://192.168.1.101:2380"  

ETCD_LISTEN_CLIENT_URLS="http://192.168.1.101:2379,http://127.0.0.1:2379"  

ETCD_MAX_SNAPSHOTS="5"  

ETCD_NAME="etcd1"  

ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.1.101:2380"  

ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.101:2379"  

ETCD_INITIAL_CLUSTER="etcd1=http://192.168.1.101:2380"  

 

more /etc/kubernetes/apiserver

KUBE_API_ADDRESS="--address=0.0.0.0" 

KUBE_API_PORT="--port=8080"

KUBELET_PORT="--kubelet-port=10250"

KUBE_ETCD_SERVERS="--etcd-servers=http://127.0.0.1:2379"

KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=192.168.0.0/16"

KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,ResourceQuota"  

KUBE_API_ARGS=""

 

more /etc/kubernetes/config

KUBE_LOGTOSTDERR="--logtostderr=true"   

KUBE_LOG_LEVEL="--v=0"  

KUBE_ALLOW_PRIV="--allow-privileged=false"  

KUBE_MASTER="--master=http://192.168.1.101:8080" 

 

3、启动

 

master服务启动:

systemctl start etcd.service

systemctl start kube-apiserver  

systemctl start kube-controller-manager  

systemctl start kube-scheduler

systemctl enable etcd.service  

systemctl enable kube-apiserver  

systemctl enable kube-controller-manager  

systemctl enable kube-scheduler

 

node服务启动:

systemctl start kubelet

systemctl start kube-proxy

systemctl enable kubelet

systemctl enable kube-proxy

 

4、建立服务测试

 

咱们使用yaml配置一个nginx的服务进行测试。

 

docker 下载两个镜像

docker pull registry.access.redhat.com/rhel7/pod-infrastructure:latest

docker pull  nginx 

 

more  nginx.yaml

kind: Pod

metadata:

  name: nginx

  labels:

     app: nginx    

spec:

     containers:

        - name: nginx

          image: nginx

          imagePullPolicy: IfNotPresent

          ports:

          - containerPort: 80

     restartPolicy: Always

---

apiVersion: v1

kind: Service

metadata:

  name: nginx-service

spec:

  type: NodePort

  sessionAffinity: ClientIP

  selector:

    app: nginx

  ports:

    - port: 80

      nodePort: 30080

 

建立一个服务

kubectl create -f nginx.yaml 


打开web输入http://192.168.1.101:30080

 

Kubernetes 资源管理概述

Kubernetes 从建立之初的核心模块之一就是资源调度。想要在生产环境使用好 Kubernetes,必须对它的资源模型以及资源管理很是了解。

Kubernetes 资源简介

什么是资源?

在 Kubernetes 中,有两个基础可是很是重要的概念:Node 和 Pod。Node 翻译成节点,是对集群资源的抽象;Pod 是对容器的封装是应用运行的实体。Node 提供资源,而 Pod 使用资源,这里的资源分为计算(CPU、Memory、GPU)、存储(Disk、SSD)、网络(Network Bandwidth、IP、Ports)。这些资源提供了应用运行的基础,正确理解这些资源以及集群调度如何使用这些资源,对于大规模的 Kubernetes 集群来讲相当重要,不只能保证应用的稳定性,也能够提升资源的利用率。

CPU 分配的是使用时间,也就是操做系统管理的时间片,每一个进程在必定的时间片里运行本身的任务(另一种方式是绑核,也就是把 CPU 彻底分配给某个 Pod 使用,但这种方式不够灵活会形成严重的资源浪费,Kubernetes 中并无提供);而对于内存,系统提供的是内存大小。

CPU 的使用时间是可压缩的,换句话说它自己无状态,申请资源很快,也能快速正常回收;而内存大小是不可压缩的,由于它是有状态的(内存里面保存的数据),申请资源很慢(须要计算和分配内存块的空间),而且回收可能失败(被占用的内存通常不可回收)。

把资源分红可压缩不可压缩,是由于在资源不足的时候,它们的表现很不同。对于不可压缩资源,若是资源不足,也就没法继续申请资源(内存用完就是用完了),而且会致使 Pod 的运行产生没法预测的错误(应用申请内存失败会致使一系列问题);而对于可压缩资源,好比 CPU 时间片,即便 Pod 使用的 CPU 资源不少,CPU 使用也能够按照权重分配给全部 Pod 使用,虽然每一个人使用的时间片减小,但不会影响程序的逻辑。

在 Kubernetes 集群管理中,有一个很是核心的功能:就是为 Pod 选择一个主机运行。调度必须知足必定的条件,其中最基本的是主机上要有足够的资源给 Pod 使用。

资源除了和调度相关以外,还和不少事情紧密相连

Kubernetes 资源的表示

用户在 Pod 中能够配置要使用的资源总量,Kubernetes 根据配置的资源数进行调度和运行。目前主要能够配置的资源是 CPU 和 Memory,对应的配置字段是 spec.containers[].resource.limits/request.cpu/memory

须要注意的是,用户是对每一个容器配置 Request 值,全部容器的资源请求之和就是 Pod 的资源请求总量,而咱们通常会说 Pod 的资源请求和 Limits。

Limits 和 Requests 的区别咱们下面会提到,这里先说说比较容易理解的 CPU 和 Memory。

CPU 通常用核数来标识,一核 CPU 相对于物理服务器的一个超线程核,也就是操做系统 /proc/cpuinfo 中列出来的核数。由于对资源进行了池化和虚拟化,所以 Kubernetes 容许配置非整数个的核数,好比 0.5 是合法的,它标识应用能够使用半个 CPU 核的计算量。CPU 的请求有两种方式,一种是刚提到的 0.51 这种直接用数字标识 CPU 核心数;另一种表示是 500m,它等价于 0.5,也就是说 1 Core = 1000m

内存比较容易理解,是经过字节大小指定的。若是直接一个数字,后面没有任何单位,表示这么多字节的内存;数字后面还能够跟着单位, 支持的单位有 EPTGMK,前者分别是后者的 1000 倍大小的关系,此外还支持  EiPiTiGiMiKi,其对应的倍数关系是 2^10 = 1024。好比要使用 100M 内存的话,直接写成 100Mi便可。

节点可用资源

理想状况下,咱们但愿节点上全部的资源均可以分配给 Pod 使用,但实际上节点上除了运行 Pods 以外,还会运行其余的不少进程:系统相关的进程(好比 SSHD、Udev等),以及 Kubernetes 集群的组件(Kubelet、Docker等)。咱们在分配资源的时候,须要给这些进程预留一些资源,剩下的才能给 Pod 使用。预留的资源能够经过下面的参数控制:

  • --kube-reserved=[cpu=100m][,][memory=100Mi][,][ephemeral-storage=1Gi]:控制预留给 Kubernetes 集群组件的 CPU、Memory 和存储资源。

  • --system-reserved=[cpu=100mi][,][memory=100Mi][,][ephemeral-storage=1Gi]:预留给系统的 CPU、Memory 和存储资源。


这两块预留以后的资源才是 Pod 真正能使用的,不过考虑到 Eviction 机制(下面的章节会提到),Kubelet 会保证节点上的资源使用率不会真正到 100%,所以 Pod 的实际可以使用资源会稍微再少一点。主机上的资源逻辑分配图以下所示:

NOTE:须要注意的是,Allocatable 不是指当前机器上能够分配的资源,而是指能分配给 Pod 使用的资源总量,一旦 Kubelet 启动这个值是不会变化的。

Allocatable 的值能够在 Node 对象的 status 字段中读取,好比下面这样:

status:
  allocatable:
    cpu: "2"
    ephemeral-storage: "35730597829"
    hugepages-2Mi: "0"
    memory: 3779348Ki
    Pods: "110"
  capacity:
    cpu: "2"
    ephemeral-storage: 38770180Ki
    hugepages-2Mi: "0"
    memory: 3881748Ki
    Pods: "110"

Kubernetes 资源对象

介绍 Kubernetes 中提供的让咱们管理 Pod 资源的原生对象。

请求(Requests)和上限(Limits)

前面说过用户在建立 Pod 的时候,能够指定每一个容器的 Requests 和 Limits 两个字段,下面是一个实例:

resources:
  requests:
    memory: "64Mi"
    cpu: "250m"
  limits:
    memory: "128Mi"
    cpu: "500m"

Requests 是容器请求要使用的资源,Kubernetes 会保证 Pod 能使用到这么多的资源。请求的资源是调度的依据,只有当节点上的可用资源大于 Pod 请求的各类资源时,调度器才会把 Pod 调度到该节点上(若是 CPU 资源足够,内存资源不足,调度器也不会选择该节点)。

注意,调度器关心节点上可分配的资源以及节点上全部 Pods 请求的资源,而不关心节点资源的实际使用状况,换句话说,若是节点上的 Pods 申请的资源已经把节点上的资源用满,即便它们的使用率很是低,好比说 CPU 和内存使用率都低于 10%,调度器也不会继续调度 Pod 上去。

Limits 是 Pod 能使用的资源上限,是实际配置到内核 cgroups 里面的配置数据。对于内存来讲,会直接转换成 docker run 命令行的 --memory 大小,最终会配置到 cgroups 对应任务的 /sys/fs/cgroup/memory/……/memory.limit_in_bytes 文件中。

NOTE:若是 Limit 没有配置,则代表没有资源的上限,只要节点上有对应的资源,Pod 就能够使用。

使用 Requests 和 Limits 概念,咱们能分配更多的 Pod,提高总体的资源使用率。可是这个体系有个很是重要的问题须要考虑,那就是怎么去准确地评估 Pod 的资源 Requests?若是评估地太低,会致使应用不稳定;若是太高,则会致使使用率下降。这个问题须要开发者和系统管理员共同讨论和定义。

Limit Range(默认资源配置)

为每一个 Pod 都手动配置这些参数是挺麻烦的事情,Kubernetes 提供了 LimitRange 资源,可让咱们配置某个 Namespace 默认的 Request 和 Limit 值,好比下面的实例:

apiVersion: "v1"
kind: "LimitRange"
metadata:
  name: you-shall-have-limits
spec:
  limits:
    - type: "Container"
      max:
        cpu: "2"
        memory: "1Gi"
      min:
        cpu: "100m"
        memory: "4Mi"
      default:
        cpu: "500m"
        memory: "200Mi"
      defaultRequest:
        cpu: "200m"
        memory: "100Mi"

若是对应 Namespace 建立的 Pod 没有写资源的 Requests 和 Limits 字段,那么它会自动拥有下面的配置信息:

  • 内存请求是 100Mi,上限是 200Mi

  • CPU 请求是 200m,上限是 500m


固然,若是 Pod 本身配置了对应的参数,Kubernetes 会使用 Pod 中的配置。使用 LimitRange 可以让 Namespace 中的 Pod 资源规范化,便于统一的资源管理。

资源配额(Resource Quota)

资源管理和调度能够认为 Kubernetes 把这个集群的资源整合起来,组成一个资源池,每一个应用(Pod)会自动从整个池中分配资源来使用。默认状况下只要集群还有可用的资源,应用就能使用,并无限制。Kubernetes 自己考虑到了多用户和多租户的场景,提出了 Namespace 的概念来对集群作一个简单的隔离。

基于 Namespace,Kubernetes 还可以对资源进行隔离和限制,这就是 Resource Quota 的概念,翻译成资源配额,它限制了某个 Namespace 能够使用的资源总额度。这里的资源包括 CPU、Memory 的总量,也包括 Kubernetes 自身对象(好比 Pod、Services 等)的数量。经过 Resource Quota,Kubernetes 能够防止某个 Namespace 下的用户不加限制地使用超过时望的资源,好比说不对资源进行评估就大量申请 16核 CPU 32G 内存的 Pod。

下面是一个资源配额的实例,它限制了 Namespace 只能使用 20 核 CPU 和 1G 内存,而且能建立 10 个 Pod、20 个 RC、5 个 Service,可能适用于某个测试场景。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota
spec:
  hard:
    cpu: "20"
    memory: 1Gi
    Pods: "10"
    replicationcontrollers: "20"
    resourcequotas: "1"
    services: "5"

Resource Quota 可以配置的选项还不少,好比 GPU、存储、Configmaps、PersistentVolumeClaims 等等,更多信息能够参考官方文档。

Resource Quota 要解决的问题和使用都相对独立和简单,可是它也有一个限制:那就是它不能根据集群资源动态伸缩。一旦配置以后,Resource Quota 就不会改变,即便集群增长了节点,总体资源增多也没有用。Kubernetes 如今没有解决这个问题,可是用户能够经过编写一个 Controller 的方式来本身实现。

应用优先级

QoS(服务质量)

Requests 和 Limits 的配置除了代表资源状况和限制资源使用以外,还有一个隐藏的做用:它决定了 Pod 的 QoS 等级

上一节咱们提到了一个细节:若是 Pod 没有配置 Limits ,那么它能够使用节点上任意多的可用资源。这类 Pod 能灵活使用资源,但这也致使它不稳定且危险,对于这类 Pod 咱们必定要在它占用过多资源致使节点资源紧张时处理掉。优先处理这类 Pod,而不是处理资源使用处于本身请求范围内的 Pod 是很是合理的想法,而这就是 Pod QoS 的含义:根据 Pod 的资源请求把 Pod 分红不一样的重要性等级。

Kubernetes 把 Pod 分红了三个 QoS 等级:

  • Guaranteed:优先级最高,能够考虑数据库应用或者一些重要的业务应用。除非 Pods 使用超过了它们的 Limits,或者节点的内存压力很大并且没有 QoS 更低的 Pod,不然不会被杀死。

  • Burstable:这种类型的 Pod 能够多于本身请求的资源(上限由 Limit 指定,若是 Limit 没有配置,则能够使用主机的任意可用资源),可是重要性认为比较低,能够是通常性的应用或者批处理任务。

  • Best Effort:优先级最低,集群不知道 Pod 的资源请求状况,调度不考虑资源,能够运行到任意节点上(从资源角度来讲),能够是一些临时性的不重要应用。Pod 能够使用节点上任何可用资源,但在资源不足时也会被优先杀死。

Pod 的 Requests 和 Limits 是如何对应到这三个 QoS 等级上的,用下面一张表格归纳:

问题:若是不配置 Requests 和 Limits,Pod 的 QoS 居然是最低的。没错,因此推荐你们理解 QoS 的概念,而且按照需求必定要给 Pod 配置 Requests 和 Limits 参数,不只可让调度更准确,也能让系统更加稳定。

NOTE:按照如今的方法根据 Pod 请求的资源进行配置不够灵活和直观,更理想的状况是用户能够直接配置 Pod 的 QoS,而不用关心具体的资源申请和上限值。但 Kubernetes 目前尚未这方面的打算。

Pod 的 QoS 还决定了容器的 OOM(Out-Of-Memory)值,它们对应的关系以下:

QoS 越高Pod OOM 值越低,也就越不容易被系统杀死。对于 Bustable Pod,它的是根据 Request 和节点内存总量共同决定的:

oomScoreAdjust := 1000 - (1000*memoryRequest)/memoryCapacity

其中 memoryRequest 是 Pod 申请的资源,memoryCapacity 是节点的内存总量。能够看到,申请的内存越多,OOM 值越低,也就越不容易被杀死。

QoS 的做用会在后面介绍 Eviction 的时候详细讲解。

Pod 优先级(Priority)

除了 QoS,Kubernetes 还容许咱们自定义 Pod 的优先级,好比:

apiVersion: scheduling.k8s.io/v1alpha1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service Pods only."

优先级的使用也比较简单,只须要在 Pod.spec.PriorityClassName 指定要使用的优先级名字,便可以设置当前 Pod 的优先级为对应的值。

     Pod 的优先级在调度的时候会使用到。首先,待调度的 Pod 都在同一个队列中,启用了 Pod priority 以后,调度器会根据优先级的大小,把优先级高的 Pod 放在前面,提早调度。

     若是在调度的时候,发现某个 Pod 由于资源不足没法找到合适的节点,调度器会尝试 Preempt 的逻辑。简单来讲,调度器会试图找到这样一个节点:找到它上面优先级低于当前要调度 Pod 的全部 Pod,若是杀死它们,能腾足够的资源,调度器会执行删除操做,把 Pod 调度到节点上。更多内容能够参考:Pod Priority and Preemption - Kubernetes。

驱逐(Eviction)

     讲述的都是理想状况下 Kubernetes 的工做情况,咱们假设资源彻底够用,并且应用也都是在使用规定范围内的资源。

     在管理集群的时候咱们经常会遇到资源不足的状况,在这种状况下咱们要保证整个集群可用,而且尽量减小应用的损失。保证集群可用比较容易理解,首先要保证系统层面的核心进程正常,其次要保证 Kubernetes 自己组件进程不出问题;可是如何量化应用的损失呢?首先能想到的是若是要杀死 Pod,要尽可能减小总数。另一个就和 Pod 的优先级相关了,那就是尽可能杀死不那么重要的应用,让重要的应用不受影响。

Pod 的驱逐是在 Kubelet 中实现的,由于 Kubelet 能动态地感知到节点上资源使用率实时的变化状况。其核心的逻辑是:Kubelet 实时监控节点上各类资源的使用状况,一旦发现某个不可压缩资源出现要耗尽的状况,就会主动终止节点上的 Pod,让节点可以正常运行。被终止的 Pod 全部容器会中止,状态会被设置为 Failed。

驱逐触发条件

目前主要有三种状况:实际内存不足、节点文件系统的可用空间(文件系统剩余大小和 Inode 数量)不足、以及镜像文件系统的可用空间(包括文件系统剩余大小和 Inode 数量)不足。

下面这图是具体的触发条件:

有了数据的来源,另一个问题是触发的时机,也就是到什么程度须要触发驱逐程序?Kubernetes 运行用户本身配置,而且支持两种模式:按照百分比和按照绝对数量。好比对于一个 32G 内存的节点当可用内存少于 10% 时启动驱逐程序,能够配置 memory.available<10%或者 memory.available<3.2Gi

NOTE:默认状况下,Kubelet 的驱逐规则是 memory.available<100Mi,对于生产环境这个配置是不可接受的,因此必定要根据实际状况进行修改。

软驱逐(Soft Eviction)和硬驱逐(Hard Eviction)

由于驱逐 Pod 是具备毁坏性的行为,所以必需要谨慎。有时候内存使用率增高只是暂时性的,有可能 20s 内就能恢复,这时候启动驱逐程序意义不大,并且可能会致使应用的不稳定,咱们要考虑到这种状况应该如何处理;另外须要注意的是,若是内存使用率太高,好比高于 95%(或者 90%,取决于主机内存大小和应用对稳定性的要求),那么咱们不该该再多作评估和考虑,而是赶忙启动驱逐程序,由于这种状况再花费时间去判断可能会致使内存继续增加,系统彻底崩溃。

为了解决这个问题,Kubernetes 引入了 Soft Eviction 和 Hard Eviction 的概念。

软驱逐能够在资源紧缺状况并没有哪些严重的时候触发,好比内存使用率为 85%,软驱逐还须要配置一个时间指定软驱逐条件持续多久才触发,也就是说 Kubelet 在发现资源使用率达到设定的阈值以后,并不会当即触发驱逐程序,而是继续观察一段时间,若是资源使用率高于阈值的状况持续必定时间,才开始驱逐。而且驱逐 Pod 的时候,会遵循 Grace Period ,等待 Pod 处理完清理逻辑。和软驱逐相关的启动参数是:

  • --eviction-soft:软驱逐触发条件,好比 memory.available<1Gi

  • --eviction-sfot-grace-period:触发条件持续多久才开始驱逐,好比 memory.available=2m30s

  • --eviction-max-Pod-grace-period:Kill Pod 时等待 Grace Period 的时间让 Pod 作一些清理工做,若是到时间尚未结束就作 Kill。


前面两个参数必须同时配置,软驱逐才能正常工做;后一个参数会和 Pod 自己配置的 Grace Period 比较,选择较小的一个生效。

硬驱逐更加直接干脆,Kubelet 发现节点达到配置的硬驱逐阈值后,当即开始驱逐程序,而且不会遵循 Grace Period,也就是说当即强制杀死 Pod。对应的配置参数只有一个 --evictio-hard,能够选择上面表格中的任意条件搭配。

设置这两种驱逐程序是为了平衡节点稳定性和对 Pod 的影响,软驱逐照顾到了 Pod 的优雅退出,减小驱逐对 Pod 的影响;而硬驱逐则照顾到节点的稳定性,防止资源的快速消耗致使节点不可用。

软驱逐和硬驱逐能够单独配置,不过仍是推荐二者都进行配置,一块儿使用。

驱逐哪些 Pods?

上面已经总体介绍了 Kubelet 驱逐 Pod 的逻辑和过程。牵涉到一个具体的问题:要驱逐哪些 Pod?驱逐的重要原则是尽可能减小对应用程序的影响。

若是是存储资源不足,Kubelet 会根据状况清理状态为 Dead 的 Pod 和它的全部容器,以及清理全部没有使用的镜像。若是上述清理并无让节点回归正常,Kubelet 就开始清理 Pod。

一个节点上会运行多个 Pod,驱逐全部的 Pods 显然是没必要要的,所以要作出一个抉择:在节点上运行的全部 Pod 中选择一部分来驱逐。虽然这些 Pod 乍看起来没有区别,可是它们的地位是不同的,

系统组件的 Pod 要比普通的 Pod 更重要,另外运行数据库的 Pod 天然要比运行一个无状态应用的 Pod 更重要。Kubernetes 又是怎么决定 Pod 的优先级的呢?这个问题的答案就藏在咱们以前已经介绍过的内容里:Pod Requests 和 Limits、优先级(Priority),以及 Pod 实际的资源使用。

简单来讲,Kubelet 会根据如下内容对 Pod 进行排序:Pod 是否使用了超过请求的紧张资源、Pod 的优先级、而后是使用的紧缺资源和请求的紧张资源之间的比例。具体来讲,Kubelet 会按照以下的顺序驱逐 Pod:

  • 使用的紧张资源超过请求数量的 BestEffort 和 Burstable Pod,这些 Pod 内部又会按照优先级和使用比例进行排序。

  • 紧张资源使用量低于 Requests 的 Burstable 和 Guaranteed 的 Pod 后面才会驱逐,只有当系统组件(Kubelet、Docker、Journald 等)内存不够,而且没有上面 QoS 比较低的 Pod 时才会作。执行的时候还会根据 Priority 排序,优先选择优先级低的 Pod。

防止波动

波动有两种状况,第一种。驱逐条件出发后,若是 Kubelet 驱逐一部分 Pod,让资源使用率低于阈值就中止,那么极可能过一段时间资源使用率又会达到阈值,从而再次出发驱逐,如此循环往复……为了处理这种问题,咱们能够使用 --eviction-minimum-reclaim解决,这个参数配置每次驱逐至少清理出来多少资源才会中止。

另一个波动状况是这样的:Pod 被驱逐以后并不会今后消失不见,常见的状况是 Kubernetes 会自动生成一个新的 Pod 来取代,并通过调度选择一个节点继续运行。若是不作额外处理,有理由相信 Pod 选择原来节点的可能性比较大(由于调度逻辑没变,而它上次调度选择的就是该节点),之因此说可能而不是绝对会再次选择该节点,是由于集群 Pod 的运行和分布和上次调度时极有可能发生了变化。

     不管如何,若是被驱逐的 Pod 再次调度到原来的节点,极可能会再次触发驱逐程序,而后 Pod 再次被调度到当前节点,循环往复…… 这种事情固然是咱们不肯意看到的,虽然看似复杂,但这个问题解决起来很是简单:驱逐发生后,Kubelet 更新节点状态,调度器感知到这一状况,暂时不往该节点调度 Pod 便可。--eviction-pressure-transition-period 参数能够指定 Kubelet 多久才上报节点的状态,由于默认的上报状态周期比较短,频繁更改节点状态会致使驱逐波动。

     使用了上面多种参数的驱逐配置实例:

–eviction-soft=memory.available<80%,nodefs.available<2Gi \
–eviction-soft-grace-period=memory.available=1m30s,nodefs.available=1m30s \
–eviction-max-Pod-grace-period=120 \
–eviction-hard=memory.available<500Mi,nodefs.available<1Gi \
–eviction-pressure-transition-period=30s \
--eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"

碎片整理和重调度

Kubernetes 的调度器在为 Pod 选择运行节点的时候,只会考虑到调度那个时间点集群的状态,通过一系列的算法选择一个当时最合适的节点。可是集群的状态是不断变化的,用户建立的 Pod 也是动态的,随着时间变化,原来调度到某个节点上的 Pod 如今看来可能有更好的节点能够选择。好比考虑到下面这些状况:

  • 调度 Pod 的条件已经再也不知足,好比节点的 Taints 和 Labels 发生了变化。

  • 新节点加入了集群。若是默认配置了把 Pod 打散,那么应该有一些 Pod 最好运行在新节点上。

  • 节点的使用率不均匀。调度后,有些节点的分配率和使用率比较高,另一些比较低。

  • 节点上有资源碎片。有些节点调度以后还剩余部分资源,可是又低于任何 Pod 的请求资源;或者 Memory 资源已经用完,可是 CPU 还有挺多没有使用。


想要解决上述的这些问题,都须要把 Pod 从新进行调度(把 Pod 从当前节点移动到另一个节点)。可是默认状况下,一旦 Pod 被调度到节点上,除非给杀死不然不会移动到另一个节点的。

 Kubernetes 社区孵化了一个称为  Descheduler 的项目,专门用来作重调度。重调度的逻辑很简单:找到上面几种状况中已经不是最优的 Pod,把它们驱逐掉(Eviction)。

Descheduler 不会决定驱逐的 Pod 应该调度到哪台机器,而是假定默认的调度器会作出正确的调度抉择。也就是说,之因此 Pod 目前不合适,不是由于调度器的算法有问题,而是由于集群的状况发生了变化。若是让调度器从新选择,调度器如今会把 Pod 放到合适的节点上。这种作法让 Descheduler 逻辑比较简单,并且避免了调度逻辑出如今两个组件中。

Descheduler 执行的逻辑是能够配置的,目前有几种场景:

  • RemoveDuplicates:RS、Deployment 中的 Pod 不能同时出如今一台机器上。

  • LowNodeUtilization:找到资源使用率比较低的 Node,而后驱逐其余资源使用率比较高节点上的 Pod,指望调度器可以从新调度让资源更均衡。

  • RemovePodsViolatingInterPodAntiAffinity:找到已经违反 Pod Anti Affinity 规则的 Pods 进行驱逐,多是由于反亲和是后面加上去的。

  • RemovePodsViolatingNodeAffinity:找到违反 Node Affinity 规则的 Pods 进行驱逐,多是由于 Node 后面修改了 Label。


固然,为了保证应用的稳定性,Descheduler 并不会随意地驱逐 Pod,仍是会尊重 Pod 运行的规则,包括 Pod 的优先级(不会驱逐 Critical Pod,而且按照优先级顺序进行驱逐)和 PDB(若是违反了 PDB,则不会进行驱逐),而且不会驱逐没有 Deployment、RS、Jobs 的 Pod 不会驱逐,Daemonset Pod 不会驱逐,有 Local storage 的 Pod 也不会驱逐。

Descheduler 不是一个常驻的任务,每次执行完以后会退出,所以推荐使用 CronJob 来运行。

总的来讲,Descheduler 是对原生调度器的补充,用来解决原生调度器的调度决策随着时间会变得失效,或者不够优化的缺陷。

资源动态调整

动态调整的思路:应用的实际流量会不断变化,所以使用率也是不断变化的,为了应对应用流量的变化,咱们应用可以自动调整应用的资源。好比在线商品应用在促销的时候访问量会增长,咱们应该自动增长 Pod 运算能力来应对;当促销结束后,有须要自动下降 Pod 的运算能力防止浪费。

运算能力的增减有两种方式改变单个 Pod 的资源,以及增减 Pod 的数量。这两种方式对应了 Kubernetes 的 HPAVPA

Horizontal Pod AutoScaling(横向 Pod 自动扩展)

横向 Pod 自动扩展的思路是这样的:Kubernetes 会运行一个 Controller,周期性地监听 Pod 的资源使用状况,当高于设定的阈值时,会自动增长 Pod 的数量;当低于某个阈值时,会自动减小 Pod 的数量。天然,这里的阈值以及 Pod 的上限和下限的数量都是须要用户配置的。

一个重要的信息:HPA 只能和 RC、Deployment、RS 这些能够动态修改 Replicas 的对象一块儿使用,而没法用于单个 Pod、Daemonset(由于它控制的 Pod 数量不能随便修改)等对象。

目前官方的监控数据来源是 Metrics Server 项目,能够配置的资源只有 CPU,可是用户能够使用自定义的监控数据(好比:Prometheus)。其余资源(好比:Memory)的 HPA 支持也已经在路上了。

Vertical Pod AutoScaling

和 HPA 的思路类似,只不过 VPA 调整的是单个 Pod 的 Request 值(包括 CPU 和 Memory)。VPA 包括三个组件

  • Recommander:消费 Metrics Server 或者其余监控组件的数据,而后计算 Pod 的资源推荐值。

  • Updater:找到被 VPA 接管的 Pod 中和计算出来的推荐值差距过大的,对其作 Update 操做(目前是 Evict,新建的 Pod 在下面 Admission Controller 中会使用推荐的资源值做为 Request)。

  • Admission Controller:新建的 Pod 会通过该 Admission  Controller,若是 Pod 是被 VPA 接管的,会使用 Recommander 计算出来的推荐值。


能够看到,这三个组件的功能是互相补充的,共同实现了动态修改 Pod 请求资源的功能。相对于 HPA,目前 VPA 还处于 Alpha,而且尚未合并到官方的 Kubernetes Release 中,后续的接口和功能极可能会发生变化。

Cluster Auto Scaler

随着业务的发展,应用会逐渐增多,每一个应用使用的资源也会增长,总会出现集群资源不足的状况。为了动态地应对这一情况,咱们还须要 CLuster Auto Scaler,可以根据整个集群的资源使用状况来增减节点。

对于公有云来讲,Cluster Auto Scaler 就是监控这个集群由于资源不足而 Pending 的 Pod,根据用户配置的阈值调用公有云的接口来申请建立机器或者销毁机器。对于私有云,则须要对接内部的管理平台。

目前 HPA 和 VPA 不兼容,只能选择一个使用,不然二者会相互干扰。并且 VPA 的调整须要重启 Pod,这是由于 Pod 资源的修改是比较大的变化,须要从新走一下 Apiserver、调度的流程,保证整个系统没有问题。目前社区也有计划在作原地升级,也就是说不经过杀死 Pod 再调度新 Pod 的方式,而是直接修改原有 Pod 来更新。

理论上 HPA 和 VPA 是能够共同工做的,HPA 负责瓶颈资源VPA 负责其余资源。好比对于 CPU 密集型的应用,使用 HPA  监听 CPU 使用率来调整 Pods 个数,而后用 VPA 监听其余资源(Memory、IO)来动态扩展这些资源的 Request 大小便可。固然这只是理想状况,

总结

集群的资源使用并非静态的,而是随着时间不断变化的,目前 Kubernetes 的调度决策都是基于调度时集群的一个静态资源切片进行的,动态地资源调整是经过 Kubelet 的驱逐程序进行的,HPA 和 VPA 等方案也不断提出,相信后面会不断完善这方面的功能,让 Kubernetes 更加智能。

资源管理和调度、应用优先级、监控、镜像中心等不少东西相关,是个很是复杂的领域。在具体的实施和操做的过程当中,经常要考虑到企业内部的具体状况和需求,作出针对性的调整,而且须要开发者、系统管理员、SRE、监控团队等不一样小组一块儿合做。可是这种付出从总体来看是值得的,提高资源的利用率能有效地节约企业的成本,也能让应用更好地发挥出做用。

 

 

 

参考连接:

https://mp.weixin.qq.com/s/fzvL02oVd4VFgDty1UlCLg

https://mp.weixin.qq.com/s/Whpt8BqN3iBMKnuMkWpSxA

相关文章
相关标签/搜索