暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Helm最佳实践

耶喝运维 2021-07-15
2081

你真的会使用Helm 么?

你有看过官方文档中的最佳实践么 ?

你在编写yaml 的时候是否在使用空格而不是tab?

你是否也发现你写的helm别人很难阅读?

如果是,请看一下下面的文章吧

本文复制粘贴于Helm官方文档(抄的抄的):

https://helm.sh/zh/docs/chart_best_practices/


一般惯例

最佳实践的这部分阐述了一般惯例。

Chart名称

chart名称必须是小写字母和数字。单词之间 可以 使用横杠分隔(-):

示例:

drupal nginx-lego aws-cluster-autoscaler 
chart名称中不能用大写字母也不能用下划线。点 . 符号也不行。

版本号

Helm尽可能使用 SemVer 2来表示版本号。(注意Docker镜像的tag不需要遵循SemVer, 因此被认为是一个不幸的例外规则。

当SemVer版本存储在Kubernetes标签中,我们通常把+字符改成_,因为标签不允许使用+作为值进行签名。

values

最佳实践的该部分包括了values的使用。这部分指南中,我们提供了关于你如何构建和使用values的建议,以及专注于设计chart的 values.yaml文件。

命名规范

变量名称以小写字母开头,单词按驼峰区分:

正确的:

chicken: true
chickenNoodleSoup: true

错误的:

Chicken: true  # initial caps may conflict with built-ins 
chicken-noodle-soup: true # do not use hyphens in the name

注意所有的Helm内置变量以大写字母开头,以便与用户定义的value进行区分:.Release.Name.Capabilities.KubeVersion

扁平或嵌套的Value

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: falsefoo: "false" 是不一样的。大整型数如:foo: 12345678 有时会被转换成科学计数法。

避免类型强制规则错误最简单的方式是字符串明确定义,其他都是不明确的。或者,简单来讲, 给所有字符串打引号

通常,为了避免整数转换问题,将整型存储为字符串更好,并用 {{ int $value }} 在模板中将字符串转回整型。

在大多数场景中,显示的类型标记更好,所以 foo: !!string 1234 会将1234作为字符串对待。但是,YAML解析器会消耗标记,因此类型数据在一次解析后会丢失。

考虑用户如何使用你的value

有三种潜在的value来源:

  • chart的values.yaml文件

  • helm install -f helm upgrade -f提供的values文件

  • 在执行helm install helm upgrade 时传递给--set --set-string 参数的values

当设计values的结构时,记得你的chart用户可能会通过-f 参数或--set选项覆盖他们。

由于--set在表现上更有限,编写你values.yaml文件的第一指导原则是 确保它容易被--set覆盖

因此使用map构建values文件更好。

很难与--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的port更加明显: --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

以它描述的参数名称开始每个注释可以很容易整理文档,并使文档工具能可靠地将文档字符串与其描述的参数关联起来。

模板

最佳实践指南的这部分聚焦于模板。

templates/结构

templates/目录结构应该如下:

  • 如果生成YAML输出,模板文件应该有扩展名.yaml。扩展名是.tpl可用于生成非格式化内容的模板文件。

  • 模板文件名称应该使用横杠符号(my-example-configmap.yaml),不用驼峰记法。

  • 每个资源的定义应该在它自己的模板文件中。

  • 模板文件的名称应该反映名称中的资源类型。比如:foo-pod.yaml bar-svc.yaml

定义模板的名称

定义的模板(在{{ define }}命令中定义的模板)是可全局访问的。这就意味着chart和所有的子chart都可以访问用{{ define }}创建的所有模板。

因此, 所有定义的模板名称应该被命名空间化。

正确的:

{{- define "nginx.fullname" }}
{{/* ... */}}
{{ end -}}

不正确的:

{{- define "fullname" -}}
{{/* ... */}}
{{ end -}}

强烈建议通过helm create命令创建新chart,因为模板名称是根据此最佳实践自动定义的。

格式化模板

模板应该使用两个 空格 缩进(永远不要用tab)。

模板命令的大括号前后应该使用空格:

正确的:

{{ .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

这样也OK:

apiVersion: batch/v1
kind: Job

metadata:
name: example

labels:
first: first
second: second

但避免这样:

apiVersion: batch/v1
kind: Job

metadata:
name: example





labels:
first: first

second: second

注释 (YAML注释 vs. 模板注释)

YAML和Helm模板都有注释标记符。

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时是可见的,而在{{- * */}}部分指定注释不会显示。

在模板和模板输出中使用JSON

YAML是JSON的超集。在某些情况下,使用JSON语法比其他YAML表示更具可读性。

比如,这个YAML更接近表示列表的普通YAML方法:

arguments:
- "--dirname"
- "/foo"

但是折叠成JSON列表样式时会更易阅读:

arguments: ["--dirname", "/foo"]

使用JSON可以很好地提高易读性。然而,JSON语法不应用于表示更复杂的结构。

在处理嵌入到YAML中的纯JSON时(比如初始化容器配置),使用JSON格式当然是最合适的。


依赖

最佳实践的这部分阐述Chart.yaml中声明的dependencies

版本

如果有可能的话,使用版本范围而不是某个固定的版本。建议的默认设置时使用补丁级别版本的匹配:

version: ~1.2.3

这样会匹配 1.2.3以及该版本的任何补丁,也就是说,~1.2.3相当于>= 1.2.3, < 1.3.0

关于完整的版本匹配语法,请参照 语义版本文档

仓库URL

如果可能的话,使用 https:// 仓库URL,而不是http:// URL。

如果这个仓库已经被添加到仓库索引文件中,仓库名称可以作为URL的别名。使用alias: @ 后跟仓库名称。

文件URL(file://...) 被认为是一种有固定部署管道组装的chart的“特例”。官方Helm仓库不允许chart使用file://

条件和标签

条件和标签可以被添加到任意 可选的 依赖中。

条件的首先格式是:

condition: somechart.enabled

somechart是依赖的chart名称。

当多个子chart(依赖)一起提供可选或可交换的特性时,这些chart应该共享相同的标签。

比如,如果nginx memcached在chart中一起提供性能优化,且需要在使用该功能时同时存在,则都应该有如下的标签部分:

tags:
- webaccelerator

这允许用户使用一个标签打开和关闭该功能。

标签和注释

最佳实践的这部分讨论关于在chart中使用标签和注释的最佳方式。

是标签还是注释?

在以下条件下,元数据项应该是标签:

  • Kubernetes使用它来识别这种资源

  • 为了查询系统,暴露给操作员会很有用

比如,我们建议使用 helm.sh/chart: NAME-VERSION 作为标签,以便操作员可以找到特定chart的所有实例。

如果元数据项不是用于查询,就应该设置为注释。

Helm钩子都是注释。

标准标签

以下表格定义了Helm chart使用的通用标签。Helm本身从不要求出现特定标签。标记为REC的是推荐标签。且 应该 放置在chart之上保持全局一致性。标记为OPT的是可选的。这些是惯用的和常用的,但操作时并不经常依赖。

名称

状态

描述

app.kubernetes.io/name

REC

app名称,反应整个app。{{ template "name" . }}经常用于此。很多Kubernetes清单会使用这个,但不是Helm指定的。

helm.sh/chart

REC

chart的名称和版本: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}

app.kubernetes.io/managed-by

REC

此值应始终设置为 {{ .Release.Service }}。用来查找被Helm管理的所有内容。

app.kubernetes.io/instance

REC

这个应该是{{ .Release.Name }}。有助于在同一应用程序中区分不同的实例。

app.kubernetes.io/version

OPT

app的版本,且被设置为 {{ .Chart.AppVersion }}.

app.kubernetes.io/component

OPT

这是通用标签,用于标记块在应用程序中可能扮演的不同角色。比如 app.kubernetes.io/component: frontend

app.kubernetes.io/part-of

OPT

当多个chart或块用于构建一个应用程序时。比如,应用软件和数据库生成一个网站。这可以设置为受支持的顶级应用程序。

你可以在 Kubernetes documentation找到更多 app.kubernetes.io作为前缀的Kubernetes标签。

pod和pod模板

最佳实践的这部分讨论在chart清单中格式化Pod和Pod模板部分。

以下(非详尽的)资源列表使用Pod模板:

  • Deployment

  • ReplicationController

  • ReplicaSet

  • DaemonSet

  • StatefulSet

镜像

容器镜像应该使用固定的tag或镜像SHA。不应该使用latest, head, canary等标签或其他被设计为“浮动的”标签。

镜像 可以 被定义在 values.yaml 文件中是的切换镜像更加容易。

image: {{ .Values.redisImage | quote }}

镜像和tag 可以 values.yaml中定义为两个独立的字段:

image: "{{ .Values.redisImage }}:{{ .Values.redisTag }}"

镜像拉取策略

helm create 通过以下方式在deployment.yaml中将 imagePullPolicy 默认设置为 IfNotPresent

imagePullPolicy: {{ .Values.image.pullPolicy }}

以及values.yaml:

image:
pullPolicy: IfNotPresent

类似地,如果Kubernetes根本没有定义,默认会将 imagePullPolicy 设置为 IfNotPresent。如果想设置一个值而不是 IfNotPresent,只需在 values.yaml 中更新为需要的值即可。

Pod模板应该声明选择器

所有的Pod模板部分应该指定一个selector。比如:

selector:
matchLabels:
app.kubernetes.io/name: MyName
template:
metadata:
labels:
app.kubernetes.io/name: MyName

这是一个很好的实践,因为它建立了集合和pod之间的关系。

但这一点对于像工作负载这样的集合来说更加重要。如果没有,标签的 所有 集合会选择匹配pod,如果你使用了改变的标签,比如版本和发布日期,这个功能会失效。

基于角色的访问控制

最佳实践的这部分讨论在chart清单中创建和格式化RBAC资源。

RBAC 资源有:

  • ServiceAccount (namespaced)

  • Role (namespaced)

  • ClusterRole

  • RoleBinding (namespaced)

  • ClusterRoleBinding

YAML 配置

RBAC和服务账户配置应该使用独立的key。它们是独立的内容。在YAML中将这两个概念分开可以消除歧义使其更加清晰。

rbac:
# Specifies whether RBAC resources should be created
create: true

serviceAccount:
# Specifies whether a ServiceAccount should be created
create: true
# The name of the ServiceAccount to use.
# If not set and create is true, a name is generated using the fullname template
name:

这个结构可以在更加复杂的需要多个服务账户的chart中扩展。

someComponent:
serviceAccount:
create: true
name:
anotherComponent:
serviceAccount:
create: true
name:

RBAC 资源应该默认创建

rbac.create 应该是布尔值,用于控制RBAC资源是否被创建。默认是 true。用户想自己管理RBAC访问控制时可以设置为false (示例如下)。

使用RBAC资源

serviceAccount.name 要设置为由chart创建的访问控制资源的ServiceAccount的名称。如果serviceAccount.create是true,则使用该名称的ServiceAccount会被创建。如果没有设置名称, 则会使用fullname模板生成一个名称。如果serviceAccount.create是false,则不会被创建,但仍然会与相同的资源关联, 以便后续手动创建的引用它的RBAC资源可以正常工作。如果serviceAccount.create是false且没有指定名称, 会使用默认的ServiceAccount。

以下辅助模板应用于ServiceAccount。

{{/*
Create the name of the service account to use
*/}}
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}


文章转载自耶喝运维,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论