Package Manager - Helm
===
###### tags: `K8s` `Advanced Packaging Tools`
## 什麼是 Helm?
* offical address:
* Helm is a tool for managing Kubernetes charts. Charts are packages of pre-configured Kubernetes resources.
* Helm 就有點像是 Ubuntu 中的 APT 系統,也類似 Ansible 中的 Galaxy;簡單來說就是可以將一個 or 多個應用包裝成一個服務,並透過 chart 的形式發佈,讓大家可以方便在 k8s 上安裝特定的服務。
* Helm 有三個觀念需要我們去了解,分別為 Chart、Release 與 Repository,其細節如下:
* Chart:主要定義要被執行的應用程式中,所需要的工具、資源、服務等資訊,有點類似 Homebrew 的 Formula 或是 APT 的 dpkg 檔案。
* Release:一個被執行於 Kubernetes 的 Chart 實例。Chart 能夠在一個叢集中擁有多個 Release,例如 MySQL Chart,可以在叢集建立基於該 Chart 的兩個資料庫實例,其中每個 Release 都會有獨立的名稱。
* Repository:主要用來存放 Chart 的倉庫,如 KubeApps。
* 可以理解 Helm 主要目標就是從 Chart Repository 中,查找部署者需要的應用程式 Chart,然後以 Release 形式來部署到 Kubernetes 中進行管理。

## Helm 系統元件
### Helm 主要分為兩種元件,Helm Client 與 Tiller Server,兩者功能如下:
* Helm Client:一個安裝 Helm CLI 的機器,該機器透過 gRPC 連接 Tiller Server 來對 Repository、Chart 與 Release 等進行管理與操作,如建立、刪除與升級等操作,細節可以查看 Helm Documentation。
* Tiller Server:主要負責接收來至 Client 的指令,並透過 kube-apiserver 與 Kubernetes 叢集做溝通,根據 Chart 定義的內容,來產生與管理各種對應 API 物件的 Kubernetes 部署檔案(又稱為 Release)。

### Helm 要解決什麼問題?
#### 一個工具被開發出來,總是被賦予要解決問題的任務,而 Helm 到底可以協助我們解決什麼樣的問題呢?
* 先來討論目前的趨勢:
* 線上的服務開始往 micro service 的方向走
* k8s 似乎也變成整個 container 大一統的平台
* 因此使用 k8s 作為承載 micro service 的平台的確也是很正常的事情,但 k8s 為了解決各式各樣的問題,提供了大量的 resource object(pod, deployment, service … etc),因此如何利用這些 resource object 來組成一個完整的系統,整個過程是很複雜的,其中包含了很多不同面向的問題需要處理,例如:
* 如何管理、編輯 & 更新大量的 k8s resource object 設定檔?
* 如何快速佈署一個含有很多設定檔的 k8s application?
* 如何分享 & 重複利用 k8s resource object 設定檔?
* 如何透過 parameter & template 的方式,使用相同的設定檔來支援不同環境上的佈署?
* 如何管理 application 的 deployment, rollback, diff & history?
* Application 發佈後如何進行驗證?
* 在 micro service 的過程中,林林總總的問題也跑了出來,而 Helm 就是為了解決上述問題而被開發出來的工具,它使用了以下幾個方式來解決上述的問題:
* 將 k8s resource object 打包到一個 chart 中 (chart 是多個 k8s resource object configuration 的集合)
* 將 chart 保存在 chart repository 中,透過 repository 的方式來儲存 & 分享 chart
* Helm 則利用 chart 來進行 deployment, upgrade, rollback, version control …. 等工作
## 安裝
### 事前準備
* 安裝前需要確認環境滿足以下幾膽:
* 已部署 Kubernetes 叢集。
* 操作端安裝 kubectl 工具。
* 操作端可以透過 kubectl 工具管理到 Kubernetes(可用的 kubectl config)。
### 安裝Helm
* Helm 有許多種安裝方式,用 binary 檔案來進行安裝:
```shell=
$ wget -qO- https://kubernetes-helm.storage.googleapis.com/helm-v2.8.1-linux-amd64.tar.gz | tar -zx
$ sudo mv linux-amd64/helm /usr/local/bin/
$ helm version
```
### 初始化 Helm
* 在開始使用 Helm 之前,我們需要建置 Tiller Server 來對 Kubernetes 的管理,而 Helm CLI 內建也提供了快速初始化指令,如下:
```shell=
$ kubectl -n kube-system create sa tiller
$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
$ helm init --service-account tiller --upgrade
```

* 完成後,就可以透過 kubectl 來查看 Tiller Server 是否被建立:
```shell=
$ kubectl get po,svc -n kube-system -l app=helm
NAME READY STATUS RESTARTS AGE
po/tiller-deploy-1651596238-5lsdw 1/1 Running 0 3m
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/tiller-deploy 192.162.204.144 <none> 44134/TCP 3m
```
* 接著透過 helm ctl 來查看資訊:
```shell=
$ export KUBECONFIG=/etc/kubernetes/admin.conf
$ export HELM_HOST=$(kubectl describe svc/tiller-deploy -n kube-system | awk '/Endpoints/{print $2}')
# wait for a few minutes
$ helm version
Client: &version.Version{SemVer:"v2.8.1", GitCommit:"6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.8.1", GitCommit:"6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2", GitTreeState:"clean"}
```
### 部署 Chart Release 實例
* 當完成初始化後,就可以透過 helm ctl 來管理與部署 Chart Release,我們可以到 KubeApps 查找想要部署的 Chart,如以下快速部屬 Jenkins 範例,首先先透過搜尋來查看目前應用程式版本:
```shell=
$ helm search jenkins
NAME VERSION DESCRIPTION
stable/jenkins 0.6.3 Open source continuous integration server. It s...
```
* 接著透過inspect指令查看該 Chart 的參數資訊:
```shell=
$ helm inspect stable/jenkins
...
Persistence:
Enabled: true
```
* 這邊需要建立一個PVC給Jenkins Chart來做儲存使用,手動建立Jenkins-pv-pvc.yaml
```shell=
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-pv
labels:
app: jenkins
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /var/nfs/jenkins
server: 172.20.3.91
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
labels:
app: jenkins
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
```
* 接著透過 kubectl 來建立:
```shell=
$ kubectl create -f jenkins-pv-pvc.yml
persistentvolumeclaim "jenkins-pvc" created
persistentvolume "jenkins-pv" created
$ kubectl get pv,pvc
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pv/jenkins-pv 10Gi RWO Recycle Bound default/jenkins-pvc 20s
NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE
pvc/jenkins-pvc Bound jenkins-pv 10Gi RWO 20s
```
* 當 PVC 建立完成後,就可以開始透過 Helm 來建立 Jenkins Release:
```shell=
$ export PVC_NAME=$(kubectl get pvc -l app=jenkins --output=template --template="{{with index .items 0}}{{.metadata.name}}{{end}}")
$ helm install --name demo --set Persistence.ExistingClaim=${PVC_NAME} stable/jenkins
NAME: demo
LAST DEPLOYED: Thu May 25 17:53:50 2017
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
demo-jenkins 1 1 1 0 1s
==> v1/Secrets:wq
NAME TYPE DATA AGE
demo-jenkins Opaque 2 1s
==> v1/ConfigMap
NAME DATA AGE
demo-jenkins-tests 1 1s
demo-jenkins 3 1s
==> v1/Service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
d;1
mo-jenkins 192.169.143.140 <pending> 8080:30152/TCP,50000:3180goo6
```
### 更新
```shell=
helm upgrade -f myvalues.yaml -f override.yaml redis ./redis
```
```shell=
ip route del default via 10.1.1.254 dev ens4
ip route add default via 10.100.37.92 dev ens4
helm repo add common https://kubernetes-charts-incubator.storage.googleapis.com/
```