리액트 18 버전에서 서버 컴포넌트를 출시하고 뒤이어 NextJS가 프레임워크 차원에서 이를 지원하기 시작하면서 프론트엔드 개발 방식이 크게 변화하고 있습니다.
서버에서 리액트 컴포넌트를 실행하고, 서버 액션을 통해 데이터 뮤테이션을 진행하는 등 클라이언트에서 처리하던 많은 작업들이 서버로 이동할 수 있게 되었습니다.
이렇게 프론트엔드 애플리케이션의 서버 의존도가 높아지면서, 서버 인프라 관리 및 모니터링의 중요성도 커지고 있는데요. 프론트엔드 개발자에게도 어느정도의 인프라 지식이 요구되고 있습니다.
이번 포스팅은 이러한 배경을 바탕으로 현대 인프라의 핵심인 쿠버네티스의 주요 개념과 자주 쓰이는 용어에 대해 살펴보고 정리해보고자 합니다.
클라우드 환경에서 서비스되는 애플리케이션은 대부분 이미지의 형태로 빌드되며, 컨테이너에서 이 이미지를 실행하는 구조를 갖습니다. 쿠버네티스란 이렇게 컨테이너화된 애플리케이션을 자동으로 배포하고 확장 관리하는데 필요한 여러가지 요소들을 자동화해주는 오픈소스 플랫폼입니다.
서비스가 안정적인 품질로 운영되기 위해서는 트래픽에 따른 서버 자원의 유연한 스케일링, 주기적인 컨테이너 상태 체크, 서비스 불가 컨테이너 종료 및 재시작, 무중단 배포, 필요시 빠른 롤백 등 다양한 작업이 적기에 이뤄져야 합니다. 수백, 수천개 규모의 컨테이너가 운용되는 대규모의 서비스일수록 이런 모든 작업을 인프라 관리자가 수동으로 처리하기는 쉽지 않습니다.
이런 작업을 똑똑한 시스템이 알아서 처리해준다면 더 쉽고 편하게 안정적인 서비스 운영 환경을 유지할 수 있지 않을까요? 이것이 바로 쿠버네티스를 사용하는 이유입니다.
쿠버네티스는 컨테이너 환경에서 서비스를 탄력적이고 안정적으로 운영하기 위한 다양한 기능들을 제공합니다. 배포, 스케일링, 로드 밸런싱과 같은 기능을 제공하며 사용자는 로깅, 모니터링 및 알림 솔루션을 자유롭게 통합할 수 있습니다.
쿠버네티스의 동작 원리와 쿠버네티스를 구성하는 핵심 컴포넌트들에 대해 살펴보겠습니다.
쿠버네티스를 배포하게되면 클러스터가 생성됩니다. 클러스터는 컨트롤 플레인과 노드 플레인으로 구분됩니다.
컨트롤 플레인은 클러스터의 관리자 역할을 수행합니다. 클러스터 전체의 상태를 관리하며, 각 워커 노드의 상태를 모니터링하고 작업 스케줄링, 리소스 관리 등 클러스터의 모든 동작을 제어합니다.
노드 플레인은 애플리케이션이 실행되는 환경을 제공하는 워커 노드들이 존재하는 공간입니다. 컨테이너화된 애플리케이션은 워커 노드의 파드(Pod) 형태로 존재하게 됩니다. 사용자가 애플리케이션에 접속하여 서비스를 이용할 때 워커 노드에서 실행 중인 파드를 통해 통신이 이루어집니다.
정리하면 컨트롤 플레인은 워커 노드들을 관리하는 사령관 역할이며, 워커 노드는 애플리케이션 서비스를 유저에게 제공하는 역할을 담당합니다.
이어서 쿠버네티스를 이루는 핵심 컴포넌트들에 대해서 살펴보겠습니다.
파드는 쿠버네티스에서 가장 작은 배포 단위로, 컨테이너가 실행되는 공간입니다.
쿠버네티스는 이미지를 컨테이너 내부에서 실행시키는데요. 이 컨테이너는 단독으로 실행되는 것이 아니라 파드 내에서 실행됩니다. 일반적으로 하나의 파드는 하나의 컨테이너로 구성되지만, 필요에 따라 여러개의 컨테이너로 구성되기도 합니다.
파드들은 앞에서 살펴 본 워커 노드에 의해 관리됩니다. 동일한 역할을 하는 파드라도 환경에 따라 다른 노드에 배치될 수 있으며, 특정 노드가 죽으면 해당 노드의 파드들이 건강한 상태의 다른 노드로 옮겨지기도 합니다.
파드는 스케일링 과정에서 자주 생성되고 사라지며, 워커 노드의 상태에 따라 이 노드에서 저 노드로 옮겨지기도 합니다.
컨테이너화된 애플리케이션은 클러스터 내에서 여러 개의 파드(Pod)로 운영됩니다. 파드는 생성과 종료가 빈번하게 이루어지는 동적인 특성을 가지기 때문에, 동일한 역할을 하는 파드들을 하나의 네트워크 엔드포인트로 묶어 제공해야 합니다.
이를 담당하는 쿠버네티스 오브젝트가 바로 서비스(Service)입니다.
서비스는 내부적으로 실행 중인 파드에 안정적으로 접근할 수 있는 방법을 제공합니다. 동적으로 변하는 파드의 IP 주소 대신, 서비스는 고정된 네트워크 엔드포인트를 노출하여 클러스터 내부 또는 외부에서 파드에 접근할 수 있도록 해줍니다.
서비스 타입에는 Cluster IP, NodePort, LoadBalencer, ExternalName등 네가지가 있으나, 각 타입에 대한 설명은 이 글에 범위에 벗어나므로 다루지는 않습니다. 자세한 내용이 궁금하신 분들은 여기를 참조하세요.
쿠버네티스의 네트워크 트래픽은 외부에서부터 들어오는 인그레스 트래픽과, 내부에서 외부로 나가는 이그레스 트래픽으로 분류됩니다. 인그레스는 클러스터 외부 트래픽을 클러스터 내부의 서비스로 라우팅해주는 역할을 하는 오브젝트입니다.
쉽게 말하면 인그레스는 클러스터 외부에서 내부로 접근하는 요청들을 어떻게 처리할 지 정의해둔 규칙들의 모음, 라우팅 테이블이라 볼 수 있습니다.
애플리케이션에 접속하려는 유저는 인그레스를 거쳐 적절한 파드로 라우팅됩니다.
지금까지 파드, 서비스, 인그레스에 대해 살펴보았는데요. 이를 조합하면 유저가 애플리케이션을 사용하기 위해 거쳐야하는 경로를 완성할 수 있습니다.
인그레스를 통해 들어온 트래픽은 서비스를 거쳐 내부 파드로 연결됩니다. 각 파드는 트래픽을 받아 유저에게 애플리케이션 서비스를 제공합니다.
실제 운영 환경에서는 트래픽 부하를 분산하고, 서비스의 안정성을 높이기 위해서 하나의 애플리케이션을 여러 개의 파드로 운영합니다. 각 파드를 하나 하나 직접 생성할수도 있지만, 이렇게 여러 개의 파드를 수동으로 생성하고 관리하는 것은 공수도 많이 들고 실수하기도 쉽습니다.
디플로이먼트는 파드 생성 및 관리를 쉽게 진행할 수 있게 도와주는 오브젝트입니다. 개발자는 애플리케이션의 desired state(원하는 상태)를 선언적으로 정의할 수 있습니다.
예를 들어 "이 애플리케이션은 항상 3개의 파드로 운영되어야 하며, 각 파드는 이 특정 버전의 이미지를 사용해야 한다"라고 정의하면, 쿠버네티스는 자동으로 이 상태를 유지하기 위해 필요한 작업들을 수행합니다.
디플로이먼트를 통해 정의된 상태를 바탕으로 쿠버네티스는 자동으로 필요한 작업(파드 생성, 업데이트, 복구 등)을 수행하여 선언된 상태를 유지합니다.
디플로이먼트는 주로 yaml 파일로 설정합니다. 개발자는 YAML 파일에 원하는 상태를 정의한 후, kubectl 명령어를 사용해 컨트롤 플레인에 전달합니다. 컨트롤 플레인은 이를 실행하여 정의된 상태를 유지하도록 관리합니다.
이번 포스팅에서는 쿠버네티스와 쿠버네티스를 구성하는 주요 컴포넌트에 대해 간단하게 살펴보았습니다. 오늘 포스팅에서 다룬 개념들 외에도 네임스페이스, 레플리카셋, 시크릿, 컨피그맵 등 다루지 못한 개념들이 많습니다.
쿠버네티스는 기능이 많고 각 개념 또한 방대하기 때문에 모든 내용을 한 번에 이해하기는 어렵습니다. 사실 저 역시 이 글을 작성하는 현재 시점에서 모르는 부분이 많습니다.(갑자기 고해성사?)
사실 실무에서는 개념적인 요소들 보다는 파드의 상태, 서버 리소스(메모리, CPU 등)를 모니터링 해야하는 경우가 많은데요. 해당 내용을 다루지 못한것이 좀 아쉬워서, 다음 포스팅에는 실제로 클라우드 환경에서 자원들을 만들어보고 모니터링 환경을 구축해보는 과정을 다뤄볼까 합니다. 언제가 될진 모르겠지만요ㅎㅎ;;
그럼 오늘도 긴 글 읽어주셔서 감사합니다.
https://kubernetes.io/ko/docs/home/
https://kubetm.github.io/k8s/
https://seongjin.me/kubernetes-pods/
https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS5675948724
https://bumday.tistory.com/165