zoukankan      html  css  js  c++  java
  • 用Helm3构建多层微服务

    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”,更新依赖关系

    ~ # 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?
  • 相关阅读:
    .NET平台系列18 .NET5的超强优势
    .NET平台系列17 .NET5中的ARM64性能
    .NET平台系列19 新世界中的.NET大统一平台架构解析
    .NET平台系列16 .NET5/Asp.Net Core 在全球Web框架权威性能测试 Web Framework Benchmarks 中的吊炸天表现
    .NET平台系列15 .NET5的吊炸天性能改进
    .NET平台系列14 .NET5中的新增功能
    从零开始学Typescript-类型注解
    从零开始学Typescript-第一个TS程序
    从零开始学Typescript-安装Typescript
    从零开始学VUE-创建VUE应用
  • 原文地址:https://www.cnblogs.com/code-craftsman/p/11958281.html
Copyright © 2011-2022 走看看