云原生篇-Kubernetes权威指南
Kubernetes之所以同时支持Docker和Rocket这两种互相竞争的容器技术,是有深刻的历史原因的。快速发展的Docker打败了谷歌名噪-时的开源容器技术Imtfy,并迅速风摩世界。但是,作为-个已经对全球IT公司产生重要影响的技术,Docker容器标准的制定不可能被任何-个公司主导。于是,CoreOS推出 了与Docker抗衡的开源容器项目Rocket,动员一些知名IT公司-起主导容器技术的标准化,并与谷歌共同发起基于CoreOS+Rocket+Kubernetes的新项 目Tectonic, 使容器技术分裂态势加剧。最后,Linux基 金会于2015年6月宜布成立开放容器技术项目。
Kubernetes入门
Kubernetes是什么
他是一个全新的基于容器技术的分布式架构解决方案。确切地说,Kubernetes是谷歌严格保密十几年的秘密武器—Borg的一个开源版本。
Kubernetes是-个完备的分布式系统支撑平台。Kubernetes具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建的智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制,以及多粒度的资源配额管理能力。同时, Kubernetes提供了完善的管理工具,这些工具涵盖了包括开发、部署测试、运维监控在内的各个环节。因此,Kubernetes是一个全新的基于容器技术的分布式架构解决方案,并且是一个一站式的完备的分布式系统开发和支撑平台。
特性:
Service的服务进程目前都基于Socket通信方式对外提供服务,比如Redis、Memcache、MySQL、Web Server,或者是实现了某个具体业务的特定TCP Server进程。虽然一个Service通常由多个相关的服务进程提供服务,每个服务进程都有一个独立的Endpoint(IP+Port)访问点,但 Kubernetes能够让我们通过Service(虚拟Cluster IP +Service Port)连接到指定的Service。
容器提供了强大的隔离功能,所以有必要把为Service提供服务的这组进程放入容器中进行隔离。为此,Kubernetes设计了Pod对象,将每个服务进程都包装到相应的Pod中,使其成为在Pod中运行的一个容器 (Container)。为了建立Service和Pod间的关联关系,Kubernetes首先给每个Pod都贴上一个标签(Label),给运行MySQL的Pod贴上name=mysql标签,给运行PHP的Pod贴上name=php标签,然后给相应的Service定义标签选择器(Label Selector),比如MySQL Service的标签选择器的选择条件为name=mysql,意为该Service要作用于所有包含name=mysql Label的Pod。这样一来,就巧妙解决了Service与Pod的关联问题。
为什么要用Kubernetes
- 降低协作难度,减少运维成本。
- 无需关注语言本身,完全面向微服务化架构。
- 自动弹性扩容,应对大流量。
基本概念
Kubernetes中的大部分概念如Node、Pod、Replication Controller、Service等都可以被看作一种资源对象,几乎所有资源对象都可以通过Kubernetes提供的kubectl工具(或者API编程调用)执行增、删、改、查等操作并将其保存在etcd中持久化存储。
Master
集群控制节点,Kubernetes的所有控制命令都发给它,它负责具体的执行过程。Master通常会占据一个
独立的服务器(高可用部署建议用3台服务器),主要原因是它太重要了,是整个集群的“首脑”,如果它宕机或者不可用,那么对集群内容器应用的管理都将失效。
在Master上运行着以下关键进程。
- Kubernetes API Server(kube-apiserver):提供了HTTP Rest接口的关键服务进程,是Kubernetes里所有资源的增、删、改、查等操作的唯一入口,也是集群控制的入口进程。
- Kubernetes Controller Manager(kube-controller-manager): Kubernetes里所有资源对象的自动化控制中心,可以将其理解为资源对象的“大总管”。
- Kubernetes Scheduler(kube-scheduler):负责资源调度(Pod调度)的进程,相当于公交公司的“调度室”。
另外,在Master上通常还需要部署etcd服务,因为Kubernetes里的所有资源对象的数据都被保存在etcd中。
Node
每个Node都会被Master分配一些工作负载(Docker容器),当某个Node宕机时,其上的工作负载会被Master自动转移到其他节点上。
在每个Node上都运行着以下关键进程。
- kubelet:负责Pod对应的容器的创建、启停等任务,同时与Master密切协作,实现集群管理的基本功能。
- kube-proxy:实现Kubernetes Service的通信与负载均衡机制的重要组件。
- Docker Engine(docker):Docker引擎,负责本机的容器创建和管理工作。
查看所有node列表
1 |
|
查看某个node的具体信息
1 |
|
展示了Node的如下关键信息:
- Node的基本信息:名称、标签、创建时间等。
- Node当前的运行状态:Node启动后会做一系列的自检工作比如磁盘空间是否不足(DiskPressure)、内存是否不足 (MemoryPressure)、网络是否正常(NetworkUnavailable)、PID资源是否充足(PIDPressure)。在一切正常时设置Node为Ready状态 (Ready=True),该状态表示Node处于健康状态,Master将可以在其上调度新的任务了(如启动Pod)。
- Node的主机地址与主机名。
- Node上的资源数量:描述Node可用的系统资源,包括CPU、 内存数量、最大可调度Pod数量等。
- Node可分配的资源量:描述Node当前可用于分配的资源量。
- 主机系统信息:包括主机ID、系统UUID、Linux kernel版本号、操作系统类型与版本、Docker版本号、kubelet与kube-proxy的版本号等。
- 当前运行的Pod列表概要信息。
- 已分配的资源使用概要信息,例如资源申请的最低、最大允许使用量占系统总量的百分比。
- Node相关的Event信息。
Pod
其根基容器为Pause
Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。Kubernetes要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,这通常采用虚拟二层网络技术来
实现,例如Flannel、Open vSwitch等,因此我们需要牢记一点:在Kubernetes里,一个Pod里的容器与另外主机上的Pod容器能够直接通信。
Pod其实有两种类型:普通的Pod及静态Pod(Static Pod)。后者比较特殊,它并没被存放在Kubernetes的etcd存储里,而是被存放在某个具体的Node上的一个具体文件中,并且只在此Node上启动、运行。而普通的Pod一旦被创建,就会被放入etcd中存储,随后会被Kubernetes Master调度到某个具体的Node上并进行绑定(Binding),随后该Pod被对应的Node上的kubelet进程实例化成一组相关的Docker容器并启动。在默认情况下,当Pod里的某个容器停止时,Kubernetes会自动检测到这个问题并且重新启动这个Pod(重启Pod里的所有容器),如果Pod所在的Node宕机,就会将这个Node上的所有Pod重新调度到其他节点上。 Pod、容器与Node的关系如图:
Pod资源文件定义
1 |
|
Event:
Event是一个事件的记录,记录了事件的最早产生时间、最后重现时间、重复次数、发起者、类型,以及导致此事件的原因等众多信息。Event通常会被关联到某个具体的资源对象上,是排查故障的重要参考信息,之前我们看到Node的描述信息包括了Event,而Pod同样有Event记录,当我们发现某个Pod迟迟无法创建时,可以用
kubectl describe pod xxxx
来查看它的描述信息,以定位问题的成因,比如下面这个Event记录信息表明Pod里的一个容器被探针检测为失败一次:
k8s分配资源以m为单位,其中100-300m表示占用0.1-0.3个CPU
在k8s中一个计算资源进行配额限制需要设置以下两个参数
- Requests:该资源的最小申请量,系统必须满足要求。
- Limits:该资源最大允许使用的量,不能被突破,当容器试图使用超过这个量的资源时,可能会被Kubernetes“杀掉”并重启。
Label
一个Label由(key=value)的键值对组成,其中key、value由用户自己定义, Label的定义可以是在各种资源上,Node、Pod、Service、RC等。Label通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除
常用的Label示例
- 版本标签:”release”:”stable”、”release”:”canary”。
- 环境标签:”environment”:”dev”、”environment”:”qa”、”environment”:”production”。
- 架构标签:”tier”:”frontend”、”tier”:”backend”、”tier”:”middleware”。
- 分区标签:”partition”:”customerA”、”partition”:”customerB”。
- 质量管控标签:”track”:”daily”、”track”:”weekly”。
Label Selector可以被类比为SQL语句中的where查询条件,例如,name=redis-slave这个Label Selector作用于Pod时,可以被类比为
1 |
|
这样的语句。
当前有两种Label Selector表达式:基于等式的(Equality-based)和基于集合的(Set-based),前者采用等式类表达式匹配标签,下面是一些具体的例子。
- name=redis-slave:匹配所有具有标签name=redis-slave的资源对象。
- env!=production:匹配所有不具有标签env=production的资源对象,比如env=test就是满足此条件的标签之一。
后者则使用集合操作类表达式匹配标签,下面是一些具体的例子。
- name in(redis-master, redis-slave):匹配所有具有标签 name=redis-master或者name=redis-slave的资源对象。
- name not in(php-frontend):匹配所有不具有标签name=php-frontend的资源对象。
可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式之间用“,”进行分隔即可,几个条件之间是“AND”的关系,即同时满足多个条件,比如下面的例子:
1 |
|
Labels定义在metadata中
1 |
|
管理对象RC和Service则通过Selector字段设置需要关联Pod的 Label:
其他管理对象如Deployment、ReplicaSet、DaemonSet和Job则可以在Selector中使用基于集合的筛选条件定义,例如:
matchLabels用于定义一组Label,与直接写在Selector中的作用相同;matchExpressions用于定义一组基于集合的筛选条件,可用的条件运算符包括In、NotIn、Exists和DoesNotExist。
如果同时设置了matchLabels和matchExpressions,则两组条件为AND关系,即需要同时满足所有条件才能完成Selector的筛选。
Label Selector在Kubernetes中的重要使用场景如下。
- kube-controller进程通过在资源对象RC上定义的Label Selector来筛选要监控的Pod副本数量,使Pod副本数量始终符合预期设定的全自动控制流程。
- kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制。
- 通过对某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod定向调度的特性。
Replication Controller
RC定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分。
- Pod期待的副本数量。
- 用于筛选目标Pod的Label Selector。
- 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板 (template)。
下面是一个完整的RC定义的例子,即确保拥有tier=frontend标签的这个Pod(运行Tomcat容器)在整个Kubernetes集群中始终只有一个副本:
在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling),这可以通过执行kubectl scale命令来一键完成:
1 |
|
需要注意的是,删除RC并不会影响通过该RC已创建好的Pod。为了删除所有Pod,可以设置replicas的值为0,然后更新该RC。另外,kubectl提供了stop和delete命令来一次性删除RC和RC控制的全部Pod。
在Kubernetes 1.2中,升级为另外一个新概念—Replica Set,官方解释其为“下一代的RC”。Replica Set与RC当前的唯一区别是,Replica Sets支持基于集合的Label selector(Set-based selector),而RC只支持基于等式的Label Selector(equality-based selector),这使得Replica Set的功能更强。下面是等价于之前RC例子的Replica Set的定义(省去了Pod模板部分的内容):
kubectl命令行工具适用于RC的绝大部分命令同样适用于ReplicaSet。此外,我们当前很少单独使用Replica Set,它主要被Deployment这个更高层的资源对象所使用,从而形成一整套Pod创建、删除、更新的编排机制。我们在使用Deployment时,无须关心它是如何创建和维护Replica Set的,这一切都是自动发生的。
RC(Replica Set)的一些特性与作用。
- 在大多数情况下,我们通过定义一个RC实现Pod的创建及副本数量的自动控制。
- 在RC里包括完整的Pod定义模板。
- RC通过Label Selector机制实现对Pod副本的自动控制。
- 通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容。
- 通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级。
Deployment
Deployment极度相似。
Deployment相对于RC的一个最大升级是我们可以随时知道当前 Pod“部署”的进度。实际上由于一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一完整过程需要一定的时间,所以我们期待系统启动N个Pod副本的目标状态,实际上是一个连续变化的“部署过程”导致的最终状态。
Deployment的典型使用场景有以下几个:
- 创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建。
- 检查Deployment的状态来看部署动作是否完成(Pod副本数量是否达到预期的值)。
- 更新Deployment以创建新的Pod(比如镜像升级)。
- 如果当前Deployment不稳定,则回滚到一个早先的Deployment版本。
- 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项,之后再恢复Deployment,进行新的发布。
- 扩展Deployment以应对高负载。
- 查看Deployment的状态,以此作为发布是否成功的指标。
- 清理不再需要的旧版本ReplicaSets。
Deployment的定义和RC也极度相似
完整案例
1 |
|
创建Deployment
1 |
|
查看信息
1 |
|
查看rs
1 |
|
Horizontal Pod Autoscaler
通过手工执行kubectl scale命令,我们可以实现Pod扩容或缩容。如果仅仅到此为止,显然不符合谷歌对Kubernetes的定位目标—自动化、 智能化。
通过追踪分析指定RC控制的所有目标Pod的负载变化情况,来确定是否需要有针对性地调整目标Pod的副本数量,这是HPA的实现原理。当前,HPA有以下两种方式作为Pod负载的度量指标。
- CPUUtilizationPercentage。
CPUUtilizationPercentage是一个算术平均值,即目标Pod所有副本自身的CPU利用率的平均值。
具体例子
等价于
1 |
|
- 应用程序自定义的度量指标,比如服务在每秒内的相应请求数 (TPS或QPS)
StatefulSet
背景
在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Akka集群、ZooKeeper集群等,这些应用集群有4个共同点。
(1)每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并通信。
(2)集群的规模是比较固定的,集群规模不能随意变动。
(3)集群中的每个节点都是有状态的,通常会持久化数据到永久存储中。
(4)如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。
StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下特性:
- StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod叫kafka-0,第2个叫kafka-1,以此类推。
- StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
- StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除StatefulSet相关的存储卷(为了保证数据的安全)。
Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例都创建了一个DNS域名,这个域名的格式为:
1 |
|
比如一个3节点的Kafka的StatefulSet集群对应的Headless Service的名称为kafka,StatefulSet的名称为kafka,则StatefulSet里的3个Pod的DNS名称分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称可以直接在集群的配置文件中固定下来。
Service
Service服务也是Kubernetes里的核心资源对象之一,Kubernetes里的每个Service其实就是我们经常提起的微服务架构中的一个微服务
Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。
Kubernetes也遵循负载均衡做法,运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制。但Kubernetes发明了一种很巧妙又影响深远的设计:
Service没有共用一个负载均衡器的IP地址,每个Service都被分配了一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。这样一来,每个服务就变成了具备唯一IP地址的通信节点,服务调用就变成了最基础的TCP网络通信问题。
我们知道,Pod的Endpoint地址会随着Pod的销毁和重新创建而发生改变,因为新Pod的IP地址与之前旧Pod的不同。而Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个生命周期内,它的Cluster IP不会发生改变。于是,服务发现这个棘手的问题在Kubernetes的架构里也得以轻松解决:
只要用Service的Name与Service的Cluster IP地址做一个DNS域名映射即可完美解决问题。
1 |
|
1 |
|
很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如Mycat、Codis等常见中间件。Kubernetes Service支持多个Endpoint,在存在多个Endpoint的情况下,要求每个Endpoint都定义一个名称来区分。下面是Tomcat多端口的Service定义样例:
1 |
|
服务发现机制
来Kubernetes通过Add-On增值包引入了DNS系统,把服务名作为DNS域名,这样程序就可以直接使用服务名来建立通信连接了。
Node IP:Node的IP地址。
Pod IP:Pod的IP地址。
Cluster IP:Service的IP地址。
Cluster IP仅仅作用于Kubernetes Service这个对象,并由Kubernetes管理和分配IP地址(来源于Cluster IP地址池)。
◎ Cluster IP无法被Ping,因为没有一个“实体网络对象”来响应。
◎ Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备TCP/IP通信的基础,并且它们属于Kubernetes集群这样一个封闭的空间,集群外的节点如果要访问这个通信端口,则需要做一些额外的工作。
◎ 在Kubernetes集群内,Node IP网、Pod IP网与Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊路由规则,与我们熟知的IP路由有很大的不同。
Service的Cluster IP属于Kubernetes集群内部的地址,无法在集群外部直接使用这个地址。那么矛盾来了:实际上在我们开发的业务系统中肯定多少有一部分服务是要提供给Kubernetes集群外部的应用或者用户来使用的,典型的例子就是Web端的服务模块,比如上面的tomcat-service,那么用户怎么访问它?
采用NodePort是解决上述问题的最直接、有效的常见做法。以tomcat-service为例,在Service的定义里做如下扩展即可(见代码中的type部分):
1 |
|
其中,nodePort:31002这个属性表明手动指定tomcat-service的NodePort为31002,否则Kubernetes会自动分配一个可用的端口。接下来在浏览器里访问http://
NodePort的实现方式是在Kubernetes集群里的每个Node上都为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务,在任意Node上运行netstat命令,就可以看到有NodePort端口被监听:
Job
与RC、Deployment、ReplicaSet、DaemonSet类似,Job也控制一组Pod容器。从这个角度来看,Job也是一种特殊的Pod副本自动控制器,同时Job控制Pod副本与RC等控制器的工作机制有以下重要差别。
(1)Job所控制的Pod副本是短暂运行的,可以将其视为一组Docker容器,其中的每个Docker容器都仅仅运行一次。当Job控制的所有Pod副本都运行结束时,对应的Job也就结束了。Job在实现方式上与
RC等副本控制器不同,Job生成的Pod副本是不能自动重启的,对应Pod副本的RestartPoliy都被设置为Never。因此,当对应的Pod副本都执行完成时,相应的Job也就完成了控制使命,即Job生成的Pod在Kubernetes中
是短暂存在的。Kubernetes在1.5版本之后又提供了类似crontab的定时任务——CronJob,解决了某些批处理任务需要定时反复执行的问题。
(2)Job所控制的Pod副本的工作模式能够多实例并行计算,以TensorFlow框架为例,可以将一个机器学习的计算任务分布到10台机器上,在每台机器上都运行一个worker执行计算任务,这很适合通过Job生成10个Pod副本同时启动运算。
Volume
Volume(存储卷)是Pod中能够被多个容器访问的共享目录。
Kubernetes的Volume概念、用途和目的与Docker的Volume比较类似,但两者不能等价。首先,Kubernetes中的Volume被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下;其次,Kubernetes中的
Volume与Pod的生命周期相同,但与容器的生命周期不相关,当容器终止或者重启时,Volume中的数据也不会丢失。最后,Kubernetes支持多种类型的Volume,例如GlusterFS、Ceph等先进的分布式文件系统。
Volume类型
emptyDir
一个emptyDir Volume是在Pod分配到Node时创建的。从它的名称就可以看出,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为这是Kubernetes自动分配的一个目录,当Pod从Node上移除时,emptyDir中的数据也会被永久删除。emptyDir的一些用途如下。
◎ 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
◎ 长时间任务的中间过程CheckPoint的临时保存目录。
◎ 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
目前,用户无法控制emptyDir使用的介质种类。如果kubelet的配置是使用硬盘,那么所有emptyDir都将被创建在该硬盘上。Pod在将来可以设置emptyDir是位于硬盘、固态硬盘上还是基于内存的tmpfs上,上面的例子便采用了emptyDir类的Volume。
hostPath
hostPath为在Pod上挂载宿主机上的文件或目录,它通常可以用于以下几方面。
◎ 容器应用程序生成的日志文件需要永久保存时,可以使用宿主机的高速文件系统进行存储。
◎ 需要访问宿主机上Docker引擎内部数据结构的容器应用时,可以通过定义hostPath为宿主机/var/lib/docker目录,使容器内部应用可以直接访问Docker的文件系统。在使用这种类型的Volume时,需要注意以下几点。
◎ 在不同的Node上具有相同配置的Pod,可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结果不一致。
◎ 如果使用了资源配额管理,则Kubernetes无法将hostPath在宿主机上使用的资源纳入管理。
1 |
|
gcePersistentDisk
使用这种类型的Volume表示使用谷歌公有云提供的永久磁盘(Persistent Disk,PD)存放Volume的数据,它与emptyDir不同,PD上的内容会被永久保存,当Pod被删除时,PD只是被卸载(Unmount),但不会被删除。需要注意的是,你需要先创建一个PD,才能使用gcePersistentDisk。 使用gcePersistentDisk时有以下一些限制条件。
◎ Node(运行kubelet的节点)需要是GCE虚拟机。
◎ 这些虚拟机需要与PD存在于相同的GCE项目和Zone中。
通过gcloud命令即可创建一个PD:
1 |
|
定义gcePersistentDisk类型的Volume的示例如下:
1 |
|
awsElasticBlockStore
NFS
使用NFS网络文件系统提供的共享目录存储数据时,我们需要在系统中部署一个NFS Server。定义NFS类型的Volume的示例如下
1 |
|
其他类型的Volume
◎ iscsi:使用iSCSI存储设备上的目录挂载到Pod中。
◎ flocker:使用Flocker管理存储卷。
◎ glusterfs:使用开源GlusterFS网络文件系统的目录挂载到Pod中。
◎ rbd:使用Ceph块设备共享存储(Rados Block Device)挂载到Pod中。
◎ gitRepo:通过挂载一个空目录,并从Git库clone一个git repository以供Pod使用。
◎ secret:一个Secret Volume用于为Pod提供加密的信息,你可以将定义在Kubernetes中的Secret直接挂载为文件让Pod访问。Secret Volume是通过TMFS(内存文件系统)实现的,这种类型的Volume总是不会被持久化的。
Persistent Volume
PV可以被理解成Kubernetes集群中的某个网络存储对应的一块存储,它与Volume类似,但有以下区别。
- PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。◎ PV并不是被定义在Pod上的,而是独立于Pod之外定义的。
- PV目前支持的类型包括:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC(Fibre Channel)、Flocker、NFS、iSCSI、RBD(Rados Block Device)、CephFS、 Cinder、GlusterFS、VsphereVolume、Quobyte Volumes、VMware Photon、Portworx Volumes、ScaleIO Volumes和HostPath(仅供单机测试)。
1 |
|
比较重要的是PV的accessModes属性,目前有以下类型。
- ReadWriteOnce:读写权限,并且只能被单个Node挂载。
- ReadOnlyMany:只读权限,允许被多个Node挂载。
- ReadWriteMany:读写权限,允许被多个Node挂载。
如果某个Pod想申请某种类型的PV,则首先需要定义一个 PersistentVolumeClaim对象:
1 |
|
这Pod的Volume定义中引用上述PVC即可
1 |
|
Namespace
Namespace(命名空间)是Kubernetes系统中的另一个非常重要的概念,Namespace在很多情况下用于实现多租户的资源隔离。
kubernetes默认会创建一个namespace,如果在创建资源时不指明namespace则默认在默认的namespace下。
创建namespace为development的namespace
1 |
|
在创建资源对象时指定对应的namespace
1 |
|
查看对应的pod(需要指明namespace)
1 |
|
Annotation
Annotation(注解)与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。Annotation则是用户
任意定义的附加信息,以便于外部工具查找。在很多时候,Kubernetes 的模块自身会通过Annotation标记资源对象的一些特殊信息。通常来说,用Annotation来记录的信息如下。
- build信息、release信息、Docker镜像信息等,例如时间戳、release id号、PR号、镜像Hash值、Docker Registry地址等。
- 日志库、监控库、分析库等资源库的地址信息。
- 程序调试工具信息,例如工具名称、版本号等。
- 团队的联系信息,例如电话号码、负责人名称、网址等。
ConfigMap
首先,把所有的配置项都当作key-value字符串,当然value可以来自某个文本文件,比如配置项password=123456、user=root、host=192.168.8.4用于表示连接FTP服务器的配置参数。这些配置项可以作为Map表中的一个项,整个Map的数据可以被持久化存储在Kubernetes的Etcd数据库中,然后提供API以方便Kubernetes相关组件或客户应用CRUD操作这些数据,上述专门用来保存配置参数的Map就是Kubernetes ConfigMap资源对象。
接下来,Kubernetes提供了一种内建机制,将存储在etcd中的ConfigMap通过Volume映射的方式变成目标Pod内的配置文件,不管目标Pod被调度到哪台服务器上,都会完成自动映射。进一步地,如果ConfigMap中的key-value数据被修改,则映射到Pod中的“配置文件”也会随之自动更新。于是,Kubernetes ConfigMap就成了分布式系统中最为简单(使用方法简单,但背后实现比较复杂)且对应用无侵入的配置中心。ConfigMap配置集中化的一种简单方案如图
ServiceAccount
Kubernetes ServiceAccount 是用来表示在 Pod 内运行的进程的身份的。它在 Kubernetes API 中提供了一个 API 对象,表示一组凭证,可以用来作为特定用户访问 API 服务器。ServiceAccounts 被用来给 Pods 访问 API 服务器的权限,用于执行诸如读写持久卷和修改其他 API 对象等任务。ServiceAccount 与一个或多个令牌相关联,可用于对 API 服务器进行身份验证。ServiceAccounts 还允许定义细粒度的授权策略,让控制特定 ServiceAccount 可以执行的操作。
Kubernetes ServiceAccounts 的主要目的是提供一种方法,让 Pods 具有身份和访问 API 服务器的授权,从而允许它们执行特定的操作
1 |
|
安装Kubernetes
kubernetes的安装可以通过kubeadm或者二进制的方式进行安装
- https://mikeygithub.github.io/2021/05/17/yuque/am5lkt/
- https://mikeygithub.github.io/2022/11/05/yuque/ii4c5fmarlf7iqma/
简单案例
搭建一个简单Java Web引用程序
1.3.1环境准备
1.3.2启 动MySQL服务
1.生成配置文件
1 |
|
2.配置文件(当然也可以在kubernetes dashboard上进行创建)
1 |
|
3.查看创建结果
1 |
|
3.查看自动创建的pod
1 |
|
4.创建与之关联的service
1 |
|
5.查看Service
1 |
|
可以看到对应的IP和端口,集群中的其他Pod就可以通过Service的Cluster-ip和端口访问它了
1.3.3启动Tomcat应用
1.创建RC
1 |
|
1 |
|
2.创建Service
1 |
|
3.浏览器访问
1 |
|
1.3.4通过浏览器访问网页
深入掌握Pod
YAML格式的Pod定义文件完整内容
1 |
|
各项详解
属性名称 | 取值类型 | 是否必选 | 取值说明 |
---|---|---|---|
version | String | Required | 版本号,例如v1 |
kind | String | Required | Pod |
metadata | Object | Required | 元数据 |
metadata.name | String | Required | Pod的名称,命名规范需复合RFC1035规范 |
metadata.namespace | String | Required | Pod所属命名空间,缺省default |
metadata.labels[] | List | 自定义标签列表 | |
metadata.annotation[] | List | ||
自定义注解列表 | |||
spec | Object | Required | Pod中容器的详细定义 |
spec.containers[] | List | Required | Pod中的容器列表 |
spec.containers[].name | String | Required | 容器名称,命名规范需复合RFC1035规范 |
spec.containers[].image | String | Required | 容器镜像 |
spec.containers[].imagePullPolicy | String | Alway(默认值)/Never/IfNotPresetn/ | |
spec.containers[].command[] | List | 容器启动命令列表,缺省使用镜像打包时使用的启动命令 | |
spec.containers[].args[] | List | 容器的启动命令列表 | |
spec.containers[].workingDir | String | 容器的工作目录 | |
spec.containers[].volumeMounts[] | List | 挂载到容器内部的存储卷配置 | |
spec.containers[].volumeMounts[].name | String | 卷名称 | |
spec.containers[].volumeMounts[].mountPath | String | 挂载在容器内的绝对路径,应少于512字符 | |
spec.containers[].volumeMounts[].readOnly | Boolean | 是否只读,默认读写模式 | |
spec.containers[].ports[] | List | 容器需要暴露的端口 | |
spec.containers[].ports[].name | String | 端口名称 | |
spec.containers[].ports[].containerPort | Int | 容器需要监听的端口 | |
spec.containers[].ports[].hostPort | Int | 容器所在主机需要监听的端口,默认和containerPort相同,所在hostPort时,同一台宿主机无法启动该容器的第二份副本 | |
spec.containers[].ports[].protocol | String | 端口协议,支持TCP和UDP,默认TCP | |
spec.containers[].envs[] | List | 容器运行前需设置的环境变量列表 | |
spec.containers[].envs[].name | String | 环境变量名称 | |
spec.containers[].envs[].value | String | 环境变量值 | |
spec.containers[].resources | Object | 资源限制和资源请求的设置 | |
spec.containers[].resources.limits | Object | 资源限制的设置 | |
spec.containers[].resources.limits.cpu | String | CPU限制,单位和core数,将用于docker run –cpu-shares参数 | |
spec.containers[].resources.limits.memory | String | 内存限制,单位可以是MiB、GiB等,将用于docker run –memory参数 | |
spec.containers[].resources.requests | Object | 资源请求的设置 | |
spec.containers[].resources.requests.cpu | String | CPU请求,单位为core数,容器启动的初始可用数量 | |
spec.containers[].resources.requests.memory | String | 内存请求,单位可以是MiB、GiB等,容器启动的初始可用数量 | |
spec.volumes[] | List | 在改Pod上定义的共享存储卷列表 | |
spec.volumes[].name | String | 共享存储卷的名称 | |
spec.volumes[].emptyDir | Object | 类型为emptyDir的存储卷,表示与Pod同生命周期的一个临时目录,其值为一个空对象{} | |
spec.volumes[].hostPath | Object | 类型为hostPath的存储卷,表示与Pod挂载到宿主机目录 | |
spec.volumes[].hostPath.path | String | Pod容器中挂载的宿主机路径 | |
spec.volumes[].secret | Object | 类型为secret的存储卷,表示挂载集群预定义的secret对象到容器内部 | |
spec.volumes[].configMap | Object | 类型为configMap的存储卷,表示挂载集群预定义的configMap对象到容器内部 | |
spec.volumes[].livenessProbe | Object | 对Pod内部容器健康检查的设置,当探测无响应几次后系统会自动重启该容器 | |
spec.volumes[].livenessProbe.exec | Object | 对Pod内容器健康检查的设置,exec方式 | |
spec.volumes[].livenessProbe.exec.command[] | List | exec方式需要指定的命令或脚本 | |
spec.volumes[].livenessProbe.httpGet | Object | 对Pod内容器健康检查的设置,httpGet方式,需要指定path、port | |
spec.volumes[].livenessProbe.tcpSocket | Object | 对Pod内容器健康检查的设置,tcpSocket方式 | |
spec.volumes[].livenessProbe.initialDelaySeconds | Number | 容器启动后首次探针的时间,单位为秒s | |
spec.volumes[].livenessProbe.timeoutSeconds | Number | 对容器健康检查的超时时间 | |
spec.volumes[].livenessProbe.periodSeconds | Number | 定期检查事件,默认10秒 | |
spec.restartPolicy | String | Pod重启策略,Always/OnFailure/Never | |
spec.nodeSelector | Object | 设置Node的Label,以key:value格式指定,Pod将被调度到具有这些Label的Node上 | |
spec.imagePullSecrets | Object | 镜像下拉使用的secret,以key:value格式指定 | |
spec.hostNetwork | Boolean | 是否使用主机网络模式,默认false,启用true将不再使用docker网桥 |
Pod的基本用法
一个Pod中可以包含多个容器,同一个Pod中的容器通信采用localhost就可以通信
静态Pod
静态Pod是由kubelet进行管理的仅存在特定Node上的Pod,他们不能通过API Server进行管理,无法对ReplicationController、Deployment或者DaemonSet进行关联,静态Pod是由Node创建的所以总在kubelet所在Node上运行。
创建静态Pod有两种方式:配置文件方式和HTTP方式
配置文件方式
HTTP方式
通过设置kubelet的启动参数
--mainfest-url
kubelet会定期从该url地址下载Pod的定义文件创建
Pod容器共享Volume
同一个Pod中的多个容器能够共享Pod级别的存储卷Volume。
1 |
|
tip:在编写yaml可以在goland中下载k8s编辑插件,编写起来智能提示更加方便
Pod的配置管理
ConfigMap
- 生成容器内的环境变量
- 设置容器启动的命令和参数(需要设置为环境变量)
- 以Volume的形式挂载为容器内部的文件或目录
通过配置文件方式创建ConfigMap
1
2
3
4
5
6
7apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appvars
data:
apploglevel: info
appdatadir: /var/data1
kubectl create -f cm-appvars.yaml
通过kubectl命令行方式创建
1
kubectl create configmap NAME --from-file=[key=]source
ConfigMap的使用
通过环境变量方式使用ConfigMap
1
2
3
4
5
6env:
- name: APPLOGLEVEL
valueFrom:
configMapKeyRef:
key: apploglevel
name: cm-appvars通过envForm方式
1
2
3envFrom:
- configMapRef:
name: cm-appvars通过valueMount使用ConfigMap
1
2
3
4
5
6
7
8
9
10
11
12volumes:
- name: app-logs
emptyDir:
{}
- name: serverxml
configMap:
name: cm-appvars
items:
- key: key-serverxml
path: server.xml
- key: key-loggingproperties
path: logging.properties
使用ConfigMap的限制条件
- ConfigMap必须在Pod之前创建,Pod才能引用它
- 如果Pod使用envFrom基于ConfigMap定义环境变量,则无效的环境变量名称(如数字开头)将被忽略,并记录事件在InvalidVariableNames中
- ConfigMap受命名空间限制,只有处于相同命名空间的Pod才能引用它
- ConfigMap无法用于静态Pod
Pod的生命周期
Pod的重启策略
- Always: 当容器失效时,由kubelet自动重启该容器
- OnFailure: 当容器终止运行且退出码不为 0 时,由 kubelet 自动 重启该容器
- Never: 不论容器运行状态如何, kubelet 都不会重启该容器。
Pod健康检查探针
- LivenessProbe探针:用于判断容器是否存活(Running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将“杀掉容器”,如果容器不包含该探针则kubelet认为该容器返回值永远是SUCCESS
- ReadinessProbe探针:用于判断容器是否可用(Ready状态),达到Ready状态才可以接收请求。
- StartupProbe探针:某些应用会遇到启动比较慢的情况,例如应用程序启动时需 要与远程服务器建立网络连接,或者遇到网络访问较慢等情况时,会造成容器启动缓慢, 此时 ReadinessProbe 就不适用了,因为这属千“有且仅有一次"的超长延时,可以通过 StartupProbe 探针解决该问题
1 |
|
Pod调度
定义Deployment,其保护三个pod副本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80创建Deployment
1
kubectl create -f nginx-deployment.yaml
查看对应的Deployment
1
kubectl get deployments
NodeSelector定向调度
用于将Pod部署到指定的Node上
首先需要在Node上打标签
1
kubectl label nodes kBs-node-1 zone=nort
查看该节点的标签
1
kubectl describe node k8s-node01
然后在Pod定义文件中修改配置 nodeSelector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22apiVersion: v1
kind: ReplicationController
metadata:
name: redis-master
labels:
name: redis-master
spec:
replicas: 1
selector:
name: redis-master
template:
metadata:
labels:
name: redis-master
spec:
containers:
- name: master
image: kubeguide/redis-master
ports:
- containerPort: 6379
nodeSelector: #配置节点选择器只要节点的标签包含zone=north的才部署
zone: north部署Pod
1
kubect create -f pod-node-selector.yaml
查看日志
1
Normal Scheduled 2m27s default-scheduler Successfully assigned default/redis-master-vrnlb to k8s-node01
NodeAffinity
Node亲和性调度,用于替换掉nodeSelector的机制
RequiredDuringSchedulingIgnoredDuringExecution: 须满足指定的规则才可以调 Pod Node 上(功能与 nodeSelector 很像,但是使用的是不同的语法),相当于硬限制。
PreferredDuringSchedulingIgnoredDuringExecution: 强调优先满足指定规则,调度 器会尝试调度 Pod Node 上,但并不强求,相当于软限制 。多个优先级规则还 可以设置权重 (weight) 值,以定义执行的先后顺序。
- RequiredDuringSchedulingIgnoredDuringExecution在运行期间如果node的lable发生改变,其不在理会
- 如果同时定义了nodeSelector和nodeAffinity那么必须同时满足才能不是到对应的节点上
1 |
|
PodAffinity
Pod亲和性与互斥调度策略,主要解决Pod之间相互依赖
拓扑域:一个拓扑域由一些node节点组成,使用region表示机架、机房等的拓扑区域,用Zone表示地区,这样子跨度更大的拓扑区域
一般情况下我们可以认为一个Node就是一个拓扑域
kubernetes自带以下拓扑域
- kubernetes.io/hoostname
- topology.kubernetes.io/region
- topology.kubernetes.io/zone
Pod的亲和性是在Pod的定义上添加topologyKey
属性
1 |
|
Pod的互斥性通过 podAntiAffinity
实现
Taints 和 Tolerations
taint是解决Node拒绝Pod的运行,简单的说被标记为Taint的节点就是存在问题的节点,比如磁盘要满、资源不足等
为节点打taint
1 |
|
键为 key, 值为 value, Taint 的效果是 NoSchedule 这意味着除非 Pod 明确声明可以容忍这个 Taint, 不会被调度到 k8s-node01上。
1 |
|
Pod优先级调度
如果发生抢占的调度,高优先级Pod就可能抢占节点N,并将低优先级的节点驱逐出节点N
配置优先级资源
1 |
|
引用优先级
1 |
|
DaemonSet
daemonSet是kubernetes 1.2版本新增的一种资源对象,用于管理集群中的每个Node上仅运行一份Pod的副本实例。
适用场景
- 在每个Node上都运行一个ClusterFs存储或者Ceph存储的Daemon进程
- 在每个Node上都运行一个日志采集程序,例如Fluentd或者Logstach
- 在每个Node上都运行一个性能监控程序,采集该节点的性能数据,例如Prometheus Node Exporter、Collectednew Relic agent或者Ganglia gmond等
1 |
|
查看结果
1 |
|
Job批处理调度
- Job Temlate Expansion 模式:一个Job对象对应一个待处理的Work item,有几个Work item就产生几个独立的Job
- Queue with Pod Per Work Item 模式:采用任务队列存放Work item,在这种模式下Job会启动N个Pod每个Pod对应一个Work Item
- Queue with Variable Pod Count 模式: 与上面的不同是Job启动的Pod数量是可变的
- Single Job with Static Work Assignment 模式:一个Job产生多个Pod但它采用程序静态方式分配任务,而不是队列动态分配
1 |
|
CronJob定时任务
1 |
|
Minutes : 可出现 “,” “-“ “*” “/“ 这四个字符,有效范围为 0-59
Hours : 可出现 “,” “-“ “*” “/“ 这四个字符,有效范围为 0-23
DayofMonth : 可出现 “,” “-“ “*” “/“ “?” “L” “W” “C” 这八个字符,有效范围为 1-31
Month : 可出现 “,” “-“ “*” “/“ 这四个字符,有效范围为 1-12的整数或者JAN-DEC
DayofWeek : 可出现 “,” “-“ “*” “/“ “?” “L” “W” “#” 这八个字符,有效范围为 1-7或者SUN-SAT
1 |
|
自定义调度器
1 |
|
注意:如果自定义调度器还未在系统中部署,则默认的调度器会忽略这个Pod,这个Pod将永远处于Pending状态
Pod容灾调度
通过设置 topologySpreadConstraints 来将 Pod 均匀调度到不同的 Zone
案例:假如我们的集群被划分为多个Zone,我们有一个应用(对应的Pod标签为app=foo)需要在每个Zone均匀调度以实现容灾
1 |
|
maxSkew: 用于指定Pod在各个Zone上调度时能容忍的最大不均衡数,数值越大,表示能接受的不均衡调度越大,值越小表示各个Zone的Pod数量分布越均匀
skew[topo]=count[topo]-min(count[topo])
例如有三个Zone,我们部署3个Pod到最高集群中,第一第二个Pod肯定是位于A、B中,第三个我们计算skew值
Zone A 的 skew=1-0=1
Zone B 的 skew=1-0=1
Zone C 的 skew=0-0=0
Init Container初始化容器
初始化操作
- 等待其他关联组件正确运行(例如数据库或某个后台服务)
- 基于环境变量或配置模板生成配置文件
- 从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库中
- 下载相关依赖包,或者对系统进行一些预配置操作
根据Pod的重启策略,当init container运行失败而且设置了RestartPolicy=Never时,Pod将会启动失败;而设置RestartPolicy=Always时,Pod将会被系统自动重启。
案例:在创建Nginx的Pod之前进行创建index.html文件
1 |
|
Pod的升级和回滚
kubernetes提供滚动升级,如果Pod是通过Deployment创建的,则用户可以在运行时修改Deployment的Pod定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的rollout动作。
Deployment的升级
1 |
|
通过kubect set image 命令
1
kubectl set image deployment/nginx-deployment nginx=nginx:latest
或者通过kubectl edit 修改 Deployment
1
kubectl edit deployment/nginx-deployment
使用 kubectl rollout status 命令查看 Deployment 的更新过程
1
kubectl rollout status deployment/nginx-deployment
滚动升级的原理
系统创建新的RelicaSet,并将其副本数量扩容,然后逐个对旧版本的Pod进行驱逐,最终只剩下新的RS中有Pod
Demplyment的回滚
查看部署历史
1
2
3
4
5[root@k8s-master01 ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>如果想要查看特定版本的信息 加上 –revision=3
1
kubectl rollout history deployment/nginx-deployment --revision=3
回滚到上一个版本
1
kubectl rollout undo deployment/nginx-deployment
回滚到指定版本
1
kubectl rollout undo deployment/nginx-deployment --to-revision=3
暂停Deployment的滚动更新操作
1
kubectl rollout pause deployment/nginx-deployment
DaemonSet的更新策略
- OnDelete(缺省)
- RollingUpdate
OnDelete:新的Pod并不会被创建,直到用户将旧的Pod删除才会触发新建操作
RollingUpdate:该模式下会自动杀掉旧的Pod新建新的Pod
1 |
|
StatefulSet的更新策略
- updateStrategy
- OnDelete
- Partitioned
- RollingUpdate
Pod的伸缩扩容
- 手动模式:通过运行 kubectl scale 命令,对一个Deployment/RC进行Pod副本数量的设置。
- 自动模式:需要根据用户的指标或者自定义业务指标并指定Pod副本数量的范围。
手动模式
1 |
|
自动模式
HPA (Horizontal Pod Autoscaler)
指标类型
- Pod资源使用率:Pod级别的性能指标,通常是一个比率值,例如CPU的使用率
- Pod自定义指标:Pod级别的性能指标,通常是一个数值,例如接收的请求数量
- Object自定义指标或外包指标:通常是一个数值,需要容器应用以某种方式提供,例如HTTP URL “metrics”提供或者使用外部服务提供的指标采集URL
扩容算法desireReplicas=ceil[currentReplicas*(currentMetericValue/desiredMetericValue)]
即 当前副本数 * (当前指标值/期望的指标值)
将结果向上取整
1 |
|
基于自定义指标的HPA实践
使用StatefulSet搭建MongoDB集群
- 创建StorageClass对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72# 定义StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://<heketi-rest-url>"
---
# 定义Service
apiVersion: v1
kind: Service
metadata:
name: mongo
labels:
name: mongo
spec:
ports:
- port: 27017
targetPort: 27017
selector:
role: mongo
clusterIP: None
---
# 定义 StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongo
spec:
selector:
matchLabels:
name: mongo
serviceName: "mongo"
template:
metadata:
labels:
role: mongo
environment: test
spec:
containers:
- name: mongo
image: mongo
command:
- "--repplSet"
- re0
- "--smallfiles"
- "--norealloc"
ports:
- containerPort: 27017
volumeMounts:
- mountPath: /data/db
name: mongo-persistent-storage
- name: mongo-sidecar
image: cvallance/mongo-k8s-sidecar
env:
- name: MONGO_SIDECAR_POD_LABELS
value: "role=mongo,environment=test"
- name: KUBERNETES_MONGO_SERVICE_NAME
value: "mongo"
volumeClaimTemplates:
- metadata:
name: mongo-persistent-storage
annotations:
volume.beta.kubernetes.io/storage-class: "fast"
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: 100Gi
深入掌握Service
Service定义详解
属性名称 | 取值类型 | 是否必选 | 取值说明 |
---|---|---|---|
version | String | Required | 版本号,例如v1 |
kind | String | Required | Service |
metadata | Object | Required | 元数据 |
metadata.name | String | Required | Pod的名称,命名规范需复合RFC1035规范 |
metadata.namespace | String | Required | Pod所属命名空间,缺省default |
metadata.labels[] | List | 自定义标签列表 | |
metadata.annotation[] | List | ||
自定义注解列表 | |||
spec | Object | Required | Service中容器的详细定义 |
spec.selector[] | List | Required | Label Selector配置,将具有指定Label标签的Pod作为管理范围 |
spec.type | string | required | Service的类型,指定Service的访问方式,默认值为ClusterIP。1.ClusterIP:虚拟服务IP地址,该地址用于Kubernetes集群内部的Pod访问,在Node上kube-proxy通过设置的iptables规则进行转发。2.NodePort:使用宿主机的端口,使能够访问各Node的外部客户端通过Node的IP地址和端口就能访问服务。3.LoadBalancer:使用外接负载均衡器完成到服务的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡器的IP地址,同时定义nodePort和ClusterIP,用于公有云环境 |
spec.clusterIP | String | ||
虚拟服务的IP地址,当type=ClusterIP时,如果不指定,则系统进行自动分配,也可以手动指定;当type=LoadBalancer时,需要指定 | |||
spec.sessionAffinity | String | ||
是否支持Session,可选值为ClientIP,默认值为None。ClientIP:表示将同一个客户端(根据客户端的IP地址决定)的访问请求都转发到同一个后端Pod | |||
spec.ports[] | List | Service端口列表 | |
spec.ports[].name | String | 端口名称 | |
spec.ports[].protocol | String | 端口协议,支持TCP和UDP,默认TCP | |
spec.ports[].port | int | 容器的工作目录 | |
spec.ports[].targetPort | int | 需要转发到后端Pod的端口号 | |
spec.ports[].nodePort | int | 当spec.type=NodePort时,指定映射到宿主机的端口号 | |
status | Object | 当spec.type=LoadBalancer时,设置外部负载均衡器的地址,用于公有云环境 | |
status.loadBalancer | Object | 外部负载均衡器 | |
status.loadBalancer.ingress | Object | 外部负载均衡器 | |
status.loadBalancer.ingress.ip | String | 外部负载均衡的IP地址 | |
status.loadBalancer.ingress.hostname | String | 外部负载均衡的主机名 |
Service的概念和原理
Service的概念
Service主要用于提供网络服务,通过Service的定义,能够为客户端应用提供稳定的接口地址(域名或IP地址)和负载均衡功能,屏蔽后端Endpoint的变化。
创建Service
使用expose命令为某个Pod创建Service
1
kubectl expose deployment webapp
使用yaml文件方式
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
name: webapp #Pod中所有含有标签 name=webapp的Pod查看Service
1
kubectl get svc
通过Service的IP地址和Service的端口号就可以实现服务的访问
Service的负载均衡机制
从服务IP到后端Pod的负载均衡机制是由每个Node上的kube-proxy负责实现的
kube-proxy的代理模式
- userspace模式:用户空间模式,由kube-proxy完成代理的实现,效率最低,不推荐使用。
- iptables模式:kube-proxy通过设置Linux Kernel的iptables规则,实现从Service到后端Endpoint列表的负载分发规则,效率高。但是,如果某个Endpoint在转发时不可用,此次客户端请求就会得到失败的响应,相比userspace模式更不可靠。应该通过Pod设置readinessprobe(服务可用健康检查)来保证只有达到ready状态的Endpoint才会被设置为Service的后端Endpoint。
- kernelspace模式:windows server上的代理模式
- ipvs模式:kube-proxy通过设置Linux Kernel的netlink接口设置IPVS规则,转发效率和吞吐量都最高,如果操作系统未启用IPVS内核模块,kube-proxy将会自动切换到iptable模式。其支持的负载均衡策略有:
- rr:round-robin,轮询
- lc:least connection,最小连接数
- dh:destination hashing,目的地址哈希
- sh:source hashing,源地址哈希
- sed:shortest expected delay,最短期望延时
- nq:never queue,永不排队
会话保持机制
通过设置sessionAffinity实现基于客户端IP保持机制,首次将某个客户端来源的请求转发到后端的某个Pod上,之后从相同的客户端IP发起的请求都将被转发到相同的Pod上。
1 |
|
设置会话的保持时间
1 |
|
将外部访问定义未Service
1 |
|
将Service暴露到集群外部
Service的类型,指定Service的访问方式,默认值为ClusterIP。
ClusterIP:虚拟服务IP地址,该地址用于Kubernetes集群内部的Pod访问,在Node上kube-proxy通过设置的iptables规则进行转发。
NodePort:使用宿主机的端口,使能够访问各Node的外部客户端通过Node的IP地址和端口就能访问服务。
LoadBalancer:使用外接负载均衡器完成到服务的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡器的IP地址,同时定义nodePort和ClusterIP,用于公有云环境
ExternalName:将Service映射为一个外部域名地址,通过externalName字段设置
NodePort类型
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8080
targetPort: 8080
nodePort: 8081
protocol: TCP
type: NodePortLoadBalancer类型
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: LoadBalancer
selector:
name: MyApp
ports:
- port: 80
targetPort: 9376
protocol: TCP
clusterIP: 10.12.0.1在服务创建成功之后,云服务商在Service的定义中补充LoadBalancer的IP的IP地址(staus字段)
1
2
3
4status:
loadBalancer:
ingress:
- ip: 192.168.100.21ExternalName类型
1
2
3
4
5
6
7
8apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
Service支持的网络协议
- TCP
- UDP
- HTTP
- PROXY
- SCTP
标记应用层协议,需要设置kube-apiserver启动参数,--feature-gates=ServiceAppProtocol=true
进行开启
1 |
|
Kubernetes的服务发现机制
环境变量方式
一个Pod运行起来的时候系统会自动为其容器运行环境注入所有集群中有效Service信息
curl http://${WEBAPP_SERVICE_HOST}:${WEBAPP_SERVICE_HOST}
DNS方式
Service在Kubernetes系统中遵循DNS命名规范,Service的DNS域名表示方法为
<servicename>.<namespace>.svc.<clusterdomain>
- servicename:服务的名称
- namespace:所在namespace的名称
- clusterdomain:Kubernetes集群设置的域名后缀
Headless Service的概念和应用
这种Service没有入口访问地址(ClusterIP地址),kube-proxy不会为其创建负载转发规则,而服务名称(DNS域名)的解析规则取决于Headless Service是否设置了Label Selector
如果Headless Service设置了Label Selector将根据Label Selector查寻后端Pod列表,自动创建Endpoint列表,将服务名(DNS域名)的解析机制设置为当客户端访问服务名时,得到的是全部Endpoint列表,而不是一个确定的IP地址。
1
2
3
4
5
6
7
8
9
10
11
12apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginxHeadless Service 没有设置 Label Selector,k8s不会自动查看Endpoint列表
DNS服务搭建和配置指南
修改每个Node上kubelet的DNS启动参数
1 |
|
部署CoreDNS服务
ConfigMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: EnsureExists
data:
Corefile: |
cluster.local {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local 169.169.0.0/16 {
fallthrough in-addr.arpa ip6.arpa
}
prometheus 9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
. {
cache 30
loadbalance
forward . /etc/resolv.conf
}Deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/name: "CoreDNS"
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
priorityClassName: system-cluster-critical
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
nodeSelector:
kubernetes.io/os: linux
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- name: coredns
image: coredns/coredns-arm64
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
args:
- conf
- /etc/coredns/Corefile
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
livenessProbe:
httpGet:
port: 8080
path: /health
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
port: 8181
scheme: HTTP
path: /ready
dnsPolicy: Default
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 169.169.0.100
ports:
- port: 53
name: dns
protocol: UDP
- port: 53
name: dns-tcp
protocol: TCP
- port: 9153
protocol: TCP
name: metrics使用一个带有 nslookup 工具的Pod来验证DNS服务
1
2
3
4
5
6
7
8
9
10
11
12apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: gcr.io/google_containers/busybox
command:
- sleep
- "3600"1
kubectl exec busybox -- nslookup redis-master
CoreDNS配置说明
Node本地DNS缓存
背景:
- CoreDNS以Service方式运行通过ClusterIP访问其访问量大时,其压力可能很大(可以通过扩容解决)
- 如果kube-proxy通过iptables解析域名性能可能很差
基于以上原因引入了Node本地DNS缓存
1 |
|
Pod DNS 域名相关特性
对Pod来说,Kubernetes会为其设置一个 <pod-ip>.<namespace>.pod.<cluster-domain>
格式的域名,其中“-”替换掉“.”符号
自定义子域名
1 |
|
Pod的DNS策略
Kubernetes可以在Pod级别通过dnsPolicy字段设置DNS策略,目前支持的
- Default:基础Pod所在宿主机的域名解析设置
- ClusterFirst:优先使用Kubernetes环境的DNS服务
- ClusterFirestWithHostNet:适用于hostNetwork模式运行的Pod
- None:忽略Kubernetes集群的DNS配置,需要通过手动配置dnsConfig自定义
1 |
|
Ingress 7 层路由机制
Service的表现形式是 IP+端口 可知其工作在TCP/IP层,我们应用层很多时候不同的路径请求到不同的应用上,service无法做到这一点(如http://www.demo.com/a和http://www.demo.com/b需要转到不同的应用上)
基于上面Kubernetes提供了Ingress策略定义和一个具体提供转发服务的Ingress Controller,两者结合实现了基于灵活Ingress策略定义的服务路由功能
Ingress只能以HTTP/S提供服务
1 |
|
核心组件运行机制
Kubernetes API Server 原理解析
api-server是提供Kubernetes各类资源对象(如Pod、RC、Service等)的CRUD及Watch等HTTP REST接口,称为集群内各个功能模块直接的数据交互和通信的中心枢纽。
Kubernetes中的CRD在API Server中的设计和实现机制,每种官方的资源对象如Node、Pod、Service等的实现都包含以下功能
- 资源对象的元数据(Schema)的定义:可以将其理解为数据库 Table的定义,定
义了对应资源对象的数据结构,官方内建资源对象的元数据定义是固化在源码中的。
- 资源对象的校验逻辑:确保用户提交的资源对象的属性的合法性。
- 资源对象的 CRUD 操作代码:可以将其理解为数据库表的 CRUD 代码,但比后
者更难,因为 API Server 对资源对象的 CRUD 操作都会保存到etcd 数据库中,对处理性
能的要求也更高,还要考虑版本兼容性和版本转换等复杂问题。
- 资源对象相关的“自动控制器”(如 RC、Deployment 等资源对象背后的控制器):
这是很重要的一个功能。Kubernetes 是一个以自动化为核心目标的平台,用户给出期望的
资源对象声明,运行过程中由资源背后的“自动控制器〞确保对应资源对象的数量、状态
行为等始终符合用户的预期。
类似地,每个自定义 CRD 的开发人员都需要实现上面的功能。为了降低编程难度与工作量,API Server 的设计者们做出了大量努力,使得直接编写 YAML 定义文件即可实现以上前了个功能。对于唯一需要编程的第 4个功能,由于 API Server 提供了大量的基础API 库,特别是易用的List-Watch 的编程框架,所以 CRD 自动控制器的编程难度大大降低。
Kubernetes Proxy API 接口
这类接口的作用是代理REST请求,即Kubernetes API Server把接收到的REST请求转发到某个Node上的kubelet时间戳的REST端口,由该进程负责响应
API Server 网络隔离的设计
API Server Network Proxy的核心设计思想是将API Server放置在一个独立的网络中,与Node节点的网络相互隔离,然后增加独立的Network Proxy进程来解决这两个网络直接连通性(connectivity)问题
Controller Manager 原理解析
一般来说,智能系统和自动系统通常会通过一个“操作系统”不断修正系统的工作状态。在Kubernetes 集群中,每个 Controller 都是这样的一个“操作系统”,它们通过 API Server提供的 (List-Watch)接口实时监控集群中特定资源的状态变化,当发生各种故障导致某资源对象的状态变化时,Controller 会尝试将其状态调整为期望的状态。比如当某个 Node意外宕机时,Node Controller 会及时发现此故障并执行自动化修复流程,确保集群始终处手预期的工作状态下。Controller Manager 是 Kubernetes 中各种操作系统的管理者,是集群内部的管理控制中心,也是Kubernetes 自动化功能的核心。
- 确保在当前集群中有N个Pod实例,N是在RC中定义的Pod副本数量
- 通过调整spec.replicas属性的值来实现系统扩容或者缩容
- 通过改变Pod模板(主要是镜像版本)来实现系统的滚动升级
Node Controller
kubelet进程在启动时通过API Server注册自身节点信息,并定时向API Server汇报状态信息,API Server在接收这些信息后会将这些信息更新到ETCD中。
ResourceQuota Controller
资源配额管理确保指定的资源对象在任何时候都不会超量占用系统物理资源,避免由于某些业务进程在设计或实现上的缺陷导致整个系统紊乱甚至意外宕机。
k8s支持三个层次的资源配额管理
- 容器级别,可以对CPU和Memory进行限制
- Pod级别,可以对一个Pod内所有的容器的资源进行限制
- Namespace级别,为Namespace级别的资源限制,包括:Pod数量、RC数量、Service数量等
Namespace Controller
用户通过API Server可以创建新的Namespace并将其保存在etcd中,Namespace Controller定时通过API Server读取这些Namespace的信息。
Service Controller 与 Endpoints Controller
Scheduler原理解析
Kubernetes scheduler是复制Pod调度的进程(组件)
Scheduler调度流程
将待调度的Pod(API 新创建的Pod、Controller Manager为补足副本而创建的Pod等)按照特定的调度算法和调度策略绑定(Bingding)到集群中某个合适的Node上,并将绑定信息写入etcd中。
scheduler只能API Server打交道
- 输入:待调度的Pod和全部计算节点的信息
- 输出:目标Pod要“安家”的最优节点
- 过滤阶段:遍历所有目标Node,筛选出符合要求的候选节点。
- 打分阶段:在过滤阶段的基础上,采用优选策略计算出每个节点积分,挑选出最佳节点后Scheduler会把目标Pod安置到此节点上,调度完成。
Scheduler Framework
多调度器特性
Kubernetes自带一个默认调度器,从1.2版本开始引入自定义调度器的特征,支持用户实现的自定义调度器,多个自定义调度器可以与默认的调度器同时运行。
kubelet运行机制解析
在Kubernetes集群的每个ndoe上都会启动一个kubelet服务进程,该进程用于处理Master下发到本节点的任务,管理Pod及Pod中的容器。每个kubelet进程都会在API Server上注册节点自身的信息,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控容器和节点资源。
节点管理
通过控制启动参数“–register-node”来决定是否想API Server注册自己
- –api-servsers:API Server的位置
- –kubeconfig:kubeconfig文件,用于访问API Server的安全配置文件
- –coud-provider:云服务商(Iaas)地址,仅用于公有云环境中。
Pod管理
kubelet通过以下方式获取在自身Node上要运行的Pod清单
静态Pod配置文件:kubelet通过启动参数–config指定目录下的Pod YAML文件(默认目录为/etc/kubernetes/manifests/),kubelet会持续监控指定目录下的文件变化,以创建和删除Pod。
HTTP端点(URL):通过–manifest-url参数配置,通过–http-check-frequency设置检查该HTTP端点数据的时间间隔,默认为20s。
API Server:kubelet通过API Server监听etcd目录,同步Pod列表
所有以非API Server方式创建的Pod都叫做 static Pod
容器健康检查
Pod通过两类探针来检测容器的健康状态
- LivenessProbe探针,用于判断容器是否健康并反馈给kubelet,如果LivenessProbe探针探测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略做相应处理。
- ReadinessProbe探针,用于判断容器是否启动完成,且准备接收请求,如果ReadinessProbe探测到容器启动失败,则Pod的状态将被修改,Endpoint Controller将从Service的Endpoint中删除包含该容器所在的Pod的IP地址的Endpoint条目。
LivenessProbe三种实现方式:
- ExecAction:在容器内部运行一个命令,如果该命令的退出状态位0,则表明容器健康
- TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果端口能被访问,则表明容器健康。
- HTTPGetAction:通过容器的IP地址和端口号及路径调用HTTP Get方法,如果响应的状态码大于或等于200且小于或等于400,则认为容器健康。
5.4.4 cAdvisor资源监控
cAdvisor是一个开源的分析容器资源使用率和性能特性的代理工具,在4194端口提供简单UI和API服务,在1.12完全弃用,1.8版本后提供标准的Metrics API,Metrics Server用于提供Core Metrics(指标核心),包括Node和Pod的CPU和内存使用数据。其他自定义指标则通过第三方组件(如prometheus)采集和存储。
5.4.5 容器运行时
容器技术最早来着Linux,所以又被称为Linux Container。LXC项目是一个Linux容器的工具集,也是真正意义上的一个Container Runtime,他的作用就是将用户的进程包装成一个Linux容器并启动运行。
Docker最早是基于LXC,后面自研Libcontainer,改名为runc,再后来为Kubernetes设计了containerd。
类似的还有红帽开源的CRI-O,openEuler社区开源的iSula等,这些container runtime还有一个共同特点就是都实现了Kubernetes提出的CRI接口规范,可直接接入Kubernetes。
5.5 kube-proxy 运行机制解析
为了支持集群的水平拓展和高可用性,Kubernetes抽象出了Service的概念。Service是对一组Pod的抽象,它会根据访问策略(如负载均衡策略)来访问这组Pod。
Kubernetes在创建服务时会为服务分配一个虚拟IP地址,客户端通过访问这个虚拟IP来访问服务,服务则负责将请求转发到后端的Pod上。这其实就是一个反向代理,但与普通的反向代理有一些不同:他的IP地址是虚拟,若想从外面访问则需要一些技巧;它的部署和启动是由Kubernetes统一自动管理的。
5.5.1 第一代 Proxy
起初,kube-Proxy进程是一个真实的TCP/UDP代理,类似HA Proxy,负责转发从Service到Pod的访问流量,这被称为userspace(用户空间代理)模式。
起初,kube-proxy 进程是一个真实的 TCP/UDP 代理,类似 HA Proxy,负责转发从Service 到Pod 的访问流量,这被称为 userspace(用户空间代理)模式。如图5.17所示,当某个客户端Pod 以ClusterIP 地址访问某个Service 时,这个流量就被 Pod 所在 Node的
iptables 转发给 kube-proxy 进程,然后由 kube-proxy建立起到后端 Pod 的 TCP/UDP连接,再将请求转发到某个后端 Pod 上,并在这个过程中实现负载均衡功能。
5.5.2 第二代Proxy
从1.2版本开始,Kubernetes将iptables作为kube-Proxy的默认模式,其工作原理
根据 Kubernetes 的网络模型,一个Node上的Pod与其他Node 上的Pod 应该能够直
接建立双向的ICP/IP通信通道,所以如果直接修改 iptables 规则,则也可以实现 kube-proxy的功能,只不过后者更加高端,因为是全自动模式的。与第一代的 userspace 模式相比,iptables 模式完全工作在内核态,不用再经过用户态的kube-proxy 中转,因而性能更强。
5.5.2 第三代 Proxy
第二代的iptables模式实现简单,性能也高,但是存在的缺点是当Service和Pod的数量过多时,每个节点上的iptables上的规则中的规则会急速膨胀,导致网络性能下降。
1.8版本引入了IPVS(IP Virtual Server)模式,iptables与IPVS虽然都是基于 Netfilter实现的但是因为定位不同,二者有着本质上的区别,iptables是为防火墙设计的,IPVS是用于高性能负载均衡,并且使用了更高效的数据结构(哈希表),允许几乎无限的规模扩张。
优势:
- 为大型集群提供了更好的可扩展性和性能;
- 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等);
- 支持服务器健康检查和连接重试等功能;
- 可以动态修改 ipset 的集合,即使 iptables 的规则正在使用这个集合。
6.深入分析集群安全机制
- 保证容器与其所在宿主机的隔离
- 限制容器给基础设施或其他容器带来的干扰
- 最小权限原则,即合理限制所有组件的权限,确保组件只执行它被授权的行为,
- 通过限制单个组件的能力来限制它的权限范围
- 明确组件间边界的划分
- 划分普通用户和管理员的角色
- 在必要时允许将管理员权限赋给普通用户
- 允许拥有 Secret 数据 (Keys Certs Passwords) 的应用在集群中运行
6.1 API Server 认证管理
Kubernetes有两种类型账号,一种是集群内部的ServiceAccount,另一种是外部用户账号。Kubernetes并不支持常规的个人账号,但拥有被Kubernetes集群的CA证书签名的有效整数,个人用户就可以被授权访问Kubernetes集群。
- 以证书方式访问普通用户或进程,包括运维人员及kubectl、kubelets等进程
- 以Service Account方式访问Kubernetes的内部服务进程
- 以匿名方式访问的进程
Kubernetes集群提供以下用户身份认证方式
- HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式
- HTTP Bearer Token认证:
- OpenID Connect Token:
- Wehook Token:
- Authentication Proxy认证:
6.2 API Server 授权管理
API Server目前支持以下授权
- AlwaysDeny:表示拒绝所有请求,仅用于测试
- AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略
- ABAC:基于属性的权限控制
- RBAC:基于角色的权限控制
- Webhook:通过调用外部的REST服务对授权进行授权
- Node:是对一种kubelet进行授权的特殊模式
可以在API Server的启动参数 –authorization-mode 可配置多种授权策略,逗号分割
6.3 Admission Control
突破认证和鉴权两道关卡后,还需要通过admission control(准入控制)所控制的一个准入控制链。
6.4 Service Account
Service Account是提供给运行在Pod里的进程用的的,为Pod里的进程提供了必要身份证明。
其主要原理是类似于在请求头中添加了Token字符串,该字符串来自Pod里指定路径下的一个文件(/run/secrets/kubernetes.io/serviceaccount/token),该token是动态生成的,是由Kubernetes Controller进程用API Server的私钥(–service-account-private-file指定的私钥)签名生成的一个JWT Secret。
6.5 Secret 私密凭据
secret主要作用是保管私密数据,如密码、OAuth Token、SSH Keys等信息。
1 |
|
6.6 Pod 安全策略
7.网络原理
7.1 Kubernetes 网络模型
每个Pod都拥有一个独立的IP地址,并假设所有Pod都在一个可以直接连通的、扁平的网络空间中。
实际上,在Kubernetes 世界里,是以Pod 为单位进行分配的。一个Pod 内部的所有容器共享一个网络堆栈(相当于一个网络命名空间,它们的IP地址、网络设备、配置等都是共享的)。按照这个网络原则抽象出来的为每个Pod 都设置一个I地址的模型也被称作 IP-per-Pod 模型。
IP-per-Pod模型是一个简单的兼容性较好的模型。从该模型的网络的端口分配、域名解析、服务发现、负载均衡、应用配置和迁移等角度来看,Pod都能被看做一台独立的虚拟机或者物理机
Kubernetes对集群网络有如下要求:
- 所有Pod都可以在不用NAT的方式下同别的Pod通信
- 所有节点上运行的代理程序(例如kubelet或操作系统守护进程)都可以在不用NAT的方式下同别的Pod通信
- 以hostnetwork模式运行的Pod都可以在不用NAT的方式下同别的Pod通信
Docker 网络基础
Docker技术依赖于近年来Linux内核虚拟化技术的发展,对Linux内核有很强的依赖
7.2.1 网络命名空间
为了支持网络协议栈的多个实例, Linu 在网络栈中引入了网络命名 这些独立的协议栈被隔离到不同的命名空间中 处于不同命名空间中的网络栈是完全隔离的,彼此之间无法通信 通过对网络资源的隔离,就能在一个宿主机上虚拟 多个不同的网络环境
Docker 正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。
对命名空间的操作
- 创建一个命名空间在命名空间中运行命令
1
ip netns add <name>
也可以先通过 bas 命令进入内部的 Shell 界面,然后运行各种命令:1
ip netns exec <name> <command>
退出到外面的命名空间时,请输入 “exit”1
ip netns exec <name> bash
7.2.2 Veth设备对
veth是linux的一种虚拟网络设备,它有点类似于两张网卡中间用一条网线连着,veth设备总是成对出现,通常用来连接不同网络命名空间(下面开始简称NS),一端连着NS1的内核协议栈,另一端连着NS2的内核协议栈,一端发送的数据会立刻被另一端接收。
利用它可以直接将两个网络命名空间连接起来。
源码在:drivers/net/veth.c
创建Veth设备对
1 |
|
查看
1 |
|
如果想要两个veth设备之间能够相互通信,我们还需要为其分配ip
7.2.3 网桥
上面说到的veth设备只能是两个设备之间进行通信,无法多个,而通过网桥可以实现不同Namespace之间的通信
网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间能够相互转发。
网桥能机械收发的报文,读取目标MAC地址的信息,将其与自己记录的MAC表结合,来决策报文的转发目标网络接口。当网络发生变化时,会将报文广播到所有接口。
1 |
|
常用命令
1 |
|
新增一个网桥设备:
1 |
|
将物理网卡和网桥连接起来
1 |
|
网桥的物理网卡作为一个网口,由于在链路层工作,就不在需要IP地址了,这样航母的IP地址自然失效:
1 |
|
给网桥配置一个IP地址
1 |
|
这样网桥就有一个IP地址,而连接到上面的网卡就是一个存链路层设备了
7.2.4 iptables和Netfilter
在Linux 网络协议栈中有 组回调函数挂接点,通过这些挂接点挂接的钩子函数可以Linux 网络栈处理数据包的过程中对数据包进行一些操作,例如过滤 修改 丢弃等。该挂接点技术就叫作 Netfilter和iptables
**Netfilter 负责在内核中执行各种挂接的规则,运行在内核模式中;而 iptables 是在用户模式下运行的进程,负责协助和维护内核中 Netfilter 的各种规则表 。二者相互配合来实现整个 Linux 网络协议栈中灵活的数据包处理机制。**
7.2.5 路由
路由功能由 IP 层维护的一张路由表来实现。当主机收到数据报文时,它用此表来决策接下来应该做什么操作。当从网络侧接收到数据报文时, IP 层首先会检查报文的IP地址是否与主机自身的地址相同 如果数据报文中的 IP 地址是主机自身的地址,那么报文将被发送到传输层相应的协议中。如果报文中的 IP 地址不是主机自身的地址,并且主机配置了路由功能,那么报文将被转发,否则报文将被丢弃。
查看LOCAL表
1 |
|
1 |
|
查看当前路由表
1 |
|
Docker的网络实现
标准的 Docker 支持以下 类网络模式
- host 模式 :使用–net=hos 指定
- container 模式:使用--net=container:NAME or_ID 指定。
- none 模式:使用 –net=none 指定
- bridge 模式:使用--net=bridge 指定,为默认设置
Kubernetes 管理模式下通常只会使用 bridge 模式
在bridge 模式下,Docker Daemon 首次启动时会创建一个虚拟网桥,默认的名称是
docker0,然后按照 RPCI918 的模型在私有网络空间中给这个网桥分配一个子网。针对由
Docker 创建的每一个容器,都会创建一个虛拟以太网设备(veth 设备对),其中一端关联到网桥上,另一端使用 Linux 的网络命名空间技术映射到容器内的etho 设备,然后在网桥的地址段内给eth0 接口分配一个IP地址,
其中 ipl 是网桥的IP地址,Docker Daemon 会在几个备选地址段里给它选一个地址,
通常是以 172 开头的一个地址,这个地址和主机的IP地址是不重叠的。ip2是 Docker 在
启动容器时在这个地址段选择的一个没有使用的IP 地址,它被分配给容器,相应的 MAC
地址也根据这个I地址,在 02:42:ac:11:00:00 和 02:42:ac:11:ff:ff 的范围内生成,这样做
可以确保不会有 ARP 冲突。
启动后,Docker 还将 Veth 设备对的名称映射到etho 网络接口。ip3就是主机的网卡
地址在一般情况下,ipl、ip2 和ip3是不同的DP 段,所以在默认不做任何特殊配置的情况
下,在外部是看不到 ipl 和ip2的。
这样做的结果就是,在同一台机器内的容器之间可以相互通信,不同主机上的容器
能相互通信,实际上它们甚至有可能在相同的网络地址范围内(不同主机上的 dockero
地址段可能是一样的)
为了让它们跨节点相互通信,就必须在主机的地址上分配端口,然后通过这个端口网络流量路由或代理到目标容器上。这样做显然意味着一定要在容器之间小心谨慎地协好端口的分配情况,或者使用动态端口的分配技术。在不同应用之间协调好端口分配情是十分困难的事情,特别是集群水平扩展时。而动态端口分配也会大大增加复杂度,例女每个应用程序都只能将端口看作一个符号(因为是动态分配的,所以无法提前设置)。且 API Server 要在分配完后,将动态端口插人配置的合适位置,服务世必须能相互找到方等。这些都是Docker 的网络模型在跨主机访问时面临的问题。
Docker 开启了一个宏伟的虚拟 网络解决方案——Libnetwork
7.4 Kubrenetes 的网络实现
Kubernetes 络的设计主要致力于解决以下问题
(1) 容器到容器之间的直接通信。
(2) 抽象的 Pod Pod 之间的通信。
(3) Pod Service 之间的通信。
(4) 集群内部与外部组件之间的通信。
7.4.1 容器到容器的通信
同一个Pod内的容器(Pod内的容器不会跨宿主机)共享同一个网络命名空间,共享同一个Linux协议栈。所以对于网络的操作就像在同一台机器上一样,甚至可以通过localhost地址访问彼此的端口。
这么做的原因是简单、安全、高效
7.4.2 Pod之间的通信
- 同一个Node上的Pod之间的通信,直接通过Docker网桥
- 不同Node上的Pod通信
Pod 的地址是与docker0 在同一个网段的,我们知道docker0网段与宿主机网卡是两个
完全不同的IP网段,并且不同Node 之间的通信只能通过宿主机的物理网卡进行,因此要
想实现不同 Node 上 Pod 容器之问的通信,就必领想办法通过主机的这个地址进行寻址
和通信。
另一方面,这些动态分配且藏在 docker0 后的“私有”IP 地址也是可以找到的。Kubernetes 会记录所有正在运行的Pod 的卫分配信息,并将这些信息保存在etcd中(作
为 Service 的 Endpoint )。这些私有卫P信息对于 Pod到Pod 的通信也是十分重要的,因为我们的网络模型要求Pod 到Pod 使用私有IP进行通信。所以首先要知道这些卫是什么。
之前提到,Kubernetes 的网络对 Pod 的地址是平面的和直达的,所以这些 Pod 的IP规划也很重要,不能有冲突。只要没有冲突,我们就可以想办法在整个 Kubernetes 的集群中找到它。
综上所述,要想支持不同 Node 上 Pod 之问的通信,就要满足两个条件:
(1)在整个 Kubernetes 集群中对Pod 的IP 分配进行规划,不能有冲突;
(2)找到一种办法,将Pod 的IP 和所在 Node 的IP 关联起来,通过这个关联让 Pod
可以相互访问。
7.5 Pod和Service网络实战
1 |
|
1 |
|
7.6 CNI 网络模型
Container Network Interface
7.6.1 CNM 网络模型简介
由三部分组成
- Network Sandbox:容器内部的网络栈,包括网络接口、路由表、DNS 等配置的管
理。Sandbox 可通过 Linux 网络命名空间、FreeBSD Jail 等机制进行实现。一个Sandbox 可以包含多个 Endpoint 。
- Endpoint:用于将容器内的 Sandbox 与外部网络相连的网络接口。可以使用 Veth
设备对、Open vSwitch 的内部 port 等技术进行实现。一个 Endpoint 仅能加人一个Network。
- Network:可以直接互连的 Endpoint 的集合。可以通过 Linux 网桥、VLAN 等技术
进行实现一个Network包含多个 Endpoint。
7.6.2 CNI 网络模型详解
CNI是CoreOS公司提出的另一种容器网络规范,已被Kubernetes、rkt、Apache Mesos等项目采纳。
- CNI规范概述
在 CNI模型中只涉及两个概念:容器和网络。
- 容器:是拥有独立 Linux 网络命名空间的环境,例如使用 Docker 或rkt 创建的容器。关键之处是容器需要拥有自己的 Linux 网络命名空间,这是加人网络的必要条件。
- 网络:表示可以互连的一组实体,这些实体拥有各自独立、唯一的卫地址,可以是容器、物理机或者其他网络设备(比如路由器)等。可以将容器添加到一个或多个网络中,也可以从一个或多个网络中删除。
对容器网络的设置和操作都通过插件(Plugin)进行具体实现,CNI指件包括两种类型:CNI Plugin 和 IPAM ( IP Address Management ) Plugin。 CNI Plugin 负责为容器配置网
络资源,IPAM Plugin 负责对容器的卫地址进行分配和管理。IPAM Plugin 作为 CNI Plugin的一部分,与 CNI Plugin一起工作。
7.8 Kubernetes 的网络策略
Network Policy机制的主要功能是对Pod或者Namespace之间的网络通信进行限制和准入控制。
7.8.1 网络策略设置说明
1 |
|
命名空间默认网络策略
1 |
|
案例
1 |
|
7.8.5 NetworkPolicy的发展
Kubernetes 从 1.12版本开始引入了对SCTP 的支持,到1.19版本时达到Beta 阶段,
默认启用,可以通过kube-apiserver 的启动参数-fcature-gates=SCTPSupport-false 进行关闭。启用后,可以在 NetworkPolicy 资源对象中设置 protocol 字段的值为 SCTP,启用对SCTP 的网络隔离设置。需要说明的是,要求 CNI插件提供对 SCTP 的支持。
另外,在区ubernetes 1.20 版本中,以下功能在网络策略(NetworkPolicy API) 中仍然
无法提供实现。如果需要这些功能,则可以选择使用操作系统提供的功能组件如 SELinux,
Open vSwitch、IPTables 等;或7层网络技术如 Ingress Controller、Service Mesh 等;或通过准人控制器 ( Admission Controller)等替代方案进行实现。
强制集群内部的流量都经过一个公共网关(最好使用 Service Mesh 或其他Proxy)。
TLS 相关功能(最好使用 Service Mesh 或其他 Proxy )。
特定于节点(Node)的网络策略(当前无法基于 Kubernetes 信息将网络策略设置在特定节点上)。
按名称指定服务或命名空间(当前仅支持通过 Label进行设置)。
创建或管理第三方提供实现的策略请求 (Policy Request )。
适用于所有命名空间或 Pod 的默认策略。
高级策略查询和可达性工具。
在单个策略声明中指向目标端口号范围的能力。
对网络安全事件进行日志记录的能力(例如连接被阻止或接受的事件)。
显式设置拒绝策略的能力(当前仅支持设置默认的拒绝策略,仅可以添加“允许”的规则)。
阻止通过本地回路(100pback)发起流量或从主机上发起流量的策略管理能力(当前 Pod 无法阻止从本机 Localhost 发起的访问,也不能阻止从其他同组节点发起的
访问)。
7.9 Kubernetes 对IPv4 和IPv6双栈的支持
在api-server启动时添加启动参数
8.存储原理和应用
8.1 Kubernetes存储机制概述
Volume是与Pod绑定的(独立于容器)与Pod具有相同的生命周期的资源对象。我们可以理解为目录或者文件。
Volume的类型:
- ConfigMap 应用配置
- Secret: 加密数据
- DownwardAPI: od Container 的元数据信息
- ServiceAccountToken: Service Account 中的 token 数据
- Projected Vol me: 种特殊的存储卷类型,用千将一个或多个上述资源对象一次 性挂载到容器内的同 个目录下
Kubemetes 管理的宿主机本地存储类型如下
- EmptyD 临时存储
- HostPat 宿主机目录
持久化存储 (PV) 和网络共享存储类型如下
- CephFS 一种开源共 存储系统
- Cinder: 一种开源共享存储系统
- CSI 容器存储接口(由存储提供商提供驱动程序和存储管理程序)
- FC (Fibre Channe :光纤存储设备
- FlexVol me: 种基千插件式驱动的存储
- Flocker 种开源共 存储系统
Glusterfs 一种开源共享存储系统
- iSCSI: iSCSI 存储设备
- Local: 本地待久化存储
- NFS: 网络文件系统
- PersistentVolumeC!aim: 简称 PVC, 持久化存储的申请空间。
- Portworx Volumes: Portworx 提供的存储服务
- Quobyte Volumes: Quobyte 提供的存储服务
- RBD (Ceph Block Device ): Ceph 块存储
存储厂商提供的存储卷类型如下
- ScaleIO Volumes: DellEMC 的存储设备
- StorageOS: StorageOS 提供的存储服务
- VsphereVolume: VMWare 提供的存储系统
公有云提供的存储卷类型如下。
- AWSElasticBlockStore: AWS 公有 云提供的 lastic Block Store
- AzureDisk: Azure 公有 云提供的 Disk
- Azure e: Azure 公有云提供的 File
- GCEPersistentDisk: GCE 公有云提供的 Persistent Disk
8.1.1 将资源对象映射为存储卷
1 |
|
8.2 持久卷(Persistent Volume)详解
PV(持久卷)是对存储资源的抽象,将存储定义为一种容器应用可以使用的资源PV 由管理员创建和配置,它与存储提供商的具体实现直接相关,例如 GlusterFS、isCSI、RBD 或 GCE 或 AWS 公有云提供的共享存储,通过插件式的机制进行管理,供应用访问和使用。除了 EmptyDir 类型的存储卷,PV 的生命周期独立于使用它的Pod。
PVC 则是用户对存储资源的一个申请。就像 Pod 消耗 Node 的资源一样,PVC 消耗PV资源。PVC 可以申请存储空间的大小(size)和访问模式(例如 Read WriteOnce.ReadOnlyMany I ReadWriteMany).
8.2.1 PV和PVC的工作原理
Kubernetes 支持的 PV 类型如下:
- AWSElasticBlockStore:AWS 公有云提供的 Elastic Block Store。
AzureFile: Azure 公有云提供的 File。
AzureDisk: Azure 公有云提供的 Disko
CephFS:一种开源共享存储系统。
Cinder:OpenStack 块存储系统。
FC ( Fibre Channel):光纤存储设备。
Flex Volume:-种插件式的存储机制。
Flocker:-种开源共享存储系统。
GCEPersistentDisk: GCE 公有云提供的 Persistent Disk。
Glusterts:-种开源共享存储系统。
HostPath:宿主机目录,仅用于单机测试。
iSCSI: isCSI 存储设备
Local:本地存储设备,从Kubernetes 1.7 版本开始引人,到 1.14 版本时达到稳定
版本,目前可以通过指定块设备(Block Device)提供 Local Pv, 或通过社区开发
的 sig-storage-local-static-provisioner 插件管理 Local PV 的生命周期。
NFS:网络文件系统。
Portworx Volumes: Portworx 提供的存储服务。
Quobyte Volumes: Quobyte 提供的存储服务。
RBD ( Ceph Block Device ): Ceph 块存储。
Scalelo Volumes: DelIEMC 的存储设备。
StorageOS: StorageOs 提供的存储服务。
VsphereVolume: VMWare 提供的存储系统。
每种存储类型都有各自的特点,在使用时需要根据它们各自的参数进行设置。
8.3 动态存储管理实战: GlusterFS
1 |
|
8.4 CSI 存储机制详解
Container Storage Interface (CSI)机制,用于在Kubernetes和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储方法。
8.4.1 CSI 的设计背景
Kubernetes 通过 PV、PVC、StorageClass 已经提供了一种强大的基于插件的存储管理机制,但是各种存储插件提供的存储服务都是基于一种被称为 “in-tree〞(树内)的方式提供的,这要求存储插件的代码必须被放进 Kubernetes 的主干代码库中才能被 Kubernetes调用,属于紧耦合的开发模式。这种“in-tree” 方式会带来一些问题:
- 存储插件的代码需要与 Kubernetes 的代码放在同一代码库中,并与Kubernetes 的二进制文件共同发布;
- 存储插件代码的开发者必须遵循 Kubernetes 的代码开发规范;
- 存储插件代码的开发者必须遵循 Kubernetes 的发布流程,包括添加对 Kubernetes存储系统的支持和错误修复;
- Kubernetes 社区需要对存储插件的代码进行维护,包括审核、测试等;
- 存储插件代码中的问题可能会影响 Kubernetes 组件的运行,并且很难排查问题;
- 存储插件代码与 Kubernetes 的核心组件(kubelet 和kube-controller-manager)享有相同的系统特权权限,可能存在可拿性和安全性问题;
- 存储插件代码与 Kubernetes 代码一样被强制要求开源、公开。
8.4.2 CSI的核心组件和部署架构
主要包括两类组件:CSI Controller 和 CSI Node
- CSI Controller 的主要功能是提供仓储服务视角对资源和存储卷进行管理和操作
- CSI Node 的主要功能对主机(Node)上的Volume进行管理和操作。
9.Kubernetes开发指南
9.1 REST概述
REST (Representation State Transfer,表述性状态传递)是由 Roy Thomas Fielding 博士在 他的论文 Archite tural Styles and the Design of Network-based Software Architectures 中提出的一个术语 REST 本身只是为分布式超媒体系统设计的 种架构风格,而不是标准
REST 提出了如下设计准则
- (1) 网络上的所有事物都被抽象为资源 (Resource)
- (2) 每个资源都对应唯一的资源标识符 (Resource Identifier
- (3) 通过通用的连接器接口 (Generic Connector Interface) 对资源进行操作
- (4) 对资源的各种操作都不会改变资源标识符。
- (5) 所有操作都是无状态的 Stateless)
9.2 Kubernetes API
通过设置 kube-apiserver 服务的启动参数 enable-swagger-ui=true 来启用Swagger UI页面,其访间地址为 http: //
9.4 Kubernetes API 的扩展
对 Kubernetes AP 进行扩展,并仍然使用 Kubernetes 的语法对新增 API 进行操作,这非常适用于在 Kubernetes 上通过其 API 实现其他功能(例如第性能指标采集服务)或者测试实验性新特性(例如外部设备驱动)
在 Kubernetes 中,所有对象都被抽象定义为某种资源对象,同时系统会为其设置一个API-URL 人口(API Endpoint ),对资源对象的操作(如新增、删除、修改、查看等)都需要通过 Master 的核心组件 API Server 调用资源对象的 API 来完成。与 API Server 的交互可以通过kubectl 命令行工具或访问其 RESTful API 进行。每个API 都可以设置多个版本,在不同的 API URL 路径下区分,例如 “/api/v1”或“/apis/extensions/v1betal” 等。使用这种机制后,用户可以很方便地定义这些 API 资源对象(YAML 配置),并将其提交给Kubernetes(调用 RESTful API),来完成对容器应用的各种管理工作。
9.4.1 使用CRD扩展API资源
CRD 本身只是一段声明,用于定义用户自定义的资源对象 但仅有 CRD 的定义并没
有实际作用,用户还需要提供管理 CRD 对象的 CRD 控制器 (CRD Controller) ,才能实现 CRD 对象的管理 CRD 控制器通常可以通过 Go 语言进行开发,需要遵循 Kubernetes 的控制器开发规范 基于客户端库 client-go 实现 Informer ResourceEventHandler Workqueue 等组件具 的功能处理逻辑,详细的开发过程请参考官方示例和 client-go 库的说明。
9.4.1 customresourcedefinition.yaml
9.4.2 apiaggregation-apiservice.yaml
10.Kubernetes运维管理
10.1 Node管理
Node的隔离与恢复
隔离节点
1
2
3
4
5
6
7
8apiVersion: v1
kind: Node
metadata:
name: k8s-node-1
labels:
kubernetes.io/hostname: k8s-node-1
spec:
unschedulable: true1
kubectl replace -f unschedule.yaml
使用pacth命令
1
kubectl patch k8s-node-1 -p '{"spec":{"unschedulable":true}}'
使用cordon和uncordon命令
1
kubectl cordon k8s-node-1
Node的扩容
在新的Node上安装docker、kubelet和kube-proxy然后配置其启动参数,将Master URL指定为当前Kubernetes集群Master的地址即可
10.2 更新资源对象的Label
添加label
1
kubectl label pod redis-master-bobrO role=backend
查看label
1
kubectl get pod -Lrole
删除一个label,
1
kubectl l abel pod redis-master-bobrO role
修改,加上–overwrite
1
kubectl label pod redis-master- bobrO role=master --overwr ite
10.3 Namespace 集群环境共享与隔离
- 创建Namespace
1
2
3
4
5
6
7
8
9
10
11
12# namespace-development.yaml
apiVersion: v1
kind: Namespace
metadata:
name: development
---
# namespace-production.yaml
apiVersion: v1
kind: Namespace
metadata:
name: production
10.4 Kubernetes资源管理
1 |
|
- CPU
CPU 的 Requests 和 Limits 是通过 CPU 数(cpus)来度量的。CPU 的资源值是绝对值,而不是相对值,比如0.1CPU 在单核或多核机器上是一样的,都严格等于 0.1 CPU core.
- CPU
- Memory
内存的 Requests 和Limits 计量单位是字节数。使用整数或者定点整数加上国际单位制(Irternational System of Units)来表示内存值。国际单位制包括十进制的E、P、T、G、MK、m. 或二进制的Ei、 pi. Ti. Gi. Mi. Ki. KiB 与MiB 是以二进制表示的字节单位
- Memory
常见的KB 与MB 则是以十进制表示的字节单位,比如:
- 1 KB (KiloByte) = 1000 Bytes = 8000 Bits;
- 1 KiB ( KibiByte) = 2’° Bytes = 1024 Bytes = 8192 Bits,
因此,128974848、129c6、129M、123Mi 的内存配置是一样的。
1 |
|
10.4.2 资源配置范围管理(LimitRange)
在 LimitRange 中, Pod Container 都可以设置 Min Max 、Max Limit/Requests Ratio 参数。 Container 还可以设置 Default Request Default Limit 参数,而 Pod 不能设置 Default Request Default Limit 参数。
1 |
|
10.4.6 Pod中多个容器共享进程命名空间
在某个应用场景中,属于同一个Pod的多个容器直接希望能访问其他容器的进程,例如使用一个Debug容器时,需要对业务应用容器进行查错,这对多个容器环境的进程命名空间(Process Namespace)的共享提出需求。
只需要在Pod定义
shareProcessNamespace=true
即可
1 |
|
10.7 Kubernetes集群监控
1 |
|
10.7.2 Prometheus+Grafana 集群性能监控平台搭建
10.7.2 prometheus-grafana.yaml
10.8.2 Fluent +Elast csearch+Kibana 日志系统部署
10.8.2 elasticsearch-fluentd-kibana.yml
10.9 Kubernetes 的审计机制
Kubemetes 为了加强对集群操作 的安全监管,从 1.4 版本开始引入审计机制,主要体现为审计日志 (Audit Log) 审计日志按照时间顺序记录了与安全相关的各种事件,这些事件有助于系统管理员快速、集中了解发生了什么事情、作用于什么对象、在什么时间发生、谁(从哪儿)触发的、在哪儿观察到的、活动的后续处理行为是怎样的,等等.
1 |
|
10.10 使用 Web UI Dashboard) 管理集群
1 |
|
10.11 Helm: Kubernetes 应用包管理工具
Helm 由 Deis 公司(己被微软收购)发起,用于对需要在 Kubernetes 上部署的复杂应
用进行定义、安装和更新,是 CNCF 基金会的毕业项目,由 Helm 社区维护。Helm 将
Kubernetes 的资源如 Deployment、 Service、ConfigMap、Ingress 等,打包到一个 Chart(图表)中,而 Chart 被保存到 Chart 仓库,由 Chart 仓库存储、分发和共享。Helm 支持应用Chart 的版本管理,简化了 Kubernetes 应用部署的应用定义、打包、部署、更新、删除和回滚等操作。
简单来说,Helm 通过将各种 Kubernetes 资源打包,类似于 Linux 的apt-get 或 yum 工具,来完成复杂软件的安装和部署,并且支持部署实例的版本管理等,大大简化了在
Kubernetes 上部署和管理应用的复杂度。
11.Trouble Shooting指导
查看服务详情
1
kubectl describe service redis-master
查看容器日志
1
kubectl logs redis-master
如果在某个 Pod 中包含多个容器,就需要通过 参数指定容器的名称来查看,例如:
1
kubectl logs <pod_name> -c <container_name>
切换集群环境
1
2kubectl config view
kubectl config use-context <name>
12.Kubernetes开发中的新功能
- 支持window server
- 对GPU的支持
- Pod的垂直扩容(VPA)
13.Kubernetes核心服务配置详解
14.相关资料
- 《Kubernetes权威指南》第四版
- 链接: https://pan.baidu.com/s/1gnLAIg_M7m-4MwTSCA6U0Q?pwd=a6m2 提取码: a6m2