Why Helm

日常操作kubernetes 的编排,一般都是以下几个步骤:

创建namespace->创建对应的rs(deployment,statefulset)->创建对应的service->创建对应的Ingress

可能有些app还需要pv/pvc或者secretmap之类。纯靠手工管理简直反人类,所以一般的云平台都会直接调用client-go进行各类resource的创建,通过提前预置的信息作为模板调用K8S API,实现统一管理,该做法比较正规,但是对于云平台的开发者来说,需要在云平台设计之初就规划好各类resource对象的模板,一旦遇到特殊化的需求,会比较被动。

那么有人会想到,能不能像docker 一样,让一个app的所有需要使用到的K8S资源全部打包在一起,然后云平台只要管理这个包就可以了,就像docker 的镜像仓库一样,开发想部署哪个应用,直接把包拉下来即可部署,啥service,configmap全给整上,想升级也就一起升级,想回退也可以一起回退,省的每次发布都要提心吊胆的看着revision 逐个资源进行回退,于是就有了helm,如果说docker是应用环境的打包,那么helm就是基于k8s的资源打包,在更高的维度解决了上述问题。

名词解释
  • chart:包含了创建Kubernetes的一个应用实例的必要资源
  • tiller:包含了应用发布配置信息
  • release:是一个chart的运行实例
  • helm: cli,通过 gRPC 协议与 tiller 进行交互,主要提供了增删查改 chart, release 和 repository 相关的功能
  • repo: 存放chart的源
结构图:

找一个网上比较清晰的图:
在这里插入图片描述

日常操作
安装

helm 是直接二进制安装的,直接基于cobra编写的,所以下载下来放在$PATH下即可

tiller 同样也可以二进制安装,但还是推荐使用容器化部署,不过在初始化前需要创建对应的rbac权限

apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system

创建rbac

kubectl create -f tiller_rbac.yml

然后安装tiller

helm init

该操作会首先创建一个tillerr的deployment,同时会在~/.helm 下生成对应目录,里面存放了对应的repo源的信息以及cache等,如果产线无法访问外网,一般会失败,所以一般可以直接到处yml,然后自己修改下,再create 一下即可

helm init --service-account tiller -o yaml >  tiller_deployment.yml
#修改yml的image,指向本地docker镜像
kubectl create -f tiller_deployment.yml

查看是否完成安装

helm version

添加本地repo,repo其实就是个简单的http 服务器,根目录有个index.yaml用于记录repo的各类版本,chart信息,可以通过helm 直接生成并启动,产线可以helm 生成后,自己启个nginx 作为repo的http服务。

helm serve &
helm repo add local http://127.0.0.1:8879/charts

如果要使用外部源,直接修改repo add的url即可

chart编写

helm 解决了K8S单应用多资源的整合,我们可以create 一个chart来看下目录结构

helm create mychart
tree mychart

mychart                                   - chart 包目录名
├── charts                              - 依赖的子包目录,里面可以包含多个依赖的chart包
├── Chart.yaml                          - chart定义,可以定义chart的名字,版本号信息。
├── templates                           - k8s配置模版目录, 我们编写的k8s配置都在这个目录, 除了NOTES.txt和下划线开头命名的文件,其他文件可以随意命名。
│   ├── deployment.yaml
│   ├── _helpers.tpl                    - 下划线开头的文件,helm视为公共库定义文件,主要用于定义通用的子模版、函数等,helm不会将这些公共库文件的渲染结果提交给k8s处理。
│   ├── ingress.yaml
│   ├── NOTES.txt                       - chart包的帮助信息文件,执行helm install命令安装成功后会输出这个文件的内容。
│   └── service.yaml
└── values.yaml                         - chart包的参数配置文件,模版可以引用这里参数。

可以看到template 里有很多资源文件,这些只是个demo,比如里面的deployment.yml完全可以删除,改成自己需要的即可
假设我的这个app只需要service, deployment, ingress 这3种资源,那么删除掉template里的其他yml,生成对应的mychart_deployment.yml, mychart_service.yml, mychart_ingress.yml即可

tree mychart

mychart
├── charts 
├── Chart.yaml 
├── templates  
│   ├── mychart_deployment.yml
│   ├── _helpers.tpl 
│   ├── mychart_ingress.yml
│   ├── NOTES.txt 
│   └── mychart_service.yml
└── values.yaml  

一般的想法是这时候是不是只要把之前写的一些yml丢进去就可以了?如果仅仅是这样的话,那每次变更不是还是需要把这些yml 重写一遍?注意到外面那个values.yaml 了么?可以理解为values.yml 是template下所有资源的配置文件,templates下的资源为文件均支持模板语言,什么是模板语言?玩过ansible的playbook或者写过django的同学还记得jinja2 么?go也有类似的, “text/template” 就是干这活的。
举个栗子

cat mychart_deployment.yml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: {{ .Values.depname }}
  labels:
    chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: {{ .Values.depname }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - containerPort: {{ .Values.service.internalPort }}
        livenessProbe:
          httpGet:
            path: /
            port: {{ .Values.service.internalPort }}
        readinessProbe:
          httpGet:
            path: /
            port: {{ .Values.service.internalPort }}
        resources:
{{ toyaml .Values.resources | indent 12 }}

不用猜,花括号里的就是对应的变量,这些变量有些是内置变量,有些是写在values.yaml里的,有些是写在chart.yaml里的

比如.Values.replicaCount

在values.yaml里就有,.Values.service.internalPort 这一类同理,只是深度不同而已。

depname: mychartdep
replicaCount: 1
image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent
service:
  name: nginx
  type: ClusterIP
  externalPort: 80
  internalPort: 80
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

除此之外,还有一些内置的变量,如:

Release.Name    release 名称,就是通过helm install -n lamp-server ./lamp-chart  指定的的release 名字 “lamp-server”
Release.Time    release 创建的时间
Release.Revision    release 版本号
Release.IsUpgrade   返回boolen,true表示该release发布或者回滚了
Release.IsInstall   返回boolen,true表示该release已安装了

内置的变量还有很多,可以自己查阅一下,细心的同学会发下最后2个是2个布尔值,难道还能用来判断?当然可以,类似puppet的pp一样,helm可以在资源文件里做一些简单的判断,甚至可以在表达式里赋予变量

#变量名以$开始命名, 赋值运算符是 := (冒号+等号)

{{- $relname := .Release.Name -}}

引用自定义变量:
#不需要 . 引用
{{ $relname }}

#简单的判断方法
{{ if 条件表达式 }}
# Do something
{{ else if 条件表达式 }}
# Do something else
{{ else }}
# Default case
{{ end }}

另外还有一些内置的函数以及管道符,比如上述文件的{{ .Chart.Name }}-{{ .Chart.Version | replace “+” “_” }}
其实就是一个很简单的管理符处理,然后将+ 替换为-号。

当然还有很多其他的用法,比如with 限定作用域,range 遍历数组等等,可以去helm的中文文档上进行查阅
http://www.coderdocument.com/docs/helm/v2/developing_templates/intro.html
最后就是子模板,上面有提到_开头的均是子模板,那么子模板是干啥的呢?
我们日常写yaml的时候经常遇到一个很苦恼的问题,就是很多东西需要重复写,在deployment里写了,还要再service里写,比如namespaces, label里的各类标签等,而这些在多个资源文件里需要重复利用的,可以写在子模板里

#定义子模板_mylabel.tpl
{{- define "mychart.app" -}} #定义子模板名称
app_name: {{ .Chart.Name }} #定义模板内容
app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
{{- end -}}
 
#资源模板文件 mychart_configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{ include "mychart.app" . | nindent 4 }} #引用mychart.app模版内容,并对输出结果缩进4个空格
data:
  myvalue: "good"
调试上线

写完chart 后,我们如何检查自己写的是否正确呢
helm lint #可以检查语法

确认语法没问题后,可以进行调试
helm install . --dry-run --debug ./mychart # --debug --dry-run 两个参数,让helm输出模版结果,但是不把模版输出结果交给k8s处理
注意,新chart用的是install,更新用的是upgrade

确认无误后,打包到repo
helm package ./mychart

repo 内可以看到
helm search

查看release
helm ls

更新k8s的release
helm upgrade $release ./mychart

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐