Skip to content

1.1

主旨

  • Helm 安装配置使用
  • Helm 系统进阶学习整理
  • Helm渲染过程

说明

环境说明

// 10.2.21.47
# helm version
version.BuildInfo{Version:"v3.11.2", GitCommit:"912ebc1cd10d38d340f048efaf0abda047c3468e", GitTreeState:"clean", GoVersion:"go1.18.10"}


// 10.2.21.47(如下版本已经被替换)
# helm version
version.BuildInfo{Version:"v3.10.0", GitCommit:"ce66412a723e4d89555dc67217607c6579ffcb21", GitTreeState:"clean", GoVersion:"go1.18.6"}

版本对应关系

Helm 版本 支持的 Kubernetes 版本
3.11.x  1.26.x - 1.23.x
3.10.x  1.25.x - 1.22.x
3.9.x   1.24.x - 1.21.x
3.8.x   1.23.x - 1.20.x
3.7.x   1.22.x - 1.19.x
3.6.x   1.21.x - 1.18.x
3.5.x   1.20.x - 1.17.x
3.4.x   1.19.x - 1.16.x

安装

helm-v3.10.0

tar xvf helm-v3.10.0-linux-amd64.tar.gz 
mv linux-amd64/helm /usr/local/bin/helm
官网安装文档地址:
https://docs.helm.sh/docs/intro/install/

github下载:
https://github.com/helm/helm/releases
https://get.helm.sh/helm-v3.11.2-linux-arm.tar.gz

下载安装版本:
wget https://get.helm.sh/helm-v3.10.0-linux-amd64.tar.gz

wget https://get.helm.sh/helm-v3.11.2-linux-amd64.tar.gz

配置仓库源

helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update
helm repo list
helm repo remove aliyun

官方链接

helm中文doc

helm英文doc

目录结构

# ls
charts  Chart.yaml  templates  values.yaml
# tree 
.
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files


Chart.yaml:用于描述这个 Chart 的基本信息,包括名字、描述信息以及版本等。
values.yaml:用于存储 templates 目录中模板文件中用到变量的值。
Templates:目录里面存放所有 yaml 模板文件。
charts:目录里存放这个 chart 依赖的所有子 chart。
NOTES.txt :用于介绍 Chart 帮助信息,helm install 部署后展示给用户。例如:如何使用这个 Chart、 列出缺省的设置等。
_helpers.tpl:放置模板助手的地方,可以在整个 chart 中重复使用。

目录文件详解

Chart.yaml

# cat Chart.yaml 
apiVersion: v2
name: mychart
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.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
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. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

values.yaml

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

replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: ""

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

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

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

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

service:
  type: ClusterIP
  port: 80

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

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

nodeSelector: {}

tolerations: []

affinity: {}

templates/deployment.yaml

# cat deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "mychart.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .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 }}

templates/_helpers.tpl

# cat _helpers.tpl 
{{/*
Expand the name of the chart.
*/}}
{{- define "mychart.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 "mychart.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 "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

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

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

{{/*
Create the name of the service account to use
*/}}
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

templates/hpa.yaml

# cat hpa.yaml 
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "mychart.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
    - type: Resource
      resource:
        name: cpu
        targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
    {{- end }}
    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
    - type: Resource
      resource:
        name: memory
        targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
    {{- end }}
{{- end }}

templates/ingress.yaml

# cat ingress.yaml 
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "mychart.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
  {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
  {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
  {{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
  name: {{ $fullName }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  {{- if .Values.ingress.tls }}
  tls:
    {{- range .Values.ingress.tls }}
    - hosts:
        {{- range .hosts }}
        - {{ . | quote }}
        {{- end }}
      secretName: {{ .secretName }}
    {{- end }}
  {{- end }}
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
            pathType: {{ .pathType }}
            {{- end }}
            backend:
              {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
              service:
                name: {{ $fullName }}
                port:
                  number: {{ $svcPort }}
              {{- else }}
              serviceName: {{ $fullName }}
              servicePort: {{ $svcPort }}
              {{- end }}
          {{- end }}
    {{- end }}
{{- end }}

templates/NOTES.txt

]# cat NOTES.txt 
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
  {{- range .paths }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
  {{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "mychart.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "mychart.fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "mychart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
  echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "mychart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

templates/serviceaccount.yaml

# cat serviceaccount.yaml 
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ include "mychart.serviceAccountName" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
  {{- with .Values.serviceAccount.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
{{- end }}

templates/service.yaml

# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "mychart.selectorLabels" . | nindent 4 }}

templates/tests/test-connection.yaml

 cat tests/test-connection.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "mychart.fullname" . }}-test-connection"
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['{{ include "mychart.fullname" . }}:{{ .Values.service.port }}']
  restartPolicy: Never

查看仓库示例

// 查看仓库中的包
# helm search repo tomcat
NAME            CHART VERSION   APP VERSION     DESCRIPTION                                       
bitnami/tomcat  10.9.1          10.1.9          Apache Tomcat is an open-source web server desi...

// 将包下载到本地
# helm pull bitnami/tomcat

示例演示

示例一

创建一个模版demo-chart包
# helm create demo-chart
Creating demo-chart
# tree demo-chart/
demo-chart/
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files
将demo-chart包部署到Kubernetes上
// helm install 命令可以从多个来源进行安装,参考命令如下:

chart 的仓库(如上所述)
本地 chart 压缩包(helm install foo foo-0.1.1.tgz)
解压后的 chart 目录(helm install foo path/to/foo)
完整的 URL(helm install foo https://example.com/charts/foo-1.2.3.tgz)
# helm  install demo-chart --generate-name 
NAME: demo-chart-1684750433
LAST DEPLOYED: Mon May 22 18:13:54 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=demo-chart,app.kubernetes.io/instance=demo-chart-1684750433" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
# kubectl get pod
NAME                                     READY   STATUS    RESTARTS   AGE
demo-chart-1684750433-68dbb4654b-vzrn9   1/1     Running   0          41s
rbd-provisioner-76f6bc6669-9l5c6         1/1     Running   4          174d
# kubectl get svc --all-namespaces
NAMESPACE     NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                        AGE
default       demo-chart-1684750433   ClusterIP   10.1.55.124    <none>        80/TCP                         52s
k8s上查看部署的应用
# kubectl get svc --all-namespaces -o wide
NAMESPACE     NAME                    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                        AGE    SELECTOR
default       demo-chart-1684750433   ClusterIP   10.1.55.124    <none>        80/TCP                         15h    app.kubernetes.io/instance=demo-chart-1684750433,app.kubernetes.io/name=demo-chart
helm卸载应用
# helm uninstall demo-chart-1684750433
release "demo-chart-1684750433" uninstalled

示例二

# helm create nginx
Creating nginx
# tree nginx/
nginx/
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files
# helm install -f values.yaml nginx .
Error: INSTALLATION FAILED: open values.yaml: no such file or directory
# cd nginx/
# helm install -f values.yaml nginx .
Error: INSTALLATION FAILED: Kubernetes cluster unreachable: Get "http://localhost:8080/version": dial tcp [::1]:8080: connect: connection refused

// 解决方法
将k8s集群的证书文件放在和helm所在的服务器即可
临时测试:
# export KUBECONFIG=/etc/kubernetes/admin.conf
长期有效:
# vi /etc/profile
此文件末尾添加
export KUBECONFIG=/etc/kubernetes/admin.conf

# source /etc/profile


// helm安装应用
# helm install -f values.yaml nginx .
NAME: nginx
LAST DEPLOYED: Mon Oct 23 17:03:56 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=nginx,app.kubernetes.io/instance=nginx" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT

// 获取部署应用的pod名称
# kubectl get pods --namespace default -l "app.kubernetes.io/name=nginx,app.kubernetes.io/instance=nginx" -o jsonpath="{.items[0].metadata.name}"
nginx-5cd949c459-d8frv

// 获取容器端口
# kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}"
80

// Forwarding from 127.0.0.1:8080 -> 80将端口暴露方便外部访问
# kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080

// 新开一个终端查看
# curl http://127.0.0.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


// 打包应用示例
# pwd
/tmp
# ls
nginx
# helm package nginx
Successfully packaged chart and saved it to: /tmp/nginx-0.1.0.tgz
# ls
nginx  nginx-0.1.0.tgz

示例三

[root@master helm]# helm search repo tomcat
NAME            CHART VERSION   APP VERSION     DESCRIPTION                                       
bitnami/tomcat  10.9.1          10.1.9          Apache Tomcat is an open-source web server desi...
[root@master helm]# helm install my-tomcat bitnami/tomcat 
NAME: my-tomcat
LAST DEPLOYED: Tue May 23 10:39:34 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: tomcat
CHART VERSION: 10.9.1
APP VERSION: 10.1.9

** Please be patient while the chart is being deployed **

1. Get the Tomcat URL by running:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w my-tomcat'

  export SERVICE_IP=$(kubectl get svc --namespace default my-tomcat --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
  echo "Tomcat URL:            http://$SERVICE_IP/"
  echo "Tomcat Management URL: http://$SERVICE_IP/manager"

2. Login with the following credentials

  echo Username: user
  echo Password: $(kubectl get secret --namespace default my-tomcat -o jsonpath="{.data.tomcat-password}" | base64 -d)
[root@master helm]# kubectl get secret --namespace default my-tomcat -o jsonpath="{.data.tomcat-password}" | base64 -d
B4Lqi3TK7o
[root@master helm]#

自定义chart包

  • 基于示例能够制作自己的业务chart包
  • 将自己的chart包能够部署到k8s环境中
  • 详细了解各个模版中各个文件各个参数意义
  • 尽可能适配不同的k8s版本、k8s集群等

打包自定义包

// 基于文件夹打包
# helm package nginx
Successfully packaged chart and saved it to: /home/opt/helm/tmp/nginx-0.1.0.tgz

基于自定义打包安装

helm install foo nginx-0.1.0.tgz
如下示例说明
基于nginx创建mynginxx应用

历史版本查看

[root@master helm]# helm ls
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
myapp           default         1               2023-05-24 13:37:21.930444942 +0800 CST failed          myapp-0.1.0     1.16.0     
mynginxx        default         2               2023-05-24 13:44:34.1743329 +0800 CST   deployed        nginx-0.1.0     1.16.0     
[root@master helm]# helm history mynginxx
REVISION        UPDATED                         STATUS          CHART           APP VERSION     DESCRIPTION     
1               Wed May 24 13:38:20 2023        superseded      nginx-0.1.0     1.16.0          Install complete
2               Wed May 24 13:44:34 2023        deployed        nginx-0.1.0     1.16.0          Upgrade complete

升级回滚

// 升级操作
[root@master helm]# helm upgrade mynginxx ./nginx/
Release "mynginxx" has been upgraded. Happy Helming!
NAME: mynginxx
LAST DEPLOYED: Wed May 24 13:44:34 2023
NAMESPACE: default
STATUS: deployed
REVISION: 2
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace default -l "app.kubernetes.io/name=nginx,app.kubernetes.io/instance=mynginxx" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace default $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace default port-forward $POD_NAME 8080:$CONTAINER_PORT
[root@master helm]# 

// 之前是一个副本,现在升级后变成两个副本
[root@master helm]# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
mynginxx-78c6558bbb-hz9zx          1/1     Running   0          6m43s
mynginxx-78c6558bbb-vm487          1/1     Running   0          30s
rbd-provisioner-76f6bc6669-9l5c6   1/1     Running   4          176d
[root@master helm]#

// 执行回滚操作
[root@master helm]# helm rollback mynginxx 1 -n default
Rollback was a success! Happy Helming!
[root@master helm]#


// 历史版本查看
[root@master helm]# helm history mynginxx
REVISION        UPDATED                         STATUS          CHART           APP VERSION     DESCRIPTION     
1               Wed May 24 13:38:20 2023        superseded      nginx-0.1.0     1.16.0          Install complete
2               Wed May 24 13:44:34 2023        superseded      nginx-0.1.0     1.16.0          Upgrade complete
3               Wed May 24 13:49:19 2023        deployed        nginx-0.1.0     1.16.0          Rollback to 1   
[root@master helm]# 



# chartrepo,固定参数,bigdata自定义项目
helm repo add local-harbor --username=admin --password=Harbor12345 https://myharbor.com/chartrepo/bigdata/ --ca-file /opt/k8s/helm/ca.crt

helm repo add local-harbor --username=admin --password=Harbor12345 https://myharbor.zznode.com/helm
helm install myrelease oci://container-registry.com/container-registry/harbor --version 1.7.4

推送helm到harbor

// 检查推送插件
[root@master helm]# helm plugin list
NAME    VERSION DESCRIPTION                      
push    0.9.0   Push chart package to ChartMuseum
[root@master helm]# 
// 若无则执行
[root@master helm]# helm plugin install https://github.com/chartmuseum/helm-push

异常排查

隐藏配置文件

/root/.cache/helm/repository
/root/.config/helm/
# helm search repo nginx
WARNING: Repo "myrepo" is corrupt or missing. Try 'helm repo update'.
WARNING: open /root/.cache/helm/repository/myrepo-index.yaml: no such file or directory
NAME                                            CHART VERSION   APP VERSION     DESCRIPTION                                       
aliyun/nginx-ingress                            0.9.5           0.10.2          An nginx Ingress controller that uses ConfigMap...
aliyun/nginx-lego                               0.3.1                           Chart for nginx-ingress-controller and kube-lego  
bitnami/nginx                                   14.2.2          1.24.0          NGINX Open Source is a web server that can be a...
WARNING: Repo "myrepo" is corrupt or missing. Try 'helm repo update'.
WARNING: open /root/.cache/helm/repository/myrepo-index.yaml: no such file or directory
# ll /root/.cache/helm/repository/
总用量 9120
-rw-r--r-- 1 root root    1400 5月  23 09:31 aliyun-charts.txt
-rw-r--r-- 1 root root  296570 5月  23 09:31 aliyun-index.yaml
-rw-r--r-- 1 root root    1216 5月  23 09:31 bitnami-charts.txt
-rw-r--r-- 1 root root 5569455 5月  23 09:31 bitnami-index.yaml
-rw-r--r-- 1 root root      93 5月  23 09:31 crossplane-alpha-charts.txt
-rw-r--r-- 1 root root   21249 5月  23 09:31 crossplane-alpha-index.yaml
-rw-r--r-- 1 root root     163 5月  23 09:31 gitlab-charts.txt
-rw-r--r-- 1 root root  708593 5月  23 09:31 gitlab-index.yaml
-rw-r--r-- 1 root root       7 5月  23 09:31 harbor-charts.txt
-rw-r--r-- 1 root root   61363 5月  23 09:31 harbor-index.yaml
-rw-r--r-- 1 root root      19 5月  23 09:31 kube-state-metrics-charts.txt
-rw-r--r-- 1 root root    8603 5月  23 09:31 kube-state-metrics-index.yaml
-rw-r--r-- 1 root root     963 5月  23 09:32 prometheus-community-charts.txt
-rw-r--r-- 1 root root 2630167 5月  23 09:32 prometheus-community-index.yaml


# ll /root/.config/helm/
总用量 8
-rw------- 1 root root  105 2月   7 2021 registry.json
-rw------- 1 root root    0 8月  28 2020 repositories.lock
-rw-r--r-- 1 root root 1683 5月  22 16:21 repositories.yaml


# cat /root/.config/helm/repositories.yaml  | grep myrepo
  name: myrepo
  url: https://myharbor.zznode.com/chartrepo/myrepo
# cat /root/.config/helm/registry.json 
{
        "auths": {
                "myharbor.zznode.com/chartrepo/library": {
                        "auth": "YWRtaW46SGFyYm9yMTIzNDU="
                }
        }
}
# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
10.2.21.47 myharbor.zznode.com 


## 删除掉省略之间的行,重新执行helm search repo list则不报错
# vi /root/.config/helm/repositories.yaml
……省略……
- caFile: ""
  certFile: ""
  insecure_skip_tls_verify: false
  keyFile: ""
  name: myrepo
  pass_credentials_all: false
  password: ""
  url: https://myharbor.zznode.com/chartrepo/myrepo
  username: ""
……省略……

示例二异常排查

// 暂时无法访问
[root@master tomcat]# kubectl get pod
NAME                               READY   STATUS    RESTARTS   AGE
my-tomcat-66d759f5cc-wlfd4         0/1     Pending   0          41m
rbd-provisioner-76f6bc6669-9l5c6   1/1     Running   4          174d
[root@master tomcat]# kubectl get svc
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes             ClusterIP      10.1.0.1       <none>        443/TCP        439d
my-tomcat              LoadBalancer   10.1.155.6     <pending>     80:15773/TCP   41m
[root@master tomcat]# 
[root@master tomcat]# kubectl describe pod my-tomcat-66d759f5cc-wlfd4
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  40m   default-scheduler  0/2 nodes are available: 2 pod has unbound immediate PersistentVolumeClaims.
  Warning  FailedScheduling  40m   default-scheduler  0/2 nodes are available: 2 pod has unbound immediate PersistentVolumeClaims.

[root@master tomcat]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
pvc-cd243181-e011-4e92-88ce-1844214d8b93   10Gi       RWO            Delete           Bound    default/maven-cache   ceph-rdb                396d
pvc-eb920709-7cd1-4c57-8f93-b47a53f57562   10Gi       RWO            Delete           Bound    default/pkg           ceph-rdb                394d
[root@master tomcat]# kubectl get pvc
NAME          STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
maven-cache   Bound     pvc-cd243181-e011-4e92-88ce-1844214d8b93   10Gi       RWO            ceph-rdb       396d
my-tomcat     Pending                                                                                       43m
pkg           Bound     pvc-eb920709-7cd1-4c57-8f93-b47a53f57562   10Gi       RWO            ceph-rdb       394d
[root@master tomcat]# 
[root@master tomcat]# kubectl describe pvc my-tomcat
Events:
  Type    Reason         Age                    From                         Message
  ----    ------         ----                   ----                         -------
  Normal  FailedBinding  3m31s (x162 over 43m)  persistentvolume-controller  no persistent volumes available for this claim and no storage class is set
[root@master tomcat]# 


// 变量剖析
[root@master templates]# cat ingress.yaml 
{{- if .Values.ingress.enabled }}
apiVersion: {{ include "common.capabilities.ingress.apiVersion" . }}
kind: Ingress
metadata:
  name: {{ template "common.names.fullname" . }}
  namespace: {{ .Release.Namespace }}

[root@master templates]# helm ls
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
my-tomcat       default         1               2023-05-23 10:39:34.100611768 +0800 CST deployed        tomcat-10.9.1   10.1.9     
[root@master templates]# 

如上的release是来自于helm ls中对应的变量值

渲染过程展示

[root@master templates]# helm install my-tomcat bitnami/tomcat --dry-run --debug
install.go:192: [debug] Original chart version: ""
install.go:209: [debug] CHART PATH: /root/.cache/helm/repository/tomcat-10.9.1.tgz

NAME: my-tomcat
LAST DEPLOYED: Tue May 23 13:59:17 2023
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
affinity: {}
args: []
catalinaOpts: ""
clusterDomain: cluster.local
command: []
common:
  exampleValue: common-chart
  global:
    imagePullSecrets: []
    imageRegistry: ""
    storageClass: ""
commonAnnotations: {}
commonLabels: {}
containerExtraPorts: []
containerPorts:
  http: 8080
containerSecurityContext:
  enabled: true
  runAsNonRoot: true
  runAsUser: 1001
customLivenessProbe: {}
customReadinessProbe: {}
customStartupProbe: {}
deployment:
  type: deployment
extraDeploy: []
extraEnvVars: []
extraEnvVarsCM: ""
extraEnvVarsSecret: ""
extraPodSpec: {}
extraVolumeClaimTemplates: []
extraVolumeMounts: []
extraVolumes: []
fullnameOverride: ""
global:
  imagePullSecrets: []
  imageRegistry: ""
  storageClass: ""
hostAliases: []
image:
  debug: false
  digest: ""
  pullPolicy: IfNotPresent
  pullSecrets: []
  registry: docker.io
  repository: bitnami/tomcat
  tag: 10.1.9-debian-11-r0
ingress:
  annotations: {}
  apiVersion: ""
  enabled: false
  extraHosts: []
  extraPaths: []
  extraRules: []
  extraTls: []
  hostname: tomcat.local
  ingressClassName: ""
  path: /
  pathType: ImplementationSpecific
  secrets: []
  selfSigned: false
  tls: false
initContainers: []
kubeVersion: ""
lifecycleHooks: {}
livenessProbe:
  enabled: true
  failureThreshold: 6
  initialDelaySeconds: 120
  periodSeconds: 10
  successThreshold: 1
  timeoutSeconds: 5
metrics:
  jmx:
    catalinaOpts: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=5555
      -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
      -Dcom.sun.management.jmxremote.local.only=true
    config: |
      jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5555/jmxrmi
      startDelaySecs: 120
      ssl: false
      lowercaseOutputName: true
      lowercaseOutputLabelNames: true
      attrNameSnakeCase: true
    containerSecurityContext:
      enabled: true
      runAsNonRoot: true
      runAsUser: 1001
    enabled: false
    existingConfigmap: ""
    image:
      digest: ""
      pullPolicy: IfNotPresent
      pullSecrets: []
      registry: docker.io
      repository: bitnami/jmx-exporter
      tag: 0.18.0-debian-11-r22
    ports:
      metrics: 5556
    resources:
      limits: {}
      requests: {}
  podMonitor:
    additionalLabels: {}
    enabled: false
    interval: 30s
    namespace: ""
    podTargetLabels: []
    relabelings: []
    scheme: http
    scrapeTimeout: 30s
    tlsConfig: {}
  prometheusRule:
    additionalLabels: {}
    enabled: false
    namespace: ""
    rules: []
nameOverride: ""
networkPolicy:
  allowExternal: true
  enabled: false
  explicitNamespacesSelector: {}
nodeAffinityPreset:
  key: ""
  type: ""
  values: []
nodeSelector: {}
persistence:
  accessModes:
  - ReadWriteOnce
  annotations: {}
  enabled: true
  existingClaim: ""
  selectorLabels: {}
  size: 8Gi
  storageClass: ""
podAffinityPreset: ""
podAnnotations: {}
podAntiAffinityPreset: soft
podLabels: {}
podManagementPolicy: ""
podSecurityContext:
  enabled: true
  fsGroup: 1001
readinessProbe:
  enabled: true
  failureThreshold: 3
  initialDelaySeconds: 30
  periodSeconds: 5
  successThreshold: 1
  timeoutSeconds: 3
replicaCount: 1
resources:
  limits: {}
  requests:
    cpu: 300m
    memory: 512Mi
schedulerName: ""
service:
  annotations: {}
  clusterIP: ""
  externalTrafficPolicy: Cluster
  extraPorts: []
  headless:
    annotations: {}
  loadBalancerIP: ""
  loadBalancerSourceRanges: []
  nodePorts:
    http: ""
  ports:
    http: 80
  sessionAffinity: None
  sessionAffinityConfig: {}
  type: LoadBalancer
sidecars: []
startupProbe:
  enabled: false
  failureThreshold: 3
  initialDelaySeconds: 30
  periodSeconds: 5
  successThreshold: 1
  timeoutSeconds: 3
tolerations: []
tomcatAllowRemoteManagement: 0
tomcatPassword: ""
tomcatUsername: user
topologySpreadConstraints: []
updateStrategy:
  type: RollingUpdate
volumePermissions:
  enabled: false
  image:
    digest: ""
    pullPolicy: IfNotPresent
    pullSecrets: []
    registry: docker.io
    repository: bitnami/bitnami-shell
    tag: 11-debian-11-r118
  resources:
    limits: {}
    requests: {}

HOOKS:
MANIFEST:
---
# Source: tomcat/templates/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: my-tomcat
  namespace: default
  labels:
    app.kubernetes.io/name: tomcat
    helm.sh/chart: tomcat-10.9.1
    app.kubernetes.io/instance: my-tomcat
    app.kubernetes.io/managed-by: Helm
type: Opaque
data:
  tomcat-password: "Z0xvSDNnUDZ6cQ=="
---
# Source: tomcat/templates/pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: my-tomcat
  namespace: default
  labels:
    app.kubernetes.io/name: tomcat
    helm.sh/chart: tomcat-10.9.1
    app.kubernetes.io/instance: my-tomcat
    app.kubernetes.io/managed-by: Helm
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: "8Gi"
---
# Source: tomcat/templates/svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-tomcat
  namespace: default
  labels:
    app.kubernetes.io/name: tomcat
    helm.sh/chart: tomcat-10.9.1
    app.kubernetes.io/instance: my-tomcat
    app.kubernetes.io/managed-by: Helm
spec:
  type: LoadBalancer
  externalTrafficPolicy: "Cluster"
  sessionAffinity: None
  ports:
    - name: http
      port: 80
      targetPort: http
  selector: 
    app.kubernetes.io/name: tomcat
    app.kubernetes.io/instance: my-tomcat
---
# Source: tomcat/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-tomcat
  namespace: default
  labels:
    app.kubernetes.io/name: tomcat
    helm.sh/chart: tomcat-10.9.1
    app.kubernetes.io/instance: my-tomcat
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: tomcat
      app.kubernetes.io/instance: my-tomcat
  strategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app.kubernetes.io/name: tomcat
        helm.sh/chart: tomcat-10.9.1
        app.kubernetes.io/instance: my-tomcat
        app.kubernetes.io/managed-by: Helm
    spec:

      affinity:
        podAffinity:

        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app.kubernetes.io/name: tomcat
                    app.kubernetes.io/instance: my-tomcat
                topologyKey: kubernetes.io/hostname
              weight: 1
        nodeAffinity:

      securityContext:
        fsGroup: 1001
      initContainers:
      containers:
        - name: tomcat
          image: docker.io/bitnami/tomcat:10.1.9-debian-11-r0
          imagePullPolicy: "IfNotPresent"
          securityContext:
            runAsNonRoot: true
            runAsUser: 1001
          env:
            - name: BITNAMI_DEBUG
              value: "false"
            - name: TOMCAT_USERNAME
              value: "user"
            - name: TOMCAT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: my-tomcat
                  key: tomcat-password
            - name: TOMCAT_ALLOW_REMOTE_MANAGEMENT
              value: "0"
          ports:
            - name: http
              containerPort: 8080
          livenessProbe:
            httpGet:
              path: /
              port: http
            failureThreshold: 6
            initialDelaySeconds: 120
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 5
          readinessProbe:
            httpGet:
              path: /
              port: http
            failureThreshold: 3
            initialDelaySeconds: 30
            periodSeconds: 5
            successThreshold: 1
            timeoutSeconds: 3
          resources:
            limits: {}
            requests:
              cpu: 300m
              memory: 512Mi
          volumeMounts:
            - name: data
              mountPath: /bitnami/tomcat
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: my-tomcat

NOTES:
CHART NAME: tomcat
CHART VERSION: 10.9.1
APP VERSION: 10.1.9

** Please be patient while the chart is being deployed **

1. Get the Tomcat URL by running:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w my-tomcat'

  export SERVICE_IP=$(kubectl get svc --namespace default my-tomcat --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
  echo "Tomcat URL:            http://$SERVICE_IP/"
  echo "Tomcat Management URL: http://$SERVICE_IP/manager"

2. Login with the following credentials

  echo Username: user
  echo Password: $(kubectl get secret --namespace default my-tomcat -o jsonpath="{.data.tomcat-password}" | base64 -d)
[root@master templates]# 
[root@master ~]# kubectl get pv pvc-cd243181-e011-4e92-88ce-1844214d8b93 -o yaml
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: maven-cache
    namespace: default
    resourceVersion: "6600512"
    uid: cd243181-e011-4e92-88ce-1844214d8b93
  persistentVolumeReclaimPolicy: Delete
  rbd:
    image: kubernetes-dynamic-pvc-2b048fd1-c1d8-11ec-922d-3e8f89442688
    keyring: /etc/ceph/keyring
    monitors:
    - 10.2.6.63:6789
    - 10.2.6.64:6789
    - 10.2.6.65:6789
    pool: k8s
    secretRef:
      name: ceph-user-secret
    user: kube
  storageClassName: ceph-rdb
  volumeMode: Filesystem
status:
  phase: Bound
# cat testpv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-tomcat
spec:
  capacity:
    storage: 8Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: ceph-rdb
  rbd:
      monitors:
      - 10.2.6.63:6789
      - 10.2.6.64:6789
      - 10.2.6.65:6789
      pool: k8s
      image: rbda
      user: kube
      secretRef:
        name: ceph-user-secret
[root@master ~]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                 STORAGECLASS   REASON   AGE
my-tomcat                                  8Gi        RWO            Delete           Available                         ceph-rdb                8s

再次执行创建:
报同样的错误,修改storageClass: "ceph-rdb"
[root@master helm]# cat tomcat/values.yaml | grep storageClass
## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass
## @param global.storageClass Global StorageClass for Persistent Volume(s)
  storageClass: ""
  ## @param persistence.storageClass PVC Storage Class for Tomcat volume
  ## If defined, storageClassName: <storageClass>
  ## If set to "-", storageClassName: "", which disables dynamic provisioning
  ## If undefined (the default) or set to null, no storageClassName spec is
  storageClass: "ceph-rdb

[root@master helm]# kubectl get pod
NAME                               READY   STATUS              RESTARTS   AGE
my-tomcat-66d759f5cc-rrj5w         0/1     ContainerCreating   0          4m2s
rbd-provisioner-76f6bc6669-9l5c6   1/1     Running             4          175d
[root@master helm]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
my-tomcat                                  8Gi        RWO            Delete           Bound    default/my-tomcat     ceph-rdb                40m
pvc-cd243181-e011-4e92-88ce-1844214d8b93   10Gi       RWO            Delete           Bound    default/maven-cache   ceph-rdb                396d
pvc-eb920709-7cd1-4c57-8f93-b47a53f57562   10Gi       RWO            Delete           Bound    default/pkg           ceph-rdb                395d
[root@master helm]# kubectl get pvc
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
maven-cache   Bound    pvc-cd243181-e011-4e92-88ce-1844214d8b93   10Gi       RWO            ceph-rdb       396d
my-tomcat     Bound    my-tomcat                                  8Gi        RWO            ceph-rdb       4m8s
pkg           Bound    pvc-eb920709-7cd1-4c57-8f93-b47a53f57562   10Gi       RWO            ceph-rdb       395d
[root@master helm]# 

[root@master helm]# kubectl describe pod my-tomcat-66d759f5cc-rrj5w 
Events:
  Type     Reason                  Age                  From                     Message
  ----     ------                  ----                 ----                     -------
  Normal   Scheduled               4m58s                default-scheduler        Successfully assigned default/my-tomcat-66d759f5cc-rrj5w to node1
  Normal   SuccessfulAttachVolume  4m59s                attachdetach-controller  AttachVolume.Attach succeeded for volume "my-tomcat"
  Warning  FailedMount             40s (x2 over 2m56s)  kubelet                  Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[default-token-b8dgk data]: timed out waiting for the condition


[root@master helm]# kubectl describe pod my-tomcat-66d759f5cc-w5gl9
Events:
  Type     Reason                  Age                  From                     Message
  ----     ------                  ----                 ----                     -------
  Normal   Scheduled               29m                  default-scheduler        Successfully assigned default/my-tomcat-66d759f5cc-w5gl9 to node1
  Warning  FailedAttachVolume      29m                  attachdetach-controller  Multi-Attach error for volume "my-tomcat" Volume is already exclusively attached to one node and can't be attached to another
  Normal   SuccessfulAttachVolume  29m                  attachdetach-controller  AttachVolume.Attach succeeded for volume "my-tomcat"
  Warning  FailedMount             2m40s (x2 over 20m)  kubelet                  Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[default-token-b8dgk data]: timed out waiting for the condition
  Warning  FailedMount             22s (x11 over 27m)   kubelet                  Unable to attach or mount volumes: unmounted volumes=[data], unattached volumes=[data default-token-b8dgk]: timed out waiting for the condition

// 目前还是失败,到此结束,不再继续剖析

解决因为PVC创建失败的问题

  Warning  FailedScheduling  40m   default-scheduler  0/2 nodes are available: 2 pod has unbound immediate PersistentVolumeClaims.

[root@master helm]# cat jenkins/values.yaml | grep -A 2 Persistence
Persistence:
  Enabled: false
  ## A manually managed Persistent Volume and Claim
  ## Requires Persistence.Enabled: true
  ## If defined, PVC must be created manually before volume will be bound
  # ExistingClaim:
[root@master helm]# 


将  Enabled: 默认true改为false

参考文档

helm3自定义chart编写:
https://www.cnblogs.com/fengzi7314/p/14872326.html


官网参考链接:(需要深入学习)
https://helm.sh/zh/docs/

历史事件

2023-10-23|新增高版本安装,调整 目录层级

2023-05-22|补充安装小节

2023-05-19|创建文档