成功的软平台都有一个优秀的打包系统,比如: Debain、Ubuntu:apt Read Hat、Centos:yum Kubernetes能够很好的组织和编排容器,但是缺少一个更高层次的应用打包工具,Helm是Kubernetes上的包管理工具。 举个例子 部署一个Mysql服务,Kubernetes需要部署下面这些对象: Service:让外界能够访问Mysql服务; Secret:定义Mysql的密码; PersistentVolumeClaim:为Mysql申请持久化存储空间; Deployment:部署Mysql Pod,并使用上面的三种支持对象。 需要将上面这些配置文件保存到对象各自的文件中,或者集中写在一个配置文件中,然后通过如下命令进行部署: kubectl apply -f <配置文件.yaml> 目前来看,kubernetes对服务的部署支持度挺高的,如果一个应用由上面的一个或几个这样的服务组成,这样的方式足够,但是如果开发的微服务架构的应用,组成应用的服务可能多达十几个甚至上百个,这样的组织方式就很不友好。 有以下缺点: 很难管理、编辑和维护如此多的服务。每个服务都有若干层次的抽象。缺乏一个更高层次的工具将这些配置组织起来。 不容易将这些服务作为一个整体统一发布。部署人员需要首先理解应用包含哪些服务,按照逻辑顺序依次执行kubectl apply。缺少一种工具来定义应用和服务,以及服务与服务之间的依赖关系。 不能高效地共享和重用服务。比如两个应用都用到Mysql服务,但是配置的参数不同,这两个应用只能分别复制一套标准的Mysql配置文件,修改后通过kubectl apply继续部署。不支持参数化配置和多环境部署。 不支持应用级别的版本管理。虽然可以通过kubectl rollout undo 继续回滚,但这个只能针对单个Deployment,不能针对整个应用的回滚。 不支持对部署的应用状态进行验证。比如能否通过预定义的账号访问Mysql服务。虽然kubernetes有健康检查,但是那个只是针对单个容器的,没有应用(服务)级别的健康检查。 Helm架构 chart 创建一个应用的信息集合,包括: 各种kubernetes对象的配置模板 参数定义 依赖关系 文档说明 chart是应用部署的自包含逻辑单元,可以想象成apt、yum中的软件安装包。 release release是chart的运行实例,代表了一个正在运行的应用。当chart被安装到Kubernetes集群中,就生成了一个release。chart能够多次安装到同一个集群,每次安装都生成一个release。 Helm是一个包管理工具,这里的包指的就是chart,有如下功能: 从零创建新chart 与存储chart的仓库交互、拉取、保存和更新chart 在kubernetes集群中安装和卸载release 更新、回滚和测试release Helm包含两个组件:Helm客户端和Tiller服务器,如下图所示: Helm 客户端是终端用户使用的命令行工具,用户可以: 在本地开发chart 管理chart仓库 与Tiller服务器交互 在远程kubernetes集群上安装chart 查看release信息 升级或卸载已有的release Tiller 服务器运行在kubernetes集群中,它会处理Helm客户端的请求,与kubernetes API Server交互。 Tiller服务器负责: 监听来自Helm客户端的请求 通过chart构建release 在kubernetes集群中安装chart,并跟踪release的状态 通过API Server 升级或卸载已有的release 简单来说,Helm客户端负责管理chart,Tiller服务器负责管理release。 Helm官方文档 Helm安装时已经默认配置好了两个仓库: stable(官方仓库) local(用户存放自己开发的chart的本地仓库)
客户端:helm 服务端:Tiller 安装 1. 下载helm客户端 点击这里下载。 选择对应版本的二进制安装包,并执行如下命令: tar -zxvf helm-v2.0.0-linux-amd64.tgz mv linux-amd64/helm /usr/local/bin/helm 2. 创建ServiceAccount和ClusterRoleBinding 使用集群预创建的ClusterRole <cluster-admin> kubectl apply -f rabc-config.yaml 3. 安装Tiller # 初始化并更换国内的源 helm init --service-account tiller --history-max 200 --tiller-image localhost:5000/tiller:v2.14.0 \ --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts # 修改镜像 kubectl set image deployments/tiller-deploy tiller=localhost:5000/tiller:v2.14.0 -n kube-system # 卸载 kubectl delete deployment tiller-deploy -n kube-system # 或者 helm reset
Kubernetes 包管理工具。 # 要开始使用Helm,请运行`helm init`命令 # 这将把Tiller安装到正在运行的Kubernetes集群中,它还将设置任何必要的本地配置 helm init 共用的命令操作包括: - helm search: 搜索charts - helm fetch: 下载一个chart到本地来查看 - helm install: 安装一个chart到kubernetes集群中 - helm list: 列出安装的Release 环境变量: - $HELM_HOME: 为Helm文件设置备用位置。 默认情况下,它们存储在"~/.helm"中 - $HELM_HOST: 设置另一个Tiller主机,格式是 host:port - $HELM_NO_PLUGINS: 设置 HELM_NO_PLUGINS=1 来取消插件 - $TILLER_NAMESPACE: 设置另一个Tiller的namespace(默认是 "kube-system") - $KUBECONFIG: 设置另一个kubernetes配置文件 (默认是 "~/.kube/config") - $HELM_TLS_CA_CERT: 用于验证Helm客户端和Tiller服务器证书的TLS CA证书的路径 (默认是 "$HELM_HOME/ca.pem") - $HELM_TLS_CERT: 用于向Tiller进行身份验证的TLS客户端证书文件的路径 (默认是 "$HELM_HOME/cert.pem") - $HELM_TLS_KEY: 用于向Tiller进行身份验证的TLS客户端密钥文件的路径 (默认是 "$HELM_HOME/key.pem") - $HELM_TLS_ENABLE: 启用Helm和Tiller之间的TLS连接 (默认是 "false") - $HELM_TLS_VERIFY: 启用Helm和Tiller之间的TLS连接并验证Tiller服务器证书 (默认是 "false") - $HELM_TLS_HOSTNAME: 用于验证Tiller服务器证书的主机名或IP地址 (默认是 "127.0.0.1") - $HELM_KEY_PASSPHRASE: 设置为PGP私钥的密码。 如果设置,则在签署helm charts时不会提示输入密码 用法: helm [command] 可选的 Commands: completion 为指定的shell(bash或zsh)生成自动补全脚本 create 创建具有给定名称的新chart delete 删除Kubernetes集群中指定的release dependency 管理chart的依赖 fetch 从仓库下载指定chart并可选择是否在本地路径下解压缩 get 下载指定的release help 帮助 history 获取release的历史记录 home 输出 HELM_HOME 的路径 init 在客户端和服务端同时初始化Helm inspect 检查chart install 安装chart压缩包 lint 检查依赖和模板配置是否正确 list 列出所有的release package 将chart目录打包成chart压缩包 plugin 增加、列出、或删除Helm的插件 repo 增加、列出、删除、更新和索引chart 仓库 reset 从集群中卸载Tiller rollback 回滚release到前一个版本 search 在charts中根据关键字搜索 serve 启动一个本地的http web 服务器 status 显示指定release的状态 template 本地模板渲染 test 测试release upgrade 升级release verify 验证给定路径上的chart是否已签名且有效的 version 输出服务端和客户端版本信息 Flags: --debug 启用详细输出 -h, --help 输出Helm的帮助信息 --home string 指定Helm配置文件的位置,用于覆盖$HELM_HOME (默认是 "/root/.helm") --host string 指定Tiller地址,用于覆盖$HELM_HOST --kube-context string 指定要使用的kubeconfig上下文的名称 --kubeconfig string 指定要使用的kubeconfig文件的绝对路径 --tiller-connection-timeout int 指定Helm将等待建立与tiller的连接持续时间(秒)(默认为300) --tiller-namespace string 指定Tiller的namespace (默认是 "kube-system") 使用 "helm [command] --help" 来输出更多关于该指令的信息
Helm使用称为chart的包装格式。chart是描述相关的一组Kubernetes资源的文件集合。 单个chart可能用于部署简单pod,或者一些复杂的应用程序堆栈。 chart通过创建为特定目录树文件,将它们打包到版本化的压缩包,然后进行部署。 Chart.yaml apiVersion: chart API 版本, 总是 "v1" (必选) name: chart的名字 (必选) version: SemVer 2 版本 (必选) kubeVersion: A SemVer range of compatible Kubernetes versions (可选) description: A single-sentence description of this project (可选) keywords: - A list of keywords about this project (可选) home: The URL of this project's home page (可选) sources: - A list of URLs to source code for this project (可选) maintainers: # (可选) - name: The maintainer's name (每个维护者必选) email: The maintainer's email (每个维护者可选) url: A URL for the maintainer (每个维护者可选) engine: gotpl # The name of the template engine (可选, 默认是gotpl) icon: A URL to an SVG or PNG image to be used as an icon (可选). appVersion: The version of the app that this contains (可选). This needn't be SemVer. deprecated: Whether this chart is deprecated (可选, boolean) tillerVersion: The version of Tiller that this chart requires. This should be expressed as a SemVer range: ">2.0.0" (可选) 通过charts/目录手动管理依赖性 通过将依赖的charts复制到charts/目录来明确表达这些依赖关系。 依赖关系可以是这些chart归档(foo-1.2.3.tgz)或解压缩的chart目录(但它的名字不能从_或.开始,这些文件会被chart加载器忽略)。 例如,WordPress chart依赖Apache chart和MySQL chart,则在WordPress chart的charts/目录中提供如下charts: wordpress: Chart.yaml # ... charts/ apache/ Chart.yaml检查依赖和模板配置是否正确 # ... mysql/ Chart.yaml # ... # 将依赖项放入charts目录,使用 helm fetch 命令 使用依赖关系的操作方面影响 指定chart依赖关系后,如何影响使用helm intall和helm upgrade的chart安装? 假设,名为“A”的chart创建以下kubernetes对象: namespace:A-Namespace StatefulSet:A-StatefulSet service:A-Service 同时,A依赖于创建对象的chart “B”: namespace:B-Namespace deployment:B-Deployment service:B-Service 安装/升级chart A之后,会创建/修改单个Helm版本。该版本会按照以下顺序创建/更新所有上述kubernetes对象: A-Namespace B-Namespace A-StatefulSet B-Deployment A-Service B-Service 因为,当helm安装/升级chart时,chart中的kubernetes对象及其所有依赖项都是: 聚合成一个单一的集合 按类型排序 按名称排序 按上述顺序创建/更新。 单个release是使用chart及其依赖关系创建的所有对象。(kubernetes类型的安装顺序由kind_sorter.go中InstallOrder给出) // InstallOrder是清单的安装顺序(按种类) // 列表中较早出现的那些对象将在列表中较晚出现的那些对象之前安装 var InstallOrder KindSortOrder = []string{ "Namespace", "NetworkPolicy", "ResourceQuota", "LimitRange", "PodSecurityPolicy", "PodDisruptionBudget", "Secret", "ConfigMap", "StorageClass", "PersistentVolume", "PersistentVolumeClaim", "ServiceAccount", "CustomResourceDefinition", "ClusterRole", "ClusterRoleList", "ClusterRoleBinding", "ClusterRoleBindingList", "Role", "RoleList", "RoleBinding", "RoleBindingList", "Service", "DaemonSet", "Pod", "ReplicationController", "ReplicaSet", "Deployment", "HorizontalPodAutoscaler", "StatefulSet", "Job", "CronJob", "Ingress", "APIService", } 模板Template和值Values Helm chart模板使用Go模板语言Go template language编写,其中添加了来自Sprig库的50个左右的附加模板函数以及一些专用函数。 所有模板文件都存储在chart的templates/目录下。当Helm渲染charts时,它将通过模板引擎渲染传递该目录中的每个文件。 模板的值有两种提供方式: chart开发人员可能会在chart内部提供一个values.yaml文件,该文件可以包含默认值。 chart用户可能会提供(使用命令helm install -f)一个包含值的YAML文件。当用户提供自定义值时,这些值将覆盖chart中values.yaml文件中的值。 预定义值 通过values.yaml文件(或通过--set标志)提供的值,可以从.Values模板中的对象访问。也可以在模板中访问其他预定义的数据片段。 以下值是预定义的,可以用于每个模板,并且不能被覆盖。与所有值一样,名称区分大小写: 内置对象 描述 Release release本身 Release.Name release的名称(不是chart的名称) Release.Time chart版本上次更新的时候。这将匹配Last Released发布对象上的时间 Release.Namespace release 发布的 namespace Release.Service 处理 release 的服务。始终是Tiller Release.Revision release的修订版本号。它从 1 开始,并随着你每次helm upgrade增加 Release.IsUpgrade 如果当前操作是升级或回滚,则设置为 true Release.IsInstall 如果当前操作是安装,则设置为 true Values 从 values.yaml 文件和用户提供的文件传入模板的值。默认情况下,Values 是空的。 Chart Chart.yaml 的内容。chart 版本可以从 Chart.Version 和维护人员 Chart.Maintainers一起获得 Files 这提供对 chart 中所有非特殊文件的访问。虽然无法使用它来访问模板,但可以使用它来访问 chart 中的其他文件(除非它们被排除使用 .helmignore) Files.Get/Files.GetString 是按名称获取文件的函数(.Files.Get ”file name“) Files.GetBytes 是将文件内容作为字节数组而不是字符串获取的函数。这对于像图片这样的东西很有用 Capabilities 提供关于 Kubernetes 集群支持的功能的信息 Capabilities.APIVersions 是一组版本信息 Capabilities.APIVersions.Has $version 指示是否在群集上启用版本(例如 batch/v1) Capabilities.KubeVersion 提供查找 Kubernetes 版本的方法。具有以下值:Major,Minor,GitVersion,GitCommit,GitTreeState,BuildDate,GoVersion,Compiler,Platform Capabilities.TillerVersion 提供查找 Tiller 版本的方法。具有以下值:SemVer,GitCommit,GitTreeState Template 包含有关正在执行的当前模板的信息 Name 到当前模板的 namespace 文件路径(例如 mychart/templates/mytemplate.yaml) BasePath 当前 chart 模板目录的 namespace 路径(例如 mychart/templates) 这些值可用于任何顶级模板。这并不意味着它们将在任何地方都要有。 注意,任何位置的chart.yaml字段将被删除,不会在chart对象内部被访问。因此,chart.yaml不能用于将任意结构化的数据传递到模板中,values.yaml文件可以用于传递。 值 values.yaml文件 Values的内容来源有四个地方: chart中的values.yaml文件 子chart来自父chart的values.yaml文件 value文件通过helm install或helm upgrade的-f标志运行用户提供的YAML值 通过--set标志运行用户提供的YAML值 上述四个来源优先级逐渐升高,高优先级会覆盖低优先级。 helm install --values=myvals.yaml wordpress # 以这种方式传递值时,将被合并到默认values文件中 # values.yaml文件的内容 imageRegistry: "quay.io/deis" dockerTag: "latest" pullPolicy: "Always" storage: "s3" # myvals.yaml文件的内容 storage: "gcs" # 执行上述命令合并后的values.yaml文件 imageRegistry: "quay.io/deis" dockerTag: "latest" pullPolicy: "Always" storage: "gcs" # 注意,最后一个字段被覆盖了,其他的不变 注意: 包含在charts中的默认values文件的名称必须为values.yaml,在命令行上指定的文件可以被指明为任何名称 如果在helm install或helm upgrade 使用--set,这些值仅在客户端转换为YAML 删除默认key 如果您需要从默认值中删除一个键,可以覆盖该键的值为 null,在这种情况下,Helm 将从覆盖值合并中删除该键。 如,stable 版本的 Drupal chart 允许配置 liveness 探测器,如果配置自定义的 image。以下是默认值: livenessProbe: httpGet: path: /user/login port: http initialDelaySeconds: 120 如果尝试覆盖 liveness Probe 处理程序 exec 而不是 httpGet,使用 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt],Helm 会将默认和重写的键合并在一起,从而产生以下 YAML: livenessProbe: httpGet: path: /user/login port: http exec: command: - cat - docroot/CHANGELOG.txt initialDelaySeconds: 120 但是,Kubernetes 会报错,因为无法声明多个 liveness Probe 处理程序。为了克服这个问题,可以指示 Helm 将 livenessProbe.httpGet 设置为空来删除它: helm install stable/drupal --set image=my-registry/drupal:0.1.0 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null 范围scope、依赖dependencies、值values values.yaml文件可以声明顶级chart的值,也可以为chart的charts/目录中包含的任何chart声明值,即values可以为chart及其任何依赖项提供值。 例如,WordPress依赖Apache和MySQL,values文件可以为所有这些组件提供值: title: "My WordPress Site" # Sent to the WordPress template mysql: max_connections: 100 # Sent to MySQL password: "secret" apache: port: 8080 # Passed to Apache 高级别的chart可以访问下面定义的所有变量:所以WordPress chart可以访问MySQL密码.Values.mysql.password 低级别的chart无法访问父级别chart中的内容:所有MySQL无法访问title属性,也无法访问.Values.apache.port 全局值 从2.0.0-Alpha.2 开始,Helm 支持特殊的 “全局” 值: title: "My WordPress Site" # Sent to the WordPress template global: # 新增加的字段,为全局值 app: MyWordPress # 此值可供所有chart使用 .Values.global.app mysql: max_connections: 100 # Sent to MySQL password: "secret" apache: port: 8080 # Passed to Apache 这提供了一种与所有子chart共享一个顶级变量的方法,这对设置metadata中像标签这样的属性很有用。 如果子chart声明了一个全局变量,则该全局将向下传递(到子chart的子chart),但不向上传递到父chart(子chart无法影响到父chart的值)。 父chart的全局变量优先于子chart中的全局变量。 当涉及到编写模板和values文件时,可以参考以下几个标准: Go templates The YAML format chart起始包 helm create命令采用可选--starter选项,可以指定起始chart。 起始chart只是普通的chart,位于$HELM_HOME/starters。作为chart开发人员,可以创作专门设计用作起始chart。设计这些chart时应考虑以下因素: chart.yaml将被生成器覆盖 用户将期望修改这样的chart内容,因此文档应该指出用户如何做到这一点 所有templates目录下的匹配项<CHARTNAME>将被替换为指定的chart名称,以便起始chart可用作模板,另外,values.yaml的<CHARTNAME>也会被替换 目前添加chart的唯一方法是手动将其复制到$HELM_HOME/starters,在chart文档中,需要解释该过程
前置条件 在编写chart的环境下安装一个Helm客户端,具体步骤参照Helm安装部分。 自定义chart Helm相关的命令查看Helm命令部分。 helm create portal # portal为chart的名字 tree portal portal/ ├── charts # 依赖目录,此chart依赖的任何其他charts ├── Chart.yaml # 此chart的YAML文件 ├── templates # 模板目录,当与值组合时,将生成有效的kubernetes manifest文件 │ ├── deployment.yaml # kubernetes Deployment │ ├── _helpers.tpl # 用于修改要生成的kubernetes对象配置的模板,可被被整个chart复用,模板partials默认位置 │ ├── ingress.yaml # kubernetes Ingress │ ├── NOTES.txt # 包含使用说明的纯文本文件,也可用模板生成其中的内容 │ ├── service.yaml # kubernetes Service │ └── tests # 测试chart是否如预期运行 │ └── test-connection.yaml # 可编写多个测试文件,或在一个文件中编写多个测试pod └── values.yaml # 此chart的默认配置值的YAML文件,声明的变量会被传递到templates中 3 directories, 8 files # .helmignore文件,构建包时要忽略的模式,每行一个模式,支持shell全局匹配,相对路径匹配和否定(前缀为!) # 所有需要的kubernetes对象都可以在templates文件夹中创建 .helmignore例子: # comment .git */temp* */*/temp* temp? NOTES.txt例子: 在 chart install 或 chart upgrade 结束时,Helm 可以为用户打印出一大堆有用的信息。这些信息是使用模板高度定制的。这个文件是纯文本的,但是它像一个模板一样处理,并且具有所有可用的普通模板函数和对象。 Thank you for installing {{ .Chart.Name }}. Your release is named {{ .Release.Name }}. To learn more about the release, try: helm status {{ .Release.Name }} helm get {{ .Release.Name }} 模板 templates目录下是yaml文件的模板,遵循Go template语法。 deployment.yaml 文件的内容如下: apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "portal.fullname" . }} labels: {{ include "portal.labels" . | indent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: app.kubernetes.io/name: {{ include "portal.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: labels: app.kubernetes.io/name: {{ include "portal.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} containers: - name: {{ .Chart.Name }} image: {{ .Values.image.repository }}:{{ .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: 80 protocol: TCP livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: / port: http resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }} 其中,双大括号括起来的部分是Go template,其中的Values是values.yaml文件中定义的。 values.yaml文件的内容如下所示: # Default values for portal. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: nginx tag: stable pullPolicy: IfNotPresent imagePullSecrets: [] nameOverride: "" fullnameOverride: "" service: type: ClusterIP port: 80 ingress: enabled: false annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: [] tls: [] # - secretName: chart-example-tls # hosts: # - chart-example.local resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: # cpu: 100m # memory: 128Mi # requests: # cpu: 100m # memory: 128Mi nodeSelector: {} tolerations: [] affinity: {} 举个例子,比如deployment的镜像: # deployment.yaml image: {{ .Values.image.repository }}:{{ .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} # values.yaml image: repository: nginx tag: stable pullPolicy: IfNotPresent 都是一一对应的关系,根据实际的应用需要进行修改对应的values.yaml中的值。 其中.Values.image.repository表示从顶层命名空间开始,先找到Values,然后在里面找到image对象,在image中找到repository对象。用dot(.)来分隔每一个namespace。 Helm中除了有Values.yaml文件,还有内置的对象。 验证chart 使用helm部署kubernetes的应用的时候,实际上是将templates渲染成最终的kubernetes能够识别的yaml格式。 在部署之前可以使用helm install --dry-run --debug <chart-dir>命令来验证chart配置,该输出中包含了模板的变量配置和最终渲染的yaml文件。 该命令还是会向Tiller服务器请求一个round-trip。 输出内容如下: [debug] Created tunnel using local port: '44431' [debug] SERVER: "127.0.0.1:44431" [debug] Original chart version: "" [debug] CHART PATH: /home/sugoi/文档/Helm/Chart/portal NAME: independent-camel REVISION: 1 RELEASED: Mon Jun 3 18:12:22 2019 CHART: portal-0.1.0 USER-SUPPLIED VALUES: {} COMPUTED VALUES: affinity: {} fullnameOverride: "" image: pullPolicy: IfNotPresent repository: nginx tag: stable imagePullSecrets: [] ingress: annotations: {} enabled: false hosts: - host: chart-example.local paths: [] tls: [] nameOverride: "" nodeSelector: {} replicaCount: 1 resources: {} service: port: 80 type: ClusterIP tolerations: [] HOOKS: --- # independent-camel-portal-test-connection apiVersion: v1 kind: Pod metadata: name: "independent-camel-portal-test-connection" labels: app.kubernetes.io/name: portal helm.sh/chart: portal-0.1.0 app.kubernetes.io/instance: independent-camel app.kubernetes.io/version: "1.0" app.kubernetes.io/managed-by: Tiller annotations: "helm.sh/hook": test-success spec: containers: - name: wget image: busybox command: ['wget'] args: ['independent-camel-portal:80'] restartPolicy: Never MANIFEST: --- # Source: portal/templates/service.yaml apiVersion: v1 kind: Service metadata: name: independent-camel-portal labels: app.kubernetes.io/name: portal helm.sh/chart: portal-0.1.0 app.kubernetes.io/instance: independent-camel app.kubernetes.io/version: "1.0" app.kubernetes.io/managed-by: Tiller spec: type: ClusterIP ports: - port: 80 targetPort: http protocol: TCP name: http selector: app.kubernetes.io/name: portal app.kubernetes.io/instance: independent-camel --- # Source: portal/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: independent-camel-portal labels: app.kubernetes.io/name: portal helm.sh/chart: portal-0.1.0 app.kubernetes.io/instance: independent-camel app.kubernetes.io/version: "1.0" app.kubernetes.io/managed-by: Tiller spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: portal app.kubernetes.io/instance: independent-camel template: metadata: labels: app.kubernetes.io/name: portal app.kubernetes.io/instance: independent-camel spec: containers: - name: portal image: "nginx:stable" imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 protocol: TCP livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: / port: http resources: {} 可以看到Deployment和Service的名字的前半部分是两个随机单词,后半部分是values.yaml中配置的值。 安装chart 有5种不同的方式来表达要安装chart到kubernetes集群中: 通过chart的引用: helm install stable/mariadb 通过chart包的路径: helm install ./nginx-1.2.3.tgz 通过一个解压后的chart包的路径: helm install ./nginx 通过绝对的URL: helm install https://example.com/charts/nginx-1.2.3.tgz 通过chart引用和repo的url: helm install --repo https://example.com/charts/nginx 打包chart 修改chart.yaml中的helm chart配置信息,然后使用如下命令将chart打成压缩文件: helm package . # 打包出portal-0.1.0.tgz 依赖 新版已经没有requirement.yaml文件,所有的依赖都在charts文件夹中,使用helm lint 命令可以和检查依赖和模板配置是否正确。
Helm 提供了一个 hook 机制,允许在 release 的生命周期中的某些点进行干预。例如,可以使用 hooks 进行如下操作: 在加载任何其他 chart 之前加载 ConfigMap 或 Secret。 在安装新 chart 之前执行job1以备份数据库,在升级完成后执行job2以恢复数据。 在删除 release 之前执行job,以便在删除 release 之前优雅地停止服务。 Hooks 像常规模板一样工作,但它们具有特殊的注释,可以使 Helm 以不同的方式使用它们。 可用的Hooks 功能 参数 描述 安装前 pre-install 在模板渲染后,资源加载到Kubernetes前,执行 安装后 post-install 在所有资源加载到Kubernetes后,执行 删除前 pre-delete 删除任何release资源前,执行 删除后 post-delete 删除所有release资源后,执行 升级前 pre-upgrade 在模板渲染后,资源加载到Kubernetes前,执行 升级后 post-upgrade 在所有资源加载到Kubernetes后,执行 回滚前 pre-rollback 在渲染模板后,资源加载到Kubernetes前,执行 回滚后 post-rollback 在所有资源加载到Kubernetes后,执行 Hooks与release生命周期 Hooks 让 chart 开发人员有机会在 release 的生命周期中的关键点执行操作。 默认情况下,release-a的生命周期如下: 用户运行helm install chart-a chart-a被加载到Tiller中 经过验证后,Tiller渲染chart-a中的模板 Tiller将渲染后产生的YAML文件加载到Kubernetes中 Tiller将release-a名称和其他数据返回给客户端 客户端接收数据并退出 假设在上述release-a的生命周期中定义两个hook:pre-install和post-install,新的生命周期如下: 用户运行helm install chart-a chart-a被加载到Tiller中 经过验证后,Tiller渲染chart-a中的模板 Tiller准备执行pre-install hook (将hook资源加载到kubernetes中) Tiller根据权重(默认分配权重为0)对hook进行排序,相同权重hook按升序排列 Tiller加载最低权重的hook(从负到正) Tiller等待直到hook操作完成(如果设置--wait标志,Tiller等待直到所有资源都处于就绪状态,并且在准备就绪前不会运行post-install hook) Tiller将渲染后产生的YAML文件加载到Kubernetes中 Tiller执行post-install hook(将hook资源加载到kubernetes中) Tiller根据权重(默认分配权重为0)对hook进行排序,相同权重hook按升序排 Tiller加载最低权重的hook(从负到正) Tiller等待直到hook操作完成 Tiller将release-a名称和其他数据返回给客户端 客户端接收数据并退出 加粗的步骤为执行hook操作的步骤。添加Hook权重是比较好的做法,如果权重不重要则设置为0,默认也为0。 等到 hook 准备就绪取决于在 hook 中声明的资源: 如果资源是 Job,Tiller 将等到作业成功完成。如果作业失败,则发布失败。这是一个阻塞操作,所以 Helm 客户端会在 Job 运行时暂停。 对于其他类型,只要 Kubernetes 将资源标记为加载(添加或更新),资源就被视为 “就绪”。当一个 hook 声明了很多资源时,这些资源将被串行执行。如果有 hook 权重,按照加权顺序执行。否则,顺序不被保证(在 Helm 2.3.0 及之后的版本中,按字母顺序排列)。 注意 Hook创建的资源不作为release的一部分进行跟踪或管理。一旦Tiller验证Hook已经达到其就绪状态,它将Hook资源放在一边。 这意味着在 Hook 中创建资源,则不能依赖于 helm delete 删除资源。要销毁这些资源,需要编写代码在 pre-delete 或 post-delete Hook中执行此操作,或者将 "helm.sh/hook-delete-policy" 注释添加到 Hook 模板文件。 例子 Hook也是Kubernetes的manifest文件,只是在metadata部分有特殊注释 。Hook也是模板文件,可以使用模板的所有功能,包括读取 .Values,.Release 和 .Template。 创建一个Hook文件存放在 templates/post-install-job.yaml文件中,将其声明为在post-install阶段运行: apiVersion: batch/v1 kind: Job metadata: name: {{.Release.Name}} labels: app.kubernetes.io/managed-by: {{.Release.Service | quote}} app.kubernetes.io/instance: {{.Release.Name | quote}} helm.sh/chart: {{.Chart.Name}}-{{.Chart.Version}} annotations: # 在这里添加注释,将资源定义为Hook,没有这些注释,这个资源将被认为是release的一部分 "helm.sh/hook": post-install,post-upgrade # 部署为多个Hook "helm.sh/hook-weight": "-5" # 设置Hook的权重,确定执行顺序, # 权重可以是正数或负数,但必须表示为字符串 "helm.sh/hook-delete-policy": hook-succeeded # 定义删除Hook资源的时间和策略 # hook-succeeded:在执行成功后删除hook # hook-failed:在执行失败后删除hook # before-hook-creation:创建新hook之前删除旧hook "helm.sh/resource-policy": keep # 指示Tiller在helm delete操作过程中跳过此资源(将变成孤儿) spec: template: metadata: name: "{{.Release.Name}}" labels: app.kubernetes.io/managed-by: {{.Release.Service | quote}} app.kubernetes.io/instance: {{.Release.Name | quote}} helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" spec: restartPolicy: Never containers: - name: post-install-job image: "alpine:3.3" command: ["/bin/sleep","{{default 10 .Values.sleepyTime}}"] 实现一个给定的Hook的不同种类资源数量没有限制。例如,可以将secret和configmap声明为per-install Hook。 子chart声明Hook时,Tiller也会渲染这些Hook。顶级chart无法禁用子chart所声明的Hook。
Helm使用Go模板 Go Template 来生成模板和资源文件。虽然Go提供了几个内置函数,但是Helm还添加了其他的函数。 在 Sprig库 中添加了几乎所有的函数。出于安全考虑,删除了env和expandenv这两个函数,这两个函数让chart作者能访问Tiller的环境变量,同时新增两个特殊的模板函数:include和required。 模板函数 模板函数遵循语法functionName arg1 arg2 ...,调用函数并将参数传递给它。常用的函数包括: define:在模板中声明一个新的命名模板 template:允许引入另一个命名模板 include:允许引入另一个命名模板,然后将结果传递给其他函数 required:声明必须的值条目 tpl:将字符串计算为模板内的模板 default:设置默认值 block:声明一个特殊的可填写模板区域(提供一个默认的实现,后续将被覆盖)【不推荐使用】 命名模板(也称为子模板)是限定在一个文件内部的模板,并给它起一个名称。 命名模板的模板名称是全局的。如果声明两个具有相同名称的模板,则最后加载的那个模板是起作用的模板。由于子 chart 中的模板与顶级模板一起编译,因此注意小心地使用特定 chart 的名称来命名模板。 通用的命名约定是为每个定义的模板添加 chart 名称:{{define "mychart.labels"}}。通过使用特定 chart 名称作为前缀,可以避免由于同名模板的两个不同 chart 而可能出现的任何冲突。 define 该 define 操作允许我们在模板文件内创建一个命名模板。它的语法如下所示: {{ define "MY.NAME" }} # body of template here {{ end }} # 例如,可以定义一个模板来封装一个 Kubernetes 标签块 {{- define "mychart.labels" }} labels: generator: helm date: {{ now | htmlDate }} {{- end }} Helm chart 通常将define创建的这些命名模板放入 partials 文件中,通常是helpers.tpl。按照惯例,define 函数应该有一个简单的文档块({{/* ... */}})来描述它所做的事情。 设置命名模板范围 上面定义的命名模板中,没有使用任何对象,只是使用函数。现在将命名模板修改为如下形式,以包含 chart 名称和 chart 版本: {{/* Generate basic labels */}} {{- define "mychart.labels" }} labels: generator: helm date: {{ now | htmlDate }} chart: {{ .Chart.Name }} version: {{ .Chart.Version }} {{- end }} # 这将不会得到我们所期望的结果 # Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: moldy-jaguar-configmap labels: generator: helm date: 2016-11-02 chart: # 未获取到Chart.Name version: # 未获取到Chart.Version # 上述两个值,不在我们定义的模板的范围内 当一个命名模板被渲染时,它将接收由该template调用传入的作用域。 {{- template "mychart.labels" }} # 没有范围被传入,因此在模板中无法访问任何内容 # 将范围传递给模板 apiVersion: v1 kind: ConfigMap metadata: name: {{ .Release.Name }}-configmap {{- template "mychart.labels" . }} # 在template末尾传递了`.` # `.`表示顶级范围,`.Values`范围,`.Values.favorite`范围 添加范围后,就可以得到正确的值。 template {{template "name"}} // 执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为"" {{template "name" pipeline}} // 执行名为name的模板,提供给模板的参数为pipeline的值# 调用上述define中定义的命名模板 apiVersion: v1 kind: ConfigMap metadata: name: {{ .Release.Name }}-configmap {{- template "mychart.labels" }} data: myvalue: "Hello World" {{- range $key, $val := .Values.favorite }} {{ $key }}: {{ $val | quote }} {{- end }} 即使这个定义在_helpers.tpl,它仍然可以在 configmap.yaml 以下位置访问,因为模板名称是全局的 。 include 虽然Go提供了template函数,能够将一个模板包含在另一个模板中,但是Go的template函数中不能使用管道来传递值到其他内置的函数中。 为了能够引入模板,然后对该模板的输出执行操作,Helm提供了include函数允许引入另一个模板,然后将结果传递给其他函数模板。 // 调用模板 mysql.labels,然后将结果缩进四个空格(在函数中使用管道来进行值的传递),其中 `.` 表示渲染的根对象 {{ include "mysql.labels" . | indent 4 }} // 调用模板 toYaml,渲染$value的内容,然后将渲染的输出传递给 indent 函数 {{- include "toYaml" $value | nindent 2}} 因为YAML的缩进级别和空白很重要,所以使用indent函数是包含代码片段的好方法,并在相关的上下文中处理缩进。 required required 函数允许根据模板渲染的要求声明特定的值条目。如果values.yaml中该条目值为空,则模板渲染将失败并显示用户提交的错误消息。 // 声明 .Values.who 条目是必需的,并且在缺少该条目(即未定义Values.who)时将显示错误消息 value: {{required "A valid .Values.who entry required!" .Values.who}} tpl tpl 函数允许开发人员将字符串计算为模板内的模板。 这对于将模板字符串作为值传递给 chart 或渲染外部配置文件很有用。 语法: {{tpl TEMPLATE_STRING VALUES}} // 模板字符串 values.yaml文件 template: "{{.Values.name}}" name: "Tom" // template {{tpl .Values.template .}} // output Tom // 外部配置文件 conf/app.conf firstName={{.Values.firstName}} lastName={{.Values.lastName}} // values.yaml文件 firstName: Peter lastName: Parkerk // template {{tpl (.Files.Get "conf/app.conf") . }} // output firstName=Peter lastName=Parker default default函数,如:default DEFAULT_VALUE GIVEN_VALUE。该功能允许在模板内部指定默认值,以防该值被省略。 {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 在实际的 chart 中: 所有静态默认值应该存在于 values.yaml 中,不应该使用 default 命令重复(否则它们将是重复多余的)。 但是,default 命令对于计算的值是合适的,因为计算值不能在 values.yaml 中声明。 运算符函数 对于模板,运算符(eq,ne,lt,gt,and,or 等等)都是已实现的功能。在管道中,运算符可以用圆括号((和))分组。 将运算符放到声明的前面,后面跟着它的参数,就像使用函数一样。要多个运算符一起使用,将每个函数通过圆括号分隔。
下面是一个action(动作)的列表。"Arguments"和"pipelines"代表数据的执行结果,细节定义在后面。 注释 {{/* a comment */}} // 注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止,就像这里表示的一样。 管道 模板语言的强大功能之一是其管道概念。利用 UNIX 的概念,管道是一个链接在一起的一系列模板命令的工具,以紧凑地表达一系列转换。管道是按顺序完成几件事情的有效方式。 {{pipeline}} // pipeline的值的默认文本表示会被拷贝到输出里 Pipeline是一个(可能是链状的)command序列。Command可以是一个简单值(argument)或者对函数或者方法的(可以有多个参数的)调用: Argument 执行结果是argument的执行结果; .Method [Argument...] 方法可以独立调用或者位于链式调用的末端,不同于链式调用中间的方法,可以使用参数; 执行结果是使用给出的参数调用该方法的返回值:dot.Method(Argument1, etc.); functionName [Argument...] 执行结果是使用给定的参数调用函数名指定的函数的返回值:function(Argument1, etc.); pipeline通常是将一个command序列分割开,再使用管道符'|'连接起来(但不使用管道符的command序列也可以视为一个管道)。在一个链式的pipeline里,每个command的结果都作为下一个command的最后一个参数。pipeline最后一个command的输出作为整个管道执行的结果。 command的输出可以是1到2个值,如果是2个后一个必须是error接口类型。如果error类型返回值非nil,模板执行会中止并将该错误返回给执行模板的调用者。 如果值为如下情况,则pipeline为 false: 一个布尔型的false 一个数字零 一个空的字符串 一个 nil(空或 null) 一个空的集合(map,slice,tuple,dict,array) 在其他情况下,条件值为true,此管道被执行。 控制结构 控制结构(模板说法中称为 “动作”)为编写模板提供了控制模板生成流程的能力。Helm 的模板语言提供了以下控制结构: if/else:用于创建条件块 with:指定范围 range:提供一个“for each“风格的循环 控制结构可以执行整个管道,而不是一个值。 if/else {{if pipeline}} // T0 {{else if pipeline}} // T1 {{else}} // T2 {{end}} // pipeline的执行,不改变dot的值 with with控制着变量作用域,.是对当前范围的引用,即.Values是告诉模板,在当前范围内查找Values对象。 {{with pipeline}} // 限定的范围 {{end}} // 修改当前dot,不修改外面的dot // 请注意!在受限范围内,此时将无法从父范围访问其他对象。{{with pipeline}} T1 {{else}} T0 {{end}} // 如果pipeline为空,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。 循环range 在Helm模板中,遍历集合(数组、切片、字典或者通道)的方式是使用range操作。 如果pipeline的值其长度为0,不会有任何输出; 否则dot依次设为数组、切片、字典或者通道的每一个成员元素并执行pipeline; 如果pipeline的值为字典,且键是可排序的基本类型,元素也会按键的顺序排序。 {{range pipeline}} T1 {{else}} T0 {{end}} // 不改变dot的值
参数(Argument) 参数代表一个简单的,由下面的某一条表示的值: 类型 描述 结果值 go基本类型 go语法的布尔值、字符串(不能跨行)、字符、整数、浮点数、虚数、复数 视为无类型字面常数 关键字nil 代表一个go的无类型的nil值 nil 字符. 代表dot的值 代表当前值 变量名 以美元符号起始加上(可为空的)字母和数字构成的字符串,$piOver2和$ 执行结果为变量的值 结构体数据的字段名 以句点起始,如.Field 执行结果为字段的值 .Field1.Field2(链式调用) 执行结果为字段的值 字段也可以在变量上使用,$x.Field1.Field2(链式调用) 执行结果为字段的值 字典类型数据的键名 以句点起始,如.Key 执行结果为该键在字典中对应的成员元素的值 键也可以和字段配合做链式调用,深度不限:.Field1.Key1.Field2.Key2(键不需要以大写字母开始) 执行结果为该键在字典中对应的成员元素的值 键也可以用于变量:$x.key1.key2(链式调用) 执行结果为该键在字典中对应的成员元素的值 数据的无参数方法名 以句点为起始,如:.Method 执行结果为dot调用该方法的返回值,dot.Method()(该方法必须有1到2个返回值,如果有2个则后一个必须是error接口类,如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误) 方法可和字段、键配合做链式调用,深度不限:.Field1.Key1.Method1.Field2.Key2.Method2 方法也可以在变量上使用:$x.Method1.Field(链式调用) 无参数的函数名 如:fun 执行结果是调用该函数的返回值fun()(对返回值的要求和方法一样) 上面某一条的实例加上括弧 用于分组 执行结果可以访问其字段或者键对应的值 print (.F1 arg1) (.F2 arg2)(.StructValuedMethod "arg").Field Arguments可以是任何类型: 如果是指针,在必要时会自动表示为指针指向的值; 如果执行结果生成了一个函数类型的值,如结构体的函数类型字段,该函数不会自动调用,但可以在if等action里视为真。 如果要调用它,使用call函数。 变量 Action里可以初始化一个变量来捕获管道的执行结果。初始化语法如下: $variable := pipeline // 其中$variable是变量的名字。声明变量的action不会产生任何输出 如果"range" action初始化了1个变量,该变量设置为迭代器的每一个成员元素 如果初始化了逗号分隔的2个变量 range $index, $element := pipeline // 这时,$index和$element分别设置为数组/切片的索引或者字典的键,以及对应的成员元素 // 注意这和go range从句只有一个参数时设置为索引/键不同 一个变量的作用域只到声明它的控制结构("if"、"with"、"range")的"end"为止 如果不是在控制结构里声明会直到模板结束为止 子模板的调用不会从调用它的位置(作用域)继承变量 模板开始执行时,$会设置为传递给Execute方法的参数,就是说,dot的初始值。
创建命名模板并通过函数访问这些命名模板,可以很容易地从一个模板中导入另一个模板。但有时需要导入的不是模板而是文件,并注入其内容而不通过模板渲染器发送内容。 Helm 通过.Files对象提供对文件的访问。该对象有几个注意点: 向Helm Chart添加额外的文件是可以的,这些文件将被捆绑并发送给Tiller 由于Kubernetes对象的存储限制,Chart必须小于1M 出于安全原因,.Files对象不能访问:1. templates/目录下的内容,2. .helmignore排除的文件 chart不保留UNIX模式信息,因此文件级权限在涉及.Files对象时不会影响文件的可用性 示例 将三个文件添加到 chart 中,将所有三个文件直接放在 mychart/ 目录中。 config1.toml: message = Hello from config 1 config2.toml: message = This is config 2 config3.toml: message = Goodbye from config 3 使用range函数来遍历它们并将它们的内容注入到ConfigMap中。 apiVersion: v1 kind: ConfigMap metadata: name: {{.Release.Name}}-configmap data: {{- $files := .Files}} {{- range tuple "config1.toml" "config2.toml" "config3.toml"}} {{.}}: |- {{$files.Get .}} {{- end}} 创建一个 $files 变量来保存 .Files 对象的引用 使用tuple函数来创建循环访问的文件列表 打印每个文件名({{.}}: |-) 打印文件的内容 {{ $files.Get . }} 运行这个模板将产生一个包含所有三个文件内容的 ConfigMap: # Source: mychart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: quieting-giraf-configmap data: config1.toml: |- message = Hello from config 1 config2.toml: |- message = This is config 2 config3.toml: |- message = Goodbye from config 3 路径 在处理文件时,对文件路径本身执行一些标准操作会非常有用。Helm 从 Go 的 path 包中导入了许多函数供使用。它们都可以使用 Go 包中的相同名称访问,但使用时小写第一个字母,例如,Base 变成 base等等。导入的功能是: Base Dir Ext IsAbs Clean Glob 模式 随着 chart 的增长,可能会发现需要组织更多地文件,可以使用 Files.Glob(pattern string) 方法通过具有灵活性的模式 glob patterns 协助提取文件。 .Glob 返回一个 Files 类型,所以可以调用 Files 返回对象的任何方法。 例如,目录结构如下: foo/: foo.txt foo.yaml bar/: bar.go bar.conf baz.yaml Glob 有多个方法可选择: {{$root := .}} {{range $path, $bytes := .Files.Glob "**.yaml"}} {{$path}}: |- {{$root.Files.Get $path}} {{end}} # 或 {{range $path, $bytes := .Files.Glob "foo/*"}} {{$path.base}}: '{{ $root.Files.Get $path | b64enc }}' {{end}}foo/: foo.txt foo.yaml bar/: bar.go bar.conf baz.yaml ConfigMap 和 Secrets 工具函数 不存在于 2.0.2 或更早的版本中 想要将文件内容放置到 configmap 和 secret 中非常常见,以便在运行时安装到 pod 中。为了解决这个问题,Files 类型上有一些实用的方法。 为了进一步组织文件,将这些方法与 Glob 方法结合使用尤其有用。 根据上面的 Glob 示例中的目录结构: apiVersion: v1 kind: ConfigMap metadata: name: conf data: {{- (.Files.Glob "foo/*").AsConfig | nindent 2 }} --- apiVersion: v1 kind: Secret metadata: name: very-secret type: Opaque data: {{(.Files.Glob "bar/*").AsSecrets | nindent 2 }} 编码 可以导入一个文件,并使用 base64 对模板进行编码以确保成功传输: apiVersion: v1 kind: Secret metadata: name: {{.Release.Name}}-secret type: Opaque data: token: |- {{.Files.Get "config1.toml" | b64enc}} 行 有时需要访问模板中文件的每一行。Lines 为此提供了一种方便的方法。 data: some-file.txt: {{range .Files.Lines "foo/bar.txt"}} {{.}}{{ end }} 目前,无法在helm install期间将外部文件传递给 chart。因此,如果要求用户提供数据,则必须使用 helm install -f 或进行加载 helm install --set。
chart有称为子chart的依赖关系,它们也有自己的值和模板。关于子chart的注意点: 子chart被认为是“独立的”,子chart不能明确依赖于其父chart 子chart无法访问其父项的值 父chart可以覆盖子chart的值 Helm有全局值的概念,可以被所有charts访问 所有子chart都保存在父chart的charts/目录中。 每个子chart都是独立的 chart。 覆盖子chart中的值 在父chart的values.yaml文件中添加如下信息: favorite: drink: coffee food: pizza pizzaToppings: - mushrooms - cheese - peppers - onions mysubchart: # 子chart的名字,这部分以下是所有内容都会发送给子chart dessert: ice cream # 子chart的values.yaml中的值 这里有一个重要的细节需要注意:没有改变子chart模板指向 .Values.mysubchart.dessert。从该子chart模板的角度来看,该值仍位于 .Values.dessert。随着模板引擎一起传递值,它会设置范围。所以对于 mysubchart 模板,只有指定给 mysubchart 的值才会在 .Values 里。 全局chart值 全局值是可以从任何chart或子chart用完全相同的名称访问的值。全局值需要明确声明,不能像使用现有的非全局值一样来使用全局值。 values 数据类型有一个保留部分,称为 Values.global, 可以设置全局值。如下所示: favorite: drink: coffee food: pizza pizzaToppings: - mushrooms - cheese - peppers - onions mysubchart: dessert: ice cream global: # 全局值 salad: caesar 与子chart共享模板 父chart和子chart可以共享命名模板。任何chart中的任何定义块都可用于其他chart,如下所示: {{- define "labels"}} from: mychart {{ end }} # 命名模板的名字是全局共享的,任何chart都可以用
引用字符串,不要引用整数值 // 使用字符串时,引用字符串(用双引号引起来)比把它们留为空白字符更安全 name: {{.Values.MyName | quote}} // 使用整数时 不要引用整数值。否则,可能会导致Kubernetes内部的解析错误 port: {{.Values.Port}} 以上两种情况,在设置env变量值时不适用 // env变量值都需要引起来 env: - name: HOST value: "http://host" - name: PORT value: "1234" 管理空格 当模板引擎运行时,它将删除{{ }}中的空白内容,但是按原样保留剩余的空白。 换行符也是空格。 YAML中的缩进空格是严格的,因此管理空格变得非常重要。Helm模板有几个工具可以使用。 第一种: 使用特殊字符修饰模板声明的大括号语法,以告诉模板引擎填充空格。 {{- 添加破折号和空格,表示应该将格左移, -}} 添加空格和破折号,表示应该删除右空格 确保-和其他指令之间有空格 - 3 意思是删除左空格并打印3 -3 意思是打印-3 第二种: 告诉模板系统如何缩进比掌握模板指令的间距更容易,可以使用indent函数:{{ indent 2 value: "true" }}。 小心随机值生成 Helm中有一些函数允许生成随机数据、加密秘钥等,在升级过程中模板会被重新渲染,当模板运行产生与上次运行不同的数据时,将触发该资源的更新。 避免使用块 Go 模板语言提供了一个 block 关键字,允许开发人员提供一个默认的实现,后续将被覆盖。在 Helm chart 中,块不是重写的最佳工具,因为如果提供了同一个块的多个实现,那么所选哪个是不可预知的。 建议是改为使用include。 一般约定 Chart名称 Chart 名称应该是小写字母和数字组成,字母开头: drupal cert-manager oauth2-proxy # Chart 名称中不能使用大写字母和下划线。Chart 名称不应使用点。 包含 chart 的目录必须与 chart 具有相同的名称。因此,chart cert-manager 必须在名为 cert-manager/ 的目录中创建。这不仅仅是一种风格的细节,而是 Helm Chart 格式的要求。 版本号 Helm 使用 SemVer2 来表示版本号。当 SemVer 版本存储在 Kubernetes 标签中时,通常会将 + 字符更改为一个 _ 字符,因为标签不允许 + 标志作为值。 格式化YAML YAML 文件应该使用两个空格缩进(而不是制表符)。 通过版本来限制Tiller 一个 Chart.yaml 文件可以指定一个 tillerVersion SemVer 约束: name: mychart version: 0.2.0 tillerVersion: ">=2.4.0" 当模板使用 Helm 旧版本不支持的新功能时,应该设置此限制。虽然此参数将接受复杂的 SemVer 规则,但最佳做法是默认为格式 >=2.4.0,其中 2.4.0 引入了 chart 中使用的新功能的版本。 此功能是在Helm 2.4.0中引入的,因此任何2.4.0版本以下的Tiller都会忽略此字段。 Values 变量名称应该以小写字母开头,单词应该用 camelcase 分隔: # 正确写法: chicken: true chickenNoodleSoup: true # 不正确写法: Chicken: true # 可能与内置变量冲突 chicken-noodle-soup: true # 不要在变量名称中使用短横 展平或嵌套值 YAML 是一种灵活的格式,并且值可以嵌套或扁平化。 # 嵌套: server: name: nginx port: 80 # 展平: serverName: nginx serverPort: 80 # 在大多数情况下,展平应该比嵌套更受青睐,因为对模板开发人员和用户来说更简单。 为了获得最佳安全性,必须在每个级别检查嵌套值: {{if .Values.server}} {{default "none" .Values.server.name}} {{end}} # 对于每一层嵌套,都必须进行存在检查。但对于展平配置,可以跳过这些检查,使模板更易于阅读和使用。 {{default "none" .Values.serverName}} # 当有大量相关变量时,且至少有一个是非可选的,可以使用嵌套值来提高可读性。 使类型清晰 YAML 的类型强制规则有时是违反直觉的。 例如: foo: false foo: "false" foo: 12345678 :在某些情况下,大整数将被转换为科学记数法 避免类型转换错误的最简单方法是明确地表示字符串(引用所有字符串),并隐含其他所有内容。 通常,为了避免整型转换问题,最好将整型存储为字符串,并在模板中使用 {{int $value}} 将字符串转换为整数。 在大多数情况下,显式类型标签受到重视,所以 foo: !!string 1234 应该将 1234 视为一个字符串。但是,YAML 解析器消费标签,因此类型数据在解析后会丢失。 考虑用户如何使用你的 values 有几种潜在的 values 来源: chart 的 values.yaml 文件 由 helm install -f 或 helm upgrade -f 提供的 value 文件 传递给 --set 或的 --set-string 标志 helm install 或 helm upgrade 命令 通过 --set-file 将 文件内容传递给 helm install or helm upgrade 在设计 value 的结构时,请记住 chart 的用户可能希望通过 -f 标志或 --set 选项覆盖它们。 由于 --set 在表现力方面比较有限,编写 values.yaml 文件的第一个指导原则可以轻松使用 --set 覆盖。 出于这个原因,使用 map 来构建 value 文件通常会更好。 # 难以配合 --set 使用 servers: - name: foo port: 80 - name: bar port: 81 Helm <=2.4 时,以上不能用 --set 来表示。在 Helm 2.5 中,访问 foo 上的端口是 --set servers[0].port=80。用户不仅难以弄清楚,而且如果稍后 servers 改变顺序,则容易出错。 # 使用方便 servers: foo: port: 80 bar: port: 81 # 访问 foo 的端口更为方便:--set servers.foo.port=80 文档'values.yaml' 应该记录'values.yaml'中的每个定义的属性。文档字符串应该以它描述的属性的名称开始,然后至少给出一个单句描述。 # 不正确 # the host name for the webserver serverHost = example serverPort = 9191 # 正确 # serverHost is the host name for the webserver serverHost = example # serverPort is the HTTP listener port for the webserver serverPort = 9191 使用参数名称开始每个注释,它使文档易于grep,并使文档工具能够可靠地将文档字符串与其描述的参数关联起来。 templates 目录结构 templates 目录的结构应如下所示: 如果产生 YAML 输出,模板文件应该有扩展名 .yaml。扩展名.tpl可用于产生不需要格式化内容的模板文件。 模板文件名应该使用横线符号(my-example-configmap.yaml),而不是 camelcase。 每个资源定义应该在它自己的模板文件中。 模板文件名应该反映名称中的资源种类。例如 foo-pod.yaml, bar-svc.yaml 定义模板的名称 定义的模板(在 {{define}} 指令内创建的模板)可以全局访问。这意味着 chart 及其所有子 chart 都可以访问所有使用 {{ define }}创建的模板。出于这个原因,所有定义的模板名称应该是带有某个 namespace。 # 正确 {{- define "nginx.fullname"}} {{/* ... */}} {{end -}} # 不正确 {{- define "fullname" -}} {{/* ... */}} {{end -}} # 强烈建议通过 helm create 命令创建新 chart 格式化模板 模板应该使用两个空格缩进(不是制表符)。 模板指令在大括号之后和大括号之前应该有空格: # 正确 {{.foo}} {{print "foo"}} {{- print "bar" -}} # 不正确 {{.foo}} {{print "foo"}} {{-print "bar"-}} 模板应尽可能地填充空格: foo: {{- range .Values.items}} {{.}} {{end -}} 块(如控制结构)可以缩进以指示模板代码的流向 {{if $foo -}} {{- with .Bar}}Hello{{ end -}} {{- end -}} 但是,由于 YAML 是一种面向空格的语言,因此代码缩进有时经常不能遵循该约定。 生成模板中的空格 最好将生成的模板中的空格保持最小。特别是,许多空行不应该彼此相邻。但偶尔空行(特别是逻辑段之间)很好。 # 最佳实践 apiVersion: batch/v1 kind: Job metadata: name: example labels: first: first second: second YAML注释与模板注释 # YAML 注释 用# # This is a comment type: sprocket # 模板注释 用/**/ {{- /* This is a comment. */ -}} type: frobnitz 记录模板功能时应使用模板注释,如解释定义的模板: {{- /* mychart.shortname provides a 6 char truncated version of the release name. */ -}} {{define "mychart.shortname" -}} {{.Release.Name | trunc 6}} {{- end -}} 在模板内部,当 Helm 用户可能(有可能)在调试过程中看到注释时,可以使用 YAML 注释。 # This may cause problems if the value is more than 100Gi memory: {{.Values.maxMem | quote}} 上面的注释在用户运行 helm install --debug 时可见,而在 {{- /* */ -}} 部分中指定的注释不是。 标签OR注释 在下列条件下,元数据项应该是标签: Kubernetes 使用它来识别此资源 为了查询系统目的,向操作员暴露是非常有用的 例如,使用 helm.sh/chart: NAME-VERSION 作为标签,以便操作员可以方便地查找要使用的特定 chart 的所有实例。 如果元数据项不用于查询,则应将其设置为注释。 Helm hook 总是注释。 标准标签 下表定义Helm chart 使用的通用标签。Helm 本身从不要求特定的标签。 标记为 REC 的标签是表示推荐的,应放置在 chart 上以保持全局一致性。 标记 OPT 是表示可选的。这些都是惯用的或通常使用的,但不是经常用于运维目的。 名称 状态 描述 app.kubernetes.io/name REC 这应该是应用程序名称,反映整个应用程序。 通常使用{{template“name” .}}来实现此目的。 许多Kubernetes清单都使用它,而不是Helm特有的。 helm.sh/chart REC 这应该是chart名字和版本: {{.Chart.Name}}-{{ .Chart.Version \replace "+" "_" }}. app.kubernetes.io/managed-by REC 这里总是被设置为 {{.Release.Service}}. 这是为了找到由Tiller管理的所有东西。 app.kubernetes.io/instance REC 这里应该是 {{.Release.Name}}. 它有助于区分同一应用程序的不同实例。 app.kubernetes.io/version OPT 应用程序的版本可以被设置为 {{.Chart.AppVersion}}. app.kubernetes.io/component OPT 这是用于标记应用可能在应用程序中扮演的不同角色的通用标签。 例如 app.kubernetes.io/component: frontend app.kubernetes.io/part-of OPT 当多个chartsor软件一起构成一个应用时. 例如,应用软件和数据库来构成网站。 这可以设置为支持的顶级应用程序。
调试模板 调试模板比较麻烦,因为模板在Tiller服务器而不是Helm客户端上渲染。然后渲染的模板被发送到KubernetesAPI服务器,可能由于格式以外的原因,服务器可能会拒绝接收这些YAML文件。 有几个命令可以帮助您进行调试: helm lint是验证 chart 是否遵循最佳实践的首选工具 helm install --dry-run --debug:让服务器渲染模板,然后返回结果清单文件的方法 helm get manifest:查看服务器上安装的模板的方法 测试chart 一个chart包含许多一起工作的Kubernetes资源和组件。在开发charts时,需要编写一些测试来验证charts在安装时是否按预期工作。这些测试也有助于使用者了解charts应该做什么。 测试在Helm chart中的templates/tests/test-connection.yaml中是一个pod定义,指定一个给定的命令来运行容器,如下所示。 apiVersion: v1 kind: Pod metadata: name: {{ include "mysql.fullname" . }}-test-connection labels: {{ include "mysql.labels" . | indent 4 }} annotations: "helm.sh/hook": test-success spec: containers: - name: wget image: busybox command: ['wget'] args: ['{{ include "mysql.fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never 在 Helm 中,有两个测试 hook:helm.sh/hook:test-success和helm.sh/hook:test-failure。 test-success 表示测试pod应该成功完成。Pod中的容器应该exit 0. test-failure 是一种断言测试容器不能成功完成的方式。Pod中的容器未exit 0,则表示成功。 主要测试如下内容: 验证来自values.yaml文件的配置是否正确注入 确保用户名和密码正常工作 确保不正确的用户名和密码不起作用 断言服务已启动并正确进行负载平衡 可以使用该helm test命令在release中运行Helm中的预定义测试。对于chart使用者来说,这是一种很好的方式来检查发布的chart(或应用程序)是否按预期工作。 可以在单个yaml文件中定义尽可能多的测试,也可以在templates/tests目录中的多个yaml文件中进行分布测试。
服务依赖管理 所有使用helm部署的应用中如果没有指定chart的名字都会生成一个随机的Release name。而真正的资源对象的名字是在YAML文件中定义的名字App name,两者连接才是资源对象的名字:Release name-App name。 使用helm chart部署的包含依赖关系的应用,都使用同一套Release name,在编写YAML文件时,要注意服务发现时需要配置的服务地址。 使用环境变量的方式,如下配置: env: - name: SERVICE_NAME value: "{{ .Release.Name }}-{{ .Values.image.env.SERVICE_NAME }}" # 使用Go template的语法 # {{ .Values.image.env.SERVICE_NAME }}的值从values.yaml文件中获得 # valus.yaml 配置如下 image: env: SERVICE_NAME: k8s-app-monitor-test 解决本地chart依赖 # 1. 在本地当前配置的目录下启动helm server,不指定参数,直接使用默认端口 helm server # 2. 将该repo加入到repo list中 helm repo add local http://localhost:8879 # 3. 在本地浏览器访问http://localhost:8879,查看到本地所有的chart # 4. 下载依赖到本地 helm dependency update # 所有的chart都会下载到本地的charts目录 helm命令自动补全 # zsh source <(helm completion zsh) # bash source <(helm completion bash)
Chart存储库是可以存储和共享charts的地方,Helm官方维护了一个Chart存储库。使用Helm可以轻松创建和运行自己的Chart存储库。 自建Chart存储库 chart repo是容纳一个或多个封装的chart的HTTP服务器。 虽然Helm可用于管理本地chart目录,但在共享chart时,首选机制是chart repo库。Helm附带了用于开发人员测试的内置服务器(helm server)。 作为repo库服务器的条件: 可以提供YAML文件和tar文件 可以回答GET请求的HTTP服务器 repo库的主要特征是存在一个名为index.yaml的特殊文件,它具有: repo库提供所有软件包的列表 允许检索和验证这些软件包的元数据 在客户端,repo库使用helm repo命令进行管理,Helm不提供将chart上传到远程存储服务器的工具(这样做会增加部署服务器的需求,从而增加配置repo库的难度)。 Chart库是能提供YAML和tar文件并回答GET请求的HTTP服务器,因此托管Chart存储库时,很多选择。例如,Google云存储桶,AmazonS3存储桶,GithubPages或者创建自己的Web服务器。 Chart库结构 Chart存储库分为两部分: index.yaml:包含Chart库中所有charts的索引 若干已打包的charts 例如有一个Chart存储库https://example.com/charts,如下所示: charts/ | |- index.yaml | |- alpine-0.1.2.tgz | |- alpine-0.1.2.tgz.prov # charts和index.yaml文件可以位于同一台服务器,也可位于不同服务器 提供的chart的下载URL:https://example.com/charts/alpine-0.1.2.tgz。 索引文件 一个有效的Chart存储库必须包含一个索引文件index.yaml,索引文件中包含库中每个chart的元数据,如chart的Chart.yaml文件的内容。 helm repo index命令,根据已经打包的charts文件给本地目录生成索引文件,索引文件示例如下: apiVersion: v1 entries: alpine: - created: 2016-10-06T16:23:20.499814565-06:00 description: Deploy a basic Alpine Linux pod digest: 99c76e403d752c84ead610644d4b1c2f2b453a74b921f422b9dcb8a7c8b559cd home: https://k8s.io/helm name: alpine sources: - https://github.com/helm urls: - https://technosophos.github.io/tscharts/alpine-0.2.0.tgz version: 0.2.0 - created: 2016-10-06T16:23:20.499543808-06:00 description: Deploy a basic Alpine Linux pod digest: 515c58e5f79d8b2913a10cb400ebb6fa9c77fe813287afbacf1a0b897cd78727 home: https://k8s.io/helm name: alpine sources: - https://github.com/helm urls: - https://technosophos.github.io/tscharts/alpine-0.1.0.tgz version: 0.1.0 nginx: - created: 2016-10-06T16:23:20.499543808-06:00 description: Create a basic nginx HTTP server digest: aaff4545f79d8b2913a10cb400ebb6fa9c77fe813287afbacf1a0b897cdffffff home: https://k8s.io/helm name: nginx sources: - https://github.com/helm/charts urls: - https://technosophos.github.io/tscharts/nginx-1.1.0.tgz version: 1.1.0 generated: 2016-10-06T16:23:20.499029981-06:00 生成的索引和包可以从由网络服务器提供,使用helm server可以启动本地服务器,在本地测试所有内容。 # 启动一个本地web服务器,在./charts目录找到chart提供服务 $ helm serve --repo-path ./charts Regenerating index. This may take a moment. Now serving you on 127.0.0.1:8879 # server命令将在启动过程中自动生成一个index.yaml文件 托管Chart存储库 普通web服务器 配置普通Web服务器来提供Chart存储库服务,只需执行以下操作: 将索引和charts置于服务器目录中 确保index.yaml可以在没有认证要求的情况下访问 确保yaml文件的正确内容类型(text/yaml或text/x-yaml) 如果要在$WEBROOT/charts以外的目录为chart提供服务,请确保Web根目录中有一个charts/目录,并将索引文件和chart放入该文件夹内。 管理Chart存储库 将chart存储到Chart存储库 Chart存储库中的chart必须被正确打包并有正确的版本号。具体步骤如下: # 打包 helm package docs/examples/alpine/ mkdir fantastic-charts mv alpine-0.1.0.tgz fantastic-charts/ # 使用本地路径和远程Chart存储库URL,并在指定路径中生成index.yaml helm repo index fantastic-charts --url https://fantastic-charts.storage.googleapis.com 添加新的chart到Chart存储库 每次将新chart添加到存储库时,都必须重新生成索引。helm repo index命令将index.yaml从头开始完全重建该文件,但仅包括它在本地找到的charts。 可以使用--merge标志向现有index.yaml文件增量添加新chart。 如果同时生成了出处文件provenance,也要一起上传。 共享charts 只要知道Chart存储库的URL就能够共享charts。 使用helm repo add [NAME] [URL]命令将Chart存储库添加到使用者的helm客户端中,并且可以给存储库取一个别名。 helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com helm repo list fantastic-charts https://fantastic-charts.storage.googleapis.com 如果Chart存储库由HTTP基本认证支持,也可以在此处提供用户名和密码: helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com --username my-username --password my-password helm repo list fantastic-charts https://fantastic-charts.storage.googleapis.com 如果Chart存储库不包含index.yaml文件,则添加不成功。 添加成功后,可以搜索charts,通过helm repo update指令来获取最新的charts。 helm repo add和helm repo update命令获取index.yaml文件并将它们存储在 $HELM_HOME/repository/cache/目录中。这是helm search找到有关charts信息的地方。
使用自定义资源定义(CRD)时,区分两个不同的部分很重要: CRD的声明。这是一个 YAML 文件,kind 类型为 CustomResourceDefinition 资源使用CRD。CRD 定义 foo.example.com/v1。任何拥有 apiVersion: example.com/v1 和种类 Foo 的资源都是使用 CRD 的资源 在使用资源之前安装 CRD 声明 Helm 优化为尽可能快地将尽可能多的资源加载到 Kubernetes 中。通过设计,Kubernetes 可以采取一整套 manifests,并将它们全部启动在线(这称为 reconciliation 循环)。 但是与 CRD 有所不同。对于 CRD,声明必须在该 CRDs 种类的任何资源可以使用之前进行注册。注册过程有时需要几秒钟。 方法 1:独立的 chart 一种方法是将 CRD 定义放在一个 chart 中,然后将所有使用该 CRD 的资源放入另一个 chart 中。 在这种方法中,每个 chart 必须单独安装。 方法 2:预安装 hook 要将这两者打包在一起,在 CRD 定义中添加一个 crd-install 钩子,以便在执行 chart 的其余部分之前完全安装它。 请注意,如果使用crd-install hook创建CRD ,则该CRD定义在helm delete运行时不会被删除。
YAML具有一些有用的特性,可以使我们的模板更少出错并更易于阅读。 标量和集合 根据YAML规范YAML spec,有两种类型的集合,以及许多标量类型。 这两种类型的集合是字典和数组: # 字典 map: one: 1 two: 2 three: 3 # 数组 sequence: - one - two - three 标量值是单个值(与集合相对)。 YAML中的标量类型 在Helm的YAML语言中,值的标量数据类型由一组复杂的规则确定,包括用于资源定义的Kubernetes schema。在推断类型时,以下规则成立。 count: 1 # int size: 2.34 # float count: "1" # string, not int size: '2.34' # string, not float isGood: true # bool answer: "true" # string # 空值的词是null(not nil) 请注意: port: "80"是有效的,并且能通过模板引擎和YAML分析器 但如果Kubernetes预期port为整数,则会失败 在某些情况下,可以使用YAML节点标签强制进行特定的类型推断: coffee: "yes, please" age: !!str 21 # !!str告诉解析器age是一个字符串 port: !!int "80" # !!int告诉解析器port是一个整数 YAML中的字符串 在YAML文档中的大部分数据都是字符串,YAML有多种表示字符串的方式。有三种内置方式声明字符串: way1: bare words:单词未被引号引起来 way2: "double-quoted strings":双引号的字符串可以使用特定的字符\进行转义 way3: 'single-quoted strings':单引号字符串是“文字”字符串,并且不使用\转义字符。唯一的转义序列是'',它被解码为一个单独的' 声明多行字符串,所有内置样式必须位于同一行上: coffee: | Latte Cappuccino Espresso # coffee的值等价于:Latte\nCappuccino\nEspresso\n 请注意|后的第一行必须正确缩进。 控制多行字符串中的空格 用来|表示一个多行字符串。但请注意字符串的内容后跟着\n。 |-,去掉行尾\n coffee: |- Latte Cappuccino Espresso # coffee值等价于:Latte\nCappuccino\nEspresso |+,保留所以\n coffee: |+ Latte Cappuccino Espresso another: value # coffee值等价于:Latte\nCappuccino\nEspresso\n\n\n 文本块内部的缩进被保留,并保留换行符: coffee: |- Latte 12 oz 16 oz Cappuccino Espresso # coffee将是Latte\n 12 oz\n 16 oz\nCappuccino\nEspresso 缩进和模板 在编写模板时,希望将文件内容注入模板。有两种方法可以做到这一点: 使用{{ .Files.Get "FILENAME" }}得到chart中的文件的内容 使用{{ include "TEMPLATE" . }}渲染模板,然后其内容放入chart 将文件插入YAML时,最好理解上面的多行规则。通常情况下,插入静态文件的最简单方法是做这样的事情: myfile: | {{ .Files.Get "myfile.txt" | indent 2 }} 使用indent 2告诉模板引擎使用两个空格缩进“myfile.txt”中的每一行。 请注意,不缩进该模板行。那是因为如果缩进了,第一行的文件内容会缩进两次。 折叠多行字符串 有时候想在YAML中用多行代表一个字符串,但是当它被解释时,要把它当作一个长行。这被称为“折叠”。 要声明一个折叠块,使用>代替|,除最后一个换行符之外的所有内容都将转换为空格。 coffee: > Latte Cappuccino Espresso # coffee的值等价于:Latte Cappuccino Espresso\n # 请注意,在折叠语法中,缩进文本将导致行被保留 coffee: >- Latte 12 oz 16 oz Cappuccino Espresso # coffee的值等价于:Latte\n 12 oz\n 16 oz\nCappuccino Espresso # 注意区别,此时除了最后一个\n,其他换行符和空格都保留 将多个文档嵌入到一个文件中 可以将多个YAML文档放入单个文件中。这是通过在一个新文档前加---,在文档结束加...来完成的 --- document:1 ... --- document: 2 ... # 在许多情况下,无论是---或...可被省略 Helm中的某些文件不能包含多个文档。例如,如果文件内部提供了多个values.yaml文档,则只会使用第一个文档。但是,模板文件可能有多个文档。发生这种情况时,文件(及其所有文档)在模板渲染期间被视为一个对象。但是,最终的YAML在被送到Kubernetes之前被分成多个文件。 建议每个文件在绝对必要时才使用多个文档。在一个文件中有多个文件可能很难调试。 YAML是JSON的Superset 因为YAML是JSON的超集,所以任何有效的JSON文档都应该是有效的YAML。 { "coffee": "yes, please", "coffees": [ "Latte", "Cappuccino", "Espresso" ] } # 以上是下面另一种表达方式: coffee: yes, please coffees: - Latte - Cappuccino - Espresso # 这两者可以混合使用(小心使用): coffee: "yes, please" coffees: [ "Latte", "Cappuccino", "Espresso"] # 所有这三个都应该解析为相同的内部表示 虽然这意味着诸如values.yaml可能包含JSON数据的文件,但Helm不会将文件扩展名.json视为有效的后缀。 YAML锚 YAML规范提供了一种方法来存储对某个值的引用,并稍后通过引用来引用该值。YAML将此称为“锚定”: coffee: "yes, please" favorite: &favoriteCoffee "Cappucino" coffees: - Latte - *favoriteCoffee - Espresso 在上面,&favoriteCoffee设置一个引用到Cappuccino。 之后,该引用被用作*favoriteCoffee。 所以coffees变成了 Latte, Cappuccino, Espresso。 虽然在少数情况下锚点是有用的,但它们的一个方面可能导致细微的错误:第一次使用YAML时,引用被扩展,然后被丢弃。 所以如果我们要解码然后重新编码上面的例子,那么产生的YAML将是: coffee: yes, please favorite: Cappucino coffees: - Latte - Cappucino - Espresso 因为Helm和Kubernetes经常读取,修改并重写YAML文件,锚将会丢失。