AKS 모듈을 만들어 보자
현재 회사에서는 일본 리전의 VM 환경을 AKS로 전환하는 작업을 진행하고 있습니다.
추가로, 몇몇 곳에서 Azure 환경을 요구하는 경우가 더 생겼습니다.
기존 AKS 환경을 구성했던 Terraform 코드는 변수 분리 정도는 되어 있지만, 모듈화가 되어 있지는 않았습니다. 거기에 Azure 진영은 참고할 모듈도 많지 않고, 몇몇 곳에서는 특별한 조건도 있었기 때문에 모듈을 직접 작성하기로 했습니다.
이번에는 모듈을 직접 만들면서 고민했던 내용과 구현 내용을 간단히 정리해 보았습니다.
글에서 NAT Gateway 관련 내용이 등장하는데, 아직 실제 사용 사례가 없어 완전히 검증되지 않았습니다. 참고만 해 주세요.
Terraform 모듈의 필요성
들어가기 전에도 잠시 설명했지만, Terraform 모듈이 필요하다고 판단한 이유는 다음과 같았습니다.
사용하는 도구만 Terraform일 뿐, 프로그래밍에서 함수나 모듈, 템플릿을 사용하는 이유와 비슷합니다.
- 변수 분리를 통한 확장성
- 변수를 하드코딩하면 설정을 변경해야 할 때 등의 상황에서 유연하게 대응할 수 없습니다. 이는 Terraform도 마찬가지입니다.
- 특히 Terraform은 리소스명을 포함한 중요 정보에 변경이 생기면 Replace 이벤트가 발생하므로 중간에 인프라 공백이 생길 수 있고, 신중하게 설정을 관리해야 합니다.
- 가독성 면에서도 좋습니다.
- 재사용성
- 변수 분리는 모듈이 없어도 가능합니다. (초기 AKS가 그렇게 구성되었습니다)
- 하지만 모듈 형태로 만들어 두면 로컬의 경우 상대 경로, 외부 공개된 모듈은 공개된 이름으로 재사용이 가능합니다. 물론 모듈을 사용하지 않아도 파일을 복사하는 식으로 할 수 있지만, 따로 관리해야 하는 리소스가 아니라면 모듈을 사용하는 것이 좋습니다.
이러한 이유 로 모듈이 필요하다고 판단한 상태에서 최대한 간단하고, 기본적인 기능만 깔끔하게 구현해 유지보수가 쉽게 만드는 것이 목적이었습니다.
모듈 설명
실제로는 모듈을 2개 만들었습니다.
1개는 CPU, GPU 등에 관계없이 단일 클러스터 환경을 고려하고 생성되었고, 다른 하나는 CPU와 GPU 클러스터를 분리하는 요구사항을 고려하여 생성되었습니다.
기본적인 고려사항은 같기 때문에, 여기서는 단일 클러스터 모듈만 설명하려 합니다.
모듈은 총 4개의 파일로 이루어져 있습니다.
variable.tf
: 모듈에 필요한 변수를 정의한 파일입니다. 아래에서 자세히 설명합니다.provider.tf
: 필요한 Provider를 정의하는 곳입니다. 여기서는 azurerm 만 사용합니다.- 구독이 여러 개일 경우 알맞은 Azure 구독 ID를 지정해 주어야 합니다.
output.tf
: Terraform 명령어 실행 후 원하는 출력을 정의합니다.- 여기서 출력된 값은 실제 모듈을 사용하는 곳에서 참조할 수 있습니다.
main.tf
: 실제 Terraform 코드가 있습니다. 역시 아래에서 설명하겠습니다.
variable.tf
파일 설명
총 8개의 변수가 있습니다.
변수명 | 설명 | 비고 |
---|---|---|
rsc_group_name | 리소스 그룹 이름 | Azure 포탈에서 직접 생성하도록 하였습니다. |
aks_cluster_name | AKS 클러스터 이름 | |
subscription_id | Azure 구독 ID | |
ops_node_vm_size | 관리용 노드 풀의 VM 크기 | |
ops_node_count | 관리용 노드 풀의 노드 수 | |
node_pools | 노드 풀의 설정을 정의하는 객체 배열 | 하단 설명 참고 |
connect_nat | NAT Gateway 연결 여부 | 연결할 NAT Gateway는 존재한다고 가정합니다. (없으면 만들어야 합니다.) |
nat_gateway_name | 연결할 NAT Gateway의 이름입니다. | |
use_blob_storage | Blob Storage 사용 여부 | AKS에서 기본적으로 비활성화되어 있기 때문에, 명시적으로 활성화해야 합니다. |
-
리소스 그룹을 직접 생성하도록 한 이유
- 리소스 그룹 생성도 당연히 자동화가 가능합니다.
하지만 리소스 그룹을 생성할 때 이름과 리전 정도는 확인하는 게 좋겠다 판단하여 일부러 하지 않았습니다. - 이것까지 자동으로 생성하고 싶다면
data
블록을resource
블록으로만 변경하면 됩니다.
- 리소스 그룹 생성도 당연히 자동화가 가능합니다.
-
관리용 노드 풀을 설정한 이유
- 작성한 모듈에서는 기본적으로 관리용 노드 풀을 생성하고, Auto-scaling은 비활성화되어 있습니다.
- 추가 노드 풀은 별도의
azurerm_kubernetes_cluster_node_pool
리소스를 사용해 설정합니다. - 이를 설정한 이유는 다음과 같습니다.
azurerm_kubernetes_cluster
리소스에는default_node_pool
이라는 항목이 있고, 이 값은 필수입니다.- 시스템 노드 풀을 직접 변경하는 것은 상당히 위험합니다. 특히 운영 중인 클러스터라면 리스크가 더 커집니다.
- 노드 풀이 여러 개이거나 변동이 많을 때도 유연하게 대처할 수 있습니다.
- AKS 운영을 해 보면서 AGIC (Application Gateway Ingress Controller) 등 노드 변경이 있어야 할 때도 유지되어야 하는 리소스들이 있다는 것을 배웠습니다. 이러한 리소스들은 관리 노드 또는 가용성이 보장된 노드에 위치해야 합니다. 그래서 사용자 설정 노드 풀은 별도로 설정하고, 기본 노드 풀은 변하지 않는 상태로 시스템이 관리하는 것이 가장 바람직하다고 판단하였습니다.
- 노드 풀이 1개이고, VM 사이즈도 고정으로 사용하는 등 극한으로 경직된 환경이라면 관리 노드 풀이 필요하지 않겠지만, 그런 케이스를 고려하지는 않았습니다.
-
node_pools
변수에 대한 설명-
node_pools
변수는 객체 배열로 이루어져 있으며, 다음과 같은 형식입니다.node_pools = [
{
name = "cpupool" # 이름
vm_size = "Standard_D8as_v5" # VM 크기
node_count = 1 # 노드 수
auto_scaling_enabled = true # Auto-scaling 활성화 여부
min_count = 1 # Auto-scaling 시 노드 최소 개수
max_count = 5 # Auto-scaling 시 노드 최대 개수
node_labels = { # 노드에 설정할 Label
"haulrest.me/usage" = "cpu"
}
node_taints = [] # 노드에 설정할 Taint
enabled = true # 노드 풀 자체의 활성화 여부
},
{
name = "gpupool"
vm_size = "Standard_NC8as_T4_v3"
node_count = 1
auto_scaling_enabled = false
node_labels = {
"haulrest.me/usage" = "gpu"
}
node_taints = [
"haulrest.me/app=gpu:NoSchedule"
]
enabled = false
}
]노드 풀은 용도와 설정, 정책 등에 따라 변할 수 있는 요소가 매우 많습니다. 최대한 많은 부분을 모듈로 커버하고 싶었기 때문에, 객체 배열을 사용하 여 타입을 정의하였습니다.
-
-
NAT Gateway 옵션에 관하여
- NAT Gateway는 특정한 아웃바운드 통신과 그로 인한 응답만 오갈 수 있도록 합니다. 일부 환경에서 이에 대한 요구사항이 있을 수 있기 때문에, 옵션을 분류해 두었습니다.
- NAT Gateway 리소스는 이 모듈에서 생성하지 않고, 옵션을 사용하려면 이미 생성된 리소스가 있어야 합니다.
이렇게 한 이유는 다음과 같습니다.
- NAT Gateway는 비용도 있고 공개 IP 정보 등을 전달해서 방화벽을 개방하는 작업도 이루어지기 때문에 모듈을 새로 작성하더라도 따로 관리하는 것이 좋다고 생각했습니다.
- 쉽게 변경이 힘들기 때문에 기존 리소스를 사용해야 하는 경우도 있다 판단하였습니다. (예를 들어, 현재 SaaS 환경의 AKS를 다시 구성하게 되어도 NAT Gateway 쪽은 이미 통보되어 있는 IP 변경이 쉽지 않기 때문에 재사용을 고려해야 합니다.)
- 조건에 따라 import를 하는 것도 고려했지만 Terraform에서 편법적인 방법을 제외하고는 Conditional import를 지원하지 않기 때문에 배제했습니다.
main.tf
파일 설명
주요 내용만 간단히 설명하겠습니다.
- 다음 구성 요소들을 정의해 두었고, 모듈을 설치하면 생성됩니다.
- 클러스터
- 노드 풀 (설정한 대로)
- VNet, Subnet
- Ingress에 쓸 공인 IP
- Network Contributor 권한 (가상 네트워크에 대해 AKS가 필요로 하는 권한)
- NAT Gateway 데이터를 불러오는 것과 Association 생성은
connect_nat
옵션이 활성화되었을 때만 작동합니다. - AKS의 아웃바운드 설정도
connect_nat
옵션이 활성화되었을 때만userAssignedNATGateway
설정을 사용합니다. - 노드 풀은 위에서 설명한 대로 클러스터의 기본 노드 풀이 따로 있고, 사용자 노드 풀은
for_each
반복문을 통해 생성하도록 하였습니다.- 기본 노드 풀에
temporary_name_for_rotation
이라는 옵션이 있는데, 이는 업데이트 등으로 노드 풀 교체가 이루어질 때 임시로 사용할 이름입니다. (적절한 값을 넣으면 됩니다.)
- 기본 노드 풀에
그 외
- 모듈 기본 세팅을 쉽게 할 수 있도록 예시 코드와 문서를 팀 내에 추가로 공유했습니다.
- GPU나 일부 최신 CPU VM의 경우 할당량 문제가 있을 수 있습니다. 이는 Azure에서 기본적으로 적은 수, 특히 GPU의 경우 0으로 할당량이 설정되어 있기 때문입니다. 특히 GPU의 경우 자동 할당량 증가 요청이 불가능해서, 직접 지원 티켓을 통해 요청해야 했습니다.
- AKS에서 Ingress를 세팅하는 과정에서 2개 Annotation이 필요합니다.
service.beta.kubernetes.io/azure-load-balancer-resource-group
: 리소스 그룹 이름service.beta.kubernetes.io/azure-pip-name
: Ingress에 사용할 공인 IP 리소스 이름
- 스토리지 계정과 같이 실제 환경에서 모듈의 범위를 넘어서는 추가 요구사항이 필요한 경우 직접 작업을 했습니다. 이 글과 관련된 내용은 아니기 때 문에 넘어가도록 하겠습니다.
마치며
위에 적은 몇 가지 내용만 주의하면, 변숫값 몇 개만을 적절히 조정하는 것으로 모듈을 통해 AKS 환경을 구현할 수 있었습니다. 실제로 만들어진 2개의 모듈을 사용하여 요구사항에 맞는 AKS 테스트 환경을 구성하여 사용하고 있습니다.
AWS에도 Kubernetes 환경을 구성해야 하는데, 이 때는 terraform-aws-modules에 많은 모듈이 있어 활용하기 좋습니다.
Azure에도 몇몇 모듈이 있지만 AWS 진영에 비해서는 부족하다고 느꼈습니다. 하지만 이렇게 모듈을 직접 만들어 보는 것도 좋은 경험이 되는 것 같습니다.
위에서 설명한 Terraform 코드는 아래에서 확인할 수 있습니다.
참고 자료
- azurerm_kubernetes_cluster 등 Terraform 공식 문서