作者:STEFAN SCHIMANSKI AND MICHAEL HAUSENBLAS JULY 21, 2017
翻译:狄卫华
原文:Kubernetes deep dive: API Server – part 2
原文链接:https://blog.openshift.com/kubernetes-deep-dive-api-server-part-2/
本系列翻译链接:
- Kubernetes深入系列:API Server-Part 1
- Kubernetes深入系列:API Server-Part 2
- Kubernetes深入系列:API Server-Part 3a
- Kubernetes深入系列:为自定义资源生成代码
欢迎来到我们的 Kubernetes API Server 深入了解系列的第二部分。 上次 我们 Stefan 和 Michael 介绍了 API Server 作用,术语,并讨论了请求流如何工作。 这次,我们将重点讨论一个我们以前提到的话题:在哪里以及如何以可靠和持久的方式管理 Kubernetes 对象的状态。 您可能还记得,API Server 本身是无状态的,并且是与分布式存储 etcd 直接通信的唯一组件。
etcd 快速介绍
在你的 *nix操作系统中,你知道 /etc
被用来存储配置数据,事实上,etcd
的名字受这个启发,添加了代表分布式的 “d”。 任何分布式系统都可能需要类似 etcd 的组件来存储关于系统状态的数据,使其能够以一致可靠的方式检索状态。 为了协调分布式设置中的数据访问,etcd使用 Raft 协议。 从概念上讲,数据模型 etcd 支持的是键值存储。 在 etcd2 中,这些键形成了一个层次结构,随着 etcd3 的引入,结构变成了一个扁平模型,同时保持了关于分层键的向后兼容性:
使用 etcd 的容器化版本,我们可以创建上面的树结构,然后按如下方式检索它:
1 | $ docker run --rm -d -p 2379:2379 \ |
现在我们已经确定了etcd 的工作原理,接下来讨论如何在 Kubernetes 中是如何使用 etcd 的。
在 etcd 中保存集群状态
在 Kubernetes 中,etcd 是控制平面的独立组件。 直到 Kubernetes 1.5.2,我们使用了 etcd2,此后版本切换到 etcd3。 请注意,在 Kubernetes 1.5.x 中,etcd3 仍然在 v2 API 模式下使用,并且将会更改为 v3 API,包括使用的数据模型。 从开发人员的角度来看,这并没有直接的影响,因为 API Server 提供了操作的抽象层 –管理 v2 与 v3 的存储后端实现。 但是从集群管理员的角度来看,需要了解 etcd 版本相关信息,因为不同版本的维护任务(如备份和恢复)需要以不同方式处理。
您可以在启动时通过许多选项修改 API Server 使用 etcd 的方式; 另请注意,下面的输出比较重要的地方将被被突出显示出来:
1 | $ kube-apiserver -h |
Kubernetes 将其对象作为 JSON 字符串或 Protocol Buffers(简称 “protobuf”)格式存储在 etcd 中。 让我们来看一个具体的例子:我们使用 OpenShift v3.5.0 集群在命名空间 apiserver-sandbox
中启动一个名字叫webserver
的 Pod 。 然后使用 etcdctl 工具,检查 etcd(在我们的环境中,etcd 版本为 3.1.0):
1 | $ cat pod.yaml |
那么在kubectl create -f pod.yaml
命令执行以后, 对象是如何在 etcd 中保存的呢?下图描述了总览流程:
- 诸如
kubectl
之类的客户端提供期望的对象状态,例如v1
版本中的 YAML。 kubectl
将 YAML 格式转换为 JSON 格式并发送到 API Server。- 在相同
Kind
的不同版本之间,API Server 可以使用注解(annotations)来执行无损转换,以存储无法在旧API 版本中记录的信息。 - API 服务器将输入对象状态转换为规范存储版本,具体取决于自身在 API Server 中的版本,通常是最新的稳定版本,例如
v1
。 - 最后但并非最不重要的是将编码为 JSON 或 protobuf 内容以某个关键字的实际存储在 etcd 中。
您可以通过配置 --storage-media-type
选项来设置 kube-apiserver 首选的序列化,默认为 application/vnd.kubernetes.protobuf
。
现在让我们来看看无损转换在实践中是如何工作的。 我们将使用类型为 Horizontal Pod Autoscaling(HPA)的Kubernetes 对象,顾名思义,它有一个连接到它的控制器,根据利用率度量指标来监控和更新ReplicationController
。
首先设置 Kubernetes API 代理(以便我们以后可以直接从本地机器访问它),然后启动 ReplicationController
和 HPA:
1 | $ kubectl proxy --port=8080 & |
现在,使用 httpie – 但如果需要,也可以使用 curl
– 我们向 API Server 请求当前稳定版本(autoscaling/v1
)中的HPA 对象以及上一个(extensions/v1beta1
)中的 HPA 对象,最后比较两个版本:
1 | $ http localhost:8080/apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex > hpa-v1beta1.json |
您可以看到 HorizontalPodAutoscaler
的格式(schema)已从 v1beta1
更改为 v1
。 API Server 能够在这些版本之间无损地转换,而不管哪个版本实际存储在 etcd 中。
借助存储流程的基本知识,我们现在将重点放在 API Server 如何对有效负载进行编码和解码以及将其存储在 JSON 或 protobuf 中的细节上,同时也考虑使用的 etcd 版本。
详细描述状态流的序列化
API Server 将所有已知的 Kubernetes 对象类型保存在名为 Scheme
的 类型注册表(type registry) 中。 在这个注册表中,记录了每个版本的类型都是如何定义的,以及如何转换它们,如何创建新对象以及如何将对象编码和解码为 JSON 或 protobuf 。
当 API Server 从 kubectl
接收到一个对象时,它会从 HTTP 路径获知期望的版本。 它使用正确版本的 Scheme 创建匹配的空对象,并使用 JSON 或 protobuf 解码器转换 HTTP 负载。 解码器将二进制有效载荷转换为创建的对象。
解码对象是给定类型的支持版本中的一种。 对于某些类型,在其整个开发过程中几个版本。 为了避免这个问题,API Server 必须知道如何在每对版本之间进行转换(例如v1⇔v1alpha1,v1⇔v1beta1,v1beta1⇔v1alpha1
),API Server 为每个版本使用一个特殊的 “内部” 版本类型。 此类型的内部版本是所有受支持版本的超集,具有其所有功能。 它将传入的对象首先转换为内部版本,然后转换为存储版本:
1 | v1beta1 ⇒ internal ⇒ v1 |
在转换的第一步中,如用户省略了某些字段,它将会设置默认值。 想象一下 v1beta1
没有在 v1
中添加的某个必填字段。 在这种情况下,用户甚至无法填写该字段的值。 然后转换步骤将设置此字段的默认值以创建有效的内部对象。
验证和准入
转换过程中还有两个更重要的步骤。 实际流程如下所示:
1 | v1beta1 ⇒ internal ⇒ | ⇒ | ⇒ v1 ⇒ json/yaml ⇒ etcd |
准入和验证步骤在写入 etcd 之前对对象的创建和更新进行管理。角色定义如下:
准入
通过验证集群全局约束来检查是否可以创建或更新对象,并可能根据集群配置设置默认值。
在 Kubernetes 中有很多这样的对象,还有更多像 OpenShift 这样的支持多租户的 Kubernetes。 他们之中有一些是:
NamespaceLifecycle
: 如果命名空间不存在,则拒绝命名空间上下文中的所有传入请求。LimitRanger
: 在名称空间的每个资源基础上使用限制。ServiceAccount
:为一个 Pod 创建一个服务帐户。DefaultStorageClass
:设置 PersistentVolumeClaims 存储类的默认值,以防用户未提供有效值。ResourceQuota
:为群集上的当前用户强制执行配额限制,并可能在配额不足时拒绝请求。
验证
检查传入对象(在创建和更新过程中)是否合法,是否只包含有效值,例如:
- 检查是否设置了所有必填字段。
- 检查所有字符串是否具有有效格式(例如,只包含小写字符)。
- 检查没有设置冲突的字段(例如,具有相同名称的两个容器)。
验证不会查看该类型的其他实例或其他类型的实例。 换句话说,验证是针对每个对象的本地静态检查,独立于任何 API Server 配置。
可以使用
--admission-control=<plugins>
标志启用/禁用准入插件。 它们中的大多数也可以由群集管理员配置。 而且,在Kubernetes 1.7 中,提供了 webhook 机制来扩展准入机制和一个初始化器的概念,以便使用控制器实现新对象的自定义准入。
存储对象的迁移
有关存储对象迁移的最后说明:将 Kubernetes 升级到新版本时,备份群集状态和遵循每个版本的记录迁移步骤越来越重要。这源于从 etcd2 到 etcd3 的转变,以及 Kubernetes 种类及其版本的不断发展。
在 etcd 中,每个对象都存储在首选的存储版本中。但是,随着时间的推移,它可能会在你的 etcd 存储中有一个非常旧版本的对象。如果此版本已被弃用并最终从 API Server 中删除,那么您将无法再解码其 protobuf 或 JSON 。出于这个原因,存在迁移过程在群集升级之前重写这些对象。
以下资源可以帮助您应对这一挑战:
- 请参阅 集群管理参考版本 1.6 以及升级到群集管理文档中的其他API版本部分。
- 在 OpenShift 文档中,请参阅 手动运行群集升级。
- 如果您希望工具(作为 CLI 工具或 Web 应用程序)协助您完成此过程,请查看 ReShifter ,该工具可以为Kubernetes 群集以及 OpenShift 群集进行备份/恢复和迁移。
下一次,在API Server系列的第三部分中,我们将讨论如何使用Custom Resource Definitions 和 用户API Server来扩展 Kubernetes API。
另外,我们希望将Sergiusz Urbaniak的荣誉授予与 etcd 相关的支持。
除特别声明本站文章均属原创(翻译内容除外),如需要转载请事先联系,转载需要注明作者原文链接地址。