404频道

学习笔记

pod状态

1. 前置检查

在排查异常状态的pod错误之前,可以先检查一下node状态,执行kubectl get node查看是否所有的node状态都正常。

2. pod状态为CrashLoopBackOff

如果pod状态为CrashLoopBackOff状态,查看pod日志来定位原因,或者describe pod看一下。

3. pod状态为Pending

pod状态为Pending状态,说明调度失败,通常跟污点、标签、cpu、内存、磁盘等资源相关。

可以通过kubectl describe pod -n {NAMESPACE} {POD_NAME},可以在最后的Event部分找到原因。

4. pod状态为Init:0/1

有些pod会有Init Containers,这些container是在pod的containers执行之前先执行。如果Init Container出现未执行完成的情况,此时pod处于Init状态。

通过kubectl get pod -n {NAMESPACE} {POD_NAME} -o yaml 找到pod的Init Containers,并找到其中的name字段。执行kubectl logs -n {NAMESPACE} {POD_NAME} -c {INIT_CONTAINER_NAME}可以查看Init Container的日志来分析原因。

5. pod状态为Terminating

pod处于此种状态的原因大致可分为:
1、pod或其控制器被删除。
解决方法:查看pod控制器类型和控制器名称,查看其控制器是否正常。如果正常pod将会被重建,如果pod没有被重建,查看controller-manager是否正常。
2、pod所在节点状态NotReady导致。
解决方法:检查该节点的网络,cpu,内存,磁盘空间等资源是否正常。检查该节点的kubelet、docker服务是否正常。检查该节点的网络插件pod是否正常。

最常见的pod处于Terminating状态的解决办法为强制删除 kubectl delete pods -n ${namespace} ${name} --grace-period=0 --force

6. pod状态为Evicted

pod处于Evicted的原因大致可分为:
1、kubelet服务启动时存在驱逐限制当节点资源可用量达到指定阈值(magefs.available<15%,memory.available<300Mi,nodefs.available<10%,nodefs.inodesFree<5%)
会优先驱逐Qos级别低的pod以保障Qos级别高的pod可用。
解决方法:增加节点资源或将被驱逐的pod迁移到其他空闲资源充足的节点上。
2、pod所在节点上被打上了NoExecute的污点,此种污点会将该节点上无法容忍此污点的pod进行驱逐。
解决方法:查看该节点上的NoExecute污点是否必要。或者pod是否可以迁移到其他节点。
3、pod所在的节点为NotReady状态

通常可以通过kubectl describe pods ${pod} -n ${namespace}的底部的Events信息来找到一些问题的原因。例如下面例子中可以看到DiskPressure信息,说明跟磁盘相关。

1
2
3
4
5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Evicted 61s kubelet, acs.deploy The node had condition: [DiskPressure].
Normal Scheduled <invalid> default-scheduler Successfully assigned ark-system/bridge-console-bridge-console-554d57bb87-nh2vd to acs.deploy

或者根据pod的Message字段来找到原因

1
2
3
4
5
6
7
8
9
10
11
12
Name:           tiller-deploy-7f6456894f-22vgr
Namespace: kube-system
Priority: 0
Node: a36e04001.cloud.e04.amtest17/
Start Time: Mon, 08 Jun 2020 12:17:26 +0800
Labels: app=helm
name=tiller
pod-template-hash=7f6456894f
Annotations: <none>
Status: Failed
Reason: Evicted
Message: Pod The node had condition: [DiskPressure].

由于pod驱逐的原因排查跟时间点相关,需要根据pod被驱逐的时间来分析当时的状态。
4、批量删除状态Evicted的pod,此操作会删除集群里面所有状态为Evicted的pod

1
2
ns=`kubectl get ns | awk 'NR>1 {print $1}'`
for i in $ns;do kubectl get pods -n $i | grep Evicted| awk '{print $1}' | xargs kubectl delete pods -n $i ;done

7. pod状态为Unknown

通常该状态为pod对应节点的为NotReady,通过查看 kubectl get node 来查看node是否为NotReady。

8. pod为running,但是Not Ready状态

1
argo-ui-56f4d67b69-8gshr   0/1     Running     0          10h

类似上面这种状态,此时说明pod的readiness健康检查没过导致的,需要先从pod的健康检查本身来排查问题。可以通过 kubectl get pods -n ${namespace} ${name} -o yaml 找到pod的健康检查部分,关键字为readiness,然后进入pod中执行对应的健康检查命令来测试健康检查的准确性。

例如readiness的配置如下,需要进入pod中执行curl http://127.0.0.1/api/status,也可以在pod对应的node节点上执行curl [http://${pod_ip}/api/status](http://127.0.0.1/api/status)

1
2
3
4
5
6
7
8
9
10
livenessProbe:
failureThreshold: 3
httpGet:
path: /api/status
port: http
scheme: HTTP
initialDelaySeconds: 120
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1

9. pod为ContainerCreating状态

通过kubectl describe pods -n ${namespace} ${name}的Events部分来分析原因,可能的原因如下:

  • 网络分配ip地址失败

10. Init:CrashLoopBackOff

  1. 通过kubectl get pod -n {NAMESPACE} {POD_NAME} -o yaml 找到pod的Init Containers,并找到其中的name字段。
  2. 执行kubectl logs -n {NAMESPACE} {POD_NAME} -c {INIT_CONTAINER_NAME}可以查看Init Container的日志来分析原因。

11. PodInitializing

需要查看initContainer的日志

12. MatchNodeSelector

查看pod的status信息可以看到如下信息:

1
2
3
4
5
status:
message: Pod Predicate MatchNodeSelector failed
phase: Failed
reason: MatchNodeSelector
startTime: "2022-03-15T05:07:57Z"

说明该pod没有调度成功,在predicate的MatchNodeSelector阶段失败了,没有匹配上node节点。

在k8s 1.21之前的版本,存在bug,节点重启后可能遇到过问题,将pod delete后重新调度可以解决。https://github.com/kubernetes/kubernetes/issues/92067

参考

排查k8s上问题通常需要监控的配合,而k8s上的监控标准为prometheus,prometheus的dashboard最通用的为grafana。本文用来记录排查k8s问题时经常遇到的dashboard,dashboard监控的数据来源包括node-exporter、metrics-server、kube-state-metrics等最场景。

node top监控

下载链接

node上的pod监控

下载链接

正向代理通常用在远程访问某个环境中的。常见的正向代理工具包括squid、nginx、3proxy。

squid

老牌的正向代理工具。

安装:yum install squid && systemctl start squid

squid默认会监听在3128端口号。

缺点:如果修改了本地的/etc/hosts文件,则需要重启squid后才可以更新。

3proxy

官方并没有提供yum的安装方式,比较简单的运行方式是以docker的形式。

执行如下的命令,即可开启3128端口作为http代理,3129端口作为sock5代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mkdir /etc/3proxy
cat > /etc/3proxy/3proxy.cfg <<EOF
log /var/log/3proxy.log D
logformat "- +_L%t.%. %N.%p %E %U %C:%c %R:%r %O %I %h %T"
rotate 7
auth none
flush
allow somepu
maxconn 200

# starting HTTP proxy with disabled NTLM auth ( -n )
proxy -p3128 -n

# starting SOCKS proxy
socks -p3129 -n
EOF
docker run -d --restart=always -p 3128:3128 -p 3129:3129 --net=host -v /var/log:/var/log -v /etc/3proxy/3proxy.cfg:/etc/3proxy/3proxy.cfg --name 3proxy 3proxy/3proxy

设置代理

终端设置代理

shell支持如下的代理环境变量:

1
2
export http_proxy=http://localhost:1080
export https_proxy=http://localhost:1080

如果是 socks5 代理同样可以使用上述两个环境变量:

1
2
export http_proxy=socks5://localhost:1080
export https_proxy=socks5://localhost:1080

相关链接

ssh命令常用参数

指定私钥连接:ssh -i ~/.ssh/id_rsa ido@192.168.1.111 -p 7744

免密登录

用来两台主机之间的ssh免密操作,步骤比较简单,主要实现如下两个操作:

  1. 生成公钥和私钥
  2. 将公钥copy到要免密登录的服务器

生成公钥和私钥

执行 ssh-keygen -b 4096 -t rsa 即可在 ~/.ssh/目录下生成两个文件id_rsa和id_rsa.pub,其中id_rsa为私钥文件,id_rsa.pub为公钥文件。

将公钥copy到要免密登录的服务器

执行 ssh-copy-id $user@$ip 即可将本地的公钥文件放到放到要免密登录服务器的 $HOME/.ssh/authorized_keys 文件中。至此,免密登录的配置就完成了。

ssh隧道

动态端口转发:执行 ssh root@xxxx -ND 127.0.0.1:1080 即可在本机的1080端口开启一个ssh隧道。

rsync

rsync的常用命令:

1
rsync -avzP --delete $local_idr  $user@$remote:$remote_dir

OpenKruise是阿里云开源的一系列基于k8s的扩展组件的集合,其中包含了像增强版的workload、sidecar容器管理、高可用性防护等特性,包含了很多的“黑科技”。

如果k8s的kube-controller-manager组件可以提供非常强的扩展能力,可以实现自定义的Deployment、StatefulSet的controller,而不是使用原生的kube-controller-manager的功能,类似于实现自定义的调度器扩展功能。那么很有可能OpenKruise的实现方案就不再会采用CRD扩展的方式,而是直接在原生的Deployment、StatefulSet等对象上通过annotation的方式来实现。

安装

可以直接使用helm的方式安装

1
2
helm repo add openkruise https://openkruise.github.io/charts/
helm install kruise openkruise/kruise --version 1.0.1

安装完成后,可以看到在kruise-system下创建了一个DeamonSet和一个Deployment。并且安装了很多的CRD和webhook组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ kubectl  get pod -n kruise-system
NAME READY STATUS RESTARTS AGE
kruise-controller-manager-67878b65d-cv6f4 1/1 Running 0 92s
kruise-controller-manager-67878b65d-jrmnd 1/1 Running 0 92s
kruise-daemon-ktwvv 1/1 Running 0 92s
kruise-daemon-nf84r 1/1 Running 0 92s
kruise-daemon-rjs26 1/1 Running 0 92s
kruise-daemon-vghw4 1/1 Running 0 92s

$ kubectl get crd | grep kruise.io
advancedcronjobs.apps.kruise.io 2022-03-05T13:21:39Z
broadcastjobs.apps.kruise.io 2022-03-05T13:21:39Z
clonesets.apps.kruise.io 2022-03-05T13:21:39Z
containerrecreaterequests.apps.kruise.io 2022-03-05T13:21:39Z
daemonsets.apps.kruise.io 2022-03-05T13:21:39Z
imagepulljobs.apps.kruise.io 2022-03-05T13:21:39Z
nodeimages.apps.kruise.io 2022-03-05T13:21:39Z
podunavailablebudgets.policy.kruise.io 2022-03-05T13:21:39Z
resourcedistributions.apps.kruise.io 2022-03-05T13:21:39Z
sidecarsets.apps.kruise.io 2022-03-05T13:21:39Z
statefulsets.apps.kruise.io 2022-03-05T13:21:39Z
uniteddeployments.apps.kruise.io 2022-03-05T13:21:39Z
workloadspreads.apps.kruise.io 2022-03-05T13:21:39Z

$ kubectl get validatingwebhookconfigurations kruise-validating-webhook-configuration
NAME WEBHOOKS AGE
kruise-validating-webhook-configuration 17 17m
$ kubectl get mutatingwebhookconfigurations kruise-mutating-webhook-configuration
NAME WEBHOOKS AGE
kruise-mutating-webhook-configuration 11 17m

功能

大类 子类 描述
通用工作负载 CloneSet 定位是用来代替k8s的Deployment,但做了很多能力的增强。增强的功能点:
1. 支持声明pvc,给pod来申请pv。当pod销毁后,pvc会同步销毁。
2. 指定pod来进行缩容。
3. 流式扩容,可以指定扩容的步长等更高级的库容特性。
4. 分批灰度。
5. 通过partition回滚。
6. 控制pod的升级顺序。
7. 发布暂停。
8. 原地升级自动镜像预热。
9. 生命周期钩子。pod的多个声明周期之间可以读取finalizer,如果finalizer中有指定的值,则controller会停止。该行为作为k8s的一种hook方式,用户可以自定义controller来控制finalizer的行为。
通用工作负载 Advanced StatefulSet 用来取代k8s原生的StatefulSet,很多增强特性跟CloneSet比较类似。
1. 原地升级。
2. 升级顺序增强。
3. 发布暂停。
4. 原地升级自动预热。
5. 序号跳过。StatefulSet创建的pod的后缀会从0开始依次累加,可以指定某个特定的序号跳过。
6. 流式扩容。
通用工作负载 Advanced DaemonSet 用来取代k8s原生的DaemonSet。1. 热升级
2. 暂停升级
任务工作负载 BroadcastJob agent类型的job,每个节点上都会执行
任务工作负载 AdvancedCronJob 原生的CronJob的扩展版本,可以周期性创建BroadcastJob。
Sidecar容器管理 SidecarSet 用来管理Sidecar容器,其最核心的功能是支持在pod不重启的情况下Sidecar容器的热升级
多区域管理 WorkloadSpread 将workload按照不同的策略来打散,随着k8s功能不断完善,部分功能k8s已经具备。支持Deployment、ReplicaSet、CloneSet。
多区域管理 UnitedDeployment k8s集群内可能存在不同种类型的node,该特性通过UnitedDeployment对象来管理将一个workload的不同pod分发到不同类型的节点上,并且可以指定不同类型节点的pod副本数。
增强运维 重启一个pod中的某个容器 该特性依赖于kurise-daemon组件实现,通过将容器进程停掉,kubelet检测到容器停掉后会自动将容器拉起。停掉容器的方式跟kubelet实现一致。
增强运维 镜像预热 通过ImagePullJob CR提供操作入口,底层的实现通过调用CRI的pod image接口实现
增强运维 控制pod中容器的启动顺序 kruise创建一个ConfigMap,并在pod中注入来挂载该ConfigMap,每个容器使用ConfigMap中的不同key。kruise依次往CM中增加key来实现控制容器启动顺序的目的。
增强运维 资源分发ResourceDestribution 可以跨namespace来分发CM、Secret,保证每个namespace下均有
应用安全防护 资源删除防护 通过webhook技术实现
应用安全防护 PodUnavailableBudget 通过webhook实现的k8s原生的pdb能力的增强,覆盖pdb不具备的场景

资料

阿里云容器服务是阿里云公有云基于Kubernetes企业级服务,在社区的Kubernetes版本基础上有能力增强, 本文用于调研社区增强功能,记录解决的问题、以及实现方法。ACK集群的增强功能有一部分是基于Kubernetes的api的prodiver实现,另外一部分是基于Kubernetes增加的额外组件,其中很多都已经开源,可以看到开源软件列表。

ACK的一些组件列表可以参见:组件概述

产品形态

  • 专有版Kubernetes:master和worker阶段均需要创建
  • 托管版Kubernetes:只需要创建worker节点,master节点通过ack托管
  • Serverless Kubernetes:master节点和worker节点均不需要自己创建

节点管理

大类 特性 描述
节点 节点自动扩缩容 完全利用k8s的autoscaler实现,提供了白屏的配置功能。https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md?spm=a2c4g.11186623.0.0.5e09135f2AAa2u&file=FAQ.md
节点资源变配 master和worker节点的资源变配,通过调用ecs的变配规格接口来实现。
节点池 将节点进行了分组,同一个分组内的节点可以统一来管理。比如,可以统一设置标签污点、设置期望节点数。通过节点池实现,节点池跟弹性伸缩组为一对一关系。

弹性伸缩

类型 特性 描述
调度层 hpa 基于k8s的hpa功能实现
自定义指标的hpa 基于prometheus-adapter实现的自定义指标监控
vpa 适用于大型单体应用。k8s的vpa功能实现,基于cluster-autoscaler实现
CronHPA 定期对pod进行伸缩,组件已开源:kubernetes-cronhpa-controller
ElasticWorkload
资源层 节点自动伸缩 k8s的node自动伸缩,基于社区的cluster-autoscaler实现
使用ECI弹性调度 通过virtual-kubelet实现,将ECI抽象为k8s的node节点

安全

大类 特性 详细描述
操作系统 基于Alibaba Cloud Linux 2支持等保2.0三级加固
基于Alibaba Cloud Linux 2支持CIS安全加固
基础设施 使用阿里云KMS提供Secret的落盘加密功能 在默认情况下,k8s的Secret是基于base64转码后明文存储的,存在一定的安全风险。k8s提供了使用外部的KMS provider来对Secret的数据进行加密的功能,apiserver和provider之间的通讯协议采用grpc接口。Secret中的数据在经过KMS provider加密后存储在etcd中。ACK借助阿里云的秘钥管理服务(KMS)提供了provider实现,该provider完全开源
为pod动态配置阿里云产品白名单 ack-kubernetes-webhook-injector组件会自动将pod ip添加到阿里云产品的白名单中。
k8s审计日志白屏化展示
安全巡检功能 基于CIS Kubernetes基线的实现,用来校验k8s的安全性
容器 配置容器安全策略 基于opa实现的策略引擎,可以根据预置的规则对k8s中创建的资源进行校验,校验不通过会返回失败
通过巡检来检查集群中存在的安全隐患的pod

可观测性

大类 功能项 描述
日志 日志采集功能基于logtail实现,可以采集容器日志 类似于开源组件log-pilot,仅需要配置环境变量,即可对日志进行收集。也可以通过AliyunLogConfig CR旁路的对日志采集进行配置。
日志 coredns日志 收集coredns日志
监控 基于arms产品支持应用性能监控
监控 基于ahas产品实现的架构感知监控
监控 node节点异常监控 npd将节点异常信息产生k8s的event
监控 k8s event监控 kube-eventer收集k8s的event

操作系统

Container OS:为容器场景而生的操作系统,操作系统镜像大大精简,提供了安全加固能力,不支持单个软件包的升级,软件包只能跟操作系统一起原子升级。
安全容器katacontainer

容器&镜像

大类 特性 描述
镜像 容器镜像服务ACR 使用公有云的容器镜像服务ACR
验证容器镜像 基于开源组件kritis的kritis-validation-hook组件通过webhook的方式对镜像进行验证,确保镜像安全

调度

大类 特性 解决问题 适用场景 具体实现
pod调度 使用Descheduler组件对Pod进行调度优化 k8s的调度为静态的,可以确保在pod调度时为最优的,但当集群运行一段时间后,集群中资源水位会发生变化,此处无法保证整个集群是最优的。 使用社区的开源组件Descheduler对pod进行重新调度
cpu调度 cpu拓扑感知调度 k8s的cpu manager特性解决的是pod调度到同一个节点上后,通过cpuset来隔离pod的cpu争抢。但却缺乏集群级别的资源视角,从而无法做到全局最优。该特性解决cpu密集型的pod调度到同一个节点上后的争抢问题,允许不是Guaranteed级别的pod实现cpuset特性。 cpu敏感型应用 通过调度器扩展来实现pod的cpu优化调度。ack-slo-manager agent负责实现每台机器上的绑核策略。
cpu Brust策略优化容器性能 k8s的cpu limit机制会在特定的时间段内将进程可以使用的时间片进行限制。但该特性不太适合一些突发类的应用,会导致这类应用在想要cpu的时间不够用,但不想用的时候却有空闲的时间片。 cpu突发型应用 ack通过slo-manager实现cpu brust的优化,该组件会监控容器的cpu throtteld状态,并且动态调整容器中的cgroup cfs quota限制。每个节点上会部署一个resource-controller的组件来动态修改pod的cgroup配置。
动态调整pod的资源上限 k8s中要想修改pod的limit配置,只能修改pod的yaml,此时一定会导致pod重建。对于想调整pod的limit限制,却又不想重启pod的场景。 通过一个Cgroups的CR来对pod使用的cpu、内存以及磁盘的上限进行动态调整,但并不会修改pod的yaml配置,因此不会导致pod的重建。每个节点上会部署一个resource-controller的组件来动态修改pod的cgroup配置。
通过控制L3 cache和MBA提高不同优先级任务的隔离能力 不同优先级的任务调度到同一台机器上存在L3 Cache和内存带宽的资源争抢的问题 cpu敏感型应用 底层利用Intel的RDT技术来实现,该技术可以跟踪和控制同一台机器上同时运行的多个应用程序的共享资源情况,比如L3 Cache、内存带宽。每个节点上会部署一个resource-controller的组件来应用RDT的控制。
控制动态水位提高不同优先级pod的资源利用效率 为了充分利用机器资源,通常将不同优先级的pod部署在同一个节点上,pod的优先级往往随着时间段而有所变化。在白天的时候,在线业务pod优先级高;晚上离线任务优先级高。该特定可以调整一个节点上pod可以使用的资源上限。 在线业务pod和离线业务pod混部 通过ConfigMap来配置一台机器上的在线业务和离线业务可以使用的资源比例。通过Cronjob的方式来修改ConfigMap的配置,从而达到变配的效果。
动态资源超卖 一台机器上的pod在设计request和limit的时候总会预留一部分buffer,如果将所有pod的buffer加起来就非常多,从而导致机器的资源利用率很难上去。 pod预留buffer 引入了reclaimed资源来解决,未完全理解实现。文档:动态资源超卖
gpu调度 GPU拓扑感知调度
共享GPU调度 GPU核共享
FPGA调度 暂未深入研究
任务调度 Gang scheduling Coscheduling将N个pod调度到M个节点上同时运行,需要部分pod同时启动该批处理任务即可运行。如果允许的部分pod为N时,则退化为Gang Scheduling。该场景要求pod要同时创建。 批处理任务 基于Scheduling Framework实现的自定义调度器,核心机制是借助了Permit插件的pod延迟绑定功能,等到同一个group下的pod都创建后再调度。参考文章:支持批任务的Coscheduling/Gang scheduling
Binpack scheduling k8s默认的调度策略会将pod优先分配到空闲的节点上,但这样集群中的节点会存在资源碎片化的问题。Binpack调度策略会优先将节点的资源用完。 减少资源碎片化,尤其是GPU场景 基于Scheduling Framework实现的自定义调度器。参考文章:支持批任务的Binpack Scheduling
Capacity Scheduling k8s支持的namespace ResourceQuota特性可以设置一个namespace下的pod可以使用的资源上限,但不够灵活。比如一个namespace下资源耗尽,另外一个namespace还有额外的资源,但这部分资源却不能给资源耗尽的namespace使用。 namespace ResourceQuota特性增强 使用ElasticQuotaTree的方式来定义每个namespace下可以使用的资源最小值和资源上限,配合Scheduling Framework扩展来实现。
弹性调度 ECI弹性调度 充分利用ECI的弹性功能,解决k8s的node资源不够灵活的问题。 将pod调度到ECI virtual kubelet技术将ECI抽象为k8s的node,并将pod指向调度到该node。调度到该node的pod最终会在ECI拉起容器。
自定义资源的优先级调度 k8s的调度器的策略采用固定的算法,会将所有node一视同仁,并不能针对某种类型的节点采用不同的策略。 自定义基于node调度策略 引入CRD ResourcePolicy用来定义节点调度的优先级。
负载感知调度 负载感知调度 在pod调度时,参考node节点历史的负载信息,优先将pod调度到负载较低的节点上,避免出现单个节点负载过高的情况。 避免node的资源使用率不均 通过调度器扩展实现,pod要开启该特性需要增加特性的annotation。

网络

大类 特性 描述
容器网络 terway网络 基于ENI实现,支持ipvlan模式,基于ipvlan和eBPF实现。NetworkPolicy基于eBPF实现。
terway网络的Hubble组件 基于eBPF实现的网络流量可视化
为pod挂载独立公网ip pod声明annotation k8s.aliyun.com/pod-with-eip:”true”
Service网络 ccm跨集群部署服务 同一个vip可以挂载到两个k8s集群内部的Service
Ingress 基于Nginx Ingress实现灰度发布 扩展Nginx Ingress实现
通过AHAS支持流控特性
基于Nginx Ingress实现流量复制
基于ALB实现了ALB Ingress 数据面直接使用ALB的七层负载均衡功能
DNS 引入kubernetes项目中的addon组件NodeLocal DNSCache来增加cache层 NodeLocal DNS Cache nodelocal dns cache位于kubernetes项目中,以DaemonSet的方式运行在k8s集群中
ExternalDNS服务 用来将DNS注册到外部的公共域名服务器
使用DNSTAP Analyser诊断异常 s使用CoreDNS DNSTAP Analyser组件来接收coredns的DNS解析报文格式dnstap协议,并最终可以输出到sls对异常的DNS解析报文进行分析。

存储

存储为ACK的一大亮点,借助云的丰富存储类型,通过k8s的CSI插件机制提供了块存储、文件存储、对象存储OSS和本地存储的支持。

image.png

本地存储

  1. LVM数据卷功能,基于lvm来动态创建pv
  2. QuotaPath,基于ext4的quota特性实现的本地存储的quota隔离功能
  3. 内存数据卷
  4. 持久化内存技术

云盘存储卷

  1. 支持云盘的在线扩容,k8s 1.16版本之前可以手工扩容磁盘,但是pvc保持必变。在k8s 1.16之后,在pvc修改后,可以自动完成云盘的扩容。
  2. 可根据磁盘的使用水位支持云盘的自动扩容,该功能通过额外的组件storage-operator来实现,策略存放到了额外的CRD StorageAutoScalerPolicy。跟k8s的hpa和vpa功能相比,该功能没有自动缩容的功能。
  3. 使用k8s的存储快照功能实现了存储快照。k8s定义了VolumeSnapshotContent(类似pv)、VolumeSnapshot(类似pvc)和VolumeSnapshotClass(类似StorageClass)三个类型来实现打快照功能,快照的恢复则借助pvc的spec.dataSource字段实现。
  4. 加密云盘功能,同样借助云上能力实现。使用时,仅需要在StorageClass的parameters参数中指定加密参数即可。

Serverless Kubernetes(ASK)

适用场景:

  1. 业务要求高弹性,如互联网在线服务存在波峰波谷特别明显

ACK提供了k8s以及k8s的管理功能,其中k8s的master节点需要单独创建和维护,每个k8s集群使用的资源是完全独立的。在ASK中,用户仅需要创建k8s集群,而不需要关心k8s集群的核心组件具体是怎么创建的。

在实际上,k8s的核心组件如etcd、kube-apiserver、kube-scheduler、kube-controller-manager可能是以pod的形式运行在另外的k8s集群之上,即所谓的k8s on k8s(KOK)的方案。而且这部分组件是多个k8s集群混部的,对用户完全屏蔽了实现细节,用户仅需要聚焦在如何使用k8s即可。

对于k8s的node节点,用户同样不需要创建,可以认为k8s的节点资源是无限多的。ask采用了virtual kubelet的技术创建了一个虚拟的k8s node节点 virtual-kubelet-${region}-${zone},整个k8s集群仅有一个节点。因为只有一个k8s节点,实际上k8s的管控作用会大大弱化,尤其是kube-scheduler,因为只有一个k8s节点,不存在pod调度的问题。

用户创建的pod资源,实际上会部署在阿里云产品弹性容器实例ECI上。新创建ask集群完成后,再到ECI上即可看到有默认的容器创建出来。ECI了创建容器的功能,给用户暴露的功能是跟k8s无关的,而ask相当于是给用户提供了以k8s的方式来创建容器。

ask还集成了knative,用户可以使用knative的Serving和Eventing的功能来实现社区通用的serverless服务。

相关链接:

分布式云容器平台ACK One

ack one提供了两个相对独立的功能,一个是第三方k8s集群的注册,另外一个是k8s多集群的管理和应用发布。两个功能的入口也未统一,其中一个是在ack界面,另外一个是在分布式云容器平台ack one。

第三方k8s集群的注册,允许用户将自己的k8s集群注册到ack上,可以从ack上来管理用户的k8s集群,而且可以部署一些ack自己的组件到用户的k8s集群上面。需要在用户的k8s集群部署一个ack-cluster-agent的服务,在用户的k8s集群跟ack可达的情况下,即可完成用户k8s集群的托管。

k8s多集群的管理功能,底层直接使用了kubevela项目,实现了k8s多集群的管理、多集群的应用发布功能。

其他

集群成本分析

大页内存介绍

一个操作系统上一台机器的物理内存是有限的,而操作系统上却运行着大量的进程,为了对进程屏蔽物理内存的差异,操作系统引入了虚拟内存的概念。Linux系统中虚拟内存是按照页的方式来进行管理的,每个页的默认大小为4K。如果物理内存非常大,就会导致虚拟内存和物理内存映射的页表(TLB)非常大。为了减少页表,一个可行的方法为增加页的大小。

可以通过如下命令来查看默认页大小:

1
2
$ getconf PAGE_SIZE
4096

在对内存要求特别严格的场景下,比如很多数据库的场景,都有巨页的需求,比如将页设置为1GB,甚至几十GB。

通常在大页内存的场景下,会将Linux的swap功能关闭,避免当页换出时导致的性能下降。

在Linux中Huge Page有2MB和1GB两种规则,其中2MB适用于GB级别的内存,1GB适用于TB级别的内存。

大页内存可以分为静态大页和透明大页两类。

  • 静态大页需要用户自行控制大页的分配、释放和使用。但该方式需要事先配置大页内存的量,因此用起来不够灵活。配置可以在系统启动的时候配置hugepages(页数量)和hugepagesz(页大小)两个参数。也可以使用修改内核参数的方式来预留。
  • 透明大页由操作系统的后台内核线程khugepaged控制大页的分配、释放和使用。

静态大页内存操作

预留大页内存

1
echo 20 >  /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

可以通过/proc/meminfo文件系统看到大页内存的使用情况

1
2
3
4
5
6
7
# cat /proc/meminfo | grep Huge
AnonHugePages: 2037760 kB
HugePages_Total: 20 # 预先分配的大页数量
HugePages_Free: 20 # 空闲大页数量
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB

挂载hugetlb文件系统

1
mount -t hugetlbfs none /mnt/huge

在应用程序中mmap映射hugetlb文件系统即可使用,对内存的操作类似于访问文件。

透明大页内存操作(待补充)

cat /sys/kernel/mm/transparent_hugepage/enabled

大页内存在k8s的支持情况

HugePage是k8s 1.9版本中引入的特性,1.10变为beta版本,1.23版本stable版本。k8s大于1.10版本该feature默认开启。

在节点上配置了大页内存后,kubelet会自动感知到大页内存的配置,会修改node配置。会在hugepages-2Mi中有对应的大页内存容量,memory字段会去掉对应的大页内存量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
allocatable:
cpu: "15"
ephemeral-storage: 41152812Ki
hugepages-1Gi: "0"
hugepages-2Mi: 40Mi
memory: 63998924Ki
pods: "110"
capacity:
cpu: "16"
ephemeral-storage: 41152812Ki
hugepages-1Gi: "0"
hugepages-2Mi: 40Mi
memory: 64756684Ki
pods: "110"

pod在使用大页内存的时候,需要以volume的方式挂载到pod中,pod对大页内存的使用跟普通文件系统相同。

自己断断续续花了几个月的时间,终于将《南渡北归》读完了。当我知道这本书的时候,迅速被这本书的内容给吸引住了。早高峰上班的地铁上基本全部花在了该书上,晚上躺在床上后临睡前也在刷该书,困的睁不开眼的时候再倒头就睡。

中学的历史课本上,在讲解中国近现代史的时候实际上带有了比较浓厚的党的特色,过分的强调了自我,而对于当时的国民政府的描述相对较少。受限于篇幅的限制,历史课本对于历史人物提的也比较少一些。知识分子在中国历史上乘井喷状爆发的有两个阶段春秋时期和民国时期,本书以民国时期我们耳熟能详的知识分子们为主角,比如胡适、陈寅恪、梅贻琦、闻一多,讲解了大师们在乱世中颠沛流离的经历,以及错综复杂的人物关系。

《南渡北归》实际分为了《南渡》、《北归》和《离别》三部曲,全书100多万字,四大名著字数最多的《水浒传》也不过才96万字,本书可以算得上一步巨著了。之所以有那么多字数,其中有一部分篇幅是大师们的一些书信资料,真正属于作者的有效字数应该跟《水浒传》不相上下。

《南渡》主要讲解了卢沟桥事变抗日战争爆发后,北平各学府的师生们纷纷南下,先是辗转到长沙成立了长沙临时联合大学。但不久长沙沦陷,又分几路辗转到云南昆明组建西南联合大学,像李政道、杨振宁在这里完成了部分学业。但在昆明好景不长,时常有敌机轰炸,师生们天天”跑警报“。后一部分又逃亡了四川宜宾长江边上的李庄镇,很多大师们在李庄镇度过了非常难忘的一段回忆。比如林徽因、梁思永,很多时候都是在病床上度过,又受限于当地的环境,无药可医。

《北归》发生在了抗日战争结束,国民政府开始组织知识分子纷纷回到北平的旧时学府,回到一别多年的故乡。可是好景不长,国共内战爆发,北平沦陷。紧接着一大批的知识分子们又开始了新一轮的逃亡,很大一部分逃亡了台湾岛,另外一部分留在了大陆。这一不同的选择,直接决定了不同的人生命运。

《离别》是读起来最为伤感的一部,大师们纷纷陨落。逃亡台湾的如傅斯年、胡适、梅贻琦等虽然过得并不富裕,谈不上悲惨,但也算在台湾开辟了一番事业,名扬千古。但逃亡台湾的知识分子,在大陆却成了千古罪人。如大陆掀起了一股非常大的反胡热潮,以至于连胡适的儿子胡思杜都登报公开批判远在海峡对岸的父亲。可惜的是,思想觉悟足够高的儿子仍然在被批斗至自杀身亡。留在大陆的知识分子的命运却大相径庭,政治觉悟高者如郭沫若风生水起。但大部分的知识分子在”三反“、”文革“运动中都遭到了残酷的迫害,被红卫兵逼迫自杀者也不在少数。当年的红卫兵如今也就其八十岁的样子,我特别想采访一番当时他们是在什么情况下怀着怎样的心情什么动力来做出如此伤天害理之事,而且自己还理直气壮。

寒门难出贵子。民国时期但凡叫上名字的大师们大都出生在名门望族,而且往往一家不止出一位大师级人物,甚至有很多家族是世交。随便举几个例子,如周树人、周作人兄弟,虽然后来两兄弟完全走上了不同的人生道路。曾国藩的后人至少五代以内都有各种英才,如在民国时期活跃的曾宝荪、曾约农、曾昭燏、曾昭抡等。梁启超的两个儿子梁思成和梁思永兄弟,分别在建筑学和考古学方向有非常高的建树。不过也正是因为出生在名门望族,因为家庭出身的问题,也往往在后来的迫害中是最惨烈的。

傅斯年在书中简直被作者推崇之至,甚至可以说是崇拜。关于傅斯年,作者描绘最多的字眼是胖子、高血压蹭蹭往上窜、活跃于政学两界、学术大鳄、气场强大、胡适的打手、思维清晰、擅长处理棘手问题。傅斯年毕业于大学,曾经领导过五四运动,出国留学后曾经担任了多年的史语所所长一职,也曾代理过北京大学校长一职。国共内战爆发后,随史语所一起逃亡台湾,在台湾期间,担任台湾大学校长,1950年突发脑淤血去世,后葬于台湾大学的”傅园“内为后世铭记。

另外一个对外印象比较深刻的人物是陈寅恪。清末著名诗人陈三立之子,同梁启超、王国维、赵元任并称为”清华国学四大导师“。国共内战北平失手后,陈寅恪从北京辗转逃亡岭南大学,后岭南大学跟中山大学合并,任教于中山大学。书中罗列了陈寅恪的多首诗,诗文引经据典,读起来相当晦涩,没点文化底蕴根本无法领会其中奥妙,可见大师功底之深。晚年在双目失明的情况下,完成了80多万字的巨著《柳如是别传》,非常人能所及。

本书描述的这段历史,实际上也就发生了一百年的时间,但却有非常多的历史是无法考证的。读史书其实想了解一个真实的历史,但本书的作者却带有了比较浓厚的个人感情色彩,比如对傅斯年推崇之至,对闻一多的评价却非常差。另外,作者有一丝错误的政治倾向,此书能够在大陆出版也算是一个奇迹了。读此书本着了解历史真相的态度,但却不可全信。

民国时期的知识分子大都有记录日志的好习惯,日记确实是一种非常好的自我反省和自我成长的好方式,可惜这么好的成长方式在现代的社会中却在逐渐消失,更多的会出现在小学生的课后作业中。另一方面,日记作为了非常好的一种史学参考资料,日记不会说谎,是作者的最真实的内心流露。过去发生的事情可以被人遗忘,但文字永存。由于没有长途电话这么高科技的产品,朋友之间的往来很多都是以书信的形式,如林徽因跟美国好友费慰梅就有大量的书信往来,这些书信也成为了非常好的史学资料。书信在当代早已成为了过去时,取而代之的是更为便捷的聊天app,无论身处何地对方永远在线,想聊天几乎没有了任何代价。

如果你有时间,此书值得一读。另外,再推荐一个关于昆明西南联合大学的纪录片《九零后》,针对超过16位超过90岁高龄的专家学者的口述,有助于了解当时那段历史。

简介

华为云开源的多云容器编排项目,目前为CNCF沙箱项目。Karmada是基于kubefed v2进行修改的一个项目,因此里面很多概念都是取自kubefed v2。

Karmada相对于kubefed v2的最大优点:完全兼容k8s的API。karmada提供了一个一套独立的karmada-apiserver,跟kube-apiserver是兼容的,使用方在调用的时候只需要访问karmada-apiserver就可以了。

架构


有三个组件构建:

  • karmada api server,用来给其他的组件提供rest api
  • karmada controller manager
  • karmada scheduler

karmada会新建一个ETCD,用来存储karmada的API对象。

karmada controller manager内部包含了多个controller的功能,会通过karmada apiserver来watch karmada对象,并通过调用各个子集群的apiserver来创建标准的k8s对象,包含了如下的controller对象:

  1. Cluster Controller:用来管理子集群的声明周期
  2. Policy Controller:监听PropagationPolicy对象,通过resourceSelector找到匹配中的资源,并创建ResourceBinding对象。
  3. Binding Controller:监听ResourceBinding对象,并创建每个集群的Work对象。
  4. Execution Controller:监听Work对象,一旦Work对象场景后,会在子集群中创建Work关联的k8s对象。

组件

karmada-aggregated-apiserver

在karmada-apiserver上注册的api信息

etcd

用来存放karmada的元数据信息

CRD

Cluster

需要使用kamada-apiserver来查询

unfold me to see the yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
apiVersion: cluster.karmada.io/v1alpha1
kind: Cluster
metadata:
finalizers:
- karmada.io/cluster-controller
name: member1
spec:
apiEndpoint: https://172.18.0.3:6443
impersonatorSecretRef:
name: member1-impersonator
namespace: karmada-cluster
secretRef:
name: member1
namespace: karmada-cluster
syncMode: Push
status:
# 支持的api列表
apiEnablements:
- groupVersion: admissionregistration.k8s.io/v1
resources:
- kind: MutatingWebhookConfiguration
name: mutatingwebhookconfigurations
- kind: ValidatingWebhookConfiguration
name: validatingwebhookconfigurations
- groupVersion: apiextensions.k8s.io/v1
resources:
- kind: CustomResourceDefinition
name: customresourcedefinitions
- groupVersion: apiregistration.k8s.io/v1
resources:
- kind: APIService
name: apiservices
- groupVersion: apps/v1
resources:
- kind: ControllerRevision
name: controllerrevisions
- kind: DaemonSet
name: daemonsets
- kind: Deployment
name: deployments
- kind: ReplicaSet
name: replicasets
- kind: StatefulSet
name: statefulsets
- groupVersion: authentication.k8s.io/v1
resources:
- kind: TokenReview
name: tokenreviews
- groupVersion: authorization.k8s.io/v1
resources:
- kind: LocalSubjectAccessReview
name: localsubjectaccessreviews
- kind: SelfSubjectAccessReview
name: selfsubjectaccessreviews
- kind: SelfSubjectRulesReview
name: selfsubjectrulesreviews
- kind: SubjectAccessReview
name: subjectaccessreviews
- groupVersion: autoscaling/v1
resources:
- kind: HorizontalPodAutoscaler
name: horizontalpodautoscalers
- groupVersion: autoscaling/v2beta1
resources:
- kind: HorizontalPodAutoscaler
name: horizontalpodautoscalers
- groupVersion: autoscaling/v2beta2
resources:
- kind: HorizontalPodAutoscaler
name: horizontalpodautoscalers
- groupVersion: batch/v1
resources:
- kind: CronJob
name: cronjobs
- kind: Job
name: jobs
- groupVersion: batch/v1beta1
resources:
- kind: CronJob
name: cronjobs
- groupVersion: certificates.k8s.io/v1
resources:
- kind: CertificateSigningRequest
name: certificatesigningrequests
- groupVersion: coordination.k8s.io/v1
resources:
- kind: Lease
name: leases
- groupVersion: discovery.k8s.io/v1
resources:
- kind: EndpointSlice
name: endpointslices
- groupVersion: discovery.k8s.io/v1beta1
resources:
- kind: EndpointSlice
name: endpointslices
- groupVersion: events.k8s.io/v1
resources:
- kind: Event
name: events
- groupVersion: events.k8s.io/v1beta1
resources:
- kind: Event
name: events
- groupVersion: flowcontrol.apiserver.k8s.io/v1beta1
resources:
- kind: FlowSchema
name: flowschemas
- kind: PriorityLevelConfiguration
name: prioritylevelconfigurations
- groupVersion: networking.k8s.io/v1
resources:
- kind: IngressClass
name: ingressclasses
- kind: Ingress
name: ingresses
- kind: NetworkPolicy
name: networkpolicies
- groupVersion: node.k8s.io/v1
resources:
- kind: RuntimeClass
name: runtimeclasses
- groupVersion: node.k8s.io/v1beta1
resources:
- kind: RuntimeClass
name: runtimeclasses
- groupVersion: policy/v1
resources:
- kind: PodDisruptionBudget
name: poddisruptionbudgets
- groupVersion: policy/v1beta1
resources:
- kind: PodDisruptionBudget
name: poddisruptionbudgets
- kind: PodSecurityPolicy
name: podsecuritypolicies
- groupVersion: rbac.authorization.k8s.io/v1
resources:
- kind: ClusterRoleBinding
name: clusterrolebindings
- kind: ClusterRole
name: clusterroles
- kind: RoleBinding
name: rolebindings
- kind: Role
name: roles
- groupVersion: scheduling.k8s.io/v1
resources:
- kind: PriorityClass
name: priorityclasses
- groupVersion: storage.k8s.io/v1
resources:
- kind: CSIDriver
name: csidrivers
- kind: CSINode
name: csinodes
- kind: StorageClass
name: storageclasses
- kind: VolumeAttachment
name: volumeattachments
- groupVersion: storage.k8s.io/v1beta1
resources:
- kind: CSIStorageCapacity
name: csistoragecapacities
- groupVersion: v1
resources:
- kind: Binding
name: bindings
- kind: ComponentStatus
name: componentstatuses
- kind: ConfigMap
name: configmaps
- kind: Endpoints
name: endpoints
- kind: Event
name: events
- kind: LimitRange
name: limitranges
- kind: Namespace
name: namespaces
- kind: Node
name: nodes
- kind: PersistentVolumeClaim
name: persistentvolumeclaims
- kind: PersistentVolume
name: persistentvolumes
- kind: Pod
name: pods
- kind: PodTemplate
name: podtemplates
- kind: ReplicationController
name: replicationcontrollers
- kind: ResourceQuota
name: resourcequotas
- kind: Secret
name: secrets
- kind: ServiceAccount
name: serviceaccounts
- kind: Service
name: services
conditions:
- lastTransitionTime: "2022-02-21T08:27:56Z"
message: cluster is healthy and ready to accept workloads
reason: ClusterReady
status: "True"
type: Ready
kubernetesVersion: v1.22.0
nodeSummary:
readyNum: 1
totalNum: 1
resourceSummary:
allocatable:
cpu: "8"
ephemeral-storage: 41152812Ki
hugepages-1Gi: "0"
hugepages-2Mi: "0"
memory: 32192720Ki
pods: "110"
allocated:
cpu: 950m
memory: 290Mi
pods: "9"

PropagationPolicy

应用发布策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
name: nginx-propagation
namespace: default
spec:
placement:
clusterAffinity:
clusterNames:
- member1
- member2
replicaScheduling:
replicaDivisionPreference: Weighted·
replicaSchedulingType: Divided
weightPreference:
staticWeightList:
- targetCluster:
clusterNames:
- member1
weight: 1
- targetCluster:
clusterNames:
- member2
weight: 1
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: nginx
namespace: default

ClusterPropagationPolicy

用来定义Cluster级别资源的发布策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: policy.karmada.io/v1alpha1
kind: ClusterPropagationPolicy
metadata:
name: serviceexport-policy
spec:
resourceSelectors:
- apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
name: serviceexports.multicluster.x-k8s.io
placement:
clusterAffinity:
clusterNames:
- member1
- member2

OverridePolicy

用于修改不同集群内的对象

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
name: example
spec:
...
overriders:
commandOverrider:
- containerName: myapp
operator: remove
value:
- --parameter1=foo

安装

karmada提供了三种安装方式:

  • kubectl karmada插件的方式安装,较新的特性,v1.0版本才会提供,当前最新版本为v0.10.1
  • helm chart方式安装
  • 使用源码安装

安装kubectl插件

karmada提供了一个cli的工具,可以是单独的二进制工具kubectl-karmada,也可以是kubectl的插件karmada,本文以kubectl插件的方式进行安装。

1
kubectl krew install karmada

接下来就可以执行 kubectl karmada命令了。

helm chart的方式安装

使用kind插件一个k8s集群 host,此处步骤略

在源码目录下执行如下的命令

1
helm install karmada -n karmada-system --create-namespace ./charts

会在karmada-system下部署如下管控的组件

1
2
3
4
5
6
7
8
9
10
11
$ kubectl get deployments -n karmada-system
NAME READY UP-TO-DATE AVAILABLE AGE
karmada-apiserver 1/1 1 1 83s
karmada-controller-manager 1/1 1 1 83s
karmada-kube-controller-manager 1/1 1 1 83s
karmada-scheduler 1/1 1 1 83s
karmada-webhook 1/1 1 1 83s

$ kubectl get statefulsets -n karmada-system
NAME READY AGE
etcd 1/1 2m1s

从源码安装一个本地测试集群

本方式仅用于本地测试,会自动使用kind拉起测试的k8s集群,包括了一个host集群和3个member集群。

执行如下命令,会执行如下的任务:

  1. 使用kind启动一个新的k8s集群host
  2. 构建karmada的控制平面,并部署控制平面到host集群
  3. 创建3个member集群并加入到karmada中
    1
    2
    3
    git clone https://github.com/karmada-io/karmada
    cd karmada
    hack/local-up-karmada.sh
    local-up-karmada.sh有两个细节问题值得关注。
    kind创建出来的集群名一定带有“kind-”的前缀,该脚本中为了去掉“kind-”前缀,使用了kubectl config rename-context 命令来rename context。https://github.com/karmada-io/karmada/blob/master/hack/util.sh#L375
    另外,使用kind创建出来的集群,context中的apiserver地址为类似https://127.0.0.1:45195这样的本地随机端口,只能在宿主机网络中访问。如果要想在两个k8s集群之间访问是不可行的。脚本中使用kubectl config set-cluster 命令将集群apiserver的地址替换为了docker网段的ip地址,以便于多个k8s集群之间的互访。https://github.com/karmada-io/karmada/blob/master/hack/util.sh#L389

可以看到创建了如下的4个k8s cluster

1
2
3
4
5
$ kind get clusters
karmada-host
member1
member2
member3

但这四个集群会分为两个kubeconfig文件 ~/.kube/karmada.config 和 ~/.kube/members.config。karmada又分为了两个context karmada-apiserver和karmada-host,两者连接同一个k8s集群。karmada-apiserver为跟karmada控制平台交互使用的context,为容器中的karmada-apiserver。karmada-host连接容器的kube-apiserver。

如果要连接host集群设置环境变量:export KUBECONFIG=”$HOME/.kube/karmada.config”
如果要连接member集群设置环境变量:export KUBECONFIG=”$HOME/.kube/members.config”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ export KUBECONFIG="$HOME/.kube/karmada.config"
# 切换到karmada-host
$ kctx karmada-host
# 可以看到karmada部署的组件
$ k get deploy -n karmada-system
NAME READY UP-TO-DATE AVAILABLE AGE
karmada-aggregated-apiserver 2/2 2 2 17m
karmada-apiserver 1/1 1 1 18m
karmada-controller-manager 2/2 2 2 17m
karmada-kube-controller-manager 1/1 1 1 17m
karmada-scheduler 2/2 2 2 17m
karmada-scheduler-estimator-member1 2/2 2 2 17m
karmada-scheduler-estimator-member2 2/2 2 2 17m
karmada-scheduler-estimator-member3 2/2 2 2 17m
karmada-webhook 2/2 2 2 17m

在host集群中可以看到会创建出如下的pod

1
2
3
4
5
6
7
8
9
10
11
12
# k get pod -n karmada-system 
NAME READY STATUS RESTARTS AGE
etcd-0 1/1 Running 0 18h
karmada-aggregated-apiserver-7b88b8df99-95twq 1/1 Running 0 18h
karmada-apiserver-5746cf97bb-pspfg 1/1 Running 0 18h
karmada-controller-manager-7d66968445-h4xsc 1/1 Running 0 18h
karmada-kube-controller-manager-869d9df85-f4bqj 1/1 Running 0 18h
karmada-scheduler-8677cdf96d-psnlw 1/1 Running 0 18h
karmada-scheduler-estimator-member1-696b54fd56-jjg6b 1/1 Running 0 18h
karmada-scheduler-estimator-member2-774fb84c5d-fldhd 1/1 Running 0 18h
karmada-scheduler-estimator-member3-5c7d87f4b4-fk4lj 1/1 Running 0 18h
karmada-webhook-79b87f7c5f-lt8ps 1/1 Running 0 18h

在karmada-apiserver集群会创建出如下的Cluster,同时可以看到有两个Push模式一个Pull模式的集群。

1
2
3
4
5
6
7
$ kctx karmada-apiserver
Switched to context "karmada-apiserver".
$ kubectl get clusters
NAME VERSION MODE READY AGE
member1 v1.22.0 Push True 61m
member2 v1.22.0 Push True 61m
member3 v1.22.0 Pull True 61m

应用发布

将context切换到karmada-host,在host集群部署应用nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ export KUBECONFIG="$HOME/.kube/karmada.config"
$ kctx karmada-apiserver
$ kubectl create -f samples/nginx/propagationpolicy.yaml
$ cat samples/nginx/propagationpolicy.yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
name: nginx-propagation
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: nginx
placement:
# 要提交到子集群1和2
clusterAffinity:
clusterNames:
- member1
- member2
replicaScheduling:
replicaDivisionPreference: Weighted
replicaSchedulingType: Divided
weightPreference:
staticWeightList:
- targetCluster:
clusterNames:
- member1
weight: 1
- targetCluster:
clusterNames:
- member2
weight: 1

在karmada-apiserver中提交deployment。deployment提交后实际上仅为一个模板,只有PropagationPolicy跟deployment关联后,才会真正部署。deployment中指定的副本数为所有子集群的副本数综合。

1
2
3
4
5
6
7
$ kubectl create -f samples/nginx/deployment.yaml
$ k get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 2/2 2 2 7m37s
# 可以看到虽然deployment的副本数为2,但在karmada-apiserver集群中实际上并没有pod创建出来
$ k get pod
No resources found in default namespace.

通过karmadactl命令可以查询环境中的所有子集群的pod状态

1
2
3
4
5
6
$ karmadactl get pod
The karmadactl get command now only supports Push mode, [ member3 ] are not push mode

NAME CLUSTER READY STATUS RESTARTS AGE
nginx-6799fc88d8-4w2gc member1 1/1 Running 0 4m16s
nginx-6799fc88d8-j77f5 member2 1/1 Running 0 4m16s

元集群访问子集群

可以看到在karmada-apiserver上注册了AA服务,group为cluster.karmada.io

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# kubectl --kubeconfig /root/.kube/karmada.config --context karmada-apiserver get  apiservice v1alpha1.cluster.karmada.io -o yaml 
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
labels:
apiserver: "true"
app: karmada-aggregated-apiserver
name: v1alpha1.cluster.karmada.io
spec:
group: cluster.karmada.io
groupPriorityMinimum: 2000
insecureSkipTLSVerify: true
service:
name: karmada-aggregated-apiserver
namespace: karmada-system
port: 443
version: v1alpha1
versionPriority: 10
status:
conditions:
- lastTransitionTime: "2022-02-21T08:27:47Z"
message: all checks passed
reason: Passed
status: "True"
type: Available

如果直接使用kubectl访问karmada-apiserver服务,会提示存在权限问题

1
2
 kubectl --kubeconfig ~/.kube/karmada-apiserver.config --context karmada-apiserver   get --raw /apis/cluster.karmada.io/v1alpha1/clusters/member1/proxy/api/v1/nodes
Error from server (Forbidden): users "system:admin" is forbidden: User "system:serviceaccount:karmada-cluster:karmada-impersonator" cannot impersonate resource "users" in API group "" at the cluster scope

给karmada-apiserver的Account system:admin授权,创建文件cluster-proxy-rbac.yaml,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-proxy-clusterrole
rules:
- apiGroups:
- 'cluster.karmada.io'
resources:
- clusters/proxy
resourceNames:
- member1
- member2
- member3
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-proxy-clusterrolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-proxy-clusterrole
subjects:
- kind: User
name: "system:admin"

执行如下命令即可给karmada-apiserver的Account system:admin 授权可访问AA服务的member1-3

1
kubectl --kubeconfig /root/.kube/karmada.config --context karmada-apiserver apply -f cluster-proxy-rbac.yaml   

通过url的形式访问AA服务,返回数据格式为json

1
kubectl --kubeconfig ~/.kube/karmada-apiserver.config --context karmada-apiserver get --raw /apis/cluster.karmada.io/v1alpha1/clusters/member1/proxy/api/v1/nodes

如果要想使用 kubectl get node 的形式来访问,则需要在kubeconfig文件中server字段的url地址后追加url /apis/cluster.karmada.io/v1alpha1/clusters/{clustername}/proxy

给特定的账号授权

在karmada-apiserver中创建账号 tom

1
kubectl --kubeconfig /root/.kube/karmada.config --context karmada-apiserver create serviceaccount tom

在karmada-apiserver中提交如下的yaml文件,用来给tom账号增加访问member1集群的权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-proxy-clusterrole
rules:
- apiGroups:
- 'cluster.karmada.io'
resources:
- clusters/proxy
resourceNames:
- member1
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-proxy-clusterrolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-proxy-clusterrole
subjects:
- kind: ServiceAccount
name: tom
namespace: default
# The token generated by the serviceaccount can parse the group information. Therefore, you need to specify the group information below.
- kind: Group
name: "system:serviceaccounts"
- kind: Group
name: "system:serviceaccounts:default"

执行如下命令提交

1
kubectl --kubeconfig /root/.kube/karmada.config --context karmada-apiserver apply -f cluster-proxy-rbac.yaml

获取karmada-apiserver集群的tom账号的token信息

1
kubectl --kubeconfig /root/.kube/karmada.config --context karmada-apiserver  get secret `kubectl --kubeconfig /root/.kube/karmada.config --context karmada-apiserver  get sa tom -oyaml | grep token | awk '{print $3}'` -oyaml | grep token: | awk '{print $2}' | base64 -d

创建~/.kube/tom.config文件,其中token信息为上一步获取的token,server的地址可以查看 ~/.kube/karmada-apiserver.config文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
clusters:
- cluster:
insecure-skip-tls-verify: true
server: {karmada-apiserver-address} # Replace {karmada-apiserver-address} with karmada-apiserver-address. You can find it in /root/.kube/karmada.config file.
name: tom
contexts:
- context:
cluster: tom
user: tom
name: tom
current-context: tom
kind: Config
users:
- name: tom
user:
token: {token} # Replace {token} with the token obtain above.

通过karmada-apiserver的tom用户访问member1集群

1
2
3
4
5
6
# 预期可以正常访问
$ kubectl --kubeconfig ~/.kube/tom.config get --raw /apis/cluster.karmada.io/v1alpha1/clusters/member1/proxy/apis

# 预期不可以访问,因为tom在member1集群没有权限
$ kubectl --kubeconfig ~/.kube/tom.config get --raw /apis/cluster.karmada.io/v1alpha1/clusters/member1/proxy/api/v1/nodes
Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:default:tom" cannot list resource "nodes" in API group "" at the cluster scope

在member1集群将tom账号绑定权限,创建member1-rbac.yaml文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tom
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tom
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tom
subjects:
- kind: ServiceAccount
name: tom
namespace: default

权限在member1集群生效

1
kubectl --kubeconfig /root/.kube/members.config --context member1 apply -f member1-rbac.yaml 

重新执行命令即可以访问子集群中的数据

1
kubectl --kubeconfig ~/.kube/tom.config get --raw /apis/cluster.karmada.io/v1alpha1/clusters/member1/proxy/api/v1/nodes

集群注册

支持Push和Pull两种模式。

push模式karmada会直接访问成员集群的kuba-apiserver。

pull模式针对的场景是中心集群无法直接子集群的场景。每个子集群运行karmada-agent组件,一旦karmada-agent部署完成后就会自动向host集群注册,karmada-agent会watch host集群的karmada-es-下的cr,并在本集群部署。

apiserver-builder-alpha

k8s提供了aggregated apiserver的方式来扩容api,该项目提供了代码生成器、基础library来供开发AA使用。

该项目的定位跟kubebuilder比较类似,kubebuilder用来生成CRD的框架,该项目用来生成AA的框架。

相关资料:

  • [Set up an Extension API Server](Set up an Extension API Server)

cluster-api-provider-nested

在同一个k8s集群内提供多租户的特性,每个租户具有独立的api-server、controller-manager和scheduler。

cluster-proportional-autoscaler

k8s默认提供了hpa机制,可以根据pod的负载情况来对workload进行自动的扩缩容。同时以单独的autoscaler项目提供了vpa功能的支持。

该项目提供提供了类似pod水平扩容的机制,跟hpa不同的是,pod的数量由集群中的节点规模来自动扩缩容pod。特别适合负载跟集群规模的变化成正比的服务,比如coredns、nginx ingress等服务。

hpa功能k8s提供了CRD来作为hpa的配置,本项目没有单独的CRD来定义配置,而是通过在启动的时候指定参数,或者配置放到ConfigMap的方式。而且一个cluster-proportional-autoscaler实例仅能针对一个workload。

kube-batch

k8s的调度器扩展,实现了Gang Scheduling特性(一组pod必须同时被调度成功,或者处于pending状态),适用于批处理系统。

由于该组件是以单独调度器的形式存在,会跟k8s默认的kube-scheduler并存,因为两个调度器之间并不能相互感知,在两个调度器并存的情况下会存在一定的冲突。

prometheus-adapter

k8s要实现hpa(水平自动扩容)的功能,需要监控指标。k8s的监控指标分为核心指标和自定义指标两大类。其中核心指标由metrics-server组件从kubelet、cadvisor等组件来获取,并通过aggregated apiserver的形式暴露给k8s,aggregated api的group信息为metrics.k8s.io。

自定义监控指标则由group custom.metrics.k8s.io对通过aggregated api暴露。本项目即为自定义监控指标的aggregated apiserver的实现。

相关参考:Horizontal Pod Autoscaling

scheduler-plugins

k8s从1.16版本开始提供了新的调度框架Kubernetes Schduling Framework机制,用户可以基于此项目来开发自己的插件。该项目可以直接构建出kube-scheduler的新镜像。

相关参考:进击的Kubernetes调度系统(一):Scheduling Framework

sig-storage-local-static-provisioner

k8s提供了local pv功能可以用来给pod挂载本地的数据盘,具体的local pv的定义如下所示,pv中包含了要亲和的节点信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- example-node

但要使用local pv功能,必须要事先创建出pv才可以,k8s本身并没有提供动态创建pv的功能。

该工具可以根据配置的规则,自动将机器上符合条件的磁盘创建出local pv以供后续创建出的pod使用。

另外,Rancher提供了一个类似的项目,local-path-provisioner,该项目已经被拉起k8s开发环境的开源项目kind使用。

相关参考:LocalVolume数据卷

0%