You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python基于Google Cloud实例模板创建带预留区域IP的虚拟机?

解决GCE实例模板创建虚拟机时的预留IP配置TypeError问题

问题背景

已通过CLI创建预留区域IP:

gcloud compute addresses create test-regional-ip --region europe-north1 --project=sindre-dev

执行以下命令确认IP已成功预留:

gcloud compute addresses list --project=sindre-dev

输出结果:

NAME               ADDRESS/RANGE   TYPE      PURPOSE  NETWORK  REGION         SUBNET  STATUS
test-regional-ip   35.228.123.45   EXTERNAL           europe-north1          RESERVED

但在Python代码中直接设置external_ip = "35.228.123.45"并尝试基于实例模板创建虚拟机时,抛出错误:

TypeError: bad argument type for built-in operation

问题原因

GCP Compute Engine的Python SDK(google-cloud-compute)中,AccessConfig的nat_ip字段要求传入预留IP的资源标识符(路径/URL),而非直接的IP字符串。直接赋值纯IP字符串会导致类型不匹配,触发TypeError。

修改后的代码

create_instance_from_template.py

from google.cloud import compute_v1

from src.routes.run_fw_engagements.c_instance_template.helpers.wait_for_extended_operation import \
    wait_for_extended_operation


def create_instance_from_template(
        project_id: str,
        zone: str,
        instance_name: str,
        instance_template_url: str,
        reserved_ip_name: str = None) -> compute_v1.Instance:
    """
    Creates a Compute Engine VM instance from an instance template and assigns a reserved regional IP.

    Args:
        project_id: ID or number of the target project.
        zone: Zone for the new instance (e.g., europe-north1-a).
        instance_name: Name of the new instance.
        instance_template_url: Full/partial URL of the instance template.
        reserved_ip_name: Name of the reserved regional IP address (e.g., test-regional-ip).

    Returns:
        Created Instance object.
    """
    instance_client = compute_v1.InstancesClient()
    template_client = compute_v1.InstanceTemplatesClient()

    # 获取实例模板的原始配置,避免覆盖模板中的网络设置
    template = template_client.get(project=project_id, instance_template=instance_template_url.split('/')[-1])
    instance_resource = compute_v1.Instance(name=instance_name)

    # 构建实例创建请求
    instance_insert_request = compute_v1.InsertInstanceRequest(
        project=project_id,
        zone=zone,
        instance_resource=instance_resource,
        source_instance_template=instance_template_url
    )

    # 处理预留IP配置
    if reserved_ip_name:
        # 从zone提取对应的region(预留IP是区域级资源)
        region = zone.rsplit('-', 1)[0]
        # 构建预留IP的资源路径
        reserved_ip_resource = f"projects/{project_id}/regions/{region}/addresses/{reserved_ip_name}"

        # 复制模板中的网络接口配置,避免完全替换
        if template.properties.network_interfaces:
            network_interface = template.properties.network_interfaces[0]
            # 清空原有access_config,添加新的配置
            network_interface.access_configs = []
        else:
            # 如果模板没有网络接口,创建新的
            network_interface = compute_v1.NetworkInterface()
            # 默认使用项目默认网络,可根据需求修改
            network_interface.network = f"projects/{project_id}/global/networks/default"

        # 创建带预留IP的AccessConfig
        access_config = compute_v1.AccessConfig(
            name="External NAT",
            type_=compute_v1.AccessConfig.Type.ONE_TO_ONE_NAT,
            nat_ip=reserved_ip_resource
        )
        network_interface.access_configs.append(access_config)
        instance_resource.network_interfaces = [network_interface]

    # 调用API创建实例
    operation = instance_client.insert(instance_insert_request)
    wait_for_extended_operation(operation, "instance creation")

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


if __name__ == '__main__':
    project_id = "sindre-dev"
    zone = "europe-north1-a"
    instance_name = "gno-collector-1"
    instance_template_url = f"https://www.googleapis.com/compute/v1/projects/{project_id}/global/instanceTemplates/{instance_name}-v1"
    # 传入预留IP的名称,而非直接IP
    reserved_ip_name = "test-regional-ip"

    create_instance_from_template(
        project_id=project_id,
        zone=zone,
        instance_name=instance_name,
        instance_template_url=instance_template_url,
        reserved_ip_name=reserved_ip_name,
    )

wait_for_extended_operation.py(无需修改)

from __future__ import annotations

import sys
from typing import Any

from google.api_core.extended_operation import ExtendedOperation


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

关键修改说明

  1. 参数调整:将external_ip改为reserved_ip_name,传入预留IP的名称而非直接IP,更符合GCP API的资源引用逻辑
  2. 资源路径构建:从zone自动提取region,构建预留IP的资源路径(projects/{project_id}/regions/{region}/addresses/{reserved_ip_name}),这是GCP API接受的格式
  3. 保留模板配置:先获取实例模板的原始网络接口配置,避免完全替换导致丢失模板中的其他网络设置(如子网、内部IP等)
  4. 正确配置AccessConfig:将构建好的资源路径赋值给nat_ip字段,而非直接使用IP字符串

内容的提问来源于stack exchange,提问作者Europa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:35:54