共计 6555 个字符,预计需要花费 17 分钟才能阅读完成。
这篇文章的内容主要围绕如何对 Kubernetes 对象的状态进行管理进行讲述,文章内容清晰易懂,条理清晰,非常适合新手学习,值得大家去阅读。感兴趣的朋友可以跟随丸趣 TV 小编一起阅读吧。希望大家通过这篇文章有所收获!
我们下面主要聚焦于探究如何对 Kubernetes 对象的状态以一种可靠,持久的方式进行管理。之前的文章中提到过 API Server 自身是无状态的,并且它是唯一能够与分布式存储 etcd 直接通信的组件。
etcd 的简要说明
在 *nix 操作系统中,我们一般使用 /etc 来存储相关配置数据。实际上 etcd 的名字就是由此发展而来,在 etc 后面加上个”d”表示”distributed”分布式。任何分布式系统都需要有像 etcd 这样能够存储系统数据的东西,使其能够以一致和可靠的方式检索相关数据。为了能实现分布式的数据访问,etcd 使用 Raft 协议。从概念上讲,etcd 支持的数据模型是键值(key-value)存储。在 etcd2 中,各个 key 是以层次结构存在,而在 etcd3 中这个就变成了遍布模型,但同时也保持了层次结构方式的兼容性。
使用容器化版本的 etcd,我们可以创建上面的树,然后按如下方式检索它:
$ docker run –rm -d -p 2379:2379 \
–name test-etcd3 quay.io/coreos/etcd:v3.1.0 /usr/local/bin/etcd \
–advertise-client-urls http://0.0.0.0:2379 –listen-client-urls http://0.0.0.0:2379
$ curl localhost:2379/v2/keys/foo -XPUT -d value= some value
$ curl localhost:2379/v2/keys/bar/this -XPUT -d value=42
$ curl localhost:2379/v2/keys/bar/that -XPUT -d value=take
$ http localhost:2379/v2/keys/?recursive=true
HTTP/1.1 200 OK
Content-Length: 327
Content-Type: application/json
Date: Tue, 06 Jun 2017 12:28:28 GMT
X-Etcd-Cluster-Id: 10e5e39849dab251
X-Etcd-Index: 6
X-Raft-Index: 7
X-Raft-Term: 2
{
action : get ,
node : {
dir : true,
nodes : [
{
createdIndex : 4,
key : /foo ,
modifiedIndex : 4,
value : some value
},
{
createdIndex : 5,
dir : true,
key : /bar ,
modifiedIndex : 5,
nodes : [
{
createdIndex : 5,
key : /bar/this ,
modifiedIndex : 5,
value : 42
},
{
createdIndex : 6,
key : /bar/that ,
modifiedIndex : 6,
value : take
}
]
}
]
}
}
现在我们已经大致了解了 etcd 是如何工作的,接下去我们继续讨论 etcd 在 Kubernetes 是如何被使用的。
集群中的 etcd
在 Kubernetes 中,etcd 是控制平面中的一耳光独立组成部分。在 Kubernetes1.5.2 版本之前,我们使用的是 etcd2 版本,而在 Kubernetes1.5.2 版本之后我们就转向使用 etcd3 版本了。值得注意的是在 Kubernetes1.5.x 版本中 etcd 依旧使用的是 v2 的 API 模型,之后这将开始变为 v3 的 API 模型,包括使用的数据模型。站在开发者角度而言这个似乎没什么直接影响,因为 API Server 与存储之前是抽象交互,而并不关心后端存储的实现是 etcd v2 还是 v3。但是如果是站在集群管理员的角度来看,还是需要知道 etcd 使用的是哪个版本,因为集群管理员需要日常对数据进行一些备份,恢复的维护操作。
你可以 API Server 的相关启动项中配置使用 etcd 的方式,API Server 的 etcd 相关启动项参数如下所示:
$ kube-apiserver -h
…
–etcd-cafile string SSL Certificate Authority file used to secure etcd communication.
–etcd-certfile string SSL certification file used to secure etcd communication.
–etcd-keyfile string SSL key file used to secure etcd communication.
…
–etcd-quorum-read If true, enable quorum read.
–etcd-servers List of etcd servers to connect with (scheme://ip:port) …
…
Kubernetes 存储在 etcd 中的数据,是以 JSON 字符串或 Protocol Buffers 格式存储。下面我们来看一个具体的例子:在 apiserver-sandbox 的命名空间中创建一个 webserver 的 pod。然后我们使用 etcdctl 工具来查看相关 etcd(在本环节中 etcd 版本为 3.1.0)数据。
$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: webserver
spec:
containers:
– name: nginx
image: tomaskral/nonroot-nginx
ports:
– containerPort: 80
$ kubectl create -f pod.yaml
$ etcdctl ls /
/kubernetes.io
/openshift.io
$ etcdctl get /kubernetes.io/pods/apiserver-sandbox/webserver
{
kind : Pod ,
apiVersion : v1 ,
metadata : {
name : webserver ,
…
下面我们来看一下这个 pod 对象是如何最终存储到 etcd 中,通过 kubectl create -f pod.yaml 的方式。下图描绘了这个总体流程:
1.
客户端(比如 kubectl)提供一个理想状态的对象,比如以 YAML 格式,v1 版本提供。
2.
Kubectl 将 YAML 转换为 JSON 格式,并发送。
3.
对应同类型对象的不同版本,API Server 执行无损耗转换。对于老版本中不存在的字段则存储在 annotations 中。
4.
API Server 将接受到的对象转换为规范存储版本,这个版本由 API Server 指定,一般是最新的稳定版本,比如 v1。
5.
最后将对象通过 JSON
或 protobuf 方式解析为一个 value,通过一个特定的 key 存入 etcd 当中。
我们可以通过配置 kube-apiserver 的启动参数 –storage-media-type 来决定想要序列化数据存入 etcd 的格式,默认情况下为 application/vnd.kubernetes.protobuf 格式。我们也可以通过配置 –storage-versions 启动参数,来确定存入 etcd 的每个群组 Group 对象的默认版本号。
现在让我们来看看无损转换是如何进行的,我们将使用 Kubernetes 对象 Horizontal Pod Autoscaling (HPA) 来列举说明。HPA 顾名思义是指通过监控资源的使用情况结合 ReplicationController 控制 Pod 的伸缩。
首先我们期待一个 API 代理(以便于我们能够在本地直接访问它),并启动 ReplicationController,以及 HPA 。
$ kubectl proxy –port=8080
$ kubectl create -f https://raw.githubusercontent.com/mhausenblas/kbe/master/specs/rcs/rc.yaml
kubectl autoscale rc rcex –min=2 –max=5 –cpu-percent=80
kubectl get hpa/rcex -o yaml
现在,你能够使用 httpie
——当然你也能够使用 curl 的方式——向 API server 请求获取 HPA 对象使用当前的稳定版本(autoscaling/v1),或者使用之前的版本(extensions/v1beta1),获取的两个版本的区别如下所示:
$ http localhost:8080/apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex hpa-v1beta1.json
$ http localhost:8080/apis/autoscaling/v1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex hpa-v1.json
$ diff -u hpa-v1beta1.json hpa-v1.json
{
kind : HorizontalPodAutoscaler ,
– apiVersion : extensions/v1beta1 ,
+ apiVersion : autoscaling/v1 ,
metadata : {
name : rcex ,
namespace : api-server-deepdive ,
– selfLink : /apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex ,
+ selfLink : /apis/autoscaling/v1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex ,
uid : ad7efe42-50ed-11e7-9882-5254009543f6 ,
resourceVersion : 267762 ,
creationTimestamp : 2017-06-14T10:39:00Z
},
spec : {
– scaleRef : {
+ scaleTargetRef : {
kind : ReplicationController ,
name : rcex ,
– apiVersion : v1 ,
– subresource : scale
+ apiVersion : v1
},
minReplicas : 2,
maxReplicas : 5,
– cpuUtilization : {
– targetPercentage : 80
– }
+ targetCPUUtilizationPercentage : 80
我们能够看到 HorizontalPodAutoscale 的版本从 v1beta1 变为了 v1。API server 能够在不同的版本之前无损耗转换,不论在 etcd 中实际存的是哪个版本。
在了解整个存储流程之后,我们下面来探究一下 API server 如何将数据进行编码,解码存入 etcd 中以 JSON 或 protobuf 的方式,同时也考虑到 etcd 的版本。
API Server 将所有已知的 Kubernetes 对象类型保存在名为 Scheme 的 Go 类型注册表(registry)中。在此注册表中,定义每种了 Kubernetes 对象的类型以及如何转换它们,如何创建新对象,以及如何将对象编码和解码为 JSON 或 protobuf。
当 API Server 从客户端接收到一个对象时,比如 kubectl,通过 HTTP 路径,能够知道这个对象的具体版本号。首先会为这个对象使用对应的版本 Scheme 创建一个空对象,然后通过 JSON 或 protobuf 将 HTTP 传过来的对象内容进行解码转换。解码完成后创建对象,存入 etcd 中。
在 API 中可能会有很多版本,如果要支持每个版本之间的直接转换,这样往往处理起来比较麻烦。比如某个 API 下面有三个版本,那么它就要支持一个版本到另两个版本的直接转换(比如 v1
v1alpha1, v1
v1beta1, v1beta1
v1alpha1)。为了避免这个问题,在 API server 中有一个特别的 “internal” 版本。当两个版本之间需要转换时,先转换为 internal 版本,再转换为相应转换的版本。这样的话,每个版本只要支持能够转换为 internal 版本,那么就能够与其它任何版本进行间接的转换。所以一个对象先转换为 internal 版本,然后在转换为稳定的 v1 版本,然后在存入 etcd 中。
v1beta1
internal
v1
在转换的第一步中,如果某些字段用户没有赋值指定,那么这些会被赋为一个默认值。比如在 v1beta1 中肯定没有在 v1 版本新增的一个字段。在这种情况下,用户肯定无法在 v1beta1 版本为这个字段赋值。这时候,在转换的第一步中,我们会为这个字段赋一个默认值以生成一个有效的 internal。
校验及准入
在转换过程中有两个重要的步骤,如下图所示:
v1beta1
internal
⇒ |
⇒ |
⇒ v1
json/yaml
etcd
admission validation
准入和校验是创建和更新对象存入 etcd 之前必须通过的步骤。它们的一些规则如下所示:
1.
准入(Admission):查看集群中的一些约束条件是否允许创建或更新此对象,并根据此集群的相关配置为对象设置一些默认值。在 Kubernetes 有很多这种约束条件,下面列举一些例子:
NamespaceLifecycle:如果命名空间不存在,则拒绝该命名空间下的所有传入请求。
LimitRanger:强制限制命名空间中资源的使用率。
ServiceAccount:为 pod 创建 service account 。
DefaultStorageClass:如果用户没有为 PersistentVolumeClaims 赋值,那么将它设置为一个默认值。
ResourceQuota:对群集上的当前用户强制执行配额约束,如果配额不足,可能会拒绝请求。
2.
校验(Validation):检查传入对象(在创建和更新期间)是否格式是否合法以及相关值是否有效。比如:
1.
检查必填字段是否已填。
2.
检查字符串格式是否正确(比如只允许小写形式)。
3.
是否有些字段存在冲突(比如,有两个容器的名字一样)。
校验(Validation)并不关心其它类型的对象实例,换言之,它只关心每个对象的静态检查,无关集群配置。
准入(Admission)可以用 flag
–admission-control= plugins 来启动或禁用。它们中的大多数可以有集群管理配置。此外,在 Kubernetes 1.7 中可以用 webhook 机制来扩展准入机制,使用控制器来实现对对象的传统的校验。
迁移存储对象
关于存储对象迁移的最后说明:当 Kubernetes 需要升级到新的版本时,根据每个版本的相关文档步骤备份相关集群的数据是至关重要的。这一方面是由于 etcd2 到 etcd3 的转变,另一方面是由于 Kubernetes 对象的 Kind 及 version 的不断发展。
在 etcd 中,每个对象是首选存储版本(preferred storage version)存在的。但是,随着时间的推移,etcd 存储中的对象可能以一个非常老的版本存在。如果在将来某个时间这个对象版本被废弃了,那么将无法再解码它的 protobuf 或 JSON。因此,在集群升级之前需要重写,迁移这些数据。
感谢你的阅读,相信你对“如何对 Kubernetes 对象的状态进行管理”这一问题有一定的了解,快去动手实践吧,如果想了解更多相关知识点,可以关注丸趣 TV 网站!丸趣 TV 小编会继续为大家带来更好的文章!