4주차 Provider & State
본 스터디 내용 요약은 @CloudNet의 가시다님의 강의를 요약한 내용입니다.
1. Provider
프로바이더는 다른 프로비져닝 API(AWS, Google Cloud, VSphere)을 동일한 선언적 형식으로 호출 할 수 있는 translator라고 생각하면 된다.
각 프로바이더는 terraform의 형식에 맞춰 API 구현을 맞춰서 각 환경에 맞는 구성을 해준다.
우리는 연동하는 대상의 프로비져닝 API에 사용될 인증만 처리해주면 쉽게 사용할 수 있다.
1.1 프로바이더 구성
프로바이더에는 다음과 같이 유지보수 및 권한을 가진 티어에 따라 식별이 가능합니다.
오른쪽 위에 다음과 같은 창을 사용해서 프로바이더를 설정 해 줄 수 있습니다.
로컬 이름과 프로바이더 지정
required_providers 블록을 사용해서 여러개의 프로바이더를 설정할 수 있습니다. 이름들이 동일 접두사로 시작하거나 동일할 경우 다음과 같이 provider를 명시해서 사용할 수 있습니다.
terraform {
required_providers {
http = {
source = "hashicorp/http"
}
aws-http = {
source = "terraform-aws-modules/http"
}
}
}
data "http" "example" { // http 프로바이더 사용
provider = aws-http // 명시적 표기사용
url = "https://checkpoint-api.hashicorp.com/v1/check/terraform"
}
단일 프로바이더와 다중 정의
동일 프로바이더를 사용하지만 다른 조건을 사용하는경우
다른 IAM 권한
다른 리전, ACCESS ID 일 경우
Alias 명시하고 provider 메타인수를 이용해 provider를 지정할 수 있습니다.
provider "aws" {
alias = "seoul" // alias 지정
region = "ap-northeast-2"
}
resource "aws_instance" "app_server2" {
provider = aws.seoul // provider 지정
ami = "ami-0ea4d4b8dc1e46212"
instance_type = "t2.micro"
}
프로바이더 요구사항 정의
프로바이더에 대한 source에 다운로드 경로 정의하고 version에 프로바이더 버전을 명시 할 수 있습니다.
terraform {
required_providers {
<프로바이더 로컬 이름> = {
source = [<호스트 주소>/]<네임스페이스>/<유형> (다운로드 주소)
version = 1.1.0 // 버전 (값 생략시 최신버전)
}
}
}
프로바이더간 전환 여부
다른 클라우드간 프로바이더만 바꿔서 바로 전환이 가능할까?
답은 안된다. -> 다른 API 로 구현된 프로바이더이다.
1.2 프로바이더 에코시스템
테라폼 에코시스템은 사용자가 원하는 구조에 따라 설계할 수 있게끔 사용됩니다.
퍼블릭 클라우드
컨테이너 관리 Iaas
보안 & 인증
CI/CD
네트워크
VCS
1.3 프로바이더 실습
[실습] 멀티 리젼에 EC2를 배포해보자
// ec2.tf
```
resource "aws_instance" "region_1" {
provider = aws.region_1 // 첫번째 region 에 ec2를 생성하게 끔 region을 설정합니다.
ami = data.aws_ami.ubuntu_region_1.id
instance_type = "t2.micro"
}
resource "aws_instance" "region_2" {
provider = aws.region_2 // 두번째 region 에 ec2를 생성하게 끔 region을 설정합니다.
ami = data.aws_ami.ubuntu_region_2.id
instance_type = "t2.micro"
}
```
// provider_data.tf
```
provider "aws" {
region = "ap-northeast-2"
alias = "region_1"
}
provider "aws" {
region = "ap-southeast-1"
alias = "region_2"
}
data "aws_region" "region_1" {
provider = aws.region_1
}
data "aws_region" "region_2" {
provider = aws.region_2
}
data "aws_ami" "ubuntu_region_1" {
provider = aws.region_1 // 다음과 같이 region 1에서 사용되는 provider를 사용합니다.
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}
data "aws_ami" "ubuntu_region_2" {
provider = aws.region_2 // 다음과 같이 region 2에서 사용되는 provider를 사용합니다.
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}
```
// 다음 코드를 실행하면 ec2가 각각의 region 에 생성되있는것을 확인 할 수 있다.
aws ec2 describe-instances --region ap-northeast-2 --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output text

2가지 주의사항
첫번째 주의사항: 프로덕션 수준의 멀티 리전은 어렵다
지역간 지연시간, 고유ID, 최종 일관성 등 고려사항이 많아서 어렵다.
두번째 주의사항: Alias를 빈번하게 사용하지 말자
별칭 사용하여 두 리전에 배포하는 모듈은 한 리전이 다운 시에 Apply가 실패한다.
workspace나 디렉토리를 이용해서 완전 분리하는것이 별칭으로 분리하는것보다 나은 선택이다.
2. State

테라폼 상태관리의 중요성에 대한 카톡
상태관리를 어떻게 해야할지 정확히 알아야합니다.
s3 버저닝과 import로 복구하는 면이 인상적입니다.
2.1 State의 목적과 의미
[실습] 상태 파일 확인
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "t101-study" // 해당 값을 변경할 예정
}
}
// tf 파일을 프로비젼이후에 serial 값이 1인것을 확인할 수 있습니다.

provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "myvpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "tf-state" // 해당값을 바꾸고 다시 apply 하게되면 serial 값은 증가 할 것입니다.
}
}
// serial 값이 3으로 변경되었습니다.

상태 파일은 terraform에서 내부 API로 변경되는 상태를 저장하기 위한 파일로 직접 편집하거나 코드로 작성해서는 안됩니다.
상태값에 따라 여러번 실행해도 결과가 동일하게끔 멱등성을 보장하게끔 실행됩니다.
팀 단위에서 테라폼 운영시 문제사항
파일을 저장하는 공유 스토리지 필요
상태 파일 잠금
동시에 작업될 시에 충돌 가능
상태 파일 격리
환경별 상태파일 격리 필요
지원되는 원격 백엔드
AWS, Azure Blob, Google Cloud Storage, Postgresql, k8s secret 등 원격으로 apply 이후에 저장이 가능하다.
수동 오류 해결: plan/ apply 실행 시마다 파일을 로드해서 apply후에 자동으로 저장한다. (버전관리시스템은 수동으로 update를 해줘야한다.)
잠금: 버전 관리시스템에서 여러명의 구성원들이 동시에 하나의 상태파일에 대해서 apply 못하게 잠금기능을 제공합니다. -lock-timeout=30s (상태파일 동시 업데이트로 충돌이 가능하다.)
시크릿: 테라폼 상태의 모든 데이터는 평문으로 tf.state에 저장될때, 암호화하는 기능을 지원 (VCS 는 모든 데이터 평문 저장함)
[실습] S3 + DynamoDB 동작
[사전준비] aws s3 생성
```
resource "aws_s3_bucket" "main" {
bucket = var.bucket_name
tags = {
Name = "terraform test"
}
}
```
// s3 버킷을 생성합니다.

```
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.4.0"
}
}
backend "s3" {
bucket = "hello-tf102-remote-backend"
key = "terraform/state-test/terraform.tfstate"
region = "ap-northeast-2"
#dynamodb_table = "terraform-lock" 주석처리
}
required_version = ">= 1.4"
}
provider "aws" {}
```
// s3를 백엔드로 설정합니다.

[사전 준비] Locking을 위한 DynamoDB 생성
```
resource "aws_dynamodb_table" "terraform_state_lock" {
name = "terraform-lock" # table이름
hash_key = "LockID" # key 이름
billing_mode = "PAY_PER_REQUEST"
attribute {
name = "LockID"
type = "S" # key 타입
}
}
```
aws dynamodb list-tables --output text
// dynamodb 생성확인

[확인] S3 버저닝 확인
```
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "terraform VPC 2" //2로 변경이후에 lock되는지 확인
}
}
```
terraform apply -> 인풋값을 입력하지 않으면 lock이 생깁니다.
// "Path":"hello-tf102-remote-backend/terraform/state-test/terraform.tfstate"}

다음 화면에서 LockId를 확인할 수 있습니다.
s3 에서는 다음과 같이 버전이 명시되는것을 확인 할 수 있습니다.

2.2 State 동기화
테라폼 구성 파일을 기존 존재하는 State 파일과 비교해서 실행계획을 세워서 생성, 수정, 삭제를 하게 됩니다.
테라폼 state 흐름
각 리소스에서 발생할 수 있는 상태변화
[실습] 유형별 상태변화 및 복구 방법
유형
구성 리소스 정의(*.tf)
State 구성 데이터
실제 리소스
기본 예상 동작
1
있음
리소스 생성
2
있음
있음
리소스 생성
3
있음
있음
있음
동작 없음
4
있음
있음
리소스 삭제
5
있음
동작 없음
유형1 : 신규 리소스 정의 → Apply ⇒ 리소스 생성
// main.tf
```
locals {
name = "mytest"
}
resource "aws_iam_user" "myiamuser1" {
name = "${local.name}1"
}
resource "aws_iam_user" "myiamuser2" {
name = "${local.name}2"
}
```
// 두번 실행시에도 멱등성을 유지하는것을 확인 할 수 있습니다. (추가 수정, 실행 없음)

유형2 : 실제 리소스 수동 제거 → Apply ⇒ 리소스 생성
// 실제 리소스 수동 제거
aws iam delete-user --user-name mytest1
aws iam delete-user --user-name mytest2
terraform plan
// 삭제이후에는 다시 생성해야 되는것을 명시해 줍니다.

terraform plan
terraform plan -refresh=false // 해당 refresh는 aws 와 같은
// 인프러스트럭쳐에 실제로 리소스가 프로비져닝 되있는지 확인합니다.
cat terraform.tfstate | jq .serial
유형3 : Apply → Apply ← 코드, State, 형상 모두 일치한 경우
// terraform apply -auto-approve
// terraform apply -auto-approve
두번 apply를 하게되면 형상이 모두 일치할 때, 다음과 같은 결과가 나오게 됩니다.

유형4 : 코드에서 일부 리소스 삭제 → Apply
locals {
name = "mytest"
}
resource "aws_iam_user" "myiamuser1" {
name = "${local.name}1"
}
// myiamuser2을 명시하지 않았을때 다음과 같은 값이 나옵니다.

유형6 : 실수로 tfstate 파일 삭제 → plan/apply
rm -rf terraform.tfstate* /tfstate 파일들 모두 삭제
terraform plan -refresh=false // tfstate만 보고 판별할 경우
terraform plan // 두개다 새로운 유저를 만드려고합니다.

// 위 상황에서 복구하는 방법은? import가 있습니다.
terraform import aws_iam_user.myiamuser1 mytest1

2.3 워크 스페이스
테라폼 상태를 격리하지 않을때, 생기는 문제점
환경을 격리하는 방법들
작업 공간을 통한 격리
파일 레이아웃을 통한 격리
워크스페이스
서로 다른 state를 갖는 실제 대상을 각 워크스페이스 별 프로비저닝을 할 수 있다.
장점
하나의 루트 모듈로 동일 구성 프로비저닝 관리 가능하다.
깃의 브랜치 전략처럼 동일한 구성에서 리소스 결과 관리가능하다
단점
State가 동일한 저장소에 저장되어 접근 권한 관리 불가능
모든 환경에 동일한 리소스가 요구되지 않아 분기처리 발생가능
완벽한 격리가 불가능
해결하기 위해 별도로 구성하는 디렉터리 기반의 레이아웃을 사용해야한다.
[실습] 워크 스페이스를 생성해서 프로비저닝 하기
// main.tf
```
resource "aws_instance" "mysrv1" {
ami = "ami-0ea4d4b8dc1e46212"
instance_type = "t2.micro"
tags = {
Name = "t101-study"
}
}
```
// terraform workspace list 으로 terraform의 workspace 확인
- default 인 것을 확인 할 수 있다.

// terraform workspace list 으로 terraform의 workspace 생성
- mywork2 가 생성된 것을 확인
- terraform apply 로 해당 워크스페이스에서 리소스들이 생성되는 것을 확인 할 수 있다.


Last updated