Terraform
처음 Terraform 단어를 접했을 때 머리속에 테라포마스 애니가 떠올랐다.
당연히 이런건 아니고
테라폼은 Hashicorp사에서 개발한 오픈소스로,
Go 언어로 개발된 IaC(Infrastructure as Code) 도구이다.
IaC
인프라를 코드로, 라는 뜻인데 과연 이게 무슨 말일까?
나는 여태까지 AWS나 GCP 리소스를 만들 때 콘솔을 이용했었다.
만들면서 느꼈던 점은 클릭 버튼과 타이핑이 좀 귀찮다라는 것이었다.
EC2를 한번만 만든것은 아니었기에, 반복적인 작업을 하다보면 중복되는 부분이 확실히 보였었다.
다시 생각해보면 콘솔말고 AWS에서 제공하는 CLI를 사용할수도 있을 것 같다.
https://docs.aws.amazon.com/ko_kr/efs/latest/ug/wt1-create-ec2-resources.html
1단계: Amazon EC2 리소스 생성 - Amazon Elastic File System
대부분의 범용 Linux 기반 AMI를 사용할 수 있습니다. 다른 Linux AMI를 사용하는 경우, 배포의 패키지 관리자를 사용하여 인스턴스에 NFS 클라이언트를 설치해야 합니다. 또한 필요할 때 소프트웨어
docs.aws.amazon.com
하지만 CLI로 EC2 하나를 만들기 위해 필요한 명령어가 무지하게 많다,,
물론 복붙하면 되지만 그것마저 귀찮다.
IaC는 이를 해결하고자 등장했다.
귀찮음을 해결하는것도 맞는 말이지만, IaC를 통해 인프라를 관리하면 이외에도 많은 장점이 있다.
작성하기 편하다.
Terraform은 tf 확장자를 가지는 HCL(Hashicorp Configuration Language)라는 언어로 구성된 파일을 통해 사용할 수 있다.
언어좀 그만 만들어.....
다행히 HCL은 json이나 yaml처럼 인간이 읽기에 친화적인 구조로 이루어져있다.
provider "aws" {
region = "ap-northeast-2"
}
굉장히 직관적인 구조를 가지고 있다!
그리고 자체적으로 내장 함수들을 지원하고 있다.
재사용할 수 있다.
IaC를 통해 인프라를 콘솔이 아닌 코드로 관리한다.
다시 말해 내가 만든 인프라 코드를, 어디서든 재사용할 수 있다.
AWS 계정의 프리티어 기간이 만료되어 다른 계정으로 migration한다고 생각해보자.
과연 내가 그동안 콘솔로 작업했던 인프라 환경을 그대로 옮길 수 있을까?
불가능에 가깝다.
하지만 IaC를 통해 코드로 남겨두면 엔터 한번이면 된다.
또, 어떤 회사에서 콘솔로 인프라를 만들기 위한 메뉴얼을 작성했다고 치자.
- EC2 서브넷 aa-subnet으로 지정해주세요.
- EC2 SG는 bb-sg로 지정해주세요.
- xxxxx....
메뉴얼대로 콘솔에서 만든다고 해도 우리는 인간이기에 클릭을 실수할 수 있다.
하지만 코드는 기계이므로 실수가 전혀 없다.
유지보수가 편해진다.
인프라 구성요소중에는 한번 설정되면 잘 안바뀌는것도 있지만 자주 바뀌는 것도 있다.
이럴때마다 콘솔을 통해 작업하게 되면 언제 누가 어떤 작업을 했는지 트래킹하기 힘들다.
하지만 코드로 작업을 하게 되면 트래킹이 쉬워진다.
또, 전임자가 남겨둔 인프라를 코드 하나만 보고 이해할 수 있게 된다!
인프라를 나 혼자만 담당하면 모르겠지만,
여럿이서 담당하게 되면 Peer Review는 필수이다.
콘솔로 작업을 했다면 문서로 정리해 내가 무엇을 했고 어떤 결과가 있었다를 보여줘야 하지만
코드로 작업했다면 코드로 리뷰를 받아보면 된다.
Terraform 구성 요소
Terraform을 구성하는 여러 요소들을 알아보자.
Provider
Terraform은 AWS부터 시작해 GCP, Azure등 여러 provider들을 지원한다.
다음과 같이 리소스를 생성할 provider를 지정할 수 있다.
provider "aws" {
region = "ap-northeast-2"
}
Resource
리소스는 지정한 provider가 실제로 생성할 인프라 리소스를 지정한다.
다음은 AWS VPC를 만드는 tf 파일이다.
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
}
State
State는 생성한 자원의 상태를 나타내며,
테라폼으로 작성된 코드를 실행(apply)하면 실제로 생성되는 파일이다.
하나 조심해야할 점은, 꼭 현재 인프라의 실제 상태를 의미하는 것은 아니다.
단순히 생성된 리소스들의 결과값이며, state 파일과 현재 인프라의 상태를 똑같이 유지하는게 핵심이다.
{
"version": 4,
"terraform_version": "0.12.24",
"serial": 3,
"outputs": {},
"resources": [
{...},
{...},
]
}
좀 더 생각해보면 state는 3곳에 각자 다른 상태로 존재할 수 있다.
- Local : 현재 개발자가 작성 및 수정하고 있는 코드를 나타내는 state
- 실제 Infra : 실제로 클라우드에 배포되어 있는 인프라 state
- Backend : Backend에 저장되어 있는 state
Backend
Backend는 .tfstate 파일을 저장하는 저장소이다.(원격 or 로컬로 구성할 수 있다)
Backend를 사용하면 여러 개발자가 동일한 .tfstate 파일을 공유하고 동시에 업데이트할 수 있다.
자체적으로 lock 매커니즘을 제공하며 보통 AWS S3, Dynamo DB와 연동한다.
설정은 다음과 같다.
terraform {
backend "s3" {
bucket = "my_bucket"
key = "my_dir/terraform.tfstate"
region = "ap-northeast-2"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
Backend에 대해서는 다른 포스팅에서 더 자세히 다루겠다.
Module
모듈은 공통적으로 재사용할 수 있는 코드를 모아놓은 것이다.
라이브러리라고 생각하면 편하다.
다음은 AWS VPC를 생성하는 모듈이다.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = true
tags = {
Terraform = "true"
Environment = "dev"
}
}
Terraform과 AWS 연결하기
Terraform을 로컬에 설치한 다음, 작성한 tf 파일을 토대로 AWS 리소스를 생성한다고 해보자.
하지만 AWS 리소스를 관리하기 위해서는 적절한 자격 증명이 필요하다.
AWS CLI 자격증명
자격증명은 다음과 같은 명령어로 할 수 있다.
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json
configure가 완료되면 ~/.aws에 Key가 담긴 credentials 파일이 생긴다.
Terraform에서 AWS 자격증명 이용하기
간단하다.
provider를 aws로만 설정해주면 Terraform은 알아서 ~/.aws/credentials에 적힌 Key를 통해 자격증명을 한다.
IAM User에 맞는 권한으로 Terraform을 사용할 수 있다.
Terraform 명령어
Terraform의 기본 명령어에 대해 알아보자.
init
명령어 사용을 위해 각종 설정(provider, state, module)을 진행한다.
지정한 Backend에 상태 저장을 위한 .tfstate 파일을 생성한다.
tfstate파일에는 가장 마지막에 apply한 테라폼 내역이 저장된다.
Backend의 default는 Local이다.
init 작업을 완료하면, local에는 .tfstate에 정의된 내용을 담은 .terraform 폴더가 생성된다.
.terraform 폴더
.terraform 폴더는 테라폼 내부적으로 사용하는 것으로, 개발 환경마다 다를 수 있으므로
.gitignore를 통해 untracking 해야 한다.
.terraform 폴더에는 module과 관련된 파일, provider의 플러그인, backend 구성 등이 있다.
만약 기존에 다른 개발자가 이미 .tfstate에 인프라를 정의해 놓은 것이 있다면,
init 작업을 통해 local에 sync를 맞출 수 있다.
init 명령어는 모든 명령어의 첫 단계이다.
init -> plan -> apply 순서로 이루어진다.
terraform init
plan
현재까지 작성한 tf 파일을 토대로 실제로 어떻게 만들어질지에 대한 예측 결과를 보여준다.
일종의 dry run처럼 작동한다! (kubectl에서의 dry run이라고 생각하면 된다)
따라서 실제 인프라에 영향을 끼치지 않는다.
live 인프라의 상태를 바로 바꾸는것은 매우 위험하니 plan을 통해 미리 점검한다고 생각하면 된다.
terraform plan
apply
plan은 미리보기 였다면, apply는 실제로 적용하는 것이다.
apply가 완료되면 실제로 AWS 인프라에 리소스가 생성되고
backend의 .tfstate 파일에도 저장되고 local의 .terraform 폴더에도 저장된다.
plan과 apply는 실제 AWS 인프라의 상태(state)와
현재 로컬에 있는 테라폼 코드의 state를 비교해서 변경사항들을 보여준다.
import
import 명령어는 테라폼을 통해 만들지 않은 리소스를 테라폼 형상 관리에 포함시키는 명령어이다.
개인적으로 테라폼을 공부하면서 이거 되면 대박이겠다! 하는 기능이었는데 실제로 있었다.
다만, 무작정 바로 import할 수는 없고 사전에 해당 리소스를 정의할 코드를 작성해놔야 한다.
예시로 보자.
현재 내 버킷들은 이렇다.
imsongkk 라는 버킷은 테라폼을 통해 만들었고, imsongkk-no-terraform은 콘솔에서 직접 만든 버킷이다.
imsongkk 버킷을 만들었던 테라폼 코드는 다음과 같다.
resource "aws_s3_bucket" "imsongkk" {
bucket = "imsongkk"
}
로컬에 있는 .tfstate 파일을 보자.
imsongkk 버킷밖에 없는것을 볼 수 있다.
이 상태에서 s3.tf 파일을 다음과 같이 수정해보자.
resource "aws_s3_bucket" "imsongkk" {
bucket = "imsongkk"
}
# 추가된 부분
resource "aws_s3_bucket" "import-test" {
bucket = "imsongkk-no-terraform"
}
이제 import를 해보자.
terraform import [RESOURCE_TYPE].[RESOURCE_NAME] ID
terraform import aws_s3_bucket.import-test imsongkk-no-terraform
AWS 정책상 버킷의 이름은 전세계에서 고유하므로 ID로 사용할 수 있다.
tfstate 파일도 확인해보자.
콘솔로 만든 리소스도 테라폼 state로 바꾸었다!
destroy
생성이 있다면 삭제도 있다.
생성했던 리소스들을 한번에 삭제 할 수 있다.
조심해서 사용하자.