运行启动脚本

如需让实例在每次启动时执行自动化任务,您可以在虚拟机 (VM) 上创建并运行自己的启动脚本。启动脚本可以执行许多操作,例如安装软件、执行更新、开启服务以及脚本中定义的任何其他任务。您可以使用启动脚本以编程方式轻松地自定义虚拟机实例,包括在创建时自定义新实例。

例如,用于安装和启动 Apache 服务器的简单启动脚本可能如下所示:

#! /bin/bash
apt-get update
apt-get install -y apache2
cat <<EOF > /var/www/html/index.html
<html><body><h1>Hello World</h1>
<p>This page was created from a simple startup script!</p>
</body></html>

启动脚本是通过元数据服务器使用启动脚本元数据键指定的。您可以使用 gcloud 命令行工具、API 或 Google Cloud Console 来提供启动脚本。

准备工作

执行此任务所需的权限

如需执行此任务,您必须拥有以下权限

  • 创建新实例所需的所有权限
  • 针对实例的 compute.instances.setMetadata 权限

执行启动脚本

在网络可用后,实例始终以 root 身份执行启动脚本。

启动脚本可以是任何文件类型。如果存在启动脚本,则 Compute Engine 将执行以下操作:

  1. 将启动脚本复制到实例中的本地文件。
  2. 设置针对文件的权限,使其可以执行。
  3. 执行文件。

例如,您可以提供 Python 脚本而不是 bash 脚本。 请注意,无论是什么类型的脚本,Compute Engine 都会逐字运行脚本。

如需执行非 bash 脚本,请在文件顶部添加 shebang 行,告知操作系统要使用哪个解释器。例如,对于 Python 脚本,您可以添加如下的 shebang 行:

#! /usr/bin/python

使用本地启动脚本

本地启动脚本是位于您本地计算机上的脚本。如需使用本地启动脚本,请将本地启动脚本文件传递给实例,或者将启动脚本的内容直接提供给元数据服务器。 以下小节中的示例显示如何从本地文件添加或直接输入启动脚本元数据。

本地启动脚本受限于元数据值长度限制 (256 KB)。如果您的启动脚本超出此限制,您将无法在本地加载该脚本。请改为将文件保存到 Cloud Storage,并在实例创建期间指定脚本网址。如需了解详情,请参阅使用存储在 Cloud Storage 上的启动脚本

提供启动脚本文件

您只能通过使用 gcloud 命令行工具传递本地启动脚本文件。请提供 --metadata-from-file 标志,后跟元数据键值对 startup-script=PATH/TO/FILE,其中 PATH/TO/FILE 是启动脚本的相对路径。

gcloud compute instances create example-instance \
    --metadata-from-file startup-script=examples/scripts/install.sh

直接提供启动脚本内容

您还可以使用控制台、gcloud 命令行工具或 API 直接输入或粘贴启动脚本内容。

控制台

在 Cloud Console 中,可以直接在启动脚本部分指定启动脚本:

  1. 在 GCP Console 中,转到虚拟机实例页面。

    转到“虚拟机实例”页面

  2. 点击创建实例
  3. 创建新实例页面上,填写实例的属性。 对于高级的配置选项,展开管理、安全、磁盘、网络、单独租用部分。
  4. 自动化部分的启动脚本下,提供启动脚本的内容。

    在 Cloud Console 中设置启动脚本的屏幕截图

  5. 点击创建以创建实例。

gcloud

gcloud 命令行工具中,指定 --metadata 标志并使用 startup-script=[CONTENTS] 键值对来提供启动脚本的内容,其中 [CONTENTS] 是启动脚本的内容。

例如,以下命令创建的实例可以在启动时执行一些系统更新、安装 Apache 以及发布单个网页。您可以运行此命令,然后访问实例的外部 IP 地址,以查看 index.html 页面的内容。

在 Linux 桌面系统上,运行类似如下的命令:

gcloud compute instances create example-instance --tags http-server \
--metadata startup-script='#! /bin/bash
# Installs apache and a custom homepage
sudo su -
apt-get update
apt-get install -y apache2
cat <<EOF > /var/www/html/index.html
<html><body><h1>Hello World</h1>
<p>This page was created from a simple start up script!</p>
</body></html>
EOF'

如果您使用 Windows 桌面系统,则可以在 cmd 终端上运行此命令:

gcloud compute instances create example-instance --tags http-server --metadata startup-script="
apt-get update;
apt-get install -y apache2;
echo \"This page was created from a simple start up script!\" ^> /var/www/html/index.html"

同样,如果您使用 PowerShell,则可以使用 --% 标记来运行此命令,将命令原样传入 gcloud 工具:

gcloud --% compute instances create example-instance --tags http-server --metadata startup-script="
apt-get update;
apt-get install -y apache2;
echo \"This page was created from a simple start up script!\" ^> /var/www/html/index.html"

API

在 API 中,使用 startup-script 作为元数据键,在请求中提供启动脚本作为元数据属性的一部分。

POST https://compute.googleapis.com/compute/v1/projects/myproject/zones/us-central1-a/instances

{
  ...
  "metadata": {
    "items": [
      {
       "key": "startup-script",
       "value": "#! /bin/bash\n\n# Installs apache and a custom homepage\napt-get update\napt-get install -y apache2\ncat <<EOF > /var/www/html/index.html\n<html><body><h1>Hello World</h1>\n<p>This page was created from a simple start up script!</p>\n</body></html>"
      }
    ]
  }
  ...
}

为 Windows 实例提供启动脚本

可以使用唯一的 Windows 专用元数据键,在 Windows 实例上运行启动脚本。请从下列任何专用键中选择。 每个键都应该与您要运行的脚本类型匹配。您还可以通过向您的实例传入不同的键来指定多个脚本。对于每个实例,每个键只能指定一次。

如果您需要在虚拟机实例启动后在该虚拟机实例上手动运行启动脚本,您可以以管理员身份使用以下命令执行此操作:

  • C:\Program Files\Google\Compute Engine\metadata_scripts/run_startup_scripts.cmd

可以按照上面的相同说明,将以下键与本地启动脚本一起使用。

脚本类型 运行时间:
  • 在 GCESysprep2 期间
  • 启动前
运行时间:
  • GCESysprep2 完成后
  • 后续每次启动时
url 启动脚本 sysprep-specialize-script-url windows-startup-script-url
cmd 启动脚本 sysprep-specialize-script-cmd windows-startup-script-cmd
bat 启动脚本 sysprep-specialize-script-bat windows-startup-script-bat
ps11 启动脚本 sysprep-specialize-script-ps1 windows-startup-script-ps1

提供存储在 Cloud Storage 上的启动脚本

您可以将脚本存储在 Cloud Storage 上,并在创建实例时提供脚本网址。这样,您就可以从任何位置访问启动脚本,同时绕过元数据服务器限制。

设置脚本的访问权限

如需使用 Cloud Storage 中的启动脚本,您必须先拥有访问脚本的权限。检查存储分区和文件的访问权限控制设置,以确保您具有权限。

默认情况下,如果您是 Project Owner 或 Editor,则应该能够访问来自同一个项目的文件,除非有访问权限控制明确禁止您访问。

提供启动脚本

  1. 创建存储分区。您可以创建新存储分区或使用现有存储分区。如需了解如何创建新存储分区,请参阅在 Google Cloud Console 中创建存储分区gsutil 中创建存储分区
  2. 将文件上传至存储分区。按照相关说明,使用 gsutilCloud Console 上传对象。
  3. 创建新实例时,提供启动脚本文件的网址

    控制台

    1. 在 GCP Console 中,转到虚拟机实例页面。

      转到“虚拟机实例”页面

    2. 点击创建实例
    3. 创建新实例页面上,填写实例的属性。 对于高级的配置选项,展开管理、安全、磁盘、网络、单独租用部分。
    4. 元数据部分中,提供 startup-script-url 作为元数据键。
    5. 框中,提供启动脚本文件的网址,格式为 gs://[BUCKET]/[FILE]https://storage.googleapis.com/[BUCKET]/[FILE]
    6. 身份和 API 访问权限部分,选择有权读取 Cloud Storage 中的启动脚本文件的服务帐号。例如,服务帐号应该具有 Storage Object Viewer 角色的权限。
    7. 点击创建以创建实例。

    gcloud

    gcloud 命令行工具中,使用 --scopes--metadata 标志创建实例,并指定 startup-script-url 键。--scopes 标志允许虚拟机访问 Cloud Storage 以下载启动脚本。您可以采用 gs://bucket/filehttps://storage.googleapis.com/bucket/file 格式提供脚本的 Cloud Storage 网址。

    gcloud compute instances create example-instance --scopes storage-ro \
        --metadata startup-script-url=gs://bucket/startupscript.sh
    

    API

    在 API 中,使用 startup-script-url 作为元数据键,在请求中提供启动脚本作为元数据属性的一部分。此外,还需提供 scopes 列表(包括 Cloud Storage 范围),以便虚拟机可以访问启动脚本文件:

    POST https://compute.googleapis.com/compute/v1/projects/myproject/zones/us-central1-a/instances
    
    {
      ...
      "serviceAccounts": [
        {
          "email": "default",
          "scopes": [
            "https://www.googleapis.com/auth/devstorage.read_only"
          ]
        }
      ],
      "metadata": {
        "items": [
          {
           "key": "startup-script-url",
           "value": "gs://bucket/myfile"
          }
        ]
      },
      "tags": {
        "items": []
      },
      "machineType": "zones/us-central1-a/machineTypes/n1-standard-1",
      "name": "example-instance"
    }
    

    Windows

    gcloud 命令行工具中,使用 --scopes--metadata 标志创建实例,并指定 windows-startup-script-url 键。--scopes 标志允许虚拟机访问 Cloud Storage 以下载启动脚本。您可以采用 gs://bucket/filehttps://storage.googleapis.com/bucket/file 格式提供脚本的 Cloud Storage 网址。

    gcloud compute instances create example-windows-instance --scopes storage-ro \
        --metadata windows-startup-script-url=gs://bucket/startupscript.ps1
    

将启动脚本应用于正在运行的实例

如果您的实例正在运行,可向其添加启动脚本,当实例下次重启时,启动脚本将会运行。实例以后每次重新启动时,启动脚本都会运行。

按照以下说明在运行的实例上设置启动脚本。

控制台

  1. 转到虚拟机实例页面
  2. 点击您要为其添加启动脚本的实例。此时将显示实例详细信息页面。
  3. 在实例详细信息页面中,完成以下步骤:

    1. 点击页面顶部的修改按钮。
    2. 自定义元数据下,点击添加一项。
    3. 使用以下键之一添加您的启动脚本:

      • startup-script:使用此键直接提供启动脚本内容。
      • startup-script-url:使用此键提供启动脚本文件的 Cloud Storage 网址。

gcloud

gcloud 命令行工具中,使用 instances add-metadata 向实例添加元数据。您可使用以下任何可用的启动脚本键:

  • --metadata startup-script=CONTENTS:使用此键直接提供启动脚本内容。
  • --metadata startup-script-url=URL:使用此键提供启动脚本文件的 Google Cloud Storage 网址。
  • --metadata-from-file startup-script=FILE:提供本地存储的启动脚本文件。

例如:

gcloud compute instances add-metadata example-instance \
    --metadata-from-file startup-script=path/to/file
gcloud compute instances add-metadata example-instance \
    --metadata startup-script-url=gs://bucket/file

API

在 API 中,向 instances().setMetadata 方法发出请求,提供新的元数据和 fingerprint 值。

指纹是 Compute Engine 生成的一串随机字符,用于执行乐观锁定。请提供匹配的指纹值以执行您的请求。每次请求后指纹都会更改,因此如果您提供的指纹不匹配,请求将被拒绝。这样,一次只能进行一次更新,以免发生冲突。

如需获取实例的当前指纹,请执行 instances().get 请求并复制指纹值:

GET https://compute.googleapis.com/compute/v1/projects/myproject/zones/us-central1-a/instances/example-instance
{

 ...
 "name": "example-instance",
 "metadata": {
  "kind": "compute#metadata",
  "fingerprint": "zhma6O1w2l8="
 },
 "...
}

接下来,使用以下元数据键之一和指纹值向 instances().setMetadata 方法发出请求:

  • startup-script:使用此键直接提供启动脚本内容。
  • startup-script-url:使用此键提供启动脚本文件的 Cloud Storage 网址。

例如:

POST https://compute.googleapis.com/compute/v1/projects/myproject/zones/us-central1-a/instances/example-instance/setMetadata

    {
     "fingerprint": "zhma6O1w2l8=",
     "items": [
      {
       "key": "startup-script-url",
       "value": "gs://bucket/file"
      }
     ]
    }

重新运行启动脚本

您可以通过连接到实例并运行以下命令之一,在虚拟机实例上重新运行启动脚本:

在 Debian、CentOS、RHEL、SLES、Container-Optimized OS 和 Ubuntu 映像上,使用以下代码

sudo google_metadata_script_runner --script-type startup --debug

startup-script: INFO Starting startup scripts.
startup-script: INFO startup-script: Return code 0.
startup-script: INFO Finished running startup scripts.

在 Container-Optimized OS 上,您还可以使用 journalctl 命令查看启动脚本输出。

sudo journalctl -u google-startup-scripts.service

启动脚本输出会写入以下日志文件:

  • CentOS 和 RHEL:/var/log/messages
  • Debian:/var/log/daemon.log
  • Ubuntu:/var/log/syslog
  • SLES:/var/log/messages

在启动脚本中设置自定义值

跨实例运行启动脚本时,可能会出现需要在启动脚本中使用自定义值的情况。例如,您可能希望在不同的实例上运行启动脚本,并让每个实例输出自定义消息。

您可以在创建实例期间将这些自定义值指定为自定义元数据键值对,并在启动脚本中引用这些键值对。如需详细了解如何创建自定义元数据键,请参阅设置自定义元数据

设置自定义元数据键值对后,您可以修改启动脚本以查询新的元数据。例如,以上脚本可改为:

#! /bin/bash
VALUE_OF_FOO=$(curl http://metadata.google.internal/computeMetadata/v1/instance/attributes/foo -H "Metadata-Flavor: Google")
apt-get update
apt-get install -y apache2
cat <<EOF > /var/www/html/index.html
<html><body><h1>Hello World</h1>
<p>The value of foo: $VALUE_OF_FOO</p>
</body></html>
EOF

问题排查

与元数据服务器的网络连接

对于 Linux 虚拟机,Compute Engine 在尝试从元数据服务器获取信息(例如自定义启动或关闭脚本)之前,将等待连接到元数据服务器。如果元数据服务器无响应,或您尚未配置网络,则虚拟机将无法完成启动。

对于映像早于 v20160606 的 Linux 虚拟机,串行端口输出会显示 "Waiting for metadata server, attempt N",其中 N 是已尝试的次数。

由于一个暂时性的网络问题,此问题可能持续长达七分钟,但应该会自行解决。如果问题在七分钟后无法自行解决,请重新创建虚拟机实例。

后续步骤