# 简化Kubernetes应用部署工具-Helm简介 微服务和容器化给复杂应用部署与管理带来了极大的挑战。Helm是目前Kubernetes服务编排领域的唯一开源子项目,做为Kubernetes应用的一个包管理工具,可理解为Kubernetes的apt-get / yum,由Deis 公司发起,该公司已经被微软收购。Helm通过软件打包的形式,支持发布的版本管理和控制,很大程度上简化了Kubernetes应用部署和管理的复杂性。 随着业务容器化与向微服务架构转变,通过分解巨大的单体应用为多个服务的方式,分解了单体应用的复杂性,使每个微服务都可以独立部署和扩展,实现了敏捷开发和快速迭代和部署。但任何事情都有两面性,虽然微服务给我们带来了很多便利,但由于应用被拆分成多个组件,导致服务数量大幅增加,对于Kubernetest编排来说,每个组件有自己的资源文件,并且可以独立的部署与伸缩,这给采用Kubernetes做应用编排带来了诸多挑战: 1. 管理、编辑与更新大量的K8s配置文件 1. 部署一个含有大量配置文件的复杂K8s应用 1. 分享和复用K8s配置和应用 1. 参数化配置模板支持多个环境 1. 管理应用的发布:回滚、diff和查看发布历史 1. 控制一个部署周期中的某一些环节 1. 发布后的验证 Helm把Kubernetes资源(比如deployments、services或 ingress等) 打包到一个chart中,而chart被保存到chart仓库。通过chart仓库可用来存储和分享chart。Helm使发布可配置,支持发布应用配置的版本管理,简化了Kubernetes部署应用的版本控制、打包、发布、删除、更新等操作。 本文简单介绍了Helm的用途、架构与实现。 ## Helm产生原因 Helm基本架构如下: ![image](https://www.kubernetes.org.cn/img/2017/09/helm-arch.jpg) # helm 安装 完本文后您应该可以自己创建chart,并创建自己的私有chart仓库。 Helm是一个kubernetes应用的包管理工具,用来管理charts——预先配置好的安装包资源,有点类似于Ubuntu的APT和CentOS中的yum。 Helm chart是用来封装kubernetes原生应用程序的yaml文件,可以在你部署应用的时候自定义应用程序的一些metadata,便与应用程序的分发。 Helm和charts的主要作用: - 应用程序封装 - 版本管理 - 依赖检查 - 便于应用程序分发 本文同时归档到kubernetes-handbook。 安装Helm 前提要求 - Kubernetes1.5以上版本 - 集群可访问到的镜像仓库 - 执行helm命令的主机可以访问到kubernetes集群 安装步骤 首先需要安装helm客户端 curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.shchmod 700 get_helm.sh./get_helm.sh 创建tiller的serviceaccount和clusterrolebinding kubectl create serviceaccount --namespace kube-system tiller kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller 然后安装helm服务端tiller helm init -i harbor-demo.dianrong.com/k8s/kubernetes-helm-tiller:v2.7.0 --service-account tiller 我们使用-i指定自己的镜像,因为官方的镜像因为某些原因无法拉取。 为应用程序设置serviceAccount: kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}' 检查是否安装成功: $ kubectl -n kube-system get pods|grep tiller tiller-deploy-3243657295-4dg28 1/1 Running 0 8m # helm version Client: &version.Version{SemVer:"v2.7.0", GitCommit:"08c1144f5eb3e3b636d9775617287cc26e53dba4", GitTreeState:"clean"} Server: &version.Version{SemVer:"v2.7.0", GitCommit:"08c1144f5eb3e3b636d9775617287cc26e53dba4", GitTreeState:"clean"} > 注意检查是否安装socat依赖 :yum install socat ### 创建自己的chart 我们创建一个名为mychart的chart,看一看chart的文件结构。 # helm create mychart # tree mychart/ mychart/ ├── charts ├── Chart.yaml ├── templates │ ├── deployment.yaml │ ├── _helpers.tpl │ ├── ingress.yaml │ ├── NOTES.txt │ └── service.yaml └── values.yaml 2 directories, 7 files 模板 Templates目录下是yaml文件的模板,遵循Go template语法。使用过Hugo的静态网站生成工具的人应该对此很熟悉。 我们查看下deployment.yaml文件的内容。 # cat mychart/templates/deployment.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: {{ template "mychart.fullname" . }} labels: app: {{ template "mychart.name" . }} chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} spec: replicas: {{ .Values.replicaCount }} template: metadata: labels: app: {{ template "mychart.name" . }} release: {{ .Release.Name }} 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 }} {{- if .Values.nodeSelector }} nodeSelector: {{ toYaml .Values.nodeSelector | indent 8 }} {{- end }} 这是该应用的Deployment的yaml配置文件,其中的双大括号包扩起来的部分是Go template,其中的Values是在values.yaml文件中定义的: cat mychart/values.yaml # 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.9 pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 ingress: enabled: false # Used to create an Ingress record. hosts: - chart-example.local annotations: # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" tls: # Secrets must be manually created in the namespace. # - 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 比如在Deployment.yaml中定义的容器镜像image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"其中的: - .Values.image.repository就是nginx - .Values.image.tag就是stable 以上两个变量值是在create chart的时候自动生成的默认值。 我们将默认的镜像地址和tag改成我们自己的镜像sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9。 ### 检查配置和模板是否有效 当使用kubernetes部署应用的时候实际上讲templates渲染成最终的kubernetes能够识别的yaml格式。 helm install --dry-run --debug <chart_dir> -n string 命令来验证chart配置。该输出中包含了模板的变量配置与最终渲染的yaml文件。 # helm install --dry-run --debug mychart/ --name test [debug] Created tunnel using local port: '45443' [debug] SERVER: "localhost:45443" [debug] Original chart version: "" [debug] CHART PATH: /root/k8s/helm/mychart NAME: test REVISION: 1 RELEASED: Fri Nov 3 13:37:44 2017 CHART: mychart-0.1.0 USER-SUPPLIED VALUES: {} COMPUTED VALUES: image: pullPolicy: IfNotPresent repository: nginx tag: 1.9 ingress: annotations: null enabled: false hosts: - chart-example.local tls: null replicaCount: 1 resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi service: externalPort: 80 internalPort: 80 name: nginx type: ClusterIP HOOKS: MANIFEST: --- # Source: mychart/templates/service.yaml apiVersion: v1 kind: Service metadata: name: test-mychart labels: app: mychart chart: mychart-0.1.0 release: test heritage: Tiller spec: type: ClusterIP ports: - port: 80 targetPort: 80 protocol: TCP name: nginx selector: app: mychart release: test --- # Source: mychart/templates/deployment.yaml apiVersion: extensions/v1beta1 kind: Deployment metadata: name: test-mychart labels: app: mychart chart: mychart-0.1.0 release: test heritage: Tiller spec: replicas: 1 template: metadata: labels: app: mychart release: test spec: containers: - name: mychart image: "nginx:1.9" imagePullPolicy: IfNotPresent ports: - containerPort: 80 livenessProbe: httpGet: path: / port: 80 readinessProbe: httpGet: path: / port: 80 resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi 我们可以看到Deployment和Service的名字前半截由两个随机的单词组成,最后才是我们在values.yaml中配置的值。 ### 部署到kubernetes 在mychart目录下执行下面的命令将nginx部署到kubernetes集群上。 # helm install . --name test NAME: test LAST DEPLOYED: Fri Nov 3 14:30:19 2017 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test-mychart ClusterIP 10.254.242.188 <none> 80/TCP 0s ==> v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE test-mychart 1 0 0 0 0s NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods --namespace default -l "app=mychart,release=test" -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实例。 export POD_NAME=$(kubectl get pods --namespace default -l "app=eating-hound-mychart" -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 # helm list NAME REVISION UPDATED STATUS CHART NAMESPACE peeking-yak 1 Fri Nov 3 15:01:20 2017 DEPLOYED mychart-0.1.0 default 删除部署的release $ helm delete eating-houndrelease "eating-hound" deleted 打包分享 我们可以修改Chart.yaml中的helm chart配置信息,然后使用下列命令将chart打包成一个压缩文件。 helm package . 打包出mychart-0.1.0.tgz文件。