[AWS-3Tier] Terraform을 이용한 VPC 및 인스턴스 생성
1. 기본설정
2. Terraform을 이용한 VPC 생성
3. Terraform을 이용한 instance 생성
1. 기본 설정
앞서 Terraform의 기본 구성을 끝냈으니 이제 리소스 생성을 위한 기본설정을 만들 차례이다.
먼저, Terraform의 모~~~~~든 리소스 구성은 전부 아래의 링크에서 볼 수 있다.
영어 못 해도 된다...그냥 페이지에서 마우스 우클릭하고 자동번역으로만 봐도 충분하다.
https://registry.terraform.io/providers/hashicorp/aws/latest/docs
https://registry.terraform.io/providers/hashicorp/aws/latest/docs
registry.terraform.io
나는 user밑에 terraform 폴더를 만든 뒤, 내부에 또 ssw라는 폴더를 하나 생성해서 진행할 예정이다.
terraform은 폴더 단위로 실행되기 때문에, 요렇게 폴더를 나눠놓으면 보다 관리가 편리하다.
그다음 이렇게 폴더를 열어주면 된다.
먼저 key.tf 파일을 만들어 준다.
파일의 이름 앞에 00~20 이런 식으로 넘버링을 해주는 게 알아보기가 쉬우니 참고...
provider는 aws
region은 ap-northeast-2 를 선택해주면 된다
provider "aws" { #aws를 사용하니까 aws를 선택
region = "ap-northeast-2" #리전은 우리가 사용한 region
}
# 키 설정
resource "aws_key_pair" "ssw_key" { #키 이름
key_name = "ssw-key" #실제로 키가 어떻게 쓰일 지
public_key = file("../../.ssh/ssw.key.pub") #키의 위치
}
여기서 키의 위치 부분만 주의해서 보면 된다.
해당 폴더가 users/user/terraform/ssw이니 ../../ 을 통해 두 번 올라간 후 .ssh폴더의 ssw.key.pub을 사용하겠다는 뜻임...
암튼 폴더랑 키 위치가 멀면 헷갈린다 ㅎㅎ...
여기까지가 기본 설정 끝!!!!
아 그리고 provider를 지정해줘야 terraform init이 가능하다!!!!!
2. Terraform을 이용한 VPC 설정
똑같이 tf 파일을 생성해준다.
resource "aws_vpc" "test_vpc" { #vpc의 이름
cidr_block = "10.0.0.0/16" #사용할 CIDR
instance_tenancy = "default" #호스트에서 인스턴스를 공유하도록 하는 일
enable_dns_hostnames = true #VPC에서 DNS호스트 이름을 활성화/비활성화 하는 플래그
enable_dns_support = true #vpc에서 DNS지원을 활성화/비활성화 하는 플래그
tags = {
"Name" = "test-vpc" #태그는 걍 tag인데..앵간하면 쓰는 게 편하긴 하다.
}
}
2.1 Subnet 설정
내가 terraform이 편하다고 느꼈던 가장 큰 이유....서브넷이나 라우트 테이블 만들 때 하나하나 노가다 할 필요 없이 한 페이지에서 전부 생성/관리 할 수 있다.
IaC의 장점이기도 하고..쓸 때마다 감동받는 기능이다.
resource "aws_subnet" "test_pub1" { #서브넷의 이름
vpc_id = aws_vpc.test_vpc.id #참조하는 vpc cidr 블록의 이름
cidr_block = "10.0.0.0/24" # cidr
availability_zone = "ap-northeast-2a" #가용영역
tags = {
"Name" = "test-pub1" #서브넷의 태그
}
}
여기서 하나 짚고 넘어가야 할 부분은 vpc_id 이다.
앞부분에서 생성한 test_vpc 리소스를 참조하는 부분인데, "[참조할 리소스].[리소스 이름].id" 의 형식으로 참조한다.
꼭 맨 끝이 id로 끝나지는 않지만(.arn 등등 뒤에 나올 예시들이 있다.) 그 앞의 형식은 동일하니 기억해놓도록 하자
우리의 아키텍쳐 상 필요한 서브넷은 총 8개 이므로 8개 만들어준다.
2.2 internet gateway / nat gateway 설정
ig는 서브넷이 외부와 통신을 할 수 있게 해주는 gateway이다.
퍼블릭 서브넷들을 라우트테이블로 묶어 ig를 연결해주며, 이 라우트테이블에 대부분 bastion이 들어간다.
ng는 서브넷들끼리 내부 통신을 하기 위한 gateway이다.
프라이빗 서브넷들을 라우트테이블로 묶어 ng를 달아준다. ng가 없으면 베스쳔을 통해 접속해도 애플리케이션의 업데이트나 install 등이 실행되지 않는다.
#internet_gateway만들기
resource "aws_internet_gateway" "test_ig" { #ig의 이름
vpc_id = aws_vpc.test_vpc.id #참조할 vpc
tags = {
"Name" = "test-ig" #tag
}
}
#ig가 포함된 라우트테이블의 생성
resource "aws_route_table" "test_rt" { #ig가 포함된 라우트테이블의 이름
vpc_id = aws_vpc.test_vpc.id #참조하는 VPC
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.test_ig.id #참조하는 ig
}
tags = {
"Name" = "test-rt"
}
}
#EIP 만들기
resource "aws_eip" "cd_ngw_ip" {
vpc = true
}
#Nat_gateway 만들기
resource "aws_nat_gateway" "test_ngw" {
allocation_id = aws_eip.test_ngw_ip.id
subnet_id = aws_subnet.test_pub1.id
tags = {
"Name" = "test-ngw"
}
}
#Nat_gateway가 포함된 Route table
resource "aws_route_table" "test_ngwrt" {
vpc_id = aws_vpc.test_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_nat_gateway.test_ngw.id
}
tags = {
"Name" = "test-ngwrt"
}
}
2.3 라우트 테이블 묶어주기
앞서 언급한 것처럼, public과 private서브넷들을 구분하여 라우트 테이블에 각각 묶어주는 작업이다.
사실 이렇게 중복되는 게 많은 부분들은 후에 나올 변수처리를 통하여 좀 더 간편하고 효율적으로 진행할 수 있지만,
일단은 노가다로 진행해보자.
resource "aws_route_table_association" "test_igas_pub1" { #라우트테이블 연결 명령
subnet_id = aws_subnet.test_pub1.id #묶어줄 서브넷
route_table_id = aws_route_table.test_rt.id #서브넷이 들어갈 라우트테이블
}
##########프라이빗 라우트 테이블 생성#############
resource "aws_route_table_association" "test_ngwass_priweb1" {
subnet_id = aws_subnet.test_priweb1.id
route_table_id = aws_route_table.test_ngwrt.id
}
2.4 보안그룹 생성
보안그룹은 서버끼리 통신을 할 때 가장 중요한 리소스 중 하나이다. 말 그대로 보안을 담당하는 역할이며, 보안그룹은 최소한으로만 열어놓는 것이 가장 좋다.
때문에 aws를 처음 배울 때는 보안그룹이 정말 까다로웠다....이게 참..뭐가 뭘 열어주는지 알 수가 있어야지 ㅠㅠ
내 기준에서 그나마 제일 확실하고 어느정도 감을 잡을 수 있는 방법은 아키텍쳐를 그려놓고 어떤 서버가 무슨 포트를 사용하는지, 어떤 트래픽을 받아줘야 하는지를 꾸준히 보는 것이었다.
사실 이런 면 때문에 terraform에서 리소스를 구성할 때도 보안그룹이 가장 까다롭긴 하다..
#베스쳔 보안그룹 만들기
resource "aws_security_group" "sg_bastion" {
name = "test-sg-bastion" #보안그룹의 이름
description = "sg for bastion" #보안그룹에 대한 설명
vpc_id = aws_vpc.test_vpc.id #참조하는 vpc
ingress = [ #수신 규칙에 대한 구성, 여러개 지정 가능
{
description = "ssh" #보안그룹에 대한 설명
from_port = 22 #시작 포트
to_port = 22 #끝 포트
protocol = "tcp" #프로토콜 이름
cidr_blocks = ["0.0.0.0/0"] #cidr블록 목록
ipv6_cidr_blocks = ["::/0"]
prefix_list_ids = null
security_groups = null #보안그룹 이름 목록
self = null
},
{
description = "jenkins"
from_port = 60010
to_port = 60010
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
prefix_list_ids = null
security_groups = null
self = null
}
]
egress = [
{
description = ""
from_port = 0
to_port = 0
protocol = "-1" #여기서 -1을 선택하는 경우 from_port와 to_port의 값이 0이 되어야함.
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
prefix_list_ids = null
security_groups = null
self = null
}
]
tags = {
Name = "test-sg-bastion"
}
}
3. Terraform을 이용한 instance 생성
복잡한 기본 구성은 모두 끝났고..이제 인스턴스를 생성할 차례이다.
사실 맨 처음에 올렸던 아키텍쳐의 구조상 인스턴스에 유저데이터도 넣고 이것저것 다 넣어야 하지만....
일단 그냥 뼈대만 만들어 놓고 차근차근 수정할까 한다. 사실 테라폼 파일 바꾸는 건 쉬우니깐..
3.1 bastion 생성
우선 bastion.tf부터 만들어준다.
베스쳔은 public subnet에 위치하며, private subnet에 tomcat이나 apache등을 깔 때 사용된다.
주로 보안의 목적으로 베스쳔만을 통해 private subnet에 접근하는 것을 권장한다.
resource "aws_instance" "test_bastion" {
ami = "ami-0263588f2531a56bd" #ami 정보(Linux18.04)
instance_type = "t2.micro" #인스턴스 타입
key_name = "ssw-key" #key의 이름
vpc_security_group_ids = [aws_security_group.sg_bastion.id] #보안그룹
availability_zone = "ap-northeast-2a" #리전
private_ip = "10.0.0.11" #고정될 private ip
subnet_id = aws_subnet.test_pub1.id #베스쳔이 생성될 서브넷을 지정
#iam_instance_profile = "jump99500" #iam_role
#user_data = file("./bastion_key.sh") #유저데이터
}
# Elastic IP 할당
resource "aws_eip" "test_bastion_ip" {
vpc = true
instance = aws_instance.test_bastion.id
depends_on = [aws_internet_gateway.test_ig]
}
output "bastion_public_ip" {
value = aws_eip.test_bastion_ip.public_ip
}
3.2 web, was 생성
베스쳔을 만들었으니, 나머지 web, was를 생성할 차례이다.
여기서 생성된 web과 was 인스턴스들의 이미지를 따서 오토스케일링이 진행된다.
생성 방법은 베스쳔과 거의 동일하다(was만 인스턴스 타입이 다름)
resource "aws_instance" "web" {
ami = "ami-0263588f2531a56bd"
instance_type = "t2.micro"
key_name = "ssw-key"
vpc_security_group_ids = [aws_security_group.sg_web.id]
availability_zone = "ap-northeast-2a"
private_ip = "10.0.2.11"
subnet_id = aws_subnet.test_priweb1.id
/*
user_data =<<-EOF
#!/bin/bash
sudo su -
sed -i "s/#Port 22/Port 22/g" /etc/ssh/sshd_config
systemctl restart sshd
EOF
*/
tags = {
"Name" = "test-web"
}
}
resource "aws_instance" "was" {
ami = "ami-0263588f2531a56bd"
instance_type = "t3.medium"
key_name = "ssw-key"
vpc_security_group_ids = [aws_security_group.sg_was.id]
availability_zone = "ap-northeast-2a"
private_ip = "10.0.4.11"
subnet_id = aws_subnet.test_priwas1.id
/*
user_data =<<-EOF
#!/bin/bash
sudo su -
sed -i "s/#Port 22/Port 22/g" /etc/ssh/sshd_config
systemctl restart sshd
EOF
*/
tags = {
"Name" = "test-was"
}
}
여기까지 인스턴스 생성을 마무리지어봤다.
테라폼으로 하면 참 빠른데 블로그랑 같이 하면 ㅎ....
그래도 정리 한 번 더 할 수 있는 느낌이라 괜찮은 것 같다.
아 그리고!!!! vscode 에서 터미널을 실행시키는 방법은 아래와 같다
위의 화면까지 실행한 뒤, terraform init -> terraform plan -> (오류가 없다면) -> terraform apply 순으로 진행시켜 주면 된다.
terraform apply --auto-approve까지 치면 중간에 yes를 입력할 필요없이 쭉~~~진행됨!
terraform destroy --auto-approve도 마찬가지이다.
그리고 그냥 큰 뼈대만 다 넣어놓고 직접 프록시나 tomcat 설치 등을 해줄 지, 아니면 앤서블로 다 할지..
일단 고민 좀 해봐야겠다. 앤서블로 하면 인스턴스부터의 내용들을 살짝 뜯어고쳐야 할 수도 있어서ㅋ.ㅋ