投稿:毛十三
管理这些资源并将它们与已部署的应用程序关联起来是一项挑战,尤其是跟踪已部署的应用程序(实际状态)及其原始来源(授权或期望状态)的更改和更新时。该应用程序的版本被锁定在 Kubernetes 平台中,使它们完全脱离了规范本身的版本(通常会在外部源代码管理库中进行跟踪)。
此外,静态规范通常不能在给定域、环境或云提供商之外重复使用,但需要花费大量时间进行创作和调试。工具可用于基于正则表达式提供的字符串进行替换,但这种自动化还需要编写或自定义以执行我们需要的任务,有些时候,这种方式会出现错误。
Helm 通过将相关的 Kubernetes 规范打包成一个简单的部署组件(称为 chart)来解决这些问题,这些组件可以参数化以获得最大的灵活性。同时,Helm 也能使用户在运行时中自定义应用程序包。如果你熟悉 apt 包或 yum 等 OS 包管理器以及 deb 或 rpm 等软件包,那么 Helm 以及 Helm chart 的概念你应该很熟悉。
先决条件
首先,你需要一个正在运行的 Kubernetes 集群,一个本地 Docker 客户端,以及一个预配置的 kubectl 客户端和 Kubernetes 集群配置。Helm 将使用 kubectl 在已配置的集群上部署 Kubernetes 资源。
* 注:Helm 的默认安装是不安全的!
安装 Helm
Helm 有两个部分:Helm 客户端(Helm)和 Helm 服务器(Tiller)。首先,你需要通过 Helm 客户端在 Kubernetes 集群上安装 Tiller。这里需要注意,用 Helm 客户端来部署 Tiller 服务器不是必须的,但现在这样做很方便。
Helm 客户端可以从源代码或预构建的二进制版本安装,通过 Linux 上的 Snap、macOS 上的 Homebrew 或 Windows 上的 Chocolatey 安装。但 Helm GitHub repo 还拥有一个安装程序 shell 脚本,该脚本将自动获取最新版本的 Helm 客户端并在本地安装。
下面是 Ubuntu 16.04 的演示示例,其中 Kubernetes 使用 kubeadm 在本地安装。
使脚本可执行并运行它,下载、安装最新版本的 Helm,此步骤需要 sudo 权限。
我们可以使用version
带有 client only flag(-c
)的命令来确保客户端可用:
此命令将挂起,并将使用我们的 kubeconfig 查找 Tiller ,但现在我们还没有 Tiller。
默认情况下,Helm 客户端使用 socat 设置端口转发到 Tiller 窗口。但在我们的例子中,由于 socat 已经安装为使用 kubeadm 进行 Kubernetes 集群初步设置的一部分,所以你并不需要执行这一步。
下面是安装时的一个例子:
如果你需要安装 socat,你可以从 apt:sudo apt-get install socat
。
此时我们应该在集群上部署 Tiller。
Tiller
Tiller 通常在你部署的 Kubernetes 集群上运行。对于开发,它也可以在本地运行并配置为与远程 Kubernetes 集群通信的方式(这很方便)!
将 Tiller 安装到集群中的最简单方法就是运行 helm init
。然后 Helm 将验证 Helm 本地环境是否已正确设置(或在必要时进行设置),使用 kubeconfig 的 current-context 连接到与 kubectl 相同的集群并安装 Tiller Pod。
init
有一堆选项来影响它的行为:
-
--canary-image
:安装 Tiller 的 canary 版本(测试最新功能); -
--client-only
:本地配置,但不安装 Tiller; -
--kube-context
:使用命名 context 来替代~/.kube/config
文件中的 current-context; -
--node-selectors
:指定安排 Tiller Pod 所需的节点标签; --override
:操纵最终 Tiller 清单的指定属性;
- 接受 Tiller 部署清单中任何有效属性的有效值
-
--output
:跳过 Tiller 部署清单的安装,只需将部署清单以 JSON 或 YAML 格式输出到 stdout 中; -
--tiller-image
:使用除最新版本之外的特定 Tiller 版本; -
--upgrade
:将 Tiller 升级到最新版本。
你也可以通过这个文档了解更多:https://docs.helm.sh/helm/#helm-init
让我们 init
通过使用 --output
标志来看一下将要部署的内容并通知它 output yaml(或者如果你愿意,可以使用 json):
在这里,我们看到了 Tiller 部署及其服务;我们可以简单地保存这些文件并使用 kubectl 进行部署,但这会有什么好处呢?
请注意两个环境变量:TILLER_NAMESPACE
和 TILLER_HISTORY_MAX
。--tiller-namespace
会影响的命名空间;TILLER_HISTORY_MAX
用于限制每个版本保存的最大修订数(0 表示没有限制),具有无限数量的修订会对性能产生影响,因此你需要在实践中使用 --history-max
标志将其设置为合理值。
Tiller 和 RBAC
限制 Tiller 将资源安装到某些命名空间是个好主意。使用 RBAC 时,我们可以通过为 Kubernetes API 提供身份(Kubernetes 服务帐户)并使用 Kubernetes 角色和绑定为其分配范围权限来限制任何应用程序对 Kubernetes API 进行访问。
这次超时我们可以将配置保持在最低限度,为 Tiller 分配集群管理集群角色,以便它可以部署到任何命名空间。
如果你的集群不是本地集群或测试集群,请不要在家中执行此操作!
首先,创建服务帐户:
现在创建集群角色绑定,将集群管理员角色分配给 Tiller 服务帐户:
部署 Tiller
现在我们可以部署 Tiller。首先使用 --service-account
标志来使用 Tiller 服务帐户:
Helm 会自动将其配置的文件放入~/.helm
中。将 Helm 客户端文件放在除 ~/.helm
之外的其他位置,$HELM_HOME
在运行之前设置环境变量 helm init,然后使用重要说明部署 Tiller。
请注意,默认情况下,Tiller 会使用不安全的 “允许未经身份验证的用户” 策略进行部署。为了防止这种情况,请 helm init 使用-tiller-tls-verify 标志运行。有关保护安装的更多信息,请参阅:https://docs.helm.sh/using_helm/#securing-your-helm-installation
* 注:默认的 Tiller 是不安全的!我们可以像在任何其他 Kubernetes 资源上一样在我们的集群上找到 Tiller:
在没有-c
的情况下运行 version
命令应该同时显示 Helm 和 Tiller 版本,并确保 Helm 可以找到并与 Tiller 对话。
是时候开始 Helming 了!
探索 chart
众所周知,chart 是一组 spec 文件,它们定义了一组 Kubernetes 资源(如服务、部署等),通常包含将应用程序部署为模板所需的所有资源。chart 资源模板使用户能够通过为模板中定义的某些(或所有)变量提供值来自定义,在安装时部署呈现资源的方式。
chart 还包括所有已定义变量的默认值,只需要很少(或不需要)自定义就能轻松部署 chart。与其他软件包管理器一样,我们希望使用 update 命令从我们配置的 repos 中获取 chart 的最新列表和更新:
请注意,Helm 跳过了“本地 chart 存储库”,但从我们唯一的“稳定”存储库中获得了更新。当你第一次安装 Helm 时,它预先配置为与本地存储库和官方 Kubernetes chart 存储库通信。官方存储库(名为 “stable”)包含一些精心策划和维护的常用软件 chart,如 elasticsearch、Influxdb、mariadb、nginx、prometheus、redis 等等。
列出你的 Helm repos 以显示已配置的内容:
你可以随时使用 helm repo add
命令添加其他的 repo。下面我们将使用稳定的 repo。
该 helm search
命令将向我们显示官方存储库中的所有可用 chart(因为它是唯一配置和更新的 repo):
请注意使用stable/
前置所有可用 chart。在 helm/charts 项目中,stable 文件夹包含经过严格升级并满足某些技术要求的所有 chart。孵化器 chart 也可用,但在满足这些标准之前还需要不断改进。你可以使用该 helm repo add
命令添加孵化器存储库(与任何其他存储库一样)并将其指向正确的 URL。
另外,请注意 CHART VERSION 和 APP VERSION 列。前者是 Helm chart 版本,必须按照 Helm 项目的规则,遵循 SemVer 2 格式。后者是实际软件的版本,在 Helm 中以自由形式存在,但与软件的发布规则相关。
helm search
可以显示所有可用的 chart。你可以通过使用过滤器进行搜索来缩小搜索结果范围:
为什么 traefik 在名单中?因为它的包描述与入口有关。我们可以helm inspect chart
看看发生了什么:
traefik chart 的关键字部分包含关键字“ingress”,因此它会显示在我们的搜索中。
部署 chart
稍后我们将探索 chart 的结构,但为了说明部署 chart 是多么容易,我们可以使用 repo 中的 chart。要安装 chart ,请使用helm install
命令,该命令仅需要一个参数:chart 的名称。你可以使用官方 helm repo 提供的容器化 Docker 注册表。
部署注册表:
刚刚发生了什么?
Helm 通过为所有变量注入默认值来呈现 Kubernetes 资源模板,然后通过将它们作为静态规范文件提交到 Kubernetes API 来部署 Kubernetes 集群上的资源。
安装 chart 后会创建一个新的 Helm 发布对象,上面的这个版本被命名为“kissable-clownfish”(如果你想使用你自己的版本名称,只需使用带有 install 命令标志的--name
)。
Helm Release 是一组基于 chart 的已部署资源。每次安装 chart 时,它都会使用自己的版本名称部署一整套 Kubernetes 资源。独特的命名有助于我们跟踪 Kubernetes 资源的相关性,并允许我们使用不同的自定义方式多次部署 chart。
在安装过程中,Helm 将打印有关创建资源的有用信息,在我们的示例中,就是 ConfigMap、Deployment、Secret 和 Service。要再次查看它,你可以使用 helm status 版本名称。我们需要使用我们新的注册表服务器,NOTES 输出的部分提供了一些使用它的线索:
此时,你的终端应该被劫持以进行端口转发。启动一个新终端并使用 Docker 与注册表进行交互。从 Kubernetes 主机上的 Docker 客户端,拉出一个像 alpine 这样的轻量级镜像:
现在重新标记它,在镜像仓库名称前加上 IP 端口并尝试推送它:
查询注册表 API ,确认注册表拥有我们的镜像:
成功!
这很简单,但我们只使用此 chart 的默认配置选项。你可能希望在部署之前自定义 chart。要查看给定 chart 可配置的选项,请使用helm inspect values
。
使用 Ctrl + C(^C
)杀死你的进程端口,然后检查 chart 值:
我们可以进行许多配置更改。最值得注意的是,我们可以为注册表部署入口记录(如果我们部署了入口控制器,则非常有用)。我们还可以配置许多不同的存储后端,比如 S3 bucket 和存储 AWS 访问密钥的相关 Secret。
但这些值来自哪里?
chart 剖析
chart 是以 chart 命名目录中的文件集合。到目前为止,我们只从远程仓库部署了一个 chart ,你可以通过查看 GitHub 上 docker-registry chart 的链接看到这些文件。
安装 chart 时,Helm 将目录的内容作为存档下载,并将其本地缓存在 Helm 客户端的工作空间目录中。默认位置是~/.helm
:
缓存目录包含归档格式的远程 chart 存储库的本地克隆:
如果我们想要浏览 chart,我们可以自己扩展存档,或者使用 Helm 命令!
使用fetch
命令和--untar
会在我们的本地系统上生成一个 unpacked chart:
关于其中大部分内容的解释,请参考 Helm 文档。现在我们将专注于 .yaml 值。之前我们用 helm inspect values 命令检查了这些值,现在,我们来看看使用 values.yaml 所展示的内容:
值文件是 chart 的作者为所有 chart 变量设置默认值的地方。你只需键入 helm install,chart 就会起作用。有些 chart 具有先决条件,通常会记录这些 chart,所以你可以提前知道它们。例如,WordPress chart 声明了以下先决条件:
- 先决条件:启用 Beta API 的 Kubernetes 1.4+ —— 底层基础架构中的 PV 配置器支持。
更新发布
如果要更改发行版的配置,你可以使用helm upgrade
命令。Helm 会更新自上次发布以来发生过变化的内容。Upgrade 与 install 使用相同的覆盖标志,因此你可以在初始安装或稍后的某个时候自定义 chart 。
我们最初的 Docker Registry Service 是 ClusterIP 类型,这就是我们需要端口转发的原因:
要确认它是以这种方式部署的,我们需要列出 Kubernetes 服务:
让我们更新服务以使用 NodePort,以便我们可以将注册表公开给外部世界。在更新期间或初始安装期间,有两种方法可以传递配置数据:
-
--values
(或-f
):指定带有覆盖的 YAML 文件 --set
:在命令行上指定覆盖
- 基本:
--set name=value
==name: value
- 键值对以逗号分隔
- 集支持多个复杂值
-
--set servers.port=80
变为:
我们知道这个 type
是一个服务,所以我们可以设置它的值为--set service.type=
:
根据上面的输出,Kubernetes 已更新我们的服务配置。该 NOTES 部分甚至已经更改,表明我们现在可以通过 http:// NODE_IP:NODE_PORT 访问我们的 docker-registry 服务。
我们可以用helm get values
来查看新设置是否生效。
没有这里提供的信息。Helm 只关注 yaml 键/值对的变化。让我们看看它是否有效:
成功!让我们通过推动镜像来测试它。
默认情况下,Docker 只信任 localhost 上的安全远程注册表或不安全注册表。由于 Kubernetes 在容器中运行我们的注册表,即使 Docker 守护程序和 docker-registry 在同一主机上运行,注册表也被视为“远程”。我们的 port-forward 使用了 localhost,因此 Docker 允许我们推送,但这次不会让我们这样通过:
有两种方法可以解决这种情况。一种方法是配置注册表服务器以支持 TLS。我们可以告诉 Docker 信任我们的非安全注册表(仅在非生产环境中执行此操作),而不是保护 docker-registry。这允许我们在不使用 SSL 证书的情况下使用注册表。
在 Kubernetes 主机上执行下一步很可能会破坏部署 kubeadm 的集群,因为它需要重新启动 Docker 并且所有 Kubernetes 服务都在容器中运行。因此,请使用 Kubernetes 主机外部的 Docker 安装 。
通过在下面创建一个配置文件来配置我们的 Docker 守护程序/etc/docker
(将示例 IP 替换为你之前存储在 NODE_IP 中的节点的 IP):
要使这些更改生效,你需要重新启动 Docker:
现在,你的 Docker 守护程序是否应该信任我们的注册表:
看它是否有效: