使用 Cloud Monitoring 监控 GKE 集群以优化费用

本教程介绍如何监控 Google Kubernetes Engine (GKE) 集群以优化资源利用率。此类优化通常是复杂的任务,因为需要通过降低资源消耗量来减少费用,但同时又不牺牲应用的稳定性和性能。本教程介绍针对过度预配的常见原因设置信息中心和提醒政策的过程,此外还提供资源建议,以帮助您确保应用可靠运行并针对费用优化。

本教程适用于希望优化 GKE 集群和应用以实现低费用、高性能和高应用稳定性的开发者和运营人员。本教程假定您熟悉 DockerKubernetes、Kubernetes CronJobs、GKE、Cloud Monitoring 和 Linux。

概览

费用优化通常被误解为旨在降低费用的一次性过程。但是,根据 Gartner 的定义,费用优化是一个需要持续执行的方法,它同时也必须最大限度地提升业务价值。当您将这种方法具体应用于 Kubernetes 领域时,其他角度也十分重要。

平衡 4 个不同的目标:降低费用、实现性能目标、实现稳定性以及最大限度地提升业务成果。

如图所示,Kubernetes 上的费用优化要求平衡 4 个不同的目标:降低费用、实现性能目标、实现稳定性以及最大限度地提升业务成果。换言之,降低费用不应以牺牲用户体验或业绩表现为代价,除非已经对这种影响有充分了解或准备。

Google Cloud 提供了平衡这些目标所需的工具。但是,并非所有为工作负载采用云端解决方案(如 Kubernetes)的团队都具备实现性能和稳定性目标的专业知识。通常,他们的解决方案是过度供应环境,以减轻对业务的影响。

过度供应可以在短期内缓解问题,但费用较高。您应该谨慎使用此方法,并且应该仅作为持续的费用优化过程的一部分实施。下图显示了在开展这一费用优化之旅的团队中发现的 4 个问题。

在团队中发现的最突出的 4 个问题:文化、装箱、优化应用容量以及非高峰时段不缩容。

  • 第一个问题是文化问题。许多采用公有云的团队并不适应随用随付的结算方式,并且通常未能完全了解其应用运行的环境(在本例中为 Kubernetes)。最近受到广泛关注的 FinOps 运动就致力于改进这种文化。FinOps 的一个最佳做法是向团队提供有关其支出和业务影响的实时信息。诸如此类的小措施会对公司文化有显著影响,从而产生更为平衡的费用优化等式。

  • 第二个问题是装箱。装箱是将应用加载到 GKE 节点中的能力。应用加载到节点中的效率越高,节省的费用就越多。

  • 第三个问题是优化应用容量。优化容量是为部署在集群中的对象配置适当的资源请求和工作负载自动扩缩目标的能力。为 pod 设置的资源越精确,应用的运行就越可靠,在大多数情况下,您在集群中打开的空间也越多。

  • 最后一个问题是集群在非高峰时段不缩容。理想情况下,为了在需求较低的时段(例如,夜间)节省费用,集群应该能够根据实际需求进行缩容。但在某些情况下,由于工作负载或集群配置阻止了集群自动扩缩器 (CA),导致无法按预期进行缩容。

为了有效地优化环境费用,您必须持续处理这些问题。为了突出实操性内容,本教程的剩余部分将跳过文化问题,并介绍如何使用 Cloud Monitoring 监控 GKE 集群中的装箱及优化应用容量。如需详细了解如何在需求较低的时段优化集群费用,请参阅在非高峰时段缩减 GKE 集群以降低费用

目标

  • 创建 GKE 集群。
  • 部署示例应用。
  • 设置用于将所需指标导出到 Cloud Monitoring 的组件。
  • 动态生成用于监控资源利用率和建议的信息中心。
  • 动态生成过度供应和供应不足的提醒政策。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

  1. 在 Google Cloud Console 中,转到项目选择器页面。

    转到“项目选择器”

  2. 选择或创建 Google Cloud 项目。

  3. 确保您的 Cloud 项目已启用结算功能。了解如何检查项目是否已启用结算功能

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备环境

  1. 在控制台中,打开 Cloud Shell。在本教程中,您将在 Cloud Shell 中执行命令。

    Cloud Shell 会话随即会在控制台的底部打开,并显示命令行提示。Cloud Shell 是一个已安装 Google Cloud CLI 的 shell 环境,其中包括 Google Cloud CLI 以及已为当前项目设置的值。该会话可能需要几秒钟来完成初始化。

  2. 在 Cloud Shell 中,配置您的 Cloud 项目 ID 和电子邮件地址,并启用 Compute Engine、GKE 和 Cloud Monitoring API:

    PROJECT_ID=YOUR_PROJECT_ID
    ALERT_EMAIL=YOUR_EMAIL_ADDRESS
    CLUSTER=gke-cost-optimization-monitoring
    gcloud config set project $PROJECT_ID
    
    gcloud services enable \
        compute.googleapis.com \
        container.googleapis.com \
        monitoring.googleapis.com
    
    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-f
    

    请替换以下内容:

    • YOUR_PROJECT_ID:您在本教程中使用的项目的 Cloud 项目 ID。
    • YOUR_EMAIL_ADDRESS:在集群中发现过度供应和供应不足时要通知的电子邮件地址。

    您可以选择其他区域和可用区

  3. 克隆 gke-cost-optimization-monitoring GitHub 代码库:

    git clone https://github.com/GoogleCloudPlatform/gke-cost-optimization-monitoring
    cd gke-cost-optimization-monitoring
    

    此代码库中的代码分布在以下文件夹中:

    • root:包含 CronJob 用于将自定义指标导出到 Cloud Monitoring 的 main.goDockerfile 文件。
    • api/:包含用于操控 Kubernetes 和 Monitoring 对象的 golang API。
    • k8s/templates/:包含用于在集群中创建 CronJob、VPA 和 Pod 纵向自动扩缩器 (HPA) 的模板。
    • monitoring/dashboards/templates/:包含用于动态创建装箱和优化应用容量信息中心的模板。
    • monitoring/policies/templates/:包含用于动态创建装箱和优化应用容量提醒政策的模板。

创建 GKE 集群

  1. 在 Cloud Shell 中创建一个 GKE 集群:

    gcloud container clusters create $CLUSTER \
        --enable-ip-alias \
        --release-channel=stable \
        --machine-type=e2-standard-2 \
        --enable-autoscaling --min-nodes=1 --max-nodes=5 \
        --enable-vertical-pod-autoscaling
    

    此设置不是生产配置,但适合本教程。在此设置中,您启用 VPA 以获取优化应用容量的基础数据。

部署示例应用

  1. 部署 Online Boutique 应用

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/main/release/kubernetes-manifests.yaml
    

    Online Boutique 应用是一个演示版的零售网店,由使用不同计算机语言编写的许多个微服务组成。

  2. 为了模拟更真实的环境,请为 Online Boutique 部署创建 HPA:

    kubectl get deployments --field-selector='metadata.name!=adservice,metadata.name!=cartservice' -o go-template-file=k8s/templates/cpu-hpa.gtpl | kubectl apply -f -
    
    kubectl get deployments --field-selector='metadata.name==cartservice' -o go-template-file=k8s/templates/memory-hpa.gtpl | kubectl apply -f -
    

    请注意,您为大多数 Online Boutique 部署创建 CPU 目标 HPA 对象,为 cartservice 部署创建内存目标 HPA,并且不为 adservice 创建 HPA 配置。此设置有助于演示不同的信息中心可视化图表,如以下部分所述。

设置用于将指标导出到 Cloud Monitoring 的组件

  1. 构建并推送自定义指标导出器代码:

    docker build . -t gcr.io/$PROJECT_ID/metrics-exporter
    docker push gcr.io/$PROJECT_ID/metrics-exporter
    

    此代码负责查询集群中的 VPA 和 HPA 对象,并将基于该数据的自定义指标发送到 Cloud Monitoring。此实现导出 VPA 目标建议和 HPA 资源目标利用率(百分比形式的 CPU 和内存)。

  2. 部署 CronJob,以便每分钟将工作负载自动扩缩器指标发送到 Cloud Monitoring:

    sed "s/PROJECT_ID/$PROJECT_ID/g" k8s/templates/metrics-exporter.yaml > k8s/metrics-exporter.yaml
    kubectl create ns custom-metrics
    kubectl apply -f k8s/metrics-exporter.yaml
    

    如果您在自己的集群(而不是之前创建的集群)中运行本教程,且该集群已启用 Workload Identity,请务必按照使用 Workload Identity 中的步骤操作,以允许将指标导出到 Cloud Monitoring。

  3. 为集群中的所有 Deployment、StatefulSet 和 DaemonSet 对象创建 VPA:

    rm k8s/vpas.yaml 2> /dev/null
    ALL_NAMESPACES=$(kubectl get namespaces -o jsonpath={.items[*].metadata.name})
    for NAMESPACE in $ALL_NAMESPACES
    do
        kubectl get deployments,statefulset,daemonset -n $NAMESPACE -o go-template-file=k8s/templates/vpa.gtpl >> k8s/vpas.yaml
    done
    kubectl apply -f k8s/vpas.yaml
    

    上面的代码段为所有命名空间中的所有对象(包括系统对象)创建 Off 模式的 VPA。此方法可以在集群级层和节点池级层提供更准确的建议。但是,为避免使 metrics-server 服务过载,我们建议您不要在部署了数百个应用的大型集群中按原样执行上述脚本。对于大型集群场景,我们建议您仅针对要进行费用优化的命名空间运行上述脚本。在这种场景中,集群和节点池级层的建议是不准确的,因此请忽略或移除这些建议。

设置用于监控资源利用率和建议的信息中心

  1. 在控制台中,打开 Monitoring 页面。

    打开 Monitoring

  2. 创建项目。

    1. 将您的项目添加到工作区页面中,选择您的 Cloud 项目。
    2. 点击添加

    创建工作区可能需要几分钟的时间。

  3. 在 Cloud Shell 中,动态生成用于费用优化的信息中心:

    YOUR_NAMESPACES=$( echo $ALL_NAMESPACES| sed 's/[a-zA-Z0-9_.-]*-system//g; s/gke-[a-zA-Z0-9_.-]*//g; s/kube-public//g; s/kube-node-lease//g; s/custom-metrics//g')
    for NAMESPACE in $YOUR_NAMESPACES
    do
        GTPL_FILE='./monitoring/dashboards/templates/app-rightsizing.gtpl'
        OUTPUT_FILE="./monitoring/dashboards/app-rightsizing-$CLUSTER-$NAMESPACE.yaml"
    
        kubectl get deployments,statefulset,daemonset -n $NAMESPACE -o go-template-file=$GTPL_FILE > $OUTPUT_FILE
    
        sed -i.bkp "s/CLUSTER_TO_REPLACE/$CLUSTER/g" $OUTPUT_FILE
        sed -i.bkp "s/NAMESPACE_TO_REPLACE/$NAMESPACE/g" $OUTPUT_FILE
    
        replace=""
        i=0
        while : ; do
            if grep -q "Y_POS_TO_REPLACE_$i" $OUTPUT_FILE
            then
                ((yPos=12 + (4 * $i)))
                replace="s/Y_POS_TO_REPLACE_$i/$yPos/g; ${replace}"
                ((i=i+1))
            else
                break
            fi
        done
        eval "sed -i.bkp '$replace' $OUTPUT_FILE"
        rm "$OUTPUT_FILE.bkp"
    done
    
    sed "s/CLUSTER_TO_REPLACE/$CLUSTER/g" ./monitoring/dashboards/templates/binpacking.yaml > ./monitoring/dashboards/binpacking.yaml
    

    除了创建装箱信息中心之外,此脚本还会为集群中的每个命名空间生成优化应用容量信息中心,但不包括系统命名空间。在本教程中,脚本仅为 default 命名空间生成一个信息中心,因为 Online Boutique 完全部署在该命名空间中。但是,如果您在自己的 GKE 集群中运行相同的脚本,该脚本会为每个命名空间生成一个信息中心。

  4. 将生成的信息中心导入 Cloud Monitoring:

    for filename in monitoring/dashboards/*.yaml; do
        echo "Creating dashboard policy based on file: $filename"
        gcloud monitoring dashboards create \
            --config-from-file=$filename
    done
    

    输出内容类似如下:

    Creating dashboard policy based on file: monitoring/dashboards/app-rightsizing-gke-cost-optimization-monitoring-default.yaml
    Created [9c1a6cb6-3424-44a8-b824-f2ec959f6588].
    Creating dashboard policy based on file: monitoring/dashboards/binpacking.yaml
    Created [97f6349d-4880-478d-9da8-ca3c8a433093].
    

查看优化应用容量信息中心

  1. 转到 Monitoring 信息中心页面。

    转到“信息中心”页面

  2. 点击 GKE - App Right-Sizing (gke-cost-optimization-monitoring:default) 信息中心。

本部分的其余内容介绍如何查看和解读信息中心中显示的图表。

已安装的演示环境具有稳定的模拟负载。换句话说,图表不会随时间发生大的变化。但如果您在自己的集群中运行相同的教程,则需要等待几个小时(最好是 24 小时或更长时间),以查看扩容和缩容的动态变化以及一天内和一周内的负载分布变化。

图表中的第一行显示命名空间过度供应;第二行显示前 5 个过度供应的应用;第三行显示前 5 个供应不足的应用。

如上图所示,信息中心的前三行总结了以下汇总的命名空间信息:

  • 第一行:CPU 和内存:命名空间过度供应。提供应用在此给定命名空间内的费用优化程度的概览。
  • 第二行:CPU 和内存:前 5 个过度供应的应用。显示寻求改善命名空间的方法时应该首先关注的应用。
  • 第三行:CPU 和内存:前 5 个供应不足的应用。与第二行类似,此行显示需要特别关注的应用。但是,在此场景中,您关注的不是节省费用,而是使应用在集群中顺畅运行。如果您看到“无可用数据”消息,则表示未发现机会。

下图是信息中心中显示的每个应用的 CPU、内存和副本的详细信息。每一行代表给定命名空间中的一个应用。通过将开发者请求的应用资源(requested_<cores|memory> 行)与应用实际使用的资源(used_<cores|memory> 行)进行比较,此信息对于优化应用容量非常有用。

优化应用容量信息中心显示 CPU、内存和副本的详细信息。

以下部分讨论了以上图表中的三行。

第一行:CPU (p/pod)

根据工作负载的配置方式,这些图表显示了可帮助您确定适合应用的容量的不同提示:

  • VPA CPU 建议 (vpa_recommended_cores):如果应用未配置 HPA(信息中心内的 CPU: adservice (p/Pod) 图表)或者 HPA 配置了除 CPU 以外的任何指标(信息中心中内的 CPU: cartservice (p/Pod) 图表),则会显示此提示。如果您看到这些提示,我们强烈建议您以静态方式应用这些提示,或者如果您愿意查看图表历史记录,请以 InitialAuto 模式启用 VPA

  • HP CPU 目标利用率 (hpa_target_utilization):如果应用基于 CPU 利用率配置了 HPA(所有其他 CPU 图表),则会显示此提示。在此场景中,我们建议您执行以下操作:

    • 过度供应情况:如果实际利用率 (used_cores) 始终远远低于 HPA 目标 (hpa_target_utilization),则表示部署以 HPA minReplicas 值中指定的值运行。在此场景中,建议的操作是减小 minReplicas 值。
    • 供应不足情况:如果实际利用率 (used_cores) 始终高于 HPA 目标 (hpa_target_utilization),则表示部署以 HPA maxReplicas 值中指定的值运行。此处建议的操作是提高 maxReplicas 值或增加请求的资源以增大 pod。
    • 了解扩容和缩容:查看 CPUReplicas 图表,了解 CPU 上的 HPA 何时触发 pod 扩容和缩容。
    • HPA 目标利用率微调:在将任何内容应用于您的环境之前,请查看我们的最佳做法
    • 此外,请务必避免在同一工作负载中将 VPA 和 HPA 与 CPU 或内存混合使用。为此,请使用 MPA

第二行:内存 (p/pod)

与 CPU 行类似,根据工作负载的配置方式,这些图表显示了可帮助您确定适合应用的容量的不同提示:

  • VPA 内存建议 (vpa_recommended_bytes):如果应用未配置 HPA(信息中心内的 Mem: adservice (p/Pod) 图表)或者 HPA 配置了除内存以外的任何指标(例如 Men: emailservice (p/Pod)),则会显示此提示。请考虑应用此类建议以避免资源浪费和“Out Of Memory Kill”(OOMKill) 事件,或者如果您愿意查看图表历史记录,请以 InitialAuto 模式启用 VPA

  • HPA 内存目标利用率 (hpa_target_utilization):如果应用基于内存利用率配置了 HPA(信息中心内的 Mem: cartservice (p/Pod) 图表),则会显示此提示。在此场景中,我们建议您执行以下操作:

    • 过度供应情况:如果实际利用率 (used_bytes) 始终远远低于 HPA 目标 (hpa_target_utilization),则表示部署以 HPA minReplicas 值中指定的值运行。在此场景中,建议的操作是减小 minReplicas 值。
    • 供应不足情况:如果实际利用率 (used_bytes) 始终高于 HPA 目标 (hpa_target_utilization),则表示部署以 HPA maxReplicas 值中指定的值运行。此处建议的操作是提高 maxReplicas 值或增加请求的资源以增大 pod。
    • 了解扩容和缩容:查看 MemReplicas 图表,了解内存上的 HPA 何时触发 pod 扩容和缩容。
    • HPA 目标利用率微调:在将任何内容应用于您的环境之前,请查看我们的最佳做法
    • 此外,请务必避免在同一工作负载中将 VPA 和 HPA 与 CPU 或内存混合使用。为此,请使用 MPA

第三行:副本

此行中的图表显示给定应用具有的 pod 数量。此信息对于了解部署了 HPA 的工作负载的实际使用资源相对于建议资源的波动程度非常有用。

查看装箱信息中心

  1. 再次转到 Monitoring 信息中心页面。

    转到“信息中心”

  2. 点击 GKE - Cluster Bin Packing (gke-cost-optimization-monitoring) 信息中心。

如下面的图表所示,装箱信息中心显示按集群(第一行)和节点池(第二行)汇总的信息。通过将集群和节点池中的可分配容量(allocable_<cores|memory> 行)与开发者请求的应用容量 (requested_<cores|memory>) 进行比较,此信息对于集群装箱非常有用。

装箱信息中心显示按集群(第一行)和节点池(第二行)汇总的信息。

您可以基于这些图表运行的一项有用分析是,了解是否有某个资源类型(例如内存)被耗尽,而另一个资源类型(例如 CPU)被浪费。在此场景下,集群自动扩缩器会触发扩容,因为没有内存可用于将被调度的 pod 放入集群中。另一个常见情况与 pod 密度(即每个节点的 pod 数)有关。在节点池:pod 数量图表中,您可以看到每个节点池中的 pod 密度,并将其与配置的信息进行比较(图表中未提供)。达到节点池的配置密度时,CA 将启动新节点以放入被调度的 pod,即使有足够的 CPU 和内存可用也是如此。

上面的信息中心未提供的另一项重要分析与自动扩缩器的最小和最大配置相关。例如,集群可能不会在夜间缩容,因为您的 HPA 或 CA 需求超过了最小值。此外,CA 可能未在预期的节点池中启动 pod,因为该节点池可能已达到配置的节点数上限。

信息中心限制

  • 虽然优化应用容量信息中心显示一个给定命名空间中所有应用的汇总信息,但由于单个信息中心内微件数量的限制,它仅显示前 8 个应用(按名称排序)的 CPU、内存和副本信息。如果您发现显示的应用不是最重要的应用,请修改信息中心以满足您的需求。
  • 装箱信息中心提供集群和节点池的汇总数据。在处理大量集群或节点池时,可视化会受到限制,因为不允许针对用于构建图表的 Monitoring Query Language (MQL) 运行过滤条件。
  • 在监控包含数百个应用的大型集群时,装箱信息中心需要很长时间来加载。在这种情况下,为了减少加载的数据量,建议您不要使用太长的时间段来过滤信息中心。

设置过度供应和供应不足提醒政策

当贵公司的财务团队询问您为何云帐单金额最近增长了一倍时,您大概不希望告诉他们这是由于过度供应的问题。为避免这种情况,我们强烈建议您创建提醒政策,在您的环境开始偏离计划时触发提醒。

  1. 在 Cloud Shell 中,创建通知渠道:

    gcloud beta monitoring channels create \
        --display-name="Cost Optimization team (Primary)" \
        --description="Primary contact method for the Cost Optimization effort"  \
        --type=email \
        --channel-labels=email_address=${ALERT_EMAIL}
    

    输出内容类似如下:

    Created notification channel [projects/your_project/notificationChannels/13166436832066782447].
    

    为了简化教程步骤,上述命令创建一个 email 类型的通知渠道。在生产环境中,建议您通过将通知渠道设置为 smspagerduty,使用同步程度较高的策略。

  2. 设置一个变量,其具有 NOTIFICATION_CHANNEL_ID 占位符中显示的值:

    NOTIFICATION_CHANNEL_ID=$(gcloud beta monitoring channels list --filter='displayName="Cost Optimization team (Primary)"' | grep 'name:' | sed 's/name: //g')
    
  3. 动态创建和部署提醒政策:

    for NAMESPACE in $YOUR_NAMESPACES
    do
        for templatefile in monitoring/policies/templates/rightsizing/*.yaml; do
            outputfile=monitoring/policies/$(basename $templatefile)
            sed "s/CLUSTER_TO_REPLACE/$CLUSTER/g;s/NAMESPACE_TO_REPLACE/$NAMESPACE/g" $templatefile > $outputfile
            echo "Creating alert policy based on file: $outputfile"
            gcloud alpha monitoring policies create \
                --policy-from-file=$outputfile \
                --notification-channels=$NOTIFICATION_CHANNEL_ID
        done
    done
    
    for templatefile in monitoring/policies/templates/binpacking/*.yaml; do
        outputfile=monitoring/policies/$(basename $templatefile)
        sed "s/CLUSTER_TO_REPLACE/$CLUSTER/g;s/NAMESPACE_TO_REPLACE/$NAMESPACE/g" $templatefile > $outputfile
        echo "Creating alert policy based on file: $outputfile"
        gcloud alpha monitoring policies create \
            --policy-from-file=$outputfile \
            --notification-channels=$NOTIFICATION_CHANNEL_ID
    done
    

    输出内容类似如下:

    Creating alert policy based on file: monitoring/policies/app-rightsizing-cpu-overprovisioning-alert.yaml
    Created alert policy [projects/rubbo-vpa-3-1/alertPolicies/18091138402474167583].
    Creating alert policy based on file: monitoring/policies/app-rightsizing-cpu-underprovisioning-alert.yaml
    Created alert policy [projects/rubbo-vpa-3-1/alertPolicies/8586234469403227589].
    Creating alert policy based on file: monitoring/policies/app-rightsizing-memory-overprovisioning-alert.yaml
    Created alert policy [projects/rubbo-vpa-3-1/alertPolicies/9685822323903723723].
    Creating alert policy based on file: monitoring/policies/app-rightsizing-memory-underprovisioning-alert.yaml
    Created alert policy [projects/rubbo-vpa-3-1/alertPolicies/15705075159352926212].
    Creating alert policy based on file: monitoring/policies/nodepools-binpacking-cpu-overprovisioning-alert.yaml
    Created alert policy [projects/rubbo-vpa-3-1/alertPolicies/14555072091442814207].
    Creating alert policy based on file: monitoring/policies/nodepools-binpacking-memory-overprovisioning-alert.yaml
    Created alert policy [projects/rubbo-vpa-3-1/alertPolicies/1442392910032052087].
    

    默认情况下,创建的提醒政策包含这样的规范:在超过一天的时间段内,当应用的过度供应超过 80% 并且节点池的过度供应超过 40% 时,则触发提醒。请务必对这些政策进行微调,以满足您的资源利用率要求。

  4. 转到 Monitoring 提醒页面以查看提醒政策。

    转到“提醒”页面

  5. 点击任何已创建的政策,然后验证或修改提醒配置的详细信息。

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请执行以下操作。

删除项目

若要避免产生费用,最简单的方法是删除您为本教程创建的项目。

  1. 在控制台中,打开管理资源页面。

    打开“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤