在 Linux 虚拟机上使用启动脚本


启动脚本是一种文件,用于在虚拟机实例 (VM) 的启动过程中执行任务。启动脚本可以应用于项目中的所有虚拟机或单个虚拟机。虚拟机级层元数据指定的启动脚本会替换项目级层元数据指定的启动脚本,并且启动脚本仅在网络可用时运行。本文档介绍如何在 Linux 虚拟机实例上使用启动脚本。如需了解如何添加项目级层启动脚本,请参阅 gcloud compute project-info add-metadata

对于 Linux 启动脚本,您可以使用 bash 或非 bash 文件。如需使用非 bash 文件,请通过将 #! 添加到文件的顶部来指定解释器。例如,如需使用 Python 3 启动脚本,请将 #! /usr/bin/python3 添加到文件的顶部。

如果您使用本文档中的某个过程来指定启动脚本,Compute Engine 将执行以下操作:

  1. 将启动脚本复制到虚拟机

  2. 设置启动脚本的运行权限

  3. 在虚拟机启动时以 root 用户身份运行启动脚本

如需了解与启动脚本有关的各种任务以及何时执行各个任务,请参阅概览

准备工作

  • 阅读启动脚本概览
  • 了解元数据服务器
  • 如果您尚未设置身份验证,请进行设置。身份验证是通过其进行身份验证以访问 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. 安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

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

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

        安装 Google Cloud CLI,然后通过运行以下命令初始化 Google Cloud CLI:

        gcloud init

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

Linux 启动脚本的元数据键

启动脚本将从元数据键指定的位置传递给虚拟机。元数据键指定启动脚本是存储在本地、存储在 Cloud Storage 中,还是直接传递给虚拟机。使用的元数据键可能还取决于启动脚本的大小。

下表显示可用于 Linux 启动脚本的元数据键,并提供根据启动脚本的存储位置和大小要使用哪个键的相关信息。

元数据键 用于
startup-script 传递在本地存储或直接添加且大小最大为 256 KB 的 bash 或非 bash 启动脚本
startup-script-url 传递存储在 Cloud Storage 中且大小超过 256 KB 的 bash 或非 bash 启动脚本。您在此处输入的字符串会按原样用于运行 gsutil。如果您的 startup-script-url 包含空格字符,请不要将空格替换为 %20 或向 startup-script-url 字符串添加英文双引号 ("")。

Linux 启动脚本的执行顺序

您可以使用多个启动脚本。存储在本地或直接添加的启动脚本会在 Cloud Storage 中存储的启动脚本之前执行。下表根据元数据键显示 Linux 启动脚本的执行顺序。

元数据键 执行顺序
startup-script 初始启动后每次启动期间的第一次
startup-script-url 初始启动后每次启动期间的第二次

直接传递 Linux 启动脚本

您可以在创建虚拟机时直接将启动脚本的内容添加到虚拟机。以下过程演示如何使用安装了 Apache 和创建基本网页的启动脚本来创建虚拟机。

控制台

将 Linux 启动脚本直接传递到新虚拟机

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

    转到“创建实例”

  2. 对于启动磁盘,选择更改,然后选择 Linux 操作系统。

  3. 展开高级选项部分,然后执行以下操作:

    1. 展开管理部分。
    2. 自动化部分中,添加以下启动脚本:

       #! /bin/bash
       apt update
       apt -y install apache2
       cat <<EOF > /var/www/html/index.html
       <html><body><p>Linux startup script added directly.</p></body></html>
       EOF
      
  4. 点击创建

将 Linux 启动脚本直接传递到现有虚拟机

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

    转到虚拟机实例

  2. 点击虚拟机的名称

  3. 点击修改

  4. 自动化下,添加启动脚本的内容。

验证启动脚本

虚拟机启动后,在网络浏览器中查看外部 IP 以验证启动脚本是否创建了网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

gcloud

将 Linux 启动脚本直接传递到新虚拟机

在创建时使用以下 gcloud compute instances create 命令直接将启动脚本的内容直接传递到虚拟机。

gcloud compute instances create VM_NAME \
  --image-project=debian-cloud \
  --image-family=debian-10 \
  --metadata=startup-script='#! /bin/bash
  apt update
  apt -y install apache2
  cat <<EOF > /var/www/html/index.html
  <html><body><p>Linux startup script added directly.</p></body></html>
  EOF'

VM_NAME 替换为虚拟机名称。

将 Linux 启动脚本直接传递到现有虚拟机

使用以下 gcloud compute instances add-metadata 命令将启动脚本直接添加到现有虚拟机:

gcloud compute instances add-metadata VM_NAME \
    --zone=ZONE \
    --metadata=startup-script='#! /bin/bash
    apt update
    apt -y install apache2
    cat <<EOF > /var/www/html/index.html
    <html><body><p>Linux startup script added directly.</p></body></html>
    EOF'

请替换以下内容:

  • VM_NAME:虚拟机的名称

  • ZONE:虚拟机的可用区

验证启动脚本

虚拟机启动后,在网络浏览器中查看外部 IP 以验证启动脚本是否创建了网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

REST

将 Linux 启动脚本直接传递到新虚拟机

在创建时使用以下 instances.insert 方法直接将启动脚本的内容直接传递到虚拟机。

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

{
  ...
  "networkInterfaces": [
    {
      "accessConfigs": [
        {
          "type": "ONE_TO_ONE_NAT"
        }
      ]
    }
  ],
  "metadata": {
    "items": [
      {
        "key": "startup-script",
        "value": "#! /bin/bash\napt update\napt -y install apache2\ncat <<EOF > /var/www/html/index.html\n<html><body><p>Linux startup script added directly.</p></body></html>\nEOF"
      }
    ]
  },
  ...
}

请替换以下内容:

  • PROJECT_ID:项目 ID

  • ZONE:要在其中创建虚拟机的可用区

将 Linux 启动脚本直接传递到现有虚拟机

  1. 使用 instances.get 方法获取虚拟机的 tags.fingerprint 值。

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

    请替换以下内容:

    • PROJECT_ID:项目 ID

    • ZONE:虚拟机的可用区

    • VM_NAME:虚拟机所在的区域

  2. 在对 instances.setMetadata 方法 的调用中使用 fingerprint 值以及启动脚本的元数据键和值来传递启动脚本:

    POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/setMetadata
    
    {
      "fingerprint": FINGERPRINT,
      "items": [
        {
          "key": "startup-script",
          "value": "#! /bin/bash\napt update\napt -y install apache2\ncat <<EOF > /var/www/html/index.html\n<html><body><p>Linux startup script added directly.</p></body></html>\nEOF"
        }
      ],
      ...
    }
    

    请替换以下内容:

    • PROJECT_ID:项目 ID

    • ZONE:虚拟机的可用区

    • VM_NAME:虚拟机所在的区域

    • FINGERPRINT:使用 instances.get 方法获取的 tags.fingerprint

验证启动脚本

虚拟机启动后,在网络浏览器中查看外部 IP 以验证启动脚本是否创建了网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

从本地文件传递 Linux 启动脚本

您可以将启动脚本存储在工作站上的本地文件中,并在创建时将本地文件作为元数据传递到虚拟机。您不能将虚拟机上存储的文件用作启动脚本。

在将 Linux 启动脚本从本地文件传递到虚拟机之前,请执行以下操作:

  1. 创建本地文件以存储启动脚本。

  2. 请注意从 gcloud CLI 到启动脚本的相对路径。

  3. 将以下启动脚本添加到文件:

    #! /bin/bash
    apt update
    apt -y install apache2
    cat <<EOF > /var/www/html/index.html
    <html><body><p>Linux startup script from a local file.</p></body></html>
    EOF
    

gcloud

将 Linux 启动脚本从本地文件传递到新虚拟机

使用带有 --metadata-from-file 标志的 gcloud compute instances create 命令创建一个虚拟机,并传递要用作启动脚本的本地文件的内容。

gcloud compute instances create VM_NAME \
  --image-project=debian-cloud \
  --image-family=debian-10 \
  --metadata-from-file=startup-script=FILE_PATH

请替换以下内容:

  • VM_NAME:虚拟机的名称

  • FILE_PATH:启动脚本文件的相对路径

将 Linux 启动脚本从本地文件传递到现有虚拟机

使用以下 gcloud compute instances add-metadata 命令,将本地文件从本地文件传递到现有虚拟机:

gcloud compute instances add-metadata VM_NAME \
  --zone=ZONE \
  --metadata-from-file startup-script=FILE_PATH

请替换以下内容:

  • VM_NAME:虚拟机的名称

  • ZONE:虚拟机的可用区

  • FILE_PATH:启动脚本文件的相对路径

验证启动脚本

在网络浏览器中查看外部 IP 以验证启动脚本是否已创建网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

从 Cloud Storage 传递 Linux 启动脚本

您可以将启动脚本存储在 Cloud Storage 中,并在创建脚本时将其传递到虚拟机。将启动脚本添加到 Cloud Storage 后,您将获得一个网址,用于在创建虚拟机时引用启动脚本。

在从 Cloud Storage 存储桶添加启动脚本之前,请执行以下操作:

  1. 创建文件以存储启动脚本。本示例使用 bash (.sh) 文件。

  2. 将以下内容添加到 bash 文件中,该文件将安装 Apache 并创建一个简单的网页:

    #! /bin/bash
    apt update
    apt -y install apache2
    cat <<EOF > /var/www/html/index.html
    <html><body><p>Linux startup script from Cloud Storage.</p></body></html>
    EOF
    
  3. 创建 Cloud Storage 存储桶

  4. 将文件添加到 Cloud Storage 存储桶

安全注意事项

  • 默认情况下,项目所有者和项目编辑者可以访问同一项目中的 Cloud Storage 文件,除非有显式访问权限控制禁止这么做。

  • 如果 Cloud Storage 存储桶或对象的安全性低于元数据,则在修改启动脚本和虚拟机重新启动时,存在提权风险。这是因为虚拟机重新启动后,启动脚本会作为 root 运行,然后可以使用关联服务账号的权限访问其他资源。

限制

控制台

将存储在 Cloud Storage 中的启动脚本传递到新虚拟机

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

    转到“创建实例”

  2. 指定虚拟机详情。

  3. 对于启动磁盘,选择更改,然后选择 Linux 操作系统。

  4. 身份和 API 访问权限部分,选择具有 Storage Object Viewer 角色 (roles/storage.objectViewer) 的服务账号。

  5. 展开高级选项部分,然后执行以下操作:

    1. 展开管理部分。
    2. 元数据部分中,添加以下内容的值:

      • :元数据键。设置为 startup-script-url 以从 Cloud Storage 添加启动脚本。

      • :元数据值。使用以下格式之一设置启动脚本文件的 Cloud Storage 位置:

        • 要求验证身份的网址https://storage.googleapis.com/BUCKET/FILE
        • gsutil URIgs://BUCKET/FILE

        请替换以下内容:

        • BUCKET:包含启动脚本文件的存储桶的名称
        • FILE:启动脚本文件的名称
  6. 如需创建虚拟机,请点击创建

将存储在 Cloud Storage 中的启动脚本传递到现有虚拟机

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

    转到虚拟机实例

  2. 点击虚拟机的名称

  3. 点击修改

  4. 元数据下,添加以下值:

    • startup-script-url

    • :启动脚本文件的 Cloud Storage 位置,其中使用以下格式之一:

      • 要求验证身份的网址https://storage.googleapis.com/BUCKET/FILE
      • gsutil URIgs://BUCKET/FILE

验证启动脚本

在网络浏览器中查看外部 IP 以验证启动脚本是否已创建网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

gcloud

将存储在 Cloud Storage 中的启动脚本传递到新虚拟机

在创建时使用以下 gcloud compute instances create 命令将 Cloud Storage 中存储的启动脚本传递到虚拟机。对于 --scope 标志的值,使用 storage-ro,以便虚拟机可以访问 Cloud Storage。

gcloud compute instances create VM_NAME \
  --image-project=debian-cloud \
  --image-family=debian-10 \
  --scopes=storage-ro \
  --metadata=startup-script-url=CLOUD_STORAGE_URL

请替换以下内容:

  • VM_NAME:虚拟机的名称。

  • CLOUD_STORAGE_URL:元数据值。使用以下格式之一设置启动脚本文件的 Cloud Storage 位置:

    • 要求验证身份的网址https://storage.googleapis.com/BUCKET/FILE
    • gsutil URIgs://BUCKET/FILE

将存储在 Cloud Storage 中的启动脚本传递到现有虚拟机

使用以下 gcloud compute instances add-metadata 命令将存储在 Cloud Storage 中的启动脚本传递到现有虚拟机:

gcloud compute instances add-metadata VM_NAME \
    --zone=ZONE \
    --metadata startup-script-url=CLOUD_STORAGE_URL

请替换以下内容:

  • VM_NAME:虚拟机的名称。

  • ZONE:虚拟机的可用区。

  • CLOUD_STORAGE_URL:元数据值。使用以下格式之一设置启动脚本文件的 Cloud Storage 位置:

    • 要求验证身份的网址https://storage.googleapis.com/BUCKET/FILE
    • gsutil URIgs://BUCKET/FILE

验证启动脚本

在网络浏览器中查看外部 IP 以验证启动脚本是否已创建网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

REST

将存储在 Cloud Storage 中的启动脚本传递到新虚拟机

在创建时使用以下 instances.insert 方法将 Cloud Storage 中存储的启动脚本传递到虚拟机。在 scopes 字段中,添加 https://www.googleapis.com/auth/devstorage.read_only,以便虚拟机可以访问 Cloud Storage。

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

{
  ...
  "networkInterfaces": [
    {
      "accessConfigs": [
        {
          "type": "ONE_TO_ONE_NAT"
        }
      ]
    }
  ],
  "serviceAccounts": [
    {
      "email": "default",
      "scopes": [
        "https://www.googleapis.com/auth/devstorage.read_only"
      ]
    }
  ],
  "metadata": {
    "items": [
      {
        "key": "startup-script-url",
        "value": "CLOUD_STORAGE_URL"
      }
    ]
  },
  ...
}

请替换以下内容:

  • PROJECT_ID:项目 ID。

  • ZONE:要在其中创建新虚拟机的可用区。

  • CLOUD_STORAGE_URL:元数据值。使用以下格式之一设置启动脚本文件的 Cloud Storage 位置:

    • 要求验证身份的网址https://storage.googleapis.com/BUCKET/FILE
    • gsutil URIgs://BUCKET/FILE

将存储在 Cloud Storage 中的启动脚本传递到现有虚拟机

  1. 使用 instances.get 方法获取虚拟机的 tags.fingerprint 值。

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

    请替换以下内容:

    • PROJECT_ID:项目 ID

    • ZONE:虚拟机的可用区

    • VM_NAME:虚拟机所在的区域

  2. 在对 instances.setMetadata 方法 的调用中使用 fingerprint 值以及启动脚本的元数据键和值来传递启动脚本:

    POST https://compute.googleapis.com/compute/v1/projects/PROJECT_ID/zones/ZONE/instances/VM_NAME/setMetadata
    
    {
      "fingerprint": FINGERPRINT,
      "items": [
        {
            "key": "startup-script-url",
            "value": "CLOUD_STORAGE_URL"
        }
      ],
      ...
    }
    

    请替换以下内容:

    • PROJECT_ID:项目 ID。

    • ZONE:虚拟机的可用区。

    • VM_NAME:虚拟机的可用区。

    • FINGERPRINT:使用 instances.get 方法获取的 tags.fingerprint 值。

    • CLOUD_STORAGE_URL:元数据值。使用以下格式之一设置为启动脚本文件的 Cloud Storage 位置:

      • 要求验证身份的网址https://storage.googleapis.com/BUCKET/FILE
      • gsutil URIgs://BUCKET/FILE

验证启动脚本

在网络浏览器中查看外部 IP 以验证启动脚本是否已创建网站。您可能需要等待大约 1 分钟来完成示例启动脚本。

从 Linux 启动脚本访问元数据

在启动脚本中,您可以访问元数据值。例如,您可以将同一个脚本用于多个虚拟机,并通过将不同的元数据值传递到每个虚拟机来单独对每个脚本进行参数化。

如需从启动脚本访问自定义元数据值,请执行以下操作:

  1. 创建用于查询元数据键值的启动脚本。例如,以下 bash 文件 (.sh) 启动脚本会查询 foo 元数据键的值。

    #! /bin/bash
    METADATA_VALUE=$(curl http://metadata.google.internal/computeMetadata/v1/instance/attributes/foo -H "Metadata-Flavor: Google")
    apt update
    apt -y install apache2
    cat <<EOF > /var/www/html/index.html
    <html><body><p>Accessing metadata value of foo: $METADATA_VALUE</p></body></html>
    EOF
    
  2. 在创建虚拟机时使用以下 gcloud compute instances create 命令设置 foo 元数据键的值。在本示例中,将启动脚本从本地文件传递到虚拟机。

    gcloud

    gcloud compute instances create VM_NAME \
      --image-project=debian-cloud \
      --image-family=debian-10 \
      --metadata-from-file=startup-script=FILE_PATH \
      --metadata=foo=bar
    

    请替换以下内容:

    • VM_NAME:虚拟机的名称

    • FILE_PATH:启动脚本文件的相对路径

    如需详细了解如何指定元数据键值对,请参阅设置自定义元数据

  3. 在本地工作站中,在网络浏览器中查看外部 IP,以验证启动脚本是否输出 foo 的值。您可能需要等待大约 1 分钟来完成示例启动脚本。

重新运行 Linux 启动脚本

通过执行以下操作,重新运行启动脚本:

  1. 连接到虚拟机

  2. 运行以下命令:

    sudo google_metadata_script_runner startup
    

查看 Linux 启动脚本的输出

您可以通过执行以下任一操作来查看 Linux 启动脚本的输出:

后续步骤