안녕하세요~! 오늘은 Terraform, 그 중에서도 Azure provider(azurerm)과 관련된 이야기입니다.
배경
배경부터 거슬러 올라가자면....애초에 이런 상황이 발생하지 말았어야 하는데요...^^;;;;;
클러스터가 생성은 terraform으로 되었지만 state가 로컬에서만 관리되다가(문제의 시발점)
어느 순간부터 변경점을 terraform이 아니라 azure cli 및 포털 액세스를 통해 적용해 온 클러스터들이 있었습니다.
거기에 추가로 tfstate 파일이 소유자의 노트북 교체로 분실되면서 상황이 더욱 더 막장으로 치닫게 되었는데요...
클러스터 두 개 그냥 뭐... 관리를... cli로............
....
..
...
........ㅠㅠ 못 참겠어서 백엔드 연동하고 마이그레이션에 착수합니다.
azure에서 terraform backend를 사용하시는 경우에는 s3 + dynamodb 이런 조합처럼 구현하실 필요는 없고
그냥 storage account에 blob container 하나 파서 집어넣으면 lock도 자동으로 적용되어 간편하게 구현이 가능합니다.
만들어 둔 container의 access key를 환경변수로 반영하든 ad로 인증하든(소스에 집어넣지 마세요!) 백엔드를 연동합니다.
# root module에 이런식으로 설정해서 연동을 하시면 되겠습니다.
terraform {
required_providers {
azurerm = {
(이부분은 생략)
}
}
backend "azurerm" {
resource_group_name = "storage account가 있는 resource group명"
storage_account_name = "storage account의 이름"
container_name = "blob container 이름"
key = "스토리지에 저장될 파일명(access key 아님)"
}
}
...
# access key 방식으로 인증하고, 환경변수로 저장해서 사용하는 경우 아래와 같이 사용 가능합니다.
# az cli 가 설치되어 있어야 하고, 로그인 된 상태여야 합니다^^ 변수부분은 미리 주입하시거나 하드코딩해주세요~
ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)
export ARM_ACCESS_KEY=$ACCOUNT_KEY
연동을 마치고 한땀한땀 resource import를 시작합니다.
Terraform import에 대한 내용은 아래의 링크에서 확인이 가능합니다.
https://www.terraform.io/cli/import/usage
클러스터와, 거기에 추가로 집어넣을 노드풀, 노드풀이 container registry로 접근하는 role assignment 등을 import하기 시작합니다. 한 땀 한 땀....
여기서 얻을 수 있는 교훈은, 테라폼을 아예 사용하지 말거나 아니면 상태 관리를 반드시 하거나
둘 중 하나를 선택하는 것이 좋겠다는 것입니다. 리소스 하나씩 import를 해 주어야 해서
여간 번거로운 것이 아닙니다.
아무튼 하나씩 import 하고 plan으로 싱크를 맞춰 가는 과정에서, 이상한 일이 발생합니다.
현상
nodepool이 container registry로 접근하여 이미지를 pull 할 수 있도록 하는 role assignment를 import했는데,
assignment id를 확인하여 반영했음에도 존재하지 않는 자원이라고 import가 실패하였습니다.
에러 메세지:
module.모듈명.azurerm_role_assignment.역할명: Importing from ID
"/subscriptions/구독명/resourceGroups/리소스그룹/providers/Microsoft.Authorization/
roleAssignments/역할ID(role assignment id)"...
module.모듈명.azurerm_role_assignment.역할명: Import prepared!
Prepared azurerm_role_assignment for import
module.모듈명.azurerm_role_assignment.역할명: Refreshing state...
[id=/subscriptions/구독명/resourceGroups/리소스그룹/providers/Microsoft.Authorization/
roleAssignments/역할ID(role assignment id)]
╷
│ Error: Cannot import non-existent remote object
│
│ While attempting to import an existing object to "module.모듈명.azurerm_role_assignment.역할명", the provider detected that
│ no object exists with the given id. Only pre-existing objects can be imported; check that the id is correct and that it is associated
│ with the provider's configured region or endpoint, or use "terraform apply" to create a new remote object for this resource.
없다니...! ID값 확인하고 붙여넣었는데 없다니...!!
이런저런 방법을 시도해봐도, 다른 리소스에 시도해도 다 없는 자원이라고 나오고!
그런데 클러스터 자체는 import가 잘만 되고!
리소스에 딸린 중첩된(??) 형태의 리소스는 import가 안 되고!
내 속도 터지고! 하는 상황이었습니다.
원인
원인 자체는 간단했지만 약간의 상상력을 필요로 했습니다.
import 할 때,
terraform import module.모듈명.리소스타입.리소스이름 리소스ID(/subscription~으로 시작하는) 형태로 진행하게 되는데요, role assignment 처럼 다른 리소스의 하위타입인 경우 리소스ID 선언부를
scope에 해당하는 타겟 리소스까지의 리소스ID를 적고, 그 밑에 다시 providers부터 roleassignment~ 등의
종속되는 하위타입의 리소스 ID를 적어 주어야 했던 것입니다...
이렇게 적어놓고 보면 너무 당연한 것 아냐? 라고 생각하실 수 있는데
providers가 여러 번 들어갈거란 생각을 못했다구요ㅠㅠㅠㅠㅠㅠㅠ너무 부자연스럽게 생긴 형태였습니다...
해결
결론적으로 성공한 버전은 이겁니다.
terraform import module.모듈명.azurerm_role_assignment.역할명 \
/subscriptions/구독ID/resourceGroups/리소스그룹/providers/Microsoft.Network/virtualNetworks\
/vnet명/providers/Microsoft.Authorization/roleAssignments\
/역할ID(role assignment id)
-- 실행 결과
module.모듈명.azurerm_role_assignment.역할명: Importing from ID "/subscriptions/구독ID
/resourceGroups/리소스그룹/providers/Microsoft.Network/virtualNetworks/vnet명/
providers/Microsoft.Authorization/roleAssignments/역할ID(role assignment id)"...
module.모듈명.azurerm_role_assignment.역할명:
Import prepared!
Prepared azurerm_role_assignment for import module.모듈명.azurerm_role_assignment.역할명:
Refreshing state... [id=/subscriptions/구독ID/resourceGroups/리소스그룹/
providers/Microsoft.Network/virtualNetworks/vnet명/
providers/Microsoft.Authorization/roleAssignments/역할ID(role assignment id)]
Import successful!
The resources that were imported are shown above.
These resources are now in your Terraform state and will henceforth be managed by Terraform.
import문이 뭔가 어색하지 않나요??? scope뒤에 providers가 또 붙는 형태로 올 거라는 생각을 못 했던 과거의 나...
아무튼 이런 식으로 not exist를 뱉던 리소스들까지도 잘 import 해주고, backend 연동해서
이제 state가 관리되는 클러스터로 편입시킬 수 있었습니다.
특히 azure 사용하시는 분들은 국내 레퍼런스가 엄청 많은 게 아니라,
언젠가는 저 같은 문제를 겪으시는 분이 계실 것 같아 공유합니다!
오늘의 교훈
Terraform import는 고통스러운 노가다 작업입니다.
그러니 terraform을 사용할 거라면 상태관리를 하고, 무슨 일이 있어도!!! 로컬에서 관리하지는 맙시다.
이상입니다. 즐건 클라우드 생활 하세요~