Mini Project 6 04
단일 EKS 클러스터, dev/prod 네임스페이스 분리: 의미와 한계
클러스터 하나를 만들고 namespace를 두 개 만든다는 것이 실제 운영 분리로 충분한지, 무엇이 나뉘고 무엇이 공유되는지 헷갈린다.
근거 · 교안 EKS 배포 환경 파트
1장: 클러스터와 노드 그룹: 모든 환경의 공유 기반
솔라의 손가락이 노트북 화면 위를 맴돌았다. 화면에는 ‘EKS 배포 환경 구성’이라는 제목의 기술 문서가 떠 있었다. 그 아래 첫 번째 항목은 명료했다.
1. EKS 클러스터 생성
2. 노드 그룹 생성
솔라는 이 두 줄에서 눈을 떼지 못했다. 개발(dev) 환경과 운영(prod) 환경을 분리하는 구성을 만들고 싶은데, 시작부터 무언가 어긋나는 느낌이었다. 두 개의 다른 세계를 만들고 싶은데, 안내서는 하나의 거대한 땅과 하나의 건물만 지으라고 말하는 것 같았다.
“언니, 잠깐 이것 좀 봐.”
거실 반대편에 있던 루나가 다가와 솔라의 노트북 화면을 들여다보았다.
“EKS 환경 구성하는 첫 단계 말이야. ‘클러스터 생성, 노드 그룹 생성’. 당연한 순서인데, 계속 마음에 걸려. 우리는 dev 환경이랑 prod 환경을 분리하고 싶잖아. 그런데 이렇게 하나의 클러스터랑 하나의 노드 그룹만 만들면, 결국 모든 게 한곳에 섞여버리는 거 아니야?”
솔라의 질문에는 날카로운 불안감이 섞여 있었다. 마치 두 가족이 살 집을 지으면서, 칸막이 없이 커다란 방 하나만 짓는 설계도를 받아 든 기분이었다.
“나중에 네임스페이스로 dev랑 prod를 나누면, 쿠버네티스가 알아서 물리적으로 분리해 주나? 그렇게 생각했는데, 정말 그럴까? 이 첫 단계가 너무… 단일한 게 이상해.”
솔라는 자신의 순진한 기대를 스스로 의심하고 있었다. 네임스페이스라는 이름표만 붙이면 보이지 않는 벽이 저절로 생길 거라는 믿음이 흔들리기 시작한 것이다.
루나는 설명하는 대신, 솔라에게 마우스를 넘겨받아 AWS 콘솔 창을 열었다.
“우리가 방금 만든 환경을 직접 보자. 클러스터 이름이 뭐였지?”
“my-eks-cluster.”
루나가 입력하자 화면에 ‘my-eks-cluster’라는 이름의 클러스터 하나가 나타났다. 상태는 ‘활성’이었다. 루나는 이어서 노드 그룹 화면으로 이동했다. 그곳에도 ‘default-nodegroup’이라는 단 하나의 노드 그룹만 존재했다. 여러 개의 EC2 인스턴스들이 그 그룹에 속해 있었다.
“자, 여기 우리 클러스터와 노드 그룹이 있어. 각각 하나씩이지. 이제 여기에 우리가 만든 개발용 애플리케이션을 배포한다고 상상해 봐. dev 네임스페이스에 말이야. 그 애플리케이션 파드(pod)들은 어디서 실행될까?”
“음… default-nodegroup에 있는 노드들 위에서 실행되겠지.”
솔라는 당연하다는 듯 대답했다.
“좋아. 그럼 이제 아주 중요한, 실제 사용자들이 접속하는 운영용 애플리케이션을 배포할 거야. 이번엔 prod 네임스페이스에. 그 파드들은 어디서 실행될까?”
”…….”
솔라의 입이 순간 멈칫했다. 대답은 너무나 명백했지만, 그 명백함이 바로 문제의 핵심이었다.
”…똑같이, default-nodegroup에 있는 노드들 위에서.”
나지막이 대답하는 솔라의 목소리에는 깨달음의 무게가 실려 있었다. 머릿속에서 막연하게 분리되어 있던 두 세계가 눈앞의 콘솔 화면 위에서 하나의 지점으로 합쳐지는 순간이었다. dev 파드와 prod 파드가 같은 물리적 서버 자원을 놓고 경쟁하게 될 모습이 선명하게 그려졌다.
솔라는 다시 아까의 기술 문서로 시선을 돌렸다.
Kubernetes(EKS) 배포 환경 구성은 EKS 클러스터 생성, Node 그룹 및 기본 Deployment 생성으로 시작한다.
이제 이 문장은 단순히 순서를 나열한 안내문으로 보이지 않았다. 그것은 이 아키텍처의 가장 근본적인 제약을 선언하는 문장이었다. 분리를 시작하기 전에, 모든 것은 하나의 기반을 공유한다는 사실. 환경 분리는 이 ‘공유’라는 대전제 위에서 시작해야 한다는 뜻이었다.
“아…” 솔라가 작은 탄식을 내뱉었다. “시작점이 환경 분리의 첫걸음이 아니었네. 오히려 무엇이 분리될 수 없는지를 알려주는 이정표였구나. 클러스터와 노드 그룹. 이게 바로 dev와 prod가 함께 발 딛고 서 있어야 할 공유 기반이었어.”
지금까지 솔라는 ‘분리’에만 집중한 나머지, 그 분리가 어떤 ‘공통 기반’ 위에서 이루어지는지를 간과했다. 집을 짓기 전에 땅부터 확인해야 한다는 사실을 뒤늦게 깨달은 셈이다.
그녀의 시선은 이제 새로운 질문을 향하고 있었다.
“좋아, 물리적인 땅과 건물이 하나라는 건 알겠어. 그럼 대체 네임스페이스라는 건 뭘 나누는 거지? 같은 컴퓨터 위에서 돌아가는데, 어떻게 ‘논리적 분리’라는 게 가능할까?“
2장: 네임스페이스의 역할: 리소스의 논리적 경계
솔라의 손끝이 키보드 위에서 잠시 멈췄다. 지난번 대화 이후, 그녀는 직접 dev와 prod 네임스페이스를 만들고, 각 환경에 필요한 애플리케이션 구성 요소들을 배포해보았다. 프론트엔드와 백엔드 배포(Deployment), 각 배포를 외부에 노출시키는 서비스(Service), 그리고 트래픽에 따라 자동으로 확장/축소하는 HPA(Horizontal Pod Autoscaler)까지. 설계 문서에 있던 모든 리소스를 my-eks-cluster 위에 올렸다.
하지만 그 결과는 솔라가 기대했던 깔끔한 분리된 세계가 아니었다. 그녀의 검은 터미널 화면에는 클러스터의 모든 리소스를 보여주는 kubectl get all --all-namespaces 명령어의 결과가 빼곡히 채워져 있었다.
NAMESPACE NAME READY STATUS RESTARTS AGE
dev pod/dev-backend-deployment-5f7d... 1/1 Running 0 15m
dev pod/dev-frontend-deployment-7b8c... 1/1 Running 0 15m
prod pod/prod-backend-deployment-6g8e... 1/1 Running 0 12m
prod pod/prod-frontend-deployment-8c9d... 1/1 Running 0 12m
...
dev service/dev-backend-service ClusterIP 10.100.x.x ...
dev service/dev-frontend-service ClusterIP 10.100.y.y ...
prod service/prod-backend-service ClusterIP 10.100.z.z ...
prod service/prod-frontend-service ClusterIP 10.100.w.w ...
...
dev와 prod 리소스들이 순서 없이 뒤섞여 목록으로 나타났다. 솔라는 미간을 찌푸렸다. 이게 최선인가? 공유된 기반 위에 세워졌다는 것은 알지만, 관리하는 모습까지 이렇게 뒤죽박죽일 줄은 몰랐다.
“언니, 역시 내 우려가 맞았어.”
솔라가 화면을 가리키며 루나에게 말했다.
“결국 이렇게 다 섞여서 보이잖아. dev 리소스랑 prod 리소스가 한눈에 다 들어오니까 뭐가 뭔지 헷갈리고, 실수로 prod 설정을 건드릴 수도 있겠어. 클러스터가 하나니 어쩔 수 없는 건가?”
솔라의 목소리에는 실망감이 묻어났다. 거대한 하나의 창고에 개발팀 물건과 운영팀 물건을 아무렇게나 쌓아둔 듯한 인상이었다.
루나는 솔라의 화면을 잠시 들여다보더니, 복잡한 목록 대신 솔라가 입력한 명령어에 주목했다. --all-namespaces. ‘모든 방을 한꺼번에 보여줘’ 라는 뜻이었다.
“솔라, 너는 지금 집 전체의 물건 목록을 뽑아달라고 요청한 거야. 그러니 당연히 모든 방의 물건이 섞여 보이지.”
루나는 솔라의 터미널에 새로운 명령어를 타이핑했다.
kubectl get all -n dev
엔터 키를 누르자, 화면이 순식간에 정리되었다. 방금 전까지 화면을 가득 채웠던 prod 관련 항목들이 모두 사라지고, 오직 dev 네임스페이스에 속한 리소스들만 나타났다.
NAME READY STATUS RESTARTS AGE
pod/dev-backend-deployment-5f7d... 1/1 Running 0 17m
pod/dev-frontend-deployment-7b8c... 1/1 Running 0 17m
NAME TYPE CLUSTER-IP ...
service/dev-backend-service ClusterIP 10.100.x.x ...
service/dev-frontend-service ClusterIP 10.100.y.y ...
NAME REFERENCE ...
horizontalpodautoscaler.autoscaling/dev-backend-hpa Deployment/dev-backend-deployment ...
horizontalpodautoscaler.autoscaling/dev-frontend-hpa Deployment/dev-frontend-deployment...
“아…!”
솔라의 입에서 짧은 탄성이 터져 나왔다. 그녀는 곧바로 명령어를 수정해 -n prod를 입력했다. 이번에는 prod 환경의 리소스들만 깨끗하게 정렬되어 나타났다.
dev라는 방에 들어가니 dev 물건만 보이고, prod라는 방에 들어가니 prod 물건만 보였다. 두 목록 어디에도 서로의 존재는 비치지 않았다. 심지어 dev의 backend-service와 prod의 backend-service는 이름이 같아도 충돌하지 않고 각자의 네임스페이스 안에서 독립적으로 존재했다.
“같은 집에 살아도 각자 자기 방이 있는 거랑 똑같네. 주소 자체가 다른 거였어. 나는 my-eks-cluster라는 집 주소만 보고 모든 게 한 공간에 있는 줄 알았는데, my-eks-cluster/dev와 my-eks-cluster/prod라는 별개의 호수가 있었던 거야.”
솔라는 깨달음을 얻은 표정으로 말을 이었다.
“이제 알겠다. 네임스페이스는 단순히 이름표가 아니구나. 리소스를 묶는 논리적인 경계 그 자체였어. 이 경계 안에서는 이름도, 서비스 연결도, 관리 범위도 모두 독립적이야. dev의 서비스는 절대 prod의 파드를 찾지 않을 거고, 그 반대도 마찬가지겠지.”
‘공유된 기반’ 위에서 ‘논리적 분리’가 어떻게 가능한지에 대한 의문이 풀리는 순간이었다. 쿠버네티스는 물리적으로는 같은 노드 위에서 파드들을 실행하지만, 네임스페이스라는 강력한 주소 체계를 통해 각 애플리케이션 그룹이 서로를 인식조차 못 하게 만드는 것이었다. 클러스터 공유와 리소스 독립성은 완벽히 양립 가능한 개념이었다.
“정확해. 그게 논리적 경계의 힘이지.” 루나가 고개를 끄덕였다. “그런데 솔라, 네가 배포할 때 사용했던 Deployment YAML 파일 기억나? 거기에도 이 경계를 더 단단하게 만드는 장치가 있었어.”
루나는 솔라가 작성했던 dev-backend-deployment.yaml 파일의 한 부분을 화면에 띄웠다.
...
spec:
containers:
- name: backend
image: my-backend:1.0
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
...
“이 resources 부분이 바로 네임스페이스의 논리적 경계를 물리적으로도 지켜주는 최소한의 안전장치야. requests는 이 파드가 노드에 배치될 때 ‘최소한 이만큼은 보장해줘’라고 요청하는 자원량이야. 일종의 ‘예약석’ 같은 거지. 그리고 limits는 이 파드가 ‘절대 넘어설 수 없는 자원 사용량의 상한선’이고.”
루나가 설명을 덧붙였다. “만약 dev의 어떤 애플리케이션이 버그 때문에 메모리를 계속 사용하더라도, 이 limits 값을 넘어서는 순간 쿠버네티스가 그 파드를 강제로 종료시켜. 다른 파드, 특히 prod의 파드를 보호하기 위해서야. 논리적인 방을 나눴을 뿐만 아니라, 각 방에서 쓸 수 있는 전기나 수도의 최대치를 정해놓은 것과 같아.”
하지만 솔라의 명쾌해진 얼굴 위로 새로운 그림자가 드리워졌다. 논리적 분리와 리소스 제한이라는 이중 안전장치를 이해하자, 역설적으로 그 모든 것이 서 있는 물리적 공유라는 근본 제약이 다시금 신경 쓰이기 시작했다.
“좋아. 논리적으로 분리되고, 리소스 상한선(limits)이라는 안전장치까지 있다는 건 알겠어. 각 방마다 일종의 차단기가 있는 셈이네. dev 방에서 합선이 나도 그 방만 전기가 끊기겠지. 하지만… 만약 건물 전체로 들어오는 주 전력선에 문제가 생기거나, 수도관 자체가 얼어붙으면 어떻게 돼? 모든 방이 동시에 영향을 받는 건 피할 수 없잖아.”
솔라의 비유는 정확했다. 그녀는 이제 네임스페이스가 제공하는 논리적 격리의 명확한 가치를 인정하면서도, 그것만으로 충분한지에 대한 본질적인 질문으로 나아가고 있었다. 이 ‘논리적 분리’가 과연 예측 불가능한 운영 환경의 혼돈 속에서 ‘안전한 분리’를 보장해 줄 수 있을까?
3장: 운영 분리의 의미: 이점과 한계
솔라의 손은 키보드에서 떨어져 있었다. 화면에는 dev와 prod 네임스페이스의 리소스들이 각자의 논리적 경계 안에서 깔끔하게 정돈된 모습이 보였지만, 그녀의 생각은 다른 곳에 가 있었다. 지난번 대화에서 마지막으로 떠올렸던 아파트 비유가 머릿속을 떠나지 않았다.
각 세대(네임스페이스)마다 개별 차단기(리소스 limits)가 있어 안전하다고 생각했지만, 건물 전체로 들어오는 주 전력선이나 수도관이 고장 나면 모든 세대가 꼼짝없이 영향을 받는다. 논리적 분리가 아무리 완벽해도, 모두가 하나의 공유된 기반 위에 서 있다는 물리적 현실은 변하지 않았다. 이 ‘안전한 듯 보이는 분리’가 과연 실제 운영의 혼돈 속에서 얼마나 튼튼한 방어막이 되어줄까?
그때, 생각에 잠긴 솔라를 보던 루나가 작은 메모지를 그녀의 노트북 옆에 내려놓았다. 메모지에는 간결한 표가 그려져 있었다. 한쪽 열에는 ‘이점’, 다른 쪽 열에는 ‘한계’라고 적혀 있었다.
“우리가 선택한 이 건축 방식의 장점과 단점을 정리해 보는 건 어때? 막연한 불안감의 실체를 파악하는 데 도움이 될 거야.”
루나의 말에 솔라는 고개를 끄덕였다. 먼저 ‘이점’ 칸을 채우는 것은 쉬웠다.
“일단 비용 효율적이지. dev와 prod를 위한 클러스터를 따로 만들고 관리하는 것보다 훨씬 저렴할 거야. 관리도 편하고. 감시해야 할 EKS 클러스터와 노드 그룹이 딱 하나씩이니까.”
솔라는 망설임 없이 적어 내려갔다. 그녀가 말한 그대로였다. 이 아키텍처를 선택하는 가장 큰 이유는 명백한 비용과 관리의 용이성이었다. 문제는 ‘한계’ 칸이었다. 솔라는 펜을 든 채 잠시 망설였다.
“자, 이제 상상력을 발휘해 볼 시간이야.” 루나가 ‘한계’ 칸을 가리키며 말했다. “만약 dev 네임스페이스의 어떤 애플리케이션에 심각한 버그가 있어서, 노드의 CPU를 미친 듯이 사용하기 시작했다고 해보자. limits 설정 때문에 결국에는 종료되겠지만, 그 몇 초, 혹은 몇 분 동안 그 노드는 어떻게 될까?”
솔라의 눈이 커졌다. “노드 전체가 버벅거리겠지. CPU 자원을 거의 다 뺏길 테니까… 아.”
그녀는 깨달았다. prod 네임스페이스의 애플리케이션 파드가 바로 그 노드 위에서 함께 실행되고 있다면, dev의 문제 때문에 직접적인 영향을 받게 된다. 고객의 요청을 처리하는 prod 서비스의 응답이 느려지거나 실패할 수도 있다. 차단기가 내려가기 전까지 집 전체의 전압이 불안정해지는 것과 같았다. 솔라는 ‘한계’ 칸에 첫 번째 항목을 적었다.
- 리소스 경합 위험: 한 네임스페이스의 문제가 같은 노드의 다른 네임스페이스에 영향을 줄 수 있음 (Noisy Neighbor 문제)
“하나 더.” 루나가 말을 이었다. “보안은 어때? dev의 어떤 파드에서 실수로 prod의 데이터베이스 서비스 주소로 접속을 시도하면, 막을 수 있을까?”
솔라는 잠시 생각에 잠겼다. 그녀는 dev와 prod 네임스페이스를 만들었지만, 둘 사이에 어떤 방화벽을 설정한 기억이 없었다. 쿠버네티스의 기본 네트워크 정책은 모든 네임스페이스 간의 통신을 허용했다.
”…아니. 막을 수 없어. 같은 클러스터 네트워크 안에 있으니까. dev의 파드가 prod의 서비스 이름을 알고 있다면 얼마든지 접속을 시도할 수 있겠구나.”
아찔한 상상이었다. 각자의 방은 있지만, 방문은 활짝 열려 있는 것과 다름없었다. 개발 환경의 실수가 운영 환경의 보안을 위협할 수 있다는 뜻이었다. 솔라는 두 번째 한계를 적었다.
- 보안 격리 제한: 기본 설정으로는 네임스페이스 간 네트워크 접근 제어가 없음.
마지막으로 솔라는 스스로 세 번째 항목을 떠올렸다. 만약 클러스터 자체에 문제가 생긴다면? EKS 컨트롤 플레인 업그레이드가 잘못되거나, 노드 그룹 전체에 영향을 미치는 버그가 발생한다면 dev와 prod 모두 동시에 마비될 것이다.
- 장애 파급 범위(Blast Radius): 클러스터나 노드 그룹 수준의 장애 발생 시 모든 환경에 영향을 줌.
메모지 위에는 단일 클러스터 아키텍처의 명암이 선명하게 드러났다. 비용과 관리의 편리함이라는 달콤한 이점 뒤에는, 리소스 경합, 보안, 장애 전파라는 뚜렷한 한계가 존재했다. 네임스페이스 분리가 결코 완벽한 운영 환경 분리가 아니라는 사실이 명백해졌다.
솔라는 이 모든 논의의 시작점이었던, 기술 문서의 첫 줄을 다시 떠올렸다.
Kubernetes(EKS) 배포 환경 구성은 EKS 클러스터 생성, Node 그룹 및 기본 Deployment 생성으로 시작한다.
이제 이 문장은 솔라에게 전혀 다른 의미로 다가왔다. 그것은 단순히 기술적 순서를 나열한 것이 아니었다. 그것은 하나의 ‘선택’이었고, 그 선택에 따라올 트레이드오프 전체를 암시하는 선언이었다. ‘공유된 기반’에서 시작한다는 것은, 이 표에 적힌 ‘이점’을 취하는 대신 ‘한계’를 감수하겠다는 아키텍처적 결단이었던 것이다.
솔라는 비로소 깨달았다. 중요한 것은 이 아키텍처가 ‘좋다’거나 ‘나쁘다’고 판단하는 것이 아니었다. 이 구조의 본질적인 특성과 트레이드오프를 정확히 이해하고, 주어진 상황에 적합한지 평가하는 것이 핵심이었다.
그녀는 깨끗한 새 메모지를 꺼내 들었다. 그리고 ‘단일 클러스터 아키텍처 평가 기준’이라는 제목 아래, 자신만의 질문들을 적기 시작했다. 이것은 미래에 비슷한 설계를 마주했을 때 그녀가 스스로에게 던질 질문들이었다.
- 비용 절감 효과는 공유 인프라의 위험을 감수할 만큼 충분한가?
- ‘한계’를 보완할 추가적인 장치가 있는가? (예: 네트워크 정책, 리소스 쿼터, RBAC)
- 이 서비스의 장애가 비즈니스에 미치는 영향은 어느 정도인가? (장애 파급 범위를 감당할 수 있는가?)
자신만의 ‘운영 적합성 평가’ 기준을 만들고 나자, 솔라의 마음속을 채웠던 막연한 불안감이 걷히고 명확한 판단의 기준이 자리 잡았다. 이제 그녀는 네임스페이스 분리가 제공하는 논리적 경계의 가치를 인정하면서도, 그것이 전부가 아님을 아는 엔지니어가 되었다. 그녀는 이제 어떤 아키텍처를 마주하든, 그 이면에 숨겨진 이점과 한계를 저울질할 수 있는 자신만의 저울을 손에 쥔 것이다.