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

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 如何开发一个完整的Helm charts应用实例(1)

文章目录

1. 简介

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

Helm 图表使用模板方法来部署应用程序。模板为项目提供结构,适用于任何类型的应用程序。

1832b220aa754cd18c504acc7686a560.png

2. 条件

3. 应用

以 Ghost 博客应用为例来演示如何开发一个完整的 Helm Chart 包,Ghost 是基于 Node.js 的开源博客平台。在开发 Helm Chart 包之前最需要做的的就是要知道应用应该如何使用、如何部署,不然是不可能编写出对应的 Chart 包的。

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

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

1832b220aa754cd18c504acc7686a560.png

后我们就可以通过 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

1832b220aa754cd18c504acc7686a560.png

清理 deployment

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

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

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 }}

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

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

需要 service.type 为 NodePort 或者 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 的资源版本。Kubernetes 在 1.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 */}}
{{- define "my-ghost.ingress.isStable" -}}
  {{- eq (include "my-ghost.ingress.apiVersion" .) "networking.k8s.io/v1" -}}
{{- end -}}
{{/* Check Ingress supports pathType */}}
{{/* pathType was added to networking.k8s.io/v1beta1 in Kubernetes 1.18 */}}
{{- define "my-ghost.ingress.supportsPathType" -}}
  {{- or (eq (include "my-ghost.ingress.isStable" .) "true") (and (eq (include "my-ghost.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" (include "my-ghost.kubeVersion" .))) -}}
{{- end -}}

上面我们通过 .Capabilities.APIVersions.Has 来判断我们应该使用的 APIVersion,如果版本为 networking.k8s.io/v1,则定义为 isStable,此外还根据版本来判断是否需要支持 pathType 属性,然后在 Ingress 对象模板中就可以使用上面定义的命名模板来决定应该使用哪些属性,如下所示:

{{- if .Values.ingress.enabled }}
{{- $apiIsStable := eq (include "my-ghost.ingress.isStable" .) "true" -}}
{{- $ingressSupportsPathType := eq (include "my-ghost.ingress.supportsPathType" .) "true" -}}
apiVersion: {{ include "my-ghost.ingress.apiVersion" . }}
kind: Ingress
metadata:
  name: {{ template "my-ghost.fullname" . }}
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    {{- if and .Values.ingress.ingressClass (not $apiIsStable) }}
    kubernetes.io/ingress.class: {{ .Values.ingress.ingressClass }}
    {{- end }}
  labels:
    {{- include "my-ghost.labels" . | nindent 4 }}
spec:
  {{- if and .Values.ingress.ingressClass $apiIsStable }}
  ingressClassName: {{ .Values.ingress.ingressClass }}
  {{- end }}
  rules:
  {{- if not (empty .Values.url) }}
  - host: {{ .Values.url }}
    http:
  {{- else }}
  - http:
  {{- end }}
      paths:
      - path: /
        {{- if $ingressSupportsPathType }}
        pathType: Prefix
        {{- end }}
        backend:
          {{- if $apiIsStable }}
          service:
            name: {{ template "my-ghost.fullname" . }}
            port:
              number: {{ .Values.service.port }}
          {{- else }}
          serviceName: {{ template "my-ghost.fullname" . }}
          servicePort: {{ .Values.service.port }}
          {{- end }}
{{- end }}

由于有的场景下面并不需要使用 Ingress 来暴露服务,所以首先我们通过一个 ingress.enabled 属性来控制是否需要渲染,然后定义了一个 $apiIsStable 变量,来表示当前集群是否是稳定版本的 API,然后需要根据该变量去渲染不同的属性,比如对于 ingressClass,如果是稳定版本的 API 则是通过 spec.ingressClassName 来指定,否则是通过 kubernetes.io/ingress.class 这个 annotations 来指定。然后这里我们在 values.yaml 文件中添加如下所示默认的 Ingress 的配置数据:

ingress:
  enabled: true
  ingressClass: nginx

现在我们再次渲染 Helm Chart 模板来验证资源清单数据:

$ 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
---
# Source: my-ghost/templates/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: release-name-my-ghost
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
  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:
  ingressClassName: nginx
  rules:
  - host: ghost.k8s.local
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: release-name-my-ghost
            port:
              number: 80

从上面的资源清单可以看出是符合我们的预期要求的,在我们安装测试前,一定要确认我们的kubernetes集群是否安装ingress controller, 你也可以minikube安装nginx ingress controller ,我们可以来安装测试下结果:

$ helm upgrade --install my-ghost ./my-ghost -n default
Release "my-ghost" does not exist. Installing it now.
NAME: my-ghost
LAST DEPLOYED: Mon Mar 28 20:03:51 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
$ helm ls -n default
NAME      NAMESPACE REVISION  UPDATED                                 STATUS    CHART           APP VERSION
my-ghost  default   1         2022-03-28 20:03:51.782130568 +0800 CST deployed  my-ghost-0.1.0  1.16.0     
$ kubectl get pods -n default
NAME                        READY   STATUS    RESTARTS   AGE
my-ghost-6f698dc49d-ccphv   1/1     Running   0          29s
$ kubectl get svc -n default
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        163m
my-ghost     NodePort    10.102.53.53   <none>        80:32204/TCP   39s
$ kubectl get ingress -n default
NAME       CLASS   HOSTS             ADDRESS   PORTS   AGE
my-ghost   nginx   ghost.k8s.local             80      49s
$ k get ingress -oyaml
apiVersion: v1
items:
- apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    annotations:
      meta.helm.sh/release-name: my-ghost
      meta.helm.sh/release-namespace: default
      nginx.ingress.kubernetes.io/ssl-redirect: "false"
    creationTimestamp: "2022-03-28T15:50:43Z"
    generation: 1
    labels:
      app.kubernetes.io/instance: my-ghost
      app.kubernetes.io/managed-by: Helm
      app.kubernetes.io/name: my-ghost
      app.kubernetes.io/version: 1.16.0
      helm.sh/chart: my-ghost-0.1.0
    name: my-ghost
    namespace: default
    resourceVersion: "17132"
    uid: d94aa7b8-6631-4e6f-aa2e-4317615cef76
  spec:
    ingressClassName: nginx
    rules:
    - host: ghost.k8s.local
      http:
        paths:
        - backend:
            service:
              name: my-ghost
              port:
                number: 80
          path: /
          pathType: Prefix
  status:
    loadBalancer:
      ingress:
      - ip: 192.168.211.51
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

正常就可以部署成功 Ghost 了,并且可以通过域名 http://ghost.k8s.local 进行访问了:

当然虚拟机部署要配置域名解析:

echo '192.168.211.51   ghost.k8s.local' >> /etc/hosts

1832b220aa754cd18c504acc7686a560.png

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
Kubernetes 关系型数据库 MySQL
容器 & 服务:Helm Charts配置文件分析
chart 是 Helm 的应用打包格式。chart 由一系列文件组成,这些文件描述了 K8s 部署应用时所需要的资源,比如 Service、Deployment、PersistentVolumeClaim、Secret、ConfigMap 等。 chart可繁可简,即可以只用于部署一个单独的服务,例如mysql、nginx等等,也可以用于部署整个应用,例如由HTTP服务、数据库、缓存、中间件等共同构成的复杂应用。
417 0
|
Kubernetes 搜索推荐 应用服务中间件
【kubernetes】新版helm3的三大概念+快速指南+自定义charts模板
chart:代表helm包,包含在 Kubernetes 集群内部运行应用程序,工具或服务所需的所有资源定义。 Repository(仓库):用来存放和共享 charts 的地方。 Release :运行在 Kubernetes 集群中的 chart 的实例,一个 chart 通常可以在同一个集群中安装多次,每一次安装都会创建一个新的 release。
472 1
【kubernetes】新版helm3的三大概念+快速指南+自定义charts模板
|
23天前
|
Kubernetes 容器
在K8S中,helm是什么?如何使用?
在K8S中,helm是什么?如何使用?
|
4月前
|
Kubernetes 关系型数据库 数据库
使用 Helm 部署 Wikijs
使用 Helm 部署 Wikijs
|
4月前
|
Prometheus Kubernetes 数据可视化
Helm3部署kubeview资源可视化工具
Helm3部署kubeview资源可视化工具
53 0
|
存储 Kubernetes 监控
Helm Dashboard GUI 工具解析
Hello folks,今天我们介绍一下由 Komodor 推出的开源项目 Helm-Dashboard。也是继 ValidKube 之后的第二个开源项目。
154 0
|
存储 Kubernetes Go
helm chart编写入门
helm chart编写入门
1957 0
|
Kubernetes 数据可视化 jenkins
可视化 Tekton 组件 Tekton Dashboard
Tekton Dashboard 使用指南。
4197 0
可视化 Tekton 组件 Tekton Dashboard
|
存储 Kubernetes Linux
如何开发一个完整的Helm charts应用实例(2)
如何开发一个完整的Helm charts应用实例(2)
如何开发一个完整的Helm charts应用实例(2)
|
Kubernetes 应用服务中间件 Linux
helm实战之开发Chart
实战自定义Chart的开发
438 0
helm实战之开发Chart