翻译自官方文档。
1. 安装
原文:Installing the NVIDIA GPU Operator — NVIDIA GPU Operator 24.9.0 documentation
前提条件
- 需要在机器上安装
kubectl
和helm
命令行工具。
可以运行以下命令安装Helm CLI:
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 \
&& chmod 700 get_helm.sh \
&& ./get_helm.sh
- 在Kubernetes集群中,为运行GPU工作负载的所有工作节点或节点组必须运行相同的操作系统版本,以便使用NVIDIA GPU驱动程序容器。或者,如果预先在节点上安装了NVIDIA GPU驱动程序,那么可以使用不同的操作系统。
对于仅运行CPU工作负载的工作节点或节点组,节点可以运行任何操作系统,因为GPU Operator不会对仅CPU的工作负载节点进行任何配置或管理。
-
节点必须配置一个容器引擎,如CRI-O或containerd。
-
如果集群使用Pod Security Admission (PSA)来限制Pod行为,请为Operator所在的namespace设置标签,将执行策略设为
privileged
:
kubectl create ns gpu-operator
kubectl label --overwrite ns gpu-operator pod-security.kubernetes.io/enforce=privileged
- Node Feature Discovery (NFD) 是Operator在每个节点上的依赖项。默认情况下,NFD master和worker会被Operator自动部署。如果NFD已经在集群中运行,那么在安装Operator时必须禁用NFD的部署。
检查集群中是否已经在运行NFD的一种方法是检查节点上的NFD标签:
kubectl get nodes -o json | jq '.items[].metadata.labels | keys | any(startswith("feature.node.kubernetes.io"))'
如果命令输出为true
,则表示NFD已在集群中运行。
安装步骤
提示
如果是在Red Hat OpenShift Container Platform上进行安装,请参考OpenShift的“安装和升级概述”。
-
添加NVIDIA Helm仓库:
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia \ && helm repo update
-
安装GPU Operator:
-
使用默认配置安装Operator:
helm install --wait --generate-name \ -n gpu-operator --create-namespace \ nvidia/gpu-operator
-
安装Operator并指定配置选项:
helm install --wait --generate-name \ -n gpu-operator --create-namespace \ nvidia/gpu-operator \ --set <option-name>=<option-value>
如需更多信息,请参阅下面的Chart自定义选项和常见部署场景。
-
常用Chart自定义选项
在使用Helm chart时,可以通过以下选项自定义设置。这些选项可在安装时通过--set
进行指定。
下表列出了最常用的选项。如需查看所有选项,请运行以下命令:
helm show values nvidia/gpu-operator
常见部署场景
以下常见的部署场景及示例命令主要适用于裸金属主机或具有GPU直通的虚拟机。
指定Operator的Namespace
Operator和相关组件会安装在同一个namespace中。该namespace是可配置的,并在安装时指定。例如,以下命令将GPU Operator安装到nvidia-gpu-operator
命名空间中:
helm install --wait --generate-name \
-n nvidia-gpu-operator --create-namespace \
nvidia/gpu-operator
如果在安装时没有指定namespace,所有GPU Operator组件将默认安装在default
命名空间中。
防止在某些节点上安装操作(operands)组件
默认情况下,GPU Operator的操作组件会部署在集群中所有GPU工作节点上。GPU工作节点通过标签feature.node.kubernetes.io/pci-10de.present=true
进行识别,其中0x10de
是分配给NVIDIA的PCI供应商ID。
要禁止操作组件在某个GPU工作节点上部署,可以为该节点添加标签nvidia.com/gpu.deploy.operands=false
:
kubectl label nodes $NODE nvidia.com/gpu.deploy.operands=false
这个标签将阻止GPU Operator的操作组件部署到带有此标签的GPU节点上。
防止在某些节点上安装NVIDIA GPU驱动程序
默认情况下,GPU Operator会在集群中所有GPU工作节点上部署驱动程序。要防止在某个GPU工作节点上安装驱动程序,可以通过以下命令为该节点添加标签:
kubectl label nodes $NODE nvidia.com/gpu.deploy.driver=false
该标签将阻止GPU Operator在该节点上部署NVIDIA GPU驱动程序。
在Red Hat Enterprise Linux上的安装
在这种场景下,使用基于UBI 8构建的NVIDIA Container Toolkit镜像:
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--set toolkit.version=v1.16.1-ubi8
将上述命令中的v1.16.1
替换为NVIDIA GPU Operator支持的版本。具体版本信息请参考平台支持页面上的GPU Operator组件矩阵。
在使用RHEL8和Kubernetes时,为支持GPU Operator,必须启用SELinux并设置为宽容模式或强制模式。此外,不支持网络受限的环境。
附GPU operator组件矩阵:
Component | Version |
---|---|
NVIDIA GPU Operator | v24.9.0 |
NVIDIA GPU Driver | 565.57.01560.35.03550.127.05 (default),535.216.01 |
NVIDIA Driver Manager for Kubernetes | v0.7.0 |
NVIDIA Container Toolkit | 1.17.0 |
NVIDIA Kubernetes Device Plugin | 0.17.0 |
DCGM Exporter | 3.3.8-3.6.0 |
Node Feature Discovery | v0.16.6 |
NVIDIA GPU Feature Discoveryfor Kubernetes | 0.17.0 |
NVIDIA MIG Manager for Kubernetes | 0.10.0 |
DCGM | 3.3.8-1 |
Validator for NVIDIA GPU Operator | v24.9.0 |
NVIDIA KubeVirt GPU Device Plugin | v1.2.10 |
NVIDIA vGPU Device Manager | v0.2.8 |
NVIDIA GDS Driver 1 | 2.20.5 |
NVIDIA Kata Manager for Kubernetes | v0.2.2 |
NVIDIA Confidential ComputingManager for Kubernetes | v0.1.1 |
NVIDIA GDRCopy Driver | v2.4.1-1 |
预安装的NVIDIA GPU驱动程序
在这种场景下,工作节点上的GPU驱动程序已预先安装:
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--set driver.enabled=false
上述命令将阻止Operator在集群的任何节点上安装GPU驱动程序。
如果没有指定driver.enabled=false
参数,而集群中的节点已经预安装了GPU驱动程序,则驱动程序Pod中的初始化容器会检测到驱动程序已安装,并给节点打上标签,以终止驱动程序Pod并防止其重新调度到该节点上。随后,Operator将继续启动其他Pod,例如容器工具包Pod。
预安装的NVIDIA GPU驱动程序和NVIDIA容器工具包
在此场景中,工作节点上已预安装NVIDIA GPU驱动程序和NVIDIA容器工具包。
提示
此场景适用于运行NVIDIA Base OS的NVIDIA DGX系统。
在安装Operator之前,请确保默认运行时已设置为nvidia
。更多信息请参考NVIDIA容器工具包文档中的配置部分。
使用以下选项安装Operator:
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--set driver.enabled=false \
--set toolkit.enabled=false
此命令会在节点上禁用GPU驱动程序和容器工具包的安装,因为它们已经预先安装在节点中。
预安装的NVIDIA容器工具包(未安装驱动程序)
在此场景中,工作节点上已预安装NVIDIA容器工具包,但尚未安装驱动程序。
将工具包配置为使用驱动程序安装的根目录/run/nvidia/driver
,因为这是驱动程序容器挂载的路径:
sudo sed -i 's/^#root/root/' /etc/nvidia-container-runtime/config.toml
使用以下选项安装Operator(该操作将部署驱动程序):
helm install --wait --generate-name \
-n gpu-operator --create-namespace \
nvidia/gpu-operator \
--set toolkit.enabled=false
该命令会跳过容器工具包的安装,但会自动安装驱动程序。
运行自定义驱动镜像
如果您希望使用自定义的驱动容器镜像(例如版本 465.27),可以按照以下步骤构建自定义的驱动容器镜像:
-
通过在构建 Docker 镜像时指定
$DRIVER_VERSION
参数来重建驱动容器。有关参考信息,驱动容器的 Dockerfile 可在 Git 仓库 中找到。 -
使用适当的 Dockerfile 构建容器。例如:
docker build --pull -t \ --build-arg DRIVER_VERSION=455.28 \ nvidia/driver:455.28-ubuntu20.04 \ --file Dockerfile .
请确保驱动容器的标签符合
driver:<version>-<os>
的格式,例如driver:455.28-ubuntu20.04
。 -
在 Helm 安装命令中通过覆盖默认值来指定新的驱动镜像和镜像库。例如:
helm install --wait --generate-name \ -n gpu-operator --create-namespace \ nvidia/gpu-operator \ --set driver.repository=docker.io/nvidia \ --set driver.version="465.27"
请注意,这些说明仅用于参考和评估。如果未使用 NVIDIA 的 GPU Operator 标准发行版本,则此类自定义配置的支持可能会有所限制。
为 containerd 指定配置选项
在使用 containerd 作为容器运行时时,可以为 GPU Operator 部署的容器工具包设置以下配置选项:
toolkit:
env:
- name: CONTAINERD_CONFIG
value: /etc/containerd/config.toml
- name: CONTAINERD_SOCKET
value: /run/containerd/containerd.sock
- name: CONTAINERD_RUNTIME_CLASS
value: nvidia
- name: CONTAINERD_SET_AS_DEFAULT
value: true
如果您需要指定自定义值,请参考以下示例命令的语法:
helm install gpu-operator -n gpu-operator --create-namespace \
nvidia/gpu-operator $HELM_OPTIONS \
--set toolkit.env[0].name=CONTAINERD_CONFIG \
--set toolkit.env[0].value=/etc/containerd/config.toml \
--set toolkit.env[1].name=CONTAINERD_SOCKET \
--set toolkit.env[1].value=/run/containerd/containerd.sock \
--set toolkit.env[2].name=CONTAINERD_RUNTIME_CLASS \
--set toolkit.env[2].value=nvidia \
--set toolkit.env[3].name=CONTAINERD_SET_AS_DEFAULT \
--set-string toolkit.env[3].value=true
这些选项定义如下:
-
CONTAINERD_CONFIG
指定主机上您希望更新的 containerd 配置文件路径,以便支持 nvidia-container-runtime。默认情况下,它指向
/etc/containerd/config.toml
(containerd 的默认位置)。如果您的 containerd 安装不在默认位置,则需要进行自定义。 -
CONTAINERD_SOCKET
指定主机上用于与 containerd 通信的套接字文件路径。Operator 会使用此路径向 containerd 守护进程发送 SIGHUP 信号,以重新加载其配置。默认情况下,它指向
/run/containerd/containerd.sock
(containerd 的默认位置)。如果您的 containerd 安装不在默认位置,则需要进行自定义。 -
CONTAINERD_RUNTIME_CLASS
指定您希望与 nvidia-container-runtime 关联的运行时类名称。启动的 pod,如果其
runtimeClassName
等于CONTAINERD_RUNTIME_CLASS
,则总是使用 nvidia-container-runtime 运行。默认值为nvidia
。 -
CONTAINERD_SET_AS_DEFAULT
一个标志,指示是否将 nvidia-container-runtime 设置为用于启动所有容器的默认运行时。如果设置为 false,只有在
runtimeClassName
等于CONTAINERD_RUNTIME_CLASS
的 pod 中的容器才会使用 nvidia-container-runtime 运行。默认值为 true。
Rancher Kubernetes Engine 2
Rancher Kubernetes Engine 2 (RKE2) 的安装请参考 RKE2 文档中的 “在 RKE2 中部署 NVIDIA Operator”。
MicroK8s
在 MicroK8s 中,可以在 ClusterPolicy
中设置以下内容:
toolkit:
env:
- name: CONTAINERD_CONFIG
value: /var/snap/microk8s/current/args/containerd-template.toml
- name: CONTAINERD_SOCKET
value: /var/snap/microk8s/common/run/containerd.sock
- name: CONTAINERD_RUNTIME_CLASS
value: nvidia
- name: CONTAINERD_SET_AS_DEFAULT
value: "true"
这些选项可以在安装 GPU Operator 时传递,命令示例如下:
helm install gpu-operator -n gpu-operator --create-namespace \
nvidia/gpu-operator $HELM_OPTIONS \
--set toolkit.env[0].name=CONTAINERD_CONFIG \
--set toolkit.env[0].value=/var/snap/microk8s/current/args/containerd-template.toml \
--set toolkit.env[1].name=CONTAINERD_SOCKET \
--set toolkit.env[1].value=/var/snap/microk8s/common/run/containerd.sock \
--set toolkit.env[2].name=CONTAINERD_RUNTIME_CLASS \
--set toolkit.env[2].value=nvidia \
--set toolkit.env[3].name=CONTAINERD_SET_AS_DEFAULT \
--set-string toolkit.env[3].value=true
此配置将设置 CONTAINERD_CONFIG
和 CONTAINERD_SOCKET
的路径,指定 CONTAINERD_RUNTIME_CLASS
为 nvidia
,并将 CONTAINERD_SET_AS_DEFAULT
设为 true
,以确保 nvidia 容器运行时在 MicroK8s 集群中得到正确配置。
验证:运行示例 GPU 应用程序
CUDA VectorAdd 示例
在第一个示例中,我们运行一个简单的 CUDA 示例,将两个向量相加。
-
创建一个文件,如
cuda-vectoradd.yaml
,内容如下:apiVersion: v1 kind: Pod metadata: name: cuda-vectoradd spec: restartPolicy: OnFailure containers: - name: cuda-vectoradd image: "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1-ubuntu20.04" resources: limits: nvidia.com/gpu: 1
-
运行该 Pod:
kubectl apply -f cuda-vectoradd.yaml
该 Pod 会启动,执行
vectorAdd
命令,然后退出。 -
查看容器的日志输出:
kubectl logs pod/cuda-vectoradd
示例输出:
[Vector addition of 50000 elements] Copy input data from the host memory to the CUDA device CUDA kernel launch with 196 blocks of 256 threads Copy output data from the CUDA device to the host memory Test PASSED Done
-
移除已停止的 Pod:
kubectl delete -f cuda-vectoradd.yaml
示例输出:
pod "cuda-vectoradd" deleted
以上步骤验证了 GPU Operator 安装是否成功,确保 GPU 资源正常分配并能在容器中运行 CUDA 应用程序。
Jupyter Notebook 部署指南
你可以按照以下步骤在集群中部署 Jupyter Notebook:
-
创建一个文件,比如
tf-notebook.yaml
,内容如下:--- apiVersion: v1 kind: Service metadata: name: tf-notebook labels: app: tf-notebook spec: type: NodePort ports: - port: 80 name: http targetPort: 8888 nodePort: 30001 selector: app: tf-notebook --- apiVersion: v1 kind: Pod metadata: name: tf-notebook labels: app: tf-notebook spec: securityContext: fsGroup: 0 containers: - name: tf-notebook image: tensorflow/tensorflow:latest-gpu-jupyter resources: limits: nvidia.com/gpu: 1 ports: - containerPort: 8888 name: notebook
-
应用清单文件以部署 Pod 并启动服务:
kubectl apply -f tf-notebook.yaml
-
检查 Pod 的状态:
kubectl get pod tf-notebook
示例输出:
NAMESPACE NAME READY STATUS RESTARTS AGE default tf-notebook 1/1 Running 0 3m45s
-
由于清单文件包含服务,获取 Notebook 的外部端口:
kubectl get svc tf-notebook
示例输出:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE tf-notebook NodePort 10.106.229.20 <none> 80:30001/TCP 4m41s
-
获取 Jupyter Notebook 的访问令牌:
kubectl logs tf-notebook
示例输出:
[I 21:50:23.391 NotebookApp] The Jupyter Notebook is running at: [I 21:50:23.391 NotebookApp] http://tf-notebook:8888/?token=3660c9ee9b225458faaf853200bc512ff2206f635ab2b1d9
现在,你可以在浏览器中通过以下 URL 访问 Notebook:
http://your-machine-ip:30001/?token=3660c9ee9b225458faaf853200bc512ff2206f635ab2b1d9
此 URL 将引导你访问运行在集群中的 Jupyter Notebook。
在商业版 Kubernetes 平台上安装
产品 | 文档 |
---|---|
Red Hat OpenShift 4using RHCOS worker nodes | NVIDIA GPU Operator on Red Hat OpenShift Container Platform |
VMware vSphere with Tanzuand NVIDIA AI Enterprise | NVIDIA AI Enterprise VMware vSphere Deployment Guide |
Google Cloud Anthos | NVIDIA GPUs with Google Anthos |
2. 升级
原文:Upgrading the NVIDIA GPU Operator — NVIDIA GPU Operator 24.9.0 documentation
先决条件
如果您的集群使用 Pod 安全性审核 (PSA) 来限制 pod 的行为,请标记 Operator 所在的命名空间,将强制策略设置为特权模式:
kubectl label --overwrite ns gpu-operator pod-security.kubernetes.io/enforce=privileged
这将确保 Operator 在执行过程中能够拥有足够的权限。如果您的集群配置了 Pod 安全性策略来限制容器的行为,此步骤尤为重要。
使用 Helm
GPU Operator 支持对现有资源的动态更新。这一功能使得 GPU Operator 能够确保集群策略规范中的设置始终被应用并保持最新。
由于 Helm 不支持自动升级现有的 CRD,您可以通过手动升级 GPU Operator chart或启用 Helm 钩子来进行升级。
选项 1:手动升级 CRD
通过此过程,所有现有的 GPU Operator 资源都会内联更新,集群策略资源也会应用来自 values.yaml
的更新。
- 指定 Operator 发布标签
首先在环境变量中指定 Operator 发布标签:
export RELEASE_TAG=v23.9.0
-
应用集群策略和 NVIDIA 驱动的自定义资源定义
更新集群策略和 NVIDIA 驱动的 CRD:
kubectl apply -f \ https://gitlab.com/nvidia/kubernetes/gpu-operator/-/raw/$RELEASE_TAG/deployments/gpu-operator/crds/nvidia.com_clusterpolicies_crd.yaml kubectl apply -f \ https://gitlab.com/nvidia/kubernetes/gpu-operator/-/raw/$RELEASE_TAG/deployments/gpu-operator/crds/nvidia.com_nvidiadrivers.yaml
示例输出:
customresourcedefinition.apiextensions.k8s.io/clusterpolicies.nvidia.com configured customresourcedefinition.apiextensions.k8s.io/nvidiadrivers.nvidia.com created
-
应用 Node Feature Discovery 的 CRD
更新 Node Feature Discovery 的 CRD:
kubectl apply -f \ https://gitlab.com/nvidia/kubernetes/gpu-operator/-/raw/$RELEASE_TAG/deployments/gpu-operator/charts/node-feature-discovery/crds/nfd-api-crds.yaml
示例输出:
customresourcedefinition.apiextensions.k8s.io/nodefeaturerules.nfd.k8s-sigs.io configured
-
更新 Operator 图表信息
更新 Helm 仓库中的 GPU Operator 图表:helm repo update nvidia
示例输出:
Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "nvidia" chart repository Update Complete. ⎈Happy Helming!⎈
-
获取图表中的值并更新值文件
获取图表中的默认值,并根据需要进行更新:helm show values nvidia/gpu-operator --version=$RELEASE_TAG > values-$RELEASE_TAG.yaml
-
升级 GPU Operator
使用更新后的values
文件升级 GPU Operator:helm upgrade gpu-operator nvidia/gpu-operator -n gpu-operator -f values-$RELEASE_TAG.yaml
示例输出:
Release "gpu-operator" has been upgraded. Happy Helming! NAME: gpu-operator LAST DEPLOYED: Thu Apr 20 15:05:52 2023 NAMESPACE: gpu-operator STATUS: deployed REVISION: 2 TEST SUITE: None
选项 2:使用 Helm Hook 自动升级 CRD
从 GPU Operator v22.09 开始,一个预升级的 Helm Hook 可以自动升级到最新的 CRD。 从 GPU Operator v24.9.0 开始,升级 CRD 的 Helm Hook 默认启用,并在使用 Helm 升级时运行一个升级 CRD 的任务。
-
指定 Operator 发布标签
在环境变量中指定 Operator 的发布标签:export RELEASE_TAG=v23.9.0
-
更新 Operator chart信息
更新 GPU Operator chart的信息:helm repo update nvidia
示例输出:
Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "nvidia" chart repository Update Complete. ⎈Happy Helming!⎈
-
获取图表中的值
获取 GPU Operator 图表的默认值文件,并根据需要进行更新:helm show values nvidia/gpu-operator --version=$RELEASE_TAG > values-$RELEASE_TAG.yaml
-
升级 GPU Operator
使用更新后的values
文件升级 GPU Operator,同时禁用 OpenAPI 验证以确保兼容性:helm upgrade gpu-operator nvidia/gpu-operator -n gpu-operator \ --disable-openapi-validation -f values-$RELEASE_TAG.yaml
说明
选项
--disable-openapi-validation
是必要的,在这种情况下,--disable-openapi-validation
选项是必要的,它可以防止 Helm 尝试验证来自新chart的 CR(自定义资源)实例是否符合旧 CRD(自定义资源定义)的要求。由于图表中的 CR 实例是为升级后的 CRD 设计的,因此这样设置可以确保兼容性。GPU Operator 中使用的 Helm Hook 会使用 Operator 镜像本身。如果由于网络错误或 NVAIE 场景下无效的 NGC 注册表密钥,导致 Operator 镜像无法成功拉取,则这些 Hook 将会失败。在这种情况下,为避免删除操作卡在 Hook 失败上,可以使用
--no-hooks
选项删除图表,从而跳过 Hook 的执行。
集群策略更新
GPU Operator 还支持使用 kubectl
命令动态更新 ClusterPolicy 自定义资源:
kubectl edit clusterpolicy
完成编辑后,Kubernetes 会自动将更新应用到集群中。
驱动升级的额外控制
虽然大多数 GPU Operator 管理的 DaemonSet 可以无缝升级,但 NVIDIA 驱动的 DaemonSet 需要特别注意。有关更多信息,请参阅 GPU 驱动升级。
在 OpenShift 中使用 OLM
如果在 OpenShift 中运行 GPU Operator 时需要升级,请参阅官方文档中的说明:升级已安装的 Operator。
3. 卸载
原文:Uninstalling the GPU Operator — NVIDIA GPU Operator 24.9.0 documentation
执行以下步骤卸载 Operator
-
可选:列出并删除 NVIDIA 驱动程序的自定义资源
运行以下命令以查看现有的 NVIDIA 驱动资源:
kubectl get nvidiadrivers
示例输出
NAME STATUS AGE demo-gold ready 2023-10-16T17:57:12Z demo-silver ready 2023-10-16T17:57:12Z
删除指定的 NVIDIA 驱动资源:
kubectl delete nvidiadriver demo-gold kubectl delete nvidiadriver demo-silver kubectl delete crd nvidiadrivers.nvidia.com
-
删除 Operator
使用以下命令删除 Operator:
helm delete -n gpu-operator $(helm list -n gpu-operator | grep gpu-operator | awk '{print $1}')
-
可选:检查 Operator 命名空间中的 Pod,以确认是否已删除或正在删除中
kubectl get pods -n gpu-operator
示例输出
No resources found.
默认情况下,Helm 不支持在删除 Chart 时删除现有的 CRD,这意味着
clusterpolicy
CRD 和nvidiadrivers
CRD 默认会保留在系统中。kubectl get crd clusterpolicies.nvidia.com
CRD 清理
Operator 使用 post-delete 钩子来执行 CRD 清理。可以在安装或升级时通过指定
--set operator.cleanupCRD=true
启用此钩子,以便在删除 Chart 时自动清理 CRD。或者,您也可以手动删除自定义资源定义:
kubectl delete crd clusterpolicies.nvidia.com
说明
在卸载 Operator 后,NVIDIA 驱动模块可能仍然加载在系统中。可以通过以下方法处理:
- 重启节点 或
- 使用以下命令卸载模块:
sudo rmmod nvidia_modeset nvidia_uvm nvidia
此外,GPU Operator 使用的 Helm 钩子依赖于 Operator 镜像本身。如果由于网络错误或无效的 NGC 注册表密钥(如在 NVAIE 环境中)导致无法成功拉取 Operator 镜像,钩子将会失败。在这种情况下,请删除该 Chart,并指定
--no-hooks
参数,以避免因钩子失败而卡住。