集群管理
1 - 以独立模式运行 kubelet
本教程将向你展示如何运行一个独立的 kubelet 实例。
你可能会有不同的动机来运行一个独立的 kubelet。 本教程旨在向你介绍 Kubernetes,即使你对此并没有太多经验也没有关系。 你可以跟随本教程学习节点设置、基本(静态)Pod 以及 Kubernetes 如何管理容器。
你学习完本教程后,就可以尝试使用带一个控制平面的集群来管理 Pod、节点和其他类别的对象。例如,你好,Minikube。
你还可以以独立模式运行 kubelet 来满足生产场景要求,例如为高可用、弹性部署的集群运行控制平面。 本教程不涵盖运行弹性控制平面所需的细节。
教程目标
- 在 Linux 系统上安装
cri-o和kubelet,并将其作为systemd服务运行。 - 启动一个运行
nginx的 Pod,监听针对此 Pod 的 IP 地址的 TCP 80 端口的请求。 - 学习此方案中不同组件之间如何交互。
Caution
本教程中所使用的 kubelet 配置在设计上是不安全的,不得用于生产环境中。
准备开始
- 对使用
systemd和iptables(或使用iptables仿真的nftables)的 Linux 系统具有管理员(root)访问权限。 - 有权限访问互联网以下载本教程所需的组件,例如:
- 实现 Kubernetes CRI 的容器运行时。
- 网络插件(通常称为 容器网络接口(CNI))。
- 必需的 CLI 工具:
curl、tar、jq。
准备好系统
配置内存交换
默认情况下,如果在节点上检测到内存交换,kubelet 将启动失败。 这意味着内存交换应该被禁用或被 kubelet 容忍。
Note
如果你配置 kubelet 为容忍内存交换,则 kubelet 仍会配置 Pod(以及这些 Pod 中的容器)不使用交换空间。 要了解 Pod 实际上可以如何使用可用的交换,你可以进一步阅读 Linux 节点上交换内存管理。
如果你启用了交换内存,则禁用它或在 kubelet 配置文件中添加 failSwapOn: false。
要检查交换内存是否被启用:
sudo swapon --show
如果此命令没有输出,则交换内存已被禁用。
临时禁用交换内存:
sudo swapoff -a
要使此变更持续到重启之后:
确保在 /etc/fstab 或 systemd.swap 中禁用交换内存,具体取决于它在你的系统上是如何配置的。
启用 IPv4 数据包转发
检查 IPv4 数据包转发是否被启用:
cat /proc/sys/net/ipv4/ip_forward
如果输出为 1,则 IPv4 数据包转发已被启用。如果输出为 0,按照以下步骤操作。
要启用 IPv4 数据包转发,创建一个配置文件,将 net.ipv4.ip_forward
参数设置为 1:
sudo tee /etc/sysctl.d/k8s.conf <<EOF
net.ipv4.ip_forward = 1
EOF
将变更应用到系统:
sudo sysctl --system
输出类似于:
...
* Applying /etc/sysctl.d/k8s.conf ...
net.ipv4.ip_forward = 1
* Applying /etc/sysctl.conf ...
下载、安装和配置组件
安装容器运行时
下载所需软件包的最新可用版本(推荐)。
本教程建议安装 CRI-O 容器运行时(外部链接)。
根据你安装的特定 Linux 发行版,有几种安装容器运行时的方式。
尽管 CRI-O 推荐使用 deb 或 rpm 包,但本教程使用
CRI-O Packaging 项目中的静态二进制包脚本,
以简化整个安装过程,并保持与 Linux 发行版无关。
此脚本安装并配置更多必需的软件,例如容器联网所用的
cni-plugins
以及运行容器所用的 crun 和
runc。
此脚本将自动检测系统的处理器架构(amd64 或 arm64),并选择和安装最新版本的软件包。
设置 CRI-O
查阅发布版本页面(外部链接)。
下载静态二进制包脚本:
curl https://raw.githubusercontent.com/cri-o/packaging/main/get > crio-install
运行安装器脚本:
sudo bash crio-install
启用并启动 crio 服务:
sudo systemctl daemon-reload
sudo systemctl enable --now crio.service
快速测试:
sudo systemctl is-active crio.service
输出类似于:
active
详细的服务检查:
sudo journalctl -f -u crio.service
安装网络插件
cri-o 安装器安装并配置 cni-plugins 包。你可以通过运行以下命令来验证安装包:
/opt/cni/bin/bridge --version
输出类似于:
CNI bridge plugin v1.5.1
CNI protocol versions supported: 0.1.0, 0.2.0, 0.3.0, 0.3.1, 0.4.0, 1.0.0
检查默认配置:
cat /etc/cni/net.d/11-crio-ipv4-bridge.conflist
输出类似于:
{
"cniVersion": "1.0.0",
"name": "crio",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"ranges": [
[{ "subnet": "10.85.0.0/16" }]
]
}
}
]
}
Note
确保默认的 subnet 范围(10.85.0.0/16)不会与你已经在使用的任一网络地址重叠。
如果出现重叠,你可以编辑此文件并进行相应的更改。更改后重启服务。
下载并设置 kubelet
下载 kubelet 的最新稳定版本。
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubelet"
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubelet"
配置:
sudo mkdir -p /etc/kubernetes/manifests
sudo tee /etc/kubernetes/kubelet.yaml <<EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
webhook:
enabled: false # 请勿在生产集群中使用!
authorization:
mode: AlwaysAllow # 请勿在生产集群中使用!
enableServer: false
logging:
format: text
address: 127.0.0.1 # 限制对 localhost 的访问
readOnlyPort: 10255 # 请勿在生产集群中使用!
staticPodPath: /etc/kubernetes/manifests
containerRuntimeEndpoint: unix:///var/run/crio/crio.sock
EOF
Note
由于你搭建的不是一个生产集群,所以你可以使用明文
HTTP(readOnlyPort: 10255)对 kubelet API 进行不做身份认证的查询。
为了顺利完成本次教学,身份认证 Webhook 被禁用,鉴权模式被设置为 AlwaysAllow。
你可以进一步了解鉴权模式和
Webhook 身份认证,
以正确地配置 kubelet 在你的环境中以独立模式运行。
参阅端口和协议以了解 Kubernetes 组件使用的端口。
安装:
chmod +x kubelet
sudo cp kubelet /usr/bin/
创建 systemd 服务单元文件:
sudo tee /etc/systemd/system/kubelet.service <<EOF
[Unit]
Description=Kubelet
[Service]
ExecStart=/usr/bin/kubelet \
--config=/etc/kubernetes/kubelet.yaml
Restart=always
[Install]
WantedBy=multi-user.target
EOF
服务配置文件中故意省略了命令行参数 --kubeconfig。此参数设置
kubeconfig
文件的路径,指定如何连接到 API 服务器,以启用 API 服务器模式。省略此参数将启用独立模式。
启用并启动 kubelet 服务:
sudo systemctl daemon-reload
sudo systemctl enable --now kubelet.service
快速测试:
sudo systemctl is-active kubelet.service
输出类似于:
active
详细的服务检查:
sudo journalctl -u kubelet.service
检查 kubelet 的 API /healthz 端点:
curl http://localhost:10255/healthz?verbose
输出类似于:
[+]ping ok
[+]log ok
[+]syncloop ok
healthz check passed
查询 kubelet 的 API /pods 端点:
curl http://localhost:10255/pods | jq '.'
输出类似于:
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {},
"items": null
}
在 kubelet 中运行 Pod
在独立模式下,你可以使用 Pod 清单运行 Pod。这些清单可以放在本地文件系统上, 或通过 HTTP 从配置源获取。
为 Pod 创建一个清单:
cat <<EOF > static-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
EOF
将 static-web.yaml 清单文件复制到 /etc/kubernetes/manifests 目录。
sudo cp static-web.yaml /etc/kubernetes/manifests/
查找 kubelet 和 Pod 的信息
Pod 网络插件为每个 Pod 创建一个网络桥(cni0)和一对 veth 接口
(这对接口的其中一个接口在新创建的 Pod 内,另一个接口在主机层面)。
查询 kubelet 的 API 端点 http://localhost:10255/pods:
curl http://localhost:10255/pods | jq '.'
要获取 static-web Pod 的 IP 地址:
curl http://localhost:10255/pods | jq '.items[].status.podIP'
输出类似于:
"10.85.0.4"
连接到 nginx 服务器 Pod,地址为 http://<IP>:<Port>(端口 80
是默认端口),在本例中为:
curl http://10.85.0.4
输出类似于:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
了解更多细节
如果你需要排查在学习本教程时遇到的问题,你可以在以下目录中查找监控和故障排查资料:
/var/lib/cni
/var/lib/containers
/var/lib/kubelet
/var/log/containers
/var/log/pods
清理
kubelet
sudo systemctl disable --now kubelet.service
sudo systemctl daemon-reload
sudo rm /etc/systemd/system/kubelet.service
sudo rm /usr/bin/kubelet
sudo rm -rf /etc/kubernetes
sudo rm -rf /var/lib/kubelet
sudo rm -rf /var/log/containers
sudo rm -rf /var/log/pods
容器运行时
sudo systemctl disable --now crio.service
sudo systemctl daemon-reload
sudo rm -rf /usr/local/bin
sudo rm -rf /usr/local/lib
sudo rm -rf /usr/local/share
sudo rm -rf /usr/libexec/crio
sudo rm -rf /etc/crio
sudo rm -rf /etc/containers
网络插件
sudo rm -rf /opt/cni
sudo rm -rf /etc/cni
sudo rm -rf /var/lib/cni
结论
本页涵盖了以独立模式部署 kubelet 的各个基本方面。你现在可以部署 Pod 并测试更多功能。
请注意,在独立模式下,kubelet 不支持从控制平面获取 Pod 配置(因为没有控制平面连接)。
你还不能使用 ConfigMap 或 Secret 来配置静态 Pod 中的容器。
接下来
- 跟随你好,Minikube 学习如何在有控制平面的情况下运行 Kubernetes。minikube 工具帮助你在自己的计算机上搭建一个练习集群。
- 进一步了解网络插件
- 进一步了解容器运行时
- 进一步了解 kubelet
- 进一步了解静态 Pod
2 - 在 Kubernetes 节点上配置交换内存
本文演示了如何使用 kubeadm 在 Kubernetes 节点上制备和启用交换内存。
教程目标
- 使用 kubeadm 在 Kubernetes 节点上制备交换内存。
- 学习配置加密和未加密的交换内存。
- 学习如何在系统启动时启用交换内存。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 1.33.要获知版本信息,请输入 kubectl version.
你需要在集群中至少有一个运行 Linux 操作系统的工作节点。 本次演示需要先安装 kubeadm 工具,安装步骤请参考 kubeadm 安装指南。
在每个需要配置交换内存的工作节点上,你需要以下工具:
fallocatemkswapswapon
对于加密的交换空间(推荐),你还需要:
cryptsetup
使用 kubeadm 安装支持交换内存的集群
创建交换文件并启用交换内存
如果当前节点未启用交换内存,则需要先制备交换空间。 本节将展示如何以加密和未加密的方式创建 4GiB 的交换文件。
你可以使用如下命令设置加密的交换文件。
请注意,此示例使用的是 cryptsetup 工具(在大多数 Linux 发行版中都可用):
# 分配存储空间并限制访问权限
fallocate --length 4GiB /swapfile
chmod 600 /swapfile
# 基于已分配的存储空间创建加密设备
cryptsetup --type plain --cipher aes-xts-plain64 --key-size 256 -d /dev/urandom open /swapfile cryptswap
# 格式化此交换空间
mkswap /dev/mapper/cryptswap
# 为换页激活交换空间
swapon /dev/mapper/cryptswap
未加密的交换文件可以按以下方式配置:
# 分配存储空间并限制访问权限
fallocate --length 4GiB /swapfile
chmod 600 /swapfile
# 格式化此交换空间
mkswap /swapfile
# 为换页激活交换空间
swapon /swapfile
验证交换内存是否启用
你可以使用 swapon -s 命令或 free 命令来验证交换内存是否启用。
使用 swapon -s:
Filename Type Size Used Priority
/dev/dm-0 partition 4194300 0 -2
使用 free -h:
total used free shared buff/cache available
Mem: 3.8Gi 1.3Gi 249Mi 25Mi 2.5Gi 2.5Gi
Swap: 4.0Gi 0B 4.0Gi
引导时启用交换内存
在设置好交换内存后,若要在系统引导时启动交换文件,通常有两种做法:
你可以设置一个 systemd 单元来激活(加密的)交换内存,或者在
/etc/fstab 文件中添加类似于 /swapfile swap swap defaults 0 0 的行。
使用 systemd 激活交换内存,可以确保在交换内存可用之前延迟启动 kubelet(如果你有这个需求)。 同样,使用 systemd 还可以让服务器在 kubelet(以及通常的容器运行时)关闭之前保持交换内存处于启用状态。
配置 kubelet
在节点上启用交换内存后,需要按如下方式配置 kubelet。 你需要为此节点选择一种交换内存行为。 在本教程中,你将配置 LimitedSwap 行为。
找到并编辑 kubelet 配置文件,然后:
- 将
failSwapOn设置为 false - 将
memorySwap.swapBehavior设置为 LimitedSwap
# 此代码片段应添加到 kubelet 的配置文件中
failSwapOn: false
memorySwap:
swapBehavior: LimitedSwap
为了使这些配置生效,需重启 kubelet。 通常你需要为此运行以下命令:
systemctl restart kubelet.service
你应该会发现 kubelet 现在处于健康状态,并且你可以根据需要运行使用交换内存的 Pod。
3 - 使用 Pod 级资源与 kubelet 资源管理器
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the PodLevelResourceManagers feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
本教程演示如何配置 kubelet 的资源管理器(拓扑、CPU 和内存)
以支持 Pod 级资源规约。你可以定义混合分配模型:
其中一些容器获得独占的、NUMA 对齐的基础设施资源,
而另一些容器则共享 Pod 级共享池中的剩余资源。
要了解该特性背后的概念,请阅读 Pod 级资源管理器概念页面。
教程目标
- 配置
kubelet资源管理器(CPU、内存和拓扑)以支持 Pod 级资源。 - 使用
pod拓扑管理器作用域部署工作负载,以实现混合的独占容器和共享容器,并达到单 NUMA 对齐。 - 查看并验证 CPU 和内存资源如何在独占容器与 Pod 共享池之间划分。
- 了解当 Pod 级共享池将为空时的准入拒绝规则。
- 使用
container拓扑管理器作用域和混合的容器分配部署工作负载。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 v1.36.要获知版本信息,请输入 kubectl version.
要完成本教程,你需要:
- 一个带有 Linux 工作节点的 Kubernetes 集群 (Pod 级资源管理器在 Windows 节点上不受支持)。
- 至少一个具有 NUMA 拓扑的工作节点 (最好有多个 NUMA 节点,以便观察对齐情况)。
- 在工作节点上拥有管理权限(root 或
sudo), 以修改kubelet配置并重启kubelet服务。 - 拥有
kubectl访问权限,且具备创建名字空间和 Pod 的权限。
确保为控制平面和工作节点启用了以下特性门控:
PodLevelResourcesPodLevelResourceManagers
创建一个名字空间
创建一个名字空间,使本教程中创建的资源与集群的其余部分隔离开来:
kubectl create namespace plrm-tutorial
在混合分配场景中使用 Pod 作用域
当拓扑管理器的作用域设置为 pod 时,kubelet 会基于 .spec.resources
为整个 Pod 执行一次 NUMA 对齐。得到的资源预算随后被划分:
请求 Guaranteed 资源的容器获得独占切片,
而未获得独占分配的容器则在 Pod 级共享池中共享剩余的预算。
第 1 步:针对 Pod 作用域配置 kubelet
若要启用此行为,请在希望以所需策略运行这些工作负载的目标工作节点上配置 kubelet。
你可以按如下方式更新这些节点的
kubelet 配置:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cpuManagerPolicy: "static"
memoryManagerPolicy: "Static"
topologyManagerScope: "pod"
topologyManagerPolicy: "single-numa-node"
- 对于
topologyManagerPolicy,有效值为single-numa-node、restricted或best-effort。 使用 Pod 级资源管理时,你不能指定其他任何值。
重启 kubelet 以应用该配置。例如,在使用 systemd 的 Linux 上:
systemctl restart kubelet.service。
第 2 步:部署混合分配的 Pod
请考虑以下示例 Pod 清单。该 Pod 在 Pod 级别(.spec.resources)
请求总计 4 个 CPU 的预算。在该 Pod 内部:
main-app容器请求 2 个完整 CPU 核心的独占分配 (requests=limits= 2 CPU)。- 由于
metrics-sidecar和logging-sidecar未指定容器级别的 requests, 这两个边车容器共享 Pod 级共享池中剩余的 CPU 核心:2 个 CPU 核心。
apiVersion: v1
kind: Pod
metadata:
name: pod-scope-mixed
annotations:
kubernetes.io/description: "A pod demonstrating pod-level scope where one container gets exclusive resources and others share the remaining pod resources in a shared pool."
spec:
# 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
# main-app 容器在容器级别满足 Guaranteed QoS 类的要求,而各边车容器未指定任何资源 request。
# 在 pod 范围下,这意味着 kubelet 可以静态地为整个 Pod 分配
# 4 个 CPU,其中 2 个独占分配给 main-app 容器,剩余的 2 个由边车在该 Pod 的共享池中共享。
resources:
requests:
cpu: "4"
memory: "4Gi"
limits:
cpu: "4"
memory: "4Gi"
initContainers:
- name: metrics-sidecar
# 注意:这是仅用于演示的占位镜像,并不是真正的 metrics 助手。
image: registry.k8s.io/pause:3.9
restartPolicy: Always
- name: logging-sidecar
# 注意:这是仅用于演示的占位镜像,并不是真正的日志代理。
image: registry.k8s.io/pause:3.9
restartPolicy: Always
containers:
- name: main-app
# 注意:这是仅用于演示的占位镜像。
image: registry.k8s.io/pause:3.9
resources:
requests:
cpu: "2"
memory: "2Gi"
limits:
cpu: "2"
memory: "2Gi"
将清单应用到你的集群:
kubectl apply -f https://k8s.io/examples/pods/resource/pod-level-resource-managers-pod-scope-mixed.yaml --namespace=plrm-tutorial
第 3 步:验证和检查资源划分
检查 Pod 是否成功运行:
kubectl get pod pod-scope-mixed --namespace=plrm-tutorial
了解背后发生了什么:
flowchart TD subgraph Pod["Pod-Level Budget: 4 CPUs, 4Gi Memory"] direction TB C1["main-app<br/>(Exclusive: 2 CPUs, 2Gi Memory)"] subgraph Pool["Pod Shared Pool: 2 CPUs, 2Gi Memory"] C2["metrics-sidecar"] C3["logging-sidecar"] end end- Pod 对齐: 拓扑管理器评估了 4 个 CPU 的 Pod 请求
(
spec.resources),并将整个 Pod 分配到单个 NUMA 节点。 - 独占分配: CPU 管理器为
main-app分配了一个专属的 2 个 CPU 切片。main-app的 CPU CFS 配额节流被禁用,使其能够不受节流地访问这些独占核心。
- Pod 共享池: 剩余的 2 个 CPU 构成一个 Pod 级共享池。
metrics-sidecar和logging-sidecar未指定任何容器级资源 (resources: {}),因此它们在这个 Pod 隔离的共享池中运行, 并启用了 CFS 配额强制执行。虽然该池由这两个边车共享, 但它与节点上的外部工作负载隔离开来,为这些边车提供专属的 NUMA 局部性,并防止节点级资源争用。
- Pod 对齐: 拓扑管理器评估了 4 个 CPU 的 Pod 请求
(
观察空共享池的准入限制
使用 pod 作用域时,如果存在需要共享池的容器,而某些 Pod 规约会导致 Pod 共享池为空,
则 kubelet 的准入控制会拒绝这些 Pod 规约。
如果来自 Guaranteed 容器的独占资源请求之和等于 Pod 级总预算,
并且至少还有一个容器需要共享池,那么 kubelet 将拒绝该 Pod。
第 4 步:检查无效的 Pod 清单
请考虑以下清单。该 Pod 请求总计 4 个 CPU 的预算。
container-a 请求独占的 1 个 CPU,container-b 请求独占的 3 个 CPU
(合计 4 个 CPU)。container-c 未请求独占资源且需要共享池,但已剩下 0 个 CPU:
第 5 步:尝试部署并观察拒绝
应用此清单:
kubectl apply -f https://k8s.io/examples/pods/resource/pod-level-resource-managers-empty-shared-pool.yaml --namespace=plrm-tutorial
查看 Pod 事件以观察准入错误:
kubectl describe pod empty-shared-pool --namespace=plrm-tutorial请注意事件消息,它说明
kubelet拒绝该 Pod 的原因是: 对于需要共享资源的容器而言,Pod 级共享池将为空:Status: Failed Reason: TopologyAffinityError Message: Pod was rejected: Pod Scope pod with pod-level resources failed admission under pod-scope topology managerflowchart TD subgraph Pod["Pod-Level Budget: 4 CPUs, 4Gi Memory"] direction TB C1["container-a<br/>(Exclusive: 1 CPU, 1Gi Memory)"] C2["container-b<br/>(Exclusive: 3 CPUs, 3Gi Memory)"] subgraph Pool["Pod Shared Pool: 0 CPUs, 0Gi Memory"] C3["container-c<br/>(Requires shared pool)"] end end Pool --> Rejection["Admission Error: Pod Rejected!"] style Rejection fill:#ffcccc,stroke:#ff0000,stroke-width:2px
在混合分配场景下使用 container 作用域
你也可以将拓扑管理器的作用域配置为 container。在此模式下,kubelet 会逐个评估每个容器以进行独占分配,
而 .spec.resources 中的 Pod 总体预算仍会强制执行 QoS 和 cgroup 限额边界。
第 6 步:为 container 作用域配置 kubelet
在目标工作节点上更新你的
kubelet 配置,以使用 container 作用域:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cpuManagerPolicy: "static"
memoryManagerPolicy: "Static"
topologyManagerScope: "container"
topologyManagerPolicy: "single-numa-node"
重启 kubelet 以应用该配置。例如,在使用 systemd 的 Linux 上:
systemctl restart kubelet.service。
第 7 步:部署容器作用域的混合工作负载
请考虑以下示例 Pod 清单。该 Pod 的总预算为 4 个 CPU:
infrastructure-sidecar请求独占的 2 个 CPU 切片(requests=limits= 2 CPU)。worker-1和worker-2未指定容器级别的 requests,而是在常规的节点级共享池中运行。
apiVersion: v1
kind: Pod
metadata:
name: container-scope-mixed
annotations:
kubernetes.io/description: "A pod demonstrating container-level scope where one container gets exclusive resources and others run in the node's shared pool."
spec:
# 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
# infrastructure-sidecar 容器在容器级别满足 Guaranteed QoS 类的要求,而各 worker
# 容器未指定任何资源 request。在 container 范围下,kubelet 会逐个评估容器以进行独占分配。
# 这意味着 infrastructure-sidecar 获得独占的 2 个 CPU 切片,
# 而 worker 容器在节点的常规共享池中运行,同时所有容器的总量仍受 Pod 级限额约束。
resources:
requests:
cpu: "4"
memory: "4Gi"
limits:
cpu: "4"
memory: "4Gi"
initContainers:
- name: infrastructure-sidecar
# 注意:这是仅用于演示的占位镜像,并不是真正的基础设施助手。
image: registry.k8s.io/pause:3.9
restartPolicy: Always
resources:
requests:
cpu: "2"
memory: "2Gi"
limits:
cpu: "2"
memory: "2Gi"
containers:
- name: worker-1
# 注意:这是仅用于演示的占位镜像。
image: registry.k8s.io/pause:3.9
- name: worker-2
# 注意:这是仅用于演示的占位镜像。
image: registry.k8s.io/pause:3.9
应用该清单:
kubectl apply -f https://k8s.io/examples/pods/resource/pod-level-resource-managers-container-scope-mixed.yaml --namespace=plrm-tutorial
第 8 步:验证部署
检查 Pod 是否正在运行:
kubectl get pod container-scope-mixed --namespace=plrm-tutorial
了解背后发生了什么:
flowchart TD subgraph Pod["Pod-Level Budget: 4 CPUs, 4Gi Memory"] direction TB C1["infrastructure-sidecar<br/>(Exclusive NUMA Slice: 2 CPUs, 2Gi Memory)"] subgraph NodePool["Node Shared Pool: Pod-level limit"] C3["worker-2"] C2["worker-1"] end end- 容器作用域对齐: 在
container作用域下,kubelet会逐个评估容器。infrastructure-sidecar直接从节点的可分配池中获得一个独占的、NUMA 对齐的 2 个 CPU 切片。 - 节点共享池:
worker-1和worker-2未指定任何容器级资源请求 (resources: {}),因此在container作用域下,它们在节点的常规共享池中运行(而不是在 Pod 隔离的池中)。 - Pod 限额强制执行: 所有容器的 CPU 消耗总量仍由 Pod 级限额(
spec.resources.limits)限制在 4 个 CPU 以内。
- 容器作用域对齐: 在
清理现场
删除本教程中创建的名字空间和所有示例 Pod:
kubectl delete namespace plrm-tutorial
接下来
- 阅读概念文档:
- 了解如何配置节点级资源管理器:
- 了解如何分配资源:
4 - 名字空间演练
Kubernetes 名字空间有助于不同的项目、团队或客户去共享 Kubernetes 集群。
名字空间通过以下方式实现这点:
- 为名字设置作用域.
- 为集群中的部分资源关联鉴权和策略的机制。
使用多个名字空间是可选的。
此示例演示了如何使用 Kubernetes 名字空间细分集群。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入 kubectl version.
环境准备
此示例作如下假设:
- 你已拥有一个配置好的 Kubernetes 集群。
- 你已对 Kubernetes 的 Pod、 服务 和 Deployment 有基本理解。
理解默认名字空间
默认情况下,Kubernetes 集群会在配置集群时实例化一个默认名字空间,用以存放集群所使用的默认 Pod、Service 和 Deployment 集合。
假设你有一个新的集群,你可以通过执行以下操作来检查可用的名字空间:
kubectl get namespaces
NAME STATUS AGE
default Active 13m
创建新的名字空间
在本练习中,我们将创建两个额外的 Kubernetes 名字空间来保存我们的内容。
我们假设一个场景,某组织正在使用共享的 Kubernetes 集群来支持开发和生产:
开发团队希望在集群中维护一个空间,以便他们可以查看用于构建和运行其应用程序的 Pod、Service 和 Deployment 列表。在这个空间里,Kubernetes 资源被自由地加入或移除, 对谁能够或不能修改资源的限制被放宽,以实现敏捷开发。
运维团队希望在集群中维护一个空间,以便他们可以强制实施一些严格的规程, 对谁可以或谁不可以操作运行生产站点的 Pod、Service 和 Deployment 集合进行控制。
该组织可以遵循的一种模式是将 Kubernetes 集群划分为两个名字空间:development 和 production。
让我们创建两个新的名字空间来保存我们的工作。
文件 namespace-dev.yaml 描述了 development 名字空间:
apiVersion: v1
kind: Namespace
metadata:
name: development
labels:
name: development
使用 kubectl 创建 development 名字空间。
kubectl create -f https://k8s.io/examples/admin/namespace-dev.yaml
将下列的内容保存到文件 namespace-prod.yaml 中,
这些内容是对 production 名字空间的描述:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
name: production
让我们使用 kubectl 创建 production 名字空间。
kubectl create -f https://k8s.io/examples/admin/namespace-prod.yaml
为了确保一切正常,我们列出集群中的所有名字空间。
kubectl get namespaces --show-labels
NAME STATUS AGE LABELS
default Active 32m <none>
development Active 29s name=development
production Active 23s name=production
在每个名字空间中创建 Pod
Kubernetes 名字空间为集群中的 Pod、Service 和 Deployment 提供了作用域。
与一个名字空间交互的用户不会看到另一个名字空间中的内容。
为了演示这一点,让我们在 development 名字空间中启动一个简单的 Deployment 和 Pod。
我们首先检查一下当前的上下文:
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://130.211.122.180
name: lithe-cocoa-92103_kubernetes
contexts:
- context:
cluster: lithe-cocoa-92103_kubernetes
user: lithe-cocoa-92103_kubernetes
name: lithe-cocoa-92103_kubernetes
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
user:
password: h5M0FtUUIflBSdI7
username: admin
kubectl config current-context
lithe-cocoa-92103_kubernetes
下一步是为 kubectl 客户端定义一个上下文,以便在每个名字空间中工作。 "cluster" 和 "user" 字段的值将从当前上下文中复制。
kubectl config set-context dev --namespace=development \
--cluster=lithe-cocoa-92103_kubernetes \
--user=lithe-cocoa-92103_kubernetes
kubectl config set-context prod --namespace=production \
--cluster=lithe-cocoa-92103_kubernetes \
--user=lithe-cocoa-92103_kubernetes
默认情况下,上述命令会添加两个上下文到 .kube/config 文件中。
你现在可以查看上下文并根据你希望使用的名字空间并在这两个新的请求上下文之间切换。
查看新的上下文:
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://130.211.122.180
name: lithe-cocoa-92103_kubernetes
contexts:
- context:
cluster: lithe-cocoa-92103_kubernetes
user: lithe-cocoa-92103_kubernetes
name: lithe-cocoa-92103_kubernetes
- context:
cluster: lithe-cocoa-92103_kubernetes
namespace: development
user: lithe-cocoa-92103_kubernetes
name: dev
- context:
cluster: lithe-cocoa-92103_kubernetes
namespace: production
user: lithe-cocoa-92103_kubernetes
name: prod
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
user:
password: h5M0FtUUIflBSdI7
username: admin
让我们切换到 development 名字空间进行操作。
kubectl config use-context dev
你可以使用下列命令验证当前上下文:
kubectl config current-context
dev
此时,我们从命令行向 Kubernetes 集群发出的所有请求都限定在 development 名字空间中。
让我们创建一些内容。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: snowflake
name: snowflake
spec:
replicas: 2
selector:
matchLabels:
app: snowflake
template:
metadata:
labels:
app: snowflake
spec:
containers:
- image: registry.k8s.io/serve_hostname
imagePullPolicy: Always
name: snowflake
应用清单文件来创建 Deployment。
kubectl apply -f https://k8s.io/examples/admin/snowflake-deployment.yaml
我们创建了一个副本大小为 2 的 Deployment,该 Deployment 运行名为 snowflake 的 Pod,
其中包含一个仅提供主机名服务的基本容器。
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
snowflake 2/2 2 2 2m
kubectl get pods -l app=snowflake
NAME READY STATUS RESTARTS AGE
snowflake-3968820950-9dgr8 1/1 Running 0 2m
snowflake-3968820950-vgc4n 1/1 Running 0 2m
这很棒,开发人员可以做他们想要的事情,而不必担心影响 production 名字空间中的内容。
让我们切换到 production 名字空间,展示一个名字空间中的资源如何对另一个名字空间不可见。
kubectl config use-context prod
production 名字空间应该是空的,下列命令应该返回的内容为空。
kubectl get deployment
kubectl get pods
生产环境需要以放牛的方式运维,让我们创建一些名为 cattle 的 Pod。
kubectl create deployment cattle --image=registry.k8s.io/serve_hostname --replicas=5
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
cattle 5/5 5 5 10s
kubectl get pods -l run=cattle
NAME READY STATUS RESTARTS AGE
cattle-2263376956-41xy6 1/1 Running 0 34s
cattle-2263376956-kw466 1/1 Running 0 34s
cattle-2263376956-n4v97 1/1 Running 0 34s
cattle-2263376956-p5p3i 1/1 Running 0 34s
cattle-2263376956-sxpth 1/1 Running 0 34s
此时,应该很清楚地展示了用户在一个名字空间中创建的资源对另一个名字空间是不可见的。
随着 Kubernetes 中的策略支持的发展,我们将扩展此场景,以展示如何为每个名字空间提供不同的授权规则。