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]#
解压完成后,可看到其中包含 helm
和 tiller
二进制文件。
客户端 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
直接执行时,默认会监听 44134
和 44135
端口,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 key
,node-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
便是用于完成此项功能的,在部署的时候,会将 config
与 chart
进行合并,共同构成我们将部署的应用。
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
: 这是应用的版本号,注意需要与apiVersion
,version
等字段区分。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