和 Kubernetes 里面的容器一樣,Helm 也提供了 [Hook](https://v2.helm.sh/docs/developing_charts/#hooks) 的機制,允許 chart 開發人員在 release 的生命周期中的某些節點來進行干預,比如我們可以利用 Hooks 來做下面的這些事情:
- 在加載任何其他 chart 之前,在安裝過程中加載 `ConfigMap` 或 `Secret`
- 在安裝新 chart 之前執行作業以備份數據庫,然后在升級后執行第二個作業以恢復數據
- 在刪除 `release` 之前運行作業,以便在刪除 `release` 之前優雅地停止服務
值得注意的是 Hooks 和普通模板一樣工作,但是它們具有特殊的注釋,可以使 Helm 以不同的方式使用它們。
Hook 在資源清單中的 `metadata` 部分用 `annotations` 的方式進行聲明:
```bash
apiVersion: ...
kind: ....
metadata:
annotations:
"helm.sh/hook": "pre-install"
# ...
```
接下來我們就來和大家介紹下 `Helm Hooks` 的一些基本使用方法。
## 1. Hooks
在 Helm 中定義了如下一些可供我們使用的 Hooks:
- 預安裝`pre-install`:在模板渲染后,kubernetes 創建任何資源之前執行
- 安裝后`post-install`:在所有 kubernetes 資源安裝到集群后執行
- 預刪除`pre-delete`:在從 kubernetes 刪除任何資源之前執行刪除請求
- 刪除后`post-delete`:刪除所有 release 的資源后執行
- 升級前`pre-upgrade`:在模板渲染后,但在任何資源升級之前執行
- 升級后`post-upgrade`:在所有資源升級后執行
- 預回滾`pre-rollback`:在模板渲染后,在任何資源回滾之前執行
- 回滾后`post-rollback`:在修改所有資源后執行回滾請求
- `crd-install`:在運行其他檢查之前添加 `CRD` 資源,只能用于 `chart` 中其他的資源清單定義的 `CRD` 資源。
## 2. 生命周期
Hooks 允許開發人員在 release 的生命周期中的一些關鍵節點執行一些鉤子函數,我們正常安裝一個 chart 包的時候的生命周期如下所示:
- 用戶運行`helm install foo`
- chart 被加載到服務端 `Tiller Server` 中
- 經過一些驗證,`Tiller Server` 渲染 `foo` 模板
- Tiller 將產生的資源加載到 kubernetes 中去
- Tiller 將 release 名稱和其他數據返回給 Helm 客戶端
- Helm 客戶端退出
如果開發人員在 install 的生命周期中定義了兩個 hook:`pre-install`和`post-install`,那么我們安裝一個 chart 包的生命周期就會多一些步驟了:
- 用戶運行`helm install foo`
- `chart` 被加載到服務端 `Tiller Server` 中
- 經過一些驗證,Tiller Server 渲染 foo 模板
- Tiller 將 hook 資源加載到 `kubernetes` 中,準備執行`pre-install hook`
- Tiller 會根據權重對 hook 進行排序(默認分配權重0,權重相同的 hook 按升序排序)
- Tiller 然后加載最低權重的 hook
- Tiller 等待,直到 hook 準備就緒
- Tiller 將產生的資源加載到 kubernetes 中
- Tiller 執行post-install hook
- Tiller 等待,直到 hook 準備就緒
- Tiller 將 release 名稱和其他數據返回給客戶端
- Helm 客戶端退出
等待 hook 準備就緒,這是一個阻塞的操作,如果 hook 中聲明的是一個 Job 資源,那么 Tiller 將等待 Job 成功完成,如果失敗,則發布失敗,在這個期間,Helm 客戶端是處于暫停狀態的。
對于所有其他類型,只要 kubernetes 將資源標記為加載(添加或更新),資源就被視為就緒狀態,當一個 hook 聲明了很多資源是,這些資源是被串行執行的。
另外需要注意的是 hook 創建的資源不會作為 release 的一部分進行跟蹤和管理,一旦 `Tiller Server` 驗證了 hook 已經達到了就緒狀態,它就不會去管它了。
所以,如果我們在 hook 中創建了資源,那么不能依賴`helm delete`去刪除資源,因為 hook 創建的資源已經不受控制了,要銷毀這些資源,需要在`pre-delete`或者`post-delete`這兩個 hook 函數中去執行相關操作,或者將`helm.sh/hook-delete-policy`這個 `annotation` 添加到 `hook` 模板文件中。
## 3. 寫一個 hook
上面我們也說了 hook 和普通模板一樣,也可以使用普通的模板函數和常用的一些對象,比如`Values`、`Chart`、`Release`等等,唯一和普通模板不太一樣的地方就是在資源清單文件中的 `metadata` 部分會有一些特殊的注釋 `annotation`。
例如,現在我們來創建一個 hook,在前面的示例 templates 目錄中添加一個 `post-install-job.yaml` 的文件,表示安裝后執行的一個 hook:
```bash
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-post-install-job
lables:
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
annotations:
# 注意,如果沒有下面的這個注釋的話,當前的這個Job就會被當成release的一部分
"helm.sh/hook": post-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
name: {{ .Release.Name }}
labels:
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
spec:
restartPolicy: Never
containers:
- name: post-install-job
image: alpine
command: ["/bin/sleep", "{{ default "10" .Values.sleepTime }}"]
```
上面的 Job 資源中我們添加一個 `annotations`,要注意的是,如果我們沒有添加下面這行注釋的話,這個資源就會被當成是 `release` 的一部分資源:
```bash
annotations:
"helm.sh/hook": post-install
```
當然一個資源中我們也可以同時部署多個 hook,比如我們還可以添加一個`post-upgrade`的鉤子:
```bash
annotations:
"helm.sh/hook": post-install,post-upgrade
```
另外值得注意的是我們為 hook 定義了一個權重,這有助于建立一個確定性的執行順序,權重可以是正數也可以是負數,但是必須是字符串才行。
```bash
annotations:
"helm.sh/hook-weight": "-5"
```
最后還添加了一個刪除 `hook` 資源的策略:
```bash
annotations:
"helm.sh/hook-delete-policy": hook-succeeded
```
刪除資源的策略可供選擇的注釋值:
- `hook-succeeded`:表示 Tiller 在 hook 成功執行后刪除 hook 資源
- `hook-failed`:表示如果 hook 在執行期間失敗了,Tiller 應該刪除 hook 資源
- `before-hook-creation`:表示在刪除新的 hook 之前應該刪除以前的 hook
當 helm 的 release 更新時,有可能 hook 資源已經存在于群集中。默認情況下,helm 會嘗試創建資源,并拋出錯誤`"... already exists"`。
我們可以選擇 `"helm.sh/hook-delete-policy": "before-hook-creation"`,取代 `"helm.sh/hook-delete-policy": "hook-succeeded,hook-failed"` 因為:
例如為了手動調試,將錯誤的 hook 作業資源保存在 kubernetes 中是很方便的。 出于某種原因,可能有必要將成功的 hook 資源保留在 kubernetes 中。同時,在 helm release 升級之前進行手動資源刪除是不可取的。 `"helm.sh/hook-delete-policy": "before-hook-creation"` 在 hook 中的注釋,如果在新的 hook 啟動前有一個 hook 的話,會使 Tiller 將以前的release 中的 hook 刪除,而這個 hook 同時它可能正在被其他一個策略使用。