kubernetes包管理工具 - Helm 3入门到实战(一)

devopscube,Devops魔方为个人公众号,主要用于一些有关Devops,容器,kubernetes,自动化运维,以及敏捷开发相关的分享。同时也会不定期的分享一些个人心得,比如推荐一些个人使用的办公小软件,对一些事件的评论等。欢迎大家关注交流。
在这里插入图片描述

一、介绍

注: 本文需要对kubernetes docker等平台有一定的理解。

kubernetes包管理工具 - Helm 3入门到实战(一)_第1张图片

Helm作为一个kuberentes平台的包管理器,已经基本上成为kubernetes的唯一应用打包工具。

那么helm是什么,为什么需要helm?

Helm相对于kubernetes的关系,就像是apt/yum之于Linux操作系统的关系,也相当于windows上将应用包装成exe文件然后只要在界面上点击需要的参数即可快速安装应用。如果把kubernetes比作操作系统的话,如何在这个“操作系统”之上去快速的安装应用,就变得非常重要。

Helm规定了如何打包kubernetes需要部署的资源,通过Go 语言的模板技术,实现能够将资源中的配置模板化,并对外暴露出可配置的属性值。

任何“打包”的目的都是为了能够在不同的平台去运行已经打包好的资源,所以其优势就是打包好的helm包可以在任意平台去部署,你需要做的只是根据平台的需求,修改相应的输入参数即可。

另外一个好处是便于应用的管理,因为kuberentes的复杂性一直令人诟病,但是也是因为其灵活的设计也很难找到更为适合的方式。成功部署一个复杂应用可能需要很多组件单独部署和配合。你需要编写大量的资源文件。如果需要在一个新的环境中部署,你仍然需要修改大量资源文件中的参数,也容易出错,不容易迁移。

再次,除了应用部署,应用的存储和分发,helm也提供了完整的机制,例如使用helm repo存储相应的应用包,可以在远程访问helm仓库进行helm的部署操作。

最后,helm在部署的同时,也实现了更新以及历史更新的管理。可以在此基础上实现回滚等高级操作。

集合以上优点,我们经常会看到基于helm可以实现各种“一键部署”,一键部署wordpress,一键部署mysql集群等。helm的精髓就在于打包一次,随处(kuberentes平台之上)运行。

Helm刚刚出了第三个大版本(2019.11.13发行3.0.0版本),本文的所有操作都是基于helm3进行的,会在下一篇文章中简单介绍一下helm2 和helm3的区别以及如何迁移。

二、 Helm组件及概念

Helm中的重要概念:

  • Chart: 就是一个用helm打包好的应用。 包含运行该应用的所有资源的定义或工具。
  • Repository(仓库):用于存储和分享打包好的应用(chart)
  • Release(实例):应用在kuberentes中运行的一个实例。一个chart可以在一个集群中多次安装,每次安装都会有一个新的实例。多个实例需要有不同的命名。

Helm的架构很简单,一个客户端Helm Client,一个服务端Helm Library,两者都是使用go语言编写。

  • Helm Client: Helm的命令行客户端helm cli,用于helm的操作
  • Helm Library: helm的服务端,通kuberents api进行交互,用于接收客户端的指令并执行部署,更新,删除和版本的管理。

请注意,helm在helm 3中有重大更新,在helm2版本及以前,helm有一个在集群中运行的服务叫Tiller,需要安装部署在集群中去执行同kuberentes的交互操作。但是在helm3中删除了Tiller这个架构,在客户端的位置几乎重新写了一个类似tiller作用的Library,很多时出于安全性的目的,设计成将所有的同kuberentes api交互的操作都采用kuberentes原生的client进行管理。后面我们将helm2和helm3的区别时也会讲到。

三、 Helm环境部署

同helm2不同,helm3无须再去安装tiller,所以只要在客户端配置好helm即可。

方法也很简单:

  • 二进制文件安装
1. 下载指定版本的helm安装包(https://github.com/helm/helm/releases)
2. 解压
# tar -zxvf helm-v3.0.0-linux-amd64.tar.gz
3. 找到helm命令的位置,然后放到希望放到的位置,例如/usr/local/bin,然后在任意位置都可以执行helm命令了
# mv linux-amd64/helm /usr/local/bin/helm

其他的安装方式请参考: https://helm.sh/docs/intro/install/

同时你也需要安装kubectl并配置好相应的kubeconfig文件,保证kubectl命令行能够访问到集群。

配置好之后就可以使用helm的功能了。

四、 Helm Chart编写

我们从编写HelmChart开始,了解Helmchart封装了哪些资源以及如何利用Go模板的技术对资源进行进一步的封装和使用。

首先我们可以利用helm create命令,创建一个简单的示例。helm create的示例chart也并不是一无所有,它包含了大部分的资源抽象,例如kubernetes deployment,service,ingress,serviceaccount等资源。

$ helm create helloworld
Creating hellworld
$ tree hellworld
hellworld
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 9 files

从结构中我们看到有不同级别的文件夹,以及一些yaml文件。

charts: 用于存放其他依赖和关联的chart。例如应用依赖数据库的chart。

Chart.yaml:存储一些元数据,例如chart的信息,描述等等

templates文件夹:是所有资源的位置,我们可以看到很多kubernetes的资源文件都在这里存放。

其中的_helpers.tpl,用于存储模板片段,可以在文件中直接使用template函数调用。

value.yaml:存储该chart的默认值,实际安装时可以对默认值进行覆盖。

NOTES.txt:相当于你运行helm install的时候给用户输出的提示。

Helloworld

程序员最喜欢helloworld,为了力求描述简单,我们清空template文件下的内容,以及value.yaml中的内容

我们添加一个configmap.yaml文件到template目录下

apiVersion: v1
kind: ConfigMap
metadata:
  name: hellworld-configmap
data:
  myvalue: "Hello World"

这是我们运行helm install命令,看到已经部署到集群中了,

$ helm install helloworld ./helloworld/
NAME: helloworld
LAST DEPLOYED: Tue Mar 31 14:52:30 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ kubectl get cm
NAME                  DATA   AGE
helloworld-configmap   1      108s

所以我们看到,helm做的操作就是将template下的kuberentes资源文件部署到集群之中。这是helm最最基本的设计,也是后续所有功能的前提。

尝试开始使用模板

我们修改configmap.yaml,不同的是使用模板的语言。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: {{ .Values.myvalue | quote }}

然后在value.yaml文件中添加一个键值对最为默认值

myvalue: helloworldfromvalue

然后运行helm install,可以看到configmap中的myvalue就是默认值。

$ helm install helloworld ./helloworld/
NAME: helloworld
LAST DEPLOYED: Tue Mar 31 15:08:11 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ kubectl get cm -o yaml
apiVersion: v1
items:
- apiVersion: v1
  data:
    myvalue: helloworldfromvalue
  kind: ConfigMap
  metadata:
    creationTimestamp: "2020-03-31T07:08:11Z"
    name: helloworld-configmap
    namespace: default
    resourceVersion: "3735016"
    selfLink: /api/v1/namespaces/default/configmaps/helloworld-configmap
    uid: 9bf21509-cd29-4e9d-b6ab-bb9dde006bb4
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

我们尝试使用传递值的方式更新value,我们看到configmap中存储的mybalue就改成了我们命令行传进去的值。

$ helm upgrade helloworld ./helloworld/ --set myvalue=helloworldfromcommand
Release "helloworld" has been upgraded. Happy Helming!
NAME: helloworld
LAST DEPLOYED: Tue Mar 31 15:12:10 2020
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None
$ kubectl get cm -o yaml
apiVersion: v1
items:
- apiVersion: v1
  data:
    myvalue: helloworldfromcommand
  kind: ConfigMap
  metadata:
    creationTimestamp: "2020-03-31T07:08:11Z"
    name: helloworld-configmap
    namespace: default
    resourceVersion: "3735330"
    selfLink: /api/v1/namespaces/default/configmaps/helloworld-configmap
    uid: 9bf21509-cd29-4e9d-b6ab-bb9dde006bb4
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

分析一下我们使用的configmap.yaml,我们使用了两种模板的值

{{ .Release.Name }} 这种是helm内部自带的值,都是一些内建的变量,所有人都可以访问

{{ .Values.myvalue | quote }} 这种是我们从values.yaml文件中获取或者从命令行中获取的值。

除了Release,helm还提供了其他内建对象,如:

  • Chart, 在Chart.yaml中定义的变量,如Chart.name,Chart.version等

  • Files,任何Chart目录下的文件都可以通过Files获取,例如ini文件

  • Capabilities, 用于提供kuberentes 集群的一些能力,如Capabilities.APIVersions,Capabilities.KubeVersion.Minor,Capabilities.KubeVersion

关于Value,我们在value.xml中是这样定义的,以及如何在文件中调用该值

# 使用.Values.key调用值
key:value
# 使用.Values.key.key1.key2调用值
key:
  key1:
    key2:value

模板方法和管道

我们在上一节提到的使用自定义变量的方法: {{ .Values.myvalue | quote }},其实是使用到了管道的技术。

模板方法的使用如下,quote是一个模板方法,可以将输入的参数添加双引号。

{{ quote .Values.myvalue }}

模板方法的调用结构是: functionname arg1 arg2 ...

Helm有60多个模板的方法。在实际操作中我们可以记录一些常用的方法。

更多的方法我们可以查看

https://godoc.org/text/template

同样使用管道,我们可以将value传递到方法之上,例如value全部大写后,添加双引号。而且默认值为”helloworld“。

{{ .Values.myvalue | default 'hellworld' | upper | quote }}

使用管道的好处是可以嵌套多个方法,更加容易阅读。

很多计算符号也是方法:

eq`, `ne`, `lt`, `gt`, `and`, `or
例如
# eq方法,判断两者是否相等,其他操作类似
eq arg1 arg2

流控制

经典的流控制包括If-else,range循环。除此之外还有一个with(控制值的相对作用域)

if-else

经典的结构为

{{ if PIPELINE }}
  # Do something
{{ else if OTHER PIPELINE }}
  # Do something else
{{ else }}
  # Default case
{{ end }}

PIPELINE输出的是true/false,用于判断。

例如:

{{ if eq .Values.myvalue "hellworld" }}
result: true
{{ end }}

因为 {{ if eq .Values.myvalue "hellworld" }}{{ end }}都各占了一行,这样实际操作会产生空行,那我们需要一种方式去除空行

{{- if eq .Values.myvalue "hellworld" }}
result: true
{{- end }}
with
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}

默认情况,我们访问值使用.Values.aaa.bbb。 使用with时可以改变当前作用域。例如上面的例子中,改变了值访问的作用域为.Values.favorite. 那么想要访问.Values.favorite.drink,就可以直接with内部写成.drink

range

ragne是针对列表来指定的,如果我们在value.yaml中配置了列表

zoo:
  - tiger
  - lion
  - monkey

我们想用循环的方式读取

{- range .Values.zoo }}
 - {{ . | title | quote }}
{{- end }}

输出结果:
  - "tiger"
  - "lion"
  - "monkey"

变量

模板中也可去定义变量,例如将.Release.Name传递给$relname

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

之后就可以在文件的任意地方调用该变量了

在range中使用变量非常方便,例如,j将列表赋值到key和val两个变量上

{{- range $key, $val := .Values.zoo }}
  {{ $key }}: {{ $val | quote }}
{{- end }}

$符号代表根,例如可以使用 . C h a r t . V e r s i o n , .Chart.Version, .Chart.Version.Release.Name。这样可以打破scope的界限,访问到chart的内建资源。避免因为scope的改变而访问不到(例如在with或range内部)

模板片段

之前我们看到有个文件叫做_helpers.tpl,我们介绍是说存储模板片段的地方。

模板片段其实也可以在文件中定义,但是为了更好管理,可以在_helpers.tpl中定义,使用时直接调用即可。

例如在_helpers.tpl中定义了如下模板片段

{{- define "mychart.labels" }}
  labels:
    generator: helm
    app: helloworld
{{- end }}

我们在configmap.yaml文件中调用如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: helloworld-configmap
  {{- template "mychart.lables" }}
data:
  myvalue: helloworld

实际的输出变成:

apiVersion: v1
kind: ConfigMap
metadata:
  name: helloworld-configmap
  labels:
    generator: helm
    app: helloworld
data:
  myvalue: helloworld

未完待续,请见下期。

你可能感兴趣的:(kubernetes,Devops)