Intro
Helm是一款非常流行的k8s包管理工具。以前就一直想用它,但看到它產(chǎn)生的文件比k8s要復(fù)雜許多秸苗,就一直猶豫,不知道它的好處能不能抵消掉它的復(fù)雜度运褪。但如果不用惊楼,而是用Kubectl來進(jìn)行調(diào)式真的很麻煩。正好最近Helm3正式版出來了秸讹,比原來的Helm2 簡單了不少檀咙,就決定還是試用一下。結(jié)果證明確實(shí)很復(fù)雜璃诀,它的好處和壞處大致相當(dāng)弧可。有了它確實(shí)能大大簡化對k8s的調(diào)式,但也需要花費(fèi)比較多的時(shí)間來學(xué)習(xí)劣欢,而且產(chǎn)生的配置文件要復(fù)雜許多棕诵。但是事實(shí)是現(xiàn)在沒有什么很方便的幫助調(diào)式k8s的工具,在沒有更好的方案之前氧秘,我還是建議用它年鸳,只是前期需要花些功夫?qū)W習(xí)和掌握它。
Helm3和Helm2的語法差不太多丸相,只是使用起來更方便搔确,不用安裝Tiller。一個(gè)比較明顯的變化是不再需要“requirements.yaml”, 依賴關(guān)系是直接在“chart.yaml”中定義灭忠。有關(guān)Helm3和Helm2的區(qū)別膳算,詳情請參見CHANGES SINCE HELM 2。
網(wǎng)上有不少講述Helm的文章弛作,但大部分都是主要講解安裝和舉一個(gè)簡單的例子涕蜂。但Helm使用起來還是比較復(fù)雜的,一定要有一個(gè)復(fù)雜的例子才能把它的功能講清楚映琳,里面有不少設(shè)計(jì)方面的問題需要思考机隙。我剛開始接觸的時(shí)候就覺得頭緒繁多蜘拉,不知從哪下手。本文就通過一個(gè)相對復(fù)雜的例子來講解用Helm3來設(shè)計(jì)配置文件的思路有鹿,使上手更容易旭旭。
這里不講Helm3的安裝,它比較很容易葱跋。也不講解Helm的基本語法持寄,你可以自己去看其他文檔。即使你不懂Helm娱俺,應(yīng)該也能猜出七八成稍味,剩下的就要讀文檔了(Charts)。Helm的語法還是比較復(fù)雜的荠卷,要想搞懂可能要花一兩天時(shí)間模庐。
本文假設(shè)你對helm有一個(gè)大概的了解,想要構(gòu)建一個(gè)復(fù)雜的微服務(wù)顶吮,但有不知如何下手悴了;或者你想了解一下構(gòu)建Helm的最佳實(shí)踐湃交,那就請你繼續(xù)讀下去。
Helm文件結(jié)構(gòu)
chart里一個(gè)很重要的概念就是模板(template),它就是Go語言模板才沧,它是里面加入了編程邏輯的k8s文件。這些模板文件在使用時(shí)都要先進(jìn)行模板解析孩革,把其中的程序邏輯轉(zhuǎn)化成對應(yīng)的編碼锅移,最終生成k8s配置文件非剃。
以上就是Helm自動(dòng)生成的chart目錄結(jié)構(gòu),在Helm里每個(gè)項(xiàng)目叫一個(gè)chart,它由下面幾個(gè)組成部分:
- "Chart.yaml":存有這個(gè)chart的基本信息压怠,
- "values.yaml":定義模板中要用到的常量菌瘫。
- “template”目錄:里面存有全部的模板文件,其中最重要的是“deployment.yaml”和“service.yaml”栖忠,分別是部署和服務(wù)文件. "helpers.tpl"用來定義變量,"ingress.yaml"和"serviceaccount.yaml"分別是對外接口和服務(wù)賬戶捐川,這里暫時(shí)沒用古沥, “NOTES.txt”是注釋文件。
- “charts”目錄: 存有這個(gè)chart依賴的所有子chart纯衍。
Helm的基本元素
Helm有四個(gè)基本元素瓦堵,值菇用,常量惋鸥,變量和共享常量(這個(gè)后面會(huì)講)
值(literal)
Helm在k8s的基礎(chǔ)之上增加了模板功能,使k8s的配置文件更加靈活滤港。里面的主要概念就是模板(Template),也就是在k8s的配置文件里增加了常量和變量以及編程邏輯添履。如果你不用這些新增功能,那么就是普通的YAML文件(k8s配置文件)叔壤,里面用到的基本元素就是值。
常量
節(jié)點(diǎn)定位(Node Anchor):
如果你想復(fù)用重復(fù)的值俺亮,能把它定義成常量嗎?YAML有一個(gè)功能叫節(jié)點(diǎn)定位(Node Anchor)本讥,類似于定義一個(gè)常量,然后引用秧了。但它有一些限制,定義的必須是一個(gè)節(jié)點(diǎn)米罚,因此不如真正的常量靈活。
例如如下文件中,用“&”定義了一個(gè)常量“&k8sdemoDatabaseService”讼渊,然后用“*k8sdemoDatabaseService”引用它。
global:
k8sdemoDatabaseService: &k8sdemoDatabaseService k8sdemo-database-service
mysqlHost: *k8sdemoDatabaseService
這時(shí),“k8sdemoDatabaseService:”是YAML文件節(jié)點(diǎn)的鍵名奶甘,“&k8sdemoDatabaseService”是節(jié)點(diǎn)定位的名字,相當(dāng)于常量名方淤,“k8sdemo-database-service”是YAML節(jié)點(diǎn)的鍵值钉赁。在上述代碼中,k8sdemoDatabaseService和mysqlHost的值都是“k8sdemo-database-service”携茂。
有關(guān)節(jié)點(diǎn)定位(Node Anchor)的詳細(xì)內(nèi)容你踩,請參見 YAML。
常量:
由于節(jié)點(diǎn)定位的局限性,Helm引入了真正的常量姓蜂,也就是在"values.yaml"里定義的內(nèi)容按厘,它可以定義是任何東西懒棉,不只限于節(jié)點(diǎn)妻导。
在"values.yaml"里定義常量:
replicaCount: 1
在部署模板里引用:
replicas: {{ .Values.replicaCount }}
那么什么時(shí)候用常量醇疼,什么時(shí)候用值(Literal)呢贵试?如果一個(gè)值在模板中出現(xiàn)多次梧疲,就要定義常量,避免重復(fù)喂分。例如“accessModes”,既要在存儲(chǔ)卷里出現(xiàn),又要在存儲(chǔ)卷申請里出現(xiàn)基括。另外如果值有可能變化(不論是隨部署環(huán)境變化魔眨,還是隨時(shí)間變化)当叭,那么就定義成常量,這樣在修改時(shí)就只用改"values.yaml"损肛,而不必修改模板文件笆焰。例如“replicas”的值(也就是集群的個(gè)數(shù))是可能變化的,就要定義成常量不皆。在模板里可以引用常量的,但在"values.yaml"里不行则果,因?yàn)樗皇瞧胀╕AML文件饰及,沒有模板解析功能,因此不支持常量渠牲,這里就只能用節(jié)點(diǎn)定位(來代替常量)旋炒。
有關(guān)Helm常量的詳細(xì)內(nèi)容步悠,請參見 Use placeholders in yaml和Use YAML with variables签杈。
變量
節(jié)點(diǎn)定位的功能是有限的,例如你想利用已有的節(jié)點(diǎn)定位鼎兽,對它進(jìn)行轉(zhuǎn)換答姥,定義一個(gè)新的節(jié)點(diǎn)定位,這在"values.yaml"里就不行了谚咬。
例如你已有節(jié)點(diǎn)定位“name”鹦付,你想在這個(gè)基礎(chǔ)上定義一個(gè)新的節(jié)點(diǎn)定位“serviceName”,這個(gè)"values.yaml"就不支持了择卦,你必須要用模板敲长。
如下所示郎嫁,這在"values.yaml"里是不支持的。
name: &name k8sdemo-backend
serviceName:*name-service
這就引出了變量的概念祈噪,但它只能在模板里才行泽铛。 換句話說,模板既支持常量辑鲤,也支持變量盔腔。但如果把變量的定義邏輯放在Helm每個(gè)模板里,就顯得很亂月褥。因此一般的做法是把這些邏輯放在一個(gè)單獨(dú)的模板文件里弛随,這個(gè)就是前面講到的"_helpers.tpl"文件。當(dāng)你需要對常量進(jìn)行轉(zhuǎn)換宁赤,生成新的常量舀透,你就在定義變量,這部分代碼就放在"_helpers.tpl"里礁击。
下面就是"_helpers.tpl"中定義"k8sdemo.name"的代碼盐杂。
{{- define "k8sdemo.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
在以上這些元素中,常量(也就是在"values.yaml"中定義的)是最靈活的哆窿,能用它時(shí)盡量用它链烈。而且因?yàn)樗嵌x在普通YAML文件中("values.yaml"),應(yīng)用程序可以直接訪問它挚躯,這樣可以實(shí)現(xiàn)應(yīng)用程序和k8s之間的數(shù)據(jù)共享强衡。但如果你需要對常量進(jìn)行編程轉(zhuǎn)換,那就沒辦法了码荔,只能定義變量漩勤,把它放在"_helpers.tpl"中。
ConfigMap和Secret
在k8s中ConfigMap和Secret是用來存儲(chǔ)共享配置參數(shù)和保密參數(shù)的缩搅,但在Helm中越败,由于有了上面講的Helm基本元素,它們完全可以代替ConfigMap的功能硼瓣,因此ConfigMap就不需要了究飞,但Secret還是需要的,因?yàn)橐鎯?chǔ)加密信息堂鲤。下面會(huì)講解亿傅。
有關(guān)ConfigMap的設(shè)計(jì)局限性,請參見把應(yīng)用程序遷移到k8s需要修改什么瘟栖?
Chart設(shè)計(jì)
現(xiàn)在我們就用一個(gè)具體的例子來展示Helm的chart設(shè)計(jì)葵擎。這個(gè)例子是一個(gè)微服務(wù)應(yīng)用程序,它共有三層: 前端半哟,后端和數(shù)據(jù)庫酬滤,只有這樣才能讓Helm的一些設(shè)計(jì)問題付出水面签餐,如果只有一層的話,就太簡單了盯串,沒有參考價(jià)值贱田。
在k8s中,每一層就是一個(gè)單獨(dú)的服務(wù)嘴脾,它里面有各種配置文件男摧。Helm的優(yōu)勢是把這些不同的服務(wù)組成一個(gè)Chart來共同管理和調(diào)式,方便了許多译打。
上面就是最終的chart目錄結(jié)構(gòu)圖耗拓。“chart”是總目錄奏司,里面有三個(gè)子目錄“k8sdemo”乔询,“k8sdemo-backend”,“k8sdemo-database”, 每一個(gè)對應(yīng)一個(gè)服務(wù)韵洋,每個(gè)服務(wù)都是一個(gè)獨(dú)立的chart竿刁,能單獨(dú)調(diào)式部署,chart之間也可以有依賴關(guān)系搪缨。其中“k8sdemo”是父chart食拜,同時(shí)也是前端服務(wù),它的“charts”目錄里有它依賴的另外兩個(gè)服務(wù)副编「旱椋“k8sdemo-backend”是后端服務(wù),“k8sdemo-database”是數(shù)據(jù)庫服務(wù)痹届。
處理Chart的依賴關(guān)系有兩種方式:
- 嵌入式:就是直接把依賴的chart放在“charts”子目錄里呻待,這樣子chart是父chart的一部分。它是一種緊耦合的關(guān)系队腐,好處是比較簡單蚕捉,但不夠靈活。
- 依賴導(dǎo)入式:就是各個(gè)chart是并列關(guān)系柴淘,各自單獨(dú)調(diào)試部署迫淹,互相獨(dú)立,需要合并時(shí)再把子chart導(dǎo)入父chart里悠就,它是一種松耦合的關(guān)系千绪,好處是比較靈活充易,但設(shè)計(jì)更復(fù)雜梗脾。 在這種結(jié)構(gòu)下,各個(gè)chart可以單獨(dú)工作也可以聯(lián)合工作盹靴,不過你需要更好的設(shè)計(jì)炸茧。
這里采用的是依賴導(dǎo)入式方式瑞妇,主要原因是我原來認(rèn)為嵌入式需要一起調(diào)試,復(fù)雜度太高梭冠,如果你覺得這不是問題辕狰,這也是個(gè)不錯(cuò)的辦法。用依賴導(dǎo)入式方式控漠,可以單獨(dú)調(diào)試各個(gè)chart蔓倍,簡單了很多。后來發(fā)現(xiàn)其實(shí)采用嵌入式也可以單獨(dú)調(diào)試子chart盐捷,只是父chart不能單獨(dú)調(diào)試而已偶翅。
調(diào)試順序:
當(dāng)你采用依賴導(dǎo)入式方式時(shí),調(diào)試順序關(guān)系不大碉渡,因?yàn)楦鱾€(gè)chart是各自獨(dú)立的聚谁,可以單獨(dú)調(diào)試。舉個(gè)例子滞诺,雖然“k8sdemo-backend”需要“k8sdemo-database”才能正常運(yùn)行形导,但當(dāng)沒有數(shù)據(jù)庫服務(wù)時(shí),你的程序也可以運(yùn)行习霹,只不過輸出的是錯(cuò)誤信息朵耕,但這并不影響你調(diào)試chart。
我先調(diào)試“k8sdemo”淋叶,它雖然依賴另外兩個(gè)chart憔披,但沒有它們也能單獨(dú)工作。然后再調(diào)試“k8sdemo-backend”和“k8sdemo-database”爸吮,最后再把它們導(dǎo)入到“k8sdemo”中去再進(jìn)行聯(lián)調(diào)芬膝。
調(diào)式“k8sdemo”
它的調(diào)試是最容易的,由于它里面沒有真正的前端代碼形娇,只要把Nginx調(diào)試成功了就可以了锰霜。只要在生成的文件基礎(chǔ)上做些修改就行了。
鍵入如下命令創(chuàng)建chart桐早,其中“k8sdemo”是chart的名字癣缅,這個(gè)名字很重要,服務(wù)的名字和label都是由它產(chǎn)生的哄酝。
helm create k8sdemo
這之后友存,系統(tǒng)會(huì)自動(dòng)創(chuàng)建前面講到的chart目錄結(jié)構(gòu)。讓后就是對已經(jīng)生成的文件進(jìn)行修改陶衅。
修改"values.yaml":
以下是"values.yaml"主要修改的地方
image:
repository: nginx:1.17.6
pullPolicy: Never
imagePullSecrets: []
nameOverride: "k8sdemo"
fullnameOverride: "k8sdemo"
service:
type: NodePort
port: 80
nodePort: 31080
另外屡立,由于"ingress.yaml"和"serviceaccount.yaml"暫時(shí)沒用,就把它們都設(shè)成了“false”
ingress:
enabled: false
serviceAccount:
# Specifies whether a service account should be created
create: false
修改"service.yaml":
apiVersion: v1
kind: Service
metadata:
name: {{ include "k8sdemo.fullname" . }}
labels:
{{- include "k8sdemo.labels" . | nindent 4 }}
spec:
type: {{.Values.service.type}}
ports:
- port: {{.Values.service.port}}
nodePort: {{.Values.service.nodePort}}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "k8sdemo.selectorLabels" . | nindent 4 }}
修改"deployment.yaml":
搀军。膨俐。勇皇。
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ .Values.image.repository }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
。焚刺。敛摘。
以上都是簡單的修改,不涉及到設(shè)計(jì)問題乳愉。由于篇幅的關(guān)系兄淫,這里沒有列出全部源碼,如果有興趣請?jiān)诒疚哪┪舱业皆创a地址蔓姚。
共享常量
在進(jìn)行下面的調(diào)試之前拖叙,先要講一個(gè)重要概念。 前面介紹Helm的基本元素時(shí)講的都是在一個(gè)chart里共享值赂乐,如果要在不同chart之間共享值(例如k8s服務(wù)名薯鳍,數(shù)據(jù)庫用戶名和端口),那么這些還不夠挨措,你需要共享常量. 通常情況下子chart和父chart之間的常量是不能共享的挖滤,如果需要共享,需要有一種特殊的方法來定義常量浅役,這就是共享常量斩松。它必須是定義在父chart中。
共享常量
例如觉既,你在“k8sdemo”的“values.yaml”加入下面代碼惧盹,注意節(jié)點(diǎn)的名字必須是子chart名(例如“k8sdemo-backend”)
k8sdemo-backend:
replicaCount: 2
k8sdemo-database:
replicaCount: 2
在“k8sdemo”的模板里就可以通過“{{ .Values.k8sdemo-backend.replicaCount }}” 來訪問。當(dāng)Helm發(fā)現(xiàn)節(jié)點(diǎn)名是子chart名時(shí)瞪讼,它會(huì)自動(dòng)拷貝這個(gè)常量到子chart的“values.yaml”中钧椰,因此,在“k8sdemo-backend”中符欠,你也可以通過“{{ .Values.replicaCount }}” 來訪問這個(gè)常量嫡霞。注意這里并沒有包含子chart名(“k8sdemo-backend”),而是只有常量名希柿,因?yàn)樽觕hart名只是一個(gè)標(biāo)識诊沪,而不是常量名的一部分。
全局常量
共享常量只能把常量共享給一個(gè)字chart曾撤,如果你需要多個(gè)子chart之間共享端姚,就需要?jiǎng)?chuàng)建全局常量,它用“global”來標(biāo)識挤悉,下面是示例渐裸。
在“k8sdemo-backend”的"values.yaml"中定義:
global:
k8sdemoDatabaseService: &k8sdemoDatabaseService k8sdemo-database-service
mysqlUserName: dbuser
mysqlUserPassword: dbuser
mysqlPort: 3306
mysqlHost: *k8sdemoDatabaseService
mysqlDatabase: service_config
在“k8sdemo-backend”的“deployment.yaml”中引用。
env:
- name: MYSQL_USER_NAME
value: {{ .Values.global.mysqlUserName }}
- name: MYSQL_USER_PASSWORD
value: {{ .Values.global.mysqlUserPassword }}
- name: MYSQL_HOST
value: {{ .Values.global.mysqlHost }}
- name: MYSQL_PORT
value: "{{ .Values.global.mysqlPort }}"
- name: MYSQL_DATABASE
value: {{ .Values.global.mysqlDatabase }}
在“k8sdemo-database”的"values.yaml"中定義:
global:
k8sdemoDatabaseService: k8sdemo-database-service
mysqlUserName: dbuser
mysqlUserPassword: dbuser
mysqlRootPassword: root
mysqlDatabase: service_config
在“k8sdemo-database”的“deployment.yaml”中引用。
env:
- name: MYSQL_ROOT_PASSWORD
value: {{ .Values.global.mysqlRootPassword }}
- name: MYSQL_USER_NAME
value: {{ .Values.global.mysqlUserName }}
- name: MYSQL_USER_PASSWORD
value: {{ .Values.global.mysqlUserPassword }}
- name: MYSQL_DATABASE
value: {{ .Values.global.mysqlDatabase }}
當(dāng)把“k8sdemo-backend”和“k8sdemo-database”導(dǎo)入"k8sdemo"后進(jìn)行聯(lián)調(diào)時(shí), 就要把上面提到的全局常量寫入"k8sdemo"的"values.yaml"文件中橄仆,這樣就能讓各個(gè)子chart共享這些常量。如下所示:
global:
k8sdemoBackendService: k8sdemo-backend-service
k8sdemoDatabaseService: &k8sdemoDatabaseService k8sdemo-database-service
mysqlUserName: dbuser
mysqlUserPassword: dbuser
mysqlRootPassword: root
mysqlPort: 3306
mysqlHost: *k8sdemoDatabaseService
mysqlDatabase: service_config
如果父chart和子chart有重復(fù)的全局常量衅斩,這時(shí)父chart("k8sdemo")的全局常量值就會(huì)覆蓋子chart的全局常量盆顾。
它的使用原則就是如果只是子chart獨(dú)有的常量就在子chart的"values.yaml"中定義,如果是共享的常量就在父chart中定義畏梆。但如果采用的是依賴導(dǎo)入方式您宪,由于子chart也要單獨(dú)調(diào)試,這時(shí)你在子chart里也要定義這些全局常量奠涌。這樣在進(jìn)行chart總調(diào)試時(shí)宪巨,就會(huì)使用父chart的中的值。
詳情請參見 Subcharts and Global Values溜畅。
調(diào)試“k8sdemo-backend”
“k8sdemo-backend”的chart需要饶笞俊(與“k8ssdemo”)不同的名字,
創(chuàng)建:
helm create k8sdemo-backend
上面就是“k8sdemo-backend”的目錄圖慈格。由于它需要建持久卷怠晴,因此這里增加了兩個(gè)文件“persistentvolume.yaml”和“persistentvolumeclaim.yaml” ( 不是自動(dòng)生成的)。
值得一提的是k8s對象的命名浴捆。一般情況下蒜田,如果不需要對其進(jìn)行引用,用chart的全名就行了选泻。例如部署的名稱冲粤,如下所示。
name: {{ include "k8sdemo.fullname" . }}
如果是服務(wù)名(Service Name)页眯,它需要在應(yīng)用程序和k8s之間共享梯捕,也需要在父chart和子chart之間共享,這時(shí)最好單獨(dú)定義一個(gè)全局共享常量窝撵。
在“values.yaml”中定義:
global:
k8sdemoBackendService: k8sdemo-backend-service
在“service.yaml”中引用:
name: {{.Values.global.k8sdemoBackendService}}
調(diào)試“k8sdemo-database”
它的調(diào)試方式與“k8sdemo-backend”大同小異科阎,就不詳細(xì)講解了。
聯(lián)合調(diào)試:
上面各個(gè)chart都單獨(dú)調(diào)試成功之后忿族,就要把它們合在一起進(jìn)行聯(lián)合調(diào)試锣笨。
在“k8sdemo”(父chart)中加入依賴關(guān)系(Chart.yaml)。
dependencies:
- name: k8sdemo-backend
repository: file://../k8sdemo-backend
version: 0.1.0
- name: k8sdemo-database
repository: file://../k8sdemo-database
version: 0.1.0
這里為了簡單起見道批,沒有用到chart庫(Chart Repository)错英,使用了本地目錄。這里的“file://”是針對chart的根的相對路徑隆豹,“file://..”就是“k8sdemo”的上級目錄椭岩。
詳情請參見How to refer to a helm chart in the same repository。
修改全局常量("values.yaml"):
global:
k8sdemoBackendService: k8sdemo-backend-service
k8sdemoDatabaseService: &k8sdemoDatabaseService k8sdemo-database-service
mysqlUserName: dbuser
mysqlUserPassword: dbuser
mysqlRootPassword: root
mysqlPort: 3306
mysqlHost: *k8sdemoDatabaseService
mysqlDatabase: service_config
只有需要在chart之間共享的常量才需要在父chart里的"values.yaml"定義,其余的在各自子chart里的"values.yaml"定義就可以了判哥。
鍵入如下命令“helm dependency update k8sdemo”献雅,更新依賴關(guān)系
~ # vagrant@ubuntu-xenial:~/jfeng45/k8sdemo/script/kubernetes/chart$ helm dependency update k8sdemo
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
Update Complete. ?Happy Helming!?
Saving 2 charts
Deleting outdated charts
完成之后,生成的圖如下所示塌计,這時(shí)在“charts”目錄下就導(dǎo)入了新的依賴關(guān)系“k8sdemo-backend”和“k8sdemo-database”的chart挺身。
有一點(diǎn)需要注意的是,單獨(dú)調(diào)試和聯(lián)合調(diào)試時(shí)锌仅,生成的k8s配置文件大部分都是一樣的章钾,但有一個(gè)地方不同
下面是聯(lián)合調(diào)試時(shí)“k8sdemo-database”的部署文件,最后一行“app.kubernetes.io/instance: ”的值是“k8sdemo”热芹。
# Source: k8sdemo/charts/k8sdemo-database/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8sdemo-database
labels:
helm.sh/chart: k8sdemo-database-0.1.0
app.kubernetes.io/name: k8sdemo-database
app.kubernetes.io/instance: k8sdemo
贱傀。。伊脓。
下面是單獨(dú)調(diào)試時(shí)“k8sdemo-database”的部署文件府寒,最后一行“app.kubernetes.io/instance: ”的值是“”k8sdemo-database”。
# Source: k8sdemo/charts/k8sdemo-database/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8sdemo-database
labels:
helm.sh/chart: k8sdemo-database-0.1.0
app.kubernetes.io/name: k8sdemo-database
app.kubernetes.io/instance: k8sdemo-database
报腔。椰棘。。
因?yàn)椤癷nstance”的名字是“{{ .Release.Name }}”榄笙,而單獨(dú)調(diào)試和聯(lián)合調(diào)試時(shí)給的“release”名字不同邪狞。而其他的值都是由配置文件決定的,因此不會(huì)有意外茅撞。
安裝k8sdemo:
vagrant@ubuntu-xenial:~/jfeng45/k8sdemo/script/kubernetes/chart$ helm upgrade k8sdemo ./k8sdemo
Release "k8sdemo" has been upgraded. Happy Helming!
NAME: k8sdemo
LAST DEPLOYED: Fri Nov 29 01:28:55 2019
NAMESPACE: default
STATUS: deployed
REVISION: 2
NOTES:
1\. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services k8sdemo)
export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
獲取Pod名稱:
vagrant@ubuntu-xenial:~/jfeng45/k8sdemo/script/kubernetes/chart$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8sdemo-74cb7b997c-pgcj4 1/1 Running 0 33s
k8sdemo-backend-5cd9d79856-dqlmz 1/1 Running 0 33s
k8sdemo-database-85855485c6-jtksb 1/1 Running 0 33s
k8sdemo-jenkins-deployment-675dd574cb-r57sb 1/1 Running 3 23d
運(yùn)行程序進(jìn)行測設(shè):
vagrant@ubuntu-xenial:~/jfeng45/k8sdemo/script/kubernetes/chart$ kubectl exec -ti k8sdemo-backend-5cd9d79856-dqlmz -- /bin/sh
~ # ./main.exe
time="2019-11-27T07:03:03Z" level=debug msg="connect to database "
time="2019-11-27T07:03:03Z" level=debug msg="dataSourceName:dbuser:dbuser@tcp(k8sdemo-database-service:3306)/service_config?charset=utf8"
time="2019-11-27T07:03:03Z" level=debug msg="FindAll()"
time="2019-11-27T07:03:03Z" level=debug msg="created=2019-10-21"
time="2019-11-27T07:03:03Z" level=debug msg="find user:{1 Tony IT 2019-10-21}"
time="2019-11-27T07:03:03Z" level=debug msg="find user list:[{1 Tony IT 2019-10-21}]"
time="2019-11-27T07:03:03Z" level=debug msg="user lst:[{1 Tony IT 2019-10-21}]"
~ #
其他問題:
由于篇幅有限帆卓,本文不可能把所有的問題都講清楚,還有兩個(gè)比較重要的問題米丘,這里簡單的提一下剑令。
1.Secret:
本文用的都是明碼,如果需要加密的話有兩種方式拄查,一種是 helm-secrets吁津,另一種是Vault,請閱讀相關(guān)文檔堕扶。
2.為不同環(huán)境設(shè)置不同的常量:
本文只創(chuàng)建了針對一種環(huán)境的文件 碍脏,如果你需要針對不同環(huán)境(例如DEV,QA稍算,PROD)配置不同的參數(shù)的話典尾,你可以在“k8sdemo”的chart里給不同的環(huán)境創(chuàng)建不同的"values.yaml",例如“values-dev.yaml”給DEV環(huán)境糊探。但在子chart里钾埂,就不能這樣做河闰,因?yàn)橄到y(tǒng)要求"values.yaml"。這時(shí)褥紫,你可以在父chart的“values-dev.yaml”里為不同的子chart創(chuàng)建常量姜性,這樣這些常量就能覆蓋子chart里定義的常量。
在“values-dev.yaml”加入下面代碼髓考。
k8sdemo-backend:
replicaCount: 2
k8sdemo-database:
replicaCount: 2
鍵入如下命令試運(yùn)行:
vagrant@ubuntu-xenial:~$ cd /home/vagrant/jfeng45/k8sdemo/script/kubernetes/chart
vagrant@ubuntu-xenial:~/jfeng45/k8sdemo/script/kubernetes/chart$ helm install --dry-run --values ./k8sdemo/values-dev.yaml --debug k8sdemo ./k8sdemo
查看結(jié)果部念,子chart中的相應(yīng)參數(shù)已被覆蓋。
詳情請參閱How to set environment related values.yaml in Helm subcharts?
常見錯(cuò)誤:
在調(diào)試過程中還是遇到了不少問題绳军,但大多數(shù)都是與語法有關(guān)的問題印机,因?yàn)镠elm和k8s都用的是YAML文件矢腻,而它對文件格式有著嚴(yán)格的要求门驾,如果不滿足要求就會(huì)報(bào)錯(cuò)。幸好它報(bào)錯(cuò)時(shí)包含了錯(cuò)誤代碼行號多柑,這樣查找起來比較容易奶是。
- Pod的狀態(tài)是CrashLoopBackOff
它的癥狀是在用“helm install --dry-run --debug”調(diào)試時(shí)沒有問題,但正式運(yùn)行時(shí)出了問題竣灌,用下面命令檢查聂沙,Pod的狀態(tài)是“CrashLoopBackOff”。
vagrant@ubuntu-xenial:~$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8sdemo-74cb7b997c-gn5v2 1/1 Running 1 47h
k8sdemo-backend-6cdbb96964-tb2xd 0/1 CrashLoopBackOff 129 9h
k8sdemo-database-deployment-578fc88c88-mm6x8 1/1 Running 12 37d
k8sdemo-jenkins-deployment-675dd574cb-r57sb 1/1 Running 3 19d
這個(gè)問題我以前調(diào)試k8s時(shí)也碰到過初嘹,主要是與Docker鏡像有關(guān)及汉,但這次明明鏡像是 好的。試了很多組合屯烦,最后終于發(fā)現(xiàn)是自動(dòng)生成的代碼出了問題坷随。
在“deployment.yaml”里有下面代碼,這是Helm自動(dòng)生成用來測試部署的驻龟。
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
把它去掉之后就沒有問題了温眉。而且它只在特定的chart(“k8sedemo-backend”)里會(huì)出錯(cuò),在“k8sdemo”里就沒有問題翁狐。我現(xiàn)在也不是特別清楚問題在哪类溢,只是把它暫時(shí)刪除掉了。
- 持久卷未能綁定到持久卷申請
它的癥狀是宿主機(jī)的持久卷未能綁定到持久卷申請露懒,導(dǎo)致持久卷申請又另外創(chuàng)建了一個(gè)持久卷闯冷。你用“kubectl get pv”就能看到新創(chuàng)建的持久卷,但實(shí)際上它是不必要的懈词,只要把持久卷申請綁定到已有的PV上就行了窃躲。這個(gè)錯(cuò)誤并不是每次都發(fā)生,而是隨機(jī)的钦睡。大部分時(shí)間綁定正確蒂窒,少數(shù)時(shí)候綁定錯(cuò)誤躁倒。我開始想是不是因?yàn)閳?zhí)行k8s文件的順序問題,但k8s文件是按照文件類別(kind)來執(zhí)行的洒琢,按理來說順序應(yīng)該是正確的秧秉。再有一個(gè)可能就是時(shí)間延遲,因?yàn)閯?chuàng)建持久卷需要時(shí)間衰抑,而如果持久卷申請沒有檢測到這個(gè)持久卷象迎,那么它就會(huì)另外創(chuàng)建一個(gè)。如果真是這樣的話呛踊,就要在創(chuàng)建時(shí)設(shè)定一個(gè)延遲砾淌。但它暫時(shí)來講對我影響不大,因此就偷了一下懶谭网,以后有時(shí)間再來調(diào)試汪厨。
源碼庫
完整源碼的github鏈接:
k8sdemo
索引:
- CHANGES SINCE HELM 2
- Charts
- YAML
- Use placeholders in yaml
- Use YAML with variables
- 把應(yīng)用程序遷移到k8s需要修改什么?
- Subcharts and Global Values
- How to refer to a helm chart in the same repository
- helm-secrets
- Vault
- How to set environment related values.yaml in Helm subcharts?
不堆砌術(shù)語愉择,不羅列架構(gòu)劫乱,不迷信權(quán)威,不盲從流行锥涕,堅(jiān)持獨(dú)立思考
Memo
本文轉(zhuǎn)載至:https://www.cnblogs.com/code-craftsman/p/11958281.html