云原生 • Kubernetes一文掌握 k8s 包管理工具 Helm

Posted 敬 之

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了云原生 • Kubernetes一文掌握 k8s 包管理工具 Helm相关的知识,希望对你有一定的参考价值。



本文导读


一、为什么要引入 Helm

1. Helm 的应用场景

在以往的应用部署过程当中,我们需要先编写一个 yaml 文件,然后该文件中包含 deployment、Service、Ingress 等等。

如果说需要部署的是单一、少数服务的应用,那么完全可以使用 yaml 文件的方式,这样会很简单。但是在实际的项目当中,微服务的数量基本不可能是一个,可能是几十个,如果说再用 yaml 文件的部署方式,那就意味着需要编写几十个 yaml 文件,这就会导致 数量多维护难 等诸多问题。

2. 使用 Helm 可以解决哪些问题

针对上述问题,Helm 的引入使用则可以将所有的 yaml 文件进行一个整体的管理,而且它能够实现 yaml 文件的高效复用。

高效复用:yaml 文件的格式和结果基本相同,一般只是属性值有所变化。使用 helm 后,针对格式和结构基本相同的 yaml 文件就不需要一遍一遍的进行重复编写了,直接复用即可。

除此之外,Helm 还可以进行应用级别的版本管理,包括版本更新、回退等等。

二、Helm 概述

Helm 是 Kubernetes 的一个 包管理工具,类似于 Linux 下的包管理工具如 yum、apt 等。可以方便的将之前打包好的 yaml 文件部署到 Kunernetes 上。

在 Helm 中有三个主要概念:

概念含义
helm一个命令行工具,主要用于 k8s 应用 Chart 的创建、打包、发布和管理。
Chart应用描述,它是一系列用于描述 k8s 资源相关文件的集合(可以理解为 yaml 的集合)。
Release基于 Chart 的部署实体,一个 Chart 被 Helm 运行后将会生成一个对应的 release,然后将在 k8s 中创建出真正运行的资源对象,它是一个应用级别的版本管理。

在 2019 年 11 月 13日,Helm 团队发布了稳定版本 Helm v3,这也是当前主流版本。该版本与往期版本相比较有主要以下变化:

  • 删除了 Tiller;
  • 支持 release 在不同命名空间中重用;
  • 支持直接将 Chart 推送到 docker 仓库中。

如下为各版本 Helm 架构简易示意图。

v3 之前版本架构

v3 版本架构

三、Helm 安装与配置(v3)

1. 安装 Helm v3

第一步:前往 Helm 官网下载压缩文件;



也可以直接使用以下网址进行下载,我这里使用的是 3.0.0 版本:

操作系统Helm二进制文件下载地址(v3.0.0)
macOShttps://get.helm.sh/helm-v3.0.0-darwin-amd64.tar.gz
linux/amd64https://get.helm.sh/helm-v3.0.0-linux-amd64.tar.gz

第二步:将压缩文件上传至我们的 Linux 系统;

第三步:解压 helm 压缩文件;

#解压文件
tar -zxvf helm-v3.0.0-linux-amd64.tar.gz

解压后会有一个 linux-arm64 目录,这其中就包含了我们需要的 helm 文件;

第四步:将解压之后的 helm 目录复制或者移动到 /usr/local/bin 目录下;

#移动文件
mv helm /usr/local/bin

完成此四步的操作之后就可以直接在 Linux 系统中使用 helm 命令进行相关操作了,如果使用 helm 命令不报错,则说明 helm 安装成功。

2. 配置 Helm 仓库

添加仓库语法如下;

#语法
helm repo add 仓库名称 仓库地址

#eg:
#添加微软仓库
helm repo add stable http://mirror.azure.cn/kubernetes/charts
#添加阿里云仓库
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

仓库成功添加之后可以使用 helm repo list 命令查看已有仓库;

四、使用 Helm 快速部署应用

在这里我们以部署可视化工具 weave 为例。

第一步:使用命令搜索应用 weave;

helm search repo weave

第二步:根据搜索到的内容选择安装;

#安装	
helm install ui stable/weave-scope

这里选择的是微软源的 weave-scope 进行安装,如果在安装过程中报错那就换一个源;

安装成功就可以使用命令 helm list 查看了;


接下来使用 kubectl get podskubectl get svc 就可以看到我们安装的 weave 相关内容和对外暴露端口;


第三步:可以看到此时并未对外暴露端口,所以我们需要修改 service 中的 yaml 文件,将 type 值改为 NodePort,使用命令 kubectl edit svc ui-weave-scope


修改过后再次查看 svc,此时已有端口暴露;

五、自定义 Chart 部署应用

我们在这里以部署自定义应用 myweb1 为例。

第一步:创建一个chart;

helm create mychart

创建成功会自动生成一个 mychart 目录(其实也就是一个模板);


在生成的目录中有以下几部分:

文件含义
charts一个普通的空文件,一般也不会写入内容
Chart.yaml当前 chart 属性的配置信息
templates自己定义的 yaml 文件存于此
values.yaml定义 yaml 文件的全局配置

第二步:进入 templates 目录,创建 deployment. yaml 文件;

[root@master mychart]# cd templates/
[root@master templates]# kubectl create deployment myweb1 --image=nginx --dry-run -o yaml > deployment.yaml
W0906 10:18:15.827722  113157 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client.
[root@master templates]# ls
deployment.yaml  ingress.yaml  serviceaccount.yaml  tests
_helpers.tpl     NOTES.txt     service.yaml

查看 deployment.yaml 文件;

第四步:设置对外暴露端口,创建 service.yaml 文件;

[root@master templates]# kubectl expose deployment myweb1 --port=80 --target-port=80  --type=NodePort --dry-run -o yaml > service.yaml
W0906 10:37:13.004600  126020 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client.
[root@master templates]# ls
deployment.yaml  ingress.yaml  serviceaccount.yaml  tests
_helpers.tpl     NOTES.txt     service.yaml

如果这里无法导出,我们就先创建一次镜像:kubectl create deployment myweb1 --image=nginx

导出 service 后再删除此镜像:kubectl delete deployment myweb1

查看 service.yaml 文件;


此时在 templates 目录中已有创建的两个 yaml 文件;


第五步:回到 mychart 父级目录,开始安装;

[root@master linux-amd64]# helm install myweb1 mychart/

安装成功后查看应用内容,应用节点与对外端口均创建成功;

myweb1 应用部署完成,此时就完成了 chart 的自定义及部署应用操作。

第六步:应用升级,每次修改 yaml 文件内容之后,我们均需对应用进行升级操作,使用如下命令。

#格式
helm upgrade 自定义应用名称 目录
#eg:
helm upgrade myweb1 mychart/

六、Helm 实现 yaml 文件高效复用

高效复用:如果若干 yaml 文件的格式和结果基本相同,只是属性值有所变化时。在使用 Helm 后,针对格式和结构基本相同的 yaml 文件就不需要一遍一遍的进行重复编写了,直接复用即可。其主要实现原理就是通过动态传递参数、动态渲染模板、动态传入参数生成 yaml 文件内容。

创建 chart 之后,目录下有一个 values.yaml 文件,基于此进行操作;


第一步:在 values.yaml 文件中定义全局变量和值;

第二步:在具体的 yaml 文件中获取定义的变量值。

原理就是以表达式的形式获取全局变量,格式为: .Values.变量名称

此处以修改 deployment.yaml 文件为例:


修改后如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: myweb1
  name:  .Release.Name-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app:  .Values.label
  strategy: 
  template:
    metadata:
      creationTimestamp: null
      labels:
        app:  .Values.label
    spec:
      containers:
      - image:  .Values.image
        name: nginx
        resources: 
status: 

七、Helm 的常用操作命令汇总

#查看仓库
helm repo list

#更新仓库
helm repo update

#删除仓库
helm repo remove 仓库名称

#搜索应用
helm search repo 名称

#安装应用
helm install 自定义应用名称 搜索出的结果名

#查看安装后的应用
helm list
helm status 应用名称	

#创建chart
helm create chart名称

云原生一文细数kubernetes常见20道问题

  • 1、K8S是什么?

  • 2、容器和主机部署应用的区别是什么?

  • 3、K8S架构的组成是什么?

  • 4、kubenetes针对pod资源对象的健康监测机制

  • 5、如何控制滚动更新过程?

  • 6、镜像下载策略是什么?

  • 7、image的状态有哪些?

  • 8、pod的重启策略是什么?

  • 9、K8S中部署应用版本回滚的命令

  • 10、标签和标签选择器的作用是什么?

  • 11、常用的标签分类有哪些?

  • 12、查看标签的方式?

  • 13、添加、修改觉删除标签的命令

  • 14、DaemonSet资源对象的特性

  • 15、Pod的生命周期有哪些状态?

  • 16、创建一个Pod的流程是如何的?

  • 17、删除一个Pod的流程是如何的?

  • 18、K8S的service是什么?

  • 19、K8S如何进服务注册?

  • 20、K8S数据持久化的方式有哪些?

1、K8S是什么?

Kubenetes是一个针对容器应用,进行自动部署,弹性伸缩和管理的开源系统。主要功能是生产环境中的容器编排。

关于K8S网上有很多介绍,大家可以根据自己的理解讲出来。

2、容器和主机部署应用的区别是什么?

容器的中心思想就是秒级启动;一次封装、到处运行;

这是主机部署应用无法达到的效果,但同时也更应该注重容器的数据持久化问题。另外,容器部署可以将各个服务进行隔离,互不影响,这也是容器的另一个核心概念。

3、K8S架构的组成是什么?


主节点主要用于暴露API,调度部署和节点的管理;

计算节点运行一个容器运行环境,一般是docker环境(类似docker环境的还有rkt),同时运行一个K8s的代理(kubelet)用于和master通信。

计算节点也会运行一些额外的组件,像记录日志,节点监控,服务发现等等。计算节点是k8s集群中真正工作的节点

Master节点:

  • Kubectl:客户端命令行工具,作为整个K8s集群的操作入口;

  • Api Server:在K8s架构中承担的是“桥梁”的角色,作为资源操作的唯一入口,它提供了认证、授权、访问控制、API注册和发现等机制。

    客户端与k8s群集及K8s内部组件的通信,都要通过Api Server这个组件;

  • Controller-manager:负责维护群集的状态,比如故障检测、自动扩展、滚动更新等;

  • Scheduler:负责资源的调度,按照预定的调度策略将pod调度到相应的node节点上;

  • Etcd:担任数据中心的角色,保存了整个群集的状态;

Node节点:

  • Kubelet:负责维护容器的生命周期,同时也负责Volume和网络的管理,一般运行在所有的节点,是Node节点的代理,当Scheduler确定某个node上运行pod之后,会将pod的具体信息(image,volume)等发送给该节点的kubelet,kubelet根据这些信息创建和运行容器,并向master返回运行状态。(自动修复功能:如果某个节点中的容器宕机,它会尝试重启该容器,若重启无效,则会将该pod杀死,然后重新创建一个容器);

  • Kube-proxy:Service在逻辑上代表了后端的多个pod。负责为Service提供cluster内部的服务发现和负载均衡(外界通过Service访问pod提供的服务时,Service接收到的请求后就是通过kube-proxy来转发到pod上的);

  • container-runtime:是负责管理运行容器的软件,比如docker

  • Pod:是k8s集群里面最小的单位。每个pod里边可以运行一个或多个container(容器),如果一个pod中有两个container,那么container的USR(用户)、MNT(挂载点)、PID(进程号)是相互隔离的,UTS(主机名和域名)、IPC(消息队列)、NET(网络栈)是相互共享的。

4、kubenetes针对pod资源对象的健康监测机制

K8s中对于pod资源对象的健康状态检测,提供了三类probe(探针)来执行对pod的健康监测:

1)livenessProbe探针

可以根据用户自定义规则来判定pod是否健康,如果livenessProbe探针探测到容器不健康,则kubelet会根据其重启策略来决定是否重启,初始探测状态为健康状态直到探测失败。如果一个容器不包含livenessProbe探针,则kubelet会认为容器的livenessProbe探针的返回值永远成功。

2)ReadinessProbe探针

同样是可以根据用户自定义规则来判断pod是否健康,如果探测失败,控制器会将此pod从对应service的endpoint列表中移除,从此不再将任何请求调度到此Pod上,直到下次探测成功。初始探测为失败状态,直到探测成功后,将pod加入到service的endpoint列表中。

3)startupProbe探针

启动检查机制,应用一些启动缓慢的业务,避免业务长时间启动而被上面两类探针kill掉,这个问题也可以换另一种方式解决,就是定义上面两类探针机制时,初始化时间定义的长一些即可。

探针检查支持以下参数设置:

  • initialDelaySeconds:初始第一次探测间隔,用于应用启动的时间,防止应用还没启动而健康检查失败

  • periodSeconds:检查间隔,多久执行probe检查,默认为10s;

  • timeoutSeconds:检查超时时长,探测应用timeout后为失败;

  • successThreshold:成功探测阈值,表示探测多少次为健康正常,默认探测1次。

探针支持分探测方案:

1)通过执行命令的方式来检查服务是否正常,比如使用cat命令查看pod中的某个重要配置文件是否存在,若存在,则表示pod健康。反之异常。

Exec探测方式的yaml文件语法如下:

spec:  
  containers:  
  - name: liveness  
    image: k8s.gcr.io/busybox  
    args:  
    - /bin/sh  
    - -c  
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600  
    livenessProbe:         #选择livenessProbe的探测机制  
      exec:                      #执行以下命令  
        command:  
        - cat  
        - /tmp/healthy  
      initialDelaySeconds: 5          #在容器运行五秒后开始探测  
      periodSeconds: 5               #每次探测的时间间隔为5秒

在上面的配置文件中,探测机制为在容器运行5秒后,每隔五秒探测一次,如果cat命令返回的值为“0”,则表示健康,如果为非0,则表示异常。

2)Httpget:通过发送http/htps请求检查服务是否正常,返回的状态码为200-399则表示容器健康(注http get类似于命令curl -I)。

Httpget探测方式的yaml文件语法如下:

spec:  
  containers:  
  - name: liveness  
    image: k8s.gcr.io/liveness  
    livenessProbe:              #采用livenessProbe机制探测  
      httpGet:                  #采用httpget的方式  
    scheme:HTTP         #指定协议,也支持https  
        path: /healthz          #检测是否可以访问到网页根目录下的healthz网页文件  
        port: 8080              #监听端口是8080  
      initialDelaySeconds: 3     #容器运行3秒后开始探测  
      periodSeconds: 3                #探测频率为3秒

上述配置文件中,探测方式为项容器发送HTTP GET请求,请求的是8080端口下的healthz文件,返回任何大于或等于200且小于400的状态码表示成功。任何其他代码表示异常。

3)tcpSocket:通过容器的IP和Port执行TCP检查,如果能够建立TCP连接,则表明容器健康,这种方式与HTTPget的探测机制有些类似,tcpsocket健康检查适用于TCP业务。

tcpSocket探测方式的yaml文件语法如下:

spec:  
  containers:  
  - name: goproxy  
    image: k8s.gcr.io/goproxy:0.1  
    ports:  
- containerPort: 8080  
#这里两种探测机制都用上了,都是为了和容器的8080端口建立TCP连接  
    readinessProbe:  
      tcpSocket:  
        port: 8080  
      initialDelaySeconds: 5  
      periodSeconds: 10  
    livenessProbe:  
      tcpSocket:  
        port: 8080  
      initialDelaySeconds: 15  
      periodSeconds: 20

在上述的yaml配置文件中,两类探针都使用了,在容器启动5秒后,kubelet将发送第一个readinessProbe探针,这将连接容器的8080端口,如果探测成功,则该pod为健康,十秒后,kubelet将进行第二次连接。

除了readinessProbe探针外,在容器启动15秒后,kubelet将发送第一个livenessProbe探针,仍然尝试连接容器的8080端口,如果连接失败,则重启容器。

探针探测的结果有以下三种可能:

  • Success:Container通过了检查;

  • Failure:Container没有通过检查;

  • Unknown:没有执行检查,因此不采取任何措施(通常是我们没有定义探针检测,默认为成功)。

5、如何控制滚动更新过程?

可以通过下面的命令查看到更新时可以控制的参数:

kubectl explain deploy.spec.strategy.rollingUpdate
  • 1

  • maxSurge:此参数控制滚动更新过程,副本总数超过预期pod数量的上限。可以是百分比,也可以是具体的值。默认为1。

    上述参数的作用就是在更新过程中,值若为3,那么怎样,先运行三个pod,用于替换旧的pod,以此类推

  • maxUnavailable:此参数控制滚动更新过程中,不可用的Pod的数量。

    这个值和上面的值没有任何关系,举个例子:我有十个pod,但是在更新的过程中,我允许这十个pod中最多有三个不可用,那么就将这个参数的值设置为3,在更新的过程中,只要不可用的pod数量小于或等于3,那么更新过程就不会停止

6、镜像下载策略是什么?

可通过命令“kubectl explain pod.spec.containers”来查看imagePullPolicy这行的解释。

K8s的镜像下载策略有三种:Always、Never、IFNotPresent

  • Always:镜像标签为latest时,总是从指定的仓库中获取镜像;

  • Never:禁止从仓库中下载镜像,也就是说只能使用本地镜像;

  • IfNotPresent:仅当本地没有对应镜像时,才从目标仓库中下载。

默认的镜像下载策略是:当镜像标签是latest时,默认策略是Always;当镜像标签是自定义时(也就是标签不是latest),那么默认策略是IfNotPresent

7、image的状态有哪些?

  • Running:Pod所需的容器已经被成功调度到某个节点,且已经成功运行,

  • Pending:APIserver创建了pod资源对象,并且已经存入etcd中,但它尚未被调度完成或者仍然处于仓库中下载镜像的过程

  • Unknown:APIserver无法正常获取到pod对象的状态,通常是其无法与所在工作节点的kubelet通信所致。

8、pod的重启策略是什么?

可以通过命令“kubectl explain pod.spec”查看pod的重启策略。(restartPolicy字段)

  • Always:但凡pod对象终止就重启,此为默认策略。

  • OnFailure:仅在pod对象出现错误时才重启

9、K8S中部署应用版本回滚的命令

#运行yaml文件,并记录版本信息;
kubectl apply -f httpd2-deploy1.yaml  --record    
 
#查看该deployment的历史版本  
kubectl rollout history deployment httpd-devploy1    

#执行回滚操作,指定回滚到版本1 
kubectl rollout undo deployment httpd-devploy1 --to-revision=1

10、标签和标签选择器的作用是什么?

标签:是当相同类型的资源对象越来越多的时候,为了更好的管理,可以按照标签将其分为一个组,为的是提升资源对象的管理效率。

标签选择器:就是标签的查询过滤条件。目前API支持两种标签选择器:

  • 基于等值关系的,如:=、==、!=(注:==也是等于的意思,yaml文件中的matchLabels字段);

  • 基于集合的,如:in、notin、exists(yaml文件中的matchExpressions字段);

11、常用的标签分类有哪些?

标签分类是可以自定义的,但是为了能使他人可以达到一目了然的效果,一般会使用以下一些分类:

  • 版本类标签(release):stable(稳定版)、canary(金丝雀版本,可以将其称之为测试版中的测试版)、beta(测试版);

  • 环境类标签(environment):dev(开发)、qa(测试)、production(生产)、op(运维);

  • 应用类(app):ui、as、pc、sc;

  • 架构类(tier):frontend(前端)、backend(后端)、cache(缓存);

  • 分区标签(partition):customerA(客户A)、customerB(客户B);

  • 品控级别(Track):daily(每天)、weekly(每周)

12、查看标签的方式?

kubectl get pod --show-labels    #查看pod,并且显示标签内容 

kubectl get pod -L env,tier      #显示资源对象标签的值  

kubectl get pod -l env,tier      #只显示符合键值资源对象的pod,而“-L”是显示所有的pod

13、添加、修改觉删除标签的命令

#对pod标签的操作  
kubectl label pod label-pod abc=123     #给名为label-pod的pod添加标签  
kubectl label pod label-pod abc=456 --overwrite      #修改名为label-pod的标签  
kubectl label pod label-pod abc-             #删除名为label-pod的标签  
kubectl get pod --show-labels  

#对node节点的标签操作     
kubectl label nodes node01 disk=ssd      #给节点node01添加disk标签  
kubectl label nodes node01 disk=sss –overwrite    #修改节点node01的标签  
kubectl label nodes node01 disk-         #删除节点node01的disk标签

14、DaemonSet资源对象的特性

DaemonSet这种资源对象会在每个k8s集群中的节点上运行,并且每个节点只能运行一个pod,这是它和deployment资源对象的最大也是唯一的区别。

15、Pod的生命周期有哪些状态?

  • Pending:表示pod已经被同意创建,正在等待kube-scheduler选择合适的节点创建,或者正在准备镜像;

  • Running:表示pod中所有的容器已经被创建,并且至少有一个容器正在运行或者是正在启动或者是正在重启;

  • Succeeded:表示所有容器已经成功终止,并且不会再启动;

  • Failed:表示pod中所有容器都是非0(不正常)状态退出;

  • Unknown:表示无法读取Pod状态,通常是kube-controller-manager无法与Pod通信。

16、创建一个Pod的流程是如何的?

  1. 客户端提交Pod的配置信息(可以是yaml文件定义好的信息)到kube-apiserver

  2. Apiserver收到指令后,通知给controller-manager创建一个资源对象;

  3. Controller-manager通过api-server将pod的配置信息存储到ETCD数据中心中;

  4. Kube-scheduler检测到pod信息会开始调度预选,会先过滤掉不符合Pod资源配置要求的节点,然后开始调度调优,主要是挑选出更适合运行pod的节点,然后将pod的资源配置单发送到node节点上的kubelet组件上。

  5. Kubelet根据scheduler发来的资源配置单运行pod,运行成功后,将pod的运行信息返回给scheduler,scheduler将返回的pod运行状况的信息存储到etcd数据中心。

17、删除一个Pod的流程是如何的?

Kube-apiserver会接受到用户的删除指令,默认有30秒时间等待优雅退出,超过30秒会被标记为死亡状态,此时Pod的状态Terminating,kubelet看到pod标记为Terminating就开始了关闭Pod的工作;

关闭流程如下:

  1. pod从service的endpoint列表中被移除;

  2. 如果该pod定义了一个停止前的钩子,其会在pod内部被调用,停止钩子一般定义了如何优雅的结束进程;

  3. 进程被发送TERM信号(kill -14)

  4. 当超过优雅退出的时间后,Pod中的所有进程都会被发送SIGKILL信号(kill -9)。

18、K8S的service是什么?

Pod每次重启或者重新部署,其IP地址都会产生变化,这使得pod间通信和pod与外部通信变得困难,这时候,就需要Service为pod提供一个固定的入口。

Service的Endpoint列表通常绑定了一组相同配置的pod,通过负载均衡的方式把外界请求分配到多个pod上。

19、K8S如何进服务注册?

Pod启动后会加载当前环境所有Service信息,以便不同Pod根据Service名进行通信。

20、K8S数据持久化的方式有哪些?

emptyDir:是最基础的Volume类型,用于存储临时数据的简单空目录。如果Pod设置了emptyDir类型Volume,Pod被分配到Node上时候,会创建emptyDir,只要Pod运行在Node上,emptyDir都会存在(容器挂掉不会导致emptyDir丢失数据),但是如果Pod从Node上被删除(Pod被删除,或者Pod发生迁移),emptyDir也会被删除,并且永久丢失。

Hostpath:将宿主机上已存在的目录或文件挂载到容器内部。类似于docker中的bind mount挂载方式。这种数据持久化方式,运用场景不多,因为它增加了pod与节点之间的耦合。


PersistentVolume (持久卷, 简称 PV) 和 Persistent VolumeClaim(持久卷声明,简称 PVC)

使得K8s集群具备了存储的逻辑抽象能力,使得在配置Pod的逻辑里可以忽略对实际后台存储技术的配置,而把这项配置的工作交给PV的配置者,即集群的管理者。

存储的PV和PVC的这种关系,跟计算的Node和Pod的关系是非常类似的;PV和Node是资源的提供者,根据集群的基础设施变化而变化,由K8s集群管理员配置;而PVC和Pod是资源的使用者,根据业务服务的需求变化而变化,由K8s集群的使用者即服务的管理员来配置。

以上是关于云原生 • Kubernetes一文掌握 k8s 包管理工具 Helm的主要内容,如果未能解决你的问题,请参考以下文章

云原生一文细数kubernetes常见20道问题

云原生技术分享| 由浅入深掌握Kubernetes系列:十分钟初识K8S

一文深入理解 Kubernetes

一文讲清K8s如何改变美团的云基础设施

云原生势头不减,你还没学会 Kubernetes ?

K8S 云平台/Go 工程师/专家 - 小鹏汽车 | 云原生招聘