云原生篇-Kubernetes权威指南

image.png
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是一个全新的基于容器技术的分布式架构解决方案,并且是一个一站式的完备的分布式系统开发和支撑平台。

image.png
特性:

  • 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
kubectl get nodes

查看某个node的具体信息

1
kubectl describe node <node_name>

展示了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

image.png
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的关系如图:
image.png
Pod资源文件定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata :
name: myweb
labels:
name: myweb
spec :
containers:
- name: myweb
image: kubeguide/ tomcat- app:v1
ports:
- containerPort: 8080
env :
- name: MYSQL SERVICE HOST
value: 'mysql'
- name: MYSQL SERVICE PORT
value: '3306'

Event:

Event是一个事件的记录,记录了事件的最早产生时间、最后重现时间、重复次数、发起者、类型,以及导致此事件的原因等众多信息。Event通常会被关联到某个具体的资源对象上,是排查故障的重要参考信息,之前我们看到Node的描述信息包括了Event,而Pod同样有Event记录,当我们发现某个Pod迟迟无法创建时,可以用kubectl describe pod xxxx来查看它的描述信息,以定位问题的成因,比如下面这个Event记录信息表明Pod里的一个容器被探针检测为失败一次:

image.png

k8s分配资源以m为单位,其中100-300m表示占用0.1-0.3个CPU

在k8s中一个计算资源进行配额限制需要设置以下两个参数

  • Requests:该资源的最小申请量,系统必须满足要求。
  • Limits:该资源最大允许使用的量,不能被突破,当容器试图使用超过这个量的资源时,可能会被Kubernetes“杀掉”并重启。

image.png
image.png

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
select * from pod where pod’s name =‘redis-slave’

这样的语句。

当前有两种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
2
name=redis-salve,env!=production
name noint {php-frontend},env!=production

Labels定义在metadata中

1
2
3
4
5
6
apiVersion: v1
kind: Pod
metadata :
name: myweb
labels:
name: myweb

管理对象RC和Service则通过Selector字段设置需要关联Pod的 Label:
image.png
其他管理对象如Deployment、ReplicaSet、DaemonSet和Job则可以在Selector中使用基于集合的筛选条件定义,例如:
image.png
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集群中始终只有一个副本:

image.png
在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling),这可以通过执行kubectl scale命令来一键完成:

1
kubectl scale re redis-salve --replicas=3 scale

需要注意的是,删除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模板部分的内容):
image.png
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也极度相似

image.png
完整案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier,oprator: In,values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080

创建Deployment

1
kubectl create -f tomcat-deployment.yaml

查看信息

1
kubectl get deployments

查看rs

1
kubectl get rs

Horizontal Pod Autoscaler

通过手工执行kubectl scale命令,我们可以实现Pod扩容或缩容。如果仅仅到此为止,显然不符合谷歌对Kubernetes的定位目标—自动化、 智能化。

通过追踪分析指定RC控制的所有目标Pod的负载变化情况,来确定是否需要有针对性地调整目标Pod的副本数量,这是HPA的实现原理。当前,HPA有以下两种方式作为Pod负载的度量指标。

  • CPUUtilizationPercentage。

    CPUUtilizationPercentage是一个算术平均值,即目标Pod所有副本自身的CPU利用率的平均值。

具体例子
image.png
等价于

1
kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10
  • 应用程序自定义的度量指标,比如服务在每秒内的相应请求数 (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
$(podname).$(headless service name)

比如一个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其实就是我们经常提起的微服务架构中的一个微服务

image.png
Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。
image.png

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
2
3
4
5
6
7
8
9
apiVersion: v1
kind:Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
selector:
tier: frontend
1
2
3
4
5
6
# 启动实例
kubectl create -f tomcat-server.yaml
# 查看暴露的端口
kubectl get endpoints
# 查看详细信息(Cluster IP)
kubectl get svc tomcat-service -o yaml

很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如Mycat、Codis等常见中间件。Kubernetes Service支持多个Endpoint,在存在多个Endpoint的情况下,要求每个Endpoint都定义一个名称来区分。下面是Tomcat多端口的Service定义样例:

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
name: service-port
- port: 8005
name: shutdown-port
selector:
tier: frontend

服务发现机制

来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
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
name: service-port
selector:
tier: frontend

其中,nodePort:31002这个属性表明手动指定tomcat-service的NodePort为31002,否则Kubernetes会自动分配一个可用的端口。接下来在浏览器里访问http://:31002/,就可以看到Tomcat的欢迎界面了

NodePort的实现方式是在Kubernetes集群里的每个Node上都为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务,在任意Node上运行netstat命令,就可以看到有NodePort端口被监听:

image.png

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等先进的分布式文件系统。

image.png

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
2
3
4
volumes:
- name: "persistent-storage"
hostPaht:
path: "/data"
gcePersistentDisk

使用这种类型的Volume表示使用谷歌公有云提供的永久磁盘(Persistent Disk,PD)存放Volume的数据,它与emptyDir不同,PD上的内容会被永久保存,当Pod被删除时,PD只是被卸载(Unmount),但不会被删除。需要注意的是,你需要先创建一个PD,才能使用gcePersistentDisk。 使用gcePersistentDisk时有以下一些限制条件。
◎ Node(运行kubelet的节点)需要是GCE虚拟机。
◎ 这些虚拟机需要与PD存在于相同的GCE项目和Zone中。

通过gcloud命令即可创建一个PD:

1
gcloud compute disks create --size=500GB --zone=us-centrall my-data-disk

定义gcePersistentDisk类型的Volume的示例如下:

1
2
3
4
5
volumes:
- name: test-volumes
gcePersistentDisk:
pdName: my-data-disk
fsType: ext4
awsElasticBlockStore
NFS

使用NFS网络文件系统提供的共享目录存储数据时,我们需要在系统中部署一个NFS Server。定义NFS类型的Volume的示例如下

1
2
3
4
5
volumes:
- name: nfs
nfs:
server: nfs-server.localhost
path: "/"
其他类型的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
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somepath
server: 172.147.0.156

比较重要的是PV的accessModes属性,目前有以下类型。

  • ReadWriteOnce:读写权限,并且只能被单个Node挂载。
  • ReadOnlyMany:只读权限,允许被多个Node挂载。
  • ReadWriteMany:读写权限,允许被多个Node挂载。

如果某个Pod想申请某种类型的PV,则首先需要定义一个 PersistentVolumeClaim对象:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriterOnce
resource:
requests:
storage: 8Gi

这Pod的Volume定义中引用上述PVC即可

1
2
3
4
volumes:
- name: mypd
persistntVolumeClaim:
claimName: myclaim

Namespace

Namespace(命名空间)是Kubernetes系统中的另一个非常重要的概念,Namespace在很多情况下用于实现多租户的资源隔离。

kubernetes默认会创建一个namespace,如果在创建资源时不指明namespace则默认在默认的namespace下。

创建namespace为development的namespace

1
2
3
4
apiVersion: v1
kind: Namespace
metadata:
name: development

在创建资源对象时指定对应的namespace

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
commond:
- sleep
- "3306"
name: busybox

查看对应的pod(需要指明namespace)

1
kubectl get pods --namespace=development 

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配置集中化的一种简单方案如图
image.png

ServiceAccount

Kubernetes ServiceAccount 是用来表示在 Pod 内运行的进程的身份的。它在 Kubernetes API 中提供了一个 API 对象,表示一组凭证,可以用来作为特定用户访问 API 服务器。ServiceAccounts 被用来给 Pods 访问 API 服务器的权限,用于执行诸如读写持久卷和修改其他 API 对象等任务。ServiceAccount 与一个或多个令牌相关联,可用于对 API 服务器进行身份验证。ServiceAccounts 还允许定义细粒度的授权策略,让控制特定 ServiceAccount 可以执行的操作。
Kubernetes ServiceAccounts 的主要目的是提供一种方法,让 Pods 具有身份和访问 API 服务器的授权,从而允许它们执行特定的操作

1
2
3
4
5
6
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-productpage
labels:
account: productpage

安装Kubernetes

kubernetes的安装可以通过kubeadm或者二进制的方式进行安装

  1. https://mikeygithub.github.io/2021/05/17/yuque/am5lkt/
  2. https://mikeygithub.github.io/2022/11/05/yuque/ii4c5fmarlf7iqma/

简单案例

搭建一个简单Java Web引用程序

1.3.1环境准备

搭建环境及启动集群

1.3.2启 动MySQL服务

1.生成配置文件

1
kubectl create deployment mysql --image=mysql -o yaml --dry-run > mysql-rc.yaml

2.配置文件(当然也可以在kubernetes dashboard上进行创建)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: ReplicationController #副本控制器
metadata:
name: mysql #RC的名称,全局唯一
spec:
replicas: 1 #pod副本的期待数量
selector:
app: mysql #根据此模板创建的pod的副本(实例)
template:
metadata:
labels:
app: mysql #pod副本拥有的标签,对应RC的Selector
spec:
containers: #容器定义配置
- name: mysql #容器名称
image: mysql:5.6 #镜像
ports:
- containerPort: 3306 #端口
env: #环境变量
- name: MYSQL_ROOT_PASSWORD
value: "123456"

3.查看创建结果
image.png

1
kubectl get rc

image.png
3.查看自动创建的pod

1
kubectl get pods

image.png
4.创建与之关联的service

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Service #表明是K8s 的Service
metadata:
name: mysql #Service的名称,全局唯一
spec:
ports:
- port: 3306 #Service对外提供的端口号
selector: #Service对应的Pod拥有这里定义的标签
app: mysql

5.查看Service
image.png

1
kubectl get svc

image.png

可以看到对应的IP和端口,集群中的其他Pod就可以通过Service的Cluster-ip和端口访问它了

1.3.3启动Tomcat应用

1.创建RC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 2
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
1
kubectl create -f myweb-rc.yaml

image.png
2.创建Service

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
selector:
app: myweb

3.浏览器访问

1
2
#IP+端口(注意这个是物理机的IP不是k8s分配的Cluster-IP)
http://10.42.0.232:30001/demo/

1.3.4通过浏览器访问网页

image.pngimage.png

深入掌握Pod

YAML格式的Pod定义文件完整内容

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
apiVersion: v1(版本,String,必选)
kind: Pod
metadata:
name: string
namespace: string
labels:
- name: string
annotations:
- name: string
spec:
containers:
- name: string
image: string
imagePullPolicy: [Always|Never|IfNotPresent]
command: [string]
args: [string]
workingDir: string
volumeMounts:
- name: string
mountPath: string
readOnly: boolean
ports:
- name: string
containerPort: int
hostPort: int
protocol: string
env:
- name: string
value: string
resources:
limits:
cpu: string
memory: string
requests:
cpu: string
memory: string
livenessProbe :
exec:
command: [string]
httpGet:
path: string
port: number
host: string
scheme: string
httpHeaders:
- name: string
value: string
tcpSocket:
port: number
initialDelaySeconds: 0
timeoutSeconds: 0
periodSeconds: 0
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always| Never | OnFailure]
nodeSelector: object
imagePullSecrets:
- name: string
hostNetwork: false
volumes:
- name: string
emptyDir: {}
hostPath:
path: string
secret:
secretName: string
items:
- key: string
path: string
configMap:
name: string
items:
- key: string
path: string

各项详解

属性名称 取值类型 是否必选 取值说明
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方式

  1. 配置文件方式

  2. HTTP方式

    通过设置kubelet的启动参数 --mainfest-urlkubelet会定期从该url地址下载Pod的定义文件创建

Pod容器共享Volume

同一个Pod中的多个容器能够共享Pod级别的存储卷Volume。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: tomcat
image: tomcat
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /usr/local/tomcat/logs
name: app-logs
- name: busybox
image: busybox
command:
- "sh -c tail -f /logs/catalina*.log"
volumeMounts:
- mountPath: /logs
name: app-logs
volumes:
- name: app-logs
emptyDir:
{}

tip:在编写yaml可以在goland中下载k8s编辑插件,编写起来智能提示更加方便

Pod的配置管理

ConfigMap

  • 生成容器内的环境变量
  • 设置容器启动的命令和参数(需要设置为环境变量)
  • 以Volume的形式挂载为容器内部的文件或目录
  1. 通过配置文件方式创建ConfigMap

    1
    2
    3
    4
    5
    6
    7
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: cm-appvars
    data:
    apploglevel: info
    appdatadir: /var/data
    1
    kubectl create -f cm-appvars.yaml
  2. 通过kubectl命令行方式创建

    1
    kubectl create configmap NAME --from-file=[key=]source

    ConfigMap的使用

  3. 通过环境变量方式使用ConfigMap

    1
    2
    3
    4
    5
    6
    env:
    - name: APPLOGLEVEL
    valueFrom:
    configMapKeyRef:
    key: apploglevel
    name: cm-appvars
  4. 通过envForm方式

    1
    2
    3
    envFrom:
    - configMapRef:
    name: cm-appvars
  5. 通过valueMount使用ConfigMap

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    volumes:
    - name: app-logs
    emptyDir:
    {}
    - name: serverxml
    configMap:
    name: cm-appvars
    items:
    - key: key-serverxml
    path: server.xml
    - key: key-loggingproperties
    path: logging.properties

使用ConfigMap的限制条件

  1. ConfigMap必须在Pod之前创建,Pod才能引用它
  2. 如果Pod使用envFrom基于ConfigMap定义环境变量,则无效的环境变量名称(如数字开头)将被忽略,并记录事件在InvalidVariableNames中
  3. ConfigMap受命名空间限制,只有处于相同命名空间的Pod才能引用它
  4. ConfigMap无法用于静态Pod

Pod的生命周期

image.png

Pod的重启策略

  • Always: 当容器失效时,由kubelet自动重启该容器
  • OnFailure: 当容器终止运行且退出码不为 0 时,由 kubelet 自动 重启该容器
  • Never: 不论容器运行状态如何, kubelet 都不会重启该容器。

Pod健康检查探针

  • LivenessProbe探针:用于判断容器是否存活(Running状态),如果LivenessProbe探针探测到容器不健康,则kubelet将“杀掉容器”,如果容器不包含该探针则kubelet认为该容器返回值永远是SUCCESS
  • ReadinessProbe探针:用于判断容器是否可用(Ready状态),达到Ready状态才可以接收请求。
  • StartupProbe探针:某些应用会遇到启动比较慢的情况,例如应用程序启动时需 要与远程服务器建立网络连接,或者遇到网络访问较慢等情况时,会造成容器启动缓慢, 此时 ReadinessProbe 就不适用了,因为这属千“有且仅有一次"的超长延时,可以通过 StartupProbe 探针解决该问题
1
2
3
4
5
6
spec:
containers:
livenessProbe:
httpGet:
port: 80
path: /status/health

Pod调度

  1. 定义Deployment,其保护三个pod副本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    apiVersion: 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
  2. 创建Deployment

    1
    kubectl create -f nginx-deployment.yaml
  3. 查看对应的Deployment

    1
    kubectl get deployments

NodeSelector定向调度

用于将Pod部署到指定的Node上

  1. 首先需要在Node上打标签

    1
    kubectl label nodes kBs-node-1 zone=nort
  2. 查看该节点的标签

    1
    kubectl describe node k8s-node01
  3. 然后在Pod定义文件中修改配置 nodeSelector

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    apiVersion: 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) 值,以定义执行的先后顺序。

  1. RequiredDuringSchedulingIgnoredDuringExecution在运行期间如果node的lable发生改变,其不在理会
  2. 如果同时定义了nodeSelector和nodeAffinity那么必须同时满足才能不是到对应的节点上
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
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity: # 设置Pod的节点亲和性
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms: # 满足一个即可
- matchExpressions: # 必须满足所有的exp
- key: beta.kubernetes.io/arch
operator: In
values:
- arm64
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: disk-type
operator: In
values:
- ssd
weight: 1
containers:
- name: with-node-affinity
image: gcr.io/google_containers/pause:2.0

PodAffinity

Pod亲和性与互斥调度策略,主要解决Pod之间相互依赖

拓扑域:一个拓扑域由一些node节点组成,使用region表示机架、机房等的拓扑区域,用Zone表示地区,这样子跨度更大的拓扑区域

一般情况下我们可以认为一个Node就是一个拓扑域

kubernetes自带以下拓扑域

  • kubernetes.io/hoostname
  • topology.kubernetes.io/region
  • topology.kubernetes.io/zone

Pod的亲和性是在Pod的定义上添加topologyKey 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity: # 设置Pod的节点亲和性
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
containers:
- name: pod-affinity
image: gcr.io/google_containers/pause:2.0

Pod的互斥性通过 podAntiAffinity 实现

Taints 和 Tolerations

taint是解决Node拒绝Pod的运行,简单的说被标记为Taint的节点就是存在问题的节点,比如磁盘要满、资源不足等

为节点打taint

1
kubectl taint nodes k8s-node01 key=value:NoSchedule

键为 key, 值为 value, Taint 的效果是 NoSchedule 这意味着除非 Pod 明确声明可以容忍这个 Taint, 不会被调度到 k8s-node01上。

1
2
3
4
5
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: NoSchedule

Pod优先级调度

如果发生抢占的调度,高优先级Pod就可能抢占节点N,并将低优先级的节点驱逐出节点N

配置优先级资源

1
2
3
4
5
6
7
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 100000
globalDefault: false
description: "this priority class should be used for xxx service pods only"

引用优先级

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: high-priority-pod
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority

DaemonSet

daemonSet是kubernetes 1.2版本新增的一种资源对象,用于管理集群中的每个Node上仅运行一份Pod的副本实例。

适用场景

  • 在每个Node上都运行一个ClusterFs存储或者Ceph存储的Daemon进程
  • 在每个Node上都运行一个日志采集程序,例如Fluentd或者Logstach
  • 在每个Node上都运行一个性能监控程序,采集该节点的性能数据,例如Prometheus Node Exporter、Collectednew Relic agent或者Ganglia gmond等
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
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
selector:
matchLabels:
name: fluentd-cloud-logging
template:
metadata:
namespace: kube-system
labels:
name: fluentd-cloud-logging
spec:
containers:
- name: fluentd-cloud-logging
image: gcr.io/google_containers/fluentd-elasticsearch:1.17
resources:
limits:
cpu: 100m
memory: 200Mi
env:
- name: FLUENTD_ARGS
value: -q
volumeMounts:
- mountPath: /var/log
name: var-log
readOnly: false
- mountPath: /var/lib/docker/containers
readOnly: false
name: containers
volumes:
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: var-log
hostPath:
path: /var/log

查看结果

1
2
3
4
5
6
[root@k8s-master01 ~]# kubectl get pods -n kube-system -o wide -l name=fluentd-cloud-logging
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
fluentd-cloud-logging-g9tj4 0/1 ContainerCreating 0 16s <none> k8s-node02 <none> <none>
fluentd-cloud-logging-lqt87 0/1 ContainerCreating 0 16s <none> k8s-master01 <none> <none>
fluentd-cloud-logging-r294l 0/1 ContainerCreating 0 16s <none> k8s-node01 <none> <none>
[root@k8s-master01 ~]#

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但它采用程序静态方式分配任务,而不是队列动态分配

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: batch/v1
kind: Job
metadata:
name: process-item-task
labels:
jobgroup: jobexample
spec:
template:
metadata:
name: jobexampple
labels:
jobgroup: jobexample
spec:
containers:
- name: c
image: busybox
command:
- "sh"
- "-c"
- "echo Processing item task"
restartPolicy: Never

CronJob定时任务

1
{Minutes} {Hours} {DayofMonth} {Month} {DayofWeek}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
schedule: "*/1 * * * *"

自定义调度器

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
schedulerName: custom-scheduler
containers:
- name: nginx
image: nginx

注意:如果自定义调度器还未在系统中部署,则默认的调度器会忽略这个Pod,这个Pod将永远处于Pending状态

Pod容灾调度

通过设置 topologySpreadConstraints 来将 Pod 均匀调度到不同的 Zone

案例:假如我们的集群被划分为多个Zone,我们有一个应用(对应的Pod标签为app=foo)需要在每个Zone均匀调度以实现容灾

1
2
3
4
5
6
7
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: foo

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
image.png

Init Container初始化容器

初始化操作

  • 等待其他关联组件正确运行(例如数据库或某个后台服务)
  • 基于环境变量或配置模板生成配置文件
  • 从远程数据库获取本地所需配置,或者将自身注册到某个中央数据库中
  • 下载相关依赖包,或者对系统进行一些预配置操作

根据Pod的重启策略,当init container运行失败而且设置了RestartPolicy=Never时,Pod将会启动失败;而设置RestartPolicy=Always时,Pod将会被系统自动重启。

案例:在创建Nginx的Pod之前进行创建index.html文件

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
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
initContainers:
- name: install
image: busybox
command:
- wget
- "-O"
- "/work-dir/index.html"
- https://kubernetes.io
volumeMounts:
- mountPath: "/work-dir"
name: workdir
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: workdir
volumes:
- name: workdir
emptyDir:
{}

Pod的升级和回滚

kubernetes提供滚动升级,如果Pod是通过Deployment创建的,则用户可以在运行时修改Deployment的Pod定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的rollout动作。

Deployment的升级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: 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.23.3
ports:
- containerPort: 80
  1. 通过kubect set image 命令

    1
    kubectl set image deployment/nginx-deployment nginx=nginx:latest

    或者通过kubectl edit 修改 Deployment

    1
    kubectl edit deployment/nginx-deployment
  2. 使用 kubectl rollout status 命令查看 Deployment 的更新过程

    1
    kubectl rollout status deployment/nginx-deployment

滚动升级的原理

系统创建新的RelicaSet,并将其副本数量扩容,然后逐个对旧版本的Pod进行驱逐,最终只剩下新的RS中有Pod
image.png

Demplyment的回滚

  1. 查看部署历史

    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>
  2. 如果想要查看特定版本的信息 加上 –revision=3

    1
    kubectl rollout history deployment/nginx-deployment --revision=3
  3. 回滚到上一个版本

    1
    kubectl rollout undo deployment/nginx-deployment
  4. 回滚到指定版本

    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
2
3
4
5
6
7
8
9
10
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
updateStrategy:
type: RollingUpdate

StatefulSet的更新策略

  • updateStrategy
  • OnDelete
  • Partitioned
  • RollingUpdate

Pod的伸缩扩容

  • 手动模式:通过运行 kubectl scale 命令,对一个Deployment/RC进行Pod副本数量的设置。
  • 自动模式:需要根据用户的指标或者自定义业务指标并指定Pod副本数量的范围。

手动模式

1
kubectl scale deployment nginx-deployment --replicas 5

自动模式

HPA (Horizontal Pod Autoscaler)

指标类型

  1. Pod资源使用率:Pod级别的性能指标,通常是一个比率值,例如CPU的使用率
  2. Pod自定义指标:Pod级别的性能指标,通常是一个数值,例如接收的请求数量
  3. Object自定义指标或外包指标:通常是一个数值,需要容器应用以某种方式提供,例如HTTP URL “metrics”提供或者使用外部服务提供的指标采集URL

扩容算法desireReplicas=ceil[currentReplicas*(currentMetericValue/desiredMetericValue)]
当前副本数 * (当前指标值/期望的指标值)将结果向上取整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
maxReplicas: 3 # 最大最小副本数
minReplicas: 1
scaleTargetRef: # 目标作用对象,可以是Deployment、RC、RS
apiVersion: apps/v1
kind: Deployment
name: php-apache
# V1版本
# targetCPUUtilizationPercentage: 50 # 期望每个Pod的CPU使用率都是50%
metrics:
- type: Resource # Resource/Pods/Object/External
resource:
name: cpu # CPU利用率超过50%后触发扩容
target:
type: Utilization
averageUtilization: 50

基于自定义指标的HPA实践

image.png

使用StatefulSet搭建MongoDB集群

image.png

  1. 创建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

  1. 使用expose命令为某个Pod创建Service

    1
    kubectl expose deployment webapp

    使用yaml文件方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp
    spec:
    ports:
    - port: 8080
    targetPort: 8080
    protocol: TCP
    selector:
    name: webapp #Pod中所有含有标签 name=webapp的Pod
  2. 查看Service

    1
    kubectl get svc
  3. 通过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
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
sessionAffinity: ClientIP # 指定模式
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
name: webapp

设置会话的保持时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
sessionAffinity: ClientIP # 指定模式
sessionAffinityConfig: # 配置
clientIP:
timeoutSeconds: 1000 # 超时时间
ports:
- port: 8080
targetPort: 8080
protocol: TCP
selector:
name: webapp

将外部访问定义未Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.1.1
ports:
- port: 8080

将Service暴露到集群外部

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,用于公有云环境

  4. ExternalName:将Service映射为一个外部域名地址,通过externalName字段设置

  • NodePort类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp
    spec:
    ports:
    - port: 8080
    targetPort: 8080
    nodePort: 8081
    protocol: TCP
    type: NodePort
  • LoadBalancer类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: 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
    4
    status:
    loadBalancer:
    ingress:
    - ip: 192.168.100.21
  • ExternalName类型

    1
    2
    3
    4
    5
    6
    7
    8
    apiVersion: 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
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
type: LoadBalancer
selector:
name: MyApp
ports:
- port: 80
targetPort: 9376
protocol: TCP
appProtocol: HTTP # 应用层协议
clusterIP: 10.12.0.1

Kubernetes的服务发现机制

环境变量方式

一个Pod运行起来的时候系统会自动为其容器运行环境注入所有集群中有效Service信息

image.png
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

  1. 如果Headless Service设置了Label Selector将根据Label Selector查寻后端Pod列表,自动创建Endpoint列表,将服务名(DNS域名)的解析机制设置为当客户端访问服务名时,得到的是全部Endpoint列表,而不是一个确定的IP地址。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    apiVersion: v1
    kind: Service
    metadata:
    name: nginx
    labels:
    app: nginx
    spec:
    ports:
    - port: 80
    clusterIP: None
    selector:
    app: nginx

    image.png

  2. Headless Service 没有设置 Label Selector,k8s不会自动查看Endpoint列表

DNS服务搭建和配置指南

修改每个Node上kubelet的DNS启动参数

1
2
--cluster-dns=169.169.0.100: 为DNS服务的ClusterIP地址
--cluster-domain=cluster.local: 为在DNS服务中设置的域名

部署CoreDNS服务

  1. 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
    31
    apiVersion: 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
    }

  2. 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
    99
    apiVersion: 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


  3. 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
    26
    apiVersion: 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
    12
    apiVersion: 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配置说明

image.png

Node本地DNS缓存

背景:

  1. CoreDNS以Service方式运行通过ClusterIP访问其访问量大时,其压力可能很大(可以通过扩容解决)
  2. 如果kube-proxy通过iptables解析域名性能可能很差

基于以上原因引入了Node本地DNS缓存

image.png
image.png

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: node-local-dns
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile


---
apiVersion: v1
kind: Service
metadata:
name: kube-dns-upstream
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "KubeDNSUpstream"
spec:
ports:
- name: dns
port: 53
protocol: UDP
targetPort: 53
- name: dns-tcp
port: 53
protocol: TCP
targetPort: 53
selector:
k8s-app: kube-dns


---
apiVersion: v1
kind: ConfigMap
metadata:
name: node-local-dns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: Reconcile
data:
Corefile: |
cluster.local:53 {
errors
cache {
success 9984 30
denial 9984 5
}
reload
loop
bind 169.254.20.10
forward . 169.169.0.100 {
force_tcp
}
prometheus :9253
health 169.254.20.10:8081
}
in-addr.arpa:53 {
errors
cache 30
reload
loop
bind 169.254.20.10
forward . 169.169.0.100 {
force_tcp
}
prometheus :9253
}
ip6.arpa:53 {
errors
cache 30
reload
loop
bind 169.254.20.10
forward . 169.169.0.100 {
force_tcp
}
prometheus :9253
}
.:53 {
errors
cache 30
reload
loop
bind 169.254.20.10
forward . 169.169.0.100 {
force_tcp
}
prometheus :9253
}


---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-local-dns
namespace: kube-system
labels:
k8s-app: node-local-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
spec:
updateStrategy:
rollingUpdate:
maxUnavailable: 10%
selector:
matchLabels:
k8s-app: node-local-dns
template:
metadata:
labels:
k8s-app: node-local-dns
annotations:
prometheus.io/port: "9253"
prometheus.io/scrape: "true"
spec:
priorityClassName: system-node-critical
serviceAccountName: node-local-dns
hostNetwork: true
dnsPolicy: Default # Don't use cluster DNS.
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
- effect: "NoExecute"
operator: "Exists"
- effect: "NoSchedule"
operator: "Exists"
containers:
- name: node-cache
image: k8s.gcr.io/k8s-dns-node-cache:1.15.13
resources:
requests:
cpu: 25m
memory: 5Mi
args: [ "-localip", "169.254.20.10", "-conf", "/etc/Corefile", "-upstreamsvc", "kube-dns-upstream" ]
securityContext:
privileged: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9253
name: metrics
protocol: TCP
livenessProbe:
httpGet:
host: 169.254.20.10
path: /health
port: 8081
initialDelaySeconds: 60
timeoutSeconds: 5
volumeMounts:
- mountPath: /run/xtables.lock
name: xtables-lock
readOnly: false
- name: config-volume
mountPath: /etc/coredns
- name: kube-dns-config
mountPath: /etc/kube-dns
volumes:
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
- name: kube-dns-config
configMap:
name: coredns
optional: true
- name: config-volume
configMap:
name: node-local-dns
items:
- key: Corefile
path: Corefile.base

Pod DNS 域名相关特性

对Pod来说,Kubernetes会为其设置一个 <pod-ip>.<namespace>.pod.<cluster-domain>格式的域名,其中“-”替换掉“.”符号

自定义子域名

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
---
apiVersion: v1
kind: Pod
metadata:
name: webapp1
labels:
app: webapp1
spec:
hostname: webapp-1
subdomain: mysubdomain #自定义子域名
containers:
- name: webapp1
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080


---
apiVersion: v1
kind: Service
metadata:
name: mysubdomain
spec:
selector:
app: webapp
clusterIP: None
ports:
- port: 8080

Pod的DNS策略

Kubernetes可以在Pod级别通过dnsPolicy字段设置DNS策略,目前支持的

  • Default:基础Pod所在宿主机的域名解析设置
  • ClusterFirst:优先使用Kubernetes环境的DNS服务
  • ClusterFirestWithHostNet:适用于hostNetwork模式运行的Pod
  • None:忽略Kubernetes集群的DNS配置,需要通过手动配置dnsConfig自定义
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet

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提供服务

image.png
image.png

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
---
apiVersion: v1
kind: Namespace
metadata:
name: nginx-ingress


---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress
namespace: nginx-ingress


---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nginx-ingress
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- update
- create
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- list
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- list
- watch
- get
- apiGroups:
- "extensions"
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- k8s.nginx.org
resources:
- virtualservers
- virtualserverroutes
- globalconfigurations
- transportservers
- policies
verbs:
- list
- watch
- get
- apiGroups:
- k8s.nginx.org
resources:
- virtualservers/status
- virtualserverroutes/status
verbs:
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nginx-ingress
subjects:
- kind: ServiceAccount
name: nginx-ingress
namespace: nginx-ingress
roleRef:
kind: ClusterRole
name: nginx-ingress
apiGroup: rbac.authorization.k8s.io


---
apiVersion: v1
kind: Secret
metadata:
name: default-server-secret
namespace: nginx-ingress
type: Opaque
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN2akNDQWFZQ0NRREFPRjl0THNhWFhEQU5CZ2txaGtpRzl3MEJBUXNGQURBaE1SOHdIUVlEVlFRRERCWk8KUjBsT1dFbHVaM0psYzNORGIyNTBjbTlzYkdWeU1CNFhEVEU0TURreE1qRTRNRE16TlZvWERUSXpNRGt4TVRFNApNRE16TlZvd0lURWZNQjBHQTFVRUF3d1dUa2RKVGxoSmJtZHlaWE56UTI5dWRISnZiR3hsY2pDQ0FTSXdEUVlKCktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUwvN2hIUEtFWGRMdjNyaUM3QlBrMTNpWkt5eTlyQ08KR2xZUXYyK2EzUDF0azIrS3YwVGF5aGRCbDRrcnNUcTZzZm8vWUk1Y2Vhbkw4WGM3U1pyQkVRYm9EN2REbWs1Qgo4eDZLS2xHWU5IWlg0Rm5UZ0VPaStlM2ptTFFxRlBSY1kzVnNPazFFeUZBL0JnWlJVbkNHZUtGeERSN0tQdGhyCmtqSXVuektURXUyaDU4Tlp0S21ScUJHdDEwcTNRYzhZT3ExM2FnbmovUWRjc0ZYYTJnMjB1K1lYZDdoZ3krZksKWk4vVUkxQUQ0YzZyM1lma1ZWUmVHd1lxQVp1WXN2V0RKbW1GNWRwdEMzN011cDBPRUxVTExSakZJOTZXNXIwSAo1TmdPc25NWFJNV1hYVlpiNWRxT3R0SmRtS3FhZ25TZ1JQQVpQN2MwQjFQU2FqYzZjNGZRVXpNQ0F3RUFBVEFOCkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWpLb2tRdGRPcEsrTzhibWVPc3lySmdJSXJycVFVY2ZOUitjb0hZVUoKdGhrYnhITFMzR3VBTWI5dm15VExPY2xxeC9aYzJPblEwMEJCLzlTb0swcitFZ1U2UlVrRWtWcitTTFA3NTdUWgozZWI4dmdPdEduMS9ienM3bzNBaS9kclkrcUI5Q2k1S3lPc3FHTG1US2xFaUtOYkcyR1ZyTWxjS0ZYQU80YTY3Cklnc1hzYktNbTQwV1U3cG9mcGltU1ZmaXFSdkV5YmN3N0NYODF6cFErUyt1eHRYK2VBZ3V0NHh3VlI5d2IyVXYKelhuZk9HbWhWNThDd1dIQnNKa0kxNXhaa2VUWXdSN0diaEFMSkZUUkk3dkhvQXprTWIzbjAxQjQyWjNrN3RXNQpJUDFmTlpIOFUvOWxiUHNoT21FRFZkdjF5ZytVRVJxbStGSis2R0oxeFJGcGZnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBdi91RWM4b1JkMHUvZXVJTHNFK1RYZUprckxMMnNJNGFWaEMvYjVyYy9XMlRiNHEvClJOcktGMEdYaVN1eE9ycXgrajlnamx4NXFjdnhkenRKbXNFUkJ1Z1B0ME9hVGtIekhvb3FVWmcwZGxmZ1dkT0EKUTZMNTdlT1l0Q29VOUZ4amRXdzZUVVRJVUQ4R0JsRlNjSVo0b1hFTkhzbysyR3VTTWk2Zk1wTVM3YUhudzFtMApxWkdvRWEzWFNyZEJ6eGc2clhkcUNlUDlCMXl3VmRyYURiUzc1aGQzdUdETDU4cGszOVFqVUFQaHpxdmRoK1JWClZGNGJCaW9CbTVpeTlZTW1hWVhsMm0wTGZzeTZuUTRRdFFzdEdNVWozcGJtdlFmazJBNnljeGRFeFpkZFZsdmwKMm82MjBsMllxcHFDZEtCRThCay90elFIVTlKcU56cHpoOUJUTXdJREFRQUJBb0lCQVFDZklHbXowOHhRVmorNwpLZnZJUXQwQ0YzR2MxNld6eDhVNml4MHg4Mm15d1kxUUNlL3BzWE9LZlRxT1h1SENyUlp5TnUvZ2IvUUQ4bUFOCmxOMjRZTWl0TWRJODg5TEZoTkp3QU5OODJDeTczckM5bzVvUDlkazAvYzRIbjAzSkVYNzZ5QjgzQm9rR1FvYksKMjhMNk0rdHUzUmFqNjd6Vmc2d2szaEhrU0pXSzBwV1YrSjdrUkRWYmhDYUZhNk5nMUZNRWxhTlozVDhhUUtyQgpDUDNDeEFTdjYxWTk5TEI4KzNXWVFIK3NYaTVGM01pYVNBZ1BkQUk3WEh1dXFET1lvMU5PL0JoSGt1aVg2QnRtCnorNTZud2pZMy8yUytSRmNBc3JMTnIwMDJZZi9oY0IraVlDNzVWYmcydVd6WTY3TWdOTGQ5VW9RU3BDRkYrVm4KM0cyUnhybnhBb0dCQU40U3M0ZVlPU2huMVpQQjdhTUZsY0k2RHR2S2ErTGZTTXFyY2pOZjJlSEpZNnhubmxKdgpGenpGL2RiVWVTbWxSekR0WkdlcXZXaHFISy9iTjIyeWJhOU1WMDlRQ0JFTk5jNmtWajJTVHpUWkJVbEx4QzYrCk93Z0wyZHhKendWelU0VC84ajdHalRUN05BZVpFS2FvRHFyRG5BYWkyaW5oZU1JVWZHRXFGKzJyQW9HQkFOMVAKK0tZL0lsS3RWRzRKSklQNzBjUis3RmpyeXJpY05iWCtQVzUvOXFHaWxnY2grZ3l4b25BWlBpd2NpeDN3QVpGdwpaZC96ZFB2aTBkWEppc1BSZjRMazg5b2pCUmpiRmRmc2l5UmJYbyt3TFU4NUhRU2NGMnN5aUFPaTVBRHdVU0FkCm45YWFweUNweEFkREtERHdObit3ZFhtaTZ0OHRpSFRkK3RoVDhkaVpBb0dCQUt6Wis1bG9OOTBtYlF4VVh5YUwKMjFSUm9tMGJjcndsTmVCaWNFSmlzaEhYa2xpSVVxZ3hSZklNM2hhUVRUcklKZENFaHFsV01aV0xPb2I2NTNyZgo3aFlMSXM1ZUtka3o0aFRVdnpldm9TMHVXcm9CV2xOVHlGanIrSWhKZnZUc0hpOGdsU3FkbXgySkJhZUFVWUNXCndNdlQ4NmNLclNyNkQrZG8wS05FZzFsL0FvR0FlMkFVdHVFbFNqLzBmRzgrV3hHc1RFV1JqclRNUzRSUjhRWXQKeXdjdFA4aDZxTGxKUTRCWGxQU05rMXZLTmtOUkxIb2pZT2pCQTViYjhibXNVU1BlV09NNENoaFJ4QnlHbmR2eAphYkJDRkFwY0IvbEg4d1R0alVZYlN5T294ZGt5OEp0ek90ajJhS0FiZHd6NlArWDZDODhjZmxYVFo5MWpYL3RMCjF3TmRKS2tDZ1lCbyt0UzB5TzJ2SWFmK2UwSkN5TGhzVDQ5cTN3Zis2QWVqWGx2WDJ1VnRYejN5QTZnbXo5aCsKcDNlK2JMRUxwb3B0WFhNdUFRR0xhUkcrYlNNcjR5dERYbE5ZSndUeThXczNKY3dlSTdqZVp2b0ZpbmNvVlVIMwphdmxoTUVCRGYxSjltSDB5cDBwWUNaS2ROdHNvZEZtQktzVEtQMjJhTmtsVVhCS3gyZzR6cFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=


---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-config
namespace: nginx-ingress
data:


---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress
namespace: nginx-ingress
spec:
replicas: 1
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
spec:
nodeSelector:
role: ingress-nginx-controller
serviceAccountName: nginx-ingress
containers:
- image: nginx/nginx-ingress:1.7.2
imagePullPolicy: IfNotPresent
name: nginx-ingress
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
securityContext:
allowPrivilegeEscalation: true
runAsUser: 101 #nginx
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
args:
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret




# mywebsite-ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mywebsite-ingress
spec:
rules:
- host: mywebsite.com
http:
paths:
- path: /demo
pathType: ImplementationSpecific
backend:
service:
name: webapp
port:
number: 8080



curl --resolve mywebsite.com:80:192.168.18.3 http://mywebsite.com/demo/

curl -H 'Host:mywebsite.com' http://192.168.18.3/demo/

核心组件运行机制

Kubernetes API Server 原理解析

api-server是提供Kubernetes各类资源对象(如Pod、RC、Service等)的CRUD及Watch等HTTP REST接口,称为集群内各个功能模块直接的数据交互和通信的中心枢纽。
image.png
image.png

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端口,由该进程负责响应

image.png

API Server 网络隔离的设计

API Server Network Proxy的核心设计思想是将API Server放置在一个独立的网络中,与Node节点的网络相互隔离,然后增加独立的Network Proxy进程来解决这两个网络直接连通性(connectivity)问题
image.png
image.png

Controller Manager 原理解析

一般来说,智能系统和自动系统通常会通过一个“操作系统”不断修正系统的工作状态。在Kubernetes 集群中,每个 Controller 都是这样的一个“操作系统”,它们通过 API Server提供的 (List-Watch)接口实时监控集群中特定资源的状态变化,当发生各种故障导致某资源对象的状态变化时,Controller 会尝试将其状态调整为期望的状态。比如当某个 Node意外宕机时,Node Controller 会及时发现此故障并执行自动化修复流程,确保集群始终处手预期的工作状态下。Controller Manager 是 Kubernetes 中各种操作系统的管理者,是集群内部的管理控制中心,也是Kubernetes 自动化功能的核心。
image.png

  • 确保在当前集群中有N个Pod实例,N是在RC中定义的Pod副本数量
  • 通过调整spec.replicas属性的值来实现系统扩容或者缩容
  • 通过改变Pod模板(主要是镜像版本)来实现系统的滚动升级

Node Controller

kubelet进程在启动时通过API Server注册自身节点信息,并定时向API Server汇报状态信息,API Server在接收这些信息后会将这些信息更新到ETCD中。
image.png

ResourceQuota Controller

资源配额管理确保指定的资源对象在任何时候都不会超量占用系统物理资源,避免由于某些业务进程在设计或实现上的缺陷导致整个系统紊乱甚至意外宕机。

k8s支持三个层次的资源配额管理

  • 容器级别,可以对CPU和Memory进行限制
  • Pod级别,可以对一个Pod内所有的容器的资源进行限制
  • Namespace级别,为Namespace级别的资源限制,包括:Pod数量、RC数量、Service数量等

image.png

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中。
image.png
scheduler只能API Server打交道

  • 输入:待调度的Pod和全部计算节点的信息
  • 输出:目标Pod要“安家”的最优节点
  • 过滤阶段:遍历所有目标Node,筛选出符合要求的候选节点。
  • 打分阶段:在过滤阶段的基础上,采用优选策略计算出每个节点积分,挑选出最佳节点后Scheduler会把目标Pod安置到此节点上,调度完成。

Scheduler Framework

image.png

多调度器特性

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。

image.png

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(用户空间代理)模式。
image.png
起初,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 上,并在这个过程中实现负载均衡功能。
image.png

5.5.2 第二代Proxy

从1.2版本开始,Kubernetes将iptables作为kube-Proxy的默认模式,其工作原理
image.png
根据 Kubernetes 的网络模型,一个Node上的Pod与其他Node 上的Pod 应该能够直
接建立双向的ICP/IP通信通道,所以如果直接修改 iptables 规则,则也可以实现 kube-proxy的功能,只不过后者更加高端,因为是全自动模式的。与第一代的 userspace 模式相比,iptables 模式完全工作在内核态,不用再经过用户态的kube-proxy 中转,因而性能更强。

5.5.2 第三代 Proxy

第二代的iptables模式实现简单,性能也高,但是存在的缺点是当Service和Pod的数量过多时,每个节点上的iptables上的规则中的规则会急速膨胀,导致网络性能下降。
image.png
1.8版本引入了IPVS(IP Virtual Server)模式,iptables与IPVS虽然都是基于 Netfilter实现的但是因为定位不同,二者有着本质上的区别,iptables是为防火墙设计的,IPVS是用于高性能负载均衡,并且使用了更高效的数据结构(哈希表),允许几乎无限的规模扩张。

优势:

  1. 为大型集群提供了更好的可扩展性和性能;
  2. 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等);
  3. 支持服务器健康检查和连接重试等功能;
  4. 可以动态修改 ipset 的集合,即使 iptables 的规则正在使用这个集合。

6.深入分析集群安全机制

  1. 保证容器与其所在宿主机的隔离
  2. 限制容器给基础设施或其他容器带来的干扰
  3. 最小权限原则,即合理限制所有组件的权限,确保组件只执行它被授权的行为,
  4. 通过限制单个组件的能力来限制它的权限范围
  5. 明确组件间边界的划分
  6. 划分普通用户和管理员的角色
  7. 在必要时允许将管理员权限赋给普通用户
  8. 允许拥有 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认证:

image.png

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
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
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: dmFsdWUtMg0K
username: dmFsdWUtMQ0K

---
apiVersion: v1
kind: Pod
metadata:
name: mypod
namespace: myns
spec:
containers:
- name: mycontainer
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret

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 正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。

image.png

对命名空间的操作

  1. 创建一个命名空间
    1
    ip netns add <name>
    在命名空间中运行命令
    1
    ip netns exec <name> <command> 
    也可以先通过 bas 命令进入内部的 Shell 界面,然后运行各种命令:
    1
    ip netns exec <name> bash 
    退出到外面的命名空间时,请输入 “exit”

7.2.2 Veth设备对

veth是linux的一种虚拟网络设备,它有点类似于两张网卡中间用一条网线连着,veth设备总是成对出现,通常用来连接不同网络命名空间(下面开始简称NS),一端连着NS1的内核协议栈,另一端连着NS2的内核协议栈,一端发送的数据会立刻被另一端接收。

利用它可以直接将两个网络命名空间连接起来。
image.png
源码在:drivers/net/veth.c

创建Veth设备对

1
ip link add vethO type veth peer name veth1

查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@k8s-node03 ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens160: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000
link/ether 00:0c:29:af:eb:e6 brd ff:ff:ff:ff:ff:ff
3: ens256: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:af:eb:f0 brd ff:ff:ff:ff:ff:ff
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
5: veth1@vethO: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether be:54:93:39:46:c0 brd ff:ff:ff:ff:ff:ff
6: vethO@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 62:20:58:8c:d1:77 brd ff:ff:ff:ff:ff:ff
[root@k8s-node03 ~]#

如果想要两个veth设备之间能够相互通信,我们还需要为其分配ip

7.2.3 网桥

上面说到的veth设备只能是两个设备之间进行通信,无法多个,而通过网桥可以实现不同Namespace之间的通信

网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间能够相互转发。

网桥能机械收发的报文,读取目标MAC地址的信息,将其与自己记录的MAC表结合,来决策报文的转发目标网络接口。当网络发生变化时,会将报文广播到所有接口。
image.png

1
yum install -y bridge-utils

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Usage: brctl [commands]
commands:
addbr <bridge> add bridge
delbr <bridge> delete bridge
addif <bridge> <device> add interface to bridge
delif <bridge> <device> delete interface from bridge
hairpin <bridge> <port> {on|off} turn hairpin on/off
setageing <bridge> <time> set ageing time
setbridgeprio <bridge> <prio> set bridge priority
setfd <bridge> <time> set bridge forward delay
sethello <bridge> <time> set hello time
setmaxage <bridge> <time> set max message age
setpathcost <bridge> <port> <cost> set path cost
setportprio <bridge> <port> <prio> set port priority
show [ <bridge> ] show a list of bridges
showmacs <bridge> show a list of mac addrs
showstp <bridge> show bridge stp info
stp <bridge> {on|off} turn stp on/off

新增一个网桥设备:

1
brctl addbr xxxx

将物理网卡和网桥连接起来

1
brctl addif xxx ethx

网桥的物理网卡作为一个网口,由于在链路层工作,就不在需要IP地址了,这样航母的IP地址自然失效:

1
ifconfig ethx 0.0.0.0

给网桥配置一个IP地址

1
ifconfig brxxx xx.xx.xx.xxx

这样网桥就有一个IP地址,而连接到上面的网卡就是一个存链路层设备了

7.2.4 iptables和Netfilter

在Linux 网络协议栈中有 组回调函数挂接点,通过这些挂接点挂接的钩子函数可以Linux 网络栈处理数据包的过程中对数据包进行一些操作,例如过滤 修改 丢弃等。该挂接点技术就叫作 Netfilter和iptables

**Netfilter 负责在内核中执行各种挂接的规则,运行在内核模式中;而 iptables 是在用户模式下运行的进程,负责协助和维护内核中 Netfilter 的各种规则表 。二者相互配合来实现整个 Linux 网络协议栈中灵活的数据包处理机制。**

image.png
image.png

7.2.5 路由

路由功能由 IP 层维护的一张路由表来实现。当主机收到数据报文时,它用此表来决策接下来应该做什么操作。当从网络侧接收到数据报文时, IP 层首先会检查报文的IP地址是否与主机自身的地址相同 如果数据报文中的 IP 地址是主机自身的地址,那么报文将被发送到传输层相应的协议中。如果报文中的 IP 地址不是主机自身的地址,并且主机配置了路由功能,那么报文将被转发,否则报文将被丢弃。

查看LOCAL表

1
ip route show table local type local
1
2
3
4
5
6
[root@k8s-node03 ~]# ip route show table local type local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
local 192.168.100.24 dev ens160 proto kernel scope host src 192.168.100.24
local 192.168.100.117 dev ens256 proto kernel scope host src 192.168.100.117
[root@k8s-node03 ~]#

查看当前路由表

1
2
3
4
5
6
[root@k8s-node03 ~]# ip route list
default via 192.168.100.1 dev ens256 proto dhcp metric 100
default via 192.168.100.2 dev ens160 proto static metric 101 linkdown
192.168.100.0/24 dev ens256 proto kernel scope link src 192.168.100.117 metric 100
192.168.100.0/24 dev ens160 proto kernel scope link src 192.168.100.24 metric 101 linkdown
[root@k8s-node03 ~]#

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地址,
image.png
其中 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
image.png

7.4 Kubrenetes 的网络实现

Kubernetes 络的设计主要致力于解决以下问题
(1) 容器到容器之间的直接通信。
(2) 抽象的 Pod Pod 之间的通信。
(3) Pod Service 之间的通信。
(4) 集群内部与外部组件之间的通信。

7.4.1 容器到容器的通信

同一个Pod内的容器(Pod内的容器不会跨宿主机)共享同一个网络命名空间,共享同一个Linux协议栈。所以对于网络的操作就像在同一台机器上一样,甚至可以通过localhost地址访问彼此的端口。

这么做的原因是简单、安全、高效

image.png

7.4.2 Pod之间的通信

  1. 同一个Node上的Pod之间的通信,直接通过Docker网桥

image.png

  1. 不同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
可以相互访问。

image.png

7.5 Pod和Service网络实战

image.pngimage.png

1
2
houte add -net 10.1.20.0 netmask 255.255.255.0 gw 192.168.130 
houte add -net 10.1.30.0 netmask 255.255.255.0 gw 192.168.131
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
---
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
labels:
name: frontend
spec:
replicas: 1
selector:
name: frontend
template:
metadata:
labels:
name: frontend
spec:
containers:
- name: php-redis
image: kubeguide/guestbook-php-frontend
env:
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 80
hostPort: 80

---
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
name: frontend
spec:
ports:
- port: 80
selector:
name: frontend

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。
image.png

7.6.2 CNI 网络模型详解

CNI是CoreOS公司提出的另一种容器网络规范,已被Kubernetes、rkt、Apache Mesos等项目采纳。

image.png

  1. 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之间的网络通信进行限制和准入控制。
image.png

7.8.1 网络策略设置说明

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
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978

命名空间默认网络策略

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
# default deny ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress


# default allow ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress


# default deny egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Egress


# default allow egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress


# default deny ingress and egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

案例

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
# nginx.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx


# networkpolicy-allow-nginxclient.yaml
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-nginxclient
spec:
podSelector:
matchLabels:
app: nginx
ingress:
- from:
- podSelector:
matchLabels:
role: nginxclient
ports:
- protocol: TCP
port: 80




# client1.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: client1
labels:
role: nginxclient
spec:
containers:
- name: client1
image: busybox
command: [ "sleep", "3600" ]

# client2.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: client2
spec:
containers:
- name: client2
image: busybox
command: [ "sleep", "3600" ]

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
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# ConfigMap
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appconfigfiles
data:
key-serverxml: |
<?xml version='1.0' encoding='utf-8'?>
......
key-loggingproperties: "handlers
......
= 4host-manager.org.apache.juli.FileHandler\r\n\r\n"


---
apiVersion: v1
kind: Pod
metadata:
name: cm-test-app
spec:
containers:
- name: cm-test-app
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
volumeMounts:
- name: serverxml
mountPath: /configfiles
volumes:
- name: serverxml
configMap:
name: cm-appconfigfiles
items:
- key: key-serverxml
path: server.xml
- key: key-loggingproperties
path: logging.properties





# Secret
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: dmFsdWUtMg0K
username: dmFsdWUtMQ0K


---
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mycontainer
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
volumes:
- name: foo
secret:
secretName: mysecret




# Downward API
---
apiVersion: v1
kind: Pod
metadata:
name: kubernetes-downwardapi-volume-example
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
annotations:
build: two
builder: john-doe
spec:
containers:
- name: client-container
image: busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
if [[ -e /etc/podinfo/annotations ]]; then
echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations




# Projected Volume
---
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "cpu_limit"
resourceFieldRef:
containerName: container-test
resource: limits.cpu
- configMap:
name: myconfigmap
items:
- key: config
path: my-group/my-config


---
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- secret:
name: mysecret2
items:
- key: password
path: my-group/my-password
mode: 511


---
apiVersion: v1
kind: Pod
metadata:
name: sa-token-test
spec:
containers:
- name: container-test
image: busybox
volumeMounts:
- name: token-vol
mountPath: "/service-account"
readOnly: true
volumes:
- name: token-vol
projected:
sources:
- serviceAccountToken:
audience: api
expirationSeconds: 3600
path: token

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的工作原理

image.png
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
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# glusterfs
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: glusterfs
labels:
glusterfs: daemonset
annotations:
description: GlusterFS DaemonSet
tags: glusterfs
spec:
template:
metadata:
name: glusterfs
labels:
glusterfs-node: pod
spec:
nodeSelector:
storagenode: glusterfs
hostNetwork: true
containers:
- image: gluster/gluster-centos:latest
name: glusterfs
volumeMounts:
- name: glusterfs-heketi
mountPath: "/var/lib/heketi"
- name: glusterfs-run
mountPath: "/run"
- name: glusterfs-lvm
mountPath: "/run/lvm"
- name: glusterfs-etc
mountPath: "/etc/glusterfs"
- name: glusterfs-logs
mountPath: "/var/log/glusterfs"
- name: glusterfs-config
mountPath: "/var/lib/glusterd"
- name: glusterfs-dev
mountPath: "/dev"
- name: glusterfs-misc
mountPath: "/var/lib/misc/glusterfsd"
- name: glusterfs-cgroup
mountPath: "/sys/fs/cgroup"
readOnly: true
- name: glusterfs-ssl
mountPath: "/etc/ssl"
readOnly: true
securityContext:
capabilities: {}
privileged: true
readinessProbe:
timeoutSeconds: 3
initialDelaySeconds: 60
exec:
command:
- "/bin/bash"
- "-c"
- systemctl status glusterd.service
livenessProbe:
timeoutSeconds: 3
initialDelaySeconds: 60
exec:
command:
- "/bin/bash"
- "-c"
- systemctl status glusterd.service
volumes:
- name: glusterfs-heketi
hostPath:
path: "/var/lib/heketi"
- name: glusterfs-run
- name: glusterfs-lvm
hostPath:
path: "/run/lvm"
- name: glusterfs-etc
hostPath:
path: "/etc/glusterfs"
- name: glusterfs-logs
hostPath:
path: "/var/log/glusterfs"
- name: glusterfs-config
hostPath:
path: "/var/lib/glusterd"
- name: glusterfs-dev
hostPath:
path: "/dev"
- name: glusterfs-misc
hostPath:
path: "/var/lib/misc/glusterfsd"
- name: glusterfs-cgroup
hostPath:
path: "/sys/fs/cgroup"
- name: glusterfs-ssl
hostPath:
path: "/etc/ssl"




# heketi
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: heketi-service-account

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: heketi
rules:
- apiGroups:
- ""
resources:
- endpoints
- services
- pods
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: heketi
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: heketi
subjects:
- kind: ServiceAccount
name: heketi-service-account
namespace: default



---
apiVersion: apps/v1
kind: Deployment
metadata:
name: heketi
labels:
glusterfs: heketi-deployment
deploy-heketi: heketi-deployment
annotations:
description: Defines how to deploy Heketi
spec:
replicas: 1
selector:
matchLabels:
name: deploy-heketi
glusterfs: heketi-pod
template:
metadata:
name: deploy-heketi
labels:
name: deploy-heketi
glusterfs: heketi-pod
spec:
serviceAccountName: heketi-service-account
containers:
- image: heketi/heketi
name: deploy-heketi
env:
- name: HEKETI_EXECUTOR
value: kubernetes
- name: HEKETI_FSTAB
value: "/var/lib/heketi/fstab"
- name: HEKETI_SNAPSHOT_LIMIT
value: '14'
- name: HEKETI_KUBE_GLUSTER_DAEMONSET
value: "y"
ports:
- containerPort: 8080
volumeMounts:
- name: db
mountPath: "/var/lib/heketi"
readinessProbe:
timeoutSeconds: 3
initialDelaySeconds: 3
httpGet:
path: "/hello"
port: 8080
livenessProbe:
timeoutSeconds: 3
initialDelaySeconds: 30
httpGet:
path: "/hello"
port: 8080
volumes:
- name: db
hostPath:
path: "/heketi-data"

---
kind: Service
apiVersion: v1
metadata:
name: heketi
labels:
glusterfs: heketi-service
deploy-heketi: support
annotations:
description: Exposes Heketi Service
spec:
selector:
name: deploy-heketi
ports:
- name: deploy-heketi
port: 8080
targetPort: 8080




# topology.json
{
"clusters": [
{
"nodes": [
{
"node": {
"hostnames": {
"manage": [
"k8s-node-1"
],
"storage": [
"192.168.18.3"
]
},
"zone": 1
},
"devices": [
"/dev/sdb"
]
},
{
"node": {
"hostnames": {
"manage": [
"k8s-node-2"
],
"storage": [
"192.168.18.4"
]
},
"zone": 1
},
"devices": [
"/dev/sdb"
]
},
{
"node": {
"hostnames": {
"manage": [
"k8s-node-3"
],
"storage": [
"192.168.18.5"
]
},
"zone": 1
},
"devices": [
"/dev/sdb"
]
}
]
}
]
}



export HEKETI_CLI_SERVER=http://localhost:8080

heketi-cli topology load --json=topology.json




# StorageClass
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gluster-heketi
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://172.17.2.2:8080"
restauthenabled: "false"




# pvc-gluster-heketi.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-gluster-heketi
spec:
storageClassName: gluster-heketi
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi




# pod-use-pvc.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: pod-use-pvc
spec:
containers:
- name: pod-use-pvc
image: busybox
command:
- sleep
- "3600"
volumeMounts:
- name: gluster-volume
mountPath: "/pv-data"
readOnly: false
volumes:
- name: gluster-volume
persistentVolumeClaim:
claimName: pvc-gluster-heketi

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的核心组件和部署架构

image.png
主要包括两类组件:CSI Controller 和 CSI Node

  • CSI Controller 的主要功能是提供仓储服务视角对资源和存储卷进行管理和操作
  • CSI Node 的主要功能对主机(Node)上的Volume进行管理和操作。

8.4.3 csi.yaml

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: //:/swagger-ui 。

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. 隔离节点

    1
    2
    3
    4
    5
    6
    7
    8
    apiVersion: v1
    kind: Node
    metadata:
    name: k8s-node-1
    labels:
    kubernetes.io/hostname: k8s-node-1
    spec:
    unschedulable: true
    1
    kubectl replace -f unschedule.yaml
  2. 使用pacth命令

    1
    kubectl patch k8s-node-1 -p '{"spec":{"unschedulable":true}}'
  3. 使用cordon和uncordon命令

    1
    kubectl cordon k8s-node-1

Node的扩容

在新的Node上安装docker、kubelet和kube-proxy然后配置其启动参数,将Master URL指定为当前Kubernetes集群Master的地址即可
image.png

10.2 更新资源对象的Label

  1. 添加label

    1
    kubectl label pod redis-master-bobrO role=backend
  2. 查看label

    1
    kubectl get pod -Lrole
  3. 删除一个label,

    1
    kubectl l abel pod redis-master-bobrO role
  4. 修改,加上–overwrite

    1
    kubectl label pod redis-master- bobrO role=master --overwr ite

10.3 Namespace 集群环境共享与隔离

  1. 创建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
2
3
4
spec.container[].resources.requests.cpu: 容器初始要求的 CPU 数量
spec.container[].resources.limits.cpu: 容器所能使用的最大 CPU 数量。
spec.container[].resources.requests.memory: 容器初始要求的内存数量
spec.container[].resources.limits.memory: 容器所能使用的最大内存数量。
    1. CPU

      CPU 的 Requests 和 Limits 是通过 CPU 数(cpus)来度量的。CPU 的资源值是绝对值,而不是相对值,比如0.1CPU 在单核或多核机器上是一样的,都严格等于 0.1 CPU core.

    1. Memory

      内存的 Requests 和Limits 计量单位是字节数。使用整数或者定点整数加上国际单位制(Irternational System of Units)来表示内存值。国际单位制包括十进制的E、P、T、G、MK、m. 或二进制的Ei、 pi. Ti. Gi. Mi. Ki. KiB 与MiB 是以二进制表示的字节单位

常见的KB 与MB 则是以十进制表示的字节单位,比如:

  • 1 KB (KiloByte) = 1000 Bytes = 8000 Bits;
  • 1 KiB ( KibiByte) = 2’° Bytes = 1024 Bytes = 8192 Bits,

因此,128974848、129c6、129M、123Mi 的内存配置是一样的。

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
# Resources requests and limits
---
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: wp
image: wordpress
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"

# Huge Page
---
apiVersion: v1
kind: Pod
metadata:
generateName: hugepages-volume-
spec:
containers:
- image: fedora:latest
command:
- sleep
- inf
name: example
volumeMounts:
- mountPath: /hugepages
name: hugepage
resources:
limits:
hugepages-2Mi: 100Mi
memory: 100Mi
requests:
memory: 100Mi
volumes:
- name: hugepage
emptyDir:
medium: HugePages

10.4.2 资源配置范围管理(LimitRange)

在 LimitRange 中, Pod Container 都可以设置 Min Max 、Max Limit/Requests Ratio 参数。 Container 还可以设置 Default Request Default Limit 参数,而 Pod 不能设置 Default Request Default Limit 参数。

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
# LimitRange
---
apiVersion: v1
kind: LimitRange
metadata:
name: mylimits
spec:
limits:
- max:
cpu: "4"
memory: 2Gi
min:
cpu: 200m
memory: 6Mi
maxLimitRequestRatio:
cpu: "2"
memory: "2"
type: Pod
- default:
cpu: 300m
memory: 200Mi
defaultRequest:
cpu: 200m
memory: 100Mi
max:
cpu: "2"
memory: 1Gi
min:
cpu: 100m
memory: 3Mi
maxLimitRequestRatio:
cpu: "5"
memory: "4"
type: Container
# invalid-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: invalid-pod
spec:
containers:
- name: kubernetes-serve-hostname
image: gcr.io/google_containers/serve_hostname
resources:
limits:
cpu: "3"
memory: 100Mi
# limit-test-nginx.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: limit-test-nginx
labels:
name: limit-test-nginx
spec:
containers:
- name: limit-test-nginx
image: nginx
resources:
limits:
cpu: "1"
memory: 512Mi
requests:
cpu: "0.8"
memory: 250Mi
# valid-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: valid-pod
labels:
name: valid-pod
spec:
containers:
- name: kubernetes-serve-hostname
image: gcr.io/google_containers/serve_hostname
resources:
limits:
cpu: "1"
memory: 512Mi

10.4.6 Pod中多个容器共享进程命名空间

在某个应用场景中,属于同一个Pod的多个容器直接希望能访问其他容器的进程,例如使用一个Debug容器时,需要对业务应用容器进行查错,这对多个容器环境的进程命名空间(Process Namespace)的共享提出需求。

只需要在Pod定义 shareProcessNamespace=true 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
shareProcessNamespace: true
containers:
- name: nginx
image: nginx
- name: shell
image: busybox
securityContext:
capabilities:
add:
- SYS_PTRACE
stdin: true
tty: true

10.7 Kubernetes集群监控

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: metrics-server
namespace: kube-system
labels:
k8s-app: metrics-server
spec:
selector:
matchLabels:
k8s-app: metrics-server
template:
metadata:
name: metrics-server
labels:
k8s-app: metrics-server
spec:
serviceAccountName: metrics-server
volumes:
- name: tmp-dir
emptyDir: {}
containers:
- name: metrics-server
image: k8s.gcr.io/metrics-server/metrics-server:v0.3.7
imagePullPolicy: IfNotPresent
args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP
ports:
- name: main-port
containerPort: 4443
protocol: TCP
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- name: tmp-dir
mountPath: /tmp
nodeSelector:
kubernetes.io/os: linux
kubernetes.io/arch: "amd64"
---
apiVersion: v1
kind: Service
metadata:
name: metrics-server
namespace: kube-system
labels:
kubernetes.io/name: "Metrics-server"
kubernetes.io/cluster-service: "true"
spec:
selector:
k8s-app: metrics-server
ports:
- port: 443
protocol: TCP
targetPort: main-port

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:metrics-server
rules:
- apiGroups:
- ""
resources:
- pods
- nodes
- nodes/stats
- namespaces
- configmaps
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:metrics-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:metrics-server
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system




---
apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
name: v1beta1.metrics.k8s.io
spec:
service:
name: metrics-server
namespace: kube-system
group: metrics.k8s.io
version: v1beta1
insecureSkipTLSVerify: true
groupPriorityMinimum: 100
versionPriority: 100

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:aggregated-metrics-reader
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rules:
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: metrics-server:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: metrics-server-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system

10.7.2 Prometheus+Grafana 集群性能监控平台搭建

10.7.2 prometheus-grafana.yaml

10.8.2 Fluent +Elast csearch+Kibana 日志系统部署image.png

10.8.2 elasticsearch-fluentd-kibana.yml

10.9 Kubernetes 的审计机制

Kubemetes 为了加强对集群操作 的安全监管,从 1.4 版本开始引入审计机制,主要体现为审计日志 (Audit Log) 审计日志按照时间顺序记录了与安全相关的各种事件,这些事件有助于系统管理员快速、集中了解发生了什么事情、作用于什么对象、在什么时间发生、谁(从哪儿)触发的、在哪儿观察到的、活动的后续处理行为是怎样的,等等.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- "RequestReceived"
rules:
- level: RequestResponse
resources:
- group: "" #core API group
resources: ["pods"]
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
- level: Request
resources:
- group: ""
- group: "extensions"

10.10 使用 Web UI Dashboard) 管理集群

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
---
apiVersion: v1
kind: Namespace
metadata:
name: kubernetes-dashboard

---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard

---
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
ports:
- port: 443
targetPort: 8443
selector:
k8s-app: kubernetes-dashboard

---
apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-certs
namespace: kubernetes-dashboard
type: Opaque

---
apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-csrf
namespace: kubernetes-dashboard
type: Opaque
data:
csrf: ""

---
apiVersion: v1
kind: Secret
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-key-holder
namespace: kubernetes-dashboard
type: Opaque

---
kind: ConfigMap
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-settings
namespace: kubernetes-dashboard

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"]
verbs: ["get", "update", "delete"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["kubernetes-dashboard-settings"]
verbs: ["get", "update"]
- apiGroups: [""]
resources: ["services"]
resourceNames: ["heapster", "dashboard-metrics-scraper"]
verbs: ["proxy"]
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"]
verbs: ["get"]

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
rules:
# Allow Metrics Scraper to get metrics from the Metrics server
- apiGroups: ["metrics.k8s.io"]
resources: ["pods", "nodes"]
verbs: ["get", "list", "watch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: kubernetes-dashboard
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubernetes-dashboard
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kubernetes-dashboard

---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kubernetes-dashboard
template:
metadata:
labels:
k8s-app: kubernetes-dashboard
spec:
containers:
- name: kubernetes-dashboard
image: kubernetesui/dashboard:v2.0.5
imagePullPolicy: Always
ports:
- containerPort: 8443
protocol: TCP
args:
- --auto-generate-certificates
- --namespace=kubernetes-dashboard
volumeMounts:
- name: kubernetes-dashboard-certs
mountPath: /certs
- mountPath: /tmp
name: tmp-volume
livenessProbe:
httpGet:
scheme: HTTPS
path: /
port: 8443
initialDelaySeconds: 30
timeoutSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
volumes:
- name: kubernetes-dashboard-certs
secret:
secretName: kubernetes-dashboard-certs
- name: tmp-volume
emptyDir: {}
serviceAccountName: kubernetes-dashboard
nodeSelector:
"kubernetes.io/os": linux
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule

---
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
ports:
- port: 8000
targetPort: 8000
selector:
k8s-app: dashboard-metrics-scraper

---
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: dashboard-metrics-scraper
template:
metadata:
labels:
k8s-app: dashboard-metrics-scraper
annotations:
seccomp.security.alpha.kubernetes.io/pod: 'runtime/default'
spec:
containers:
- name: dashboard-metrics-scraper
image: kubernetesui/metrics-scraper:v1.0.6
ports:
- containerPort: 8000
protocol: TCP
livenessProbe:
httpGet:
scheme: HTTP
path: /
port: 8000
initialDelaySeconds: 30
timeoutSeconds: 30
volumeMounts:
- mountPath: /tmp
name: tmp-volume
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2001
serviceAccountName: kubernetes-dashboard
nodeSelector:
"kubernetes.io/os": linux
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
volumes:
- name: tmp-volume
emptyDir: {}

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 上部署和管理应用的复杂度。

image.png

11.Trouble Shooting指导

  1. 查看服务详情

    1
    kubectl describe service redis-master
  2. 查看容器日志

    1
    kubectl logs redis-master

    如果在某个 Pod 中包含多个容器,就需要通过 参数指定容器的名称来查看,例如:

    1
    kubectl logs <pod_name> -c <container_name>
  3. 切换集群环境

    1
    2
    kubectl config view
    kubectl config use-context <name>

12.Kubernetes开发中的新功能

  • 支持window server
  • 对GPU的支持
  • Pod的垂直扩容(VPA)

13.Kubernetes核心服务配置详解

14.相关资料

  1. 《Kubernetes权威指南》第四版
  2. 链接: https://pan.baidu.com/s/1gnLAIg_M7m-4MwTSCA6U0Q?pwd=a6m2 提取码: a6m2

云原生篇-Kubernetes权威指南
https://mikeygithub.github.io/2021/05/22/yuque/云原生篇-Kubernetes权威指南/
作者
Mikey
发布于
2021年5月22日
许可协议