Keep learning, keep living...

0%

Kubernetes服务目录简要介绍

在一些场景下,运行在CloudFoundryKubernetes等平台上的Cloud Native应用需要使用各种各样的外部服务,如数据库,消息队列等。但应用开发者并不想关心这些服务的配置、管理和位置等信息,以便将精力集中在业务逻辑开发上。为了满足这种需要,CloudFoundryGoogle等一系列公司创建了Open Service Broker API(OSBAPI)项目,致力于将外部服务的生命周期管理,应用平台与服务代理的交互,服务实例的访问等内容标准化。

OSBAPI规范定义了高度抽象的应用平台与外部服务代理的交互逻辑,规范化外部服务的使用,主要包括:

  • 注册服务代理: 应用平台会获取服务代理提供的服务列表及相应规格,OSBAPI使用术语Plan来表示规格。
  • Provision服务实例: OSBAPI使用术语Provision来统一指定服务资源的供应。在外部服务具体实现上,可以是创建虚拟机实例,也可以是分配资源,也可以是创建SaaS帐号等等。
  • 绑定应用及服务实例: 当服务实例创建完成后,开发者想要其应用与该服务实例进行交互。从服务代理的角度来看,就是将应用与服务实例进行绑定。具体实现逻辑可以非常灵活,一般情况下,会生成访问该服务实例所需的新凭证。比如,生成数据库实例的IP、端口、用户名、密码等等。这些凭证会被返回给应用平台以提供给具体应用使用。

OSBAPI的具体工作模式如下图:

Kubernetes的Service Catalog项目实现了OSBAPI规范,Kubernetes平台的应用通过service catalog来使用外部服务。
大体实现架构如图:

Service catalog实现将service catalog资源对象的管理映射到OSBAPI相应资源的操作, 架构上主要包括两部分:

  • API Server:
    它基于API-aggregation实了一个扩展API Server, 使用etcd做为后端存储。
    Kubernetes用户和service catalog controller通过与API Server交互完成Service Catalog资源的CRUD操作。
  • Controller:
    Controller实现了OSBAPI规范。它监控API Server上资源对象的事件,并完成与OSBAPI对应的操作。比如,当用户创建一个ClusterServiceBroker对象, API Server会存储该资源并产生一个事件。Controller会接收到该事件,接着去请求指定的服务代理获取该服务代理所提供的资源。

Service catalog的API组为: servicecatalog.k8s.io, 当前的API资源版本为: v1beta1,提供的主要资源有:

  • ClusterServiceBroker:
    Service Broker在Kubernetes集群内的表示, 包含Service Broker的相关信息。
  • ClusterServiceClass:
    表示特定ServiceBroker所提供的外部服务,ClusterServiceBroker创建后,controller会自动从ClusterServiceBroker所提供的URL拉取服务列表,为每个外部服务创建一个ClusterServiceClass。
  • ClusterServicePlan:
    表示外部服务的规格,ClusterServiceBroker创建后,controller也会自动创建ClusterServicePlan对象。
  • ServiceInstance:
    外部服务的服务实例,创建该对象时,controller会发送OSBAPI Provision请求。
  • ServiceBinding:
    创建该对象后,controller发送OSBAPI的Binding请求,并将返回的凭证信息存储在kubernetes的Secret对象中。该Secret对象需要被挂载到需要使用该服务的Pod中。

其他的一些资源具体可参考: https://svc-cat.io/docs/resources/

当存储有相关凭证信息的Secret对象挂载到特定应用的Pod中,该应用则可以使用这些信息与外部服务进行交互, 如下图所示:

下面通过一个实例来说明Kubernetes与外部服务的交互。

首先,我们基于python的openbrokerapi来实现一个demo broker, 它只是简单的返回服务信息,并不真实分配服务资源。

源码broker.py内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from openbrokerapi import api
from openbrokerapi.catalog import ServicePlan
from openbrokerapi.service_broker import (
ServiceBroker,
Service,
ProvisionedServiceSpec,
ProvisionState,
UpdateServiceSpec,
BindState,
Binding,
DeprovisionServiceSpec,
LastOperation,
UnbindDetails,
ProvisionDetails,
UpdateDetails,
BindDetails,
DeprovisionDetails
)

class ExampleServiceBroker(ServiceBroker):
def catalog(self):
return Service(
id='6b6b5306-5c37-4fba-a402-186f8a0dfa0a',
name='demo-service',
description='demo service does nothing',
bindable=True,
plans=[
ServicePlan(
id='9a411f4c-01fa-4996-a780-a0d1b8dcb234',
name='default',
description='default service plan',
),
],
tags=['demo', 'service'],
plan_updateable=True,
)
def provision(self, instance_id: str, service_details: ProvisionDetails,
async_allowed: bool) -> ProvisionedServiceSpec:
return ProvisionedServiceSpec(
state=ProvisionState.SUCCESSFUL_CREATED,
dashboard_url="http://demo.local/{}".format(instance_id)
)
def bind(self, instance_id: str, binding_id: str, details: BindDetails) -> Binding:
return Binding(
state=BindState.SUCCESSFUL_BOUND,
credentials = {
"uri": "http://demo.local/{}".format(binding_id),
"username": "abc",
"password": "123456"
}
)
def update(self, instance_id: str, details: UpdateDetails, async_allowed: bool) -> UpdateServiceSpec:
pass
def unbind(self, instance_id: str, binding_id: str, details: UnbindDetails):
return details
def deprovision(self, instance_id: str, details: DeprovisionDetails, async_allowed: bool) -> DeprovisionServiceSpec:
return DeprovisionServiceSpec(
is_async=False,
operation=None
)
def last_operation(self, instance_id: str, operation_data: str) -> LastOperation:
pass

api.serve(ExampleServiceBroker(), None, port=5000)

运行该broker:

1
python broker.py

该虚拟机IP为10.0.0.231,它监听5000端口。

我们假定Kubernetes集群搭建和Service Catalog已经安装完成。

首先,在Kubernetest集群创建ClusterServiceBroker:

demo-broker.yaml的内容如下:

1
2
3
4
5
6
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ClusterServiceBroker
metadata:
name: demo-broker
spec:
url: http://10.0.0.231:5000

创建demo-broker:

1
2
[root@fg-t1 yaml]# kubectl create -f demo-broker.yaml
clusterservicebroker.servicecatalog.k8s.io/demo-broker created

查看clusterservicebrokers列表:

1
2
3
[root@fg-t1 yaml]# kubectl get clusterservicebrokers
NAME URL STATUS AGE
demo-broker http://10.0.0.231:5000 Ready 70s

查看clusterserviceclasses列表, 可以看到demo-broker所提供的demo-service:

1
2
3
[root@fg-t1 yaml]# kubectl get clusterserviceclasses
NAME EXTERNAL-NAME BROKER AGE
6b6b5306-5c37-4fba-a402-186f8a0dfa0a demo-service demo-broker 34s

查看clusterserviceplans列表:

1
2
3
[root@fg-t1 yaml]# kubectl get clusterserviceplans
NAME EXTERNAL-NAME BROKER CLASS AGE
9a411f4c-01fa-4996-a780-a0d1b8dcb234 default demo-broker 6b6b5306-5c37-4fba-a402-186f8a0dfa0a 40s

接下来,创建服务实例。
demo-instance.yaml内容如下:

1
2
3
4
5
6
7
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceInstance
metadata:
name: demo-instance
spec:
clusterServiceClassExternalName: demo-service
planName: default

创建ServiceInstance对象:

1
2
[root@fg-t1 yaml]# kubectl create -f demo-instance.yaml
serviceinstance.servicecatalog.k8s.io/demo-instance created

查看实例列表,可以看到新创建的demo-instance实例:

1
2
3
[root@fg-t1 yaml]# kubectl get serviceinstances
NAME CLASS PLAN STATUS AGE
demo-instance ClusterServiceClass/demo-service default Ready 11s

接着,创建Binding:
demo-binding.yaml内容如下:

1
2
3
4
5
6
7
8
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceBinding
metadata:
name: demo-binding
spec:
instanceRef:
name: demo-instance
secretName: demo-instance-credentials

创建ServiceBinding对象:

1
2
[root@fg-t1 yaml]# kubectl create -f demo-binding.yaml
servicebinding.servicecatalog.k8s.io/demo-binding created

查看ServiceBindings列表可以看到demo-binding创建成功:

1
2
3
[root@fg-t1 yaml]# kubectl get servicebindings
NAME SERVICE-INSTANCE SECRET-NAME STATUS AGE
demo-binding demo-instance demo-instance-credentials Ready 7s

我们在demo-binding.yaml中指定凭证信息存储在Secret对象demo-instance-credentials中,我们来查看这个Secret对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@fg-t1 yaml]# kubectl get secret demo-instance-credentials -o yaml
apiVersion: v1
data:
password: MTIzNDU2
uri: aHR0cDovL2RlbW8ubG9jYWwvYmY2YWRlZTItYmJkNC0xMWU5LWFhYmYtMDI0MmFjMTEwMDA3
username: YWJj
kind: Secret
metadata:
creationTimestamp: "2019-08-11T01:10:03Z"
name: demo-instance-credentials
namespace: default
ownerReferences:
- apiVersion: servicecatalog.k8s.io/v1beta1
blockOwnerDeletion: true
controller: true
kind: ServiceBinding
name: demo-binding
uid: bf6adf2b-bbd4-11e9-aabf-0242ac110007
resourceVersion: "307730"
selfLink: /api/v1/namespaces/default/secrets/demo-instance-credentials
uid: 7c159d9c-0cb0-4a4e-86da-ea7480255e5e
type: Opaque
1
2
3
4
5
6
[root@fg-t1 yaml]# echo 'aHR0cDovL2RlbW8ubG9jYWwvYmY2YWRlZTItYmJkNC0xMWU5LWFhYmYtMDI0MmFjMTEwMDA3' | base64 --decode
http://demo.local/bf6adee2-bbd4-11e9-aabf-0242ac110007
[root@fg-t1 yaml]# echo 'MTIzNDU2' |base64 --decode
123456
[root@fg-t1 yaml]# echo 'YWJj' |base64 --decode
abc

可以看到,返回的内容正是我们在broker.py中所指定的内容。

最后,我们清理掉这些实例内容:

1
2
3
4
5
6
[root@fg-t1 yaml]# kubectl delete servicebindings demo-binding
servicebinding.servicecatalog.k8s.io "demo-binding" deleted
[root@fg-t1 yaml]# kubectl delete serviceinstance demo-instance
serviceinstance.servicecatalog.k8s.io "demo-instance” deleted
[root@fg-t1 yaml]# kubectl delete clusterservicebroker demo-broker
clusterservicebroker.servicecatalog.k8s.io "demo-broker” deleted