AWS::CloudFormation::Init
使用类型 AWS::CloudFormation::Init
来将 Amazon EC2 实例上的元数据置入 cfn-init 帮助程序脚本。如果您的模板调用 cfn-init 脚本,该脚本会查找来源于 AWS::CloudFormation::Init
元数据键内的资源元数据。有关 cfn-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 实例上执行命令。其执行顺序即为命令名称的字母顺序。
键 | 必需 | 描述 |
---|---|---|
命令 |
必需 |
指定要运行的命令的数组或字符串。如果您使用了数组,则不需要对空格字符进行转义或用引号将命令参数引起来。请勿使用数组指定多个命令。 |
env |
可选 |
设置该命令的环境变量。这个属性会覆盖而不是追加到现有的环境。 |
cwd |
可选 |
工作目录。 |
测试 |
可选 |
用于确定 对于 Linux,测试命令必须返回退出代码 |
ignoreErrors |
可选 |
一个用于确定在命令键中包含的命令失败(返回非零值)时, |
waitAfterCompletion |
可选 |
仅限于 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 抽取。这些文件会按词典顺序写入磁盘。下表列出了支持的键。
键 | 描述 |
---|---|
content |
字符串或格式正确的 JSON 对象。如果您将 JSON 对象用作内容,则 JSON 将被写到磁盘上的文件中。在 JSON 对象写入磁盘之前,对所有内置函数(例如 注意如果创建符号链接,帮助程序脚本会修改目标文件的权限。目前,您无法在不修改目标文件的权限的情况下创建符号链接。 |
源 |
用于加载文件的 URL。不能使用内容键指定该选项。 |
encoding |
编码格式。仅限在内容为字符串时使用。如果使用的是源,则不会应用编码。 有效值: |
组 |
拥有这个文件的组的名称。在 Windows 系统中不受支持。 |
owner |
拥有这个文件的用户的名称。在 Windows 系统中不受支持。 |
mode |
是一个六位数的八进制值,表示这个文件的模式。在 Windows 系统中不受支持。将前三位数用于符号链接,将后三位数用于设置权限。要创建符号链接,请指定 |
身份验证 |
要使用的身份验证方法的名称。这会覆盖任何默认的身份验证。您可以使用该属性选择通过 AWS::CloudFormation::Authentication 资源定义的身份验证方法。 |
context |
指定需要作为 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 服务启动。
下表列出了支持的键。
键 | 描述 |
---|---|
ensureRunning |
设置为 true 以确保该服务在 设置为 false 可确保 忽略该键,将不更改服务状态。 |
已启用 |
设置为 true 可确保启动时自动启动服务。 设置为 false 可确保启动时不会自动启动服务。 忽略该键,将不更改这个属性。 |
文件 |
一系列文件。如果 |
来源 |
一系列目录。如果 |
软件包 |
软件包管理器映射至软件包名称列表。如果 |
命令 |
一系列命令名称。如果 |
示例
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"
用户
您可以使用用户键在 EC2 实例上创建 Linux/UNIX 用户。该用户键在 Windows 系统中不受支持。
下表列出了支持的键。
键 | 描述 |
---|---|
uid |
用户 ID。如果用户名还有另一个用户 ID,那么此创建过程会失败。如果该用户 ID 已分配给现有用户,则操作系统可能会拒绝创建请求。 |
groups |
一系列组名。该用户将被添加到列表的每个组中。 |
homeDir |
用户的主目录。 |
示例
创建的用户属于非交互式系统用户,带有 /sbin/nologin
Shell。这是特意设计的,无法修改。
JSON
"users" : { "myUser" : { "groups" : ["groupOne", "groupTwo"], "uid" : "50", "homeDir" : "/tmp" } }
YAML
users: myUser: groups: - "groupOne" - "groupTwo" uid: "50" homeDir: "/tmp"