欢迎来到深入学习Kubernetes API Server的系列文章的第二部分。在上一部分中我们对APIserver总体,相关术语及request请求流进行探讨说明。在本部分文章中,我们主要聚焦于探究如何对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=来启动或禁用。它们中的大多数可以有集群管理配置。此外,在Kubernetes 1.7中可以用webhook机制来扩展准入机制,使用控制器来实现对对象的传统的校验。

迁移存储对象

关于存储对象迁移的最后说明:当Kubernetes需要升级到新的版本时,根据每个版本的相关文档步骤备份相关集群的数据是至关重要的。这一方面是由于etcd2到etcd3的转变,另一方面是由于Kubernetes 对象的Kind及version的不断发展。

在etcd中,每个对象是首选存储版本(preferred storage version)存在的。但是,随着时间的推移,etcd存储中的对象可能以一个非常老的版本存在。如果在将来某个时间这个对象版本被废弃了,那么将无法再解码它的protobuf 或JSON。因此,在集群升级之前需要重写,迁移这些数据。下面这些资料能够对version切换提供一些帮助:

请参阅集群管理文档升级API版本部分。Upgrading to a different API version

下一次,在深入学习Kubernetes APIServer的第三部分中,我们将讨论如何使用Custom Resource Definitions扩展和自定义API资源。

资深专家深度剖析Kubernetes API Server第2章(共3章)的更多相关文章

  1. 资深专家深度剖析Kubernetes API Server第1章(共3章)

    欢迎来到深入学习Kubernetes API Server的系列文章,在本系列文章中我们将深入的探究Kubernetes API Server的相关实现.如果你对Kubernetes的内部实现机制比较 ...

  2. 资深专家深度剖析Kubernetes API Server第3章(共3章)

    在本系列的前两部分中我们介绍了API Server的总体流程,以及API对象如何存储到etcd中.在本文中我们将探讨如何扩展API资源. 在一开始的时候,扩展API资源的唯一方法是扩展相关API源代码 ...

  3. 深度剖析Kubernetes API Server三部曲 - part 2

    欢迎来到深入学习Kubernetes API Server的系列文章的第二部分.在上一部分中我们对APIserver总体,相关术语及request请求流进行探讨说明.在本部分文章中,我们主要聚焦于探究 ...

  4. 深度剖析Kubernetes API Server三部曲 - part 1

    欢迎来到深入学习Kubernetes API Server的系列文章,在本系列文章中我们将深入的探究Kubernetes API Server的相关实现.如果你对Kubernetes 的内部实现机制比 ...

  5. 深度剖析Kubernetes API Server三部曲 - part 3

    在本系列的前两部分中我们介绍了API Server的总体流程,以及API对象如何存储到etcd中.在本文中我们将探讨如何扩展API资源. 在一开始的时候,扩展API资源的唯一方法是扩展相关API源代码 ...

  6. Kubernetes API server工作原理

    作为Kubernetes的使用者,每天用得最多的命令就是kubectl XXX了. kubectl其实就是一个控制台,主要提供的功能: 1. 提供Kubernetes集群管理的REST API接口,包 ...

  7. kubernetes API Server 权限管理实践

    API Server权限控制方式介绍 API Server权限控制分为三种:Authentication(身份认证).Authorization(授权).AdmissionControl(准入控制). ...

  8. kubernetes API Server安全

    用户访问API Server(以下简称Server),K8S的安全检查步骤:认证和授权. 认证解决用户是谁的问题,就是验证用户名密码;授权解决用户能做什么的问题,就是检查该用户是否拥有权限访问请求的资 ...

  9. 利用curl命令访问Kubernetes API server

    kubectl 通过访问 Kubernetes API 来执行命令.我们也可以通过对应的TLS key, 使用curl 或是 golang client做同样的事. API 请求必须使用 JSON 格 ...

随机推荐

  1. 想开发VR游戏?你需要注意这些东西

    转自:http://www.gamelook.com.cn/2016/03/246620 开发VR游戏很难吗?有些人会说是,但在HTC虚拟现实新科技部门副总经理鲍永哲看来,VR游戏的门槛并不比一般的游 ...

  2. ruby on rails 中render的

    Ruby rails页面跳转代码如下: 1.render(:text => string) 2.render(:inline => string, [:type => "r ...

  3. 开发环境入门 linux基础 (部分) 复制 用户和组操作 权限更改

    复制 用户和组操作 权限更改 CP 复制命令 cp 源文件 目标文件 a) –r(recursive,递归的):递归地复制目录.当复制一个目录时,复制该目录中所有的内容,其中包括子目录的全部内容. b ...

  4. PowerDesigner生成sql脚本时去掉双引号并把字段名设为大写

    Database菜单—Edit Current RDBMS 找到Script---sql—Format--- CaseSensitivityUsingQuote,把它设置为NO 这样再用sql pre ...

  5. python 函数和方法的区别

    一.函数和方法的区别 1.函数要手动传self,方法不用传 2.如果是一个函数,用类名去调用,如果是一个额方法,用对象去调用 举例说明: class Foo(object): def __init__ ...

  6. java反射专题一

    一丶Class的理解 /* * Class类是反射的源头 * 创建一个类,通过编译(javac.exe),生成对应的.class文件,之后使用java.exe加载(JVM的类加载器完成的)此.clas ...

  7. 【271】IDL-ENVI二次开发

    参考:String Processing Routines —— 字符串处理函数 01   STRING 返回字符串. 02   STRCMP 比较字符串,一样返回1,不一样返回0,默认大小写敏感. ...

  8. CUDA编程接口:异步并发执行的概念和API

    1.主机和设备间异步执行 为了易于使用主机和设备间的异步执行,一些函数是异步的:在设备完全完成任务前,控制已经返回给主机线程了.它们是: 内核发射; 设备间数据拷贝函数; 主机和设备内拷贝小于64KB ...

  9. Eclipse中,将jar包导入为User Library

    项目右键 Properties -> bulid path -> Add Library -> User Library -> User Libraries -> New ...

  10. MySQL存储引擎 -- MyISAM 与 InnoDB 理论对比

    MySQL常用的两种存储引擎一个是MyISAM,另一个是InnoDB.两种存储引擎各有各的特点. 1. 区别:(1)事务处理:MyISAM是非事务安全型的.-----而非事务型的系统,一般也称为数据仓 ...