本篇文章参考了知乎笔记, 只做格式整理

如何区分 apiVersion

  • v1: Pod, Service, ConfigMap, Secret, Namespace, PersistentVolumeClaim
  • apps/v1: Deployment, StatefulSet, DaemonSet, ReplicaSet

accessMode 访问模式

  • ReadWriteOnce (RWO): 只能被单个节点挂载读写
  • ReadOnlyMany (ROX): 可以被多个节点挂载为只读
  • ReadWriteMany (RWX): 可以被多个节点挂载读写
  • ReadWriteOncePod (RWOP): 只能被单个 Pod 挂载读写, 是在 Kubernetes 1.22+ 和一些 CSI 驱动下才支持的模式, 保证同一卷只能被同一个 Pod 读写, 即使 Pod 在不同节点重调度也不允许与其他 Pod 并发访问

Service 服务

  • 内部服务: 通过统一的接口来访问一组 Pod, 使得 Pod 的 IP 地址可以动态变化, 而不影响访问者的使用 (对访问者透明)
  • 外部服务: 通过统一的接口放接受外部的访问输入

k8s_service_concept.png

Headless Service 无头服务

  • 在 Kubernetes 中定义一个 Service 时, 如果我们将 clusterIP: None 设置在 Service 中, 就创建了一个 Headless Service;
  • 这意味着 Kubernetes 不会为这个 Service 分配一个虚拟 IP, 也不会自动做负载均衡, 而是会将 DNS 查询解析为后端每一个 Pod 的 IP 地址;
    • 默认的 pod 会为了负载均衡来隐藏 pod 名称和 ip 地址, 而 Headless Service 则会暴露 pod 名称和 ip 地址;
    • 通过暴露这些信息来允许直接访问, 独立持久化存储

Config Map 配置映射

  1. 使用k8s部署应用, 当你将应用配置写进代码中, 就会存在一个问题, 更新配置时也需要打包镜像, configmap可以将配置信息和docker镜像解耦;
  2. 使用微服务架构的话, 存在多个服务共用配置的情况, 如果每个服务中单独一份配置的话, 那么更新配置就很麻烦, 使用configmap可以友好的进行配置共享;

使用ConfigMap有以下几个限制条件:

  • ConfigMap必须在pod之前创建
  • Configmap受namespace的限制, 只能相同namespace的pod才可以引用

最简单的例子就是, 部署 hdfs-kubernetes 环境的时候, 使用一个 configmap.yaml
来专门存储 hdfs 的配置文件 (即 core.xml 和 hdfs-site.xml) 即可

Stateful Set 状态集合

Deployment 只适合于编排"无状态应用", 它会假设一个应用的所有 Pod 是完全一样的, 互相之间也没有顺序依赖, 也无所谓运行在哪台宿主机上;
正因为每个Pod都一样, 在需要的时候可以水平扩/缩, 增加和删除 Pod

但是并不是所有应用都是无状态的, 尤其是每个实例之间有主从关系的应用和数据存储类应用, 针对这类应用使用Deployment控制器无法实现正确调度, 所以Kubernetes里采用了另外一个控制器StatefulSet负责调度有状态应用的Pod, 保持应用的当前状态始终等于应用定义的所需状态

什么是StatefulSet

当你需要关心Pod的部署顺序, 对应的持久化存储或者要求 Pod 拥有固定的网络标识(即使重启或者重新调度后也不会变)时, StatefulSet控制器会帮助你, 完成调度目标

每个由StatefulSet创建出来的Pod都拥有一个序号(从0开始)和一个固定的网络标识;你还可以在YAML定义中添加 VolumeClaimTemplate 来声明Pod存储使用的PVC

k8s_statefulset_concept.png

StatefulSet 把有状态应用需要保持的状态抽象分为了两种情况:

  • 拓扑状态; 这种情况意味着, 应用的多个实例之间不是完全对等的关系;这些应用实例, 必须按照某些顺序启动
    • 比如应用的主节点 A 要先于从节点 B 启动;而如果你把 A 和 B 两个 Pod 删除掉, 它们再次被创建出来时也必须严格按照这个顺序才行;
    • 并且, 新创建出来的 Pod, 必须和原来 Pod 的网络标识一样, 这样原先的访问者才能使用同样的方法, 访问到这个新 Pod
  • 存储状态; 这种情况意味着, 应用的多个实例分别绑定了不同的存储数据;
    • 对于这些应用实例来说, Pod A 第一次读取到的数据, 和Pod A 被重新创建后再次读取到的数据, 应该是同一份 ;
    • 这种情况最典型的例子, 就是一个数据库应用的多个存储实例;

所以, StatefulSet 的核心功能, 就是通过某种方式记录这些状态, 然后在 Pod 被重新创建时, 能够为新 Pod 恢复这些状态

保持应用的拓扑状态

Headless Service

想要维护应用的拓扑状态, 必须保证能用固定的网络标识访问到固定的Pod实例, Kubernetes是通过Headless Service 给每个Endpoint(Pod)添加固定网络标识的, 所以接下来我们花些时间了解下Headless Service

Service: Service 是在逻辑抽象层上定义了一组Pod, 为他们提供一个统一的固定IP和访问这组Pod的负载均衡策略;
对于 ClusterIP 模式的 Service 来说, 它的 A 记录的格式是: serviceName.namespace.svc.cluster.local, 当你访问这条 A 记录的时候, 它解析到的就是该 Service 的 VirtualIP 地址;
对于指定了 clusterIP=None 的 Headless Service来说, 它的 A 记录的格式跟上面一样, 但是访问记录后返回的是Pod的IP地址集合;Pod 也会被分配对应的 DNS A 记录, 格式为: podName.serviceName.namesapce.svc.cluster.local

普通的Service都有ClusterIP, 它其实就是一个虚拟IP, 会把请求转发到该Service所代理的某一个Pod上