Helm 是 Deis (https://deis.com/) 开发的一个用于 kubernetes 的包管理器。每个包称为一个 Chart,一个 Chart 是一个目录(一般情况下会将目录进行打包压缩,形成 name-version.tgz 格式的单一文件,方便传输和存储),可以将 Helm 看作 Kubernetes 下的 apt-get/yum。

基本概念

chart

chart 是描述相关的一组 Kubernetes 资源的文件集合。 chart 通过创建为特定目录树的文件,将它们打包到版本化的压缩包,然后进行部署。

  • Chart.yaml 是必须的,它记录了 chart 的一些信息: chart 版本和名字等
  • templates 下是 kubernetes资源的模板
    • NOTES.txt 说明文件,helm install之后展示给用户看的内容
    • deployment.yaml 创建k8s资源的yaml文件
    • _helpers.tpl: 下划线开头的文件,可以被其他模板引用.
  • values.yaml 存放了模板 中的变量的值
  • charts: 依赖其他包的charts文件
  • requirements.yaml: 依赖的charts
  • README.md: 开发人员自己阅读的文件

一个最小的chart目录,只需要包含一个Chart.yaml,和templates目录下一个k8s资源文件

Release

Release表示在 Kubernetes 集群上运行的 Chart 的一个实例。在同一个集群上,一个 Chart 可以安装很多次。每次安装都会创建一个新的 release。例如一个 MySQL Chart,如果想在服务器上运行两个数据库,就可以把这个 Chart 安装两次。每次安装都会生成自己的 Release,会有自己的 Release 名称。

Repository

Repository用于发布和存储 Chart 的存储库。

helm

helm就是一个命令行下客户端工具,主要用于kubernetes应用chart的创建/打包/发布已经创建和管理和远程Chart仓库。

Tiller

Tiller就是helm的服务端,部署于kubernetes内,Tiller接受helm的请求,并根据chart生成kubernetes部署文件(helm称为release),然后提交给 Kubernetes 创建应用。Tiller 还提供了 Release 的升级、删除、回滚等一系列功能。

架构

Chart Install 过程

  • Helm从指定的目录或者tgz文件中解析出Chart结构信息
  • Helm将指定的Chart结构和Values信息通过gRPC传递给Tiller
  • Tiller根据Chart和Values生成一个Release
  • Tiller将Release发送给Kubernetes运行。

Chart Update过程

  • Helm从指定的目录或者tgz文件中解析出Chart结构信息
  • Helm将要更新的Release的名称和Chart结构,Values信息传递给Tiller
  • Tiller生成Release并更新指定名称的Release的History
  • Tiller将Release发送给Kubernetes运行

Chart Rollback过程

  • helm将会滚的release名称传递给tiller
  • tiller根据release名称查找history
  • tiller从history中获取到上一个release
  • tiller将上一个release发送给kubernetes用于替换当前release

我们可以简单的看一下基本的交互:

安装和使用

安装

client 管理 charts,可在本地运行,一般运行在CI/CD Server上。而 server (tiller)运行在Kubernetes集群上,管理chart安装的release。

客户端安装

到 Helm Release 下载二进制文件,根据使用 的操作系统不同下载不同的版本,这里以 Linux上V2.15.1 为例,解压后将可执行文件 helm 拷贝至 usr/local/ bin 目录下即可, 这样 Helm 客户端就在这台机器上安装完了。

# 在helm客户端主机上,一般为master主机
wget https://get.helm.sh/helm-v2.14.2-linux-amd64.tar.gz
tar xf helm-v2.14.2-linux-amd64.tar.gz
mv helm /usr/local/bin/
helm version

服务端安装

服务端安装就是Tiller服务的安装,我们需要将Tiller服务安装在k8s集群中,来监听来自 Helm client 的请求,安装 chart 到 Kubernetes 集群,并跟踪随后的发布通过与 Kubernetes 交互升级或卸载 chart。

需要创建helm相关的角色,我们来看一下对应的资源配置清单clusterrole.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'

创建ClusterRole

$ kubectl create -f clusterrole.yaml

创建ServiceAccount并使用ClusterRoleBinding将其与ClusterRole关联

$ kubectl create serviceaccount -n kube-system tiller
$ kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

初始化Helm,其实也就是在k8s上部署tiller

$ helm init --service-account tiller --skip-refresh
Creating /root/.helm
Creating /root/.helm/repository
Creating /root/.helm/repository/cache
Creating /root/.helm/repository/local
Creating /root/.helm/plugins
Creating /root/.helm/starters
Creating /root/.helm/cache/archive
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.

helm init. Helm 默认会去 gcr.io 拉取 tiller 的镜像,有时镜像拉不下来,可以指定 tiller 的镜像:

helm init --tiller-image registry.cn-hangzhou.aliyuncs.com/softputer/tiller:v2.15.1

tiller默认被部署在k8s集群中的kube-system这个namespace下。

kubectl get pod -n kube-system -l app=helm

再次helm version可以打印客户端和服务端的版本

helm version
Client: &version.Version{SemVer:"v2.7.2", GitCommit:"8478fb4fc723885b155c924d1c8c410b7a9444e6", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.7.2", GitCommit:"8478fb4fc723885b155c924d1c8c410b7a9444e6", GitTreeState:"clean"}

到这里就安装好了。

使用

直接创建release

先看一个实例安装redis:下面声明了一个版本名称为my-redis,并且指定该部署将使用持久化存储,大小为15G

$ helm install --name my-redis \
--set persistence.enabled=true,persistence.size=15Gi stable/redis

我们看一下安装的过程

NAME:   my-influx
LAST DEPLOYED: Thu Mar 14 12:36:50 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1beta1/Deployment
NAME                   DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
my-influx-redis-slave  1        1        1           0          0s

==> v1beta2/StatefulSet
NAME                    DESIRED  CURRENT  AGE
my-influx-redis-master  1        0        0s

==> v1/Pod(related)
NAME                                    READY  STATUS             RESTARTS  AGE
my-influx-redis-slave-556f9894b7-wd5cf  0/1    ContainerCreating  0         0s
my-influx-redis-master-0                0/1    Pending            0         0s

==> v1/Secret
NAME             TYPE    DATA  AGE
my-influx-redis  Opaque  1     0s

==> v1/ConfigMap
NAME                    DATA  AGE
my-influx-redis         3     0s
my-influx-redis-health  3     0s

==> v1/Service
NAME                    TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)   AGE
my-influx-redis-master  ClusterIP  10.107.184.240  <none>       6379/TCP  0s
my-influx-redis-slave   ClusterIP  10.97.224.81    <none>       6379/TCP  0s


NOTES:
** Please be patient while the chart is being deployed **
Redis can be accessed via port 6379 on the following DNS names from within your cluster:

my-influx-redis-master.default.svc.cluster.local for read/write operations
my-influx-redis-slave.default.svc.cluster.local for read-only operations


To get your password run:

    export REDIS_PASSWORD=$(kubectl get secret --namespace default my-influx-redis -o jsonpath="{.data.redis-password}" | base64 --decode)

To connect to your Redis server:

1. Run a Redis pod that you can use as a client:

   kubectl run --namespace default my-influx-redis-client --rm --tty -i --restart='Never' \
    --env REDIS_PASSWORD=$REDIS_PASSWORD \
   --image docker.io/bitnami/redis:4.0.13 -- bash

2. Connect using the Redis CLI:
   redis-cli -h my-influx-redis-master -a $REDIS_PASSWORD
   redis-cli -h my-influx-redis-slave -a $REDIS_PASSWORD

To connect to your database from outside the cluster execute the following commands:

    kubectl port-forward --namespace default svc/my-influx-redis 6379:6379 &
    redis-cli -h 127.0.0.1 -p 6379 -a $REDIS_PASSWORD

查看release

$ helm ls
NAME         REVISION    UPDATED                     STATUS      CHART          APP VERSION    NAMESPACE
my-influx    1           Tue May 30 21:18:43 2017       DEPLOYED    redis-6.3.0    4.0.13         default

删除release

$ helm del  my-influx
release "my-influx" deleted

创建chart来创建release

Helm 提供了 create 指令建立一个 Chart 基本结构

[root@master mychart]# helm create mychart
Creating mychart
[root@master mychart]# ls
mychart
[root@master mychart]# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml                     # 部署相关资源
│   ├── _helpers.tpl                            # 模版助手
│   ├── ingress.yaml                            # ingress资源
│   ├── NOTES.txt                                   # chart的帮助文本,运行helm install展示给用户
│   ├── service.yaml                            # service端点
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 8 files

在上面我们已经对具体的文件做过了说明,这边在具体描述一下

  • charts目录中是本chart依赖的chart,当前是空的
  • Chart.yaml这个yaml文件用于描述Chart的基本信息,如名称版本等

    $ cat Chart.yaml apiVersion: v1 description: A Helm chart for Kubernetes name: mychart version: 0.1.0

  • templates是Kubernetes manifest文件模板目录,模板使用chart配置的值生成Kubernetes manifest文件。模板文件使用的Go语言模板语法

  • templates/NOTES.txt 纯文本文件,可在其中填写chart的使用说明

  • value.yaml 是chart配置的默认值

我们一般只要编写模版文件和对应的赋值文件,比如

rm -rf mychart/templates/*
# 我们首先创建一个名为 mychart/templates/configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "Hello World"

使用helm在Kubernetes上安装chart时,实际上是将chart的模板生成Kubernetes使用的manifest yaml文件。 在编写chart的过程中可以chart目录下使用helm install –dry-run –debug ./来验证模板和配置。

helm install --dry-run --debug ./
[debug] Created tunnel using local port: '22635'

[debug] SERVER: "127.0.0.1:22635"

[debug] Original chart version: ""
[debug] CHART PATH: /root/helm/hello-svc

NAME:   foolish-zebra
REVISION: 1
......输出基于配置值和模板生成的yaml文件

当我们设置完 Chart 后,就可以通过 helm 指令打包

$ helm package example/
example-0.1.0.tgz

通过helm安装

$ helm install ./example-0.1.0.tgz

也可以不打包,直接安装,由于创建的yaml文件在template下,tiller读取此文件,会将其发送给kubernetes。

[root@master mychart]# helm install ./mychart/
NAME:   enervated-dolphin
LAST DEPLOYED: Sun Jul 21 09:29:13 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME               DATA  AGE
mychart-configmap  1     0s

[root@master mychart]# kubectl get cm mychart-configmap
NAME                DATA   AGE
mychart-configmap   1      2m6s
[root@master mychart]# kubectl describe cm mychart-configmap
Name:         mychart-configmap
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
myvalue:
----
this is my chart configmap
Events:  <none>


[root@master mychart]# helm get manifest enervated-dolphin

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "this is my chart configmap"

查看release

helm list
NAME            REVISION        UPDATED                         STATUS                      CHART           NAMESPACE
enervated-dolphin   1               Thu Dec 21 22:04:19 2017        DEPLOYED        enervated-dolphin-0.1.0 default

删除release

helm delete enervated-dolphin
release "enervated-dolphin" deleted

添加模板调用

一、内置对象

1、release

修改下之前的configmap为如下内容

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"

模板指令 {{.Release.Name}} 将 release 名称注入模板。也就是将上面的enervated-dolphin的release的名称赋值给当前的{{.Release.Name}}

Release 是可以在模板中访问的顶级对象之一。

Release:这个对象描述了 release 本身。它里面有几个对象:
Release.Name:release 名称
Release.Time:release 的时间
Release.Namespace:release 的 namespace(如果清单未覆盖)
Release.Service:release 服务的名称(始终是 Tiller)。
Release.Revision:此 release 的修订版本号。它从 1 开始,每 helm upgrade 一次增加一个。
Release.IsUpgrade:如果当前操作是升级或回滚,则将其设置为 true。
Release.IsInstall:如果当前操作是安装,则设置为 true。

2、value

Helm 模板提供的内置对象。四个内置对象之一是 Values。该对象提供对传入 chart 的值的访问。其内容来自四个来源:

  • chart 中的 values.yaml 文件
  • 如果这是一个子 chart,来自父 chart 的 values.yaml 文件
  • value 文件通过 helm install 或 helm upgrade 的 - f 标志传入文件(helm install -f myvals.yaml ./mychart)
  • 通过 –set(例如 helm install –set foo=bar ./mychart)

修改对应的yaml文件

# 编辑values.yaml
domain: anchnet.com

# 在模版中引用
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  domain: {{.Values.domain}}

注意我们在最后一行 {{ .Values.domain}} 获取 domain` 的值。还可以使用上面其他的几种方法来实现。

3、chart

Chart.yaml 文件的内容。任何数据 Chart.yaml 将在这里访问。例如 {{.Chart.Name}}-{{.Chart.Version}} 将打印出来 mychart-0.1.0。

二、模版函数和管道

1、模版函数

修改配置文件

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{quote .Values.favorite.drink}}
  food: {{quote .Values.favorite.food}}

模板函数遵循语法 functionName arg1 arg2…。在上面的代码片段中,quote .Values.favorite.drink 调用 quote 函数并将一个参数传递给它。

2、管道

修改配置文件

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{.Release.Name}}-configmap
data:
  myvalue: "Hello World"
  drink: {{.Values.favorite.drink | quote}}
  food: {{.Values.favorite.food | quote}}

没有调用 quote ARGUMENT,我们调换了顺序。我们使用管道(|)将 “参数” 发送给函数:.Values.favorite.drink | quote。

三、模版语法

1、使用 default 函数

drink: {{.Values.favorite.drink | default "tea" | quote}}

2、运算符函数

对于模板,运算符(eq,ne,lt,gt,and,or 等等)都是已实现的功能。在管道中,运算符可以用圆括号(( 和 ))分组。

3、流程控制

Helm 的模板语言提供了以下控制结构:

  • if/else 用于创建条件块
  • with 指定范围
  • range,它提供了一个 “for each” 风格的循环

除此之外,它还提供了一些声明和使用命名模板段的操作:

  • define 在模板中声明一个新的命名模板
  • template 导入一个命名模板
  • block 声明了一种特殊的可填写模板区域

总计

其实说到底模版就是go模版的语法和使用功能。

基础命令

http://hub.kubeapps.com/

completion  # 为指定的shell生成自动完成脚本(bash或zsh)
create      # 创建一个具有给定名称的新 chart
delete      # 从 Kubernetes 删除指定名称的 release
dependency  # 管理 chart 的依赖关系
fetch       # 从存储库下载 chart 并(可选)将其解压缩到本地目录中
get         # 下载一个命名 release
help        # 列出所有帮助信息
history     # 获取 release 历史
home        # 显示 HELM_HOME 的位置
init        # 在客户端和服务器上初始化Helm
inspect     # 检查 chart 详细信息
install     # 安装 chart 存档
lint        # 对 chart 进行语法检查
list        # releases 列表
package     # 将 chart 目录打包成 chart 档案
plugin      # 添加列表或删除 helm 插件
repo        # 添加列表删除更新和索引 chart 存储库
reset       # 从集群中卸载 Tiller
rollback    # 将版本回滚到以前的版本
search      # 在 chart 存储库中搜索关键字
serve       # 启动本地http网络服务器
status      # 显示指定 release 的状态
template    # 本地渲染模板
test        # 测试一个 release
upgrade     # 升级一个 release
verify      # 验证给定路径上的 chart 是否已签名且有效
version     # 打印客户端/服务器版本信息
dep         # 分析 Chart 并下载依赖

仓库

将制作好的charts包可以上传至helm仓库,可以放在自己的自建私有仓库,流入:kubeapps/Monocular/minior等,可以利用helm命令一键安装。

上传至公有云公共仓库,例如国内的阿里目前创建的Apphub等,在现今的云原生生态当中,已经有很多成熟的开源软件被制作成了 Helm Charts,使得用户可以非常方便地下载和使用,比如 Nginx,Apache、Elasticsearch、Redis 等等。不过,在开放云原生应用中心 App hub(Helm Charts 中国站) 发布之前,国内用户一直都很难直接下载使用这些 Charts。而现在,AppHub 不仅为国内用户实时同步了官方 Helm Hub 里的所有应用,还自动替换了这些 Charts 里所有不可访问的镜像 URL(比如 gcr.io, quay.io 等),终于使得国内开发者通过 helm install “一键安装”应用成为了可能。