在KubeFATE中定制化部署联邦学习组件的深入分析 - FederatedAI/KubeFATE GitHub Wiki

概述

KubeFATE按照部署环境分成Docker-Compose与Kubernetes两部分。前者作为快速上手的实验环境,后者为生产系统FATE集群设计。本文主要针对在Kubernetes环境进行讨论。目标是有自定义FATE部署的高级用户的如何自定义部署模块,增减FATE模块等需求。

KubeFATE分成两部分,KubeFATE CLI与KubeFATE服务。其中KubeFATE CLI是可执行二进制文件,直接下载到客户机使用;KubeFATE服务一般部署在与FATE,FATE-Serving一致的Kubernetes上,并且设置Service Account,使得KubeFATE服务有权限创建Pod, Service,Ingress, 具体的步骤可以参考:

  1. 使用KubeFATE在Kubernetes上部署联邦学习集群(v1.5.0版本)
  2. 使用KubeFATE在kubernetes上部署FATE集群

KubeFATE CLI提供了相应的命令,包括集群管理(kubefate cluster)、任务管理(kubefate job),Chart管理(kubefate chart),用户管理(kubefate user)四大部分。具体可参考kubefate help。接收用户指令后通过调用KubeFATE服务的RESTful API实现FATE, FATE Serving的集群管理。 KubeFATE服务的底层是基于Helm v3。用户传入的指令会经过渲染得到Helm Chart,并通过任务分配进行部署操作。

术语约定

本文提到的术语作以下约定:

  1. 客户机:指用户使用KubeFATE CLI的机器,可以是笔记本、Mac、Linux,不要求在Kubernetes集群内,但需要网络可联通到Kubernetes创建的Ingress;
  2. Kubernetes管理机:指可以使用kubectl的机器,可以在或者不在Kubernetes集群内,但需要网络联通到Kubernetes的API Server,且有足够的权限去创建Service Account,Service, Ingress和Pod。如果需要执行KubeFATE项目自带的RBAC.yaml,需要管理员权限;
  3. 服务器:指部署Kubernetes集群的机器。

Helm 3与Helm Chart

Helm v3.0.0以后的版本经常简称Helm 3,于2019年11月份发布。相较于之前版本(简称为Helm 2),有非常大的改变。最显著的区别是移除了Tiller组件。Helm 2是典型的客户端-服务器结构,Tiller组件作为服务,与Helm客户端交互,并通过Kubernetes API使用Kubernetes集群。可以看出,这个功能其实与KubeFATE服务比较雷同,我们选择了升级版本的Helm 3进行开发。Helm 3没有服务器端,通过Client直接与Kubernetes API直接交互,动态抓取集群状态。Helm 3的设计旨在简化权限的管理,避免状态同步带来的问题,但这个设计的缺点是权限管理完全依赖Kubernetes,配置繁杂,与第三方组件兼容需要在用户端做大量工作。由于FATE本身的配置比较复杂,为了简化用户的配置使用难度,以及与上层系统的兼容,我们把状态信息管理在KubeFATE服务内。明白这个架构后,可以总结两点:

  1. 由于Helm 3与Helm 2本身不兼容,KubeFATE必须与Helm 3使用,不兼容Helm 2。如果发现调用出问题,请检查是否客户机是否预先安装了Helm 2。如是,需要删除。
  2. 同步Helm 3与KubeFATE的状态是一个难题,在某些极端意外情况下,可能出现两者状态不统一,一个常见解决方法是通过Helm的命令去删除已有集群。这部分会随着版本更新逐渐修复各种意外情况,如果发现也可通过issue提到KubeFATE项目。

Helm Chart是Helm使用的包格式。Chart就是一个Kubernetes相关资源的文件集合。Helm Chart有特定的目录布局要求,它们可以打包到部署的版本存档中。另外,Helm Chart有一个社区,提供很多现成的Chart下载部署。可以通过helm pull ${chart_repo}/${chart_name}来下载。

(下文本节介绍会部分引用Helm Chart官方文档

Chart的文件结构

Chart是一个组织在文件目录的集合,名称就是Chart的名称。譬如WordPress的Chart可以存在wordpress/目录中,结构如下:

wordpress/
  Chart.yaml          # 包含了chart信息的YAML文件
  LICENSE             # 可选: 包含chart许可证的纯文本文件
  README.md           # 可选: 可读的README文件
  values.yaml         # chart 默认的配置值
  values.schema.json  # 可选: 一个使用JSON结构的values.yaml文件
  charts/             # 包含chart依赖的其他chart
  crds/               # 自定义资源的定义
  templates/          # 模板目录, 当和values 结合时,可生成有效的Kubernetes manifest文件
  templates/NOTES.txt # 可选: 包含简要使用说明的纯文本文件

用户如果需要开发Helm Chart,可以使用helm create NAME [flags],其中[flag]为:

--debug                       enable verbose output
--kube-apiserver string       the address and the port for the Kubernetes API server
--kube-as-group stringArray   Group to impersonate for the operation, this flag can be repeated to specify multiple groups.
--kube-as-user string         Username to impersonate for the operation
--kube-context string         name of the kubeconfig context to use
--kube-token string           bearer token used for authentication
--kubeconfig string           path to the kubeconfig file
-n, --namespace string            namespace scope for this request
--registry-config string      path to the registry config file (default "~/.config/helm/registry.json")
--repository-cache string     path to the file containing cached repository indexes (default "~/.cache/helm/repository")
--repository-config string    path to the file containing repository names and URLs (default "~/.config/helm/repositories.yaml")

来初始化Chart目录。其中关键的文件有,

Chart.yaml文件

为该Chart的metadata描述,里面包括apiVersion, name, versiondependencies等字段。我们可以通过该文件对Chart进行版本控制。这里还有一个重要的概念叫Chart的依赖,通过Chart.yaml的dependencies来描述。前面讲到Helm Chart有社区提供现成的Chart供下载部署,那我们在实现自己的Chart的时候可通过添加依赖,使用社区中已有的Chart,作为集群部署的一部分。譬如,部署一个Wordpress需要依赖一个Apache作为HTTP服务器,MySQL为数据库,可以在Chart.yaml里添加类似以下的内容,

dependencies:
  - name: apache
    version: 1.2.3
    repository: https://example.com/charts
  - name: mysql
    version: 3.2.1
    repository: https://another.example.com/charts

其中,

  • name字段是你需要的chart的名称
  • version字段是你需要的chart的版本
  • repository字段是chart仓库的完整URL

在定义好dependence后就可以通过helm dependency update下载依赖的Chart到chart/目录下。

Templates目录 和 values.yaml

Helm Chart模板是按照Go模板语言书写的,增加了部分函数。所有的模板文件存储在template/文件夹下。当Helm渲染Chart时,它会通过模板引擎遍历目录中每个文件。用户通过value.yaml文件包含模板的默认值。Values通过模板中的.Values对象访问values.yaml文件。譬如一个Deis数据库的Chart, 定义模板文件如下:

apiVersion: v1
kind: ReplicationController
metadata:
  name: deis-database
  namespace: deis
  labels:
    app.kubernetes.io/managed-by: deis
spec:
  replicas: 1
  selector:
    app.kubernetes.io/name: deis-database
  template:
    metadata:
      labels:
        app.kubernetes.io/name: deis-database
    spec:
      serviceAccount: deis-database
      containers:
        - name: deis-database
          image: {{ .Values.imageRegistry }}/postgres:{{ .Values.dockerTag }}
          imagePullPolicy: {{ .Values.pullPolicy }}
          ports:
            - containerPort: 5432
          env:
            - name: DATABASE_STORAGE
              value: {{ default "minio" .Values.storage }}

那Chart对应的value.yaml需要包含:

  • imageRegistry: Docker镜像的源注册表
  • dockerTag: Docker镜像的tag
  • pullPolicy: Kubernetes的拉取策略
  • storage: 后台存储,默认设置为"minio"

value.yaml的设置如下:

imageRegistry: "quay.io/deis"
dockerTag: "latest"
pullPolicy: "Always"
storage: "s3"

除此之外,模板中为了使用需要,提供了默认的预定义值如下:

  • Release.Name: 版本名称(非chart的);
  • Release.Namespace: 发布的chart版本的命名空间;
  • Release.Service: 组织版本的服务;
  • Release.IsUpgrade: 如果当前操作是升级或回滚,设置为true;
  • Release.IsInstall: 如果当前操作是安装,设置为true;
  • Chart: Chart.yaml的内容。因此,chart的版本可以从 Chart.Version 获得, 并且维护者在Chart.Maintainers里;
  • Files: chart中的包含了非特殊文件的类图对象。这将不允许您访问模板, 但是可以访问现有的其他文件(除非被.helmignore排除在外)。 使用{{ index .Files "file.name" }}可以访问文件或者使用{{.Files.Get name }}功能。 您也可以使用{{ .Files.GetBytes }}作为[]byte方位文件内容;
  • Capabilities: 包含了Kubernetes版本信息的类图对象。({{ .Capabilities.KubeVersion }})和支持的Kubernetes API 版本({{ .Capabilities.APIVersions.Has "batch/v1" }});

Helm Chart的基本知识如上,推荐读者前往Helm Chart的官方文档了解更多细节,这些都是如何自定义FATE, KubeFATE安装的基础:

KubeFATE架构以及渲染流程

KubeFATE的架构以及部署FATE的示意图如下:

KubeFATE的架构以及部署FATE的示意图 KubeFATE的服务部分,FATE集群都部署在Kuberentes的环境上,需要可以访问,并有权限去操作部署FATE集群的Kubernetes的kube-apiserver,一般会部署在同一个Kubernetes集群并使用service account,具体做法请参考代码中示例,以及本系列文章:使用KubeFATE在kubernetes上部署FATE集群。图中电脑为客户机,通过KubeFATE CLI访问KubeFATE服务的REST APIs模块进行操作。同时REST APIs也可外接其他管理软件,譬如FATE-Cloud作为一个组织内部的基础设施运维提供方。在API层下,我们使用了服务Facade的设计模式,并组合不同的服务接口。外部通过调用:

  1. Helm:也就是Helm 3的接口,主要做集群的部署,删除,升级等;
  2. Kubernetes APIs:FATE模块健康监控等。 集群的信息,用户鉴权信息,渲染后的Helm Chart都会缓存在MySQL中。

从架构图可以看出,如果我们需要自定义部署的集群,譬如增减模块,集成第三方软件,自定义模块内容等操作,其实就是需要自定义部署的Helm Chart。在代码中,我们提供了以下内容可参考:

  1. 每个版本都带有FATE与FATE-Serving的默认Chart,在:https://github.com/FederatedAI/KubeFATE/tree/master/helm-charts ,可通过Github的tag来切换不同版本;
  2. KubeFATE的CLI中有专门的Chart管理命令:
    • kubefate chart upload:上传新的Chart;
    • kubefate chart ls:列举KubeFATE中已有的Chart。上传过的Chart会按照类型与版本缓存在MySQL中;
    • kubefate chart delete:删除KubeFATE中已有的Chart。
  3. 我们的Chart内提供了Makefile来初始化和打包Helm Chart。一个建议是创建新的Helm Chart从我们默认Chart里修改。

在普通的Helm Chart基础上,我们做了另外一层抽象,也就是KubeFATE的渲染流程。拿FATE v1.5.0为例子,过程如下图:

KubeFATE的渲染流程

我们kubefate cluster install命令传入cluster.yaml文件,里面包含了chartName以及chartVersion字段。在KubeFATE服务会现在MySQL中查询是否已经有对应的本地Chart,如果没有会在FATECLOUD_REPO_URL中去查找。这个字段在部署KubeFATE服务的yaml,也就是代码中的k8s-deploy/kubefate.yaml中定义。在部署KubeFATE时,可以选择自定义的http地址。在离线部署环境下,可以选择用kubefate chart upload上传需要的chart文件,或者按照Helm Chart Repository的标准 创建内部仓储。另外,因为Harbor符合OCI标准,可以直接使用Harbor作为私有内部Chart仓储,参考Managing Helm Charts.

在KubeFATE服务找到部署需求的Helm Chart后,会读入。在原生的Helm 3基础上,我们多做了一层template的渲染。在KubeFATE中,cluster.yaml是用来供用户设置部署FATE什么模块,各模块的设置的。所以,每个KubeFATE的Chart中,会有一个value-template.yaml,我们还是使用标准的Go Template 为模板语言,渲染出标准Helm 3的value.yaml

得到用户自定义的value.yaml后,KubeFATE调用Helm 3根据value.yaml与Helm Chart template目录,创建出FATE v1.5.0集群。

总结一下自定义KubeFATE chart的注意点:

  1. 如果需要新建一个FATE, FATE-Serving的chart,建议拷贝已有的chart进行修改,保证value-template.yaml已经包含;
  2. cluster.yaml是用户的接口,需要考虑哪些变量需要透给用户。在决定一个变量往上透传到cluster.yaml,请保证value-template.yaml已经设置,可以生成合适的value.yaml,供Chart使用;
  3. 生成value.yaml后,就是标准的Helm 3的流程,建议熟悉Helm 3的Chart制作流程,本文没有提到的如hook这些功能也是可以使用的;
  4. Helm Chart是一个社区,我们可以通过dependencies集成其它系统。也欢迎大家PR自定义的Helm Chart到KubeFATE。目前KubeFATE的Chart目录为./helm-charts,PR时可以取合适的名字作为文件夹放在该目录下。