[AWS] CloudFormation
CloudFormation์ ์ธํ๋ผ ๊ตฌ์ฑ์์๋ฅผ ๋ฏธ๋ฆฌ ์ค์ ํด์ ์์คํ
์ ๊ทธ๋๊ทธ๋ ๋น ๋ฅด๊ณ ํธ๋ฆฌํ๊ฒ ๊ตฌ์ถํ ์ ์๊ฒ ๋์์ฃผ๋ ์๋น์ค๋ค.
์๋ฅผ ๋ค์ด ์ ์ ํ์ด์ง๋ S3๋ฅผ ๋ง๋ค๊ณ , Cloudfront๋ ๋ง๋ค์ด์ ์ค์ ํด์ค์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ECS๋ก ์๋ฒ๋ฅผ ๊ตฌ์ถํ ๊ฒฝ์ฐ์๋ ECS ํด๋ฌ์คํฐ ๋ง๋ค๊ณ , ์์
์ ์ํ๊ณ , ํ๊ฒ๊ทธ๋ฃน๋ง๋ค๊ณ , ๋ก๋๋ฐธ๋ฐ์ ๋ง๋ค๊ณ , ECS ์๋น์ค ๋ง๋ค์ด์ ๋์ฐ๋ ๋ฑ์ ๋ง์ ๋ฆฌ์์ค ์์ฑ ๋ฐ ์ค์ ์์
์ด ํ์ํ๋ค.
์ด๋ฐ ์์
์ ํ๋ฒ๋ง ํ๋ ๊ฒ์ด๋ผ๋ฉด ๊ด์ฐฎ๋ค. ํ์ง๋ง ์ฌ๋ฌ๊ฐ์ ์์คํ
์ ๋ง๋ค ๊ฒฝ์ฐ์๋ ์ด๊ฑธ 2-3๋ฒ ๋ฐ๋ณตํ ์๋ ์๊ณ , ๊ทธ ์ด์์ ํ ์๋ ์๋ค.
์ด๊ฑธ ๋งค๋ฒ ํ๋ํ๋ ํ๋ ค๋ฉด ์ ๋ง ๋ฒ๊ฑฐ๋กญ๊ธฐ ๊ทธ์ง์์ ๊ฒ์ด๋ค. cloudformation์ ์ด๊ฑธ ๋ชจ๋ํํด์ค๋ค.
๊ธฐ๋ฅ์ ์ผ๋ก๋ ์คํ์์ค Terraform๊ณผ ๋น์ทํ๋ค๊ณ ํ ์ ์๋ค.
๋น์ฉ
๋น์ฐํ ๊ณต์ง๋ ์๋๋ค.
์์ฒญ ๋น์ธ๋ค๊ณ ํ ์ ๋๋ ์๋๋ฐ, ๋ถ๊ณผ ๋ฐฉ์์ด ์กฐ๊ธ ๋
ํนํ๋ค.
https://aws.amazon.com/ko/cloudformation/pricing/
ํ ํ๋ฆฟ ์์ฑ
CloudFormation์์ ๊ฐ์ฅ ์ค์ํ๊ฒ ํ
ํ๋ฆฟ์ด๋ค.
ํ
ํ๋ฆฟ์์ค๋ฅผ ์์ฑํด์ ์ด๋ค ๋ฆฌ์์ค๋ฅผ ์ด๋ป๊ฒ ๋ง๋ค๊ณ , ์ด๋ป๊ฒ ์ค์ ํ ์ง๋ฅผ ์ ๋ถ ์ง์ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋๋ S3์ CloudFront๋ง ๋ง๋ค์ด์ฃผ๋ ๋น๊ต์ ๊ฐ๋จํ ํํ์ ํ ํ๋ฆฟ์ ์์ฑํ๋ ค ํ๋ค.
ํ
ํ๋ฆฟ ์ฌ์ฉ๋ฒ์ ์ฌ๊ธฐ
https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/template-guide.html
์ํ์ ์ฌ๊ธฐ์ ๋ค์ด๋ฐ์ ์ ์๋ค.
์ฐธ๊ณ ํด์ ์ง๋ฉด ์ข๋ค. ๋ฑ ๋ง๋๊ฒ ์์ผ๋ฉด ๊ทธ๊ฑฐ ๊ทธ๋ฅ ๊ทธ๋๋ก ์จ๋ ๋๋ค.
https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/cfn-sample-templates.html
์ผ๋จ ํ
ํ๋ฆฟ์ ๊ธฐ์กด์ ์ธ ํํ๋ ์ด๋ ๋ค.
yaml๋ ์์ง๋ง ๋๋ json์ด ๋ ํธํด์ ์ด๊ฑธ๋ก ํ๋ค.
{
"AWSTemplateFormatVersion": "version date",
"Description": "JSON string",
"Metadata": {},
"Parameters": {},
"Rules": {},
"Mappings": {},
"Conditions": {},
"Transform": {},
"Resources": {},
"Outputs": {}
}
ํ๋์ฉ ์ง์ด๋ณด๊ฒ ๋ค.
AWSTemplateFormatVersion, Description
์ผ๋จ ๋ฒ์ ์ "2010-09-09"๋ก ํ๋ฉด ๋๋ค.
์ค๋ช
๋ ์์๋ณผ ์๋ง ์๊ฒ ์จ์ฃผ๋ฉด ๋๋ค. ํ๊ธ์ ๋ฃ์ผ๋ฉด ์ค๋ฅ๋ ๋์ง ์์ง๋ง ????๋ก ๊นจ์ ธ์ ๋์จ๋ค.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "์ ์ ์ฌ์ดํธ ๋ฐฐํฌ",Metadata
๋ฉํ๋ฐ์ดํฐ๋ ๋น์ฅ ํ์ํ์ง๋ ์๊ณ , ๊ทธ๋ ๊ฒ ์ค์ํ์ง๋ ์์์ ์ง๋๊ฐ๋ค.
Parameters
ํ๋ผ๋ฏธํฐ๋ ๋ง ๊ทธ๋๋ก ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ๋ ๋ถ๋ถ์ด๋ค.
"Parameters": {
"BucketName": {
"Type": "String",
"Description": "bucket name to generate",
"AllowedPattern": "(?!-)[a-zA-Z0-9-.]{1,63}(?<!-)",
"ConstraintDescription": "must be a valid S3 bucket name."
}
},
๋ด ๊ฒฝ์ฐ์๋ ๋ฒํท์ด๋ฆ์ ๊ทธ๋๊ทธ๋ ๋ฐ์์ ๊ทธ๊ฑธ๋ก ์์ฑํ๊ฒ ํ๋ ค๊ณ ํ๋ค.
ํด๋น ๊ฐ์ ๋ํ ํ๋ผ๋ฏธํฐ๊ฐ์ด๋ค.
Mappings
๋งคํ์ ๋ง ๊ทธ๋๋ ๋งคํ๊ฐ๋ค์ ์ง์ ํด์ฃผ๋ ๋ถ๋ถ์ด๋ค. ํค-๊ฐ์ ํํ๋ก ์ด๋ฐ์ ๋ฐ ๊ฐ๋ค์ ์ง์ ํด๋๊ณ Fn::FindInMap๋ผ๋ ํจ์๋ก ๊บผ๋ด์ฌ ์ ์๋ค.
๊ผญ ํ์ํ๊ฑด ์๋์ง๋ง, ์์ผ๋ฉด ํธ๋ฆฌํ๋ค.
๋ด ๊ฒฝ์ฐ์๋ ๊ทธ๋๊ทธ๋ ๋ฆฌ์ ์ ๋ง๋ S3 ๋๋ฉ์ธ ๋งํฌ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ฌ์ฉํ ๊ฒ์ด๋ค.
"Mappings": {๏ปฟ
"Region2S3WebsiteSuffix": {
"af-south-1": { "Suffix": ".s3-website.af-south-1.amazonaws.com" },
"ap-east-1": { "Suffix": ".s3-website.ap-east-1.amazonaws.com" },
"ap-northeast-1": {
"Suffix": ".s3-website-ap-northeast-1.amazonaws.com"
},
"ap-northeast-2": {
"Suffix": ".s3-website.ap-northeast-2.amazonaws.com"
},
"ap-northeast-3": {
"Suffix": ".s3-website.ap-northeast-3.amazonaws.com"
},
"ap-south-1": { "Suffix": ".s3-website.ap-south-1.amazonaws.com" },
"ap-southeast-1": {
"Suffix": ".s3-website-ap-southeast-1.amazonaws.com"
},
"ap-southeast-2": {
"Suffix": ".s3-website-ap-southeast-2.amazonaws.com"
},
"ca-central-1": { "Suffix": ".s3-website.ca-central-1.amazonaws.com" },
"cn-north-1": { "Suffix": ".s3-website.cn-north-1.amazonaws.com.cn" },
"cn-northwest-1": {
"Suffix": ".s3-website.cn-northwest-1.amazonaws.com.cn"
},
"eu-central-1": { "Suffix": ".s3-website.eu-central-1.amazonaws.com" },
"eu-north-1": { "Suffix": ".s3-website.eu-north-1.amazonaws.com" },
"eu-south-1": { "Suffix": ".s3-website.eu-south-1.amazonaws.com" },
"eu-west-1": { "Suffix": ".s3-website-eu-west-1.amazonaws.com" },
"eu-west-2": { "Suffix": ".s3-website.eu-west-2.amazonaws.com" },
"eu-west-3": { "Suffix": ".s3-website.eu-west-3.amazonaws.com" },
"me-south-1": { "Suffix": ".s3-website.me-south-1.amazonaws.com" },
"sa-east-1": { "Suffix": ".s3-website-sa-east-1.amazonaws.com" },
"us-east-1": { "Suffix": ".s3-website-us-east-1.amazonaws.com" },
"us-east-2": { "Suffix": ".s3-website.us-east-2.amazonaws.com" },
"us-west-1": { "Suffix": ".s3-website-us-west-1.amazonaws.com" },
"us-west-2": { "Suffix": ".s3-website-us-west-2.amazonaws.com" }
}
},Resources
๊ทธ๋ฆฌ๊ณ ๋ฆฌ์์ค๊ฐ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ด๋ค.
๊ฐ AWS ๋ฆฌ์์ค๋ค์ ์ด๋ค ์ค์ ๊ฐ์ผ๋ก ์์ฑํ ๊ฒ์ธ์ง๋ฅผ ์ฌ๊ธฐ์ ์ ์ํ๋ค.
๊ฐ ๋ฆฌ์์ค์ ๋ํ๊ฑด ๋ค์ ๋ฌธ์๋ฅผ ์ฐธ์กฐํ๋ฉด ์ข๋ค.
https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
๋ด ๊ฒฝ์ฐ์๋ ๋จผ์ S3๋ฅผ ๋ง๋ ๋ค.
"S3BucketForWebsiteContent": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "PublicRead",
"WebsiteConfiguration": {
"IndexDocument": "index.html",
"ErrorDocument": "error.html"
},
"BucketName": { "Ref": "BucketName" }
}
},
์ฌ๊ธฐ์ "Type": "AWS::S3::Bucket"์ ๋ฒํท์ ์์ฑํ๋ ๋ช
๋ น์ด๋ ๋ป์ด๋ค.
{ "Ref": "BucketName" }๋ ๋ณ์ "BucketName"๋ฅผ ์ฐธ์กฐํ๋ค๋ ๊ฒ์ธ๋ฐ, ์์์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ๊ทธ "BucketName"๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
๊ทธ๋ฅ ๋ฒํท๋ช
๋ฐ์๋๋ก ๋ง๋ค์ด์ฃผ๋๊ฒ ๋ค๋ค.
๊ทธ๋ค์์๋ CloudFront ๋ฐฐํฌ๋ฅผ ์์ฑํ๋ค.
"WebsiteCDN": {
"Type": "AWS::CloudFront::Distribution",
"Properties": {
"DistributionConfig": {
"Comment": "CDN for S3-backed website",
"Enabled": "true",
"DefaultCacheBehavior": {
"ForwardedValues": { "QueryString": "true" },
"TargetOriginId": "only-origin",
"ViewerProtocolPolicy": "allow-all"
},
"DefaultRootObject": "index.html",
"Origins": [
{
"CustomOriginConfig": {
"HTTPPort": "80",
"HTTPSPort": "443",
"OriginProtocolPolicy": "http-only"
},
"DomainName": {
"Fn::Join": [
"",
[
{ "Ref": "S3BucketForWebsiteContent" },
{
"Fn::FindInMap": [
"Region2S3WebsiteSuffix",
{ "Ref": "AWS::Region" },
"Suffix"
]
}
]
]
},
"Id": "only-origin"
}
]
}
}
}
์กฐ๊ธ ๋ ์ค์ ์ด ๋ง๊ธด ํ๋ฐ, DomainName ์์ฑ์ ๋ฐฉ๊ธ ๋ง๋ S3 ๋ฒํท ๊ฒฝ๋ก๋ฅผ ๋ฃ์ด์ฃผ๋ ๊ฒ๋ง ์์๋ ๋๋ค.
๋๋จธ์ง๋ ๊ฑฐ์ ๊ธฐ๋ณธ๊ฐ์ด๋ค.
Outputs
์์ํ์ ๋ง ๊ทธ๋๋ก ์ถ๋ ฅ์ด๋ค.
๋ฐฐํฌ๋ Cloudfront์ ID๋ฅผ ์๊ณ ์ถ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ ์ ์๋ค.
"Outputs": {
"WebsiteID": {
"Value": {
"Ref": "WebsiteCDN"
},
"Description": "The URL of the newly created website"
}
}
์ ์ฒด ํ ํ๋ฆฟ ์์ค
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "์ ์ ์ฌ์ดํธ ๋ฐฐํฌ",
"Metadata": {},
"Parameters": {
"BucketName": {
"Type": "String",
"Description": "bucket name to generate",
"AllowedPattern": "(?!-)[a-zA-Z0-9-.]{1,63}(?<!-)",
"ConstraintDescription": "must be a valid S3 bucket name."
}
},
"Rules": {},
"Mappings": {
"Region2S3WebsiteSuffix": {
"af-south-1": { "Suffix": ".s3-website.af-south-1.amazonaws.com" },
"ap-east-1": { "Suffix": ".s3-website.ap-east-1.amazonaws.com" },
"ap-northeast-1": {
"Suffix": ".s3-website-ap-northeast-1.amazonaws.com"
},
"ap-northeast-2": {
"Suffix": ".s3-website.ap-northeast-2.amazonaws.com"
},
"ap-northeast-3": {
"Suffix": ".s3-website.ap-northeast-3.amazonaws.com"
},
"ap-south-1": { "Suffix": ".s3-website.ap-south-1.amazonaws.com" },
"ap-southeast-1": {
"Suffix": ".s3-website-ap-southeast-1.amazonaws.com"
},
"ap-southeast-2": {
"Suffix": ".s3-website-ap-southeast-2.amazonaws.com"
},
"ca-central-1": { "Suffix": ".s3-website.ca-central-1.amazonaws.com" },
"cn-north-1": { "Suffix": ".s3-website.cn-north-1.amazonaws.com.cn" },
"cn-northwest-1": {
"Suffix": ".s3-website.cn-northwest-1.amazonaws.com.cn"
},
"eu-central-1": { "Suffix": ".s3-website.eu-central-1.amazonaws.com" },
"eu-north-1": { "Suffix": ".s3-website.eu-north-1.amazonaws.com" },
"eu-south-1": { "Suffix": ".s3-website.eu-south-1.amazonaws.com" },
"eu-west-1": { "Suffix": ".s3-website-eu-west-1.amazonaws.com" },
"eu-west-2": { "Suffix": ".s3-website.eu-west-2.amazonaws.com" },
"eu-west-3": { "Suffix": ".s3-website.eu-west-3.amazonaws.com" },
"me-south-1": { "Suffix": ".s3-website.me-south-1.amazonaws.com" },
"sa-east-1": { "Suffix": ".s3-website-sa-east-1.amazonaws.com" },
"us-east-1": { "Suffix": ".s3-website-us-east-1.amazonaws.com" },
"us-east-2": { "Suffix": ".s3-website.us-east-2.amazonaws.com" },
"us-west-1": { "Suffix": ".s3-website-us-west-1.amazonaws.com" },
"us-west-2": { "Suffix": ".s3-website-us-west-2.amazonaws.com" }
}
},
"Resources": {
"S3BucketForWebsiteContent": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "PublicRead",
"WebsiteConfiguration": {
"IndexDocument": "index.html",
"ErrorDocument": "error.html"
},
"BucketName": { "Ref": "BucketName" }
}
},
"WebsiteCDN": {
"Type": "AWS::CloudFront::Distribution",
"Properties": {
"DistributionConfig": {
"Comment": "CDN for S3-backed website",
"Enabled": "true",
"DefaultCacheBehavior": {
"ForwardedValues": { "QueryString": "true" },
"TargetOriginId": "only-origin",
"ViewerProtocolPolicy": "allow-all"
},
"DefaultRootObject": "index.html",
"Origins": [
{
"CustomOriginConfig": {
"HTTPPort": "80",
"HTTPSPort": "443",
"OriginProtocolPolicy": "http-only"
},
"DomainName": {
"Fn::Join": [
"",
[
{ "Ref": "S3BucketForWebsiteContent" },
{
"Fn::FindInMap": [
"Region2S3WebsiteSuffix",
{ "Ref": "AWS::Region" },
"Suffix"
]
}
]
]
},
"Id": "only-origin"
}
]
}
}
}
},
"Outputs": {
"WebsiteID": {
"Value": {
"Ref": "WebsiteCDN"
},
"Description": "The URL of the newly created website"
}
}
}์คํ ์์ฑ
๊ทธ๋ผ ์ด์ ์คํ์ ๋ง๋ค์ด๋ณด์.
์คํ์ CloudFormation์ ํตํด ์์ฑ๋๋ ๋ฆฌ์์ค์ ๋ฌถ์ ๋จ์๋ค.
๋ฆฌ์์ค๋ค์ ํ๋์ ๋จ์๋ก ๋ฌถ์ด์ ํ๋ฒ์ ๋ง๋ค๊ฑฐ๋ ์ง์ธ์ ์๊ฒ ํด์ค๋ค.
์์ฑ ํ์ด์ง๋ก ์ด๋ํด, ๋ฐฉ๊ธ ์์ฑํ ํ ํ๋ฆฟ ์์ค๋ฅผ ์ ๋ก๋ํ๋ค.

์ด๋ฆ์ ์ ์ง๊ณ , ํ ํ๋ฆฟ์์ ์๊ตฌํ๋ ํ๋ผ๋ฏธํฐ๊ฐ์ ๋ฃ์ด์ค๋ค.
์๊น ํ๋ผ๋ฏธํฐ๋ก ์ง์ ํ ๋ฒํท๋ช
์ด๋ค.
๋ค๋ฅธ๊ฑด ์ฅ์ฅ ๋๊ฒจ๋ ๋๋ค.

๊ทธ๋ ๊ฒ ํด์ ๋ง๋ค์ด์ก๋ค๋ฉด

ํ๋๋ผ๋ ์คํจํ๋ฉด ํต์งธ๋ก ๋กค๋ฐฑ์ด ๋๊ณ

์ ๋๋ค๋ฉด, ์ด๋ ๊ฒ ํ๋์ฉ ๋ค ๊น์์ค ๊ฒ์ด๋ค.

๋ฒํท๋ ๋ง๋ค์ด์ก๊ณ

CloudFront๋ ๋ง๋ค์ด์ก๋ค.

์ด๋๋ก ๋ฒํท์ ๋ฆฌ์์ค๋ฅผ ์ฌ๋ ค๋ณด๋ฉด, ์ ๋ ๊ฒ์ด๋ค.

์คํ ์ญ์
Cloudformation์ ์ฅ์ ์ค ํ๋๋, ์ญ์ ๋ ํ๋์ ์คํ ๋จ์๋ก ์ฒ๋ฆฌํ ์ ์๋ค๋ ๊ฒ์ด๋ค.

๊ทธ๋์ ์คํ์ ์ญ์ ํ๋ฉด ๊ทธ ์์ ํฌํจ๋ ๋ฆฌ์์ค๋ค๋ ์ญ์์ผ๋ก ์ญ์ ๊ฐ ๋๋ค.

์ฐธ์กฐ
https://docs.aws.amazon.com/ko_kr/AWSCloudFormation/latest/UserGuide/Welcome.html