Rainbond Helm 应用商店对接管理实现分析

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: Rainbond 是一个完全开源,简单易用的云原生应用管理平台。除了支持内置的本地组件库, 云原生应用商店, 还支持 Helm 应用商店. 用户把常用的 Helm 仓库对接到 Rainbond 后, 可以简单, 方便地对Helm应用进行配置和安装。本文简单描述Rainbond 对接应用商店的实现原理。

Rainbond Helm 应用商店对接管理实现分析

本文作者:Rainbond 项目维护者:黄润豪

Rainbond 是一个完全开源,简单易用的云原生应用管理平台。除了支持内置的本地组件库, 云原生应用商店, 还支持 Helm 应用商店. 用户把常用的 Helm 仓库对接到 Rainbond 后, 可以简单, 方便地对Helm应用进行配置和安装。

Rainbond Helm 应用商店的功能

添加 Helm 应用商店

当前版本支持基于Helm v3协议添加外部应用商店。

添加 Helm 应用商店

Helm 应用列表

Helm 应用列表

搜索 Helm 应用

Helm 应用列表

Helm 应用商店实现

在开始介绍基本原理时, 我们先简单介绍下 Helm 仓库, 让大家对 Helm 仓库有基础的理解.

Helm 仓库

Helm 仓库的应用模板列表索引会放在 index.yaml 里面,可以通过 <仓库地址>/index.yaml 获取,

比如: https://openchart.goodrain.com/goodrain/rainbond/index.yaml

index.yaml 详情如下:

apiVersion: v1
entries:
  nfs-client-provisioner:
  - apiVersion: v1
    appVersion: 3.1.0
    created: "2021-07-08T08:20:40Z"
    description: nfs-client is an automatic provisioner that used your *already configured*
      NFS server, automatically creating Persistent Volumes.
    digest: 1197847e6ee3720e4f7ee493985fce3703aa42813fa409dc6c0ebaa4fef3f175
    home: https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client
    keywords:
    - nfs
    - storage
    maintainers:
    - email: bart@verwilst.be
      name: verwilst
    name: nfs-client-provisioner
    sources:
    - https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client
    urls:
    - charts/nfs-client-provisioner-1.2.8.tgz
    version: 1.2.8
  rainbond-console:
  - apiVersion: v2
    appVersion: 1.16.0
    created: "2021-07-08T08:36:05Z"
    description: Goodrain Rainbond-console Helm chart for Kubernetes
    digest: 5ecba097ee7425d16a1a4512c6bb82ca3e7d04b8491d098226c4a9517e3507be
    home: https://github.com/goodrain/rainbond
    icon: https://www.rainbond.com/images/rainbondlog.png
    name: rainbond-console
    sources:
    - https://github.com/goodrain/rainbond/
    type: application
    urls:
    - charts/rainbond-console-5.3.1.tgz
    version: 5.3.1
generated: "2021-07-08T08:36:12Z"
serverInfo: {}

可以看到 index.yaml 记录了应用模板列表, 每个应用模板的元数据, 包括 版本号(version), 说明(description), charts 包地址(urls) 等等.

基本原理

Rainond Helm 应用商店的基本原理: 识别 Helm 仓库, 获得 index.yaml, 解析得到每个应用模板, 并转化为 Rainbond 模型(Golang 结构体).

┌────────────────┐        ┌───────────────────────┐
│                │        │                       │
│   index.yaml   ├───────►│ application templates │
│                │        │                       │
└────────────────┘        └───────────────────────┘

添加 Helm 应用商店

添加Helm 应用商店需要把 Helm 应用商店的元数据记录下来, 持久化到数据库中. 在正式添加前, 还需要检查商店的可用心.

代码如下:

func (a *appStoreRepo) Create(ctx context.Context, appStore *domain.AppStore) error {
    // Check the availability of the app store.
    if err := a.isAvailable(ctx, appStore); err != nil {
        return err
    }
    return a.appStoreDao.Create(&model.AppStore{
        Name:     appStore.Name,
        URL:      appStore.URL,
        Branch:   appStore.Branch,
    })
}

Helm 应用列表

Helm 应用商店的主要逻辑:

  • 建立 HTTP 请求去获取 index.yaml 文件
  • 将 HTTP 响应反序列化到结构体 IndexFile 中. IndexFile 是 Helm 官方定义的结构体.
  • 遍历 IndexFile 的 Entries, 生成我们想要的应用模板列表(appTemplates)

代码如下:

func (h *helmAppTemplate) fetch(ctx context.Context, appStore *domain.AppStore) ([]*domain.AppTemplate, error) {
      // 建立 HTTP 请求去访问 index.yaml
    req, err := http.NewRequest("GET", appStore.URL+"/index.yaml", nil)
    ...
    body, err := ioutil.ReadAll(resp.Body)
    ...
    jbody, err := yaml.YAMLToJSON(body)
    ...
      // 解析返回结果, 反序列化到结构体 hrepo.IndexFile 中
    var indexFile hrepo.IndexFile
    if err := json.Unmarshal(jbody, &indexFile); err != nil {
        return nil, errors.Wrap(err, "read index file")
    }
    ...
      // 解析成应用列表
    var appTemplates []*domain.AppTemplate
    for name, versions := range indexFile.Entries {
        appTemplate := &domain.AppTemplate{
            Name:     name,
            Versions: versions,
        }
        appTemplates = append(appTemplates, appTemplate)
    }
    return appTemplates, nil
}

应用列表的缓存

为了加快响应速度, 应用模板列表会被缓存起来, 目前支持的缓存是内存.

代码如下:

appTemplates, err := s.appTemplater.Fetch(ctx, appStore)
if err != nil {
  return nil, err
}
appStore.AppTemplates = appTemplates
s.store.Store(appStore.Key(), appStore)

防止缓存击穿

使用缓存时, 我们还应该考虑高并发情况下缓存击穿的问题. 缓存击穿的意思是在某个数据过期后, 有大量的并发访问该数据, 从而导致数据库压力剧增. 当然, 我们获取应用列表不是去访问数据库, 而是发出 HTTP 请求, 获取 index.yaml.

为了解决防止缓存击穿, 引入了 singleflight. singleflight 会确保同一个 key, 同一时刻, 只会有一个请求在执行. 也就是说, 对于同一个 Helm 应用商店, 在同一个时刻, 只会有一个获取 index.yaml 文件的 HTTP 请求.

代码如下:

type helmAppTemplate struct {
    singleflight.Group
}
func (h *helmAppTemplate) Fetch(ctx context.Context, appStore *domain.AppStore) ([]*domain.AppTemplate, error) {
    // single flight to avoid cache breakdown
    v, err, _ := h.Do(appStore.Key(), func() (interface{}, error) {
        return h.fetch(ctx, appStore)
    })
    appTemplates := v.([]*domain.AppTemplate)
    return appTemplates, err
}

Charts 包的缓存

每次安装应用时, Helm 会先拉取应用的 Charts 包, 缓存到本地存储中.

Helm 对 Charts 包的缓存

在 Mac 中, Helm 缓存 charts 包的路径是 $HOME/Library/Caches/helm/repository:

.
├── bitnami-charts.txt
├── bitnami-index.yaml
├── mysql-8.5.1.tgz
├── nginx-8.8.3.tgz
├── phpmyadmin-8.2.4.tgz
├── rainbond-charts.txt
├── rainbond-index.yaml
├── rainbond-operator-1.3.0.tgz
├── redis-cluster-5.0.1.tgz

所有 Helm 仓库的 chart 包都会缓存到该目录下. 需要注意的是, 该目录下的 charts 包是不区分仓库的. 也就是说, 如果 bitnami 和 rainbond 仓库都有版本为 8.5.1 的 mysql 应用, 那么 bitnami 的 mysql-8.5.1.tgz 就可能会被 rainbond 的 mysql-8.5.1.tgz 覆盖掉, 导致 bitnami 仓库的缓存失效. 详情请查看 #4618

Rainbond 对 Charts 包的缓存

Rainbond 没有直接沿用 Helm 对 Charts 包的缓存方式, 而是做了一些扩展, 使其支持区分仓库的 Charts 包的缓存:

  • 构造 Charts 包的缓存路径, 格式为 主缓存路径/仓库名/应用名/应用版本/.
  • 构造应用的包名 应用名-版本号.tgz.
  • 检查存储中时候已经有目标 Charts 包, 并检查他们的 hash 是否一样.
  • 如果缓存红没有目标 Charts 包, 则进行下载, 将Charts 包缓存起来.

代码如下:

func (h *Helm) locateChart(chart, version string) (string, error) {
    repoAndName := strings.Split(chart, "/")
    ...
    // Charts 包的路径为: 主缓存路径/仓库名/应用名/应用版本/
    chartCache := path.Join(h.settings.RepositoryCache, chart, version)
    cp := path.Join(chartCache, repoAndName[1]+"-"+version+".tgz")
    if f, err := os.Open(cp); err == nil {
        defer f.Close()
        // 检查 hash
        // check if the chart file is up to date.
        hash, err := provenance.Digest(f)
        ...
        // get digiest from repo index.
        digest, err := h.getDigest(chart, version)
        ...
        if hash == digest {
            return cp, nil
        }
    }
    cpo := &ChartPathOptions{}
    cpo.Version = version
    settings := h.settings
    // 下载 Charts 包
    cp, err := cpo.LocateChart(chart, chartCache, settings)
    ...
    return cp, err
}

接下来

目前的实现还有非常大的优化的空间, 比如应用列表缓存插件化, 应用列表缓存可过期, 清理 Charts 包缓存.

应用列表缓存插件化

应用列表缓存目前只支持内存. 随着 Helm 商店数量的越来越多, 应用模板的数量也将越来越多, 这对缓存的大小的要求将会比较高. 这场景下, 内存显然不是合适选择. 所以, 有必然将缓存抽象出来, 与内存解耦, 变得插件化, 可以轻松得对接其它的缓存, 比如 redis.

应用列表缓存可过期

细心的同学可能已经发现, 目前的内存缓存是不支持过期的, 也就是说, 在不重启应用的情况下, 缓存会越来越大. 所以, 不管是缓存在内存中, 还是缓存在 redis 中, 都非常有必要支持缓存过期. 缓存的过期策略可以是 LRU, LFU 等等, 同时结合过期时间, 清理长时间不被使用的缓存.

另外, 减少应用模板中不必要的字段, 从而减小每个应用模板的大小, 也是个不错的优化点.

清理 Charts 包缓存

Charts 包缓存在磁盘上, 不会过期, 目前也没有清理的机制, 这可能会使得磁盘的占用非常大. 接下来, 需要设计合适的方案对其进行定期的清理.

总结

本文结合代码和大家一起分析了Rainbond Helm 应用商店对接实现.

Reference

  1. Helm: https://github.com/helm/helm
  2. What is cache penetration, cache breakdown and cache avalanche?: https://www.pixelstech.net/article/1586522853-What-is-cache-penetration-cache-breakdown-and-cache-avalanche
  3. singleflight: https://pkg.go.dev/golang.org/x/sync/singleflight
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
负载均衡 应用服务中间件 API
Nginx、Kong、Apisix、Gateway网关比较
Nginx、Kong、Apisix、Gateway网关比较
5551 1
Nginx、Kong、Apisix、Gateway网关比较
|
Ubuntu
Ubuntu系统镜像下载,国内镜像站大全(山大/清华/阿里/浙大/中科大...)
装Ubuntu,是很多理工科同学入门的第一个挑战,首先我们就需要找到一个能用的iso镜像,根据你的网络环境的不同,不同的站点下载速度会不一样,下面列举一下几个比较好用的,都是来自Ubuntu官方推荐镜像站链接导航国内分区
13198 1
|
存储 缓存 NoSQL
Harbor高可用集群设计及部署(基于离线安装方式一)
基于Harbor离线安装方式的高可用方案设计及部署。
1040 0
|
XML Java 数据格式
SpringBoot中yaml格式、语法规则及数据读取方式(3种)
SpringBoot中yaml格式、语法规则及数据读取方式(3种)
1279 0
|
Kubernetes 容器
k8s容器时间与服务器时间不一致问题
k8s容器时间与服务器时间不一致问题
466 0
|
缓存 Linux 开发工具
CentOS 7- 配置阿里镜像源
阿里镜像官方地址http://mirrors.aliyun.com/ 1、点击官方提供的相应系统的帮助 :2、查看不同版本的系统操作: 下载源1、安装wget yum install -y wget2、下载CentOS 7的repo文件wget -O /etc/yum.
267563 0
|
9月前
|
存储 运维 监控
OpenFeature 实战:统一特征开关在风控模型的落地与灰度发布方案
在金融风控场景中,模型迭代速度与线上稳定性之间的平衡是一大挑战。传统硬编码方式存在耦合度高、控制粒度粗、缺乏审计等问题,导致误拦截损失显著。本文介绍了基于 OpenFeature 的解决方案,通过动态配置、细粒度控制和多语言支持实现高效特征管理,并结合灰度发布、熔断机制和安全审计提升系统稳定性与发布安全性。实战数据显示,该方案显著缩短上线周期、降低故障率并提升模型覆盖率,具备高可用性和可扩展性,适用于复杂风控环境下的策略迭代需求。
473 8
|
存储 Kubernetes 安全
第四章 Helm仓库介绍配置国内仓库地址
第四章 Helm仓库介绍配置国内仓库地址
7288 2
|
运维 前端开发 Java
云巧组装式交付介绍
Gartner在2021年10月19日,正式发布了2022年重要战略趋势。其中包括了“组装式应用”这一战略。 云巧是“组装式应用”理念的落地,是围绕生态,面向产业的首个产业数字组件中心。 你可以从本文了解组装式开发的理念,以及阿里云GTS通过组装式理念交付项目的最佳实践:云巧。 如果你是阿里及阿里云生态合作伙伴的开发者,可以进一步访问云巧首页:https://gts.work/portal/yunqiao ,进一步了解云巧的能力。 即使你不是阿里及阿里云生态合作伙伴的开发者,也可以在自己的日常的开发过程中通过运用可组装式理念提升业务交付效率。
6457 1
云巧组装式交付介绍
|
Java 关系型数据库 数据库连接
MyBatis-Plus整合SpringBoot及使用
务必记住,随着MyBatis-Plus版本的更新,一些具体的配置和使用方式可能会有所变动。在实际开发过程中,建议参考MyBatis-Plus的官方文档,以获取最新和详细的指导。
835 1

热门文章

最新文章