欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

用Helm3构建多层微服务

程序员文章站 2022-05-22 22:21:57
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配置文件。

用Helm3构建多层微服务

以上就是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的设计局限性,请参见

chart设计

现在我们就用一个具体的例子来展示helm的chart设计。这个例子是一个微服务应用程序,它共有三层: 前端,后端和数据库,只有这样才能让helm的一些设计问题付出水面,如果只有一层的话,就太简单了,没有参考价值。

在k8s中,每一层就是一个单独的服务,它里面有各种配置文件。helm的优势是把这些不同的服务组成一个chart来共同管理和调式,方便了许多。

用Helm3构建多层微服务

上面就是最终的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

用Helm3构建多层微服务

上面就是“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。

用Helm3构建多层微服务

有一点需要注意的是,单独调试和联合调试时,生成的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:
本文用的都是明码,如果需要加密的话有两种方式,一种是 ,另一种是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链接:

索引:

  1. changes since helm 2
  2. charts
  3. yaml
  4. use placeholders in yaml
  5. use yaml with variables
  6. subcharts and global values
  7. how to refer to a helm chart in the same repository
  8. vault
  9. how to set environment related values.yaml in helm subcharts?