启动时在 Linux 实例上运行命令 - Amazon Elastic Compute Cloud
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

启动时在 Linux 实例上运行命令

当您在 Amazon EC2 中启动实例时,您可以选择将用户数据传递到可用于执行常见自动配置任务甚至在实例启动后运行脚本的实例。您可以将两类用户数据传递到 Amazon EC2:Shell 脚本和 cloud-init 指令。您还可以将此数据以纯文本、文件(这非常适合通过命令行工具启动实例)或者 base64 编码文本(用于 API 调用)的形式传递到启动实例向导中。

如果您对更复杂的自动化方案感兴趣,可以考虑使用 Amazon CloudFormation 或 Amazon OpsWorks。有关更多信息,请参阅下列内容:

有关在启动时在 Windows 实例上运行命令的信息,请参阅 Amazon EC2 用户指南(适用于 Windows 实例) 中的启动时在您的 Windows 实例上运行命令管理 Windows 实例配置

在以下示例中,在 Amazon Linux 2 上安装 LAMP Web 服务器中的命令转换成了 Shell 脚本和一组在实例启动时运行的 cloud-init 指令。在每个示例中,以下任务都根据用户数据执行:

  • 更新发行版软件包。

  • 安装必要的 Web 服务器、phpmariadb 程序包。

  • 通过 httpd 启动和打开 systemctl 服务。

  • ec2-user 将添加到 apache 组。

  • 为 Web 目录以及其中的文件设置适当的所有权和文件权限。

  • 创建简单网页来测试 Web 服务器和 PHP 引擎。

前提条件

本主题中的示例假定以下内容:

  • 您的实例具有可从互联网访问的公用 DNS 名称。有关更多信息,请参阅 Network settings (网络设置) 部分和 创建安全组 中的自动分配公有 IP

  • 与您的实例关联的安全组将被配置为允许 SSH(端口 22)流量,以便您可以连接到该实例,以查看输出日志文件。有关更多信息,请参阅创建安全组

  • 您的实例将使用 Amazon Linux 2 AMI 启动。这些指令适合与 Amazon Linux 2 一起使用,这些命令和指令可能不适用于其他 Linux 发行版。有关其他发行版的更多信息,如它们对 cloud-init 的支持,请参阅各自的具体文档。

用户数据和 Shell 脚本

如果您熟悉 Shell 脚本编写,要在启动时将指令发送到实例,这是最简单、最完整的方式。在启动时添加这些任务会增加启动实例所需的时间。您应多等待几分钟让这些任务完成,然后测试用户脚本是否已成功完成。

重要

默认情况下,用户数据脚本和 cloud-init 指令仅在首次启动实例时在引导周期内运行。您可以更新您的配置,以确保您的用户数据脚本和 cloud-init 指令在每次重新启动实例时都会运行。有关更多信息,请参阅Amazon知识中心中的如何利用用户数据在每次重新启动 Amazon EC2 Linux 实例时自动运行脚本?

用户数据 Shell 脚本必须以 #! 字符以及要读取脚本的解释器的路径(通常为 /bin/bash))开始。有关 Shell 脚本的精彩介绍,请参阅 Linux 文档项目 (tldp.org) 中的 BASH 编程操作方法

作为用户数据输入的脚本是作为根用户加以运行的,因此在脚本中不使用 sudo 命令。请注意,您创建的任何文件都将归根用户所有;如果您需要非根用户具有文件访问权限,应在脚本中相应地修改权限。此外,这是因为脚本不交互运行,所以无法包含要求用户反馈的命令(如 yum update,无 -y 标志)。

如果您使用 Amazon API(包括 Amazon CLI),则在启动实例时必须使用实例配置文件。实例配置文件提供用户数据脚本发出 API 调用所需的适当 Amazon 凭证。有关更多信息,请参阅 IAM 用户指南 中的使用实例配置文件。您分配给 IAM 角色的权限取决于您使用 API 调用的服务。有关更多信息,请参阅 Amazon EC2 的 IAM 角色

cloud-init 输出日志文件捕获控制台输出,因此,如果实例的行为不符合您的预期,可在启动后方便地调试脚本。要查看日志文件,请连接到实例并打开 /var/log/cloud-init-output.log

在处理用户数据脚本时,该脚本将复制到 /var/lib/cloud/instances/instance-id/ 目录并从该目录运行。脚本在运行后无法删除。请在从实例创建 AMI 之前务必删除 /var/lib/cloud/instances/instance-id/ 中的用户数据脚本。否则,该脚本将存在于从 AMI 启动的任何实例上的此目录中。

用户数据和控制台

您可在启动实例时指定实例用户数据。如果实例的根卷是 EBS 卷,您还可以停止实例并更新其用户数据。

启动时指定实例用户数据

按照启动实例的程序进行操作。User data(用户数据)字段位于启动实例向导的 高级详细信息 部分。在 User data(用户数据)字段中输入您的 Shell 脚本,然后完成实例启动程序。

在下面的示例脚本中,脚本将创建并配置我们的 Web 服务器。

#!/bin/bash yum update -y amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2 yum install -y httpd mariadb-server systemctl start httpd systemctl enable httpd usermod -a -G apache ec2-user chown -R ec2-user:apache /var/www chmod 2775 /var/www find /var/www -type d -exec chmod 2775 {} \; find /var/www -type f -exec chmod 0664 {} \; echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php

让实例有足够的时间启动和执行脚本中的命令,然后查看脚本是否完成了预期的任务。

对于我们的示例,在 Web 浏览器中输入脚本创建的 PHP 测试文件的 URL。此 URL 是实例的公用 DNS 地址,后接正斜杠和文件名。

http://my.public.dns.amazonaws.com/phpinfo.php

您应该会看到 PHP 信息页面。如果您无法看到 PHP 信息页,请检查所用的安全组是否包含允许 HTTP (端口 80) 通信的规则。有关更多信息,请参阅向安全组添加规则

(可选)如果您的脚本没有完成您期望的任务,或者如果您只是想验证您的脚本是否已完成且没有错误,请连接到实例,检查 cloud-init 输出日志文件 (/var/log/cloud-init-output.log),然后查找输出中的错误消息。

对于其他调试信息,您可以使用以下指令创建包含 cloud-init 数据部分的 Mime 分段存档:

output : { all : '| tee -a /var/log/cloud-init-output.log' }

此指令将您脚本的命令输出发送到 /var/log/cloud-init-output.log。有关 cloud-init 数据格式以及创建 Mime 分段存档的更多信息,请参阅 cloud-init 格式

查看和更新实例用户数据

要更新实例用户数据,您必须先停止实例。如果实例正在运行,那么您可以查看用户数据,但不能进行修改。

警告

当您停止某个实例时,任何实例存储卷上的数据都将被擦除。要保留实例存储卷中的数据,请确保将其备份到持久性存储中。

修改实例用户数据
  1. 通过以下网址打开 Amazon EC2 控制台:https://console.aws.amazon.com/ec2/

  2. 在导航窗格中,选择实例

  3. 选择所需实例,然后依次选择实例状态停止实例。如果此选项处于禁用状态,则表示实例已停止,或者其根设备是实例存储卷。

  4. 当系统提示您确认时,选择 Stop。停止实例可能需要几分钟时间。

  5. 在实例仍被选中的情况下,依次选择操作实例设置编辑用户数据

  6. 根据需要修改用户数据,然后选择保存

  7. 开启实例。开启实例后,新的用户数据将在实例上可见;但不会运行用户数据脚本。

用户数据和 cloud-init 指令

cloud-init 程序包在启动时配置新 Amazon Linux 实例的特定方面;最值得注意的是,它为 ec2-user 配置 .ssh/authorized_keys 文件,以便您使用自己的私有密钥登录。有关 cloud-init 软件包为 Amazon Linux 实例执行的配置任务的更多信息,请参阅 cloud-init

可在启动时将 cloud-init 用户指令传递给实例,方式与传递脚本相同,只是语法不同。有关 cloud-init 的更多信息,请参阅 http://cloudinit.readthedocs.org/en/latest/index.html

重要

默认情况下,用户数据脚本和 cloud-init 指令仅在首次启动实例时在引导周期内运行。您可以更新您的配置,以确保您的用户数据脚本和 cloud-init 指令在每次重新启动实例时都会运行。有关更多信息,请参阅Amazon知识中心中的如何利用用户数据在每次重新启动 Amazon EC2 Linux 实例时自动运行脚本?

在启动时添加这些任务会增加启动实例所需的时间。您应多等待几分钟让这些任务完成,然后测试用户数据指令是否已完成。

使用用户数据将 cloud-init 指令传递给实例
  1. 按照启动实例的程序进行操作。User data(用户数据)字段位于启动实例向导的 高级详细信息 部分。在 User data(用户数据)字段中输入您的 cloud-init 指令文本,然后完成实例启动程序。

    在以下示例中,这些指令在 Amazon Linux 2 上创建并配置 Web 服务器。要将命令标识为 cloud-init 指令,顶部的 #cloud-config 行是必需的。

    #cloud-config repo_update: true repo_upgrade: all packages: - httpd - mariadb-server runcmd: - [ sh, -c, "amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2" ] - systemctl start httpd - sudo systemctl enable httpd - [ sh, -c, "usermod -a -G apache ec2-user" ] - [ sh, -c, "chown -R ec2-user:apache /var/www" ] - chmod 2775 /var/www - [ find, /var/www, -type, d, -exec, chmod, 2775, {}, \; ] - [ find, /var/www, -type, f, -exec, chmod, 0664, {}, \; ] - [ sh, -c, 'echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php' ]
  2. 让实例有足够的时间启动和运行用户数据中的指令,然后查看指令是否完成了预期的任务。

    对于此示例,在 Web 浏览器中输入指令创建的 PHP 测试文件的 URL。此 URL 是实例的公用 DNS 地址,后接正斜杠和文件名。

    http://my.public.dns.amazonaws.com/phpinfo.php

    您应该会看到 PHP 信息页面。如果您无法看到 PHP 信息页,请检查所用的安全组是否包含允许 HTTP (端口 80) 通信的规则。有关更多信息,请参阅向安全组添加规则

  3. (可选)如果您的指令没有完成您期望的任务,或者如果您只是想验证您的指令是否已完成且没有错误,请连接到实例,检查输出日志文件 (/var/log/cloud-init-output.log),然后查找输出中的错误消息。对于其他调试信息,您可以将以下行添加到指令:

    output : { all : '| tee -a /var/log/cloud-init-output.log' }

    此指令将 runcmd 输出发送到 /var/log/cloud-init-output.log

用户数据和 Amazon CLI

您可以使用 Amazon CLI 指定、修改和查看实例的用户数据。有关使用实例元数据从实例查看用户数据的信息,请参阅从实例检索实例用户数据

在 Windows 上,您可以使用 Amazon Tools for Windows PowerShell 而不是使用 Amazon CLI。有关更多信息,请参阅 Amazon EC2 用户指南(适用于 Windows 实例) 中的用户数据和 Tools for Windows PowerShell

示例:启动时指定用户数据

要在启动实例时指定用户数据,请结合使用 run-instances 命令与 --user-data 参数。使用 run-instances,Amazon CLI 将对您的用户数据执行 base64 编码。

以下示例显示如何在命令行上指定字符串形式的脚本:

aws ec2 run-instances --image-id ami-abcd1234 --count 1 --instance-type m3.medium \ --key-name my-key-pair --subnet-id subnet-abcd1234 --security-group-ids sg-abcd1234 \ --user-data echo user data

以下示例显示如何使用文本文件指定脚本。请务必使用 file:// 前缀指定该文件。

aws ec2 run-instances --image-id ami-abcd1234 --count 1 --instance-type m3.medium \ --key-name my-key-pair --subnet-id subnet-abcd1234 --security-group-ids sg-abcd1234 \ --user-data file://my_script.txt

以下是具有 Shell 脚本的示例文本文件。

#!/bin/bash yum update -y service httpd start chkconfig httpd on
示例:修改停止的实例的用户数据

您可以使用 modify-instance-attribute 命令修改已停止的实例的用户数据。使用 modify-instance-attribute,Amazon CLI 不会对用户数据执行 base64 编码。

  • Linux 计算机上,使用 base64 命令对用户数据进行编码。

    base64 my_script.txt >my_script_base64.txt
  • Windows 计算机上,使用 certutil 命令可对用户数据进行编码。您必须先删除第一行 (BEGIN CERTIFICATE) 和最后一行 (END CERTIFICATE),然后才能将此文件用于 Amazon CLI。

    certutil -encode my_script.txt my_script_base64.txt notepad my_script_base64.txt

使用 --attribute--value 参数可通过编码的文本文件指定用户数据。请务必使用 file:// 前缀指定该文件。

aws ec2 modify-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData --value file://my_script_base64.txt
示例:清除停止的实例的用户数据

要删除现有的用户数据,请按以下方式使用 modify-instance-attribute 命令:

aws ec2 modify-instance-attribute --instance-id i-1234567890abcdef0 --user-data Value=
示例:查看用户数据

要检索实例的用户数据,请使用 describe-instance-attribute 命令。使用 describe-instance-attribute,Amazon CLI 不会对用户数据执行 base64 解码。

aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData

以下是具有已进行 base64 编码的用户数据的示例输出。

{ "UserData": { "Value": "IyEvYmluL2Jhc2gKeXVtIHVwZGF0ZSAteQpzZXJ2aWNlIGh0dHBkIHN0YXJ0CmNoa2NvbmZpZyBodHRwZCBvbg==" }, "InstanceId": "i-1234567890abcdef0" }
  • Linux 计算机上,使用 --query 选项获取已编码的用户数据和用于对该数据进行解码的 base64 命令。

    aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData --output text --query "UserData.Value" | base64 --decode
  • Windows 计算机上,使用 --query 选项获取已编码的用户数据和用于对该数据进行解码的 certutil 命令。请注意,编码的输出存储在一个文件中,解码的输出存储在另一个文件中。

    aws ec2 describe-instance-attribute --instance-id i-1234567890abcdef0 --attribute userData --output text --query "UserData.Value" >my_output.txt certutil -decode my_output.txt my_output_decoded.txt type my_output_decoded.txt

下面是示例输出。

#!/bin/bash yum update -y service httpd start chkconfig httpd on

将 Shell 脚本和 cloud-init 指令组合使用

默认情况下,一次只能在用户数据中包含一个内容类型。不过,您可以在 mime-multi part 文件中使用 text/cloud-configtext/x-shellscript,以便在用户数据中同时包含 Shell 脚本和 cloud-init 指令。

下面显示了 mime-multi part 的格式。

Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config cloud-init directives --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.txt" #!/bin/bash shell script commands --//--

例如,以下用户数据包含了 cloud-init 指令和 bash Shell 脚本。cloud-init 指令会创建一个文件(/test-cloudinit/cloud-init.txt),然后将 Created by cloud-init 写入该文件。bash Shell 脚本会创建一个文件(/test-userscript/userscript.txt),然后将 Created by bash shell script 写入该文件。

Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config runcmd: - [ mkdir, /test-cloudinit ] write_files: - path: /test-cloudinit/cloud-init.txt content: Created by cloud-init --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.txt" #!/bin/bash mkdir test-userscript touch /test-userscript/userscript.txt echo "Created by bash shell script" >> /test-userscript/userscript.txt --//--