안녕하세요. 카카오클라우드입니다. 유연한 저장방식, 리소스 활용도 개선, 개발과 운영 측면에서의 편의성 강화를 위한 전략적 접근으로 고려되는 기술인 컨테이너(Container)에 대해 알아보겠습니다.
컨테이너는 과거 값비싼 서버를 나눠 쓰는 고민에서 출발하여 오랜 기간 동안 리눅스 기술 토대 위에 잘 쌓아 올려져 왔습니다. 이러한 점에서 컨테이너를 공부하는 것은 단순히 새로운 기술을 배우는 것 이상으로 리눅스나 전산 기초를 더 깊이 이해하는 과정에 가깝죠. 때문에 어떤 요구와 기술을 바탕으로 컨테이너가 발전되어 왔는지 알아보면서 리눅스 커널, 네트워크, 파일시스템, 컴퓨터 구조 등 관련된 기반 지식을 연결 지어 정리해보면 좋습니다.
서비스 개발자를 위한 '컨테이너 인터널'
이제 서비스 개발자에게 컨테이너 내부에 대한 이해는 중요합니다. 물론 자신이 담당하는 서비스에 대한 명확한 이해가 선행되어야 하죠. 컨테이너를 아무리 잘 안다고 해도 담당 서비스를 알지 못하면 트러블슈팅이나 성능 개선이 어려울 수 있기 때문입니다. 본 글에서는 개발자로서 컨테이너 관련 지식이 전혀 없는 분들이라도 기초부터 잘 쌓아 올려 접근하기 쉽도록, 컨테이너와 관련해서 꼭 이해해야 할 주요 기술 개념에 대해 소개해드리겠습니다.
1) 클라우드
먼저 클라우드(Cloud)입니다. 클라우드라는 용어를 누가 처음 사용했고, 어디서 처음 언급되었는지 알 수 있는 공식자료는 없습니다. 웹(Web)과 일부 책에서는 구글 에릭슈미트가 발표에서 처음 언급했다거나 구글 개발자들 대화 중에 (구글의 IDC 센터가 전 세계 각 지에 퍼져 있어) 본인이 개발한 애플리케이션이 어디로 배포됐는지 모르겠다는 의미로 “구름 (Cloud) 저편 어딘가에 있다”는 표현에서 유래됐다 등 다양한 설들이 있습니다.
“클라우드”에 대한 미국 국립 표준 기술연구소(NIST)의 정의는 다음과 같습니다. “공유 구성이 가능한 컴퓨팅 리소스(네트워크, 서버, 스토리지, 애플리케이션)의 통합으로 어디서나 간편하게, 요청에 따라 네트워크를 통해 언제든 접근하는 것을 가능하게 하는 모델이다. 이는 최소한의 이용 절차 또는 서비스 공급자의 상호 작용을 통해, 신속히 할당되어 제공된다.” 하지만 이 정의대로 클라우드가 컴퓨팅 리소스를 공유할 수 있고, 간편하게 요청 기반으로 제공할 수 있으려면 가상화와 API(Application Programming Interface) 또한 반드시 이해해야 합니다.
2) 가상화
가상화의 사전적 의미는 “Something is not real.(실제가 아닌)”으로, 컴퓨팅에는 “a hardware environment that is not real.” 즉, 실제가 아닌 하드웨어 환경을 의미합니다. 가상화(Virtualization)는 가짜(not real) 하드웨어 환경을 만들어 내는 기술로, 실제 물리장비인 '호스트'와 가상머신인 '게스트'를 구분합니다.
- 호스트(Host) : 가상화 소프트웨어(이하, 하이퍼바이저)를 구동하는 물리 시스템
- 게스트(Guest) : 하이퍼바이저(Hypervisor) 위에 설치되는 가상머신(Virtual Machine)
가상화에는 '컨테이너 가상화', '네트워크 가상화', '하이퍼바이저 가상화', '스토리지 가상화' 등 다양한 기술 스택이 존재합니다.
그중 우리는 앞의 세 가지 영역의 가상화에 대해 알아보겠습니다.
✅ 컨테이너(애플리케이션) 가상화
컨테이너는 애플리케이션 구동에 필요한 모든 것들을 패키징하고 격리된 환경에서 실행합니다. “컨테이너" 기반의 가상화로 애플리케이션 실행환경을 가상화합니다. “패키징"과 “격리된 실행"이라는 특징 때문에 서버별 환경에 영향받지 않고 사람의 개입 없이 자동으로 배포할 수 있습니다.
✅ 네트워크 가상화
네트워크의 한정된 물리주소라는 제약을 해결하고, 물리 IP 사용으로 인한 여러 의존성 구성을 가상 네트워크를 통해 추상화하여 단순하고 유연하게 할 수 있습니다.
✅ 하이퍼바이저(소프트웨어) 가상화
하이퍼바이저는 한 대의 머신에서 다수의 운영체제(OS)를 동시에 실행하는 소프트웨어로, 하드웨어를 가상화합니다. 가상머신 모니터(VMM, Virtual Machine Monitor) 라고도 합니다. 몇 가지 타입이 존재하고, 예를 들면 오락실 게임을 PC에서 돌릴 때 사용하는 에뮬레이터와 유사합니다.
3) API
API(Application Programming Interface)는 가상화를 통해 가능해진 하드웨어와 애플리케이션 제공을 “추상화(인터페이스)”하는 레이어입니다. 추상화를 하는 목적은 인터페이스(Interface)를 정의함으로써 리소스의 요청 및 제어 방법을 표준화하고, 이를 기준으로 리소스 딜리버리를 위한 인프라 체계를 정의하기 위함입니다.
추상화는 세부 구현을 가림으로써 합의된 인터페이스 범주 내에서 다양한 구현이 가능하고, 필요에 따라 교체하여 사용할 수 있습니다. API는 명세로 랩핑 되는데, 다양한 리소스에 연결된 API들이 명세 안에서 목적에 맞게 구성됩니다. 명세는 오픈스택, 쿠버네티스 등 오케스트레이션 도구 위에서 컨트롤러들을 부리기 위한 일종의 작업 지시서로, 컴퓨팅 리소스 할당, 가상 네트워크 구성, 컨테이너 배치, 오토스케일링 등 다양한 작업을 자동화합니다.
4) 컨테이너
컨테이너(Container)를 얘기하기에 앞서 도커(Docker) 얘기를 살짝 하고 가보겠습니다. 도커는 마치 "컨테이너"를 지칭하는 일반 명사처럼 두루 사용되고 있죠. 대화를 하다 보면 컨테이너에 관련된 내용에서 도커 컨테이너, 도커 이미지, 도커라이즈드 등 다들 습관처럼 도커란 용어가 입에 착 붙어서 나오게 되는데요. 사실 '도커'는 컨테이너 관련 제품인 도커를 만든 회사의 이름입니다. 도커는 컨테이너를 세상에 널리 알리며 주목받았고, 컨테이너를 다루는 도구와 플랫폼을 개발하고 있습니다. 지금은 도커 말고도 여러 회사가 도커와 같은 컨테이너 런타임, 컨테이너 플랫폼 개발을 하고 있습니다.
자, 그럼 컨테이너란 무엇일까요? 컨테이너란 격리된 환경과 제한된 리소스로 제어되는 프로세스를 의미합니다. "격리된 환경"과 "제한된 리소스" 뭔가 어렵게 보이지만 호스트가 다세대주택이라고 생각해볼게요. 이 다세대 주택은 여러 가족이 들어와 편안하게 살 수 있도록 독립적인 공간을 만들고, 생활에 필요한 전기와 수도, 가스도 공급해 줍니다. (참고로 … 집주인은 ‘커널(kernel)’이십니다) 컨테이너는 다세대 주택 안에 이렇듯 독립적으로 마련된 공간과 같습니다. 즉, 어플리케이션이 다른 프로세스의 영향을 받지 않게 격리하고 주어진 리소스 안에서 실행될 수 있게 해주죠. 그런데 꼭 따로 한 집씩 살림을 차려줘야 할까요? 그냥 같이 어우러져 살 수는 없는 것일까요?
또 다른 예를 들어볼게요. 어릴 적 집에서 PC를 같이 써 본 경험을 떠올려 보세요. PC를 공유하다 보면 내가 설치한 프로그램을 누나가 지운다거나, 웹서핑하다가 바이러스에 걸린다거나, 동생이 이상한 프로그램을 설치해서 잘 동작하던 프로그램이 안 돌아간다거나 했던 적이 한 번쯤 있었을 것 같습니다. 서버는 훨씬 다양한 경로로 서버 관리자, 개발자, 원격사용자(FTP, HTTP, SSH, ...) 등 더 많은 사람이 이용하고 있는데요. 같이 쓰다 보면 당연히 서로 영향을 주고받습니다. 누군가 서버의 FTP로 1TB짜리 대용량 파일 업로드를 시작하면 같은 서버를 이용하는 다른 사용자들은 타임아웃이나 응답이 느려지는 경험을 하게 될 수도 있고요. (잘못 작성된) 특정 애플리케이션이 서버의 cpu와 memory를 과도하게 사용하여 다른 애플리케이션이 리소스 부족으로 실패하거나 행업(Hang Up)될 수도 있습니다. 해당 서버가 실서비스를 운영하는 상황이면 서버 관리자와 각 애플리케이션 담당자들은 더욱 예민해질 수 있는 상황이죠.😡
같이 살려면 서로 조금씩 희생하고 배려하는 자세가 필요합니다. 그럼 애플리케이션에 ‘배려’라는 프로그래밍을 구현해야 할까요? 글쎄요. 한 방법일 수는 있을 것 같은데… 배려하려면 상대방을 알아야 하겠죠. 같이 사는 프로세스가 많지 않다면 cpu, memory 등 리소스도 서로 얼마씩 사용할지 합의하고, 함께 쓰는 라이브러리 버전도 합의해 볼 만 합니다. 하지만 불특정 다수가 사용하는 서버에서 어떤 애플리케이션이 설치되고 어떤 라이브러리가 사용될지 예상하기는 쉽지 않습니다. 그리고 서버가 한두 대가 아니라면 더 파악하기 어렵겠지요.
컨테이너는 배려를 구현하는 것보다는 앞서 설명해드린 프로세스 간 서로를 격리시키고 각자 필요한 환경을 꾸려서 실행하는 방법인 다세대주택의 방식으로 해결합니다. 그리고 공유 자원(cpu, memory, network ..)에 대해서 프로세스별로 할당치 만큼만 사용하도록 제한함으로써 누군가 자원을 과도하게 소비하여 피해를 주는 일이 없도록 합니다.
격리된 환경은 '배포'에도 장점이 됩니다. 서버 환경은 여러 사람의 손을 거치며 시간이 지날수록 변하게 되고, 새로 설치된 서버는 하드웨어, 커널 버전, 시스템 프로그램, 라이브러리 등 이전과 다르게 구성될 가능성이 높습니다. 일 년 전에 애플리케이션을 설치했던 서버에 다시 동일한 애플리케이션을 설치한다고 생각해 봅시다. 일 년 동안 서버가 아무 변경 없이 동일한 상태로 유지가 되었다고 보장할 수 있을까요? 대부분 확신하기 어렵습니다. 동일한 애플리케이션이라도 달라진 환경에 대한 대응을 누군가 해주어야 하는데요. 애플리케이션 실행에 필요한 프로그램이나 관련 의존성 라이브러리들이 서버에 설치되어 있는지, 설치된 버전은 맞는지, 설정에 오류가 없는지 등을 관리자가 일일이 확인해 보아야 합니다.
컨테이너에서는 실행파일뿐만 아니라 의존성 라이브러리, 설정 등 실행에 필요한 모든 것들을 “이미지”라고 하는 것에 모아 패키징함으로써 이 “이미지”에 있는 살림살이만으로도 서버 환경에 의존하지 않고 실행되는 것을 보장합니다.
5) VM vs 컨테이너
격리된 환경을 제공하는 방법은 컨테이너만 있는 게 아닙니다. 앞에서도 언급한 VM(Virtual Machine, 가상머신) 은 많이들 아실 것 같습니다. VMware 나 Parallels 등의 프로그램을 이용하면 윈도우즈(Windows)나 Mac에서 다른 운영체제(이하, OS)를 설치하여 사용이 가능한데 이것이 바로 VM 입니다. 아마도 Mac, 우분투(Ubuntu) 등 윈도우 이외의 OS를 써보신 분들은 공공기관, 은행 사이트나 인터넷 강의 시청을 위해 VM으로 윈도우를 설치하여 사용해 본 경험이 한 번쯤 있으실 것 같습니다. 저는 (Mac 용 스타크래프트가 없던 시절) Mac에 윈도우 VM을 설치하여 스타크래프트를 즐겼던 기억이 있네요.
VM은 컨테이너보다 훨씬 강력한 격리를 제공합니다. “하이퍼바이저” 라는 소프트웨어가 하드웨어 리소스를 가상화하고, 그 위에 별도의 운영체제가 설치됩니다. 이 운영체제는 서버의 “호스트OS”와 구분해서 “게스트OS“ (이하, GuestOS)라고 부릅니다. 하이퍼바이저는 서버 위에 여러 종류의 GuestOS를 VM으로 실행할 수 있습니다.
이처럼 VM은 서버 안에 또 다른 서버가 있는 것처럼 별도의 OS 환경으로 동작하는데요. 부팅과정도 있고, 여러 프로그램을 설치하여 사용하는 방식이 일반적인 물리 서버를 사용하는 것과 동일합니다. 반면, 컨테이너는 (GuestOS 없이) 호스트 OS를 사용하고 프로세스를 격리하여 실행하는 방식으로 VM과는 매우 다릅니다. 원래 컨테이너는 리눅스 커널에서 지원하는 기능들로 만들어지기에 윈도우, 맥 등 다른 OS에서는 동작하지 않습니다. 이에 비해 VM은 다양한 종류의 OS를 설치하고 구동할 수 있습니다. 그렇다면 Mac이나 윈도우에서도 컨테이너 사용이 가능한 것은 어찌 된 것일까요? 이것은 윈도우나 Mac 에서 컨테이너 지원을 위해 리눅스 VM을 올리고 그 위에서 컨테이너를 기동하기 때문입니다.
컨테이너와 VM을 비교해 보았는데요. 참고로 근래에는 컨테이너 개념이 “리눅스 컨테이너”에 국한되기보다는 격리된 환경의 관점에서 경량 VM까지 아울러서 좀 더 폭넓게 해석되는 경향이 있습니다. 컨테이너는 VM에 비해 작고 빠릅니다.
컨테이너는 호스트 커널에서 바로 처리되고 하드웨어를 직접 관리하는 반면에 VM은 GuestOS가 하드웨어 사용 시 하이퍼바이저의 처리를 기다려야 합니다. 하이퍼바이저는 여러 GuestOS를 상대해야 하고, 하드웨어는 한정된 자원이다 보니 개별 GuestOS의 사정에 맞춰서 리소스를 제공하기 쉽지 않습니다. 이런 이유로 컨테이너와 VM의 성능 차이가 발생할 수밖에 없긴 하지만, 둘 다 각자 클라우드에서 필요한 역할이 있습니다.
컨테이너는 애플리케이션을 패키징하고 서버 환경에 제약 없이 구동할 수 있기 때문에 일종의 “앱" 자판기가 가능합니다. 그리고 VM은 하드웨어를 가상화하고 그 위에 다양한 GuestOS를 올리는 “컴퓨팅" 자판기가 가능합니다. 자판기라고 표현한 이유는 코인만 넣으면 즉, 요청만 하면 원하는 제품을 제공해 주기 때문입니다. AWS, Azure, GCP 등 퍼블릭(Public) 클라우드 환경에서는 실제로 VM 기반으로 인프라를 구성하고 그 위에서 컨테이너 형태로 애플리케이션을 구성해 다양한 IT 리소스를 명세와 API 기반으로 자동화하여 제공하고 있습니다.
지금까지 컨테이너의 개념에 대해서 간단히 살펴보았는데요. 사실, 컨테이너는 어느 하루 아침에 docker run 명령어로 바로 실행할 수 있게 나온 것이 아닙니다. 컨테이너는 “프로세스 격리"라는 문제를 오랜 세월에 걸쳐 풀어오면서 필요한 요건들이 하나 둘 씩 생겨났고 그것들을 구현해 온 결과이죠. 도커(회사)는 이러한 결과물들을 모아 개발에 활용할 수 있는 수준으로 정리하여 2013년 “도커 플랫폼”을 공개합니다.
6) 쿠버네티스
‘쿠버네티스’는 도커와 같은 컨테이너 런타임을 통해 컨테이너를 ‘오케스트레이션’하는 플랫폼입니다. '컨테이너 오케스트레이션'이란 용어는 거창하지만, 컨테이너에 필요한 설정과 관리, 적절한 노드로 배치, 모니터링 등을 제공하는 것을 의미합니다. 어느 정도 감이 오시나요?
쿠버네티스는 물리머신에서 사람이 해왔던 업무 대부분을 (혹은 그 이상으로) 해결하는데요. 사람이 거의 개입하지 않고도 배포부터 운영관리의 다양한 부분을 자동화할 수 있습니다. 구글의 사례를 통해 어떻게 대규모 서버에 배포와 관리를 할 수 있는지, 쿠버네티스 탄생의 역사를 조금 살펴보겠습니다.
구글은 이미 오래전에 매주 20억 개가 넘는 컨테이너를 생성하고 운영하였습니다. 이 정도 규모에 사람을 마냥 늘려서 대응하는 것은 매우 비효율적이죠. 그래서 구글은 ‘borg’라는 사내 플랫폼을 이용해 컨테이너를 관리하게 됩니다. 2014년에는 ‘borg의 오픈소스 버전’인 ‘쿠버네티스’를 만들어 CNCF(Coud Native Computing Foundation)에 기증(2016)하게 되는데요, 이게 바로 지금 우리가 쓰고 있는 쿠버네티스의 시작인 셈입니다.
당시 구글은 10여 년에 걸친 대규모 서비스 워크로드 운영 경험을 기반으로 쿠버네티스를 만들었다고 발표합니다. 20억 개의 컨테이너를 관리하는 상상을 해보세요! 이 많은 수의 컨테이너들을 실행하게 될 수많은 서버가 세계 데이터 센터에 나뉘어 있고, 컨테이너마다의 필요와 요구사항을 판단하여 배치해야 합니다. 가히 구글 개발자들이 “구름(클라우드) 너머 저 어딘가에”라고 할만하죠.
쿠버네티스는 사용자의 명세를 기반으로 컨테이너의 스펙에 적합한 노드를 자동으로 선정합니다. 전 세계 서버들을 컨테이너의 스펙과 비교하여 필터링하고 추려진 노드들을 스코어링(Scoring)하여 가장 최적인 노드로 컨테이너를 배치합니다. 개발자는 노드가 어떤 서버에 배치되었는지 몰라도 됩니다. 쿠버네티스가 노드 선정과 배포를 더 간단하게 할 수 있었던 것은 컨테이너가 실행환경을 포함하고 있고 서버에 따라 영향을 받지 않기 때문이었습니다. 컨테이너가 없었다면 구글은 정말로 전세계 데이터센터에 수십억 개의 애플리케이션을 설치할 인력을 고민했을지도 모릅니다.
쿠버네티스는 명세(일종의 작업지시서)를 기반으로 다양한 “컨트롤러”가 협력하는 방식으로 동작하는데요, 여러 종류의 빌트인 컨트롤러들이 있고 필요하면 커스텀 컨트롤러를 구현할 수도 있습니다. 각 컨트롤러는 각자 처리하는 명세를 정의하고 그에 맞춰 구현됩니다. 컨트롤러는 (사람 대신) 컨테이너를 배치하고, (사람 대신) 상태를 모니터링하고, (사람 대신) 상태가 달라지면 명세에 정의된 스펙대로 맞추려는 노력도 합니다.
7) 참고자료
- Mastering KVM Virtualization (2016) by Humble 외
- Xen으로 배우는 가상화 기술의 이해 (2014) by 박은병, 김태훈 외
- 그림으로 배우는 클라우드 인프라와 API의 구조 (2017) by 히라야마 쯔요시 외
- 오픈스택을 다루는 기술 (2017) by 장현정
- Large-scale cluster management at Google with Borg (2015) by Eric Tune 외
- Performance Evaluation of Docker Container and Virtual Machine (2020) by Amit MPotdar 외
- Container’s Anatomy (2015, slideshare) by Jérôme Petazzoni
- Linux Container Performance Analysis (2017, 유튜브) by Brendan Gregg
- Docker overview (도커 공식 사이트)
(※ 이 글은 카카오엔터프라이즈의 기술블로그 ‘Tech&(테크앤)’의 Krew Insight 콘텐츠 ‘서비스 개발자를 위한 컨테이너 뽀개기 (a.k.a 컨테이너 인터널)’을 재구성한 콘텐츠입니다. )
✅ 함께 읽으면 좋은 콘텐츠를 소개합니다.
- <지식 사전> 컨테이너화(Containerization)란? - 비즈니스에 유연성과 확장성 더하기
댓글