在 Kubernetes 中使用 Helm Hooks 遷移數(shù)據(jù)庫(kù)
如果你應(yīng)用程序中使用的是關(guān)系型數(shù)據(jù)庫(kù),隨著時(shí)間的推移你的數(shù)據(jù)庫(kù)結(jié)構(gòu)必然或多或少會(huì)有一些變化。在部署你新版本的應(yīng)用之前,必須確保數(shù)據(jù)庫(kù)的結(jié)構(gòu)是最新的,本文不是關(guān)于如何生成和管理 schema 遷移的,而是如何將其作為 Kubernetes 上應(yīng)用部署過(guò)程的一部分來(lái)完成遷移。
在應(yīng)用中執(zhí)行遷移
我們可以將自動(dòng)遷移程序作為服務(wù)啟動(dòng)的一部分而存在,這看上去是可行的,可以保證服務(wù)不會(huì)在遷移之前就啟動(dòng),并消除了在過(guò)時(shí)的 schema 結(jié)構(gòu)上運(yùn)行應(yīng)用的風(fēng)險(xiǎn)。
但是當(dāng)我們將應(yīng)用程序跑在 Kubernetes 集群上的時(shí)候,這會(huì)帶來(lái)一些其他問(wèn)題。
每次創(chuàng)建或重啟新的 Pod 時(shí)候,它都會(huì)嘗試再次執(zhí)行遷移操作,如果你的遷移腳本寫得足夠優(yōu)秀,是可以規(guī)避這些問(wèn)題,但是這并不是一個(gè)很好的設(shè)計(jì)。 如果遷移需要一段比較長(zhǎng)的時(shí)間(比如在一個(gè)大表上添加一列),你的 Pod 可能會(huì)錯(cuò)過(guò)就緒狀態(tài)的檢查,在遷移完成之前會(huì)殺掉容器重啟。我們當(dāng)然可以增加 Pod 的初始延遲時(shí)間,但是這個(gè)時(shí)間是沒(méi)辦法控制的,而且也會(huì)導(dǎo)致正常運(yùn)行的時(shí)候等待大量的時(shí)間。
使用 init 容器
Init 容器[1]是指在你的 Pod 中的常規(guī)容器啟動(dòng)之前將運(yùn)行完成的容器。這對(duì)于在你的應(yīng)用程序啟動(dòng)之前執(zhí)行任何需要的設(shè)置都是非常有用的(例如下載一些配置文件)。使用 init 容器來(lái)運(yùn)行數(shù)據(jù)庫(kù)遷移似乎是一個(gè)更好的方式,但我們將面臨與在應(yīng)用程序中啟動(dòng)的方式相同的問(wèn)題。
如果同時(shí)創(chuàng)建多個(gè) Pods,則可能會(huì)同時(shí)運(yùn)行多個(gè) init 容器。 每次創(chuàng)建新的 Pod 時(shí),init 容器都會(huì)運(yùn)行。
使用 Helm Hooks 執(zhí)行任務(wù)
Kubernetes jobs
首先,我們來(lái)看看 Kubernetes 中的 job 資源對(duì)象。Jobs 允許我們運(yùn)行1個(gè)或多個(gè) Pod 來(lái)完成任務(wù)。和 Deployment 中的 Pod 不同,Job 中的 Pod 在退出時(shí)不會(huì)重新創(chuàng)建(除非它們失敗,并且 Job 被配置為在失敗時(shí)重新啟動(dòng))。
這對(duì)于運(yùn)行一個(gè)只需要運(yùn)行一次就能完成的任務(wù)來(lái)說(shuō)是非常有用的,而運(yùn)行數(shù)據(jù)庫(kù)遷移顯然就是一個(gè)一次性的任務(wù)。
現(xiàn)在要做的是在部署應(yīng)用程序的新版本之前自動(dòng)運(yùn)行一個(gè) Job 來(lái)執(zhí)行遷移任務(wù)。
Helm release 生命周期
Helm[2] 允許你將你的應(yīng)用程序定義的所有 K8S 資源清單打包在一個(gè)Chart 中一次性部署,并使用模板來(lái)定制每個(gè)部署(例如允許在多個(gè)環(huán)境中用不同的參數(shù)部署同一個(gè) Chart)。
Helm 還提供了 Hooks[3] 鉤子來(lái)決定部署過(guò)程中何時(shí)創(chuàng)建資源,我們可以利用這一點(diǎn),在創(chuàng)建或更新任何資源之前執(zhí)行遷移任務(wù)。下面是我們的遷移任務(wù)的示例:
apiVersion:?batch/v1
kind:?Job
metadata:
??name:?"{{?.Release.Name?}}"
??labels:
??annotations:
????"helm.sh/hook":?pre-install,pre-upgrade
????"helm.sh/hook-weight":?"-1"
????"helm.sh/hook-delete-policy":?hook-succeeded
spec:
??template:
????metadata:
??????name:?"{{?.Release.Name?}}"
????spec:
??????restartPolicy:?Never
??????containers:
??????-?name:?db-migrations??#?我們需要構(gòu)建一個(gè)專門用于數(shù)據(jù)庫(kù)遷移的docker鏡像
????????image:?"database-migrations"
Hooks 是通過(guò) annotations 來(lái)進(jìn)行配置的:
"helm.sh/hook":pre-install,pre-upgrade是告訴 helm 在安裝之前和升級(jí)應(yīng)用程序之前執(zhí)行這個(gè) Job 任務(wù)"helm.sh/hook-weight": "-1"是用于定義 helm 應(yīng)該以何種順序創(chuàng)建實(shí)現(xiàn)相同鉤子的資源helm.sh/hook-delete-policy: hook-succeeded是告訴 helm 在 Job 執(zhí)行成功后刪除該 Job 資源對(duì)象。
其余部分就是一個(gè)普通的 Job 資源對(duì)象模板。
問(wèn)題
Helm hooks 的缺點(diǎn)
你的遷移任務(wù)很可能需要一些配置和或 Secret 才能運(yùn)行(至少需要db 服務(wù)器地址和憑證),但是這個(gè) Job 資源將在 Chart 中所有其他資源之前創(chuàng)建。這意味著我們的 Job 將無(wú)法掛載 Chart 創(chuàng)建的ConfigMap 資源。要解決這個(gè)問(wèn)題我們可以創(chuàng)建一個(gè)實(shí)現(xiàn)相同鉤子的 ConfigMap,如下所示:
apiVersion:?v1
kind:?ConfigMap
metadata:
??name:?db-migrations
annotations:
????"helm.sh/hook":?pre-install,pre-upgrade
????"helm.sh/hook-weight":?"-10"?#?使用一個(gè)比遷移任務(wù)更小的權(quán)重?
????"helm.sh/hook-delete-policy":?hook-succeeded
data:
??DB_ADDR:?{{?.Values.db.addr?}}
??DB_NAME:?{{?.Values.db.name?}}
我們可以配置這個(gè) hook 的權(quán)重比遷移任務(wù)的權(quán)重更小,這樣就可以在遷移任務(wù)執(zhí)行之前創(chuàng)建這個(gè) ConfigMap 資源,這樣就可以在 Job 中掛載這個(gè) ConfigMap 來(lái)獲取配置信息了。
部署策略和回滾
默認(rèn)情況下,Kubernetes Deployment 默認(rèn)更新策略是滾動(dòng)更新。這意味著在部署過(guò)程中,將有 Pod 同時(shí)運(yùn)行應(yīng)用程序的上一個(gè)和新版本。這將要求所有的遷移至少要向后兼容以前的版本。
如果你需要使用 helm rollback 命令回滾到應(yīng)用程序的以前版本,你重新部署的版本的遷移任務(wù)也會(huì)再次運(yùn)行。在回滾期間試圖向下遷移到以前版本的數(shù)據(jù)庫(kù)結(jié)構(gòu),很可能會(huì)導(dǎo)致現(xiàn)有的 Pods 運(yùn)行失敗。最后,如果你必須回滾到一個(gè)更老的版本,你需要確保當(dāng)前的數(shù)據(jù)庫(kù)結(jié)構(gòu)與你計(jì)劃回滾到的版本向后兼容。
原文鏈接:https://itnext.io/database-migrations-on-kubernetes-using-helm-hooks-fb80c0d97805
參考資料
Init 容器: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
[2]Helm: https://helm.sh/
[3]Hooks: https://helm.sh/docs/topics/charts_hooks/
K8S進(jìn)階訓(xùn)練營(yíng),點(diǎn)擊下方圖片了解詳情

