我们将首先安装对流层库:
$ pip install troposphere
安装完成后,您可以创建一个名为 helloworld-cf-template.py 的新文件。
我们将通过从对流层模块导入一些定义来开始我们的文件:
"""Generating CloudFormation template."""
from troposphere import (
Base64,
ec2,
GetAtt,
Join,
Output,
Parameter,
Ref,
Template,
)
从代码的角度来看,我们要做的第一件事是初始化一个模板变量。在我们的脚本结束时,模板将包含我们基础设施的完整描述,我们将能够简单地打印其输出以获取我们的 CloudFromation 模板:
t = Template()
在本书中,我们将同时创建和运行多个 CloudFormation 模板。为了帮助我们识别给定堆栈中的内容,我们可以提供描述。创建模板后,添加如下描述:
t.add_description("Effective DevOps in AWS: HelloWorld web application")
当我们使用 Web 命令行界面启动 EC2 实例时,我们选择了使用哪个密钥对以获得对主机的 SSH 访问。为了不失去这种能力,我们的模板首先要有一个参数,它为 CloudFormation 用户提供在启动 EC2 实例时选择要使用的密钥对的能力。
为此,我们将创建一个 Parameter 对象,并通过提供标识符、描述、参数类型、描述和约束描述来初始化它,以帮助做出正确的决定
当我们启动堆栈时。为了让这个参数存在于我们的最终模板中,我们还将使用模板类中定义的 add_paramter() 函数:
t.add_parameter(Parameter(
"KeyPair",
Description="Name of an existing EC2 KeyPair to SSH",
Type="AWS::EC2::KeyPair::KeyName",
ConstraintDescription="must be the name of an existing EC2 KeyPair.",
))
接下来我们要看的是安全组。我们将完全按照我们对 KeyPair 参数所做的那样进行。
ApplicationPort = 3000
t.add_resource(ec2.SecurityGroup(
"SecurityGroup",
GroupDescription="Allow SSH and TCP/{} access".format(ApplicationPort),
SecurityGroupIngress=[
ec2.SecurityGroupRule(
IpProtocol="tcp",
FromPort="22",
ToPort="22",
CidrIp="0.0.0.0/0",
),
ec2.SecurityGroupRule(
IpProtocol="tcp",
FromPort=ApplicationPort,
ToPort=ApplicationPort,
CidrIp="0.0.0.0/0",
),
],
))
在下一节中,我们将不再需要登录到我们的 EC2 实例并手动安装 helloworld.js 文件及其初始化脚本。为此,我们将利用 EC2 提供的 UserData 功能。
创建 EC2 实例时,您可以通过 UserData 可选参数提供一组命令,以便在虚拟机启动后运行。
user_data = Base64(Join('\n', [
"#!/bin/bash",
"sudo yum install --enablerepo=epel -y nodejs",
"wget http://bit.ly/2vESNuc -O /home/ec2-user/helloworld.js",
"wget http://bit.ly/2vVvT18 -O /etc/init/helloworld.conf",
"start helloworld"
]))
我们现在将专注于我们模板的主要资源,我们的 EC2 实例。创建实例需要提供用于标识资源的名称、映像 ID、实例类型、安全组、用于 SSH 访问的密钥对以及用户数据。
为了简单起见,我们将对 AMI ID ( ami-a4c7edb2 ) 和实例类型 ( t2.micro ) 进行硬编码。
创建我们的 EC2 实例所需的其余信息是安全组信息和密钥对名称,我们之前通过定义参数和资源收集了这些信息。在 CloudFormation 中,您可以使用关键字 Ref 来引用模板的预先存在的小节。
在对流层中,这是通过调用 Ref() 函数来完成的。和以前一样,我们将在 add_resource 函数的帮助下将结果输出添加到我们的模板中:
t.add_resource(ec2.Instance(
"instance",
ImageId="ami-a4c7edb2",
InstanceType="t2.micro",
SecurityGroups=[Ref("SecurityGroup")],
KeyName=Ref("KeyPair"),
UserData=user_data,
))
在我们脚本的最后一部分,我们将专注于生成模板的输出部分,当 CloudFormation 创建堆栈时,该部分会被填充。
此选择允许您打印出在堆栈启动期间计算的有用信息。在我们的例子中,有两个有用的信息,访问我们的 Web 应用程序的 URL 和实例的公共 IP 地址,以便我们可以根据需要通过 SSH 访问它。
为了检索此类信息,CloudFormation 使用函数 Fn::GetAtt 。在对流层,这被转换为使用 GetAttr() 函数:
t.add_output(Output(
"InstancePublicIp",
Description="Public IP of our instance.",
Value=GetAtt(instance, "PublicIp"),
))
t.add_output(Output(
"WebUrl",
Description="Application endpoint",
Value=Join("", [
"http://", GetAtt(instance, "PublicDnsName"), ":", ApplicationPort
]),
))
此时,我们可以让我们的脚本输出我们生成的模板的最终结果:
print t.to_json()