This page looks best with JavaScript enabled

k8s - helm

使用helm管理kubernetes的应用

 ·  ☕ 7 min read

本文将介绍如何helm安装kubernetes的应用,以及如何使用helm构建自己的应用。

使用helm管理kubernetes应用

Helm是一个kubernetes应用的包管理工具,用来管理 charts– 预先配置好的安装包资源,有点类似于Ubuntu的APT和CentOS中的yum。

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

Helm和charts的主要作用:

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

安装helm

前提要求

  • Kubernetes1.5以上版本
  • 集群可访问到的镜像仓库
  • 执行helm命令的主机可以访问到kubernetes集群

安装步骤

首先需要安装helm客户端

1
2
3
curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh
chmod 700 get_helm.sh
./get_helm.sh

创建tiller的serviceaccountclusterrolebinding

1
2
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

然后安装helm服务端tiller

1
helm init 

(目前最新版v2.8.2,可以使用阿里云镜像,如: helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.8.2 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

我们使用-i指定自己的镜像,因为官方的镜像因为某些原因无法拉取,官方镜像地址是:gcr.io/kubernetes-helm/tiller:v2.8.1,我在DockerHub上放了一个备份jimmysong/kubernetes-helm-tiller:v2.8.1,该镜像的版本与helm客户端的版本相同,使用helm version可查看helm客户端版本。

为应用程序设置serviceAccount

1
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

检查是否安装成功:

1
2
3
4
5
6
$ kubectl -n kube-system get pods|grep tiller
tiller-deploy-dbb85cb99-xwgpd          1/1     Running   0          16s

$ helm version
Client: &version.Version{SemVer:"v2.12.3", GitCommit:"eecf22f77df5f65c823aacd2dbd30ae6c65f186e", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.12.3", GitCommit:"eecf22f77df5f65c823aacd2dbd30ae6c65f186e", GitTreeState:"clean"}

安装自己的chart

我们创建一个名为mychart的chart,看一看chart的文件结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ helm create mychart

$ tree mychart
mychart
|-- charts # 依赖的chart
|-- Chart.yaml # Chart本身的版本和配置信息
|-- templates # 配置模板目录
|   |-- deployment.yaml #kubernetes Deployment object
|   |-- _helpers.tpl #用于修改kubernetes objcet配置的模板
|   |-- ingress.yaml #kubernetes ingress
|   |-- NOTES.txt #helm提示信息
|   |-- service.yaml #kubernetes Serivce
|   `-- tests
|       `-- test-connection.yaml
`-- values.yaml #kubernetes object configuration

3 directories, 8 files

模板

Templates目录下是yaml文件的模板,遵循Go template语法。使用过Hugo的静态网站生成工具的人应该对此很熟悉。

我们查看下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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    app.kubernetes.io/name: {{ include "mychart.name" . }}
    helm.sh/chart: {{ include "mychart.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "mychart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: {{ include "mychart.name" . }}
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}

这是该应用的Deployment的yaml配置文件,其中的双大括号包扩起来的部分是Go template,其中的Values是在values.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
37
38
39
40
41
42
43
44
45
46
47
48
49
# Default values for mychart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: nginx
  tag: 1.11
  pullPolicy: IfNotPresent

nameOverride: ""
fullnameOverride: ""

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  paths: []
  hosts:
    - chart-example.local
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources:
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

nodeSelector: {}

tolerations: []

affinity: {}

比如在Deployment.yaml中定义的容器镜像image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"其中的:

  • .Values.image.repository就是nginx
  • .Values.image.tag就是stable

以上两个变量值是在create chart的时候自动生成的默认值。

我们将默认的镜像地址和tag改成我们自己的镜像nginx:1.11

检查配置和模板是否有效

当使用kubernetes部署应用的时候实际上讲templates渲染成最终的kubernetes能够识别的yaml格式。

使用helm install --dry-run --debug <chart_dir>命令来验证chart配置。该输出中包含了模板的变量配置与最终渲染的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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
$ helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '35273'

[debug] SERVER: "127.0.0.1:35273"

[debug] Original chart version: ""
[debug] CHART PATH: /root/k8s/helm/mychart

NAME:   handy-antelope
REVISION: 1
RELEASED: Tue Feb 26 20:46:10 2019
CHART: mychart-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
fullnameOverride: ""
image:
  pullPolicy: IfNotPresent
  repository: nginx
  tag: 1.11
ingress:
  annotations: {}
  enabled: false
  hosts:
  - chart-example.local
  paths: []
  tls: []
nameOverride: ""
nodeSelector: {}
replicaCount: 1
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi
service:
  port: 80
  type: ClusterIP
tolerations: []

HOOKS:
---
# handy-antelope-mychart-test-connection
apiVersion: v1
kind: Pod
metadata:
  name: "handy-antelope-mychart-test-connection"
  labels:
    app.kubernetes.io/name: mychart
    helm.sh/chart: mychart-0.1.0
    app.kubernetes.io/instance: handy-antelope
    app.kubernetes.io/managed-by: Tiller
  annotations:
    "helm.sh/hook": test-success
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args:  ['handy-antelope-mychart:80']
  restartPolicy: Never
MANIFEST:

---
# Source: mychart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: handy-antelope-mychart
  labels:
    app.kubernetes.io/name: mychart
    helm.sh/chart: mychart-0.1.0
    app.kubernetes.io/instance: handy-antelope
    app.kubernetes.io/managed-by: Tiller
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: mychart
    app.kubernetes.io/instance: handy-antelope
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: handy-antelope-mychart
  labels:
    app.kubernetes.io/name: mychart
    helm.sh/chart: mychart-0.1.0
    app.kubernetes.io/instance: handy-antelope
    app.kubernetes.io/managed-by: Tiller
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: mychart
      app.kubernetes.io/instance: handy-antelope
  template:
    metadata:
      labels:
        app.kubernetes.io/name: mychart
        app.kubernetes.io/instance: handy-antelope
    spec:
      containers:
        - name: mychart
          image: "nginx:1.11"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            limits:
              cpu: 100m
              memory: 128Mi
            requests:
              cpu: 100m
              memory: 128Mi

我们可以看到Deployment和Service的名字前半截由两个随机的单词组成,最后才是我们在values.yaml中配置的值。

部署到kubernetes

在 mychart 目录下执行下面的命令将 nginx 部署到 kubernetes 集群上。

 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
$ helm install .
NAME:   pining-woodpecker
LAST DEPLOYED: Tue Feb 26 20:51:30 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Deployment
NAME                       DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
pining-woodpecker-mychart  1        0        0           0          0s

==> v1/Pod(related)
NAME                                        READY  STATUS             RESTARTS  AGE
pining-woodpecker-mychart-5779b8bd49-cvtbr  0/1    ContainerCreating  0         0s

==> v1/Service
NAME                       TYPE       CLUSTER-IP    EXTERNAL-IP  PORT(S)  AGE
pining-woodpecker-mychart  ClusterIP  10.1.165.110  <none>       80/TCP   0s


NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=pining-woodpecker" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:80

现在nginx已经部署到kubernetes集群上,本地执行提示中的命令在本地主机上访问到nginx实例。

1
2
3
export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=pining-woodpecker" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl port-forward $POD_NAME 8080:80	

在本地访问http://127.0.0.1:8080即可访问到nginx。

查看部署的relaese

1
2
3
4
$ helm list
NAME             	REVISION	UPDATED                 	STATUS  	CHART        	APP VERSION	NAMESPACE
pining-woodpecker	1       	Tue Feb 26 20:51:30 2019	DEPLOYED	mychart-0.1.0	1.0        	default

删除部署的release

1
helm delete pining-woodpecker

打包分享

我们可以修改Chart.yaml中的helm chart配置信息,然后使用下列命令将chart打包成一个压缩文件。

$ helm package .
Successfully packaged chart and saved it to: /root/k8s/helm/mychart/mychart-0.1.0.tgz

依赖

我们可以在~/k8s/helm/mychart/requirements.yaml中定义应用所依赖的chart,例如定义对mariadb的依赖:

1
2
3
4
dependencies:
- name: mariadb
  version: 0.6.0
  repository: https://kubernetes-charts.storage.googleapis.com

使用helm lint .命令可以检查依赖和模板配置是否正确。

$ helm lint .
==> Linting .
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, no failures

$ helm dep list
NAME   	VERSION	REPOSITORY                                      	STATUS
mariadb	0.6.0  	https://kubernetes-charts.storage.googleapis.com	missing

安装源

使用第三方chat库

  • 添加fabric8库
1
$ helm repo add fabric8 https://fabric8.io/helm
  • 搜索fabric8提供的工具(主要就是fabric8-platform工具包,包含了CI、CD的全套工具)
1
$ helm search fabric8

我们在前面安装chart可以通过HTTP server的方式提供。

1
2
3
$ helm serve
Regenerating index. This may take a moment.
Now serving you on 127.0.0.1:8879

访问http://localhost:8879可以看到刚刚安装的chart。

注意事项

下面列举一些常见问题,和在解决这些问题时候的注意事项。

服务依赖管理

所有使用helm部署的应用中如果没有特别指定chart的名字都会生成一个随机的Release name,例如romping-frogsexy-newton等,跟启动docker容器时候容器名字的命名规则相同,而真正的资源对象的名字是在YAML文件中定义的名字,我们成为App name,两者连接起来才是资源对象的实际名字:Release name-App name

而使用helm chart部署的包含依赖关系的应用,都会使用同一套Release name,在配置YAML文件的时候一定要注意在做服务发现时需要配置的服务地址,如果使用环境变量的话,需要像下面这样配置。

1
2
3
env:
 - name: SERVICE_NAME
   value: "{{ .Release.Name }}-{{ .Values.image.env.SERVICE_NAME }}"

这是使用了Go template的语法。至于{{ .Values.image.env.SERVICE_NAME }}的值是从values.yaml文件中获取的,所以需要在values.yaml中增加如下配置:

1
2
3
image:
  env:
    SERVICE_NAME: k8s-app-monitor-test

解决本地chart依赖

在本地当前chart配置的目录下启动helm server,我们不指定任何参数,直接使用默认端口启动。

1
helm serve

将该repo加入到repo list中。

1
helm repo add local http://localhost:8879

在浏览器中访问http://localhost:8879可以看到所有本地的chart。

然后下载依赖到本地。

1
helm dependency update

这样所有的chart都会下载到本地的charts目录下。

设置helm命令自动补全

为了方便helm命令的使用,helm提供了自动补全功能,如果使用zsh请执行:

1
2
source <(helm completion zsh)
source <(helm completion zsh | sed -E 's/\["(.+)"\]/\[\1\]/g')

如果使用bash请执行:

1
source <(helm completion bash)

原文链接:https://jimmysong.io/kubernetes-handbook/practice/helm.html

Share on

tux
WRITTEN BY
tux
devops

What's on this Page