Skip to main content

Terraform

Lumie 플랫폼의 인프라는 Terraform을 사용하여 Oracle Cloud Infrastructure(OCI)에 프로비저닝됩니다. 멀티 테넌시 환경을 지원하며, 두 개의 OCI 계정에 걸쳐 K3s 클러스터를 구성합니다.

아키텍처 개요

멀티 계정 구성

프로바이더 구성

# 0214 계정 (마스터 + 워커)
provider "oci" {
alias = "account_0214"
region = var.region
config_file_profile = "DEFAULT"
}

# 0213 계정 (워커 전용)
provider "oci" {
alias = "account_0213"
region = var.region
config_file_profile = "SECOND"
}

네트워크 구성

VCN 및 서브넷

0214 계정 (마스터 계정)

resource "oci_core_vcn" "vcn_0214" {
compartment_id = var.compartment_0214_ocid
cidr_blocks = ["10.0.0.0/16"]
display_name = "k3s-vcn"
dns_label = "k3svcn"
}

resource "oci_core_subnet" "subnet_0214" {
compartment_id = var.compartment_0214_ocid
vcn_id = oci_core_vcn.vcn_0214.id
cidr_block = "10.0.0.0/24"
display_name = "k3s-public-subnet"
prohibit_public_ip_on_vnic = false
}

0213 계정 (워커 계정)

resource "oci_core_vcn" "vcn_0213" {
compartment_id = var.compartment_0213_ocid
cidr_blocks = ["10.1.0.0/16"]
display_name = "k3s-vcn-0213"
dns_label = "k3svcn0213"
}

resource "oci_core_subnet" "subnet_0213" {
compartment_id = var.compartment_0213_ocid
vcn_id = oci_core_vcn.vcn_0213.id
cidr_block = "10.1.0.0/24"
display_name = "k3s-public-subnet"
prohibit_public_ip_on_vnic = false
}

VCN 피어링

두 계정 간의 VCN을 연결하기 위해 Local Peering Gateway를 사용합니다:

# 0214 계정의 LPG (요청자)
resource "oci_core_local_peering_gateway" "lpg_0214" {
provider = oci.account_0214
compartment_id = var.compartment_0214_ocid
vcn_id = oci_core_vcn.vcn_0214.id
display_name = "lpg-to-0213"
peer_id = oci_core_local_peering_gateway.lpg_0213.id
}

# 0213 계정의 LPG
resource "oci_core_local_peering_gateway" "lpg_0213" {
provider = oci.account_0213
compartment_id = var.compartment_0213_ocid
vcn_id = oci_core_vcn.vcn_0213.id
display_name = "lpg-to-0214"
}

보안 그룹

K3s 클러스터에 필요한 포트를 허용하는 보안 규칙을 설정합니다:

# SSH (22)
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
min = 22
max = 22
}
}

# K3s API Server (6443)
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
min = 6443
max = 6443
}
}

# Kubelet API (10250)
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
min = 10250
max = 10250
}
}

# Flannel VXLAN (8472 UDP)
ingress_security_rules {
protocol = "17"
source = "0.0.0.0/0"
udp_options {
min = 8472
max = 8472
}
}

컴퓨트 인스턴스

인스턴스 사양

모든 인스턴스는 ARM64 기반의 VM.Standard.A1.Flex 셰이프를 사용합니다:

resource "oci_core_instance" "nodes_0214" {
for_each = var.nodes_0214

compartment_id = var.compartment_0214_ocid
availability_domain = data.oci_identity_availability_domains.ads_0214.availability_domains[0].name
display_name = "k3s-${each.key}"
shape = "VM.Standard.A1.Flex"

shape_config {
ocpus = each.value.ocpus
memory_in_gbs = each.value.memory_in_gbs
}

source_details {
source_type = "image"
source_id = data.oci_core_images.ubuntu_arm64_0214.images[0].id
boot_volume_size_in_gbs = 50
}
}

노드 구성

변수를 통해 노드 구성을 정의합니다:

# terraform.tfvars (실제 현행 구성)
nodes_0214 = {
"master" = {
ocpus = 2
memory_in_gbs = 12
role = "master"
}
"worker-2" = {
ocpus = 2
memory_in_gbs = 12
role = "worker"
}
}

nodes_0213 = {
"worker-3" = {
ocpus = 2
memory_in_gbs = 12
role = "worker"
}
"worker-4" = {
ocpus = 2
memory_in_gbs = 12
role = "worker"
}
}

worker-1은 2026-04 비용 최적화로 제거되었습니다. 해당 50GB MinIO 디스크는 master 노드로 이전되었습니다(free tier 한도 정렬).

Network Load Balancer

애플리케이션 NLB (0214 계정)

nlb_0214.tf에 정의된 Layer 4 TCP NLB입니다. 고정 IP 168.107.42.253에 Cloudflare가 프록시됩니다.

resource "oci_core_public_ip" "nlb_public_ip_0214" {
lifetime = "RESERVED"
display_name = "k3s-nlb-public-ip"

# OCI 프로바이더 버그 #1708 워크어라운드 — private_ip_id 변경을 무시하지 않으면
# terraform apply 시 NLB IP 연결이 해제되어 TCP :443 타임아웃 발생
lifecycle {
ignore_changes = [private_ip_id]
}
}

백엔드: 0214 worker-2 + 0213 worker-3/4 (LPG 경유). 마스터는 제외됩니다.

Teleport 전용 NLB (0213 계정)

nlb_teleport_0213.tf에 정의된 전용 NLB입니다. 고정 IP 158.180.89.154에 Cloudflare DNS-only 레코드로 연결됩니다.

  • 포트: TCP :443 → NodePort 30443
  • 백엔드: worker-3, worker-4 (0213 VCN 로컬)
  • TLS 처리: L4 TCP passthrough, Teleport proxy pod이 TLS 종료
  • lifecycle { ignore_changes = [private_ip_id] } 동일하게 적용

스토리지

블록 볼륨

MinIO용 50GB 블록 볼륨 배분:

계정노드볼륨
OCI-0214master, worker-2각 50GB (worker-1 디스크 master로 이전됨)
OCI-0213worker-3, worker-4각 50GB

각 워커 노드에는 MinIO용 50GB 블록 볼륨이 연결됩니다:

resource "oci_core_volume" "volumes_0214" {
for_each = local.worker_nodes_0214

compartment_id = var.compartment_0214_ocid
availability_domain = data.oci_identity_availability_domains.ads_0214.availability_domains[0].name
display_name = "k3s-${each.key}-minio"
size_in_gbs = 50
vpus_per_gb = 10
}

resource "oci_core_volume_attachment" "attachments_0214" {
for_each = local.worker_nodes_0214

attachment_type = "paravirtualized"
instance_id = oci_core_instance.nodes_0214[each.key].id
volume_id = oci_core_volume.volumes_0214[each.key].id
is_read_only = false
is_shareable = false
}

변수 구성

필수 변수

# OCI 계정 정보
variable "tenancy_0214_ocid" {
description = "0214 Account Tenancy OCID"
type = string
}

variable "compartment_0214_ocid" {
description = "0214 Account Compartment OCID"
type = string
}

variable "tenancy_0213_ocid" {
description = "0213 Account Tenancy OCID"
type = string
}

variable "compartment_0213_ocid" {
description = "0213 Account Compartment OCID"
type = string
}

# SSH 키
variable "ssh_public_key" {
description = "SSH public key for instance access"
type = string
}

네트워크 변수

variable "vcn_0214_cidr" {
description = "0214 VCN CIDR block"
type = string
default = "10.0.0.0/16"
}

variable "vcn_0213_cidr" {
description = "0213 VCN CIDR block"
type = string
default = "10.1.0.0/16"
}

출력값

Ansible 통합

Terraform 출력값은 Ansible 동적 인벤토리에서 사용됩니다:

output "master_info" {
description = "K3s master node information"
value = {
name = "master"
public_ip = oci_core_instance.nodes_0214["master"].public_ip
private_ip = oci_core_instance.nodes_0214["master"].private_ip
k3s_url = "https://${oci_core_instance.nodes_0214["master"].private_ip}:6443"
}
}

output "workers_info" {
description = "K3s worker nodes information"
value = merge(
{
for name, instance in oci_core_instance.nodes_0214 :
name => {
account = "0214"
public_ip = instance.public_ip
private_ip = instance.private_ip
}
if name != "master"
},
{
for name, instance in oci_core_instance.nodes_0213 :
name => {
account = "0213"
public_ip = instance.public_ip
private_ip = instance.private_ip
}
}
)
}

배포 과정

1. 사전 준비

# OCI CLI 구성 확인
oci iam user list --profile DEFAULT
oci iam user list --profile SECOND

# SSH 키 생성 (필요한 경우)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519

2. Terraform 초기화

cd provision/terraform

# 초기화
terraform init

# 계획 확인
terraform plan

3. 인프라 배포

# 배포 실행
terraform apply

# 출력값 확인
terraform output

4. 상태 확인

# 인스턴스 상태 확인
terraform show

# 특정 리소스 확인
terraform state show oci_core_instance.nodes_0214[\"master\"]

문제 해결

VCN 피어링 문제

# 피어링 상태 확인
terraform output peering_status

# LPG 상태 확인
oci network local-peering-gateway get --local-peering-gateway-id <lpg-id>

인스턴스 연결 문제

# 인스턴스 상태 확인
oci compute instance list --compartment-id <compartment-id>

# 보안 그룹 규칙 확인
oci network security-list get --security-list-id <security-list-id>

블록 볼륨 문제

# 볼륨 상태 확인
oci bv volume list --compartment-id <compartment-id>

# 볼륨 연결 상태 확인
oci compute volume-attachment list --compartment-id <compartment-id>

모범 사례

1. 상태 관리

# 원격 상태 저장소 사용 (권장)
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "lumie/cluster/terraform.tfstate"
region = "ap-chuncheon-1"
}
}

2. 변수 관리

# terraform.tfvars 파일 사용
cp terraform.tfvars.example terraform.tfvars
# 필요한 값들 설정

3. 태그 관리

freeform_tags = {
"Environment" = "production"
"Project" = "lumie"
"ManagedBy" = "terraform"
}

4. 리소스 명명 규칙

  • VCN: k3s-vcn-<account>
  • 인스턴스: k3s-<role>-<number>
  • 볼륨: k3s-<node>-<purpose>

비용 최적화

1. 인스턴스 크기 조정

# 개발 환경용 작은 인스턴스
shape_config {
ocpus = 1
memory_in_gbs = 6
}

# 프로덕션 환경용 큰 인스턴스
shape_config {
ocpus = 4
memory_in_gbs = 24
}

2. 스토리지 최적화

# 성능 vs 비용 균형
vpus_per_gb = 10 # 기본값
# vpus_per_gb = 0 # 최저 비용 (낮은 성능)
# vpus_per_gb = 20 # 높은 성능 (높은 비용)

관련 문서