티스토리 뷰

카테고리 없음

Terraform 기초 다지기

미니대왕님 2023. 1. 26. 14:29

GitHub에서 코드 바로 실행해보기

https://www.terraform.io/downloads : 테라폼 다운로드

목차

1. Terraform이란?

2. 실습 환경 설명, terraform 설치

3. IAM User & ACCESS KEY 설정

4. 간단하게 EC2 배포해보기 (+멱등성 체험)

5. Terraform으로 프로비저닝한 리소스들 삭제

 

1. Terraform이란?

먼저 IaC(Infrastructure as Code, 코드형 인프라)란? 코드형태로 인프라를 작성, 정의, 배포, 업데이트 함

IaC 도구는 크게 애드 혹 스크립트, 구성 관리 도구, 서버 템플릿 도구, 서버 프로비전 도구 4가지로 나뉜다

  • 구성 관리 도구 : Ansible, Chef, Puppet - 서버를 관리하는 쪽에 좀 더 초점
  • 서버 프로비전 도구 : Terraform, AWS Cloudformation - 서버와 인프라 자체를 구성하는데에 좀 더 초점

 

2. 실습 환경 설명, terraform 설치

사용 에디터 : Sublime Text 3 (Install Package로 Terraform 설치했음)

테라폼 다운 : www.terraform.io/downloads.html 여기에서 본인 OS에 맞는 terraform 다운로드

테라폼을 어느 위치에서도 사용할 수 있게 하기 위해 환경변수 PATH도 설정해주었다

 

 

일단 C:\에 terraform이란 새 폴더를 만들어서 terraform 파일을 넣어둔다

 

 

환경 변수를 검색해서 시스템 환경 변수 편집으로 들어가자

 

 

위와 같은 방법으로 시스템 변수의 Path에 C:\terraform을 추가해주고 확인을 눌러준다

 

 

cmd로 아무 위치에서 terraform을 쳤을 때 위와 같이 나오면 terraform 설치는 끝이다

 

그리고 main.tf를 위치시킬 폴더를 하나 아무데나 생성해주자

난 Terraform이란 폴더를 하나 만들어줬고 cmd에서 그 폴더 내로 이동한 후 진행하겠다

 

해당 폴더에서도 terraform 명령어가 먹히는지 확인

 

3. IAM User & ACCESS KEY 설정

테라폼에서 AWS의 리소스를 프로비저닝 하기 위해서는 해당 계정의 ACCESS KEY와 SECRET KEY가 필요하다

간단한 실습만 진행할거긴 하지만, 그래도 루트 계정으로 진행하는건 위험하기도 하고 보안상으로도 좋지 않기 때문에 최소한의 권한만 있는 IAM User를 만든 후 진행해보겠다

콘솔 > IAM > 사용자 > 사용자 추가

 

 

테라폼으로만 접근할거니까 프로그래밍 방식 액세스만 허용해주자

 

 

EC2만 만들어볼거니까 AmazonEC2FullAccess만 연결해준다

(VPC는 기본 VPC로 진행할 예정)

 

생성을 완료하면 이렇게 ACCESS KEY와 SECRET KEY를 다운받을 수 있는 창이 뜬다

각각의 키를 메모장에 복사해두던지 csv 파일을 다운받든지 하자 (지금 지나면 다시 못받는다)

 

이제 다시 cmd로 돌아와서, 액세스 키와 시크릿 키를 다음과 같이 등록해주자

set AWS_ACCESS_KEY_ID=[본인 계정 액세스 키]
set AWS_SECRET_ACCESS_KEY=[본인 계정 시크릿 키]

 

 set 명령은 일반 변수 등록이라 현재 터미널에서만 유용하다 (다른 cmd 창을 키면 초기화 되어있음)

영구 등록이 필요하면 setx 명령을 사용하면 되고, linux의 경우엔 export를 사용하면 된다

 

4. 간단하게 EC2 배포해보기 (+멱등성 체험)

일단 기본 VPC가 배포되어 있어야 한다 (없으면 작업 > 기본 VPC 생성으로 만들면 됨)

기본 VPC로 진행하면 VPC id나 서브넷 id 등을 지정할 필요가 없다

지금은 맛보기만 하는거기때문에 최대한 간단하게 간단하게 해보겠다

 

 

일단 만들어둔 폴더에 main.tf 파일을 생성한 다음, 공급자(provider)를 설정해줘야 한다

provider "aws" {
  region = "ap-northeast-2"
}

 

PROVIDER는 AWS, Azure 등 공급자의 이름이다

따라서 위 구문으로 인해 테라폼은 아마존 웹 서비스의 서울 리전에서 인프라를 배포해야 한다는걸 인지하게 된다

이제 EC2를 만들기 위해 아래에 다음 구문을 적어주자

 

resource "aws_instance" "testEC2" {
  ami           = "ami-0e17ad9abf7e5c818"
  instance_type = "t2.micro"
}

aws_instance 자리엔 생성할 리소스 타입을, testEC2 자리엔 가독성을 위해 다른 코드들과 구분될만한 이름을 적어주면 된다

AMI는 서울 리전의 Amazon Linux 2 AMI이다

 

 

간단한 코드가 완성되었으니, 실행을 해보자

cmd에서 아래 명령들을 차례대로 쳐주자

 

terraform init
terraform plan
terraform apply
terraform destroy

 

init : 테라폼을 수행하기 위한 공급자의 최신 플러그인들을 자동 설정 - provider 구문이 설정된 후에 실행할 수 있다

 

 

plan : 테라폼이 실행했을 시, 실제로 생성되거나 변경되는 내역을 보여준다

테라폼은 멱등성(Idempotency)이라는 중요한 개념이 있기 때문에, 같은 코드들을 조금씩 변경하면서 실행했을 때 매번 새롭게 생성되는것이 아니라 변경된 부분만 반영되어 프로비저닝된다

 

 

마지막으로 apply 명령으로 테라폼을 실제로 실행시키게 되는데, 변경 내역을 확인한다고 하기 위해 중간에 yes를 한번 쳐주고 진행하면 끝난다

 

 

완료된 후 콘솔에 가보면

 

 

설정했던 대로의 인스턴스가 만들어져 있다

 

근데 우리는 멱등성을 경험해보고 싶기 때문에, 코드를 아래와 같이 좀 수정해서 다시 실행시켜보겠다

provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_instance" "testEC2" {
  ami           = "ami-0e17ad9abf7e5c818"
  instance_type = "t2.micro"

  tags = {
    Name = "Web Server"
  }
}

별건 없고 그냥 Tag만 추가해주었다

이러고 다시 apply를 해주면

 

 

아까 그 인스턴스에 그대로 태그만 생긴게 확인되었다

: 멱등성 때문에 변경된 부분만 반영해서 변경된 것

(물론 user data같은걸 바꾸게 된다면 종료하고 다시 만들어야하긴 한다)

 

5. Terraform으로 프로비저닝한 리소스들 삭제

terraform destroy

그냥 그대로 destory 명령만 해주면 삭제될 변경사항들이 쭈루룩 생기면서 yes를 누르라고 나온다

yes만 마저 눌러주면 알아서 전부 삭제된다

 

 

 

6.AWS 보안 그룹(예시)

 

 

VPC를 만들었으니, 이제 보안 그룹을 만들어서 앞으로 만들 EC2나 RDS 등에게 적용할 준비를 해야 합니다.

구성은 단순하며, 이번엔 기본 구성 방법과 Module을 이용한 방법에 대해 소개합니다.

#기본 생성 방식
resource "aws_security_group" "web" {
  vpc_id = "${module.vpc.vpc_id}"     #생성할 위치의 VPC ID
  name = "WEB"                        #그룹 이름
  description = "Terraform WEB SG"    #설명
  ingress {
    from_port = 22                    #인바운드 시작 포트
    to_port = 22                      #인바운드 끝나는 포트
    protocol = "tcp"                  #사용할 프로토콜
    cidr_blocks = ["0.0.0.0/0"]       #허용할 IP 범위
}
  ingress {
    from_port = 80
    to_port = 80
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
}
 ingress {
    from_port = 443
    to_port = 443
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
}
  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks     = ["0.0.0.0/0"]
  }
}

 

#모듈을 이용한 방식
module "security-group" {
  source  = "terraform-aws-modules/security-group/aws" #사용할 모듈 소스
  version = "3.1.0"                                    #모듈 버전

  name        = "Web"                                  #그룹이름
  description = "Web"                                  #설명
  vpc_id      = "${module.vpc.vpc_id}"                 #생성할 위치의 VPC ID
  use_name_prefix = "false"                            #해당 옵션을 false 시키지 않을 경우, 그룹이름 뒤에 고유 넘버링이 부착되어 생성됨
  ingress_with_cidr_blocks = [
    {
      from_port   = 443                                #인바운드 시작 포트
      to_port     = 443                                #인바운드 끝나는 포트
      protocol    = "tcp"                              #사용할 프로토콜
      description = "https"                            #설명
      cidr_blocks = "0.0.0.0/0"                        #허용할 IP 범위
    },
    {
      from_port   = 80
      to_port     = 80
      protocol    = "tcp"
      description = "http"
      cidr_blocks = "0.0.0.0/0"
    },
    {
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      description = "ssh"
      cidr_blocks = "0.0.0.0/0"
    }
]
  egress_with_cidr_blocks = [
    {
      from_port   = 0                                #아웃바운드 시작 포트
      to_port     = 0                                #아웃바운드 끝나는 포트
      protocol    = "-1"                             #사용할 프로토콜
      description = "all"                            #설명
      cidr_blocks = "0.0.0.0/0"                      #허용할 IP 범위
    }
]
}

모듈을 사용하더라도 VPC처럼 여러 옵션들이 들어가는 게 아니기 때문에 코드가 절약되고 하지는 않습니다.

문법이 약간 다르기 때문에 편리한 방법을 선택하여 사용하시면 됩니다.

 

이렇게 보면서 궁금하실 만한 점은 WEB서버용은 추가했는데, RDS용은 어떻게 추가하느냐가 될 것 같은데요.

기본 방식으로는 resource "aws_security_group" "web"를 resource "aws_security_group" "rds"라는 식으로 리소스를 추가하여 생성하시면 되고,

 

모듈 또한 module "security-group"에서 module "security-group 2"식으로 리소스를 추가하여 생성하시면 됩니다.

어때요? 단순하죠?

 

코드 기반이라는 선입견 때문에 어렵다고 느껴지실 수 있지만 자세히 보다 보면 일정한 문법이 있기 때문에 그리 어렵지만은 않습니다.

한번 따라 해 보시고 익숙해져 보세요.

 

7. ALB 만들기 (예시)

resource "aws_alb" "terraform-alb1" {
    name                            = "web1"
    internal                        = false
    load_balancer_type              = "application"
    idle_timeout                    = 60
    security_groups                 = ["sg-084b72f3dd7d5480e"]
    subnets                         = ["subnet-0ba04b52302cc82ad","subnet-069242ebff19529f5"]
    enable_deletion_protection      = false
    ####deletion protection 활성화되어 있으면 삭제가 안 된다.
    lifecycle {
        create_before_destroy       = true
    }
    tags = {
        Name                        = "web1"
        Env                         = "stg"
        CreateUser                  = "terraform@email.com"
        Owner                       = "iac"
        Role                        = "alb"
        Service                     = "network"
    }
}
/*
#####################ALB 타겟 그룹 설정
resource "aws_alb_target_group" "tf_alb_target_group-web1" {
    name                            = "web1"
    port                            = 80
    protocol                        = "HTTP"
    vpc_id                          = aws_vpc.tf_vpc99.id
    health_check {
        interval                    = 30
        path                        = "/"
        timeout                     = 3
        healthy_threshold           = 3
        unhealthy_threshold         = 2
        matcher                     = "200"
    }
    tags = {
        Name                        = "web1"
        Env                         = "stg"
        CreateUser                  = "terraform@email.com"
        Owner                       = "iac"
        Role                        = "alb_target_group"
        Service                     = "network"
    }
}
*/
####################ALB listener HTTP service
resource "aws_alb_listener" "tf_alb_listener_80" {
    load_balancer_arn               = aws_alb.terraform-alb1.arn
    port                            = "80"
    protocol                        = "HTTP"
    default_action {
        type                        = "redirect"
        redirect {
            port                    = "443"
            protocol                = "HTTPS"
            status_code             = "HTTP_301"
        }
    }
}
###################ALB listener HTTPS service
resource "aws_alb_listener" "tf_alb_listener_443" {
    load_balancer_arn               = aws_alb.terraform-alb1.arn
    port                            = "443"
    protocol                        = "HTTPS"
    ssl_policy                      = "ELBSecurityPolicy-2016-08"
    #depends_on                     = aws_alb_target_group.tf_alb_target_group-web1
    //certificate_arn                 = aws_acm_certificate.tf4wxyzcom.arn
    default_action {
        type = "forward"
       // target_group_arn = aws_alb_target_group.tf_alb_target_group-web1.arn
    }
}

/*
###ALB target group attachment
resource "aws_alb_target_group_attachment" "tf_attachment-web31" {
//    target_group_arn                = aws_alb_target_group.tf_alb_target_group-web1.arn
    //target_id                       = aws_instance.tf_instance_web31.id
    port                            = 80
}
*/

 

************************************************************************************************************

 

테라폼에서 VPC, Subnet, Gateway, Route 생성하기

 

 
[root@keystone terraform-example]cat vpc-subnet.tf
 
resource "aws_vpc" "kube-vpc" {
  cidr_block = "10.0.0.0/16"
  tags {
    Name = "kube-vpc"
  }
}
 
resource "aws_subnet" "kube-subnet-c" {
  vpc_id = "${aws_vpc.kube-vpc.id}"
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-northeast-2c"
}


resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
   Name = "terraform-vpc"
  }
}

resource "aws_subnet" "public_subnet" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.0.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
   Name = "terraform-vpc-public-subnet"
  }

}

resource "aws_subnet" "private_subnet" {
  vpc_id = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  availability_zone = "ap-northeast-2a"

  tags = {
   Name = "terraform-vpc-private-subnet"
  }
}
 

 

내용은 간단하다. 

 

  1. CIDR가 10.0.0.0/16인 aws_vpc 리소스를 생성하되, kube-vpc라는 이름으로 생성하도록 tags를 준다.
  2. aws_subnet 리소스를 생성하되, CIDR는 10.0.1.0/24 이며 AZ는 ap-northeast-2c 이다. 

 

여기서 흥미로운 점은 aws_subnet의 vpc_id가 ${aws_vpc.kube-vpc.id} 로 설정되었다는 점인데, aws_vpc 리소스 중 kube-vpc라는 이름의 VPC를 선택해 지정하겠다는 뜻이다. 단, ${리소스이름} 으로 참조를 할 때는 AWS에서 보이는 이름이 아닌 테라폼 내에서 고유하게 식별되기 위한 이름을 사용해야 한다. AWS에서 쓰이는 이름을 쓰고 싶다면 ${} 없이 단순 문자열로 이름을 입력한다.

 

 

두 번째로 게이트웨이를 정의하는 gateway.tf 파일을 작성한다. 이번에도 크게 어렵지 않은 내용이다. kube-gateway라는 태그 이름을 가진 게이트웨이를 생성하되, 연결되는 VPC를 kube-vpc로 하겠다는 내용이다.

 

1
2
3
4
5
6
7
8
[root@keystone terraform-example]cat gateway.tf
 
resource "aws_internet_gateway" "kube-gateway" {
  vpc_id = "${aws_vpc.kube-vpc.id}"
tags {
    Name = "kube-gateway"
  }
}
 
cs

 

 

세 번째로 외부 통신을 위한 라우팅 룰인 route.tf 파일을 작성한다. 마찬가지로 크게 어렵지 않다. Subnet, Route, VPC 등을 다른 파일 또는 동일 파일의 테라폼 변수 이름을 통해 참조해 입력했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@keystone terraform-example]cat routes.tf
 
resource "aws_route_table" "kube-route-table" {
  vpc_id = "${aws_vpc.kube-vpc.id}"
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.kube-gateway.id}"
  }
  tags {
    Name = "kube-route-table"
  }
}
 
resource "aws_route_table_association" "my-subnet-association" {
  subnet_id      = "${aws_subnet.kube-subnet-c.id}"
  route_table_id = "${aws_route_table.kube-route-table.id}"
}
 
cs

 

 

모든 파일을 작성했다면 terraform plan 명령어를 입력한다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@keystone terraform-example]# terraform plan
...
 
Terraform will perform the following actions:
 
  + aws_internet_gateway.kube-gateway
      id:                                          <computed>
      owner_id:                                    <computed>
      tags.%:                                      "1"
      tags.Name:                                   "kube-gateway"
      vpc_id:                                      "${aws_vpc.kube-vpc.id}"
 
...
 
 
Plan: 5 to add, 0 to change, 0 to destroy.
 
------------------------------------------------------------------------
 
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
 
cs

 

이것이 테라폼의 다른 특징 중 하나인데, terraform plan 명령어는 현재 디렉터리에서 변경된 사항들을 적용하기 전, 미리 결과를 확인하는 용도로 사용된다. 즉 Dry run과 같은 용도로 사용될 수 있기 때문에 실제로 변경된 사항을 적용하기 전에 한 번쯤은 실행해보는 것을 추천한다.

 

출력의 마지막에 terraform apply를 실행하라고 되어 있으니, 실제로 VPC, Subnet 등을 만들기 위해 이를 실행해보자.

중간에 yes를 입력하라고 뜨는데, 정말로 실행할 것인지를 다시 묻는 것이므로 yes를 입력해야 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@keystone terraform-example]terraform apply
...
 
 
Plan: 5 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
 
aws_vpc.kube-vpc: Creating...
....
 
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
cs

 

출력이 너무 많아 적절히 생략하였지만 출력 내용을 자세히 보면 어떠한 속성으로 AWS 오브젝트가 생성되는지를 확인할 수 있다. 어쨌든, AWS 관리 콘솔의 VPC에 가면 실제로 생성된 것을 확인할 수 있다.

 

 

 

현재 파일 구조는 아래와 같다. gateway.tf, routes.tf, vpc-subnet.tf가 추가되었다.

 

1
2
3
4
5
6
7
8
9
10
11
12
[root@keystone terraform-example]tree
.
├── aws-key.pub
├── aws-provider.tf
├── gateway.tf
├── key.tf
├── routes.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── vpc-subnet.tf
 
0 directories, 8 files
cs

 

테라폼은 terraform apply를 통해 어떠한 변경 사항을 적용할때마다 이전 상태에 대한 백업 파일을 생성한다. 위에서 terraform.tfstate.backup이 바로
댓글