本文我們展示如何開發一個簡單的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