Gom3rye

팀 프로젝트) AWS MSK 구축하기 (feat. Terraform) 본문

현대 오토에버 클라우드 스쿨

팀 프로젝트) AWS MSK 구축하기 (feat. Terraform)

Gom3rye 2025. 11. 11. 10:07
728x90
반응형

공식문서: Amazon MSK 개발자 안내서에 오신 것을 환영합니다. - Amazon Managed Streaming for Apache Kafka

 

MSK(Managed Streaming for Apache Kafka): 고가용성을 갖춘 Apache Kafka 클러스터. AWS에서 완전 관리형으로 제공하므로 내부적인 브로커 설정, zookeeprt나 kraft 컨트롤러 등을 직접 설치하고 관리할 필요 없이 알아서 다 해줌.

 

구축되면 bootstrapserver를 엔드포인트를 노출시켜주므로, 기존의 kafka 클러스터로 구성된 아키텍쳐에서 해당 주소만 바꿔치기 하면 간편하게 마이그레이션이 가능하다!!!!

 

즉, 특정 VPC에 위치하여 같은 VPC 대역 안에 있는 친구들이 bootstrapserver로 접근 가능하게 함. HA를 위해 최소 2개 이상 AZ에 속한 서브넷을 필요로 하며, 서브넷당 1개 이상의 브로커가 배치됨. (브로커가 모든 AZ에 고르게 퍼져야 하는 듯. AZ가 2개면 브로커 갯수도 2의 배수로 설정해야 함 안그럼 에러 남ㅠ)

 

준비물: 소속될 VPC, 서브넷, 보안그룹, 필요시 KSM 키나 IAM역할(우리는 생략)

serverless 비용: 존재하는 것 만으로 드는 비용 + 데이터가 오가는 양마다 드는 비용(GB단위)

 

Terraform을 이용해 원클릭으로 배포 및 삭제가 가능하도록 할 예정 (15-30분 소요)

msk.tf

# Terraform AWS Provider 설정
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0" 
    }
  }
}

provider "aws" {
  region = "ap-northeast-2" 
}

# 1. 기존 리소스 정보 가져오기 (Data Sources)
# --- 1-1. 기존 VPC 정보 가져오기 ---
data "aws_vpc" "solog_vpc" {
  tags = {
    Name = "solog-unified-vpc"
  }
}

# --- 1-2. MSK 브로커를 배치할 기존 서브넷 정보 가져오기 ---
data "aws_subnets" "solog_public_subnets" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.solog_vpc.id]
  }
}

# --- 1-3. 기존 EKS 워커 노드의 보안 그룹 정보 가져오기 ---
data "aws_security_group" "eks_producer_sg" {
  tags = {
    Name = "eks-cluster-sg-eks-datacenter-cluster-1029477356"
  }
}

data "aws_security_group" "eks_consumer_sg" {
  tags = {
    Name = "eks-cluster-sg-eks-monitoring-cluster-1453820597"
  }
}

# 2. MSK 클러스터를 위한 새 보안 그룹 생성
#variable "kafka_ports" {
#  description = "EKS가 MSK에 접속하기 위해 열어줄 Kafka 포트 목록"
#  type        = list(number)
#  default     = [9092, 9098, 9094] 
#}

resource "aws_security_group" "msk_sg" {
  name        = "my-msk-cluster-sg"
  description = "Allow inbound traffic from EKS worker nodes to MSK"
  vpc_id      = data.aws_vpc.solog_vpc.id

  # --- 인바운드 규칙 (Ingress) ---
  ingress {
    from_port                = 0
    to_port                  = 0
    protocol                 = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # --- 아웃바운드 규칙 (Egress) ---
  # MSK가 외부(EKS 응답 등)와 통신하기 위해 모든 아웃바운드를 허용합니다.
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1" # 모든 프로토콜
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "my-msk-cluster-sg"
  }
}

# 3. AWS MSK 클러스터 생성 (KRaft + EBS)
resource "aws_msk_cluster" "msk_cluster" {
  cluster_name = "solog-msk-cluster2"

  kafka_version = "3.6.0"

  number_of_broker_nodes = 2
  broker_node_group_info {
    instance_type = "kafka.t3.small"

    client_subnets  = data.aws_subnets.solog_public_subnets.ids
    security_groups = [aws_security_group.msk_sg.id]

    storage_info {
      ebs_storage_info {
        volume_size = 100
        # volume_type = "GP3" # (선택) GP3 타입을 명시할 수 있습니다.
        # throughput = 125    # (선택) GP3 사용 시 처리량 (MiB/s)
      }
    }
  }

  # --- 보안 및 인증 설정 --- 안해~
  client_authentication {
    #sasl {
      #iam = true
    #}
    unauthenticated = true
  }

  # 전송 중 암호화 (네트워크 트래픽 암호화)
  encryption_info {
    encryption_in_transit {
      client_broker = "TLS_PLAINTEXT" # TLS_PLAINTEXT, TLS 중 선택
    }
  } 
#
#  # CloudWatch 로깅 설정
  logging_info {
    broker_logs {
      cloudwatch_logs {
        enabled   = true
        log_group = "/aws/msk"
      }
    }
  }

  tags = {
    Name = "my-msk-cluster"
  }
} 

# 4. (선택) 생성된 MSK 클러스터 정보 출력

output "msk_cluster_arn" {
  description = "생성된 MSK 클러스터의 ARN"
  value       = aws_msk_cluster.msk_cluster.arn
}

output "msk_bootstrap_servers_iam" {
  description = "IAM 인증용 부트스트랩 서버 주소"
  value       = aws_msk_cluster.msk_cluster.bootstrap_brokers_sasl_iam
}

output "msk_bootstrap_servers_tls" {
  description = "TLS 인증용 부트스트랩 서버 주소"
  value       = aws_msk_cluster.msk_cluster.bootstrap_brokers_tls
}

output "msk_bootstrap_servers_plaintext" {
  description = "Plaintext (비인증, 9092 포트) 부트스트랩 서버 주소"
  value       = aws_msk_cluster.msk_cluster.bootstrap_brokers
}
 

 

이제 output에 정의된 바와 같이 bootstrap server 주소가 stdout으로 출력 될 것임. 잘 킵해뒀다가 쓰면 끝~

msk_bootstrap_servers_plaintext = "b-1.sologmskcluster2.tak97t.c4.kafka.ap-northeast-2.amazonaws.com:9092,b-2.sologmskcluster2.tak97t.c4.kafka.ap-northeast-2.amazonaws.com:9092"

msk_bootstrap_servers_tls = "b-1.sologmskcluster2.tak97t.c4.kafka.ap-northeast-2.amazonaws.com:9094,b-2.sologmskcluster2.tak97t.c4.kafka.ap-northeast-2.amazonaws.com:9094"

msk_cluster_arn = "arn:aws:kafka:ap-northeast-2:484400672545:cluster/solog-msk-cluster2/93e22e78-17bc-4d81-9755-4bf53f63b035-4"

우리는 TLS 안 할거라고 했으니 9092 버전, 최상단의 버전을 쓰면 될 것임.

*주의!!! 브로커별 주소가 컴마 , 로 연결된 형태로 나타나는데, 이건 고가용성을 위한 표준이므로 전체를 통으로 사용해야 함. "b-1.sologmskcluster2.tak97t.c4.kafka.ap-northeast-2.amazonaws.com:9092,b-2.sologmskcluster2.tak97t.c4.kafka.ap-northeast-2.amazonaws.com:9092" 이거 다 써야 함!!

 

상세한 이유


MSK는 Kubernetes 위에서 동작하는 것이 아니라, AWS의 EC2/VPC 네트워크 위에서 직접 동작합니다. K8s의 Service 같은 자동 중계 기능이 없습니다.

대신 MSK는 Kafka가 원래 설계된 표준 "부트스트랩(Bootstrap)" 방식을 그대로 사용합니다.

  • Kafka 클라이언트의 기본 동작: Kafka 클라이언트(Fluentd, Java, Python 등)는 애초에 "대표 주소 1개"가 아니라 "브로커 주소 목록"을 받도록 설계되었습니다.

"아무거나 하나만 써도 되나요?"

 

아니요, 절대 안 됩니다. 반드시 b-1...:9092,b-2...:9092 전체 목록을 다 사용해야 합니다.

  • 이유: 고가용성 (High Availability, HA)

만약 b-1 주소 하나만 Fluentd 설정에 넣었다고 가정해 보겠습니다.

  • 장애 상황: AWS가 b-1 브로커가 있는 EC2 인스턴스를 점검하거나 해당 하드웨어에 장애가 발생하면, b-1 브로커는 잠시 다운됩니다.
  • 결과: Fluentd는 b-1 외에 다른 주소를 모르기 때문에, MSK에 접속 자체가 실패하고 데이터(로그)는 유실됩니다.

올바른 설정:

  • b-1..., b-2... 전체 목록을 Fluentd 설정에 넣습니다.
  • 장애 상황: b-1 브로커가 다운됩니다.
  • 결과: Fluentd(Kafka 클라이언트)가 "어? b-1이 죽었네. 그럼 목록에 있는 다음 주소(b-2)로 접속해봐야지"라며 자동으로 b-2에 접속하여 부트스트랩을 계속합니다.

+ 참고: 포트들 뭔 차이?

포트 인증 방식 (Authentication) 암호화 (Encryption) Terraform (client_authentication)
9092 비인증 (Unauthenticated) 없음 (Plaintext, 평문) unauthenticated = true
9094 비인증 (Unauthenticated) 필수 (TLS/SSL) unauthenticated = true
9096 SASL/SCRAM (ID/PW) 필수 (TLS/SSL) sasl.scram = true
9098 AWS IAM (Role/User) 필수 (TLS/SSL) sasl.iam = true
 

 

 

 

 

728x90
반응형