kubernetes应用管理工具插件helm的安装与使用


helm组件结构


Helm 是 C/S 架构,主要分为客户端 helm 和服务端 Tiller。安装时可直接在 Helm 仓库的 Release 页面 下载所需二进制文件或者源码包。
由于存储在GCS中,注意需要科学上网。
下载二进制可执行程序的文件包后解压文件,这里以 Linux amd64 为例。

[root@master helm]# ls -lh
total 26M
-rw-r--r--. 1 root root 26M May 29 11:41 helm-v2.14.0-linux-amd64.tar.gz
drwxr-xr-x. 2 root root  64 May 15 11:44 linux-amd64
[root@master helm]# tree linux-amd64/
linux-amd64/
├── helm
├── LICENSE
├── README.md
└── tiller

0 directories, 4 files
[root@master helm]#

解压完成后,可看到其中包含 helmtiller 二进制文件。

客户端 helm


helm 是个二进制文件,直接将它移动至 /usr/bin 目录下即可。

[root@master helm]# cp linux-amd64/helm /usr/bin/
[root@master helm]# which helm
/usr/bin/helm

这时候便可直接通过 helm 命令使用了。比如,我们验证当前使用的版本。

[root@master helm]# helm version
Client: &version.Version{SemVer:"v2.14.0", GitCommit:"05811b84a3f93603dd6c2fcfe57944dfa7ab7fd0", GitTreeState:"clean"}
Error: could not find tiller
[root@master helm]#

这里解释一下,为什么helm工具直接就可以访问k8s的apiserver了呢?
其实helm是先找当前主机上的kubectl的配置文件(环境变量 $KUBECONFIG 或者 ~/.kube/config 中的内容),通过解析这个来获得apiserver的地址、以及用来访问的证书,以此来访问apiserver。

服务端 Tiller


以下讨论中,前提都是 环境变量$KUBECONFIG或文件$HOME/.kube/config 已正确配置,并且 kebectl 有集群管理员级别操作集群的权限。

本地安装

刚才我们解压的文件中,还有一个二进制文件 tiller 。我们可以直接执行它,用于在本地启动服务。

[root@master linux-amd64]# ./tiller
[main] 2019/05/29 13:27:15 Starting Tiller v2.14.0 (tls=false)
[main] 2019/05/29 13:27:15 GRPC listening on :44134
[main] 2019/05/29 13:27:15 Probes listening on :44135
[main] 2019/05/29 13:27:15 Storage driver is ConfigMap
[main] 2019/05/29 13:27:15 Max history per release is 0

直接执行时,默认会监听 4413444135 端口,44134 端口用于和 helm 进行通信,而 44135 主要是用于做探活的,在部署至 K8S 时使用。
当我们使用客户端连接时,只需设置 HELM_HOST 环境变量即可。

[root@master ~]# export HELM_HOST=localhost:44134
You have new mail in /var/spool/mail/root
[root@master ~]# helm version
Client: &version.Version{SemVer:"v2.14.0", GitCommit:"05811b84a3f93603dd6c2fcfe57944dfa7ab7fd0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.0", GitCommit:"05811b84a3f93603dd6c2fcfe57944dfa7ab7fd0", GitTreeState:"clean"}
[root@master ~]#

默认安装

官方提供了一种一键式安装的方式。那便是 helm init 执行这条命令后,会同时在 K8S 中部署服务端 Tiller 和初始化 helm 的默认目录 $HELM_HOME 默认值为 $HOME/.helm

这种方式下会默认使用官方镜像 k8s.gcr.io/kubernetes-helm/tiller 网络原因可能会导致安装失败。所以我从阿里云的docker镜像仓库上pull下来后重新打了tag。

如果不用这种默认镜像名,也可以在helm init时指定镜像名。
helm init --tiller-image IMAGE_REPOSITORY/tiller:v2.14.0

#!/usr/bin/env bash

images=(
    tiller:v2.14.0
)

for imageName in ${images[@]} ; do
    docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
    docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
    docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done

然后执行helm自动安装命令。

[root@master linux-amd64]# helm init
Creating /root/.helm
Creating /root/.helm/repository
Creating /root/.helm/repository/cache
Creating /root/.helm/repository/local
Creating /root/.helm/plugins
Creating /root/.helm/starters
Creating /root/.helm/cache/archive
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation
You have new mail in /var/spool/mail/root
[root@master linux-amd64]#

再次执行 helm version 测试服务端 tiller 是否成功安装。

[root@master linux-amd64]# helm version
Client: &version.Version{SemVer:"v2.14.0", GitCommit:"05811b84a3f93603dd6c2fcfe57944dfa7ab7fd0", GitTreeState:"clean"}
Error: could not find a ready tiller pod
You have new mail in /var/spool/mail/root
[root@master linux-amd64]#

发现 tiller 并没有成功部署。
于是调查了一下 deployment 的状态,发现 image 的名称和我刚才打的tag不一致……

[root@master linux-amd64]# kubectl get deploy -o wide --all-namespaces | sed -n '1p;/tiller/Ip'
NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS                IMAGES                                  SELECTOR
kube-system   tiller-deploy             0/1     1            0           32m     tiller                    gcr.io/kubernetes-helm/tiller:v2.14.0   app=helm,name=tiller
[root@master linux-amd64]#

重新打一下tag。

[root@master linux-amd64]# docker images | sed -n '1p;/tiller/Ip'
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
k8s.gcr.io/tiller                    v2.14.0             a982228e2db1        2 weeks ago         94.2MB
[root@master linux-amd64]# docker tag k8s.gcr.io/tiller:v2.14.0 gcr.io/kubernetes-helm/tiller:v2.14.0 && \
> docker rmi k8s.gcr.io/tiller:v2.14.0
Untagged: k8s.gcr.io/tiller:v2.14.0
[root@master linux-amd64]# docker images | sed -n '1p;/tiller/Ip'
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
gcr.io/kubernetes-helm/tiller        v2.14.0             a982228e2db1        2 weeks ago         94.2MB
[root@master linux-amd64]#

由于我的环境是一个master加一个sysnode,我想在sysnode上运行集群相关的addons,所以将 tiller 的镜像导出出来,传到sysnode上导入。

sysnode
sysnode这个role的名字是通过给node打一个topology keynode-role.kubernetes.io/sysnode=sysnode,这样实现的。
[root@master linux-amd64]# kubectl get no
NAME      STATUS   ROLES     AGE     VERSION
master    Ready    master    6d10h   v1.14.0
sysnode   Ready    sysnode   6d9h    v1.14.0
[root@master linux-amd64]# kubectl get no --show-labels | sed -n '1p;/sysnode/Ip'
NAME      STATUS   ROLES     AGE     VERSION   LABELS
sysnode   Ready    sysnode   6d9h    v1.14.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=sysnode,kubernetes.io/os=linux,node-role.kubernetes.io/sysnode=sysnode
[root@master linux-amd64]#

在sysnode上导入镜像后tiller成功部署了。
helm 客户端也成功连接上 tiller了。

[root@master linux-amd64]# kubectl get deploy -o wide -n kube-system | sed -n '1p;/tiller/Ip'
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS                IMAGES                                  SELECTOR
tiller-deploy             1/1     1            1           61m     tiller                    gcr.io/kubernetes-helm/tiller:v2.14.0   app=helm,name=tiller
[root@master linux-amd64]# helm version
Client: &version.Version{SemVer:"v2.14.0", GitCommit:"05811b84a3f93603dd6c2fcfe57944dfa7ab7fd0", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.0", GitCommit:"05811b84a3f93603dd6c2fcfe57944dfa7ab7fd0", GitTreeState:"clean"}
You have new mail in /var/spool/mail/root
[root@master linux-amd64]#

手动安装

通过上面的描述,可能你已经发现,安装服务端,其实也就是一次普通的部署,我们可以通过以下方式来自行通过 kubectl 完成部署。

[root@master linux-amd64]# helm init --dry-run --debug # 篇幅原因,以下内容进行了省略
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: helm
    name: tiller
  name: tiller-deploy
  namespace: kube-system
spec:
  replicas: 1
  strategy: {}
  ...
status: {}

---
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: helm
    name: tiller
  name: tiller-deploy
  namespace: kube-system
spec:
  ports:
  - name: tiller
    port: 44134
    targetPort: tiller
  selector:
    app: helm
    name: tiller
  type: ClusterIP
status:
  loadBalancer: {}

...
[root@master linux-amd64]#

将输出内容保存至文件中,自行修改后,通过 kubectl 进行部署即可。建议在修改过程中,尽量不要去更改标签及选择器。

修改默认的stable repository

由于国内网络环境,我们可以在helm init的时候指定一个stable repo url,而不用默认的Google的repo。
helm init --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts ... # 这里是使用的阿里的repo
如果已经配置了默认的stable repo,想要修改,可以参照官方文档的helm repo命令。

关于helm的安全性问题

如何安全地安装helm

我们应该注意到,现在helm客户端和tiller服务端都已经安装好,并且helm客户端是通过当前节点上kubectl的配置文件中设置的权限来执行对apiserver的操作的,这在专用的集群下,没有问题,如果你和其他用户共享这个集群,并且不论是不是专用的集群,如果是生产环境下使用tiller,强烈建议遵照 helm 官方secure指引,配置必须的安全选项。
大致上来说,和tiller默认的安装(部署)方式有以下区别:

  • release的数据并不会以configmap的形式存放于集群中,而是以secret的形式存放。
  • tiller的api服务启用tls认证。
  • 给tiller服务分配一个有合适权限的serviceAccount。

关于为什么要这么做,以及由此可以拓展出在集群内部署拥有操作apiserver的应用时的安全性考虑

  • 在实际生产环境应用中,往往需要配置一个客户端(kubectl或某种编程语言的k8s client)控制多个k8s集群的情况,如果有高手通过某些途径获得了其中一个k8s集群的地址,就能访问到没有做安全限制的tiller的api,从而达到绕过k8s本身的rbac权限控制的情况。
  • 假如业务应用运行在k8s上,此应用又被黑客控制了,就可以通过这个应用获得访问apiserver的权限,进而操纵没有认证控制的tiller。

RBAC 使用


上面的内容中,均未提及到权限控制相关的内容,但是在生产环境中使用,我们一般都是会进行权限控制的。
这里我们创建一个 ServiceAccount 命名为 tiller,为了简单,我们直接将它与 cluster-admin 进行绑定。

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

将此内容保存为 tiller-rbac.yaml,开始进行部署操作。

[root@master helm]# kubectl apply -f tiller-rbac.yaml
serviceaccount/tiller created
clusterrolebinding.rbac.authorization.k8s.io/tiller created

更新一下tiller的设置(此处为在deployment中设置serviceaccount)。

[root@master helm]# helm init --service-account tiller --history-max 200 --upgrade
$HELM_HOME has been configured at /root/.helm.

Tiller (the Helm server-side component) has been upgraded to the current version.
You have new mail in /var/spool/mail/root
[root@master helm]# kubectl get deploy tiller-deploy -o yaml -n kube-system | grep -i serviceaccount
      automountServiceAccountToken: true
      serviceAccount: tiller
      serviceAccountName: tiller
You have new mail in /var/spool/mail/root
[root@master helm]#

以此方式完成部署。

用helm部署应用


helm 概念


Chart

chart 就是 Helm 所管理的包,类似于 Yum 所管理的 rpm 包或者是 Homebrew 管理的 Formulae。它包含着一个应用要部署至 K8S 上所必须的所有资源。

Release

release 就是 chart 在 K8S 上部署后的实例。chart 的每次部署将产生一次 Release。这和上面类比的包管理器就有所不同了,多数的系统级包管理器所安装的包只会在系统中存在一份。我们可以以 Pip 在虚拟环境下单得包安装,或者 Npm 的local install来进行类比。

Repository

repository 就是字面意思,存储 chart 的仓库。还记得我们上面执行 helm init 时的输出吗?默认情况下,初始化 Helm 的时候,会添加两个仓库,一个是 stable 仓库kubernetes-charts.storage.googleapis.com,另一个则是 local 仓库,地址是http://127.0.0.1:8879/charts。

Config

前面提到了 chart 是应用程序所必须的资源,当然我们实际部署的时候,可能就需要有些自定义的配置了。Config 便是用于完成此项功能的,在部署的时候,会将 configchart 进行合并,共同构成我们将部署的应用。

chart 结构


在项目目录下,通过以下命令创建一个 chart

[root@master helm]# pwd
/root/helm
[root@master helm]# helm create saythx
Creating saythx
[root@master helm]# tree -a saythx
saythx
├── charts
├── Chart.yaml
├── .helmignore
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 9 files
[root@master helm]#

先来看一下默认创建的 chart 中包含了什么文件和目录,对其进行解释。

Chart.yaml

[root@master helm]# cat saythx/Chart.yaml
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: saythx
version: 0.1.0
You have new mail in /var/spool/mail/root

这个文件是每个 chart 必不可少的一个文件,其中包含着几个重要的属性,如:

  • apiVersion: 目前版本都为v1
  • appVersion: 这是应用的版本号,注意需要与 apiVersionversion等字段区分。
  • name: 通常要求 chart 的名字必须和它所在的目录保持一致,且此字段必须。
  • version: 表明当前 chart 的版本号,会直接影响 Release 的记录,且此字段必须。
  • description: 描述。

charts

charts 文件夹是用于存放依赖的 chart 的。当有依赖需要管理时,可添加 requirements.yaml 文件,可用于管理项目内或外部的依赖。

.helmignore

.helmignore 类似于 .gitignore.dockerignore 之类的,用于忽略一些不想包含在 chart 内的文件。

templates

templates 文件夹内存放着 chart 所使用的模板文件,也是 chart 的实际执行内容。在使用 chart 进行安装的时候,会将下面介绍的 values.yaml 中的配置项与 templates 中的模板进行组装,生产最终要执行的配置文件。

templates 中,推荐命名应该清晰,如 xx-deployment.yaml,中间使用 - 进行分割,避免使用驼峰式命名。

NOTES.txt 文件在 helm install 完成后,会进行回显,可用于解释说明如何访问服务等。

values.yaml

values.yaml 存放着项目的一些可配置项,如镜像的名称或者 tag 之类的。作用就是用于和模板进行组装。

编写 chart


了解完结构之后,我们来实际编写chart。所有完整代码可在SayThx项目获取。

# Chart.yaml
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for SayThx.
name: saythx
version: 0.1.0
maintainers:
  - name: Jintao Zhang

可添加 maintainers 字段,表示维护者。

# values.yaml

# backend is the values for backend
backend:
  image: taobeier/saythx-be
  tag: "1.0"
  pullPolicy: IfNotPresent
  replicas: 1

# namespace is the values for deploy namespace
namespace: work

# service.type is the values for service type
service:
  type: NodePort

values.yaml 文件中定义了我们预期哪些东西是可配置的,比如 namespace 以及镜像名称 tag 等。这里只是贴出了部分内容,仅做说明使用,完整内容可查看我们的示例项目

values.yaml 文件的时候,由于是使用 YAML 格式的配置,所以它非常的灵活,即可以使用如上面例子中的 backend 那种字典类型的, 也可以写成简单的 k-v 形式。但通常来讲,应该尽可能的将它写的清晰明确。并且容易被替换。

# templates/backend-service.yaml 

apiVersion: v1
kind: Service
metadata:
  labels:
    app: backend
  name: saythx-backend
  namespace: {{ .Values.namespace }}
spec:
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 8080
  selector:
    app: backend
  type: {{ .Values.service.type }}

将部署文件模板化,与配置项进行组装。

1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Values.namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services saythx-frontend)
  export NODE_IP=$(kubectl get nodes --namespace {{ .Values.namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- else if contains "ClusterIP" .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Values.namespace }} -l "app=frontend" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace {{ .Values.namespace }} port-forward $POD_NAME 8080:80
{{- end }}

上面这是 NOTES.txt 文件内的内容。 这些内容会在 helm install 执行成功后显示在终端,用于说明服务如何访问或者其他注意事项等。
当然,这里的内容主要是为了说明如何编写 chart ,在实践中,尽量避免硬编码配置在里面。

部署


直接部署

Helm 的 chart 可以直接在源码目录下通过 helm install 完成部署。例如:

helm install saythx

打包

helm package saythx

文章作者: 少年G
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 少年G !
评论
  目录