24-Kubernetes-0
1、Kubernetes 组件
(1)前置基础:容器化与 K8s 起源
# 核心特性优势
K8s 能成为云原生核心,源于其贴合企业级场景的核心能力,也是其与其他容器管理平台的核心差异:
- 轻量级
- 开源免费
- 弹性伸缩
- 负载均衡
- 故障自愈
- 可扩展
#官方集群规模限制
K8s 官方为保障集群的稳定性和性能,定义了单集群的最大规模限制,也是企业规划 K8s 集群的重要参考:
- 单个节点上的 Pod 数量**不超过 110;
- 单集群的节点数量**不超过 5000;
- 单集群的 Pod 总数**不超过 150000;
- 单集群的容器总数不超过 300000
若业务需要超大规模集群,可采用K8s 联邦(Kubefed)实现多集群管理。
(2)K8s 整体架构:分布式集群设计
K8s 采用主从架构(Master/Worker) 设计,也称为控制面(Control Plane)+ 数据面(Data Plane) 架构,集群由多台 Master 节点和多台 Worker 节点组成,所有节点通过网络互通,实现集群的高可用和分布式调度。
#1宏观架构
宏观架构的典型部署形式为3 台 Master 节点 + N 台 Worker 节点(3 台 Master 满足分布式一致性算法的最小集群要求),适用于绝大多数企业级生产环境。
# 2微观架构
#控制面(Master 节点)组件
API Server
Scheduler
Controller Manager
etcd
#数据面(Worker 节点)组件
Kubelet
Kube-proxy
Container Runtime
#配套资源与交互工具
-Container Registry(容器镜像仓库)
-kubectl:K8s 的官方命令行工具
-Dashboard:K8s 的可视化 GUI 管理界面
(3)K8s 核心组件:功能与职责详解
K8s 的所有核心能力均由其组件实现,组件遵循**单一职责原则**,每个组件负责特定的功能,且组件间通过**API Server实现通信(etcd 仅与 API Server 直连),保障了集群的解耦性和可维护性。
#Master 节点 - 控制面:集群的 “指挥中心”
控制面组件是 K8s 集群的核心,负责集群的资源管理、调度决策、状态监控、配置存储,所有组件均运行在 Master 节点上,且支持多实例部署实现高可用。
'1、Kube-apiserver(API 服务器)
Kube-apiserver 是 K8s 集群的唯一入口,也是所有组件间通信的枢纽,是 K8s 最核心的组件,没有 API Server,整个集群将无法正常工作。
核心功能:
- 提供RESTful API 接口:支持 JSON/YAML 格式的请求,开发和运维人员通过 kubectl、Dashboard 或自定义程序调用 API,实现集群资源的增删改查;
- 提供认证、授权、准入控制:对所有访问集群的请求进行身份验证(如令牌、证书)、权限校验(如 RBAC 角色权限)、准入策略检查(如 Pod 安全策略),保障集群的安全性;
- 作为组件通信枢纽:所有其他组件(Scheduler、Controller Manager、Kubelet 等)均通过 API Server 查询或修改集群状态,组件间不直接通信;
- 提供etcd 数据缓存:将 etcd 中的热点数据缓存到内存中,减少对 etcd 的访问压力,提升集群响应速度;
- 支持高可用部署:通过负载均衡器(如 haproxy、Nginx)挂载多台 API Server 实例,实现请求的负载均衡,保障 API 服务的高可用。
'2、etcd:集群的 “配置数据库
etcd 是由 CoreOS 公司开发的高可用分布式键值(Key-Value)数据库,基于 Raft 强一致性算法实现,是 K8s 集群的唯一数据存储,所有集群资源的状态、配置信息均持久化存储在 etcd 中。
核心定位:
K8s 的 “配置管理员”,存储集群的所有元数据(如 Pod、服务、节点、部署的状态和参数),是集群的 “数据唯一来源”。
核心特性:
- 强一致性:基于 Raft 算法,保证集群中所有 etcd 节点的数据完全一致,适合存储核心配置数据;
- 高可用:支持多节点集群部署,单节点故障不影响数据的可用性,集群最小规模为 3 个节点;
- 持久化存储:支持数据的磁盘持久化,避免节点故障导致数据丢失;
- 轻量级:体积小、性能高,单节点支持每秒数万次的读写请求;
- 仅与 API Server 通信:etcd 的读写请求仅能由 API Server 发起,其他组件需通过 API Server 间接操作 etcd,保障数据的安全性和一致性。
'3、Kube-controller-manager(控制管理器)
Kube-controller-manager 是 K8s 集群的“监控运维中心”,由多个独立的控制器(Controller)组成,每个控制器负责监控一类集群资源的状态,核心目标是保证集群的实际状态与用户定义的期望状态一致。
核心功能:
- 故障检测与恢复:监控节点、Pod 的运行状态,当节点故障时自动将该节点上的 Pod 调度到健康节点,当 Pod 故障时自动重启或重建 Pod;
- 服务与资源管理:实现 Deployment、StatefulSet、Service、ConfigMap 等资源的生命周期管理,支持应用的扩缩容、版本更新、回滚;
- 资源状态同步:将集群资源的实际状态与 etcd 中存储的期望状态进行对比,发现差异时立即执行调谐操作,直至实际状态与期望状态一致。
核心机制:
- Control Loop(控制循环):每个控制器都采用 “监听 - 对比 - 调谐” 的控制循环机制,持续监听资源状态,发现差异后执行调谐操作;
- 自动重试:当调谐操作失败时,控制器会自动重试,直至操作成功,保障集群的**最终一致性**;
- 无状态设计:多实例部署时,通过领导者选举(Leader Election)机制选出一个主控制器执行操作,避免多实例冲突。
常见控制器:节点控制器(Node Controller)、Pod 控制器(Pod Controller)、部署控制器(Deployment Controller)、服务控制器(Service Controller)、端点控制器(Endpoint Controller)等。
'4、Kube-scheduler(调度器)
Kube-scheduler 是 K8s 集群的“部署规划师”,核心职责是为未调度的 Pod选择最合适的 Worker 节点运行,调度的核心原则是资源适配、策略优先、负载均衡 。
核心调度流程:
- 监听未调度 Pod:持续通过 API Server 监听集群中处于 “未调度” 状态的 Pod;
- 节点筛选(预选):根据 Pod 的资源需求(如 CPU、内存、存储)、节点的健康状态、调度策略(如节点亲和性、污点和容忍度),筛选出符合条件的健康节点;
- 节点打分(优选):对筛选后的节点进行打分,打分维度包括节点资源利用率、Pod 分布均匀性、自定义策略等,得分最高的节点为最优节点;
- 绑定 Pod 与节点:将 Pod 与最优节点进行绑定,并通过 API Server 将绑定信息持久化到 etcd 中,同时通知目标节点的 Kubelet 启动 Pod
核心特点:
- 可配置:支持通过配置文件定义调度策略,适配企业的个性化业务需求;
- 可扩展:支持自定义调度器,替代默认的 Kube-scheduler,满足复杂的调度场景;
- 无状态:多实例部署时,通过领导者选举机制选出一个主调度器,避免多实例重复调度。
#Worker 节点 - 数据面:集群的 “执行中心”
数据面组件运行在每一台 Worker 节点上,负责接收控制面的指令,**实际运行容器化应用**,并将容器和节点的运行状态上报给控制面,是 K8s 集群的 “算力载体”。
1、Kubelet
Kubelet 是运行在每台 Worker 节点上的**核心代理程序**,也是 Worker 节点中**唯一能与 API Server 通信的组件,相当于节点的 “管家”,负责节点上所有 Pod 的生命周期管理。
核心功能:
- 获取 Pod 清单:从 API Server、本地文件目录或指定的 HTTPServer 获取 Pod 的配置清单(PodSpec),明确 Pod 的运行参数;
- Pod 生命周期管理:根据 Pod 清单,调用容器运行时创建、启动、停止、删除 Pod,保障 Pod 的正常运行;
- 状态上报:持续将节点的资源状态(CPU、内存、磁盘使用率)、Pod 的运行状态(运行、暂停、故障、终止)上报给 API Server,最终持久化到 etcd 中;
- 健康检查:对 Pod 内的容器执行**存活性检查(Liveness Probe) 和就绪性检查(Readiness Probe),存活性检查失败则重启容器,就绪性检查失败则将容器从服务负载均衡中移除,保障业务可用性;
- 资源抽象:将容器运行时、网络、存储分别抽象为CRI(容器运行时接口)、CNI(容器网络接口)、CSI(容器存储接口),实现与底层基础设施的解耦,支持对接不同的容器运行时、网络插件和存储插件。
核心特点:与节点强绑定,每个 Worker 节点仅有一个 Kubelet 实例,是节点的 “核心守护进程”。
2、Kube-proxy
Kube-proxy 是运行在每台 Worker 节点上的**网络代理程序**,负责 K8s 集群的网络通信和负载均衡,核心职责是为 Pod 和 Service 提供稳定的网络访问能力。
核心功能
- 维护网络规则:通过 API Server 监听集群中 Service 和 Endpoint 的变化,在节点上维护对应的 iptables 或 ipvs 网络规则,实现 Pod 的网络访问;
- TCP/UDP 数据包转发:根据网络规则,将访问 Service 的请求转发到对应的 Pod 上,实现**服务发现**;
- 分布式负载均衡:每个 Worker 节点的 Kube-proxy 都会维护相同的网络规则,实现负载均衡的分布式部署,服务调用无需经过中央负载均衡器,减少网络跳转和单点故障;
- 支持四层负载均衡:基于 TCP/UDP 协议实现负载均衡,是 K8s 七层负载均衡(Ingress)的基础。
3、Container Runtime(容器运行时)
Container Runtime 是容器和镜像的实际操作工具,是 K8s 集群的 “干活者”,负责容器的创建、启动、停止、删除,以及镜像的拉取、存储、管理,在 Kubelet 的指挥下工作。
核心定位:K8s 与底层容器技术的交互层,通过 CRI 接口与 Kubelet 通信,实现解耦。
主流容器运行时
- Docker:最经典的容器运行时,生态丰富,易于使用,适合开发和测试环境;
- containerd:从 Docker 中剥离的核心容器运行时,轻量级、高性能、稳定性高,是 K8s 官方推荐的生产环境容器运行时;
- CRI-O:专为 K8s 设计的轻量级容器运行时,兼容 OCI 标准,适合极简部署场景。
核心功能:镜像管理、容器生命周期管理、容器资源隔离、容器网络配置等。
(4)K8s 核心工作流程:组件协同逻辑
1、节点状态上报:控制面感知集群算力
每台 Worker 节点上的 Kubelet 会以固定时间间隔(默认 10 秒)向 API Server 上报节点的状态信息,包括:
- 节点的硬件资源状态(CPU、内存、磁盘、网络的使用率);
- 节点的健康状态(是否正常运行、是否有硬件故障);
- 节点上已运行的 Pod 状态(运行、暂停、故障等)。
API Server 接收到节点状态后,将其持久化存储到 etcd中,使控制面能实时感知整个集群的算力分布和健康状态。
2、提交资源请求:用户定义期望状态
开发 / 运维人员通过kubectl或Dashboard向 API Server 提交 Pod/Deployment 等资源的配置文件(YAML/JSON),配置文件中定义了应用的期望状态(如 Pod 的数量、资源需求、镜像地址、端口等)。
API Server 对请求进行认证、授权、准入控制,验证通过后,将资源的期望状态持久化到 etcd中。
3、Pod 调度:控制面分配运行节点
Kube-scheduler 通过 API Server 持续监听 etcd 中**未调度的 Pod**资源;
当发现未调度 Pod 时,Kube-scheduler 执行**节点筛选 + 节点打分**的调度流程,为 Pod 选择最优的 Worker 节点;
调度完成后,Kube-scheduler 通过 API Server 将**Pod 与节点的绑定信息**持久化到 etcd 中。
4、容器启动:数据面执行运行指令
目标 Worker 节点的 Kubelet 通过 API Server 监听到**本地节点被绑定了新的 Pod**;
Kubelet 根据 Pod 清单中的配置,通过**CRI 接口**向容器运行时下发**创建容器**的指令;
容器运行时从**Container Registry**拉取指定的容器镜像(若本地已存在则直接使用);
容器运行时基于镜像创建并启动容器,完成 Pod 的部署;
Kubelet 将 Pod 的**启动成功状态**上报给 API Server,最终持久化到 etcd 中。
5、网络配置:实现 Pod 的可访问性
Kube-proxy 通过 API Server 监听到集群中新增了 Pod 和对应的 Service 资源;
Kube-proxy 在本地节点上**创建对应的 iptables/ipvs 网络规则**;
网络规则将 Service 的访问请求转发到对应的 Pod 上,实现 Pod 的**服务发现和负载均衡**,使 Pod 能被集群内或集群外的应用访问。
6、状态监控与故障自愈:保障业务高可用
Kube-controller-manager 通过 API Server 持续监听 etcd 中所有资源的**实际状态**,并与**期望状态**进行对比;
若发现差异(如 Pod 故障停止、节点下线、Pod 数量不足),立即执行**调谐操作**(如重启故障 Pod、调度 Pod 到健康节点、自动扩缩容 Pod 数量);
Kubelet 持续对 Pod 执行**健康检查**,若容器存活性检查失败则自动重启,就绪性检查失败则将其从服务负载均衡中移除;
所有调谐操作的结果都会通过 API Server 上报并持久化到 etcd 中,直至集群的**实际状态与期望状态一致**。
7 核心总结
K8s 的工作流程本质是 **“声明式 API”** 的实现:用户只需定义 “期望状态”,无需关注具体的执行过程,K8s 通过组件的协同工作,自动将集群的实际状态调整为期望状态,实现了 **“运维自动化”**,这也是 K8s 的核心价值所在。
2、etcd
(1)介绍
#核心底层技术:Raft 强一致性算法
Raft 是一种分布式一致性算法,设计目标是替代 Paxos 算法,实现更简单、更易于理解和实现的分布式数据一致性,是 etcd 的核心。
1. Raft 的核心角色
- Leader(领导者):集群中唯一的领导者,负责接收客户端的写请求,将请求同步到所有 Follower 节点,完成数据的持久化;
- Follower(跟随者):集群中的普通节点,接收 Leader 的同步数据,响应 Leader 的心跳请求,不处理客户端的写请求;
- Candidate(候选者):当 Leader 节点故障时,Follower 节点会转换为 Candidate 节点,参与领导者选举,获胜者成为新的 Leader。
2. Raft 的核心流程:领导者选举、日志复制、安全性保障,确保集群中所有节点的数据完全一致。
#etcd 的底层架构
etcd 的底层架构分为四个核心模块,模块间协同工作,实现 etcd 的所有功能:
- HTTP Server:对外提供 HTTP/HTTPS API 接口,处理用户的读写请求,同时处理其他 etcd 节点的同步请求和心跳请求,是 etcd 的 “门户”;
- Store:etcd 的 “业务处理层”,负责处理 etcd 支持的各类功能事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等,是 etcd 大多数 API 功能的具体实现;
- Raft:etcd 的 “核心算法层”,实现 Raft 强一致性算法,负责领导者选举、日志复制、数据同步,保障 etcd 集群的数据一致性;
- WAL(Write Ahead Log,预写式日志):etcd 的 “持久化存储层”,是 etcd 的数据存储方式,所有数据在提交前都会先记录到 WAL 日志中,避免数据丢失。
- Snapshot(快照):为了防止 WAL 日志文件过大,etcd 会定期创建数据状态快照,保存当前的集群状态,可用于快速恢复集群;
- Entry(日志条目):WAL 中存储的具体日志内容,包含客户端的请求信息、数据变更记录等。
#etcd 的版本特性差异
etcd 目前支持V2和V3两个大版本,两个版本在接口设计、存储引擎、性能上有巨大差异,K8s 目前主要使用 etcd V3 版本。
(2)安装
[root@localhost ~]# MIRROR_URL="https://mirrors.aliyun.com/github-release/etcd-io/etcd/LatestRelease/etcd-${ETCD_VERSION}-linux-amd64.tar.gz"
[root@localhost ~]# curl -L "${MIRROR_URL}" -o /tmp/etcd-${ETCD_VERSION}-linux-amd64.tar.gz
[root@etcd ~]# cd /tmp/
[root@etcd tmp]# ls
etcd-v3.5.10-linux-amd64.tar.gz
[root@etcd tmp]# tar -xf etcd-v3.5.10-linux-amd64
[root@etcd tmp]# cd etcd-v3.5.10-linux-amd64/
[root@etcd etcd-v3.5.10-linux-amd64]# cp etcd* /usr/bin/
[root@etcd ~]# etcd --version
etcd Version: 3.5.10
Git SHA: 0223ca52b
Go Version: go1.20.10
Go OS/Arch: linux/amd64
[root@etcd ~]# etcdctl version
etcdctl version: 3.5.10
API version: 3.5
(3)启动服务
[root@etcd ~]# cd /tmp/etcd-v3.5.10-linux-amd64/
[root@etcd etcd-v3.5.10-linux-amd64]# ls
Documentation etcdctl README-etcdctl.md README.md
etcd etcdutl README-etcdutl.md READMEv2-etcdctl.md
[root@etcd etcd-v3.5.10-linux-amd64]# ./etcd #前台运行
#列出成员列表
[root@etcd ~]# etcdctl --endpoints=localhost:2379 member list --write-out=table
+------------------+---------+---------+-----------------------+-----------------------+------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER |
+------------------+---------+---------+-----------------------+-----------------------+------------+
| 8e9e05c52164694d | started | default | http://localhost:2380 | http://localhost:2379 | false |
+------------------+---------+---------+-----------------------+-----------------------+------------+
#写入数据
[root@etcd ~]# etcdctl --endpoints=localhost:2379 put /key1 val1
#读取数据
[root@etcd ~]# etcdctl --endpoints=localhost:2379 get /key1
/key1
val1
# 按 Key 的前缀查询数据
[root@etcd ~]# etcdctl --endpoints=localhost:2379 get --prefix /
/key1
val1
# 只显示键值
[root@etcd ~]# etcdctl --endpoints=localhost:2379 get --prefix / --keys-only
/key1
# watch key 变化
[root@etcd ~]# etcdctl --endpoints=localhost:2379 watch --prefix /
PUT
/name
k1s
PUT
/name
k2s
PUT
/name
k3s
'下面进行添加数据,上面可以动态显示'
[root@etcd ~]# etcdctl --endpoints=localhost:2379 put /name k1s
OK
[root@etcd ~]# etcdctl --endpoints=localhost:2379 put /name k2s
OK
[root@etcd ~]# etcdctl --endpoints=localhost:2379 put /name k3s
OK
(4)灾备
# 声明 环境变量
export ETCDCTL_API=3
export ENDPOINTS=localhost:2379
# 备份
[root@etcd ~]# mkdir -p /data/etcd_backup_dir/
[root@etcd ~]# etcdctl --endpoints=${ENDPOINTS} snapshot save /data/etcd_backup_dir/etcd-snapshot.db
# 还原
[root@etcd ~]# etcdutl --data-dir=/data/etcd/etcd-restore snapshot restore /data/etcd_backup_dir/etcd-snapshot.db
(5)Kubernetes 数据如何保存在 etcd 中
#查看etcd的pod名
[root@master01 ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
etcd-master01 1/1 Running 11 (96s ago) 9d
#进入etcd容器
[root@master01 ~]# kubectl exec -it -n kube-system etcd-master01 -- sh
#定义环境变量
export ETCDCTL_API=3
#登录数据库,查看所有的key
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --keys-only --prefix /
#查看某一个key
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --keys-only --prefix /registry/pods/default/redis-pv-sts-0
#查看某一个key的值
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get /registry/pods/default/redis-pv-sts-0
#会输出 etcd 中存储的这个 ConfigMap 的原始键值数据
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key /etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get /registry/configmaps/default/info
(6)etcd备份脚本
#!/bin/bash
ETCDCTL_PATH='/usr/local/bin/etcdctl'
ENDPOINTS='https://192.168.200.153:2379'
ETCD_DATA_DIR="/var/lib/etcd"
BACKUP_DIR="/var/backups/kube_etcd/etcd-$(date +%Y-%m-%d-%H-%M-%S)"
KEEPBACKUPNUMBER='5'
ETCDBACKUPPERIOD='30'
ETCDBACKUPSCIPT='/usr/local/bin/kube-scripts'
ETCDBACKUPHOUR=''
ETCDCTL_CERT="/etc/ssl/etcd/ssl/admin-master1.pem"
ETCDCTL_KEY="/etc/ssl/etcd/ssl/admin-master1-key.pem"
ETCDCTL_CA_FILE="/etc/ssl/etcd/ssl/ca.pem"
[ ! -d $BACKUP_DIR ] && mkdir -p $BACKUP_DIR
export ETCDCTL_API=2;$ETCDCTL_PATH backup --data-dir $ETCD_DATA_DIR --backup-dir $BACKUP_DIR
sleep 3
{
export ETCDCTL_API=3;$ETCDCTL_PATH --endpoints="$ENDPOINTS" snapshot save $BACKUP_DIR/snapshot.db \
--cacert="$ETCDCTL_CA_FILE" \
--cert="$ETCDCTL_CERT" \
--key="$ETCDCTL_KEY"
} > /dev/null
sleep 3
cd $BACKUP_DIR/../;ls -lt |awk '{if(NR > '$KEEPBACKUPNUMBER'){print "rm -rf "$9}}'|sh
if [[ ! $ETCDBACKUPHOUR ]]; then
time="*/$ETCDBACKUPPERIOD * * * *"
else
if [[ 0 == $ETCDBACKUPPERIOD ]];then
time="* */$ETCDBACKUPHOUR * * *"
else
time="*/$ETCDBACKUPPERIOD */$ETCDBACKUPHOUR * * *"
fi
fi
crontab -l | grep -v '#' > /tmp/file
echo "$time sh $ETCDBACKUPSCIPT/etcd-backup.sh" >> /tmp/file && awk ' !x[$0]++{print > "/tmp/file"}' /tmp/file
crontab /tmp/file
rm -rf /tmp/file
3、Kube-apiserver
Kube-apiserver:高可用部署与请求处理流程
1、高可用部署架构
Kube-apiserver 的高可用是整个 K8s 集群高可用的基础,主流的高可用部署架构为:
负载均衡器(haproxy/Nginx)+ 多台 API Server 实例 + etcd 集群
- 负载均衡器:为多台 API Server 提供统一的访问入口(VIP + 端口),实现请求的负载均衡和故障转移,当某台 API Server 故障时,自动将请求转发到健康的实例;
- 多台 API Server 实例:无状态设计,共享同一个 etcd 集群,所有实例的功能完全一致,可水平扩展;
- etcd 集群:为所有 API Server 实例提供统一的数存储,保障数据一致性。
2、核心请求处理流程
API Server 接收的所有请求(如创建 Pod、查询节点),均遵循以下处理流程:
1. 认证(Authentication):验证请求的身份合法性,支持令牌、证书、用户名密码等多种认证方式;
2. 授权(Authorization):验证请求者是否有操作该资源的权限,主流授权方式为 RBAC(基于角色的访问控制);
3. 准入控制(Admission Control):对请求进行策略检查,如 Pod 安全策略、资源配额检查,通过后才能进入下一步;
4. 数据验证:验证请求的资源配置是否合法(如字段是否存在、值是否符合规范);
5. 数据操作:若为写请求,将数据持久化到 etcd;若为读请求,从 etcd 或本地缓存中读取数据;
6. 返回响应:将处理结果以 JSON/YAML 格式返回给请求者。
4、Kubelet
Kubelet:核心接口与健康检查机制
1、三大核心抽象接口
Kubelet 通过CRI、CNI、CSI三大接口,实现了与底层基础设施的解耦,使 K8s 能对接不同的容器运行时、网络插件和存储插件,这也是 K8s 生态扩展性强的核心原因:
- CRI(Container Runtime Interface,容器运行时接口):Kubelet 与容器运行时的交互接口,定义了容器和镜像的管理规范,支持对接 Docker、containerd、CRI-O 等;
- CNI(Container Network Interface,容器网络接口):Kubelet 与网络插件的交互接口,定义了容器网络的配置规范,支持对接 Calico、Flannel、Weave 等网络插件;
- CSI(Container Storage Interface,容器存储接口):Kubelet 与存储插件的交互接口,定义了容器存储的配置规范,支持对接 Ceph、NFS、云厂商块存储等。
2、两大核心健康检查机制
Kubelet 通过存活性检查和就绪性检查,保障 Pod 内容器的运行状态,是 K8s 故障自愈的核心基础:
存活性检查(Liveness Probe):检查容器是否正常运行,若检查失败,Kubelet 会根据 Pod 的重启策略(RestartPolicy)重启容器,适用于容器卡死、进程崩溃等场景;
就绪性检查(Readiness Probe):检查容器是否准备好接收请求,若检查失败,Kubelet 会将该容器从 Service 的 Endpoint 中移除,不再将请求转发给该容器,直至检查成功,适用于容器启动初期加载数据、依赖服务未就绪等场景。
检查方式:支持 HTTP 请求检查、TCP 端口检查、命令行执行检查三种方式,可根据应用特性灵活配置。
5、Kube-proxy
Kube-proxy 支持iptables和ipvs两种工作模式,均基于 Linux 内核技术实现,核心差异在于性能和功能,目前生产环境主流使用ipvs 模式。
| 特性 | iptables 模式 | ipvs 模式 |
|---|---|---|
| 底层技术 | 基于 Linux iptables(防火墙)规则实现 | 基于 Linux ipvs(虚拟服务器)实现,是 Linux 内核的负载均衡模块 |
| 性能 | 规则匹配为线性遍历,当 Pod 数量过多时,规则数量剧增,性能下降明显 | 规则匹配为哈希表查询,性能更高,支持十万级以上 Pod 的大规模集群 |
| 负载均衡策略 | 仅支持轮询、随机 | 支持轮询、随机、源 IP 哈希、最小连接数等多种策略,灵活性更高 |
| 故障转移 | 依赖 iptables 的超时机制,故障转移速度慢 | 支持主动健康检查,故障转移速度快 |
| 部署复杂度 | 低,无需额外配置 | 稍高,需要加载 Linux ipvs 内核模块 |
6、常用插件
K8s 常用插件:集群功能扩展
K8s 的核心组件仅提供基础的容器编排和管理能力,插件是 K8s 生态的重要组成部分,用于扩展集群的功能,适配企业的实际业务需求。K8s 插件遵循可插拔设计,可根据需要灵活部署和卸载,以下是 K8s 最常用的核心插件:
1、DNS 插件:集群内服务发现
DNS 插件是 K8s 集群的必备插件,负责为集群内的所有资源提供域名解析服务,实现服务发现 —— 让 Pod 能通过域名访问其他 Pod 或 Service,而非通过固定的 IP 地址,解决了 IP 地址动态变化的问题。
主流插件:CoreDNS(K8s 1.13 及以上版本的官方默认 DNS 插件)、Kube-DNS(老版本 K8s 的 DNS 插件);
核心功能:为 Service、Pod、Namespace 等资源分配域名,实现域名到 IP 地址的解析,支持 A 记录、SRV 记录等 DNS 解析类型;
核心域名规则:服务名。命名空间.svc.cluster.local,如 nginx-service.default.svc.cluster.local,集群内的 Pod 可通过该域名直接访问对应的 Service。
2、Ingress Controller:集群外网入口
K8s 的 Service 仅能实现集群内访问或节点端口访问,Ingress Controller 是 K8s 集群的七层负载均衡插件,负责为集群内的服务提供外网访问入口,实现 HTTP/HTTPS 协议的路由转发、域名绑定、SSL 证书配置等功能。
核心组成
Ingress:K8s 的一种资源对象,用于定义 HTTP/HTTPS 的路由规则(如域名到 Service 的映射、路径转发规则);
Ingress Controller:实际执行路由规则的组件,如 Nginx Ingress Controller、Traefik、HAProxy Ingress Controller,主流为 Nginx Ingress Controller。
核心功能:外网访问入口、七层路由转发、域名绑定、SSL 证书管理、负载均衡、限流降级等。
核心价值:替代传统的 Nginx、HAProxy 等七层负载均衡器,实现路由规则的声明式管理,与 K8s 集群深度集成。
3、Metrics Server:集群资源监控
Metrics Server 是 K8s 集群的核心监控插件,负责采集集群节点和 Pod 的资源使用指标,包括 CPU 使用率、内存使用率、磁盘使用率、网络流量等,是 K8s 实现弹性伸缩(HPA)和资源配额的基础。
核心功能:采集节点和 Pod 的资源指标,以 API 形式提供给 K8s 集群;支持通过 kubectl top node/pod 命令实时查看集群资源使用情况;为 HPA(水平 Pod 自动扩缩容)提供指标数据,实现根据资源使用率自动扩缩容 Pod 数量。
核心特点:轻量级、无状态、可水平扩展,采集的数据仅保留短时间(默认 15 分钟),适合实时监控,不适合长期存储。
4、Dashboard:可视化 GUI 管理界面
Dashboard 是 K8s 官方提供的可视化 GUI 管理插件,为 K8s 集群提供图形化的操作和监控界面,替代部分 kubectl 命令行操作,适合新手入门和快速的集群运维。
核心功能:
图形化展示集群内的所有资源(节点、Pod、Service、Deployment、ConfigMap 等)的状态和参数;
支持通过图形界面创建、更新、删除集群资源,无需编写 YAML 配置文件;
提供 Pod 的日志查看、容器终端连接、资源使用监控等功能;
支持 RBAC 权限控制,可为不同用户分配不同的操作权限。
5、其他常用插件
Calico/Flannel:网络插件,负责为 Pod 提供集群内的网络互通能力,实现 Pod 的 IP 地址分配、跨节点通信;
CSI 存储插件:存储插件,如 Ceph-CSI、NFS-CSI,负责为 Pod 提供持久化存储能力,实现数据的持久化保存;
Prometheus+Grafana:监控告警插件,与 Metrics Server 配合,实现集群资源指标的长期存储、可视化展示和告警;
ELK/EFK:日志收集插件,负责采集 Pod 的日志,实现日志的集中收集、存储、检索和分析。
7、CNI
(1)k8s组网
Kubemetes 经典的主机内组网模型是 veth pair+ bridge 的方式 。
当 Kubemetes 调度 Pod 在某个节点上运行时,它会在该节点的 Linux内核中为 Pod 创建 network namespace ,供 Pod 内所有运行的容器使用 。 从容器的角度看, Pod 是具有一个网络接口的物理机器, Pod 中的所有容器都会看到此网络接口 。 因此,每个容器通过 localhost 就能访问同一个 Pod 内的其他容器 。
Kubemetes 使用 veth pair 将容器与主机的网络协议栈连接起来 ,从而使数据包可以进出 Pod。容器放在主机根 network namespace 中 veth pair 的一端连接到 Linux 网桥 ,可让同一节点上的各 Pod 之间相互通信 。
Kubernetes 网络基础原则:
1.每个 Pod 都拥有一个独立的 IP 地址,而且假定所有 Pod 都在一个可以直接连通的、扁平的网络空间中,不管是否运行在同一 Node 上都可以通过 Pod 的 IP 来访问。
2.Kubernetes 中 Pod 的 IP 是最小粒度 IP。同一个 Pod 内所有的容器共享一个网络协议栈,该模型称为 IP-per-Pod 模型。
3.Pod 内部看到的 IP 地址和端口与外部保持一致。同一个 Pod 内的不同容器共享网络,可以通过 localhost 来访问对方的端口,类似同一个 VM 内的不同进程。
4.IP-per-Pod 模型从端口分配、域名解析、服务发现、负载均衡、应用配置等角度看,Pod 可以看作是一台独立的 VM 或物理机。
(2)CNI
CNI 即容器网络接口( Container Network Interface ) 。
在 CNI 标准中,网络插件是独立的可执行文件,被上层的容器管理平台调用 。 网络插件只有两件事情要做:把容器加入网络或把容器从网络中删除。
Kubernetes 使用 CNI 网络插件的工作流程 :
•Kubelet 调用 CRI 创建 pause 容器,生成对应的 network namespace;
•调用网络 driver ;
•CNI driver 根据配置调用具体的 CNI 插件;
•CNI 插件给 pause 容器配置正确的网络, Pod 中的其他容器都是用 pause 容器的网络栈 。
(3)flannel
1)介绍
Flannel 可以为容器提供跨节点网络服务,其模型为集群内所有容器使用一个网络,然后在每个主机上从该网络中划分一个子网 。 flannel 为主机上的容器创建网络时,从子网中划分一个 IP 给容器 。 根据 Kubernetes 的模型,为每个 Pod 提供一个 IP, flannel 的模型正好与之契合 。 而且 flannel 安装方便且简单易用 。
Flannel不同backend 讲解
flannel 通过在每一个节点上启动一个叫 flanneld 的进程,负责每一个节点上的子网划分,并将相关的配置信息(如各个节点的子网网段 、外部 IP 等)保存到 etcd 中,而具体的网络包转发交给具体的 backend 实现 。
flanneld 可以在启动时通过配置文件指定不同的 backend 进行网络通信,目前比较成熟的 backend 有 UDP 、VXLAN 和 Host Gateway 三种。
UDP:早期版本的Flannel使用 UDP 封装完成报文的跨越主机转发,其安全性及性能略有不足。
VXLAN:Linux 内核在在 2012 年底的 v3.7.0之后加入了 VXLAN 协议支持,因此新版本的 Flannel也从 UDP 转换为 VXLAN,VXLAN 本质上是一种 tunnel(隧道)协议,用来基于三层网络实现虚拟的二层网络,目前flannel 的网络模型已经是基于 VXLAN 的叠加(覆盖)网络。
Host-GW:也就是 Host GateWay,通过在 node 节点上创建到达各目标容器地址的路由表而完成报文的转发,因此这种方式要求各 node 节点本身必须处于同一个局域网(二层网络)中,因此不适用于网络变动频繁或比较大型的网络环境,但是其性能较好。
目前, VXLAN 是官方推荐的一种 backend 实现方式; Host Gateway 一般用于对网络性能要求比较高的场景, 但需要基础网络架构的支持 ; UDP 则用于测试及一些比较老的不支持 VXLAN 的 Linux 内核 。
2)UDP模式
#UDP模式
#删除calico组件
[root@master01 ~] # kubectl delete -f calico.yaml
[root@master01 ] # mv /etc/cni/net.d/10-calico.conflist /tmp #每个node都要执行
#导入镜像
[root@master01 ~] # ctr -n k8s.io image import flannel-image/flannel-v0.26.6.img
#部署 flannel 网络插件
kubectl apply -f kube-flannel-udp.yml
# 手工加载内核模块 (每个node都要执行)
modprobe br_netfilter
echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf
#验证是否安装成功
[root@master01 flannel-yaml] # kubectl get pods -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-49nbq 1/1 Running 2 (76s ago) 81s
kube-flannel-ds-jwq6z 1/1 Running 0 81s
kube-flannel-ds-vbnh9 1/1 Running 2 (75s ago) 81s
[root@master01 flannel-yaml] # cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=true
#查看类型
[root@master01 ~] # kubectl logs kube-flannel-ds-49nbq -n kube-flannel | grep udp
I0316 08:34:20.711882 1 main.go:468] Found network config - Backend type: udp
#flanneld 进程启动后,通过 ip addr 命令可以发现节点中多了一个 叫 flannel0 的网络接口 :
[root@master01 ~] # ip a | grep flannel0
14: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc fq_codel state UNKNOWN group default qlen 500
inet 10.244.0.0/32 scope global flannel0
#通过 netstat -ulnp 命令可以看到此时 flanneld 进程监听在 UDP 8285 端口
[root@master01 ~]# ss -nulp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
UNCONN 0 0 192.168.88.10:8285 0.0.0.0:* users:(("flanneld",pid=345895,fd=10),("flanneld",pid=345895,fd=7))
[root@master01 ~] # cat yaml/nginx-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx-dep
name: nginx-dep
spec:
replicas: 3
selector:
matchLabels:
app: nginx-dep
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx-dep
spec:
containers:
- image: www.sxtb.online/app/nginx:1.22.1
name: nginx
[root@master01 ~] # kubectl apply -f yaml/nginx-dep.yaml
deployment.apps/nginx-dep created
[root@master01 ~] # kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-dep-85447f7b85-cwjht 1/1 Running 0 88m 10.244.2.4 worker02 <none> <none>
nginx-dep-85447f7b85-ff6rs 1/1 Running 0 88m 10.244.1.4 worker01 <none> <none>
nginx-dep-85447f7b85-zjsr4 1/1 Running 0 88m 10.244.2.3 worker02 <none> <none>
#观察路由表UDP类型
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tm-dep-598b4f8c45-7tzlp 1/1 Running 0 19m 10.244.1.20 worker01 <none> <none>
tm-dep-598b4f8c45-khmd9 1/1 Running 0 19m 10.244.2.13 worker02 <none> <none>
#两个podA:10.244.1.20访问PodB:10.244.2.13
'1、查看podA的路由表,从src:10.244.1.20 dest:10.244.2.13匹配10.244.0.0通过eth0网卡发送到网关10.244.1.1
root@tm-dep-598b4f8c45-7tzlp:~# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.244.0.0 10.244.1.1 255.255.0.0 UG 0 0 0 eth0
'2、查看worker1的路由表,匹配10.244.0.0规则,发到flannel0的网卡,进行包头封装
'3.发送给 flannel0 接口的 IP 包将被 flanneld 进程接收, flanneld 进程接收 IP 包后在原有的基础上进行 UDP 封包, UDP 封包的形式为 192.168.88.20:8285 → 192.168.88.30:8285 。
#tcpdump -nni ens192 -s0 -vv -w flannel-udp-master-eth0.pcap 使用抓包命令
Frame 147: 126 bytes on wire (1008 bits),
126 bytes captured (1008 bits)Ethernet II, Src: VMware 70:14:25 (00:0c:29:70:14:25), Dst: VMware_11:9e:e7 (00:0c:29:11:9e:e7)
Internet Protocol Version 4, Src: 192.168.88.20, Dst: 192.168.88.30
User Datagram Protocol, Src Port: 8285, Dst Port: 8285
Internet Protocol Version 4, Src: 10.244.1.20, Dst: 10.244.2.13
Internet Control Message Protocol
[root@worker01 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.244.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel0
'4.flanneld 将封装好的 UDP 报文经 ens160 发出,从这里可以看出网络包在通过 ens160 发出前先是加上了 UDP 头( 8 个字节),再加上 IP 头( 20 个字节)进行封装,这也是 flannel0 的 MTU 要比 eth0 的 MTU 小 28 个字节的原因,即防止封包后的以太网帧超过 eth0 的 MTU, 而在经过 eth0 时被丢弃。
'5.网络包经过主机网络从节点 worker-01 到达节点 worker-02。
'6.主机 worker-02 收到 UDP 报文后, Linux 内核通过 UDP 端口号 8285 将包交给正在监听的 flanneld 。
'7. 运行在 worker-02 中的 flanneld 将 UDP 包解封包后得到 IP 包:10.244.1.20 → 10.244.2.13。
'8.解封包后的 IP 包匹配到主机 worker-01 上的路由规则 ( 10.244.1.0 ),内核通过查本机路由表知道应该将 IP 包发送到 cni0 网桥 。
'9.cni0 网桥将 IP 包转发给连接在该网桥上的容器 B ,至此整个流程结束 。 回程报文将按上面的数据流原路返回 。
3)VXLAN模式
#修改 flannel 配置文件
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
#部署 flannel 网络插件
kubectl apply -f kube-flannel-vxlan.yml
#检查运行状态
[root@master01 flannel-yaml]# kubectl get pods -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-c2lfs 1/1 Running 0 2m26s
kube-flannel-ds-pjk7v 1/1 Running 0 2m26s
kube-flannel-ds-vv8gn 1/1 Running 0 2m26s
[root@master01 flannel-yaml]# kubectl logs kube-flannel-ds-c2lfs | grep -i vxlan
error: error from server (NotFound): pods "kube-flannel-ds-c2lfs" not found in namespace "default"
[root@master01 flannel-yaml]# kubectl logs kube-flannel-ds-c2lfs -n kube-flannel | grep -i vxlan
I0316 11:44:46.689645 1 main.go:468] Found network config - Backend type: vxlan
'1.同 UDP Backend 模式,容器 A 中的 IP 包通过容器 A 内的路由表被发送到 cni0 。
'2.到达 cni0 中的 IP 包通过匹配 worker-01 中的路由表发现通往 10.244.1.4 的 IP 包应该交给 flannel.1 接口
'3.flannel.1 收到报文后将按照配置进行封包 。 首先,通过 etcd 得知 10.244.1.4 属于节点 worker-01 ,并得到节点 worker-01 的 IP 。 然后,通过节点 master-01 中的转发表得到节点 worker-01 对应的 MAC 地址,根据 flannel.1 设备创建时的设置参数进行 VXLAN 封包 。
'4.通过 master-01 跟 worker-01 之间的网络连接, VXLAN 包到达 worker-01 的 eth0 接口 。
'5.通过端口 8472, VXLAN 包被转发给 flannel.1 进行解包。
'6.解封装后的 IP 包匹配 worker-01 中的路由 表( 10.244.1.0 ),内核将 IP 包转发给 cni0 。
'7.cni0 将 IP 包转发给连接在 cni0 上的容器 B 。
#抓包查看封装情况
Frame 163: 148 bytes on wire (1184 bits), 148 bytes captured (1184 bits)
Ethernet II, Src: VMware 70:14:25 (00:0c:29:70:14:25), Dst: VMware_11:9e:e7 (00:0c:29:11:9e:e7)
Internet Protocol Version 4, Src: 192.168.88.20, Dst: 192.168.88.30
User Datagram Protocol, Src Port: 34668, Dst Port: 8472
Virtual eXtensible Local Area Network
Ethernet II, Src: ae:8b:c4:49:4b:ac (ae:8b:c4:49:4b:ac), Dst: 5a:84:db:1c:d0:4e (5a:84:db:1c:d0:4e)
Internet Protocol Version 4, Src: 10.244.1.20, Dst: 10.244.2.13
Internet Control Message Protocol
4)host-gw
Host Gateway 简称 host-gw ,从名字中就可以想到这种方式是通过把主机当作网关实现跨节点网络通信的 。 那么 ,具体如何实现跨节点通信呢?与 UDP 和 VXLAN 模式类似, 要使用 host-gw 模式,需要将 flannel 的Backend 中的 Type 参数设置成 “host-gw“
1.同 UDP 、 VXLAN 模式一致,通过容器 A 的路由 表 IP 包到达 cni0 。
2.到达 cni0 的 IP 包匹配到 master-01 中的路由规则( 10.244.1.0 ),并且网关为 172.16.66.33 ,即 worker-01 ,所以内核将 IP 包发送给 worker-01 (172.16.66.33)。
3.IP 包通过物理网络到达主机 worker-01 的 eth0 。
4.到达主机 worker-01 eth0 的 IP 包匹配到 worker-01 中的路由 表( 10.244.1.0 ) , IP 包被转发给cni0 。
5.cni0 将 IP 包转发给连接在 cni0 上的容器 B 。
host-gw 模式下,各个节点之间的跨节点网络通信要通过节点上的路由表实现,因此必须要通信双方所在的宿主机能够直接路由 。 这就要求 flannel host-gw 模式下集群中所有的节点必须处于同一个二层网络内, 这个限制使得 host-gw 模式无法适用于集群规模较大且需要对节点进行网段划分的场景 。 host-gw 的另外一个限制则是随着集群中节点规模的增大,flanneld 维护主机上成千上万条路由表的动态更新也是一个不小的压力 ,因此在路由方式下 ,路由表规则的数量是限制网络规模的一个重要因素。
(4)calico
1)工作原理
Calico 在每一个计算节点利用 Linux 内核的一些能力实现了一个高效的 vRouter 负责数据转发,而每个 vRouter 通过 BGP 把 自己运行的工作负载的路由信息 向整个 Calico 网络传播 。 小规模部署可以直接互联,大规模下可以通过指定的 BGP Route Reflector 完成 。 最终保证所有的工作负载之间的数据流量都是通过 IP 路由的方式完成互联的 。 Calico 节点组网可以直接利用数据中心的网络结构(无论是 L2 还是 L3 ),不需要额外的 NAT 或隧道 。
组件
Felix
Felix 是一个守护程序,作为 agent 运行在托管容器或虚拟机的 Calico 节点上 。 Felix 负责刷新主机路由和 ACL 规则等,以便为该主机上的 Endpoint 正常运行提供所需的网络连接和管理。 进出容器 、 虚拟机和物理主机的所有流量都会遍历 Calico ,利用 Linux 内核原生的路由和 iptables 生成的规则 。
BGP Client (BIRD )
Calico 在每个运行 Felix 服务的节点上都部署一个 BGP Client ( BGP 客户端)。 BGP 客户端的作用是读取 Felix 编写到内核中的路由信息,由 BGP 客户端对这些路由信息进行分发 。 具体来说,当 Felix 将路由插入 Linux 内核 FIB 时, BGP 客户端将接收它们,并将它们分发到集群中的其他工作节点 。
Route Reflector
简单的 BGP 可能成为较大规模部署的性能瓶颈,因为它要求每个 BGP 客户端连接到网状拓扑中的每一个其他 BGP 客户端。 随着集群规模的增大,一些设备的路由表甚至会被撑满 。
因此,在较大规模的部署中, Calico 建议使用 BGP Route Reflector (路由器反射器)。互联网中通常使用 BGP Route Reflector 充当 BGP 客户端连接的中心点,从而避免与互联网中的每个 BGP 客户端进行通信。
2) IPIP 隧道模式
IPIP 模式(三层隧道封装)
#原理:
当 Pod 流量跨子网或底层网络不支持 BGP 时,Calico 将 Pod 的原始 IP 包封装在宿主机 IP 包内(IP-in-IP),通过 tunl0 隧道接口传输,对端节点解封装后转发。
#特点:
兼容性能,适用于公有云、跨子网等复杂网络。
默认启用CrossSubnet模式:同子网内使用 BGP 直连,跨子网才触发 IPIP 封装,兼顾性能与兼容性
封装开销较小(约 20 字节 IP 头),性能优于 VXLAN
#适用场景:
节点分布在不同子网、VPC 或网络策略禁止 BGP 的环境
Calico 可以创建并管理一个 3 层平面网络,为每个工作负载分配一个完全可路由的 IP 地址 。 工作负载可以在没有 IP 封装或 NAT 的情况下进行通信,以实现裸机性能,简化故障排除和提供更好的互操作性 。 我们称这种网络管理模式为 vRouter 模式 。 vRouter 模式直接使用物理机作为虚拟路由器,不再创建额外的隧道 。 然而在需要使用 overlay 网络的环境中, Calico 也提供了 IP-in-IP (简称 ipip )的隧道技术 。
和其他 overlay 模式一样 , ipip 是在各节点之间 “架起” 一个隧道,通过隧道两端节点上的容器网络连接,实现机制简单说就是用 IP 包头封装原始 IP 报文 。 启用 ipip 模式时 ,Calico 将在各个节点上创建一个名为 tunl0 的虚拟网络接口
#部署 calico 网络插件
kubectl apply -f calico-ipip.yaml
[root@master01 ~]# ip a | grep tunl0
6: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
inet 10.244.241.64/32 scope global tunl0
[root@master01 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node1-6f686bc547-2vfbq 1/1 Running 0 39s 10.244.5.8 worker01 <none> <none>
node1-6f686bc547-gm2bz 1/1 Running 0 39s 10.244.5.7 worker01 <none> <none>
node2-5d995b6d66-x9gp6 1/1 Running 0 42s 10.244.30.68 worker02 <none> <none>
#worker01节点的10.244.5.8 ,访问worker02节点的10.244.30.68
[root@master01 ~]# kubectl exec -it node1-6f686bc547-2vfbq -- bash
[root@node1-6f686bc547-2vfbq /]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
#可以看到pod从自己的eth0发出请求,匹配规则第一条,到tunl0进行包头封装
src-ip 192.168.88.20 dest-ip 192.168.88.30
[root@worker01 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.244.30.64 192.168.88.30 255.255.255.192 UG 0 0 0 tunl0
192.168.88.0 0.0.0.0 255.255.255.0 U 100 0 0 ens160
#通过抓包可以看到封装的Src: 192.168.88.20, Dst: 192.168.88.30 Src Port: 34668, Dst Port: 8472
Frame 163: 148 bytes on wire (1184 bits), 148 bytes captured (1184 bits)
Ethernet I, Src: VMware 70:14:25 (00:0c:29:70:14:25), Dst: WMware_11:9e:e7 (00:0c:29:11:9e:7)
Internet Protocol Version 4, Src: 192.168.88.20, Dst: 192.168.88.30
User Datagram Protocol, Src Port: 34668, Dst Port: 8472
Virtual eXtensible Local Area Network
Ethernet I, Src: ae:8b:c4:49:4b:ac (ae:8b:c4:49:4b:ac), Dst: 5a:84:db:1c:d0:4e (5a:84:db:1c:d0:4e)
Internet Protocol Version 4, Src: 10.244.1.20, Dst: 10.244.2.13
Internet Control Message Protocol
3)BGP模式
#BGP 模式(无封装,直连路由)
#原理:
每个节点运行 BGP 客户端(BIRD),将本节点上 Pod 的 IP 路由信息通过 BGP 协议广播给集群内其他节点,所有节点据此更新内核路由表,实现 直接三层转发,无需隧道或封装。
#特点:
无额外封装开销,性能最佳,延迟最低。
要求节点之间 三层网络可达(即支持 IP 路由)。
不依赖 VXLAN 或 IPIP 等 Overlay 技术。
#适用场景:
节点位于同一 VPC 或同一二层/三层网络中,追求高性能的生产环境
#Pod 1 访问 Pod 2 流程如下
1、数据包从 Pod1 出到达Veth Pair另一端(宿主机上,以cali前缀开头)
2、宿主机根据路由规则,将数据包转发给下一跳(网关)
3、到达 Node2,根据路由规则将数据包转发给 cali 设备,从而到达 Pod2。
其中,这里最核心的 下一跳 路由规则,就是由 Calico 的 Felix 进程负责维护的。这些路由规则信息,则是通过 BGP Client 中 BIRD 组件,使用 BGP 协议来传输。
不难发现,Calico 项目实际上将集群里的所有节点,都当作是边界路由器来处理,它们一起组成了一个全连通的网络,互相之间通过 BGP 协议交换路由规则。这些节点,我们称为 BGP Peer。
而 Flannel host-gw 和 Calico 的唯一不一样的地方就是当数据包下一跳到达node2节点容器时发生变化,并且出数据包也发生变化,知道它是从veth的设备流出,容器里面的数据包到达宿主机上,这个数据包到达node2之后,它又根据一个特殊的路由规则,这个会记录目的通信地址的cni网络,然后通过cali设备进去容器,这个就跟网线一样,数据包通过这个网线发到容器中,这也是一个二层的网络互通才能实现。
8、pod调度策略
(1)nodeSelector-节点标签
#nodeSelector 提供了一种最简单的方法将 Pod 约束调度到具有特定标签的节点上,这个特性工作中经常会用到,现在需要部署 Redis 或MySQL,把这些应用调度到有 SSD 磁盘的节点上。
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: redis
replicas: 1
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis-server
image: redis:5-alpine
nodeSelector:
disktype: ssd
#调度器在调度时会检查节点是否有如下的 Tag: disktype: ssd ,如果没有则调度失败,pod 会进入 Pending 状态
[root@master01 ~] # kubectl get node --show-labels #查看节点的标签
[root@master01 ~] # kubectl label nodes worker01 disktype=ssd #给node节点打标签
[root@master01 ~] # kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-cache-6587556b4f-79w6t 1/1 Running 0 105s
#可以看到调度成功
(2)nodeAffinity(节点亲和性)
节点亲和性概念上类似于 nodeSelector, 可以根据节点上的标签来约束 Pod 可以调度到哪些节点上。
#节点亲和性有两种:
•requiredDuringSchedulingIgnoredDuringExecution: 调度器只有在规则被满足的时候才能执行调度。此功能类似于 nodeSelector, 但其语法表达能力更强。
•preferredDuringSchedulingIgnoredDuringExecution: 调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该 Pod。
#支持的操作符:
| 操作符 | 行为 |
|---|---|
| In | 标签值存在于提供的字符串集中 |
| NotIn | 标签值不包含在提供的字符串集中 |
| Exists | 对象上存在具有此键的标签 |
| DoesNotExist | 对象上不存在具有此键的标签 |
#required affinity(硬亲和)只调度到指定标签的节点上,节点资源不够就失败
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: redis
replicas: 1
template:
metadata:
labels:
app: redis
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: redis-server
image: redis:5-alpine
nodeSelector:
disktype: ssd
#将pod 调度到具有 disktype: ssd 的节点上
[root@master01 ~] # kubectl get pods -o wide #可以看到全部调到了worker01的节点上
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cache-58b56bfb45-8jb52 1/1 Running 0 7s 10.244.5.12 worker01 <none> <none>
redis-cache-58b56bfb45-dfpds 1/1 Running 0 6s 10.244.5.13 worker01 <none> <none>
redis-cache-58b56bfb45-x24tk 1/1 Running 0 5s 10.244.5.14 worker01 <none> <none>
#preferred 软亲和 优先调度到指定标签节点,节点资源不足,就调度到其他节点
[root@master01 ~] # cat redis-ruan.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
spec:
selector:
matchLabels:
app: redis
replicas: 5
template:
metadata:
labels:
app: redis
spec:
imagePullSecrets:
- name: harbor-secret
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: redis-server
image: harbor.sxtb.online/library/redis:alpine
resources:
limits:
memory: 1Gi
cpu: 1
requests:
memory: 256Mi
cpu: 100m
[root@master01 ~] # kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cache-99ff8bc4b-44jgz 1/1 Running 0 2s 10.244.5.39 worker01 <none> <none>
redis-cache-99ff8bc4b-72k2v 1/1 Running 0 2s 10.244.5.40 worker01 <none> <none>
redis-cache-99ff8bc4b-8x558 1/1 Running 0 2s 10.244.30.74 worker02 <none> <none>
(3)podAffinity (pod反亲和性)
'匹配节点上已存在的pod的标签是否有和 yaml文件内定义的标签的pod,如果有,就在此节点创建新pod
#根据标签匹配
不是看名字,是看 label
#拓扑域 topologyKey 按什么维度凑一起:
kubernetes.io/hostname = 同一个节点
topology.kubernetes.io/zone = 同一个可用区
#规则
requiredDuringSchedulingIgnoredDuringExecution:必须满足(硬亲和)
preferredDuringSchedulingIgnoredDuringExecution:尽量满足(软亲和)
#podAffinity(pod 亲和性)硬亲和
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx
image: nginx
resources:
limits:
memory: 1Gi
cpu: 1
requests:
memory: 256Mi
cpu: 100m
#podAffinity(pod 亲和性)软亲和
spec:
imagePullSecrets:
- name: harbor-secret
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: "kubernetes.io/hostname"
#测试原理,创建一个pod在worker1,创建一个pod在worker2(带app:redis标签),创建的每一个新pod都会在worker2,如果找不到标签,硬亲和就penning,软亲和就按分数随机调度
(4)podAntiAffinity(pod反亲和性)
'匹配节点上已存在的pod的标签是否有和 yaml文件内定义的标签的pod,如果没有,就在此节点创建新pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: "kubernetes.io/hostname"
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 10
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx
image: nginx
resources:
limits:
memory: 1Gi
cpu: 1
requests:
memory: 256Mi
cpu: 100m
#测试原理,创建一个pod在worker1,创建一个pod在worker2(带app:redis标签),创建的每一个新pod都会在worker1
(5)taint and toleration
#污点和容忍
污点:给节点 “贴标签”,意思是:我这个节点有 “毛病”,不想让随便的 Pod 跑上来。
容忍:给 Pod “开权限”,意思是:我不怕这个 “毛病”,我可以跑在这个节点上。
核心逻辑:节点有污点,Pod 必须有对应容忍才能跑上去。
# 格式:key=value:effect
kubectl taint nodes 节点名 key=value:effect
'key=value:自定义标识(比如 env=prod、role=master)
'effect:触发的规则(核心):
NoSchedule:不调度 → 没有容忍的 Pod 绝对不能来
PreferNoSchedule:尽量不调度 → 尽量不让 Pod 来,实在没节点也能来
NoExecute:不执行 + 驱逐 → 不仅新 Pod 不能来,已运行的无容忍 Pod 会被赶走
#容忍的结构(Pod 上配置)
spec:
tolerations:
- key: "env" # 匹配污点的 key
operator: "Equal" # 匹配方式(Equal=等于,Exists=存在即可)
value: "test" # 匹配污点的 value
effect: "NoSchedule"# 匹配污点的 effect
tolerationSeconds: 3600 # 仅对 NoExecute 生效:容忍3600秒后驱逐
# 查看节点污点
kubectl describe nodes master-01 | grep -i taint
#将 Master 也当作 Node 使用
kubectl taint node xxx-nodename node-role.kubernetes.io/master-
#将 Master 恢复成 Master Only 状态
kubectl taint node xxx-nodename node-role.kubernetes.io/master="":NoSchedule
#给worker1加污点,禁止调度到本机
[root@master01 ~] # kubectl taint node worker01 nodetype=gpu:NoSchedule
node/worker01 tainted
#创建新pod测试,只会调度到worker2
[root@master01 ~] # kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cache-6bbb957959-ftcb8 1/1 Running 0 7s 10.244.30.77 worker02 <none> <none>
redis-cache-6bbb957959-t9ssk 1/1 Running 0 6s 10.244.30.78 worker02 <none <none>
redis-cache-6bbb957959-v9lzs 1/1 Running 0 8s 10.244.30.76 worker02 <none> <none>
#给worker2设置PreferNoSchedule污点,即使都有污点,也可以调度
[root@master01 ~] # kubectl taint node worker02 nodetype=gpu:PreferNoSchedule
node/worker02 tainted
[root@master01 ~] # kubectl apply -f nginx-node2.yaml
deployment.apps/node2 created
[root@master01 ~] # kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node2-6b7d54c8db-p8w9p 1/1 Running 0 6s 10.244.30.79 worker02 <none> <none>
redis-cache-6bbb957959-ftcb8 1/1 Running 0 4m12s 10.244.30.77 worker02 <none> <none>
redis-cache-6bbb957959-t9ssk 1/1 Running 0 4m11s 10.244.30.78 worker02 <none> <none>
redis-cache-6bbb957959-v9lzs 1/1 Running 0 4m13s 10.244.30.76 worker02 <none> <none>
#修改worker2的容忍策略为NoExecute,会驱逐所有已创建的pod
[root@master01 ~] # kubectl taint node worker02 nodetype-
node/worker02 untainted
[root@master01 ~] # kubectl taint node worker02 nodetype=gpu:NoExecute
node/worker02 tainted
[root@master01 ~] # kubectl get pods
NAME READY STATUS RESTARTS AGE
node2-6b7d54c8db-knsbh 0/1 Pending 0 6s
redis-cache-6bbb957959-j6fdw 0/1 Pending 0 6s
redis-cache-6bbb957959-jczt9 0/1 Pending 0 6s
redis-cache-6bbb957959-l7578 0/1 Pending 0 6s
#测试容忍策略
完全匹配
Bash
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: "nodetype"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
[root@master01 ~] # kubectl apply -f nginx-node2.yaml
deployment.apps/node2 configured
[root@master01 ~] # kubectl get pods -o wide #可以发现node2已经运行在worker1节点上了
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
node2-bf4f6b4c5-qq5sj 1/1 Running 0 10s 10.244.5.45 worker01 <none> <none>
#匹配任意 taint value
#operator 为 Exists
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: "nodetype"
operator: "Exists"
value: ""
effect: "NoSchedule"
#匹配任意 taint effect
spec:
containers:
- name: nginx
image: nginx
tolerations:
- key: "nodetype"
operator: "Equal"
value: "gpu"
effect: ""
# 标记 worker-01 为不可调度
kubectl cordon worker-01
# 恢复 worker-01 可调度
kubectl uncordon worker-01
# 基础用法:排空 node-name 节点,忽略DaemonSet(DaemonSet Pod 会自动重建,无需驱逐)
kubectl drain node-name --ignore-daemonsets
# 常用完整参数(应对实际场景)
kubectl drain worker-01 \
--ignore-daemonsets \ # 忽略DaemonSet(必加,否则会因DaemonSet Pod无法驱逐失败)
--delete-emptydir-data \ # 允许删除挂载emptyDir的Pod(emptyDir数据会丢失,需确认)
--timeout=10m \ # 超时时间(默认5分钟,复杂场景可延长)
--grace-period=30 # 给Pod优雅退出的时间(默认30秒)
9、认证及鉴权
(1)认证
Kubernetes API Server 组件是 Kubernetes 集群资源操作的唯一入口,它通过 HTTP RESTful 的形式暴露服务,允许不同的用户、外部组件等访问它。我们使用 curl 命令去模拟访问 apisever 请求过程中,发生了什么。
#curl https://192.168.88.10:6443/api/v1/namespaces -k
{
"kind": "Status", // K8s API返回的类型标识:表示这是一个"操作状态"响应(而非资源数据)
"apiVersion": "v1", // K8s API的版本号:v1是核心API组的稳定版本
"metadata": {}, // 元数据字段:该类型响应无额外元数据,故为空
"status": "Failure", // 操作结果:Failure表示本次API请求执行失败(成功则为Success)
"message": "namespaces is forbidden: User \"system:anonymous\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope", // 详细错误描述:匿名用户(system:anonymous)无权在集群范围列出(list)namespaces资源
"reason": "Forbidden", // 错误原因:Forbidden(权限禁止),属于403错误的核心标识
"details": { // 错误详情:补充说明具体受影响的资源
"kind": "namespaces" // 受影响的资源类型:本次请求的是namespaces(命名空间)资源
},
"code": 403 // HTTP状态码:403 = 禁止访问(客户端有请求权限,但服务器拒绝执行)
}
#认证方式
#1. 客户端证书认证(X.509 Certificate)
这是 K8s 最核心、最常用的认证方式,适用于集群组件(如 kubelet、kube-controller-manager) 和管理员级别的用户。
'核心原理
基于公钥 / 私钥(PKI)体系,API Server 配置信任的 CA 根证书;
用户 / 组件携带「由集群 CA 签名的客户端证书」访问 API Server,API Server 验证证书合法性后识别身份。
'典型使用场景
kubectl默认使用证书认证(证书文件通常在~/.kube/config中);
Master 节点组件(kube-scheduler、kube-controller-manager)访问 API Server;
给集群添加新的管理员用户(生成证书并配置到 kubeconfig)。
'关键命令 / 配置示例
'查看kubectl当前使用的证书信息(kubeconfig中的client-certificate)
kubectl config view --minify | grep client-certificate
'用证书通过curl访问API Server(你之前的问题可这样解决)
curl -k \
--cert /etc/kubernetes/pki/apiserver-kubelet-client.crt \ # 客户端证书
--key /etc/kubernetes/pki/apiserver-kubelet-client.key # 私钥
https://192.168.88.10:6443/api/v1/namespaces
#2. ServiceAccount(服务账户)
专为Pod 内的应用程序设计的认证方式,是 Pod 访问 API Server 的标准方式(无需手动管理证书 /token)。
'核心原理
每个 Namespace 默认有default ServiceAccount;
创建 Pod 时,K8s 自动挂载 ServiceAccount 的 token、CA 证书到 Pod 的/var/run/secrets/kubernetes.io/serviceaccount/目录;
Pod 内应用通过该 token 访问 API Server,API Server 验证 token 合法性。
'典型使用场景
微服务 Pod 访问 K8s API(如查询 Pod 列表、创建 ConfigMap);
运维工具(如 Prometheus、Flux)以 Pod 形式部署,通过 ServiceAccount 访问集群。
'实操示例
'1. 在Pod内查看自动挂载的ServiceAccount token
kubectl exec -it <pod-name> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
'2. 在Pod内用token访问API Server
kubectl exec -it <pod-name> -- curl -k \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
https://kubernetes.default.svc/api/v1/namespaces
#3. 静态 Token 认证(Static Token File)
基于「预定义的 token 列表文件」的简单认证方式,适用于测试环境或临时用户(生产环境不推荐)。
#核心原理
提前创建一个包含「token: 用户名:用户组」的静态文件;
API Server 启动时指定--token-auth-file参数加载该文件;
用户访问时在请求头携带Authorization: Bearer <token>,API Server 匹配文件中的 token 验证身份。
配置示例
'1. 创建静态token文件(/etc/kubernetes/tokens.csv)
echo "abc123,user1,group1" > /etc/kubernetes/tokens.csv
'2. 修改API Server启动参数(添加--token-auth-file=/etc/kubernetes/tokens.csv)
'3. 用token访问API Server
curl -k -H "Authorization: Bearer abc123" https://192.168.88.10:6443/api/v1/namespaces
#4. OIDC 认证(OpenID Connect)
适用于企业级身份集成,对接外部身份系统(如 LDAP、Keycloak、Azure AD、GitHub),支持单点登录(SSO)。
'核心原理
基于 OIDC 协议(OAuth 2.0 的扩展),API Server 作为 OIDC 客户端;
用户先在外部身份系统认证,获取 JWT 格式的 ID Token;
携带该 Token 访问 API Server,API Server 验证 Token 合法性并映射为 K8s 用户。
'典型使用场景
企业内部员工通过统一身份平台访问 K8s 集群;
多集群统一身份认证。
(2)鉴权
#1. RBAC(基于角色的访问控制)
这是 K8s 1.8 + 默认且推荐 的鉴权方式,也是生产环境的标配,灵活性极高,能精细化控制用户 / 服务账户的权限。
'核心原理
Role:命名空间级别的权限集合(仅对某一个 Namespace 生效);
ClusterRole:集群级别的权限集合(对所有 Namespace 生效,或操作集群级资源如nodes、namespaces);
RoleBinding:将 Role 绑定到用户 / ServiceAccount(仅在当前 Namespace 生效);
ClusterRoleBinding:将 ClusterRole 绑定到用户 / ServiceAccount(集群范围生效)。
'典型使用场景
给开发人员授予「仅查看测试环境 Namespace 下的 Pod」权限;
给 Prometheus 的 ServiceAccount 授予「集群范围查看所有 Pod / 节点指标」的权限;
限制运维人员仅能操作指定 Namespace 的资源,无法修改集群级配置。
'实操示例
'1. 创建ClusterRole(集群级权限:允许查看所有namespaces)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- apiGroups: [""] # 核心API组(无组名)
resources: ["namespaces"] # 要操作的资源
verbs: ["get", "list"] # 允许的操作(查看/列出)
'2. 创建ClusterRoleBinding(将ClusterRole绑定到默认ServiceAccount)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: namespace-reader-binding
subjects:
- kind: ServiceAccount
name: default # 绑定的ServiceAccount名称
namespace: default # ServiceAccount所在Namespace
roleRef:
kind: ClusterRole
name: namespace-reader # 关联上面创建的ClusterRole
apiGroup: rbac.authorization.k8s.io
'验证权限:用default ServiceAccount的token访问namespaces(此时不会报403)
TOKEN=$(kubectl get secrets -o jsonpath='{.items[?(@.metadata.annotations["kubernetes.io/service-account.name"]=="default")].data.token}' | base64 -d)
curl -k -H "Authorization: Bearer $TOKEN" https://192.168.88.10:6443/api/v1/namespaces
#2. Node 鉴权(Node Authorization)
专为kubelet设计的专用鉴权方式,用于控制 kubelet 仅能执行自身职责相关的操作(如上报节点状态、管理本节点的 Pod)。
'核心原理
API Server 内置 Node 鉴权规则,仅允许 kubelet 访问与自身节点相关的资源;
kubelet 通过 X.509 证书认证后,Node 鉴权会校验其请求是否符合节点管理的权限范围。
'典型使用场景
kubelet 向 API Server 上报节点健康状态;
kubelet 创建 / 删除本节点上的 Pod;
禁止 kubelet 修改其他节点的资源(如操作其他节点的 Pod)。
#3. ABAC(基于属性的访问控制)
早期的鉴权方式,通过「属性规则文件」定义权限,灵活性低且难以维护,生产环境已被 RBAC 取代(仅兼容老版本)。
'核心原理
提前编写 JSON 格式的规则文件,每个规则包含「用户、资源、操作、是否允许」等属性;
API Server 启动时指定--authorization-mode=ABAC --authorization-policy-file=/etc/kubernetes/abac-policy.json;
鉴权时匹配规则文件中的属性,判断是否允许操作。
'示例规则文件(abac-policy.json)
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "user1", // 认证后的用户名
"resource": "namespaces", // 资源类型
"verb": "list", // 操作类型
"namespace": "*", // 所有命名空间
"allow": true // 允许该操作
}
}
#4. Webhook 鉴权(Webhook Authorization)
适用于企业级自定义权限系统,将鉴权逻辑外包给外部服务(如企业内部的权限中心)。
'核心原理
API Server 接收到已认证的请求后,向外部 Webhook 服务发送鉴权请求(包含用户、资源、操作等信息);
外部服务返回「允许 / 拒绝」的结果,API Server 根据结果决定是否执行操作。
'典型使用场景
企业已有统一的权限管理平台,需对接 K8s 实现权限统一管控;
需实现 RBAC 无法满足的复杂鉴权规则(如基于时间、IP、资源标签的权限控制)。
#5. AlwaysAllow/AlwaysDeny(测试 / 特殊场景)
AlwaysAllow:允许所有已认证的请求(仅用于测试环境,禁用安全校验);
AlwaysDeny:拒绝所有请求(仅用于调试,无实际生产价值)。
(3)user+X509认证+RBAC鉴权
#user(用户账户)+X509认证+RBAC鉴权
#1、使用 OpenSSL 命令创建 X509 证书来创建一个 User
'生成私钥
[root@master01 ~] # openssl genrsa -out dev-user.key 2048
'使用我们刚刚创建的私钥创建一个证书签名请求文件:dev-user.csr,要注意需要确保在-subj参数中指定用户名和组(CN表示用户名 common name ,O表示组 orgnazation ):
[root@master01 ~] # openssl req -new -key dev-user.key -out dev-user.csr -subj "/CN=dev-user/O=developer"
'然后找到我们 Kubernetes 集群的 CA 证书,如果使用的是 kubeadm 安装的集群,CA 相关证书位于 /etc/kubernetes/pki/ 目录下面,我们会利用该目录下面的 ca.crt 和 ca.key两个文件来批准上面的证书请求。生成最终的证书文件,我们这里设置证书的有效期为 3650 天:
[root@master01 ~] # openssl x509 -req -in dev-user.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out dev-user.crt -days 3650
'查看证书文件,验证证书是否是CA签发的'
[root@master01 ~] # ls
dev-user.crt dev-user.csr dev-user.key
[root@master01 ~] # openssl verify -CAfile /etc/kubernetes/pki/ca.crt dev-user.crt
dev-user.crt: OK
'使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context):
[root@master01 ~] # kubectl config set-credentials dev-user --client-certificate=dev-user.crt --client-key=dev-user.key --embed-certs=true
--embed-certs=true 是 kubectl config set-credentials 命令的关键选项,作用是:将指定的客户端证书(dev-user.crt)和私钥(dev-user.key)的内容直接嵌入到 kubeconfig 文件中,而非仅记录证书 / 私钥的文件路径。
'为这个用户设置新的 Context,我们这里指定特定的一个 namespace:
[root@master01 ~] # kubectl config set-context dev-user-context --cluster=kubernetes --namespace=dev-project --user=dev-user
'使用指定上下文,执行命令'
[root@master01 ~] # kubectl get pods --context=dev-user-context
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "dev-project"
#2、创建角色
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-user-role
namespace: dev-project
rules:
- apiGroups: ["", "apps"] #其中Pod属于core这个组,用空字符,Deployment和ReplicaSet都属于apps组
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] #所有的操作方法也可以使用['*']
[root@master01 ~] # kubectl create ns dev-project
[root@master01 ~] # kubectl apply -f x509-role.yaml
#3、创建角色权限绑定)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-user-rolebinding
namespace: dev-project
subjects: # 绑定对象(Subjects):指定"谁"将获得权限(可以是User/ServiceAccount/Group)
- kind: User # 绑定对象类型:User(用户,对应kubectl config配置的dev-user)
name: dev-user # 用户名:必须和认证时的用户名一致(如证书中的CN字段)
apiGroup: "" # 用户所属的API组:普通用户填空字符串(RBAC的默认组)
roleRef: # 角色引用(RoleRef):指定"绑定什么权限"(关联已创建的Role/ClusterRole)
kind: Role # 引用的角色类型:Role(仅对dev-project命名空间生效),若为集群级则填ClusterRole
name: dev-user-role # 引用的Role名称:必须和已创建的Role名称完全一致
apiGroup: rbac.authorization.k8s.io # 角色所属的API组:RBAC固定为rbac.authorization.k8s.io
[root@master01 ~] # kubectl apply -f x509-rolebounding.yaml
#4、验证
'使用 dev-user-context 上下文来操作集群
[root@master01 ~] # kubectl -n dev-project get pods --context=dev-user-context
[root@master01 ~] # kubectl -n dev-project get secrets --context=dev-user-context
(4)SA+token认证+RBAC鉴权
创建 SA 时:K8s 自动生成一个包含 JWT Token 的 Secret,Token 里包含 SA 的名称、命名空间等身份信息;
创建 Pod 时:K8s 自动把这个 Secret 挂载到 Pod 内部的固定路径(`/var/run/secrets/kubernetes.io/serviceaccount/token`);
Pod 访问 API Server 时:应用携带这个 Token 在请求头(`Authorization: Bearer <Token>`)中,API Server 会:
- 验证 Token 的签名(由 K8s 自己的密钥签发,无需 CA 证书);
- 解析 Token 中的 SA 身份(如`system:serviceaccount:dev-project:dev-sa`);
- 执行 RBAC 鉴权(判断这个 SA 是否有权限执行该操作)。
# 1. 查看dev-sa对应的Secret(K8s自动创建)
kubectl get secrets -n dev-project | grep dev-sa
输出示例:dev-sa-token-xxxx kubernetes.io/service-account-token 3 10m
# 2. 解码Token(查看SA的身份信息)
SECRET_NAME=$(kubectl get secrets -n dev-project -o jsonpath='{.items[?(@.metadata.annotations["kubernetes.io/service-account.name"]=="dev-sa")].metadata.name}') kubectl get secret $SECRET_NAME -n dev-project -o jsonpath='{.data.token}' | base64 -d # 输出的Token是JWT格式,可去https://jwt.io/解码,能看到SA的身份: # "sub": "system:serviceaccount:dev-project:dev-sa" → 这就是SA的唯一标识
# 3. 用Token访问API Server(无需证书,仅需Token)
TOKEN=$(kubectl get secret $SECRET_NAME -n dev-project -o jsonpath='{.data.token}' | base64 -d) curl -k -H "Authorization: Bearer $TOKEN" https://192.168.88.10:6443/api/v1/namespaces/dev-project/pods
#ServiceAccount(服务用户)+token认证+鉴权
#1、创建一个 ServiceAccount用户
[root@master01 ~] # kubectl -n default create sa ns-user
#2、然后新建一个 Role角色
[root@master01 ~] # vim sa-role.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: ns-user-role
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
- apiGroups: ["", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
[root@master01 ~] # kubectl apply -f sa-role.yaml
#3、然后创建一个 RoleBinding 对象
[root@master01 ~] # cat sa-rolebunding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: namespace-user-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: ns-user
roleRef:
kind: Role
name: ns-user-role
apiGroup: rbac.authorization.k8s.io
[root@master01 ~] # kubectl apply -f sa-rolebunding.yaml
#4、创建长期的token
[root@master01 ~] # vim sa-token.yaml
apiVersion: v1
kind: Secret
metadata:
name: ns-user-token # 自定义Secret名称
namespace: default
annotations:
kubernetes.io/service-account.name: ns-user # 关联ns-user这个SA
type: kubernetes.io/service-account-token
[root@master01 ~] # kubectl apply -f sa-token.yaml
'查看token 一键解码Token(推荐)
[root@master01 ~] # kubectl get secret ns-user-token -n default -o jsonpath='{.data.token}' | base64 -d
'获取token'
[root@master01 ~] # TOKEN=$(kubectl get secret ns-user-token -n default -o jsonpath='{.data.token}' | base64 -d)
'添加 SA 凭证到 config
[root@master01 ~] # kubectl config set-credentials ns-user-sa --token=$TOKEN
'创建上下文(关联集群 + SA 凭证 + 命名空间)'
[root@master01 ~] # kubectl config set-context ns-user-context --cluster=kubernetes --user=ns-user-sa --namespace=default
--cluster=kubernetes # 你的集群名称(可通过kubectl config get-clusters查看)
#4、验证
'用Token访问API Server,请求查看default命名空间的Pod
[root@master01 ~]# curl -k -H "Authorization: Bearer $TOKEN" https://192.168.88.10:6443/api/v1/namespaces/default/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "340889"
},
"items": []
}
'切换上下文,验证 切换到该上下文(后续 kubectl 以 ns-user 身份执行)
[root@master01 ~] # kubectl config get-contexts #查看上下文
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
dev-user-context kubernetes dev-user dev-project
kubernetes-admin@kubernetes kubernetes kubernetes-admin default
* ns-user-context kubernetes ns-user-sa default
[root@master01 ~] # kubectl config use-context ns-user-context #切换上下文
[root@master01 ~] # kubectl get pods
NAME READY STATUS RESTARTS AGE
test-pod 0/1 ImagePullBackOff 0 29m
[root@master01 ~] # kubectl get secrets
Error from server (Forbidden): secrets is forbidden: User "system:serviceaccount:default:ns-user" cannot list resource "secrets" in API group "" in the namespace "default"
(5)SA(集群)+token+RBAC
#1、创建SA
'在 default 命名空间创建 SA(SA 本身是命名空间级资源,通过 ClusterRoleBinding 获得集群权限)
kubectl -n default create sa cluster-monitor-sa
#2、创建 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-monitor-role # 集群级 Role 名称
rules:
'权限1:查看所有命名空间的 Pod(核心 API 组)
- apiGroups: [""]
resources: ["pods", "namespaces", "nodes"] # 集群级资源(nodes)+ 命名空间资源
verbs: ["get", "list", "watch"]
'权限2:查看所有命名空间的 Deployment(apps API 组)
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets"]
verbs: ["get", "list", "watch"]
#3、创建ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-monitor-rolebinding # 集群级绑定名称
subjects:
- kind: ServiceAccount # 绑定对象是 SA
name: cluster-monitor-sa # 关联的 SA 名称
namespace: default # SA 所在的命名空间(SA 是命名空间级资源,必须指定)
apiGroup: ""
roleRef:
kind: ClusterRole # 绑定的是集群级 Role
name: cluster-monitor-role # 关联上面创建的 ClusterRole
apiGroup: rbac.authorization.k8s.io # RBAC 固定 API 组
#4、token设置
apiVersion: v1
kind: Secret
metadata:
name: cluster-monitor-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: cluster-monitor-sa # 关联集群级 SA
type: kubernetes.io/service-account-token # 固定类型,自动生成 Token
'获取token'
[root@master01 ~] # TOKEN=$(kubectl get secret cluster-monitor-sa-token -n default -o jsonpath='{.data.token}' | base64 -d)
'添加 SA 凭证到 config
[root@master01 ~] # kubectl config set-credentials cluster-monitor-sa-token --token=$TOKEN
'创建上下文(关联集群 + SA 凭证 + 命名空间)'
[root@master01 ~] # kubectl config set-context cluster-monitor-sa-token --cluster=kubernetes --user=cluster-monitor-sa-token --namespace=default
--user=cluster-monitor-sa-token # 关键:填Token凭证的名称,不是Role名
#5、验证
[root@master01 ~] # kubectl config get-contexts #查看上下文
[root@master01 ~] # kubectl config use-context cluster-monitor-sa-token #切换上下文
[root@master01 ~] # kubectl get pods
#删除上下文
[root@master01 ~]# kubectl config delete-context cluster-monitor-sa-token
#查看上下文详细信息
[root@master01 ~]# kubectl config view
#查看当前上下文
[root@master01 ~]# kubectl config current-context
(6)config配置文件
[root@master01 ~]# cat ./.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data:
server: https://192.168.88.10:6443 # API Server的地址和端口(集群核心入口)
name: kubernetes #集群名称
contexts:
# 上下文1:cluster-monitor-sa-token(你刚创建的集群级SA上下文)
- context:
cluster: kubernetes # 关联上面定义的集群名称(kubernetes)
namespace: default # 默认操作的命名空间(执行kubectl无需加-n)
user: cluster-monitor-sa-token # 关联下面users段的SA Token凭证
name: cluster-monitor-sa-token # 上下文名称(切换时用这个名字)
# 上下文2:dev-user-context(普通用户dev-user的上下文)
- context:
cluster: kubernetes
namespace: dev-project # 默认操作dev-project命名空间
user: dev-user # 关联users段的dev-user证书凭证
name: dev-user-context
# 上下文3:kubernetes-admin@kubernetes(集群管理员上下文)
- context:
cluster: kubernetes
namespace: default
user: kubernetes-admin # 关联管理员证书凭证(最高权限)
name: kubernetes-admin@kubernetes
# 上下文4:ns-user-context(普通SA的上下文)
- context:
cluster: kubernetes
namespace: default
user: ns-user-sa # 关联ns-user-sa的Token凭证
name: ns-user-context
current-context: cluster-monitor-sa-token # 当前默认使用的上下文(刚切换的这个)
kind: Config
preferences: {}
users:
# 用户1:cluster-monitor-sa-token(集群级SA的Token凭证)
- name: cluster-monitor-sa-token # 凭证名称(上下文通过这个名称关联)
user:
token: # SA的Token(省略了具体值,实际是长字符串)
# 用户2:dev-user(普通用户的证书凭证)
- name: dev-user
user:
client-certificate-data: # 客户端证书的Base64编码(省略值)
client-key-data: # 客户端私钥的Base64编码(省略值)
# 用户3:kubernetes-admin(集群管理员的证书凭证)
- name: kubernetes-admin
user:
client-certificate-data: # 管理员证书(集群初始化时自动生成)
client-key-data: # 管理员私钥(最高权限)
# 用户4:ns-user-sa(普通SA的Token凭证)
- name: ns-user-sa
user:
token: # ns-user-sa的Token(省略值)
(7)准入规则
#1. 核心作用
限制命名空间级别的资源总量(如 default 命名空间最多创建 10 个 Pod、总 CPU 限制不超过 2 核);
支持两类资源限制:
计算资源:CPU、内存(requests/limits);
对象数量:Pod、Deployment、Service、ConfigMap 等对象的最大数量;
属于「准入控制」规则:创建资源时,K8s 会检查是否超出配额,超出则拒绝创建(返回 403 Forbidden)。
#模板 1:基础资源配额(CPU / 内存 + 对象数量)
apiVersion: v1
kind: ResourceQuota
metadata:
name: default-namespace-quota # 配额名称
namespace: default # 作用的命名空间
spec:
hard: # 硬限制(必须遵守,超出则拒绝)
# 1. 计算资源限制(requests总和)
requests.cpu: "2" # 该命名空间所有Pod的CPU requests总和≤2核
requests.memory: 2Gi # 该命名空间所有Pod的内存requests总和≤2Gi
# 2. 计算资源限制(limits总和)
limits.cpu: "4" # CPU limits总和≤4核
limits.memory: 4Gi # 内存limits总和≤4Gi
# 3. 对象数量限制
pods: "10" # 最多创建10个Pod
deployments.apps: "5" # 最多创建5个Deployment
services: "5" # 最多创建5个Service
configmaps: "10" # 最多创建10个ConfigMap
secrets: "5" # 最多创建5个Secret
#模板 2:针对 SA 的配额(限制特定 SA 的资源使用)
如果想限制 cluster-monitor-sa 这个 SA 在 default 命名空间的资源使用(精细化管控):
apiVersion: v1
kind: ResourceQuota
metadata:
name: cluster-monitor-sa-quota
namespace: default
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
pods: "3"
scopeSelector: # 仅作用于使用cluster-monitor-sa的Pod
matchExpressions:
- operator: In
scopeName: ServiceAccount
values: ["cluster-monitor-sa"]
#模板 3:配合 LimitRange(强制 Pod 设置 requests/limits)
apiVersion: v1
kind: LimitRange
metadata:
name: default-limit-range
namespace: default
spec:
limits:
- default: # 默认limits(Pod未设置时生效)
cpu: 500m
memory: 512Mi
defaultRequest: # 默认requests
cpu: 200m
memory: 256Mi
type: Container # 作用于容器级别
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: default
spec:
hard:
configmaps: "1"
# 详细查看配额使用
kubectl describe resourcequota default-namespace-quota -n default
# 查看配额配置
kubectl get resourcequota -n default
10、Headlamp
Headlamp 是一款易于使用且可扩展的 Kubernetes 用户界面, 包含 Kubernetes Dashboard 的功能集(即列出和查看资源),并提供额外的扩展功能。
#Headlamp 特性:
•支持集群内操作或本地桌面应用模式
•多集群管理
•通过插件实现功能扩展
•与 RBAC 关联的操作权限控制(无权限时禁止删除/更新)
•可撤销的创建/更新/删除操作
•带文档支持的日志、执行和资源编辑器
读写/交互模式(操作基于权限控制)
(1)集群内部署
集群内部署就是把 Headlamp 部署到 Kubernetes 集群中,再将 Headlamp 暴露到集群外部,我们就可以通过 web 界面管理集群了,集群内部署使用 Helm 工具即可。
第一步:添加 chart 仓库
# 添加仓库
helm repo add headlamp https://kubernetes-sigs.github.io/headlamp/
# 搜索仓库中 chart 包
$ helm search repo headlamp
NAME CHART VERSION APP VERSION DESCRIPTION
headlamp/headlamp 0.40.0 0.40.0 Headlamp is an easy-to-use and extensible Kuber...
# 下载离线 chart 包
helm pull headlamp/headlamp --version=0.40.0
# 解压 chart 包
tar xvf headlamp-0.40.0.tgz
下载离线 chart 包的目的主要是方便查看 values.yaml 中提供了哪些可以自定义的参数
第二步: 安装 headlamp
$ helm upgrade --install my-headlamp \
--set service.type=NodePort \
--namespace webui \
--create-namespace \
headlamp
#启动不起来
[root@master01 ~]# kubectl edit deployment my-headlamp -n webui
containers:
- args:
- -in-cluster
- -in-cluster-context-name=main
- -plugins-dir=/headlamp/plugins
# - -session-ttl=86400 ← 这行被删除
#生成令牌
先创建一个sa 然后创建clusterrole,创建rolebinding,创建secrets,然后查看令牌
第三步:访问 headlamp
#通过四层 Service 访问管理界面
$ kubectl -n webui get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-headlamp NodePort 10.106.59.169 <none> 80:30998/TCP 114s
使用任意节点 IP+30998 端口就可以看到 headlamp 登录界面 。
#通过 七层 Ingress 访问
修改 valumes.yaml 中配置,按如下修改, 改为自己的域名即可。这个配置文件可以为我们创建 ingress 规则, 不过要通过 ingress 访问应用,需要集群中配置好了 Ingress Controller,根据自己安装的 Ingress Controller修改 ingressClassName 的值。
[root@master01 headlamp]# vim values.yaml
ingress:
# -- Enable ingress controller resource
enabled: true
ingressClassName: "nginx"
hosts:
- host: ui.xxhf.cc
paths:
- path: /
type: Prefix
tls:
- secretName: headlamp-tls
hosts:
- ui.xxhf.cc
#Headlamp 需要使用 HTTPS 协议访问,ingress 规则中启动了 TLS,我们需要创建一个 TLS 类型的 Secret,保存证书和私钥。
kubectl -n webui \
create secret tls headlamp-tls \
--cert=ui.xxhf.cc_bundle.crt \
--key=ui.xxhf.cc.key
#更新 headlamp Release,使配置生效。
$ helm upgrade --install my-headlamp --set service.type=NodePort --namespace webui --create-namespace headlamp
$ helm upgrade <RELEASE_NAME> <CHART> --values <你的values.yaml文件路径> -n <NAMESPACE>
#Release 更新后可以看到 ingress 规则创建成功了
[root@master01 ~]# kubectl -n webui get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
my-headlamp nginx ui.xxhf.cc 80, 443 18m
#使用上面的域名访问 headlamp,可以看到如下登录界面 :
https://ui.xxhf.cc:30443
(2)windows桌面应用部署
桌面应用是通过 kubeconfig 文件连接集群,也支持多集群的管理,还是比较方便的。
第一步:下载应用
主流的操作系统都支持

我这里下载一个 windows 桌面应用: https://github.com/kubernetes-sigs/headlamp/releases/download/v0.40.1/Headlamp-0.40.1-win-x64.exe
第二步:安装
安装成功后可以看到如下的界面:

第三步: 添加 kubeconfig 文件连接集群
在 Kubernetes 集群中下载 kubeconfig 文件,需要确保和集群中的 kube-apiserver 网络是通的。


Headlamp 访问
集群内部署方式访问 Headlamp 需要使用 Account token ,使用下面的命令生成一个 token,这个 token 默认有效期是一个小时。
Bash kubectl create token my-headlamp -n webui
登录成功后可以看到如下界面 :



管理功能比 Dashboard 项目丰富一些,使用体验还不错。
11、HPA弹性伸缩
(1)Metrics Server
Metrics Server 是一个专门用来收集 Kubernetes 核心资源指标(metrics)的工具,它定时从所有节点的 kubelet 里采集信息,但是对集群的整体性能影响极小,每个节点只大约会占用 1m 的 CPU 和 2MB 的内存,所以性价比非常高。
下面的这张图来自 Kubernetes 官网,你可以对 Metrics Server 的工作方式有个大概了解:
它调用 kubelet 的 API 拿到节点和 Pod 的指标,再把这些信息交给 apiserver,这样 kubectl、HPA 就可以利用 apiserver 来读取指标了

#拉取配置文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.8.1/high-availability-1.21+.yaml
#修改YAML 添加 --kubelet-insecure-tls
[root@master01 ~]# vim high-availability-1.21+.yaml
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10250
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --kubelet-insecure-tls #添加一行
#部署
[root@master01 ~]# kubectl apply -f high-availability-1.21+.yaml
#验证
[root@master01 ~]# kubectl top node
NAME CPU(cores) CPU(%) MEMORY(bytes) MEMORY(%)
master01 286m 14% 1459Mi 38%
worker01 133m 6% 1173Mi 31%
worker02 131m 6% 1076Mi 38%
(2)HPA
“HorizontalPodAutoscaler”,简称是 “hpa”。顾名思义,它是专门用来自动伸缩 Pod 数量的对象,适用于 Deployment 和 StatefulSet,但不能用于 DaemonSet 对象。
HorizontalPodAutoscaler 的能力完全基于 Metrics Server,它从 Metrics Server 获取当前应用的运行指标,主要是 CPU 使用率,再依据预定的策略增加或者减少 Pod 的数量。
#下面我们就来看看该怎么使用 HorizontalPodAutoscaler,首先要定义 Deployment 和 Service,创建一个 Nginx 应用,作为自动伸缩的目标对象:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-hpa
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx-hpa
template:
metadata:
labels:
app: nginx-hpa
spec:
containers:
- name: nginx
image: nginx:1.22.1
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 10Mi
limits:
cpu: 100m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: nginx-hpa-svc
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-hpa
#接下来我们要用命令 kubectl autoscale 创建一个 HorizontalPodAutoscaler 的样板 YAML 文件,它有三个参数:
•min,Pod 数量的最小值,也就是缩容的下限。
•max,Pod 数量的最大值,也就是扩容的上限。
•cpu-percent,CPU 使用率指标,当大于这个值时扩容,小于这个值时缩容。
好,现在我们就来为刚才的 Nginx 应用创建 HorizontalPodAutoscaler,指定 Pod 数量最少 2 个,最多 10 个,CPU 使用率指标设置的小一点,5%,方便我们观察扩容现象:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
maxReplicas: 10 # 最大副本数,HPA 最多将 Pod 扩展到 10 个
minReplicas: 2 # 最小副本数,HPA 至少保持 2 个 Pod 运行
scaleTargetRef: # 扩缩容的目标资源引用
apiVersion: apps/v1 # 目标资源的 API 版本
kind: Deployment # 目标资源类型为 Deployment
name: nginx-hpa # 目标 Deployment 的名称
metrics: # 扩缩容的指标配置
- type: Resource # 指标类型为资源指标(CPU/内存)
resource:
name: cpu # 监控 CPU 资源
target:
type: Utilization # 目标类型为利用率(相对于 request 的百分比)
averageUtilization: 5 # 目标平均 CPU 利用率为 5%,当高于此值时扩容,低于时缩容
#HorizontalPodAutoscaler 会根据 YAML 里的描述,找到要管理的 Deployment,把 Pod 数量调整成 2 个,再通过 Metrics Server 不断地监测 Pod 的 CPU 使用率。
#下面我们来给 Nginx 加上压力流量,运行一个测试 Pod,使用的镜像是“httpd:alpine”,它里面有 HTTP 性能测试工具 ab(Apache Bench):
[root@master01 ~] # kubectl run test -it --image=httpd:alpine -- sh
/usr/local/apache2 # ab -c 10 -t 60 -n 100000 'http://nginx-hpa-svc/'
#打开实时监控
[root@master01 ~] # kubectl get pods -w #可以发现pod资源不足时会自定创建新的
NAME READY STATUS RESTARTS AGE
nginx-hpa-5bbd45fb6b-74zlh 1/1 Running 0 2m19s
nginx-hpa-5bbd45fb6b-lkss6 1/1 Running 0 5m32s
test 1/1 Running 0 77s
[root@master01 ~] # kubectl get hpa nginx-hpa -w #可以看到副本数量在逐步增加
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa Deployment/nginx-hpa cpu: 0%/5% 2 10 2 2m14s
nginx-hpa Deployment/nginx-hpa cpu: 69%/5% 2 10 2 3m46s
nginx-hpa Deployment/nginx-hpa cpu: 194%/5% 2 10 4 4m1s
nginx-hpa Deployment/nginx-hpa cpu: 148%/5% 2 10 8 4m16s
nginx-hpa Deployment/nginx-hpa cpu: 76%/5% 2 10 10 4m31s
nginx-hpa Deployment/nginx-hpa cpu: 0%/5% 2 10 10 4m46s
由于 Metrics Server 大约每 15 秒采集一次数据,所以 HorizontalPodAutoscaler 的自动化扩容和缩容也是按照这个时间点来逐步处理的。
当它发现目标的 CPU 使用率超过了预定的 5% 后,就会以 2 的倍数开始扩容,一直到数量上限,然后持续监控一段时间,如果 CPU 使用率回落,就会再缩容到最小值
12、prometheus监控
(1)kube-prometheus组件

组件
Kube-prometheus 包含的组件:
#Prometheus Operator
在 Kubernetes 上自动化管理 Prometheus 集群的 Operator
#Highly available Prometheus
可用部署的 Prometheus 时序数据库,负责拉取和存储监控指标
#Highly available Alertmanager
高可用部署的告警管理组件,负责告警的路由、分组和发送
#Prometheus node-exporter
节点级指标采集器,采集宿主机 CPU、内存、磁盘等系统指标
#Prometheus Adapter for Kubernetes Metrics APIs
将 Prometheus 指标适配为 Kubernetes Metrics API,为 HPA 提供自定义指标
#kube-state-metrics
生成 Kubernetes 对象状态指标(如 Pod 状态、Deployment 副本数等)
#Grafana
监控数据可视化平台,提供仪表盘展示 Prometheus 中的指标数据
(2)Operator
#Operator 是由 CoreOS 开发的,用来扩展 Kubernetes API,特定的应用程序控制器,它用来创建、配置和管理复杂的有状态应用,如数据库、缓存和监控系统。Operator 基于 Kubernetes 的资源和控制器概念之上构建,但同时又包含了应用程序特定的领域知识。创建 Operator 的关键是 CRD(自定义资源)的设计。
#CR
CR 代表 自定义资源(Custom Resource)是一种扩展 Kubernetes API 的方式,允许用户定义自己的资源类型和规范。它们允许用户在Kubernetes中创建和管理自定义资源对象,这些对象可以与Kubernetes核心资源(如Pod、Service、Deployment等)一样进行操作。
#CRD
CRD(Custom Resource Definition)是一种 Kubernetes 资源,用于定义 CR 的结构和行为。通过创建CRD对象,用户可以定义新的 CR 类型,包括它们的API结构、字段、验证规则和操作行为。
通过使用CR和CRD,用户可以扩展Kubernetes的功能,以适应特定的应用需求。它们提供了一种自定义资源的机制,使用户能够以声明的方式定义和操作自己的资源类型,而无需修改Kubernetes核心代码。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# 名字必需与下面的 spec 字段匹配,并且格式为 '<名称的复数形式>.<组名>'
name: crontabs.stable.example.com
spec:
# 组名称,用于 REST API: /apis/<组>/<版本>
group: stable.example.com
# 列举此 CustomResourceDefinition 所支持的版本
versions:
- name: v1
# 每个版本都可以通过 served 标志来独立启用或禁止
served: true
# 其中一个且只有一个版本必需被标记为存储版本
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
cronSpec:
type: string
image:
type: string
replicas:
type: integer
# 可以是 Namespaced 或 Cluster
scope: Namespaced
names:
# 名称的复数形式,用于 URL:/apis/<组>/<版本>/<名称的复数形式>
plural: crontabs
# 名称的单数形式,作为命令行使用时和显示时的别名
singular: crontab
# kind 通常是单数形式的驼峰命名(CamelCased)形式。你的资源清单会使用这一形式。
kind: CronTab
# shortNames 允许你在命令行使用较短的字符串来匹配资源
shortNames:
- ct
[root@master01 ~] # kubectl api-resources | grep examp
crontabs ct stable.example.com/v1 true CronTa
[root@master01 ~] # vim cron.yaml
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * */5"
image: my-awesome-cron-image
[root@master01 ~] # kubectl apply -f cron.yaml
crontab.stable.example.com/my-new-cron-object created
[root@master01 ~] # kubectl get ct
NAME AGE
my-new-cron-object 8s
(3)Prometheus Operator
Prometheus Operator是一种为 Kubernetes 提供本地部署和管理 Prometheus 及相关监控组件的工具。该项目的目的是简化和自动化为 Kubernetes 集群配置基于 Prometheus 的监控堆栈。
#Prometheus Operator 包括但不限于以下功能:
1.Kubernetes自定义资源:使用Kubernetes自定义资源来部署和管理Prometheus、Alertmanager和相关组件。通过定义自定义资源对象(CRD),可以在Kubernetes中声明和配置 Prometheus 实例、Alertmanager 实例等。
2.简化的部署配置:通过本地的Kubernetes资源,可以配置Prometheus的基本设置,如版本、持久化、数据保留策略和副本数。这样可以更方便地进行部署配置,无需涉及复杂的配置文件。
3.Prometheus 目标配置:基于熟悉的Kubernetes标签查询,自动生成监控目标的配置。无需学习Prometheus特定的配置语言,即可快速定义要监控的目标。这样可以简化监控目标的配置过程。
#安装部署
[root@master01 ~]# wget https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.69.1/bundle.yaml
[root@master01 ~]# kubectl create -f bundle.yaml
#Prometheus Operator 在Kubernetes中引入了自定义资源,用于声明 Prometheus 和 Alertmanager 集群的期望状态以及Prometheus的配置。
1.Prometheus:Prometheus自定义资源用于定义Prometheus实例的配置和规范。可以指定版本、持久化配置、存储策略、副本数等参数,以及与其他资源的关联关系。
2.ServiceMonitor:ServiceMonitor 是一个自定义资源,用于定义要由Prometheus监控的服务和指标。可以指定服务的标签选择器,以便Prometheus可以动态地发现和监控符合条件的服务。
3.Alertmanager:Alertmanager 自定义资源用于定义Alertmanager实例的配置和规范。可以指定接收告警的通知渠道、告警路由的规则等参数。
Prometheus 资源以声明方式描述了 Prometheus 部署的期望状态,而 ServiceMonitor 和 PodMonitor 资源描述了Prometheus 要监控的目标。
#部署测试应用
#我们部署一个简单的测试应用,起了 3 个副本,通过8080端口对外映射metrics指标信息。
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
replicas: 3
selector:
matchLabels:
app: example-app
template:
metadata:
labels:
app: example-app
spec:
containers:
- name: example-app
image: registry.cn-beijing.aliyuncs.com/xxhf/instrumented_app
ports:
- name: web
containerPort: 8080
#创建对应的 service 对象。
kind: Service
apiVersion: v1
metadata:
name: example-app
labels:
app: example-app
spec:
selector:
app: example-app
ports:
- name: web
port: 8080
#创建 ServiceMonitor 对象
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: example-app
labels:
team: frontend
spec:
selector:
matchLabels:
app: example-app
endpoints:
- port: web
#部署 Prometheus
#创建RBAC 策略
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/metrics
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources:
- configmaps
verbs: ["get"]
- apiGroups:
- networking.k8s.io
resources:
- ingresses
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: default
#Prometheus自定义资源定义了底层的具体StatefulSet的特性(副本数、资源请求/限制等),同时通过spec.serviceMonitorSelector字段指定应包含哪些ServiceMonitor。
#之前我们创建了一个带有team: frontend标签的 ServiceMonitor 对象,现在我们在Prometheus对象中定义,Prometheus应选择所有带有team: frontend标签的ServiceMonitor。这使得前端团队可以创建新的ServiceMonitors和Services,而无需重新配置Prometheus对象。
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: prometheus
spec:
replicas: 1
serviceAccountName: prometheus
serviceMonitorSelector:
matchLabels:
team: frontend
resources:
requests:
memory: 400Mi
enableAdminAPI: false
#将对象部署到集群中
kubectl apply -f prometheus.yaml
kubectl -n default get prometheus prometheus -w
#暴露 Prometheus SVC
#要访问Prometheus界面,需要将Prometheus服务暴露给外部。为了简单起见,我们可以使用一个NodePort类型的Service来实现。
apiVersion: v1
kind: Service
metadata:
name: prometheus
spec:
type: NodePort
ports:
- name: web
nodePort: 30900
port: 9090
protocol: TCP
targetPort: web
selector:
prometheus: prometheus
#访问网页http://192.168.88.10:30900/

(4)kube-prometheus
'1、Prometheus Server':Prometheus生态最重要的组件,主要用于抓取和存储时间序列数据,同时提供数据的查询和告警策略的配置管理。
'2、Alertmanager':Prometheus生态用于告警的组件,Prometheus Server会将告警发送给Alertmanager,Alertmanager根据路由配置将告警信息发送给指定的人或组。Alertmanager支持邮件、Webhook、微信、钉钉、短信等媒介进行告警通知。
'3、Push Gateway':Prometheus本身是通过Pull的方式拉取数据的,但是有些监控数据可能是短期的,如果没有采集数据可能会出现丢失。Push Gateway可以用来解决此类问题,它可以用来接收数据,也就是客户端可以通过Push的方式将数据推送到PushGateway,之后Prometheus可以通过Pull拉取该数据。
'4、Exporter':主要用来采集监控数据,比如主机的监控数据可以通过node_exporter采集,MySQL的监控数据可以通过mysql_exporter采集,之后Exporter暴露一个接口,比如/metrics,Prometheus可以通过该接口采集到数据。
'5、PromQ'L:PromQL其实不算Prometheus的组件,它是用来查询数据的一种语法,比如可以通过SQL语句查询数据库的数据,通过LogQL语句查询Loki的数据,通过PromQL语句查询Prometheus的数据。
'6、Service Discovery':用来发现监控目标的自动发现,常用的有基于Kubernetes、Consul、Eureka、文件的自动发现等。
'7Grafana':用于展示数据,便于数据的查询和观测。
#安装组件,如下所示:
•Prometheus Operator
•高可用的 Prometheus
•高可用的 Alertmanager
•主机监控 Node Exporter
•Prometheus Adapter
•容器监控 kube-state-metrics
•图形化展示 Grafana
具体可以通过 https://github.com/prometheus-operator/kube-prometheus/ 找到该项目进行查看。有了kube-prometheus项目,安装也变得非常简单,只需要两条命令即可。
首先需要通过该项目地址找到和自己Kubernetes版本对应的Kube Prometheus Stack的版本,我们使用的 kubernetes 1.32.2 ,那么对应的 Kube Prometheus Stack 版本是 release-0.15 。
#从 github 下载 对应分支的代码
[root@master01 ~]# git clone -b release-0.16 https://github.com/prometheus-operator/kube-prometheus.git
一、清理之前的实验环境
# 1. 删除之前部署的所有监控资源
kubectl delete -f manifests-orig/ --ignore-not-found=true
kubectl delete -f manifests-orig/setup/ --ignore-not-found=true
# 2. 删除所有 monitoring.coreos.com 的自定义资源
kubectl delete alertmanager --all --all-namespaces --ignore-not-found=true
kubectl delete prometheus --all --all-namespaces --ignore-not-found=true
kubectl delete servicemonitor --all --all-namespaces --ignore-not-found=true
kubectl delete prometheusrule --all --all-namespaces --ignore-not-found=true
# 3. 删除 CRD
kubectl delete crd $(kubectl get crd | grep monitoring.coreos.com | awk '{print $1}') --ignore-not-found=true
# 4. 删除 monitoring 命名空间(如果存在)
kubectl delete namespace monitoring --ignore-not-found=true
# 5. 确认清理完成
kubectl get crd | grep monitoring.coreos.com
kubectl get pods --all-namespaces | grep monitoring
二、部署 kube-prometheus
方法一:直接部署(最简单)
# 1. 进入项目目录
cd /root/kube-prometheus
# 2. 部署 CRD 和 Operator
kubectl create -f manifests/setup/
# 等待几秒让 CRD 生效
sleep 5
# 3. 部署所有监控组件
kubectl create -f manifests/
# 4. 查看部署状态
kubectl get pods -n monitoring -w
[root@master01 kube-prometheus]# kubectl get pods -n monitoring
NAME READY STATUS RESTARTS AGE
alertmanager-main-0 2/2 Running 0 11m
alertmanager-main-1 2/2 Running 0 11m
alertmanager-main-2 2/2 Running 0 11m
blackbox-exporter-d989f64d9-5vwps 3/3 Running 0 11m
grafana-8665b584b-xd7pt 1/1 Running 0 11m
kube-state-metrics-76ddfbb447-d55nr 3/3 Running 0 11m
node-exporter-6m6dp 2/2 Running 0 11m
node-exporter-m7qst 2/2 Running 0 11m
node-exporter-n8nkt 2/2 Running 0 11m
prometheus-adapter-599c88b6c4-kqsf4 1/1 Running 0 11m
prometheus-adapter-599c88b6c4-rrqc8 1/1 Running 0 11m
prometheus-k8s-0 2/2 Running 0 11m
prometheus-k8s-1 2/2 Running 0 11m
prometheus-operator-6b64df5498-9v5hx 2/2 Running 0 11m
为了后期更新方便,这里按照类型把相关对象的资源清单文件 放在不同的目录下,命令如下:
Bash
mkdir alertmanager grafana kubeStateMetrics nodeExporter prometheusAdapter prometheusOperator prometheus blackboxExporter kubernetesControlPlane
mv alertmanager-* alertmanager
mv grafana-* grafana
mv kubeStateMetrics-* kubeStateMetrics
mv nodeExporter-* nodeExporter
mv prometheusAdapter-* prometheusAdapter
mv prometheusOperator-* prometheusOperator
mv prometheus-* prometheus
mv blackboxExporter-* blackboxExporter
mv kubernetesControlPlane-* kubernetesControlPlane
#修改svc为NodePort
[root@master01 kube-prometheus]# kubectl edit svc grafana -n monitoring
service/grafana edited
[root@master01 kube-prometheus]# kubectl -n monitoring get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
grafana NodePort 10.97.21.102 <none> 3000:32634/TCP 17m
#浏览器访问http://192.168.88.10:32634/login
(5)云原生应用监控
#ServiceMonitor
如果使用二进制的方式安装 Prometheus,用户需要通过 Prometheus 的一个配置文件来配置需要监控哪些数据,或者配置一些告警策略。这个配置文件的维护非常麻烦,特别是监控项非常多的情况下,很容易出现配置错误,而在Kubernetes上部署Prometheus,可以不用去维护这个配置文件,而是通过一个叫ServiceMonitor 的资源来自动发现监控目标并动态生成配置。
#监控 example-app 应用
1.部署应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
replicas: 3
selector:
matchLabels:
app: example-app
template:
metadata:
labels:
app: example-app
spec:
containers:
- name: example-app
image: registry.cn-beijing.aliyuncs.com/xxhf/instrumented_app
ports:
- name: web
containerPort: 8080
2.为应用创建 SVC
YAML
kind: Service
apiVersion: v1
metadata:
name: example-app
labels:
app: example-app
spec:
selector:
app: example-app
ports:
- name: web
port: 8080
3.创建 serviceMonitor
YAML
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: example-app
labels:
team: frontend
app.kubernetes.io/part-of: kube-prometheus
spec:
jobLabel: app.kubernetes.io/name
selector:
matchLabels:
app: example-app
namespaceSelector:
matchNames:
- default
endpoints:
- port: web
4.验证
[root@master01 yun-app]# kubectl get svc -n monitoring
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus-k8s NodePort 10.97.127.70 <none> 9090:31595/TCP,8080:30232/TCP 32m
浏览器访问http://192.168.88.10:31595/

#监控etcd
1.创建 etcd svc
YAML
apiVersion: v1
kind: Service
metadata:
labels:
app: kube-etcd
name: kube-etcd
namespace: kube-system
spec:
ports:
- name: http-metrics
port: 2381
protocol: TCP
targetPort: 2381
selector:
component: etcd
type: ClusterIP
2.创建 serviceMonitor
YAML
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: kube-etcd
namespace: kube-system
labels:
app.kubernetes.io/part-of: kube-prometheus
spec:
jobLabel: component
selector:
matchLabels:
app: kube-etcd
namespaceSelector:
matchNames:
- kube-system
endpoints:
- port: http-metrics
[root@master01 ~] # vim /etc/kubernetes/manifests/etcd.yaml #修改配置文件
spec:
containers:
- command:
- --listen-metrics-urls=http://0.0.0.0:2381 #修改监听地址
- 验证抓取目标是否生效

- 为 etcd 配置 dashboard


(6)非云原生的应用监控
#新开机器,单独部署mysql
#1.安装 mysql-exporter
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.16.0/mysqld_exporter-0.16.0.linux-amd64.tar.gz
tar xvf mysqld_exporter-0.16.0.linux-amd64.tar.gz -C /usr/local/
ln -s /usr/local/mysqld_exporter-0.16.0.linux-amd64 /usr/local/mysqld_exporter
cd /usr/local/mysqld_exporter
#2.创建授权
MySQL
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'XXXXXXXX' WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
连接 mysql 需要用户名、密码,所以下载之后,首先要创建配置文件,把用户名、密码以及mysql服务器地址,这些基本信息填进去。
在 mysqld_exporter 的目录下,创建一个 .my.cnf 的文件,内容参考下面的内容:
[client]
host=127.0.0.1
port=3306
user=exporter
password=3HdexTlk3nOw0k!3Nji
3.启动 mysql_exporter
./mysqld_exporter
mysql_exporter 默认监听 9104 端口,请求 mysql_exporter 对外暴露的 metrics 接口。
Bash
[root@db-srv mysqld_exporter]# curl 127.0.0.1:9104/metrics
# HELP go_gc_duration_seconds A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 4.5418e-05
go_gc_duration_seconds{quantile="0.25"} 4.7015e-05
go_gc_duration_seconds{quantile="0.5"} 4.8137e-05
... ...
我们需要在 Prometheus 配置文件中添加一个 Job,用来配置抓取 mysql-exporter metric endpoint, 有两种配置方式,一种是使用 prometheus-operator 提供 CRD ScrapeConfig ,另一种方法是让prometheus 加载外部配置文件
'方法一 使用 prometheus-operator 提供 CRD ScrapeConfig
#配置抓取目标
[root@master01 prometheus] # kubectl edit prometheus -n monitoring k8s
spec:
scrapeConfigSelector:
matchLabels: #添加配置
prometheus: system-monitoring-prometheus
[root@master01 ~] # vim mysql-sc.yaml
apiVersion: monitoring.coreos.com/v1alpha1
kind: ScrapeConfig
metadata:
name: static-config
namespace: monitoring
labels:
prometheus: system-monitoring-prometheus
spec:
staticConfigs:
- labels:
job: mysql
targets:
- 192.168.11.42:9104
#浏览器中访问 ip:9090查看配置文件中
static_configs:
- targets:
- 192.168.88.100:9104
labels:
job: mysql
'方法二 使用 prometheus 默认的配置文件,添加一个抓取mysql 的 job。
首先创建一个空文件,然后通过该文件创建一个 Secret,这个 Secret 可作为 Prometheus 的静态配置:
$ touch additional-scrape-configs.yaml
$ kubectl -n monitoring create secret generic additional-scrape-configs --from-file=additional-scrape-configs.yaml
创建完 Secret后,需要编辑 Prometheus 的配置,加载我们自己创建的外部配置文件 。
$ kubectl edit prometheus -n monitoring k8s
添加以下 配置文件 ,不需要重启 Prometheus 即可生效。之后在 additional-scrape-configs.yaml 文件内添加我们需要的静态配置即可。
spec:
additionalScrapeConfigs:
name: additional-scrape-configs # secret-name
key: additional-scrape-configs.yaml # key
#vim additional-scrape-configs.yaml
- job_name: mysql
static_configs:
- targets:
- 192.168.88.100:9104
可以看到此处的内容和传统配置的内容一致,只需要添加对应的job即可。之后通过该文件更新该 Secret:
# kubectl -n monitoring create secret generic additional-scrape-configs --from-file=additional-scrape-configs.yaml --dry-run=client -o yaml | kubectl -n monitoring apply -f -
在 prometheus 的配置文件中可以验证 job 已经添加成功。
最后在 Grafana 中创建 Dashboard,导入 MySQL Dashboard 14057
(7)blackbox
在 additional-scrape-configs.yaml 文件内继续添加以下静态配置,用于黑盒监控的配置:
- job_name: 'blackbox-web'
metrics_path: /probe
params:
module: [http_2xx] # Look for a HTTP 200 response.
static_configs:
- targets:
- http://prometheus.io # Target to probe with http.
- https://prometheus.io # Target to probe with https.
- http://www.xinxianghf.cn
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:19115 # The blackbox exporter's real hostname:port.
可以看到此处的内容和传统配置的内容一致,只需要添加对应的job即可。之后通过该文件更新该Secret:
Bash
kubectl -n monitoring create secret generic additional-scrape-configs --from-file=additional-scrape-configs.yaml --dry-run=client -o yaml | kubectl -n monitoring replace -f -
#验证配置文件 是否生效 登录 Grafana,导入Dashboard 13659
(8)配置告警
从配置文件可以看出,Alertmanager 的配置主要分为5大块:
global:
# The smarthost and SMTP sender used for mail notifications.
smtp_smarthost: 'localhost:25'
smtp_from: 'alertmanager@example.org'
smtp_auth_username: 'alertmanager'
smtp_auth_password: 'password'
# The directory from which notification templates are read.
templates:
- '/etc/alertmanager/template/*.tmpl'
# The root route on which each incoming alert enters.
route:
group_by: ['job', 'namespace', 'alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 3h
# A default receiver
receiver: default-team-mails
# All the above attributes are inherited by all child routes and can
# overwritten on each.
# The child route trees.
routes:
- receiver: team-X-pager
match_re:
job: node_exporter
continue: true
- receiver: team-X-pager
match_re:
job: mysqld_exporter
continue: true
- receiver: team-DB-pager
match_re:
job: .*
continue: true
inhibit_rules:
- source_matchers: [severity="critical"]
target_matchers: [severity="warning"]
equal: [alertname, cluster, service]
receivers:
- name: 'team-X-mails'
email_configs:
- to: 'team-X+alerts@example.org'
- name: 'team-X-pager'
email_configs:
- to: 'team-X+alerts-critical@example.org'
- name: 'team-Y-pager'
pagerduty_configs:
- service_key: <team-Y-key>
- name: 'team-DB-pager'
pagerduty_configs:
- service_key: <team-DB-key>
#Global:
全局配置,主要进行一些通用的配置,比如邮件通知的账号、密码、SMTP服务器、微信告警等。Global块配置下的配置选项在本配置文件内的所有配置项下可见,但是文件内其他位置的子配置可以覆盖Global配置。
#Templates:
用于放置自定义模板的位置。
#Route:
告警路由配置,用于告警信息的分组路由,可以将不同分组的告警发送给不同的收件人。比如将数据库告警发送给DBA,服务器告警发送给OPS。
#Inhibit_rules:
告警抑制,主要用于减少告警的次数,防止“告警轰炸”。比如某个宿主机宕机,可能会引起容器重建、漂移、服务不可用等一系列问题,如果每个异常均有告警,会一次性发送很多告警,造成告警轰炸,并且也会干扰定位问题的思路,所以可以使用告警抑制,屏蔽由宿主机宕机引来的其他问题,只发送宿主机宕机的消息即可。
#Receivers:告警收件人配置,每个receiver都有一个名字,经过route分组并且路由后需要指定一个receiver,就在此处配置。
#路由规则
Alertmanager 的配置比较复杂且经常需要变更,我们详细看一下:
route:
group_by: ['job', 'namespace', 'alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 3h
# A default receiver
receiver: default-team-mails
# The child route trees.
routes:
- receiver: team-X-pager
match_re:
job: node_exporter
continue: true
- receiver: team-X-pager
match_re:
job: mysqld_exporter
continue: true
- receiver: team-DB-pager
match_re:
job: .*
continue: true
#从配置文件可以看出,路由配置块的顶级配置由route开始,它是整个路由的入口,称作根路由。每一条告警进来后,都先进入route,之后根据告警自身的标签和route.group_by配置的字段进行分组。比如可以根据job、alertname或者其他自定义的标签名称进行分组,分组后进入子路由(通过route.routes配置子路由),进一步进行更加细粒度的划分,比如job名称包含mysql的发送给DBA组。
Route 常用的配置:
•receiver:
告警的通知目标,需要和receivers配置中的name进行匹配。需要注意的是,route.routes下也可以有receiver配置,优先级高于route.receiver配置的默认接收人,当告警没有匹配到子路由时,会使用route.receiver进行通知,比如上述配置中的 default-team-mails。
•group_by:
分组配置,值类型为列表。比如配置成['job', 'severity'],代表告警信息包含job和severity标签的会进行分组,且标签的key和value都相同才会被分到一组。
•continue:
决定匹配到第一个路由后,是否继续后续匹配。默认为false,即匹配到第一个子节点后停止继续匹配。
•match:
一对一匹配规则,比如match配置的为job: mysql,那么具有job=mysql的告警会进入该路由。
•match_re:
和match类似,只不过match_re是正则匹配。
•matchers:
这是Alertmanager 0.22版本新添加的一个配置项,用于替换match和match_re。
•group_wait:
告警通知等待,值类型为字符串。若一组新的告警产生,则会等group_wait后再发送通知,该功能主要用于当告警在很短时间内接连产生时,在group_wait内合并为单一的告警后再发送,防止告警过多,默认值为30s。
•group_interval:
同一组告警通知后,如果有新的告警添加到该组中,再次发送告警通知的时间,默认值为5m。
•repeat_interval:
如果一条告警通知已成功发送,且在间隔repeat_interval后,该告警仍然未被设置为resolved,则会再次发送该告警通知,默认值为4h。
以上即为Alertmanager常用的路由配置,可以看到 Alertmanager 的路由和匹配规则非常灵活,通过不同的路由嵌套和匹配规则可以达到不同的通知效果。
#配置钉钉告警
#部署dingtalk插件,连接钉钉
tar -xf prometheus-webhook-dingtalk-2.1.0.linux-amd64.tar.gz
cd prometheus-webhook-dingtalk-2.1.0.linux-amd64/
mkdir /usr/local/dingtalk
mv * /usr/local/dingtalk/
cd /usr/local/dingtalk/
cat>/usr/local/dingtalk/dingtalk.service<<EOF
[Unit]
Description=dingtalk
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/usr/local/dingtalk/
ExecStart=/usr/local/dingtalk/prometheus-webhook-dingtalk
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
ln -s /usr/local/dingtalk/dingtalk.service /etc/systemd/system
systemctl enable dingtalk.service --now
cp config.example.yml config.yml
vim config.yml
targets:
webhook:
url: https://oapi.dingtalk.com/robot/send?access_token=5a4886f9cf538049381b551abb9d09ead8b3956ca07c7cabc42160810d184de8
# secret for signature
secret: SEC95233c4478749c260aa671d7f7cb19ce0fcbb23efe25407136b5844e79b4df36
message:
title: '{{ template "legacy.title" . }}'
text: '{{ template "legacy.content" . }}'
mention:
all: true
mobiles: ['15369303093']
systemctl restart dingtalk.service
#修改alert的配置文件
Alertmanager 的配置文件是通过 Secret 进行存储的,其原始文件为 alertmanager-secret.yaml,内容大致如下:
#vim alertmanager-secret.yaml
"receivers":
- "name": "dingtalk"
webhook_configs:
- url: http://192.168.88.10:8060/dingtalk/webhook/send #修改ip地址
send_resolved: true
#kaf alertmanager-secret.yaml 加载配置文件
修改后重新应用, alertmanager 会自动加载新的配置文件,钉钉就可以收到消息了。
true
#从配置文件可以看出,路由配置块的顶级配置由route开始,它是整个路由的入口,称作根路由。每一条告警进来后,都先进入route,之后根据告警自身的标签和route.group_by配置的字段进行分组。比如可以根据job、alertname或者其他自定义的标签名称进行分组,分组后进入子路由(通过route.routes配置子路由),进一步进行更加细粒度的划分,比如job名称包含mysql的发送给DBA组。
Route 常用的配置:
•receiver:
告警的通知目标,需要和receivers配置中的name进行匹配。需要注意的是,route.routes下也可以有receiver配置,优先级高于route.receiver配置的默认接收人,当告警没有匹配到子路由时,会使用route.receiver进行通知,比如上述配置中的 default-team-mails。
•group_by:
分组配置,值类型为列表。比如配置成[‘job’, ‘severity’],代表告警信息包含job和severity标签的会进行分组,且标签的key和value都相同才会被分到一组。
•continue:
决定匹配到第一个路由后,是否继续后续匹配。默认为false,即匹配到第一个子节点后停止继续匹配。
•match:
一对一匹配规则,比如match配置的为job: mysql,那么具有job=mysql的告警会进入该路由。
•match_re:
和match类似,只不过match_re是正则匹配。
•matchers:
这是Alertmanager 0.22版本新添加的一个配置项,用于替换match和match_re。
•group_wait:
告警通知等待,值类型为字符串。若一组新的告警产生,则会等group_wait后再发送通知,该功能主要用于当告警在很短时间内接连产生时,在group_wait内合并为单一的告警后再发送,防止告警过多,默认值为30s。
•group_interval:
同一组告警通知后,如果有新的告警添加到该组中,再次发送告警通知的时间,默认值为5m。
•repeat_interval:
如果一条告警通知已成功发送,且在间隔repeat_interval后,该告警仍然未被设置为resolved,则会再次发送该告警通知,默认值为4h。
以上即为Alertmanager常用的路由配置,可以看到 Alertmanager 的路由和匹配规则非常灵活,通过不同的路由嵌套和匹配规则可以达到不同的通知效果。
```yaml
#配置钉钉告警
#部署dingtalk插件,连接钉钉
tar -xf prometheus-webhook-dingtalk-2.1.0.linux-amd64.tar.gz
cd prometheus-webhook-dingtalk-2.1.0.linux-amd64/
mkdir /usr/local/dingtalk
mv * /usr/local/dingtalk/
cd /usr/local/dingtalk/
cat>/usr/local/dingtalk/dingtalk.service<<EOF
[Unit]
Description=dingtalk
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/usr/local/dingtalk/
ExecStart=/usr/local/dingtalk/prometheus-webhook-dingtalk
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
ln -s /usr/local/dingtalk/dingtalk.service /etc/systemd/system
systemctl enable dingtalk.service --now
cp config.example.yml config.yml
vim config.yml
targets:
webhook:
url: https://oapi.dingtalk.com/robot/send?access_token=5a4886f9cf538049381b551abb9d09ead8b3956ca07c7cabc42160810d184de8
# secret for signature
secret: SEC95233c4478749c260aa671d7f7cb19ce0fcbb23efe25407136b5844e79b4df36
message:
title: '{{ template "legacy.title" . }}'
text: '{{ template "legacy.content" . }}'
mention:
all: true
mobiles: ['15369303093']
systemctl restart dingtalk.service
#修改alert的配置文件
Alertmanager 的配置文件是通过 Secret 进行存储的,其原始文件为 alertmanager-secret.yaml,内容大致如下:
#vim alertmanager-secret.yaml
"receivers":
- "name": "dingtalk"
webhook_configs:
- url: http://192.168.88.10:8060/dingtalk/webhook/send #修改ip地址
send_resolved: true
#kaf alertmanager-secret.yaml 加载配置文件
修改后重新应用, alertmanager 会自动加载新的配置文件,钉钉就可以收到消息了。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)