在 vSphere 集群中添加或移除节点

本文档介绍了如何在 VMware Solution on Google Cloud的 VMware Cloud Foundation (VCF) 9.1 中向现有 vSphere 集群添加一个或多个 Compute Engine 裸金属节点(集群扩容),以及从集群中移除一个或多个节点(集群缩容)。当您需要增加或减少管理网域或 VI 工作负载网域中现有集群的计算和 vSAN 存储容量时,可以扩展或压缩集群。无论您是添加或移除单个节点还是多个节点,都适用相同的过程:在 ADD_HOSTS 或 REMOVE_HOSTS 环境变量中列出一个或多个节点。

架构摘要

在现有 vSphere 集群中添加或移除节点时,工作流会使用以下架构:

  • 添加节点:每个新节点都是一个 z3-highmem-192-highlssd-metal 裸金属实例,您可以在与集群现有节点相同的可用区、布置政策和机器类型中部署该实例。每个节点在虚拟私有云 (VPC) 网络的 ESXi 管理子网上都有一个主要网络接口 (nic0),以及用于 vMotion、vSAN 和 NSX 隧道端点 (TEP) 的加 VLAN 标签的 Dynamic Network Interface。该节点会加入现有的可用区级网络端点组 (NEG)。然后,您在 SDDC Manager 中使用集群的网络池调试该节点,并将其添加到集群中。
  • 移除节点:您首先从 VCF 中的集群中移除节点,这会迁移工作负载虚拟机并清空主机的 vSAN 数据。然后,您需要从 SDDC Manager 中停用该主机,并将其从可用区级 NEG 中移除,最后再删除底层的 Compute Engine 裸金属实例。
  • IP 地址对齐:与集群创建一样,VCF 从集群的网络池中分配 vMotion 和 vSAN IP 地址,NSX 从 NSX IP 地址池中分配 TEP (vmk10) IP 地址。您需要将新节点的 Compute Engine Dynamic NIC 与 VCF 分配的 IP 地址对齐,同时保持所有 VCF 验证处于启用状态。
  • 无需更改设备:添加或移除集群节点不需要新的 Cloud DNS 记录、后端服务、高可用性 (HA) 政策或管理设备的转发规则。

准备工作

在添加或移除节点之前,请验证以下部分中的要求并配置环境变量。

验证环境要求

验证您的环境是否满足以下要求:

  • 权限:您在目标项目中拥有所需的 Identity and Access Management (IAM) 角色,包括 Compute Admin (roles/compute.admin)、DNS Administrator (roles/dns.admin) 和 Secret Manager Secret Accessor (roles/secretmanager.secretAccessor)。
  • Secret Manager 中的凭据:如果您在初始部署后更改或轮替了 VCF 中的 ESXi root、SDDC Manager、vCenter Server 或 NSX Manager 密码,Secret Manager 不会自动更新。在运行本文档中的命令之前,请更新 Secret Manager 中的相应 Secret,或直接提供更新后的密码,使其与您的有效 VCF 凭据一致。
  • 集群和网域运行状况:目标集群及其网域运行状况良好(vCenter Server、vSAN、NSX Manager、SDDC Manager 和 VCF Operations),并且没有失败的任务。
  • 添加节点:

    • 您的项目在目标区域和可用区中有足够的 z3 裸金属配额。
    • 新节点使用与集群中现有节点相同的机器类型(对于 vSAN Express Storage Architecture [ESA],为 z3-highmem-192-highlssd-metal),并分配给集群的现有布置政策(每个布置政策仅属于一个集群)。
    • 每个新节点都有 NVMe 本地 SSD 设备,且没有预先存在的分区。
    • 如果您的组织强制执行 constraints/compute.trustedImageProjects 组织政策或使用 VPC Service Controls 服务边界,请确保您的项目允许对 smve-prod-public-images 项目 (projects/999037279871) 进行映像访问。如需了解详情,请参阅管理节点容量。
    • 基于 VLAN 的网络:VMware 解决方案要求 VPC 网络的子网将解析子网掩码属性设置为仅通过学习广播主要范围,以支持基于 VLAN 的网络。在 Google Cloud 控制台支持中心中提交支持服务工单,并提供以下信息:

      • 产品:Google Cloud VMware Engine
      • 优先级:P2
      • 类别:Network Connectivity
      • 业务影响:常规技术问题
      • 提供更多详细信息:Allowlist L2 network connectivity for my project,以及您的项目 ID、项目编号和部署的目标区域和可用区

      如需详细了解对基于 VLAN 的网络的支持,请参阅第 2 层模拟:ARP 广播和自定义 MAC 学习。

  • 如需移除节点,请执行以下操作:

    • 其余节点具有足够的计算和 vSAN 存储容量,可满足活跃工作负载的需求。
    • 移除节点后,集群会保留至少三个节点(vSAN 的最低要求,允许一个故障 (FTT=1))或有效存储政策要求的最低主机数量(请参阅 vSAN 允许的故障数和主机数量)。
  • Jumpbox 虚拟机:您可以访问安装了 gcloud CLI、curl 和 jq 且与 VPC 网络(例如,在 bm-gcp-demo 子网中)建立了网络连接的 Jumpbox 虚拟机。

查看示例环境参数

本文档中的命令使用以下示例参数:

输入参数 示例值 说明
项目 ID、区域和可用区 PROJECT_ID / us-east4 / us-east4-b 与目标集群位于同一项目、区域和可用区
客户 ID demo 现有资源名称中使用的标识符
目标集群 cl02(esxi-h7、esxi-h8、esxi-h9) 管理网域或 VI 工作负载网域中的现有集群
布置政策 bm-cl02 集群的现有组布置政策
网络池和 VDS cl02-np01/cl02-vds-01 集群的现有网络池和 vSphere Distributed Switch (VDS)
NSX TEP IP 池 cl02-tep-pool 集群的现有 NSX IP 地址池
NSX Manager nsx01.vmware.gve 集群网域的 NSX Manager(或具有专用 NSX 实例的 wld01 工作负载网域的 nsx05.vmware.gve)
机器类型 z3-highmem-192-highlssd-metal 与集群中现有节点相同的机器类型
VPC 网络和子网(VLAN ID) bm-demo;bm-mgmt-demo(未加标签)、bm-vmotion-demo (2)、bm-vsan-demo (3)、bm-nsx-demo (4) 现有 VPC 网络和第 2 层子网
区域 NEG bm-mgmt-demo,bm-nsx-tep-demo 现有管理和 NSX TEP NEG
SDDC 管理器 sddcm.vmware.gve (192.168.0.3) 现有 SDDC Manager 设备
ESXi root 密码 esxi-root-password 包含 ESXi root 密码的现有 Secret Manager 密钥

规划 IP 地址和主机名

以下示例向 cl02 集群添加两个节点(esxi-h10 和 esxi-h11),然后移除其中一个节点 (esxi-h11)。如需添加或移除单个节点,请仅在环境变量数组中包含该节点:

ESXi 主机 管理 IP (nic0) vMotion (VLAN 2) vSAN (VLAN 3) NSX TEP (VLAN 4) 可用性网域 操作
esxi-h10 192.168.0.164 192.168.2.24 192.168.3.24 192.168.4.24 4 添加
esxi-h11 192.168.0.165 192.168.2.25 192.168.3.25 192.168.4.25 5 先添加,后移除

在集群的布置政策中,将集群的节点分布在各个可用性网域中:由于现有节点(esxi-h7 到 esxi-h9)使用可用性网域 1 到 3,因此新节点使用可用性网域 4 和 5。

验证区域配额

在预配裸金属节点或创建集群之前,请验证您的项目是否有足够的区域和全球 Compute Engine 配额。在将该区域中所有现有集群的资源与项目配额进行比较之前,请先将这些资源添加到以下增量值中:

配额指标 每个裸金属节点所需的资源 3 节点集群所需的资源
Z3的compute.googleapis.com/cpus_per_vm_family 192 个 vCPU 3 × 192 = 576 个 vCPU
Z3的compute.googleapis.com/local_ssd_total_storage_per_vm_family 72 TB 3 × 72 TB = 216 TB
compute.googleapis.com/hyperdisk_balanced_total_storage 128 GB 3 × 128 GB = 384 GB
compute.googleapis.com/resource_policies 向现有集群添加节点时为 0 每个集群 1 项分散布置政策
compute.googleapis.com/regional_backend_services_protocol_forwarding_fast_ha 在不添加新设备的情况下添加节点或集群时,此值为 0 每个新的 VCF 管理设备 VIP 或 NSX Edge 端点 1 个

如需查看所需配额的完整列表以及有关如何查看或申请增加配额的说明,请参阅配额和限制。

设置环境变量

在跳转盒 shell 中设置以下环境变量。在 ADD_HOSTS 环境变量中列出要添加的节点,在 REMOVE_HOSTS 环境变量中列出要移除的节点(每个变量一个或多个条目):

export PROJECT_ID="PROJECT_ID"
export REGION="us-east4"
export ZONE="us-east4-b"
export CUSTOMER_ID="demo"
export VPC_NETWORK="bm-${CUSTOMER_ID}"
export DOMAIN="vmware.gve"
export DNS_ZONE="dns-zone-${CUSTOMER_ID}"
export REV_DNS_ZONE="rev-dns-zone-${CUSTOMER_ID}"
export MGMT_SUBNET="bm-mgmt-${CUSTOMER_ID}"
export VMOTION_SUBNET="bm-vmotion-${CUSTOMER_ID}"
export VSAN_SUBNET="bm-vsan-${CUSTOMER_ID}"
export TEP_SUBNET="bm-nsx-${CUSTOMER_ID}"
export MGMT_NEG="bm-mgmt-${CUSTOMER_ID}"
export TEP_NEG="bm-nsx-tep-${CUSTOMER_ID}"
export CLUSTER_NAME="cl02"
export PLACEMENT_POLICY="bm-${CLUSTER_NAME}"          # Existing placement policy of the cluster
export NETWORK_POOL_NAME="${CLUSTER_NAME}-np01"        # Existing network pool of the cluster
export VDS_NAME="${CLUSTER_NAME}-vds-01"               # Existing VDS of the cluster
export MACHINE_TYPE="z3-highmem-192-highlssd-metal"    # Same machine type as existing nodes
export ESXI_IMAGE_FAMILY="astra-vcf-9-1-x"
export ESXI_IMAGE_PROJECT="smve-prod-offline-depot"
export SDDC_MANAGER="sddcm.vmware.gve"                 # 192.168.0.3
export VCF_USER="admin@local"
export VCF_PASSWORD="VCF_ADMIN_PASSWORD"
export ESXI_PASSWORD_SECRET="esxi-root-password"
export NSX_MANAGER="nsx01.vmware.gve"                  # NSX Manager of the cluster's domain
export MTU=8896

# Nodes to add: name mgmt_ip vmotion_ip vsan_ip tep_ip availability_domain
ADD_HOSTS=(
  "esxi-h10 192.168.0.164 192.168.2.24 192.168.3.24 192.168.4.24 4"
  "esxi-h11 192.168.0.165 192.168.2.25 192.168.3.25 192.168.4.25 5"
)

# Nodes to remove (short hostnames)
REMOVE_HOSTS=(esxi-h11)

# Retrieve an SDDC Manager API token (re-run when the token expires)
TOKEN=$(curl -s -X POST "https://${SDDC_MANAGER}/v1/tokens" \
    -H "Content-Type: application/json" \
    -d "{\"username\":\"${VCF_USER}\",\"password\":\"${VCF_PASSWORD}\"}" | jq -r '.accessToken')

在运行这些命令之前,请替换以下内容:

  • PROJECT_ID:目标 Google Cloud项目的 ID。
  • VCF_ADMIN_PASSWORD:您的 SDDC Manager 管理员密码。

确认您计划使用的 IP 地址均未被现有转发规则或实例使用:

gcloud compute forwarding-rules list --project="${PROJECT_ID}" --regions="${REGION}" \
    --format="table(name,IPAddress,subnetwork.basename())"
gcloud compute instances list --project="${PROJECT_ID}" --zones="${ZONE}" \
    --format="table(name,networkInterfaces[].networkIP.list())"

向集群添加节点

如需通过添加裸金属节点来扩展现有 vSphere 集群,请完成以下部分中的步骤。

创建 DNS 记录

由于正向和反向 Cloud DNS 托管区域已存在,因此只需为新节点创建正向 (A) 和反向 (PTR) 记录:

for entry in "${ADD_HOSTS[@]}"; do
  read -r HOST MGMT_IP _ <<< "${entry}"
  gcloud dns record-sets create "${HOST}.${DOMAIN}." \
      --rrdatas="${MGMT_IP}" --type=A --ttl=300 \
      --zone="${DNS_ZONE}" --project="${PROJECT_ID}"
  REV=$(echo "${MGMT_IP}" | awk -F. '{print $4"."$3"."$2"."$1".in-addr.arpa."}')
  gcloud dns record-sets create "${REV}" \
      --rrdatas="${HOST}.${DOMAIN}." --type=PTR --ttl=300 \
      --zone="${REV_DNS_ZONE}" --project="${PROJECT_ID}"
done

# Verify DNS records
gcloud dns record-sets list --zone="${DNS_ZONE}" --project="${PROJECT_ID}" \
    --filter="name~'esxi-h1[01]'"

创建裸金属实例

在集群的现有布置政策中创建每个新节点,并为其配置主 ESXi 管理 NIC (nic0) 以及用于 vMotion(VLAN 2)、vSAN(VLAN 3)和 NSX TEP(VLAN 4)的加 VLAN 标签的 Dynamic NIC。

for entry in "${ADD_HOSTS[@]}"; do
  read -r HOST MGMT_IP VMOTION_IP VSAN_IP TEP_IP AD <<< "${entry}"
  # Create boot disk
  gcloud compute disks create "${HOST}" \
      --image-project="${ESXI_IMAGE_PROJECT}" \
      --image-family="${ESXI_IMAGE_FAMILY}" \
      --type=hyperdisk-balanced --interface=NVME --size=128GB \
      --guest-os-features=IDPF \
      --zone="${ZONE}" --project="${PROJECT_ID}"

  # Create bare-metal ESXi host
  gcloud compute instances create "${HOST}" \
      --zone="${ZONE}" \
      --machine-type="${MACHINE_TYPE}" \
      --can-ip-forward \
      --resource-policies="${PLACEMENT_POLICY}" \
      --availability-domain="${AD}" \
      --hostname="${HOST}.${DOMAIN}" \
      --disk="name=${HOST},boot=yes,auto-delete=yes" \
      --labels=gcve-node=true \
      --tags=mm-gcve-node \
      --deletion-protection \
      --maintenance-policy=TERMINATE \
      --restart-on-failure \
      --network-performance-configs=total-egress-bandwidth-tier=TIER_1 \
      --project="${PROJECT_ID}" \
      --network-interface="network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=${MGMT_SUBNET},no-address,private-network-ip=${MGMT_IP}" \
      --network-interface="network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=${VMOTION_SUBNET},no-address,private-network-ip=${VMOTION_IP},vlan=2" \
      --network-interface="network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=${VSAN_SUBNET},no-address,private-network-ip=${VSAN_IP},vlan=3" \
      --network-interface="network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=${TEP_SUBNET},no-address,private-network-ip=${TEP_IP},vlan=4"
done

# Verify instance creation and network interfaces
gcloud compute instances list --project="${PROJECT_ID}" --zones="${ZONE}" \
    --filter="resourcePolicies~${PLACEMENT_POLICY}" \
    --format="table(name,status,networkInterfaces[].vlan.list(),networkInterfaces[].networkIP.list())"

更新 NEG 成员资格

可用区中集群的所有节点都必须属于子网的同一可用区级 NEG。将新节点添加到管理 NEG 和 NSX TEP NEG。您无需修改高可用性政策或转发规则:

for entry in "${ADD_HOSTS[@]}"; do
  read -r HOST _ <<< "${entry}"
  for NEG in "${MGMT_NEG}" "${TEP_NEG}"; do
    gcloud compute network-endpoint-groups update "${NEG}" \
        --zone="${ZONE}" --add-endpoint="instance=${HOST}" --project="${PROJECT_ID}"
  done
done

# Verify NEG endpoints
for NEG in "${MGMT_NEG}" "${TEP_NEG}"; do
  gcloud compute network-endpoint-groups list-network-endpoints "${NEG}" \
      --zone="${ZONE}" --project="${PROJECT_ID}" \
      --format="table(networkEndpoint.instance,networkEndpoint.ipAddress)"
done

重置 ESXi root 密码

重置每个新节点的 root 密码,使其与 Secret Manager 中存储的 ESXi root 密码一致。在后续步骤中,您将使用同一 Secret 来调试节点。如果您在初始部署后更改或轮替了 ESXi root 或 VCF 密码,请先在 Secret Manager 中更新相应的 Secret,然后再运行这些命令。

如需在每个新节点上重置 root 密码,请从 jumpbox 虚拟机运行以下命令,并在出现提示时输入初始 ESXi root 密码:

ESXI_ROOT_PASSWORD=$(gcloud secrets versions access latest \
    --secret="${ESXI_PASSWORD_SECRET}" --project="${PROJECT_ID}")

for entry in "${ADD_HOSTS[@]}"; do
  read -r HOST _ <<< "${entry}"
  ssh -o StrictHostKeyChecking=no "root@${HOST}.${DOMAIN}" \
      "esxcli system account set -i root -p '${ESXI_ROOT_PASSWORD}' -c '${ESXI_ROOT_PASSWORD}'"
done

将新 IP 地址添加到网络池和 TEP 池

集群的网络池 (cl02-np01) 和 NSX TEP 池 (cl02-tep-pool) 最初仅包含现有集群节点的 IP 地址。将新节点的 Compute Engine Dynamic NIC 的确切 IP 地址添加到这两个池中,以便 VCF 和 NSX 从 Compute Engine NIC 上已配置的集合中分配 IP 地址。

  1. 更新网络池(vMotion 和 vSAN):使用 VCF Operations 或 SDDC Manager API 将新节点的 vMotion 和 vSAN IP 地址添加到 cl02-np01 网络池:

    VCF Operations

    在 VCF Operations 中,前往网络池管理,选择 cl02-np01 网络池,修改 vMotion 和 vSAN 网络,然后为新节点添加 IP 地址范围。

    SDDC Manager API

    运行以下命令,为每个新节点向网络池添加一个单 IP 范围:

    POOL_ID=$(curl -s "https://${SDDC_MANAGER}/v1/network-pools" -H "Authorization: Bearer ${TOKEN}" \
        | jq -r --arg n "${NETWORK_POOL_NAME}" '.elements[] | select(.name==$n) | .id')
    NETWORKS=$(curl -s "https://${SDDC_MANAGER}/v1/network-pools/${POOL_ID}/networks" \
        -H "Authorization: Bearer ${TOKEN}")
    VMOTION_NET_ID=$(echo "${NETWORKS}" | jq -r '.elements[] | select(.type=="VMOTION") | .id')
    VSAN_NET_ID=$(echo "${NETWORKS}" | jq -r '.elements[] | select(.type=="VSAN") | .id')
    
    for entry in "${ADD_HOSTS[@]}"; do
      read -r HOST MGMT_IP VMOTION_IP VSAN_IP _ <<< "${entry}"
      curl -s -X POST "https://${SDDC_MANAGER}/v1/network-pools/${POOL_ID}/networks/${VMOTION_NET_ID}/ip-pools" \
          -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
          -d "{\"start\":\"${VMOTION_IP}\",\"end\":\"${VMOTION_IP}\"}"
      curl -s -X POST "https://${SDDC_MANAGER}/v1/network-pools/${POOL_ID}/networks/${VSAN_NET_ID}/ip-pools" \
          -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
          -d "{\"start\":\"${VSAN_IP}\",\"end\":\"${VSAN_IP}\"}"
    done
    
    # Verify the network pool IP ranges
    curl -s "https://${SDDC_MANAGER}/v1/network-pools/${POOL_ID}/networks" -H "Authorization: Bearer ${TOKEN}" \
        | jq -r '.elements[] | [.type, (.ipPools | map(.start + "-" + .end) | join(","))] | @tsv'
    
  2. 更新 NSX TEP IP 池:在 NSX Manager(NSX_MANAGER 主机)中,前往网络 > IP 地址池,修改 cl02-tep-pool IP 地址池,然后将新节点的 TEP IP 地址添加到子网的 IP 范围(在本例中为 192.168.4.24–192.168.4.25),同时将网关设置为 192.168.4.1。验证更新后的池配置:

    NSX_PW="NSX_ADMIN_PASSWORD"
    curl -s -u "admin:${NSX_PW}" "https://${NSX_MANAGER}/policy/api/v1/infra/ip-pools" \
        | jq -r '.results[] | [.id, .display_name] | @tsv'
    
    TEP_POOL_ID="TEP_POOL_ID"   # ID of cl02-tep-pool from the preceding output
    curl -s -u "admin:${NSX_PW}" \
        "https://${NSX_MANAGER}/policy/api/v1/infra/ip-pools/${TEP_POOL_ID}/ip-subnets" \
        | jq '.results[] | {cidr, gateway_ip, allocation_ranges}'
    

    在运行这些命令之前,请替换以下内容:

    • NSX_ADMIN_PASSWORD:您的 NSX Manager admin 密码(可从 SDDC Manager /v1/credentials 端点或 VCF Operations 获取)。
    • TEP_POOL_ID:cl02-tep-pool IP 地址池的 ID。

调试节点

使用 VCF Operations 或 SDDC Manager API 将新节点调试到 SDDC Manager 清单中:

VCF Operations

  1. 在 VCF Operations 中,依次前往资产清单 > 主机 > 委托主机。
  2. 选择 cl02-np01 网络池,然后选择 vSAN ESA 存储类型。
  3. 输入新的主机 FQDN 和 root 凭据。
  4. 接受 SSL 指纹,验证主机,然后点击调试。

SDDC Manager API

运行以下命令来验证和调试主机,然后轮询任务状态,直到其返回 Successful:

ADD_FQDNS=$(for entry in "${ADD_HOSTS[@]}"; do
  read -r HOST _ <<< "${entry}"; echo "${HOST}.${DOMAIN}"
done | jq -R . | jq -s .)

HOSTS_JSON=$(jq -n --argjson f "${ADD_FQDNS}" --arg p "${ESXI_ROOT_PASSWORD}" \
    --arg np "${POOL_ID}" --arg npn "${NETWORK_POOL_NAME}" \
    '$f | map({fqdn: ., username: "root", password: $p, storageType: "VSAN_ESA",
               networkPoolId: $np, networkPoolName: $npn})')

# 1. Validate hosts
curl -s -X POST "https://${SDDC_MANAGER}/v1/hosts/validations" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d "${HOSTS_JSON}"

# 2. Commission hosts and poll until Successful
TASK_ID=$(curl -s -X POST "https://${SDDC_MANAGER}/v1/hosts" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
    -d "${HOSTS_JSON}" | jq -r '.id')
curl -s "https://${SDDC_MANAGER}/v1/tasks/${TASK_ID}" -H "Authorization: Bearer ${TOKEN}" | jq -r '.status'

如果主机 SSL 指纹显示内部(非 FQDN)证书名称,请在主机上运行 esxcli network ip dns search add -d vmware.gve 命令,重新运行 /var/lib/vmware/osdata/init.sh 脚本,然后运行 /sbin/generate-certificates 命令并重启 rhttpproxy 和 hostd 服务。

检查扩容前的 IP 调整

在 SDDC Manager 中,让所有 vSAN、vMotion 和网络连接验证保持启用状态。仅当 VCF 分配给每个新节点的 vmk1 和 vmk2 IP 地址与其 Compute Engine Dynamic NIC IP 地址匹配时,这些验证检查才会通过。

  1. 验证未使用的池 IP 地址是否与新的 Compute Engine NIC IP 地址匹配:将网络池范围与现有主机已使用的 IP 地址以及新节点的 Compute Engine NIC 进行比较。现有主机未使用的唯一池 IP 地址应为新节点的 IP 地址:

    # List vMotion and vSAN IP addresses already used by hosts
    curl -s "https://${SDDC_MANAGER}/v1/hosts" -H "Authorization: Bearer ${TOKEN}" | jq -r '
      .elements | sort_by(.fqdn)[] | [.fqdn,
        (([.ipAddresses[]? | select(.type=="VMOTION") | .ipAddress] | first) // "-"),
        (([.ipAddresses[]? | select(.type=="VSAN")    | .ipAddress] | first) // "-")] | @tsv'
    
    # List Compute Engine dynamic NIC IPs of the new nodes (VLAN 2 = vMotion, 3 = vSAN, 4 = TEP)
    for entry in "${ADD_HOSTS[@]}"; do
      read -r HOST _ <<< "${entry}"
      echo "== ${HOST}"
      gcloud compute instances describe "${HOST}" --zone="${ZONE}" --project="${PROJECT_ID}" \
          --format="table(networkInterfaces[].name,networkInterfaces[].vlan,networkInterfaces[].networkIP)"
    done
    
  2. 确认 SDDC Manager 不会绕过验证:在 SDDC Manager 设备上(以 root 身份),验证 application.properties 文件不包含任何 validation.disable.* 属性:

    # Must return no output. If any validation.disable.* line is present, remove it
    # and run: systemctl restart domainmanager
    grep -n 'validation.disable' /etc/vmware/vcf/domainmanager/application.properties
    

展开集群

使用 VCF Operations 或 SDDC Manager API 将已调试的节点添加到目标集群:

VCF Operations

在 VCF Operations(或 SDDC Manager 界面)中,打开集群的网域,选择 cl02 集群,依次选择操作 > 添加主机,选择已调试的节点,将 vmnic0 接口映射到集群 VDS 的 uplink1 上行链路,然后提交工作流。前往车队管理 > 任务,监控任务并记录其 ID(将 TASK_ID 变量设置为此值,以便在下一部分中使用)。

SDDC Manager API

运行以下命令以验证扩容规范并扩容集群:

CLUSTER_ID=$(curl -s "https://${SDDC_MANAGER}/v1/clusters" -H "Authorization: Bearer ${TOKEN}" \
    | jq -r --arg c "${CLUSTER_NAME}" '.elements[] | select(.name==$c) | .id')

HOST_SPECS=$(curl -s "https://${SDDC_MANAGER}/v1/hosts?status=UNASSIGNED_USEABLE" \
    -H "Authorization: Bearer ${TOKEN}" \
    | jq --argjson f "${ADD_FQDNS}" --arg vds "${VDS_NAME}" '
        [.elements[] | select(.fqdn as $x | $f | index($x))
         | {id, hostNetworkSpec: {vmNics: [{id: "vmnic0", uplink: "uplink1", vdsName: $vds}]}}]')
echo "${HOST_SPECS}" | jq 'length'   # Must equal the number of ADD_HOSTS

EXPANSION_SPEC=$(jq -n --argjson h "${HOST_SPECS}" \
    '{clusterExpansionSpec: {hostSpecs: $h, deployWithoutLicenseKeys: true}}')

# 1. Validate (resolve any reported error before continuing)
curl -s -X POST "https://${SDDC_MANAGER}/v1/clusters/${CLUSTER_ID}/validations" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
    -d "${EXPANSION_SPEC}" | jq '{executionStatus, resultStatus}'

# 2. Expand the cluster and poll the task
TASK_ID=$(curl -s -X PATCH "https://${SDDC_MANAGER}/v1/clusters/${CLUSTER_ID}" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
    -d "${EXPANSION_SPEC}" | jq -r '.id')
curl -s "https://${SDDC_MANAGER}/v1/tasks/${TASK_ID}" -H "Authorization: Bearer ${TOKEN}" | jq -r '.status'

当您扩展集群时,SDDC Manager 会从网络池中分配 vMotion 和 vSAN IP 地址,而不会保留主机顺序。如果 SDDC Manager 为新节点分配的 IP 地址与其 Compute Engine 动态 NIC 不同,则任务会在 vSAN 和 vMotion 连接的验证步骤暂停。让任务保持失败状态,完成对齐 vMotion 和 vSAN Dynamic NIC中的步骤,然后重试同一任务。请勿针对这些节点启动新的扩展请求:扩展失败后,节点不再处于未分配状态,SDDC Manager 会拒绝新的扩展请求。

对齐 vMotion 和 vSAN Dynamic NIC

如果集群扩容任务在 vSAN 和 vMotion 连接验证步骤暂停,请完成本部分中的步骤。如果任务通过了该步骤,请仅运行第 1 步以确认对齐,然后继续执行对齐 NSX TEP Dynamic NIC。

  1. 读取 VCF 分配的 IP 地址,并将其与 Compute Engine NIC 进行比较:运行检查扩容前的 IP 地址一致性第 1 步中的两个命令。只有新节点在范围内。

  2. 先删除所有不匹配的 Dynamic NIC,然后再重新添加:在重新添加任何接口之前,请先删除每个新节点上的每个不匹配的 Dynamic NIC(用于 vMotion 的 nic0.2 接口和用于 vSAN 的 nic0.3 接口);否则,交换的 IP 地址会失败,并显示 IP_IN_USE 错误:

    # Example: VCF swapped the IP addresses of esxi-h10 and esxi-h11
    HOST="esxi-h10"
    VCF_VMOTION_IP="192.168.2.25"
    VCF_VSAN_IP="192.168.3.25"
    
    # 2a. Delete mismatched NICs (run on all mismatched nodes before running 2b)
    gcloud compute instances network-interfaces delete "${HOST}" \
        --network-interface=nic0.2 --zone="${ZONE}" --project="${PROJECT_ID}" --quiet
    gcloud compute instances network-interfaces delete "${HOST}" \
        --network-interface=nic0.3 --zone="${ZONE}" --project="${PROJECT_ID}" --quiet
    
    # 2b. Add the dynamic NICs back with the VCF-allocated IP addresses
    gcloud compute instances network-interfaces add "${HOST}" \
        --parent-nic-name=nic0 --vlan=2 \
        --network="${VPC_NETWORK}" --subnetwork="${VMOTION_SUBNET}" \
        --private-network-ip="${VCF_VMOTION_IP}" --no-address \
        --zone="${ZONE}" --project="${PROJECT_ID}"
    gcloud compute instances network-interfaces add "${HOST}" \
        --parent-nic-name=nic0 --vlan=3 \
        --network="${VPC_NETWORK}" --subnetwork="${VSAN_SUBNET}" \
        --private-network-ip="${VCF_VSAN_IP}" --no-address \
        --zone="${ZONE}" --project="${PROJECT_ID}"
    
  3. 重试失败的任务:在 VCF Operations 中,前往舰队管理

    任务,以重试扩容任务,或运行以下 SDDC Manager API 命令:

    curl -s -X PATCH "https://${SDDC_MANAGER}/v1/tasks/${TASK_ID}" \
        -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
        -d '{"status": "RETRY"}'
    

    如果任务再次失败,请重复执行第 1 步和第 2 步,然后再重试。

对齐 NSX TEP Dynamic NIC

NSX 将新节点配置为传输节点后,请验证并调整新节点上的 VLAN 4 NSX TEP (vmk10) Dynamic NIC。

  1. 检索 NSX 分配的 TEP IP 地址:查询 NSX Manager(或在每个 ESXi 主机上运行 esxcli network ip interface ipv4 get -i vmk10 命令):

    # Map transport node ID to hostname
    curl -s -u "admin:${NSX_PW}" "https://${NSX_MANAGER}/api/v1/transport-nodes" \
        | jq -r '.results[] | [.id, .display_name] | @tsv'
    
    # Map transport node ID to TEP device and IP address
    curl -s -u "admin:${NSX_PW}" "https://${NSX_MANAGER}/api/v1/transport-nodes/state" \
        | jq -r '.results[] | [.transport_node_id, .state,
            ([.host_switch_states[]?.endpoints[]? | .device_name + "=" + .ip] | join(","))] | @tsv'
    
  2. 重新创建不匹配的 TEP NIC:将 NSX 分配的 vmk10 IP 地址与每个新节点的 Compute Engine TEP NIC(VLAN 4)进行比较。只有新节点在范围内;请勿修改现有节点的 TEP NIC。对于所有不匹配的新节点,首先将每个节点与 TEP NEG 分离,然后删除所有不匹配节点上的 nic0.4 接口。然后,您重新添加 nic0.4 接口,并将节点重新挂接到 TEP NEG:

    # Example: Compute Engine has 192.168.4.24 on esxi-h10, but NSX assigned 192.168.4.25
    HOST="esxi-h10"
    CURRENT_TEP_IP="192.168.4.24"
    NSX_TEP_IP="192.168.4.25"
    
    # 2a. Detach the node from the TEP NEG
    gcloud compute network-endpoint-groups update "${TEP_NEG}" \
        --zone="${ZONE}" --project="${PROJECT_ID}" \
        --remove-endpoint="instance=${HOST},ip=${CURRENT_TEP_IP}"
    
    # 2b. Delete the mismatched TEP NIC on all mismatched nodes first
    gcloud compute instances network-interfaces delete "${HOST}" \
        --network-interface=nic0.4 --zone="${ZONE}" --project="${PROJECT_ID}" --quiet
    
    # 2c. Re-add nic0.4 with the NSX-allocated IP address
    gcloud compute instances network-interfaces add "${HOST}" \
        --parent-nic-name=nic0 --vlan=4 \
        --network="${VPC_NETWORK}" --subnetwork="${TEP_SUBNET}" \
        --private-network-ip="${NSX_TEP_IP}" --no-address \
        --zone="${ZONE}" --project="${PROJECT_ID}"
    
    # 2d. Re-attach the node to the TEP NEG
    gcloud compute network-endpoint-groups update "${TEP_NEG}" \
        --zone="${ZONE}" --project="${PROJECT_ID}" \
        --add-endpoint="instance=${HOST}"
    

验证集群扩容

集群扩容任务成功后,请验证以下内容:

  • vCenter Server:新节点已连接且在 cl02 集群中运行状况良好,没有网络警报。
  • vSAN:对于 cl02 集群,前往监控 > vSAN > Skyline Health,确认所有健康检查均通过,并且数据存储区容量包含新节点。
  • NSX Manager:依次前往系统 > Fabric > 主机,确认每个新节点都显示成功和启动,并且叠加隧道已启动。
  • VCF 操作:新节点已分配给 cl02 集群,并且任务列表中没有失败的任务。

在 ESXi 主机 shell(例如 esxi-h10)中,验证巨型帧连接性(8896 MTU 减去 28 字节 IP/ICMP 标头 = 8868 字节):

esxcli network ip interface ipv4 get                        # Verify vmk0, vmk1, vmk2, and vmk10 IPs
vmkping -S vmotion -I vmk1 -d -s 8868 192.168.2.21          # vMotion (netstack might be default)
vmkping -I vmk2 -d -s 8868 192.168.3.21                     # vSAN
vmkping ++netstack=vxlan -I vmk10 -d -s 8868 192.168.4.21   # NSX TEP

vSAN 故障容忍度和主机数量

请参阅下表,确定在添加节点时集群需要多少个节点,或在移除节点时必须保留多少个节点:

可容忍的故障数 数据放置方案 主机数量下限
FTT=1 RAID-1(镜像)或 RAID-5(纠删码) 3(建议使用 4 个或更多,以便在主机故障后或维护期间进行重建)
FTT=2 RAID-1(镜像) 5
FTT=2 RAID-6(纠删码) 6

从集群中移除节点

完成移除前检查

在从集群中移除任何节点之前,请验证以下事项:

  • 主机数量:移除节点后,集群至少保留三个节点,并且至少保留可容忍的 vSAN 故障和主机数量中列出的有效存储政策所需的最小主机数量。
  • 容量:在 vCenter Server 中,对于 cl02 集群,请前往监控 > vSAN > 数据迁移预检查,针对您计划移除的每个节点(使用完整数据迁移选项)进行检查,并确认其余节点具有足够的 vSAN 和计算容量。
  • 健康状况:vSAN Skyline Health 通过,没有错误,并且 VCF Operations 或 SDDC Manager 中没有失败的任务。
  • 高可用性政策领导者:确认没有后端服务使用您要移除的节点作为其高可用性政策领导者。如果后端服务使用其中一个节点作为其领导者,请将领导者更新为剩余节点(迁移后运行设备虚拟机的宿主机):

    gcloud compute backend-services list --regions="${REGION}" --project="${PROJECT_ID}" \
        --format="table(name,haPolicy.leader.networkEndpoint.instance)"
    
    # Run only if a node being removed is an HA policy leader (example: move rbs-vc02-demo to esxi-h0)
    NEG_LINK=$(gcloud compute network-endpoint-groups describe "${MGMT_NEG}" \
        --zone="${ZONE}" --project="${PROJECT_ID}" --format="value(selfLink)")
    gcloud compute backend-services update "rbs-vc02-${CUSTOMER_ID}" \
        --ha-policy-leader-backend-group="${NEG_LINK}" --ha-policy-leader-instance=esxi-h0 \
        --region="${REGION}" --project="${PROJECT_ID}"
    

从集群中移除节点

从 VCF 中的集群中移除目标节点。VCF 会将每个节点置于维护模式,迁移其虚拟机,并撤出其 vSAN 数据。您可以使用 VCF Operations 或 SDDC Manager API 移除节点:

VCF Operations

在 VCF Operations(或 SDDC Manager 界面)中,打开集群的网域,选择 cl02 集群,依次选择操作 > 移除主机,选择要移除的节点,然后确认。

SDDC Manager API

运行以下命令,验证压缩规范并从集群中移除主机:

CLUSTER_ID=$(curl -s "https://${SDDC_MANAGER}/v1/clusters" -H "Authorization: Bearer ${TOKEN}" \
    | jq -r --arg c "${CLUSTER_NAME}" '.elements[] | select(.name==$c) | .id')

REMOVE_FQDNS=$(for HOST in "${REMOVE_HOSTS[@]}"; do echo "${HOST}.${DOMAIN}"; done | jq -R . | jq -s .)

COMPACTION_SPEC=$(curl -s "https://${SDDC_MANAGER}/v1/hosts" -H "Authorization: Bearer ${TOKEN}" \
    | jq --argjson f "${REMOVE_FQDNS}" '
        {clusterCompactionSpec: {hosts: [.elements[] | select(.fqdn as $x | $f | index($x)) | {id}]}}')
echo "${COMPACTION_SPEC}" | jq '.clusterCompactionSpec.hosts | length'   # Must equal the number of REMOVE_HOSTS

# 1. Validate the compaction specification
curl -s -X POST "https://${SDDC_MANAGER}/v1/clusters/${CLUSTER_ID}/validations" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
    -d "${COMPACTION_SPEC}" | jq '{executionStatus, resultStatus}'

# 2. Remove the hosts and poll the task until Successful
TASK_ID=$(curl -s -X PATCH "https://${SDDC_MANAGER}/v1/clusters/${CLUSTER_ID}" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
    -d "${COMPACTION_SPEC}" | jq -r '.id')
curl -s "https://${SDDC_MANAGER}/v1/tasks/${TASK_ID}" -H "Authorization: Bearer ${TOKEN}" | jq -r '.status'

停用节点

从集群中移除节点后,使用 VCF Operations 或 SDDC Manager API 将这些节点从 SDDC Manager 清单中停用:

VCF Operations

在 VCF Operations 中,前往资产清单 > 主机,选择未分配的节点,然后点击退役(如需了解详情,请参阅 VMware Cloud Foundation 文档中的“退役主机”)。

SDDC Manager API

运行以下命令以发送停用请求并轮询任务,直到返回 Successful:

DECOMMISSION_SPEC=$(echo "${REMOVE_FQDNS}" | jq 'map({fqdn: .})')
TASK_ID=$(curl -s -X DELETE "https://${SDDC_MANAGER}/v1/hosts?skipFailedHosts=false" \
    -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
    -d "${DECOMMISSION_SPEC}" | jq -r '.id')
curl -s "https://${SDDC_MANAGER}/v1/tasks/${TASK_ID}" -H "Authorization: Bearer ${TOKEN}" | jq -r '.status'

# Verify that the nodes are no longer listed in the inventory
curl -s "https://${SDDC_MANAGER}/v1/hosts" -H "Authorization: Bearer ${TOKEN}" \
    | jq -r '.elements[] | [.fqdn, .status] | @tsv'

从 NEG 中移除节点

从管理 NEG 和 NSX TEP NEG 中移除已停用的节点:

for HOST in "${REMOVE_HOSTS[@]}"; do
  for NEG in "${MGMT_NEG}" "${TEP_NEG}"; do
    gcloud compute network-endpoint-groups update "${NEG}" \
        --zone="${ZONE}" --remove-endpoint="instance=${HOST}" --project="${PROJECT_ID}"
  done
done

# Verify NEG endpoints
for NEG in "${MGMT_NEG}" "${TEP_NEG}"; do
  gcloud compute network-endpoint-groups list-network-endpoints "${NEG}" \
      --zone="${ZONE}" --project="${PROJECT_ID}" \
      --format="table(networkEndpoint.instance,networkEndpoint.ipAddress)"
done

如果 gcloud CLI 拒绝在没有明确 IP 地址的情况下从 TEP NEG 中移除,请在 --remove-endpoint 标志中添加端点 IP 地址(如验证命令所示):--remove-endpoint="instance=HOST,ip=TEP_IP"。

删除裸金属实例

清除每个已停用主机上的防删除保护,并删除 Compute Engine 裸金属实例(Compute Engine 会自动删除实例的启动磁盘):

for HOST in "${REMOVE_HOSTS[@]}"; do
  # Clear deletion protection, and then delete the instance and its boot disk
  gcloud compute instances update "${HOST}" --no-deletion-protection \
      --zone="${ZONE}" --project="${PROJECT_ID}"
  gcloud compute instances delete "${HOST}" \
      --zone="${ZONE}" --project="${PROJECT_ID}" --quiet
done

# Verify remaining instances in the placement policy
gcloud compute instances list --project="${PROJECT_ID}" --zones="${ZONE}" \
    --filter="resourcePolicies~${PLACEMENT_POLICY}" --format="table(name,status)"

清理 DNS 记录和 IP 池

删除已移除节点的正向 (A) 和反向 (PTR) Cloud DNS 记录:

for HOST in "${REMOVE_HOSTS[@]}"; do
  MGMT_IP=$(gcloud dns record-sets describe "${HOST}.${DOMAIN}." --type=A \
      --zone="${DNS_ZONE}" --project="${PROJECT_ID}" --format="value(rrdatas[0])")
  REV=$(echo "${MGMT_IP}" | awk -F. '{print $4"."$3"."$2"."$1".in-addr.arpa."}')
  gcloud dns record-sets delete "${REV}" --type=PTR --zone="${REV_DNS_ZONE}" --project="${PROJECT_ID}"
  gcloud dns record-sets delete "${HOST}.${DOMAIN}." --type=A --zone="${DNS_ZONE}" --project="${PROJECT_ID}"
done

已移除节点的 vMotion、vSAN 和 TEP IP 地址会在网络池和 NSX TEP 池中再次释放。您可以保留这些 IP 地址以供未来节点使用(方法是为该未来节点分配相同的 Compute Engine NIC IP 地址),也可以将其从池中移除,以便 VCF 和 NSX 不会将其分配给 Compute Engine NIC 使用不同 IP 地址的未来节点。

验证节点移除

完成节点移除工作流后,请验证以下内容:

  • vCenter Server:cl02 集群仅显示剩余节点,所有节点均已连接且运行状况良好,并且 vSphere HA 和 DRS 未报告任何错误。
  • vSAN:Skyline Health 通过,并且所有虚拟对象都符合要求(对于 cl02 集群,请依次前往监控 > vSAN > 虚拟对象)。
  • NSX Manager:移除的节点不再列为传输节点,其余节点显示为启动。
  • VCF Operations 和 SDDC Manager:移除的节点不再位于主机清单中,并且没有失败的任务。
  • Google Cloud:已移除节点的裸金属实例、启动磁盘、NEG 端点和 DNS 记录会被删除。

排查集群扩容和节点移除问题

下表列出了在 vSphere 集群中添加或移除节点时的常见问题、原因和解决方法:

问题 原因 解决方法
集群扩展在 vSAN 和 vMotion 的连接检查时失败;新节点报告运行状况不佳 当您扩展集群时,SDDC Manager 会从网络池中分配 vmk1 和 vmk2 IP 地址,而不会保留主机顺序。 请保持启用验证功能。仅将新节点的 IP 地址添加到网络池;如果任务在连接性检查时暂停,请调整 Dynamic NIC 并重试同一任务。
NSX 主机状态为 Degraded,或新节点上的隧道已关闭 NSX Manager 会从 IP 地址池中随机分配 TEP (vmk10) IP 地址,因此这些地址可能与 Compute Engine TEP NIC 不匹配。 按照对齐 NSX TEP Dynamic NIC 中的说明对齐 NSX TEP Dynamic NIC。
NSX 无法准备新节点 集群的 NSX TEP IP 地址池没有可用的 IP 地址。 将新节点的 TEP IP 地址添加到 TEP IP 地址池,然后重试该任务。
SDDC Manager 拒绝针对相同节点的新扩展请求 失败的扩容任务会使节点处于 UNASSIGNED_USEABLE 状态之外。 重试失败的任务(PATCH /v1/tasks/TASK_ID,{"status": "RETRY"}),而不是提交新的扩容请求。
节点配置失败,因为节点已配置 您无法对同一主机进行两次调试。 继续执行集群扩容,或者如果您必须再次调试节点(例如,使用不同的网络池),请先停用该节点。
重新添加 Dynamic NIC 时出现 IP_IN_USE 错误 两个节点交换了 IP 地址,或者计划的 IP 地址与另一个节点重叠。 请先删除所有新节点上不匹配的 Dynamic NIC,然后再重新添加任何接口,并验证您的 IP 地址方案。
节点移除验证失败 移除节点会导致集群的主机数量少于其最小规模,或者剩余节点容量不足。 请先移除较少的节点或增加容量;不要使用 "force": true 属性。
gcloud CLI 拒绝删除裸金属实例 实例已启用防删除保护。 在删除实例之前,请运行 gcloud compute instances update HOST --no-deletion-protection 命令。
添加节点后 vSAN 容错域不正确 您在其他布置政策中创建了新节点,或者两个集群共用一个布置政策。 在集群自己的布置政策中创建新节点,并将其分布在各个可用性网域中。
vSAN 错误、vMotion 超时或 NSX 叠加层分片 配置的 MTU 大于 VPC 网络 MTU(8896 字节)。 新节点会继承集群的 VDS 和池;请在所有组件中将 MTU 设置为 8896 字节。
主机 SSL 指纹或证书显示内部名称 未在主机上设置 DNS 搜索网域。 运行 esxcli network ip dns search add -d vmware.gve 命令,重新运行 /var/lib/vmware/osdata/init.sh 脚本,然后运行 /sbin/generate-certificates 命令并重启 rhttpproxy 和 hostd 服务。
主机无法 ping 通子网网关 (.1) Google Cloud 不会响应子网网关 IP 地址上的 ICMP 回显请求。 预期行为;忽略网关 ping 警告。

后续步骤