关于腾讯云:Aggregated-APIServer-构建云原生应用最佳实践

作者

张鹏,腾讯云容器产品工程师,领有多年云原生我的项目开发落地教训。目前次要负责腾讯云 TKE 云原生 AI 产品的开发工作。

谢远东,腾讯高级工程师,Kubeflow Member、Fluid(CNCF Sandbox) 外围开发者,负责腾讯云 TKE 在 AI 场景的研发和反对工作。

概述

随着 Kubernetes 的日趋成熟,越来越多的公司、企业开始应用 K8s 来构建本人的云原生平台,基于 kubernetes 良好的扩展性以及成熟稳固的架构,你能够疾速部署并治理本人的云原生利用。

目前咱们也在基于 kubernetes 打造一个云原生 AI 平台(咱们称它为:SKAI),该平台具备极致弹性多云兼容性高易用可观测性可复现性的特点,旨在利用云原生的思维和技术,为 AI 场景的数据处理、模型训练、模型上线推理等需要构建弹性可扩大的零碎架构,从而晋升资源利用率。

为了使咱们的平台更加的云原生,咱们没有抉择罕用的 web 框架来构建 API 服务,而是应用 kubernetes 扩大来构建整个平台,这样使咱们的平台能更好的和 kubernetes 交融,能够无缝适配任何基于 k8s 的多云混合云环境。

为什么抉择 Aggregated APIServer?

抉择独立 API 还是 Aggregated APIServer ?

只管应用 gin、go-restful 等 go 语言 web 框架能够轻易地构建出一个稳固的 API 接口服务,但以 kubernetes 原生的形式构建 API 接口服务还是有很多劣势,例如:

  • 能利用 kubernetes 原生的认证、受权、准入等机制,有更高的开发效率;
  • 能更好的和 K8s 零碎交融,借助 K8s 生态更快的推广本人的产品,不便用户上手;
  • 借助于 K8s 成熟的 API 工具及标准,构建出的 API 接口更加标准参差;

然而在很多场景下,咱们还是不能确定到底应用聚合 API(Aggregated APIServer)还是独立 API 来构建咱们的服务,官网为咱们提供了两种抉择的比照;如果你不能确定应用聚合 API 还是独立 API,上面的表格或者对你有帮忙:

思考 API 聚合的状况 优选独立 API 的状况
你在开发新的 API 你曾经有一个提供 API 服务的程序并且工作良好
你心愿能够是应用 kubectl 来读写你的新资源类别 不要求 kubectl 反对
你心愿在 Kubernetes UI (如仪表板)中和其余内置类别一起查看你的新资源类别 不须要 Kubernetes UI 反对
你心愿复用 Kubernetes API 反对个性 你不须要这类个性
你有志愿取承受 Kubernetes 对 REST 资源门路所作的格局限度,例如 API 组和名字空间。(参阅 API 概述) 你须要应用一些非凡的 REST 门路以便与曾经定义的 REST API 放弃兼容
你的 API 是申明式的 你的 API 不合乎申明式模型
你的资源能够天然地界定为集群作用域或集群中某个名字空间作用域 集群作用域或名字空间作用域这种二分法很不适合;你须要对资源门路的细节进行管制

首先咱们心愿咱们的 SKAI 平台能更好的和 k8s 联合,并且它是一个申明式的 API,尽可能的复用 Kubernets API 的个性,显然聚合 API 对咱们来说更加适宜。

抉择 CRDs 还是 Aggregated APIServer?

除了聚合 API,官网还提供了另一种形式以实现对规范 kubernetes API 接口的扩大:CRD(Custom Resource Definition ),能达到与聚合 API 根本一样的性能,而且更加易用,开发成本更小,但相较而言聚合 API 则更为灵便。针对这两种扩大形式如何抉择,官网也提供了相应的参考。

通常,如果存在以下状况,CRD 可能更适合:

  • 定制资源的字段不多;
  • 你在组织外部应用该资源或者在一个小规模的开源我的项目中应用该资源,而不是在商业产品中应用;
    聚合 API 可提供更多的高级 API 个性,也可对其余个性进行定制;例如,对存储层进行定制、对 protobuf 协定反对、对 logs、patch 等操作反对。

两种形式的外围区别是定义 api-resource 的形式不同。在 Aggregated APIServer 形式中,api-resource 是通过代码向 API 注册资源类型,而 Custom Resource 是间接通过 yaml 文件向 API 注册资源类型。

简略来说就是 CRD 是让 kube-apiserver 意识更多的对象类别(Kind),Aggregated APIServer 是构建本人的 APIServer 服务。尽管 CRD 更简略,然而短少更多的灵活性,更具体的 CRDs 与 Aggregated API 的比照可参考官网文档。

对于咱们而言,咱们心愿应用更多的高级 API 个性,例如 “logs” 或 “exec”,而不仅仅局限于 CRUD ,所以咱们最终抉择了 Aggregated APIServer 。

APIServer 扩大的基本原理

kube-apiserver 作为整个 Kubernetes 集群操作 etcd 的惟一入口,负责 Kubernetes 各资源的认证&鉴权,校验以及 CRUD 等操作,提供 RESTful APIs,供其它组件调用:

kube-apiserver 其实蕴含三种 APIServer:

  • AggregatorServer:负责解决 apiregistration.k8s.io 组下的 APIService 资源申请,同时将来自用户的申请拦挡转发给 Aggregated APIServer(AA);
  • KubeAPIServer:负责对申请的一些通用解决,包含:认证、鉴权以及各个内建资源(pod, deployment,service)的 REST 服务等;
  • ApiExtensionsServer:负责 CustomResourceDefinition(CRD)apiResources 以及 apiVersions 的注册,同时解决 CRD 以及相应 CustomResource(CR)的REST申请(如果对应 CR 不能被解决的话则会返回404),也是 apiserver Delegation 的最初一环;

三个 APIServer 通过 delegation 的关系关联,在 kube-apiserver 初始化创立的过程中,首先创立的是 APIExtensionsServer,它的 delegationTarget 是一个空的 Delegate,即什么都不做,继而将 APIExtensionsServer 的 GenericAPIServer,作为 delegationTarget 传给了 KubeAPIServer,创立出了 KubeAPIServer,再而后,将 kubeAPIServer 的 GenericAPIServer 作为 delegationTarget 传给了 AggregatorServer,创立出了 AggregatorServer,所以他们之间 delegation 的关系为: Aggregator -> KubeAPIServer -> APIExtensions,如下图所示:

如何疾速构建 Aggregated APIServer?

尽管官网提供了一个 sample-apiserver,咱们能够参考实现本人的 Aggregated APIServer。但齐全手工编写太过简单,也不便于前期保护,咱们最终抉择了官网举荐的工具 apiserver-builder,apiserver-builder 能够帮忙咱们疾速创立我的项目骨架,并且应用 apiserver-builder 构建的我的项目目录构造比拟清晰,更利于前期保护。

装置 apiserver-builder 工具

通过 Go Get 装置

$ GO111MODULE=on go get sigs.k8s.io/apiserver-builder-alpha/cmd/apiserver-boot

通过安装包装置

  • 下载最新版本
  • 解压到 /usr/local/apiserver-builder/
  • 如果此目录不存在,则创立此目录
  • 增加/usr/local/apiserver-builder/bin到您的门路 export PATH=$PATH:/usr/local/apiserver-builder/bin
  • 运行apiserver-boot -h

初始化我的项目

实现 apiserver-boot 装置后,可通过如下命令来初始化一个 Aggregated APIServer 我的项目:

$ mkdir skai-demo
$ cd skai-demo 
$ apiserver-boot init repo --domain skai.io

执行后会生成如下目录:

.
├── BUILD.bazel
├── Dockerfile
├── Makefile
├── PROJECT
├── WORKSPACE
├── bin
├── cmd
│   ├── apiserver
│   │   └── main.go
│   └── manager
│       └── main.go -> ../../main.go
├── go.mod
├── hack
│   └── boilerplate.go.txt
├── main.go
└── pkg
    └── apis
        └── doc.go
  • hack 目录寄存主动脚本
  • cmd/apiserver 是 aggregated server的启动入口
  • cmd/manager 是 controller 的启动入口
  • pkg/apis 寄存 CR 相干的构造体定义,会在下一步主动生成

生成自定义资源

$ apiserver-boot create group version resource --group animal --version v1alpha1 --kind Cat --non-namespaced=false
Create Resource [y/n]
y
Create Controller [y/n]
n

可依据本人的需要抉择是否生成 Controller,咱们这里临时抉择不生成, 对于须要通过 namespace 隔离的 resource 须要减少 –non-namespaced=false 的参数,默认都是 true。

执行实现后代码构造如下:

└── pkg
    └── apis
        ├── animal
        │   ├── doc.go
        │   └── v1alpha1
        │       ├── cat_types.go
        │       ├── doc.go
        │       └── register.go
        └── doc.go

能够看到在 pkg/apis 下生成了 animal 的 group 并在 v1alpha1 版本下新增了 cat_types.go 文件,此文件蕴含了咱们资源的根底定义,咱们在 spec 中减少字段定义,并在曾经实现的 Validate 办法中实现根底字段的校验。

// Cat
// +k8s:openapi-gen=true
type Cat struct {
        metav1.TypeMeta   `json:",inline"`
        metav1.ObjectMeta `json:"metadata,omitempty"`
        Spec   CatSpec   `json:"spec,omitempty"`
        Status CatStatus `json:"status,omitempty"`
}
// CatSpec defines the desired state of Cat
type CatSpec struct {
    Name string `json:"name"`
}
func (in *Cat) Validate(ctx context.Context) field.ErrorList {
    allErrs := field.ErrorList{}
    if len(in.Spec.Name) == 0 {
        allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "name"), in.Spec.Name, "must be specify"))
    }
    return allErrs
}

部署运行

实现以上步骤,你其实曾经领有一个残缺的 Aggregated APIServer,接下来咱们试着将它运行起来;apiserver-boot 自身提供了两种运行模式:in-cluster、local; local 模式下只作为独自的 API 服务部署在本地不便做调试,过于简略这里不做过多介绍,次要关注一下 in-cluster 模式;in-cluster 能够将你的 Aggregated APIServer 部署在任何 K8s 集群中,例如:minikube,腾讯 TKE,EKS 等,咱们这里应用 EKS 集群作为演示。

创立EKS集群&配置好本地kubeconfig;

执行部署命令 ;

$ apiserver-boot run in-cluster --image=xxx/skai.io/skai-demo:0.0.1 --name=skai-demo --namespace=default

在执行部署命令过程中,apiserver-boot 次要帮咱们做了如下几件事件:

  • 主动生成 APIServer Dockerfile 文件;
  • 通过 APIServer Dockerfile 构建服务镜像,并将镜像推送到指定仓库;
  • 在config目录下生成 CA 及其他 APIServer 部署须要的证书文件;
  • 在config目录下生成 APIServer 部署须要的 Deployment、Service、APIService、ServiceAccount 等 yaml 文件;
  • 将上一步生成的 yaml 文件部署到集群中;

性能验证

确认 Resource 注册胜利

$ kubectl api-versions |grep animal
animal.skai.io/v1alpha1

确认 Aggregated APIServer 能失常工作

$ kubectl get apiservice v1alpha1.animal.skai.io 
NAME                      SERVICE             AVAILABLE   AGE
v1alpha1.animal.skai.io   default/skai-demo   True        19h

创立并查看新增的 Resource

创立

$ cat lucky.yaml
apiVersion: animal.skai.io/v1alpha1
kind: Cat
metadata:
  name: mycat
  namespace: default
spec:
  name: lucky
# 创立自定义 resource
$ kubectl apply -f lucky.yaml

查找

# 查找自定义 resource 列表
$ kubectl get cat
NAME    CREATED AT
mycat   2021-11-17T09:08:10Z
# 查找自定义资源详情
$ kubectl get cat mycat -oyaml
apiVersion: animal.skai.io/v1alpha1
kind: Cat
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"animal.skai.io/v1alpha1","kind":"Cat","metadata":{"annotations":{},"name":"mycat"},"spec":{"name":"lucky"}}
  creationTimestamp: "2021-11-17T09:08:10Z"
  name: mycat
  resourceVersion: "17"
  uid: 98af0905-f01d-4042-bad3-71b96c0919f4
spec:
  name: lucky
status: {}

总结

本文从实战角度登程介绍咱们开发 SKAI 平台过程中抉择 Aggregated API 的起因,以及 kube-apisever 的扩大原理,最初介绍了 apiserver-builder 工具,并演示如何一步一步构建起本人的 Aggregated API,并将它部署到 EKS 集群中。心愿该篇 Aggregated APIServer 最佳实际能够帮忙行将应用 K8s API 扩大来构建云原生利用的开发者。

对于咱们

更多对于云原生的案例和常识,可关注同名【腾讯云原生】公众号~

福利:

①公众号后盾回复【手册】,可取得《腾讯云原生路线图手册》&《腾讯云原生最佳实际》~

②公众号后盾回复【系列】,可取得《15个系列100+篇超实用云原生原创干货合集》,蕴含Kubernetes 降本增效、K8s 性能优化实际、最佳实际等系列。

【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理