클라우드 관련 프로젝트를 진행하면서 terraform을 사용하고 있습니다.
terraform을 사용할 때 한 개의 파일에 모든 기능을 다 넣어도 되지만, 기능을 모듈별로 분리하여 재사용 가능한 구조로 terraform을 사용하면 더 효율적으로 코드를 작성할 수 있습니다.
Workspace, Module을 활용해 재활용 가능한 테라폼 보일러플레이트를 만들어보겠습니다.
개발 환경
- 사용된 소스코드
- vscode
- ubuntu 18.04 (WSL2, microsoft kernel)
- docker-compose
terraform은 따로 설치하지 않고, docker를 사용해 구동합니다.
terraform을 설치해서 사용하셔도 무관합니다. 단 , Docker-compose에 기재된 /infra를 기준으로 절대경로가 고정되어 있어서 , terraform을 설치해서 사용하실 경우 모듈 경로를 모두 바꿔주셔야 합니다.
terraform-boilerplate를 아래 명령어를 이용해 clone 받으시거나
$ git clone https://github.com/KimKiHyuk/terraform-boilerplate.git
프로젝트 루트폴더에 아래와 같이 docker-compose 파일을 생성합니다.
docker-compose.yml
version: '3.7'services: tf: image: hashicorp/terraform:0.13.3 volumes: - .:/infra working_dir: /infra
프로젝트 구조
- terraform/
— 루트 디렉토리
- arch/
— 아키텍처를 모은 디렉토리
- modules/
— 아키텍처에 사용될 서브모듈
- scripts/
— 설치, 셋팅 스크립트
- utility/
— 키페어 생성 등 유틸리티 모음
- terraform.tfstate.d
— 현재 인프라 상태를 추적하기 위해 .tfstate 파일들을 모아둔 곳 입니다.
workspace 별로 각기 다른 인프라를 관리할 수 있습니다.
아키텍처
2개의 EC2 A, B를 만듭니다. A는 public subnet을, B는 Private subnet을 물려줍니다.
A에는 Nginx를 설치하고 80포트를 열어 웹 서버를 구축합니다.
B는 A에서만 접근 가능하고, 인터넷에서 B에 접근할 수 없지만, B는 인터넷에 접근할 수 있는 아키텍처를 Terraform으로 생성해보겠습니다.
Key Pair 생성하기
먼저 EC2에 접근할 수 있는 private key를 생성해보겠습니다.
프로젝트 루트 디렉토리에서 키페어를 위한 workspace를 생성합니다.
docker-compose run --rm tf workspace new keypair
workspace를 생성하면 방금 생성한 workspace로 자동 스위칭 됩니다.
list 명령어를 이용해서 확인해봅시다.
docker-compose run --rm tf workspace list
앞으로 작업하는 모든 변경 이력들은 terraform.tfstate.d/keypair/terraform.tfstate 에 저장됩니다.
이제 key pair를 생성해봅시다.
terraform/utility/aws/generate_key/main.tf
var.* 라고 기재된 부분들은 외부에서 입력되는 매개변수입니다.
매개변수로 access_key, secret_key, region, name을 입력받습니다.
terraform/utility/aws/generate_key/variables.tf
위에서 사용할 매개변수를 선언하는 코드입니다.
만약 매개변수가 들어오지 않는다면 default 키워드를 이용해 기본값을 설정할 수 있습니다.
terraform/utility/aws/generate_key/output.tf
테라폼이 실행된 후 결과값을 출력하는 코드입니다.
키 이름, private, public 키값을 출력해줍니다.
모든 코드 작성이 끝났으므로, 테라폼을 실행시켜 봅시다.
테라폼은 아래와 같이 크게 4단계로 동작합니다.
- init 에서 테라폼 설정파일을 생성하고, 필요한 플러그인을 다운로드 받습니다.
2. plan에서는 어떤 인프라가 생성/삭제 될 지, 또 이에 대한 설정값을 확인할 수 있습니다.
3. Apply는 현재 계획한 인프라를 AWS, Azure 등 클라우드에 실제로 배포하는 명령어입니다.
4. Destroy는 현재 계획한 인프라를 삭제하는 명령어입니다.
이 모든 명령어는 tfstate라는 파일에 의존적입니다. 따라서 tfstate를 억지로 수정하거나, 변경하시면 안됩니다.
또한 전혀 다른 인프라를 생성 및 관리하시려면 workspace를 새로 만드셔서 새로운 tfstate를 생성하셔야 합니다.
init 명령어를 사용해서 초기 설정을 해줍니다.
docker-compose run --rm tf init \
terraform/utility/aws/generate_keypair
apply 명령어를 이용해 키페어를 생성합니다.
apply 명령어를 실행하면 plan의 결과도 함께 출력됩니다.
docker-compose run --rm tf apply \
-var "region=us-east-2" \
-var "access_key=***********" \
-var "secret_key=***********" \
terraform/utility/aws/generate_keypair
어떤 리소스가 생성되는지 확인하고, 마지막으로 이 명령을 승인할 것 인지 물어봅니다. 이 때 yes를 입력합니다.
--auto-approve 명령어를 이용해 마지막 승인을 묻는 과정을 생략할 수 있습니다.
성공적으로 작업이 끝나면, output.tf에서 작성한 private, public key와 key 이름을 출력할 수 있습니다.
이 중 ssh_private_key_pem 출력문으로 private key를 파일로 만듭니다.
docker-compose run --rm tf output ssh_private_key_pem > test-key.pem
만든 key 파일이 실행될 수 있도록 권한을 설정해줍니다.
chmod 700 test-key.pem
EC2에 접근하기 위한 key pair를 생성했습니다.
아키텍처 생성하기
docker-compose run --rm tf workspace new infra
infra라는 새로운 workspace를 생성합니다.
테라폼 코드를 재사용하기 위해선 Module 기능을 활용해야 합니다.
module은 미리 정의한 테라폼 코드를 다시 사용할 수 있는 함수와 같은 기능입니다.
아래는 아키텍처를 생성하는 메인 프로그램입니다.
terraform/arch/ec2_with_nginx/dev/main.tf
아래 그림을 구성하는 소스코드이며, module 키워드를 통해 다른 디렉토리에 위치한 리소스들을 가져다가 재사용할 수 있습니다.
네트워크 설정
module은 source 에 명시된 위치의 *.tf 파일들을 실행합니다.
docker-compose에서 정의한 디렉토리에서 테라폼을 실행하므로 모든 source의 시작 디렉토리는 /infra를 가르킵니다.
먼저 VPC를 생성하고, 생성한 VPC id를 public, private 서브넷과 인터넷 게이트웨이를 생성할 때 사용합니다.
생성된 vpc는 vpc_id를 리턴(출력)하도록 output 파일을 만들어줍니다.
또한 vpc를 생성하기 위한 variable을 정의해줍니다.
vpc를 생성하면,
module.aws_vpc.vpc_id
위와 같이 생성된 vpc 정보 output에 접근할 수 있습니다.
이제 vpc_id를 이용해 NAT, 인터넷 게이트웨이를 생성합니다.
vpc_id를 사용해 인터넷 게이트웨이를 생성합니다.
eip를 생성하고, 이를 NAT에 물려줍니다.
public, private 서브넷을 각각 하나씩 생성합니다.
라우팅 설정
위 네트워크 설정에서 만든 서브넷들에 라우팅 테이블을 만들어줍니다.
private-route 에 NAT를 물려주어 내부에서 인터넷으로 통신이 가능하도록 설정합니다.
public-route에 인터넷 게이트웨이를 물려주어 인터넷과 연결이 가능하도록 구성합니다.
EC2
이제 EC2를 생성해보겠습니다.
동일한 sg 그룹에 2개의 EC2를 생성합니다.
aws_ec2_public은 인터넷에서 접속 가능한 인스턴스입니다.
인스턴스를 배포할 때 nginx 도커 웹서버를 생성하도록 구성합니다.
docker_image 매개변수에 도커 이미지 이름인 nginx를 주고, port를 설정해줍니다.
실제 EC2를 생성시키는 코드는 아래와 같습니다.
provisioner “file” {
source = "스크립트 파일 위치"
}provisioner "remote-exec" {
...
inline = [ commands ]}
EC2가 생성될 때 실행할 스크립트를 file로 던져주고, 이를 실행하는 커맨드를 remote-exec inline에서 지정해줍니다.
위에서 file 매개변수로 던져줄 스크립트입니다.
docker를 설치하고, 전달된 NGINX docker image를 pull 받고 실행하는 쉘 스크립트입니다.
위와 같은 인프라 구성을 마쳤고, A 인스턴스 생성 시 Nginx가 실행되도록 스크립트를 넣어주었습니다.
init 명령어로 테라폼 초기설정을 구성합니다.
docker-compose run --rm tf init terraform/arch/ec2_with_nginx/dev/
Apply 명령어를 이용해 배포해봅시다.
docker-compose run --rm tf apply \
-var "region=us-east-2" \
-var "access_key=*********" \
-var "secret_key=*********" \
terraform/arch/ec2_with_nginx/dev/
Nginx에 잘 붙는것을 확인할 수 있습니다.
이제 인스턴스에 SSH로 접근해보겠습니다.
ssh -i test-key.pem ubuntu@<public_ip>
ip 확인이 안되시면 아래 명령어로 확인 가능합니다
docker-compose run --rm tf output public_ip
docker-compose run --rm tf output private_ip
private 인스턴스에 접근하기 위해서 public 인스턴스안에도 pem키를 전송해주어야 합니다.
scp를 이용해서 로컬머신에서 public 인스턴스로 pem키를 전송해줍니다.
scp -i test-key.pem test-key.pem ubuntu@<public_ip>:~
10.10.20.0/24 대역의 public 인스턴스에서
10.10.21.0/24 대역에 있는 private 인스턴스에 핑을 날려봅니다.
public 인스턴스에서 .pem키를 사용해 private 인스턴스에 접속합니다.
ssh -i test-key.pem ubuntu@<private_ip>
접속한 인스턴스는 외부 인터넷에서 직접적인 접근이 불가능합니다.
하지만 NAT 덕분에 외부 인터넷으로의 접근은 가능합니다.