亚马逊EC2自定义AMI未运行引导程序(用户数据)

29

在创建自定义 AMI (镜像)的 EC2 实例时,我遇到了一个问题。如果我使用自定义的引导程序/用户数据脚本启动Windows默认 2012 服务器实例,例如:

<powershell>
PowerShell "(New-Object System.Net.WebClient).DownloadFile('http://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe','C:\vcredist_x64.exe')"
</powershell>

它将按预期工作并转到URL并下载该文件,并将其存储在C:驱动器上。

但是,如果我设置Windows服务器实例,然后从中创建映像,并将其存储为自定义AMI,然后使用完全相同的自定义用户数据脚本部署它,它将无法正常工作。但是,如果我转到实例网址 (http://169.254.169.254/latest/user-data),它将显示脚本已成功导入但尚未执行。

检查错误日志后,我经常注意到以下内容:

Failed to fetch instance metadata http://169.254.169.254/latest/user-data with exception The remote server returned an error: (404) Not Found.
5个回答

43

更新于2017年4月15日:适用于EC2Launch和Windows Server 2016 AMIs

根据AWS关于EC2Launch的文档,Windows Server 2016用户可以继续使用在EC2Config 2.1.10中引入的persist标签:

对于EC2Config版本2.1.10及更高版本或EC2Launch,您可以在用户数据中使用true来在用户数据执行后启用插件。

用户数据示例:

<powershell>
    insert script here 
</powershell> 
<persist>true</persist>

对于随后的启动:

Windows Server 2016用户必须额外启用配置和启用EC2Launch,而不是EC2Config。 EC2Config已在Windows Server 2016 AMI中废弃,取而代之的是EC2Launch。

运行以下powershell以安排Windows任务,在下一次启动时运行用户数据:

C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 –Schedule

按照设计,此任务在第一次运行后将被禁用。但是,使用persist标记会导致Invoke-UserData通过Register-FunctionScheduler安排一个单独的任务,在随后的启动中保留您的用户数据。您可以在C:\ProgramData\Amazon\EC2-Windows\Launch\Module\Scripts\Invoke-Userdata.ps1中自行查看。
进一步的故障排除:
如果您的用户数据脚本出现其他问题,您可以在源自WS 2016基础AMI的实例中找到用户数据执行日志,位于C:\ProgramData\Amazon\EC2-Windows\Launch\Log\UserdataExecution.log

原始答案:适用于EC2Config和旧版本的Windows Server

在初始启动后,用户数据执行将自动被禁用。当您创建镜像时,执行可能已经被禁用。这可以在C:\Program Files\Amazon\Ec2ConfigService\Settings\Config.xml中手动配置。

使用EC2Config服务配置Windows实例的文档提供了几个选项:

  • 使用schtasks.exe /Create编程创建一个定时任务,在系统启动时运行,并将定时任务指向位于C:\Program Files\Amazon\Ec2ConfigServer\Scripts\UserScript.ps1的用户数据脚本(或其他脚本)。

  • 在Config.xml中编程启用用户数据插件。

以下是来自文档的示例:

<powershell>
$EC2SettingsFile="C:\Program Files\Amazon\Ec2ConfigService\Settings\Config.xml"
$xml = [xml](get-content $EC2SettingsFile)
$xmlElement = $xml.get_DocumentElement()
$xmlElementToModify = $xmlElement.Plugins

foreach ($element in $xmlElementToModify.Plugin)
{
    if ($element.name -eq "Ec2SetPassword")
    {
        $element.State="Enabled"
    }
    elseif ($element.name -eq "Ec2HandleUserData")
    {
        $element.State="Enabled"
    }
}
$xml.Save($EC2SettingsFile)
</powershell>
  • 从 EC2Config 版本 2.1.10 开始,您可以使用 <persist>true</persist> 在用户数据执行后启用插件。

例如,来自文档的示例:

<powershell>
    insert script here
</powershell>
<persist>true</persist>

我认为这需要再次更新,他们希望您在Windows2016上使用EC2Launch,而在之前的操作系统版本上使用EC2Config:https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-user-data.html - Jeremy Thompson
@JeremyThompson 你能澄清需要更新什么吗?据我所知,这个答案已经提出了那个建议。 - Anthony Neace
当然,其中写着:“启用Windows Server 2016(EC2Launch)上的用户数据执行”,他们明确指出“启用Windows Server 2012 R2及更早版本(EC2Config)上的用户数据执行”。你可能是对的,EC2Config 2.1.10可以在Win2016上使用,但文档表明要在Win2016上使用EC2Launch,在先前的操作系统上使用另一个。 - Jeremy Thompson
2
@JeremyThompson 啊 :) 不不,那不是它的意思。让我澄清一下:我提到了 EC2Config 的那个版本,是为了引出 EC2Launch 继续支持相同的功能——通过用户数据脚本来支持此前的标记持久化解决方案。你说得对,WS 2016 需要使用 EC2Launch——答案并不反对这种说法——只是提到了 EC2Config 2.1.10 是为了让人们了解这个功能最初来自哪里,相同的解决方案也适用于 EC2Launch,以及为什么会这样。希望这可以帮到你。 - Anthony Neace

11

我发现的另一个解决方案是使用 EC2Launch 运行 Sysprep

问题在于 AWS 在自定义 AMI 中没有重新建立到配置文件服务(169.254.169.254)的路由。请参见 SanjitPatel 在这篇帖子中的回复。因此,当我尝试使用自定义 AMI 创建 Spot 请求时,我的新实例无法找到用户数据。

使用 Sysprep 关闭实例会强制 AWS 在实例上重新执行所有设置工作,就像第一次运行一样。因此,当您创建实例、关闭它并使用 Sysprep 创建自定义 AMI 之后,AWS 将为新实例正确设置配置文件服务路由并执行您的用户数据。这也避免了手动更改 Windows 任务和在随后的启动中执行用户数据,正如 persist 标记所做的那样。

以下是快速步骤:

  1. 使用 AWS Windows AMI 之一(Windows Server 2016 Nano Server 不支持 Sysprep)创建一个实例,并传递所需的用户数据(这可能是可选的,但最好确保 AWS 正确地连接设置脚本以处理用户数据)。
  2. 根据需要自定义您的实例。
  3. 使用 Sysprep 关闭您的实例。只需打开 EC2LaunchSettings 应用程序,然后单击 “关闭并使用 Sysprep”。完整说明在此
  4. 从您刚关闭的实例创建您的自定义 AMI。
  5. 使用您的自定义 AMI 创建其他实例,在实例创建时传递用户数据。用户数据将在实例启动时执行。在我的情况下,我使用了 Spot 请求屏幕,其中有一个用户数据文本框。

希望这可以帮助您!


谢谢!这正是我所需要的。我有一个自定义AMI,用于使用Lambda函数启动Spot实例,但用户数据无法运行。日志显示它是空的。 - rgoliveira

4
在初始引导(UserData)脚本结束时,只需按照下面所示添加persist标记。完美地运行。
<powershell>
    insert script here
</powershell>
<persist>true</persist>

1

对于那些从Google进入并运行Server 2016实例的人来说,似乎这已经不再可能。

Server2016没有ec2config服务,因此您无法使用persist标志。

<persist>true</persist>

这段内容描述了Anthony Neace的帖子。

Server 2016使用EC2Launch,但我还没有看到如何在每次启动时运行脚本。您可以在第一次启动时运行脚本,但是后续启动将不会运行它。


1
您可以通过启用计划任务“Amazon Ec2 Launch - Instance Initialization”来完成。它将仅在下一次启动时运行一次,然后再次变为禁用状态。 - SkyFx
@SkyFx - 有没有一种方法可以在每次实例启动时运行脚本? - Lou O.
1
是的,此任务仅运行 C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 Powershell 脚本。该脚本首先禁用任务,然后调用用户数据。因此,您可以在用户数据中使用 Powershell 重新启用任务。使用Powershell - SkyFx
1
或者,想一想,只需在InitializeInstance.ps1中注释掉任务禁用即可。 - SkyFx
1
@LouO。请查看我的更新答案;EC2Launch 的设计是为了继续支持保留标记。 - Anthony Neace

0
我在AMI烘焙过程中添加了以下PowerShell脚本,帮助我解决了这个问题。这是Windows Server 2019。
$EC2LaunchInitInstance = "C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1"
$EC2LaunchSysprep = "C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\SysprepInstance.ps1"
Invoke-Expression -Command "$EC2LaunchInitInstance -Schedule"
Invoke-Expression -Command "$EC2LaunchSysprep -NoShutdown"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接