配置静态外部 IP 地址


您可以为虚拟机 (VM) 实例分配静态外部 IP 地址。您还可以更改、列出和释放虚拟机的静态 IP 地址。外部 IP 地址可以是静态地址或临时地址

如果虚拟机需要固定不变的外部 IP 地址,请执行以下操作:

  1. 获取静态外部 IP 地址。您可以预留新的外部 IP 地址,也可以提升现有的临时外部 IP 地址。
  2. 将预留的 IP 地址分配给现有虚拟机,或在创建新虚拟机时分配该地址。

如果您的内部 Compute Engine 网络中需要静态 IP 地址,请改为参阅预留静态内部 IP 地址

如需了解如何预留静态外部 IP 地址或创建全球外部 IP 地址,请参阅预留静态外部 IP 地址

准备工作

  • 了解 IP 地址
  • 了解静态外部 IP 地址的配额和限制
  • 了解外部 IP 地址价格
  • 如果您尚未设置身份验证,请进行设置。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以选择以下任一选项向 Compute Engine 进行身份验证:

    Select the tab for how you plan to use the samples on this page:

    Console

    When you use the Google Cloud console to access Google Cloud services and APIs, you don't need to set up authentication.

    gcloud

    1. Install the Google Cloud CLI, then initialize it by running the following command:

      gcloud init
    2. Set a default region and zone.
    3. Terraform

      如需在本地开发环境中使用本页面上的 Terraform 示例,请安装并初始化 gcloud CLI,然后使用您的用户凭据设置应用默认凭据。

      1. Install the Google Cloud CLI.
      2. To initialize the gcloud CLI, run the following command:

        gcloud init
      3. If you're using a local shell, then create local authentication credentials for your user account:

        gcloud auth application-default login

        You don't need to do this if you're using Cloud Shell.

      如需了解详情,请参阅 Set up authentication for a local development environment

      REST

      如需在本地开发环境中使用本页面上的 REST API 示例,请使用您提供给 gcloud CLI 的凭据。

        Install the Google Cloud CLI, then initialize it by running the following command:

        gcloud init

      如需了解详情,请参阅 Google Cloud 身份验证文档中的使用 REST 时进行身份验证

所需的角色

如需获得配置和管理静态 IP 地址所需的权限,请让管理员向您授予项目的以下 IAM 角色:

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

这些预定义角色包含配置和管理静态 IP 地址所需的权限。如需查看所需的确切权限,请展开所需权限部分:

所需权限

配置和管理静态 IP 地址需要以下权限:

  • 针对虚拟机实例的 compute.instances.update 权限
  • 针对虚拟机实例的 compute.instances.updateNetworkInterface 权限
  • 针对虚拟机实例的 compute.instances.addAccessConfig 权限
  • 针对虚拟机实例的 compute.instances.deleteAccessConfig 权限
  • 网络上的 compute.networks.list
  • 针对子网的 compute.subnetworks.use 权限
  • 针对子网的 compute.subnetworks.list 权限
  • 创建虚拟机:
    • 针对项目的 compute.instances.create 权限
    • 使用自定义映像创建虚拟机:针对映像的 compute.images.useReadOnly 权限
    • 使用快照创建虚拟机:针对快照的 compute.snapshots.useReadOnly 权限
    • 使用实例模板创建虚拟机:针对实例模板的 compute.instanceTemplates.useReadOnly 权限
    • 为虚拟机分配旧版网络:针对项目的 compute.networks.use 权限
    • 为虚拟机指定静态 IP 地址:针对项目的 compute.addresses.use 权限
    • 使用旧版网络时为虚拟机分配外部 IP 地址:针对项目的 compute.networks.useExternalIp 权限
    • 为虚拟机指定子网:针对项目或所选子网的 compute.subnetworks.use 权限
    • 在使用 VPC 网络时为虚拟机分配外部 IP 地址:针对项目或所选子网的 compute.subnetworks.useExternalIp 权限
    • 为虚拟机设置虚拟机实例元数据:针对项目的 compute.instances.setMetadata 权限
    • 为虚拟机设置标记:针对虚拟机的 compute.instances.setTags 权限
    • 为虚拟机设置标签:针对虚拟机的 compute.instances.setLabels 权限
    • 为虚拟机设置要使用的服务账号:针对虚拟机的 compute.instances.setServiceAccount 权限
    • 为虚拟机创建新磁盘:针对项目的 compute.disks.create 权限
    • 以只读或读写模式挂接现有磁盘:针对磁盘的 compute.disks.use 权限
    • 以只读模式挂接现有磁盘:针对磁盘的 compute.disks.useReadOnly 权限

您也可以使用自定义角色或其他预定义角色来获取这些权限。

限制

  • 一次只能有一个资源使用某个静态外部 IP 地址。

  • IP 地址分配给资源后,无法分辨该 IP 地址是静态地址还是临时地址。您可以将 IP 地址与为相关项目预留的静态外部 IP 地址列表进行比较。如需查看项目可用的静态外部 IP 地址列表,请使用 gcloud compute addresses list 子命令

  • 每个虚拟机都可以有多个网络接口,并且每个接口可以分配有以下 IP 地址:

    • 内部 IPv4 地址(必需)
    • 外部 IPv4 地址
    • /96 IPv6 地址范围,可以是内部或外部,但不能同时是两者
  • 您无法更改静态 IP 地址的名称。

  • 分配的外部 IP 地址与虚拟机位于同一物理主机上,并且与虚拟机位于同一区域,可用于包括路由、延迟时间和定价在内的所有用途。无论互联网地理位置查找信息如何,情况都是如此。

注意:网络接口可以接收来自多个转发规则的流量,这些转发规则可能适用于其他外部 IP 地址。任意数量的外部 IP 地址都可以通过这些转发规则来引用某个网络接口,但每个网络接口只能分配有一个外部 IPv4 地址范围和一个外部 /96 IPv6 地址范围。

如需详细了解负载平衡和转发规则,请参阅负载平衡文档。

查看可用的静态外部 IP 地址

如需列出您为项目预留的静态外部 IP 地址,请按照以下步骤操作。

控制台

  1. 在 Google Cloud 控制台中,进入 IP 地址页面。

    进入“IP 地址”

  2. 点击外部 IP 地址

gcloud

使用 gcloud compute addresses list 命令

  • 如需列出所有 IP 地址,请使用以下命令:

    gcloud compute addresses list
  • 如需列出所有全球 IP 地址,请使用以下命令:

    gcloud compute addresses list --global
  • 如需列出给定区域中的所有区域级 IP 地址,请使用以下命令:

    gcloud compute addresses list \
        --regions=REGION
    

    REGION 替换为要列出其地址的区域。您可以通过指定以英文逗号分隔的区域名称来列出多个区域的地址:

    gcloud compute addresses list \
        --regions=REGION1,REGION2,..REGION_n_
    

API

  • 如需列出区域级 IPv4 或 IPv6 地址,请调用 addresses.list 方法

    GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/regions/REGION/addresses
    

    替换以下内容:

    • PROJECT_ID:此请求的项目 ID
    • REGION:此请求的地区名称
  • 如需列出所有区域中的所有地址,请调用 addresses.aggregatedList 方法

    GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/aggregated/addresses
    
  • 如需列出全球 IPv4 或 IPv6 地址,请调用 globalAddresses.list 方法

    GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/global/addresses
    

    请替换以下内容:

    PROJECT_ID:此请求的项目 ID

Go

import (
	"context"
	"fmt"
	"io"

	compute "cloud.google.com/go/compute/apiv1"
	"google.golang.org/api/iterator"

	"cloud.google.com/go/compute/apiv1/computepb"
)

// listRegionalExternal retrieves list external IP addresses in Google Cloud Platform region.
func listRegionalExternal(w io.Writer, projectID, region string) ([]*computepb.Address, error) {
	// projectID := "your_project_id"
	// region := "europe-west3"

	ctx := context.Background()
	// Create the service client.
	addressesClient, err := compute.NewAddressesRESTClient(ctx)
	if err != nil {
		return nil, err
	}
	defer addressesClient.Close()

	// Build the request.
	req := &computepb.ListAddressesRequest{
		Project: projectID,
		Region:  region,
	}

	// List the addresses.
	it := addressesClient.List(ctx, req)

	// Iterate over the results.
	var addresses []*computepb.Address
	for {
		address, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, err
		}
		addresses = append(addresses, address)
	}

	// Print the addresses.
	fmt.Fprint(w, "Fetched addresses: \n")
	for _, address := range addresses {
		fmt.Fprintf(w, "%s\n", *address.Name)
	}

	return addresses, nil
}

// listGlobalExternal retrieves list external global IP addresses in Google Cloud Platform.
func listGlobalExternal(w io.Writer, projectID string) ([]*computepb.Address, error) {
	// projectID := "your_project_id"

	ctx := context.Background()
	// Create the service client.
	addressesClient, err := compute.NewGlobalAddressesRESTClient(ctx)
	if err != nil {
		return nil, err
	}
	defer addressesClient.Close()

	// Build the request.
	req := &computepb.ListGlobalAddressesRequest{
		Project: projectID,
	}

	// List the addresses.
	it := addressesClient.List(ctx, req)

	// Iterate over the results.
	var addresses []*computepb.Address
	for {
		address, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, err
		}
		addresses = append(addresses, address)
	}

	// Print the addresses.
	fmt.Fprint(w, "Fetched addresses: \n")
	for _, address := range addresses {
		fmt.Fprintf(w, "%s\n", *address.Name)
	}

	return addresses, nil
}

Java


import com.google.cloud.compute.v1.Address;
import com.google.cloud.compute.v1.AddressesClient;
import com.google.cloud.compute.v1.GlobalAddressesClient;
import com.google.cloud.compute.v1.ListAddressesRequest;
import com.google.cloud.compute.v1.ListGlobalAddressesRequest;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

public class ListStaticExternalIp {

  public static void main(String[] args)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // Project ID or project number of the Google Cloud project you want to use.
    String projectId = "your-project-id";
    // Region where the VM and IP is located.
    String region = "your-region-id";

    listStaticExternalIp(projectId, region);
  }

  // Lists all static external IP addresses, either regional or global.
  public static List<Address> listStaticExternalIp(String projectId, String region)
          throws IOException {
    // Use regional client if a region is specified
    if (region != null) {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      try (AddressesClient client = AddressesClient.create()) {
        ListAddressesRequest request = ListAddressesRequest.newBuilder()
                .setProject(projectId)
                .setRegion(region)
                .build();

        return Lists.newArrayList(client.list(request).iterateAll());
      }
    } else {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      try (GlobalAddressesClient client = GlobalAddressesClient.create()) {
        ListGlobalAddressesRequest request = ListGlobalAddressesRequest.newBuilder()
                .setProject(projectId)
                .build();

        return Lists.newArrayList(client.list(request).iterateAll());
      }
    }
  }
}

Python

from typing import List, Optional

from google.cloud.compute_v1.services.addresses.client import AddressesClient
from google.cloud.compute_v1.services.global_addresses import GlobalAddressesClient
from google.cloud.compute_v1.types import Address


def list_static_ip_addresses(
    project_id: str, region: Optional[str] = None
) -> List[Address]:
    """
    Lists all static external IP addresses, either regional or global.

    Args:
    project_id (str): project ID.
    region (Optional[str]): The region of the IP addresses if regional. None if global.

    Returns:
    List[Address]: A list of Address objects containing details about the requested IPs.
    """
    if region:
        # Use regional client if a region is specified
        client = AddressesClient()
        addresses_iterator = client.list(project=project_id, region=region)
    else:
        # Use global client if no region is specified
        client = GlobalAddressesClient()
        addresses_iterator = client.list(project=project_id)

    return list(addresses_iterator)  # Convert the iterator to a list to return

配置静态外部 IP 地址

以下部分介绍了如何为虚拟机配置静态外部 IP 地址。

创建一个使用静态外部 IP 地址的虚拟机

预留静态外部 IP 地址后,您可以将其分配给虚拟机。

控制台

  1. 在 Google Cloud 控制台中,转到创建实例页面。

    转到“创建实例”

  2. 指定虚拟机详情。

  3. 展开高级选项部分。

  4. 展开网络部分。

  5. 网络接口部分,展开网络接口以进行修改。

  6. 如需分配 IPv4 地址,请执行以下操作:

    1. 请选择一个网络。
    2. 外部 IPv4 地址列表中选择 IP 地址。
  7. 如需分配 IPv6 地址,请执行以下操作:

    1. 选择包含 IPv6 子网的网络。
    2. 子网列表中选择双栈子网。
    3. 对于 IP 栈类型,请选择 IPv4 和 IPv6(双栈)
    4. 外部 IPv6 地址列表中选择新预留的外部 IPv6 地址。或者,选择创建 IP 地址并预留新的静态外部 IPv6 地址。
    5. 网络服务层级部分,选择高级
  8. 如需完成默认网络接口的修改,请点击完成

  9. 继续虚拟机创建过程。

gcloud

您可以创建虚拟机并分配您已预留的静态区域级外部 IP 地址。

  • 如需分配静态外部 IPv4 地址,请执行以下操作:

    gcloud compute instances create VM_NAME --address=IP_ADDRESS
    

    请替换以下内容:

    • VM_NAME:虚拟机的名称。
    • IP_ADDRESS:要分配给虚拟机的 IP 地址使用预留的静态外部 IP 地址,而不是地址名称。
  • 如需分配静态外部 IPv6 地址,请执行以下操作:

    gcloud compute instances create VM_NAME \
        --subnet=SUBNET \
        --stack-type=IPV4_IPV6 \
        --external-ipv6-address=IPV6_ADDRESS \
        --external-ipv6-prefix-length=96 \
        --ipv6-network-tier=PREMIUM \
        --zone=ZONE
    

Terraform

您可以使用 google_compute_instance 资源分配外部 IP 地址。

resource "google_compute_instance" "default" {
  name         = "dns-proxy-nfs"
  machine_type = "n1-standard-1"
  zone         = "us-central1-a"

  boot_disk {
    initialize_params {
      image = "ubuntu-1404-trusty-v20160627"
    }
  }

  network_interface {
    network = "default"
    access_config {
      nat_ip = google_compute_address.default.address
    }
  }
}

REST

如需为新虚拟机分配静态外部 IPv4 地址,请执行以下操作:

在您创建新实例的请求中,明确提供 networkInterfaces[].accessConfigs[].natIP 属性和您要使用的外部 IPv4 地址。例如:

{
  "name": "VM_NAME",
  "machineType": "zones/ZONE/machineTypes/MACHINE_TYPE",
  "networkInterfaces": [{
    "accessConfigs": [{
      "type": "ONE_TO_ONE_NAT",
      "name": "External NAT",
      "natIP": "IPV4_ADDRESS"
     }],
    "network": "global/networks/default"
  }],
  "disks": [{
      "autoDelete": "true",
      "boot": "true",
      "type": "PERSISTENT",
      "initializeParams": {
          "sourceImage": "SOURCE_IMAGE"
      }
}]
}

如需为新虚拟机分配静态外部 IPv6 地址,请执行以下操作:

在您创建新实例的请求中,明确提供 networkInterfaces[].ipv6AccessConfigs[].externalIpv6 属性和您要使用的外部 IPv6 地址。例如:

{
  "name": "VM_NAME",
  "machineType": "zones/ZONE/machineTypes/MACHINE_TYPE",
  "networkInterfaces": [{
          "accessConfigs": [{
              "name": "external-nat",
              "type": "ONE_TO_ONE_NAT"
          }],
          "ipv6AccessConfigs": [{
        "externalIpv6": "IOV6_ADDRESS",
        "externalIpv6PrefixLength": 96,
        "name": "external-ipv6-access-config",
        "networkTier": "PREMIUM",
        "type": "DIRECT_IPV6"
          }],
      "stackType": "IPV4_IPV6",
      "subnetwork":"SUBNETWORK
  }],
  "disks": [{
    "autoDelete": "true",
    "boot": "true",
    "mode": "READ_WRITE",
    "type": "PERSISTENT",
    "initializeParams": {
        "sourceImage": "SOURCE_IMAGE"
    },
  }],
 }

Go


// assignStaticExternalToNewVM creates a new VM instance and assigns a static external IP address to it.
// NOTE: ip address is expected to exist and be located in the same region as new VM
func assignStaticExternalToNewVM(w io.Writer, projectID, zone, instanceName, ipAddress string) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"
	// ipAddress := 301.222.11.123

	ctx := context.Background()
	instancesClient, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer instancesClient.Close()

	imagesClient, err := compute.NewImagesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewImagesRESTClient: %w", err)
	}
	defer imagesClient.Close()

	// List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details.
	newestDebianReq := &computepb.GetFromFamilyImageRequest{
		Project: "debian-cloud",
		Family:  "debian-12",
	}
	newestDebian, err := imagesClient.GetFromFamily(ctx, newestDebianReq)
	if err != nil {
		return fmt.Errorf("unable to get image from family: %w", err)
	}

	req := &computepb.InsertInstanceRequest{
		Project: projectID,
		Zone:    zone,
		InstanceResource: &computepb.Instance{
			Name: proto.String(instanceName),
			Disks: []*computepb.AttachedDisk{
				{
					InitializeParams: &computepb.AttachedDiskInitializeParams{
						DiskSizeGb:  proto.Int64(10),
						SourceImage: newestDebian.SelfLink,
						DiskType:    proto.String(fmt.Sprintf("zones/%s/diskTypes/pd-standard", zone)),
					},
					AutoDelete: proto.Bool(true),
					Boot:       proto.Bool(true),
					Type:       proto.String(computepb.AttachedDisk_PERSISTENT.String()),
				},
			},
			MachineType: proto.String(fmt.Sprintf("zones/%s/machineTypes/n1-standard-1", zone)),
			NetworkInterfaces: []*computepb.NetworkInterface{
				{
					AccessConfigs: []*computepb.AccessConfig{
						{
							Type:        proto.String(computepb.AccessConfig_ONE_TO_ONE_NAT.String()),
							Name:        proto.String("External NAT"),
							NetworkTier: proto.String(computepb.AccessConfig_PREMIUM.String()),
							NatIP:       proto.String(ipAddress),
						},
					},
				},
			},
		},
	}

	op, err := instancesClient.Insert(ctx, req)
	if err != nil {
		return fmt.Errorf("unable to create instance: %w", err)
	}

	if err = op.Wait(ctx); err != nil {
		return fmt.Errorf("unable to wait for the operation: %w", err)
	}

	fmt.Fprintf(w, "Static address %s assigned to new VM", ipAddress)

	return nil
}

Java


import com.google.cloud.compute.v1.AccessConfig;
import com.google.cloud.compute.v1.AccessConfig.Type;
import com.google.cloud.compute.v1.Address.NetworkTier;
import com.google.cloud.compute.v1.AttachedDisk;
import com.google.cloud.compute.v1.AttachedDiskInitializeParams;
import com.google.cloud.compute.v1.GetInstanceRequest;
import com.google.cloud.compute.v1.ImagesClient;
import com.google.cloud.compute.v1.InsertInstanceRequest;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AssignStaticExternalNewVmAddress {

  public static void main(String[] args)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // Project ID or project number of the Google Cloud project you want to use.
    String projectId = "your-project-id";
    // Instance ID of the Google Cloud project you want to use.
    String instanceId = "your-instance-id";
    // Name of the zone to create the instance in. For example: "us-west3-b"
    String zone = "your-zone-id";
    // machine type of the VM being created. This value uses the
    // following format: "zones/{zone}/machineTypes/{type_name}".
    // For example: "zones/europe-west3-c/machineTypes/f1-micro"
    String machineType = String.format("zones/%s/machineTypes/{your-machineType-id}", zone);
    // boolean flag indicating if the instance should have an external IPv4 address assigned.
    boolean externalAccess = true;
    // external IPv4 address to be assigned to this instance. If you specify
    // an external IP address, it must live in the same region as the zone of the instance.
    // This setting requires `external_access` to be set to True to work.
    String externalIpv4 = "your-externalIpv4-id";

    assignStaticExternalNewVmAddress(projectId, instanceId, zone,
            externalAccess, machineType, externalIpv4);
  }

  // Create a new VM instance with assigned static external IP address.
  public static Instance assignStaticExternalNewVmAddress(String projectId, String instanceName,
                                                          String zone, boolean externalAccess,
                                                          String machineType, String externalIpv4)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    String sourceImage;
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (ImagesClient imagesClient = ImagesClient.create()) {
      sourceImage = imagesClient.getFromFamily("debian-cloud", "debian-11").getSelfLink();
    }
    AttachedDisk attachedDisk = buildAttachedDisk(sourceImage, zone);

    return createInstance(projectId, instanceName, zone,
            attachedDisk, machineType, externalAccess, externalIpv4);
  }

  private static AttachedDisk buildAttachedDisk(String sourceImage, String zone) {
    AttachedDiskInitializeParams initializeParams = AttachedDiskInitializeParams.newBuilder()
            .setSourceImage(sourceImage)
            .setDiskSizeGb(10)
            .setDiskType(String.format("zones/%s/diskTypes/pd-standard", zone))
            .build();

    return AttachedDisk.newBuilder()
            .setInitializeParams(initializeParams)
            // Remember to set auto_delete to True if you want the disk to be deleted
            // when you delete your VM instance.
            .setAutoDelete(true)
            .setBoot(true)
            .build();
  }

  // Send an instance creation request to the Compute Engine API and wait for it to complete.
  private static Instance createInstance(String projectId, String instanceName,
                                         String zone, AttachedDisk disks,
                                         String machineType, boolean externalAccess,
                                         String externalIpv4)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (InstancesClient client = InstancesClient.create()) {
      Instance instanceResource =
              buildInstanceResource(instanceName, disks, machineType, externalAccess, externalIpv4);

      InsertInstanceRequest build = InsertInstanceRequest.newBuilder()
              .setProject(projectId)
              .setRequestId(UUID.randomUUID().toString())
              .setZone(zone)
              .setInstanceResource(instanceResource)
              .build();
      client.insertCallable().futureCall(build).get(60, TimeUnit.SECONDS);

      GetInstanceRequest getInstanceRequest = GetInstanceRequest.newBuilder()
              .setInstance(instanceName)
              .setProject(projectId)
              .setZone(zone)
              .build();

      return client.get(getInstanceRequest);
    }
  }

  private static Instance buildInstanceResource(String instanceName, AttachedDisk disk,
                                                String machineType, boolean externalAccess,
                                                String externalIpv4) {
    NetworkInterface networkInterface =
            networkInterface(externalAccess, externalIpv4);

    return Instance.newBuilder()
            .setName(instanceName)
            .addDisks(disk)
            .setMachineType(machineType)
            .addNetworkInterfaces(networkInterface)
            .build();
  }

  private static NetworkInterface networkInterface(boolean externalAccess, String externalIpv4) {
    NetworkInterface.Builder build = NetworkInterface.newBuilder()
            .setNetwork("global/networks/default");
    if (externalAccess) {
      AccessConfig.Builder accessConfig = AccessConfig.newBuilder()
              .setType(Type.ONE_TO_ONE_NAT.name())
              .setName("External NAT")
              .setNetworkTier(NetworkTier.PREMIUM.name());
      if (externalIpv4 != null) {
        accessConfig.setNatIP(externalIpv4);
      }
      build.addAccessConfigs(accessConfig.build());
    }

    return build.build();
  }
}

Python

from __future__ import annotations

import re
import sys
from typing import Any
import warnings

from google.api_core.extended_operation import ExtendedOperation
from google.cloud import compute_v1


def get_image_from_family(project: str, family: str) -> compute_v1.Image:
    """
    Retrieve the newest image that is part of a given family in a project.

    Args:
        project: project ID or project number of the Cloud project you want to get image from.
        family: name of the image family you want to get image from.

    Returns:
        An Image object.
    """
    image_client = compute_v1.ImagesClient()
    # List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details
    newest_image = image_client.get_from_family(project=project, family=family)
    return newest_image


def disk_from_image(
    disk_type: str,
    disk_size_gb: int,
    boot: bool,
    source_image: str,
    auto_delete: bool = True,
) -> compute_v1.AttachedDisk:
    """
    Create an AttachedDisk object to be used in VM instance creation. Uses an image as the
    source for the new disk.

    Args:
         disk_type: the type of disk you want to create. This value uses the following format:
            "zones/{zone}/diskTypes/(pd-standard|pd-ssd|pd-balanced|pd-extreme)".
            For example: "zones/us-west3-b/diskTypes/pd-ssd"
        disk_size_gb: size of the new disk in gigabytes
        boot: boolean flag indicating whether this disk should be used as a boot disk of an instance
        source_image: source image to use when creating this disk. You must have read access to this disk. This can be one
            of the publicly available images or an image from one of your projects.
            This value uses the following format: "projects/{project_name}/global/images/{image_name}"
        auto_delete: boolean flag indicating whether this disk should be deleted with the VM that uses it

    Returns:
        AttachedDisk object configured to be created using the specified image.
    """
    boot_disk = compute_v1.AttachedDisk()
    initialize_params = compute_v1.AttachedDiskInitializeParams()
    initialize_params.source_image = source_image
    initialize_params.disk_size_gb = disk_size_gb
    initialize_params.disk_type = disk_type
    boot_disk.initialize_params = initialize_params
    # Remember to set auto_delete to True if you want the disk to be deleted when you delete
    # your VM instance.
    boot_disk.auto_delete = auto_delete
    boot_disk.boot = boot
    return boot_disk


def wait_for_extended_operation(
    operation: ExtendedOperation, verbose_name: str = "operation", timeout: int = 300
) -> Any:
    """
    Waits for the extended (long-running) operation to complete.

    If the operation is successful, it will return its result.
    If the operation ends with an error, an exception will be raised.
    If there were any warnings during the execution of the operation
    they will be printed to sys.stderr.

    Args:
        operation: a long-running operation you want to wait on.
        verbose_name: (optional) a more verbose name of the operation,
            used only during error and warning reporting.
        timeout: how long (in seconds) to wait for operation to finish.
            If None, wait indefinitely.

    Returns:
        Whatever the operation.result() returns.

    Raises:
        This method will raise the exception received from `operation.exception()`
        or RuntimeError if there is no exception set, but there is an `error_code`
        set for the `operation`.

        In case of an operation taking longer than `timeout` seconds to complete,
        a `concurrent.futures.TimeoutError` will be raised.
    """
    result = operation.result(timeout=timeout)

    if operation.error_code:
        print(
            f"Error during {verbose_name}: [Code: {operation.error_code}]: {operation.error_message}",
            file=sys.stderr,
            flush=True,
        )
        print(f"Operation ID: {operation.name}", file=sys.stderr, flush=True)
        raise operation.exception() or RuntimeError(operation.error_message)

    if operation.warnings:
        print(f"Warnings during {verbose_name}:\n", file=sys.stderr, flush=True)
        for warning in operation.warnings:
            print(f" - {warning.code}: {warning.message}", file=sys.stderr, flush=True)

    return result


def create_instance(
    project_id: str,
    zone: str,
    instance_name: str,
    disks: list[compute_v1.AttachedDisk],
    machine_type: str = "n1-standard-1",
    network_link: str = "global/networks/default",
    subnetwork_link: str = None,
    internal_ip: str = None,
    external_access: bool = False,
    external_ipv4: str = None,
    accelerators: list[compute_v1.AcceleratorConfig] = None,
    preemptible: bool = False,
    spot: bool = False,
    instance_termination_action: str = "STOP",
    custom_hostname: str = None,
    delete_protection: bool = False,
) -> compute_v1.Instance:
    """
    Send an instance creation request to the Compute Engine API and wait for it to complete.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        zone: name of the zone to create the instance in. For example: "us-west3-b"
        instance_name: name of the new virtual machine (VM) instance.
        disks: a list of compute_v1.AttachedDisk objects describing the disks
            you want to attach to your new instance.
        machine_type: machine type of the VM being created. This value uses the
            following format: "zones/{zone}/machineTypes/{type_name}".
            For example: "zones/europe-west3-c/machineTypes/f1-micro"
        network_link: name of the network you want the new instance to use.
            For example: "global/networks/default" represents the network
            named "default", which is created automatically for each project.
        subnetwork_link: name of the subnetwork you want the new instance to use.
            This value uses the following format:
            "regions/{region}/subnetworks/{subnetwork_name}"
        internal_ip: internal IP address you want to assign to the new instance.
            By default, a free address from the pool of available internal IP addresses of
            used subnet will be used.
        external_access: boolean flag indicating if the instance should have an external IPv4
            address assigned.
        external_ipv4: external IPv4 address to be assigned to this instance. If you specify
            an external IP address, it must live in the same region as the zone of the instance.
            This setting requires `external_access` to be set to True to work.
        accelerators: a list of AcceleratorConfig objects describing the accelerators that will
            be attached to the new instance.
        preemptible: boolean value indicating if the new instance should be preemptible
            or not. Preemptible VMs have been deprecated and you should now use Spot VMs.
        spot: boolean value indicating if the new instance should be a Spot VM or not.
        instance_termination_action: What action should be taken once a Spot VM is terminated.
            Possible values: "STOP", "DELETE"
        custom_hostname: Custom hostname of the new VM instance.
            Custom hostnames must conform to RFC 1035 requirements for valid hostnames.
        delete_protection: boolean value indicating if the new virtual machine should be
            protected against deletion or not.
    Returns:
        Instance object.
    """
    instance_client = compute_v1.InstancesClient()

    # Use the network interface provided in the network_link argument.
    network_interface = compute_v1.NetworkInterface()
    network_interface.network = network_link
    if subnetwork_link:
        network_interface.subnetwork = subnetwork_link

    if internal_ip:
        network_interface.network_i_p = internal_ip

    if external_access:
        access = compute_v1.AccessConfig()
        access.type_ = compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT.name
        access.name = "External NAT"
        access.network_tier = access.NetworkTier.PREMIUM.name
        if external_ipv4:
            access.nat_i_p = external_ipv4
        network_interface.access_configs = [access]

    # Collect information into the Instance object.
    instance = compute_v1.Instance()
    instance.network_interfaces = [network_interface]
    instance.name = instance_name
    instance.disks = disks
    if re.match(r"^zones/[a-z\d\-]+/machineTypes/[a-z\d\-]+$", machine_type):
        instance.machine_type = machine_type
    else:
        instance.machine_type = f"zones/{zone}/machineTypes/{machine_type}"

    instance.scheduling = compute_v1.Scheduling()
    if accelerators:
        instance.guest_accelerators = accelerators
        instance.scheduling.on_host_maintenance = (
            compute_v1.Scheduling.OnHostMaintenance.TERMINATE.name
        )

    if preemptible:
        # Set the preemptible setting
        warnings.warn(
            "Preemptible VMs are being replaced by Spot VMs.", DeprecationWarning
        )
        instance.scheduling = compute_v1.Scheduling()
        instance.scheduling.preemptible = True

    if spot:
        # Set the Spot VM setting
        instance.scheduling.provisioning_model = (
            compute_v1.Scheduling.ProvisioningModel.SPOT.name
        )
        instance.scheduling.instance_termination_action = instance_termination_action

    if custom_hostname is not None:
        # Set the custom hostname for the instance
        instance.hostname = custom_hostname

    if delete_protection:
        # Set the delete protection bit
        instance.deletion_protection = True

    # Prepare the request to insert an instance.
    request = compute_v1.InsertInstanceRequest()
    request.zone = zone
    request.project = project_id
    request.instance_resource = instance

    # Wait for the create operation to complete.
    print(f"Creating the {instance_name} instance in {zone}...")

    operation = instance_client.insert(request=request)

    wait_for_extended_operation(operation, "instance creation")

    print(f"Instance {instance_name} created.")
    return instance_client.get(project=project_id, zone=zone, instance=instance_name)


def assign_static_external_ip_to_new_vm(
    project_id: str, zone: str, instance_name: str, ip_address: str
) -> compute_v1.Instance:
    """
    Create a new VM instance with assigned static external IP address.

    Args:
        project_id (str): project ID or project number of the Cloud project you want to use.
        zone (str): name of the zone to create the instance in. For example: "us-west3-b"
        instance_name (str): name of the new virtual machine (VM) instance.
        ip_address(str): external address to be assigned to this instance. It must live in the same
        region as the zone of the instance and be precreated before function called.

    Returns:
        Instance object.
    """
    newest_debian = get_image_from_family(project="debian-cloud", family="debian-12")
    disk_type = f"zones/{zone}/diskTypes/pd-standard"
    disks = [disk_from_image(disk_type, 10, True, newest_debian.self_link, True)]
    instance = create_instance(
        project_id,
        zone,
        instance_name,
        disks,
        external_ipv4=ip_address,
        external_access=True,
    )
    return instance

更改现有实例的外部 IP 地址或为其分配一个外部 IP 地址

您可以为现有实例更改或分配外部 IP 地址(临时或静态)。

虚拟机可以有多个接口,每个接口可以有一个外部 IP 地址。如果该实例已有一个外部 IP 地址,您必须先移除该地址。然后,您可以为现有实例分配新的外部 IP 地址。

控制台

  1. 在 Google Cloud 控制台中,前往虚拟机实例页面。

    转到“虚拟机实例”

  2. 点击要为其分配外部 IP 地址的虚拟机的名称。此时将显示实例详细信息页面。
  3. 实例详细信息页面中,完成以下步骤:

    1. 点击修改
    2. 展开网络接口
    3. 选择要为实例分配的所需外部 IP 地址:
      1. 对于外部 IPv4 地址,选择临时或静态外部 IPv4 地址。
      2. 对于外部 IPv6 地址,选择临时或静态外部 IPv6 地址。
    4. 点击完成
  4. 点击保存

gcloud

  1. 可选:预留静态外部 IP 地址。

    如果您要分配静态外部 IP 地址,则必须预留一个地址并确保该地址当前未被其他资源使用。视需要按照说明操作,选择预留新的静态外部 IP 地址取消分配静态外部 IP 地址

    如果打算使用临时外部 IP 地址,可以跳过此步骤,Compute Engine 会随机分配一个临时外部 IP 地址。

  2. 移除所有现有的 IP 地址分配,如取消分配静态外部 IP 地址中所述。

  3. 分配新的外部 IP 地址。

    • 如需分配 IPv4 地址,请使用 instances add-access-config 子命令

      注意:请勿将 IP_ADDRESS 替换为静态 IP 地址的名称。您必须使用实际 IP 地址。
      gcloud compute instances add-access-config VM_NAME \
        --access-config-name="ACCESS_CONFIG_NAME" --address=IP_ADDRESS
      

      请替换以下内容:

      • VM_NAME:虚拟机的名称。
      • ACCESS_CONFIG_NAME:此访问配置的名称。请确保用引号括住全名。
      • IP_ADDRESS:要添加的 IP 地址。

      如果您希望 Compute Engine 分配临时外部 IP 地址,而非使用静态外部 IP 地址,请忽略 --address IP_ADDRESS 属性:

      gcloud compute instances add-access-config VM_NAME \
        --access-config-name="ACCESS_CONFIG_NAME"
      
    • 如需分配 IPv6 地址范围,请使用 instance network-interfaces update 子命令

      gcloud compute instances network-interfaces update VM_NAME \
        --network-interface=NIC \
        --ipv6-network-tier=PREMIUM \
        --stack-type=IPV4_IPV6 \
        --external-ipv6-address=IPV6_ADDRESS \
        --external-ipv6-prefix-length=96 \
        --zone=ZONE
      

      替换以下内容:

      • VM_NAME:虚拟机的名称。
      • NIC:网络接口的名称。
      • IPV6_ADDRESS:要分配给虚拟机的 IPv6 地址。指定 /96 范围内的第一个 IPv6 地址。
      • ZONE:虚拟机的可用区。

REST

您可以通过为虚拟机添加新的访问配置来更改该虚拟机的外部 IPv4 或 IPv6 地址。

  1. 移除所有现有的 IP 地址分配,如取消分配静态外部 IP 地址中所述。

  2. instances.deleteAccessConfig 方法发出 POST 请求,删除现有的访问配置。

    POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/deleteAccessConfig
    
  3. instances.addAccessConfig 方法发出 POST 请求,以向虚拟机的网络接口添加新的访问配置。

Go

import (
	"context"
	"fmt"
	"io"

	"google.golang.org/protobuf/proto"

	compute "cloud.google.com/go/compute/apiv1"
	"cloud.google.com/go/compute/apiv1/computepb"
)

// assignStaticAddressToExistingVM assigns a static external IP address to an existing VM instance.
// Note: VM and assigned IP must be in the same region.
func assignStaticAddressToExistingVM(w io.Writer, projectID, zone, instanceName, IPAddress, networkInterfaceName string) error {
	// projectID := "your_project_id"
	// zone := "europe-central2-b"
	// instanceName := "your_instance_name"
	// IPAddress := "34.111.222.333"
	// networkInterfaceName := "nic0"

	ctx := context.Background()
	instancesClient, err := compute.NewInstancesRESTClient(ctx)
	if err != nil {
		return fmt.Errorf("NewInstancesRESTClient: %w", err)
	}
	defer instancesClient.Close()

	reqGet := &computepb.GetInstanceRequest{
		Project:  projectID,
		Zone:     zone,
		Instance: instanceName,
	}

	instance, err := instancesClient.Get(ctx, reqGet)
	if err != nil {
		return fmt.Errorf("could not get instance: %w", err)
	}

	var networkInterface *computepb.NetworkInterface
	for _, ni := range instance.NetworkInterfaces {
		if *ni.Name == networkInterfaceName {
			networkInterface = ni
			break
		}
	}

	if networkInterface == nil {
		return fmt.Errorf("No network interface named '%s' found on instance %s", networkInterfaceName, instanceName)
	}

	var accessConfig *computepb.AccessConfig
	for _, ac := range networkInterface.AccessConfigs {
		if *ac.Type == computepb.AccessConfig_ONE_TO_ONE_NAT.String() {
			accessConfig = ac
			break
		}
	}

	if accessConfig != nil {
		// network interface is immutable - deletion stage is required in case of any assigned ip (static or ephemeral).
		reqDelete := &computepb.DeleteAccessConfigInstanceRequest{
			Project:          projectID,
			Zone:             zone,
			Instance:         instanceName,
			AccessConfig:     *accessConfig.Name,
			NetworkInterface: networkInterfaceName,
		}

		opDelete, err := instancesClient.DeleteAccessConfig(ctx, reqDelete)
		if err != nil {
			return fmt.Errorf("unable to delete access config: %w", err)
		}

		if err = opDelete.Wait(ctx); err != nil {
			return fmt.Errorf("unable to wait for the operation: %w", err)
		}
	}

	reqAdd := &computepb.AddAccessConfigInstanceRequest{
		Project:  projectID,
		Zone:     zone,
		Instance: instanceName,
		AccessConfigResource: &computepb.AccessConfig{
			NatIP: &IPAddress,
			Type:  proto.String(computepb.AccessConfig_ONE_TO_ONE_NAT.String()),
		},
		NetworkInterface: networkInterfaceName,
	}

	opAdd, err := instancesClient.AddAccessConfig(ctx, reqAdd)
	if err != nil {
		return fmt.Errorf("unable to add access config: %w", err)
	}

	if err = opAdd.Wait(ctx); err != nil {
		return fmt.Errorf("unable to wait for the operation: %w", err)
	}

	fmt.Fprintf(w, "Static address %s assigned to the instance %s\n", IPAddress, instanceName)

	return nil
}

Java


import com.google.cloud.compute.v1.AccessConfig;
import com.google.cloud.compute.v1.AccessConfig.Type;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AssignStaticExistingVm {

  public static void main(String[] args)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // Project ID or project number of the Google Cloud project you want to use.
    String projectId = "your-project-id";
    // Instance ID of the Google Cloud project you want to use.
    String instanceId = "your-instance-id";
    // Name of the zone to create the instance in. For example: "us-west3-b"
    String zone = "your-zone-id";
    // Name of the network interface to assign.
    String netInterfaceName = "your-netInterfaceName-id";

    assignStaticExistingVmAddress(projectId, instanceId, zone, netInterfaceName);
  }

  // Updates or creates an access configuration for a VM instance to assign a static external IP.
  // As network interface is immutable - deletion stage is required
  // in case of any assigned ip (static or ephemeral).
  // VM and ip address must be created before calling this function.
  // IMPORTANT: VM and assigned IP must be in the same region.
  public static Instance assignStaticExistingVmAddress(String projectId, String instanceId,
                                                       String zone, String netInterfaceName)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (InstancesClient client = InstancesClient.create()) {
      Instance instance = client.get(projectId, zone, instanceId);

      NetworkInterface networkInterface = null;
      for (NetworkInterface netInterface : instance.getNetworkInterfacesList()) {
        if (netInterface.getName().equals(netInterfaceName)) {
          networkInterface = netInterface;
          break;
        }
      }

      if (networkInterface == null) {
        throw new IllegalArgumentException(
                String.format(
                        "No '{network_interface_name}' variable found on instance %s.",
                        instanceId)
        );
      }
      AccessConfig accessConfig = null;
      for (AccessConfig config : networkInterface.getAccessConfigsList()) {
        if (config.getType().equals(Type.ONE_TO_ONE_NAT.name())) {
          accessConfig = config;
          break;
        }
      }

      if (accessConfig != null) {
        // Delete the existing access configuration first
        client.deleteAccessConfigAsync(projectId, zone, instanceId,
                        accessConfig.getName(), netInterfaceName)
                .get(30, TimeUnit.SECONDS);
      }

      // Add a new access configuration with the new IP
      AccessConfig newAccessConfig = AccessConfig.newBuilder()
              // Leave this field undefined to use an IP from a shared ephemeral IP address pool
              // .setNatIP(ipAddress)
              .setType(Type.ONE_TO_ONE_NAT.name())
              .setName("external-nat")
              .build();

      client.addAccessConfigAsync(projectId, zone, instanceId, netInterfaceName, newAccessConfig)
              .get(30, TimeUnit.SECONDS);

      // return updated instance
      return client.get(projectId, zone, instanceId);
    }
  }
}

Python

import uuid

from google.cloud.compute_v1 import InstancesClient
from google.cloud.compute_v1.types import AccessConfig
from google.cloud.compute_v1.types import AddAccessConfigInstanceRequest
from google.cloud.compute_v1.types import DeleteAccessConfigInstanceRequest


def assign_static_ip_to_existing_vm(
    project_id: str,
    zone: str,
    instance_name: str,
    ip_address: str,
    network_interface_name: str = "nic0",
):
    """
    Updates or creates an access configuration for a VM instance to assign a static external IP.
    As network interface is immutable - deletion stage is required in case of any assigned ip (static or ephemeral).
    VM and ip address must be created before calling this function.
    IMPORTANT: VM and assigned IP must be in the same region.

    Args:
        project_id (str): Project ID.
        zone (str): Zone where the VM is located.
        instance_name (str): Name of the VM instance.
        ip_address (str): New static external IP address to assign to the VM.
        network_interface_name (str): Name of the network interface to assign.

    Returns:
        google.cloud.compute_v1.types.Instance: Updated instance object.
    """
    client = InstancesClient()
    instance = client.get(project=project_id, zone=zone, instance=instance_name)
    network_interface = next(
        (ni for ni in instance.network_interfaces if ni.name == network_interface_name),
        None,
    )

    if network_interface is None:
        raise ValueError(
            f"No network interface named '{network_interface_name}' found on instance {instance_name}."
        )

    access_config = next(
        (ac for ac in network_interface.access_configs if ac.type_ == "ONE_TO_ONE_NAT"),
        None,
    )

    if access_config:
        # Delete the existing access configuration first
        delete_request = DeleteAccessConfigInstanceRequest(
            project=project_id,
            zone=zone,
            instance=instance_name,
            access_config=access_config.name,
            network_interface=network_interface_name,
            request_id=str(uuid.uuid4()),
        )
        delete_operation = client.delete_access_config(delete_request)
        delete_operation.result()

    # Add a new access configuration with the new IP
    add_request = AddAccessConfigInstanceRequest(
        project=project_id,
        zone=zone,
        instance=instance_name,
        network_interface="nic0",
        access_config_resource=AccessConfig(
            nat_i_p=ip_address, type_="ONE_TO_ONE_NAT", name="external-nat"
        ),
        request_id=str(uuid.uuid4()),
    )
    add_operation = client.add_access_config(add_request)
    add_operation.result()

    updated_instance = client.get(project=project_id, zone=zone, instance=instance_name)
    return updated_instance

将外部 IP 地址限制为仅用于特定虚拟机

对于某些工作负载,您可能有安全和网络限制等方面的基本要求。例如,您可能希望限制外部 IP 地址,使得只有特定的虚拟机可以使用这些地址。此方式有助于防止数据渗漏或者有助于维持网络隔离状态。借助组织政策,您可以在某些限制条件下将外部 IP 地址仅用于特定的虚拟机,以控制组织或项目中的虚拟机实例对外部 IP 地址的使用。

用于控制虚拟机的外部 IP 地址的限制条件如下:

constraints/compute.vmExternalIpAccess

如需使用该限制,请指定一个包含 allowedList 列表的政策,该列表中列出了可以具有外部 IP 地址的虚拟机。如果未指定政策,则允许所有虚拟机使用所有外部 IP 地址。政策实施后,只有 allowedValues 列表中列出的虚拟机才能获得外部 IP 地址(临时或静态),组织或项目中其他未在政策中明确定义的 Compute Engine 虚拟机会被禁止使用外部 IP 地址。

系统根据虚拟机的 URI 在许可和拒绝名单中识别虚拟机:

projects/PROJECT_ID/zones/ZONE/instances/VM_NAME

限制外部 IP 地址的规范

  • 您只能将此列表限制条件应用于虚拟机。
  • 您不能追溯应用限制。政策启用前,所有具有外部 IP 地址的虚拟机都会保留其外部 IP 地址。
  • 此限制在同一个政策中只接受 allowedListdeniedList 其中之一,不能同时接受。
  • 由您或拥有所需权限的管理员负责管理和维护实例生命周期和完整性。此限制仅用于验证虚拟机的 URI,而不会阻止系统更改、删除或重新创建许可名单中的虚拟机。

限制外部 IP 地址所需的权限

如需在项目级层或组织级层设置限制,您必须已被授予组织的 orgpolicy.policyAdmin 角色。

在组织级层设置政策限制条件

控制台

  1. 转到组织政策页面。

    转到“组织政策”

  2. 如有必要,请从项目下拉菜单中选择所需的组织。
  3. 点击为虚拟机实例定义许可的外部 IP
  4. 点击修改以修改外部 IP 政策。如果您无法访问修改工具,则表示您没有正确的权限
  5. 选择自定义,为特定虚拟机设置组织政策。

    修改组织政策页面上的自定义选项。

  6. 选择所需的政策执行政策类型

  7. 对于政策值,选择自定义

  8. 输入虚拟机的 URI,然后按 Enter 键。URI 必须采用以下格式:

    projects/PROJECT_ID/zones/ZONE/instances/VM_NAME
    
  9. 点击新建政策值,然后根据需要输入虚拟机的 URI。

  10. 点击保存以应用该限制条件。

gcloud

如需为外部 IP 访问权限设置限制,您首先需要查找组织 ID。您可以通过运行 organizations list 命令并在响应中查找数字 ID 来找到组织 ID:

gcloud organizations list

gcloud CLI 会按以下格式返回组织列表:

DISPLAY_NAME               ID
example-organization1      29252605212
example-organization2      1234567890

使用 gcloud resource-manager org-policies set-policy 命令可以设置政策。您将需要以 JSON 文件的形式提供政策。按以下格式创建一个 JSON 文件:

{
"constraint": "constraints/compute.vmExternalIpAccess",
"listPolicy": {
  "allowedValues": [
     "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME",
     "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME",
     "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME"
  ]
 }
}

替换以下内容:

  • PROJECT_ID:此请求的项目 ID,例如 example-project。 请注意,设置项目 ID 与设置组织政策不同,设置组织政策时需要使用组织的数字 ID。
  • ZONE:虚拟机所在的区域
  • VM_NAME:虚拟机的名称

或者,您可以指定 deniedValues 列表来指示您希望明确禁止其拥有外部 IP 地址的虚拟机。列表中没有列出的实例可隐式拥有外部 IP 地址。您只能指定 allowedValuesdeniedValues,但不能同时指定这两者。

然后,使用请求传入该文件:

gcloud resource-manager org-policies set-policy MY_POLICY.JSON --organization=ORGANIZATION_ID

ORGANIZATION_ID 替换为组织的数字 ID。

如果您不希望任何虚拟机具有外部 IP 访问权限,您可以设置一个政策,并将 allValues 设置为 DENY

{
  "constraint": "constraints/compute.vmExternalIpAccess",
  "listPolicy": {
    "allValues": "DENY"
  }
}

REST

使用 setOrgPolicy() API 定义您的限制。您指定的 allowedValue 列表中的虚拟机可以具有外部 IP 地址。或者,您可以指定 deniedValues 列表来指示您希望明确禁止其拥有外部 IP 地址的虚拟机。列表中没有列出的实例可隐式拥有外部 IP 地址。您只能指定 allowedValuesdeniedValues,但不能同时指定这两者。

例如,以下请求针对组织应用了 compute.vmExternalIpAccess 限制,允许组织内某些项目中的虚拟机实例拥有外部 IP 地址:

POST https://cloudresourcemanager.googleapis.com/v1/organizations/ORGANIZATION_ID:setOrgPolicy

其中 ORGANIZATION_ID 是组织的数字 ID。

现在,在请求正文中,提供此限制所需的政策:

{
  "policy": {
    "constraint": "constraints/compute.vmExternalIpAccess",
    "listPolicy": {
      "allowedValues": [
        "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME",
        "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME",
        "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME"
        ]
      }
    }
 }

如果您不希望任何虚拟机具有外部 IP 访问权限,您可以设置一个政策,并将 allValues 设置为 DENY

{
  "policy": {
    "constraint": "constraints/compute.vmExternalIpAccess",
    "listPolicy": {
      "allValues": "DENY"
      }
    }
 }

在项目级层设置政策

在项目级层设置的政策会替换组织级层的政策。例如,如果在组织级层政策的 allowedValues 列表中含有 example-vm-1,但在项目级层政策的 deniedValues 列表中含有同一虚拟机,则该虚拟机不允许拥有外部 IP 地址。

控制台

按照设置组织级层的政策限制下记录的相同流程进行操作,只是要从项目(而非组织)选择器中选择您所需的项目。

项目选择器。

gcloud

使用 gcloud resource-manager org-policies set-policy 命令可以设置政策。您将需要以 JSON 文件的形式提供政策。按以下格式创建一个 JSON 文件:

{
 "constraint": "constraints/compute.vmExternalIpAccess",
 "listPolicy": {
  "allowedValues": [
   "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME"
  ]
 }
}

替换以下内容:

  • PROJECT_ID:此请求的项目 ID,例如 example-project。 请注意,设置项目 ID 与设置组织政策不同,设置组织政策时需要使用组织的数字 ID。
  • ZONE:虚拟机的可用区。
  • VM_NAME:虚拟机的名称。

或者,您可以指定您希望明确禁止其拥有外部 IP 地址的虚拟机的 deniedValues 列表。列表中没有列出的实例可隐式拥有外部 IP 地址。您只能指定 allowedValuesdeniedValues,但不能同时指定这两者。

然后,使用请求传入该文件:

gcloud resource-manager org-policies set-policy MY_POLICY.JSON --project=example-project

REST

使用 setOrgPolicy API 定义您的限制。您指定的 allowedValue 列表中的虚拟机可以具有外部 IP 地址。或者,您可以指定 deniedValues 列表来指示您希望明确禁止其拥有外部 IP 地址的虚拟机。列表中没有列出的实例可隐式拥有外部 IP 地址。您只能指定 allowedValuesdeniedValues,但不能同时指定这两者。

例如,以下请求针对项目设置了 compute.vmExternalIpAccess 限制,允许特定虚拟机拥有外部 IP 地址:

POST https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID:setOrgPolicy

PROJECT_ID 替换为此请求的项目 ID。

请求正文包含此限制的所需政策:

{
  "policy": {
    "constraint": "constraints/compute.vmExternalIpAccess",
    "listPolicy": {
      "allowedValues": [
        "projects/PROJECT_ID/zones/ZONE/instances/VM_NAME"
      ]
    }
  }
}

限制外部 IP 地址的最佳做法

  • 请避免将 deniedValues 列表与此限制一起使用。如果您在 deniedValues 列表中定义了值,这意味着只有 deniedValues 列表中的虚拟机被禁止使用外部 IP 地址。如果您想准确控制可以拥有外部 IP 地址的虚拟机,那么这可能会产生安全问题。如果您要从 allowedValues 列表中移除某些虚拟机,请更新现有政策以从 allowedList 中移除虚拟机,而不是将虚拟机放入 deniedValues 列表中的较低层级。

  • 如果您希望针对大部分资源层次结构设置政策,但要排除某些项目,请使用 setOrgPolicy 方法恢复默认政策,只需指定 restoreDefault 对象以允许项目中的所有虚拟机与外部 IP 地址关联即可。项目的当前政策不会受默认设置的影响。

  • 将组织政策与 IAM 角色一起使用可以更好地控制您的环境。此政策仅适用于虚拟机,但如果您想更好地控制和限制网络设备的外部 IP 地址,可以将 compute.networkAdmin 角色授予有关方。

  • 如果启用了政策的组织或项目中有任何服务和产品正在 Compute Engine 上运行,则这些服务和产品受此组织政策的约束。具体而言,Google Kubernetes Engine、Dataflow、Dataproc 和 Cloud SQL 等服务受此政策的影响。如果因此导致问题,Google 建议在未应用组织政策的其他项目中设置其他服务和产品,并根据需要使用共享 VPC

管理静态外部 IP 地址

以下部分介绍了如何管理虚拟机的静态外部 IP 地址。

确定内部 IP 地址是临时地址还是静态地址

静态和临时内部 IP 地址的行为在大多数情况下看起来都相同。但是,对于静态内部 IP 地址,即使您删除并重新创建资源,也可以对同一资源使用相同的 IP 地址。通常,如果您停止或删除资源,临时 IP 地址会被释放。

如需确定地址是静态地址还是临时地址,请执行以下操作:

  1. 在 Google Cloud 控制台中,进入 IP 地址页面。

    进入“IP 地址”

  2. 在列表中找到地址,并查看类型列以获取 IP 地址类型。

取消分配静态外部 IP 地址

如果取消分配某个 IP 地址,系统会将其从资源中移除,但会预留该 IP 地址。取消分配该 IP 地址后,您可以将其重新分配给其他资源。

您还可以通过删除虚拟机来取消分配 IPv4 或 IPv6 地址。

控制台

  1. 在 Google Cloud 控制台中,进入 IP 地址页面。

    进入“IP 地址”

  2. 点击外部 IP 地址

  3. 选择您要取消分配的静态 IP 地址。

  4. 点击 查看操作,然后选择重新分配给其他资源选项。

  5. 关联到下拉列表中,选择

  6. 点击确定

gcloud

  1. 使用 gcloud compute addresses list 命令检查某个静态 IP 地址是否已被使用:

    gcloud compute addresses list
    

    输出类似于以下内容:

    NAME                      REGION    ADDRESS                  STATUS
    example-address-ipv4      REGION    198.51.100.1             RESERVED
    example-address-new-ipv4  REGION    203.0.113.1              IN_USE
    example-address-ipv6      REGION    2001:db8:1:1:1:1:1:1     RESERVED
    example-address-new-ipv6  REGION    2001:db8:4:4:4:4:4:4     IN_USE
    
    • 如果该 IP 地址未被使用,则状态为 RESERVED
    • 如果该 IP 地址已被使用,则状态为 IN_USE
  2. 检索使用该 IP 地址的虚拟机的名称:

    gcloud compute addresses describe ADDRESS_NAME \
      --region=REGION
    

    替换以下内容:

    • ADDRESS_NAME:IPv6 地址资源的名称
    • REGION:IPv6 地址资源所在的区域

    输出类似于以下内容:

    address: IP_ADDRESS
    addressType: EXTERNAL
    ...
    region: https://www.googleapis.com/compute/v1/projects/PROJECT/regions/REGION
    selfLink: https://www.googleapis.com/compute/v1/projects/PROJECT/regions/REGION/addresses/ADDRESS_NAME
    status: IN_USE
    subnetwork: https://www.googleapis.com/compute/v1/projects/PROJECT/regions/REGION/subnetworks/SUBNET
    users:
    - https://www.googleapis.com/compute/v1/projects/PROJECT/zones/ZONE/instances/VM_NAME
    

    users 字段会显示使用该 IP 地址的虚拟机的名称。

  3. 从虚拟机取消分配该 IP 地址。

    • 如需取消分配 IPv4 地址,请删除虚拟机的访问配置文件:

      1. 获取要删除的访问配置的名称。如需获取名称,请使用 gcloud compute instances describe 命令。 将 VM_NAME 替换为虚拟机名称。

        gcloud compute instances describe VM_NAME
        

        访问配置将以如下格式显示:

        networkInterfaces:
          - accessConfigs:
            - kind: compute#accessConfig
              name: external-nat
              natIP: 203.0.113.1
              type: ONE_TO_ONE_NAT
        
      2. 使用 gcloud compute instances delete-access-config 命令删除访问配置:

        gcloud compute instances delete-access-config VM_NAME \
          --access-config-name="ACCESS_CONFIG_NAME"
        

        替换以下内容:

        • VM_NAME:虚拟机的名称。
        • ACCESS_CONFIG_NAME:要删除的访问配置的名称。请确保用引号括住全名。
    • 如需取消分配 IPv6 地址范围,请使用 instance network-interfaces update 命令

      gcloud compute instances network-interfaces update VM_NAME \
        --network-interface=nic0 \
        --stack-type=IPV4_ONLY \
        --zone=ZONE
      

      替换以下内容:

      • VM_NAME:使用该 IP 地址的虚拟机的名称。
      • ZONE:虚拟机的可用区。
  4. 检查静态外部 IP 地址目前是否可以使用,以及是否标记为 RESERVED 而非 IN_USE

    gcloud compute addresses list \
      --filter="ADDRESS_NAME AND region=REGION"
    

    替换以下内容:

    • ADDRESS_NAME:IP 地址资源的名称。
    • REGION:IP 地址资源所在的区域。

在您的静态外部 IP 地址可以使用后,您可以选择将其分配给另一个实例

REST

如需取消分配静态外部 IPv4 或 IPv6 地址,请执行以下步骤:

  • 对于 IPv4 地址,请删除附加到使用该地址的虚拟机的访问配置。

    1. 如需查看虚拟机的访问配置详细信息,请向 instances.get 方法发出 GET 请求。

      GET https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME
      
    2. instances.deleteAccessConfig 方法发出 POST 请求,删除现有的访问配置。

      POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/deleteAccessConfig
      

      替换以下内容:

      • PROJECT_ID:此请求的项目 ID。
      • ZONE:该虚拟机所在的可用区
      • VM_NAME:虚拟机的名称
  • 对于 IPv6 地址,请更新 IPv6 地址关联的虚拟机的网络接口的堆栈类型。

    1. instances.updateNetworkInterface 方法发出 PATCH 请求。

    2. 在请求正文中,将 stackType 字段的值更新为 IPV4_ONLY

      例如:

      PATCH https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/updateNetworkInterface
      
      {
        "networkInterfaces": [{
          ...
          "stackType" : "IPV4_ONLY"
          ...
          }]
      }
      

Java


import com.google.cloud.compute.v1.AccessConfig;
import com.google.cloud.compute.v1.AccessConfig.Type;
import com.google.cloud.compute.v1.Instance;
import com.google.cloud.compute.v1.InstancesClient;
import com.google.cloud.compute.v1.NetworkInterface;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class UnassignStaticIpAddress {

  public static void main(String[] args)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // TODO(developer): Replace these variables before running the sample.
    // Project ID or project number of the Google Cloud project you want to use.
    String projectId = "your-project-id";
    // Instance ID of the Google Cloud project you want to use.
    String instanceId = "your-instance-id";
    // Name of the zone to create the instance in. For example: "us-west3-b"
    String zone = "your-zone";
    // Name of the network interface to assign.
    String netInterfaceName = "your-netInterfaceName";

    unassignStaticIpAddress(projectId, instanceId, zone, netInterfaceName);
  }

  public static Instance unassignStaticIpAddress(String projectId, String instanceId,
                                                 String zone, String netInterfaceName)
          throws IOException, ExecutionException, InterruptedException, TimeoutException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests.
    try (InstancesClient client = InstancesClient.create()) {
      Instance instance = client.get(projectId, zone, instanceId);
      NetworkInterface networkInterface = null;
      for (NetworkInterface netIterface : instance.getNetworkInterfacesList()) {
        if (netIterface.getName().equals(netInterfaceName)) {
          networkInterface = netIterface;
          break;
        }
      }

      if (networkInterface == null) {
        throw new IllegalArgumentException(
                String.format(
                        "No '{network_interface_name}' variable found on instance %s.",
                        instanceId)
        );
      }

      AccessConfig accessConfig = null;
      for (AccessConfig config : networkInterface.getAccessConfigsList()) {
        if (config.getType().equals(Type.ONE_TO_ONE_NAT.name())) {
          accessConfig = config;
          break;
        }
      }

      if (accessConfig != null) {
        // Delete the existing access configuration first
        client.deleteAccessConfigAsync(projectId, zone, instanceId,
                        accessConfig.getName(), netInterfaceName).get(30, TimeUnit.SECONDS);
      }

      // return updated instance
      return client.get(projectId, zone, instanceId);
    }
  }
}

Python

import uuid

from google.cloud.compute_v1 import InstancesClient
from google.cloud.compute_v1.types import DeleteAccessConfigInstanceRequest


def unassign_static_ip_from_existing_vm(
    project_id: str,
    zone: str,
    instance_name: str,
    network_interface_name: str = "nic0",
):
    """
    Updates access configuration for a VM instance to unassign a static external IP.
    VM (and IP address in case of static IP assigned) must be created before calling this function.

    Args:
        project_id (str): Project ID.
        zone (str): Zone where the VM is located.
        instance_name (str): Name of the VM instance.
        network_interface_name (str): Name of the network interface to unassign.
    """
    client = InstancesClient()
    instance = client.get(project=project_id, zone=zone, instance=instance_name)
    network_interface = next(
        (ni for ni in instance.network_interfaces if ni.name == network_interface_name),
        None,
    )

    if network_interface is None:
        raise ValueError(
            f"No network interface named '{network_interface_name}' found on instance {instance_name}."
        )

    access_config = next(
        (ac for ac in network_interface.access_configs if ac.type_ == "ONE_TO_ONE_NAT"),
        None,
    )

    if access_config:
        # Delete the existing access configuration
        delete_request = DeleteAccessConfigInstanceRequest(
            project=project_id,
            zone=zone,
            instance=instance_name,
            access_config=access_config.name,
            network_interface=network_interface_name,
            request_id=str(uuid.uuid4()),
        )
        delete_operation = client.delete_access_config(delete_request)
        delete_operation.result()

    updated_instance = client.get(project=project_id, zone=zone, instance=instance_name)
    return updated_instance

释放静态外部 IP 地址

如果您不再需要某个静态外部 IPv4 或 IPv6 地址,可以通过删除 IP 地址资源来释放该 IP 地址。 删除虚拟机的操作不会自动释放静态外部 IP 地址。如果您不再需要某些静态外部 IP 地址,必须手动释放这些地址。

如需释放静态外部 IP 地址,请参阅 VPC 文档中的释放静态外部 IP 地址

后续步骤