【问题标题】:How to create Google Compute Engine instance with particular service account setup on it?如何创建具有特定服务帐户设置的 Google Compute Engine 实例?
【发布时间】:2014-05-14 10:42:02
【问题描述】:

当您在 Google Compute Engine 中创建实例 A 时,它将获得预定义的“默认”服务帐户(这基本上意味着您可以从 A 查询 google API,并使用“默认”服务帐户进行身份验证)。 我想做的是使用服务帐户设置 GCE 实例,这与默认帐户不同。考虑到 GCE API,这在概念上应该是可行的,但会因异常而失败:

{ 
"name": "operation-1400060483459-4f958fbc7d7b9-cd817778-b80d1cad",
"operationType": "insert",
"status": "DONE", 
"user": "some_name@developer.gserviceaccount.com",
"error": { 
   "errors": [ { 
      "code": "SERVICE_ACCOUNT_ACCESS_DENIED", 
      "message": "The user does not have access to service account 'some_name@developer.gserviceaccount.com'"
 } ] } }

这是我在 python 中的代码,用于设置实例:

discovery_service = discovery.build('compute',
                config['compute_api_version'],
                http=SignedJwtAssertionCredentials(
                  service_account_name="some_name@developer.gserviceaccount.com",
                  private_key=key_data,
                  scope='https://www.googleapis.com/auth/compute')
               .authorize(httplib2.Http()))

instance = {}
# sets instance configuration details here
# ...
# ...
instance['serviceAccounts'] = [{
  'email': "some_name@developer.gserviceaccount.com",
  'scopes': ['https://www.googleapis.com/auth/devstorage.full_control',
              'https://www.googleapis.com/auth/compute',
              'https://www.googleapis.com/auth/userinfo.email', ]
}]
discovery_service.instances().insert(project=project, zone=zone, body=instance)

其中最奇怪的部分是,异常显示“用户无权访问服务帐户 'some_name@developer.gserviceaccount.com'”,但它所指的“用户”是 'some_name@developer.gserviceaccount .com' 本身!这意味着“some_name@developer.gserviceaccount.com”无权访问“some_name@developer.gserviceaccount.com”,这没有任何意义。

【问题讨论】:

  • 你能解决这个问题吗?

标签: python google-compute-engine


【解决方案1】:

我相信您需要创建一个新的服务帐户才能使用来自非 GCE 实例的 API。您引用的服务帐户仅在 GCE 实例中有效。

  1. 为此,请转到 Cloud Console > 项目 > APIs & Auth > Credentials。
  2. 创建新的客户 ID
  3. 服务帐号
  4. 下载 .p12 文件并将其作为私钥加载。 (见下面的例子)

您还需要从引导磁盘创建一个实例,该磁盘通常是从GCE supplied images 之一创建的。

这是一个使用 JSON Web 令牌的示例,它对我有用。它改编自位于此处的文档:https://cloud.google.com/compute/docs/tutorials/python-guide#addinganinstance

from apiclient import discovery
from oauth2client.file import Storage
from oauth2client.client import SignedJwtAssertionCredentials
import httplib2
import os.path

INSTANCE_NAME = 'my-instance'
API_VERSION = 'v1'
GCE_URL = 'https://www.googleapis.com/compute/%s/projects/' % (API_VERSION)
PROJECT_ID = '***'
SERVICE_ACOUNT_CLIENT_ID = '***.apps.googleusercontent.com'
SERVICE_ACCOUNT_EMAIL_ADDRESS = '***@developer.gserviceaccount.com'
GCE_SCOPE = 'https://www.googleapis.com/auth/compute'
ZONE = 'us-central1-a'
DEFAULT_SERVICE_EMAIL = 'default'
DEFAULT_SCOPES = ['https://www.googleapis.com/auth/devstorage.full_control',
                  'https://www.googleapis.com/auth/compute']

SOURCE_IMAGE_URL = 'projects/ubuntu-os-cloud/global/images/ubuntu-1410-utopic-v20141217'


def main():
    f = file('private-key.p12', 'rb')
    oauth_key_data = f.read()
    f.close()

    http = httplib2.Http()
    oauth_storage = Storage('compute-creds.dat')
    oauth_credentials = oauth_storage.get()

    if oauth_credentials is None or oauth_credentials.invalid:
        oauth_credentials = SignedJwtAssertionCredentials(
                          service_account_name=SERVICE_ACCOUNT_EMAIL_ADDRESS,
                          private_key=oauth_key_data,
                          scope=GCE_SCOPE)
        oauth_storage.put(oauth_credentials)
    else:
        oauth_credentials.refresh(http)

    http = oauth_credentials.authorize(http)

    gce_service = discovery.build('compute', 'v1', http=http)



    project_url = '%s%s' % (GCE_URL, PROJECT_ID)
    image_url = '%s%s/global/images/%s' % (
             GCE_URL, 'ubuntu-os-cloud', 'ubuntu-1410-utopic-v20141217')
    machine_type_url = '%s/zones/%s/machineTypes/%s' % (
        project_url, ZONE, 'n1-standard-1')
    network_url = '%s/global/networks/%s' % (project_url, 'default')

    instance = {
        'name': INSTANCE_NAME,
        'machineType': machine_type_url,
        'disks': [{
            'autoDelete': 'true',
            'boot': 'true',
            'type': 'PERSISTENT',
            'initializeParams' : {
              'diskName': INSTANCE_NAME,
              'sourceImage': SOURCE_IMAGE_URL
            }
          }],
        'networkInterfaces': [{
          'accessConfigs': [{
            'type': 'ONE_TO_ONE_NAT',
            'name': 'External NAT'
           }],
          'network': network_url
        }],
        'serviceAccounts': [{
             'email': DEFAULT_SERVICE_EMAIL,
             'scopes': DEFAULT_SCOPES
        }]
      }
    # Create the instance
    request = gce_service.instances().insert(
       project=PROJECT_ID, body=instance, zone=ZONE)
    response = request.execute(http=http)
    response = _blocking_call(gce_service, http, response)

    print response

def _blocking_call(gce_service, auth_http, response):
    """Blocks until the operation status is done for the given operation."""

    status = response['status']
    while status != 'DONE' and response:
        operation_id = response['name']

        # Identify if this is a per-zone resource
        if 'zone' in response:
            zone_name = response['zone'].split('/')[-1]
            request = gce_service.zoneOperations().get(
                project=PROJECT_ID,
                operation=operation_id,
                zone=zone_name)
        else:
            request = gce_service.globalOperations().get(
                project=PROJECT_ID, operation=operation_id)

        response = request.execute(http=auth_http)
        if response:
            status = response['status']
    return response

main()

【讨论】:

  • 如果您使用 DEFAULT_SERVICE_EMAIL,那么您将在实例上获得的服务帐户将是默认项目帐户。这不是我想要的,因为我想获得我选择的服务帐户的实例 - 从概念上讲,你应该能够做到这一点,但这似乎不起作用。我会稍微改一下我最初的问题,以减少混乱。
【解决方案2】:

仅供参考:在 GCE 中,您通常会获得 两个 默认服务帐户:

  • -compute@developer.gserviceaccount.com
  • @cloudservices.gserviceaccount.com

注意不同的电子邮件后缀(developer.gserviceaccount.comcloudservices.gserviceaccount.com)。似乎使用您自己的服务帐户,即使它具有Owner 角色,也不会授予您访问<number>@cloudservices.gserviceaccount.com 帐户的权限,只能访问第一个 (<number>-compute@developer.gserviceaccount.com)。

在我的情况下,我在尝试使用我自己的服务帐户创建实例时遇到上述错误,同时指定该实例将使用上面的第二个服务帐户。一旦我修复了请求实例将使用第一个帐户的调用,它就起作用了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-28
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-13
    • 1970-01-01
    相关资源
    最近更新 更多