從入門到實踐:創做一個本身的 Helm Chart

前言

咱們平時在平常生活中會常常在不一樣的平臺上與各類各樣的應用打交道,好比從蘋果的 App Store 裏下載的淘寶、高德、支付寶等應用,或者是在 PC 端安裝的 Word、Photoshop、Steam。這些各種平臺上的應用程序,對用戶而言,大多隻須要點擊安裝就可以使用。linux

file

然而,在雲 (Kubernetes)上,部署一個應用每每卻不是那麼簡單。若是想要部署一個應用程序到雲上,首先要準備好它所須要的環境,打包成 Docker 鏡像,進而把鏡像放在部署文件 (Deployment) 中、配置服務 (Service)、應用所需的帳戶 (ServiceAccount) 及權限 (Role)、命名空間 (Namespace)、密鑰信息 (Secret)、可持久化存儲 (PersistentVolumes) 等資源。也就是編寫一系列互相相關的 YAML 配置文件,將它們部署在 Kubernetes 集羣上。git

可是即使應用的開發者能夠把這些 Docker 鏡像存放在公共倉庫中,而且將所需的 YAML 資源文件提供給用戶,用戶仍然須要本身去尋找這些資源文件,並把它們一一部署。假若用戶但願修改開發者提供的默認資源,好比使用更多的副本 (Replicas) 或是修改服務端口 (Port),他還須要本身去查須要在這些資源文件的哪些地方修改,更不用提版本變動與維護會給開發者和用戶形成多少麻煩了。github

可見最原始的 Kubernetes 應用形態並不便利。golang


Helm 與 Helm Chart

file

在這樣的大環境下,有一系列基於 Kubernetes 的應用包管理工具橫空出世。而咱們今天的主角 Helm,就是這其中最受歡迎的選擇之一。redis

file

開發者按照 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 Chart

那麼站在開發者的角度上,咱們應該如何去創做一個 Helm 應用包呢?api

file

準備工做

首先咱們須要一個準備部署的鏡像。這個鏡像能夠是一個 Java 程序、一個 Python 腳本、甚至是一個空的 linux 鏡像跑幾條命令。bash

file

這裏咱們使用一個簡單的基於 golang 的 hello world HTTP 服務。該服務經過讀取環境變量 USERNAME 得到用戶本身定義的名稱,而後監聽 80 端口。對於任意 HTTP 請求,返回 Hello ${USERNAME}。好比若是設置 USERNAME=world(默認場景),該服務會返回 Hello worldapp

而後咱們使用 Dockerfile 對鏡像進行打包。先對 Golang 代碼進行編譯,而後將編譯後的程序放在基於 alpine 的鏡像中,以縮小鏡像體積。
file

在 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

應用分享

file
那麼製做完成的應用如何和其餘人分享呢?Helm 官方推出的 ChartMuseum 提供了 Chart 倉庫的構建方法,使用它能夠建立本身的 Chart 倉庫。然而自行維護一個倉庫自己成本不小,並且對於用戶而言若是每個開發者都是本身的倉庫,他就須要將所需應用對應的倉庫都加入本身的檢索列表中,很不利於應用的傳播與分享。
file
咱們團隊近期推出了開放雲原生應用中心,Cloud Native App Hub,同步了各種應用,同時還提供了開發者上傳應用的渠道。

在咱們的開放雲原生應用中心中,應用來自兩個渠道:

  • 一方面,咱們按期從一些國外的知名 Helm 倉庫同步 Chart 資源,在同步的過程當中,會對 Chart 內部使用的一部分 Docker 鏡像進行同步替換(例如 gcr.io 或者 quay.io 的鏡像),方便國內用戶訪問使用;
  • 另外一方面,咱們和 Helm 官方庫同樣在 Github 上接受開發者經過 Pull Request 的形式提交本身的應用。提交成功的應用會在短時間內同步至雲原生應用中心,和其餘官方應用展現在一塊兒供其餘用戶使用。

file

雲原生應用開發大賽

參賽點擊:https://developer.aliyun.com/special/apphubchallenge
file


參考

  1. Helm Chart 創做指南 https://cloudnativeapp.gitbook.io/handbook/helm-chart-creation-tutorial
  2. 開放雲原生應用中心 - Cloud Native App Hub https://developer.aliyun.com/hub
  3. 開放雲原生應用中心 Github 倉庫 https://github.com/cloudnativeapp/charts
相關文章
相關標籤/搜索