K8S第二次安装

作者:victory0431日期:2025/11/9

文章目录

  • Kubernetes 集群初始化问题总结文档
    • 概述
    • 遇到的问题及解决方案
      • 1. Kubelet cgroup 驱动配置错误
        • 2. CoreDNS Pod 处于 Pending 状态
        • 3. 节点污点阻止 CoreDNS 调度
    • 最终验证结果
    • 经验总结
    • 问题已解决!🎉

Kubernetes 集群初始化问题总结文档

概述

本文档总结了在 Kubernetes 集群初始化过程中遇到的主要问题及其解决方案。通过系统性的诊断和修复,最终成功建立了稳定运行的 Kubernetes 控制平面。

遇到的问题及解决方案

1. Kubelet cgroup 驱动配置错误

问题描述:
在集群初始化完成后,发现 kubelet 服务存在 cgroup 驱动配置错误。日志显示 kubelet 配置中的 cgroupDriver 被设置为 “systemd”,但实际需要的是 “cgroupfs”。

诊断过程:

  • 检查 /var/lib/kubelet/config.yaml 配置文件
  • 确认 cgroupDriver 字段值为 “systemd”
  • 查看 kubelet 服务日志确认配置问题

解决方案:

  1. 使用 sed 命令将配置文件中的 cgroupDriver: systemd 修改为 cgroupDriver: cgroupfs
  2. 重启 kubelet 服务以应用更改
  3. 验证修改结果,通过日志确认 CgroupDriver 已正确设置为 “cgroupfs”

2. CoreDNS Pod 处于 Pending 状态

问题描述:
集群初始化后,CoreDNS Pod 一直处于 Pending 状态,无法正常启动。

诊断过程:

  • 检查 Pod 状态发现 CoreDNS 处于 Pending
  • 分析原因发现缺少网络插件支持
  • 检查发现存在 calico.yaml 配置文件

解决方案:

  1. 应用 Calico 网络插件配置:kubectl apply -f ~/calico.yaml
  2. 等待 Calico 组件启动完成
  3. 观察 CoreDNS Pod 状态变化

3. 节点污点阻止 CoreDNS 调度

问题描述:
即使部署了网络插件,CoreDNS Pod 仍然处于 Pending 状态。

诊断过程:

  • 检查节点污点配置发现存在 node-role.kubernetes.io/master:NoSchedule
  • 检查 CoreDNS 部署的容忍度设置
  • 发现 CoreDNS 有容忍 control-plane 但节点标记为 master

解决方案:

  1. 移除节点上的 master 污点:kubectl taint nodes --all node-role.kubernetes.io/master-
  2. 等待 CoreDNS Pod 自动调度并启动

最终验证结果

所有问题解决后,集群状态恢复正常:

  • 节点状态:Ready
  • 所有系统组件正常运行:
    • etcd
    • kube-apiserver
    • kube-controller-manager
    • kube-scheduler
    • kube-proxy
    • CoreDNS
    • Calico 网络插件

经验总结

  1. 配置一致性很重要:确保 kubelet 配置与容器运行时的 cgroup 驱动保持一致
  2. 网络插件必不可少:CoreDNS 等组件依赖网络插件才能正常工作
  3. 污点和容忍度需要匹配:节点上的污点必须与 Pod 的容忍度设置相匹配才能正常调度
  4. 系统性诊断方法:从日志分析入手,逐步排查问题根源

通过以上步骤,成功解决了 Kubernetes 集群初始化过程中遇到的关键问题,确保了集群的稳定运行。

/home/user/k8s_init_complete_v2.sh

1#!/bin/bash
2
3YELLOW="\033[1;33m"
4GREEN="\033[1;32m"
5RED="\033[1;31m"
6RESET="\033[0m"
7
8echo -e "${YELLOW}=== Kubernetes 初始化完善执行流程 ===${RESET}"
9
10# 阶段一:环境清理与依赖检查(基础保障)
11echo -e "${YELLOW}=== 阶段一:环境清理与依赖检查 ===${RESET}"
12
13# 步骤 1:清理历史残留
14echo -e "${YELLOW}=== 步骤 1/20:清理历史残留 ===${RESET}"
15# 先启动 containerd 服务,避免 kubeadm reset 出现连接错误
16echo "启动 containerd 服务..."
17sudo systemctl start containerd 2>/dev/null || true
18# 等待 containerd 启动
19sleep 5
20# 重置 kubeadm
21sudo kubeadm reset -f
22# 删除残留文件
23sudo rm -rf /etc/kubernetes/* /var/lib/kubelet/* /var/lib/etcd/* /etc/cni/net.d/* /home/user/kubeadm-config.yaml ~/.kube/config
24echo -e "${GREEN}=== 步骤 1 执行完成 ===${RESET}"
25
26# 步骤 2:检查系统依赖(必过校验)
27echo -e "${YELLOW}=== 步骤 2/20:检查系统依赖 ===${RESET}"
28# 校验内核版本(需 ≥4.19)
29kernel_version=$(uname -r | cut -d '.' -f1-2)
30if [[ $(echo "$kernel_version >= 4.19" | bc) -ne 1 ]]; then
31  echo -e "${RED}❌ 内核版本过低(当前 $kernel_version),需升级至 4.19+${RESET}"
32  exit 1
33fi
34
35# 校验关键工具是否安装
36required_tools=("kubeadm" "kubelet" "kubectl" "containerd" "crictl" "ss" "jq")
37for tool in "${required_tools[@]}"; do
38  if ! command -v $tool &> /dev/null; then
39    echo -e "${RED}❌ 缺失依赖工具:$tool,请先安装${RESET}"
40    exit 1
41  fi
42done
43
44# 校验端口占用(6443/2379/2380/10250 需空闲)
45occupied_ports=()
46for port in 6443 2379 2380 10250; do
47  if sudo ss -tuln | grep -q ":$port"; then
48    occupied_ports+=($port)
49  fi
50done
51if [[ ${#occupied_ports[@]} -gt 0 ]]; then
52  echo -e "${RED}❌ 端口被占用:${occupied_ports[*]},请释放后重试${RESET}"
53  exit 1
54fi
55
56echo -e "${GREEN}✅ 步骤 2 校验通过:系统依赖满足要求${RESET}"
57
58# 阶段二:Containerd 配置与校验(核心组件)
59echo -e "${YELLOW}=== 阶段二:Containerd 配置与校验 ===${RESET}"
60
61# 步骤 3:配置 Containerd(cgroupfs 驱动)
62echo -e "${YELLOW}=== 步骤 3/20:配置 Containerd ===${RESET}"
63sudo tee /etc/containerd/config.toml > /dev/null << EOF
64version = 2
65root = "/var/lib/containerd"
66state = "/run/containerd"
67plugin_dirs = []
68
69[grpc]
70  address = "/run/containerd/containerd.sock"
71  uid = 0
72  gid = 0
73
74[ttrpc]
75  address = ""
76  uid = 0
77  gid = 0
78
79[debug]
80  address = ""
81  uid = 0
82  gid = 0
83  level = ""
84
85[metrics]
86  address = ""
87  grpc_histogram = false
88
89[cgroup]
90  path = ""
91
92[timeouts]
93  "io.containerd.timeout.shim.cleanup" = "5s"
94  "io.containerd.timeout.shim.load" = "5s"
95  "io.containerd.timeout.shim.shutdown" = "3s"
96  "io.containerd.timeout.task.state" = "2s"
97
98[plugins]
99  [plugins."io.containerd.grpc.v1.cri"]
100    disable_tcp_service = true
101    stream_server_address = "127.0.0.1"
102    stream_server_port = "0"
103    stream_idle_timeout = "4h0m0s"
104    enable_tls_streaming = false
105    sandbox_image = "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/pause:3.10.1"
106    stats_collect_period = 10
107    systemd_cgroup = false
108    enable_selinux = false
109    selinux_category_range = 1024
110    max_container_log_line_size = 16384
111    disable_cgroup = false
112    disable_apparmor = false
113    restrict_oom_score_adj = false
114    max_concurrent_downloads = 3
115    max_concurrent_uploads = 5
116    disable_proc_mount = false
117    unset_seccomp_profile = ""
118    tolerate_missing_hugetlb_controller = true
119    disable_hugetlb_controller = true
120    ignore_image_defined_volumes = false
121    netns_mounts_under_state_dir = false
122    enable_unprivileged_ports = false
123    enable_unprivileged_icmp = false
124
125    [plugins."io.containerd.grpc.v1.cri".containerd]
126      default_runtime_name = "runc"
127      disable_snapshot_annotations = true
128      discard_unpacked_layers = false
129      ignore_rdt_not_enabled_errors = false
130
131      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
132        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
133          runtime_type = "io.containerd.runc.v2"
134          runtime_engine = ""
135          runtime_root = ""
136          privileged_without_host_devices = false
137          base_runtime_spec = ""
138          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
139            SystemdCgroup = false
140
141    [plugins."io.containerd.grpc.v1.cri".cni]
142      bin_dir = "/opt/cni/bin"
143      conf_dir = "/etc/cni/net.d"
144      max_conf_num = 1
145      conf_template = ""
146      [plugins."io.containerd.grpc.v1.cri".cni.ipam]
147      [plugins."io.containerd.grpc.v1.cri".cni.interface]
148
149    [plugins."io.containerd.grpc.v1.cri".registry]
150      config_path = ""
151
152      [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
153        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
154          endpoint = ["https://registry-1.docker.io"]
155        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.cn-hangzhou.aliyuncs.com"]
156          endpoint = ["https://registry.cn-hangzhou.aliyuncs.com"]
157
158[plugins."io.containerd.internal.v1.opt"]
159  path = "/opt/containerd"
160
161[plugins."io.containerd.monitor.v1.cgroups"]
162  no_prometheus = false
163
164[plugins."io.containerd.runtime.v2.task"]
165  platforms = ["linux/amd64"]
166
167[plugins."io.containerd.service.v1.containers-service"]
168  rdt_config_file = ""
169
170[plugins."io.containerd.snapshotter.v1.btrfs"]
171  root_path = "/var/lib/containerd/snapshots/btrfs"
172
173[plugins."io.containerd.snapshotter.v1.native"]
174  root_path = "/var/lib/containerd/snapshots/native"
175
176[plugins."io.containerd.snapshotter.v1.overlayfs"]
177  root_path = "/var/lib/containerd/snapshots/overlayfs"
178
179[plugins."io.containerd.snapshotter.v1.zfs"]
180  root_path = "/var/lib/containerd/snapshots/zfs"
181EOF
182echo -e "${GREEN}=== 步骤 3 执行完成 ===${RESET}"
183
184# 步骤 4:启动 Containerd 并校验
185echo -e "${YELLOW}=== 步骤 4/20:启动并校验 Containerd ===${RESET}"
186# 重启服务
187sudo systemctl daemon-reload
188sudo systemctl start containerd
189sudo systemctl enable containerd
190
191# 等待启动(10秒)
192sleep 10
193
194# 校验状态(必须为 active (running))
195if ! sudo systemctl is-active --quiet containerd; then
196  echo -e "${RED}❌ Containerd 启动失败,查看日志:sudo journalctl -u containerd -n 20${RESET}"
197  exit 1
198fi
199
200# 校验 cgroup 驱动配置(必须为 false,即 cgroupfs)
201if ! grep -qE "systemd_cgroup = false|SystemdCgroup = false" /etc/containerd/config.toml; then
202  echo -e "${RED}❌ Containerd cgroup 驱动配置错误,未启用 cgroupfs${RESET}"
203  exit 1
204fi
205
206# 校验 CRI 接口连通性
207if ! sudo crictl info &> /dev/null; then
208  echo -e "${RED}❌ CRI 接口连通失败,Containerd 配置异常${RESET}"
209  exit 1
210fi
211
212echo -e "${GREEN}✅ 步骤 4 校验通过:Containerd 正常运行(cgroupfs 驱动)${RESET}"
213
214# 步骤 5:预拉取 Pause 镜像(避免后续组件启动时重复拉取)
215echo -e "${YELLOW}=== 步骤 5/20:预拉取 Pause 镜像 ===${RESET}"
216# 检查镜像是否已存在,如果不存在则拉取
217if ! sudo ctr -n k8s.io images ls | grep -q "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/pause:3.10.1"; then
218    if ! sudo ctr -n k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/pause:3.10.1; then
219        echo -e "${RED}❌ Pause 镜像拉取失败,检查网络或镜像仓库${RESET}"
220        exit 1
221    fi
222else
223    echo -e "${GREEN}✅ Pause 镜像已存在,跳过拉取${RESET}"
224fi
225echo -e "${GREEN}=== 步骤 5 执行完成 ===${RESET}"
226# 注释掉原来的拉取命令,避免重复拉取
227# pause_image="registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.9"
228# if ! sudo crictl pull $pause_image; then
229#     echo -e "${RED}❌ Pause 镜像拉取失败,检查网络或镜像仓库${RESET}"
230#     exit 1
231# fi
232
233echo -e "${GREEN}✅ 步骤 5 校验通过:Pause 镜像拉取成功${RESET}"
234
235# 阶段三:Kubelet 配置与校验(节点代理)
236echo -e "${YELLOW}=== 阶段三:Kubelet 配置与校验 ===${RESET}"
237
238# 步骤 6:配置 Kubelet 服务
239echo -e "${YELLOW}=== 步骤 6/20:配置 Kubelet 服务 ===${RESET}"
240sudo mkdir -p /etc/systemd/system/kubelet.service.d/
241sudo tee /etc/systemd/system/kubelet.service.d/10-kubeadm.conf > /dev/null << EOF
242[Service]
243Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
244Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
245# 移除不支持的 --container-runtime 标志,将其配置在 config.yaml 文件中
246Environment="KUBELET_EXTRA_ARGS=--cgroup-driver=cgroupfs"
247ExecStart=
248ExecStart=/usr/bin/kubelet \$KUBELET_KUBECONFIG_ARGS \$KUBELET_CONFIG_ARGS \$KUBELET_EXTRA_ARGS
249EOF
250echo -e "${GREEN}=== 步骤 6 执行完成 ===${RESET}"
251
252# 步骤 7:创建 Kubelet 配置文件
253echo -e "${YELLOW}=== 步骤 7/20:创建 Kubelet 配置文件 ===${RESET}"
254sudo mkdir -p /var/lib/kubelet
255sudo tee /var/lib/kubelet/config.yaml > /dev/null << EOF
256apiVersion: kubelet.config.k8s.io/v1beta1
257kind: KubeletConfiguration
258cgroupDriver: cgroupfs
259runtimeRequestTimeout: "15m"
260maxPods: 110
261containerRuntimeEndpoint: "unix:///run/containerd/containerd.sock"
262EOF
263echo -e "${GREEN}=== 步骤 7 执行完成 ===${RESET}"
264
265# 阶段四:镜像预拉与配置文件创建(初始化前置)
266echo -e "${YELLOW}=== 阶段四:镜像预拉与配置文件创建 ===${RESET}"
267
268# 步骤 8:预拉取 K8s 核心镜像(避免初始化时拉取超时)
269echo -e "${YELLOW}=== 步骤 8/20:预拉取 K8s 核心镜像 ===${RESET}"
270images=(
271    "registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.33.5"
272    "registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.33.5"
273    "registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.33.5"
274    "registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.33.5"
275    "registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.12.0"
276    "registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.12-0"
277    "swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/pause:3.10.1"
278)
279
280for image in "${images[@]}"; do
281    # 检查镜像是否已存在,如果不存在则拉取
282    if ! sudo ctr -n k8s.io images ls | grep -q "$image"; then
283        if ! sudo ctr -n k8s.io images pull "$image"; then
284            echo -e "${RED}❌ 镜像拉取失败: $image${RESET}"
285            exit 1
286        fi
287    else
288        echo -e "${GREEN}✅ 镜像已存在,跳过拉取: $image${RESET}"
289    fi
290done
291echo -e "${GREEN}=== 步骤 8 执行完成 ===${RESET}"
292# 注释掉原来的拉取命令,避免重复拉取
293# if ! sudo kubeadm config images pull --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --kubernetes-version=v1.33.5; then
294#   echo -e "${RED}❌ K8s 镜像拉取失败,检查网络或镜像仓库地址${RESET}"
295#   exit 1
296# fi
297
298# 校验镜像是否拉取成功(使用实际拉取的镜像版本,适配crictl输出格式)
299echo -e "${YELLOW}=== 校验镜像是否拉取成功 ===${RESET}"
300required_images=(
301  "kube-apiserver:v1.33.5"
302  "kube-controller-manager:v1.33.5"
303  "kube-scheduler:v1.33.5"
304  "etcd:3.5.12-0"
305  "coredns:v1.12.0"
306)
307
308# 获取镜像列表
309image_list=$(sudo crictl images --quiet)
310
311# 检查每个必需的镜像
312for img in "${required_images[@]}"; do
313  # 提取镜像名称和标签
314  image_name=$(echo "$img" | cut -d':' -f1)
315  image_tag=$(echo "$img" | cut -d':' -f2)
316  
317  # 构造搜索模式
318  search_pattern="${image_name}\s\+${image_tag}"
319  
320  # 检查镜像是否存在
321  if ! sudo crictl images | grep -q "$search_pattern"; then
322    echo -e "${RED}❌ 缺失核心镜像:$img,请重新拉取${RESET}"
323    exit 1
324  fi
325done
326
327echo -e "${GREEN}✅ 步骤 8 校验通过:K8s 核心镜像拉取完成${RESET}"
328
329# 步骤 9:创建 Kubeadm 配置文件
330echo -e "${YELLOW}=== 步骤 9/20:创建 Kubeadm 配置文件 ===${RESET}"
331sudo tee /home/user/kubeadm-config.yaml > /dev/null << EOF
332apiVersion: kubeadm.k8s.io/v1beta3
333kind: InitConfiguration
334localAPIEndpoint:
335  advertiseAddress: 10.16.233.177
336  bindPort: 6443
337nodeRegistration:
338  criSocket: unix:///run/containerd/containerd.sock
339  taints:
340  - effect: NoSchedule
341    key: node-role.kubernetes.io/master
342---
343apiVersion: kubeadm.k8s.io/v1beta3
344kind: ClusterConfiguration
345kubernetesVersion: v1.33.5
346imageRepository: registry.cn-hangzhou.aliyuncs.com/google_containers
347networking:
348  podSubnet: 192.168.0.0/16
349controlPlaneEndpoint: "10.16.233.177:6443"
350controllerManager:
351  extraArgs:
352    configure-cloud-routes: "false"
353scheduler:
354  extraArgs:
355    bind-address: 0.0.0.0  # 修复:将 address 改为 bind-address(当前版本支持的参数)
356EOF
357
358# 校验配置文件语法(必须无报错)
359if ! kubeadm config validate --config=/home/user/kubeadm-config.yaml; then
360  echo -e "${RED}❌ Kubeadm 配置文件语法错误,请检查格式${RESET}"
361  exit 1
362fi
363
364echo -e "${GREEN}✅ 步骤 9 校验通过:Kubeadm 配置文件有效${RESET}"
365
366
367# 阶段五:K8s 初始化与核心校验(关键阶段)
368echo -e "${YELLOW}=== 阶段五:K8s 初始化与核心校验 ===${RESET}"
369
370# 步骤 10:启动 Kubelet 服务
371echo -e "${YELLOW}=== 步骤 10/20:启动 Kubelet 服务 ===${RESET}"
372# 重启服务
373sudo systemctl daemon-reload
374sudo systemctl start kubelet
375sudo systemctl enable kubelet
376echo -e "${GREEN}=== 步骤 10 执行完成 ===${RESET}"
377
378# 步骤 11:执行 Kubeadm 初始化
379echo -e "${YELLOW}=== 步骤 11/20:执行 Kubeadm 初始化 ===${RESET}"
380# 开启 debug 日志,便于排查
381sudo kubeadm init --config=/home/user/kubeadm-config.yaml --ignore-preflight-errors=all --v=5
382
383# 校验初始化是否成功(必须返回 0)
384if [ $? -ne 0 ]; then
385  echo -e "${RED}❌ Kubeadm 初始化失败,查看日志:sudo journalctl -u kubelet -n 50${RESET}"
386  exit 1
387fi
388
389echo -e "${GREEN}✅ 步骤 11 执行完成:Kubeadm 初始化无语法错误${RESET}"
390
391# 步骤 12:校验 Kubelet 状态
392echo -e "${YELLOW}=== 步骤 12/20:校验 Kubelet 状态 ===${RESET}"
393# 等待启动(5秒)
394sleep 5
395
396# 校验状态(必须为 active (running))
397if ! sudo systemctl is-active --quiet kubelet; then
398  echo -e "${RED}❌ Kubelet 启动失败,查看日志:sudo journalctl -u kubelet -n 20${RESET}"
399  exit 1
400fi
401
402# 校验 cgroup 驱动配置(日志中必须含 CgroupDriver: cgroupfs)
403if ! sudo journalctl -u kubelet -n 10 --no-pager | grep -q "CgroupDriver: cgroupfs"; then
404  echo -e "${RED}❌ Kubelet cgroup 驱动配置错误,未启用 cgroupfs${RESET}"
405  exit 1
406fi
407
408echo -e "${GREEN}✅ 步骤 12 校验通过:Kubelet 正常运行(cgroupfs 驱动)${RESET}"
409
410# 步骤 13:配置 Kubectl 客户端
411echo -e "${YELLOW}=== 步骤 13/20:配置 Kubectl 客户端 ===${RESET}"
412mkdir -p $HOME/.kube
413sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
414sudo chown $(id -u):$(id -g) $HOME/.kube/config
415
416# 校验 Kubectl 连通性(必须返回集群版本)
417if ! kubectl version --short; then
418  echo -e "${RED}❌ Kubectl 客户端配置失败,无法连接集群${RESET}"
419  exit 1
420fi
421
422echo -e "${GREEN}✅ 步骤 13 校验通过:Kubectl 客户端配置成功${RESET}"
423
424echo -e "${GREEN}=== Kubernetes 初始化完善执行流程完成 ===${RESET}"
425

明白了,K8S使用的是containerd而不是Docker。让我检查containerd的默认运行时配置:

user 终端

sudo grep -A 20 “default_runtime_name” /etc/containerd/config.toml

问题就是这里!containerd的默认运行时还是 runc ,需要改为 nvidia 。让我修改配置:

user 终端

sudo sed -i ‘s/default_runtime_name = “runc”/default_runtime_name = “nvidia”/’ /etc/containerd/config.toml

现在重启containerd:

user 终端

sudo systemctl restart containerd

现在删除Device Plugin Pod让它重新创建:

user 终端

kubectl delete pod -n kube-system -l name=nvidia-device-plugin-ds

等待几秒钟后检查新Pod状态:

user 终端

sleep 10 && kubectl get pods -n kube-system | grep nvidia-device-plugin

还是有问题。让我检查新的日志:

user 终端

kubectl logs -n kube-system nvidia-device-plugin-daemonset-6h2fg

检查Pod描述:

user 终端

kubectl describe pod nvidia-device-plugin-daemonset-6h2fg -n kube-system | grep -A 10 “Last State”

现在错误变成了CDI设备解析问题。让我检查CDI配置:

user 终端

sudo nvidia-ctk cdi list

CDI设备列表为空!让我生成CDI配置:

user 终端

sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml

看来CDI模式有问题。让我回到更简单的方案,修改Device Plugin使用传统模式。先删除现有的DaemonSet:

user 终端

kubectl delete daemonset nvidia-device-plugin-daemonset -n kube-system

现在用环境变量禁用CDI模式,使用传统模式:

user 终端

kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/deployments/static/nvidia-device-plugin.yml -o yaml | sed ‘s/- name: FAIL_ON_INIT_ERROR\n value: “false”/- name: FAIL_ON_INIT_ERROR\n value: “false”\n - name: DEVICE_DISCOVERY_STRATEGY\n value: “nvml”/’ | kubectl apply -f -

让我用更简单的方法,直接下载YAML文件并修改:

user 终端

curl -s https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/deployments/static/nvidia-device-plugin.yml > /tmp/nvidia-device-plugin.yml

user 终端

sed -i ‘/- name: FAIL_ON_INIT_ERROR/a\ - name: DEVICE_DISCOVERY_STRATEGY\n value: “nvml”’ /tmp/nvidia-device-plugin.yml

user 终端

kubectl apply -f /tmp/nvidia-device-plugin.yml

等待并检查状态:

user 终端

sleep 15 && kubectl get pods -n kube-system | grep nvidia-device-plugin

还是有问题。让我检查日志:

user 终端

kubectl logs -n kube-system nvidia-device-plugin-daemonset-gbmpp

检查Pod描述:

user 终端

kubectl describe pod nvidia-device-plugin-daemonset-gbmpp -n kube-system | grep -A 10 “Last State”

还是CDI问题。让我回到最简单的方案 - 完全禁用CDI,使用传统的NVIDIA运行时模式。首先修改containerd配置:

user 终端

sudo sed -i ‘s/default_runtime_name = “nvidia”/default_runtime_name = “runc”/’ /etc/containerd/config.toml

现在修改Device Plugin YAML,显式指定使用nvidia运行时:

user 终端

sed -i ‘/tolerations:/i\ runtimeClassName: nvidia’ /tmp/nvidia-device-plugin.yml

user 终端

kubectl apply -f /tmp/nvidia-device-plugin.yml

重启containerd并等待:

user 终端

sudo systemctl restart containerd && sleep 10 && kubectl get pods -n kube-system | grep nvidia-device-plugin

还是同样的问题。让我检查是否runtimeclass存在:

user 终端

kubectl get runtimeclass

runtimeclass存在。让我检查这个Pod的具体错误:

user 终端

kubectl describe pod nvidia-device-plugin-daemonset-4qgq4 -n kube-system | grep -A 5 “Events:”

让我检查完整的事件和错误:

user 终端

kubectl describe pod nvidia-device-plugin-daemonset-4qgq4 -n kube-system | tail -20

问题还是CDI!让我检查NVIDIA容器运行时的配置,禁用CDI:

\etc\nvidia-container-runtime\config.toml 1 - 20

user 终端

sudo cat /etc/nvidia-container-runtime/config.toml

就是这个配置!NVIDIA容器运行时被配置为CDI模式,但CDI设备没有生成。让我改成传统模式:

user 终端

sudo tee /etc/nvidia-container-runtime/config.toml << EOF
disable-require = false
swarm-resource = “DOCKER_RESOURCE_GPU”

[nvidia-container-runtime]
mode = “auto”

[nvidia-container-runtime.modes]
[nvidia-container-runtime.modes.cdi]
default-kind = “nvidia.com/gpu”
annotation-prefixes = [“cdi.k8s.io/”]
spec-annotation = “cdi.k8s.io/devices”
EOF

现在重启containerd并重新创建Pod:

user 终端

sudo systemctl restart containerd && kubectl delete pod -n kube-system -l name=nvidia-device-plugin-ds

等待并检查:

user 终端

sleep 15 && kubectl get pods -n kube-system | grep nvidia-device-plugin

太好了!Pod现在是Running状态!让我验证GPU资源是否已经注册:

user 终端

kubectl get nodes -o yaml | grep -A 5 nvidia.com/gpu

完美!节点现在报告了GPU资源:

  • nvidia.com/gpu.present: “true” - 表示GPU存在
  • nvidia.com/gpu: “1” - 表示有1个GPU可用
    让我最后验证一下Device Plugin日志确认一切正常:

user 终端

kubectl logs -n kube-system nvidia-device-plugin-daemonset-f7j5m

太好了!Device Plugin现在正常运行,日志显示:

  1. ✅ 配置加载成功
  2. ✅ 启动了GRPC服务器用于 nvidia.com/gpu
  3. ✅ 成功注册了设备插件与Kubelet
  4. ✅ GPU设备被发现并注册
    总结:问题已解决!

根本原因是NVIDIA容器运行时被配置为CDI模式,但系统中没有正确配置CDI设备。通过将运行时改为"auto"模式,Device Plugin现在可以正常使用传统的NVML方式发现GPU。

现在你的K8S集群可以看到GPU资源了,可以开始部署需要GPU的工作负载了!

任务完成

你说得对,我重新理一下思路。既然containerd层面已经验证过GPU正常,那问题应该在K8s的GPU调度机制上。让我重新检查几个关键点:

user 终端

kubectl delete pod gpu-debug-pod

让我重新创建一个正确的测试Pod,使用完整的CUDA镜像而不是基础镜像:

test-gpu-k8s.yaml

  • 15 - 0

查看变更

user 终端

kubectl apply -f test-gpu-k8s.yaml

user 终端

kubectl get pod gpu-test-k8s

镜像拉取问题,让我先检查这个镜像是否存在:

user 终端

sudo ctr -n k8s.io images list | grep cuda:12.1.1-runtime

runtime镜像不存在,让我用回之前的base镜像,但用正确的方式测试。既然containerd验证过GPU正常,那现在应该检查K8s设备插件是否正确报告了GPU资源:

user 终端

kubectl get node 10-16-233-177 -o yaml | grep -A10 -B5 “nvidia.com/gpu”

节点确实报告了1个GPU资源。让我检查设备插件的日志,看是否有错误:

user 终端

kubectl logs -n kube-system daemonset/nvidia-device-plugin-daemonset

设备插件运行正常,已成功注册。让我重新用base镜像创建Pod,但先确认镜像路径:

user 终端

sudo ctr -n k8s.io images list | grep cuda | head -5

好的,base镜像存在。让我重新创建Pod,但这次用bash来验证容器内环境:

test-gpu-bash.yaml

  • 15 - 0

查看变更

user 终端

kubectl delete pod gpu-test-k8s && kubectl apply -f test-gpu-bash.yaml

user 终端

kubectl get pod gpu-bash-test

user 终端

kubectl logs gpu-bash-test

关键问题确认!容器内没有NVIDIA设备文件,说明NVIDIA容器运行时没有被正确调用。让我检查containerd的runtime配置,确认默认运行时是否设置正确:

user 终端

sudo grep -A20 “runtimes” /etc/containerd/config.toml

看到了问题!containerd配置了nvidia运行时,但默认运行时还是runc。K8s需要知道什么时候使用nvidia运行时。让我检查K8s是否配置了使用nvidia运行时:

user 终端

kubectl get runtimeclass

有nvidia运行时类!让我修改Pod配置来使用这个运行时类:

test-gpu-runtime.yaml

  • 16 - 0

查看变更

user 终端

kubectl delete pod gpu-bash-test && kubectl apply -f test-gpu-runtime.yaml

user 终端

kubectl get pod gpu-runtime-test

user 终端

kubectl logs gpu-runtime-test

成功了! 🎉

问题根源找到了:K8s集群需要显式指定 runtimeClassName: nvidia 来使用NVIDIA容器运行时。总结关键发现:

  1. containerd层面GPU正常 - 之前用ctr测试过
  2. 设备插件运行正常 - 日志显示已注册,节点报告GPU资源
  3. 问题在于运行时选择 - 默认的runc不会注入GPU设备,必须使用nvidia运行时类
    现在K8s的GPU感知能力验证成功!容器内正确显示了NVIDIA A800 GPU信息。

任务完成

我发现了问题!从日志可以看到关键错误信息:

1User provided device_type of 'cuda', but CUDA is not available. Disabling
2

这表明容器内PyTorch检测不到CUDA,正在使用CPU计算。让我进入容器检查GPU环境:

user 终端

kubectl exec -it progen2-deploy-no-weights-65fd648546-dkd9c – nvidia-smi

容器内没有nvidia-smi命令,说明NVIDIA驱动和CUDA环境没有正确挂载。让我检查当前Pod的配置:

user 终端

kubectl get pod progen2-deploy-no-weights-65fd648546-dkd9c -o yaml | grep -A5 -B5 nvidia

Pod配置显示GPU资源已分配,但容器内缺少NVIDIA工具。让我检查容器内的Python环境和CUDA检测:

user 终端

kubectl exec progen2-deploy-no-weights-65fd648546-dkd9c – python -c “import torch; print(‘CUDA available:’, torch.cuda.is_available()); print(‘CUDA device count:’, torch.cuda.device_count()); print(‘PyTorch version:’, torch.version)”

问题找到了!PyTorch版本是1.12.1+cu116,但CUDA显示不可用。让我检查容器内的CUDA环境变量和库文件:

user 终端

kubectl exec progen2-deploy-no-weights-65fd648546-dkd9c – env | grep -i cuda

CUDA环境变量存在,让我检查容器内的CUDA库文件:

user 终端

kubectl exec progen2-deploy-no-weights-65fd648546-dkd9c – ls -la /usr/local/cuda/lib64/ | head -10

CUDA库文件存在但很少。让我检查容器内的NVIDIA设备文件:

user 终端

kubectl exec progen2-deploy-no-weights-65fd648546-dkd9c – ls -la /dev/nvidia*

问题明确了!容器内缺少NVIDIA设备文件,这是典型的GPU设备未正确挂载问题。让我检查当前Pod的runtimeClass配置:

user 终端

kubectl get pod progen2-deploy-no-weights-65fd648546-dkd9c -o yaml | grep runtimeClass

Pod没有配置runtimeClass!让我检查deployment的配置文件:

progen\docker_progen\progen2-deployment_no_weight.yaml 1 - 50

user\progen\docker_progen

progen\docker_progen\progen2-deploy-no-weights.yaml 1 - 50

问题找到了!deployment配置中缺少 runtimeClassName: nvidia 配置。让我更新deployment文件来修复GPU支持:

progen\docker_progen\progen2-deploy-no-weights.yaml

  • 1 - 0

查看变更

现在让我重新应用deployment配置:

user 终端

kubectl apply -f progen2-deploy-no-weights.yaml

让我等待新的Pod启动完成:

user 终端

kubectl get pod -l app=progen2-no-weights -w

新的Pod正在创建中,让我检查详细状态:

user 终端

kubectl describe pod progen2-deploy-no-weights-79df67f774-9phlj | tail -20

新Pod无法调度,因为GPU资源不足。让我检查当前GPU使用情况:

user 终端

kubectl get node -o yaml | grep -A10 -B5 nvidia.com/gpu

节点显示有1个GPU可用,但新Pod无法调度。这是因为旧的Pod还在占用GPU资源。让我先删除旧的Pod:

user 终端

kubectl delete pod progen2-deploy-no-weights-65fd648546-dkd9c

现在让我等待新Pod启动完成:

user 终端

kubectl get pod -l app=progen2-no-weights

让我等待新Pod完全启动:

user 终端

kubectl wait --for=condition=ready pod -l app=progen2-no-weights --timeout=60s

让我检查Pod详细状态和事件:

user 终端

kubectl get pod progen2-deploy-no-weights-79df67f774-9phlj -o wide

很好!新Pod已经运行。现在让我验证GPU环境是否已正确配置:

user 终端

kubectl exec progen2-deploy-no-weights-79df67f774-9phlj – nvidia-smi

太棒了!现在GPU设备已经正确挂载,nvidia-smi可以正常访问。让我验证PyTorch是否能检测到CUDA:

user 终端

kubectl exec progen2-deploy-no-weights-79df67f774-9phlj – python -c “import torch; print(‘CUDA available:’, torch.cuda.is_available()); print(‘CUDA device count:’, torch.cuda.device_count()); print(‘Current device:’, torch.cuda.current_device() if torch.cuda.is_available() else ‘None’)”

完美!CUDA现在可以正常检测到了。让我检查应用日志确认是否开始使用GPU:

user 终端

kubectl logs progen2-deploy-no-weights-79df67f774-9phlj --tail=50

让我检查服务是否正常运行:

user 终端

kubectl get svc progen2-service

让我测试服务是否可访问:

user 终端

curl -s http://10.16.233.177:30090/ | head -10

让我实时查看应用日志来确认GPU使用情况:

user 终端

kubectl logs progen2-deploy-no-weights-79df67f774-9phlj -f --tail=100

现在让我验证完整的部署状态:

user 终端

kubectl get deploy,svc,pod -l app=progen2-no-weights

问题已解决!🎉

根本原因分析:

  1. 缺失runtimeClass配置 :原deployment配置中缺少 runtimeClassName: nvidia ,导致容器无法正确挂载GPU设备
  2. GPU设备不可见 :容器内缺少NVIDIA设备文件(/dev/nvidia*),nvidia-smi命令不可用
  3. PyTorch检测失败 :由于设备文件缺失,PyTorch无法检测到CUDA环境
    修复步骤:
  4. ✅ 在deployment配置中添加了 runtimeClassName: nvidia
  5. ✅ 重新应用配置并重启Pod
  6. ✅ 验证GPU设备已正确挂载(nvidia-smi可正常访问)
  7. ✅ 验证PyTorch已能检测到CUDA设备(CUDA available: True)
    当前状态:
  • 🟢 Pod状态 :progen2-deploy-no-weights-79df67f774-9phlj 运行正常(1/1 Running)
  • 🟢 GPU检测 :容器内nvidia-smi可正常访问,显示NVIDIA A800 80GB GPU
  • 🟢 PyTorch CUDA :已检测到1个CUDA设备,device_count=1
  • 🟢 服务访问 :可通过 http://10.16.233.177:30090/ 正常访问Gradio界面
  • 🟢 GPU资源 :Pod已正确分配GPU资源(nvidia.com/gpu: 1)
    验证结果:
1# GPU设备检测
2CUDA available: True
3CUDA device count: 1
4Current device: 0
5
6# nvidia-smi输出
7NVIDIA A800 80GB PCIe | 33°C | 0%利用率 | 0MiB/81920MiB内存使用
8

现在ProGen2服务已经正确配置GPU支持,模型计算将使用GPU加速而不是CPU。你可以重新访问 http://10.16.233.177:30090/ 进行蛋白质序列生成,应该能看到GPU开始有计算任务运行了。

任务完成


K8S第二次安装》 是转载文章,点击查看原文


相关推荐


Bash 的 while 循环语句
hubenchang05152025/11/7

#Bash 的 while 循环语句 Bash 的 while 循环语句的语法为: while 条件命令 do 命令 ... done 只要条件为真,就执行循环。 其中,条件命令返回成功(0)时为真(true),返回失败(非 0)时为假(false)。 如果省略(部分)换行,则需要使用分号(;)区分: while 条件命令; do 命令; 命令; done 示例: number=0 while [ "$number" -lt 10 ] # 只要 "$number" 小于 1


【Concept Plugin 3】轻量级插件化解决方案|动态类加载
不够优雅2025/11/3

背景 去年的这篇文章【Spring Boot】插件化动态类加载解决方案发现反响很不错 看来大家对于插件化这样的功能还是有一定需求,于是趁热打铁,实现了大家呼声比较高的功能,就有了Concept Plugin 3 新特性 支持集群环境,支持AWS S3对象存储加载插件 支持动态Spring接口,支持解析yaml文件 提供更简单的Observable集成方式 框架介绍 Concept Plugin 3是一款轻量级插件化解决方案,提供可插拔的插件能力 GitHub 传送门 Concep


Redis(94)如何启用Redis的数据加密?
Victor3562025/10/31

启用Redis的数据加密通常涉及两个主要方面:传输层加密(TLS/SSL)和存储层加密。以下是详细的步骤和代码示例,以帮助您在Redis中实现数据加密。 1. 传输层加密(TLS/SSL) 传输层加密确保在客户端和Redis服务器之间传输的数据是加密的。Redis 6.0及以上版本支持TLS/SSL。 步骤 1: 生成证书和密钥 可以使用OpenSSL生成自签名证书和密钥: # 生成CA私钥 openssl genpkey -out ca.key -algorithm RSA -pkeyopt


Vue 3 v-for 指南:从基础到精通,彻底掌握列表渲染的艺术
前端摸鱼匠2025/10/28

文章目录 一、初识 v-for:动态世界的基石1.1 遍历数组:最常见的场景1.1.1 基础语法:`item in items`1.1.2 获取索引:`(item, index) in items`1.1.3 嵌套循环:处理二维或多维数据 1.2 遍历对象:探索属性的奥秘1.2.1 基础语法:`(value, key, index) in object`1.2.2 对象遍历的顺序 1.3 遍历数字:生成固定序列 二、核心机制:Key 的深度解析2.1 为什么需要 Key:


Python编程实战 · 基础入门篇 | 元组(tuple)
程序员爱钓鱼2025/10/26

在学习完列表(list)之后,你会发现 Python 中的序列类型非常灵活。但除了可变的列表,还有一种不可变的序列类型——元组(tuple)。 元组在数据处理、函数返回值、解包操作等场景中都非常常见,是 Python 程序员必须掌握的基础结构之一。 一 什么是元组(tuple) 元组(Tuple)是一个有序、不可变的序列类型。 它与列表类似,也可以存放多个不同类型的元素,但一旦创建,内容就不能被修改。 创建元组使用小括号 (),元素之间用逗号分隔: t = (1, 2, 3) print(t


复杂结构数据挖掘(三)关联规则挖掘实验
nju_spy2025/10/23

目录 1. 数据集导入观察 2. Apriori算法、FP-Growth算法 使用方式 3. 朴素 brute-force 4. grid_search 参数比较 + 三种算法比较 5. main 函数 与 报告生成 6. 实验结果总结 – 通过调整不同级别的支持度与置信度,比较 Apriori 算法、FP-Growth 算法以及一种进行穷举搜索的基准方法。 比较维度包括:生成的频繁项集数量、规则挖掘所用的存储空间、计算成本 – 发现一些有趣的关联规则,并就这些规则所揭


MySQL数据库05:DQL查询运算符
Doro再努力2025/10/22

文章目录 一、运算符简介 二、算术运算符 三、比较运算符 四、逻辑运算符 五、条件查询 五、位运算符(了解) 5.1按位与 5.2按位或 5.3按位异或 5.4按位右移 5.5按位左移 5.6按位取反 一、运算符简介         数据库中的表结构确立后,表中的数据代表的意义就已经确定。通过MSQL运算符进行运算,就可以获取到表结构以外的另一种数据。例如,学生表中存在一个birth字段,这个字段表示学生的出生年份,而运用MySQL的算术运算符用当前的年份减学生出


主流的 MCU 开发语言为什么是 C 而不是 C++?
淘晶驰AK2025/10/20

要说主流 MCU 开发为啥是 C 而不是 C++,你先琢磨琢磨 MCU 那点家当 —— 存储空间按 KB 算,RAM 可能就几十 KB,算力更是抠抠搜搜,稍微重点的操作都怕给它干死机。这种环境下,代码的每一个字节、每一次内存访问都得精打细算,而 C 语言天生就带着这种 "极简主义" 的基因。 你想啊,写 MCU 程序,核心就是跟寄存器打交道,直接往地址里塞数,中断服务程序得像闪电一样快,容不得半点拖泥带水。C 语言就像一把朴实的扳手,没有多余的零件,编译器一转就是干干净净的机器码,你写*(vo


jenkins在使用中遇到的问题
tingting01192025/10/19

一、jenkins使用docker命令权限拒绝 将jenkins用户添加到docker组: # usermod -aG docker jenkins # systemctl restart jenkins 二、推送私有仓库提示拒绝 # cp -r /root/.docker ./jenkins # chown -R jenkins.jenkins .docker 三、jenkins 提示kubectl 命令Authentication required # s


美团LongCat-Audio-Codec:给语音大模型装上“顺风耳”与“巧舌”
墨风如雪2025/10/18

各位AI圈的朋友们,又一个重磅消息!美团在2025年10月17日悄然揭开了它在语音AI领域深耕已久的秘密武器——LongCat-Audio-Codec的神秘面纱。这可不是一个普通的编解码器,它旨在为语音大模型(Speech LLM)打造一套全新的“听”与“说”的链路。想象一下,我们离那个能真正流畅、自然、高效对话的AI,又近了一大步! 语音AI的“死结”与LongCat的破解之道 你可能想不到,现在我们与智能设备的那些看似流畅的语音交互背后,其实隐藏着不少“死结”。语音大模型在理解语义、生成逼

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0