K8s基本概念

K8s基本概念

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

  • 调度流程:

    1. 预选调度(xxx predicates预选策略选出候选节点):内置多个预选算法,以插件形式注册在系统中;包含资源要求的相互匹配、标签选择是否满足、端口是否被占用、使用卷要求、是否容忍节点污点、是否符合亲缘性非亲缘性要求
    2. 确定最优节点 (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,则会定期从接口上拉取配置文件

注意事项: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

    1. 作为环境变量引入env.valueFrom.configMapKeyRef.{name key};envFrom.configMap引用所有键值对
    2. 挂载卷的形式: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又被有效的删除,通常会经历如下的过程:
    1. 用户发送命令删除pod,默认设置 grace period为30s
    2. API服务器中的Pod对象被更新使用宽限时间,超过grace period的时间后,pod将被视为死亡
    3. 客户端查看时,pod状态变更为Teminating
    4. 与步骤3同时发生:当kubelet发现Pod被标记为terminating,并且graceful period为30s时,kubelet开启pod关闭过程
      1. 如果pod中的容器定义了preStop hook,将被触发,如果preStop运行超过graceful period,将触发下一步并延长2s的graceful period
      2. 容器被发送SigTerm信号
    5. 与步骤3同时发生:pod从各种上级资源对象的列表中移除,正在关闭中的pod也不会接受来自load balancers的流量
    6. 最终如果还是超过graceful period的时限,pod会被SigKill信号杀死
    7. 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,此时需要设定jobspec.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,滚动升级时,还可以设置maxUnavailablemaxSurge来控制滚动更新的过程,分别制定更新过程中不可用状态的Pod数量上线,pod总数超过期望副本数的最大值;roll-over时,也就是一次跟新还没结束就开始下一个更新,进行几次更新就会创建几个RS,之前的RS都会被认为是旧版本
    • recreate

    通常不建议更新Deployment的标签选择器,会导致pod列表发生变化,可能与其他控制器产生冲突

  • 使用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安全证书的设置,步骤如下:

    1. 创建自签名的密钥和SSL证书文件
    2. 保存证书到k8s的一个Secret资源对象
    3. 将该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的使用可以按照如下流程

  1. 资源供应:
    • 静态模式:管理员手动创建很多PV,定义PVC时根据后端存储特定进行设置
    • 动态模式:集群管理员无须手工创建PV, 而是通过StorageClass的设置对后端存储进行描述, 标记为某种类型。 此时要求PVC对存储的类型进行声明, 系统将自动完成PV的创建及与PVC的绑定。 PVC可以声明Class为"", 说明该PVC禁止使用动态模式。
  2. 资源绑定:PV与PVC匹配后,进行绑定,用户即可使用该存储;如果在系统中没有满足PVC要求的PV, PVC则会无限期处于Pending状态, 直到等到系统管理员创建了一个符合其要求的PV 。
  3. 资源使用:在容器应用挂载了一个PVC后, 就能被持续独占使用。 不过,多个Pod可以挂载同一个PVC, 应用程序需要考虑多个实例共同访问一块存储空间的问题。
  4. 资源释放:使用完毕后, 用户可以删除PVC, 与该PVC绑定的PV将会被标记为“已释放”, 但还不能立刻与其他PVC进行绑定。 通过之前PVC写入的数据可能还被留在存储设备上, 只有在清除之后该PV才能再次使用。
  5. 资源回收:PV的存储空间完成回收,才能供新的PVC绑定和使用 。

6.4 StorageClass

SC抽象了存储资源的定义,一旦创建,无法更改,如需更改,只能删除原有定义重建。

关键参数如下:

  • 提供者 provisioner
  • 参数,paramenters 资源提供者的参数设置,这个根据不同的资源有所不同

7. 参考

  • k8s官网文档
  • 《Kubernetes In Action》
  • 《Kubernetes权威指南》第四版
Comments are closed.