가상 머신과 Docker
Docker는 지금까지 사용해왔던 VMWare, Microsoft Hyper-V, Xen, 리눅스 KVM 등의 가상 머신과 비슷합니다.
가상머신에 리눅스를 설치한 뒤 각종 서버 프로그램과 DB를 설치하고, 개발한 애플리케이션이나 웹사이트를 실행했습니다. 이렇게 구축한 가상 머신 이미지를 여러 서버에 복사해서 실행하면 이미지 하나로 서버를 계속 만들어낼 수 있었습니다.
가상 머신
가상 머신은 편하긴 하지만 성능이 좋지 못한 것이 단점이었습니다. 지금까지 CPU에 가상화를 위한 기능들이 많이 추가 되었지만 가상 머신은 리얼 머신에 비해 속도가 느립니다.
전가상화(Full Virtualization)의 느린 속도를 개선하기 위해 반가상화(Paravirtualization) 방식이 개발되었고, 현재 널리 쓰이고 있습니다.
가상 머신 자체는 완전한 컴퓨터라 항상 게스트 OS를 설치해야 합니다. 그래서 이미지 안에 OS가 포함되기 때문에 이미지 용량이 커집니다.
네트워크와 인터넷 속도가 아무리 빨라졌다 하더라도 가상화 이미지를 주고받는 것은 꽤 부담스럽습니다. 특히 오픈 소스 가상화 소프트웨어는 OS를 가상화하는 것에만 초점이 맞춰져 있습니다. 그래서 이미지를 생성하고 실행하는 기능만 있을 뿐 배포와 관리 기능이 부족합니다.
Docker
Docker는 반가상화보다 좀더 경량화된 방식입니다. 아래 그림과 같이 게스트 OS를 설치하지 않습니다. Docker 이미지에 서버 운영을 위한 프로그램과 라이브러리만 격리해서 설치할 수 있고, OS 자원(시스템 콜)은 호스트와 공유합니다. 이렇게 되면서 이미지 용량이 크게 줄어들었습니다.
Docker는 하드웨어를 가상화하는 계층이 없기 때문에 메모리 접근, 파일시스템, 네트워크 속도가 가상 머신에 비해 월등히 빠릅니다. 아래의 표를 보면 수치상으로 호스트와 Docker 컨테이너 사이의 성능차이는 크지 않습니다.
Docker는 가상 머신과 달리 이미지 생성과 배포에 특화된 기능을 제공합니다. Git에서 소스를 관리하는 것처럼 이미지 버전 관리 기능을 제공합니다. 또한 중앙 관리를 위해 저장소에 이미지를 올리고 받을 수 있습니다. 그리고 GitHub 처럼 Docker 이미지를 공유할 수 있는 Docker hub도 제공합니다.
다양한 API를 제공하기 때문에 원하는 만큼 자동화를 할 수 있어 개발고 서버 운용에 매우 유용합니다.
리눅스 컨테이너
오래 전부터 리눅스/유닉스 환경은 chroot 라는 명령을 제공했습니다. chroot는 파일시스템에서 루트 디렉터리(/)를 변경하는 명령입니다. chroot 로 특정 디렉터리를 루트 디렉터리로 설정하면 chroot jail(감옥)이라는 환경이 생성되는데, chroot jail 안에서는 바깥의 파일과 디렉터리에 접근할 수 없습니다. 이처럼 chroot는 디렉터리 경로를 격리하기 때문에 서버 정보 유출과 피해를 최소하 하는데 주로 사용되었습니다.
chroot는 chroot jail에 들어갈 실행 파일과 공유 라이브러리를 직접 준비해야 하고 설정 방법이 복잡합니다. 또한 완벽한 가상 환경이 아니기 때문에 각종 제약이 많습니다. 이후 리눅스는 LXC(LinuX Container)라는 시스템 레벨 가상화를 제공했습니다.
LXC는 컴퓨터를 통째로 가상화하여 OS를 실행하는 것이 아닌 리눅스 커널 레벨에서 제공하는 일종의 격리(isolate)된 가상 공간입니다. 이 가상 공간에는 OS가 설치되지 않기 때문에 가상 머신이라 하지 않고, 컨테이너라 부릅니다.
리눅스 커널의 Control Groups(cgroups)는 CPU, 메모리, 디스크 네트워크 자원을 할당하여 완전한 형태의 가상 공간을 제공합니다. 또한 프로세스 트리, 사용자 계정, 파일시스템, IPC 등을 격리시켜 호스트와 별개의 공간을 만듭니다. 이것을 Namespace isolation (namespaces) 이라고 합니다.
LXC는 리눅스 커널의 cgroups 와 namespaces 기능을 활용하여 가상 공간을 제공합니다.
LXC는 격리된 공간만 제공할 뿐 개발 및 서버 운영에 필요한 부가 기능이 부족했습니다. Docker는 리눅스 커널의 cgroups 와 namespaces를 기반으로 하여 이미지, 컨테이너 생성 및 관리 기능과 다양한 부가 기능을 제공합니다.
Docker가 처음 개발될 당시에는 LXC를 기반으로 구현을 하였지만 버전 0.9부터는 LXC를 대신하는 libcontainer 를 개발하여 사용하고 있습니다. 내부적으로는 실행 드라이버(exec driver)라고 하는데 libcontainer는 native, LXC는 lxc로 표시됩니다. 실행 옵션에 따라 libcontainer 를 사용하지 않고 LXC를 사용할 수도 있습니다.