2024年小编完成了k8s官方文档的阅读,记录了一些笔记。
如有不对的地方,可以评论指出。内容如下:
deployment
deployment更新后旧的Replicaset的pod为0了,但是旧的Replicaset还存在,为什么?
1.Deployment 资源管理的 ReplicaSet 具有版本控制的功能,旧的 ReplicaSet 保留是为了支持快速回滚操作。如果新版本的 ReplicaSet 部署出现问题,可以快速回滚到先前的版本。
2.保留多少个历史版本的配置是:dp中spec.revisionHistoryLimit 设置的数值,默认是10。
3.kubectl rollout history deployment dp的名字 # 查看dp历史有多个旧版本
如何回滚(回滚不改变副本的多少)
kubectl rollout history deployment dp的名字 # 查看dp历史有多个旧版本
kubectl rollout undo deployment dp的名字 #回滚上一个版本
kubectl rollout undo deployment dp的名字 --to-revision=2 #回滚到指定的版本
kubectl rollout history deployment dp的名字 --revision=2 #查看指定版本的具体配置
什么时候不能回滚deployment
1.你不可以回滚处于暂停状态的 Deployment,除非先恢复其执行状态。通过status或者describe看pause是不是为true。
2.没有历史版本的
StatefulSet
为什么删除StatefulSet前最好先缩容为0后再删除
1.它为 Pod 的创建和启动顺序提供保障,但在删除时,StatefulSet 本身并没有提供对 Pod 终止顺序的明确保证。这意味着,如果直接删除一个 StatefulSet,它的 Pod 可能会同时被终止,且没有保证它们会按照某个特定顺序来终止。
2.StatefulSet 提供有序的启动和缩容。当你将 StatefulSet 缩容到 0,Kubernetes 会按照 Pod 名字的逆序(从编号大的 Pod 开始)逐个删除,这种方式可以确保每个 Pod 都按顺序终止。
headless服务的主要作用
主要作用:(如果客户端要同时访问多个节点,这样可行。)
nslookup 服务名 直接返回的是一个数组,包含每个pod的ip
误解:
并不是为了实现给每个pod都能通过svc访问,例如: pod名字.svc名字
ClusterIP也能实现这个功能(通过svc访问pod:pod名字.svc名字 )(只有statefulset有此能力,dp的svc没这个能力)原因:当 StatefulSet 控制器创建 Pod 时, 它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签值设置为 Pod 名称。 这个标签允许你给 StatefulSet 中的特定 Pod 绑定一个 Service。
更新策略OnDelete有坑
.spec.updateStrategy有2个值可选:OnDelete和RollingUPdate(默认)
坑:
设置为 OnDelete 时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod,以此来对 StatefulSet 的 .spec.template 的变动作出反应。
设置为RollingUPdate时,将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。
statefulset的坑
在默认 Pod 管理策略(OrderedReady) 下使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。
如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如, 由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。
在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。 由于已知问题(https://github.com/kubernetes/kubernetes/issues/67250),StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。
恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样, StatefulSet 才会开始使用被还原的模板来重新创建 Pod。
DaemonSet
可以容忍的污点
DaemonSet 控制器会自动将一组容忍度添加到 DaemonSet Pod:
容忍度键名 | 效果 | 描述 |
---|---|---|
node.kubernetes.io/not-ready | NoExecute | DaemonSet Pod 可以被调度到不健康或还不准备接受 Pod 的节点上。在这些节点上运行的所有 DaemonSet Pod 将不会被驱逐。 |
node.kubernetes.io/unreachable | NoExecute | DaemonSet Pod 可以被调度到从节点控制器不可达的节点上。在这些节点上运行的所有 DaemonSet Pod 将不会被驱逐。 |
node.kubernetes.io/disk-pressure | NoSchedule | DaemonSet Pod 可以被调度到具有磁盘压力问题的节点上。 |
node.kubernetes.io/memory-pressure | NoSchedule | DaemonSet Pod 可以被调度到具有内存压力问题的节点上。 |
node.kubernetes.io/pid-pressure | NoSchedule | DaemonSet Pod 可以被调度到具有进程压力问题的节点上。 |
node.kubernetes.io/unschedulable | NoSchedule | DaemonSet Pod 可以被调度到不可调度的节点上。 |
node.kubernetes.io/network-unavailable | NoSchedule | 仅针对请求主机联网的 DaemonSet Pod 添加此容忍度,即 Pod 具有 spec.hostNetwork: true 。这些 DaemonSet Pod 可以被调度到网络不可用的节点上。 |
Job
job的selector通常不需要配置,及job的常用参数
完整job的配置举例
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
parallelism: 2
completions: 6
activeDeadlineSeconds: 100
ttlSecondsAfterFinished: 100
其中:
completions是完成pod的个数(默认是1)
parallelism是pod的并行运行数(默认是1)
backoffLimit是Job失败之前一个pod重试的次数(默认是6)
activeDeadlineSeconds意味着job从开始执行起最多运行100秒(无默认值,不设置不限制)
ttlSecondsAfterFinished在完成100秒后自动删除对象(设置为0结束后立即删除,不设置永远不删除)
job的注意事项
1.job创建后spec.template 字段不可变,无法被修改
2.job完成后,想重新执行此pod的办法有3个:
方法1:删除旧job,重新执行此job
方法2:用新的job的名字,重新执行
方法3:用cornjob
CronJob
关键参数讲解
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
startingDeadlineSeconds: 200
concurrencyPolicy: Replace
successfulJobsHistoryLimit: 5
failedJobsHistoryLimit: 5
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
解释:
startingDeadlineSeconds:计算从预期创建 Job 到当前时间的时间差。 如果时间差大于该限制,则跳过此次执行。(可选)
concurrencyPolicy:是否新旧job同时运行的,有3个选项:Allow(默认),Forbid,Replace
successfulJobsHistoryLimit:要保留多少成功完成的 Job(默认为3)
failedJobsHistoryLimit:要保留多少失败完成的 Job(默认为1)
cronjob的template允许修改
只会影响以后的job,当前运行的job不受影响。
crobjob踩坑-当cronjob错过了超过100次启动
当cronjob错过了超过100次启动,后续的job也可能不会启动了。报错:
Cannot determine if job needs to be started. Too many missed start time (> 100). Set or decrease .spec.startingDeadlineSeconds or check clock skew.
原因:
1.任务调度过期:如果你的 CronJob 在长时间内无法按计划执行,可能会出现超过 100 次错过的调度时间。Kubernetes 默认会阻止在这种情况下重新启动 Job。
2.时钟偏差:如果你的节点之间的系统时钟有较大的偏差,可能会导致 CronJob 的调度时间计算不准确。
3.startingDeadlineSeconds 设置不当:如果 startingDeadlineSeconds 的时间过短,导致 CronJob 任务没能在规定时间内启动,可能会频繁错过调度时间。
解决办法:
1.设置或调整(比如增大) .spec.startingDeadlineSeconds
2.修复时钟偏差
3.减少任务频率
4.手动执行一次cronjob
如何手动执行一次cronjob的job
kubectl create job --from=cronjob/my-cronjob my-manual-job
my-cronjob
:是你已经存在的 CronJob
名称。
my-manual-job
:是手动创建的新 Job
的名称,必须是新的名字,不能与已有的 Job 名称冲突。
pod
pod中容器的启动和关闭的顺序
1、pod中的多个常规容器,启动和关闭的顺序是随机的
2、init容器(含边车)先于常规容器启动,晚于常规容器结束。init容器(含边车)中有多个容器的时候,按顺序启动,按倒序结束。
3、init容器先于sidecar容器启动
怎么保证secret的安全(待验证)
给init容器设置有读取secret的权限,读取后放入emptydir。常规容器设置权限使得没有权限直接读取secret。
这样保证了secret的安全。
调试容器(临时容器)
https://kubernetes.io/zh-cn/docs/tasks/debug/debug-application/debug-running-pod/#ephemeral-container
kubectl debug 调试容器,命令为:
kubectl debug -it pod名字 --image=有终端的镜像 --target=容器名字 -n 命名空间
临时容器一旦添加到pod中就不能修改和删除
查看pod中是否有临时容器
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.ephemeralContainers}'
或者
kubectl get pod <pod-name> -n <namespace> -o yaml 在输出中找ephemeralContainer
QoS(服务质量)类的作用是什么
不同的pod有不同的分类,当node资源不够时要驱逐哪些pod。
最不可能被驱逐的pod(Guaranteed):pod中所有容器的cpu和内存都有limit和request,而且limit和request的值相等。
稍微不可能被驱逐的pod(Burstable):不满足Guaranteed,至少有一个容器有内存或cpu的limit或request。
最可能被驱逐的pod(BestEffort):不满足上面2个的,也就是所有容器都没cpu和内存的limit和request。
PriorityClass调度(驱逐)优先级
1.当 Kubernetes 集群中的资源不足时,具有PriorityClass更高优先级的 Pod 会优先被调度。
2.当集群资源不足,需要驱逐 Pod 时,PriorityClass优先级低的 Pod 会优先被驱逐
配置举例:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "This priority class is for high-priority pods."
value
: 定义了优先级的数值(从 -2,147,483,648 到 1,000,000,000(含)),越大优先级越高。(默认:0)
globalDefault
: 如果设置为 true
,则所有没有指定 PriorityClass
的 Pod 默认使用这个优先级。(没有默认的)
使用举例:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
priorityClassName: high-priority
containers:
- name: my-container
image: nginx
PriorityClass和pod中priority值的关系:
PriorityClass
和 Pod 的 priority
相关,但它们是不同的概念。简单来说,PriorityClass
定义了一个优先级类别,而 Pod 的 priority
则是从 PriorityClass
派生出来的实际优先级值。
使用PriorityClass的注意事项:
1.如果你升级一个已经存在的但尚未使用此特性的集群,该集群中已经存在的 Pod 的优先级等效于零。
2.添加一个将 globalDefault 设置为 true 的 PriorityClass 不会改变现有 Pod 的优先级。 此类 PriorityClass 的值仅用于添加 PriorityClass 后创建的 Pod。
3.如果你删除了某个 PriorityClass 对象,则使用被删除的 PriorityClass 名称的现有 Pod 保持不变, 但是你不能再创建使用已删除的 PriorityClass 名称的 Pod。
Pod的hostname和subdomain字段
hostname:
创建Pod时其主机名(从Pod内部观察)取自Pod的metadata.name值。然而,spec.hostname(可选)优先于metadata.name成为该 Pod 的主机名。pod的name始终是metadata.name。
subdomain:
就是一个命名空间中的svc地址还有一个点,不做详细说明,请参考:
https://kubernetes.io/zh-cn/docs/concepts/services-networking/dns-pod-service/#pod-s-hostname-and-subdomain-fields
DNS策略dnsPolicy
None: 忽略环境中的 DNS 设置。Pod 会使用其 dnsConfig 字段所提供的 DNS 设置。
Default: 从运行所在的节点继承域名解析配置。
ClusterFirst: 用集群的coredns (默认是此值,而不是Default)
ClusterFirstWithHostNet: 对于以 hostNetwork 方式运行的 Pod,应将其 DNS 策略显式设置为 "ClusterFirstWithHostNet",否则会采用Default的方式。
举例
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsPolicy: "None"
dnsConfig:
nameservers:
- 192.0.2.1 # 这是一个示例
searches:
- ns1.svc.cluster-domain.example
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
hostPort使用举例
只做端口映射,不共用主机的网络
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
containers:
- name: example-container
image: nginx
ports:
- containerPort: 80
hostPort: 8080 # 将容器的 80 端口映射到宿主机的 8080 端口
优雅关闭的理解(重点)
比如deployment更新镜像或者缩容时,都会把正在运行的pod给删除掉,那么会对业务有影响吗?
目前分析看,大概率不会(有点类似于nginx的优雅关闭)。原因如下:
1.新流量不会再来了
任何正在终止的 Pod 所对应的端点都不会立即从 EndpointSlice 中被删除,EndpointSlice API(以及传统的 Endpoints API)会公开一个状态来指示其处于 终止状态。 正在终止的端点始终将其 ready 状态设置为 false(为了向后兼容 1.26 之前的版本), 因此负载均衡器不会将其用于常规流量。
2.默认为优雅关闭,已到该pod的流量有处理的事件。如下参数有30秒的时间(也可以按需修改):
terminationGracePeriodSeconds: 30
3.会自动触发PreStop Hook来完成指定的操作。(非必须)
4.Kubernetes 将发送 SIGTERM 信号给 Pod 中的主进程,主进程要对此信号做相应的处理。
service
创建svc的时候会同时创建endpoints和endpointslice吗?
会。在 Kubernetes 1.21 及之后的版本中,EndpointSlice 成为默认的端点管理方式,但为了兼容性,一些场景下 Kubernetes 仍然会创建 Endpoints,同时也创建 EndpointSlice。这是为了保证与依赖旧版本 Endpoints 机制的组件或工具的兼容性。
endpoints最多可以支撑多少个pod端点
1000个。再多的话也是没有流量的。所以推荐用EndpointSlice
NodePort和LoadBalancer类型的svc有clusterIP吗
有。这2种svc是在ClusterIP类型svc基础上构建的。
LoadBalancer类型的svc是在NodePort类型的svc上构建的,所以其也有nodeport。
会话亲和性
如果你想确保来自特定客户端的连接每次都传递到同一个 Pod,你可以配置基于客户端 IP 地址的会话亲和性。
可以通过设置 Service 的 .spec.sessionAffinity 为 ClientIP 来设置基于客户端 IP 地址的会话亲和性(默认为 None)。
还可以通过设置 Service 的 .spec.sessionAffinityConfig.clientIP.timeoutSeconds 来设置最大会话粘性时间(默认值为 10800,即 3 小时)。(此参数windows不支持)
为什么k8s将 Service IP 范围中的第 10 个 IP 地址分配给 DNS 服务
约定成俗
k8s service internalTrafficPolicy和externalTrafficPolicy 的区别
两者都是有2个值,Local和Cluster,默认值都是Cluster
local是本node,绝不跨节点转发
cluster是集群所有node都可达
internalTrafficPolicy 用于指定内部流量的处理方式
externalTrafficPolicy 用于控制外部流量的处理。
Ingress
默认后端(非必须)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
kubernetes.io/ingress.class: "nginx" # 指定使用 NGINX Ingress 控制器
spec:
defaultBackend:
service:
name: default-service # 默认服务的名称
port:
number: 80 # 默认服务的端口
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-service # 匹配的服务名称
port:
number: 80
资源后端和svc后端(待补充)
资源后端用的较少,暂不写本节内容。
路径匹配pathType:Exact和Prefix的介绍
Exact:精确匹配 URL 路径,且区分大小写。
Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写, 并且对路径中各个元素逐个执行匹配操作。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。 如果每个 p 都是请求路径 p 的元素前缀,则请求与路径 p 匹配。
pathType举例
类型 | 路径 | 请求路径 | 匹配与否? |
---|---|---|---|
Prefix | / | (所有路径) | 是 |
Exact | /foo | /foo | 是 |
Exact | /foo | /bar | 否 |
Exact | /foo | /foo/ | 否 |
Exact | /foo/ | /foo | 否 |
Prefix | /foo | /foo , /foo/ | 是 |
Prefix | /foo/ | /foo , /foo/ | 是 |
Prefix | /aaa/bb | /aaa/bbb | 否 |
Prefix | /aaa/bbb | /aaa/bbb | 是 |
Prefix | /aaa/bbb/ | /aaa/bbb | 是,忽略尾部斜线 |
Prefix | /aaa/bbb | /aaa/bbb/ | 是,匹配尾部斜线 |
Prefix | /aaa/bbb | /aaa/bbb/ccc | 是,匹配子路径 |
Prefix | /aaa/bbb | /aaa/bbbxyz | 否,字符串前缀不匹配 |
Prefix | / , /aaa | /aaa/ccc | 是,匹配 /aaa 前缀 |
Prefix | / , /aaa , /aaa/bbb | /aaa/bbb | 是,匹配 /aaa/bbb 前缀 |
Prefix | / , /aaa , /aaa/bbb | /ccc | 是,匹配 / 前缀 |
Prefix | /aaa | /ccc | 否,使用默认后端 |
混合 | /foo (Prefix), /foo (Exact) | /foo | 是,优选 Exact 类型 |
主机名匹配举例
主机 | host 头部 | 匹配与否? |
---|---|---|
*.foo.com | bar.foo.com | 基于相同的后缀匹配 |
*.foo.com | baz.bar.foo.com | 不匹配,通配符仅覆盖了一个 DNS 标签 |
*.foo.com | foo.com | 不匹配,通配符仅覆盖了一个 DNS 标签 |
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wildcard-host
spec:
rules:
- host: "foo.bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 80
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: service2
port:
number: 80
默认 IngressClass
如果集群中有多个 IngressClass 被标记为默认,准入控制器将阻止创建新的未指定 ingressClassName 的 Ingress 对象。 解决这个问题需要确保集群中最多只能有一个 IngressClass 被标记为默认。
kubectl get ingressclass (-A)
自己部署的k8s 怎么让ingress 暴露在node节点的80端口
1.deployment用hostNetwork
2.nodeSelector选好对应的节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
spec:
template:
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/hostname: vm50
这样就可以用node的80和443端口和ingress来通信了,注意副本数量和nodeSelector。
怎么为集群部署多个ingress控制器
请进入recording\k8s\yaml-合集\two-ingress-together查看举例例子。
思路:
1.部署2个ingress控制器,并设置为不同的ingressclass类
kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 17d
nginx2 k8s.io/ingress-nginx2 <none> 6m49s
2.创建ingress的时候选择指定的ingressclass类
ConfigMap和Secret
1.ConfigMap/Secret 中保存的数据不可超过 1 MiB
不可变的ConfigMap/Secret (Kubernetes v1.21 [stable])
Kubernetes 特性 Immutable Secret 和 ConfigMap 提供了一种将各个 Secret 和 ConfigMap 设置为不可变更的选项。对于大量使用 ConfigMap 的集群 (至少有数万个各不相同的 ConfigMap 给 Pod 挂载)而言,禁止更改 ConfigMap 的数据有以下好处:
- 保护应用,使之免受意外(不想要的)更新所带来的负面影响。
- 通过大幅降低对 kube-apiserver 的压力提升集群性能, 这是因为系统会关闭对已标记为不可变更的 ConfigMap 的监视操作。
你可以通过将 immutable
字段设置为 true
创建不可变更的 ConfigMap。 例如:
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
...
immutable: true
一旦某 ConfigMap 被标记为不可变更,则 无法 逆转这一变化,,也无法更改 data
或 binaryData
字段的内容。你只能删除并重建 ConfigMap。 因为现有的 Pod 会维护一个已被删除的 ConfigMap 的挂载点,建议重新创建这些 Pods。
关于spec.data和spec.stringData和spec.binaryData
这三个都是可选字段
configmap的选项有:
spec.data : 明文存储普通的配置
spec.binaryData :适合存储非文本数据,比如图像、压缩文件等
secret的选项有:
spec.data: 键的值是base64编码的
spec.stringData: 键的值是明文
如果某个key同时出现data和stringdata,则stringdata优先级高
secret的重要点:
即便是用明文和stringData,查看secret的时候也是显示的是data和base64编码的
但是:
用stringData时,
metadata.annotations.kubectl.kubernetes.io/last-applied-configuration中的stringData值还是明文
用data时:
metadata.annotations.kubectl.kubernetes.io/last-applied-configuration中的data值还是base64
可选secret和configMap
当你在 Pod 中引用 Secret /configMap时,你可以将该 Secret /configMap标记为可选,就像下面例子中所展示的那样。 如果可选的 Secret 不存在,Kubernetes 将忽略它。
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
optional: true
配置可选时,如果创建pod是时候,对应的资源没创建出来。当该资源创建出来后,不会自动挂载到pod中。要想挂载需要重启pod。
以volume挂载secret和configMap时,值可以动态更新
经过下面测试,挂载后修改secret的值,pod中的值不是立马更新的,大概几分钟后完成了更新。
如下(注意secret要先创建,pod启动成功后再修改secret的值):
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
username: abc
password: bcd
---
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
command:
- /bin/sh
- -c
- sleep 36000
image: busybox
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
optional: true
限制指定命名空间secret的数量
更多ResourceQuota请访问:资源配额 | Kubernetes
apiVersion: v1
kind: ResourceQuota
metadata:
name: secret-quota
namespace: default
spec:
hard:
secrets: "1"
限制对Secret的访问
Kubernetes 提供了名为 kubernetes.io/enforce-mountable-secrets
的注解, 你可以添加到你的 ServiceAccount 中。当应用了这个注解后, ServiceAccount 的 Secret 只能挂载到特定类型的资源上,从而增强集群的安全性。
你可以使用以下清单将注解添加到一个 ServiceAccount 中:
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
kubernetes.io/enforce-mountable-secrets: "true"
name: my-serviceaccount
namespace: my-namespace
当此注解设置为 “true” 时,Kubernetes 控制平面确保来自该 ServiceAccount 的 Secret 受到特定挂载限制。
-
在 Pod 中作为卷挂载的每个 Secret 的名称必须列在该 Pod 中 ServiceAccount 的
secrets
字段中。 -
在 Pod 中使用
envFrom
引用的每个 Secret 的名称也必须列在该 Pod 中 ServiceAccount 的secrets
字段中。 -
在 Pod 中使用
imagePullSecrets
引用的每个 Secret 的名称也必须列在该 Pod 中 ServiceAccount 的secrets
字段中。
通过理解并执行这些限制,集群管理员可以维护更严格的安全配置,并确保 Secret 仅被适当的资源访问。
secret的良好实践记录
1.静态加密secret的数据
2.在searviceAccount中使用 kubernetes.io/enforce-mountable-secrets 注解来强制执行有关如何在 Pod 中使用 Secret 的特定规则。
3.限制对Secret的watch,list的访问,以及间接能看到secret权限的权限(比如:如果一个用户可以创建使用某 Secret 的 Pod,则该用户也可以看到该 Secret 的值。)
4.实现对特定事件发出警报的审计规则,例如同一用户并发读取多个 Secret 时发出警报
5.使用外部存储secret,比如应用的secret存储在nacos等,或搜索: Kubernetes Secret 存储 CSI
6.如果一个pod中有多个容器,可考虑通过挂载卷或环境变量的方式只把获取secret的权限给到需要获取secret的容器中。
7.自己的应用程序必须避免以明文记录 Secret 数据,还必须避免将这些数据传输给不受信任的一方。
调度
node只有亲和性,pod有亲和性和反亲和性
要使用 Pod 间亲和性,可以使用 Pod 规约中的 .affinity.podAffinity
字段。 对于 Pod 间反亲和性,可以使用 Pod 规约中的 .affinity.podAntiAffinity
字段。
Affinity比nodeSelector更强大
如果你同时指定了 nodeSelector
和 nodeAffinity
,两者必须都要满足, 才能将 Pod 调度到候选节点上。
Affinity可实现:
1.required*** 类似于 nodeSelector ,必须要满足此条件
2.preferred** 尽量满足此条件,即便不满足也能被调度
3.有权重的能力
4.能够定义规则允许哪些 Pod 可以被放置在一起。
例子1
- 节点必须包含一个键名为
topology.kubernetes.io/zone
的标签, 并且该标签的取值必须为antarctica-east1
或antarctica-west1
。 - 节点最好具有一个键名为
another-node-label-key
且取值为another-node-label-value
(权重1)或another-node-label-value1
(权重50)的标签。 - 你可以使用
operator
字段来为 Kubernetes 设置在解释规则时要使用的逻辑操作符。 你可以使用In
、NotIn
、Exists
、DoesNotExist
、Gt
和Lt
之一作为操作符。
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- antarctica-east1
- antarctica-west1
preferredDuringSchedulingIgnoredDuringExecution:
# preferred必须要设置权重,required没有此字段
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
- weight: 50
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value1
containers:
- name: with-node-affinity
image: registry.k8s.io/pause:2.0
#说明:单个 matchExpressions 字段中指定多个表达式, 则只有当所有表达式都满足(各表达式按逻辑与操作组合)时,Pod 才能被调度到节点上。
preferred支持权重(必须要设置)
示例如上一个小节的内容。
preferredDuringSchedulingIgnoredDuringExecution
亲和性类型的每个实例设置 weight
字段,其取值范围是 1 到 100。 当调度器找到能够满足 Pod 的其他调度请求的节点时,调度器会遍历节点满足的所有的偏好性规则, 并将对应表达式的 weight
值加和。
最终的加和值会添加到该节点的其他优先级函数的评分之上。 在调度器为 Pod 作出调度决定时,总分最高的节点的优先级也最高。
pod(反)亲和性(计算量大)
使用场景举例
亲和性: web和缓存2个应用 采用亲和性,调度到同一个node上
方法1:谁先部署都行(推荐)
因为涉及到谁先部署的问题,所以2个应用都应该设置亲和性,而且都用preferred。
方法2:2个应用启动顺序固定
后部署的应用配置require亲和性
示例:
以一个三节点的集群为例。你使用该集群运行一个带有内存缓存(例如 Redis)的 Web 应用程序。 在此例中,还假设 Web 应用程序和内存缓存之间的延迟应尽可能低。 你可以使用 Pod 间的亲和性和反亲和性来尽可能地将该 Web 服务器与缓存并置。
在下面的 Redis 缓存 Deployment 示例中,副本上设置了标签 app=store
。 podAntiAffinity
规则告诉调度器避免将多个带有 app=store
标签的副本部署到同一节点上。 因此,每个独立节点上会创建一个缓存实例。
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
下例的 Deployment 为 Web 服务器创建带有标签 app=web-store
的副本。 Pod 亲和性规则告诉调度器将每个副本放到存在标签为 app=store
的 Pod 的节点上。 Pod 反亲和性规则告诉调度器决不要在单个节点上放置多个 app=web-store
服务器。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.16-alpine
创建前面两个 Deployment 会产生如下的集群布局,每个 Web 服务器与一个缓存实例并置, 并分别运行在三个独立的节点上。
node-1 | node-2 | node-3 |
---|---|---|
webserver-1 | webserver-2 | webserver-3 |
cache-1 | cache-2 | cache-3 |
pod(反)亲和性中topologyKey
不允许为空
示例
apiVersion: v1
kind: Pod
metadata:
name: with-pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: topology.kubernetes.io/zone
containers:
- name: with-pod-affinity
image: registry.k8s.io/pause:2.0
示例yaml解读:
亲和性规则规定,只有节点属于特定的区域 且该区域中的其他 Pod 已打上 security=S1
标签时,调度器才可以将示例 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(称之为 “Zone V”)的集群,此区域由带有 topology.kubernetes.io/zone=V
标签的节点组成,那么只要 Zone V 内已经至少有一个 Pod 打了 security=S1
标签, 调度器就可以将此 Pod 调度到 Zone V 内的任何节点。相反,如果 Zone V 中没有带有 security=S1
标签的 Pod, 则调度器不会将示例 Pod 调度给该区域中的任何节点。
反亲和性规则规定,如果节点属于特定的区域 且该区域中的其他 Pod 已打上 security=S2
标签,则调度器应尝试避免将 Pod 调度到此节点上。 例如,如果我们有一个具有指定区域(我们称之为 “Zone R”)的集群,此区域由带有 topology.kubernetes.io/zone=R
标签的节点组成,只要 Zone R 内已经至少有一个 Pod 打了 security=S2
标签, 调度器应避免将 Pod 分配给 Zone R 内的任何节点。相反,如果 Zone R 中没有带有 security=S2
标签的 Pod, 则反亲和性规则不会影响将 Pod 调度到 Zone R。
除了 labelSelector
和 topologyKey
,你也可以指定 labelSelector
要匹配的名字空间列表,方法是在 labelSelector
和 topologyKey
所在层同一层次上设置 namespaces
。 如果 namespaces
被忽略或者为空,则默认为 Pod 亲和性/反亲和性的定义所在的名字空间。
你可以针对 Pod 间亲和性与反亲和性为其 operator
字段使用 In
、NotIn
、Exists
、 DoesNotExist
等值。
大集群不建议用pod(反)亲和性
Pod 间亲和性和反亲和性都需要相当的计算量,因此会在大规模集群中显著降低调度速度。 我们不建议在包含数百个节点的集群中使用这类设置。
Pod拓扑分布约束(重要)
拓扑分布约束(Topology Spread Constraints)实现了一组pod怎么在zone和hostname等等之间的平均分布,以及允许的最大不平衡偏差。
请详细阅读:https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/topology-spread-constraints/
重要
.spec.nodeName
的优先级高,慎用
pod中使用.spec.nodeName
可以把pod调度到指定的node上。
1.可以忽略标签,污点noscheduler等
2.和指定的node绑定了,很不好。
容忍所有污点(taints)要怎么配置
比如kube-proxy要用到
tolerations:
- operator: Exists
原理
tolerations
的结构
一个 toleration
通常由以下几个字段组成:
key
:taint
的键operator
: 匹配规则,通常是Equal
或Exists
value
: 与key
配对的值(仅当operator: Equal
时有意义)effect
:taint
的效果,可能是NoSchedule
,PreferNoSchedule
, 或NoExecute
operator: Exists
的意思
当 operator
设置为 Exists
时,意味着这个 toleration
可以容忍任何具有指定 effect
的 taints
,而无需指定特定的 key
和 value
。简单来说,它表明 Pod 容忍所有存在的 taints
,不论它们的 key
和 value
是什么。
调度器调优-调整节点打分的百分比(大集群)
设置百分比,比如只给30%的node打分,来减少调度器的压力。
参数为:percentageOfNodesToScore
此参数只适合在大集群中调整或设置
1.少于100个node时不用考虑此内容,因为硬编码到代码中了,会给所有node打分
2.几百个node时也不用太考虑这个调优,效果不明显
3.有默认值,不同的node数量,默认值是不同的。
4.会依次遍历所有的node
官方链接:https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/scheduler-perf-tuning/
ServiceAccount
每个服务账号都与一个 Kubernetes 名字空间绑定。
RBAC
良好实践
最小特权1:
RoleBinding 有命名空间的概念,应优先使用。
ClusterRoleBinding 没有命名空间的概念,尽量不使用。
role和clusterRole也类似
最小特权2:
管理员不应使用 cluster-admin 账号,除非特别需要。
最小特权3:
避免将用户添加到 system:masters 组。任何属于此组成员的用户都会绕过所有 RBAC 权限检查, 始终具有不受限制的超级用户访问权限,并且不能通过删除 RoleBinding 或 ClusterRoleBinding 来取消其权限。顺便说一句,如果集群使用 Webhook 鉴权,此组的成员身份也会绕过该 Webhook(来自属于该组成员的用户的请求永远不会发送到 Webhook)。
最小特权4:获取secret的权限要注意。
若允许对 Secrets 执行 get 访问,用户就获得了访问 Secret 内容的能力。 同样需要注意的是:list 和 watch 访问也会授权用户获取 Secret 的内容。例如,当返回 List 响应时(例如,通过 kubectl get secrets -A -o yaml),响应包含所有 Secret 的内容。
最小特权5:创建工作负载带来的风险
创建工作负载(Pod 或管理 Pod 的工作负载资源) 的权限隐式地授予了对该命名空间中许多其他资源的访问权限,例如可以挂载在 Pod 中的 Secret、ConfigMap 和 PersistentVolume。 此外,由于 Pod 可以被任何服务账号运行, 因此授予创建工作负载的权限也会隐式地授予该命名空间中任何服务账号的 API 访问级别。
可以运行特权 Pod 的用户可以利用该访问权限获得节点访问权限, 并可能进一步提升他们的特权。如果你不完全信任某用户或其他主体, 不相信他们能够创建比较安全且相互隔离的 Pod,你应该强制实施 Baseline 或 Restricted Pod 安全标准。
最小特权6:创建持久卷PersistentVolume带来的风险
允许某人或某个应用创建任意的 PersistentVolume,则这种访问权限包括创建 hostPath 卷, 这意味着 Pod 将可以访问对应节点上的下层主机文件系统。授予该能力会带来安全风险。
你应该只允许以下实体具有创建 PersistentVolume 对象的访问权限:
1.需要此访问权限才能工作的用户(集群操作员)以及你信任的人,
2.Kubernetes 控制平面组件,这些组件基于已配置为自动制备的 PersistentVolumeClaim 创建 PersistentVolume。 这通常由 Kubernetes 提供商或操作员在安装 CSI 驱动程序时进行设置。
(重点)在需要访问持久存储的地方,受信任的管理员应创建 PersistentVolume,而受约束的用户应使用 PersistentVolumeClaim 来访问该存储。
命名空间
什么对象在命名空间中,什么对象不在命名空间中?
# 方法一:执行下面的命令,查看所有对象,有一列有此说明
kubectl api-resources
# 方法二:
# 位于名字空间中的资源
kubectl api-resources --namespaced=true
# 不在名字空间中的资源
kubectl api-resources --namespaced=false
kubectl
1.执行前先验证yaml的语法是否正确
从 Kubernetes v1.25 开始,API 服务器提供了服务器端字段验证, 可以检测对象中未被识别或重复的字段。它在服务器端提供了 kubectl --validate
的所有功能。--validate
标志来设置字段验证级别。它接受值 ignore
、warn
和 strict
,同时还接受值 true
(等同于 strict
)和 false
(等同于 ignore
)。
kubectl apply/create/replace --validate='strict' -f *.yaml
监控
kubectl top 查看node和pod是资源使用情况
kubectl top node/pod 对应的实例
展示的结果是资源的使用情况,而不是资源的限制水位
资源限制(LimitRange)和配额(ResourceQuota)
用法示例
LimitRange从字面意义上来看就是对范围进行限制,实际上是对cpu和内存资源使用范围的限制
前面我们讲到过资源配额,资源配额是对整个名称空间的资源的总限制,是从整体上来限制的,而LimitRange则是对pod和container级别来做限制的。
LimitRange示例:
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
namespace: example
spec:
limits:
- default: # default limit
memory: 512Mi
cpu: 2
defaultRequest: # default request
memory: 256Mi
cpu: 500m
max: # max limit
memory: 800Mi
cpu: 3
min: # min request
memory: 100Mi
cpu: 0.3
maxLimitRequestRatio: # max value for limit / request
memory: 2
cpu: 2
type: Container # limit type, support: Container / Pod / PersistentVolumeClaim
ResourceQuota示例
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
namespace: example
spec:
hard: # 只有hard没有soft
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
requests.nvidia.com/gpu: 4
注意事项
1.如果命名空间下的计算资源(如 cpu 和 memory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 提示: 可使用 LimitRanger 准入控制器来为没有设置计算资源需求的 Pod 设置默认值。
2.不管是资源竞争还是配额的修改,都不会影响已经创建的资源使用对象
3.设置LimitRange时,如果没有设置default,default的值会根据max和min来自动生成。
4.我测试默认ResourceQuota功能是默认启用的,在其它环境如果没有启用此功能,可以检查是不是k8s这个功能没有开启。
etcd
1.如何将事件对象存储在单独的专用 etcd 实例中?
答案(未验证):以下是实现步骤的概述:
- 部署单独的 etcd 集群
首先,你需要部署一个新的 etcd
集群,用于专门存储事件数据。这可以使用静态 Pods、Deployment 或者独立部署的方式来创建。
- 配置 Kubernetes API Server 使用外部 etcd
修改 Kubernetes API Server 的启动参数,将事件的存储目标配置为新的 etcd
集群。
在 API Server 的启动配置中,添加以下参数:
bash
复制代码
--etcd-servers-overrides=/events#<your-etcd-events-endpoint>
<your-etcd-events-endpoint>
需要替换为你新部署的 etcd 集群的地址。
这个参数告诉 API Server 将所有的事件数据发送到指定的 etcd 实例,而不是默认的 etcd 集群。API Server 仍然会将其他 Kubernetes 数据存储在主 etcd 集群中。
- 验证
- 确保 API Server 已成功连接到新的
etcd
实例并将事件数据存储在那里。 - 通过
etcdctl
或其他工具查看新 etcd 集群中的数据,确保事件数据被写入。
这样,你就能够将事件数据与其他 Kubernetes 数据分离,减少对主 etcd 的压力并提高整体性能。
安全
怎么避免在pod产生-派生炸弹(Fork Bomb)?
通过修改kubelet的配置,限制每个pod能用的pid数量即可解决此问题。
kubelet 可以通过设置PodPidsLimit
参数来限制每个容器内的进程数量。
本来设置的是30但是有的服务起不来,索性改为500
1.【kubelet节点】 /var/lib/kubelet/config.yaml文件中添加如下的内容
# 500仅仅是举例
podPidsLimit: 500
2.【kubelet节点】容器kubelet:systemctl restart kubelet
3.【kubectl节点】开启kubectl的proxy: kubectl proxy
4.【kubectl节点】执行下面命令,验证结果:
# 值从-1(无限制),修改为了500
curl -X GET http://127.0.0.1:8001/api/v1/nodes/<node-name>/proxy/configz | jq . | grep -i pid
开启apiserver的优先级和公平性
原文:
https://kubernetes.io/zh-cn/docs/concepts/cluster-administration/flow-control/
1.递归场景禁用优先级和公平性,要不然调用顺序乱了。
小知识点
标签知识汇总
说明:没有or选项,比如选择标签是a或者是b的pod,这个干不了
# 等值
environment = production # 等于
environment == production # 等于
tier != frontend # 不等于
# 集合
environment in (production, qa)
tier notin (frontend, backend)
partition # 不校验值
!partition # 不校验值
# 命令中的用法
kubectl get pods -l environment=production,tier=frontend
kubectl get pods -l 'environment in (production),tier in (frontend,qa)'
kubectl get pods -l 'environment,environment notin (frontend)'
# yaml中的用法
selector:
matchLabels: # 必须要value
component: redis
matchExpressions:
- { key: tier, operator: In, values: [cache] }
- { key: environment, operator: NotIn, values: [dev] }
- { key: app, operator: Exists} # key存在即可
- { key: app1, operator: DoesNotExist} # key不存在即可
# -L 的用法,输出新增了一列
kubectl get pod -L 'app'
输出结果中新增了一列app,如下:
NAME READY STATUS RESTARTS AGE APP
my-nginx-898d47487-4n8nc 1/1 Running 1 (14h ago) 2d10h nginx
推荐使用的标签
参考:https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/common-labels/
键 | 描述 | 示例 | 类型 |
---|---|---|---|
app.kubernetes.io/name | 应用程序的名称 | mysql | 字符串 |
app.kubernetes.io/instance | 用于唯一确定应用实例的名称 | mysql-abcxyz | 字符串 |
app.kubernetes.io/version | 应用程序的当前版本(例如语义版本 1.0、修订版哈希等) | 5.7.21 | 字符串 |
app.kubernetes.io/component | 架构中的组件 | database | 字符串 |
app.kubernetes.io/part-of | 此级别的更高级别应用程序的名称 | wordpress | 字符串 |
app.kubernetes.io/managed-by | 用于管理应用程序的工具 | Helm | 字符串 |
# 这是一段节选
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app.kubernetes.io/name: mysql
app.kubernetes.io/instance: mysql-abcxyz
app.kubernetes.io/version: "5.7.21"
app.kubernetes.io/component: database
app.kubernetes.io/part-of: wordpress
app.kubernetes.io/managed-by: Helm
有些资源的selector只能用matchLabels而不能用matchExpressions
这些资源通常包括以下几种:
ReplicationController 的 selector 仅支持 matchLabels,这是较旧的 Kubernetes 资源类型。
Service 的 selector 只支持 matchLabels。你无法使用 matchExpressions 进行更复杂的选择,只能通过标签精确匹配。
NetworkPolicy 的 podSelector 和 namespaceSelector 也只能使用 matchLabels。
PVC 的 selector 用于绑定特定的 PersistentVolume (PV),在 PV 的 labelSelector 中,只能使用 matchLabels。
原因有一下几点:
1. 历史原因
一些较早引入的 Kubernetes 资源(如 ReplicationController 和 Service)是在 matchLabels 概念存在之前设计的。
2. 性能优化
matchLabels 基于精确匹配,查询相对简单且性能高。对于像 Service或NetworkPolicy这种资源,通常要求实时且高效地根据标签来选择目标。
字段选择器field-selector
根据对象yaml中指定字段来筛选或选择对象,比如:
kubectl get pods --field-selector status.phase=Running # =或== 等于
kubectl get pods --field-selector status.phase!=Running # != 不等于
# 下面是与,将筛选 status.phase 字段不等于 Running 同时 spec.restartPolicy 字段等于Always的Pod
kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always
# 一次筛选多种资源,比如下面:
kubectl get statefulsets,services -A --field-selector metadata.namespace!=default
基于集合的in,notin,exists不能用在字段选择器中
每个对象支持的可选择的字段有限,如下
https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/field-selectors/#%E6%94%AF%E6%8C%81%E5%AD%97%E6%AE%B5%E5%88%97%E8%A1%A8
类别 | 字段 |
---|---|
Pod | spec.nodeName spec.restartPolicy spec.schedulerName spec.serviceAccountName spec.hostNetwork status.phase status.podIP status.nominatedNodeName |
Event | involvedObject.kind involvedObject.namespace involvedObject.name involvedObject.uid involvedObject.apiVersion involvedObject.resourceVersion involvedObject.fieldPath reason reportingComponent source type |
Secret | type |
Namespace | status.phase |
ReplicaSet | status.replicas |
ReplicationController | status.replicas |
Job | status.successful |
Node | spec.unschedulable |
CertificateSigningRequest | spec.signerName |
怎么看Controller Manager 和 Scheduler 谁是leader
只有leader在工作,其它的备用。为了高可用和不脑裂,建议部署3个节点即可,多了没用。
建议适当提高pod的资源限制,来提升工作效率
跨城市的集群,流量是要不停的跨城市的
这2个资源都有leader,最简单的方法是通过lease查看,如下(HOLDER列有主机名):
也可以通过etcd查看(我还不会)
$ kubectl get lease -n kube-system
NAME HOLDER
apiserver-dcy6x33qrqgh25c53e5ogdmrqi apiserver-dcy6x33qrqgh**
apiserver-hus2hf7lxutirnqy6oeuhexr5m apiserver-hus2hf7lxuti**
apiserver-vltgz5wal2ttthyljr4rbjkd3u apiserver-vltgz5wal2tt**
kube-controller-manager vm38_4c7e4afe-eef8-466**
kube-scheduler vm38_2868bc3c-45bc-411**
为什么apiserver 不需要leader
所以apiserver多部署几个是没问题的,通常建议2-5个
每个apiserver节点会把自己发布到租约中
具体原因:
1. 无状态架构 (Stateless Architecture):
- Kubernetes API Server 是无状态的,它不需要维护复杂的状态或进行调度任务等。因此,可以同时运行多个 API Server 副本,它们通过一致的后端(例如 etcd 数据存储)处理集群中的请求。所有 API Server 实例都可以直接从 etcd 读取和写入数据,保证数据的一致性。
2. 负载均衡 (Load Balancing):
- API Server 的多副本可以通过负载均衡器来分发请求,每个副本独立处理 API 请求。这种设计使得它们能够同时处理多个客户端请求,无需 leader 进行协调。
3. 一致性通过 etcd 保证:
- API Server 不直接管理复杂的状态,而是将所有持久化的状态保存在 etcd 中。etcd 本身是一个分布式系统,使用强一致性协议(Raft)来处理写入请求并保证数据一致性。API Server 通过调用 etcd,保证了数据的一致性,不需要额外的 leader 选举来协调 API Server 副本之间的操作。
4. 高可用性和容错:
- 多个 API Server 副本可以同时存在且相互独立,任何一个副本的故障不会影响集群的操作。其他副本会继续处理请求,这种无状态设计天然支持高可用性,且不需要进行 leader 选举来管理副本间的工作分配。
总结来说,Kubernetes API Server 通过负载均衡和无状态设计处理请求,数据一致性由 etcd 保证,因此不需要像 Controller Manager 和 Scheduler 那样的 leader 选举机制。
默认的镜像拉取策略
如果你省略了 imagePullPolicy 字段,并且你为容器镜像指定了摘要, 那么 imagePullPolicy 会自动设置为 IfNotPresent。
如果你省略了 imagePullPolicy 字段,并且容器镜像的标签是 :latest, imagePullPolicy 会自动设置为 Always。
如果你省略了 imagePullPolicy 字段,并且没有指定容器镜像的标签, imagePullPolicy 会自动设置为 Always。
如果你省略了 imagePullPolicy 字段,并且为容器镜像指定了非 :latest 的标签, imagePullPolicy 就会自动设置为 IfNotPresent。
重要
容器的 imagePullPolicy
的值总是在对象初次创建时设置的, 如果后来镜像的标签或摘要发生变化,则不会更新。
例如,如果你用一个非 :latest
的镜像标签创建一个 Deployment, 并在随后更新该 Deployment 的镜像标签为 :latest
,则 imagePullPolicy
字段不会变成 Always
。 你必须手动更改已经创建的资源的拉取策略。
什么是容器运行时类(Runtime Class)?
就是不同的运行时定义不同的类,创建deployment等资源的时候指定对应的运行时类。可以用节点标签实现类似效果。
容器的PostStart和PreStop的注意事项
如果 PostStart 或 PreStop 回调失败,它会杀死容器。应该尽量轻量。
如果 PostStart 回调程序执行时间过长或挂起,它可能会阻止容器进入 running 状态。这是因为 PostStart 是 Kubernetes 中的一个容器生命周期钩子,容器在 PostStart 钩子完成之前不会被标记为 Running。
回调处理程序的日志不会在 Pod 事件中公开。 如果处理程序由于某种原因失败,它将播放一个事件。 对于 PostStart,这是 FailedPostStartHook 事件,对于 PreStop,这是 FailedPreStopHook 事件。
PostStart:不能保证回调会在容器入口点(ENTRYPOINT)之前执行,两者理论上是同时触发的。
PreStop:终止宽限周期在 PreStop 回调被执行之前即开始计数, 所以无论回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。
关于restartPolicy
Deployment,StatefulSet,DaemonSet和ReplicaSet的这个参数只能被设置为Always
job和cronjob 的这个参数只能被设置为 OnFailure或Never
pod(独立的)这个参数可以被设置为Always或OnFailure或Never
各种资源创建后选择运算符的部分不能修改
选择运算符举例:
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
比如:deployment,daemonset等待,这些资源创建后,spec.selector部分则不能修改。如果需要修改,请删除资源重新创建。
需要修改deployment,statefulset等不可变参数该怎么办
先删除,再新建。如果怕pod被删除,可以通过加参数不删除级联的pod,deployment的举例:
kubectl delete deployment my-deployment --cascade=orphan
然后马上新建新的deployment,接管上面未删除的pod。pod的标签也可以做适当的修改。
--cascade
参数有多个选项,分别控制不同的删除行为:
--cascade=true
(默认):删除控制器时会级联删除它管理的所有子资源(如 Pod)。--cascade=false
:只删除控制器,不会立即删除子资源,但这些资源最终会被控制器垃圾回收器删除。--cascade=orphan
:删除控制器,但保留子资源(例如 Pod),子资源变为“孤儿”。
怎么给kubelet配置https证书
怀疑(不对):apiserver访问kubelet默认走的是http(可能)而不是https。
验证结果:默认已经开启了https和权限认证
用kubeadm安装的k8s集群默认已经开启了https,验证方法:
1.在node上执行 curl http://127.0.0.1:10250/pods 会提示是https
2.在node上执行 curl -k https://127.0.0.1:10250/pods 会提示Unauthorized
3.cat /var/lib/kubelet/config.yaml 能看到不允许匿名访问,webhook也是开启的
避坑指南
名称是同一资源中必须是唯一的,比如node
如果在 Node 对象未被删除并重建的条件下,重新创建了同名的物理主机, 则 Kubernetes 会将新的主机看作是老的主机,这可能会带来某种不一致性。
命名空间的名字不能起什么
1.避免使用前缀 kube- 创建名字空间,因为它是为 Kubernetes 系统名字空间保留的。
2.避免和公共顶级域名重复。清单:https://data.iana.org/TLD/tlds-alpha-by-domain.txt