7. 持久化
上面我们使用的 Ghost 镜像默认使用 SQLite 数据库,所以非常有必要将数据进行持久化,当然我们要将这个开关给到用户去选择,修改 templates/deployment.yaml
模板文件,增加 volumes 相关配置:
# other spec... spec: volumes: - name: ghost-data {{- if .Values.persistence.enabled }} persistentVolumeClaim: claimName: {{ .Values.persistence.existingClaim | default (include "my-ghost.fullname" .) }} {{- else }} emptyDir: {} {{ end }} containers: - name: ghost-app image: {{ .Values.image }} volumeMounts: - name: ghost-data mountPath: /var/lib/ghost/content # other spec...
这里我们通过 persistence.enabled 来判断是否需要开启持久化数据,如果开启则需要看用户是否直接提供了一个存在的 PVC 对象,如果没有提供,则我们需要自己创建一个合适的 PVC 对象,如果不需要持久化,则直接使用 emptyDir:{} 即可,添加 templates/pvc.yaml 模板,内容如下所示:
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} kind: PersistentVolumeClaim apiVersion: v1 metadata: name: {{ template "my-ghost.fullname" . }} labels: {{- include "my-ghost.labels" . | nindent 4 }} spec: {{- if .Values.persistence.storageClass }} storageClassName: {{ .Values.persistence.storageClass | quote }} {{- end }} accessModes: - {{ .Values.persistence.accessMode | quote }} resources: requests: storage: {{ .Values.persistence.size | quote }} {{- end -}}
其中访问模式、存储容量、StorageClass
、存在的 PVC 都通过 Values 来指定,增加了灵活性。对应的 values.yaml
配置部分我们可以给一个默认的配置:
## 是否使用 PVC 开启数据持久化 persistence: enabled: true ## 是否使用 storageClass,如果不适用则补配置 # storageClass: "xxx" ## ## 如果想使用一个存在的 PVC 对象,则直接传递给下面的 existingClaim 变量 # existingClaim: your-claim accessMode: ReadWriteOnce # 访问模式 size: 1Gi # 存储容量
8. 定制
除了上面的这些主要的需求之外,还有一些额外的定制需求,比如用户想要配置更新策略,因为更新策略并不是一层不变的,这里和之前不太一样,我们需要用到一个新的函数 toYaml
:
{{- if .Values.updateStrategy }} strategy: {{ toYaml .Values.updateStrategy | nindent 4 }} {{- end }}
意思就是我们将 updateStrategy
这个 Values 值转换成 YAML 格式,并保留4个空格。然后添加其他的配置,比如是否需要添加 nodeSelector
、容忍、亲和性这些,这里我们都是使用 toYaml
函数来控制空格,如下所示:
{{- if .Values.nodeSelector }} nodeSelector: {{- toYaml .Values.nodeSelector | nindent 8 }} {{- end -}} {{- with .Values.affinity }} affinity: {{- toYaml . | nindent 8 }} {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} {{- end }}
接下来当然就是镜像的配置了,如果是私有仓库还需要指定 imagePullSecrets
:
{{- if .Values.image.pullSecrets }} imagePullSecrets: {{- range .Values.image.pullSecrets }} - name: {{ . }} {{- end }} {{- end }} containers: - name: ghost image: {{ printf "%s:%s" .Values.image.name .Values.image.tag }} imagePullPolicy: {{ .Values.image.pullPolicy | quote }} ports: - containerPort: 2368
对应的 Values 值如下所示:
image: name: ghost tag: latest pullPolicy: IfNotPresent ## 如果是私有仓库,需要指定 imagePullSecrets # pullSecrets: # - myRegistryKeySecretName
然后就是 resource
资源声明,这里我们定义一个默认的 resources
值,同样用 toYaml
函数来控制空格:
resources: {{ toYaml .Values.resources | indent 10 }}
最后是健康检查部分,虽然我们之前没有做 livenessProbe,但是我们开发 Chart 模板的时候就要尽可能考虑周全一点,这里我们加上存活性和可读性、启动三个探针,并且根据 livenessProbe.enabled 、readinessProbe.enabled 以及 startupProbe.enabled 三个 Values 值来判断是否需要添加探针,探针对应的参数也都通过 Values 值来配置:
{{- if .Values.startupProbe.enabled }} startupProbe: httpGet: path: / port: 2368 initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} periodSeconds: {{ .Values.startupProbe.periodSeconds }} timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} failureThreshold: {{ .Values.startupProbe.failureThreshold }} successThreshold: {{ .Values.startupProbe.successThreshold }} {{- end }} {{- if .Values.livenessProbe.enabled }} livenessProbe: httpGet: path: / port: 2368 initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.livenessProbe.periodSeconds }} timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} failureThreshold: {{ .Values.livenessProbe.failureThreshold }} successThreshold: {{ .Values.livenessProbe.successThreshold }} {{- end }} {{- if .Values.readinessProbe.enabled }} readinessProbe: httpGet: path: / port: 2368 initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.readinessProbe.periodSeconds }} timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} failureThreshold: {{ .Values.readinessProbe.failureThreshold }} successThreshold: {{ .Values.readinessProbe.successThreshold }} {{- end }}
默认的 values.yaml
文件如下所示:
replicaCount: 1 image: name: ghost tag: latest pullPolicy: IfNotPresent node_env: production url: ghost.k8s.local service: type: ClusterIP port: 80 ingress: enabled: true ingressClass: nginx ## 是否使用 PVC 开启数据持久化 persistence: enabled: true ## 是否使用 storageClass,如果不适用则补配置 # storageClass: "xxx" ## ## 如果想使用一个存在的 PVC 对象,则直接传递给下面的 existingClaim 变量 # existingClaim: your-claim accessMode: ReadWriteOnce # 访问模式 size: 1Gi # 存储容量 nodeSelector: {} affinity: {} tolerations: {} resources: {} startupProbe: enabled: false livenessProbe: enabled: false readinessProbe: enabled: false
现在我们再去更新 Release:
$ helm upgrade --install my-ghost ./my-ghost -n default Release "my-ghost" has been upgraded. Happy Helming! NAME: my-ghost LAST DEPLOYED: Thu Mar 17 16:03:02 2022 NAMESPACE: default STATUS: deployed REVISION: 2 TEST SUITE: None ➜ helm ls -n default NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION my-ghost default 2 2022-03-17 16:05:07.123349 +0800 CST deployed my-ghost-0.1.0 1.16.0 ➜ kubectl get pods -n default NAME READY STATUS RESTARTS AGE my-ghost-6dbc455fc7-cmm4p 1/1 Running 0 2m42s ➜ kubectl get pvc -n default NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE my-ghost Bound pvc-2f0b7d5a-04d4-4331-848b-af21edce673e 1Gi RWO nfs-client 4m59s k get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-d62828dd-56ba-4819-a67a-0cd67b65dcd2 1Gi RWO Delete Bound default/my-ghost standard 7s $ k get pv pvc-d62828dd-56ba-4819-a67a-0cd67b65dcd2 -oyaml apiVersion: v1 kind: PersistentVolume metadata: annotations: hostPathProvisionerIdentity: d798e478-e315-4720-abba-dd9e6af28464 pv.kubernetes.io/provisioned-by: k8s.io/minikube-hostpath creationTimestamp: "2022-03-30T13:02:22Z" finalizers: - kubernetes.io/pv-protection name: pvc-d62828dd-56ba-4819-a67a-0cd67b65dcd2 resourceVersion: "1594" uid: b1edac7a-c960-4152-bf3b-a1038790b3a8 spec: accessModes: - ReadWriteOnce capacity: storage: 1Gi claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: my-ghost namespace: default resourceVersion: "1591" uid: d62828dd-56ba-4819-a67a-0cd67b65dcd2 hostPath: path: /tmp/hostpath-provisioner/default/my-ghost type: "" persistentVolumeReclaimPolicy: Delete storageClassName: standard volumeMode: Filesystem status: phase: Bound ➜ kubectl get ingress -n default NAME CLASS HOSTS ADDRESS PORTS AGE my-ghost nginx ghost.k8s.local 192.168.31.31 80 3h24m
到这里我们就基本完成了这个简单的 Helm Charts 包的开发,当然以后可能还会有新的需求,我们需要不断去迭代优化。
当我们设置storageclass: ""或者注释storageclass时,minikube会自动一个hostpath本地pv
my-ghost/value.yaml配置
当设置持久enabled: false
,它为非持久化部署。
persistence: enabled: false
9. 共享 Charts
Helm Charts 包开发完成了,如果别人想要使用我们的包,则需要我们共享出去,我们可以通过 Chart 仓库来进行共享,Helm Charts 可以在远程存储库或本地环境/存储库中使用,远程存储库可以是公共的,如 Bitnami Charts 也可以是托管存储库,如 Google Cloud Storage 或 GitHub。为了演示方便,这里我们使用 GitHub 来托管我们的 Charts 包。
我们可以使用 GitHub Pages 来创建 Charts 仓库,GitHub 允许我们以两种不同的方式提供静态网页:
通过配置项目提供其 docs/ 目录的内容
通过配置项目来服务特定的分支
这里我们将采用第二种方法,首先在 GitHub 上创建一个代码仓库:https://github.com/Ghostwritten/helm-demo,将上面我们创建的 my-ghost 包提交到仓库 charts 目录下,然后打包 chart 包:
$ helm package helm-demo/my-ghost Successfully packaged chart and saved it to: /root/my-ghost-0.1.0.tgz
我们可以将打包的压缩包放到另外的目录 repo/stable
中去,现在仓库的结构如下所示:
$ tree . . ├── charts │ └── my-ghost │ ├── Chart.yaml │ ├── templates │ │ ├── deployment.yaml │ │ ├── _helpers.tpl │ │ ├── ingress.yaml │ │ ├── pvc.yaml │ │ └── service.yaml │ └── values.yaml ├── LICENSE ├── README.md └── repo └── stable └── my-ghost-0.1.0.tgz 5 directories, 10 files
执行如下所示命令生成 index 索引文件:
helm repo index repo/stable --url https://raw.githubusercontent.com/cnych/helm101/main/repo/stable
上述命令会在 repo/stable
目录下面生成一个如下所示的 index.yaml
文件:
$ cat repo/stable/index.yaml apiVersion: v1 entries: my-ghost: - apiVersion: v2 appVersion: 1.16.0 created: "2022-03-30T21:39:37.202992379+08:00" description: A Helm chart for Kubernetes digest: 44d40c93d408f4d109a66b8ca61b14417c6d6b465cf636ae0a0767b73d5c6d13 name: my-ghost type: application urls: - https://raw.githubusercontent.com/ghostwritten/helm-demo/main/repo/stable/my-ghost-0.1.0.tgz version: 0.1.0 generated: "2022-03-30T21:39:37.201867108+08:00"
该 index.yaml
文件是我们通过仓库获取 Chart 包的关键。然后将代码推送到 GitHub
,如何学习本地项目上传github请参阅:
$ yum -y install git $ git config --global user.name "ghostwritten" $ git config --global user.email "1zoxun1@gmail.com" $ git config list #查看 /root/.ssh/id_rsa.pub 并复制到github的设置页面的SSH keys $ cat /root/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqgcbG1iD0m/6KWZPu4uJv+vM7ZBfrbnCib6egfz8+YGWMpTYnD8EgFZ1j/cH6G3odktP0ZjvUriFY+SZIYQtpIdvQ7ciG25HQC9WRREAchGbTcvyw0Jt4F2S5EJJVBzEYFwlhz2JH1iUqbzyPjyRH 1zoxun1@gmail.com
并复制到github的设置页面的SSH keys
$ pwd /root/helm-demo $ git init $ git add * $ git commit -m "add helm-demo" $ git remote add origin https://github.com/Ghostwritten/helm-demo.git $ git push origin main
接下来为该仓库设置 GitHub Pages
,首先在本地新建一个 gh-pages
分支:
只将 repo/stable/index.yaml
文件保留到根目录下面,其他文件忽略,然后推送到远程仓库:
$ tree . ├── index.yaml ├── LICENSE └── README.md 0 directories, 3 files
$ git push origin gh-pages
在 GitHub Pages
页面选择使用 gh-pages 分支即可:
现在我们就可以通过 http://smoothies.com.cn/helm-demo/
来获取我们的 Chart 包了。
使用如下所示命令添加 repo 仓库:
$ helm repo add helm-demo http://smoothies.com.cn/helm-demo/ "helm-demo" has been added to your repositories
我们也可以使用 helm search
来搜索仓库中的 Chart 包,正常就包含上面我们的 my-ghost
了:
$ helm search repo helm-demo NAME CHART VERSION APP VERSION DESCRIPTION helm-demo/my-ghost 0.1.0 1.16.0 A Helm chart for Kubernetes
接下来就可以正常使用 chart 包进行操作了,比如进行安装:
helm install hello-ghost helm-demo/my-ghost
✈推荐阅读: