用Helm3構建多層微服務

来源:https://www.cnblogs.com/code-craftsman/archive/2019/11/29/11958281.html

Helm是一款非常流行的k8s包管理工具。以前就一直想用它,但看到它產生的文件比k8s要複雜許多,就一直猶豫,不知道它的好處能不能抵消掉它的複雜度。但如果不用,而是用Kubectl來進行調式真的很麻煩。正好最近Helm3正式版出來了,比原來的Helm2簡單了不少,就決定還是試用一下。結果證明確實很復 ...


Helm是一款非常流行的k8s包管理工具。以前就一直想用它,但看到它產生的文件比k8s要複雜許多,就一直猶豫,不知道它的好處能不能抵消掉它的複雜度。但如果不用,而是用Kubectl來進行調式真的很麻煩。正好最近Helm3正式版出來了,比原來的Helm2簡單了不少,就決定還是試用一下。結果證明確實很複雜,它的好處和壞處大致相當。有了它確實能大大簡化對k8s的調式,但也需要花費比較多的時間來學習,而且產生的配置文件要複雜許多。但是事實是現在沒有什麼很方便的幫助調式k8s的工具,在沒有更好的方案之前,我還是建議用它,只是前期需要花些功夫學習和掌握它。

Helm3和Helm2的語法差不太多,只是使用起來更方便,不用安裝Tiller。一個比較明顯的變化是不再需要“requirements.yaml”, 依賴關係是直接在“chart.yaml”中定義。有關Helm3和Helm2的區別,詳情請參見CHANGES SINCE HELM 2

網上有不少講述Helm的文章,但大部分都是主要講解安裝和舉一個簡單的例子。但Helm使用起來還是比較複雜的,一定要有一個複雜的例子才能把它的功能講清楚,裡面有不少設計方面的問題需要思考。我剛開始接觸的時候就覺得頭緒繁多,不知從哪下手。本文就通過一個相對複雜的例子來講解用Helm3來設計配置文件的思路,使上手更容易。

這裡不講Helm3的安裝,它比較很容易。也不講解Helm的基本語法,你可以自己去看其他文檔。即使你不懂Helm,應該也能猜出七八成,剩下的就要讀文檔了(Charts)。Helm的語法還是比較複雜的,要想搞懂可能要花一兩天時間。

本文假設你對helm有一個大概的瞭解,想要構建一個複雜的微服務,但有不知如何下手;或者你想瞭解一下構建Helm的最佳實踐,那就請你繼續讀下去。

Helm文件結構

chart里一個很重要的概念就是模板(template),它就是Go語言模板,它是裡面加入了編程邏輯的k8s文件。這些模板文件在使用時都要先進行模板解析,把其中的程式邏輯轉化成對應的編碼,最終生成k8s配置文件。

file

以上就是Helm自動生成的chart目錄結構,在Helm里每個項目叫一個chart,它由下麵幾個組成部分:

  • "Chart.yaml":存有這個chart的基本信息,
  • "values.yaml":定義模板中要用到的常量。
  • “template”目錄:裡面存有全部的模板文件,其中最重要的是“deployment.yaml”和“service.yaml”,分別是部署和服務文件. "helpers.tpl"用來定義變數,"ingress.yaml"和"serviceaccount.yaml"分別是對外介面和服務賬戶,這裡暫時沒用, “NOTES.txt”是註釋文件。
  • “charts”目錄: 存有這個chart依賴的所有子chart。

Helm的基本元素

Helm有四個基本元素,值,常量,變數和共用常量(這個後面會講)

值(literal)

Helm在k8s的基礎之上增加了模板功能,使k8s的配置文件更加靈活。裡面的主要概念就是模板(Template),也就是在k8s的配置文件里增加了常量和變數以及編程邏輯。如果你不用這些新增功能,那麼就是普通的YAML文件(k8s配置文件),裡面用到的基本元素就是值。

常量

節點定位(Node Anchor):

如果你想復用重覆的值,能把它定義成常量嗎?YAML有一個功能叫節點定位(Node Anchor),類似於定義一個常量,然後引用。但它有一些限制,定義的必須是一個節點,因此不如真正的常量靈活。
例如如下文件中,用“&”定義了一個常量“&k8sdemoDatabaseService”,然後用“*k8sdemoDatabaseService”引用它。

global:
  k8sdemoDatabaseService: &k8sdemoDatabaseService k8sdemo-database-service
  mysqlHost: *k8sdemoDatabaseService

這時,“k8sdemoDatabaseService:”是YAML文件節點的鍵名,“&k8sdemoDatabaseService”是節點定位的名字,相當於常量名,“k8sdemo-database-service”是YAML節點的鍵值。在上述代碼中,k8sdemoDatabaseService和mysqlHost的值都是“k8sdemo-database-service”。

有關節點定位(Node Anchor)的詳細內容,請參見 YAML

常量:

由於節點定位的局限性,Helm引入了真正的常量,也就是在"values.yaml"里定義的內容,它可以定義是任何東西,不只限於節點。

在"values.yaml"里定義常量:

replicaCount: 1

在部署模板里引用:

replicas: {{ .Values.replicaCount }}

那麼什麼時候用常量,什麼時候用值(Literal)呢?如果一個值在模板中出現多次,就要定義常量,避免重覆。例如“accessModes”,既要在存儲捲里出現,又要在存儲捲申請里出現。另外如果值有可能變化(不論是隨部署環境變化,還是隨時間變化),那麼就定義成常量,這樣在修改時就只用改"values.yaml",而不必修改模板文件。例如“replicas”的值(也就是集群的個數)是可能變化的,就要定義成常量。在模板里可以引用常量的,但在"values.yaml"里不行,因為它只是普通YAML文件,沒有模板解析功能,因此不支持常量,這裡就只能用節點定位(來代替常量)。

有關Helm常量的詳細內容,請參見 Use placeholders in yamlUse YAML with variables

變數

節點定位的功能是有限的,例如你想利用已有的節點定位,對它進行轉換,定義一個新的節點定位,這在"values.yaml"里就不行了。
例如你已有節點定位“name”,你想在這個基礎上定義一個新的節點定位“serviceName”,這個"values.yaml"就不支持了,你必須要用模板。

如下所示,這在"values.yaml"里是不支持的。

name: &name k8sdemo-backend
serviceName:*name-service

這就引出了變數的概念,但它只能在模板里才行。 換句話說,模板既支持常量,也支持變數。但如果把變數的定義邏輯放在Helm每個模板里,就顯得很亂。因此一般的做法是把這些邏輯放在一個單獨的模板文件里,這個就是前面講到的"_helpers.tpl"文件。當你需要對常量進行轉換,生成新的常量,你就在定義變數,這部分代碼就放在"_helpers.tpl"里。

下麵就是"_helpers.tpl"中定義"k8sdemo.name"的代碼。

{{- define "k8sdemo.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

在以上這些元素中,常量(也就是在"values.yaml"中定義的)是最靈活的,能用它時儘量用它。而且因為它是定義在普通YAML文件中("values.yaml"),應用程式可以直接訪問它,這樣可以實現應用程式和k8s之間的數據共用。但如果你需要對常量進行編程轉換,那就沒辦法了,只能定義變數,把它放在"_helpers.tpl"中。

ConfigMap和Secret

在k8s中ConfigMap和Secret是用來存儲共用配置參數和保密參數的,但在Helm中,由於有了上面講的Helm基本元素,它們完全可以代替ConfigMap的功能,因此ConfigMap就不需要了,但Secret還是需要的,因為要存儲加密信息。下麵會講解。

有關ConfigMap的設計局限性,請參見把應用程式遷移到k8s需要修改什麼?

Chart設計

現在我們就用一個具體的例子來展示Helm的chart設計。這個例子是一個微服務應用程式,它共有三層: 前端,後端和資料庫,只有這樣才能讓Helm的一些設計問題付出水面,如果只有一層的話,就太簡單了,沒有參考價值。

在k8s中,每一層就是一個單獨的服務,它裡面有各種配置文件。Helm的優勢是把這些不同的服務組成一個Chart來共同管理和調式,方便了許多。

file

上面就是最終的chart目錄結構圖。“chart”是總目錄,裡面有三個子目錄“k8sdemo”,“k8sdemo-backend”,“k8sdemo-database”, 每一個對應一個服務,每個服務都是一個獨立的chart,能單獨調式部署,chart之間也可以有依賴關係。其中“k8sdemo”是父chart,同時也是前端服務,它的“charts”目錄里有它依賴的另外兩個服務。“k8sdemo-backend”是後端服務,“k8sdemo-database”是資料庫服務。

處理Chart的依賴關係有兩種方式:

  1. 嵌入式:就是直接把依賴的chart放在“charts”子目錄里,這樣子chart是父chart的一部分。它是一種緊耦合的關係,好處是比較簡單,但不夠靈活。
  2. 依賴導入式:就是各個chart是併列關係,各自單獨調試部署,互相獨立,需要合併時再把子chart導入父chart里,它是一種松耦合的關係,好處是比較靈活,但設計更複雜。 在這種結構下,各個chart可以單獨工作也可以聯合工作,不過你需要更好的設計。

這裡採用的是依賴導入式方式,主要原因是我原來認為嵌入式需要一起調試,複雜度太高,如果你覺得這不是問題,這也是個不錯的辦法。用依賴導入式方式,可以單獨調試各個chart,簡單了很多。後來發現其實採用嵌入式也可以單獨調試子chart,只是父chart不能單獨調試而已。

調試順序:

當你採用依賴導入式方式時,調試順序關係不大,因為各個chart是各自獨立的,可以單獨調試。舉個例子,雖然“k8sdemo-backend”需要“k8sdemo-database”才能正常運行,但當沒有資料庫服務時,你的程式也可以運行,只不過輸出的是錯誤信息,但這並不影響你調試chart。

我先調試“k8sdemo”,它雖然依賴另外兩個chart,但沒有它們也能單獨工作。然後再調試“k8sdemo-backend”和“k8sdemo-database”,最後再把它們導入到“k8sdemo”中去再進行聯調。

調式“k8sdemo”

它的調試是最容易的,由於它裡面沒有真正的前端代碼,只要把Nginx調試成功了就可以了。只要在生成的文件基礎上做些修改就行了。

鍵入如下命令創建chart,其中“k8sdemo”是chart的名字,這個名字很重要,服務的名字和label都是由它產生的。

helm create k8sdemo

這之後,系統會自動創建前面講到的chart目錄結構。讓後就是對已經生成的文件進行修改。

修改"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"暫時沒用,就把它們都設成了“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 }}
。。。

以上都是簡單的修改,不涉及到設計問題。由於篇幅的關係,這裡沒有列出全部源碼,如果有興趣請在本文末尾找到源碼地址。

共用常量

在進行下麵的調試之前,先要講一個重要概念。 前面介紹Helm的基本元素時講的都是在一個chart里共用值,如果要在不同chart之間共用值(例如k8s服務名,資料庫用戶名和埠),那麼這些還不夠,你需要共用常量. 通常情況下子chart和父chart之間的常量是不能共用的,如果需要共用,需要有一種特殊的方法來定義常量,這就是共用常量。它必須是定義在父chart中。

共用常量

例如,你在“k8sdemo”的“values.yaml”加入下麵代碼,註意節點的名字必須是子chart名(例如“k8sdemo-backend”)

k8sdemo-backend:
  replicaCount: 2
k8sdemo-database:
  replicaCount: 2

在“k8sdemo”的模板里就可以通過“{{ .Values.k8sdemo-backend.replicaCount }}” 來訪問。當Helm發現節點名是子chart名時,它會自動拷貝這個常量到子chart的“values.yaml”中,因此,在“k8sdemo-backend”中,你也可以通過“{{ .Values.replicaCount }}” 來訪問這個常量。註意這裡並沒有包含子chart名(“k8sdemo-backend”),而是只有常量名,因為子chart名只是一個標識,而不是常量名的一部分。

全局常量

共用常量只能把常量共用給一個字chart,如果你需要多個子chart之間共用,就需要創建全局常量,它用“global”來標識,下麵是示例。

在“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 }}

當把“k8sdemo-backend”和“k8sdemo-database”導入"k8sdemo"後進行聯調時, 就要把上面提到的全局常量寫入"k8sdemo"的"values.yaml"文件中,這樣就能讓各個子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有重覆的全局常量,這時父chart("k8sdemo")的全局常量值就會覆蓋子chart的全局常量。

它的使用原則就是如果只是子chart獨有的常量就在子chart的"values.yaml"中定義,如果是共用的常量就在父chart中定義。但如果採用的是依賴導入方式,由於子chart也要單獨調試,這時你在子chart里也要定義這些全局常量。這樣在進行chart總調試時,就會使用父chart的中的值。

詳情請參見 Subcharts and Global Values

調試“k8sdemo-backend”

“k8sdemo-backend”的chart需要取(與“k8ssdemo”)不同的名字,
創建:

helm create k8sdemo-backend

file

上面就是“k8sdemo-backend”的目錄圖。由於它需要建持久捲,因此這裡增加了兩個文件“persistentvolume.yaml”和“persistentvolumeclaim.yaml” ( 不是自動生成的)。

值得一提的是k8s對象的命名。一般情況下,如果不需要對其進行引用,用chart的全名就行了。例如部署的名稱,如下所示。

name: {{ include "k8sdemo.fullname" . }}

如果是服務名(Service Name),它需要在應用程式和k8s之間共用,也需要在父chart和子chart之間共用,這時最好單獨定義一個全局共用常量。

在“values.yaml”中定義:

global:
  k8sdemoBackendService: k8sdemo-backend-service

在“service.yaml”中引用:

name: {{.Values.global.k8sdemoBackendService}}

調試“k8sdemo-database”

它的調試方式與“k8sdemo-backend”大同小異,就不詳細講解了。

聯合調試:

上面各個chart都單獨調試成功之後,就要把它們合在一起進行聯合調試。
在“k8sdemo”(父chart)中加入依賴關係(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”,更新依賴關係

~ # [email protected]:~/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

完成之後,生成的圖如下所示,這時在“charts”目錄下就導入了新的依賴關係“k8sdemo-backend”和“k8sdemo-database”的chart。

file

有一點需要註意的是,單獨調試和聯合調試時,生成的k8s配置文件大部分都是一樣的,但有一個地方不同

下麵是聯合調試時“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
。。。

下麵是單獨調試時“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
。。。

因為“instance”的名字是“{{ .Release.Name }}”,而單獨調試和聯合調試時給的“release”名字不同。而其他的值都是由配置文件決定的,因此不會有意外。

安裝k8sdemo:

[email protected]:~/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名稱:

[email protected]:~/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

運行程式進行測設:

[email protected]:~/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:[email protected](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}]"
~ #

其他問題:

由於篇幅有限,本文不可能把所有的問題都講清楚,還有兩個比較重要的問題,這裡簡單的提一下。

1.Secret:
本文用的都是明碼,如果需要加密的話有兩種方式,一種是 helm-secrets,另一種是Vault,請閱讀相關文檔。

2.為不同環境設置不同的常量:
本文只創建了針對一種環境的文件 ,如果你需要針對不同環境(例如DEV,QA,PROD)配置不同的參數的話,你可以在“k8sdemo”的chart里給不同的環境創建不同的"values.yaml",例如“values-dev.yaml”給DEV環境。但在子chart里,就不能這樣做,因為系統要求"values.yaml"。這時,你可以在父chart的“values-dev.yaml”里為不同的子chart創建常量,這樣這些常量就能覆蓋子chart里定義的常量。

在“values-dev.yaml”加入下麵代碼。

k8sdemo-backend:
    replicaCount: 2
k8sdemo-database:
    replicaCount: 2

鍵入如下命令試運行:

[email protected]:~$ cd /home/vagrant/jfeng45/k8sdemo/script/kubernetes/chart
[email protected]:~/jfeng45/k8sdemo/script/kubernetes/chart$ helm install --dry-run --values ./k8sdemo/values-dev.yaml --debug k8sdemo ./k8sdemo

查看結果,子chart中的相應參數已被覆蓋。

詳情請參閱How to set environment related values.yaml in Helm subcharts?

常見錯誤:

在調試過程中還是遇到了不少問題,但大多數都是與語法有關的問題,因為Helm和k8s都用的是YAML文件,而它對文件格式有著嚴格的要求,如果不滿足要求就會報錯。幸好它報錯時包含了錯誤代碼行號,這樣查找起來比較容易。

  1. Pod的狀態是CrashLoopBackOff

它的癥狀是在用“helm install --dry-run --debug”調試時沒有問題,但正式運行時出了問題,用下麵命令檢查,Pod的狀態是“CrashLoopBackOff”。

[email protected]:~$ 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

這個問題我以前調試k8s時也碰到過,主要是與Docker鏡像有關,但這次明明鏡像是 好的。試了很多組合,最後終於發現是自動生成的代碼出了問題。
在“deployment.yaml”里有下麵代碼,這是Helm自動生成用來測試部署的。

livenessProbe:
  httpGet:
    path: /
    port: http
readinessProbe:
  httpGet:
    path: /
    port: http

把它去掉之後就沒有問題了。而且它只在特定的chart(“k8sedemo-backend”)里會出錯,在“k8sdemo”里就沒有問題。我現在也不是特別清楚問題在哪,只是把它暫時刪除掉了。

  1. 持久捲未能綁定到持久捲申請

它的癥狀是宿主機的持久捲未能綁定到持久捲申請,導致持久捲申請又另外創建了一個持久捲。你用“kubectl get pv”就能看到新創建的持久捲,但實際上它是不必要的,只要把持久捲申請綁定到已有的PV上就行了。這個錯誤並不是每次都發生,而是隨機的。大部分時間綁定正確,少數時候綁定錯誤。我開始想是不是因為執行k8s文件的順序問題,但k8s文件是按照文件類別(kind)來執行的,按理來說順序應該是正確的。再有一個可能就是時間延遲,因為創建持久捲需要時間,而如果持久捲申請沒有檢測到這個持久捲,那麼它就會另外創建一個。如果真是這樣的話,就要在創建時設定一個延遲。但它暫時來講對我影響不大,因此就偷了一下懶,以後有時間再來調試。

源碼庫

完整源碼的github鏈接:
k8sdemo

索引:

  1. CHANGES SINCE HELM 2
  2. Charts
  3. YAML
  4. Use placeholders in yaml
  5. Use YAML with variables
  6. 把應用程式遷移到k8s需要修改什麼?
  7. Subcharts and Global Values
  8. How to refer to a helm chart in the same repository
  9. helm-secrets
  10. Vault
  11. How to set environment related values.yaml in Helm subcharts?

您的分享是我們最大的動力!

更多相關文章
  • Accept Encoding和Content Encoding Accept Encoding和Content Encoding是HTTP中用來對採用何種壓縮格式傳輸正文進行協定的一對header。工作原理如下: 瀏覽器發送請求,通過Accept Encoding帶上自己支持的內容編碼格式列表 服 ...
  • 作者:水濤 座右銘:天行健,君子以自強不息 自白:我寫博文上來蹭蹭就是乾,我突然覺得我需要幽默一點了,好了,下麵我們說正經的 一、官方定義: DefinePlugin 允許創建一個在 編譯 時可以配置的全局常量。這可能會對開發模式和生產模式的構建允許不同的行為非常有用。如果在開發構建中,而不在發佈構 ...
  • 直接上代碼,複製粘貼就能用: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF ...
  • JS基礎語法 roadmap Part 1 - 2: Part 3 - 4: Part 5 - 6 ...
  • Google 跟蹤代碼管理器是一個跟蹤代碼管理系統 (TMS),可以幫助您快速輕鬆地更新網站或移動應用上的跟蹤代碼及相關代碼段(統稱為“代碼”)。將一小段跟蹤代碼管理器代碼添加到項目後,您可以通過網頁界面安全輕鬆地部署 Google Analytics(分析)和衡量代碼配置。 在通過clickCla ...
  • 預解析:就是在解析代碼之前 1. 預解析做什麼事? 把變數的聲明提前了 提前到當前所在的作用域的最上面 函數的聲明也會被提前 提前到當前所在的作用域的最上面 舉例: function f1() { console.log(num); var num = 10; } f1(); //此時運行結果是un ...
  • 從函數嵌套來分析: (層數一般5層內) var num=10; function f1() { var num=20; function f2() { var num=30; function f3() { var num=50; console.log(num); } f3(); } f2(); ...
  • 最近我在做angularjs程式時遇到了一個問題 1.頁面有很多選擇框,一個選擇框裡面有眾多的選擇項,和一個預設選定的項,像下麵這樣(很多選擇框,不只一個): 2.眾多的選項要從後臺介面得到,預設項從另一個後臺介面得到,這就需要$promise.then()操作 3.而多個$promise.then ...
一周排行
x