테라폼 stage, module
September 2023 (1608 Words, 9 Minutes)
State
State 목적과 의미
탄경
- 공유 스토리지 필요성: 여러 팀원이 동일한 테라폼 상태 파일을 사용하기 위해 공유 스토리지가 필요
race condition
을 통한 state 관리 - 파일 잠금: 여러 팀원이 동시에 테라폼을 실행할 경우, 상태 파일의 동시 수정으로 인한 충돌이 발생 가능
- 환경 격리
isolate state
: 개발, 스테이징, 프로덕션과 같은 다양한 환경에서 상태 파일을 격리할 필요
버전 관리 시스템의 한계
- 수동 오류: 팀원이 상태 파일을 최신 상태로 유지하지 않으면 문제가 발생할 수 있습니다.
- 잠금 미지원: 대부분의 VCS는 상태 파일의 잠금을 지원하지 않습니다.
- 시크릿 노출 위험: 상태 파일에는 민감한 정보가 평문으로 저장될 수 있어, 보안 위험이 있습니다.
장점점
테라폼은 이러한 문제를 해결하기 위해 원격 백엔드(AWS S3, Azure Blob Storage 등)를 지원합니다.
- 자동 동기화: 테라폼 작업을 실행할 때마다 원격 백엔드에서 자동으로 상태 파일을 불러오고 저장합니다.
- 잠금 지원
lock
: 원격 백엔드를 사용하면 자동으로 상태 파일의 잠금을 관리할 수 있습니다. - 암호화 지원: 대부분의 원격 백엔드는 데이터 전송 및 저장 시 암호화를 지원합니다.
state 동기화 구조는 다음과 같습니다.
- 테라폼 구성과 State 흐름 : Plan 과 Apply 중 각 리소스에 발생할 수 있는 네 가지 사항, 아래 실행 계획 출력 기호와 의미
기호 | 의미 |
---|---|
+ | Create |
- | Destroy |
-/+ | Replace |
~ | Updated in-place |
워크스페이스
Terraform의 workspace는 Terraform 설정의 상태를 분리할 수 있는 환경입니다. 여러 workspace를 사용하면 같은 구성을 가진 별도의 환경을 만들 수 있습니다.
예를 들어, 개발(dev
), 스테이징(staging
), 그리고 프로덕션(production
) 환경을 관리해야 하는 경우, 각 환경에 대한 서로 다른 workspace를 생성할 수 있습니다. 이렇게 하면 Terraform 설정은 동일하지만, 각 workspace의 상태는 각각 관리됩니다.
주요 기능 및 장점
- 상태 분리: 각 workspace는 자체 상태 파일을 가지므로 여러 환경에서 동일한 구성을 안전하게 재사용할 수 있습니다.
- 변수 재사용: 동일한 Terraform 코드를 다른 매개변수와 함께 여러 환경에서 실행할 수 있습니다. 예를 들어, 스테이징 환경에서는 작은 VM 인스턴스를, 프로덕션에서는 큰 VM 인스턴스를 사용할 수 있습니다.
- 효율적인 리소스 관리: Workspace를 사용하면, 각 환경에 필요한 클라우드 리소스를 따로 관리할 수 있으므로, 리소스를 더 효율적으로 활용할 수 있습니다.
사용법
기본적인 명령어는 다음과 같습니다:
- 새 workspace 생성:
terraform workspace new [workspace_name]
- workspace 목록 보기:
terraform workspace list
- workspace 전환:
terraform workspace select [workspace_name]
여러 workspace를 사용하면, 환경마다 다른 terraform.tfstate
파일이 생성되므로 각각의 환경을 독립적으로 관리할 수 있습니다.
장단점 총정리
- 장점: 동일한 테라폼 구성으로 다양한 환경의 리소스를 프로비저닝하고 관리할 수 있습니다.
- 단점: 완벽한 격리가 어렵고, 상태 파일에 대한 접근 권한 관리가 복잡해질 수 있습니다.
- 유형1 : 신규 리소스 정의 → Apply ⇒ 리소스 생성(처음부터 Terraform 코드로 작업)
-
실습을 위해서 5.2 디렉터리를 신규 생성 후 열기 → main.tf 파일 생성
mkdir 5.2 && cd 5.2
locals { name = "mytest" } resource "aws_iam_user" "myiamuser1" { name = "${local.name}1" } resource "aws_iam_user" "myiamuser2" { name = "${local.name}2" }
-
실행
# **terraform init && terraform apply -auto-approve** terraform state list terraform state show aws_iam_user.myiamuser1 # **ls *.tfstate cat terraform.tfstate | jq # terraform apply -auto-approve ls *.tfstate** # iam 사용자 리스트 확인 aws iam list-users | jq
-
- 유형2 : 실제 리소스 수동 제거 → Apply ⇒ 리소스 생성
-
실행
# 실제 리소스 수동 제거 **aws iam delete-user --user-name mytest1 aws iam delete-user --user-name mytest2** aws iam list-users | jq # 아래 명령어 실행 결과 차이는? (refresh 유무) # 실제 리소스는 제거되었으나 -refrest=false로 인해 변경사항 없으므로 인식 **terraform plan** **terraform plan -refresh=false cat terraform.tfstate | jq .serial # terraform apply -auto-approve** terraform state list **cat terraform.tfstate | jq .serial** # iam 사용자 리스트 확인 aws iam list-users | jq
-
- 유형3 : Apply → Apply - 코드, State, 형상 모두 일치한 경우 ⇒ 변경사항 없음
-
실행
**# Serial 값일 동일! terraform apply -auto-approve cat terraform.tfstate | jq .serial terraform apply -auto-approve cat terraform.tfstate | jq .serial terraform apply -auto-approve cat terraform.tfstate | jq .serial**
-
- 유형4 : 코드에서 일부 리소스 삭제 → Apply ⇒ 리소스 삭제
-
main.tf 파일 수정
locals { name = "mytest" } resource "aws_iam_user" "myiamuser1" { name = "${local.name}1" }
-
실행
# 코드 변경으로 인해 user2 삭제 **terraform apply -auto-approve** terraform state list terraform state show aws_iam_user.myiamuser1 # **ls *.tfstate cat terraform.tfstate | jq** # iam 사용자 리스트 확인 aws iam list-users | jq
-
유형5 : 리소스만 있으며 코드, State는 없는 경우 → Import 또는 신규코드 작성
- 유형6 : 실수로 tfstate 파일 삭제 → plan/apply
-
실행
# 실수로 tfstate 파일 삭제 rm -rf terraform.tfstate* # 아래 두 명령 결과 차이는? # Local에 State 파일이 없으므로 두 명령 모두 새로운 리소스 추가(add) 계획 **terraform plan** **terraform plan -refresh=false** # apply할 경우 어떤 결과 발생? # 이미 리소스가 있으므로 Error 발생: EntityAlreadyExists: User with name mytest1 already exists. **terraform apply -auto-approve** terraform state list **cat terraform.tfstate | jq** # iam 사용자 리스트 확인 aws iam list-users | jq # 다음 실습을 위해 iam user 삭제 **aws iam delete-user --user-name mytest1**
⇒ 🤔 위 상황에서 복구 하는 방법은? import 등 방법이 있습니다!
-
모듈
정의와 필요성
- 테라폼 모듈은 재사용 가능한 테라폼 코드의 집합입니다.
- 시간이 지날수록 인프라 구성이 복잡해지며, 이를 효과적으로 관리하기 위해 모듈이 필요합니다.
문제점
- 단일 파일 구조에서 계속 업데이트할 경우 다음과 같은 문제가 발생합니다:
- 코드를 찾고 수정하기 어려워짐
- 리소스 간 복잡한 연관 관계로 인해 변경 분석이 어려워짐
- 비슷한 구성이 여러 환경에서 반복됨
- 새 프로젝트를 위해 기존 구성을 이해하기 어려움
종류
- 루트 모듈 (Root Module): 최상위 모듈로, 테라폼이 실행되어 인프라를 프로비저닝하는 모듈
- 자식 모듈 (Child Module): 루트 모듈에서 호출되는 추가적인 모듈 집합
모듈의 장점
- 관리성: 연관된 구성 요소를 묶어 단위별로 쉽게 찾고 업데이트 가능
- 캡슐화: 모듈은 논리적으로 묶여 독립적으로 관리되며, 필요한 부분만 외부에 노출
- 재사용성: 이미 검증된 모듈을 다른 프로젝트에서도 쉽게 사용 가능
- 일관성과 표준화: 모듈을 사용하면 구성의 일관성을 유지하고 복잡성 감
기본원칙
모듈은 테라폼 구성의 라이브러리나 패키지에 해당하는 개념입니다. 복잡한 테라폼 프로젝트를 더 효율적으로 관리할 수 있도록 도와주는 방법으로 사용됩니다.
원칙
- 디렉터리 명명 규칙:
- 모듈의 디렉터리는
terraform-<프로바이더 이름>-<모듈 이름>
형식을 따르는 것을 권장합니다. 이는 Terraform Cloud와 Terraform Enterprise에서도 사용되며, 디렉터리가 테라폼에 특화되어 있고 어떤 프로바이더의 리소스를 사용하고 있는지, 그리고 모듈의 이름이 무엇인지 쉽게 알 수 있게 해줍니다.
- 모듈의 디렉터리는
- 모듈화 구조:
- 처음부터 모듈화를 고려하여 테라폼 구성을 작성하는 것이 좋습니다. 이렇게 하면 나중에 다른 모듈에서도 이 모듈을 쉽게 호출할 수 있습니다. 또한 리소스들을 논리적으로 그룹화할 수 있습니다.
- 독립적 관리:
- 각 모듈은 독립적으로 관리되어야 하며, 이는 Version Control System(VCS)에서의 관리도 쉽게 해줍니다. 특히 리모트 모듈을 사용하지 않더라도 독립적으로 관리하는 것이 좋습니다.
모듈화 해보기
# terraform init 시 생성되는 modules.json 파일 확인
tree .terraform
.terraform
├── modules
│ └── modules.json
...
## 모듈로 묶여진 리소스는 module이라는 정의를 통해 단순하게 재활용하고 반복 사용할 수 있다.
## 모듈의 결과 참조 형식은 module.<모듈 이름>.<output 이름>으로 정의된다.
cat .terraform/modules/modules.json | jq
{
"Modules": [
{
"Key": "",
"Source": "",
"Dir": "."
},
{
"Key": "mypw1",
"Source": "../modules/terraform-random-pwgen",
"Dir": "../modules/terraform-random-pwgen"
},
{
"Key": "mypw2",
"Source": "../modules/terraform-random-pwgen",
"Dir": "../modules/terraform-random-pwgen"
}
]
}
[도전과제4] 테라폼 레지스트리에 공개된 모듈을 사용하여 리소스를 배포해본다.
모듈 main.tf
resource "aws_db_instance" "mont" {
allocated_storage = var.allocated_storage
storage_type = "gp2"
engine = "mysql"
engine_version = var.engine_version
instance_class = var.instance_class
name = var.name
username = var.username
password = var.password
parameter_group_name = var.parameter_group_name
skip_final_snapshot = var.skip_final_snapshot
vpc_security_group_ids = var.vpc_security_group_ids
subnet_ids = var.subnet_ids
}
모듈 variables.tf
variable "name" {
description = "The name of the DB instance"
type = string
}
variable "username" {
description = "Username for the master DB user"
type = string
}
variable "password" {
description = "Password for the master DB user"
type = string
}
output.tfvars
db_name = "prod-db"
db_admin_user = "admin"
db_admin_password = "securepassword"
해당구조와 같이 작성한 모듈과 tf파일들로 생성을 진행하였습니다.