쿠버네티스로 6개월을 날렸던 이야기 - 초보자가 놓치는 7가지

|Operation Risk|22분 읽기

모든 게 완벽해 보였던 그 순간

지금도 선명하게 기억나는 장면이 있습니다. 첫 쿠버네티스 클러스터 배포를 마치고 대시보드를 열었을 때였어요. 노드 세 개에 파드 몇 개가 떠 있고, 모든 상태가 초록색으로 표시되고 있었죠. 그 초록색을 보면서 '이제 됐다'고 생각했습니다.

그런데 3주 후, 부하 테스트를 돌리는 순간 모든 게 무너졌어요. 깔끔하게 실패한 게 아니라 정말 엉망진창으로 무너졌습니다. 파드가 줄줄이 죽고, 노드 하나가 흔들리니까 그 위에 있던 것들이 연쇄적으로 따라 내려갔어요. 그때부터 시작된 6개월간의 시행착오는 지금 돌이켜봐도 아찔합니다.

문제의 뿌리는 단순했어요. 저는 쿠버네티스를 '도커를 좀 더 고급스럽게 실행하는 방법' 정도로 이해하고 있었습니다. 컨테이너를 넣으면 앱이 돌아간다, 딱 거기까지였죠. 초보자의 시선으로 쿠버네티스를 다뤘지, 운영 환경을 책임지는 엔지니어의 시선은 아니었던 겁니다. 이 둘의 차이가 6개월짜리 수업료를 만들었어요.

가장 아픈 교훈들 - 7가지 실수

돌아보니 제가 저지른 실수들은 대부분 같은 결을 가지고 있었어요. 개별 설정의 문제가 아니라, 쿠버네티스가 애초에 무엇을 위한 시스템인지에 대한 오해에서 출발한 것들이었습니다.

실수 1: 도커 컴포즈의 확장판이라고 생각했던 것

많은 개발자가 Docker Compose에서 쿠버네티스로 넘어오잖아요. 저도 그랬고, 머릿속에는 "컨테이너를 넣으면 앱이 나온다"는 단순한 공식이 박혀 있었습니다. 소규모 개발 환경에서는 이 접근이 통하기도 해요. 그래서 더 위험합니다. 통하니까 잘못된 줄 모르거든요.

쿠버네티스는 Docker Compose와 철학이 다른 물건이에요. 장애 관리, 스케줄링, 네트워킹, 상태 관리에 대한 나름의 기준과 원칙을 가진 오케스트레이션 시스템입니다. 이 기준을 이해하지 못한 채 쓰면 도구와 계속 싸우게 됩니다. 명령어 몇 개 더 외운다고 풀리는 문제가 아니라, 관리 단위 자체를 바꿔야 했어요. 개별 앱이 아니라 클러스터 전체가 관리 대상이 되는 거죠. 이 전환이 머릿속에서 일어나기 전까지는 뭘 해도 임시방편이었습니다.

실수 2: 리소스 제한을 설정하지 않은 것

CPU와 메모리 제한을 설정하지 않아도 처음에는 멀쩡하게 돌아가요. 그래서 '굳이?' 싶었습니다. 하지만 파드 하나에서 메모리 누수가 생기는 순간, 같은 노드에 있던 다른 파드들이 리소스 부족으로 하나둘 죽기 시작합니다. 범인은 하나인데 피해자는 전부인 상황이죠.

더 고약했던 건 디버깅이었어요. 로그를 아무리 뒤져도 진짜 원인이 안 보였습니다. 애플리케이션 로직에는 문제가 없는데 파드가 계속 재시작되니까, 완전히 엉뚱한 곳을 파고 있었어요. 한참 뒤에야 깨달았습니다. 리소스 제한은 선택사항이 아니라, 한 파드의 문제가 노드 전체로 번지는 걸 막는 격벽이라는 걸요. 이걸 안 거는 건 배가 침수돼도 칸막이 없이 운항하는 거나 마찬가지였어요.

실수 3: 복제본을 하나만 실행한 것

프로덕션에서 단일 복제본만 돌리는 건 배포 전략이 아니에요. 그냥 장애를 기다리는 상태죠. 노드 하나가 다운되면 서비스 전체가 멈춥니다. 쿠버네티스는 복제본을 여러 개 띄우고 노드에 분산시키는 기능을 기본으로 제공하는데, 이걸 안 쓰면 애초에 쿠버네티스를 도입한 이유의 절반을 버리는 셈입니다.

당시 저는 "일단 돌아가면 되지"라는 생각이었어요. 그런데 운영 환경의 전제는 정반대더라고요. "언제든 죽을 수 있다"를 깔고 시스템을 설계해야 한다는 것. 죽지 않게 만드는 게 아니라, 죽어도 서비스가 멀쩡하게 만드는 게 핵심이었습니다.

실수 4: 비밀정보를 환경변수로 처리한 것

환경변수는 파드 스펙, 로그, 대시보드에 그대로 노출됩니다. 그런데 초보자 입장에서는 이게 가장 간단해 보이거든요. 저도 데이터베이스 비밀번호를 환경변수에 그냥 박아뒀어요. 다행히 첫 보안 리뷰에서 바로 걸려 큰일로 번지진 않았지만, 지금 생각해도 아찔합니다. 로그 수집 파이프라인에 한 번 빨려 들어갔으면 어디까지 퍼졌을지 모르니까요.

쿠버네티스 Secrets를 제대로 쓰거나, 더 민감한 환경이라면 외부 Secrets Manager에서 주입받는 방식을 써야 합니다. 지금은 당연하게 느껴지지만, 당시에는 "왜 이렇게 복잡하게 하지?" 싶었어요. 복잡한 데는 대개 이유가 있더라고요.

실수 5: 모든 걸 default 네임스페이스에 넣은 것

네임스페이스 하나에 서비스 수십 개가 뒤섞여 있으니, 문제가 터졌을 때 뭐가 뭔지 구분이 안 됐어요. 디버깅할 때마다 정리되지 않은 긴 목록을 위아래로 훑어야 했죠. 네임스페이스를 쓰면 논리적으로 영역을 나눌 수 있고, RBAC도 걸 수 있고, 환경별로 다른 정책을 적용할 수도 있습니다.

만드는 데 비용이 거의 안 드는데 안 쓸 이유가 없더라고요. 지금은 개발/스테이징/프로덕션은 물론이고, 팀별로도 네임스페이스를 갈라서 관리합니다. 경계를 미리 그어두면 사고가 한 칸을 넘지 못해요.

실수 6: 헬스체크 프로브를 설정하지 않은 것

프로브가 없으면 쿠버네티스는 애플리케이션이 진짜 정상인지 알 길이 없습니다. 컨테이너가 떴다는 사실만 알 뿐이죠. 파드는 실행 중인데 실제로는 요청을 하나도 처리 못 하는 상태여도, 시스템은 그 파드로 계속 트래픽을 흘려보냅니다. 사용자 입장에서는 멀쩡해 보이는 서버가 에러만 뱉는 거예요.

readiness probe와 liveness probe는 YAML 몇 줄이면 끝나는데, 효과는 그 길이에 비해 압도적으로 큽니다. 애플리케이션이 진짜 준비됐을 때만 트래픽을 받고, 망가지면 알아서 재시작되니까요. '컨테이너가 떴다'와 '서비스가 된다' 사이의 간극을 메워주는 게 이 몇 줄이었어요.

실수 7: 노드 장애 상황을 테스트하지 않은 것

초보자들은 애플리케이션은 테스트하지만 클러스터는 테스트하지 않아요. 저도 그랬습니다. 일부러 노드를 내려서 무슨 일이 벌어지는지 직접 본 적이 없었어요. 그러니 시스템이 장애 상황에서 어떻게 반응하는지 전혀 몰랐죠. 첫 부하 테스트가 사실상 첫 장애 테스트였던 겁니다. 가장 나쁜 타이밍에요.

이게 쿠버네티스를 진짜 이해하는 사람과 이해한다고 착각하는 사람을 가르는 선인 것 같아요. 직접 노드를 내려보고, 파드를 강제로 죽여보고, 네트워크를 끊어보면서 반응을 관찰해야 비로소 감이 생깁니다. 문서로 읽은 동작과 눈앞에서 본 동작은 완전히 다른 기억으로 남거든요.

진짜 운영팀들은 어떻게 하고 있을까

쓴맛을 보고 나니 패턴이 보이기 시작했어요. 쿠버네티스를 잘 굴리는 팀에는 몇 가지 공통된 습관이 있더라고요. 화려한 기술이 아니라, 지루할 만큼 기본적인 것들이었습니다.

처음부터 배포를 자동화합니다

프로덕션에서 손으로 kubectl apply를 치는 사람은 없어요. 모든 변경은 반드시 CI/CD 파이프라인을 거칩니다. 도구 취향의 문제가 아니라, 코드에서 클러스터까지 일관되고 추적 가능한 경로를 확보하는 문제예요.

저희 팀도 처음엔 "급하니까 일단 수동으로"였어요. 그러다 문제가 터졌을 때 누가 언제 뭘 바꿨는지 추적이 안 돼서 크게 고생했습니다. 변경 이력이 사람 머릿속에만 있으니까 원인 추적이 추리가 되더라고요. 지금은 아무리 사소한 설정 변경이라도 코드 리뷰와 파이프라인을 반드시 거치게 합니다.

설정을 코드처럼 관리합니다

모든 게 버전 관리 시스템 안에 들어가요. 클러스터 상태, 배포 설정, ConfigMap까지 전부 Git에서 관리됩니다. 문제가 생겼을 때 무엇이 언제 바뀌었는지 정확히 짚을 수 있거든요. 추적 가능성은 사고가 난 뒤가 아니라 사고가 나기 전에 깔아두는 거예요.

Infrastructure as Code는 이제 선택이 아니라 전제입니다. Helm 차트든 Kustomize든 도구 자체는 중요하지 않아요. 인프라 상태가 코드로 남아 있어서 언제든 재현 가능하다는 점이 본질입니다.

권한을 엄격하게 제한합니다

RBAC는 사고 난 다음에 붙이는 게 아니라 처음부터 깔고 가는 겁니다. 원칙은 단순해요. 꼭 필요한 것보다 많은 권한은 주지 않는다.

개발자는 자기가 담당하는 네임스페이스에만 닿을 수 있고, 운영팀도 환경별로 권한이 갈립니다. 처음엔 번거롭게 느껴질 수 있어요. 하지만 실수로 프로덕션을 건드리는 사고를 막는 가장 확실한 방법입니다. 권한을 좁히는 건 사람을 못 믿어서가 아니라, 실수의 폭발 반경을 줄이는 일이에요.

애플리케이션뿐 아니라 클러스터 전체를 모니터링합니다

애플리케이션 메트릭도 중요하지만 그것만으로는 부족합니다. 노드 CPU 사용률, 파드 eviction 비율, PersistentVolume 용량 같은 클러스터 레벨 지표가 진짜 조기 경보거든요. 앱은 멀쩡한데 아래 인프라가 서서히 차오르는 상황을, 앱 메트릭만 보면 놓칩니다.

Prometheus와 Grafana로 클러스터 상태를 실시간으로 보고, 임계치를 넘으면 알림이 오게 해뒀어요. 사용자가 문제를 발견하기 전에 우리가 먼저 알아야 하니까요. 장애 대응의 절반은 '얼마나 일찍 아느냐'에서 갈립니다.

장애를 의도적으로 테스트합니다

이건 영웅 놀이가 아니라 계획된 업무예요. 어떤 팀은 매주 하기도 하더라고요. Chaos Engineering이라고 부르는 방법인데, 일부러 시스템에 장애를 일으켜 복구 능력을 검증하는 겁니다.

핵심 질문은 "뭔가 실패할까?"가 아니라 "실패했을 때 무슨 일이 일어나는지 우리가 알고 있나?"예요. 노드를 임의로 종료해보고, 네트워크 지연을 끼워 넣어보고, 리소스 부족 상황을 만들어보면서 반응을 관찰합니다. 평온할 때 깨보는 게, 새벽에 진짜로 깨지는 것보다 훨씬 싸거든요.

기술적 문제가 아닌 인식의 문제

6개월을 돌이켜보니 명령어 자체는 그렇게 복잡하지 않았어요. 개념도 배울 만했고요. 정작 시간을 잡아먹은 건 직관을 기르는 일이었습니다.

숙련된 운영자들은 배포 전에 항상 몇 가지를 스스로에게 물어요.

  • 이 파드가 다운되면 어떻게 될까?
  • 이 노드가 사라지면 어떻게 될까?
  • 이 파드는 리소스를 얼마나 먹고, 그게 옆 파드에 어떤 영향을 줄까?
  • 이 설정에 자격증명이 새어 나오고 있지는 않을까?

초보자들은 아직 이런 질문을 하지 않아요. 부주의해서가 아니라, 아직 실패를 겪어보지 못했기 때문이죠. 질문은 보통 사고가 가르쳐주거든요. 저도 그랬고요.

대규모 쿠버네티스 도입을 돕는 컨설팅 일을 하면서 이 패턴을 여러 번 봤는데, 기술 지식 자체가 발목을 잡는 경우는 의외로 드물었어요. 진짜 어려운 건 팀의 기준을 "작동은 한다"에서 "우리가 중요하게 여기는 환경에서 안정적으로 작동한다"로 옮기는 일이었습니다.

이 둘은 완전히 다른 문제이고, 해결책도 다릅니다. 앞쪽은 코드의 문제지만, 뒤쪽은 습관과 조직 문화의 문제거든요.

간단한 체크리스트

클러스터를 프로덕션에 올릴 준비가 됐다고 판단하기 전에, 아래를 하나씩 짚어보세요.

리소스 관리

  • 모든 파드에 CPU/메모리 제한이 설정되어 있나요?
  • 한 파드에 문제가 생겨도 다른 파드들이 영향받지 않나요?

가용성

  • 중요한 서비스는 복제본이 여러 개 있나요?
  • 노드가 다운되어도 서비스가 계속 작동하나요?

모니터링

  • readiness/liveness probe가 설정되어 있나요?
  • 쿠버네티스가 애플리케이션 상태를 제대로 파악할 수 있나요?

조직화

  • 네임스페이스가 적절히 구성되어 있나요?
  • 부하가 높은 상황에서도 환경을 구분할 수 있나요?

보안

  • 시크릿이 제대로 관리되고 있나요?
  • 보안 검토에서 눈에 띄는 문제가 발견되지 않을까요?

배포

  • 파이프라인을 통한 배포가 가능한가요?
  • 모든 변경사항을 추적할 수 있나요?

운영

  • 클러스터 모니터링이 구축되어 있나요?
  • 사용자가 문제를 발견하기 전에 우리가 먼저 알 수 있나요?

이 질문들에 대한 답이 "아직은..."이라면, 바로 거기가 다음에 손댈 지점입니다.

결국 얻은 것

지난 6개월을 후회하지는 않아요. 눈앞에서 시스템이 무너지는 경험은 문서 어디에서도 배울 수 없는 걸 가르쳐주거든요. 다만 누군가 좀 더 일찍 말해줬으면 좋았겠다는 생각은 듭니다.

쿠버네티스는 배포의 지름길이 아니라, 인프라를 관리하는 새로운 방식이라는 것. 그리고 도구의 신뢰성은 결국 그 도구를 다루는 습관에 달려 있다는 것. 같은 클러스터라도 누가 어떤 습관으로 운영하느냐에 따라 전혀 다른 시스템이 됩니다.

대시보드의 초록색 점들은 목표가 아니에요. 그건 그냥 지금 이 순간의 스냅샷일 뿐이죠. 진짜 목표는 장애가 예측 가능한 방식으로 발생하고, 자동으로 복구되며, 사용자가 알아차리기 전에 무슨 일이 벌어지는지 알려주는 시스템을 만드는 겁니다.

그런 시스템은 하루아침에 만들어지지 않아요. 기초부터 차근차근 쌓고, 지루해 보이는 부분일수록 건너뛰지 마세요. 사고는 늘 그 건너뛴 자리에서 터지더라고요.

지금 운영하는 클러스터의 실제 모습과, 머릿속으로 원하는 모습 사이에는 어떤 간극이 있을까요?

#쿠버네티스#DevOps#클러스터관리#컨테이너오케스트레이션#인프라