大家好,我是腾意。

业务的容器化及微服务化过程,基本上都是通过将单体大应用分解为多个小的服务,并进行容器化编排运行来实现的,这种构建逻辑分解了单体应用的复杂性,让每个微服务都能够独立进行部署和扩展,实现了敏捷开发和运维。但另一方面,微服务化拆解巨大的单体应用为巨量的微服务程序,几乎必然地导致了应用管理复杂度的增加,例如,在Kubernetes系统之上,每个应用基本上都有着不止一个资源,而每个应用在不同的环境(如qa、test和prod等)中,存在使用不同的配置参数的可能性等复杂问题。幸运的是,容器生态系统现在已经发展到了简便程度,因为有了Helm。

1.1 Helm基础

由前面文章中的应用部署过程可知,在Kubernetes系统上部署容器化应用时,需要事先手动编写资源配置清单文件以定义资源对象,而且其每一次的配置定义基本上都是硬编码,基本上无法实现复用。对于较大规模的应用场景,应用程序的配置、分发、版本控制、查找、回滚甚至是查看都将是用户的噩梦。Helm可大大简化应用管理的难度。

简单来说,Helm就是Kubernetes的应用程序包管理器,类似于Linux系统之上的yum或apt-get等,可用于实现帮助用户查找、分享及使用Kubernetes应用程序,目前的版本由CNCF(Microsoft、Google、Bitnami和Helm社区)维护。它的核心打包功能组件称为chart,可以帮助用户创建、安装及升级复杂应用。

Helm将Kubernetes的资源(如Deployments、Services或ConfigMap等)打包到一个Charts中,制作并测试完成的各个Charts将保存到Charts仓库进行存储和分发。另外,Helm实现了可配置的发布,它支持应用配置的版本管理,简化了Kubernetes部署应用的版本控制、打包、发布、删除和更新等操作。Helm架构组件如图15-1所示。

img

图15-1 Helm架构组件

简单来说,Helm其实就是一个基于Kubernetes的程序包(资源包)管理器,它将一个应用的相关资源组织成为Charts,并通过Charts管理程序包,其使用优势可简单总结为如下几个方面:

·管理复杂应用:Charts能够描述哪怕是最复杂的程序结构,其提供了可重复使用的应用安装的定义。

·易于升级:使用就地升级和自定义钩子来解决更新的难题。

·简单分享:Charts易于通过公共或私有服务完成版本化、分享及主机构建。

·回滚:可使用“helm rollback”命令轻松实现快速回滚。

1.1.1 Helm的核心术语

Helm将Kubernetes应用的相关配置组织为Charts,并通过它完成应用的常规管理操作。通常来说,使用Charts管理应用的流程包括从0开始创建Charts、将Charts及其相关的文件打包为归档格式、将Charts存储于仓库(repository)中并与之交互、在Kubernetes集群中安装或卸载Charts,以及管理经Helm安装的应用的版本发行周期。因此,对Helm来说,它具有以下几个关键概念。

·Charts:即一个Helm程序包,它包含了运行一个Kubernetes应用所需要的镜像、依赖关系和资源定义等,必要时还会包含Service的定义;它类似于APT的dpkg文件或者yum的rpm文件。

·Repository:Charts仓库,用于集中存储和分发Charts,类似于Perl的CPAN,或者Python的PyPI。

·Config:应用程序实例化安装运行时使用的配置信息。

·Release:应用程序实例化配置后运行于Kubernetes集群中的一个Charts实例;在同一个集群上,一个Charts可以使用不同的Config重复安装多次,每次安装都会创建一个新的Release。

事实上,Charts更像是存储于Kubernetes集群之外的程序,它的每次安装是指在集群中使用专用配置运行一个实例,执行过程有点类似于在操作系统上基于程序启动一个进程。

1.1.2 Helm架构

Helm主要由Helm客户端、Tiller服务器和Charts仓库(repository)组成,如图15-2所示。

Helm客户端是命令行客户端工具,采用Go语言编写,基于gRPC协议与Tiller server交互(见图15-2)。它主要完成如下任务。

img

图15-2 Helm成员间通信

·本地Charts开发。

·管理Charts仓库。

·与Tiller服务器交互:发送Charts以安装、查询Release的相关信息以及升级或卸载已有的Release。

Tiller server是托管运行于Kubernetes集群之中的容器化服务应用,它接收来自Helm客户端的请求,并在必要时与Kubernetes API Server进行交互。它主要完成以下任务。

·监听来自于Helm客户端的请求。

·合并Charts和配置以构建一个Release。

·向Kubernetes集群安装Charts并对相应的Release进行跟踪。

·升级和卸载Charts。

通常,用户于Helm客户端本地遵循其格式编写Charts文件,而后即可部署于Kuber-netes集群之上运行为一个特定的Release。仅在有分发需求时,才应该将同一应用的Charts文件打包成归档压缩格式,提交到特定的Charts仓库。仓库既可以运行为公共托管平台,也可以是用户自建的服务器,仅供特定的组织或个人使用。

1.1.3 安装Helm Client

Helm的安装方式有两种:预编译的二进制程序和源码编译安装。这里先介绍预编译的二进制程序的安装方式,源码编译安装的方式将在1.1.4节的Tiller编译安装中一并说明。

Helm的每个发行版都提供了主流操作系统的专用版本,主要包括Linux、Mac OS和Windows,用户安装前按需下载合用的平台上的相关发行版本即可。Helm项目托管在GitHub之上,项目地址为https://github.com/kubernetes/helm 。

安装之前首先下载合用版本的压缩包并将其展开,本示例中使用的是v2.9.1的版本。执行具体命令时,需要替换为下载到的版本:


~]$tar-zxvf helm-v2.9.1-linux-amd64.tgz

而后,将其二进制程序文件复制或移动到系统PATH环境变量指向的目录中即可,如/usr/local/bin/目录(管理员用户才有写入文件至此目录的权限):


~]$sudo mv linux-amd64/helm/usr/local/bin/

Helm的各种管理功能均可通过其子命令完成,获取其使用帮助,直接使用“help”子命令即可:


~]$helm help

需要注意的是,Helm的运行依赖于本地安装并配置完成的kubectl,方能与运行于Kubernetes集群之上的Tiller服务器进行通信,因此,运行Helm的节点也应该是可以正常使用kubectl命令的主机,或者至少是有着可用kubeconfig配置文件的主机。Mac OS或Windows系统上的安装方式请参考Helm官方文档。

1.1.4 安装Tiller server

Tiller是Helm的服务器端,一般应该运行于Kubernetes集群之上,不过,出于研发使用的目的,也可以将其部署于本地,且需要能够与远程Kubernetes集群正常通信,这里选择将其托管运行于集群之上。另外,对于了RBAC授权插件的Kubernetes集群来说,还需要事先创建相关的ServiceAccount才能进行安装。

下面的资源配置清单示例中(tiller-rbac.yaml)定义了一个名为tiller的ServiceAccount,并通过ClusterRoleBinding将其绑定至集群管理员角色cluster-admin,从而使得它拥有集群级别所有的最高权限:


apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
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,而后执行命令以完成绑定:


~]$ kubectl apply -f tiller-rbac.yaml
serviceaccount "tiller" created
clusterrolebinding.rbac.authorization.k8s.io "tiller" created

而后使用如下命令进行Tiller server环境的初始化,完成Tiller server安装:


~]$ helm init --service-account tiller

注意 helm init命令进行初始化时,Kuberntes集群会到gcr.io/kubernetes-helm/上获取需要的镜像,镜像标签同Helm的版本号。请确保Kubernetes集群能够正确访问此镜像仓库。

若有必要,可以基于SSL/TLS实现Helm和Tiller之间的通信,这就需要在部署Tiller时为其指定专用的选项--tiller-tls-verify,并为后续的所有Helm命令额外附加--tls选项。部署完成后可使用“kubectl get pods”命令确认Tiller Pod运行正常,如下面的命令所示:


~]$ kubectl get pods -n kube-system -l app=helm
NAME                             READY  STATUS    RESTARTS   AGE
tiller-deploy-5c688d5f9b-gt65w   1/1    Running   0          4m

安装完成后,运行“helm version”命令即可显示客户端和服务端的版本号,若两者均显示正常,则表示安装成功。到此为止,运行于Kubernetes集群中的Tiller Server已经配置完成,管理员可按需实现后续的其他管理工作,例如,搜索可用的Charts,安装Charts到集群中等。

如果希望在安装时自定义一些参数以设定其运行机制,例如Tiller的版本或者在Kubernetes集群上的目标名称空间,则可以以类似如下方式使用命令:

·--canary-image:安装canary分支,即项目Master的分支。

·--tiller-image:安装指定版本的镜像,默认同Helm版本。

·--kube-context:安装至指定的Kubernetes集群。

·--tiller-namespace:安装至指定的名称空间,默认为kube-system。

此外,Tiller将数据存储于ConfigMap资源中,因此卸载后重新安装并不会导致数据丢失,必要时,管理员尽可放心重新安装或升级。卸载Tiller的方法常用的有两种方式。

1)kubectl delete deployment tiller-deploy--namespace kube-system

2)helm reset

至此,Helm和Tiller的安装设定工作已经完成,接下来便可尝试使用Helm带来的诸多便捷之处。

1.1.5 Helm快速入门

Charts是Helm的程序包,它们存储于Charts仓库中。Kubernetes官方的Charts仓库保存了一系列精心制作和维护的Charts,仓库的默认名称为“stable”。安装Charts到Kubernetes集群时,Helm首先会到Kubernetes官方的Charts仓库中获取到相关的Charts,而后将其安装并创建为Release。

提示 Helm的官方仓库为https://kubernetes-charts.storage.googleapis.com ,进行后续的操作之前请确保拥有访问此站点的能力。

“helo repo”相关的命令可用于管理使用的Charts仓库,其update子命令能够更新使用的默认仓库的元数据信息,其命令及执行结果如下所示:


~]$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. * Happy Helming! *

“helm search”命令可列出stable仓库中维护的所有Charts的列表,如下面命令结果中列出的部分Charts:


~]$ helm search
NAME                        CHART VERSION  APP VERSION   DESCRIPTION
stable/coredns              0.9.0          1.0.6  CoreDNS is a DNS server that 
    chains plugins and...
stable/docker-registry      1.4.0          2.6.2  A Helm chart for Docker Registry
stable/etcd-operator        0.7.7          0.7.0  CoreOS etcd-operator Helm chart 
    for Kubernetes
stable/gitlab-ce            0.2.1          GitLab  Community Edition
stable/grafana              1.9.0          5.1.2  The leading tool for querying 
    and visualizing t...
stable/jenkins              0.16.1         2.107  Open source continuous inte-
    gration server. It s...
stable/kubernetes-dashboard 0.6.8          1.8.3  General-purpose web UI for Kub-
    ernetes clusters
stable/mysql                0.5.0          5.7.14  Fast, reliable, scalable, and 
    easy to useopen-...
……

也可以为search命令添加一个过滤器,仅列出符合条件的Charts,例如下面的命令以redis为过滤条件,仅显示与redis相关的所有Charts:


~]$ helm search redis
NAME                          CHART VERSION  APP VERSION   DESCRIPTION
stable/prometheus-redis-exporter 0.1.1      0.16.0  Prometheus exporter for Redis 
    metrics
stable/redis                     3.3.0      4.0.9  Open source, advanced key-
    value store. It is of...

“helm inspect”命令能够打印出指定的Charts的详细信息:


~]$ helm inspect stable/redis
appVersion: 4.0.9
description: Open source, advanced key-value store. It is often referred to as a data
  structure server since keys can contain strings, hashes, lists, sets and sorted
  sets.
……

安装指定的Charts为Kubernetes集群的Release,可使用“helm install”命令进行,例如若需要安装stable/redis,则为命令行使用“-n”选项指定Release名称即可。当然,也可以先执行安装测试,例如:


~]$ helm install stable/redis -n redis --dry-run
NAME:   redis

若无错误信息返回,则移除--dry-run选项即可进入安装流程,命令会返回安装过程的执行步骤,以及最后的注意信息,它们通常是应用的使用帮助,因此是需要特别注意的内容:


~]$ helm install stable/redis -n redis
NAME:   redis
LAST DEPLOYED: Thu May 17 13:10:11 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Secret
NAME   TYPE    DATA  AGE
redis  Opaque  1     0s

==> v1/Service
NAME          TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)   AGE
redis-master  ClusterIP  10.105.63.22   <none>       6379/TCP  0s
redis-slave   ClusterIP  10.105.114.60  <none>       6379/TCP  0s
……
NOTES:
** Please be patient while the chart is being deployed **
Redis can be accessed via port 6379 on the following DNS names from within your 
    cluster:

redis-master.default.svc.cluster.local for read/write operations
redis-slave.default.svc.cluster.local for read-only operations
……

多数应用的Charts都可以接受用户提供的配置参数进行特定的场景化部署,所有可配置参数都会通过Charts模板提供,用户把需要提供参数值的配置选项存储在YAML格式的配置文件中并通过-f命令行选项加载即可。

若要列出已经安装生成的Release,则需要使用“helm list”命令:


~]$ helm list
NAME    REVISION  UPDATED                    STATUS     CHART        NAMESPACE
redis   1         Thu May 17 13:10:11 2018   DEPLOYED   redis-3.3.0  default

而要删除Release,则使用“helm delete”命令即可。


~]$ helm delete redis
release "redis" deleted

升级或回滚应用,要分别使用“helm upgrade”和“helm rollback”命令,而且还可以使用“helm history”命令获取指定的Release变更的历史。不过,若默认仓库中不存在所需要的某Charts,而对用户来说该Charts是日常部署任务,则用户可以自行编写Charts并分享。事实上,helm install命令支持基于多种安装源进行应用部署,这包括Charts仓库、本地的Charts压缩包、本地Charts目录,甚至是指定某个Charts的URL。

1.2 Helm Charts

Charts是Helm使用的Kubernetes程序包打包格式,一个Charts就是一个描述一组Kubernetes资源的文件的集合。事实上,一个单独的Charts既能用于部署简单应用,例如一个memcached Pod,也能部署复杂的应用,如由HTTP服务器、DB服务器、Cache服务器和应用程序服务器等共同组成的Web应用栈。

从物理的角度来描述,Charts是一个遵循特定规范的目录结构,它能够打包成为一个可用于部署的版本化归档文件。

1.2.1 Charts文件组织结构

一个Charts就是按特定格式组织的目录结构,目录名即为Charts名,目录名称本身不包含版本信息。例如,一个jenkins Charts的目录结构应该如下所示:


jenkins/
    Chart.yaml
    LICENSE
    README.md
    requirements.yaml
    values.yaml
    charts/
    templates/
    templates/NOTES.txt

目录结构中除了charts/和templates/是目录之外,其他的都是文件。它们的基本功用如下。

·Chart.yaml:当前Charts的描述信息,yaml格式的文件。

·LICENSE:当前Charts的许可证信息,纯文本文件;此为可选文件。

·README.md:易读格式的README文件;可选。

·requirements.yaml:当前Charts的依赖关系描述文件;可选。

·values.yaml:当前Charts用到的默认配置值。

·charts/:目录,存放当前Charts依赖到的所有Charts文件。

·templates/:目录,存放当前Charts用到的模板文件,可应用于Charts生成有效的Kuber-netes清单文件。

·templates/NOTES.txt:纯文本文件,Templates简单使用注解。

尽管Charts和Templates目录均为可选,但至少应该存在一个Charts依赖文件或一个模板文件。另外,Helm保留使用charts/和templates/目录以及上面列出的文件名称,其他文件都将被忽略。

1.2.2 Chart.yaml文件组织格式

Chart.yaml用于提供Charts相关的各种元数据,如名称、版本、关键词、维护者信息、使用的模板引擎等,它是一个Charts必备的核心文件,主要包含以下字段。

·name:当前Charts的名称,必选字段。

·version:遵循语义化版本规范第2版的版本号,必选字段。

·description:当前项目的单语句描述信息,可选字段。

·keywords:当前项目的关键词列表,可选字段。

·home:当前项目的主页URL,可选字段。

·sources:当前项目用到的源码的来源URL列表,可选字段。

·maintainers:项目维护者信息,主要嵌套name、email和URL几个属性组成;可选字段。

·engine:模板引擎的名称,默认为gotpl,即go模板。

·icon:URL,指向当前项目的图标,SVG或PNG格式的图片;可选字段。

·appVersion:本项目用到的应用程序的版本号,可选字段,且不必为语义化版本。

·deprecated:当前Charts是否已废弃,可选字段,布尔型值。

·tillerVersion:当前Charts依赖的Tiller版本号,可以是语义化版本号的范围,如“>2.4.0”;可选字段。

例如,下面的示例信息是rediss Charts中使用的Chart.yaml的内容,用户自行定义Charts编写相关文件时,要采用类似的文件格式:


appVersion: 4.0.9
description: Open source, advanced key-value store. It is often referred 
  to as a data structure server since keys can contain strings, hashes, 
  lists, sets and sorted sets.
engine: gotpl
home: http://redis.io/
icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png
keywords:
- redis
- keyvalue
- database
maintainers:
- email: containers@bitnami.com
  name: bitnami-bot
name: redis
sources:
- https://github.com/bitnami/bitnami-docker-redis
version: 3.3.0

1.2.3 Charts中的依赖关系

Helm中的一个Charts可能会依赖不止一个其他的Charts,这种依赖关系可经requirements.yaml进行动态链接,也可直接存储于charts/目录中进行手动管理。不过,尽管手动管理依赖关系对个别管理场景也有着些许优势,但使用动态管理的方式却是推荐的首选方式。

1.requirements.yaml文件

requirements.yaml文件本质上只是一个简单的依赖关系列表,它具有类似如下定义中的格式中的可用字段:


dependencies:
  - name:
    version:
    repository:
    alias:
    tags:
    condition:
    import-values:
      - child: 
        parent:

上述示例中的字段基本可以见名知义,具体如下。

·name:被依赖的Charts的名称。

·version:被依赖的Charts的版本。

·repository:被依赖的Charts所属的仓库及其URL;如果是非官方的仓库,则需要先用helm repo add命令将其添加进本地可用仓库。

·alias:为被依赖的Charts创建一个别名,从而让当前Charts可以将所依赖的Charts对应到新名称,即别名;可选字段。

·tags:默认情况下所有的Charts都会被装载,若给定了tags,则仅装载那些匹配到的Charts。

·condition:类似于tags字段,但需要通过自定义的条件来指明要装载的charts。

·import-values:导入子Charts中的的值;被导入的值需要在子charts中导出。

如下所示的示例,是Wordpress Charts中定义的动态依赖关系:


dependencies:
- name: mariadb
  version: 2.1.1
  repository: https://kubernetes-charts.storage.googleapis.com/
  condition: mariadb.enabled
  tags:
    - wordpress-database

一旦依赖关系文件配置完成,即可使用“helm dependency update”命令更新依赖关系,并自动下载被依赖的Charts至charts/目录中。

2.Charts目录

若需要对依赖关系进行更多的控制,则所有被依赖到的Charts都能以手工方式直接复制到Charts目录中。一个被依赖到的Charts既可以是归档格式,也可以是展开的目录格式,不过,其名称不能以下划线(_)或点号(.)开头,此类文件会被Charts装载器自动忽略。

例如,Wordpress Charts依赖关系在其Charts目录中的反映类似如下所示:


charts/
└── mariadb
    ├── Chart.yaml
    ├── README.md
    ├── templates
       ├── configmap.yaml
       ├── deployment.yaml
       ├── _helpers.tpl
       ├── NOTES.txt
       ├── pvc.yaml
       ├── secrets.yaml
       ├── svc.yaml
       ├── test-runner.yaml
       └── tests.yaml
    └── values.yaml

1.2.4 模板和值

Helm Charts模板(template)遵循Go模板语言格式,并支持50种以上的来自Spring库的模板函数附件,以及为数不少的其他专用函数。所有的模板文件都存储于Templates目录中,在当前Charts被Helm引用时,此目录中的所有模板文件都会传递给模板引擎进行处理。

模板文件中用到的值(value)有如下两种提供方式。

·通过Charts的values.yaml文件提供,通常用于提供默认值。

·在运行“helm install”命令时传递包含所需要的自定义值的YAML文件;此处传递的值会覆盖默认值。

下面的示例是Wordpress Charts中Deployment模板文件的部分内容:


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: {{ template "fullname" . }}
  labels:
    app: {{ template "fullname" . }}
    chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    release: "{{ .Release.Name }}"
    heritage: "{{ .Release.Service }}"
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: {{ template "fullname" . }}
    spec:
      containers:
      - name: {{ template "fullname" . }}
        image: "{{ .Values.image }}"
        imagePullPolicy: {{ default "" .Values.imagePullPolicy | quote }}

示例中模板相关的代码部分会由模板引擎运行,并生成相应的结果。供依赖的值可由values.yaml文件或由用户在运行helm install命令时通过选项提供,除此之外,char模板还包含一些固定的预定义值,如Release.Name、Release.Time、Release.Time、Release.Service、Release.IsUpgrade、Release.IsInstall、Release.Revision、Chart.Name、Chart.Version、Files和Capabilities等。

而在values.yaml一类的文件中定义值(value)时,既可以将它们定义为全局作用域,也可以定义为仅供Charts目录下的某个Charts所使用。一般来说,上级Charts可访问下级Charts中的值,但下级Charts不能访问其上级Charts的值。

如下面示例中的内容,其中title属于全局作用域,max_connections和password则仅属于mysql Charts,port仅属于apache Charts:


title: "My WordPress Site" # Sent to the WordPress template

mysql:
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  port: 8080 # Passed to Apache

Go模板语法请参考godoc站点中的内容,地址为https://godoc.org/text/template 。

1.2.5 其他需要说明的话题

定义Charts时还需要用到许可证文件(License)、自述文件(README.md)以及说明文件(NOTE.txt),其中说明文件需要为用户提供重要的使用帮助及注意事项等。基于Charts的格式规范,用户即可自定义相关应用程序的Charts,并将其通过仓库完成分享。

Charts中也支持使用文件来描述安装、配置、使用和许可证信息。一般说来,README文件必须为Markdown格式,因此其后缀名通常是“.md”,它一般应该包含如下内容。

·当前Charts提供的应用或服务的描述信息。

·运行当前Charts需要满足的条件。

·values.yaml文件中选项及默认值的描述。

·其他任何有助于安装或配置当前Charts的有用信息。

另外,templates/NOTES.txt文件中的内容将会在Charts安装完成后予以输出,通常用于向用户提供当前Charts相关的使用或初始访问方式的信息。另外,使用“helm status”命令查看某Release的相关状态信息时,此文件中的内容也会输出。

1.2.6 自定义Charts

一个典型的服务类容器化应用通常会由Pod控制器(常用的为Deployment)、Service、ConfigMap、Secret、Ingress和PersistentVolumeClaim等资源对象组成,其中前两者基本上是必备的资源,后面三个则可按需进行定义。Helm Charts包含了这些组件的yaml格式的资源配置文件模板,它们能够通过values.yaml配置文件获取模板中的各个变量所需的“值”,甚至支持用户在部署操作的运行时进行配置。

1.生成一个空Charts

创建一个新Charts的有效方式是使用“helm create”命令,它能够在新目录中创建一个名为mychart的新图表。例如,下面的命令会于命令执行的当前目录中创建一个名为mychart的子目录作为Charts存储路径:


~]$ helm create mychart
Creating mychart

此命令会初始化出一个空的Charts目录结构,它有着所需要的各个核心文件:


~]$ tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
   ├── deployment.yaml
   ├── _helpers.tpl
   ├── ingress.yaml
   ├── NOTES.txt
   └── service.yaml
└── values.yaml

由命令生成的各文件还有着各自应该具有的通用组织结构框架,例如,Chart.yaml文件的默认内容如下:


apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: mychart
version: 0.1.0

事实上,它甚至直接在values.yaml将要使用的镜像文件定义中为nginx生成了一个可直接安装容器化Nginx应用的Charts,其中的部分内容如下所示:


replicaCount: 1

image:
  repository: nginx
  tag: stable
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

因此,用户仅需要在各文件现有框架的基础上按需进行修改即可定义出所需的Charts来。

2.修改Charts以部署自定义服务

这里以此前使用的容器应用“ikubernetes/myapp:v1”为示例来说明如何定义一个Charts。使用“helm create”命令生成的Charts会创建一个用于运行、由默认值提供的镜像的Deployment对象,这就意味着若要部署不同的应用仅通过修改values.yaml中的引用镜像文件即可。当然,必要时,用户还需要额外确认是否需要默认的Ingress对象的定义,以及需要额外用到存储资源。

例如,这里将values.yaml文件的内容修改为如下所示:


repository: ikubernetes/myapp
  tag: v1
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

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

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 500m
    memory: 512Mi

nodeSelector: {}

tolerations: []

affinity: {}

而后通过“helm lint”命令确认修改后的Charts是否遵循最佳实践且模板格式良好:


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

1 chart(s) linted, no failures

多数情况下,“helm lint”命令报告的错误信息,根据其错误提示中的行号信息即能定位出错误所在。确保一切问题都得以解决之后,即可通过“helm install”命令调试运行以查看由Charts定义的容器化应用是否能够正确部署:


~]$ helm install --name myapp --dry-run --debug ./mychart --set service.type=
    NodePort
[debug] Created tunnel using local port: '40255'

[debug] SERVER: "127.0.0.1:40255"

[debug] Original chart version: ""
[debug] CHART PATH: /home/ik8s/charts/mychart

NAME:   myapp
REVISION: 1
RELEASED: Tue Jun  5 16:14:45 2018
CHART: mychart-0.1.0
USER-SUPPLIED VALUES:
service:
  type: NodePort
……

确认上述命令输出信息无误后,移除命令中的“--dry-run”选项后再次运行命令即可完成应用的部署:


~]$ helm install --name myapp ./mychart --set service.type=NodePort
NAME:   myapp
LAST DEPLOYED: Tue Jun  5 16:20:22 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/Service
NAME           TYPE      CLUSTER-IP    EXTERNAL-IP  PORT(S)       AGE
myapp-mychart  NodePort  10.108.65.88  <none>       80:30315/TCP  1s

==> v1/Deployment
NAME           DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
myapp-mychart  1        1        1           0          0s

==> v1/Pod(related)
NAME                            READY  STATUS             RESTARTS  AGE
myapp-mychart-67b9997c8b-nbsfk  0/1    ContainerCreating  0         0s

NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports
      [0].nodePort}" services myapp-mychart)
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].
      status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

而后,通过上述NOTES中的命令提示运行相关的命令获取访问端点后即可通过浏览器访问相应的服务:


~]$ export NODE_PORT=$(kubectl get --namespace default -o \
 jsonpath="{.spec.ports[0].nodePort}" services myapp-mychart)
~]$ export NODE_IP=$(kubectl get nodes --namespace default -o \
jsonpath="{.items[0].status.addresses[0].address}")
~]$ echo http://$NODE_IP:$NODE_PORT
http://172.16.0.70:30376

而后通过浏览器访问测试所部署的myapp应用。

3.Charts仓库(Repository)

至此,一个自定义的Charts即于本地设定完成,不过,它仅能用于本地访问。当然,用户也可以通过“helm package”命令将其打包为tar格式后分享给团队或者社区:


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

Helm将在工作目录中创建一个mychart-0.1.0.tgz包,它使用Chart.yaml文件中定义的元数据的名称和版本。通过将程序包作为参数传递给helm install,用户可以基于该程序包而不是本地目录进行安装:


~]$ helm install --name myapp2 mychart-0.1.0.tgz --set service.type=NodePort

为了使软件包更容易分享,Helm内置了从HTTP服务器安装软件包的支持。运行时,Helm读取服务器上托管的仓库索引,该索引描述了有哪些Charts程序包可用以及它们位于何处。使用“helm serve”命令即可运行本地仓库来输出本地创建的Charts:


~]$ helm serve
Regenerating index. This may take a moment.
Now serving you on 127.0.0.1:8879

此命令会占据当前终端,于是,另启一个终端即可测试访问本地仓库服务中的Charts:


~]$ helm search local
NAME            CHART VERSION   APP VERSION   DESCRIPTION
local/mychart   0.1.0           1.0           A Helm chart for Kubernetes

尽管Helm能够管理本地仓库,但创建好的Charts如需向外分享,就需要为其创建用于共享的仓库,并将计划共享的所有Charts都放置于仓库中。事实上,Charts仓库服务器就是HTTP服务器,任何能支持YAML文件及tar文件传输的HTTP服务器程序都可以作为仓库服务器使用。不过,一般来说,一个仓库应该有一个index.html主页用于描述当前仓库中的所有Charts及其元数据信息。然而,到目前为止,Helm并不支持将Charts上传至仓库中,因为这样做会对仓库服务器程序多出很多额外的要求,并且也会增加其配置的复杂度。这就意味着,上传Charts到仓库中需要借助于其他手段来进行,如FTP或SSH协议等,另外,如需自建Charts仓库,则各流行的Web服务器程序基本上都能满足要求,如Apache或Nginx等。安全起见,通过互联网提供服务时,建议使用HTTPS的服务器提供仓库服务。

而在Helm客户端,仓库的管理需要使用“helm repo”命令进行,它可以添加、删除、列出、及索引仓库。例如,使用如下命令添加incubator仓库以便使用更多的Charts:


~]$ helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
"incubator" has been added to your repositories

列出所有可用的仓库列表,可以使用“helm repo list”命令进行:


~]$ helm repo list
NAME       URL
stable     https://kubernetes-charts.storage.googleapis.com
local      http://127.0.0.1:8879/charts
incubator  http://storage.googleapis.com/kubernetes-charts-incubator

仓库服务器上的可用Charts及其版本等经常会发生更新,必要时,可使用“helm repo update”命令获取最新的仓库元数据信息:


~]$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "incubator" chart repository
Update Complete.  *Happy Helming! *

而要删除指定的仓库配置,“helm repo remove”即可轻松完成。

4.配置依赖关系

构建存在依赖关系的Charts时,还需要为其定义依赖项,例如,前面创建的myapp依赖于数据库管理系统MySQL时,在mychart的目录中创建requirements.yaml文件给出依赖的Charts列表定义其依赖关系即可,文件内容类似如下所示:


dependencies:
- name: mysql
  version: 0.6.0
  repository: https://kubernetes-charts.storage.googleapis.com

而后,需要运行“helm dependency update”命令为Charts更新依赖关系。更新过程中,Helm会自动生成一个锁定文件requirements.lock,以便后续再次获取依赖关系时使用已知的工作版本。运行下面的命令来引入定义的MySQL依赖项时,会自动下载MySQL相关的Charts程序包至mychart/charts子目录中,如下面的命令输出结果所示:


~]$ helm dependency update ./mychart
Hang tight while we grab the latest from your chart repositories...
......
Saving 1 charts
Downloading mysql from repo https://kubernetes-charts.storage.googleapis.com
Deleting outdated charts

此时,再次部署myapp Charts,就会同时部署依赖到的mysql Charts。另外,用户也可以手动将所依赖到的程序包直接放置于mychart/charts目录中来定义依赖关系,此时不必要再使用requirements.yaml文件。

1.3 Helm实践:部署EFK日志管理系统

应用程序的日志收集和监控通常是其必要的外围功能,它们有助于记录、分析性能表现及排查故障等,例如此前在查看Pod对象的日志时使用的kubectl log命令便是获取容器化应用日志的一种方式。然而,对于分布式部署的应用来说,类似这种逐一查看各实例相关日志的方式存在着操作烦琐且效率低下等诸多问题,再加上需要额外获取操作系统级别的多个日志源中的日志信息,其管理成本势必会进一步上升。解决此类需求的常见方案是使用集中式日志存储和管理系统,它们于各节点部署日志采集代理程序从日志源采集日志并发往中心存储管理系统,并经由单个面板进行数据可视化。事实上,对于任何基础设施或分布式系统,统一日志管理都是必不可少的基础组件。同样地,Kubernetes也要实现在整个集群级别收集和聚合日志,以便用户可以从单个仪表板监控整个集群,其常用的架构形式之一,如图15-3所示。一种流行的开源解决方案是将fluentd作为节点级代理程序进行日志采集,并将之聚合存储于Elasticsearch进行日志分析,以及通过Kibana进行数据可视化。这种组合通常简称为EFK。

img

图15-3 基于节点日志采集代理完成日志收集

在Kubernetes上部署fluentd和Kibana的方式易于实现,fluentd由DaemonSet控制器部署于集群中的各节点,而Kibana则由Deployment控制器部署并确保其持续运行即可。但Elastic-Search是一个有状态的应用,需要使用StatefulSet 控制器创建并管理相关的Pod对象,而且它们还分别需要专用的持久存储系统存储日志数据,因此,其部署过程较之前两者要略为烦琐,其部署架构如图15-4所示。

img

图15-4  Kubernetes集群上的f luentd、Elastic- search和Kibana

可用的部署方式除了Kubernetes项目在其Addons目录中提供了资源配置清单用于部署EFK之外,Kubeapps(https://hub.kubeapps.com )也为此三者分别提供了相应的Charts定义以帮助用户通过Helm轻松完成其部署。

1.3.1 ElasticSearch集群

ElasticSearch相关Charts位于Kubeapps的incubator仓库中,它使用StatefulSet和Deployment控制器实现了一个可动态伸缩的ElasticSearch集群,并将集群的角色分离为三类节点:客户端节点(上载节点)、master节点和data节点,各自负载实现集群的一部分功能,如图15-5所示。每个master节点和data节点分别需要用到各自的存储卷以持久存储数据,incubator/elasticsearch默认定义它们通过PVC依赖于default或指定的存储类动态创建PV存储卷。

img

图15-5 多角色ElasticSearch集群

·客户端节点:也称上载节点或摄取节点,负责执行由一个或多个摄取处理器组成的预处理流水线。需要两个以上客户端实例的场景并不多见,因此其副本数通常为2。

·master节点:负责轻量级群集范围的相关操作,如创建或删除索引、跟踪并判定集群的成员节点,以及决定将哪些分片分配到哪些节点等,因此,一个健康的集群必须要有一个稳定的主节点。master节点的数量取决于公式“(客户端副本数/2)+1”的计算结果,但至少应该为3。

·数据节点:负责保存包含编入索引的文档的分片,并处理与数据相关的操作,例如数据的增删改查(CRUD)、搜索和聚合等,这些操作通常是I/O、内存和CPU密集型的任务,其所需的节点数量取决于存储及计算的实际需求,因此,监视这些资源并在过载时添加更多的数据节点非常重要。

incubator/elasticsearch的部署模板中定义了众多配置参数,随着Charts版本的迭代,各参数的默认值也可能会有变动,当前的0.4.9版本中,使用的镜像来自仓库“centerforopenscience/elasticsearch”,镜像文件的标签为“5.4”,各master节点的PVC存储卷大小都是4Gi,各data节点的PVC存储卷大小均为30Gi,rbac相关的各资源创建为禁用状态。部署于生产环境时,默认设置中的资源请求和资源限制,以及data节点的PVC存储卷空间等较小,而且在启用了rbac授权插件的集群中还需要创建elasticsearch所需要的各ClusterRole及ClusterRoleBinding资源。这些需要自定义的配置参数可以通过values文件进行设置,或者直接由“helm install”命令的“--set”选项提供。一个示例性的values文件(els-values.yaml)如下所示:


# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
appVersion: "5.5"

image:
  repository: "centerforopenscience/elasticsearch"
  tag: "5.5"
  pullPolicy: "IfNotPresent"

cluster:
  name: "elasticsearch"
  config:
  env:
    MINIMUM_MASTER_NODES: "2"

client:
  name: client
  replicas: 2
  serviceType: ClusterIP
  heapSize: "2048m"
  antiAffinity: "soft"
  resources:
    limits:
      cpu: "1"
      # memory: "8192Mi"
    requests:
      cpu: "25m"
      memory: "2048Mi"

master:
  name: master
  exposeHttp: false
  replicas: 3
  heapSize: "2048m"
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    name: data
    size: "4Gi"
    #storageClass: "glusterfs"
  antiAffinity: "soft"
  resources:
    limits:
      cpu: "1"
      # memory: "8192Mi"
    requests:
      cpu: "25m"
      memory: "2048Mi"

data:
  name: data
  exposeHttp: false
  replicas: 2
  heapSize: "4096m"
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    name: data
    size: "120Gi"
    #storageClass: "glusterfs"
  terminationGracePeriodSeconds: 3600
  antiAffinity: "soft"
  resources:
    limits:
      cpu: "1"
      # memory: "16384Mi"
    requests:
      cpu: "25m"
      memory: "4096Mi"

## Install Default RBAC roles and bindings
rbac:
  create: true

需要特别说明的是,未明确定义持久存储使用的存储类时,无须持久保存数据,或者无可用的实现动态供给PV的存储时,也可以使用emptyDir存储卷,实现方法是将上面示例中master.persistence.enabled和data.persistence.enabled配置参数的值分别设置为“false”。

提示 incubator/elasticsearch相关的配置参数列表获取地址为https://github.com/kubernetes/charts/tree/master/incubator/elasticsearch 。

下面的命令将在logs名称空间中部署ElasticSearch集群的各角色及其相关的其他资源,其通过前面示例中的Values文件els-values.yaml获取各自定义的配置参数:


~]$ kubectl create namespace logs
~]$ helm install incubator/elasticsearch -n efk-els --namespace=logs -f els-
    values.yaml

提示 仅用于测试,且无可用的实现动态供给PV的存储类时,则无须定义Values文件,并将上面的命令替换为“helm install incubator/elasticsearch-n efk-els--namespace=logs--set master.persistence.enabled="false",data.persistence.enabled="false",rbac.create= "true"”即可。

从命令最后提供的类似如下的提示信息中可以看出,部署完成后在Kubernetes集群内部访问ElasticSearch服务的接入端点为“efk-elasticsearch-client.logs.svc.cluster.local:9200”。随后部署fluentd时将基于此访问端点将采集到的日志导入到ElasticSearch集群中,而Kibana也将通过此端点为ElasticSearch中的数据提供可视化的搜索及展示接口:


NOTES:
The elasticsearch cluster has been installed.

Elasticsearch can be accessed:

  * Within your cluster, at the following DNS name at port 9200:

    efk-els-elasticsearch-client.logs.svc.cluster.local

……

使用cirros镜像创建一个测试客户端,通过此端点对ElasticSearch的服务发起测试访问请求。例如,下面的命令可用于请求显示ElasticSearch集群的欢迎信息:


~]$ kubectl run cirros-$RANDOM  --rm -it --image=cirros -- sh
/ # curl http://efk-els-elasticsearch-client.logs.svc.cluster.local:9200
{
  "name" : "efk-els-elasticsearch-client-55dccf5f75-7hhc9",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : " uGffeD5bSoO8q29v4uLaBw",
  "version" : {
    "number" : "5.5.2",
    "build_hash" : "b2f0c09",
    "build_date" : "2017-08-14T12:33:14.154Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.0"
  },
  "tagline" : "You Know, for Search"
}
/ #

还可以通过类似如下的命令了解ElasticSearch集群当前的工作状态,它显示出当前集群处于正常工作状态“green”,共有7个节点,其中有2个节点为数据节点。由于是新创建的集群,因此目前尚不存在任何数据分片:


/ # curl http://efk-els-elasticsearch-client.logs.svc.cluster.local:9200/_cluster/
healthpretty
{
  "cluster_name" : "elasticsearch",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 7,
  "number_of_data_nodes" : 2,
  "active_primary_shards" : 0,
  "active_shards" : 0,
……
}
/ #

1.3.2 日志采集代理fluentd

fluentd是一个开源的数据收集器,基于C和Ruby语言开发,它目前有数百种以Ruby Gem形式独立存在的可选插件,用于连接多种数据源和数据输出组件等,如fluent-plugin-elasticsearch插件用于实现将采集到的数据发送给ElasticSearch。

提示 fluentd的可用插件列表获取地址为https://www.fluentd.org/plugins 。

运行时,fluentd从配置文件获取数据源、数据输出目标、过滤器等相关的配置信息,这些配置信息以source、match、filter、system、label和@include配置参数给出。

img

图15-6 f luentd架构图

·source:定义数据源,每个数据源都需要有其专有类型的定义。

·match:数据输出的目标位置,如文件或各种存储系统。

·filter:过滤器,即事件处理流水线,通常运行于输入和输出之间。

·system:系统级设置,如处理器名称等。

·label:用于分组过滤器及输出目标。

·@include:引用配置信息中某些已有的定义,以达到配置复用之目的。

下面的配置示例中给出了fluentd收集Kubernetes平台上容器日志的输入及输出相关的定义:


<source>
      @id fluentd-containers.log
      @type tail
      path /var/log/containers/*.log
      pos_file /var/log/fluentd-containers.log.pos
      time_format %Y-%m-%dT%H:%M:%S.%NZ
      tag raw.kubernetes.*
      format json
      read_from_head true
</source>
<match raw.kubernetes.**>
      @id raw.kubernetes
      @type detect_exceptions
      remove_tag_prefix raw
      message log
      stream stream
      multiline_flush_interval 5
      max_bytes 500000
      max_lines 1000
</match>

incubator/fluentd-elasticsearch是Kubeapps上定义的基于fluentd收集容器及系统日志并将其输出至ElasticSearch中的Charts之一,它内置了基于ConfigMap资源定义的fluentd配置文件,基本无须修改即能投入使用。另外,fluentd是运行于各节点上的日志采集代理,因此,它受控于DaemonSet控制器。

基于此Charts部署fluentd时通常仅需为其指定ElasticSearch服务的访问端点即可,因此可直接基于命令启动部署操作:


~]$ helm install local/fluentd-elasticsearch -n efk-flu --namespace=logs  \
    --set elasticsearch.host="efk-els-elasticsearch-client.logs.svc.cluster.local"

不过,若需要收集master节点上的日志,就需要为部署的Pod对象添加tolerations以容忍master上的taints。修改Charts中默认的values.yaml文件中的配置,并以之完成部署即能实现。

确认fluentd相关的各Pod对象正常运行之后,即可到ElasticSearch集群中查看其收集并存储的日志的索引及数据信息,例如,仍于此前启动的测试Pod中向ElasticSearch发起访问请求,查看已生成的索引,命令如下:


/ # curl http://efk-els-elasticsearch-client.logs.svc.cluster.local:9200/_cat/indices
green open logstash-2018.06.10 UJtHdGeHTISVFkW8sO_PjQ 5 1  6 0  337.3kb 168.6kb
green open logstash-2018.06.09 eotV9S2QQuCK1JUPEnmieQ 5 1 47470 0  97.6mb 48.9mb
/ #

命令结果中显示出以“logstash-YYYY.MM.DD”格式命名的索引列表,即表示fluentd已经能够正常采集到日志数据并输出到指定的ElasticSearch集群中。

1.3.3 可视化组件Kibana

Kibana是ElasticSearch的数据分析及可视化平台,能够用来搜索、查看存储在Elastic-Search索引中的数据。它可以通过各种图表进行高级数据分析及展示,用户基于Web GUI可以快速创建仪表板(dashboard)实时显示ElasticSearch的查询结果。Kibana配置过程简单便捷,图形样式丰富,可借助于ElasticSearch创建柱形图、折线图、散点图、直方图、饼图和地图等数据展示接口。Kibana增强了ElasticSearch的数据分析能力,让用户能够更加智能地分析数据和展示数据。

类似于fluentd,Kibana也通过URL访问ElasticSearch,但它要通过环境变量ELASTIC-SEARCH_URL来指定。部署于Kubernetes上的Kibana一般会由集群外的客户端访问,因此需要为其配置Ingress资源,也可以使用NodePort或LoadBalancer类型的Service资源进行服务暴露。它默认通过HTTP提供服务,在使用Ingress暴露到互联网时,建议将其配置为HTTPS类型的服务。

stable/kibana是Kubeapps上提供的Charts,下面是适用于当前配置环境设定的Values文件(kibana-values.yaml),它使用镜像文件kibana:5.5运行容器,环境变量ELASTICSEARCH_URL的值为前面部署的ElasticSearch集群的访问接口,并通过NodePort类型的Service资源进行服务暴露:


image:
  repository: "kibana"
  tag: "5.5"
  pullPolicy: "IfNotPresent"

env:
  ELASTICSEARCH_URL: http://efk-els-elasticsearch-client.logs.svc:9200
  SERVER_PORT: 5601

service:
  type: NodePort
  externalPort: 443
  internalPort: 5601

ingress:
  enabled: false

而后,使用下面的命令即可完成Kibana部署:


~]$ helm install stable/kibana -n efk-kib --namespace=logs -f kibana-values.yaml
NAME:   efk-kib
LAST DEPLOYED: Sun Jun 10 09:19:49 2018


RESOURCES:
==> v1/Service
NAME       TYPE     CLUSTER-IP   EXTERNAL-IP  PORT(S)   AGE
efk-kib-kibana  NodePort  10.110.201.6  <none>       443:30978/TCP  0s
……

确认相关的Pod资源能够正常运行之后,即可通过如上面命令显示的Service资源中30978端口于集群外访问Kibana,其初始界面如图15-7所示。在“Management”中添加相应的索引模式加载相关的索引数据即可完成数据搜索及可视化配置,由fluentd收集的日志以“logstash-YYYY.MM.DD”为索引名根据索引产生的日志按天保存于单独的索引中,因此Kibana默认的模式“logstash-*”便能够加载这些索引中的数据。

img

图15-7 Kibana的初始界面

创建好索引模式之后,即可通过“Discover”搜索数据,或者在“Visualize”界面中定义可视化图形,并将它们集成于可在“Dashboard”中创建的仪表板里。

注意 Kibana程序(非Charts)的版本号不能高于ElasticSearch,否则会导致不兼容。

1.4 小结

目前来说,Kubernetes部署及管理应用程序的接口仍然相当复杂,在维护较多的资源时,用户必然会受困于其复杂多变的资源配置清单,Helm通过Charts实现了类似于yum、dnf或apt-get等程序包管理器的功能,大大降低了用户的使用成本。本篇详细讲解了Helm的使用方式,并通过示例演示了其使用方法。

版权声明:如无特殊说明,文章均为本站原创,版权所有,转载需注明本文链接

本文链接:http://www.bianchengvip.com/article/Kubernetes-Helm-Package-Manager/