1. 介绍

Helm 是 Kubernetes 的包管理工具,其部署单元是 Chart。与大多数包管理器类似,Helm 也支持 Chart 之间的依赖管理。虽然 Helm 支持手动列出依赖关系,但它 没有提供直接查看已安装 Chart(即 Release)依赖树的功能

在本教程中,我们将探索 Helm 中依赖项的枚举与解析机制:

  • 首先回顾 Chart 依赖的基本概念
  • 接着使用 Helm 原生命令列出 Chart 依赖
  • 然后通过 Shell 脚本实现列出已安装 Release 的依赖
  • 最后演示一个支持递归列出依赖的脚本

本文的代码在 Debian 12(Bookworm)系统中测试,使用 GNU Bash 5.2.15 和 Helm 3.14。除非特别说明,这些命令也适用于大多数 POSIX 兼容环境。


2. Chart 依赖简介

即使是最基础的 Kubernetes 部署,也由多个组件构成。

例如,一个空的 Kubernetes 集群 依然运行多个 Pod:

$ kubectl get all --namespace=kube-system
NAME                               READY   STATUS    RESTARTS        AGE
pod/coredns-5dd0666b68-lp7b5       1/1     Running   1               9d
pod/etcd-xost                      1/1     Running   1               9d
pod/kube-apiserver-xost            1/1     Running   1               9d
pod/kube-controller-manager-xost   1/1     Running   1               9d
pod/kube-proxy-tldfh               1/1     Running   1               9d
pod/kube-scheduler-xost            1/1     Running   1               9d
pod/storage-provisioner            1/1     Running   1 (6d22h ago)   9d

NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   9d

NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   9d

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   1/1     1            1           9d

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/coredns-5dd0666b68   1         1         1       9d

同样,Helm 安装的 Chart 通常也会生成包含多个 Pod 和资源的 Release

$ helm install gabe565/nightscout --generate-name --create-namespace --namespace nightscout
[...]
$ kubectl get all --namespace nightscout
NAME                                        READY   STATUS    RESTARTS   AGE
pod/nightscout-1709766626-f7d430108-tzp8j   1/1     Running   0          49s
pod/nightscout-1709766626-mongodb-0         1/1     Running   0          49s

NAME                                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
service/nightscout-1709766626           ClusterIP   10.105.224.56   <none>        1337/TCP    49s
service/nightscout-1709766626-mongodb   ClusterIP   10.101.52.236   <none>        27017/TCP   49s

NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nightscout-1709766626   1/1     1            1           49s

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/nightscout-1709766626-f7d430108   1         1         1       49s

NAME                                             READY   AGE
statefulset.apps/nightscout-1709766626-mongodb   1/1     49s

由于这些自然的资源树结构,Helm 官方支持依赖管理机制。例如,一个依赖 Chart 可以通过名称、版本和 URL 被嵌入到主 Chart 的元数据中。

需要注意的是,语法在 Helm 不同版本中略有不同:

  • Helm 2 使用 requirements.yaml
  • Helm 3 将依赖信息整合进 Chart.yaml

但无论哪种方式,依赖解析机制是相同的。


3. 列出 Chart 依赖

Helm 提供了 dependency 子命令用于列出 Chart 的依赖。

不过,不同于其他包管理器,Helm 并不支持直接列出已安装或远程 Chart 的依赖树

要使用该功能,我们需要先下载并解压 Chart:

$ helm pull gabe565/ascii-movie --untar

也可以指定版本:

$ helm pull gabe565/ascii-movie --version 0.0.3 --untar

进入解压目录后,可以看到 Chart.yamlcharts/ 目录:

$ cd ascii-movie
$ ls
Chart.lock  charts  Chart.yaml  README.md  templates  values.yaml

然后运行 Helm 命令列出依赖:

$ helm dependency list
NAME    VERSION REPOSITORY                              STATUS
common  1.5.1   https://bjw-s.github.io/helm-charts     unpacked

说明该 Chart 依赖一个名为 common 的 Chart。

我们可以将这个过程封装成一条命令:

$ helm pull <CHART> --untar && helm dependency list ./<CHART_NAME> && rm -rf <CHART_NAME>

其中:

  • <CHART> 可以是任何支持的 Chart 源格式(如 repo 名、OCI 地址等)
  • <CHART_NAME> 是 Chart 名,通常与解压后的目录名一致

4. 查看已安装 Chart 的依赖

虽然 Helm 本身不支持查看已安装 Chart 的依赖,但我们可以通过 Release 信息反推出来。

先查看目标 Release 的信息:

$ helm list nightscout-1709766626
NAME                   NAMESPACE   REVISION  UPDATED                                  STATUS    CHART              APP VERSION
nightscout-1709766626  nightscout  1         2024-03-08 03:08:24.619666646 -0500 EST  deployed  nightscout-0.10.0  15.0.2

得知该 Release 是由 nightscout-0.10.0 Chart 安装而来。

接下来通过 Helm 命令列出所有匹配的 Chart:

$ helm list --filter nightscout --all-namespaces
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
gabe565/nightscout      0.10.0          15.0.2          Web-based CGM (Continuous Glucose Monitor) to a...

这时,我们可以使用之前的方法:

$ helm pull gabe565/nightscout --untar && helm dependency list ./nightscout && rm -rf nightscout
NAME    VERSION REPOSITORY                              STATUS
common  1.5.1   https://bjw-s.github.io/helm-charts     unpacked
mongodb 14.5.1  https://charts.bitnami.com/bitnami      unpacked

如果本地没有配置该仓库,也可以使用 OCI 地址:

$ helm pull oci://ghcr.io/gabe565/charts/nightscout --untar && helm dependency list ./nightscout && rm -rf nightscout

⚠️ 注意:这种方式只能列出一级依赖,无法展示完整的依赖树。


5. 自动化列出已安装 Chart 的依赖

如果我们满足以下前提条件,就可以实现自动化列出依赖:

  • Chart 仓库已配置或 Chart 来自 Artifact Hub
  • Chart 标识符格式为 name-version
  • Chart 依赖无循环

5.1 初始化脚本

#!/usr/bin/env bash

release_filter=$1
multilevel=$2

# 全局数组,用于保存待处理的 Chart 标识符
charts=()

5.2 Chart 依赖处理函数

function chart_deps() {
  [ ${#charts[@]} -eq 0 ] && return

  local chart_identifier="${charts[0]}"
  local chart_name chart_version repo_url
  local deps

  IFS=- read chart_name chart_version repo_url <<< "$chart_identifier"

  printf 'Processing %s from %s ...\n' $chart_name $repo_url

  if [[ $repo_url =~ 'oci://' ]]; then
    helm pull $repo_url --version $chart_version --untar >/dev/null 2>&1
  else
    helm pull $chart_name --repo $repo_url --version $chart_version --untar >/dev/null 2>&1
  fi

  if [ $? -ne 0 ]; then
    printf 'Unable to pull %s from %s.\n\n' $chart_name $repo_url
    return
  fi

  deps="$(helm dependency list ./$chart_name)"

  printf '%s deps:\n%s\n\n' "$chart_name-$chart_version" "$deps"

  if [ -n "$deps" ] && [ -n "$multilevel" ]; then
    charts+=($(echo "$deps" | awk 'NR > 1 { print $1"-"$2"-"$3; }'))
  fi

  rm -rf $chart_name
  rm -rf $chart_name-$chart_version.tgz
}

5.3 主流程

# 获取匹配的 Release 数量
releases="$(helm list --filter $release_filter --all-namespaces --no-headers | wc --lines)"
if [ $releases -ne 1 ]; then
  >&2 printf 'No or multiple release matches: --filter %s.' $release_filter
  exit 1
fi

# 获取 Chart 标识符
chart_identifier="$(helm list --filter $release_filter --all-namespaces --output=yaml | awk '/chart:/ { print $2; }')"
IFS=- read chart_name chart_version <<< "$chart_identifier"

# 获取仓库 URL
chart_path="$(helm search repo $chart_name --version $chart_version --output yaml | grep 'version: '$chart_version -B 1 | awk '/name:/ { print $2; }')"
if [ -n "$chart_path" ]; then
  IFS=/ read repo_name chart_name <<< "$chart_path"
  repo_url="$(helm repo list | awk '/'$repo_name'/ { print $2; }')"
else
  repo_url="$(helm search hub $chart_name --output yaml | grep 'version: '$chart_version -B 2 | awk 'NR == 1 && /url:/ { print $2; }')"
fi

if [ -z "$repo_url" ]; then
  >&2 printf 'Repository not found for release chart %s version %s.' $chart_name $chart_version
  exit 1
fi

chart_identifier=$chart_identifier-$repo_url
charts+=("$chart_identifier")

# 开始处理依赖队列
while [ ${#charts[@]} -ne 0 ]; do
  chart_deps 
  charts=("${charts[@]:1}")
done

该脚本可以递归列出所有依赖项,适用于 Release 或任意 Chart。


6. 总结

本文介绍了如何使用 Helm 命令和 Shell 脚本查看已安装 Chart 的依赖。

虽然 Helm 没有原生支持查看 Release 的依赖树,但我们可以通过下载 Chart 并结合脚本实现这一功能。

关键点如下:

  • Helm 依赖信息存储在 Chart.yaml
  • 使用 helm dependency list 可查看本地 Chart 的依赖
  • 通过 helm pull + Shell 脚本可实现查看已安装 Chart 的依赖
  • 使用队列机制可实现递归依赖解析

如果你需要管理复杂的 Helm Chart 依赖关系,这个脚本将是一个非常实用的工具。


原始标题:How to List Installed Helm Chart Dependencies Recursively