EC2 구성하기

key pair 생성

ec2를 접속할 때 사용할 key pair를 생성한다.

ssh-keygen -t rsa -b 4069 -C {자신의 메일} -f "./my_key" -N ""
# rsa 방식으로 4096비트를 사용해서 생성한다.

생성 완료되면

Generating public/private rsa key pair.
Your identification has been saved in ./my_key
Your public key has been saved in ./my_key.pub
The key fingerprint is:
SHA256:[여러 문자] [입력한 메일 주소]
The key's randomart image is:
+---[RSA 4069]----+
|+O               |
|++@+.   . .      |
|oo.+   + o +     |
|=.  o o +        |
|E..  . .So       |
|o* .   ..        |
|..* ..+.         |
|.. o.  o         |
|   .o .          |
+----[SHA256]-----+

이런 메세지가 뜨면서 파일 두개가 생성된다.
my_key와 my_key.pub이 생성되는데 .pub가 공개키, my_key 암호키 즉 쌍(pair)으로 생성된다.

생성한 키페어를 테라폼 파일에 등록한다.

# ec2.tf
resource "aws_key_pair" "ec2_key" {
    key_name = "my_key"
  public_key = "my_key.pub 안에 있는 내용 전부 붙여넣기" or file("./my_key.pub")
}

ec2.tf를 만들어 주고 위의 코드를 작성해서 생성한 키페어를 테라폼에서 사용할 수 있게 만들어준다.

ec2 생성

aws_instance를 사용해서 쉽게 생성할 수 있다.

#ec2.tf

resource "aws_instance" "real_ec2" {
    ami = "ami-0ba5cd124d7a79612"
    #ubuntu 20.04의 ami
    instance_type = "t3.small"
    security_groups = [aws_security_group.realPublicSG.id]
    subnet_id = aws_subnet.realPublicSubnet.id
    key_name = aws_key_pair.ec2_key.key_name
    root_block_device { #볼륨 설정 가능
            volume_size = 200
            volume_type = "gp3"
    }
    tags ={ #이름 설정 가능
            Name = "real_ec2"
    }
}
  • 인스턴스 타입을 설정할 수 있다.
  • ami의 경우 콘솔에서 ami검색을 통해 id를 확인할 수 있다.

작성완료 했으면 terraform plan 후 apply
(apply는 실제 환경에 적용이기 때문에 자주하지 않더라고 plan은 자주해서 오류를 바로바로 잡아내자!)

RDS 생성

rds.tf 파일을 생성한 후 아래와 같이 작성한다.

#rds.tf

#rds의 서브넷 그룹으로 사용할 subnet들 미리 지정
resource "aws_db_subnet_group" "realDBSubnetGroup" {
  name = "test"
  subnet_ids = [
    aws_subnet.realPrivateSubnet1.id,
    aws_subnet.realPrivateSubnet2.id,
    aws_subnet.realPublicSubnet.id,
  ]
  tags = {
    "Name" = "real-db-subnet-group"
  }
}

resource "aws_db_instance" "real_rds" { 
    allocated_storage = 50
    max_allocated_storage = 80
    skip_final_snapshot = true
    vpc_security_group_ids = [aws_security_group.realPrivateSG.id ]
        #보안그룹 지정.
    db_subnet_group_name = aws_db_subnet_group.realDBSubnetGroup.name
        #서브넷 그룹 지정.
    publicly_accessible = true
    engine = "mariadb"
    engine_version = "10.6.8"
    instance_class = "db.t3.small"
    db_name = "testDB"
    username = "admin"
    password = "testtest"
    tags = {
        "Name" = "realDB"
    }
}

생성완료 후 terraform plan 후 apply로 적용을 한다.

구성도

이런 형태로 구축하려고 한다.

서브넷 추가하기

#vpc.tf
#퍼블릭 서브넷
resource "aws_subnet" "realPublicSubnet" {
    vpc_id = aws_vpc.realVPC.id
    cidr_block = "10.0.0.0/24"
    availability_zone = "ap-northeast-2c"
    tags = {
        Name = "real-public-subnet"
    }
}
#프라이빗 서브넷
resource "aws_subnet" "realPrivateSubnet1" {
    vpc_id = aws_vpc.realVPC.id
    cidr_block = "10.0.1.0/24"
    availability_zone = "ap-northeast-2c"
    tags = {
        Name ="real-private-subnet1"
    }
}
resource "aws_subnet" "realPrivateSubnet2" {
    vpc_id = aws_vpc.realVPC.id
    cidr_block = "10.0.2.0/24"
    availability_zone = "ap-northeast-2a"
    tags = {
        Name = "real-private-subnet2"
    }
}
  • vpc_id 부분에 바로전에 만들었던 vpc의 id를 추가하면 쉽게 서브넷을 추가할 수 있다.
  • 아까 만들었던 이름을 사용해서 id를 쉽게 얻을 수 있다. (aws_vpc.testVPC.id)

IGW (인터넷 게이트웨이 추가)

# vpc.tf
resource "aws_internet_gateway" "realIGW" {
    vpc_id = aws_vpc.realVPC.id
    tags = {
        Name="real-IGW"
    }
}
  • vpc에서 외부와 통신할 수 있게 public subnet과 통신한 인터넷 게이트웨이를 추가한다.

라우팅 테이블 작성

#vpc.tf
##라우팅 테이블 만들기
#퍼블릭 라우팅 테이블
resource "aws_route_table" "realPublicRoute" {
    vpc_id = aws_vpc.realVPC.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.realIGW.id 
            #퍼블릭 서브넷에 인터넷 게이트웨이 연결.
    }
    tags = {
    Name = "real-public-route"
    }
}
#프라이빗 라우팅 테이블
resource "aws_route_table" "realPrivateRoute" {
    vpc_id = aws_vpc.realVPC.id
    tags = {
        Name = "real-private-route"
    }
}

#퍼블릭 라우팅 테이블 연결
resource "aws_route_table_association" "realPublicRTAssociation"{
    subnet_id = aws_subnet.realPublicSubnet.id
    route_table_id = aws_route_table.realPublicRoute.id
}

#프라이빗 라우팅 테이블 연결
resource "aws_route_table_association" "realPrivateRTAssociation1"{
    subnet_id = aws_subnet.realPrivateSubnet1.id
    route_table_id = aws_route_table.realPrivateRoute.id
}
resource "aws_route_table_association" "realPrivateRTAssociation2"{
    subnet_id = aws_subnet.realPrivateSubnet2.id
    route_table_id = aws_route_table.realPrivateRoute.id
}
  • 다음으로는 라우팅 테이블을 만들어 서브넷과 연결시켜준다.

보안그룹 생성

#vpc.tf
#퍼블릭 보안 그룹
resource "aws_security_group" "realPublicSG" {
    vpc_id = aws_vpc.realVPC.id
    name = "real public SG"
    description = "real public SG"
    tags = {
        Name = "real pulbic SG"
    }

}
#퍼블릭 보안 그룹 규칙
resource "aws_security_group_rule" "realPublicSGRulesHTTPingress" {
    type = "ingress"
    from_port = 80
    to_port=80
    protocol = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
    security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "realPublicSGRulesSSHingress" {
    type = "ingress"
    from_port = 22
    to_port= 22
    protocol = "TCP"
    cidr_blocks=["0.0.0.0/0"]
    security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "realPublicSGRulesALLegress" {
    type = "egress"
    from_port = 0
    to_port= 0
    protocol = "ALL"
    cidr_blocks=["0.0.0.0/0"]
    security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
#프라이빗 보안 그룹
resource "aws_security_group" "realPrivateSG" {
    vpc_id = aws_vpc.realVPC.id
    name = "real private SG"
    description = "real private SG"
    tags = {
        Name = "real private SG"
    }
}
#프라이빗 보안 그룹 규칙
resource "aws_security_group_rule" "realPrivateSGRulesRDSingress" {
    type = "ingress"
    from_port = 3306
    to_port=3306
    protocol = "TCP"
    security_group_id = aws_security_group.realPrivateSG.id
    source_security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "realPrivateSGRulesRDSegress" {
    type = "egress"
    from_port = 3306
    to_port= 3306
    protocol = "TCP"
    security_group_id = aws_security_group.realPrivateSG.id
    source_security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
  • public, private subnet의 보안그룹을 생성해준다.

    • public의 경우 ssh접속을 위한 22번 포트와, http 통신을 위한 80를 ingress로, 열어주고 외부로 나가는 egress는 모든 트래픽을 열어준다.
    • private의 경우 rds를 연결하기 위해 3306포트를 열어준다.
  • 테라폼에서는 보안 그룹을 설정할 때 인라인으로 규칙을 추가해주거나 aws_security_group_rule
    으로 추가할 수 있음.

  • 만약 인라인으로 지정하게 된다면 한 보안 그룹에서 인바운드로 허용하는 액세스원이 다른 보안 그룹인 경우 이를 지정 할 수 없다.
    => aws_security_group_rule를 사용해서 추가해주자.

  • ingress는 외부에서 내부로 들어오는 트래픽, egress는 내부에서 외부로 나가는 트래픽을 의미.

계획 및 적용

$ terraform plan
.
.
.
Plan: 16 to add, 0 to change, 0 to destroy.
$ terraform apply

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

aws_security_group.testPrivateSG: Creating...
aws_route_table.testPrivateRoute2: Creating...
aws_route_table.testPrivateRoute1: Creating...
aws_route_table.testPublicRoute: Creating...
aws_security_group.testPublicSG: Creating...
aws_route_table.testPrivateRoute2: Creation complete after 1s [id=rtb-01a08a475c7aa3b6e]
aws_route_table_association.testPrivateRTAssociation2: Creating...
aws_route_table.testPrivateRoute1: Creation complete after 1s [id=rtb-0de978f9daf7138da]
aws_route_table_association.testPrivateRTAssociation1: Creating...
aws_route_table_association.testPrivateRTAssociation2: Creation complete after 0s [id=rtbassoc-04c687539b690b97e]
aws_route_table_association.testPrivateRTAssociation1: Creation complete after 0s [id=rtbassoc-00c81afb4d66a4afb]
aws_route_table.testPublicRoute: Creation complete after 1s [id=rtb-0b21780b54c48f288]
aws_route_table_association.testPublicRTAssociation: Creating...
aws_route_table_association.testPublicRTAssociation: Creation complete after 1s [id=rtbassoc-015a0b583a70d3d07]
aws_security_group.testPrivateSG: Creation complete after 2s [id=sg-0564ca6903f5d71ae]
aws_security_group.testPublicSG: Creation complete after 2s [id=sg-0d17e51c8f54212ee]
aws_security_group_rule.testPublicSGRulesHTTPegress: Creating...
aws_security_group_rule.testPrivateSGRulesRDSegress: Creating...
aws_security_group_rule.testPublicSGRulesHTTPSingress: Creating...
aws_security_group_rule.testPublicSGRulesHTTPSegress: Creating...
aws_security_group_rule.testPublicSGRulesHTTPingress: Creating...
aws_security_group_rule.testPublicSGRulesSSHegress: Creating...
aws_security_group_rule.testPrivateSGRulesRDSingress: Creating...
aws_security_group_rule.testPublicSGRulesSSHingress: Creating...
aws_security_group_rule.testPublicSGRulesSSHegress: Creation complete after 1s [id=sgrule-1099277773]
aws_security_group_rule.testPrivateSGRulesRDSegress: Creation complete after 1s [id=sgrule-640893485]
aws_security_group_rule.testPublicSGRulesHTTPSingress: Creation complete after 1s [id=sgrule-1338266698]
aws_security_group_rule.testPrivateSGRulesRDSingress: Creation complete after 1s [id=sgrule-3779608258]
aws_security_group_rule.testPublicSGRulesHTTPingress: Creation complete after 2s [id=sgrule-768399379]
aws_security_group_rule.testPublicSGRulesHTTPSegress: Creation complete after 2s [id=sgrule-292654581]
aws_security_group_rule.testPublicSGRulesSSHingress: Creation complete after 3s [id=sgrule-1743733607]
aws_security_group_rule.testPublicSGRulesHTTPegress: Creation complete after 3s [id=sgrule-3303017385]

Apply complete! Resources: 16 added, 0 changed, 0 destroyed.

이런식으로 적용이 완료 되면 성공이다.

테라폼 사용하기

테라폼 공식 레퍼런스
-> 필요한 프로바이더, 리소스등을 어떻게 사용하는 상세히 나와 있다. 모르는거 있을때 참고하면 좋다.

테라폼 작동방식

  • 프로바이더 작성
    무엇을 사용해서 인프라를 구성할 지 결정 ex)aws, 구글 클라우드 서비스 등

  • 초기화 (init)
    terraform init 명령어를 통해 테라폼을 사용할 수 있게 기본적인 설정 진행.

  • 리소스 작성
    사용하고 싶은 리소스를 .tf 파일로 작성

  • 계획 / 적용 (plan/apply)
    terraform plan : 작성한 리소스들이 실제로 어떻게 만들어질지 예측 및 결과를 보여줌 (실제로 만드는 건 아님)
    terraform apply : 작성한 리소스들을 바탕으로 실제 인프라를 생성함.

프로바이더 작성

사용하게 될 서비스(aws, gcp, docker 등)를 작성

  • 사용할 디렉토리 생성.
    mkdir [디렉토리 이름]
$ mkdir test
  • 그 후 .tf파일 작성
#main.tf (파일명은 상관 없음. 본인이 알아볼 수 있는 이름으로 설정.)
terraform {
  required_version = "1.2.9" #자신의 terraform 버전으로 설정.

  required_providers {
      aws = { #사용할 프로바이더 aws 설정.
          source = "hashicorp/aws" #저장 경로.
          version = "~> 3.0" #버전 설정
      }
  }
}

provider "aws" {
  region     = "ap-northeast-2" #aws의 리전 설정. ap-northeast-2는 서울리전.
  # access_key 와 secert_key 를 파일에 적어서 구현할 수는 있지만 권장안함.
  # 보안이슈로 파일에 직접적는것보단 export나 위의 configure로 설정하는 것이 좋음.
  # access_key = "my-access-key"
  # secret_key = "my-secret-key"
}

key 부분은 전 게시물에 aws CLI를 설정했으면 넘어가도 된다.
(그리고 파일에 key값을 명시하는 것은 보안상의 이유로 추천하지 않는다.)

초기화

  • 파일 작성 후 terraform init 명령어를 통해 초기화
    $ terraform init
    Terraform has been successfully initialized!
    ##블라블라 하면서 초록색으로 성공적으로 초기화되었습니다! 뜸

    만약 프로바이더의 tf파일의 변경사항이 있을 경우
    terraform init -upgrade 해주면 마지막 변경사항으로 업데이트 된다.

리소스 작성

EC2, RDS 등 실제로 구축하게 될 자원들을 작성해야한다.

  • vpc 생성으로 테스트 해보기
#vpc.tf
resource "aws_vpc" "testVPC"{
    cidr_block = "10.0.0.0/16"
    enable_dns_hostnames = true
}

리소스의 경우 .tf 형식을 가지며 한파일에 전부 있어도 되고, 여러개로 나눠져있어도 된다. (위의 vpc.tf파일의 내용이 제일 위에 만들었던 provider.tf에 있어도 상관없음.
보통은 사용하는 aws 리소스별로 나눈다.

계획/적용

계획 (plan) : 실제 적용하기 전 코드를 테스트 해보는 것.
적용 (apply) : 실제 환경에 적용한다.

  • 위의 vpc.tf 파일을 생성했으면 plan 명령어를 사용한다.
$ terraform plan

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_vpc.testVPC will be created
  + resource "aws_vpc" "testVPC" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

이런식으로 출력되면 plan이 성공한 것이다.

👉 이런 에러가 출력될때가 있다.
An argument or block definition is required here. To set an argument, use the │ equals sign "=" to introduce the argument value.
이건 복사/붙여넣기로 파일 만들었을때 문자가 깨져서 나오는 것이므로 .tf파일을 직접 손으로 작성한 후 다시 plan할 것.

  • plan에 성공했을 경우 apply 명령어를 사용한다.
$ 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_vpc.testVPC will be created
  + resource "aws_vpc" "testVPC" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

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 #yes를 입력해줘야 한다.

aws_vpc.testVPC: Creating...
aws_vpc.testVPC: Creation complete after 2s [id=vpc-0d827147de6c34491]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

apply의 경우 실제로 환경에 적용하는 것이므로 진짜 적용할건지 한번 더 물어본다. yes를 입력해주면 적용한다.

여기까지 완료했으면 aws console을 가서 확인해보자. 새로운 vpc가 생긴것을 확인할 수 있다.


내용을 변경하고 싶으면 내용 변경 후 plan->apply를 반복하면 된다.

#vpc.tf
resource "aws_vpc" "testVPC"{
    cidr_block = "10.0.0.0/16"
    enable_dns_hostnames = true
    tags = {
        Name="testVPC" # 콘솔에서 표시될 이름 지정
    }
}

이렇게 이름을 추가 후 plan/apply를 한 후 콘솔을 확인해보면 이름이 지정된 것을 확인할 수 있다.

+ Recent posts