用Helm3構建多層微服務

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

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

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

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

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

Helm文件結構

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

file

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

  • "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):json

若是你想複用重複的值,能把它定義成常量嗎?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」,更新依賴關係

~ # 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

完成以後,生成的圖以下所示,這時在「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:

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

運行程序進行測設:

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}]"
~ #

其餘問題:

因爲篇幅有限,本文不可能把全部的問題都講清楚,還有兩個比較重要的問題,這裏簡單的提一下。

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

鍵入以下命令試運行:

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

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

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

常見錯誤:

在調試過程當中仍是遇到了很多問題,但大多數都是與語法有關的問題,由於Helm和k8s都用的是YAML文件,而它對文件格式有着嚴格的要求,若是不知足要求就會報錯。幸虧它報錯時包含了錯誤代碼行號,這樣查找起來比較容易。

  1. Pod的狀態是CrashLoopBackOff

它的症狀是在用「helm install --dry-run --debug」調試時沒有問題,但正式運行時出了問題,用下面命令檢查,Pod的狀態是「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

這個問題我之前調試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?

不堆砌術語,不羅列架構,不迷信權威,不盲從流行,堅持獨立思考

相關文章
相關標籤/搜索