Contents
1. Kubernetes简介
1.1 是什么
- 基于容器技术的分布式架构方案,源自谷歌内部的大规模集群管理系统Borg
-
希腊语,领航员;抽象数据中心的硬件基础设施,对外暴露的是巨大的资源池
1.2 用途
- 单体应用=>微服务:单机=>集群管理,应用内耦合=>不便维护=>拆解服务=>微服务架构;
- 微服务部署难点:环境需求的差异、持续交付的需求=>容器(单容器单进程模型)=>集群资源的自动化管理和大量容器应用的自动化部署
- 核心功能:集中一切资源、提高资源利用率、帮助开发聚焦核心应用(远离服务发现、扩容、基础设施)、健康检查和自我修复、自动扩容、简化部署
2. Kubernetes组件结构与基本概念
2.1 结构
– Master(主节点、控制平面、1-3台物理主机):整个集群的控制节点,以单进程的形式运行多个组件:
– kubernetes API server:提供HTTP Rest接口,集群内增删改查的唯一入口,集群控制的唯一入口
– Kubernetes Controller Manager:所有资源的自动化控制中心
– kubernetes Scheduler:资源调度器,自动化分配pod
– etcd持久化存储服务:存储所有资源对象的数据
– Node(工作节点,一台物理主机):运行一定的工作负载(pod),
– kubelet: 负责pod对应的容器的创建、启停任务;与master协作,实现集群整体的基本管理
– kube-proxy:实现k8s service的通信与负载均衡机制的重要组件
– 容器引擎:docker,负责容器创建和管理
– 除了上述组件的附加组件:
– k8s dns
– dashboard
– Ingress控制器
– Heapster
– CNI插件
– 关于组件
– 单组件多实例:工作节点的组件运行在一台机器上,master节点上的组件除了调度器和controller manager同一时间只能有一个实例起作用外,其余组件可以以单组件多实例并行运行的模式运行在多台机器上
– 组件如何运行:除了kubelet是系统组件,其他组件作为pod运行;可以在kube-system namespace下查看
2.2 核心组件
2.2.1 K8s API server
- 集群数据唯一访问入口
- 安全:HTTPS安全传输与CA签名数字证书强制双向认证
- RBAC访问控制策略
- api层(Restful api 接口:get list watch)->访问控制(鉴权、核准访问权)->注册表层(保存所有资源对象,如何创建对象、转换资源不同版本)->etcd数据库(存储kv数据,提供watch接口以实现List Watch机制)
List Watch的事件监听链机制:
- 此外还有kube proxy api 接口,通过FQDN(全限定域名)转发请求到特定pod、服务,例如:
curl http://master-ip:8080/api/v1/proxy/namespaces/default/pods/my-pod-name/
访问对应的pod
2.2.2 Controller Manager
- 各种资源的Controller通过设置资源的期望状态、watch资源对象事件自动化管理资源对象;controller manager则负责管理这些controller
-
常见的有:replication controller\node controller\resourceQuota controller\namespace controller\service controller\endpoints controller\pv controller
2.2.3 Scheduler调度器
-
负责pod的调度:接收controller要求创建pod的信号,在node list中选取合适的node分配pod,最后由node上的kubelet实际创建管理pod
-
调度流程:
- 预选调度(xxx predicates预选策略选出候选节点):内置多个预选算法,以插件形式注册在系统中;包含资源要求的相互匹配、标签选择是否满足、端口是否被占用、使用卷要求、是否容忍节点污点、是否符合亲缘性非亲缘性要求
- 确定最优节点 (xxx Priority优选策略计算节点积分,选取积分最高的节点)
2.2.4 kubelet
- 注册节点到master
- 管理pod:创建、停止、健康检查(探针)
2.2.5 kubernetes Service Proxy
- 确保对服务ip和端口的某个连接最终会到达某个pod,同时负责负载均衡
- 功能:
- 起初,kube proxy是真实的tcp/udp代理:pod用cluster IP访问某个服务->流量被本机的iptables转发到本机的kube-proxy->kube-proxy建立到后端pod的tcp/udp连接
- 1.2版本后,kube-proxy只负责监听service\endpoints的变更信息,更新iptables,流量则通过iptables的nat机制直接路由到目标pod;相比前一个方式,iptables的转发操作不需要经过kube-proxy,效率更高
- 进一步,iptables规则膨胀后效率降低,目前使用第三种方式,使用IPVS(IP virtual Server)+ipset,IPVS同样基于内核的netfilter(提供网络协议栈上的操作挂载点,在各个阶段放置hook函数,处理、转发、丢弃流量数据)实现,但是ipVS专门用于高性能负载均衡,并且使用hash表,易于扩张。ipset是iptables的扩展(iptables是线性结构,ipset具有索引)
2.3 基本概念
- Master:集群控制节点
- Node:工作节点
- Pod:工作负载的基本单元
- pod内有一个或者多个业务相关的容器
- 结构上,有一个根容器pause容器;业务容器共享pause容器的ip\挂接的卷资源,pause容器的状态与业务容器无关,代表了整个pod的状态(某个具体业务容器的死亡,不能影响整个pod的状态判断,因而采用此结构;所有资源基于pause容器,简化了共享资源时发生的容器通信过程)。
- pod分为静态和动态,静态pod数据不存放在master上,而是放在node上的具体文件中,只存在此node上,无法被调度
- pod中一个容器死亡会重启整个pod;所在的node死亡则重新调度到别的node
- Label:标签,一组键值对,可以标记版本、环境、架构、分区、质量管控、开发阶段;资源对象之间可以通过标签选择器进行相互匹配
- Replication Controller: rc,定义了一个期望场景:
- pod期待的副本数量
- 筛选pod的标签
- 创建、恢复pod的pod template
- 可以通过命令进行pod的滚动升级。
- 已废弃,继任者:replicaSet\depeloyment,rc的命令基本使用于rs,rs标签选择功能更丰富,而deplyment则是更高一级的资源,隐藏管理rs的细节,提供自动化的pod创建、删除、更新的机制
- deplyment
- horizontal pod autoscaler:hpa,pod横向自动扩容,追踪分析rc控制的pod的负载情况,确定是否调整副本数量。
- 指标:cpu平均利用率(已使用/request)、自定义的度量指标(tps,qps)
- StatefulSet:rc,rs,deployment,daemonset、job都是面向无状态的,statefulset是有状态的
- 每个pod都有稳定、唯一的网络标识,例如ss-0,ss-1
- pod的启停顺序受控,操作第n个pod时,前n-1个必然已经是运行且ready的状态
- pod采用持久化存储,删除pod不会删除相关的存储卷(为了数据安全)
- 需要与headless service配合使用,访问其中的pod需要使用唯一的dns域名
- service:服务入口,
- 通过endpoint访问后端的pod
- 服务发现:
- 内部:唯一的cluster IP和service name,通过dns服务访问
- 外部:NodePort、LB
- job:批处理任务,处理work item
- job控制的容器短暂运行,仅运行一次,失败后不重启====>cronJob,定时启动
- 可以多实例并行处理
- namespace:资源隔离,资源可见行
- annotation:注解,kv对,通常可以记辅助信息
- 存储\数据、配置相关:
- Volume:卷,emtpyDir gitrepo hostpath…
- Persistent Volume:pv 持久卷
- 网络存储
- 定义在pod之外
- 可以定义访问模式,通过pvc引用
- configmap:配置集中化,配置存储在etcd中,声明configmap对象定义配置,pod可以挂载configmap将配置映射到容器中;如果需要加密,可以使用Secret
3. 资源对象定义基本与常用命令
3.1 YAML文件
-
K8s内所有的资源对象都是使用YAML文件定义的;Yaml Ain’t Markup Language(发音
/ˈjæməl/
) -
基本语法:
- 大小写敏感
- 缩进表示层级关系
- 缩进时不允许使用tab键,缩进的空格数没有约束,同一层级的空格数一样
- #表示注释
- 支持的数据结构
- 对象:键值对的集合
-
eg:
name: jeff
,或者同一个对象所有的键值对写在一行:postions: {home: 0, company: 1}
-
数组:一组按次序排列的值
-
一组连词线开头的行,构成一个数组:
- cat - dog - goldfish # 子成员也可以是一个数组 - - pig1 - pig2 - pig3 # 也可以行内 animal: [cat, dog]
- 纯标量 scalars:单个不可分割的值
-
字符串、布尔、整数、浮点、Null、时间、日期
- 两个感叹号可以用来进行强制类型转换
-
字符串默认不需要使用引号,双引号会对特殊字符转义,单引号不会;字符串可以多行,但是第二行开始,每一行之前要有一个空格,换行符会被转义称空格,
|
、>
可以用来保留换行、折叠换行;+ -
可以用来表述是否保留换行符 -
锚点&和别名*,可以用来引用
3.2 K8s资源对象普遍格式
- 指定api版本,
- 资源对象的类型:kind
- metadata:包含名称、命名空间、标签和关于容器的其他信息
- spec包含pod内容的实际说明,例如pod的容器、卷和其他数据
- status包含运行中的资源的当前信息,例如pod所处的条件、每个容器的描述和状态以及内部IP和其他基本信息
可以通过kubectl explain
命令发现资源对象定义所需要的字段信息eg:kubectl explain pod.spec
4. Pod详细
4.1 pod基本
- 单容器单进程模型=>需要一种更高级别的结构将容器组合在一起作为一个单位=>pod:为一些密切相关的进程提供几乎相同的环境,使得它们环境一致的同时又保持相对隔离
- pod中如何设置容器:
- 将多层应用分散在多个容器中:例如前后端
- 基于扩缩容考虑而分割到多个pod中:具有相近扩缩容需求的容器放一起
- 一个pod多个容器的模式通常为一个主进程+多个辅助进程
- 静态Pod:仅存在于特定Node的pod,无法被高一级的资源对象管理
- 配置文件方式:设置kubelet的启动参数
--config
,kubelet会定期读取该目录,如果有配置文件,则启动对应的pod.如果需要删除pod,删除配置文件即可。api server是无法删除这个pod的,但是能查看这个pod的状态 - Http方式,设置kubelet的启动参数
--manifest-url
,则会定期从接口上拉取配置文件
- 配置文件方式:设置kubelet的启动参数
注意事项:Docker会根据pid=1的进程的状态来判断容器的状态,因此如果bash运行脚本时,bash进程是第一个进程,pid=1;运行完脚本,bash就会退出;如果pod此时被高一级的资源托管,可能会出现反复启动、停止的死循环。解决办法是让应用运行在前台,如果不能运行在前台,可以使用类似supervisor这样的工具辅助进行前台运行的功能。
4.2 Pod配置与卷挂载
- pod容器共享Volume:同一个Pod中的多个容器能共享Pod级别的存储卷Volume,Volume可以有多种类型,各个容器各自进行挂载:
- volumes
- pod模板中定义volumeMounts字段
- ConfigMap:集中式的配置管理使得应用和配置分离。应用方便服用,配置也更灵活;Configmap存储kv对,也可以表示一个完整配置文件的内容的内容
- ConfigMap典型用法:
- 生成容器内的环境变量
- 设置容器启动命令的启动参数(需要设置为环境变量)
- 以Volume的形式挂载为容器内部的文件或者目录
-
使用:创建COnfigMap对象=>在pod定义中引用configMap
- 作为环境变量引入env.valueFrom.configMapKeyRef.{name key};envFrom.configMap引用所有键值对
- 挂载卷的形式:volumes.configMap.{name items}
- 注意:
- configMap必须在pod创建之前创建
- ConfigMap不能跨namespace
- 静态pod无法引用configmap
- 挂载时,pod只能挂载其为目录,目录下包含item内容,并且会覆盖原有目录下的内容,
- Download API: pod自身信息的获取,例如自己的名字、ip、所处的namespace、pod资源使用量、注解和标签
- 环境变量 resourceFieldRef.xxx
- 卷挂载
- 敏感数据使用Secret,不过Secret只是多了一层base64编码
4.3 Pod生命周期、重启策略、可用性检查、优雅退出
- 生命周期,pod启动、容器开始运行、退出不完全成功、未知状态
- Pending
- Running
- Successed
- Failed
- Unknown
- 重启策略,重启延迟时间以2N递增,直到5min,并且在成功后10min重置此时间
- Always: 容器一旦失效,kubelet立马重启
- OnFailure: 容器终止运行且退出码不为0,kubelet自动重启
- Never:不重启
- 容器可用性检查
- LivenessProbe 存活探针,通过exec http tcp socket三种方式进行存活检查
- ReadinessProbe 就绪探针,类似,判断pod是否可用
- 两者配置类似:initialDelaySeconds,timeoutSeconds
- 复杂场景:Pod Readiness Gates机制
- 容器生命期hook: exec http
- PostStart
- preStop
- Pod的终止过程:一个pod被删除时,为了应用能够优雅的退出,同时pod又被有效的删除,通常会经历如下的过程:
- 用户发送命令删除pod,默认设置 grace period为30s
- API服务器中的Pod对象被更新使用宽限时间,超过grace period的时间后,pod将被视为死亡
- 客户端查看时,pod状态变更为
Teminating
- 与步骤3同时发生:当kubelet发现Pod被标记为terminating,并且graceful period为30s时,kubelet开启pod关闭过程
- 如果pod中的容器定义了
preStop hook
,将被触发,如果preStop运行超过graceful period,将触发下一步并延长2s的graceful period - 容器被发送SigTerm信号
- 如果pod中的容器定义了
- 与步骤3同时发生:pod从各种上级资源对象的列表中移除,正在关闭中的pod也不会接受来自load balancers的流量
- 最终如果还是超过graceful period的时限,pod会被SigKill信号杀死
- Kubelet完成pod删除过程,grace period被设置为0,pod对象在api服务器上消失,并且客户端也无法查看到该对象
- 默认,
graceful period
是30s,如果不想pod优雅的退出,可以--grace-period=0 --force
进行强制删除,强制删除不保证应用优雅的结束,需要谨慎使用
4.4 Pod调度
4.4.1 标签、注解
- 标签用于组织组员,
metadata.labels
- 展示标签:
--show-labels
,-l xx_label=xxx
- 利用多个标签条件,进行筛选
spec.nodeSelector
通过标签选择被调度到的节点- 注解:也是一种键值对,但是不会用来筛选pod,通常API对象引入新的字段时,会首先放在注解字段
4.4.2 停止和移除Pod
kubectl delete po xx-pod
,kubectl delete po -l xx-label=xx
通过标签删除一组pod- 通过删除命名空间,删除所有pod,
kubectl delete po --all
- 被rc\rs托管的pod,删除一个pod立马会新建另一个,要彻底删除,需要调整上层资源(删除所有资源,
kubectl delete all --all
)
4.5 Pod复杂调度
- 副本管理:RC、RS、Deployment:自动部署一个容器应用的多份副本;其中RC目前不推荐用,推荐用Deployment、RS
- NodeSelector:通过标签来筛选node,如果指定了pod的nodeselector,但是集群中不存在包含相应标签的node,那么pod无法被成功调度
- NodeAffinity: Node亲和性调度,是用于替换NodeSelector的全新调度策略,有两种
- RequiredDuringSchedulingIgnoredDuringExecution: 类似nodeSelector的硬性限制,必须被满足的条件
- PreferredDuringSchedulingIgnoredDuringExecution: 强调优先满足指定规则,相当于软限制,多个优先级规则还可以设置权重,以定义执行的先后顺序;IgnoredDuringExecution的意思是: 如果一个Pod所在的节点在Pod运行期间标签发生了变更, 不再符合该Pod的节点亲和性需求, 则系统将
忽略Node上Label的变化, 该Pod能继续在该节点运行 - 如果同时定义了nodeSelector和nodeAffinity,必须两个条件都满足才能调度
- 如果nodeAffinity指定了多个nodeSelectorTerms,匹配其中一个即可
- 一个nodeSelectorTerm中有多个matchExpressions,则都满足才行调度
- PodAffinity:pod亲和与互斥调度(antiAffinity)策略,另一种pod调度方式,通过node上运行的pod的标签,而不是node的标签,来进行调度,基本与nodeAffinity类似,其中可以使用topologykey字段来筛选node的范围。
- Taints和Tolerations(污点和容忍):之前的是pod如何选择node,污点则是node是否拒绝pod。Taint需要和Toleration配合使用, 让Pod避开那些不合适的Node。 在Node上设置一个或多个Taint之后, 除非Pod明确声明能够容忍这些污
点, 否则无法在这些Node上运行。 Toleration是Pod的属性, 让Pod能够
(注意, 只是能够, 而非必须) 运行在标注了Taint的Node上。- node设置污点属性,pod设置是否能容忍
- 可以自定义驱逐行为,应对节点故障
-
Pod Priority Preemption: pod优先级调度,某些情况下,系统可以选择释放优先级不高的资源,保障最重要的负载能够获取足够的资源稳定运行。定义负载重要性的指标:
- Priority: 优先级
- QoS: 服务质量等里
- 系统定义的其他度量指标
优先级抢占调度策略的核心行为是驱逐(eviction)和抢占(Preemption);驱逐由节点上的kubelet完成,抢占由api server上的scheduler完成
- 节点资源不足时,kubelet会eviction优先级低的,同样优先级时驱逐资源量超过申请量最大倍数的
- 当一个pod因为资源无法满足而不能被导读时,调度器有可能选择驱逐部分低优先级的pod实例来满足此pod的调度目标
用户可以自定义kind为PriorityClass的对象,表征优先级,并在pod中引用此优先级
-
DaemonSet: 在每个Node上仅运行一份Pod的副本实例;支持滚动升级
-
Job 批处理调度:批处理任务分为几种模式:
- Job Template Expansion模式:一个job对象对应一个待处理的work item,适合work item少,单次处理耗时长数据大的场景
- Queue with pod Per Work Item模式:采用一个队列存放Work item,一个Job对象作为消费者去完成这些work item,此模式下,job会创建多个pod,每个pod对应一个work item
- Queue with Variable Pod Count模式,也是任务队列的模式,与上一个类似,但是pod数量是可变的
- Single Job with Static Work Assignment模式,一个job产生多个pod,采用静态方式分配任务,而不是用队列动态分配
考虑并行运行的i情况,kubernetes的job还可以分为以下三种
- Non-paralel jobs: 一个job启动一个pod,除非pod异常,才重启,一旦正常结束,job结束
- Parallel jobs with a fixed completion count: 并行job启动多个pod,此时需要设定job
spec.completions
参数为一个证书,正常结束的pod数量达到此值后,job结束,job.spec.parallelism
参数可以控制并行度 - Parallel jobs with a work queue: 任务队列方式的并行Job需要一个独立的Queue, Work item都在一个Queue中存放, 不能设置
job.spec.completions
参数
- Cronjob:定时任务
4.6 pod升级、回滚、扩缩容
-
升级的方式
- 升级的过程中删除旧版本pod,使用新版本pod
- 先创建新版本,等待成功运行后再删除旧版本
- 滚动升级
- 可以通过
kubectl rolling-update
命令手动升级RC:通过伸缩两个RC将旧pod替换为新pod;但是已经过时了。 -
使用Deployment声明式地升级应用:实际过程同样是伸缩两个RS来实现,升级方式:
- 默认是滚动rollingUpdate,滚动升级时,还可以设置
maxUnavailable
、maxSurge
来控制滚动更新的过程,分别制定更新过程中不可用状态的Pod数量上线,pod总数超过期望副本数的最大值;roll-over时,也就是一次跟新还没结束就开始下一个更新,进行几次更新就会创建几个RS,之前的RS都会被认为是旧版本 - recreate
通常不建议更新Deployment的标签选择器,会导致pod列表发生变化,可能与其他控制器产生冲突
- 默认是滚动rollingUpdate,滚动升级时,还可以设置
-
使用Deployment回滚:
kubectl rollout history
可以查看Deployment的历史记录,如果创建Deployment时使用了--record
参数,CHANGE-Cause列就能看到每个版本使用的命令了,可以通过--revision=3
选择特定版本来查看- 回滚到上一个版本
rollout undo
- 回滚到指定版本
--to-revision
- 回滚到上一个版本
- DaemonSet的更新策略:
- OnDelete: 默认升级策略,只有手动删除旧版本的pod,才会触发新建操作
- RollingUpdate:整个过程与Deployment类似,但是不支持查看和管理DaemonSet的历史记录,不能自动回滚旧版本,需要重新提交旧版本的配置才行
- StatefulSet的更新策略:和Deployment\StatefulSet看齐
-
扩缩容:
kubectl scale
对Deployment进行扩缩容- 自动扩缩容:Horizontal Pod Autoscaler HPA控制器,基于COU使用率进行自动Pod扩缩容的功能
- 指标类型:Pod资源使用率,pod自定义性能指标、对象自定义指标或者外部自定义指标(依赖自定义metrics Server:eg. Prometheus、Microsoft Azure…)
- 扩缩容算法:需求数量=当前数量*(当前指标值/期望指标值),向上取整
4.7 有状态的副本集合StatefulSet
- 什么是有状态的副本集?
- 应用中每一个实例都是不可替代的个体,拥有稳定的名字和状态
- 有状态的pod在失败后重新调度或者启动后,需要保持原有的状态、网络、名称,每个StatefulSet里的pod拥有一组独立的数据卷而有所区别,名字上也不是随机生成,而是有一定的规律(SS名称+index值)
- 基于上述定义,有状态的pod通常可以用他们的名称唯一的定位;
- SS通常对应headless Service
- 扩缩容
- 缩容时,过程可预测,通常删除index最大的pod
- 为了留有时间方便集群转移数据,缩容通常是线性的
- statefulSet中伙伴节点发现:可以通过一个headless service创建SRV记录指向pod的主机名
SRV记录:DNS服务器会把域名解析到一个IP地址,然后此IP地址的主机上将一个子目录与域名绑定。域名解析时会添加解析记录,这些记录包括:A记录、AAAA记录、CNAME记录、MX记录、NS记录等,其中,SRV记录记录了哪台计算机提供了哪个服务的信息。
SRV常见格式:
_Service._Proto.Name TTL Class SRV Priority Weight Port Target Service: 服务名称,前缀“_”是为防止与DNS Label(普通域名)冲突。 Proto: 服务使用的通信协议,_TCP、_UDP、其它标准协议或者自定义的协议。 Name: 提供服务的域名。 TTL: 缓存有效时间。 CLASS: 类别 Priority: 该记录的优先级,数值越小表示优先级越高,范围0-65535。 Weight: 该记录的权重,数值越高权重越高,范围0-65535。 Port: 服务端口号,0-65535。 Target: host地址。
当一个pod需要获取一个StatefulSet里其他pod列表时,需要做的就是触发一次headless service的FDQN的SRV DNS查询.例如Node.js中查询命令为:dns.resolveSrv("kubia.default.svc.cluster.local", callBackFunction)
,返回的记录顺序为随机,不存在优先级顺序。
5. 服务详细
5.1 集群内部访问服务
下述为服务暴露方式的演进过程
- 对于容器应用最简便的方式就是通过TCP/IP+端口号来实现;例如定义一个Web服务的RC,指定容器的ContainerPort为8080,获取pod的ip地址后,既可以通过IP+端口的方式访问应用服务
- 但是Pod是无状态且动态的,因此Pod的Ip是不可靠的,且存在多个应用实例时,需要对服务进行负载均衡,Service资源对象就是做这样的事情的;在RC的基础上,可以通过命令
kubectl expose rc webapp
为webapp这个rc暴露一个服务;k8s会为该服务分配一个虚拟的ClusterIP,集群内可以通过该ip与端口访问该服务;负载均衡的方式,有两种- RoundRobin 默认为轮询
- SessionAffinity: 基于客户端IP进行会话保持,同一个客户端ip,分配同样的后端pod ip
- 多端口服务,复杂的应用在会暴露多个端口,或者不同的端口使用不同的协议,可以在Service的YAML文件中进行指定
Service提供服务的过程中,Service为服务的最前端,之后是EndPoint对象,将服务与Pod IP+port进行关联,流量最后才会到达Pod。通常,Endpoint对象根据服务的标签选择器生成。也可以通过YAML文件创建并绑定服务。
-
外部服务service,如果创建Service时不指定标签选择器,那么将无法创建endpoint对象,这个时候,手动创建endpoint对象绑定服务,并将服务路由到特定的外部ip,即可实现对集群外部服务的访问。相应的,如果之后外部服务添加到集群内,更新服务的标签选择器即可改回来,反之,如果把内部服务迁到外部,删除标签选择器并修改endpoint即可。
服务的
spec.type
默认是ClusterIP,即集群会默认分配给服务一个唯一的虚拟ClusterIP以供服务发现和定位。如果设置其为ExternalName,就可以使服务指向另一个外部服务,在DNS级别创建一条CNAME记录,当解析该服务域名时,DNS服务器将得到另一个域名,从而访问实际的外部服务。 - headless service:通常service通过一个ClusterIP暴露服务并且隐藏了后端pod ip,但是某些情况下,客户端想连接所有的pod,或者用户想要摒弃service提供的默认负载均衡策略;那么用户可以指定clusterIp为None。这类服务即为headless service。这时候,通过dns解析服务时,将会获取多个A记录。可以临时新建一个pod使用nsloop命令查询。
A记录:将域名指向一个IPv4地址(例如:100.100.100.100)
5.2 外部访问
上面有关服务的内容仅限集群内部的服务暴露,但是我们还需要对集群外部提供服务。
- type=nodeport;通过每个 Node 上的 IP 和静态端口(
NodePort
)暴露服务。NodePort
服务会路由到ClusterIP
服务,这个ClusterIP
服务会自动创建 -
type=loadbalancer;使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到
NodePort
服务和ClusterIP
服务。例如apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: MyApp ports: - protocol: TCP port: 80 targetPort: 9376 clusterIP: 10.0.171.239 loadBalancerIP: 78.11.24.19 type: LoadBalancer status: loadBalancer: ingress: - ip: 146.148.47.155
在这些情况下,将根据用户设置的 loadBalancerIP
来创建负载均衡器。 某些云提供商允许设置 loadBalancerIP
。如果没有设置 loadBalancerIP
,将会给负载均衡器指派一个临时 IP。 如果设置了 loadBalancerIP
,但云提供商并不支持这种特性,那么设置的 loadBalancerIP
值将会被忽略掉。设置的146.148.47.155为云服务商提供的负载均衡器的IP地址。 对该Service的访问请求将会通过LoadBalancer转发到后端Pod上, 负载分发的实现方式则依赖于云服务商提供的LoadBalancer的实现机制。
除了上述service的两种方式;还可以通过Ingress来暴露服务, Ingress 不是服务类型,但它充当集群的入口点。 它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下公开多个服务。
- Ingress是一个 API 对象,用于管理对集群中服务的外部访问,通常是 HTTP。Ingress 可以提供负载平衡,SSL 终端和基于名称的虚拟主机。Ingress定义了集群外部到服务的HTTP或者HTTPS路由,流量路由由Ingress的路由规则控制,实现http层的代理,Ingress控制器则负责通过负载平衡实现入口。将 HTTP 和 HTTPS 以外的服务公开给 Internet 时,通常使用以下类型的服务 Service.Type=NodePort或者Service.Type=LoadBalancer
- Ingress可以为同一个域名下的不同路径,分配不同服务,将负载均衡器的数量保持在最低水平,并实现7层代理上的策略控制
- 或者将不同的域名路由到同一个IP地址上的多个主机名
-
Ingressde的TLS安全设置,为了Ingress提供https的安全访问,可以为Ingress中的域名进行TLS安全证书的设置,步骤如下:
- 创建自签名的密钥和SSL证书文件
- 保存证书到k8s的一个Secret资源对象
- 将该Secret对象设置到Ingress.spec.tls字段
6. 共享存储
持久存储PersistentVolume(PV)与持久存储声明PersistentVolumeClaim(PVC)的作用在于屏蔽底层存储实现,将存储抽象为一种字眼,可供pod进行消费,方便用户使用。PVC则是对资源的一种申请。申请时,不同的应用对存储的大小、读写速度、并发性能有一定的要求,这个时候,又引入了StorageClass对象,用于标记存储资源的特性和性能。基于上述资源对象,应用可以实现根据自己需求,申请特定存储资源了。
Kubernetes从1.9版本开始引入容器存储接口Container Storage Interface(CSI) 机制, 目标是在Kubernetes和外部存储系统之间建立一套标准的存储管理接口, 通过该接口为容器提供存储服务, 类似于
CRI(容器运行时接口) 和CNI(容器网络接口) 。
6.1 PV
关键参数
- 存储能力 capacity 未来可能加入IOPS 吞吐率等指标
- 存储卷模式 Volume Mode,包括文件系统,块设备等
- 访问模式 Access Modes ,PV可以支持多种,但是PVC只支持一种
- RWO
- ROX
- RWX
- 存储类别 Class,storage class ,不设置类别的PV,只能被不要求类别的PVC神情
- 回收策略 Reclaim Policy
- 保留
- 回收空间
- 删除
- 挂载参数 mount opions
- 节点亲和性 node affinity
生命阶段
- available 可用状态,未与某个pvc绑定
- Bound 已绑定
- released 绑定的PVC已删除,资源已释放,但是集群还没回收
- Failed 自动资源回收失败
6.2 PVC
关键配置
- 资源请求 request.storage 目前仅支持存储大小
- 访问模式 access mode
- 存储卷模式 文件|块
- PV选择条件 selector 对于PV的筛选
- 存储类别 class 可以不设置
6.4 PV与PVC的生命周期
一次PV的使用可以按照如下流程
- 资源供应:
- 静态模式:管理员手动创建很多PV,定义PVC时根据后端存储特定进行设置
- 动态模式:集群管理员无须手工创建PV, 而是通过StorageClass的设置对后端存储进行描述, 标记为某种类型。 此时要求PVC对存储的类型进行声明, 系统将自动完成PV的创建及与PVC的绑定。 PVC可以声明Class为””, 说明该PVC禁止使用动态模式。
- 资源绑定:PV与PVC匹配后,进行绑定,用户即可使用该存储;如果在系统中没有满足PVC要求的PV, PVC则会无限期处于Pending状态, 直到等到系统管理员创建了一个符合其要求的PV 。
- 资源使用:在容器应用挂载了一个PVC后, 就能被持续独占使用。 不过,多个Pod可以挂载同一个PVC, 应用程序需要考虑多个实例共同访问一块存储空间的问题。
- 资源释放:使用完毕后, 用户可以删除PVC, 与该PVC绑定的PV将会被标记为“已释放”, 但还不能立刻与其他PVC进行绑定。 通过之前PVC写入的数据可能还被留在存储设备上, 只有在清除之后该PV才能再次使用。
- 资源回收:PV的存储空间完成回收,才能供新的PVC绑定和使用 。
6.4 StorageClass
SC抽象了存储资源的定义,一旦创建,无法更改,如需更改,只能删除原有定义重建。
关键参数如下:
- 提供者 provisioner
- 参数,paramenters 资源提供者的参数设置,这个根据不同的资源有所不同
7. 参考
- k8s官网文档
- 《Kubernetes In Action》
- 《Kubernetes权威指南》第四版