[번역] 쿠버네티스와 Erlang VM

posted by donghyun

last updated

8 min read

태그

원문 Kubernetes and the Erlang VM: orchestration on the large and the small

쿠버네티스와 얼랭 VM, 큰 규모와 작은 규모의 orchestration

쿠버네티스의 기능들을 살펴보고 Elixir와 같은 Erlang VM에서 실행되는 언어들과 비교하면 “자가 치유”, “수평 확장”, “분산처리” 같은 많은 키워드들을 공유한다는 인상을 받을 수 있습니다.

이러한 공유는 종종 혼란을 초래합니다. 그들은 뚜렷이 분리됩니까? 혹시 중복되나요? 예를 들어 쿠버네티스가 자가 치유를 제공한다면 Elixir의 fault tolerance에 어떤 목적이 있습니까?

이 아티클에서는 이러한 주제중 많은 부분을 살펴보고 대체로 보완적인 방법을 보여주고 겹치는 드문 경우에 대해 논의해볼 것입니다.

자가치유

쿠버네티스는 실패한 컨테이너를 자동으로 다시 시작하거나 교체합니다. 또한 사용자 정의된 헬스 체크에 응답하지 않는 컨테이너를 종료 할 수도 있습니다. 마찬가지로 Erlang 및 Elixir에서는 Supervisor의 도움을 받아 코드를 구조화하여 실패시 애플리케이션의 일부를 자동으로 다시 시작합니다.

쿠버네티스는 fault-tolerance를 클러스터 내에서 제공하고 Erlang/Elixir는 애플리케이션 내에서 이를 제공합니다. 이를 더 잘 이해하기 위해 데이터베이스(또는 다른 외부 시스템)와 통신해야하는 애플리케이션을 살펴 보겠습니다. 대부분의 언어는 데이터베이스 connection pool을 유지하여 이를 처리합니다.

잘못된 구성이나 하드웨어 오류로 인해 데이터베이스가 오프라인 상태가되면 데이터베이스와 Erlang / Elixir시스템 모두 헬스 체크에 부정 응답하여 결국 쿠버네티스가 동작하여 잠재적으로 그들을 재배치 합니다. 이것은 node-wide 오류이며 쿠버네티스의 도움을 받게됩니다.

그러나 데이터베이스 연결의 일부가 산발적으로 실패하면 어떻게 됩니까? 예를 들어 시스템에 부하가 걸리고 갑자기 MySQL의 prepared statement limit과 같은 연결 제한에 도달하기 시작했다고 가정해봅시다. 이 실패로 인해 헬스 체크가 실패하지는 않지만 많은 연결 중 하나가 해당 제한에 도달할 때마다 코드가 실패합니다. 현재의 애플리케이션에서 이 오류를 추론할 수 있습니까? 잘못된 연결이 끊어질 것이라고 자신있게 말할 수 있습니까? 결함있는 연결 대신 다른 연결이 시작될까요? 이 오류가 애플리케이션에서 연속적으로 발생하여 나머지 연결 풀이 다운되지 않는다고 편안하게 말할 수 있겠습니까?

fault-tolerance를 위한 Erlang/Elixir의 추상화를 통해 언어 수준에서 이러한 질문에 대해 추론할 수 있습니다. 그것은 연결, 리소스, 메모리 내 상태, 백그라운드 워커 등에 대해 추론할 수 있는 메커니즘을 제공합니다. 당신은 시작 방법, 종료 방법, 뭔가 잘못되었을 때 어떻게 해야하는지 명시적으로 말할 수 있습니다. 이러한 기능들은 부분적인 오류가 발생하는 경우에도 매우 유용할 수 있습니다. 예를 들어 뉴스 웹사이트가 있고 실시간 주식시세 표시기가 다운되었다고 가정해보겠습니다. 웹사이트가 계속 실행되어 잠재적으로 오래된 데이터를 제공해야합니까, 아니면 모든 것이 중단되어야 합니까? Erlang/Elixir에서 제공하는 멘탈 모델을 통해 이러한 시나리오를 추론할 수 있습니다. 물론 몇번의 재시도 후 또는 심지어 즉시 오류가 bubble up 하도록 만들어 그것을 k8s에서 처리할 수 있는 node-wide 오류가 되게 할 수 있습니다.

간단히 말해서 쿠버네티스와 컨테이너는 개별 노드가 실패할 때 개별 노드를 다시 시작할 수 있는 기능과 격리를 제공하지만 선택한 언어에 관계없이 자체 소프트웨어 내에서 격리 및 오류 처리를 대체하지는 않습니다. k8s와 Erlang/Elixir를 사용하면 대규모 (클러스터) 및 소규모 (언어/인스턴스) 에서 유사한 자가치유 및 fault-tolerance 원칙을 적용할 수 있습니다.

서비스 검색(discovery)와 분산형 Erlang

Erlang VM은 또한 분산형 Erlang을 제공하여 동일하거나 다른 컴퓨터에서 실행되는 다른 인스턴스간에 메시지를 교환할 수 있습니다. Elixir에서는 다음과 같이 쉽죠.

for node <- Node.list() do
  # Send :hello_world message to named process "MyProcess" in each node
  send {node, MyProcess}, :hello_world
end

분산 모드에서 실행할 때 (어떤 방식으로든 필요조건이 아니며 명시적으로 활성화해야함) Erlang VM은 데이터를 자동으로 직렬화 및 역직렬화하고 노드간의 연결이 활성 상태인지 확인하지만, 노드 검색(discovery)는 제공하지 않습니다. 각 노드의 위치를 정확히 말하고 노드를 함께 연결하는 것은 프로그래머의 책임입니다.

운 좋게도 쿠버네티스는 즉시 사용가능한 서비스 검색(discovery)를 제공합니다. 즉 k8s를 사용하면 노드 discovery를 완전히 자동화할 수 있는 반면 사용 않으면 수동으로 해야하고 오류가 나기 쉽습니다. Libcluster와 같은 라이브러리는 이를 정확히 수행합니다(그리고 직접 롤링하는것도 복잡하지 않습니다). 이것은 쿠버네티스와 얼랭 VM이 서로를 보완하는 또다른 좋은 예입니다!

하지만 여전히 궁금할 수 있는건 쿠버네티스의 Service Discovery를 사용하여 시스템이 서로 통신하는 것이 상대적으로 쉬워졌는데 분산형 Erlang을 실행하면 어떤 이점이 있습니까? 특히 Thrift, gRPC 등과 같은 RPC프로토콜을 고려할 때 말이죠?

서로다른 언어와 서로 통신하는 시스템에 대해 이야기 할 때 기존 RPC 메커니즘 중 하나를 선택하는 것이 최선의 선택일 가능성이 높으며 Erlang / Elixir에서도 잘 들어맞습니다. 제 생각에 Erlang VM이 실제로 빛나는 시나리오는 동일한 컨테이너의 여러 배포가 있고 그들이 서로 정보를 교환하는 경우입니다. 예를 들어 같은 채팅방, 같은 도시 구역, 또는 같은 산악 트랙에 있는 사용자를 추적하려는 실시간 애플리케이션을 구축한다고 가정해봅시다. 사용자가 연결하고 연결끊고 노드가 작동하거나 중지되면, 클러스터를 주의깊게 관찰하여 토폴로지 변경 사항을 확인하면서 데이터베이스를 업데이트하거나 복잡한 RPC 메커니즘을 통해 통신할 수 있습니다.

Erlang VM을 사용하면 모든 것이 VM에서 제공되므로 직렬화 프로토콜, 연결 관리 등에 대해 걱정할 필요없이 이 정보를 직접 브로드캐스트하거나 교환 할 수 있습니다. 이것은 피닉스가 분산 웹 실시간 시스템을 쉽게 구축 할 수 있도록 하는 많은 기능 중 하나입니다.

자동화된 롤아웃 vs 핫 코드 스와핑

배포와 관련하여 쿠버네티스는 모든 인스턴스를 동시에 변경하지 않도록 애플리케이션 또는 해당 구성에 대한 변경 사항을 자동으로 롤아웃합니다. 한편, Erlang VM은 핫 코드 스와핑을 지원하므로 해당 인스턴스를 종료하지 않고 단일 인스턴스 내에서 프로덕션에서 실행중인 코드를 변경할 수 있습니다.

이 두가지 배포 기술은 분명히 상충됩니다. 사실, 핫 코드 스와핑은 일반적으로 불변 컨테이너의 전체 아이디어와 잘 어울리지 않습니다. 이건 쿠버네티스와 Erlang VM이 적합하지 않다는 의미일까요? 아닙니다. 왜냐하면 당신은 핫 코드 스와핑 할 필요 없기 때문입니다. 사실 대부분의 사람들에게 해당합니다. 대부분의 Elixir 애플리케이션은 blue-green, canary 같은 기술을 사용하여 배포됩니다.

핫 코드 스와핑에 대한 진실은 실전에서 실행하는 것이 꽤 복잡하다는 것입니다. 다시 한 번 데이터베이스를 예로 들어보겠습니다. 새 버전의 소프트웨어를 배포할 때 데이터베이스를 업데이트 할 때마다 파괴적인 변경을 수행해서는 안됩니다. 예를 들어 칼럼명을 바꾸려면 새 칼럼을 추가하고 데이터를 마이그레이션 한 다음 컬럼을 제거해야 합니다. 칼럼의 이름을 변경하면 롤아웃을 수행 할 때마다 오류가 발생합니다. 두 버전의 소프트웨어가 동시에 실행되기 때문입니다(하나는 이전 열을 사용하고 다른 하나는 새 열을 사용함). 핫 코드 스와핑에서는 애플리케이션 내부의 모든 상태에 적용된다는 점을 제외하면 정확히 동일한 문제가 있습니다. 핫 코드 스와핑을 사용하는 회사들은 종종 소프트웨어 개발 시간만큼 업그레이드를 테스트하는 시간을 쓴다고 얘기합니다.

물론 핫 코드 스와핑이 쓸모 없다는 의미는 아닙니다. Erlang VM 개발은 대부분 비즈니스 요구에 의해 주도되며 핫 코드 스와핑에 대한 정당한 요구가 있었습니다. 특히 telephone switch를 구축할 때 업데이트는 위해 인스턴스를 종료할 적절한 순간이 존재하지 않습니다. 시스템은 언제라도 며칠 또는 몇 주와 같은 장기 running connection으로 가득차 있기 때문입니다. 따라서 라이브 시스템 업그레이드를 할 수 있다는것은 매우 유용합니다. 비슷한 요구 사항이 있는 경우 핫 코드 스와핑이 옵션이 될 수 있습니다. 또 다른 옵션은 배포시 더 스마트 한 클라이언트를 사용하고 노드간에 클라이언트 연결을 마이그레이션하는 것입니다.

핫 코드 스와핑은 개발 중에 서버를 다시 시작할 필요없이 라이브 코드 로딩을 제공하거나 전체 인스턴스를 교체 할 필요가 없는 프로덕션의 더 작은 구성 요소를 교체하는 등의 다른 상황에서도 사용할 수 있습니다.

구성 관리 및 구성 공급자

Elixir와 쿠버네티스에서 제공하는 또 다른 기능은 구성 관리입니다. 그러나 이전에 보았듯이 그 둘은 서로 극명히 다른 레벨에서 동작합니다. Elixir는 애플리케이션 구성을 위한 통합 API를 제공하지만 상대적으로 낮은 수준입니다. 프로덕션 시스템에서는 쿠버네티스에서 제공하는 것과 같은 상위 수준 도구로 Configuration과 Secret을 모두 관리하는 경우가 많습니다. 다행히 구성 공급자의 도움을 받아 상기 구성 도구를 배포 워크플로우에 통합할 수 있습니다. 이 기능은 Elixir 릴리즈의 일부로, 공식적으로 버전 1.9에서 포함되었습니다.

주의: 팟 리소스

쿠버네티스로 Erlang및 Elixir를 프로비저닝 할 때 특정 구성에 주의를 기울일 필요가 있습니다. 바로 팟 리소스들입니다.

다른 기술을 사용할 때, 큰 노드를 여러 개의 작은 팟 / 컨테이너로 나누는게 일반적입니다. 예를 들어 코어가 8개인 노드 하나가 있는경우 각 CPU의 절반씩을 하나의 팟에 할당하고 메모리를 균등하게 분배해 총 16개의 pod을 가질 수 있습니다.

이 접근 방식은 CPU와 I/O concurrency를 동시에 이용할 수 없는 많은 기술에서 의미가 있습니다. 하지만 Erlang VM은 시스템 리소스 관리에 탁월하며 큰 pod을 Erlang및 Elixir 응용 프로그램에 할당하는 것이 여러 개의 작은 pod으로 분할하는 것보다 여러분의 시스템은 더 효율적일것입니다.

Erlang VM이 다른 응용 프로그램과 머신을 공유하는 경우 busy waiting을 줄이는 것을 고려하고자 할 수 있습니다. 이렇게하면 VM이 CPU 사용량을 낮추도록 최적화하여 더 나은 이웃이 되도록 할 수 있지만, 지연 시간이 좀더 길어집니다.

결론

쿠버네티스와 Erlang VM은 서로 다른 수준에서 작동합니다. 쿠버네티스는 클러스터 내에서 오케스트레이션 되고 Erlang VM은 인스턴스 내의 언어 수준에서 오케스트레이션 됩니다. Fred Hebert는 이 차이를 트윗에서 잘 요약했습니다.

Still seeing bad comparisons between kubernetes and #Erlang/OTP. [Kubernetes와 # Erlang / OTP 사이에 여전히 잘못된 비교가 이뤄집니다.] K8s is to OTP what region failover is to k8s. [K8s는 OTP에서 k8s에 대한 지역 장애 조치입니다.] They operate on different layers of abstraction and impact distinct components. [이들은 서로 다른 추상화 계층에서 작동하며 서로 다른 구성 요소에 영향을줍니다.]

OTP allows handling partial failures WITHIN an instance, something k8s can’t help with.— Fred Hebert (@mononcqc) April 29, 2019

OTP를 사용하면 인스턴스 내에서 부분적인 실패를 처리 할 수 있습니다. k8s는 도움이되지 않습니다

Erlang / Elixir를 사용하고 있고 Kubernetes가 다른 언어와 비교하여 어떻게 적용되는지 궁금하다면 다른 기술과 마찬가지로 Erlang VM에 Kubernetes를 사용할 수 있습니다. Erlang / Elixir 소프트웨어는 일반적으로 수평 및 수직으로 확장 할 수 있으므로 K8 내에서 리소스를 할당하는 방법에 대한 많은 옵션을 제공합니다.

다른 영역에서 Kubernetes와 Erlang VM은 K8s Service Discovery를 사용하여 Erlang VM 인스턴스를 연결하는 것과 같이 서로를 훌륭하게 보완 할 수 있습니다. 물론 Distributed Erlang은 필수 사항이 아니며 Erlang / Elixir는 확장 성과 안정성 덕분에 stateless 앱에서도 훌륭한 언어입니다.

프로덕션에서 핫 코드 스와핑이 정말로 필요한 몇 안되는 경우, Erlang VM은 이를 수행하는 데 가장 적합한 플랫폼 중 하나 일 수 있지만 그러면 두 기술을 함께 쓰기엔 어려움을 겪게 될 것입니다.

마지막으로 Kubernetes와 그 개념에 감사한다면 Erlang 및 Elixir와 함께 작업하는 것이 즐거울 수 있습니다. 이는 소규모 및 대규모에 유사한 관용구를 적용 할 수있는 기회를 제공하기 때문입니다.