목표
이전에 aws로 배포했던 웹 어플리케이션을 테라폼을 사용하여
인프라를 효율적으로 프로비저닝
참고
https://kalswn.tistory.com/entry/AWS-CodeDeploy-Flask <= flask_codedeploy/github Action
https://kalswn.tistory.com/entry/AWS-Flask-MySQL-react-%EB%B0%B0%ED%8F%AC <= AWS로 웹 어플리케이션 배포
#1 Terraform 사용자 생성
#2 Terraform VPC 생성
### vpc.tf ###
provider "aws" {
region = "ap-northeast-2"
}
# vpc
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "vpc"
}
}
# subnet (public)
resource "aws_subnet" "public_subnet_2a" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.10.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "public_subnet_2a"
}
}
resource "aws_subnet" "public_subnet_2c" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.30.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "public_subnet_2c"
}
}
# subnet (private)
resource "aws_subnet" "private_subnet_2a" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.20.0/24"
availability_zone = "ap-northeast-2a"
tags = {
Name = "private_subnet_2a"
}
}
resource "aws_subnet" "private_subnet_2c" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.40.0/24"
availability_zone = "ap-northeast-2c"
tags = {
Name = "private_subnet_2c"
}
}
# igw
resource "aws_internet_gateway" "vpc_igw" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "vpc_igw"
}
}
# route_table(public)
resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.vpc_igw.id
}
tags = {
Name = "public_route_table"
}
}
resource "aws_route_table_association" "public_rtb_a" {
subnet_id = aws_subnet.public_subnet_2a.id
route_table_id = aws_route_table.public_route_table.id
}
resource "aws_route_table_association" "public_rtb_b" {
subnet_id = aws_subnet.public_subnet_2c.id
route_table_id = aws_route_table.public_route_table.id
}
# route_table(private)
resource "aws_route_table" "private_route_table_1" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "private_route_table_1"
}
}
resource "aws_route_table_association" "private_rtb_1_a" {
subnet_id = aws_subnet.private_subnet_2a.id
route_table_id = aws_route_table.private_route_table_1.id
}
resource "aws_route_table" "private_route_table_2" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "private_route_table_2"
}
}
resource "aws_route_table_association" "private_rtb_2_a" {
subnet_id = aws_subnet.private_subnet_2c.id
route_table_id = aws_route_table.private_route_table_2.id
}
C:\aws>terraform apply
# front bucket 생성
### s3_front.tf ###
# bucket 생성
resource "aws_s3_bucket" "fs-front-bucket" {
bucket = "fs-front-bucket"
tags = {
Name = "fs-front-bucket"
}
}
# 정적 웹 사이트의 기본 index 페이지에 연결
resource "aws_s3_bucket_website_configuration" "fs-front-bucket" {
bucket = aws_s3_bucket.fs-front-bucket.id
index_document {
suffix = "index.html"
}
error_document {
key = "index.html"
}
}
resource "aws_s3_bucket_versioning" "fs-front-bucket" {
bucket = aws_s3_bucket.fs-front-bucket.id
versioning_configuration {
status = "Enabled"
}
}
# Amazon S3 버킷 소유권 컨트롤을 정의
resource "aws_s3_bucket_ownership_controls" "fs-front-bucket" {
bucket = aws_s3_bucket.fs-front-bucket.id
rule {
object_ownership = "BucketOwnerPreferred"
}
}
# Amazon S3 버킷에 대한 퍼블릭 액세스 허용
resource "aws_s3_bucket_public_access_block" "fs-front-bucket" {
bucket = aws_s3_bucket.fs-front-bucket.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
# ACL 공개 읽기 권한 설정 -정적 웹 호스팅을 위해
resource "aws_s3_bucket_acl" "fs-front-bucket" {
depends_on = [
aws_s3_bucket_ownership_controls.fs-front-bucket,
aws_s3_bucket_public_access_block.fs-front-bucket
]
bucket = aws_s3_bucket.fs-front-bucket.id
acl = "public-read"
}
output "fs-front-bucket-url" {
value = aws_s3_bucket.fs-front-bucket.website_endpoint
}
# aws_s3_bucket.FS_front_bucket will be created
+ resource "aws_s3_bucket" "FS_front_bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "FS_front_bucket"
+ bucket_domain_name = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Name" = "FS_front_bucket"
}
+ tags_all = {
+ "Name" = "FS_front_bucket"
}
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
}
# aws_s3_bucket_acl.FS_front_bucket will be created
+ resource "aws_s3_bucket_acl" "FS_front_bucket" {
+ acl = "public-read-write"
+ bucket = (known after apply)
+ id = (known after apply)
}
** ERROR
Error: validating S3 Bucket (fs_front_bucket) name: only lowercase alphanumeric characters and hyphens allowed in "fs_front_bucket"
< S3 버킷(fs_front_bucket) 이름 유효성 검사 중: "fs_front_bucket"에는 소문자 알파벳, 숫자, 및 하이픈만 허용됩니다.>
해결
철자 확인!
**ERROR
Error: creating S3 Bucket (fs-front-bucket) ACL: operation error S3: PutBucketAcl, https response error StatusCode: 403, RequestID: Q4PQR0K6M9SDRYHT, HostID: Sq7CAn32Jz3MrjxBVjabNIE2vKpPzxbDC+iJeV82/vdShgL3f6Xhxrc74BAWn61ecP6H/xRpc+SrROkwGIQMPg==, api error AccessDenied: Access Denied
resource "aws_s3_bucket_public_access_block" "fs-front-bucket" {
bucket = aws_s3_bucket.fs-front-bucket.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
resource "aws_s3_bucket_acl" "fs-front-bucket" {
depends_on = [
aws_s3_bucket_public_access_block.fs-front-bucket
]
bucket = aws_s3_bucket.fs-front-bucket.id
acl = "public-read"
}
- 브라우저에서 버킷 웹 사이트 엔드포인트로 접속
# back bucket 생성
### s3_back.tf ###
# bucket 생성
resource "aws_s3_bucket" "fs-back-bucket" {
bucket = "fs-back-bucket"
tags = {
Name = "fs-back-bucket"
}
}
resource "aws_s3_bucket_versioning" "fs-back-bucket" {
bucket = aws_s3_bucket.fs-back-bucket.id
versioning_configuration {
status = "Enabled"
}
}
# Amazon S3 버킷에 대한 퍼블릭 액세스 차단
resource "aws_s3_bucket_public_access_block" "fs-back-bucket" {
bucket = aws_s3_bucket.fs-back-bucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# bucket 정책 설정
resource "aws_s3_bucket_policy" "bucket-policy" {
bucket = aws_s3_bucket.fs-back-bucket.id
depends_on = [
aws_s3_bucket_public_access_block.fs-back-bucket
]
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codedeploy.amazonaws.com"
},
"Action": "s3:Get*",
"Resource": [
"${aws_s3_bucket.fs-back-bucket.arn}",
"${aws_s3_bucket.fs-back-bucket.arn}/*"
]
},
{
"Effect": "Allow",
"Principal": {
"Service": "codedeploy.amazonaws.com"
},
"Action": "s3:List*",
"Resource": [
"${aws_s3_bucket.fs-back-bucket.arn}"
],
"Condition": {
"StringEquals": {
"s3:prefix": [
"",
"folder/"
],
"s3:delimiter": [
"/"
]
}
}
}
]
}
EOF
}
output "fs-back-bucket-arn" {
value = aws_s3_bucket.fs-back-bucket.arn
}
**ERROR
Error: putting S3 Bucket (fs-back-bucket) Policy: operation error S3: PutBucketPolicy, https response error StatusCode: 400, RequestID: 7GG2M4SY0R7WJT29, HostID: iZTv28amEdl0ybcu5gX7rqTOVb1oVPhNUs9Yx5luVZ///6cbVRSjmGdwZ+jOlvkcT+LEv4HsRbc=, api error MalformedPolicy: Policy has invalid action
S3 버킷 정책이 올바른 형식이 아니거나 유효하지 않은 액션을 포함하고 있다는 것
S3 버킷 정책은 JSON 형식을 따라야 하며, 정책 내에 유효한 S3 액션을 지정
"Action": "s3:List",
"Action": "s3:List*", <= 수정
- back bucket 생성 확인
# flaskserver 인스턴스 생성
resource "aws_instance" "flaskServer" {
ami = "ami-086cae3329a3f7d75"
instance_type = "t2.micro"
subnet_id = aws_subnet.public_subnet_2a.id
key_name = aws_key_pair.my_key_pair.key_name
vpc_security_group_ids = [aws_security_group.flask_sg.id]
associate_public_ip_address = true
# 인스턴스에 IAM 역할 할당
iam_instance_profile = aws_iam_instance_profile.flask_profile.name
user_data = <<EOF
#!/bin/bash
sudo apt-get update
sudo spt-get install -y php
sudo systemctl restart apache2
sudo apt update
sudo apt install -y ruby-full
sudo apt install wget
cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
sudo ./install auto
EOF
tags = {
Name = "flaskServer"
}
}
# RSA private 키 생성
resource "tls_private_key" "my_key" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "aws_key_pair" "my_key_pair" {
key_name = "myKeyPair"
public_key = tls_private_key.my_key.public_key_openssh
}
resource "local_file" "my_downloads_key" {
filename = "myKeyPair.pem"
content = tls_private_key.my_key.private_key_pem
}
# 보안그룹 생성
resource "aws_security_group" "flask_sg" {
name_prefix = "flask_sg"
vpc_id = aws_vpc.vpc.id
}
resource "aws_security_group_rule" "flask_sg_ingress_ssh" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.flask_sg.id
}
resource "aws_security_group_rule" "flask_sg_ingress_http" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.flask_sg.id
}
resource "aws_security_group_rule" "flask_sg_ingress_flask" {
type = "ingress"
from_port = 5000
to_port = 5000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.flask_sg.id
}
resource "aws_security_group_rule" "flask_sg_egress_mysql" {
type = "egress"
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.flask_sg.id
}
resource "aws_security_group_rule" "flask_sg_egress_all" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.flask_sg.id
}
# 역할생성
resource "aws_iam_role" "flask_role" {
name = "flask_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
# AmazonS3FullAccess 정책을 연결
resource "aws_iam_role_policy_attachment" "s3_full_access" {
role = aws_iam_role.flask_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
# EC2 인스턴스 프로필 생성 및 역할 연결
resource "aws_iam_instance_profile" "flask_profile" {
name = "flask-instance-profile"
role = aws_iam_role.flask_role.name
}
output "flask_ip" {
value = aws_instance.flaskServer.public_ip
}
output "access_key" {
value = tls_private_key.my_key.private_key_pem
sensitive = true
}
**ERROR
Error: Inconsistent dependency lock file
│
│ The following dependency selections recorded in the lock file are inconsistent with the current configuration:
│ - provider registry.terraform.io/hashicorp/local: required by this configuration but no version is selected
│ - provider registry.terraform.io/hashicorp/tls: required by this configuration but no version is selected
│
│ To update the locked dependency selections to match a changed configuration, run:
│ terraform init -upgrade
일반적으로 Terraform에서 모듈 또는 공급자(Provider)의 종속성을 관리하는 데 사용되는 terraform.tfstate 또는 terraform.tfstate.backup 파일이 예상과 다르다는 것
버전 충돌이나 파일이 손상되었을 때 발생
해결
C:\aws>terraform init
**ERROR
Error: Output refers to sensitive values
│
│ on flaskInastance.tf line 115:
│ 115: output "access_key" {
민감한 정보는 로그로 출력해주지 않음
해결
방법 x
ssh client로 접속하기 위해 key 내용을 확인하고 싶었던건데
폴더에 파일로 저장되어 파일을 import에 접속
**ERROR
2023-11-27T07:09:28 ERROR [codedeploy-agent(10689)]: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Missing credentials - please check if this instance was started with an IAM instance profile
AWS CodeDeploy 에이전트가 실행 중인 인스턴스에서 AWS 자격 증명을 찾을 수 없을 때 발생
해결
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com" <= ec2 가 아닌 lambda 로 설정되어 있었음
},
"Action": "sts:AssumeRole"
}
]
}
#최종실행
#1 terraform init
C:\aws\FIRSTSTEP_Terraform>terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Finding latest version of hashicorp/local...
- Finding latest version of hashicorp/tls...
- Installing hashicorp/aws v5.26.0...
- Installed hashicorp/aws v5.26.0 (signed by HashiCorp)
- Installing hashicorp/local v2.4.0...
- Installed hashicorp/local v2.4.0 (signed by HashiCorp)
- Installing hashicorp/tls v4.0.4...
- Installed hashicorp/tls v4.0.4 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
#2 terraform apply
C:\aws\FIRSTSTEP_Terraform>terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# aws_iam_instance_profile.flask_profile will be created
+ resource "aws_iam_instance_profile" "flask_profile" {
+ arn = (known after apply)
+ create_date = (known after apply)
+ id = (known after apply)
+ name = "flask-instance-profile"
+ name_prefix = (known after apply)
+ path = "/"
+ role = "flask_role"
+ tags_all = (known after apply)
+ unique_id = (known after apply)
}
# aws_iam_role.flask_role will be created
+ resource "aws_iam_role" "flask_role" {
+ arn = (known after apply)
+ assume_role_policy = jsonencode(
{
+ Statement = [
+ {
+ Action = "sts:AssumeRole"
+ Effect = "Allow"
+ Principal = {
+ Service = "ec2.amazonaws.com"
}
},
]
+ Version = "2012-10-17"
}
)
...
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: <= "yes"
Apply complete! Resources: 36 added, 0 changed, 0 destroyed.
Outputs:
access_key = <sensitive>
flask_ip = "52.78.4.158"
fs-back-bucket-arn = "arn:aws:s3:::fs-back-bucket"
#flaskserver 인스턴스로 접속
# flaskserver 인스턴스 초기 설정 CodeDeploy agent 설치
sudo apt-get update
sudo apt-get install -y php
sudo systemctl restart apache2
sudo apt update
sudo apt install -y ruby-full
sudo apt install wget
cd /home/ubuntu
# wget https://bucket-name.s3.region-identifier.amazonaws.com/latest/install
# 서울 region의 경우
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto
ubuntu@ip-10-0-10-149:~$ sudo service codedeploy-agent status
● codedeploy-agent.service - LSB: AWS CodeDeploy Host Agent
Loaded: loaded (/etc/init.d/codedeploy-agent; generated)
Active: active (running) since Mon 2023-11-27 09:57:12 UTC; 8s ago
Docs: man:systemd-sysv-generator(8)
Process: 9813 ExecStart=/etc/init.d/codedeploy-agent start (code=exited, status=0/SUCCESS)
Tasks: 2 (limit: 1121)
Memory: 58.8M
CPU: 1.095s
CGroup: /system.slice/codedeploy-agent.service
├─9843 "codedeploy-agent: master 9843" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
└─9845 "codedeploy-agent: InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller of m>
Nov 27 09:57:12 ip-10-0-10-149 systemd[1]: Starting LSB: AWS CodeDeploy Host Agent...
Nov 27 09:57:12 ip-10-0-10-149 codedeploy-agent[9813]: Starting codedeploy-agent:
Nov 27 09:57:12 ip-10-0-10-149 systemd[1]: Started LSB: AWS CodeDeploy Host Agent.
lines 1-15/15 (END)
- 생성 확인
Terraform
- VPC
- S3 bucket front / back
- flaskServer 인스턴스
생성 됨을 확인
# 리소스 정리
terraform destroy