使用 AWS::CloudFormation::Init
类型来将 Amazon EC2 实例上的元数据置入 cfn-init
帮助程序脚本。如果您的模板调用 cfn-init
脚本,该脚本会查找来源于 AWS::CloudFormation::Init
元数据键内的资源元数据。有关更多信息,请参阅 cfn-init。
cfn-init
支持 Linux 系统的所有元数据类型。在满足下面部分中说明的条件时,它还支持 Windows 的元数据类型。
有关使用 AWS::CloudFormation::Init
和 cfn-init
帮助程序脚本创建 Linux 堆栈的示例,请参阅 在 Amazon EC2 上部署应用程序。有关 Windows 堆栈示例,请参阅 引导 AWS CloudFormation Windows 堆栈。
语法
配置过程分为几个部分。以下模板代码段演示如何在模板内将 cfn-init
的元数据连接到 EC2 实例资源。
元数据分成数个配置键,您可以将它们分成几个配置集。您可以在模板中调用 cfn-init
时指定配置集。如果未指定配置集,cfn-init
将查找名为 config 的单个配置键。
注意
cfn-init
帮助程序脚本将以下列顺序处理这些配置部分:packages、groups、users、sources、files、commands,然后是 services。如果您需要以不同顺序处理,请将各节分为不同的配置键,然后使用配置集指定处理这些配置键应采用的顺序。
JSON
"Resources": {
"MyInstance": {
"Type": "AWS::EC2::Instance",
"Metadata" : {
"AWS::CloudFormation::Init" : {
"config" : {
"packages" : {
:
},
"groups" : {
:
},
"users" : {
:
},
"sources" : {
:
},
"files" : {
:
},
"commands" : {
:
},
"services" : {
:
}
}
}
},
"Properties": {
:
}
}
}
YAML
Resources:
MyInstance:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
packages:
:
groups:
:
users:
:
sources:
:
files:
:
commands:
:
services:
:
Properties:
:
注意
以下各节包含使用类似 Unix 的 Shell 脚本语言(例如 Bash)编写的脚本示例。要改为为 PowerShell 创建脚本,请确保您熟悉 PowerShell 语言。PowerShell 语法与类似 Unix 的 Shell 不同,需要您熟悉 PowerShell 的工作方式。
配置集
如果您创建多个配置键并使 cfn-init
以特定的顺序处理这些键,则可创建一个配置集,其中包含以特定顺序排列的多个配置键。
单个配置集
以下模板代码段创建名为 ascending
和 descending
的两个配置集,每个各包含 2 个配置键。
JSON
"AWS::CloudFormation::Init" : {
"configSets" : {
"ascending" : [ "config1" , "config2" ],
"descending" : [ "config2" , "config1" ]
},
"config1" : {
"commands" : {
"test" : {
"command" : "echo \"$CFNTEST\" > test.txt",
"env" : { "CFNTEST" : "I come from config1." },
"cwd" : "~"
}
}
},
"config2" : {
"commands" : {
"test" : {
"command" : "echo \"$CFNTEST\" > test.txt",
"env" : { "CFNTEST" : "I come from config2" },
"cwd" : "~"
}
}
}
}
YAML
AWS::CloudFormation::Init:
configSets:
ascending:
- "config1"
- "config2"
descending:
- "config2"
- "config1"
config1:
commands:
test:
command: "echo \"$CFNTEST\" > test.txt"
env:
CFNTEST: "I come from config1."
cwd: "~"
config2:
commands:
test:
command: "echo \"$CFNTEST\" > test.txt"
env:
CFNTEST: "I come from config2"
cwd: "~"
相关 cfn-init
调用
以下示例 cfn-init
调用引用了前述示例配置集。为明确起见,示例调用采用的是缩写形式。完整的语法请参阅 cfn-init。
-
如果
cfn-init
调用指定了ascending
配置集:cfn-init -c ascending
该脚本会首先处理
config1
,然后再处理config2
,而test.txt
文件将包含文本I come from config2
。 -
如果
cfn-init
调用指定了descending
配置集:cfn-init -c descending
该脚本会首先处理
config2
,然后再处理config1
,而test.txt
文件将包含文本I come from config1
。
多个配置集
您可以创建多个配置集,并使用 cfn-init
脚本对其进行一连串调用。每个配置集都包含配置键列表或对其他配置集的引用的列表。例如,以下模板代码段可创建三个配置集。第一个配置集test1
包含一个名为 1
的配置键。第二个配置集test2
包含对 test1
配置集的引用和一个名为 2
的配置键。在默认情况下,第三个配置集包含对配置集test2
的引用。
JSON
"AWS::CloudFormation::Init" : {
"configSets" : {
"test1" : [ "1" ],
"test2" : [ { "ConfigSet" : "test1" }, "2" ],
"default" : [ { "ConfigSet" : "test2" } ]
},
"1" : {
"commands" : {
"test" : {
"command" : "echo \"$MAGIC\" > test.txt",
"env" : { "MAGIC" : "I come from the environment!" },
"cwd" : "~"
}
}
},
"2" : {
"commands" : {
"test" : {
"command" : "echo \"$MAGIC\" >> test.txt",
"env" : { "MAGIC" : "I am test 2!" },
"cwd" : "~"
}
}
}
}
YAML
AWS::CloudFormation::Init:
1:
commands:
test:
command: "echo \"$MAGIC\" > test.txt"
env:
MAGIC: "I come from the environment!"
cwd: "~"
2:
commands:
test:
command: "echo \"$MAGIC\" >> test.txt"
env:
MAGIC: "I am test 2!"
cwd: "~"
configSets:
test1:
- "1"
test2:
-
ConfigSet: "test1"
- "2"
default:
-
ConfigSet: "test2"
相关 cfn-init
调用
以下 cfn-init
调用引用了前述模板代码段中声明的配置集。为明确起见,示例调用采用的是缩写形式。完整的语法请参阅 cfn-init。
-
如果您仅指定
test1
:cfn-init -c test1
cfn-init
仅处理配置键1
。 -
如果您仅指定
test2
:cfn-init -c test2
cfn-init
会首先处理配置键1
,然后再处理配置键2
。 -
如果您指定
default
配置集(或根本没有配置集):cfn-init -c default
如果您指定 configset
test2
,则将获得相同的特性。
命令
您可以使用命令键在 EC2 实例上执行命令。其执行顺序即为命令名称的字母顺序。
键 | 必需 | 描述 |
---|---|---|
|
必填 |
指定要运行的命令的数组或字符串。如果您使用了数组,则不需要对空格字符进行转义或用引号将命令参数引起来。请勿使用数组指定多个命令。 |
|
可选 |
设置该命令的环境变量。这个属性会覆盖而不是追加到现有的环境。 |
|
可选 |
工作目录。 |
|
可选 |
用于确定 对于 Linux,测试命令必须返回退出代码 |
|
可选 |
一个用于确定在命令键中包含的命令失败(返回非零值)时, |
|
可选 |
仅限于 Windows 系统。指定命令执行结束后,在该命令引起重启的情况下需要等待的时间长短 (以秒为单位)。默认值为 60 秒,而值“forever”会使 |
示例
以下示例代码段在 ~/test.txt
文件不存在时调用 echo 命令。
JSON
"commands" : {
"test" : {
"command" : "echo \"$MAGIC\" > test.txt",
"env" : { "MAGIC" : "I come from the environment!" },
"cwd" : "~",
"test" : "test ! -e ~/test.txt",
"ignoreErrors" : "false"
},
"test2" : {
"command" : "echo \"$MAGIC2\" > test2.txt",
"env" : { "MAGIC2" : "I come from the environment!" },
"cwd" : "~",
"test" : "test ! -e ~/test2.txt",
"ignoreErrors" : "false"
}
}
YAML
commands:
test:
command: "echo \"$MAGIC\" > test.txt"
env:
MAGIC: "I come from the environment!"
cwd: "~"
test: "test ! -e ~/test.txt"
ignoreErrors: "false"
test2:
command: "echo \"$MAGIC2\" > test2.txt"
env:
MAGIC2: "I come from the environment!"
cwd: "~"
test: "test ! -e ~/test2.txt"
ignoreErrors: "false"
文件
您可以使用 files
密钥在 EC2 实例上创建文件。其内容既可以来自模板,也可以从 URL 抽取。这些文件会按词典顺序写入磁盘。下表列出了支持的键。
键 | 描述 |
---|---|
|
字符串或格式正确的 JSON 对象。如果您将 JSON 对象用作内容,则 JSON 将被写到磁盘上的文件中。在 JSON 对象写入磁盘之前,对所有内置函数(例如 注意如果创建符号链接,帮助程序脚本会修改目标文件的权限。目前,您无法在不修改目标文件的权限的情况下创建符号链接。 |
|
用于加载文件的 URL。不能使用内容键指定该选项。 |
|
编码格式。仅限在内容为字符串时使用。如果使用的是源,则不会应用编码。 有效值: |
|
拥有这个文件的组的名称。在 Windows 系统中不受支持。 |
|
拥有这个文件的用户的名称。在 Windows 系统中不受支持。 |
|
是一个六位数的八进制值,表示这个文件的模式。在 Windows 系统中不受支持。将前三位数用于符号链接,将后三位数用于设置权限。要创建符号链接,请指定 |
|
要使用的身份验证方法的名称。这会覆盖任何默认的身份验证。您可以使用该属性选择通过 AWS::CloudFormation::Authentication 资源定义的身份验证方法。 |
|
指定需要作为 Mustache 模板 |
示例
以下示例代码段将在更大范围的安装中创建一个名为 setup.mysql
的文件。
JSON
"files" : {
"/tmp/setup.mysql" : {
"content" : { "Fn::Join" : ["", [
"CREATE DATABASE ", { "Ref" : "DBName" }, ";\n",
"CREATE USER '", { "Ref" : "DBUsername" }, "'@'localhost' IDENTIFIED BY '",
{ "Ref" : "DBPassword" }, "';\n",
"GRANT ALL ON ", { "Ref" : "DBName" }, ".* TO '", { "Ref" : "DBUsername" },
"'@'localhost';\n",
"FLUSH PRIVILEGES;\n"
]]},
"mode" : "000644",
"owner" : "root",
"group" : "root"
}
}
YAML
files:
/tmp/setup.mysql:
content: !Sub |
CREATE DATABASE ${DBName};
CREATE USER '${DBUsername}'@'localhost' IDENTIFIED BY '${DBPassword}';
GRANT ALL ON ${DBName}.* TO '${DBUsername}'@'localhost';
FLUSH PRIVILEGES;
mode: "000644"
owner: "root"
group: "root"
完整的模板可通过以下网址获取:https://s3.amazonaws.com/cloudformation-templates-us-east-1/Drupal_Single_Instance.template
下面的示例代码段创建指向现有文件 /tmp/myfile2.txt
的符号链接 /tmp/myfile1.txt
。目标文件 /tmp/myfile1.txt
的权限由模式值 644
定义。
JSON
"files" : {
"/tmp/myfile2.txt" : {
"content" : "/tmp/myfile1.txt",
"mode" : "120644"
}
}
YAML
files:
/tmp/myfile2.txt:
content: "/tmp/myfile1.txt"
mode: "120644"
Mustache 模板主要用于创建配置文件。例如,您可以将配置文件存储在一个 S3 存储桶中,并从模板插入 Refs 和 GetAtts,而不是使用 Fn::Join。以下示例片段会将 Content for test9
输出到 /tmp/test9.txt
。
JSON
"files" : {
"/tmp/test9.txt" : {
"content" : "Content for {{name}}",
"context" : { "name" : "test9" }
}
}
YAML
files:
/tmp/test9.txt:
content: "Content for {{name}}"
context:
name: "test9"
在使用 Mustache 模板时,请注意以下几点:
-
必须存在用于要处理的文件的上下文键。
-
上下文键必须是键值映射,但可以嵌套。
-
您可以使用内容键来处理含有内联内容的文件,并使用源键来处理远程文件。
-
Mustache 支持取决于 pystache 版本。版本 0.5.2 支持 Mustache 1.1.2 规范
。
组
您可以使用组键创建 Linux/UNIX 组,并分配组 ID。该组键在 Windows 系统中不受支持。
要创建组,请添加新的密钥值对,将新的组名映射到可选组 ID。组键可以包含一个或多个组名。下表列出了可用的密钥。
键 | 描述 |
---|---|
|
组 ID 编号。 如果指定了组 ID,且存在该名称的组,那么创建组将失败。如果其他组已使用指定的 ID,则操作系统可能会拒绝创建该组。 示例: |
示例代码段
以下代码段指定两个组,其中一个名为 groupOne
但没有分配组 ID,另一个组名为 groupTwo
,指定的相应组 ID 值为 45
。
JSON
"groups" : {
"groupOne" : {},
"groupTwo" : { "gid" : "45" }
}
YAML
groups:
groupOne: {}
groupTwo:
gid: "45"
程序包
您可以使用包键下载和安装各种预包装的应用程序和组件。在 Windows 系统上,软件包键仅支持 MSI 安装程序。
支持的软件包格式
cfn-init
脚本目前支持以下软件包格式:apt、msi、python、rpm、rubygems、yum 和 Zypper。软件包的处理顺序如下:先是 rpm、yum/apt/zypper,然后是 rubygems 和 python。rubygems 和 python 之间没有处理顺序,且不保证以任何顺序安装每个包管理器内的包。
指定版本
在每个包管理器内,会使用包名和一系列版本对每个包进行指定。版本可以是字符串、一系列版本、空字符串或者空列表。空字符串或者空列表表示您需要最新的版本。对于 rpm 管理器,版本是以磁盘或 URL 上的文件的路径的方式指定的。
如果指定了软件包版本,cfn-init
会尝试安装该版本,即使实例上已经安装了更高的软件包版本。一些包管理器支持多个版本,但其他的包管理器可能不支持。有关更多信息,请查看相关的包管理器文档。如果您不指定版本且已安装了某版本的软件包,则 cfn-init
脚本将不安装新版本,因为将假定您要保留并使用现有版本。
示例代码段
RPM、yum、Rubygems 和 Zypper
下面的代码段为 rpm 指定版本 URL,从 yum 和 Zypper 请求最新版本并从 rubygems 请求 chef 的版本 0.10.2:
JSON
"rpm" : {
"epel" : "http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm"
},
"yum" : {
"httpd" : [],
"php" : [],
"wordpress" : []
},
"rubygems" : {
"chef" : [ "0.10.2" ]
},
"zypper" : {
"git" : []
}
YAML
rpm:
epel: "http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm"
yum:
httpd: []
php: []
wordpress: []
rubygems:
chef:
- "0.10.2"
zypper:
git: []
MSI 包
下面的代码段为 MSI 包指定 URL:
JSON
"msi" : {
"awscli" : "https://s3.amazonaws.com/aws-cli/AWSCLI64.msi"
}
YAML
msi:
awscli: "https://s3.amazonaws.com/aws-cli/AWSCLI64.msi"
服务
您可以使用此服务键来定义实例启动后应启用或禁用哪些服务。在 Linux 系统上,将通过使用 sysvinit 或 systemd 来支持此键。在 Windows 系统上,将通过使用 Windows 服务管理器来支持此键。
您还可以借助服务键来指定源、软件包和文件之间的依赖关系,从而确保在因安装的文件导致重启时,cfn-init
会完成服务重启。例如,如果您下载 Apache HTTP Server 软件包,则在堆栈创建过程中,软件包安装将自动启动 Apache HTTP Server。然而,如果 Apache HTTP Server 配置在堆栈创建过程的后期被更新,则要等到重启 Apache 服务器之后,更新才会生效。您可以使用服务键来确保 Apache HTTP 服务启动。
下表列出了支持的键。
键 | 描述 |
---|---|
|
设置为 true 以确保该服务在 设置为 false 可确保 忽略该密钥,将不更改服务状态。 |
|
设置为 true 可确保启动时自动启动服务。 设置为 false 可确保启动时不会自动启动服务。 忽略该密钥,将不更改这个属性。 |
|
一系列文件。如果 |
sources |
一系列目录。如果 |
软件包 |
软件包管理器映射至软件包名称列表。如果 |
命令 |
一系列命令名称。如果 |
示例
Linux
下面的 Linux 代码段配置服务如下:
-
如果
cfn-init
修改了/etc/nginx/nginx.conf
或/var/www/html
,则 nginx 服务会重启。 -
如果
cfn-init
使用 yum 安装或更新 php 或 spawn-fcgi,则 php-fastcgi 服务将会重启。 -
sendmail 服务将使用 systemd 停止和禁用。
JSON
"services" : {
"sysvinit" : {
"nginx" : {
"enabled" : "true",
"ensureRunning" : "true",
"files" : ["/etc/nginx/nginx.conf"],
"sources" : ["/var/www/html"]
},
"php-fastcgi" : {
"enabled" : "true",
"ensureRunning" : "true",
"packages" : { "yum" : ["php", "spawn-fcgi"] }
}
},
"systemd": {
"sendmail" : {
"enabled" : "false",
"ensureRunning" : "false"
}
}
}
YAML
services:
sysvinit:
nginx:
enabled: "true"
ensureRunning: "true"
files:
- "/etc/nginx/nginx.conf"
sources:
- "/var/www/html"
php-fastcgi:
enabled: "true"
ensureRunning: "true"
packages:
yum:
- "php"
- "spawn-fcgi"
systemd:
sendmail:
enabled: "false"
ensureRunning: "false"
要将 systemd 与服务一起使用,该服务必须配置一个 systemd 单元文件。以下单元文件允许 systemd 启动和停止多用户服务目标中的 cfn-hup 进程守护程序:
[Unit]
Description=cfn-hup daemon
[Service]
ExecStart=/usr/bin/cfn-hup -v
PIDFile=/var/run/cfn-hup.pid
[Install]
WantedBy=multi-user.target
此配置假设 cfn-hup
安装在 /usr/bin
目录下。但是,安装 cfn-hup
的实际位置可能因平台而异。您可以在 /etc/systemd/system/cfn-hup.service.d/override.conf
中创建覆盖文件来覆盖此配置,如下所示:
# In this example, cfn-hup executable is available under /usr/local/bin
[Service]
ExecStart=
ExecStart=/usr/local/bin/cfn-hup -v
Windows
下面的 Windows 代码段启动 cfn-hup 服务,将其设置为自动,并在 cfn-init
修改指定配置文件的情况下重新启动该服务:
JSON
"services" : {
"windows" : {
"cfn-hup" : {
"enabled" : "true",
"ensureRunning" : "true",
"files" : ["c:\\cfn\\cfn-hup.conf", "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf"]
}
}
}
YAML
services:
windows:
cfn-hup:
enabled: "true"
ensureRunning: "true"
files:
- "c:\\cfn\\cfn-hup.conf"
- "c:\\cfn\\hooks.d\\cfn-auto-reloader.conf"
源
您可以使用该资源键来下载存档文件并在 EC2 实例上的目标目录中取出文件。此键在 Linux 和 Windows 系统中完全受支持。
支持的格式
支持的格式为:
-
tar
-
tar+gzip
-
tar+bz2
-
zip
示例
GitHub
如果您将 GitHub 用作源控制系统,则可以使用 cfn-init
和源打包机制来提取特定的应用程序版本。GitHub 允许您通过 URL 从特定版本创建 .zip 或 .tar,如下所示:
https://github.com/<your directory>/(zipball|tarball)/<version>
例如,下面的代码段会将版本 main
作为一个 .tar
文件拉取。
JSON
"sources" : {
"/etc/puppet" : "https://github.com/user1/cfn-demo/tarball/main"
}
YAML
sources:
/etc/puppet: "https://github.com/user1/cfn-demo/tarball/main"
S3Bucket
以下示例从 S3 桶下载一个 tarball 并将其解压到 /etc/myapp
:
注意
您可以对来源使用身份验证凭证,但不能将身份验证键放在源数据块中。相反,您可以将存储桶密钥包含在 S3AccessCreds
数据块中。有关 Amazon S3 身份验证凭证的更多信息,请参阅 AWS::CloudFormation::Authentication。
JSON
"sources" : {
"/etc/myapp" : "https://s3.amazonaws.com/amzn-s3-demo-bucket
/myapp.tar.gz"
}
YAML
sources:
/etc/myapp: "https://s3.amazonaws.com/amzn-s3-demo-bucket
/myapp.tar.gz"
Users
您可以使用用户键在 EC2 实例上创建 Linux/UNIX 用户。users
键在 Windows 系统中不受支持。
下表列出了支持的键。
键 | 描述 |
---|---|
|
用户 ID。如果用户名还有另一个用户 ID,那么此创建过程会失败。如果该用户 ID 已分配给现有用户,则操作系统可能会拒绝创建请求。 |
|
一系列组名。该用户将被添加到列表的每个组中。 |
|
用户的主目录。 |
示例
创建的用户属于非交互式系统用户,带有 /sbin/nologin
Shell。这是特意设计的,无法修改。
JSON
"users" : {
"myUser" : {
"groups" : ["groupOne", "groupTwo"],
"uid" : "50",
"homeDir" : "/tmp"
}
}
YAML
users:
myUser:
groups:
- "groupOne"
- "groupTwo"
uid: "50"
homeDir: "/tmp"