메인 콘텐츠로 건너뛰기
page thumbnail

Kubernetes 기초 개념과 활용 개요

wislan
wislan
조회수 23
요약

개요

Kubernetes(쿠버네티스, 줄여서 K8s)는 컨테이너로 패키징된 애플리케이션을 자동으로 배포하고, 확장하고, 관리하기 위한 오픈소스 플랫폼이다. 구글이 내부에서 사용하던 대규모 시스템 운영 경험을 바탕으로 만들었고, 현재는 클라우드 네이티브 환경의 사실상 표준 오케스트레이션 도구로 자리 잡았다.

Generated Image

간단히 말해, 쿠버네티스는 "컨테이너를 어디에 몇 개 띄울지, 문제가 생기면 어떻게 복구할지"를 대신 고민해 주는 시스템이다. 개발자는 애플리케이션이 어떻게 실행되어야 하는지의 '원하는 상태'를 선언적으로 적어두고, 쿠버네티스는 실제 상태가 그에 맞게 유지되도록 계속 조정하는 역할을 맡는다.

쿠버네티스는 클라우드 서비스 제공자마다 조금씩 다른 인프라 환경을 추상화해 주기 때문에, 같은 설정으로 온프레미스 서버, 퍼블릭 클라우드, 하이브리드 환경 등 다양한 인프라에서 비교적 일관되게 애플리케이션을 운영할 수 있도록 도와준다. 이런 이유로 마이크로서비스 아키텍처나 DevOps, CI/CD 환경에서 특히 많이 사용된다.

Kubernetes의 탄생 배경과 목표

쿠버네티스는 컨테이너 기술의 확산과 함께 등장했다. 컨테이너는 애플리케이션과 그 실행에 필요한 라이브러리, 설정 등을 하나의 패키지로 묶어 어디서든 동일하게 실행되도록 도와주지만, 컨테이너가 수십 개, 수백 개, 나아가 수천 개가 되면 이를 직접 손으로 관리하기는 사실상 불가능에 가까워진다.

구글은 이미 오래전부터 수많은 서비스를 내부 컨테이너 기반으로 운영해왔고, 이를 자동으로 스케줄링하고 관리하는 시스템을 구축해 사용하고 있었다. 이러한 내부 경험을 토대로, 누구나 사용할 수 있는 컨테이너 오케스트레이션 플랫폼이 필요하다고 판단해 쿠버네티스를 오픈소스로 공개하게 되었다.

쿠버네티스의 핵심 목표는 크게 세 가지로 요약할 수 있다. 첫째, 컨테이너 애플리케이션의 배포와 롤링 업데이트를 자동화하는 것, 둘째, 애플리케이션의 원하는 상태(예를 들면 "항상 5개의 인스턴스를 유지")를 선언적으로 정의하고 이를 시스템이 스스로 맞춰 가도록 하는 것, 셋째, 장애가 발생했을 때 자동 복구와 재배치를 통해 서비스의 가용성을 높이는 것이다.

기본 개념: 컨테이너와 오케스트레이션

쿠버네티스를 이해하려면 먼저 컨테이너의 개념을 짚고 넘어가는 것이 도움이 된다. 컨테이너는 가상머신과 비슷해 보이지만, 전체 운영체제를 포함하지 않고 호스트 OS의 커널을 공유하면서 필요한 부분만 격리하여 실행한다. 그 결과, 가상머신보다 훨씬 가볍고 빠르게 실행할 수 있으며, 애플리케이션이 어느 환경에서든 같은 방식으로 동작하도록 해준다.

하지만 실제 서비스 환경에서는 하나의 서버에 여러 개의 컨테이너를 띄우고, 서비스가 성장하면 서버 대수도 계속 늘어나는 구조가 된다. 이때 어떤 컨테이너를 어떤 서버에 배치할지, 리소스 사용량을 어떻게 균형 있게 나눌지, 트래픽 증가에 따라 컨테이너 수를 몇 개로 늘릴지 같은 문제를 모두 자동으로 처리해 줄 필요가 생긴다. 이렇게 여러 컨테이너를 조율하는 과정을 "오케스트레이션"이라고 부르며, 쿠버네티스는 바로 이 역할을 맡는 시스템이다.

쿠버네티스는 컨테이너 런타임(예: Docker 호환 런타임) 위에 올라가서, "컨테이너 단위"가 아니라 "애플리케이션 단위"로 배포와 관리를 할 수 있도록 추상화 계층을 제공한다. 사용자는 개별 컨테이너를 직접 관리하기보다는 쿠버네티스가 제공하는 리소스 개념을 통해 애플리케이션의 전체 구성을 표현하게 된다.

쿠버네티스 아키텍처: 클러스터, 노드, 컨트롤 플레인

쿠버네티스는 기본적으로 "클러스터"라는 단위로 동작한다. 클러스터란 여러 대의 서버(물리 혹은 가상)를 하나의 논리적 그룹으로 묶은 것으로, 쿠버네티스는 이 클러스터 전체를 하나의 큰 컴퓨터처럼 다루며 컨테이너를 배치한다.

클러스터를 구성하는 서버는 크게 "마스터 역할을 하는 컨트롤 플레인"과 "실제 애플리케이션 컨테이너가 실행되는 워커 노드"로 나눌 수 있다. 컨트롤 플레인은 클러스터의 두뇌에 해당하며, 전체 상태를 저장하고, 새로운 작업을 어디에 배치할지 결정하며, 사용자의 요청을 처리하는 API 서버를 제공한다. 워커 노드는 실제 CPU·메모리·스토리지 같은 자원을 제공하며, 그 위에서 컨테이너가 실행된다.

각 노드에는 "kubelet"이라는 에이전트가 설치되어 있고, 컨트롤 플레인과 통신하면서 해당 노드에 어떤 컨테이너를 띄울지 지시를 받고 실행 상태를 보고한다. "kube-proxy"라는 컴포넌트는 네트워크 트래픽을 적절한 컨테이너로 전달해 주며, 서비스 단위의 로드밸런싱을 담당한다. 이런 구조 덕분에 클러스터에 노드를 추가하거나 제거하는 작업을 비교적 유연하게 할 수 있다.

핵심 리소스: Pod, Deployment, Service

쿠버네티스에서 가장 작은 배포 단위는 "Pod"이다. Pod는 하나 이상의 컨테이너를 묶어 놓은 단위로, 같은 Pod 안의 컨테이너들은 항상 같은 노드에서 함께 실행되며 네트워크와 스토리지를 공유한다. 실무에서는 보통 하나의 Pod에 하나의 주요 컨테이너를 넣고, 필요한 경우 사이드카 컨테이너를 함께 넣어 기능을 확장하는 패턴을 사용한다.

"Deployment"는 Pod를 여러 개 복제하고, 롤링 업데이트를 수행하며, 원하는 수의 Pod를 항상 유지하도록 관리하는 상위 개념이다. 개발자는 "이 이미지 버전으로 Pod를 최소 3개 유지해라"와 같은 규칙을 Deployment에 정의하면, 쿠버네티스가 Pod의 생성과 삭제를 자동으로 조정해 준다. 애플리케이션 버전을 변경할 때도 Deployment의 설정만 수정하면, 쿠버네티스가 기존 Pod를 순차적으로 종료하고 새 버전으로 교체하는 롤링 업데이트를 수행한다.

"Service"는 네트워크 관점에서 Pod 집합을 하나의 논리적인 엔드포인트로 묶는 역할을 한다. Pod는 언제든지 재시작되거나 다른 노드로 옮겨지면서 IP가 바뀔 수 있기 때문에, 클라이언트가 직접 Pod를 가리키게 하면 안정적인 접근이 어렵다. Service는 이러한 Pod의 집합에 고정된 이름과 IP를 부여하고, 내부적으로는 해당 Pod들로 트래픽을 분산시켜 준다. 이를 통해 애플리케이션 구성 요소들 간 통신을 단순하고 안정적으로 유지할 수 있다.

선언형 구성과 원하는 상태(Desired State)

쿠버네티스의 중요한 특징 중 하나는 "선언형" 접근 방식이다. 사용자는 시스템에게 "어떻게 할지"를 일일이 지시하는 대신, "최종적으로 어떤 상태가 되기를 원하는지"를 문서(YAML/JSON)로 선언한다. 예를 들어 Deployment에서 replica 수를 3이라고 적어두면, 쿠버네티스는 현재 Pod가 2개만 떠 있으면 하나를 추가하고, 4개가 떠 있으면 하나를 줄여서 결국 3개가 되도록 맞춘다.

이러한 설정 파일은 코드처럼 버전 관리 시스템에 함께 저장할 수 있어서, 인프라를 코드로 다루는 "Infrastructure as Code" 문화와도 잘 맞는다. 또한 특정 시점의 구성 상태를 쉽게 재현하거나 롤백할 수 있어, 운영 안정성과 추적 가능성을 높여준다.

간단한 예로, 다음과 같은 YAML 구성 파일을 이용해 웹 애플리케이션을 배포할 수 있다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-web
  template:
    metadata:
      labels:
        app: my-web
    spec:
      containers:
        - name: my-web
          image: nginx:stable
          ports:
            - containerPort: 80

이 파일을 쿠버네티스에 적용하면, 시스템은 자동으로 nginx 컨테이너를 가진 Pod를 세 개 띄우고, 이후에도 계속해서 세 개가 유지되도록 관리한다.

스케줄링과 자동 복구, 오토스케일링

쿠버네티스 스케줄러는 새로운 Pod를 어느 노드에 배치할지 결정하는 역할을 한다. 스케줄러는 각 노드의 현재 리소스 사용량, Pod의 요구사항(CPU·메모리, 특정 노드에 배치해야 하는 조건, 영역/가용 영역 등) 등을 고려해, 전체적으로 균형 잡힌 상태를 유지하려고 한다. 이를 통해 일부 노드에만 과부하가 걸리는 문제를 줄이고, 클러스터 자원을 효율적으로 활용할 수 있다.

또한 쿠버네티스는 Pod의 상태를 계속 감시하면서, 컨테이너가 비정상 종료되거나 응답하지 않을 때 자동으로 재시작하거나 다른 노드에 새로 배치해 준다. 이 기능 덕분에 일시적인 장애나 특정 노드의 문제에도 서비스 전체는 비교적 안정적으로 유지된다. 개발자는 liveness probe, readiness probe 등을 설정해 애플리케이션이 실제로 정상 동작하는지 판단하는 기준을 정할 수 있다.

트래픽이 많아지면 자동으로 Pod 수를 늘렸다가, 다시 줄어들면 Pod를 줄이는 "오토스케일링" 기능도 제공한다. 대표적으로 Horizontal Pod Autoscaler(HPA)는 CPU 사용률이나 사용자 정의 지표를 기준으로 replica 수를 동적으로 조정해 준다. 이를 적절히 활용하면, 피크 시간대에는 성능을 확보하고, 한가한 시간에는 비용을 줄이는 유연한 운영이 가능하다.

스토리지와 stateful 워크로드

컨테이너와 Pod는 기본적으로 일시적(휘발성)인 존재로 취급되며, 재시작 시 내부 데이터가 사라질 수 있다. 하지만 실제 서비스에서는 데이터베이스나 메시지 브로커처럼 상태를 유지해야 하는 애플리케이션도 많기 때문에, 쿠버네티스는 영구 스토리지를 지원하기 위한 여러 메커니즘을 제공한다.

가장 기본적인 개념은 "볼륨(Volume)"이며, 이는 Pod에 마운트해서 사용할 수 있는 스토리지 영역을 의미한다. 쿠버네티스는 클라우드 스토리지(예: 각 클라우드의 블록 스토리지), 네트워크 파일 시스템, 로컬 디스크 등 다양한 스토리지 백엔드를 추상화해 "PersistentVolume"이라는 형태로 제공한다. 개발자는 "PersistentVolumeClaim"을 통해 필요한 용량과 성능 수준을 선언하고, 쿠버네티스는 이에 맞는 실제 스토리스를 연결해 준다.

stateful 애플리케이션을 위해서는 "StatefulSet"이라는 리소스가 사용된다. StatefulSet은 각 Pod에 고유한 이름과 안정적인 네트워크 ID, 전용 스토리지를 보장해준다. 예를 들어 데이터베이스 클러스터처럼 각 노드가 일정한 순서와 ID를 필요로 하는 경우, Deployment 대신 StatefulSet을 사용하면 보다 안전하고 예측 가능한 배포가 가능하다.

네트워크 모델과 서비스 디스커버리

쿠버네티스 네트워크 모델의 기본 원칙은 "모든 Pod가 평면 네트워크 상에서 서로 직접 통신할 수 있어야 한다"는 것이다. 각 Pod는 고유한 IP를 부여받고, 별도의 NAT 없이 클러스터 내부에서 서로 접근할 수 있도록 구성된다. 이를 구현하기 위해 다양한 CNI(Container Network Interface) 플러그인들이 사용되며, 클러스터 관리자에게 선택의 여지가 주어진다.

Service는 클러스터 내에서 서비스 디스커버리를 가능하게 해주는 핵심 요소다. 각 Service는 고정된 가상 IP와 DNS 이름을 가지며, 그 뒤에 여러 Pod가 연결된다. 클라이언트는 Service 이름만 알고 있으면 되고, 실제 어떤 Pod 인스턴스가 트래픽을 처리하는지는 쿠버네티스가 로드밸런싱을 통해 알아서 결정한다.

클러스터 외부에서 접근해야 하는 경우에는 "NodePort", "LoadBalancer", 혹은 Ingress 리소스를 활용한다. NodePort는 각 노드의 특정 포트를 열어 Service에 연결하고, LoadBalancer는 클라우드 제공자가 지원하는 외부 로드밸런서를 자동으로 프로비저닝한다. Ingress는 HTTP/HTTPS 기반의 라우팅 규칙을 정의하여, 하나의 진입점으로 여러 서비스를 호스팅할 수 있게 해주며, 도메인 기반 라우팅과 TLS 종료 같은 기능을 제공한다.

보안과 권한 관리

쿠버네티스는 대규모 환경에서 여러 팀과 애플리케이션이 동시에 동작할 수 있기 때문에, 보안과 권한 관리가 매우 중요하다. 먼저 클러스터 리소스는 "네임스페이스(namespace)"로 구분하여 논리적으로 격리할 수 있다. 이를 통해 팀이나 프로젝트별로 공간을 나누어, 서로의 리소스를 간섭하지 않도록 할 수 있다.

"RBAC(Role-Based Access Control)"은 어떤 사용자가 어떤 네임스페이스에서 어떤 작업을 할 수 있는지를 세밀하게 통제하는 기능이다. 예를 들어 한 팀에게는 자기 네임스페이스의 Deployment 수정 권한만 주고, 클러스터 전체 설정에 대한 권한은 제한하는 식으로 역할을 나눌 수 있다. 또한 서비스 계정(ServiceAccount)을 이용해 애플리케이션이 클러스터 내부 API에 접근할 때 부여되는 권한을 최소화할 수 있다.

네트워크 보안 측면에서는 "네트워크 폴리시(NetworkPolicy)"를 통해 Pod 간 통신을 화이트리스트 방식으로 제어할 수 있다. 기본적으로는 모든 Pod가 서로 통신할 수 있지만, 네트워크 폴리시를 설정해 "이 서비스는 이 데이터베이스 Pod에만 접근 가능"과 같이 규칙을 정의함으로써, 내부 공격이나 잘못된 설정으로 인한 피해 범위를 제한할 수 있다.

쿠버네티스 생태계와 관련 도구

쿠버네티스가 널리 사용되면서, 이를 둘러싼 방대한 생태계가 형성되었다. 패키지 관리 측면에서는 "Helm"이 대표적이다. Helm은 쿠버네티스 애플리케이션을 차트(chart)라는 템플릿 형태로 관리하고 배포할 수 있게 해준다. 복잡한 애플리케이션도 여러 리소스를 하나의 패키지로 묶어 버전 관리와 재사용이 쉬워진다.

또한 모니터링과 로깅을 위한 도구들도 풍부하다. Prometheus는 메트릭 수집과 모니터링에 널리 사용되며, Grafana와 함께 대시보드를 구성해 클러스터 상태를 시각적으로 확인할 수 있다. 로그 수집에는 Loki, Elasticsearch, Fluentd/Fluent Bit 등을 활용해 중앙집중식 로그 시스템을 구축하는 사례가 많다.

서비스 메쉬(Service Mesh) 분야에서는 Istio, Linkerd 등이 대표적이다. 서비스 메쉬는 애플리케이션 코드 변경 없이도 서비스 간 통신에 대해 트래픽 제어, 관찰성, 보안을 강화하는 기능을 제공한다. 쿠버네티스는 이러한 도구들이 쉽게 통합될 수 있도록 표준 인터페이스와 확장 메커니즘을 제공하는 기반이 된다.

주요 사용 사례와 장단점

쿠버네티스는 특히 마이크로서비스 아키텍처를 도입한 조직에서 많이 사용된다. 서비스를 작은 단위로 쪼개어 독립적으로 배포·확장하고자 할 때, 쿠버네티스의 자동 배포, 롤링 업데이트, 오토스케일링 기능이 큰 효과를 발휘한다. 또한 여러 클라우드와 온프레미스 환경을 아우르는 하이브리드, 멀티 클라우드 전략을 구사할 때도 공통된 운영 플랫폼으로 활용하기 좋다.

반면, 쿠버네티스는 학습 곡선이 상당히 가파른 편이다. 개념과 리소스 종류가 많고, 네트워크와 스토리지, 보안 구성까지 고려해야 하기 때문에 작은 팀이나 단순한 서비스에는 과도한 복잡성이 될 수 있다. 또한 자체 클러스터를 직접 구축하고 운영하려면 인프라와 운영에 대한 높은 수준의 역량이 요구된다.

이러한 부담을 줄이기 위해, 주요 클라우드 업체들은 관리형 쿠버네티스 서비스(예: 각종 Managed Kubernetes)를 제공해, 컨트롤 플레인 운영과 업그레이드, 일부 노드 관리 등을 대신 맡아준다. 이를 활용하면 쿠버네티스의 이점을 누리면서도 운영 부담을 상당 부분 줄일 수 있지만, 여전히 애플리케이션 레벨의 구성과 보안, 모니터링 등은 사용자 책임으로 남는다.

도입 시 고려사항과 베스트 프랙티스

조직이 쿠버네티스를 도입할 때는 기술 자체보다 "왜 필요한가"를 먼저 명확히 하는 것이 중요하다. 배포 자동화, 마이크로서비스, 멀티 클라우드 같은 구체적인 목표가 있다면 쿠버네티스가 좋은 선택이 될 수 있지만, 단순히 "요즘 다 쓰니까"라는 이유라면 오버엔지니어링이 될 가능성이 크다.

초기에는 작은 범위부터 시작해, 비핵심 서비스나 내부 도구 등을 대상으로 파일럿 프로젝트를 진행해 보는 것이 일반적이다. 이를 통해 조직 내부에 쿠버네티스 경험과 운영 노하우를 축적하고, 점진적으로 범위를 넓혀 가는 접근이 안정적이다. 또한 "모든 것을 쿠버네티스 안으로" 가져오려 하기보다는, 컨테이너와 쿠버네티스에 적합한 워크로드와 그렇지 않은 워크로드를 구분하는 것도 현실적인 전략이다.

운영 단계에서는 선언형 구성과 GitOps 같은 패턴을 적극적으로 활용하는 것이 좋다. 모든 클러스터 설정과 애플리케이션 구성을 코드로 관리하고, 변경 사항은 반드시 리뷰와 자동 테스트를 거쳐 적용하는 프로세스를 갖추면, 복잡한 쿠버네티스 환경에서도 비교적 예측 가능하고 안정적인 운영이 가능해진다. 모니터링과 경보 시스템을 잘 구축해 클러스터와 애플리케이션 상태를 항상 추적하는 것도 필수적이다.

맺음말

쿠버네티스는 현대 클라우드 네이티브 환경에서 핵심적인 역할을 하는 플랫폼으로, 컨테이너 기반 애플리케이션의 배포와 운영을 표준화하고 자동화해 준다. 다만 강력한 기능만큼이나 개념과 구성 요소가 복잡하므로, 목적과 현재 조직의 역량을 잘 고려해 단계적으로 도입하는 것이 중요하다.

쿠버네티스를 이해하는 것은 단지 한 가지 도구를 배우는 것을 넘어, 인프라 운영을 선언형으로 관리하고, 자동화와 자가 복구, 확장성을 전제로 시스템을 설계하는 새로운 패러다임을 받아들이는 과정이기도 하다. 이러한 관점에서 쿠버네티스를 학습하면, 단기간의 유행을 넘어 장기적으로도 유용한 인프라 사고방식을 얻을 수 있다.