terrafom tutorial문서에서 아주 간단하게 사용방법을 알아봤다. 이제 좀 복잡한 인프라를 구성하면서 테라폼의 다양한 기능들을 살펴보려 한다. 이 문서는 terrafom tutorial를 기반으로 개선을 하는게 내용이므로 링크의 문서를 참고해야 한다.
테라폼으로 구성할 인프라는 아래와 같다.
2개의 가용영역을 사용한다. : 현재 서울리전은 3개의 가용영역을 가지고 있지만, 지금은 2개의 가용영역만 사용한다. 배열로 관리 할 거라서 3개로 확장은 간단하다.
퍼블릭 서브넷과 프라이빗 서브넷을 구성한다. 2개의 가용영역에 배치되므로 총 4개의 서브넷이 배치된다.
퍼블릭 서브넷으로의 인터넷에서의 접근을 위해서 인터넷 게이트웨이를 만든다.
프라이빗 서브넷에서 인터넷으로 접근하기 위한 Nat gateway를 만든다.
여기에서 사용한 예제의 원본은 여기에서 확인 할 수 있다. 원본의 내용을 개선하는 형태로 이루어진다.
버전
Terraform v0.11.13으로 테스트했다. (2019년 7월 2일)현재 최신버전은 v.12다. v.12 부터 for, if 문을 지원하는 등 변화가 크다.
가용영역 설정
ap-northeast-2a, ap-northeast-2b 두 개의 가용영역을 선택하기로 했다. 변수를 아래와 같이 정의했다.
variable "availability_zone" {
description = "Seoul region availability zone"
type = "list"
default = ["ap-northeast-2a", "ap-northeast-2b"]
}
거의 모든 terraform 리소스(resource)는 메타데이터 매개변수를 가지고 있다. count는 terrafrom의 count를 증가하면서 구문 블럭을 추가실행 하도록 해주는 매개변수다. HCL(HashCorp Configuration Language)는 선언적언어이기 때문에 루프나 if/else 문을 제공하지 않는다. 이는 인프라를 코드화하는 작업을 어렵게 만들 수 있다. count를 이용해서 루프를 실행 할 수 있다. 아래의 예제를 보자.
count : 가용영역(availability_zone)의 크기는 2이므로 이 리소스 블럭은 두 반복된다. for 루프문의 다른 구현이라고 볼 수 있겠다. 첫번째 실행에서는 ap-northeast-2a, 다은 번째 실행에서는 ap-northeast-2b가 될 것이다. 결론적으로 2개의 서브넷은 서로 다른 가용영역에 위치하게 될 것이다.
cidr_block : count.index는 0, 1이 될 것이다. 결국 10.100.0.0/24, 10.100.1.0/24 두 개의 서브넷이 만들어진다.
tags : Name 태그를 사용했다. 태그는 필터링, 분류, 과금, 통계 등을 위한 중요한 데이터다. 나중에 활용 할 수 있도록 자원에 대한 정보를 포함해야 한다. element를 이용해서 availability_zone을 가져오기로 했다.
Contents
소개
- 2개의 가용영역을 사용한다. : 현재 서울리전은 3개의 가용영역을 가지고 있지만, 지금은 2개의 가용영역만 사용한다. 배열로 관리 할 거라서 3개로 확장은 간단하다.
- 퍼블릭 서브넷과 프라이빗 서브넷을 구성한다. 2개의 가용영역에 배치되므로 총 4개의 서브넷이 배치된다.
- 퍼블릭 서브넷으로의 인터넷에서의 접근을 위해서 인터넷 게이트웨이를 만든다.
- 프라이빗 서브넷에서 인터넷으로 접근하기 위한 Nat gateway를 만든다.
여기에서 사용한 예제의 원본은 여기에서 확인 할 수 있다. 원본의 내용을 개선하는 형태로 이루어진다.버전
가용영역 설정
variable "availability_zone" { description = "Seoul region availability zone" type = "list" default = ["ap-northeast-2a", "ap-northeast-2b"] }Public subnet 정의
resource "aws_subnet" "public" { count = "${length(var.availability_zone)}" availability_zone = "${element(var.availability_zone, count.index)}" vpc_id = "${aws_vpc.default.id}" cidr_block = "${cidrsubnet(var.vpc_cidr, 8, count.index)}" tags { Name = "Public Subnet - ${element(var.availability_zone, count.index)}" } }resource "aws_instance" "example" { count = 3 ami = "ami-2d39803a" instance_type = "t2.micro" }for i := 0; i < 3; i++ { resource "aws_instance" "example" { ami = "ami-2d39803a" instance_type = "t2.micro" } }- prefix : 10.100.0.0/16 이 될 것이다.
- newbits : 24 비트 서브넷을 사용하기로 했으니 8이 된다.
- netnum : 네트워크 인덱스로 0부터 시작한다.
terraform console명령으로 함수를 테스트해볼 수 있다.# terraform console > cidrsubnet("10.100.0.0/16", 8, 0) 10.100.0.0/24 > cidrsubnet("10.100.0.0/16", 8, 1) 10.100.1.0/24 > cidrsubnet("10.100.0.0/16", 8, 2) 10.100.2.0/24 > cidrsubnet("10.100.0.0/16", 8, 3) 10.100.3.0/24- count : 가용영역(availability_zone)의 크기는 2이므로 이 리소스 블럭은 두 반복된다. for 루프문의 다른 구현이라고 볼 수 있겠다. 첫번째 실행에서는 ap-northeast-2a, 다은 번째 실행에서는 ap-northeast-2b가 될 것이다. 결론적으로 2개의 서브넷은 서로 다른 가용영역에 위치하게 될 것이다.
- cidr_block : count.index는 0, 1이 될 것이다. 결국 10.100.0.0/24, 10.100.1.0/24 두 개의 서브넷이 만들어진다.
- tags : Name 태그를 사용했다. 태그는 필터링, 분류, 과금, 통계 등을 위한 중요한 데이터다. 나중에 활용 할 수 있도록 자원에 대한 정보를 포함해야 한다. element를 이용해서 availability_zone을 가져오기로 했다.
Private subnet도 동일한 방법으로 구성했다.resource "aws_subnet" "private" { count = "${length(var.availability_zone)}" availability_zone = "${element(var.availability_zone, count.index)}" vpc_id = "${aws_vpc.default.id}" cidr_block = "${cidrsubnet(var.vpc_cidr, 8, count.index+2)}" tags { Name = "Private Subnet - ${element(var.availability_zone, count.index)}" } }NAT Gateway
- Publc subnet에 위치해야 한다. 그래야 internet gateway를 통해서 패킷을 인터넷으로 보낼 수 있다.
- 고정된 EIP가 필요하다.
EIP를 만든다.resource "aws_eip" "nat_eip" { vpc = true depends_on = ["aws_internet_gateway.gw"] }resource "aws_nat_gateway" "nat" { allocation_id = "${aws_eip.nat_eip.id}" subnet_id = "${element(aws_subnet.public.*.id, 0)}" depends_on = ["aws_internet_gateway.gw"] }- allocation_id : EIP를 NAT gateway에 할당했다.
- subnet_id : NAT gateway는 0번 퍼블릭 서브넷에만 배치하기로 했다. 실제 환경에서 가용성이 중요하다면 두 개 서브넷에 배치할 것이다.
- depends_on : Internet gateway가 만들어진 후 NAT gateway를 전개한다.
Private subnet 라우팅 테이블에 nat gateway로 향하는 룰(0.0.0.0/0을 nat gateway로)을 추가했다.resource "aws_route_table" "private" { vpc_id = "${aws_vpc.default.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_nat_gateway.nat.id}" } tags { Name = "Private Subnet Route Table" } }resource "aws_route_table_association" "public" { count = "${length(var.availability_zone)}" subnet_id = "${element(aws_subnet.public.*.id, count.index)}" route_table_id = "${aws_route_table.public.id}" } resource "aws_route_table_association" "private" { count = "${length(var.availability_zone)}" subnet_id = "${element(aws_subnet.private.*.id, count.index)}" route_table_id = "${aws_route_table.private.id}" }- 2개의 가용영역에있는 서브넷들이 배치된다. 따라서 count에 가용영역의 크기가 설정되게 했다.
- element 함수를 이용해서 2개의 서브넷들이 배치되게했다.
terraform plan, terraform apply 로 우리가 정의한 형상을 전개하자.검증
# aws ec2 describe-vpcs { "Vpcs": [ { "CidrBlock": "10.100.0.0/16", "DhcpOptionsId": "dopt-535a9438", "State": "available", "VpcId": "vpc-0b22c42f51fdb0dcc", "OwnerId": "522373083963", "InstanceTenancy": "default", "CidrBlockAssociationSet": [ { "AssociationId": "vpc-cidr-assoc-0d094cfcf3aadec7e", "CidrBlock": "10.100.0.0/16", "CidrBlockState": { "State": "associated" } } ], "IsDefault": false, "Tags": [ { "Key": "Name", "Value": "test-vpc" } ] } }# aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-0b22c42f51fdb0dcc" { "Subnets": [ { "AvailabilityZone": "ap-northeast-2a", "AvailabilityZoneId": "apne2-az1", "AvailableIpAddressCount": 250, "CidrBlock": "10.100.0.0/24", "DefaultForAz": false, "MapPublicIpOnLaunch": false, "State": "available", "SubnetId": "subnet-04a9e32c2d120e3db", "VpcId": "vpc-0b22c42f51fdb0dcc", "OwnerId": "522373083963", "AssignIpv6AddressOnCreation": false, "Ipv6CidrBlockAssociationSet": [], "Tags": [ { "Key": "Name", "Value": "Public Subnet - ap-northeast-2a" } ], "SubnetArn": "arn:aws:ec2:ap-northeast-2:522373083963:subnet/subnet-04a9e32c2d120e3db" }, { "AvailabilityZone": "ap-northeast-2a", "AvailabilityZoneId": "apne2-az1", "AvailableIpAddressCount": 251, "CidrBlock": "10.100.2.0/24", "DefaultForAz": false, "MapPublicIpOnLaunch": false, "State": "available", "SubnetId": "subnet-085a9890bad2c315f", "VpcId": "vpc-0b22c42f51fdb0dcc", "OwnerId": "522373083963", "AssignIpv6AddressOnCreation": false, "Ipv6CidrBlockAssociationSet": [], "Tags": [ { "Key": "Name", "Value": "Private Subnet - ap-northeast-2a" } ], "SubnetArn": "arn:aws:ec2:ap-northeast-2:522373083963:subnet/subnet-085a9890bad2c315f" }, { "AvailabilityZone": "ap-northeast-2b", "AvailabilityZoneId": "apne2-az2", "AvailableIpAddressCount": 251, "CidrBlock": "10.100.1.0/24", "DefaultForAz": false, "MapPublicIpOnLaunch": false, "State": "available", "SubnetId": "subnet-0c83fb7904b24fcd8", "VpcId": "vpc-0b22c42f51fdb0dcc", "OwnerId": "522373083963", "AssignIpv6AddressOnCreation": false, "Ipv6CidrBlockAssociationSet": [], "Tags": [ { "Key": "Name", "Value": "Public Subnet - ap-northeast-2b" } ], "SubnetArn": "arn:aws:ec2:ap-northeast-2:522373083963:subnet/subnet-0c83fb7904b24fcd8" }, { "AvailabilityZone": "ap-northeast-2b", "AvailabilityZoneId": "apne2-az2", "AvailableIpAddressCount": 251, "CidrBlock": "10.100.3.0/24", "DefaultForAz": false, "MapPublicIpOnLaunch": false, "State": "available", "SubnetId": "subnet-024e00d05b78a7b10", "VpcId": "vpc-0b22c42f51fdb0dcc", "OwnerId": "522373083963", "AssignIpv6AddressOnCreation": false, "Ipv6CidrBlockAssociationSet": [], "Tags": [ { "Key": "Name", "Value": "Private Subnet - ap-northeast-2b" } ], "SubnetArn": "arn:aws:ec2:ap-northeast-2:522373083963:subnet/subnet-024e00d05b78a7b10" } ] }# aws ec2 describe-nat-gateways { "NatGateways": [ { "CreateTime": "2019-06-30T17:24:06.000Z", "NatGatewayAddresses": [ { "AllocationId": "eipalloc-02e98bc2373661a12", "NetworkInterfaceId": "eni-010eae84a6d2d6b10", "PrivateIp": "10.100.0.75", "PublicIp": "13.125.104.150" } ], "NatGatewayId": "nat-086cb496e5638f1ab", "State": "available", "SubnetId": "subnet-04a9e32c2d120e3db", "VpcId": "vpc-0b22c42f51fdb0dcc", "Tags": [] } ] }aws ec2 describe-route-tables --filters "Name=vpc-id,Values=vpc-0b22c42f51fdb0dcc" { "RouteTables": [ { "Associations": [ { "Main": false, "RouteTableAssociationId": "rtbassoc-0d88c73aacd13c265", "RouteTableId": "rtb-06af924fba0573df9", "SubnetId": "subnet-085a9890bad2c315f" }, { "Main": false, "RouteTableAssociationId": "rtbassoc-041db9aaf61c917e4", "RouteTableId": "rtb-06af924fba0573df9", "SubnetId": "subnet-024e00d05b78a7b10" } ], "PropagatingVgws": [], "RouteTableId": "rtb-06af924fba0573df9", "Routes": [ { "DestinationCidrBlock": "10.100.0.0/16", "GatewayId": "local", "Origin": "CreateRouteTable", "State": "active" }, { "DestinationCidrBlock": "0.0.0.0/0", "NatGatewayId": "nat-086cb496e5638f1ab", "Origin": "CreateRoute", "State": "active" } ], "Tags": [ { "Key": "Name", "Value": "Private Subnet Route Table" } ], "VpcId": "vpc-0b22c42f51fdb0dcc" }, { "Associations": [ { "Main": false, "RouteTableAssociationId": "rtbassoc-04b91f19661b8a7bf", "RouteTableId": "rtb-0aec7d07ce3ea6efe", "SubnetId": "subnet-04a9e32c2d120e3db" }, { "Main": false, "RouteTableAssociationId": "rtbassoc-0ab3664a359182289", "RouteTableId": "rtb-0aec7d07ce3ea6efe", "SubnetId": "subnet-0c83fb7904b24fcd8" } ], "PropagatingVgws": [], "RouteTableId": "rtb-0aec7d07ce3ea6efe", "Routes": [ { "DestinationCidrBlock": "10.100.0.0/16", "GatewayId": "local", "Origin": "CreateRouteTable", "State": "active" }, { "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": "igw-07a20b7d0c86d2850", "Origin": "CreateRoute", "State": "active" } ], "Tags": [ { "Key": "Name", "Value": "Public Subnet Route Table" } ], "VpcId": "vpc-0b22c42f51fdb0dcc" } }- Associations에 2개의 public subnet이 설정된 걸 확인 할 수 있다.
- 0.0.0.0/0은 Internet gateway로 향하고 있다.
Private Subnet Route Table역시 최초에 설계한 모습 그대로 설정된 걸 알 수 있다.정리
Recent Posts
Archive Posts
Tags