资讯详情

如何开发一个完整的Helm charts应用实例

文章目录

    • 1. 简介
    • 2. 条件
    • 3. 应用
    • 4. 基础模板
    • 5. 命名模板
    • 6. 版本兼容
    • 7. 持久化
    • 8. 定制
    • 9. 共享 Charts


1. 简介

Helm 图表是在 Kubernetes 构建高效集群的最佳实践之一。它是一种使用 Kubernetes 资源集合的包装形式。Helm 图表使用这些资源来定义应用程序。

Helm 模板方法用于部署应用程序。模板为任何类型的应用程序提供结构。

在这里插入图片描述

2. 条件

  • 安装配置 Minikube 集群(请遵循我们的指南如何 Ubuntu 上安装 Minikube和如何在 CentOS 上安装 Minikube
  • 你需要懂得Helm 安装配置;
  • 你需要掌握基础helm语法写作技巧

3. 应用

以 Ghost 以博客应用为例,展示如何开发一个完整的博客应用 Helm Chart 包,Ghost 是基于 Node.js 开源博客平台。 Helm Chart 包之前最需要做的就是知道应用程序应该如何使用和部署,否则就不可能写相应的 Chart 包的。

启动 Ghost 最简单的方法是直接使用镜像启动:

docker run -d --name my-ghost -p 2368:2368 ghost 

之后我们就可以通过了 http://localhost:2368 访问 Ghost 博客了。

docker rm -f my-ghost 

假如我们想在那里 Kubernetes 集群群中部署两个副本 Ghost,以下资源清单文件可直接应用:

# ghost/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata:   name: ghost spec:   selector:     matchLabels:       app: ghost-app   replicas: 2   template:     metadata:       labels:         app: ghost-app     spec:       containers:         - name: ghost-app           image: ghost           ports:             - containerPort: 2368 --- # ghost/service.yaml apiVersion: v1 kind: Service metadata:   name: ghost spec:   type: NodePort   selector:     app: ghost-app   ports:     - protocol: TCP       port: 80       targetPort: 2368 

直接通过 kubectl 应用上述资源对象:

$ kubectl apply -f  ghost/ service/ghost created deployment.apps/ghost created  $ kubectl get pod -l app=ghost-app NAME                    READY   STATUS    RESTARTS   AGE ghost-ddb558557-7szrc   1/1     Running   0          2m13s ghost-ddb558557-brn9p   1/1     Running   0          2m13s   $ kubectl get svc ghost NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE ghost   NodePort   10.97.232.158   <none>        80:30152/TCP   2m44s  

通过 http://<nodeip>:31950 访问到 Ghost : 清理 deployment

$ delete -f ghost/ deployment.apps "ghost" deleted service "ghost" deleted 

好像要部署 Ghost 这很简单,但如果我们需要为不同的环境设置不同的环境呢?例如,我们想在不同的环境中部署它(staging、prod)我们需要一遍又一遍地复制我们吗? Kubernetes 资源清单文件,这还只是一个场景,还有很多场景可能需要我们去部署应用,这种方式维护起来是非常困难的,这个时候就可以理由 Helm 来解放我们吧。

  • 官方安装 helm
  • helm v3.8.0 命令入门指南

4. 基础模板

现在我们开始创造新的 Helm Chart 包。直接使用 helm create 命令即可:

$ helm create my-ghost  Creating my-ghost ? tree my-ghost my-ghost ├── Chart.yaml ├── charts ├── templates │   ├── NOTES.txt │   ├── _helpers.tpl │   ├── deployment.yaml │   ├── hpa.yaml │   ├── ingress.yaml │   ├── service.yaml │   ├── serviceaccount.yaml │   └── tests │       └── test-connection.yaml └── values.yaml  3 directories, 10 files 

该命令将创建默认 Helm Chart 包脚手架,helm charts 详情请参阅本片文章,以删除以下未使用的文件:

rm -f my-ghost/templates/tests/test-connection.yaml rm -f my-ghost/templates/serviceaccount.yaml
rm -f my-ghost/templates/ingress.yaml
rm -f my-ghost/templates/hpa.yaml
rm -f my-ghost/templates/NOTES.txt

然后修改 templates/deployment.yaml 模板文件:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ghost
spec:
  selector:
    matchLabels:
      app: ghost-app
  replicas: { 
        { 
         .Values.replicaCount }}
  template:
    metadata:
      labels:
        app: ghost-app
    spec:
      containers:
        - name: ghost-app
          image: { 
        { 
         .Values.image }}
          ports:
            - containerPort: 2368
          env:
            - name: NODE_ENV
              value: { 
        { 
         .Values.node_env | default "production" }}
            { 
        { 
        - if .Values.url }}
            - name: url
              value: http://{ 
        { 
         .Values.url }}
            { 
        { 
        - end }}

这和我们前面的资源清单文件非常类似,只是将 replicas 的值使用 { { .Values.replicaCount }} 模板来进行替换了,表示会用 replicaCount 这个 Values 值进行渲染,然后还可以通过设置环境变量来配置 Ghost,同样修改 templates/service.yaml 模板文件的内容:

# templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ghost
spec:
  selector:
    app: ghost-app
  type: { 
        { 
         .Values.service.type }}
  ports:
    - protocol: TCP
      targetPort: 2368
      port: { 
        { 
         .Values.service.port }}
      { 
        { 
        - if (and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort))) }}
      nodePort: { 
        { 
         .Values.service.nodePort }}
      { 
        { 
        - else if eq .Values.service.type "ClusterIP" }}
      nodePort: null
      { 
        { 
        - end }}

同样为了能够兼容多个场景,这里我们允许用户来定制 Servicetype,如果是 NodePort 类型则还可以配置 nodePort 的值,不过需要注意这里的判断,因为有可能即使配置为 NodePort 类型,用户也可能不会主动提供 nodePort,所以这里我们在模板中做了一个条件判断:

{ 
        { 
        - if (and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort))) }}

需要 service.typeNodePort 或者 LoadBalancer 并且 service.nodePort 不为空的情况下才会渲染 nodePort

然后最重要的就是要在 values.yaml 文件中提供默认的 Values 值,如下所示是我们提供的默认的 Values 值:

# values.yaml
replicaCount: 1
image: ghost
node_env: production
url: ghost.k8s.local

service:
  type: NodePort
  port: 80

然后我们可以使用 helm template 命令来渲染我们的模板输出结果:

$  helm template --debug my-ghost
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain3/content/helm/manifests/my-ghost

---
# Source: my-ghost/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ghost
spec:
  selector:
    app: ghost-app
  type: NodePort
  ports:
    - protocol: TCP
      targetPort: 2368
      port: 80
---
# Source: my-ghost/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ghost
spec:
  selector:
    matchLabels:
      app: ghost-app
  replicas: 1
  template:
    metadata:
      labels:
        app: ghost-app
    spec:
      containers:
        - name: ghost-app
          image: ghost
          ports:
            - containerPort: 2368
          env:
            - name: NODE_ENV
              value: production
            - name: url
              value: http://ghost.k8s.local

上面的渲染结果和我们上面的资源清单文件基本上一致了,只是我们现在的灵活性更大了,比如可以控制环境变量、服务的暴露方式等等。

5. 命名模板

虽然现在我们可以使用 Helm Charts 模板来渲染安装 Ghost 了,但是上面我们的模板还有很多改进的地方,比如资源对象的名称我们是固定的,这样我们就没办法在同一个命名空间下面安装多个应用了,所以一般我们也会根据 Chart 名称或者 Release 名称来替换资源对象的名称。

前面默认创建的模板中包含一个 _helpers.tpl 的文件,该文件中包含一些和名称、标签相关的命名模板,我们可以直接使用即可,下面是默认生成的已有的命名模板:

{ 
        { 
        /*
Expand the name of the chart.
*/}}
{ 
        { 
        - define "my-ghost.name" -}}
{ 
        { 
        - default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{ 
        { 
        - end }}

{ 
        { 
        /*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{ 
        { 
        - define "my-ghost.fullname" -}}
{ 
        { 
        - if .Values.fullnameOverride }}
{ 
        { 
        - .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{ 
        { 
        - else }}
{ 
        { 
        - $name := default .Chart.Name .Values.nameOverride }}
{ 
        { 
        - if contains $name .Release.Name }}
{ 
        { 
        - .Release.Name | trunc 63 | trimSuffix "-" }}
{ 
        { 
        - else }}
{ 
        { 
        - printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{ 
        { 
        - end }}
{ 
        { 
        - end }}
{ 
        { 
        - end }}

{ 
        { 
        /*
Create chart name and version as used by the chart label.
*/}}
{ 
        { 
        - define "my-ghost.chart" -}}
{ 
        { 
        - printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{ 
        { 
        - end }}

{ 
        { 
        /*
Common labels
*/}}
{ 
        { 
        - define "my-ghost.labels" -}}
helm.sh/chart: { 
        { 
         include "my-ghost.chart" . }}
{ 
        { 
         include "my-ghost.selectorLabels" . }}
{ 
        { 
        - if .Chart.AppVersion }}
app.kubernetes.io/version: { 
        { 
         .Chart.AppVersion | quote }}
{ 
        { 
        - end }}
app.kubernetes.io/managed-by: { 
        { 
         .Release.Service }}
{ 
        { 
        - end }}

{ 
        { 
        /*
Selector labels
*/}}
{ 
        { 
        - define "my-ghost.selectorLabels" -}}
app.kubernetes.io/name: { 
        { 
         include "my-ghost.name" . }}
app.kubernetes.io/instance: { 
        { 
         .Release.Name }}
{ 
        { 
        - end }}

然后我们可以将 Deployment 的名称和标签替换掉:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: { 
        { 
         template "my-ghost.fullname" . }}
  labels:
{ 
        { 
         include "my-ghost.labels" . | indent 4 }}
spec:
  selector:
    matchLabels:
{ 
        { 
         include "my-ghost.selectorLabels" . | indent 6 }}
  replicas: { 
        { 
         .Values.replicaCount }}
  template:
    metadata:
      labels:
{ 
        { 
         include "my-ghost.selectorLabels" . | indent 8 }}
    spec:
      containers:
        - name: ghost-app
          image: { 
        { 
         .Values.image }}
          ports:
            - containerPort: 2368
          env:
            - name: NODE_ENV
              value: { 
        { 
         .Values.node_env | default "production" }}
            { 
        { 
        - if .Values.url }}
            - name: url
              value: http://{ 
        { 
         .Values.url }}
            { 
        { 
        - end }}

为 Deployment 增加 label 标签,同样 labelSelector 中也使用 my-ghost.selectorLabels 这个命名模板进行替换,同样对 Service 也做相应的改造:

apiVersion: v1
kind: Service
metadata:
  name: { 
        { 
         template "my-ghost.fullname" . }}
  labels:
{ 
        { 
         include "my-ghost.labels" . | indent 4 }}
spec:
  selector:
{ 
        { 
         include "my-ghost.selectorLabels" . | indent 4 }}
  type: { 
        { 
         .Values.service.type }}
  ports:
    - protocol: TCP
      targetPort: 2368
      port: { 
        { 
         .Values.service.port }}
      { 
        { 
        - if (and (or (eq .Values.service.type "NodePort") (eq .Values.service.type "LoadBalancer")) (not (empty .Values.service.nodePort))) }}
      nodePort: { 
        { 
         .Values.service.nodePort }}
      { 
        { 
        - else if eq .Values.service.type "ClusterIP" }}
      nodePort: null
      { 
        { 
        - end }}

现在我们可以再使用 helm template 渲染验证结果是否正确:

$ helm template --debug my-ghost
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain3/content/helm/manifests/my-ghost

---
# Source: my-ghost/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: release-name-my-ghost
  labels:
    helm.sh/chart: my-ghost-0.1.0
    app.kubernetes.io/name: my-ghost
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  selector:
    app.kubernetes.io/name: my-ghost
    app.kubernetes.io/instance: release-name
  type: NodePort
  ports:
    - protocol: TCP
      targetPort: 2368
      port: 80
---
# Source: my-ghost/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: release-name-my-ghost
  labels:
    helm.sh/chart: my-ghost-0.1.0
    app.kubernetes.io/name: my-ghost
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "1.16.0"
    app.kubernetes.io/managed-by: Helm
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: my-ghost
      app.kubernetes.io/instance: release-name
  replicas: 1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: my-ghost
        app.kubernetes.io/instance: release-name
    spec:
      containers:
        - name: ghost-app
          image: ghost
          ports:
            - containerPort: 2368
          env:
            - name: NODE_ENV
              value: production
            - name: url
              value: http://ghost.k8s.local

6. 版本兼容

于 Kubernetes 的版本迭代非常快,所以我们在开发 Chart 包的时候有必要考虑到对不同版本的 Kubernetes 进行兼容,最明显的就是 Ingress 的资源版本。Kubernetes1.19 版本为 Ingress 资源引入了一个新的 API:networking.k8s.io/v1,这与之前的 networking.k8s.io/v1beta1 beta 版本使用方式基本一致,但是和前面的 extensions/v1beta1 这个版本在使用上有很大的不同,资源对象的属性上有一定的区别,所以要兼容不同的版本,我们就需要对模板中的 Ingress 对象做兼容处理。

创建ingress对象,确保你已经安装了ingress controller组件

新版本的资源对象格式如下所示:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

而旧版本的资源对象格式如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

现在我们再为 Ghost 添加一个 Ingress 的模板,新建 templates/ingress.yaml 模板文件,先添加一个 v1 版本的

Ingress 模板:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ghost
spec:
  ingressClassName: nginx
  rules:
  - host: ghost.k8s.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ghost
            port:
              number: 80

然后同样将名称和服务名称这些使用模板参数进行替换:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: { 
        { 
         template "my-ghost.fullname" . }}
  labels:
{ 
        { 
         include "my-ghost.labels" . | indent 4 }}
spec:
  ingressClassName: nginx
  rules:
  - host: { 
        { 
         .Values.url }}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: { 
        { 
         template "my-ghost.fullname" . }}
            port:
              number: { 
        { 
         .Values.service.port }}

然后接下来我们来兼容下其他的版本格式,这里需要用到 Capabilities 对象,在 Chart 包的 _helpers.tpl 文件中添加几个用于判断集群版本或 API 的命名模板:

{ 
        { 
        /* Allow KubeVersion to be overridden. */}}
{ 
        { 
        - define "my-ghost.kubeVersion" -}}
  { 
        { 
        - default .Capabilities.KubeVersion.Version .Values.kubeVersionOverride -}}
{ 
        { 
        - end -

{ 
        { 
        /* Get Ingress API Version */}}
{ 
        { 
        - define "my-ghost.ingress.apiVersion" -}}
  { 
        { 
        - if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" (include "my-ghost.kubeVersion" .)) -}}
      { 
        { 
        - print "networking.k8s.io/v1" -}}
  { 
        { 
        - else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}}
    { 
        { 
        - print "networking.k8s.io/v1beta1" -}}
  { 
        { 
        - else -}}
    { 
        { 
        - print "extensions/v1beta1" -}}
  { 
        { 
        - end -}}
{ 
        { 
        - end -}}

{ 
        { 
        /* Check Ingress stability */}}
{ 
        { 
        - 

标签: m44电容柜用m44r电阻器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台