咱们平时在平常生活中会常常在不一样的平台上与各类各样的应用打交道,好比从苹果的 App Store 里下载的淘宝、高德、支付宝等应用,或者是在 PC 端安装的 Word、Photoshop、Steam。这些各种平台上的应用程序,对用户而言,大多只须要点击安装就可以使用。linux
然而,在云 (Kubernetes)上,部署一个应用每每却不是那么简单。若是想要部署一个应用程序到云上,首先要准备好它所须要的环境,打包成 Docker 镜像,进而把镜像放在部署文件 (Deployment) 中、配置服务 (Service)、应用所需的帐户 (ServiceAccount) 及权限 (Role)、命名空间 (Namespace)、密钥信息 (Secret)、可持久化存储 (PersistentVolumes) 等资源。也就是编写一系列互相相关的 YAML 配置文件,将它们部署在 Kubernetes 集群上。git
可是即使应用的开发者能够把这些 Docker 镜像存放在公共仓库中,而且将所需的 YAML 资源文件提供给用户,用户仍然须要本身去寻找这些资源文件,并把它们一一部署。假若用户但愿修改开发者提供的默认资源,好比使用更多的副本 (Replicas) 或是修改服务端口 (Port),他还须要本身去查须要在这些资源文件的哪些地方修改,更不用提版本变动与维护会给开发者和用户形成多少麻烦了。github
可见最原始的 Kubernetes 应用形态并不便利。golang
在这样的大环境下,有一系列基于 Kubernetes 的应用包管理工具横空出世。而咱们今天的主角 Helm,就是这其中最受欢迎的选择之一。redis
开发者按照 Helm Chart 的格式,将应用所需的资源文件包装起来,经过模版化 (Templating) 的方式将一些可变字段(好比咱们以前提到的暴露哪一个端口、使用多少副本)暴露给用户,最后将封装好的应用包,也就是 Helm Chart,集中存放在统一的仓库中供用户浏览下载。docker
站在用户角度,用户只须要一行简单的命令就能够完成应用的安装、卸载与升级。对于安装以后状态,也能够经过 helm list
或者是原生的 kubectl 进行查询。json
$ helm install redis stable/redis $ kubectl get pods NAME READY STATUS RESTARTS AGE redis-master-0 1/1 Running 0 63s redis-slave-0-0 1/1 Running 0 63s redis-slave-1-0 1/1 Running 0 13s $ helm delete redis
那么站在开发者的角度上,咱们应该如何去创做一个 Helm 应用包呢?api
首先咱们须要一个准备部署的镜像。这个镜像能够是一个 Java 程序、一个 Python 脚本、甚至是一个空的 linux 镜像跑几条命令。bash
这里咱们使用一个简单的基于 golang 的 hello world HTTP 服务。该服务经过读取环境变量 USERNAME
得到用户本身定义的名称,而后监听 80 端口。对于任意 HTTP 请求,返回 Hello ${USERNAME}
。好比若是设置 USERNAME=world
(默认场景),该服务会返回 Hello world
。app
而后咱们使用 Dockerfile 对镜像进行打包。先对 Golang 代码进行编译,而后将编译后的程序放在基于 alpine 的镜像中,以缩小镜像体积。
在 Docker 构建好镜像以后,咱们把镜像上传到仓库中,好比 Docker Hub 或是阿里云容器镜像仓库。准备工做作好以后,咱们就能够开始创做 Helm Chart 了。
运行 helm create my-hello-world
,会获得一个 helm 自动生成的空 chart。这个 chart 里的名称是 my-hello-world
。 须要注意的是,Chart 里面的 my-hello-world 名称须要和生成的 Chart 文件夹名称一致。若是修改 my-hello-world,则须要作一致的修改。 如今,咱们看到 Chart 的文件夹目录以下:
my-hello-world ├── charts ├── Chart.yaml ├── templates │ ├── deployment.yaml │ ├── _helpers.tpl │ ├── ingress.yaml │ ├── NOTES.txt │ └── service.yaml └── values.yaml
在根目录下的 Chart.yaml 文件内,声明了当前 Chart 的名称、版本等基本信息,这些信息会在该 Chart 被放入仓库后,供用户浏览检索。好比咱们能够把 Chart 的 Description 改为 "My first hello world helm chart"。在 Chart.yaml 里有两个跟版本相关的字段,其中 version 指明的是 Chart 的版本,也就是咱们应用包的版本;而 appVersion 指明的是内部实际使用的应用版本。
在 templates 文件夹内存放了各种应用部署所须要使用的 YAML 文件,好比 Deployment 和 Service。在咱们当前的应用内,咱们只须要一个 deployment,而有的应用可能包含不一样组件,须要多个 deployments,那么咱们就能够在 templates 文件夹下放置 deploymentA、deploymentB 等。一样的,若是咱们须要配置 serviceaccount, secret, volumes 等内容,也能够在里面添加相应的配置文件。
apiVersion: apps/v1beta2 kind: Deployment metadata: name: {{ template "my-hello-world.fullname" . }} labels: {{ include "my-hello-world.labels" . | indent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: app.kubernetes.io/name: {{ include "my-hello-world.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: labels: app.kubernetes.io/name: {{ include "my-hello-world.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - name: USERNAME value: {{ .Values.Username }} ......
Helm Chart 对于应用的打包,不只仅是将 Deployment 和 Service 以及其它资源整合在一块儿。咱们看到 deployment.yaml 和 service.yaml 文件被放在 templates/ 文件夹下,相较于原生的 Kubernetes 配置,多了不少渲染所用的可注入字段。好比在 deployment.yaml 的 spec.replicas
中,使用的是 .Values.replicaCount
而不是 Kubernetes 自己的静态数值。这个用来控制应用在 Kubernetes 上应该有多少运行副本的字段,在不一样的应用部署环境下能够有不一样的数值,而这个数值即是由注入的 Values
提供。
replicaCount: 1 image: repository: somefive/hello-world pullPolicy: IfNotPresent nameOverride: "" fullnameOverride: "" service: type: ClusterIP port: 80 ...... Username: AppHub
在根目录下咱们看到有一个 values.yaml
文件,这个文件提供了应用在安装时的默认参数。在默认的 Values
中,咱们看到 replicaCount: 1
,说明该应用在默认部署的状态下只有一个副本。
为了使用咱们要部署应用的镜像,咱们看到 deployment.yaml 中,在 spec.template.spec.containers
里, image
和 imagePullPolicy
都使用了 Values
中的值。其中 image
字段由 .Values.image.repository
和 .Chart.AppVersion
组成。
看到这里,同窗们应该就知道须要变动的字段了:一个是位于 values.yaml 内的 image.repository
,另外一个是位于 Chart.yaml 里的 AppVersion
。将它们与咱们须要部署应用的 docker 镜像匹配起来,这里咱们把 values.yaml 里的 image.repository
设置成 somefive/hello-world
,把 Chart.yaml 里的 AppVersion
设置成 1.0.0
便可。
相似的,咱们能够查看 service.yaml 内要部署的服务,其中的主要配置也在 values.yaml 中。默认生成的服务将 80 端口暴露在 Kubernetes 集群内部。咱们暂时不须要对这一部分进行修改。
因为部署的 hello-world 服务会从环境变量中读取 USERNAME
环境变量,因此将这个配置加入 deployment.yaml。
- name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - name: USERNAME value: {{ .Values.Username }}
如今咱们的 deployment.yaml 模版会从 values.yaml 中加载 Username
字段,所以相应的,咱们也在 values.yaml 中添加 Username: AppHub
。这样,咱们的应用就会从 values.yaml 中读取 Username
,把它放入镜像的环境变量中启动了。
在准备好咱们的应用后,咱们可使用 Helm lint 来粗略地检查一下制做的 Chart 有没有什么语法上的错误。若是没有问题的话,就可使用 helm package 命令对咱们的 Chart 文件夹进行打包,打包后咱们能够获得一个 my-hello-world-0.1.0.tgz 的应用包。这个即是咱们完成的应用了。
$ helm lint --strict my-hello-world 1 chart(s) linted, 0 chart(s) failed [INFO] Chart.yaml: icon is recommended $ helm package my-hello-world
咱们可使用 helm install
命令尝试安装一下刚刚作好的应用包,而后用 kubectl 查看一下运行 pod 的状态。经过 port-forward 命令将该 pod 的端口映射到本地端口上,这个时候就能够经过访问 localhost 来访问部署好的应用了。
$ helm install my-hello-world-chart-test my-hello-world-0.1.0.tgz $ kubectl get pods NAME READY STATUS RESTARTS AGE my-hello-world-chart-test-65d6c7b4b6-ptk4x-0 1/1 Running 0 4m3s $ kubectl port-forward my-hello-world-chart-test-65d6c7b4b6-ptk4x 8080:80 $ curl localhost:8080 Hello AppHub
有的同窗可能会有疑惑,虽然咱们应用开发者把可配置的信息暴露在 values.yaml 中,用户使用应用的时候想要修改该怎么办呢?答案很简单,用户只须要在 install 时使用 set 参数,设置想要覆盖的参数便可。
应用开发者在 Chart 的 values 配置中只是提供了默认的安装参数,用户也能够在安装时指定本身的配置。相似的,若是用户能够用 upgrade 命令替代 install,实如今原有部署好的应用的基础上变动配置。
$ helm install my-app my-hello-world-0.1.0.tgz --set Username="Cloud Native" $ helm install my-super-app my-hello-world-0.1.0.tgz -f my-values.yaml $ helm upgrade my-super-app my-hello-world-0.1.0.tgz -f my-new-values.yaml
咱们注意到在安装 Chart 指令运行后,屏幕的输出会出现:
NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods -l "app=my-hello-world,release=my-hello-world-chart-test2" -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
这里的注释是由 Chart 中的 templates/NOTES.txt
提供的。
咱们注意到原始的 NOTES 中,所写的 "app={{ template "my-hello-world.name" . }},release={{ .Release.Name }}"
和 deployment.yaml 中所写的配置不太同样,能够把它改为 "app.kubernetes.io/name={{ template "my-hello-world.name" . }},app.kubernetes.io/instance={{ .Release.Name }}"
,将 values.yaml 中的 version
更新成 0.1.1
。而后从新打包 Chart(运行 helm package
),获得新的 my-hello-world-0.1.1.tgz 以后,升级原有 Chart(运行 helm upgrade my-hello-world-chart-test2 my-hello-world-0.1.1.tgz --set Username="New Chart"
),就能看到更新事后的 NOTES 了。
NOTES: 1. Get the application URL by running these commands: export POD_NAME=$(kubectl get pods -l "app.kubernetes.io/name=my-hello-world,app.kubernetes.io/instance=my-hello-world-chart-test2" -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
那么制做完成的应用如何和其余人分享呢?Helm 官方推出的 ChartMuseum 提供了 Chart 仓库的构建方法,使用它能够建立本身的 Chart 仓库。然而自行维护一个仓库自己成本不小,并且对于用户而言若是每个开发者都是本身的仓库,他就须要将所需应用对应的仓库都加入本身的检索列表中,很不利于应用的传播与分享。
咱们团队近期推出了开放云原生应用中心,Cloud Native App Hub,同步了各种应用,同时还提供了开发者上传应用的渠道。
在咱们的开放云原生应用中心中,应用来自两个渠道:
参赛点击:https://developer.aliyun.com/special/apphubchallenge