쿠버네티스 이해하기: watcher vs informer

Kubernetes는 다양한 환경에서 컨테이너화된 애플리케이션을 관리하기 위한 오케스트레이션 표준으로 자리 잡았는데, 그 이유 중 하나는 애플리케이션이 클러스터 내의 상태 변화에 동적으로 대응할 수 있기 때문이다. 이러한 기능을 가능하게 하는 두 가지 핵심 개념은 와처와 인포머이다. 이 포스팅에서는 이 두 개념이 무엇인지, 어떻게 다른지, 언제 두 개념을 함께 사용해야 하는지에 

Watcher: 쿠버네티스의 실시간 알림

쿠버네티스의 와처는 즉각성과 실시간 업데이트에 관한 것이다. 쿠버네티스 리소스에서 와처를 사용하면 애플리케이션이 쿠버네티스 API 서버와 직접 통신을 연결한다. 이는 파드, 서비스 또는 디플로이먼트와 같은 특정 리소스 유형에 대한 업데이트를 subscribe하는 것과 비슷하다.

작동 방식은 다음과 같다. 쿠버네티스 API 서버는 클라이언트에 대해 HTTP 연결을 열어둔다. 파드, 서비스와 같은 지정된 리소스가 변경될 때마다 해당 리소스의 현재 상태를 나타내는 새 JSON 객체를 보낸다. 이는 변경 사항이 발생할 때 애플리케이션에 정보를 제공하는 간단하고 직관적인 방법이다.

하지만 단순하고 저순준 api인 만큼 대가가 따른다. 와처는 상태를 유지하지 않고 단지 변경 사항을 알리기만 한다. 애플리케이션에서 전체 컨텍스트를 이해하거나 변경 이력을 유지해야 하는 경우 직접 컴포넌트 개발이 

Informer: 정교하고 상태가 저장된 감시

인포머는 와처를 한 단계 더 발전시킨 것이다. 이는 쿠버네티스의 고 패키지인 ’client-go’의 일부이며 쿠버네티스 API와 상호 작용하는 보다 정교한 방법을 제공한다. 인포머는  와처를 아래 추가 기능들로 감싼다.

1. 캐싱: 인포머는 감시하는 리소스의 로컬 캐시를 유지하여 애플리케이션이 API 서버에 접속하지 않고도 리소스의 현재 상태를 쿼리할 수 있도록 한다.

2. 인덱싱: 이 기능을 사용하면 특정 리소스 필드를 기반으로 빠른 조회 및 목록 작업을 수행할 수 있다.

3. 이벤트 핸들러: 핸들러를 사용하면 다양한 유형의 리소스 변경(추가, 업데이트, 삭제)에 대한 이벤트 핸들러를 등록하여 변경에 구조적으로 대응할 수 있다.

4. 복원력: API 서버의 감시가 중단될 경우 자동으로 재연결 로직을 처리하여 서버 상태의 일관성을 보장한다.

인포머의 로컬 캐시는 특히 강력합니다. 이 캐시는 API 서버의 이벤트 스트림을 수신하여 로컬 저장소를 업데이트하고 클러스터의 현재 상태와 항상 동기화되도록 한다. 그런 다음 이 캐시는 애플리케이션에 대한 단일 데이터 소스 역할을 하게 된다. 와처에 비해 불필요한 요청과 리소스를 많이 줄게 된다.

파이썬으로 작성된 와처 예시를 보면 와처는 스트림으로 이벤트를 계속 받는다. 이후 이벤트 타입에 따라 다르게 처리되는데 캐싱, 이벤트 핸들러등은 직접 구현하여야 한다.

인포머는 와처에서 추상화 레이어가 추가된 고수준 기능이다. 로컬 캐싱 메커니즘, 정교한 이벤트 핸들러, 운영 내부에 복원력과 같은 기능이 추가되었다. 인포머는 변경 사항을 알려줄 뿐만 아니라 리소스의 현재 상태를 유지하고, 재연결을 처리하며, 복잡한 이벤트 중심 로직에 집중할 수 있게 한다.

인포머의 예시 코드를 보자. 인포머는 파이썬 kubernetes 패키지엔 없고, 고 패키지에 구현되어 있다. 인포머의 경우, 인포머 자체에서 처리해주는 것들이 있기 때문에 사용자는 단순히 이벤트 핸들러에만 집중할 수 있고, 와처에 비해 효율적이다.

와처와 인포머를 사용할 경우

와처를 사용할지 인포머를 사용할지는 어플리케이션의 복잡성에 따라 결정된다.

와처를 사용하는 경우:

  - 변경 사항에 대한 응답으로 간단한 작업을 수행해야 하는 경우.

  - 애플리케이션이 상태를 유지할 필요가 없는 경우.

  - 적은 수의 리소스를 관리하고 있는 경우.

인포머를 사용해야 하는 경우:

  - 리소스의 로컬 상태를 최신 상태로 유지해야 하는 경우.

  - 리소스를 관리하거나 모니터링하는 컨트롤러 또는 운영자를 구축하는 경우.

  - 이벤트 핸들러를 사용하여 이벤트를 보다 체계적으로 처리하려는 경우.

  - 복원력 및 자동 재연결 처리가 필요한 경우

요약

와처는 좋아하는 프로그램이 새 에피소드를 공개하는 순간 알려주는 기본 구독 서비스와 같다. 반면, 인포머는 알림뿐만 아니라 에피소드를 녹화하고, 출연진에 대한 추가 정보를 제공하며, 연결이 끊어질 경우 중단한 지점부터 재생을 재개하는 프리미엄 서비스와 비슷하다.

쿠버네티스를 기반으로 구축하는 개발자에게는 와처와 인포머 중 어느 쪽을 이해하고 선택하는 것이 중요하다. 올바른 선택은 코드를 간소화하고, 성능을 개선하며, 동적 컨테이너 환경에서 애플리케이션이 필요한 만큼의 응답성과 복원력을 갖도록 보장할 수 있다.

cgroup 드라이버 이해하기: systemd, cgroupfs, 그리고 쿠버네티스 관점으로

소개

쿠버네티스와 같은 컨테이너 오케스트레이션 기술에서 효율적인 리소스 관리는 컨테이너화된 애플리케이션의 원활한 운영을 보장하는 핵심 기술이다. 클러스터가 커지고 워크로드가 변동함에 따라 리소스 사용량을 할당, 추적, 제한하는 기능은 필수적인 기능이다. 바로 이 부분에서 control group(cgroup)은 기본 메커니즘으로 프로세스 그룹 간에 시스템 리소스를 격리하고 관리할 수 있게 해준다. cgroup 드라이버는 이러한 cgroups의 추상화를 더욱 세밀하게 조절하여 시스템의 리소스 할당을 관리하는 구조화된 방식을 제공한다.

cgroup 드라이버, 특히 systemd 및 cgroupfs는 리소스 할당을 조율하는 지휘자 역할을 하며, 각각 고유한 접근 방식과 기능을 가지고 있다. 이 두 드라이버는 모두 쿠버네티스와 같은 최신 컨테이너 오케스트레이션 시스템의 효율적인 기능에 필수적인 요소이지만, 컨테이너 오케스트레이션의 리소스 관리 메커니즘을 자세히 알아보려면 개별적으로, 쿠버네티스와 함께 작동하는 방식의 미묘한 차이를 이해하는 것이 중요하다.

이 포스트에서는 cgroup 드라이버의 레이어를 살펴보고, systemd와 cgroupfs에 대해 간단하게 알아보고, Kubernetes 환경 내에서 이들 간의 상호 작용을 본다. 그 다음 systemd, cgroupfs 및 Kubernetes에서의 역할에 보고, 컨테이너화된 애플리케이션의 최적화된 운영에 필수적인 일관된 리소스 관리 프레임워크를 형성하기 위해 이러한 구성 요소가 어떻게 결합되는지 이해한다.

cgroup 드라이버

control group(cgroup)은 Linux 커널의 핵심 기능으로, 프로세스를 그룹화하고 리소스 할당을 체계적으로 관리할 수 있는 메커니즘을 제공한다. cgroup은 서로 다른 프로세스 그룹 간에 CPU, 메모리 및 디스크 I/O와 같은 리소스를 격리하고 제어할 수 있도록 하여 시스템의 전반적인 안정성과 성능을 유지하며, 각 프로세스가 리소스를 할당받을 수 있도록 한다. 지금의 컨테이너 기술이 있게 한 가장 핵심적인 기술이다.

리눅스 시스템과 cgroup 사이에서 중개자 역할을 하는 cgroup 드라이버는 이러한 리소스 관리를 조율하는데 중요한 역할을 한다. 이 드라이버는 cgroup을 생성, 구성 및 삭제하여 시스템에서 리소스가 할당되고 관리되는 방식에 대한 구조화된 접근 방식을 제공한다.

cgroup 드라이버에는 systemd와 cgroupfs의 두 가지 기본 드라이버가 있으며, 각 드라이버는 cgroup 관리에 대한 서로 다른 접근 방식을 가진다. systemd와 cgroupfs 중 어떤 것을 선택할지는 배포할 환경의 특정 요구 사항으로 결정되는 경우가 많다. 

요약하면, systemd는 보다 통합적인 접근 방식을 제공하는 반면, cgroupfs는 cgroup 관리를 위한 전용의 간단한 인터페이스를 제공한다.

cgroup 그리고 cgroupV2

cgroupV2는 cgroup의 단점들을 해결하려는 업데이트 버전이다. 2016년부터 개발되어 왔으며 커널 4.5부터 도입되었는데 우분투에서는 21.10부터 기본값으로 cgroupV2를 쓴다고 한다. 하지만 현재 대부분의 리눅스 버전에서 이미 cgroupV2를 쓰고 있을 확률이 높다.

cgroupV2는 통합 계층 구조를 적용하여 복잡성과 잠재적 혼란을 줄였다. 이 통합 계층 구조는 리소스 관리를 간소화하고 다양한 프로세스 그룹에 리소스를 할당할 때 일관된 접근 방식을 보장한다. 간단하게 cgroup보다 훨씬 간결한 리소스 관리 인터페이스를 제공한다. 이외에 PSI(압력 스톨 정보) 및 IO 비용 모델과 같은 향상된 기능, 간소화된 구성, 향상된 리소스 제어 메커니즘, 최신 컨테이너화된 워크로드 및 쿠버네티스와 같은 오케스트레이션 시스템과의 더 나은 연계 등 몇 가지 주목할 만한 개선된 것들도 많다.

systemd

UNIX와 유사한 운영 체제의 주요 초기 시스템인 systemd는 부팅 프로세스와 서비스 관리 방식을 크게 간소화하는 기술이다. systemd는 초기화 시스템으로서의 역할 외에도 시스템 리소스를 관리하기 위해 cgroup 기능과 복잡하게 얽혀 있으며, 프로세스 수명 주기 관리와 리소스 할당 간의 공생 관계를 보여준다.

cgroup 관리를 systemd에 통합하는 것은 시스템 초기화 및 리소스 관리에 대한 총체적인 접근 방식이다. 이러한 통합 접근 방식은 효율적이기는 하지만, 컨테이너를 사용하기 이전 베어메탈 서버를 그대로 쓰던 시절에 간단하게 많이 쓰였지만, 특히 쿠버네티스와 같은 복잡한 오케스트레이션 환경에서 효과적으로 활용하기 위해서는 미묘한 이해가 필요하다.

cgroupfs

cgroupfs는 systemd와 뚜렷한 대조를 이루며, cgroup 관리에만 집중하는 전용 cgroup 드라이버를 구현한다. 가상 파일 시스템을 추상화하여 리소스 할당을 관리하기 위한 단순하고 직관적인 인터페이스를 제공한다. 이러한 추상화를 통해 관리자와 도구가 cgroup과 상호 작용하는 인터페이스를 보다 간단하고 이해하기 쉽게 만들 수 있다. 단순히 cat FILE, echo 1 >> FILE 과 같은 파일 시스템을 수정하는 것만으로 cgroup을 관리 할 수 있는 것이다.

독립적으로 운영하든 쿠버네티스와 같은 컨테이너 오케스트레이션 환경 내에서 운영하든, cgroupfs는 효과적이고 간단한 cgroup 관리를 위한 방법이다

쿠버네티스에 통합

쿠버네티스가 cgroup 드라이버로 systemd와 cgroupfs를 모두 지원한다는 것은 다양한 리소스 관리 패러다임에 부합하는 플랫폼의 유연성을 보여준다. cgroup 드라이버는 쿠버네티스가 노드와 파드 전반에서 리소스를 관리하는 방식에서 중요한 역할을 하며 리소스 할당, 격리, 모니터링 및 서비스 품질(QoS) 적용에 영향을 미친다.

쿠버네티스의 원활한 운영을 위해서는 쿠버네티스, 선택한 cgroup 드라이버, 컨테이너 런타임 간의 호환성이 매우 중요하다. 쿠버네티스 역시 1.25부터 cgroup V2를 정식 지원한다. cgroupV2 지원으로의 전환은 리소스 관리를 더욱 세분화하여 리소스 사용량을 더욱 정밀하게 제어하고 모니터링할 수 있을 것으로 기대된다.

결론

systemd, cgroupfs, 그리고 이 둘의 쿠버네티스와의 상호 작용을 통해 컨테이너 오케스트레이션의 리소스 관리 메커니즘 대략적으로 이해했다. 이러한 이해는 리소스 효율성과 애플리케이션 성능 간의 균형을 보장하여 강력하고 탄력적인 컨테이너 오케스트레이션 에코시스템을 조성함으로써 Kubernetes 환경을 최적화하려는 모든 사람에게 중요한 역할을 할 것이다.

참조

https://kubernetes.io/docs/setup/production-environment/container-runtimes/#cgroup-drivers

https://kubernetes.io/docs/concepts/architecture/cgroups/

https://kubernetes.io/blog/2022/08/31/cgroupv2-ga-1-25/

GPU 쿠버네티스 클러스터 on legacy infra (금융, 전금법 도메인)

쿠버네티스는 이제 IT 인프라에서는 많이 적용되고 관심도도 많은 도구이다. 자원을 효율적으로 사용할 수도 있고 장애 발생시 대처도 비교적 쉬워진다. 하지만 많은 곳에서는 여전히 기존 방식의 인프라를 활용하고 있는데, 대부분의 금융권 기업들이 그렇다. 기존 방식의 인프라에서는 서버 한대에 어플리케이션이 직접 띄워지는 방식으로 돌아간다. 그래서 서버에서 돌아갈 어플리케이션이 어느정도의 cpu, ram, storage를 사용할지 예상해야 서버 스펙을 산정하고 구매에 들어간다. 이 과정에서 기능이 변경/추가되거나 예상치 못한 스토리지 사용이 있다면 스펙을 다시 산정하고 적용에 들어가야 한다. 물론 배포, 백업 과정도 번거롭다. 개발자나 인프라를 관리하는 입장에서는 부가적인 일이 상당히 많아지는 것이다.

그래서 최근 legacy한 인프라를 운영중인 곳에서도 쿠버네티스를 도입하기 시작하고 있다. 나도 실험적으로 금융권 기업에 쿠버네티스 클러스터를 구성하면서 이들이 쿠버네티스를 준비하고 있는 것들을 좀 보았다. 나는 기존의 인프라 운영방식 위에 gpu 클러스터를 구성한 것이라 꽤 많은 애로사항이 있었고 그 과정을 여기에 정리하려 한다.

패키지

클러스터가 구성되기 위해서는 클러스터를 구성하는 기본적인 패키지들이 설치되어야 한다. 대부분의 금융권에서는 레드햇 계열의 OS를 사용하는데 대부분 os 패키지를 설치되어있는 버전 그대로 사용한다. (OS 패키지조차 업그레이드 할 수 있는 환경이 아닌 곳이 많다.) 여기서 초반에 많은 삽질을 하게 된다. 

레드햇에서 제공하는 podman을 쓴다면 crio, containerd 와 같은 cri 구현체를 따로 설치해야하고 podman 삭제 후 docker를 설치해도 된다. 여기서 주의해야할 부분은 cri 구현체는 대부분 runc를 사용하는데 runc 버전과 cri 구현체의 호환성이 맞아야하고 그러면 또 커널버전까지 맞춰야할 수도 있다는 점. 그리고 podman과 docker는 한 서버에 공존할 수 없다는 것이다. 이 패키지 버전을 맞추는 과정에서 수많은 하위 패키지들이 필요하고 디펜던시 맞추는 것이 고생스럽다. 이게 모두 yum install 이 안되기 때문에 벌어진다.

gpu 드라이버

gpu 클러스터를 사용하기 위해 gpu 드라이버를 설치하는데 온라인에선 쉽지만 오프라인에선 몇번의 삽질이 필요하다. OS 개발용 패키지들이 필요하고 gpu에 맞는 드라이버 버전을 선별해야 한다.

네트워크

나에겐 가장 어려웠던 부분이다. HA를 위해 L4를 세팅하고 kubeadm 에 설정해서 클러스터를 구성한다. 문제는 cni인데 cni 설정은 인프라 네트워크 환경에 많이 달려있다. 다른 서비스와 충돌하지 않는 ip 대역 설정이 있고 OS 에 따른 iptable 문제가 있다. (rhel8부터는 iptables을 사용하지 않고 nfttables라는 것을 사용한다.) 그리고 네트워크 인프라에서 ip-in-ip 프로토콜을 지원하지 않는다면 cni의 세팅도 역시 바뀌어야 한다. 이 부분이 특히 해결하는데 어려웠다. 네트워크 인프라 쪽에서는 쿠버네티스에 대해 모르다보니 도움 받을 수 있는 건 없었고 블랙박스 환경에서 문제가 뭔지 찾아야 했다.

레지스트리

오프라인 환경에서 가장 큰 문제는 컨테이너 이미지를 받을 수 없다는 것이다. 내가 구성했던 클러스터들에서는 usb로 컨테이너 이미지를 다 옮기고 이미지를 로드해서 쿠버네티스 구성과 서비스를 띄운다. 이런 사용방식에서 과연 쿠버네티스가 맞는 솔루션인지는 모르겠지만 말이다. 노드에 이미지가 로드되어 있지 않다면 애초에 그냥 파드를 띄울 수가 없다.

레지스트리의 필요성은 이것말고도 더 있다. 쿠버네티스를 관리하는 kubelet은 노드에 대해서 많은 역할을 하고 있는데 스토리지를 비우는 것도 한다. 만약 노드에 스토리지가 특정 퍼센트 이상 사용되면 kubelet은 바로 지워도 되는 것들을 삭제한다. 그 중 가장 첫번째는 사용하지 않는 컨테이너 이미지다. 일반적으로 컨테이너 이미지는 다시 당겨오면 되기 때문에 사용되지 않고 있다면 삭제해도 아무런 문제가 되지 않는다. 하지만 이 레거시 인프라에서는 다시 당겨올 수 있는 레지스트리가 없다. 이미지가 한번 삭제되면 다시 usb로 이미지를 옮겨서 직접 로드해야 한다. 그래서 서비스 운영시에 kubelet이 노드 스토리지를 확보하기 위해 이미지를 삭제하면 상당히 큰 장애가 발생하는 것이다.

마무리

그래서 레거시 인프라에서 클러스터를 구성하기 위해 필요한 것을 정리해보면 아래 것들이다.

  1. 호환성이 맞는 os, cri,  driver 패키지 리스트

  2. 드라이버 설치를 위해 필요한 패키지, 드라이버 설치 파일

  3. OS 버전과 네트워크 환경 체크

  4. 레지스트리 서비스 확보

물론 이것들을 확보했다고 해서 레거시 인프라에서 클러스터를 원활하게 운영할 수 있는 것은 아니다. 단지 최소한의 세팅이 된 것이다. 나머지는 클러스터를 더 운영해보고 서비스를 올려보며 경험해보고 추가할 예정이다.