配置虚拟机启动例程

使用 Google Distributed Cloud 1.13.0 版及更高版本时,您可以指定启动例程以自定义启动时虚拟机的初始化。您可以配置虚拟机以创建 SSH 密钥、添加用户和密码、安装软件包、写入文件、配置网络设置等。

这些启动任务使用 cloud-init API 或启动脚本 API(但不能同时使用两者)进行配置。这些启动指令在 VirtualMachine YAML 清单文件中指定,并在每次虚拟机启动时自动执行。

前提条件

如需使用启动指令配置虚拟机,您必须满足以下前提条件:

以下部分介绍如何使用 cloud-init API启动脚本在虚拟机清单中指定启动例程。

使用 cloud-init API 初始化虚拟机

Cloud-init 通常用于云实例初始化以及在启动期间自定义虚拟机。虚拟机初始化通常涉及软件包安装、代码库设置、SSH 密钥创建、将数据写入文件以及设置虚拟机其他方面等任务。通过 spec.cloudInit 字段将 cloud-init 配置 YAML 整合到 VirtualMachine 自定义资源中。当虚拟机实例启动时,cloud-init 会读取提供的数据并相应地初始化虚拟机。

请注意我们的 cloud-init 实现的以下详细信息:

  • 您可以在创建或更新虚拟机时,在 VirtualMachine YAML 清单中指定 cloud-init 数据。如需了解如何通过应用清单来创建虚拟机,请参阅教程:在 VM Runtime on GDC 中创建和管理 Linux 虚拟机

  • 我们在虚拟机规范中使用 NoCloud 数据源 spec.cloudInit.noCloud

  • 您可以在 VirtualMachine 清单的单独部分中指定用户数据网络数据。部分命名和结构取决于您决定使用的数据格式。

  • 您可以使用以下数据格式指定 cloud-init 配置信息:

    • 清除文字
    • Base64 编码的字符串
    • Kubernetes Secret

为了帮助您开始使用,我们提供了常见虚拟机初始化任务的一些配置示例

Cloud-init 用户数据

VM Runtime on GDC 支持采用 cloud-config 语法的 cloud-init 用户数据,因此用户数据应以 #cloud-config 开头。您可以将用户数据的格式设置为明文、base64 编码的字符串或 Kubernetes Secret。

如需详细了解用户数据语法和模块参考,请参阅 cloud-init 文档

将 Cloud-init 用户数据指定为明文

以下示例清单显示如何将用户数据指定为明文。在此示例中,在虚拟机启动时,cloud-init 会执行命令:

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userData: |
        #cloud-config
        runcmd:
          - echo hello

将 Cloud-init 用户数据指定为 base64 编码的字符串

下面的示例展示了如何采用 base64 编码格式指定用户数据。在此示例中,用户数据由与明文示例相同的 echo hello 命令组成:

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userDataBase64: I2Nsb3VkLWNvbmZpZwpydW5jbWQ6CiAgLSBlY2hvIGhlbGxvCg==

将 Cloud-init 用户数据指定为 Kubernetes Secret

以下示例展示了 VirtualMachineSecret 的 YAML 清单。VirtualMachine 配置中的 spec.cloudInit.noCloud.secretRef 部分表示 cloud-init 用户数据位于名为 my-sec 的 Kubernetes Secret 中。相应的 Secret 配置以键值对的形式指定用户数据。在这种情况下,base64 编码的值是采用 cloud-config 语法的 cloud-init 用户数据。

在引用的 Secret 中,使用数据键 userData(显示)或 userdata 来指定 cloud-init 用户数据。

在此示例中,用户数据由与明文示例相同的 echo hello 命令组成:

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      secretRef:
        name: my-sec
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-sec
data:
  userData: I2Nsb3VkLWNvbmZpZwpydW5jbWQ6CiAgLSBlY2hvIGhlbGxvCg==

如果找不到引用的 Secret 或者 Secret 中不存在数据键 userDatauserdata,请注意以下虚拟机启动行为:

  • 为了创建虚拟机,系统会将虚拟机置于 ErrorConfiguration 状态,并提供详细原因和消息。

  • 在其他情况下,虚拟机会继续使用旧的 cloud-init 用户数据,直到正确配置虚拟机。因此,客户机代理启用或停用更新在正确配置虚拟机后才会生效。

如需检索虚拟机信息(包括所使用的 cloud-init 用户数据),请使用以下命令:

kubectl get vm VM_NAME -o yaml --kubeconfig KUBECONFIG_PATH

请替换以下内容:

  • VM_NAME:您的虚拟机的名称。

  • KUBECONFIG_PATH:包含虚拟机的集群的 kubeconfig 文件路径。

如需检索相关的 Kubernetes 警告事件,请使用 kubectl get eventkubectl describe gvm

Cloud-init 网络数据

与用户数据类似,您可以将网络数据的格式设置为明文、base64 编码的字符串或 Kubernetes Secret。与用户数据不同,网络数据不使用 cloud-config 语法。

使用明文或 base64 编码的字符串时,允许的大小上限为 2048 字节。如果用户数据大小接近或大于 2048 字节,请将其指定为 Kubernetes Secret。

如需详细了解网络数据语法和相关详情,请参阅 cloud-init 文档中的 Networking Config 版本 2

将 Cloud-init 网络数据指定为明文

以下示例清单显示如何将网络数据指定为明文。在此示例中,cloud-init 为名称以“e”(e*) 开头的所有以太网设备启用 DHCP:

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userData: |
        #cloud-config
        runcmd:
          - echo hello
      networkData: |
        version: 2
        ethernets:
          alleths:
            match:
              name: e*
            dhcp4: true

将 Cloud-init 网络数据指定为 base64 编码的字符串

下面的示例展示了如何采用 base64 编码格式指定网络数据。在此示例中,网络数据由明文示例中指定的相同 DHCP 配置组成:

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      networkDataBase64: dmVyc2lvbjogMgpldGhlcm5ldHM6CiAgYWxsZXRoczoKICAgIG1hdGNoOgogICAgICBuYW1lOiBlKgogICAgZGhjcDQ6IHRydWUK

将 Cloud-init 网络数据指定为 Kubernetes Secret

以下示例展示了 VirtualMachineSecret 的 YAML 清单。VirtualMachine 配置中的 spec.cloudInit.noCloud.networkDataSecretRef 部分表示 cloud-init 网络数据位于名为 my-sec 的 Kubernetes Secret 中。相应的 Secret 配置以键值对的形式指定网络数据。在这种情况下,base64 编码的值是 cloud-init 网络数据。

在引用的 Secret 中,使用数据键 networkData(显示)或 networkdata 来指定 cloud-init 网络数据。

在此示例中,网络数据由明文示例中指定的相同 DHCP 配置组成:

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      networkDataSecretRef:
        name: my-sec
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-sec
data:
  networkData: dmVyc2lvbjogMgpldGhlcm5ldHM6CiAgYWxsZXRoczoKICAgIG1hdGNoOgogICAgICBuYW1lOiBlKgogICAgZGhjcDQ6IHRydWUK

Cloud-init 示例

以下部分包含使用 cloud-init 进行虚拟机初始化的一些常见用例的明文示例:

配置已获授权的 SSH 密钥

以下用户数据示例将已获授权的 SSH 密钥 ssh-rsa AAAAB3NzaK8L93bWxnyp 分配给默认用户。

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userData: |
        #cloud-config
        ssh_authorized_keys:
          - ssh-rsa AAAAB3NzaK8L93bWxnyp

添加新用户

以下用户数据示例创建一个用户 test,并为 test 授予完整的 sudo 访问权限。此示例为用户分配未到期的密码 pwd

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userData: |
        #cloud-config
        users:
        - default
        - name: test
          sudo: ALL=(ALL) NOPASSWD:ALL
        chpasswd:
          list: |
            test:pwd
          expire: False

在第一次启动时运行命令

以下用户数据示例运行 echo 命令和 ls 命令。您可以在虚拟机启动时使用这些命令来安装软件包等。

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userData: |
        #cloud-config
        runcmd:
          - [ echo, hello ]
          - [ ls, -l, / ]

写入文件

以下用户数据示例将 bash 脚本写入虚拟机 /var/lib/google 目录中的 test 文件。cloud-init 指令设置文件所有者的读取、写入和执行文件权限 (0744)。

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  cloudInit:
    noCloud:
      userData: |
        #cloud-config
        write_files:
        - path: /var/lib/google/test
          permissions: 0744
          content: |
            #!/bin/bash
            echo hello

排查 cloud-init 问题

如果您的虚拟机初始化出现问题并且您使用的是 cloud-init,请在虚拟机中检查以下 cloud-init 日志:

  • /var/log/cloud-init.log:默认情况下,cloud-init 会将级别为 DEBUG 或更高级别的所有事件写入此日志。

  • /var/log/cloud-init-output.log:默认情况下,cloud-init 会将所有 cloud-init 阶段中的 stdout 和 stderr 定向到此日志。

使用启动脚本初始化虚拟机

启动脚本用于在虚拟机 (VM) 实例的启动过程中执行任务。您可以在 VirtualMachine 规范的 spec.startupScripts 部分中指定一个或多个脚本。启动脚本可用于初始化您的虚拟机。虚拟机初始化通常涉及软件包安装、代码库设置、SSH 密钥创建、将数据写入文件以及设置虚拟机其他方面等任务。

请注意启动脚本的以下详细信息:

  • 您可以在创建或更新虚拟机时,在 VirtualMachine YAML 清单中指定启动脚本。如需了解如何通过应用清单来创建虚拟机,请参阅教程:在 VM Runtime on GDC 中创建和管理 Linux 虚拟机

  • 每次虚拟机启动时,指定的脚本都会运行。

  • 在脚本顶部添加 #!/bin/...,以指示脚本解释器。例如,添加 #!/bin/bash 以使用 Bash shell 执行脚本。

  • 您不能在同一个 VirtualMachine 清单中同时指定 cloud-init API 指令 (spec.cloudInit) 和启动脚本 (spec.startupScripts)。

脚本格式

您可以使用以下数据格式指定启动脚本:

  • 清除文字
  • Base64 编码的字符串
  • Kubernetes Secret

请注意使用不同脚本格式的以下规则:

  • 使用明文或 base64 编码的字符串时,脚本内容的大小上限为 2048 字节。如果脚本内容大小接近或大于 2048 字节,请将脚本指定为 Kubernetes Secret。

  • 使用 Kubernetes Secret 时,请使用引用的 Secret 中的数据键 script 来指定脚本内容。

  • 如果未找到引用的 Secret 或引用的 Secret 中不存在数据键 script,则虚拟机会继续运行该脚本。但是,虚拟机不会写入或更新脚本内容。在这种情况下,您可以使用 kubectl get eventkubectl describe gvm 来查找 Kubernetes 警告事件。

以下示例 VirtualMachine YAML 清单包含三个脚本,每种受支持的格式各一个。在此示例中,每个脚本都会运行 myscript1(明文示例)中显示的 echo hello 命令。

apiVersion: vm.cluster.gke.io/v1
kind: VirtualMachine
metadata:
  name: "my-vm"
spec:
  ...
  startupScripts:
  - name: myscript1
    script: |
      #!/bin/bash
      echo hello
  - name: myscript2
    scriptBase64: IyEvYmluL2Jhc2gKICAgICAgZWNobyBoZWxsbwo=
  - name: myscript3
    scriptSecretRef:
      name: my-sec
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: my-sec
data:
  script: IyEvYmluL2Jhc2gKICAgICAgZWNobyBoZWxsbwo=

脚本问题排查

如需检查脚本结果或日志,请运行以下命令:

journalctl -u cloud-final

启动脚本日志条目以以下文本开头:

started to run the command /var/lib/google/startup-scripts/SCRIPT_NAME ...

日志条目包含 SCRIPT_NAME(启动脚本的名称)。