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 支持特性[1] | 你不需要这类特性 |
你有意愿取接受 Kubernetes 对 REST 资源路径所作的格式限制,例如 API 组和名字空间。(参阅 API 概述[2]) | 你需要使用一些特殊的 REST 路径以便与已经定义的 REST API 保持兼容 |
你的 API 是声明式的[3] | 你的 API 不符合声明式[4]模型 |
你的资源可以自然地界定为集群作用域或集群中某个名字空间作用域 | 集群作用域或名字空间作用域这种二分法很不合适;你需要对资源路径的细节进行控制 |
首先我们希望我们的 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 的对比可参考官方文档[5]。
对于我们而言,我们希望使用更多的高级 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[6],我们可以参考实现自己的 Aggregated APIServer。但完全手工编写太过复杂,也不便于后期维护,我们最终选择了官方推荐的工具 apiserver-builder[7],apiserver-builder 可以帮助我们快速创建项目骨架,并且使用 apiserver-builder 构建的项目目录结构比较清晰,更利于后期维护。
安装 apiserver-builder 工具
通过 Go Get 安装
$ GO111MODULE=on go get sigs.k8s.io/apiserver-builder-alpha/cmd/apiserver-boot
通过安装包安装
- 下载[8]最新版本
- 解压到 /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集群[9]&配置好本地kubeconfig[10];
执行部署命令 ;
$ 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 扩展来构建云原生应用的开发者。
参考资料
[1]
Kubernetes API 支持特性: 【https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/#common-features】
[2]
API 概述: 【https://kubernetes.io/zh/docs/concepts/overview/kubernetes-api/】
[3]
声明式API: 【https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/#declarative-apis】
[4]
声明式: 【https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/#declarative-apis】
[5]
官方文档: 【https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/custom-resources/#compare-ease-of-use】
[6]
sample-apiserver: 【https://github.com/kubernetes/sample-apiserver】
[7]
apiserver-builder: 【https://github.com/kubernetes-sigs/apiserver-builder-alpha】
[8]
下载: 【https://github.com/kubernetes-sigs/apiserver-builder-alpha/releases】
[9]
EKS集群: 【https://cloud.tencent.com/document/product/457/39813】
[10]
kubeconfig: 【https://cloud.tencent.com/document/product/457/39814】
互动赢好礼
精读文章,回答问题赢好礼
Q1: 扩展API服务器与 kube-apiserver 之间会有网络延迟限制吗?如果有,能容忍的最大延迟是多久?
Q2:基于 Kubernetes 来构建 AI 平台最大的优势是什么?
11月21日上午11点,由作者选出回答最佳的5位读者,送腾讯云定制企鹅一个。
重 磅 来 袭
【云原生正发声】第十三期【11月23日 19:30】将和你一起深研小红书 Service Mesh 落地与Aeraki 组件优化扩展。
本期将由小红书基础架构云原生开发工程师“ 王城程” ,为大家介绍小红书 Service Mesh 发展历程,以及升级 Mesh 基于 Istio 改造适配公司内部开发环境方案介绍,Aeraki 改造生产环境适配,大规模落地业务,推广的经验分享。
快快扫码进入直播预约吧!
往期精选推荐
- K8s 平台可以如何处理 Pod 预授权问题
- 一个优秀的云原生架构需要注意哪些地方
- 多平台容器镜像构建就看这一篇
- 容器服务 TKE 存储插件与云硬盘 CBS 最佳实践应用
- 云原生 AI 前沿:Kubeflow Training Operator 统一云上 AI 训练
点个“在看”每天学习最新技术
相关文章
- 世界上的程序语言已这么丰富,为什么还不够?
- 如何用 纯C++(ndk)开发安卓应用 ?
- C++的性能, C#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥
- .NET Native:C++的性能 C#的产能?
- C#开发微信公众平台-就这么简单(附Demo)
- Android、iPhone和Java三个平台一致的加密方法
- 3个简单步骤,测试你的应用是否兼容Java 8
- 基于Storm构建分布式实时处理应用初探
- 创建以API为中心的Web应用
- 大数据七大趋势 第一个趋势是物联网
- 如何才能发挥大数据的价值?
- 坚守“品质阅读”初心 红板报让新闻更好看
- 10大行业大数据应用痛点及解决策略
- 2017年中国大数据行业最新发展图览
- 关于安防与大数据不能不说的九个发展趋势
- Web项目管理工具精选(上)
- 东方金信CTO石棋玲:浅析大数据风险分析与控制应用
- 基于Hadoop大数据分析应用场景与实战
- 同样是开源应用,百度PaddlePaddle和谷歌TensorFlow有什么不同?
- 三年大改版,Java 7企业版终于拥抱HTML 5