K8s相关问题

详解 Kubernetes OOM 和 CPU Throttling 问题

以下是对原文内容的整理,使其更加通顺连贯:

主要讲解了Kubernetes中容器的OOM(内存不足)和CPU Throttling(CPU限制)问题。首先解释了limit和request的概念:limit是硬限制,容器的CPU和内存不能超过设置的limit值;request是软限制,只要达到request,就可以被调度。

接着指出了几个关键点:

  1. 如果节点资源不足,pod可能会被驱逐。
  2. 如果内存不足,pod会直接被OOM Kill。
  3. 如果CPU消耗高于limit,不会被Kill,但会受到限制(Throttling),影响上层应用的响应速度。检测到CPU Throttling应作为告警规则。
  4. 如果进程被OOM Kill,会有退出码137。

然后用一个示例说明OOM原理:有两个容器,其中一个400M限制的Redis容器使用800M,另一个容器加上节点自身内存占用超过节点1G内存时,就会被Kill。

检测OOM有两种方式:

  1. 如果支持,可以查看node_vm_stat_oom_kill指标。
  2. 监控系统日志,发现”oom kill”关键词打印。

作者希望在OOM之前就能发现内存不足,提出了一个合理的内存利用率指标计算方式。

对于CPU利用率,通过容器的CPU使用率除以limit即可,但前提是设置了limit。

对于CPU Throttling,cAdvisor提供了两个指标:throttled和total期间数,两者相除可得到Throttling的比率,只要比率大于0就说明CPU受到限制。

最后作者表示会将CPU Throttling相关的指标整理到知识星球中,提供了相关二维码,并鼓励关注、三连支持。




这个博主下面很多

在k8s里创建pod时自动添加/etc/hosts条目

以下是对原文内容的整理:

本节课程讲解如何在Kubernetes中创建Pod时,自动在Pod的/etc/hosts文件中添加条目。

默认情况下,当创建一个Pod时,Kubernetes会在该Pod的/etc/hosts文件中添加一条记录,左边是Pod的IP地址,右边是Pod的名称。示例中创建了一个Pod,它的IP地址是172.16.209.1,因此在/etc/hosts中添加了一条”172.16.209.1 Pod名称”的记录。

除了默认添加的记录外,我们还可以手动指定在/etc/hosts中添加其他条目。方法是在Pod的spec下添加一个hostAliases字段。

示例修改了之前创建的Pod的YAML文件,在spec下添加了hostAliases字段,其中包含两个条目:

  1. 192.168.1.101 vm101
  2. 192.168.1.102 vm102

这意味着在该Pod的/etc/hosts中,192.168.1.101会解析为vm101,192.168.1.102会解析为vm102。

在hostAliases下,可以继续添加更多的IP-主机名条目,格式为[“ip地址 主机名1 主机名2…”]。

修改完YAML文件后,重新创建该Pod,进入Pod内部查看/etc/hosts,可以看到添加的两条手动指定的条目已经生效。

通过这种方式,我们能够在Pod的/etc/hosts中添加自定义的主机名解析条目,并在Pod内直接使用这些主机名访问对应的IP地址,非常方便。只需在Pod的YAML文件中添加hostAliases字段并指定条目即可。




设置kubernetes里worker最大pod数


以下是对原文内容的整理:

本节课讲解如何设置Kubernetes worker节点能运行的最大Pod数量。

在Kubernetes架构中,用户发送请求给API Server,API Server将请求发送给Controller Manager,Controller Manager告诉Kubelet在节点上创建Pod。每个节点都可以设置自己能够运行的最大Pod数量,如果已运行Pod数超过该限制,当Controller Manager发送创建新Pod的请求时,Kubelet将拒绝执行。

默认情况下,每个节点能够运行的最大Pod数是110个。我们可以通过describe节点查看节点的配置信息,其中的Allocatable字段显示了最大Pod数量限制。

如果想修改该限制,需要调整Kubelet的启动参数。首先用psaux查看Kubelet进程,可以看到Kubelet支持许多启动参数,其中就包括设置最大Pod数的–max-pods参数。

接着查看Kubelet的启动脚本,一般是/etc/kubernetes/kubelet.env或类似的文件。在该文件中可以定义Kubelet启动参数对应的环境变量,比如MAX_PODS=5即表示最大Pod数为5。

修改后重启Kubelet进程,再通过describe节点查看,可以发现最大Pod数已更新为5。这时再创建第6个Pod就会失败,被拒绝运行。

最后,将节点的最大Pod数还原为默认值110,恢复测试前的状态。

需注意的是,将最大Pod数设置过低可能会导致资源浪费,所以一般保持默认值或根据实际情况适当调整即可。该配置由Kubelet负责执行,并由Controller Manager发送创建Pod请求,而Kubelet来决定是否可以创建。






k8s 了解runAsUser的作用

容器里的进程默认是以root用户的身份运行的,但是对于有的程序来说并不希望以root用户的身份来运行,所以我们可以通过runAsUser来设置容器里的进程以普通用户的身份来运行。

以下是对原文内容的整理:

容器里的进程默认是以root用户身份运行的,但有些程序不建议使用root用户运行,比如ElasticSearch和Kibana等。在创建Pod时,我们可以通过设置securityContext来指定容器里的进程以普通用户身份运行。

首先,创建一个名为pod1的Pod,查看容器进程运行用户,显示是root用户。

然后删除pod1,修改其YAML文件,在spec.containers.securityContext下添加runAsUser:1000。这里的1000可以是任意数值,代表运行进程的UID,不需要事先在系统里存在对应的用户。

保存修改并重新创建pod1,查看容器内进程运行用户,可以看到是UID为1000的普通用户。

再次删除pod1,修改其YAML,使用nginx镜像并添加runAsUser:10000。创建后,发现Pod无法正常运行,查看日志发现有许多permission denied错误。

原因是nginx进程需要使用root权限运行,而我们设置的UID为10000是普通用户,没有相应权限,因此导致运行失败。

这就展示了runAsUser的作用,可以让容器里的进程以非root的普通用户身份运行,增强了安全性,避免了一些需要root权限才能运行的应用在容器内受到影响。但也需要注意,如果应用本身需要root权限,使用普通用户运行也会导致无法正常工作。

总之,runAsUser允许我们指定容器内进程的运行用户身份,在满足应用需求的前提下,尽量以普通用户身份运行,增强容器安全性。



了解k8s里的SA serviceAccount

在k8s里,每个pod都要以某个SA来运行,如果没有指定SA的话,默认使用的是default这个sa。pod被创建之后,会以投射卷的方式在pod里生成一个token。#kubernetes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
一、K8S中的服务账户(SA)的作用,以及如何在创建pod时指定使用哪个SA。同时,通过一个实例演示了如何查看pod所使用的SA和token信息。
00:01 - K8S中有两种账户,用户账户和服务账户
00:13 - 创建Pod时,如果没有指定使用哪一个SA,则使用default SA
02:08 - 使用SA服务账户时,在Pod中生成的token包含绑定的SI信息和有效期

二、使用Google Cloud Storage创建数据备份的流程,包括创建存储容器、上传数据、设置访问权限等步骤。同时还介绍了如何创建服务帐户和令牌。
03:02 - 使用SA serviceAccount创建的令牌每小时会变一次,有效期为一年。
04:48 - 可以手动创建有效期较长的令牌,例如8640小时。
05:09 - 可以使用secret方式创建永不过期的令牌,确保SUBACCOUNT名字与SI名字一致。


三、如何通过生成token和设置权限来实现对系统资源的访问控制,并通过示例演示了如何判断用户是否具备某项权限。
06:02 - 如何生成一个永不过期的token,存储在describe secret s i e 中
07:18 - SAE现在没有任何权限,需要给其刘邦或权限才能访问
08:56 - 给SA1 Class admin权限后,就可以访问default下面的SI1了


四、SA的概念和作用,以及在K8S中如何创建和使用SA。此外,还详细讲解了不同版本中SI和token的不同之处。
09:00 - SA serviceAccount可以获取当前命名空间里的所有信息
09:41 - Pod里的token有效期为一年,每隔一周和一小时会轮换
11:09 - K8S版本不同,创建SI的方式和secret也会不同

以下是对原文内容的整理:

Kubernetes有两种账户:用户账户和服务账户(Service Account,简称SA)。用户账户用于登录Kubernetes,而SA则在创建Pod时使用。

如果没有指定使用哪个SA,则使用default这个SA。每个命名空间都会有一个default SA,即使删除它也会自动重新创建。

我们创建一个pod1,查看其使用的SA为default。可以通过修改YAML文件,在spec.serviceAccount指定使用其他SA,比如sa1。

使用SA运行Pod时,会通过挂载卷的方式在Pod内生成一个Token文件。该Token是经过Base64编码的,包含了SA的相关信息,比如所在命名空间、Pod名称、SA名称、有效期等。Token默认有效期为1年,但每小时会轮换一次。删除Pod后Token立即失效。

我们也可以手动为SA创建Token,有效期可指定,包括永不过期。创建永不过期Token的方式是创建一个类型为kubernetes.io/service-account-token的Secret,确保metadata.annotations.kubernetes.io/service-account.name与目标SA名称一致即可。

接着查看SA的权限。默认SA没有任何权限,需要手动授予,比如绑定admin角色。有了足够权限后,使用该SA的Token就能访问Kubernetes资源了。

最后介绍了在不同Kubernetes版本中SA行为的变化:
1.20及以前,创建SA时自动创建Secret,Token通过挂载卷暴露给Pod;
1.21-1.23,创建的Secret中的Token与Pod内的Token不同,Pod内Token由Kubelet重新生成;
1.24及以后,创建SA时不再创建Secret,全权交给Kubelet生成Token。

总之,SA主要用于为Pod内的进程提供身份认证,根据授予的权限控制对Kubernetes资源的访问,是较为安全的资源访问方式。


一文讲透webhook在k8s中作用


以istio envoy proxy 为例子,深刻理解webhook在k8s中的作用

以下是对原文内容的整理:

大家好,今天是2024年4月9日,给大家带来一个关于Kubernetes中Webhook作用的精讲。

我们先思考一个问题:当我们在一个命名空间中启用了Istio服务网格后,会发生什么? Istio会以Sidecar的形式为该命名空间下的每个Deployment注入一个Envoy代理Pod,所有流量都会经过该代理Pod进行拦截和分配。但是,这个Sidecar实际上是谁创建的呢?

答案是:由Webhook创建的。如果针对某个特定命名空间启用了Istio,那么在我们交付自己业务Deployment的YAML文件时,Webhook会帮我们创建一个Envoy Sidecar。

Webhook分为两种:Mutating和Validating。Mutating的作用是修改对象,比如为Deployment添加Sidecar。Validating则用于验证字段合法性。

在Kubernetes中,有三种Webhook:Admission Webhook、Authorization Webhook和Conversion Webhook。我们这节课讲的是Admission Webhook,它包含了Mutating Admission Webhook 和 Validating Admission Webhook。

Mutating Webhook会先被调用,修改我们提交的对象,比如为Deployment添加Sidecar容器和相关字段。然后Validating Webhook会对修改后的对象进行字段合法性验证。通过这两个步骤后,对象才会真正部署到Kubernetes中。

所以,Webhook的作用就是在我们的对象交付到Kubernetes之前,对对象进行修改和验证等额外操作。在Istio的例子中,Mutating Webhook帮助我们添加了Envoy Sidecar,使流量能被拦截和分配。

总之,Webhook允许在资源对象创建之前、修改之前以及修改之后进行拦截,对资源对象的规格进行最后一步添加、修改或验证,使其最终满足我们的需求,是一种非常有用的机制。




k8s里标签的管理


k8s里标签的管理

在K8S环境里,所有的对象都是含有标签的,可以利用这些标签来定位到某一个资源。比如说,我可以查找含有某个标签的pod,可以查找含有某一个标签的命名空间或者节点。

对于标签来说,它的格式是一个键值对的格式,也就是一个键等于某一个值。这种格式在K8S里面,它是可以有多个特殊字符的,可以包含像比如说可以包含连字符、斜杠、点等等这些符号。如果说我们要写多个键值对的话,那么我们在这里面就使用逗号来隔开。比如说,key1=value1,key2=value2。

我们现在来看一个例子,比如说kubectl get nodes。这里面,比如说我要查看vm31这个节点上面的标签,那这里面它其实就含有了多个标签。你看这是多少个键,这是第一个标签,等号是作为键和值的连接符,这个键里面包括了点、连字符,可以有也可以没有,那这里面的话你看这是对应的一个值,这是第一个标签。然后这个位置从这个开始到这个位置,这是第二个标签,你看它是使用逗号来隔开的,然后这个是第三个标签,以此类推。

所以说,如果要查看标签,可以通过这种方式,通用语法就是kubectl get 你的资源类型,比如说你要是想查看pods,那么我这里面就写pods。我要是想查看命名空间,我这里就直接写上namespace等等等等。可以查看某一种资源类型的标签。

我举个例子来说,kubectl get nodes --show-labels。那么我这样写的话,我有没有指定查看的是哪个节点的标签呢?没有。那这样的话,它显示的是所有节点的标签,你看这是vm31、vm32,它们的这样的一些标签。

那如果说我要是想查看所有的命名空间,比如说kubectl get namespaces --show-labels。那么就可以查看到每一个命名空间,它们的标签是什么。

如果说我要查看pods,我这里面就有一个pod,我要想查看所有的pod的标签的话,那么就是kubectl get pods --show-labels。在这里面,我可以再次来创建一个pod。那这样的话,你看我列出来的就是所有的pod的标签,你看这就是pod1,它的标签是什么; pod2,它的标签是什么。这样子就是列出某一个资源类型里面所有的对象的标签。

当然了,如果说我要是想查看某一个资源类型,某一个资源具体的资源的话,那么在这里面,我们就加上一个资源名。比如说我要查看pod4的标签,那这里面就是kubectl get pod pod4 --show-labels

如果说我想查看default这个命名空间的标签的话,那么就是加上default,这是一个命名空间,然后我加上--show-labels就可以了。

那么我要查看某一个节点的话,kubectl get nodes。如果你没有加上名字的话,是它显示的是所有节点的标签。那么在这里面,我可以加上一个,比如说vm31。这样的话,它只显示vm31这一个节点的标签。

如果说我要是想查看vm32这个节点的标签的话,我就直接写上vm32这个对象的名字。这样子,我们现在已经知道了如何去查看标签。

当然了,我们也可以去对对象来设置标签。它的一个通用语法就是kubectl label资源类型名称key=value。那如果说这是我们可以具体的某一个资源,来设置标签。设置什么标签呢?key=value。

在这里面,我们现在来试一下kubectl get pods --show-labels。这里面查看的是所有的pod的标签。我现在想给pod1来设置标签,那就是kubectl label pod pod1 xx=xx。这样的话,我现在就给它设置了这样的一个标签了。

那我要是想取消这个标签的话,取消这个标签的话,我们所需要做的就是把key=value后面的值换成一个连字符。这里面我的键的名字叫xx,值是xx。我所需要做的就是把等号后面的值一起都给它换成这是一个连字符,那就相当于是把这个键给它取消掉了。

此时我们再次来看一下,然后我再重新去设置一个,比如说我在这里面再来设置一个xx1=xx1。再设置一次,比如说我设置一个yy=yy

这里面的意思就是说,xx这个键,你已经设置过了,也就是说这个xx这个标签里面它已经存在了,就是那个pod1的标签里面已经存在了xx这个键。你现在再次想给它设置这样的标签,也就是键的名字叫xx的话,那就是已经存在了,你没法设置。

那如果说我现在就想设置的话,要不就是你先把原有的这个xx键的这个标签给它取消,要不就是我加上一个--overwrite,也就是覆盖就可以了。

那这里面,我想看kubectl get pods --selector=xx=yy --show-labels。还有xx=yy这个标签的pod有哪些呢?那此时它列出来的就是一个pod,pod2是没有这个标签的。

我现在再次给它取消掉这个标签,取消标签的话,刚才已经讲了,就是直接写上,就是把等号后面的值变成一个连字符,然后这次就没有xx=yy这个标签了。

我刚才做的时候,是给某一个节点设置的标签。那如果说我要是想给所有的对象,比如说我这里面写的是pod,我要给所有的pod来设置这个标签的话,我就可以不用去写具体的名字,我要加上--all

这样的话,就是说对所有的这些pod都设置了这个标签。那你得要加上--all,你不加--all的话,它这里面你并没有指定是哪个对象来设置的,那你一定得要加上--all,表示的是我要对pod所有的pod,也就是所有的对象来添加才可以。

所以说一定得要加上–all。那么我们再次来查看,还有什么含有xx=xx标签的pod,你看这里面就有pod1、pod2,它们都含有这些标签。

那当然了,如果取消的话也是一样的道理,那这样的话,我们就把所有的pod的这个xx键的标签,全部都给它取消掉了。

同样的道理,我们对节点也是可以去做相同的设置的。比如说我现在想给所有的节点,都来设置一个标签,设置xx=xx这个标签。kubectl label node --all xx=xx

kubectl get nodes --show-labels,你看它就只有vm31,因为我们当前只有vm31这一个节点。

那如果说我要对vm32来设置这个标签的话,那就是kubectl label node vm32 xx=xx。这样的话,vm32也是具备了这个标签。

那么kubectl get nodes --show-labels,你看它们都含有了这个标签。

同样的道理,那如果说我要是取消的话,也是一样的,我现在把这两个节点都给它取消,kubectl label node --all xx-。然后把key=value后面的值变成一个连字符,这样的话就全部给它取消掉了。

在这里面,为了后面的练习做准备,我们给vm31设置一个标签xx=x,kubectl label node vm31 xx=x。给vm32设置一个yy=yy的标签,kubectl label node vm32 yy=yy

这里面有个标签,我现在你看这个role这个标签,这是我们自己去定义的。yy这个标签,这是我们自己指定的。其实在K8S环境这里面,它给某些对象也设置了一些内置的标签。

像比如说kubectl get nodes,可以看到role这一列,role这一列里面,对于我们的master来说,它给自动加了一个control-plane。然后我节点上它这里面是一个node。

这个标签是怎么设置的?其实它就是有一个标签可以设置,那就是这个标签叫node-role.kubernetes.io/xxx。这是属于它内置的标签,标签的名字就是node-role.kubernetes.io/,等号后面是值xxx或yyyyy,这种格式。它的前缀前面就是node-role.kubernetes.io是固定的,值是什么无所谓,记住了值是什么无所谓,你想怎么写就怎么写,不写没关系。

关键的是这个xx这个位置,xx这个位置,你所写的名字它就会出现在role这个位置上。你看对于我们的这个vm32来说,它是没有这个标签的,我们自己可以来给它设置一个,kubectl label node vm32 node-role.kubernetes.io/worker=

就是这样,那么这里面比如说我就起名字叫worker。值可以写,也可以不写,无所谓。那这样的话,我们现在来看一下kubectl get nodes --show-labels,你看它这里面就出现了这样的一个worker。

也就是我们设置标签的时候,这个值你可以写和不写,无所谓。关键的就是我们现在想设置role这个位置,那么就是关键是这一块的这个位置,你怎么写的。这就是我们所说的对标签来进行管理。



k8s里配置pod容忍



这节课我们来学习如何配置pod的容忍。如果要系统学习K8S的内容,可以参加我们的CKA培训课程。在上节课我们给大家讲了关于节点的污点设置,我们说如果有一个节点有污点的话,那么当我们去创建一个pod,被指定在这个节点上运行的时候,此时这个pod是不会被创建的。

我在这里面给vm32设置了一个污点,我设置了key=value这样的一个污点。然后我现在有了这样的一个pod,它是会被运行在vm32上运行,因为vm32才有这个污点。在这里面,我有没有指定任何的选项,说可以容忍这个污点呢?此时并没有,所以说我们的这个pod此时它是创建不出来的,它的状态是Pending。

我现在把这个pod删除掉。那对于我们的vm32这个节点来说,它现在已经是有了污点。我现在要是想让这个pod,想让这个pod能够在含有污点的节点上运行的话,我们得要让这个pod能够忍受容忍这个污点,也就是我们得要在pod上面去配置一个toleration,这样的一个字段。也就是说我要去容忍哪一个污点。

那这里面的toleration是怎么写的呢?这里面其实我们可以去参考,所谓天下文章一大抄,关键在于我们会抄还是不会抄。可以去kubectl explain pod.spec下面去找。当然了,如果说我们这样找的话,可能会比较麻烦。

那这里让我随便写一个toleration,例如key=value:NoSchedule。有了toleration的话,它这个pod并不会真的去运行在有污点的节点上。

在这里面,大家都看清楚了,我们就能够去找到这个toleration这个选项,在这里面的spec里面,我们开始把它贴过来就可以了。它这里面指定的就是一个toleration,我能够容忍一个什么样的一个污点。

现在我们因为准备让它在vm32这个节点上运行,那就是kubectl describe node vm32,我们来看一下它的污点。vm32上的污点叫做key=value:NoSchedule

那么toleration的意思就是容忍,我要容忍什么呢?那么就是要容忍key=value。我们现在来容忍一下,我把它写过来叫key。然后effect值是什么呢?effect指的是NoSchedule。那我们在这里面写上NoSchedule。

这是为NoSchedule,我要容忍的是key=value这个污点。也就是说我要容忍的是我要设置key=value:NoSchedule这样的一个污点。

这里面有人说你在里面只写了一个键,没有写值。然后这里面我们现在来看一下这个operator,我来改变一下它的顺序,这样的话我们可能会更好理解一点。

我在这里面给它加上一个连字符,其实这个顺序无所谓。然后这里面,我现在就记住这几个元素的顺序无所谓,但是第一个选项,你得给它加上一个连字符。我现在改的这个顺序,主要是让我们看起来会更好看一些,更好解释一些。

那么这里面,我要容忍什么呢?key是什么?key是key。effect值是什么?effect值是NoSchedule。这里少写了一个e,应该是NoSchedule。

然后这里面有人说没有写value,记住我们这里这里面写的是Exists。Exists的意思就是说,污点里面只要含有key就可以了,那么它的值是什么无所谓,只要含有这个key就可以。

那这样的话,我们现在再回来创建一下,你看现在就能够容忍key的污点,当中的键是key,值是什么无所谓,且effect的值是NoSchedule。我现在把它创建给pod1,你看现在就可以能够正常运行了。为什么呢?因为它能够容忍key这个污点,值是什么无所谓,因为我们在这里面写的是Exists。

然后我现在把这个pod来给它删除。这个operator这个值啊,除了Exists之外,我们还有一个值是可以写的,还可以写成Equal,Equal的意思就是等于。等于什么呢?就是key这个键的值,这个值它的值要等于一个什么?

我们得想一个value,value是什么呢?你必须得要完整的等于value才可以。比如说我要想一个值是value1。它这里面容忍的是什么呢?就是key等于value1的这样的一个键值对,我才能够容忍,且effect值是NoSchedule。

因为这里面写的是一个Equal,但是我实际的污点是key=value。而你容忍的是key=value1,那这样的话,那就是我容忍的污点和它实际存在的污点不匹配,那么此时我的这个pod状态就是Pending。

所以说我们在里面value的话,我们怎样改成是和它实际是相同的才可以?那么你所具备的一个污点是它,那么我能够容忍的污点也是它。那这样的话,我们的这个pod它就能够正常的运行起来,它为Running状态。

好,那这里面有一个问题,就是各位大家你还记得,就是如果说它这里面有多个污点的话,就是key除了key=value之外,下面还有一个label,value对应,还有一个value对应,有多个污点的话,你的这个pod里面必须得要容忍所有的污点才可以。

比如说我举例来说,kubectl label node vm32 key2=value2。那这样的话,你看我来给它设置的是两个污点了,此时这是有两个污点了。

有两个污点的话,那么我现在再次去创建pod1,各位你们看清楚了,这个pod不能创建出来。为什么呢?因为你这里面有两个污点,key和key2,你只能容忍了一个key的污点,那还剩下key2这个污点我是无法容忍的,所以说此时这个pod还是不能创建。

那如果说你要是容忍啊,如果今天有多个污点,我们必须得全部都得要去设置容忍才可以。

那在这里面,我就直接去复制粘贴key2,这里面的值是什么呢?这里面的值这是value2,因为我这里面写的是Equal。

那么我现在就把这个节点上的两个污点都来给它容忍。加上key2=value2:NoSchedule。

那这样的话,这个pod它是能够正常的创建的。

我现在把这两个污点来给它取消,kubectl taint node vm32 key-。取消key这个污点。kubectl taint node vm32 key2-。来取消key2这个污点。

现在没有任何的污点了。没有任何的污点的话,然后我现在让各位想一个问题,你觉得我现在创建pod1,能否创建出来?

这里面它有了这个toleration配置,既然配置了这个容忍,但是呢它是没有污点的。那此时各位,你觉得pod能创建出来吗?

有的人说不能,有人说能。你就直接思考一下,我们前面所举的例子吧。

这个女生嫌弃男生有很多毛病,所以说它不愿意嫁给它。后来你涉及到容忍,你说你有毛病没关系,我也能容忍你。而且你本来就没有这些毛病,那自然更好了。

所以说它自然是可以创建的。

这就是我们给大家所讲解的,关于pod的toleration选项。这个toleration选项可以让pod在有指定的、有特定污点的节点上面去运行。


k8s中污点和容忍 都是什么? 为什么需要这些东西?


在K8S中,污点(Taint)和容忍(Toleration)是用来控制Pod可以被调度到哪些节点上的机制。

污点(Taint)是添加到节点上的一个小标记,它由一个键(key)、值(value)和效果(effect)构成。当给一个节点添加了一个或多个污点后,只有Pod中配置了和这些污点相匹配的容忍才能够被调度到该节点。

容忍(Toleration)则是附加到Pod上的一个小标记,用于允许Pod调度到带有与之匹配的污点的节点上。容忍由键(key)、操作符(operator)、值(value)和效果(effect)构成,用于匹配节点上的污点。

我们需要污点和容忍主要有以下几个原因:

  1. 专门节点分配
    通过给节点添加污点,然后只给特定的Pod配置相匹配的容忍,就能够将Pod调度到预期的节点上,比如将某些Pod调度到GPU节点、SSD节点等特殊硬件节点上。

  2. 节点专用
    通过给需要保留给特殊workload使用的节点添加污点,除了配置了容忍的Pod外,其他Pod就不能调度到这些节点上了,从而避免资源竞争。

  3. 节点隔离
    给有问题(如不健康)的节点添加一个污点,新创建的Pod就不会调度到这些节点,但已存在于该节点的Pod不受影响。这为我们手动排查问题赢得时间。

  4. 发布新功能
    通过先给一部分节点添加污点,然后控制只有配置了容忍的Pod才能调度到这些节点,在发布新功能时可以先进行小规模测试。

总之,污点和容忍为K8S提供了一种节点和Pod的灵活调度匹配机制,增强了调度的细粒度控制能力,使集群的资源利用更加灵活高效。


好的,通过一些例子来说明污点和容忍的使用场景:

  1. 专用节点

假设我们有一个包含4个节点的Kubernetes集群,我们希望其中有2个节点专门用于运行具有较大资源需求的组件。我们可以:

  • 给这2个节点添加一个污点,比如 node-role.kubernetes.io/worker=true:NoSchedule
  • 对需要在这2个节点上运行的Pod,给它们配置相匹配的容忍,比如:
1
2
3
4
5
tolerations:
- key: "node-role.kubernetes.io/worker"
operator: "Equal"
value: "true"
effect: "NoSchedule"

这样,就只有配置了该容忍的Pod才能被调度到这2个打了污点的节点上。

  1. 节点保留

假设集群中有一个节点需要被预留给Kubernetes系统组件使用,我们可以:

  • 给该节点添加一个污点,比如 key=node-reserved:NoSchedule
  • 对系统关键Pod(如kube-proxy)配置一个相匹配的容忍

这样普通用户创建的Pod就无法调度到该节点,保证系统Pod有足够资源运行。

  1. 基于节点污点的发布策略

当需要发布一个新功能时,可以:

  • 先给部分节点添加一个临时污点,如 node-role.kubernetes.io/new-feature=true:NoSchedule
  • 对新功能对应的Pod配置相匹配的容忍
  • 逐步移除其他节点上的污点,完成新功能的滚动发布

这样可以逐步在部分节点上预先进行新功能的测试,减小风险。

  1. 节点隔离

如果发现某个节点有问题,但恰好在该节点上运行着一些不能被干扰的关键Pod,可以:

  • 先给该节点添加一个临时污点,如node.kubernetes.io/unreachable:NoExecute
  • 新创建的Pod将不会调度到该节点,但已运行在该节点的Pod不受影响

这为我们手动排查问题赢得了时间。

通过以上例子可以看出,污点和容忍为Kubernetes提供了细粒度的节点控制能力,让我们能够灵活高效地调度和管理Pod。



了解K8S里pause容器的作用



了解K8S里pause容器的作用

前面我们已经介绍过,每个pod都会有一个pause容器。那么这个pause容器有什么用呢,来听听这节课的内容吧。

当我们发送一个请求,说我要创建一个pod,比如用YAML文件,我把请求发送给kubelet。kubelet会调用容器运行时(container runtime)来给我们创建第一个容器,也就是pause容器。记住pause容器它是被创建出来的第一个容器。

pause容器它首先会去建立一个共享的网络命名空间、IPC命名空间等,来实现资源的共享。然后它才会去创建我们的业务容器,比如容器1、容器2。这样容器1和容器2,它们都是在同一个共享的命名空间里面的,从而实现了它们资源的共享。

好,这里面我现在有一个小小的pod的样本文件,这里面创建了两个容器,c1容器和c2容器,这两个都是我们的业务容器。c1容器,我所使用的是nginx;第二个容器呢,我使用的是busybox。

我现在把这个pod创建出来,它是在vm72这个节点上运行的。我们进入到第二个容器里面,也就是进入到了这个busybox的容器里面。来看busybox,它是否能够看到第一个容器所开启的端口80呢?

好,我们来看一下。大家来看一下,这里面它是能够看到端口80的。说明了什么?因为我们在创建这个容器c1的时候,它里面运行的是nginx,它会使用一个端口,在我们这个共享的网络命名空间里面使用。所以我在第二个容器里面,第二个容器它访问的也是共享的网络命名空间,所以第二个容器,它也是能够看到这个端口80的。

所以在这里面,我直接访问自己的localhost,你看它这里面是能够看到nginx相关的页面的。

那么我现在来看一下,它能不能看到nginx进程呢?因为我现在就是在busybox这个容器里面,能否看到第一个容器里面的进程呢?

好,我们来看一下top命令。各位你看,它这里面也是能够看到c1,也就是我们第一个容器里面的nginx进程的。为什么呢?因为容器c1所创建的进程,是在我们的这个进程命名空间里面的,共享的进程命名空间里面。那第二个容器它也能够访问这个共享的进程命名空间,所以你第一个容器里面运行的进程,第二个容器这里面它也是能够看得到的。

当我们这个pod如果不删除,这个进程命名空间、网络命名空间它其实是一直存在的。

我举个例子来说,kubectl get pods -o wide。我们现在来看一下它的IP地址,这是172.16.200.66。然后我到这个节点上去,我们找到pod1,我们就来看它的一个容器的IP地址。

你看这里面包括了三个容器,一个是pause容器,还有我们所定义的c1和c2容器。我们查看其中的一个,比如这个容器来看一下它的IP。kubectl exec -it pod1 -c c1 -- ip addr。你看是172.16.200.66, 172.16.200.66。

我现在删除掉这个容器,kubectl delete pod pod1 --grace-period=0 --force。删除掉这个容器的话,也只是删除掉了这个容器,删除掉这个容器之后,我并没有删除这个网络命名空间,所以它的这个IP地址是不变的。然后这个容器它是会被自动重建出来。

我们再来看一下,你看它又重新创建出来的,它的IP地址也是不会变的,因为它要保持和我们的pod的这个网络命名空间的地址是一样的。你看,它是不变的。

也就是说只要容器删除了,只要pod不删除这个网络命名空间就是在的,所以这里面的容器你随便怎么删除,它删除了之后重建,它还会使用这个网络命名空间,它的IP地址还是不变的。只有我们把pod删除掉了,pod删除掉了之后,那整个的这个网络命名空间也就删除掉了,那么我们重新创建的时候,它会重新创建一个网络命名空间,这里面它的IP地址才会发生变化。

不过这里面有一个问题我要说一下,我们刚才讲了,我在c2容器里面,能够看到c1容器里面的进程。那是因为我们在创建pod的时候,我给它加了shareProcessNamespace=true这样的一个选项。如果说你没有加这个选项的话,我们在其他容器里面是看不到其它容器的进程的。比如说在c2容器里面,你就看不到c1容器里的进程了。我们加了这句话就能够看得到的,就实现了进程命名空间的共享。

好,这就包括了pause容器的作用。当然它也包括了,像我们可以创建一个volume在容器里面,pod里面的容器可以共享、同时访问volume里面的数据,它这个也是由pause容器给我们实现的。这就是我们所说的pause容器的作用。



K8S经验分享 | 3分钟了解静态Pod


K8S经验分享 | 3分钟了解静态Pod

大家好,我是七哥。今天我们来聊一个特殊的pod - 静态pod。

  1. 什么是静态pod?

静态pod是由节点上的kubelet直接管理的pod,不是通过API server来进行调度和管控的。它们的配置文件放置在节点上的/etc/kubernetes/manifests目录里。当静态pod退出或删除时,kubelet会尝试重新创建它们。

  1. 常规pod和静态pod的区别

a) 创建和管理方式:

  • 常规pod: 由API server通过各种控制器(如Deployment、ReplicaSet)创建和管理。如果pod失败或被删除,kubelet会自动重新创建它们。可以通过ReplicaSet或Deployment控制器实现pod的自动扩展。
  • 静态pod: 通常需要手动创建,更新或替换时也需要手动操作。

b) 配置文件存储位置:

  • 常规pod: 定义通常存储在etcd中,通过YAML或JSON文件进行描述。
  • 静态pod: 配置文件通常位于节点上的特定目录(/etc/kubernetes/manifests),而不是存储在etcd中。
  1. 如何创建静态pod?

工作原理:kubelet持续监听/etc/kubernetes/manifests目录。一旦这个目录中有了新的配置文件,kubelet会按照配置文件的内容拉起新的pod。

需要注意的点:

  • 静态pod的生命周期由节点上的kubelet直接管理,包括创建、重启、销毁等操作,而不是通过API server。
  • 停止kubelet不会直接导致静态pod被删除,但会影响kubelet对静态pod的管理和监控。
  • kubelet的配置文件在/etc/kubernetes/kubelet.d目录中。重启kubelet服务会导致对应的静态pod也重启。
  1. 静态pod的使用场景

a) Kubernetes核心组件:
API server、scheduler、controller manager的配置文件放置在/etc/kubernetes/manifests目录中,通过静态pod的方式提升了各组件与kubelet交互的解耦性。

b) 生产环境案例:
在master节点上放置HAProxy的静态配置,通过HAProxy指向API server的地址列表,实现API server的高可用性。这确保了请求可以均匀分发到多个API server实例上,并在某个实例不可用时进行自动故障转移。

总结:以上就是关于静态pod的介绍,希望大家能够理解其中的精髓。欢迎与七哥互动,点赞再看,我们下期再见!



4个步骤,K8S容器化实现,如此简单!

标题: K8S经验分享 | 4个步骤,K8S容器化实现,如此简单!

引言:
大家好,我是七哥。今天我们一起来探讨一个面试官经常会提到的问题,这也是许多小伙伴感到困惑、不知道该如何回答的一个话题。虽然Kubernetes已经盛行了10年,但目前仍然有很多中小型的业务架构处在单体模式下。这种模式下虽然能够正常运行当前的应用,但在容器化的今天,如果不迁移到Kubernetes这种强大的架构体系下,是不是有点不太合群了呢?

当前业务场景:
目前所有服务都部署在虚拟机上。这种方式在创建或扩容新虚拟机时需要更多时间,并且资源利用率相对较低,导致整体部署和维护成本比较高。

需求分析:
好在企业的技术栈比较统一,主要语言是Java。现在的需求比较明确:针对现有的Java应用做一个容器化的实现。

规划:
经过和研发的讨论,我们做出了以下规划:

  1. 开发小伙伴提供编译好的jar包
  2. 通过现有方式进行容器化实现
  3. 测试验证没问题后,通过Kubernetes进行实现
  4. 通过域名进行访问

实现过程:
第一步:研发提供对应的jar包

第二步:创建Dockerfile

  • Dockerfile的主要功能是为应用程序定义其运行环境和预设变量
  • 编译完成后,可以通过Docker运行这个镜像进行本地测试验证
  • 符合预期后,将当前镜像打上tag并推送到私有仓库

细节数据:

1
2
3
4
5
# Dockerfile示例
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD spring-boot-web.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

第三步:运行和测试

  • 运行镜像
  • 进行测试访问
  • 测试无问题后推送镜像到仓库

第四步:在Kubernetes集群中运行应用

  1. 定义控制器文件:
    • Deployment控制器
    • Service控制器
    • Ingress控制器等
  2. 控制器的主要内容和格式基本不变,需要变化的是:
    • 镜像地址和tag
    • 应用端口
    • 启动参数等
  3. 根据这些变量定义所需的控制器,并部署到Kubernetes集群中

控制器示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Deployment控制器示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: private-registry.com/myapp:v1
ports:
- containerPort: 8080
1
2
3
4
5
6
7
8
9
10
11
12
13
# Service控制器示例
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: NodePort
  1. 通过NodePort方式临时暴露端口,供研发小伙伴测试和访问

总结:
这些步骤看起来有些复杂,但只有第一次会这样。一旦标准化后,我们就可以通过CI/CD工具实现快速部署。借助Kubernetes可以提升系统的弹性和可扩展性,同时也会简化日常管理,降低运维成本。