我們將首先安裝對(duì)流層庫(kù):
$ pip install troposphere
安裝完成后,您可以創(chuàng)建一個(gè)名為 helloworld-cf-template.py 的新文件。
我們將通過(guò)從對(duì)流層模塊導(dǎo)入一些定義來(lái)開(kāi)始我們的文件:
"""Generating CloudFormation template."""
from troposphere import (
Base64,
ec2,
GetAtt,
Join,
Output,
Parameter,
Ref,
Template,
)
從代碼的角度來(lái)看,我們要做的第一件事是初始化一個(gè)模板變量。在我們的腳本結(jié)束時(shí),模板將包含我們基礎(chǔ)設(shè)施的完整描述,我們將能夠簡(jiǎn)單地打印其輸出以獲取我們的 CloudFromation 模板:
t = Template()
在本書(shū)中,我們將同時(shí)創(chuàng)建和運(yùn)行多個(gè) CloudFormation 模板。為了幫助我們識(shí)別給定堆棧中的內(nèi)容,我們可以提供描述。創(chuàng)建模板后,添加如下描述:
t.add_description("Effective DevOps in AWS: HelloWorld web application")
當(dāng)我們使用 Web 命令行界面啟動(dòng) EC2 實(shí)例時(shí),我們選擇了使用哪個(gè)密鑰對(duì)以獲得對(duì)主機(jī)的 SSH 訪問(wèn)。為了不失去這種能力,我們的模板首先要有一個(gè)參數(shù),它為 CloudFormation 用戶(hù)提供在啟動(dòng) EC2 實(shí)例時(shí)選擇要使用的密鑰對(duì)的能力。
為此,我們將創(chuàng)建一個(gè) Parameter 對(duì)象,并通過(guò)提供標(biāo)識(shí)符、描述、參數(shù)類(lèi)型、描述和約束描述來(lái)初始化它,以幫助做出正確的決定
當(dāng)我們啟動(dòng)堆棧時(shí)。為了讓這個(gè)參數(shù)存在于我們的最終模板中,我們還將使用模板類(lèi)中定義的 add_paramter() 函數(shù):
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.",
))
接下來(lái)我們要看的是安全組。我們將完全按照我們對(duì) KeyPair 參數(shù)所做的那樣進(jìn)行。
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",
),
],
))
在下一節(jié)中,我們將不再需要登錄到我們的 EC2 實(shí)例并手動(dòng)安裝 helloworld.js 文件及其初始化腳本。為此,我們將利用 EC2 提供的 UserData 功能。
創(chuàng)建 EC2 實(shí)例時(shí),您可以通過(guò) UserData 可選參數(shù)提供一組命令,以便在虛擬機(jī)啟動(dòng)后運(yùn)行。
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"
]))
我們現(xiàn)在將專(zhuān)注于我們模板的主要資源,我們的 EC2 實(shí)例。創(chuàng)建實(shí)例需要提供用于標(biāo)識(shí)資源的名稱(chēng)、映像 ID、實(shí)例類(lèi)型、安全組、用于 SSH 訪問(wèn)的密鑰對(duì)以及用戶(hù)數(shù)據(jù)。
為了簡(jiǎn)單起見(jiàn),我們將對(duì) AMI ID ( ami-a4c7edb2 ) 和實(shí)例類(lèi)型 ( t2.micro ) 進(jìn)行硬編碼。
創(chuàng)建我們的 EC2 實(shí)例所需的其余信息是安全組信息和密鑰對(duì)名稱(chēng),我們之前通過(guò)定義參數(shù)和資源收集了這些信息。在 CloudFormation 中,您可以使用關(guān)鍵字 Ref 來(lái)引用模板的預(yù)先存在的小節(jié)。
在對(duì)流層中,這是通過(guò)調(diào)用 Ref() 函數(shù)來(lái)完成的。和以前一樣,我們將在 add_resource 函數(shù)的幫助下將結(jié)果輸出添加到我們的模板中:
t.add_resource(ec2.Instance(
"instance",
ImageId="ami-a4c7edb2",
InstanceType="t2.micro",
SecurityGroups=[Ref("SecurityGroup")],
KeyName=Ref("KeyPair"),
UserData=user_data,
))
在我們腳本的最后一部分,我們將專(zhuān)注于生成模板的輸出部分,當(dāng) CloudFormation 創(chuàng)建堆棧時(shí),該部分會(huì)被填充。
此選擇允許您打印出在堆棧啟動(dòng)期間計(jì)算的有用信息。在我們的例子中,有兩個(gè)有用的信息,訪問(wèn)我們的 Web 應(yīng)用程序的 URL 和實(shí)例的公共 IP 地址,以便我們可以根據(jù)需要通過(guò) SSH 訪問(wèn)它。
為了檢索此類(lèi)信息,CloudFormation 使用函數(shù) Fn::GetAtt 。在對(duì)流層,這被轉(zhuǎn)換為使用 GetAttr() 函數(shù):
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
]),
))
此時(shí),我們可以讓我們的腳本輸出我們生成的模板的最終結(jié)果:
print t.to_json()