helm介绍与使用

时间:2022-07-22
本文章向大家介绍helm介绍与使用,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

简介

helm是k8s的一个包管理工具,可以简化k8s应用的部署和管理,可以理解为yum和或者apt等包管理工具。 helm有几个非常重要的概念

  • Chart helm的软件包,采用TAR格式,类似于yum的rpm包,包含了一组定义k8s资源的相关yaml文件
  • Repository helm的软件仓库,本质上可以理解为一个web服务器,该服务器保存了一系列的Chart软件包供用户下载,并且提供了一个Repository的Chart包的清单文件以供查询,Helm可以同时管理多个不同的Repository
  • Release 使用helm install 命令在k8s集群中部署的Chart成为Release,可以理解为helm使用Chart包部署的一个应用实例
  • helm helm是一个命令行下的客户端工具,主要用于k8s应用程序Chart的创建、打包、发布、创建、以及管理本地和远程的Chart仓库
  • Tiller tiller是helm的服务端,部署在k8s集群中,tiller用于接收helm的请求,并根据Chart生成k8s的部署文件,即Release,然后提交给k8s创建应用,Tiller还提供了Release的升级、删除、 回滚等功能

Chart安装、升级、回滚过程

安装过程

  1. helm从指定的目录或者TAR文件解析出Chart结构信息
  2. helm将指定的Chart结构和Values信息通过gRPC传递给Tiller
  3. Tiller根据Chart和Values生成一个Release
  4. Tiller将Release发送给k8s用于生成Release

升级过程

  1. Helm从指定的目录或者TAR文件中解析出Chart结构信息
  2. Helm将需要更新的Release的名称、Chart结构和Values信息传递给Tiller
  3. Tiller生成Release并更新指定名称的Release的History
  4. Tiller将Release发送给k8s用于更新Release

回滚过程

  1. Helm将要回滚的Release的名称传递给Tiller
  2. Tiller根据Release的名称查找History
  3. Tiller从History中获取上一个Release
  4. Tiller将上一个Release发送给k8s用于替换当前的Release

helm简单使用

创建一个chart

helm create myapp

查看目录结构

# tree myapp/
    myapp/
    ├── charts
    ├── Chart.yaml
    ├── templates
    │   ├── deployment.yaml
    │   ├── _helpers.tpl
    │   ├── ingress.yaml
    │   ├── NOTES.txt
    │   ├── serviceaccount.yaml
    │   ├── service.yaml
    │   └── tests
    │       └── test-connection.yaml
    └── values.yaml

目录结构介绍

  • Chart.yaml 用于描述这个 Chart的相关信息,包括名字、描述信息以及版本等。
  • values.yaml 用于存储 templates 目录中模板文件中用到变量的值。
  • NOTES.txt 用于介绍 Chart 部署后的一些信息,例如:如何使用这个 Chart、列出缺省的设置等。
  • Templates 目录下是 YAML 文件的模板,该模板文件遵循 Go template 语法

创建仓库

可以找一台机器用来作为chart的存储仓库,类似于harbor一样,当然helm也可以使用helm作为仓库来存储chart,但这里还是通过将chart存储到本地目录里,然后通过web把改目录映射出去即可

helm serve --address 192.168.0.130:8879 --repo-path /data/helm/repository/ --url http://192.168.0.130:8879/charts/ &
helm repo index /data/helm/repository --url http://192.168.0.130:8879

–repo-path 指定存储chart的目录 –address 指定仓库地址 这样我们就把仓库创建好了,下面会涉及到打包应用以及将应用放到仓库里的操作。

客户端初始化配置

在本地打包应用之前需要先初始化一下,否则会打包失败报错

Error: open /home/jenkins/.helm/repository/local/index.yaml: no such file or directory

初始化操作

helm init --client-only

打包应用

前面创建了一个myapp的应用,现在需要将他打包

# 需要进到myapp的同级目录
helm package myapp

打包完成会生成一个tar包,将此tar包拷贝到helm仓库中

cp myapp-0.1.0.tgz /data/helm/repository/

拷贝完成后可以通过访问192.168.0.130:8879看到我们的myapp这个应用包

添加远程仓库并启动一个release

添加远程仓库

在k8s的目标主机上执行此命令来完成远程仓库的添加
helm repo add cicd http://192.168.0.130:8879/charts

添加完成后执行下update
helm repo update

安装一个release
helm install -f values.yaml test-helm cicd/myapp  

test-helm即为release的名称

删除release
helm delete --purge test-helm

内置对象

helm内置了一些对象,这些对象可以从模板引擎传递到模板中,这样我们在使用的时候就可以通过传入不通的参数来完成多个应用的部署操作了 下面介绍两个常用的对象

  • Release
  • Values

内置对象一般首字母大写,Release对象描述了release本身,Release对象中又包含了几个子对象:

Release  
Release.Name     release的名称  
Release.Time     release的时间  
Release.Namespace     release的namespace  
Release.Revision     此release的修订版本号,从1开始,每helm upgrade一次,就会增加1

Values对象是从values.yaml文件中读取或者命令行传入的值里传入模板中,而Values中我们可以自定义一些类似于变量的东西,类似于下面的示例:

replicaCount: 1
image:
  repository: harbor.devilf.cc
  tag: latest
cat deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
  labels:
    name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicas}} 
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  selector:
    matchLabels:
      name: {{ .Release.Name }}
  template:
    metadata:
      labels:
        name: {{ .Release.Name }}
    spec:
      containers:
        - name: {{ .Release.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: IfNotPresent

知道这两个基本上就足够用了,Values这个对象非常的常用和灵活,可以从上面的deployment类型的资源文件里看到,如同变量替换一样方便,我们以后部署的时候再也不会通过使用sed或者其他操作来完成镜像地址的替换了!

示例

通常在k8s里部署一个pod时,需要一个deployment类型的文件,一个service类型的文件,有时还需要ingress类型的文件,如果只有几个应用的话我们可以通过脚本或者手动的方式去写yaml文件来完成部署操作,但如果项目非常多,这个时候对于yaml文件的管理将会非常不便!!! 下面以一个java程序为例,通过制作一个deployment类型的模板和service类型的模板,来完成该项目的部署

准备好Chart

cat myapp/templates/app-deployment.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
  labels:
    name: {{ .Release.Name }}
spec:
  replicas: {{ .Values.replicas}} 
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  selector:
    matchLabels:
      name: {{ .Release.Name }}
  template:
    metadata:
      labels:
        name: {{ .Release.Name }}
    spec:
      containers:
        - name: {{ .Release.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: {{ .Values.resources.limits.cpu }}
              memory: {{ .Values.resources.limits.memory }}
            requests:
              cpu: {{ .Values.resources.requests.cpu }}
              memory: {{ .Values.resources.requests.memory }}
          {{ if .Values.volumeMounts.name }}
          volumeMounts:
          - name: {{ .Values.volumeMounts.name }}
            mountPath: {{ quote .Values.volumeMounts.path }} 
            readOnly: false
            subPath: {{ .Values.volumeMounts.subpath }}
          {{ end }}
        {{ if .Values.imageOther.repository }}
        - name: {{ .Release.Name }}-other
          image: "{{ .Values.imageOther.repository }}:{{ .Values.imageOther.tag }}"
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: {{ .Values.resources.limits.cpu }}
              memory: {{ .Values.resources.limits.memory }}
            requests:
              cpu: {{ .Values.resources.requests.cpu }}
              memory: {{ .Values.resources.requests.memory }}
        {{ end }}
          {{ if .Values.livenessProbe.path }}
          livenessProbe:
            httpGet:
              path: {{ .Values.livenessProbe.path }}
              port: {{ .Values.livenessProbe.port }}
            initialDelaySeconds: 60
            periodSeconds: 30
            timeoutSeconds: 60
          {{ end }}
      {{ if .Values.volumeMounts.name }}
      volumes:
      - name: {{ .Values.volumeMounts.name }}
        persistentVolumeClaim:
          claimName: {{ .Values.volumeMounts.name }}
      {{ end }}
      imagePullSecrets:
        - name: {{ .Values.imagePullSecrets }}
cat myapp/templates/app-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}
  labels:
    name: {{ .Release.Name }}
spec:
  ports:
    - name: {{ .Release.Name }}
      protocol: TCP
      targetPort: {{ .Values.containerPort.inner }}
      port: {{ .Values.containerPort.inner }}
      nodePort: {{ .Values.containerPort.outer }}
    {{ if .Values.containerPortOther.inner }}
    - name: {{ .Release.Name }}-other
      protocol: TCP
      targetPort: {{ .Values.containerPortOther.inner }}
      port: {{ .Values.containerPortOther.inner }}
      {{ if .Values.containerPortOther.outer }}
      nodePort: {{ .Values.containerPortOther.outer }}
      {{ end }}
    {{ end }}
  selector:
    name: {{ .Release.Name }}
  sessionAffinity: None
  type: NodePort

以上用到了一些流程控制,有时候可能一个pod会启动两个容器,或者映射多个端口,这时候我们可以使用简单的if 进行流程控制

然后修改values.yaml文件,来满足我们模板中定义的一些对象

cat myapp/values.yaml

replicaCount: 1
image:
  repository:
  tag:
imageOther:
  repository:
  tag:
containerPort:
  inner:
  outer:
containerPortOther:
  innerPort:
  outerPort:
imagePullSecrets: mima
resources:
  limits:
    cpu: 400m
    memory: 4096Mi
  requests:
    cpu: 100m
    memory: 128Mi
volumeMounts:
  name:
  path:
  subpath:
livenessProbe:
  path: 
  port:

chart.yaml参考

cat myapp/Chart.yaml 

apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: myapp
version: 0.1.0

整体的一个目录结构如下

# tree myapp
myapp
├── Chart.yaml
├── templates
│   ├── app-deployment.yaml
│   ├── app-svc.yaml
│   └── _helpers.tpl
└── values.yaml

可以把多余的一些文件或者目录删除掉

打包应用

进入到myapp的同级目录执行
helm package myapp
cp myapp-0.1.0.tgz /data/helm/repository/

k8s主机上安装应用

添加远程仓库地址

helm repo add cicd http://192.168.0.130:8879/charts
helm repo update

配置values.yaml文件,也可以通过--set进行指定 我们先测试指定一个镜像地址

cat values.yaml
replicaCount: 1
image:
  repository: harbor.devilf.cc/onair/user-manager
  tag: 202002101644-2.0.0-SNAPSHOT-1ed8d49
imageOther:
  repository: 
  tag: 
containerPort:
  inner: 9999
  outer: 32222
containerPortOther:
  innerPort: 
  outerPort: 
imagePullSecrets: mima
resources:
  limits:
    cpu: 400m
    memory: 4096Mi
  requests:
    cpu: 100m
    memory: 128Mi
volumeMounts:
  name:
  path:
  subpath:
livenessProbe:
  path: 
  port:

安装应用

# helm upgrade -i -f values.yaml test-helm cicd/myapp

Release "test-helm" does not exist. Installing it now.
NAME:   test-helm
LAST DEPLOYED: Thu Feb 13 15:59:12 2020
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Pod(related)
NAME                        AGE
test-helm-7c8b8584cd-w2vk4  0s

==> v1/Service
NAME       AGE
test-helm  0s

==> v1beta1/Deployment
NAME       AGE
test-helm  0s

下面再测试下两个镜像地址

# cat values.yaml 
replicaCount: 1
image:
  repository: alpha-harbor.yunshicloud.com/onair/user-manager
  tag: 202002101644-2.0.0-SNAPSHOT-1ed8d49
imageOther:
  repository: alpha-harbor.yunshicloud.com/onair/user-api
  tag: 202002110943-2.0.0-SNAPSHOT-5c011f4
containerPort:
  inner: 9999
  outer: 32222
containerPortOther:
  innerPort: 8088
  outerPort: 32223
imagePullSecrets: mima
resources:
  limits:
    cpu: 400m
    memory: 4096Mi
  requests:
    cpu: 100m
    memory: 128Mi
volumeMounts:
  name:
  path:
  subpath:
livenessProbe:
  path: 
  port:

安装方式是一样的,这里先把之前的给删除掉

# helm delete --purge test-helm
删除完成后,添加一个镜像地址,然后再次运行
# helm upgrade -i -f values.yaml test-helm cicd/myapp

这里我请求另一个服务的健康检查接口

也是没有问题的

到这里,基本上helm就可以结合Jenkins完成CD工作了,只需要创建一个values.yaml文件即可。