티스토리 뷰

반응형

 

시나리오

[Users]

1) 사용자는 80번 포트를 통해 Route53에 Request를 보낸다. (http://example.com:80)

2) Route53은 이를 Internet Gateway를 통해 Load Balancer로 전달한다.

3) Load Balancer는 이를 8080번 포트로 Auto Scailing되고 있는 EC2 서버에 전달한다.

4) EC2 서버는 Request보낸 것에 대한 Response를 NAT Gateway를 통해 보낸다.

 

[Admin]

1) SSH Key를 사용해 Public Subnet에 위치한 관리자용 EC2에 연결한다.

2) 관리자용 EC2에 저장된 SSH Key를 사용해 웹 서버가 구동중인 EC2에 연결한다.

 

 

 


vpc.tf

# VPC를 10.0.0.0/16 B Class로 생성
resource "aws_vpc" "myVpc" {
    cidr_block = "10.0.0.0/16"
    enable_dns_hostnames = true
    enable_dns_support = true
    instance_tenancy = "default"
}

# Public Subnet에 부여할 CIDR 목록을 변수로 생성
variable "aws_public_subnets" {
    default = [ "10.0.1.0/26", "10.0.1.64/26", "10.0.1.128/26", "10.0.1.192/26" ]
}

# Private Subnet에 부여할 CIDR 목록을 변수로 생성
variable "aws_private_subnets" {
    default = [ "10.0.11.0/26", "10.0.11.64/26", "10.0.11.128/26", "10.0.11.192/26" ]
}

# VPC가 존재할 가용 영역 목록을 변수로 생성
variable "aws_azs" {
    default = [ "ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c", "ap-northeast-2d" ]
}

###########################################################################################
# VPC는 여러 가용영역(AZ)에 걸칠 수 있지만, 여러 리전(Region)에 걸칠 수는 없다.
# Subnet은 여러 가용영역(AZ)에 걸칠 수 없다. 하나의 Subnet은 하나의 가용영역에 위치해야 한다.
###########################################################################################

# Public Subnet 4개를 생성. Count를 이용한 반복문.
resource "aws_subnet" "myPublicSubnet" {
    count = length(var.aws_public_subnets)
    vpc_id = aws_vpc.myVpc.id
    cidr_block = var.aws_public_subnets[count.index]
    availability_zone = var.aws_azs[count.index]

    map_public_ip_on_launch = true
}

# Private Subnet 4개를 생성. Count를 이용한 반복문.
resource "aws_subnet" "myPrivateSubnet" {
    count = length(var.aws_private_subnets)
    vpc_id = aws_vpc.myVpc.id
    cidr_block = var.aws_private_subnets[count.index]
    availability_zone = var.aws_azs[count.index]

    map_public_ip_on_launch = false
}

# VPC에 연결된 Internet Gateway를 생성
resource "aws_internet_gateway" "myIgw" {
    vpc_id = aws_vpc.myVpc.id
}

# NAT Gateway에 부여할 EIP(Elastic IP)를 생성
resource "aws_eip" "myEip" {
    vpc = true
}

# Private Subnet의 출구역할인 NAT Gateway를 생성
resource "aws_nat_gateway" "myNat" {
    allocation_id = aws_eip.myEip.id
    subnet_id = aws_subnet.myPublicSubnet.0.id

    depends_on = [ aws_internet_gateway.myIgw ]
}

###########################################################################################
# depends_on은 특정 resource가 생성된 후 생성하라는 뜻이다.
# 예를 들어, EIP가 생성되기도 전에 NAT Gateway에 대한 resource를 생성하려고 하면,
# 존재하지 않는다는 에러가 발생할 수 있기 때문에 이와같이 의존성을 부여하는 것이다.
###########################################################################################

# Public Subnet에 부여할 Routing Table의 규칙을 생성
resource "aws_route_table" "myPublicRouteTable" {
    vpc_id = aws_vpc.myVpc.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.myIgw.id
    }
}

# Private Subnet에 부여할 Routing Table의 규칙을 생성
resource "aws_route_table" "myPrivateRouteTable" {
    vpc_id = aws_vpc.myVpc.id
    route {
        cidr_block = "0.0.0.0/0"
        nat_gateway_id = aws_nat_gateway.myNat.id
    }
}

# Public Subnet에 위에서 생성한 aws_route_table을 연결
resource "aws_route_table_association" "myPublicRouting" {
    count = length(var.aws_public_subnets)

    route_table_id = aws_route_table.myPublicRouteTable.id
    subnet_id = aws_subnet.myPublicSubnet.*.id[count.index]
}

# Private Subnet에 위에서 생성한 aws_route_table을 연결
resource "aws_route_table_association" "myPrivateRouting" {
    count = length(var.aws_private_subnets)

    route_table_id = aws_route_table.myPrivateRouteTable.id
    subnet_id = aws_subnet.myPrivateSubnet.*.id[count.index]
}

 

security-group.tf

# Admin이 연결할 EC2에 적용할 보안그룹을 생성
resource "aws_security_group" "myBastion_sg" {
    name = "bastion_security_group"
    vpc_id = aws_vpc.myVpc.id

    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = [ "0.0.0.0/0" ] # 여기에 접속가능한 IP목록
    }
    # 지금은 InBound할 수 있는 CIDR을 모두로 설정했지만,
    # 실제 사용시에는 관리자 PC만 접근할 수 있도록 관리자 PC의 IP주소로 설정해야 한다.
    
    egress {
        from_port = 0
        to_port = 0
        protocol = "-1"
        cidr_blocks = [ "0.0.0.0/0" ]
    }
}

# 웹 서버가 구동될 EC2들에 적용할 보안그룹을 생성
resource "aws_security_group" "myEC2_sg" {
    name = "EC2_security_group"
    vpc_id = aws_vpc.myVpc.id

    ingress {
        from_port = 8080
        to_port = 8080
        protocol = "tcp"
        cidr_blocks = [ "0.0.0.0/0" ]
    }

    ingress {
        from_port = 22
        to_port = 22
        protocol = "tcp"
        security_groups = [ aws_security_group.myBastion_sg.id ]
    }
    
###########################################################################################
# 보안그룹을 통해 트래픽을 제어할 때, CIDR로 그에 대한 대상을 정할수도 있지만,
# 위와 같이 특정 보안그룹을 대상으로 정할수도 있다.
# 이렇게 하면 myBastion_sg 보안그룹이 적용된 대상의 tcp/22 InBound 트래픽만을 허용한다.
###########################################################################################

    egress {
        from_port = 0
        to_port = 0
        protocol = "-1"
        cidr_blocks = [ "0.0.0.0/0" ]
    }
}

# Load Balancer에 적용할 보안그룹을 생성
resource "aws_security_group" "myNlb_sg" {
    name = "NLB_security_group"
    vpc_id = aws_vpc.myVpc.id

    ingress {
        from_port = 80
        to_port = 80
        protocol = "tcp"
        cidr_blocks = [ "0.0.0.0/0" ]
    }
    
###########################################################################################
# Load Balancer용과 웹 서버용의 보안그룹을 분리하는 것이 좋다고 한다.
# Load Balancer에서 HTTP(80)/HTTPS(443) 에 대한 트래픽을 허용해 받고,
# Listener를 통해 웹 서버로 전달할 때는 HTTP(80) 포트로 변환해 전달하도록 구성할 수 있다.
###########################################################################################

    egress {
        from_port = 0
        to_port = 0
        protocol = "-1"
        cidr_blocks = [ "0.0.0.0/0" ]
    }
}

 

route53.tf

# route53의 새로운 zone을 생성
resource "aws_route53_zone" "myRoute53" {
    name = "chanjun-project.com"
}

# 생성한 zone에 새로운 record를 생성
# A Type으로 alias(dualstack.[dns_name]) 를 생성
resource "aws_route53_record" "myRoute53Record" {
    name = format("%s.%s", "cccr", aws_route53_zone.myRoute53.name)
    type = "A"
    zone_id = aws_route53_zone.myRoute53.id

    alias {
        evaluate_target_health = true
        name = format("%s.%s", "dualstack", aws_lb.myLb.dns_name)
        zone_id = aws_lb.myLb.zone_id
    }
}

 

key.tf

# 관리자용 EC2에 SSH 접속을 위한 Key-pair를 생성
resource "aws_key_pair" "bastion" {
    key_name = "bastion_key"
    public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAjba3ZPuEsxxxxxxxxxxxxx1UH7EUVKS0d1D9nO5zXfXaaaasdt1/6ON/4QB5Xsmt+Ea8VIC9EC0SZ9dN4DtSV5ugXJ4rtdgiUH1mK5BFTMFexsXx1HRrIwUSk+Pnq2hEymBTKBmaG7+T+yohdSBHwYH04VRUXPcHIUMNS7orQNAylyFKKVQ6BxuEAkQATDNwPb9yq4j6xkEjiklY0wLDMN7ZivllvWGhqXa1WeR7bULAjC0j1bVzBIGea7NDixXHBfjEEhEVxg4iUXyBrDBx/tkcBEitTLfS3ii/NoltJtiAE5oLtJVvlQ== rsa-key-20210131"
}

# 웹 서버가 구동중인 EC2에 SSH 접속을 위한 Key-pair 생성
# 이 Key-pair의 Public Key는 관리자용 EC2에 보관해야 한다.
resource "aws_key_pair" "ec2" {
    key_name = "ec2_key"
    public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEA5zaEi5o+HDxxxxxxxxxxxxxxxxxxxxQODhii6mdxGCjeiJ3uuPsadasd7f3LuTo3wSnK22hmym+6DEQ9irHG03rWAhTgHkVkxz1JRdJW5ODNfrrYtPX0Quc1zwJ7w3B+E3Z/3QY2P9pbLX1UDwwjc3Mxmfu5+cSnB+qt4t78taUMrkNRfMsmYzDKGa09acFmMyUhUD1YxbJEAv35Y+1JnAUe/0ULgYh52lFUifPSMjoJqg7UEgkAM8cgdBZWUfXVBaYAUjFEAbK/1/W4U8s3qJhn1xD4Ku1Opop+4Q/QOFku3fWhwtHPgTmoL2iQ== rsa-key-20210131"
}

 

ec2.tf

# 관리자용 EC2 Instance를 생성
resource "aws_instance" "bastion" {
    ami = "ami-027ce4ce0590e3c98"
    instance_type = "t2.micro"
    subnet_id = aws_subnet.myPublicSubnet.0.id
    key_name = aws_key_pair.bastion.id

    vpc_security_group_ids = [ aws_security_group.myBastion_sg.id ]
}

# 웹 서버가 구동될 EC2 Instance는 Auto Scaling을 통해 생성할 것이고,
# 생성할 때 각 Instance에 적용할 Launch Configuration을 생성
resource "aws_launch_configuration" "myLaunchConfig" {
    image_id = "ami-027ce4ce0590e3c98"
    instance_type = "t2.micro"
    key_name = aws_key_pair.ec2.id

    security_groups = [ aws_security_group.myEC2_sg.id ]
    user_data = <<-EOF
        #!/bin/bash
        sudo yum install httpd -y
        echo "<html><h1>webpage 1</h1></html>" | sudo tee /var/www/html/index.html
        sudo sed -i '136s/80/8080/g' /etc/httpd/conf/httd.conf
        sudo service httpd start
        sudo chkconfig httpd on
    EOF

    lifecycle {
        create_before_destroy = true
    }
    depends_on = [ aws_security_group.myEC2_sg ]
}

# 웹 서버가 구동될 EC2들을 Auto Scaling하기 위한 Auto Scaling Group 생성
resource "aws_autoscaling_group" "myAsg" {
    launch_configuration = aws_launch_configuration.myLaunchConfig.id
    health_check_type = "ELB"
    # *를 통해 모든 Private Subnet을 Auto Scaling할 AZ의 범위를 지정
    vpc_zone_identifier = aws_subnet.myPrivateSubnet.*.id

    min_size = 1
    max_size = 5

    tag {
        key = "Name"
        value = "ch4njun-asg-example"
        propagate_at_launch = true
    }
}

# Load Balancer를 생성 (ELB를 생성할 수도 있지만 여기서는 ALB를 생성한다.)
# ELB에 대한 코드는 다른 포스팅에서 확인할 수 있다.
resource "aws_lb" "myLb" {
    name = "chanjun-alb"
    internal = false
    load_balancer_type = "network"
    subnets = aws_subnet.myPublicSubnet.*.id
}

# Load Balancer에 설정할 Listener를 생성
resource "aws_lb_listener" "myListener" {
    load_balancer_arn = aws_lb.myLb.arn
    
    # 즉, tcp/80 포트로 트래픽이 들어오면 default_action을 수행해라! 라는 거다.
    port = 80
    protocol = "TCP"

    default_action {
    	# default_action은 aws_lb_target_group으로 forward해라!! 라는 거다.
        target_group_arn = aws_lb_target_group.myTarget.arn
        type = "forward"
    }
}

# 위에서 설명한 aws_lb_target_group을 지정해, Listener가 수행할 Action의 Target을 생성
resource "aws_lb_target_group" "myTarget" {
    name = "target-group"
    port = 8080
    protocol = "TCP"
    vpc_id = aws_vpc.myVpc.id
    target_type = "instance"

    health_check {
        port = "traffic-port"
        protocol = "TCP"
    }
}

# Target을 생성했음 붙여야지! 여기서 원래 count를 1로 고정하면 안되는데...
# 고정안하면 에러가나서 ㅠㅠ 우선은 이렇게 넘어간다!!
# 원래는 data "aws_instances" "test" 를 통해 ASG로 생성된 Instance들을 긁어오고,
# 긁어온 모든 EC2 Instance들에게 Target Group을 부착을 해줘야 한다.
resource "aws_lb_target_group_attachment" "myTargetAttachment" {
    count = 1

    target_group_arn = aws_lb_target_group.myTarget.arn
    target_id = element(data.aws_instances.test.ids, count.index)
    depends_on = [ data.aws_instances.test ]
}

data "aws_instances" "test" {
    instance_tags = {
        "aws:autoscaling:groupName" = aws_autoscaling_group.myAsg.name
    }
    depends_on = [ aws_launch_configuration.myLaunchConfig ]
}

 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함