你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

快速入门:使用 Bicep 部署 Azure Nexus Kubernetes 群集

  • 使用 Bicep 部署 Azure Nexus Kubernetes 群集。

Bicep 是一种特定于域的语言 (DSL),使用声明性语法来部署 Azure 资源。 它提供简明的语法、可靠的类型安全性以及对代码重用的支持。 Bicep 会针对你的 Azure 基础结构即代码解决方案提供最佳创作体验。

先决条件

如果没有 Azure 帐户,请在开始前创建一个免费帐户

  • 安装必需的 Azure CLI 扩展的最新版本。

  • 本文需要 2.61.0 或更高版本的 Azure CLI。 如果使用 Azure Cloud Shell,则最新版本已安装。

  • 如果你有多个 Azure 订阅,请使用 az account 命令选择应在其中计收资源费用的相应订阅 ID。

  • 有关支持的 VM SKU 列表,请参阅参考部分中的 VM SKU 表。

  • 有关受支持的 Kubernetes 版本的列表,请参阅受支持的 Kubernetes 版本

  • 使用 az group create 命令创建资源组。 Azure 资源组是用于部署和管理 Azure 资源的逻辑组。 创建资源组时,系统会提示你指定一个位置。 此位置是资源组元数据的存储位置,也是资源在 Azure 中运行的位置(如果你在创建资源期间未指定其他区域)。 以下示例在“eastus”位置创建名为“myResourceGroup”的资源组。

    az group create --name myResourceGroup --___location eastus
    

    以下输出示例类似于成功创建资源组:

    {
      "id": "/subscriptions/<guid>/resourceGroups/myResourceGroup",
      "___location": "eastus",
      "managedBy": null,
      "name": "myResourceGroup",
      "properties": {
        "provisioningState": "Succeeded"
      },
      "tags": null
    }
    
  • 若要部署 Bicep 文件或 ARM 模板,需要对要部署的资源具有写入权限,并且需要对 Microsoft.Resources/deployments 资源类型的所有操作具有访问权限。 例如,若要部署群集,需要 Microsoft.NetworkCloud/kubernetesclusters/write 和 Microsoft.Resources/deployments/* 权限。 有关角色和权限的列表,请参阅 Azure 内置角色

  • 需要 Azure Operator Nexus 群集的 custom ___location 资源 ID。

  • 需要根据特定的工作负载要求创建各种网络,并且必须具有适用于工作负载的相应 IP 地址。 为确保顺利实施,建议咨询相关支持团队以获取帮助。

  • 本快速入门假设读者基本了解 Kubernetes 的概念。 有关详细信息,请参阅 Azure Kubernetes 服务 (AKS) 的 Kubernetes 核心概念

查阅 Bicep 文件

在部署 Kubernetes 模板之前,让我们查看内容以了解其结构。

// Azure parameters

@description('The name of Nexus Kubernetes cluster')
param kubernetesClusterName string

@description('The Azure region where the cluster is to be deployed')
param ___location string = resourceGroup().___location

@description('The custom ___location of the Nexus instance')
param extendedLocation string

@description('The metadata tags to be associated with the cluster resource')
param tags object = {}

@description('The username for the administrative account on the cluster')
param adminUsername string = 'azureuser'

@description('The object IDs of Azure Active Directory (AAD) groups that will have administrative access to the cluster')
param adminGroupObjectIds array = []

// Networking Parameters

@description('The Azure Resource Manager (ARM) id of the network to be used as the Container Networking Interface (CNI) network')
param cniNetworkId string

@description('The ARM id of the network to be used for cloud services network')
param cloudServicesNetworkId string

@description('The CIDR blocks used for Nexus Kubernetes PODs in the cluster')
param podCidrs array = ['10.244.0.0/16']

@description('The CIDR blocks used for k8s service in the cluster')
param serviceCidrs array = ['10.96.0.0/16']

@description('The IP address of the DNS service in the cluster')
param dnsServiceIp string = '10.96.0.10'

@description('The Layer 2 networks associated with the initial agent pool')
param agentPoolL2Networks array = []
// {
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN'
// }

@description('The Layer 3 networks associated with the initial agent pool')
param agentPoolL3Networks array = []
// {
//   ipamEnabled: 'True/False'
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN|IPVLAN'
// }

@description('The trunked networks associated with the initial agent pool')
param agentPoolTrunkedNetworks array = []
// {
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN'
// }

@description('The Layer 2 networks associated with the cluster')
param l2Networks array = []
// {
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN'
// }

@description('The Layer 3 networks associated with the cluster')
param l3Networks array = []
// {
//   ipamEnabled: 'True/False'
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN|IPVLAN'
// }

@description('The trunked networks associated with the cluster')
param trunkedNetworks array = []
// {
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN'
// }

@description('The LoadBalancer IP address pools associated with the cluster')
param ipAddressPools array = []
// {
//   addresses: [
//     'string'
//   ]
//   autoAssign: 'True/False'
//   name: 'string'
//   onlyUseHostIps: 'True/False'
// }

// Cluster Configuration Parameters

@description('The version of Kubernetes to be used in the Nexus Kubernetes cluster')
param kubernetesVersion string = 'v1.27.1'

@description('The number of control plane nodes to be deployed in the cluster')
param controlPlaneCount int = 1

@description('The zones/racks used for placement of the control plane nodes')
param controlPlaneZones array = []
// "string" Example: ["1", "2", "3"]

@description('The zones/racks used for placement of the agent pool nodes')
param agentPoolZones array = []
// "string" Example: ["1", "2", "3"]

@description('The size of the control plane nodes')
param controlPlaneVmSkuName string = 'NC_G6_28_v1'

@description('The number of worker nodes to be deployed in the initial agent pool')
param systemPoolNodeCount int = 1

@description('The size of the worker nodes')
param workerVmSkuName string = 'NC_P10_56_v1'

@description('The configurations for the initial agent pool')
param initialPoolAgentOptions object = {}
// {
//   "hugepagesCount": integer,
//   "hugepagesSize": "2M/1G"
// }

@description('The cluster wide SSH public key that will be associated with the given user for secure remote login')
param sshPublicKeys array = []
// {
//   keyData: "ssh-rsa AAAAA...."
// },
// {
//   keyData: "ssh-rsa AAAAA...."
// }

@description('The control plane SSH public key that will be associated with the given user for secure remote login')
param controlPlaneSshKeys array = []
// {
//   keyData: "ssh-rsa AAAAA...."
// },
// {
//   keyData: "ssh-rsa AAAAA...."
// }

@description('The agent pool SSH public key that will be associated with the given user for secure remote login')
param agentPoolSshKeys array = []
// {
//   keyData: "ssh-rsa AAAAA...."
// },
// {
//   keyData: "ssh-rsa AAAAA...."
// }

@description('The labels to assign to the nodes in the cluster for identification and organization')
param labels array = []
// {
//   key: 'string'
//   value: 'string'
// }
@description('The taints to apply to the nodes in the cluster to restrict which pods can be scheduled on them')
param taints array = []
// {
//   key: 'string'
//   value: 'string:NoSchedule|PreferNoSchedule|NoExecute'
// }
@description('The association of IP address pools to the communities and peers, allowing for announcement of IPs.')
param bgpAdvertisements array = []

@description('"The list of additional BgpPeer entities that the Kubernetes cluster will peer with. All peering must be explicitly defined.')
param bgpPeers array = []

@description('The indicator to specify if the load balancer peers with the network fabric.')
param fabricPeeringEnabled string = 'False'


resource kubernetescluster 'Microsoft.NetworkCloud/kubernetesClusters@2025-02-01' = {
  name: kubernetesClusterName
  ___location: ___location
  tags: tags
  extendedLocation: {
    name: extendedLocation
    type: 'CustomLocation'
  }
  properties: {
    kubernetesVersion: kubernetesVersion
    managedResourceGroupConfiguration: {
      name: '${uniqueString(resourceGroup().name)}-${kubernetesClusterName}'
      ___location: ___location
    }
    aadConfiguration: {
      adminGroupObjectIds: adminGroupObjectIds
    }
    administratorConfiguration: {
      adminUsername: adminUsername
      sshPublicKeys: empty(sshPublicKeys) ? [] : sshPublicKeys
    }
    initialAgentPoolConfigurations: [
      {
        name: '${kubernetesClusterName}-nodepool-1'
        administratorConfiguration: {
          adminUsername: adminUsername
          sshPublicKeys: empty(agentPoolSshKeys) ? [] : agentPoolSshKeys
        }
        count: systemPoolNodeCount
        vmSkuName: workerVmSkuName
        mode: 'System'
        labels: empty(labels) ? null : labels
        taints: empty(taints) ? null : taints
        agentOptions: empty(initialPoolAgentOptions) ? null : initialPoolAgentOptions
        attachedNetworkConfiguration: {
          l2Networks: empty(agentPoolL2Networks) ? null : agentPoolL2Networks
          l3Networks: empty(agentPoolL3Networks) ? null : agentPoolL3Networks
          trunkedNetworks: empty(agentPoolTrunkedNetworks) ? null : agentPoolTrunkedNetworks
        }
        availabilityZones: empty(agentPoolZones) ? null : agentPoolZones
        upgradeSettings: {
          maxSurge: '1'
        }
      }
    ]
    controlPlaneNodeConfiguration: {
      administratorConfiguration: {
        adminUsername: adminUsername
        sshPublicKeys: empty(controlPlaneSshKeys) ? [] : controlPlaneSshKeys
      }
      count: controlPlaneCount
      vmSkuName: controlPlaneVmSkuName
      availabilityZones: empty(controlPlaneZones) ? null : controlPlaneZones
    }
    networkConfiguration: {
      cniNetworkId: cniNetworkId
      cloudServicesNetworkId: cloudServicesNetworkId
      dnsServiceIp: dnsServiceIp
      podCidrs: podCidrs
      serviceCidrs: serviceCidrs
      attachedNetworkConfiguration: {
        l2Networks: empty(l2Networks) ? null : l2Networks
        l3Networks: empty(l3Networks) ? null : l3Networks
        trunkedNetworks: empty(trunkedNetworks) ? null : trunkedNetworks
      }
      bgpServiceLoadBalancerConfiguration: {
        bgpAdvertisements: empty(bgpAdvertisements) ? null : bgpAdvertisements
        bgpPeers: empty(bgpPeers) ? null : bgpPeers
        fabricPeeringEnabled: fabricPeeringEnabled
        ipAddressPools: empty(ipAddressPools) ? null : ipAddressPools
      }
    }
  }
}

阅读文章并保存名为 kubernetes-deploy.bicep 的模板文件后,继续按下一节所述部署模板。

部署 Bicep 文件

  1. 创建一个名为 kubernetes-deploy-parameters.json 的文件,并以 JSON 格式添加所需参数。 可以使用以下示例作为起点。 将值替换成自己的值。
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "kubernetesClusterName":{
      "value": "myNexusK8sCluster"
    },
    "adminGroupObjectIds": {
      "value": [
        "00000000-0000-0000-0000-000000000000"
      ]
    },
    "cniNetworkId": {
      "value": "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.NetworkCloud/l3Networks/<l3Network-name>"
    },
    "cloudServicesNetworkId": {
      "value": "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.NetworkCloud/cloudServicesNetworks/<csn-name>"
    },
    "extendedLocation": {
      "value": "/subscriptions/<subscription_id>/resourceGroups/<managed_resource_group>/providers/microsoft.extendedlocation/customlocations/<custom-___location-name>"
    },
    "___location": {
      "value": "eastus"
    },
    "sshPublicKeys": {
      "value": [
        {
          "keyData": "ssh-rsa AAAAA...."
        },
        {
          "keyData": "ssh-rsa BBBBB...."
        }
      ]
    }
  }
}
  1. 部署模板。
    az deployment group create \
      --resource-group myResourceGroup \
      --template-file kubernetes-deploy.bicep \
      --parameters @kubernetes-deploy-parameters.json

如果没有足够的容量来部署请求的群集节点,则会显示一条错误消息。 但是,此消息不提供任何有关可用容量的详细信息。 它会指出群集创建由于容量不足而无法继续。

注释

容量计算考虑到了整个平台群集,而不是仅限于单个机架。 因此,如果在容量不足的某个区域(这里,一个机架等于一个区域)中创建代理池,但另一个区域具有足够的容量,则群集创建将继续,但最终会超时。仅当在创建群集或代理池期间未指定特定区域时,这种容量检查方法才有意义。

查看已部署的资源

部署完成后,可以使用 CLI 或 Azure 门户查看资源。

若要查看 myNexusK8sCluster 资源组中 myResourceGroup 群集的详细信息,请执行以下 Azure CLI 命令:

az networkcloud kubernetescluster show \
  --name myNexusK8sCluster \
  --resource-group myResourceGroup

此外,若要获取与 myNexusK8sCluster 资源组中的 myResourceGroup 群集关联的代理池名称列表,可以使用以下 Azure CLI 命令。

az networkcloud kubernetescluster agentpool list \
  --kubernetes-cluster-name myNexusK8sCluster \
  --resource-group myResourceGroup \
  --output table

连接至群集

请注意,成功创建 Nexus Kubernetes 群集并将其连接到 Azure Arc 后,可以使用群集连接功能轻松连接到它。 通过群集连接,可以从任意位置安全地访问和管理群集,方便执行交互式开发、调试和群集管理任务。

有关可用选项的更多详细信息,请参阅连接到 Azure Operator Nexus Kubernetes 群集

注释

创建 Nexus Kubernetes 群集时,Nexus 会自动创建专用于存储群集资源的托管资源组,在此组中,会建立 Arc 连接的群集资源。

要访问群集,需要设置群集连接 kubeconfig。 使用相关 Microsoft Entra 实体登录到 Azure CLI 后,可以获取从任意位置(甚至在群集周围的防火墙以外)与群集通信所需的 kubeconfig

  1. 设置 CLUSTER_NAMERESOURCE_GROUPSUBSCRIPTION_ID 变量。

    CLUSTER_NAME="myNexusK8sCluster"
    RESOURCE_GROUP="myResourceGroup"
    SUBSCRIPTION_ID=<set the correct subscription_id>
    
  2. 查询具有 az 的托管资源组并将其存储在 MANAGED_RESOURCE_GROUP

     az account set -s $SUBSCRIPTION_ID
     MANAGED_RESOURCE_GROUP=$(az networkcloud kubernetescluster show -n $CLUSTER_NAME -g $RESOURCE_GROUP --output tsv --query managedResourceGroupConfiguration.name)
    
  3. 以下命令会启动 connectedk8s 代理,你可以通过该代理连接到指定 Nexus Kubernetes 群集的 Kubernetes API 服务器。

    az connectedk8s proxy -n $CLUSTER_NAME  -g $MANAGED_RESOURCE_GROUP &
    
  4. 使用 kubectl 将请求发送到群集:

    kubectl get pods -A
    

    现在应会看到来自群集的响应,其中包含所有节点的列表。

注释

如果看到错误消息“无法将访问令牌发布到客户端代理,无法连接到 MSI”,则可能需要执行 az login 以使用 Azure 重新进行身份验证。

添加代理池

在上一步骤中创建的群集包含单个节点池。 让我们使用 Bicep 文件添加第二个代理池。 以下示例创建一个名为 myNexusK8sCluster-nodepool-2 的代理池:

  1. 查看模板。

在添加代理池模板之前,让我们查看内容以了解其结构。

// Azure Parameters
@description('The name of Nexus Kubernetes cluster')
param kubernetesClusterName string

@description('The Azure region where the cluster is to be deployed')
param ___location string = resourceGroup().___location

@description('The custom ___location of the Nexus instance')
param extendedLocation string

@description('Tags to be associated with the resource')
param tags object = {}

@description('The username for the administrative account on the cluster')
param adminUsername string = 'azureuser'

@description('The agent pool SSH public key that will be associated with the given user for secure remote login')
param agentPoolSshKeys array = []
// {
//   keyData: "ssh-rsa AAAAA...."
// },
// {
//   keyData: "ssh-rsa AAAAA...."
// }

// Cluster Configuration Parameters
@description('Number of nodes in the agent pool')
param agentPoolNodeCount int = 1

@description('Agent pool name')
param agentPoolName string = 'nodepool-2'

@description('VM size of the agent nodes')
param agentVmSku string = 'NC_P10_56_v1'

@description('The zones/racks used for placement of the agent pool nodes')
param agentPoolZones array = []
// "string" Example: ["1", "2", "3"]

@description('Agent pool mode')
param agentPoolMode string = 'User'

@description('The configurations for the initial agent pool')
param agentOptions object = {}
// {
//   "hugepagesCount": integer,
//   "hugepagesSize": "2M/1G"
// }

@description('The labels to assign to the nodes in the cluster for identification and organization')
param labels array = []
// {
//   key: 'string'
//   value: 'string'
// }
@description('The taints to apply to the nodes in the cluster to restrict which pods can be scheduled on them')
param taints array = []
// {
//   key: 'string'
//   value: 'string:NoSchedule|PreferNoSchedule|NoExecute'
// }

// Networking Parameters
@description('The Layer 2 networks to connect to the agent pool')
param l2Networks array = []
// {
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN|IPVLAN'
// }

@description('The Layer 3 networks to connect to the agent pool')
param l3Networks array = []
// {
//   ipamEnabled: 'True/False'
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN|IPVLAN'
// }

@description('The trunked networks to connect to the agent pool')
param trunkedNetworks array = []
// {
//   networkId: 'string'
//   pluginType: 'SRIOV|DPDK|OSDevice|MACVLAN|IPVLAN'
// }

resource agentPools 'Microsoft.NetworkCloud/kubernetesClusters/agentPools@2025-02-01' = {
  name: '${kubernetesClusterName}/${kubernetesClusterName}-${agentPoolName}'
  ___location: ___location
  tags: tags
  extendedLocation: {
    name: extendedLocation
    type: 'CustomLocation'
  }
  properties: {
    administratorConfiguration: {
      adminUsername: adminUsername
      sshPublicKeys: empty(agentPoolSshKeys) ? null : agentPoolSshKeys
    }
    attachedNetworkConfiguration: {
      l2Networks: empty(l2Networks) ? null : l2Networks
      l3Networks: empty(l3Networks) ? null : l3Networks
      trunkedNetworks: empty(trunkedNetworks) ? null : trunkedNetworks
    }
    count: agentPoolNodeCount
    mode: agentPoolMode
    vmSkuName: agentVmSku
    labels: empty(labels) ? null : labels
    taints: empty(taints) ? null : taints
    agentOptions: empty(agentOptions) ? null : agentOptions
    availabilityZones: empty(agentPoolZones) ? null : agentPoolZones
    upgradeSettings: {
      maxSurge: '1'
    }
  }
}

阅读文章并保存名为 kubernetes-add-agentpool.bicep 的模板文件后,继续按下一节所述部署模板。

  1. 创建一个名为 kubernetes-nodepool-parameters.json 的文件,并以 JSON 格式添加所需参数。 可以使用以下示例作为起点。 将值替换成自己的值。
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "kubernetesClusterName":{
        "value": "myNexusK8sCluster"
      },
      "extendedLocation": {
        "value": "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/microsoft.extendedlocation/customlocations/<custom-___location-name>"
      }
    }
  }
  1. 部署模板。
    az deployment group create \
      --resource-group myResourceGroup \
      --template-file kubernetes-add-agentpool.bicep \
      --parameters @kubernetes-nodepool-parameters.json

注释

可以使用初始代理池配置在群集本身的初始创建期间添加多个代理池。 但是,如果要在初始创建之后添加代理池,可以使用上述命令为 Nexus Kubernetes 群集创建其他代理池。

以下输出示例成功创建了代理池。

$ az networkcloud kubernetescluster agentpool list --kubernetes-cluster-name myNexusK8sCluster --resource-group myResourceGroup --output table
This command is experimental and under development. Reference and support levels: https://aka.ms/CLI_refstatus
Count    Location    Mode    Name                          ProvisioningState    ResourceGroup    VmSkuName
-------  ----------  ------  ----------------------------  -------------------  ---------------  -----------
1        eastus      System  myNexusK8sCluster-nodepool-1  Succeeded            myResourceGroup  NC_P10_56_v1
1        eastus      User    myNexusK8sCluster-nodepool-2  Succeeded            myResourceGroup  NC_P10_56_v1

清理资源

不再需要资源组时,可将其删除。 资源组和资源组中的所有资源都会被删除。

使用 az group delete 命令可删除资源组、Kubernetes 群集以及除 Operator Nexus 网络资源外的所有相关资源。

az group delete --name myResourceGroup --yes --no-wait

后续步骤

现在即可直接通过群集连接或通过 Azure Operator Service Manager 来部署 CNF。