k8s helm

helm chart hub

Helm 是一个 Kubernetes 应用的包管理工具,用来管理 chart——预先配置好的安装包资源

Helm chart 是用来封装 Kubernetes 原生应用程序的 YAML 文件,可以在你部署应用的时候自定义应用程序的一些 metadata,便与应用程序的分发。

Helm 和 chart 的主要作用是:

  • 应用程序封装
  • 版本管理
  • 依赖检查
  • 便于应用程序分发

结构

Helm chart的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
mychart
├── Chart.yaml
├── charts # 该目录保存其他依赖的 chart(子 chart)
├── templates # chart 配置模板,用于渲染最终的 Kubernetes YAML 文件
│   ├── NOTES.txt # 用户运行 helm install 时候的提示信息
│   ├── _helpers.tpl # 用于创建模板时的帮助类
│   ├── deployment.yaml # Kubernetes deployment 配置
│   ├── ingress.yaml # Kubernetes ingress 配置
│   ├── service.yaml # Kubernetes service 配置
│   ├── serviceaccount.yaml # Kubernetes serviceaccount 配置
│   └── tests
│       └── test-connection.yaml
└── values.yaml # 定义 chart 模板中的自定义配置的默认值,可以在执行 helm install 或 helm update 的时候覆盖

templates/ 目录包括了模板文件。当Helm评估chart时,会通过模板渲染引擎将所有文件发送到templates/目录中。 然后收集模板的结果并发送给Kubernetes。

values.yaml 文件也导入到了模板。这个文件包含了chart的 默认值 。这些值会在用户执行helm installhelm upgrade时被覆盖。

Chart.yaml 文件包含了该chart的描述。你可以从模板中访问它。charts/目录 可以 包含其他的chart(称之为 子chart)。 指南稍后我们会看到当涉及模板渲染时这些是如何工作的。

templates

如果你看看 mychart/templates/ 目录,会注意到一些文件已经存在了:

  • NOTES.txt: chart的”帮助文本”。这会在你的用户执行helm install时展示给他们。
  • deployment.yaml: 创建Kubernetes 工作负载的基本清单
  • service.yaml: 为你的工作负载创建一个 service终端基本清单。
  • _helpers.tpl: 放置可以通过chart复用的模板辅助对象

命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装
helm install full-coral ./mychart
# Helm检索版本并查看实际加载的模板
helm get manifest full-coral
# 卸载发布
helm uninstall full-coral
# 测试模板渲染但又不想安装任何内容时
helm install --debug --dry-run goodly-guppy ./mychart [--set key=value]
# 单独测试子chart
helm install --generate-name --dry-run --debug mychart/charts/mysubchart

# 是验证chart是否遵循最佳实践的首选工具
helm lint
# 这是让服务器渲染模板的好方法,然后返回生成的清单文件
helm template --debug / helm install --dry-run --debug

对象

在上一部分中,我们用``在模板中插入版本名称。Release是你可以在模板中访问的高级对象之一。

  • Release

    : 该对象描述了版本发布本身。包含了以下对象:

    • Release.Name: release名称
    • Release.Namespace: 版本中包含的命名空间(如果manifest没有覆盖的话)
    • Release.IsUpgrade: 如果当前操作是升级或回滚的话,需要将该值设置为true
    • Release.IsInstall: 如果当前操作是安装的话,需要将该值设置为true
    • Release.Revision: 此次修订的版本号。安装时是1,每次升级或回滚都会自增
    • Release.Service: 该service用来渲染当前模板。Helm里一般是Helm
  • Values: Values是从values.yaml文件和用户提供的文件传进模板的。Values默认为空

  • Chart:Chart.yaml文件内容。Chart.yaml里的任意数据在这里都可以可访问的。比如-会打印出mychart-0.1.0

  • Files

    在chart中提供访问所有的非特殊文件。当你不能使用它访问模板时,你可以访问其他文件。 这个 文件访问部分了解更多信息

    • Files.Get 通过文件名获取文件的方法。 (.Files.Getconfig.ini
    • Files.GetBytes 用字节数组代替字符串获取文件内容的方法。 对图片之类的文件很有用
    • Files.Glob 用给定的shell glob模式匹配文件名返回文件列表的方法
    • Files.Lines 逐行读取文件内容的方法。迭代文件中每一行时很有用
    • Files.AsSecrets 使用Base 64编码字符串返回文件体的方法
    • Files.AsConfig 使用YAML格式返回文件体的方法
  • Capabilities

    : 提供关于Kubernetes集群支持功能的信息

    • Capabilities.APIVersions 是一个版本集合
    • Capabilities.APIVersions.Has $version 说明集群中的版本 (e.g., batch/v1) 或是资源 (e.g., apps/v1/Deployment) 是否可用
    • Capabilities.KubeVersionCapabilities.KubeVersion.Version 是Kubernetes的版本号
    • Capabilities.KubeVersion.Major Kubernetes的主版本
    • Capabilities.KubeVersion.Minor Kubernetes的次版本
    • Capabilities.HelmVersion 包含Helm版本详细信息的对象,和 helm version 的输出一致
    • Capabilities.HelmVersion.Version 是当前Helm版本的语义格式
    • Capabilities.HelmVersion.GitCommit Helm的git sha1值
    • Capabilities.HelmVersion.GitTreeState 是Helm git树的状态
    • Capabilities.HelmVersion.GoVersion 是使用的Go编译器版本
  • Template

    : 包含了已经被执行的当前模板信息

    • Template.Name: 当前模板的命名空间文件路径 (e.g. mychart/templates/mytemplate.yaml)
    • Template.BasePath: 当前chart模板目录的路径 (e.g. mychart/templates)

内置的值都是以大写字母开始。 这是符合Go的命名惯例。当你创建自己的名称时,可以按照团队约定自由设置。 就像很多你在 Artifact Hub 中看到的chart,其团队选择使用首字母小写将本地名称与内置对象区分开,本指南中我们遵循该惯例。

子chart

但chart可以使用依赖,称为 子chart,且有自己的值和模板。 该章节我们会创建一个子chart并能看到访问模板中的值的不同方式。

在深入研究代码之前,需要了解一些子chart的重要细节:

  1. 子chart被认为是“独立的”,意味着子chart从来不会显示依赖它的父chart。
  2. 因此,子chart无法访问父chart的值。
  3. 父chart可以覆盖子chart的值。
  4. Helm有一个 全局值 的概念,所有的chart都可以访问。

为了做这些练习,我们可以从本指南开始时创建的mychart/开始,并在其中添加一个新的chart。

1
2
3
4
$ cd mychart/charts
$ helm create mysubchart
Creating mysubchart
$ rm -rf mysubchart/templates/*

docker-compose 转换

通过helm create建立helm chart目录结构](https://helm.sh/zh/docs/helm/helm_create/)

比如’helm create foo’会创建一个目录结构看起来像这样:

1
2
3
4
5
6
7
foo/
├── .helmignore   # Contains patterns to ignore when packaging Helm charts.
├── Chart.yaml    # Information about your chart
├── values.yaml   # The default values for your templates
├── charts/       # Charts that this chart depends on
└── templates/    # The template files
    └── tests/    # The test files

有docker-compose.yml,可以使用kompose转换成k8s的配置文件(不太标准,要人工修改),可以先把生成出来的放到templates目录下面

1
2
3
4
5
6
7
8
9
10
11
12
kompose -f docker-compose.yml convert
.....
INFO Kubernetes file "elasticsearch-service.yaml" created 
INFO Kubernetes file "frontend-service.yaml" created 
INFO Kubernetes file "gateway-service.yaml" created 
INFO Kubernetes file "jobmanager-service.yaml" created 
INFO Kubernetes file "kibana-service.yaml" created 
INFO Kubernetes file "minio-service.yaml" created 
INFO Kubernetes file "mysql-service.yaml" created 
INFO Kubernetes file "nacos-service.yaml" created 
INFO Kubernetes file "portal-service.yaml" created 

k8s不支持dockerfile启动

docker-compose 依靠dockerfile,需要手动打镜像

docker-compose.yml 例子

1
2
3
4
5
6
7
8
  business:
    #restart: always
    build:
      context: ./
      dockerfile: Dockerfile
      args:
        jarpath: chinaunicom_smartward_business.jar
    container_name: business  #指定容器名
1
2
3
docker build  \
    --build-arg jarpath=xxx \
    -t ${IMAGE_NAME} -f xxx.Dockerfile .

编写helmchart

helm的版本需要使用v3,安装方式参考链接。编写helmchart涉及k8s常用的资源,deployment/statefulset/configmap/secret/pv/pvc/storageclass/service/ingress/serviceaccount/…。

初始化chart

比如helm create example-java会创建一个目录结构看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
├── Chart.yaml # 对于chart的一些描述
├── charts   # 用来放依赖的charts
├── templates  # helm 会把这个目录下面所有的文件进行渲染,然后把结果发送到k8s
│   ├── NOTES.txt
│   ├── _helpers.tpl  # 保留这个文件其他的删掉
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
└── values.yaml  # chart的默认值

3 directories, 9 files

保留templates/_helpers.tpl,删掉templates目录其他文件

编写chart

简单的应用可以只关注values.yamltemplates/*.yaml ,更加详细的编写方式,可以参考官方文档

模板文件可以通过`` ,values.yaml里面的值

values.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 副本数
replicaCount: 1

# 镜像
image:
  repository: example-java:latest
  pullPolicy: IfNotPresent # IfNotPresent 不会重新pull最新的镜像 Always每次部署会pull

# 数据库默认配置
mysql:
  userName: root
  password: ""
  address: "127.0.0.1:3306"
  dbname: ""
  
# service 配置
service:
  type: NodePort
  port: 9012

templates/deployment.yaml,templates下面根据需要可以有多个yaml文件,一般应用使用Deployment就好,除非有以下需要可使用StatefulSet

  • pod之间有唯一性,并非相同副本,需要独立标识(pod id,网络id, pv),更新后不变可重用
  • pod固定有序缩放更新

这里以 Deployment 为例说明

创建templates/deployment.yaml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
apiVersion: apps/v1
kind: Deployment
metadata:
  # Deployment名字  "example-java.fullname" 相当于helm install用的chart name
  name: 
  labels:
spec:
  replicas: 
  selector:
    matchLabels:
      # 资源之间通过标签找到对方 例如:replicaCount>1 , service就可以匹配多个pod,为它们提供服务
  template:
    metadata:
      labels:
        # 定义一些标签方便匹配
    spec:
      containers:
        - name: -backend
          image: ""
          imagePullPolicy: 
          # 获取values里面配置,配置环境变量
          env:
            - name: MYSQL_USERNAME
              # 要注意string和数字类型的区别,填写应答时候填写纯数字yaml会默认是数字类型,例如password需要string类型,就会报错所以需要使用quote或者直接加引号强转成string
              value: 
            - name: MYSQL_PASSWORD
              value: 
            - name: MYSQL_ADDRESS
              value: 
            - name: MYSQL_DBNAME
              value: ""
          ports:
            - name: backend
              containerPort: 9012 # 应用监听端口
              protocol: TCP
          resources:

关于复杂程序多个服务之间依赖的问题

  • cointainer之间用127.0.0.1:containerPort访问
  • pod之间用service_name:service_port访问

创建templates/service.yaml 关于service更详细配置概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
  name: 
  labels:
spec:
  type: 
  ports:
    - port:  # service port
      targetPort: backend # 容器端口 不设置默认和port一个值, 这里可以是deployment.yaml ports里面 name 或者 containerPort ,所以填9012也可以
      # nodePort: 节点端口 不设置随机 部署好可以看到 一般范围30000~32767
      protocol: TCP
      name: http
  selector:

关于应用配置

  • 小的,1M以内,文件数也少,希望可以随时变的可以使用ConfigMap,如果是密钥之类可以用Secret
1
2
3
4
5
# 生成ConfigMap配置
kubectl create configmap [configmap 名字] --from-file=[目录或文件] --dry-run -o yaml > [配置名].yaml

# example
kubectl create configmap redis-config --from-file=web --dry-run -o yaml > redis-config-configmap.yaml

添加挂载例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: apps/v1
kind: Deployment
metadata:
....
spec:
    ....
    template:
       ....
        spec:
            containers:
              - name: xxxx
                .....
                volumeMounts: #挂载到容器
                    - name: conf
                      mountPath: /docker-entrypoint-initdb.d #挂载container目录
										- name: sshsecret
                      mountPath: /root/.ssh
            volumes:
              - name: conf
                configMap:
                    name: mysql-initdb-config #指定为configmap文件
							- name: sshsecret
                secret:
                    secretName: secret-name
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-initdb-config
data:
  initdb.sql: |
    CREATE TABLE friends (id INT, name VARCHAR(256), age INT, gender VARCHAR(3));
    INSERT INTO friends VALUES (1, 'John Smith', 32, 'm');
    INSERT INTO friends VALUES (2, 'Lilian Worksmith', 29, 'f');
    INSERT INTO friends VALUES (3, 'Michael Rupert', 27, 'm');
  • 大文件,文件结构复杂,不变的/静态配置

    可以在镜像构建阶段,用COPY配置到指定目录

打包chart

不要用压缩软件生成tgz

1
2
# 打包 得到tgz文件
helm package example-java

debug写好的helm chart

1
2
# 测试 语法格式错误等等,输出渲染好的k8s应用配置
helm template --dry-run  example-java-0.1.0.tgz

生成tgz文件

使用helm package详解生成tgz文件

1
helm package 目录

不要使用tar -czvf xxx.tgz dir 之类的压缩命令,否则接口会500

标签和注释

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

  • Kubernetes使用它来识别这种资源
  • 为了查询系统,暴露给Operator会很有用

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

Helm钩子都是注释。

| 名称 | 状态 | 描述 | | :—————————– | :— | :———————————————————– | | 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或块用于构建一个应用程序时。比如,应用软件和数据库生成一个网站。这可以设置为受支持的顶级应用程序。 |

You can find more information on the Kubernetes labels, prefixed with app.kubernetes.io, in the Kubernetes documentation.