본문 바로가기

infra(terraform, aws, git)

(에러)terraform dynamodb validException key error

terraform을 실행시킬 때 backend라는 개념과 dynamodb라는 개념이 있다.

이러한 개념을 잠깐 설명하자면, 만약 여럿이서 resource를 관리하려고 할 때 서로 같은 state를 공유하기 위해 backend에 state file을 지정하여 init할 때 현재 state file의 상태를 보고 판단한다. 보통 backend는 s3를 이용하는데 아래와 같이 준다.

s3에 접근하기 위한 key들과 s3 bucket이름, 그리고 저장될 파일경로 등등이 쓰인다. 만약 backend로 state를 공유하지 않는다면,

A 사람: apply로 name이 "test"인 ecr 생성

B 사람: 똑같이 name이 "test"인 ecr을 생성.

위의 경우에 B사람은 이미 test가 있다는 에러가 뜬다. 물론 이러한 에러는 resource type을 썼을 때만 에러가 생기고, 만약 test라는 ecr이 있는 경우를 알았을 경우 data type을 이용하여 가져오면 되는데 언제가 test ecr이 있을 지도 모르기 때문에 리스크가 있다.

따라서 state file을 공유하면서 init할 때 backend로 state 파일경로를 지정해주면 현재 상태를 가져와서 resource로 ecr을 지정해줘도 문제가 없다는 것이다.

그렇다면 dynamodb_table은 무엇일까?

dynamodb_table은 특정 hash_key값을 가지게 되고, value는 bucketName/path으로 저장되어있다. bucketName/path는 unique key이기 때문이다.(s3 bucket 이름은 같은 지역내에서는 중복불가) 즉, state file을 수정하고 싶다면 lock key를 가져와 수정할 수 있는 권한을 가져야 한다. fcntl.flock이랑 똑같은 개념이다. 권한을 가져와서 수정했다면 이는 s3에 저장이 되므로 충돌날 일은 없는 것이다.

 

그렇다면 한번 실행해보자.

## main.tf
terraform {
  backend "s3" {
    encrypt        = true
  }
}
provider "aws" {
  access_key = var.access_key
  secret_key = var.secret_key
  region     = var.region
}

resource "aws_s3_bucket" "bucket" {
  bucket = var.bucket_name
  acl    = "private"
  cors_rule {
    allowed_headers = [
      "*",
    ]
    allowed_methods = [
      "GET",
      "POST",
      "PUT",
    ]
    allowed_origins = [
      "*",
    ]
    expose_headers  = []
    max_age_seconds = 60000
  }
}

resource "aws_ecr_repository" "test" {
  count = var.env == "test" ? 1 : 0
  name                 = "test"
  image_tag_mutability = "MUTABLE"

  image_scanning_configuration {
    scan_on_push = true
  }
}
resource "aws_dynamodb_table" "state-locking" {
  name = "state-locking-test"
  hash_key = "test"
  read_capacity = 20
  write_capacity = 20

  attribute {
    name = "test"
    type = "S"
  }
}

backend에는 민감한 정보가 들어있을 수 있으므로, 위와같이 main.tf에는 간단히 적어주고, backend.conf파일을 따로 만들어서 거기에 적어주고 cli로 넘겨주면 된다.

bucket         = "xxx"
key            = "xxx"
region         = "xxx"
dynamodb_table = "xxx"
encrypt        = false
profile        = "xxx"
access_key     = "xxx"
secret_key     = "xxx"
terraform init -backend-config=[backend config file path]

init이 성공적으로 완료된 것을 알 수 있다.

아! 만약 위와같이 debug 레벨의 log를 보고 싶다면, export TF_LOG=1로 설정하면 된다.

그리고 실행되는 과정중에

이런 에러들은 일단 신경쓸 필요 없다. dynamo table이 아직 안만들어졌기 때문에 apply할때 만들어 주기 때문에 상관없다.

 

그런 다음 아래 명령어 plan을 실행하여 확인해보자. 참고로 secret.tfvars를 만들어서 민감한 aws key나 다른 정보를 저장해서 혼자 가지고만 있어야 한다.

terraform plan -var-file=secret.tfvars

그러면 이러한 에러가 뜬다. 이러한 에러는 다른 게시물에 써놨으니 확인하면 된다.

일단 -lock=false를 넣어서 실해보라고 하니까 실행해보자.

terraform plan -var-file=secret.tfvars -lock=false

흠 뭔가 +-가 생기고 잘되는 거 같으니 apply해보자

terraform apply -var-file=secret.tfvars -lock=false

위의 결과를 보면 뭔가 된거같은데 빨간색 에러가 뜬다.

key가 맞지 않다고 에러가 뜬다.

일단 aws로 가서 dynamodb table을 가보자.

잘 생성된 것 같은데..... 항목을 보니까 아무것도 보이질 않는다. tf state파일이 저장되어야하는데...

이런 에러는 처음에 dynamodb hash_key를 생성할 때 다음과 같이 되어있어서 key 값을 우리가 알아서 지정해서 공유하는 걸로 알고 있었다.

하지만 사실 hash_key값은 LockID로 고정이고 이 값을 찾아 lock 권한을 부여하는 것이다.

따라서 위의 main.tf에서 key값이 LockID가 아니라 test이므로 LockID로 바꿔서 다시 terraform desctroy -var-file=secret.tfvars -lock=false를 하면 잘 될 것이다.