文章目录

  • 应用示例
  • 基础模板
  • 命名模板
  • 版本兼容
  • 持久化
  • 定制

我的文和网上现有的文可能只差百分之一,但是这百分之一,就够了。

应用示例

如果我们想要在 Kubernetes 集群中部署两个副本的 Ghost,可以直接应用下面的资源清单文件即可:

# ghost/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: ghost
spec:selector:matchLabels:app: ghost-appreplicas: 2template:metadata:labels:app: ghost-appspec:containers:- name: ghost-appimage: ghostports:- containerPort: 2368
---
# ghost/service.yaml
apiVersion: v1
kind: Service
metadata:name: ghost
spec:type: NodePortselector:app: ghost-appports:- protocol: TCPport: 80targetPort: 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 Chart 包。直接使用 helm create 命令即可:

$ helm create my-ghostCreating 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.yaml3 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-appreplicas: {{ .Values.replicaCount }}template:metadata:labels:app: ghost-appspec:containers:- name: ghost-appimage: {{ .Values.image }}ports:- containerPort: 2368env:- name: NODE_ENVvalue: {{ .Values.node_env | default "production" }}{{- if .Values.url }}- name: urlvalue: 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-apptype: {{ .Values.service.type }}ports:- protocol: TCPtargetPort: 2368port: {{ .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.localservice:type: NodePortport: 80

接着,确保我们在前文我让你们创建的目录下,做语法检查,如下为正常,不正常再改。

$ helm lint mychart/
==> Linting .
[INFO] Chart.yaml: icon is recommended1 chart(s) linted, no failures

然后我们可以使用 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-apptype: NodePortports:- protocol: TCPtargetPort: 2368port: 80
---
# Source: my-ghost/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: ghost
spec:selector:matchLabels:app: ghost-appreplicas: 1template:metadata:labels:app: ghost-appspec:containers:- name: ghost-appimage: ghostports:- containerPort: 2368env:- name: NODE_ENVvalue: production- name: urlvalue: http://ghost.k8s.local

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


命名模板

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

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

然后我们可以将 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-appimage: {{ .Values.image }}ports:- containerPort: 2368env:- name: NODE_ENVvalue: {{ .Values.node_env | default "production" }}{{- if .Values.url }}- name: urlvalue: 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: TCPtargetPort: 2368port: {{ .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
# 具体结果就不展示了,太多字数了。

版本兼容

于 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-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /
spec:ingressClassName: nginxrules:- http:paths:- path: /testpathpathType: Prefixbackend:service:name: testport:number: 80

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

apiVersion: extensions/v1beta1
kind: Ingress
metadata:name: minimal-ingressannotations:kubernetes.io/ingress.class: nginxnginx.ingress.kubernetes.io/rewrite-target: /
spec:rules:- http:paths:- path: /testpathbackend:serviceName: testservicePort: 80

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

Ingress 模板:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ghost
spec:ingressClassName: nginxrules:- host: ghost.k8s.localhttp:paths:- path: /pathType: Prefixbackend:service:name: ghostport:number: 80

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

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: {{ template "my-ghost.fullname" . }}labels:
{{ include "my-ghost.labels" . | indent 4 }}
spec:ingressClassName: nginxrules:- host: {{ .Values.url }}http:paths:- path: /pathType: Prefixbackend: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 对象模板中就可以使用上面定义的命名模板来决定应该使用哪些属性。

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

ingress:enabled: trueingressClass: nginx

(讲真的,这一part我没看太懂,语法啥的只能勉强接收一下,等我下午去把语法啥的学一下再说了。)

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

$ helm template --debug my-ghost
# 自行测验

从上面的资源清单可以看出是符合我们的预期要求的,在我们安装测试前,一定要确认我们的kubernetes集群是否安装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: Wed May 18 19:02:14 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-05-18 19:02:14.606629268 -0700 PDT 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

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

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

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

持久化

上面我们使用的 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-appimage: {{ .Values.image }}volumeMounts:- name: ghost-datamountPath: /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-claimaccessMode: ReadWriteOnce  # 访问模式size: 1Gi  # 存储容量

定制

除了上面的这些主要的需求之外,还有一些额外的定制需求,比如用户想要配置更新策略,因为更新策略并不是一层不变的,这里和之前不太一样,我们需要用到一个新的函数 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: ghostimage: {{ printf "%s:%s" .Values.image.name .Values.image.tag }}imagePullPolicy: {{ .Values.image.pullPolicy | quote }}ports:- containerPort: 2368

对应的 Values 值如下所示:

image:name: ghosttag: latestpullPolicy: 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: 2368initialDelaySeconds: {{ .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: 2368initialDelaySeconds: {{ .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: 2368initialDelaySeconds: {{ .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: ghosttag: latestpullPolicy: IfNotPresentnode_env: production
url: ghost.k8s.localservice:type: ClusterIPport: 80ingress:enabled: trueingressClass: nginx## 是否使用 PVC 开启数据持久化
persistence:enabled: true## 是否使用 storageClass,如果不适用则补配置# storageClass: "xxx"#### 如果想使用一个存在的 PVC 对象,则直接传递给下面的 existingClaim 变量# existingClaim: your-claimaccessMode: ReadWriteOnce  # 访问模式size: 1Gi  # 存储容量nodeSelector: {}affinity: {}tolerations: {}resources: {}startupProbe:enabled: falselivenessProbe:enabled: falsereadinessProbe:enabled: false

现在我们再去更新 Release:

$ helm upgrade --install my-ghost ./my-ghost -n default➜ 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     4m59sk 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

到这里我们就基本完成了这个简单的 Helm Charts 包的开发,当然以后可能还会有新的需求,我们需要不断去迭代优化。

当我们设置storageclass: ""或者注释storageclass时,minikube会自动一个hostpath本地pv
my-ghost/value.yaml配置

当设置持久enabled: false,它为非持久化部署。

persistence:enabled: false

helm 构建 chart相关推荐

  1. k8s使用helm打包chart并上传到腾讯云TencentHub

    本文只涉及Helm的Chart操作,不会对其他知识进行过多描述.至于安装这块,麻烦自行百度吧,一大堆呢. 在容器化的时代,我们很多应用都可以部署在docker,很方便,而再进一步,我们还有工具可以对d ...

  2. k8s核心技术-Helm(自定义chart部署)---K8S_Google工作笔记0047

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 然后上一节我们用helm来快速使用现成的chart部署了一个一个应用 现在我们使用自己做的char ...

  3. helm部署jenkins到k8s并创建pipeline构建项目

    安装部署nfs 1. nfs服务器创建目录 [yeqiang@harbor ~]$ sudo mkdir /home/nfs/jenkins -p 2. 设置其他用户可以写入该目录 [yeqiang@ ...

  4. Helm 3 完整教程(二十二):如何设置 Chart 安装完成后提示 NOTE 信息

    推荐阅读 Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506 Helm3(K8S 资源对象管理工具)博客专栏:https: ...

  5. helm chart

    helm  功能近似于 yum 1.  本地 helm 初始化: helm init helm init --upgrade --tiller-image=hub.easystack.io/capta ...

  6. Helm chart仓库操作

    配置国内存放 chart 仓库的地址 chart图标是存放资源清单的东西,它是存在于远程仓库当中的 阿里云仓库(https://kubernetes.oss-cn-hangzhou.aliyuncs. ...

  7. Helm系列-helm chart实战(一)

    目录 1. 什么是Chart? 2. Chart 基础使用 2.1 查找 Chart 2.1.1  搜索所有公开可用的 charts 2.1.2 搜索本地 stable 源中可用的 charts 2. ...

  8. Helm Chart 使用简析

    文章目录 一.Chart 文件结构 二.Chart 模版赋值 2.1 通过values.yaml文件取值 2.2 手动使用 --set 指定 2.3 通过 -f 指定文件 三.Chart 模版语法 3 ...

  9. Jenkins X基本概念:Jenkins K8S helm Draft gitops

    概念 Jenkins X是基于Kubernetes的持续集成.持续部署平台.也是Jenkins的子项目.Jenkins X旨在使程序员在研发过程中能够轻松遵循DevOps原理和最佳实践. 介绍 Jen ...

最新文章

  1. Intersection of Two Linked Lists——经典问题
  2. [Vue.js]实战 -- 电商项目(二)
  3. 基于物理着色(二)- Microfacet材质和多层材质
  4. layui select监听选中的值 二级联动
  5. HDU-3374 String Problem (最小表示法)
  6. 文件名lin.php是什么,宝塔面板-PHP服务添加‘扩展名=ixed.5.4.lin
  7. 游戏开发之继承中的构造函数、析构函数及继承中的常见问题解决方案(C++基础)
  8. 利用RazorSQL如何创建SSH密钥
  9. delphi 调试控件代码_机器人调试(六十七)
  10. ROS 位置姿态Odometry仿真模拟(gmapping)
  11. 什么是数据可视化大屏?有哪些优点
  12. 医学图像处理与深度学习入门
  13. 用迭代算法实现扭蛋机例子
  14. 【Jupyter常用快捷键】
  15. vue3在控制台打印相关变量的值
  16. linux find返回数据不一样,Linux学习笔记——find命令
  17. 二分类资料校准曲线calibration curve的绘制
  18. 看雪ctf 流浪者 WP
  19. 基于javaSpringboot的装修验收管理系统设计和实现
  20. python2B 之 DataFrame 选取多列并进行赋值

热门文章

  1. 新手预算2000元左右买什么吉他好?高性价比单板民谣吉他推荐
  2. 全国青少年编程等级考试scratch三级真题2021年9月(含题库答题软件账号)
  3. 企业微信工作台集成CAS实现单点登录
  4. 联想电脑为什么没有计算机,联想笔记本电脑没有声音怎么办
  5. elementUI中el-table每行异常高度原因排查,累死
  6. poj 1655 Balancing Act 树状dp
  7. C# OPC类库 升级版本 OPCAutomation.dll
  8. Linux+宝塔安装swoole教程
  9. Oracle 中的 TO_DATE 和 TO_CHAR 函数 日期处理
  10. 苹果手机还原网络设置会怎样_苹果手机老是信号不好,只要掌握这4个小技巧,信号便能立马增强...