-
Kubernetes 와 데이터베이스Kubernetes 2025. 3. 13. 18:27
Gemini 2.0 은 이제 그림도 그려줍니다! Kubernetes 와 데이터베이스
Kubernetes 세상에서 꺼지지 않는 떡밥이 몇개 있다. kubectl 발음하는 방법부터, 네임스페이스 어떻게 나눠서 써요? 까지. 이러한 떡밥들 중 모두의 호기심을 자극은 하면서도 적극적으로 탐구되지 않는 요소 중 하나가 바로 "DB K8s 위에 올리는거 어때요?" 다. 다들 각자의 의견이 있지만 대체로 의견은 전부 "하지 마세요"다.
다만 궁금한건 "왜?" 이다.
왜 다들 K8s 위에 DB 를 올리는걸 추천하지 않을까? 정답은 K8s 사용자 수 만큼이나 많겠지만, 그 중 한가지 답변을 정리해보고자 한다.
언제 이런 고민을 할까?
DB 를 K8s 위에 띄우는건 꾸준한 수요가 있긴 하지만 사실 흔하게 하는 고민이 아니기도 하다.
경험적으로는 보통 다음 경우 중 한가지가 아닐까 예상해본다.- 요구사항에 만족하는 Managed Service 가 존재하지 않을 경우
MySQL, Postgres, MSSQL 등의 대중적인 DB 를 평범하게 사용한다면 보통 AWS RDS, Google Cloud DB, OpenSearch 등과 메이저한 서비스들 부터 비용이 부담된다면 Neon, Planetscale, MongoDB Atlas 과 같은 서비스들도 존재해서 클라우드 베이스로 작동하는 서비스라면 어지간해서는 이러한 선택지를 사용하는것이 더 편하다.이런경우 자연스럽게 직접 DB 를 배포하는 방향을 생각해보게 되고 관리 포인트를 단순화하기 위해 Kubernetes 위에 DB 를 띄우는 방법을 고민해보게 된다. - 하지만 ClickHouse, GraphDB 와 같이 Managed Service 자체가 존재하지 않는 경우도 존재하고, Postgres 에 특정 Extension 을 추가하려는데 제공되는 서비스가 Postgres 호환이라 불가능한 경우도 있다. 예를 들어, GIS 기능을 활용해야 하는 서비스나, TSDB 를 특정 Extension과 함께 사용해야 하는 경우 Managed Service 만으로는 충족하기 어려울 수 있다.
- Managed Service 사용 자체가 불가능한 경우
- 반대로 Managed Service 가 존재하고 사용하고 싶어도 못사용하는 경우도 있다. 사내에 적절한 클라우드 인프라가 존재하지 않아 모든걸 온프렘에서 해결해야 된다던가 엣지환경에서 작동중인 서비스라던지 민감한 데이터를 처리할 경우 보안수준을 만족하지 못해서 사용하지 못하는 경우도 존재한다. 특히 금융 정보나 개인 정보와 같이 높은 수준의 보안 및 규제 준수가 요구되는 환경에서는 외부 Managed Service를 사용하는 것 자체가 정책적으로 불가능할 수 있다.
- DB 가 너무 많은 경우일단 기본적으로 전체 DB 에 대한 생성, 백업, 복구, 삭제 등 라이프사이클 관리는 물론이고 실시간으로 모니터링 하면서 이상 유무들을 우선순위에 맞게 파악을 할 수 있어야하는데, 아쉽게도 대다수의 Managed Service 들은 이러한 사용케이스에 대응이 잘 되어있지 않고 미묘하게 불편한점들이 많아 직접 구축을 하게된다. 이렇게 불편한것들을 하나하나씩 처리해 나아가면서 무언가를 만들고 나서보면 결국 Kubernetes 를 재발명 했다는 사실을 알게된다.
- 그럴바에는 그냥 처음부터 삽질을해서 Kubernetes 위에 DB 를 올릴 수 있게 만드는게 장기적으로 편해진다.
- 한 팀에서 관리해야하는 DB 가 너무 많을때도 간혹 문제가 된다. 전사의 모든 DB 를 한 팀에서 책임지는 경우, 각 게임 서버마다 독립적인 데이터베이스를 운영해야 하는 경우, MSA 환경에서 수많은 마이크로 서비스들이 각자 데이터베이스를 필요로 하는 경우 등등 요 근래 수십 수백대의 DB 한 팀에서 관리하는 케이스가 종종 보이는데 이정도 규모가 되면 어느정도 자동화 체계를 갖추지 않으면 생존하기가 어려워진다. 그 이유는 단순하게도 100대의 DB 에서 하루에 1% 정도만 이슈가 발생한다해도 매일매일 이슈가 발생한다는 소리기도 하고 100대나 되는것을 매일매일 사람손으로 진단할수도 없는 노릇이다.
- Managed Service 를 사용하기에는 비용이 부담되는 소규모 서비스일 경우이러한 서비스들은 사실 문제가 되는 경우도 거의 없어서 Kubernetes 위에 Pod 형태로 띄우는게 훨씬 좋지 않나? 에 대한 유혹을 쉽게 느끼게 된다.
(그리고 필자는 이를 지지한다. 필자의 서비스도 5년 넘게 K8s 위에서 굴러가는데 잘굴러간다.) - 가끔식 계륵같은 서비스들이 존재한다. 예를들어 사내에 Harbor 나 Gitlab 을 띄운다고 해보자. 이들은 Postgres 가 있어야 작동이 가능한데 사실 서비스가 돌아가는게 중요하지 부하가 크지도 않고 고가용성이 보장되야만 하는 그런 종류의 서비스도 아니다. 근데 이런거에 RDS 같은것을 사용하는것은
비용적으로도 합리적이지는 않다.
기안 올려서 승인받는 과정도 귀찮고
이러한 상황을 전제로 여러가지 요소들을 한번 생각해보자.
Database on Kubernetes: 장점
일단 Kubernetes 위에 DB 를 띄우면 뭐가 좋을까?
다들 막연하게 떠오르는 생각들이 있겠지만 구체화된것은 별로 없는데, 한번 명시적으로 뽑아보자면 다음과 같은 요소가 DB 를 K8s 위에 올렸을때 얻을 수 있는 장점들이라고 생각한다.
단일 조작 체계
가장 큰 장점은 무엇보다도
kubectl
하나로 모든것을 조작할 수 있는 환경이라고 자신있게 말할 수 있다.이는 생각보다 큰 장점인데, 서로 다른 서비스에 접속하는게 생각보다 큰일이다. 사내 서비스 접속하다가, OTP 찾아서 AWS, GCP 접속하다가 SSH 들어갔다가, git commit / push 했다가 kubectl 살펴보다보면 쉽게 찾을 수 있는 이슈도 지연이 발생하고, 귀찮아서 접속을 안해보게 되고, 결국 방치되고 버려지게 된다. 😇
구체적인 효과의 정도는 각 업무환경에따라 크게 달라지겠지만 대체로 이렇게 사용해야 하는 수단이 한가지 줄어드는 것 만으로도 부서를 넘나드는 이슈를 해결하는데에 있어 커뮤니케이션 비용을 매우 합리적으로 줄일 수 있는것은 물론 접속하기가 귀찮아서 방치되고 소외되는 리소스를 자주 확인하고 관리할 수 있는 상태로 만들어서 보이지 않는 효과가 크다.
일관된 보안 체계
이 방식의 간과된 또 하나의 장점은 보안 체계도 통합이 가능하다는 것이다. 대표적인게 방화벽과 접근제어다.
방화벽
기존의 방화벽의 룰은 단순하다. CIDR 블록을 기반으로 화이트/블랙리스트를 설정해서 IP 주소를 기준으로 판단한다. 다만 이 방식은 현대에 들어와서 유효하지 않은 경우가 많아졌는데, 왜냐하면 IP 주소의 수명이 짧아졌기 때문이다. 당장 Pod 만해도 생성될때마다 새로운 IP 주소를 할당받아서 사용하는데 이러한 IP 주소 기반의 룰 설정은 유효하지 않는 경우가 많아지고 CIDR 블록 통째로 열어주는 경우가 많다.
그럼 Kubernetes 내부에서는 어떻게 방화벽을 설정할까? 기본적으로 NetworkPolicy 라는 리소스를 활용한다. 개별 Pod 에 달려있는 Label, Name, Namespace 와 같은 메타데이터들을 통해 룰을 설정해 트래픽을 허가하거나 거절할 수 있다. 수시로 변동되는 리소스들에 대해 더 세밀하고도 일관성있는 룰 설정이 가능하다는 장점을 가지고 있다. (e.g., 같은 네임스페이스 내부에서만 통신이 가능하다, dev 라벨이 있는 Pod 로부터의 통신은 거절한다.)
문제는 이 둘이 섞여있을때 이다. DB 는 외부에있고 클러스터 내부에는 여러 종류의 리소스가 같이 운영되고 있을때 DB 의 방화벽 룰을 어떻게 설정해야 하는가? 보통 대다수는 이 이슈를 해결하지 못해 Proxy 서버를 둬서 IP 주소를 바꿔치기 하던가, 특정 노드그룹에만 Pod 이 배치되도록 하고 노드그룹 전체에 대해서 트래픽을 허용하던가 등의 수단을 취한다. 가능한 방법이긴 하지만 이상적이진 않다.
하지만 DB 가 클러스터 내부로 들어오면 Kubernetes Network Policy 를 통해서 일괄적인 관리를 수행할 수 있기 때문에 안정성, 그리고 관리적 측면에서 큰 이득을 볼 수 있다.
접근제어
모든 조작체계가 kubectl 로 통합된다는 소리는 이제 기본적으로 kubectl 사용만 잘 모니터링하는데에만 공을 쏟으면 된다는 소리이기도 하다.
일반적으로 VM 위에 DB 를 띄우려 한다면- VM 을 띄우기 위한 웹 콘솔 혹은 IaC 환경에 대한 접근 권한
- 띄워진 VM 에 접속하기 위한 SSH Key 관리 (이걸 또 주기적으로 로테이션 해줘야한다)
- 띄워진 VM 에 접속하기 위한 SSH 권한 관리 (회사 규모에 따라 다른 내용이긴 하다)
- VM 의 HTTP Proxy 설정 혹은 방화벽 관리
- VM 에서 실행할 바이너리에 대한 감사 (금융 / 개인정보쪽으로 민감한 경우)
아마 이정도의 승인이 필요할것이다. 생각만해도 하기 싫어진다. 하지만 DB 를 Kubernetes 위에 띄울경우 클러스터를 구축하는 과정에서 앞서 말한 승인 과정들이 이미 진행되었을 것이기 때문에 (아쉽지만, 없어지는게 아니다.) K8s 에 접근하기위한 권한만 존재하면 누구나 쉽게 DB 를 띄울 수 있게 된다!
K8s 접근권한 관리도 RBAC, Audit Log 만 잘 활용해도 어지간한 이슈를 충분히 추적하고 관리할 수 있으며, 부족하다 싶으면 Admission Webhook 같은 존재들도 있기 때문에 기존 VM 방식보다도 꽤 섬세한 권한관리, 심사 및 모니터링이 가능하다. 가장 큰 장점은 뭐니뭐니해도 이 모든게 HTTP 로 동작하다보니 간단한 웹서버 개발만으로도 커스텀한 룰 추가가 가능하다는 점이 아닐까 한다.
모니터링 통합
마지막으로 큰 장점 중 하나는 모니터링 통합이다. 모니터링 설정이란게 은근이 시간소모적이고 가끔 놓쳐도 잘 티가나지 않아서, 나중에 장애나고 정작 필요할때 데이터가 없어서 땅을치고 후회하기 쉬운 요소이다. K8s 의 경우 Prometheus Operator 만 대충 띄워놔도 기본적인 시스템 메트릭이 전부 수집되며 DB 뿐만 아니라 기존 Application 들도 같이 모니터링이 되기 때문에 하나의 알람룰로 모든 서비스를 관리하는게 가능해진다.
이러한점을 통해 얻게 되는 또 하나의 장점은 모니터링 업무에 대한 점진적인 로드맵을 가져갈 수 있다는 것이다. VM 으로 구축을 할 경우 최소한의 모니터링을 구축하는 것 자체가 공수가 상당히 많이 들어서 (Fluentd 깔고, Exporter 설치하고, Prometheus 에 수동으로 Exporter 등록하고, 대시보드 만들고... 등등) 안하던가, 전부 하던가의 선택을 강요받게 되는데, K8s 의 경우 Pod 를 띄우기만해도 CPU, Mem 같은 기본 메트릭은 전부 수집되는 상황이고 기본적인 시각화도 이미 되어있는 상황이기 때문에 로드맵을 세우고 추후 Slow Query 분석, Cache Hit 트랙킹 등으로 필요에 따라 점차 모니터링을 고도화 해 나아가는 작은 규모의 작업을 여러번하는 방식으로 점진적으로 고도화되는 스텝을 밟아갈 수 있다.
자동화된 관리
가장 기본적인것도 까먹을 수 없다. 모종의 이유로 Pod 가 꺼지면 kubelet 이 알아서 살려준다. 로그도 너무 많이 쌓였다 싶으면 알아서 정리해준다. 노드 죽었다 싶으면 다른 노드로 옮겨준다. LB 설정도 알아서 바꿔준다.
물론 이것들 전부 systemd, logrotate, 24시간 대응 온콜 담당자(?) 잘 구성해두면 가능하긴 하지만 굳이 바퀴를 두번 발명 할 필요는 없도록 만들어주는게 K8s 를 선택했을때 큰 혜택이라고 생각한다.
DB, K8s 의 특성
장점만 봤을때, DB 를 K8s 위에 띄우는것은 여러모로 이점이 많아보인다. 하지만 일반적인 어플리케이션과 DB 는 다르고 장점을 상쇄하는 이슈들이 상당 수 존재한다. 이 이슈들에 대해서 이야기 해보기전에 먼저 DB 와 K8s 의 특성을 한번 생각해보자.
DB 라는 Application 의 대표적이면서도 까다로운 특성은 두가지로 요약할 수 있다.
- Stateful 한 서비스 이다.
물리적으로 Application 이 실행되는 CPU 와 데이터가 저장된 Storage 간에 긴밀한 통신이 가능해야 한다. 안되면? 이 통신 구간이 전체 서비스의 주요한 Bottleneck 이 될 가능성이 높고 성능 향상에 큰 한계를 맡게된다. - 대체로 매우 높은 Uptime 이 필요하다.
Elasticsearch, CockroachDB 와 같이 수시로 Rebalancing 하는것을 전제로 HA 가 잘 작동되는 몇몇 예외가 존재하긴 하지만, 기본적으로 한번 프로세스가 실행되면 절대로 죽으면 안되는 가정을 깔고 간다. 만약 죽으면? 서버단에서 클라이언트 로직이 잘 되어있으면 다른 Replica 로 Failover 가 되긴 하겠지만, 백업 DB 의 마스터 승격이 되기까지 딜레이가 존재하고 전체 서비스 중단이 발생하게 된다.
DB 의 위와같은 특성은 K8s 와 상당히 상성이 안좋은데, K8s 은 설계단계부터
- 모든 노드는 언제든지 다른 노드로 대체가 가능하다.
하지만 물리적으로 저장된 데이터를 다른 노드로 순식간에 전송은 못하므로 (...) 보통은 Remote Storage 를 사용한 Persistent Volume 으로 사용하는데, 이는 CPU 와 Storage 간에 물리적 거리를 발생시키고, 단순 통신 지연 뿐 아닌 추가적인 장애 발생 가능 구간이 생겨 서비스 안정도가 떨어진다는 의미를 갖기도 한다. - Pod 는 짧은 수명을 가지고 수시로 교체될 수 있다.
사실 이는 Kubernetes 뿐만 아니라 일반적으로 많이 거론되는 이야기 이기도 하다. 무엇이 되었던간에 장기간 운영은 예상하지 못한 결과를 가지고 온다. 1시간에 1MB 정도 발생하는 메모리 누수도 1년이면 200GB 가 넘어가고, 버그픽스를 위해 Runtime 중에 API 적당히 찔러서 어떻게둔 살려둔 프로세스가 2년 넘게 작동하다가 모종의 이유로 죽고 다시 안살아나면 남아있는 사람들이 괴로워진다. 하지만 하루 단위로 프로세스가 수시로 종료되고 다시 실행해도 서비스 안정도에 이상이 없도록 전체적인 구조가 설계되어있다면 이러한 괴로움에서 좀 더 빠르게 벗어날 수 있고 K8s 는 이 사상을 잘 실천할 수 있게 해주는 플랫폼이다. - 다만, 우리가 사용하는 전통적인 DB 들 (MySQL, Postgres, Mongo... 등등) 이 이런 사상에 잘 안 어울릴 뿐이다. 🫠
와 같은 특징을 가지고 있다보니 DB 를 안정적으로 K8s 에서 동작시키기가 참 까다로워진다...
Database on Kubernetes: 이슈
이러한 둘간의 상성을 생각해봤을때 발생하는 이슈들이 좀 많다.
인프라 이슈
- CPU 와 Storage 간 거리첫번째 성능 측면의 이슈는 당연한 이야기 이다. 물리적으로 거리가 멀어서 응답속도도 떨어지고, 대체로 SATA3 (5Gbps) 보다도 낮은 대역폭을 가지고 IP 프로토콜등을 (e.g., iSCSI, NFS 등) 통해서 통신하는게 일반적이기 때문에 아무래도 성능적인 이슈가 존재할 수 밖에 없다. 이는 특히, 높은 IOPS 와 낮은 Latency를 요구하는 트랜잭션 처리량이 많은 DB의 경우 성능 저하가 더욱 두드러지게된다.이러한 이슈가 발생할 경우 DB CPU 랑 메모리도 남아돌고 Disk Utilization 도 여유가 있어보이는데 성능이 미묘하게 안나오는 상황이 연출되는데 온갖 삽질을 다 하다가 최후에 Disk Latency 가 이상한걸 보고 이슈를 발견은 했는데 DB 를 다른 노드로 이전하기는 또 부담스러운 경우가 발생할 수 있다.
- 그리고 두번째, 스토리지까지의 통신구간이 추가되는 만큼 안정성 측면의 이슈가 발생한다. 아무래도 바로 옆에있는것 보다 좀 멀리있으면 장애 확률이 올라간다. IP 프로토콜을 사용할 경우 데이터센터 내부에서 위아래에 있는 다른 서버들과 물리적으로 같은 네트워크 스위치를 공유해서 사용하는데, 문제는 IP 프로토콜은 기본적으로 "best-effort" delivery model 점이다. 쉽게 말하면, "최선은 다하겠지만 안하면 어쩔 수 없고" 다. 옆에 존재하는 서버가 BatchJob 돌리면서 네트워크 스위치에 과부화 걸었다고 DB 성능에 이슈가 발생되는 시나리오가 충분히 발생될 수 있다는 소리이다.
- Kubernetes 는 기본적으로 Pod 가 어떤 노드에서든 배포되어 돌아갈 수 있도록 일반적으로 NFS, iSCSI 와 같은 프로토콜을 사용하는 Remote Storage 를 사용하고, 그로 인해 Storage 와 CPU 간이 물리적인 거리가 존재한다. 이는 두가지 이슈를 고민하게 만드는데 바로 성능 측면의 이슈와 안정성 측면의 이슈다.
- 공유하는 리눅스 커널 자원다만, DB 는 좀 다르다. DB 는 성능을 하드웨어적인 한계까지 끌어올리는데 특화된 App 이고 제한된 자원을 가진 리눅스의 각종 커널기능등을 적극적으로 활용하는데,
mmap
,inotify
,file (ulimit)
,memlock
등의 키워드들이 대표적인 예시다. 문제는 이들 자원들은 아직 리눅스 커널, 컨테이너 (네임스페이스) 컨트리뷰터들 사이에서도 아직 적극적으로 논의되지 않는 내용들이며 개별 컨테이너마다 완벽하게 격리되는 리소스들이 아니라는 점이다. - 다르게 표현하면 하나의 인스턴스에 DB 컨테이너는 50개 정도 띄웠더니 해당 노드의
inotify
자원이 고갈되어서 정상적인 작동이 안되는 (...) 이슈가 발생할 수 있으며, 이들에 대해서는 각 개별 DB 들의 특성을 분석하고 모니터링이 필요한 지표들이 사전에 검토 할 필요가 있다.
(e.g., 하나의 노드에 DB Pod 가 3개 이상 실행되고 있어도 inotify 자원은 괜찮은가? mmap 등으로 사용한 캐시 용량은 적절한가? 등등) - K8s 는 일반적으로 같은 리눅스 커널을 공유하는 수십개의 컨테이너들이 하나의 노드에서 같이 돌아가게 된다. 일반적으로 이는 큰 이슈는 안된다. 대다수의 애플리케이션은 CPU 나 Memory 같은 자원이 Bottleneck 인지라 다른데서 이슈가 터지기 전에 CPU, Memory 가 먼저 부족해지고 이는 모니터링 하기가 쉽다.
안정성 이슈
- 새롭게 추가되는 장애포인트Kubernetes 로 Container 안에 DB 를 띄워두면 문제가 좀 복잡해진다.
- DB 의 문제거나
- DB 가 떠있는 VM 의 문제거나
(e.g., SSD 가 물리적인 수명이 다 되어서 느려짐) - DB 가 떠있는 VM 에 같이 떠있는 다른 Container 의 문제거나
(e.g., 다른 Container 가 CPU, Disk, inotify 같은 자원들을 다 써버릴 경우) - DB 가 떠있는 Container 에 적용된 네트워크 구성 문제거나
- (e.g., K8s 업그레이드 하다가 CNI 설정 실수로 컨테이너 네트워크가 먹통)
- DB 가 떠있는 Container 에 적용된 cgroup 설정 이슈거나
(e.g., DB 가 구져서... cgroup 인지 못하고 메모리 할당 잘못함) - DB 가 사용하고 있는 스토리지에 이슈던가
(e.g., Ceph 가 리밸런싱하면서 특정 블록 액세스 속도가 심각하게 저하됨) - ....(이하생략)
- 하지만 DB 는 Stateful 하다. 마음대로 다시시작하기도 까다롭고 다른데로 옮겼는데 해결이 안되면? 매우 심각하게 머리가 아파진다. 또 Elasticsearch 와 같은 일부 DB 들은 재기동시 데이터 무결성 보장을 위해서 Degraded 상태로 진입하게 되는데 이 또한 서비스의 품질을 특정 기간동안 떨어뜨리는 이슈라 까다롭다.
- VM 안에 DB 를 하나 띄워두면 장애가 발생했을때 고민거리는 단순해진다. DB 의 문제거나, DB 가 떠있는 VM 문제다.
- 약한 조작 격리거기다 실수를 저질렀을때 복구에 들어가는 공수가 Application 과는 다르다. 실수의 종류에 따라 다르지만, 모든 manifest 를 전부 삭제한 경우라면, Stateless 한 서비스는 어딘가 저장되어있던 yaml 을 그냥 다시 배포하면 끝난다. 하지만 Stateful 한 서비스는 수 TB 까지도 갈 수 있는 데이터도 같이 복구해야한다. 또 재기동하는데 앞서 언급한것처럼 무결성을 보장하기위해 Degraded 상태로 진입할 가능성도 있다.
- 이러한 실수를 원천적으로 차단하도록 권한을 부여하던가 실수에도 내성있는 복구 방안을 마련하던가 해두는편이 있는데, 이는 확실히 아무것도 안하고 그냥 노드속에 들어가
apt install
정도로 끝낼 수 있는 기존의 방식보다는 RBAC 설정 강화, 네임스페이스 정책 설정등의 추가적인 공수가 필요하다. kubectl
명령어 하나로 DB 랑 Application 이랑 동시에 조작이 가능하다. DB 는 아마 kubectl 로 조작할 일이 초창기 이후로는 별로 없겠지만, Application 은 수시로 배포되고 아키텍처가 변경되고 AB 테스팅등을 진행하느라 수시로 형상이 바뀐다. 이는 즉 언제든지 Application 을 조작하다가 실수로 클러스터 전역을 대상으로rm -rf /
만큼 치명적일 실수를 저지를 가능성이 있다는 것이다.
사용성 이슈
- 설정 이슈하면 되는 업무이기는 하지만 추가적인 부담과 공수가 들어간다는것은 변함이 없고, 컨테이너 환경에서 실행됨으로 인한 이슈 (e.g., 커널자원 접근 권한 없음, 전체 가용가능한 메모리 인식 오류) 도 무시할 수 없다.
- DB 종류마다 다르긴 하지만, DB 설정이 까다로워지는 경우가 존재할 수도 있다. 일단 기존에 단순히 vim 으로 파일을 수정해 변경할 수 있었던 설정도 ConfigMap 등을 통해서 관리할 수 있도록 변경해줘야하고, Elasticsearch, Postgres 등에 플러그인을 설치해야 된다면 새로운 컨테이너 이미지를 만들던가 컨테이너가 실행될때 어떻게든 바이너리를 넣어줄 방법을 고민해줘야 한다.
- 네트워크 설정 이슈이는 즉 DB 가 가진 IP 주소가 클라이언트가 직접 접근이 가능해야 하는데... Kubernetes 는 자체적인 사설 IP 주소를 사용하는 케이스가 대부분이고 외부에서 특정 Pod 으로 직접적인 통신은 대체로 불가능하게 막아둔다.
- 이런 경우를 만나게 될경우 어찌어찌 해결한다해도 앞으로 DB 의 버전이 올라가면서 어떤종류의 변화가 발생할지 사전에 인지하기가 어렵기 때문에 고민이 되는 포인트이기도 하다.
- 일부 DB 의 경우 자신의 IP 주소를 클라이언트에게 알려주는것이 강제되는 경우가 존재한다. Redis Cluster, MongoDB Cluster, HBase 등이 대표적인 예시인데 이들은 데이터를 샤딩해서 저장하는것이 가능하고 프로토콜적으로 DB 가 클라이언트에게 자신의 IP 주소를 알려주고 해당 IP 주소"들"을 통해서 자신이 원하는 데이터가 저장되어있는 인스턴스에게 직접 요청을 하는 구조를 가진다.
- 라이프사이클하지만, K8s 에는 "꺼진상태" 라는 개념이 없다. 선언이 되어있으면 실행이 되는것이고, 재시작이란 기존 Pod 선언을 삭제하고 새로운 Pod 을 선언하는 것이다. 즉, DB 의 라이프사이클에 존재하는 "꺼진상태" 를 K8s 에서 기본적으로 지원해주질 않고 파일을 그냥 붙여넣기만 하면 되는 간단한 방법을 수행하기위해서 수많은 워크어라운드 들을 고민해봐야한다.
- 필자라면 entrypoint.sh 를 조작하여 특정 파일이 존재하면 DB 프로세스를 실행하지 않고 sleep infinity 명령을 실행해서 Pod 는 실행중이지만 대기 상태인 방법을 한번 고민해볼 것 같다. 하지만 이 조차도 추가적인 업무 공수를 요구하는 것인지라 도입에 주저하게되는 원인 중 하나가 될 수 있다라고 생각한다.
- DB 를 운영할때는 비상상황에 대한 대응도 생각을 해봐야한다. Replica 구성으로 DB Pod 를 3대 띄워놨는데 모종의 이유로 한대가 죽어버려서 DB 를 백업에서 복구하고 다른 두 DB 와 상태를 동기화 하는 시나리오를 고민해보자. 기존 방식으로는 보통 수행하는것이 DB 프로세스를 종료하고 특정 디렉토리에 백업해뒀던 DB 파일들을 그대로 붙여넣기하고 프로세스를 다시 시작하는 것이다. 원시적이지만 빠르고 효과적인 방식이다.
Database on Kubernetes: 해소법
이렇게 K8s 위에 DB 를 띄우는것에 대해서 다양한 이슈들이 존재하긴 하지만, 사실 이러한 이슈들은 "귀찮은"거지 해소를 못하는 내용은 아니다.
(언제나 길은 있다. 얼마나 쉽게 그 길을 걸어갈 수 있냐의 문제일 뿐)설정 이슈나, 약한 조작 격리 이슈, 네트워크 설정 이슈의 경우 머리를 잘 쓰면 되긴 한다. 그럼 나머지, 특히 성능이나 운영 안정성과 관련된 이슈들은 어떻게 해결할 수 있을까? 여러가지 방법이 있겠지만 그 중 필자가 가장 선호하는 방법은 다음과 같다.
Kubernetes 에 기대했던 기능들의 일부를 포기한다.
이 모든 이슈의 근본적인 이유는 DB 의 상성과 잘 맞지 않는 Kubernetes 가 제공하는 편리한 기능들을 억지로 사용하려고 해서라고 생각한다. 그럼 반대로 몇몇 기능만 포기하면 여전히 Kubernetes 의 강력한 장점들을 가져가면서도 VM 만큼의 성능과 안정성을 가지고 갈 수 있다.
예를들어
- 노드 하나에 DB Pod 하나만 떠있으면 다른 Pod 들이 같이 구동됨으로 인해 발생할지 모르는 이슈들에 대해서 고민할 필요가 없다.
- DB 전용으로 사용할 노드그룹을 만들어놓고 해당 노드들에 Taint 설정만 적절히 해두면 커널 자원 결합등의 이슈에 대해서 고민할 필요가 없다. 혹은 Kata Containers 와 같은 CRI 구현체들을 통해서 Pod 를 커널레벨에서 완전히 격리된 하나의 VM 으로 실행하는 방법도 존재한다.
- Pod 가 항상 동일한 Node 에서 동일한 Local Volume 을 사용하면 Remote Storage 에 대한 걱정을 할 필요가 없다.
- Local Persistent Volume 만 적절하게 선언한다면 큰 공수 없이 특정 PV 를 사용하는 PVC 를 사용하는 Pod 는 항상 동일한 노드에 배정하게 만들 수 있다. 이 경우 별도의 오버헤드 없이 특정 노드에 존재하는 물리적 디스크를 직접적으로 사용하므로 스토리지 성능에 대한 걱정을 덜 수 있다.
- Pod IP 를 부여하지 않고 Node 와 같은 네트워크 네임스페이스를 사용하면 K8s 로 인해 야기되는 네트워크 관련 장애에 대해서 고민할 필요가 없다.
- hostNetwork 를 적절히 사용하면 Kubernetes CNI 가 별도로 생성한 클러스터 네트워크를 사용하지 않고 매우 단순하게 리눅스 호스트의 네트워크 네임스페이스를 공유해서 사용할 수 있다. 이 경우 CNI 설정 이슈로 인해서 클러스터 네트워크에 장애가 발생해도 DB pod 의 네트워크는 안정적으로 유지되게 만들 수 있다.
그 이외에 사용성 관련 이슈들도 DB 의 종류에 따라 다르지만 operator 를 사용하는것도 한가지 방법이 될 수 있다. 아직 충분히 성숙한 오픈소스 프로젝트가 흔하진 않지만 ECK, minio operator, MongoDB Operator 등은 이미 Production 에서 안정적으로 사용중인 사례들이 다수 존재하기때문에 만약 자신이 사용하려는 DB 에 적절한 operator 가 존재한다면 (혹은 만들 수 있다면) 이들을 사용하는것도 충분히 유효한 요소 중 하나이다.
그래서 K8s 위에 DB 올리기 할만 한가요?
프로덕션 환경에 걸맞게 K8s 위에 DB 를 올려서 운영이 가능한가? 라고 묻는다면 의심할 여지없이 가능하다고 말할 수 있다. DB 라는 분야가 보수적이고 공개된 사례가 적어서 그렇지 Operator 개발이 "할만한" 작업이 되어가면서 조금씩 K8s 위에 DB 를 올리는 사례가 늘어가고 있는걸로 인지하고 있다.
하지만 그 방법이 그냥 VM 에 DB 를 설치해서 사용하는 것 보다 "효율적인가?" 라고 묻는다면, 현재 상황을 정말 엄밀히 따져 볼 필요가 있다라고 생각한다. 필자 개인적으로 생각할때 이 방법이 가치가 있는 경우는 두가지가 존재한다고 생각한다.
- 운영하는 DB 의 개수가 정말 많은 경우 (약 100대 이상)Kubernetes 클러스터 운영 및 DB 전문가를 영입하고 유지하는 비용부터 클러스터 인프라 운영 비용까지 종합적으로 고려가 필요하겠지만 DB 운영규모가 100대 수준을 넘어가기 시작하면 알려진 Managed DB 를 사용하는 비용보다 이를 관리하는 팀을 꾸리는 비용이 더 저렴할 가능성이 크다고 생각한다.
- 어차피 사람손으로 운영하는게 힘든 규모라 운영성 업무를 자동화에 초점을 맞춘 상황에서 K8s 생태계에서 이미 검증되고 잘 사용중인 여러가지 이점들을 처음부터 다시 개발하지 않고 그대로 얻어갈 경우 DB 를 K8s 위에 올려서 사용할 가치가 충분히 크다고 본다.
- 수시간 정도의 장애가 허용되는 서비스의 경우
- 수시간 정도의 장애가 허용된다면 사실 어떻게 운영해도 돌아가기만 하면 충분하다. 특정 클라우드 콘솔 들어가서 DB 를 띄우는것보다
kubectl apply -f
명령어 하나 치는게 훨씬 편하고 빠르게 다양한 시도를 해볼 수 있어 충분히 가치가 있다. 특히, 개발/테스트 환경, 스테이징 환경, 혹은 내부적인 데모 시스템과 같이 높은 가용성이 필수는 아닌 서비스의 경우에는 압도적으로 편리하다 자부할 수 있다.
하지만 그 이외의 경우라면...? 개인적으로는 별로 추천하진 않는다. 🤣
'Kubernetes' 카테고리의 다른 글
원리부터 파악하는 컨테이너 이미지 PULL (w/ curl) (6) 2023.06.30 [Kubernetes] Controlplane 죽이기 (1) 2023.06.26 - 요구사항에 만족하는 Managed Service 가 존재하지 않을 경우