如何阻止我的 Amazon EC2 Windows 实例在完成引导之前发回 CREATE_COMPLETE 信号?
上次更新时间:2020 年 2 月 11 日
我正在使用 AWS CloudFormation 中的 cfn-init (cfn-init.exe) 和 cfn-signal (cfn-signal.exe) 帮助程序脚本引导 Amazon Elastic Compute Cloud (Amazon EC2) Windows 实例。我的 cfn-init 脚本会过早返回信号。然后,AWS CloudFormation 在我的 Windows 实例完成引导之前便将其标记为 CREATE_COMPLETE。
简短描述
在 Windows 实例中,UserData 脚本由 Ec2ConfigService 进程执行。UserData 会调用 cfn-init.exe,它作为 Ec2ConfigService 的子进程运行。
您的 Windows 实例发回 CREATE_COMPLETE 信号可能是出于以下原因:
- 如果 cfn-init.exe 执行的步骤之一需要系统重启,则系统会关闭,然后将执行权交还给 Ec2ConfigService 进程。系统会继续处理 UserData 脚本,然后执行 cfn-signal.exe 来向 AWS CloudFormation 发回信号。
- cfn-signal 在重启后未发回信号,因为 UserData 仅运行一次。
在以下代码示例中,cfn-signal.exe 直接从 UserData 调用。如果 cfn-init.exe 进程执行重启,则无法调用 cfn-signal.exe 命令,因为 UserData 仅运行一次。
JSON 示例:
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"<script>\n",
"cfn-init.exe -v -s ",
{
"Ref": "AWS::StackId"
},
" -r WindowsInstance",
" --configsets ascending",
" --region ",
{
"Ref": "AWS::Region"
},
"\n",
"cfn-signal.exe -e %ERRORLEVEL% --stack ",
{
"Ref": "AWS::StackId"
},
" --resource WindowsInstance --region ",
{
"Ref": "AWS::Region"
},
"\n",
"</script>"
]
]
}
}
YAML 示例:
UserData:
Fn::Base64: !Sub |
<script>
cfn-init.exe -v -s ${AWS::StackId} -r WindowsInstance --configsets ascending --region ${AWS::Region}
cfn-signal.exe -e %ERRORLEVEL% --stack ${AWS::StackId} --resource WindowsInstance --region ${AWS::Region}
</script>
解决方法
1. 使用模板中 cfn-init Metadata 部分的 configsets 将需要重启的配置和不需要重启的配置分开。
2. 将 cfn-signal.exe 从 AWS::EC2::Instance 或 AWS::AutoScaling::LaunchConfiguration 资源的 UserData 部分移至模板的 AWS::CloudFormation::Init Metadata 部分。
3. 执行 cfn-signal.exe 作为最后一个 configset 运行的最后一个命令。
4. 在 JSON 或 YAML 模板中将 UserData 更改为 cfn-init 并制定升序 configset。
JSON 示例:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "cfn-init example using configsets",
"Parameters": {
"AMI": {
"Type": "AWS::EC2::Image::Id"
}
},
"Resources": {
"WindowsInstance": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"AWS::CloudFormation::Init": {
"configSets": {
"ascending": [
"config1",
"config2",
"config3"
]
},
"config1": {
"files": {
"C:\\setup\\setenvironment.ps1": {
"content": {
"Fn::Join": [
"",
[
"$Folder = 'C:\\Program Files\\Server\\packages\\bin.20182.18.0826.0815\\'\n",
"$OldPath = [System.Environment]::GetEnvironmentVariable('path')\n",
"$NewPath = $OldPath + ';' + $Folder\n",
"[System.Environment]::SetEnvironmentVariable('path',$NewPath,'Machine')"
]
]
}
}
}
},
"config2": {
"commands": {
"0-restart": {
"command": "powershell.exe -Command Restart-Computer",
"waitAfterCompletion": "forever"
}
}
},
"config3": {
"commands": {
"01-setenvironment": {
"command": "powershell.exe -ExecutionPolicy Unrestricted C:\\setup\\setenvironment.ps1",
"waitAfterCompletion": "0"
},
"02-signal-resource": {
"command": {
"Fn::Join": [
"",
[
"cfn-signal.exe -e %ERRORLEVEL% --resource WindowsInstance --stack ",
{
"Ref": "AWS::StackName"
},
" --region ",
{
"Ref": "AWS::Region"
}
]
]
}
}
}
}
}
},
"Properties": {
"ImageId": {
"Ref": "AMI"
},
"InstanceType": "t2.medium",
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"<script>\n",
"cfn-init.exe -v -s ",
{
"Ref": "AWS::StackId"
},
" -r WindowsInstance",
" --configsets ascending",
" --region ",
{
"Ref": "AWS::Region"
},
"</script>"
]
]
}
}
},
"CreationPolicy": {
"ResourceSignal": {
"Count": "1",
"Timeout": "PT30M"
}
}
}
}
}
YAML 示例:
AWSTemplateFormatVersion: '2010-09-09'
Description: cfn-init example using configsets
Parameters:
AMI:
Type: 'AWS::EC2::Image::Id'
Resources:
WindowsInstance:
Type: 'AWS::EC2::Instance'
Metadata:
AWS::CloudFormation::Init:
configSets:
ascending:
- config1
- config2
- config3
config1:
files:
C:\setup\setenvironment.ps1:
content: !Sub |
$Folder = 'C:\Program Files\Server\packages\bin.20182.18.0826.0815\'
$OldPath = [System.Environment]::GetEnvironmentVariable('path')
$NewPath = $OldPath + ';' + $Folder
[System.Environment]::SetEnvironmentVariable('path',$NewPath,'Machine')
config2:
commands:
0-restart:
command: powershell.exe -Command Restart-Computer
waitAfterCompletion: forever
config3:
commands:
01-setenvironment:
command: powershell.exe -ExecutionPolicy Unrestricted C:\setup\setenvironment.ps1
waitAfterCompletion: '0'
02-signal-resource:
command: !Sub >
cfn-signal.exe -e %ERRORLEVEL% --resource WindowsInstance --stack ${AWS::StackName} --region ${AWS::Region}
Properties:
ImageId: !Ref AMI
InstanceType: t2.medium
UserData:
Fn::Base64: !Sub |
<script>
cfn-init.exe -v -s ${AWS::StackId} -r WindowsInstance --configsets ascending --region ${AWS::Region}
</script>
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT30M
在前面的模板中,信号不再在 UserData 中运行,这意味着您无法检索 cfn-init 进程提供的退出代码。默认情况下,如果未从 UserData 或 Metadata 部分收到信号,AWS CloudFormation 无法创建或更新堆栈。然后堆栈会返回一个“timeout exceeded”错误。
提示:要进行问题排查,请使用 Windows 实例中位于 c:\cfn\log 位置的日志。
5. 将 waitAfterCompletion 参数设置为 forever。
注意:waitAfterCompletion 的默认值为 60 秒。如果您将该值更改为 forever,cfn-init 将退出,然后仅在重启完成后才恢复。