Helm Chart開發入門

本文我們展示如何開發一個簡單的Chart,其中包含了一個Deployment和Service簡單的Template,最後安裝該Chart。整個Chart的代碼已經放到https://github.com/twingao/httpbin

先使用helm create命令創建一個Chart。

helm create httpbin
Creating httpbin

查看httpbin的目錄結構。

tree httpbin -a
httpbin
├── charts
├── Chart.yaml
├── .helmignore
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files

Helm規範了Chart的目錄和文件結構,這些目錄或者文件都有確定的用途。

  • charts/,包含其它Chart,稱之爲Sub Chart,或者依賴Chart。

  • Chart.yaml,包含Chart的說明,可在從模板中訪問Chart定義的值。

  • .helmignore,定義了在helm package時哪些文件不會打包到Chart包tgz中。

  • ci/,缺省沒有該目錄,持續集成的一些腳本。

  • templates/,用於放置模板文件,主要定義提交給Kubernetes的資源yaml文件。安裝Chart時,Helm會根據chart.yaml、values.yam以及命令行提供的值對Templates進行渲染,最後會將渲染的資源提交給Kubernetes。

  • _helpers.tpl,定義了一些可重用的模板片斷,此文件中的定義在任何資源定義模板中可用。

  • NOTES.txt,提供了安裝後的使用說明,在Chart安裝和升級等操作後,

  • tests/,包含了測試用例。測試用例是pod資源,指定一個的命令來運行容器。容器應該成功退出(exit 0),測試被認爲是成功的。該pod定義必須包含helm測試hook註釋之一:helm.sh/hook: test-success或helm.sh/hook: test-failure。

  • values.yaml,values文件對模板很重要,該文件包含Chart默認值。Helm渲染template時使用這些值。

查看values.yaml文件,需要做一些配置修改:

  • 將鏡像改爲image.repository=docker.io/kennethreitz/httpbin。
  • 不創建serviceAccount,serviceAccount.create=false
  • 爲了Kubernetes集羣外能訪問Service,將type改爲NodePort。並增加一個參數,爲nodePort配置一個固定端口。

values.yaml修改如下:

cd httpbin/

vi values.yaml
# Default values for httpbin.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: docker.io/kennethreitz/httpbin
  tag: latest
  pullPolicy: IfNotPresent

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

serviceAccount:
  # Specifies whether a service account should be created
  create: false
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name:

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: NodePort
  port: 80
  nodePort: 30080

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: chart-example.local
      paths: []
  tls: []
  #  - 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

nodeSelector: {}

tolerations: []

affinity: {}

由於配置了固定的nodePort,所以在service.yaml中增加該參數,並引用了對應的value值。

vi templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "httpbin.fullname" . }}
  labels:
    {{- include "httpbin.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
      nodePort: {{ .Values.service.nodePort }}
  selector:
    {{- include "httpbin.selectorLabels" . | nindent 4 }}

查看deployment.yaml,可以看出鏡像的tag使用變量{{ .Chart.AppVersion }},該參數定義在Chart.yaml文件中。

vi templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "httpbin.fullname" . }}
  labels:
    {{- include "httpbin.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "httpbin.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "httpbin.selectorLabels" . | nindent 8 }}
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
    {{- end }}
      serviceAccountName: {{ include "httpbin.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
    {{- end }}
    {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
    {{- end }}

將Chart.yaml文件中的appVersion改爲latest。appVersion一般配置爲鏡像的版本號。

vi Chart.yaml
apiVersion: v2
name: httpbin
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
appVersion: latest

現在已經開發完成了httpbin的Chart。我們看一下模板的渲染後的效果。

helm template my-release httpbin
---
# Source: httpbin/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-release-httpbin
  labels:
    helm.sh/chart: httpbin-0.1.0
    app.kubernetes.io/name: httpbin
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/version: "latest"
    app.kubernetes.io/managed-by: Helm
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
      nodePort: 30080
  selector:
    app.kubernetes.io/name: httpbin
    app.kubernetes.io/instance: my-release
---
# Source: httpbin/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-release-httpbin
  labels:
    helm.sh/chart: httpbin-0.1.0
    app.kubernetes.io/name: httpbin
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/version: "latest"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: httpbin
      app.kubernetes.io/instance: my-release
  template:
    metadata:
      labels:
        app.kubernetes.io/name: httpbin
        app.kubernetes.io/instance: my-release
    spec:
      serviceAccountName: default
      securityContext:
        {}
      containers:
        - name: httpbin
          securityContext:
            {}
          image: "docker.io/kennethreitz/httpbin:latest"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {}
---
# Source: httpbin/templates/tests/test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: "my-release-httpbin-test-connection"
  labels:

    helm.sh/chart: httpbin-0.1.0
    app.kubernetes.io/name: httpbin
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/version: "latest"
    app.kubernetes.io/managed-by: Helm
  annotations:
    "helm.sh/hook": test-success
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args:  ['my-release-httpbin:80']
  restartPolicy: Never

然後我們安裝Chart。

cd ..

helm install my-release httpbin
NAME: my-release
LAST DEPLOYED: Sun Mar 29 12:43:29 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services my-release-httpbin)
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

查看Kubernetes的資源。

kubectl get all
NAME                                      READY   STATUS    RESTARTS   AGE
pod/my-release-httpbin-74956dc94f-mgwcp   1/1     Running   0          104s

NAME                         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
service/kubernetes           ClusterIP   10.1.0.1      <none>        443/TCP        105d
service/my-release-httpbin   NodePort    10.1.170.94   <none>        80:30080/TCP   104s

NAME                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-release-httpbin   1/1     1            1           104s

NAME                                            DESIRED   CURRENT   READY   AGE
replicaset.apps/my-release-httpbin-74956dc94f   1         1         1       104s

根據安裝說明Notes,可以執行這些命令給出如何訪問該服務。

export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services my-release-httpbin)
export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
http://192.168.1.55:30080

訪問httpbin服務。說明該Chart開發完成,並能夠正確安裝。

curl -i http://192.168.1.55:30080/status/200
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Sun, 29 Mar 2020 04:47:41 GMT
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: 0

根據上面渲染的Kubernetes資源,httpbin/templates/tests/test-connection.yaml文件定義了測試用例,在Pod中通過wget my-release-httpbin:80來測試該Chart是否安裝成功。

helm test my-release
Pod my-release-httpbin-test-connection pending
Pod my-release-httpbin-test-connection pending
Pod my-release-httpbin-test-connection succeeded
NAME: my-release
LAST DEPLOYED: Sun Mar 29 12:43:29 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE:     my-release-httpbin-test-connection
Last Started:   Sun Mar 29 12:49:13 2020
Last Completed: Sun Mar 29 12:49:31 2020
Phase:          Succeeded
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services my-release-httpbin)
  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

將Chart打包,tgz包文件中會自動添加Chart.yaml的version參數。

helm package httpbin
Successfully packaged chart and saved it to: /root/httpbin-0.1.0.tgz

ls
httpbin  httpbin-0.1.0.tgz
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章