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

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS Agent(兼容OpenClaw),2核4GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 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
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
机器学习/深度学习 算法
低光图像增强
这篇摘要讨论了低光照图像增强技术,涉及HDRNet、GAN、轻量化伪影、语义分割网络和Retinex等方法。核心任务是提升图像亮度和细节。方法包括分布映射(如伽马矫正、直方图均衡化)、模型优化(Retinex理论)和深度学习(亮度增强与噪声去除)。传统方法不依赖数据,但可能产生伪影;深度学习方法需大量训练数据,无监督学习更优。不足之处在于缺乏成对数据集和精确标签。
701 1
|
3月前
|
人工智能 安全 Linux
OpenClaw(龙虾AI)保姆级部署(阿里云+Windows/Mac/Linux)+ 免费大模型配置+国民应用集成指南
2026年,AI圈最火的词非“养虾”莫属。这里的“虾”,不是餐桌上的海鲜,而是开源AI智能体OpenClaw的中文昵称——因图标形似小龙虾得名,更因“能干活、真落地”的核心能力,成为个人与企业追捧的“数字员工”。它打破了传统AI“只说不做”的局限,不再是单纯的聊天工具,而是能直接操控电脑、自动完成任务的自动化引擎,口号“THE AI THAT ACTUALLY DOES THINGS”精准点出其价值。
2578 1
|
XML Java 数据格式
SpringBoot中yaml格式、语法规则及数据读取方式(3种)
SpringBoot中yaml格式、语法规则及数据读取方式(3种)
1482 0
|
Kubernetes 容器
k8s容器时间与服务器时间不一致问题
k8s容器时间与服务器时间不一致问题
562 0
|
12月前
|
存储 运维 监控
OpenFeature 实战:统一特征开关在风控模型的落地与灰度发布方案
在金融风控场景中,模型迭代速度与线上稳定性之间的平衡是一大挑战。传统硬编码方式存在耦合度高、控制粒度粗、缺乏审计等问题,导致误拦截损失显著。本文介绍了基于 OpenFeature 的解决方案,通过动态配置、细粒度控制和多语言支持实现高效特征管理,并结合灰度发布、熔断机制和安全审计提升系统稳定性与发布安全性。实战数据显示,该方案显著缩短上线周期、降低故障率并提升模型覆盖率,具备高可用性和可扩展性,适用于复杂风控环境下的策略迭代需求。
783 8
|
存储 Kubernetes 安全
第四章 Helm仓库介绍配置国内仓库地址
第四章 Helm仓库介绍配置国内仓库地址
7978 2
|
NoSQL 网络协议 Redis
Nomad 系列 -Nomad 网络模式
Nomad 系列 -Nomad 网络模式
|
前端开发 JavaScript 数据可视化
前端自动化测试:Jest与Cypress的实战应用与最佳实践
【10月更文挑战第26天】前端自动化测试在现代软件开发中至关重要,Jest和Cypress分别是单元测试和端到端测试的流行工具。本文通过解答一系列问题,介绍Jest与Cypress的实战应用与最佳实践,帮助开发者提高测试效率和代码质量。
612 2
|
Java 关系型数据库 数据库连接
MyBatis-Plus整合SpringBoot及使用
务必记住,随着MyBatis-Plus版本的更新,一些具体的配置和使用方式可能会有所变动。在实际开发过程中,建议参考MyBatis-Plus的官方文档,以获取最新和详细的指导。
917 1
|
机器学习/深度学习 人工智能 算法
AI入门必读:Java实现常见AI算法及实际应用,有两下子!
本文全面介绍了人工智能(AI)的基础知识、操作教程、算法实现及其在实际项目中的应用。首先,从AI的概念出发,解释了AI如何使机器具备学习、思考、决策和交流的能力,并列举了日常生活中的常见应用场景,如手机助手、推荐系统、自动驾驶等。接着,详细介绍了AI在提高效率、增强用户体验、促进技术创新和解决复杂问题等方面的显著作用,同时展望了AI的未来发展趋势,包括自我学习能力的提升、人机协作的增强、伦理法规的完善以及行业垂直化应用的拓展等...
2395 3
AI入门必读:Java实现常见AI算法及实际应用,有两下子!