딥다이브: Immutable Kubernetes Architecture

안녕하세요? LINE Infra Platform 팀에서 사내 클라우드 네이티브 서비스인 Nucleo의 운영 및 개발을 담당하고 있는 이어형입니다. 지난 6월 29일 Open Infra Days Korea의 세션 중 하나인 “딥다이브: Immutable Kubernetes Architecture” 세션에서 발표한 내용, 즉 안전하게 Kubernetes(이하 ‘k8s’)를 구축하고 운영하고 관리하기 위한 기술을 소개해 드리려고 합니다.
제 세션의 자료와 동영상을 보실 수 있도록 공유해 드리니 아래의 자료를 참고해 주세요.


Immutable Infrastructure

Chad Fowler


시스템 관리자로서 내가 가장 무서워하는 것 중 하나는 오랜 기간 운영된 서버, 특히 시스템과 응용 프로그램을 여러 번 업그레이드한 서버입니다.
왜일까요? 오래된 시스템은 필연적으로 보이지 않는 문제를 키우기 때문입니다.

업그레이드가 필요한가요? 문제 없습니다. 업그레이드된 신규 시스템을 도입하고 이전 것을 버리세요.
애플리케이션의 신규 버전을 배포해야 한다고요? 마찬가지입니다. 새 버전의 애플리케이션이 탑재된 서버를 생성하고 이전 것을 제거하세요.

Trash Your Servers and Burn Your Code: Immutable Infrastructure and Disposable Components – Chad Fowler 발췌

예전에도 애플리케이션이 탑재된 OS 이미지 기반으로 애플리케이션을 배포하고자 하는 노력은 많이 있었습니다. 그러나 이런 배포 방식은 클라우드 기술의 발전 및 그에 따른 컨테이너 기술의 발전이 있기까지는 실제로 활용되지 못했다 해도 과언이 아닙니다. 완벽하게 독립된 공간과 하드웨어를 본 떠 만든 VM과 달리 컨테이너는 대부분 namespace를 통해서 OS에서 격리된 공간을 만들어서 사용자에게 제공합니다. 하지만 컨테이너 기술은 동일한 OS 위에서만 사용되는 등 사용이 제한적이어서 실사용자들은 특정 상황에서만 사용할 수 있었습니다.

Docker는 컨테이너 기술에 레이어를 관리하기 위한 스토리지 방법론을 적용해서 재연성을 극대화시킨 기술입니다. 이로 인해 개발자들은 자신이 개발한 애플리케이션이 다양한 OS에서 동일하게 실행되리라는 것을 기대할 수 있게 되었습니다. 이후 이렇게 재연성이 높은 방법이 여럿 나타나면서 이미지 기반의 배포 방법은 더욱 발전했습니다. 또한, 컨테이너의 높은 재연성을 최대한 활용하기 위해 컨테이너 호스트 OS(CoreOS, RancherOS, Atomic 등)가 등장했습니다. 컨테이너 호스트 OS는 호스트 OS에는 오로지 컨테이너를 관리하는 서비스만 탑재하고 호스트 OS 위에서 여러 컨테이너를 실행하는 기술입니다. 다만, 이 기술은 k8s에 전문화된 OS(예: k8s node)를 만드는 역할이 아니라 범용적인 컨테이너를 배포하기 위한 역할만을 담당하고 있습니다. 왜냐하면 컨테이너 호스트에서는 호스트 OS까지만 불변(immutable)하며 이 호스트 OS에 탑재되는 컨테이너는 mutable, 즉 변할 수 있기 때문입니다.

LinuxKit

IT 산업 전반적으로 컨테이너 사용이 극대화되는 가운데, 컨테이너 생태계는 다음 단계로 도약하게 됩니다. 2017년 발표된 Moby 프로젝트를 통해 Moby는 사용자가 언제든지 컴포넌트를 레고와 같이 조립해서 커스텀 컨테이너 시스템을 생성하게 해주는 개방형 프레임워크로 등장합니다. 이후 Moby는 이미지의 생성 뿐만 아니라 이미지의 배포까지 확장한 LinuxKit을 발표했습니다.

LinuxKit은 다음의 특성을 가지고 있습니다.

  • 사용성을 저하시키지 않는 안전한 디폴트 세팅을 제공합니다.
  • LinuxKit의 구성 요소는 모두 대체 가능하며 커스터마이즈 할 수 있습니다.
  • Linux 배포판 빌드를 위해 불변(immutable)하는 인프라가 적용되어 있습니다.
  • 완전히 stateless하지만, 영구 저장소(persistent storage)와 연결 가능합니다.
  • 사용이 쉽고 개발과 배포 과정을 쉽게 반복할 수 있습니다.
  • 컨테이너로 구성된, 컨테이너 운영을 위한 도구입니다.
  • Docker 또는 k8s와 같은 컨테이너 오케스트레이션(Container Orchestration)을 포함, 클러스터 애플리케이션을 구축하고 실행하기 위해 설계되었습니다.
  • Docker를 구축한 경험을 바탕으로, 다용도의 툴킷으로 재설계되었습니다.
  • Infrakit 혹은 그와 유사한 외부 도구로 관리되도록 설계되었습니다.

Docker가 Dockerfile로 구성되듯이 LinuxKit은 하나의 yaml 파일로 구성되며, 그 파일 안에서 다음의 컴포넌트로 LinuxKit의 구성을 정의합니다.

  • Kernel (필수)
  • Init (필수)
  • onboot
  • Services
  • onshutdown
  • files
  • trust

각 컴포넌트는 컨테이너 이미지들로 구성됩니다. 즉, 컨테이너 이미지를 추가하거나 변경 혹은 삭제함으로써 LinuxKit의 구성을 변경할 수 있습니다. Kernel과 Init은 시스템 구성에 필요한 컴포넌트로 이루어져 있으며 onboot은 DHCP처럼 부팅 시 1회성으로 필요한 작업을 정의합니다. Services 컴포넌트에는 OS가 실행 중에 제공할 서비스를 구성하여 넣습니다. 예를 들면 NTPD와 SSHD, getty, NGINX 등과 같은 서비스들이 Services 컴포넌트로 정의됩니다.

LinuxKit이 제공하는 기능은 크게 build와 push, run입니다.

  • build: yaml 파일로부터 불변한 이미지를 생성할 수 있게 하며, LinuxKit이 지원하는 이미지 포맷은 다양합니다. 예를 들어 AWS, Docker, dynamic-vhd, GCP, iso-bios, iso-efi, Kernel+initrd, Kernel+squashfs, qcow2-bios, qcow2-efi, raw-bios, raw-efi, rpi3, tar, tar-kernel-initrd, VHD, VMDK 등을 지원합니다. 즉, LinuxKit은 하나의 yaml(구성)으로 이종의 환경에서 구동될 수 있습니다. 빌드된 LinuxKit 이미지는 LinuxKit 자체의 PXE Server 기능으로 BareMetal을 위해서도 사용할 수 있습니다.
  • push: 빌드한 LinuxKit 이미지를 이미지 관리 서비스를 제공하는 다양한 플랫폼(AWS, Azure, GCP, OpenStack, Packet, Scaleway, vCenter 등)으로 업로드할 수 있습니다.
  • run: 빌드한 LinuxKit 이미지는 HyperKit(macOS)과 Hyper-V(Windows), QEMU(Linux), VirtualBox, VMware 등과 같이 로컬 호스트에서 구동되는 VM은 물론, AWS와 Azure, GCP, OpenStack, Packet, Scaleway, vCenter, VMware 등 외부 서비스가 제공하는 VM이나 PM 위에서도 구동 가능합니다.

Immutable Kubernetes

최근에는 k8s가 널리 사용됨에 따라 모든 퍼블릭 클라우드에서 k8s 클러스터를 지원하게 되었고, 덕분에 k8s를 사용하는 것이 아주 쉬워졌습니다. 하지만 k8s 자체를 사용자가 필요한 컴포넌트로 구성하고 기능을 튜닝하여 사용하려면 결국 사용자가 직접 제어할 수 있는 환경이 필요합니다.

직접 구성하고 튜닝한 k8s를 배포하고 관리하는 방법은 여러가지 있습니다. 그중에서도 가장 편리한 방법은 kubeadm라는 도구를 사용하는 것인데, kubeadm은 아무 것도 없는 환경에서 k8s를 빌드하려고 할 때 필요로 하는 많은 기능을 제공하는 도구입니다. 다만 kubeadm으로 클러스터를 셋업하려면 Docker나 cri-containerd가 설치되어 있어야 하며, kubelet이나 kubeadm, kubectl 등의 바이너리가 호스트에 존재해야 합니다. 이런 준비 단계까지도 해결해주는 배포 방법이 필요하면 kubespray와 같은 도구를 사용해야 합니다.

만약 Docker와 kubelet, kubeadm, kubectl 바이너리가 모두 설치되어 있는 이미지만 있다면 사용자는 kubeadm만으로 아주 쉽게 클러스터를 관리할 수 있습니다. 즉, 복잡한 k8s 셋업 과정을 거치지 않고 준비된 이미지와 kubeadm만으로 k8s 클러스터를 제어할 수 있습니다. LinuxKit Kubernetes같은 프로젝트를 이용하면 필요한 환경, 즉 Docker가 설치되어 있고, kubelet와 kubeadm, kubectl의 바이너리가 설치된 이미지를 만들 수 있으며, 이에 따라 사용자는 kubeadm만으로도 마스터 노드의 bootstrapping(kubeadm init)과 워커 노드의 bootstrapping(kubeadm join)만으로도 k8s 클러스터를 생성하고, 확장하고, 제어할 수 있습니다. 또한, LinuxKit은 userdata 등을 제공하여 부팅 시의 인자 값을 이용하면 클러스터를 자동으로 생성하고 확장할 수 있습니다. 따라서, 클러스터 셋업에 필요한 이미지가 준비된다면 새로운 이미지의 노드를 추가하고 과거 이미지의 노드를 제거하는 작업을 주기적으로 수행하는 방식으로 k8s 클러스터를 업그레이드하고 관리할 수 있습니다. 마치 에스컬레이터의 계단이 도착 지점에서는 사라진 후 다시 재사용 되듯이 노드를 재사용할 수 있게 됩니다.

LinuxKit을 이용하여 맥북에서 버추얼 머신을 실행하고 k8s 클러스터를 생성하는 것을 시연한 영상은 이곳에서 보실 수 있습니다. 자세한 코드는 github을 참고하시면 됩니다. 다만 현재 공식 저장소의 코드로는 AWS에서 구동이 되지 않는 상태이기 때문에 별도 저장소에서 테스트 가능합니다. GCE에서 LinuxKit 이미지를 Nested VM 방식으로 구동시켜 테스트하려면 호스트 내에서 브릿지를 통해 내부 네트워크를 생성해야 하지만 문서가 부족하여 테스트하는 것이 쉽지 않습니다. 테스트를 해보고 싶으신 분은 이 스크립트를 참고하세요.

마지막으로

LinuxKit Kubernetes 프로젝트는 현재 프로토타입 단계에 있습니다. LinuxKit 자체가 1년 밖에 안 된 프로젝트이고 내부적으로 많은 코드가 변경되고 있으며 컨테이너의 이해도가 높아야 LinuxKit을 구성하는 yaml 파일을 설계할 수 있기 때문에 아직은 프로덕션 단계에 적용하기에 무리라고 생각합니다. 또한, 하나의 yaml 파일로 다양한 플랫폼에서 LinuxKit을 구동시켜보면, 실제로는 구동이 안되는 상황을 자주 만나게 됩니다. 하지만 장기적으로 볼 때, 이미지 기반의 배포 기술이 발전하면 결국 모두가 만족할만한 목표에 다다르게 되지 않을까 조심스럽게 추측합니다. 저희도 Linux Kubernetes를 단기적으로 테스트 용도로는 사용하고 있습니다. 앞으로도 이 프로젝트의 행보를 계속 지켜볼 예정입니다.

긴 글 읽어주셔서 감사합니다. 아, 참고로 LINE에서는 현재 Cloud Native Platform 채용을 진행하고 있으니, 많은 지원 기다리고 있겠습니다.

Related Post