멋진 신세계, 컨테이너: Docker를 소개합니다

Premist (Minku Lee)
Making Shakr
Published in
7 min readNov 20, 2017

--

마이크로소프트웨어 390호, <오픈의 꿈>에 투고한 글을 옮겼습니다.

2008년, Amazon이 가상머신 호스팅 서비스인 Elastic Compute Cloud (EC2)를 정식으로 공개하였다. 절대적인 컴퓨팅 성능으로는 온프레미스 서버보다 비싼 비용을 주어야 하지만, 필요에 따라 즉시 스케일이 가능하고, 서비스를 만들기 위한 서비스들이 준비되어 있고, 이를 API를 통해 서로 연결할 수 있다는 점은 엔지니어를 사로잡기 충분했다. 기존에 서버를 관리하고 웹 애플리케이션을 배포하는 전통적인 방식에서 이러한 IaaS 기반의 인프라 구축 서비스로 이전하는 기업이 빠르게 늘어갔고, 새로 비즈니스를 시작하는 스타트업은 클라우드에서 웹 애플리케이션을 구동하는 것을 전제로 계획을 세우는 방식이 정착하기 시작했다.

시간이 흘러 2017년 현재, 가상머신을 이어 클라우드 환경에서 새로운 컴퓨팅 단위가 될 기술로 컨테이너가 주목받고 있다. Amazon, Microsoft, Google, IBM 등 주요 클라우드 제공자는 이미 컨테이너를 프로덕션 환경에서 구동할 수 있는 서비스를 제공하고 있고, Docker Swarm이나 Kubernetes, Marathon 등 컨테이너 기반으로 애플리케이션을 배포하고 운영할 수 있는 오픈소스 소프트웨어도 활발하게 개발되고 있다. Right Scale의 조사에 따르면 DevOps 도구 중 Docker의 사용률이 Chef, Puppet, Ansible을 앞지르고 1위를 차지했는데, 이에서 볼 수 있듯이 컨테이너를 개발 워크플로우에 도입하는 기업이 점점 많아지고 있다.

‘멋진 신세계, 컨테이너’ 시리즈에서는 이렇게 활발한 생태계 성장과 실제 사용이 이루어지고 있는 컨테이너에 대해서 같이 알아보고, 컨테이너를 개발 프로젝트에서 어떻게 활용할 수 있는지, 그리고 컨테이너 기반 웹 애플리케이션을 프로덕션 환경에 배포할 때 유의해야 할 점을 소개하고자 한다.

컨테이너의 역사 그리고 Docker

한 컴퓨터 내에서 자원을 효과적이고 안전하게 격리하기 위한 시도는 꽤 오래 전으로 거슬러 올라간다. 1979년, Unix V7에서 처음 등장한 chroot 시스템 호출은 현재 실행 중인 프로세스와 자식 프로세스가 접근할 수 있는 루트 디렉터리를 특정 디렉터리로 변경하고, 해당 디렉터리 상위의 디렉터리는 수정하지 못하도록 하였다. 2000년에는 FreeBSD에 chroot의 개념이 jail이라는 기능으로 추가되었고, jail을 통해 격리된 시스템마다 IP 주소와 설정을 할당할 수 있는 기능을 제공하였다. 2004년에는 오라클이 Solaris Zone을 발표했고, 2005년에는 Linux 커널을 패치하여 리소스 격리를 구현한 OpenVZ 프로젝트가 등장했다.

2007년, 구글이 내부적인 사용을 위해 Process Containers를 발표했고, 이 기능은 Control Groups (cgroups) 라는 이름으로 리눅스 커널에 추가되었다. 이 cgroups를 이용하여 만들어진 것이 LXC(Linux Containers)인데, 커널에 이미 존재하는 네임스페이스 관련 여러 기능을 활용하여 각 환경을 격리하는 기능을 제공하였다.

2013년 초, dotCloud가 새로운 컨테이너 엔진 Docker를 오픈 소스로 공개하였다. Docker를 제작한 회사인 dotCloud는 기존에는 Heroku나 Google App Engine과 같은 PaaS를 제작하던 회사였지만, 성장세와 유명세에 힘입어 Docker에 집중하기로 결정하고 사명을 Docker Inc.로 바꾸고 PaaS 비즈니스를 매각하였다.

Docker의 경우에도 초기 개발 단계에서는 LXC를 이용하여 자원 격리를 구현했지만, 1.0 버전 공개를 하기 전에 Go로 제작된 오픈 소스 컨테이너 라이브러리인 libcontainer를 개발하여 사용하고 있다.

현재 구동되는 컨테이너 시스템의 대부분은 Docker를 사용하고 있으며, CoreOS사에서 제작한 rkt 컨테이너 엔진을 사용하기도 한다.

환경 구축의 해결사

개발자라면 환경 구축에 대해 고민하던 적이 한두 번 이상은 있을 것이다. 새로 팀에 합류한 팀원에게 개발 환경을 구축해주려고 할 때 생길 수 있는 고민부터, 프로덕션 서버에 특정 패키지가 설치되어 있지 않아 오류가 발생하는 경우까지, 개발할 당시에는 간과하기 쉬운 요소가 나중에 발목을 잡는 경우도 많다.

개발 단계에서의 환경 구성을 도와주는 툴의 대표적인 예로는 Vagrant가 있는데, 기본적으로는 VM을 사용하여 환경을 관리하기 때문에 VM 내부에서 어떤 것이 바뀌었는지 추적하기 어렵고, 커널을 포함한 운영체제의 구성 요소를 모두 담고 있어 이미지의 용량이 굉장히 크고, Virtualbox와 같은 VM 하이퍼바이저를 설치해야 한다.

배포 단계에서는 Chef, Puppet, Ansible까지 환경을 구성하기 위핸 단계를 코드로 작성하는 다양한 도구가 나와 있다. 이러한 도구의 경우 환경을 프로비저닝할 때 각각의 단계가 실행되기 때문에 오랜 시간이 지난 후에도 해당 환경을 동일하게 복제해낸다는 보장이 없고, 환경 외부에 제어를 위한 별도의 서버를 두어 관리를 해야 한다. 또한 여러 환경에 대응해야 하기 때문에 운영체제의 기능을 아우를 수 있는 고수준의 추상화를 필요로 해서, 내장되어 있는 모듈을 사용하는 등 간단한 작업시에는 불편함을 느끼지 않을 수는 있어도 복잡한 작업을 하기 시작할수록 서버의 형상 관리에 신경을 많이 써야 한다.

반면 Docker는 이미지를 생성하기 위한 각 단계를 Dockerfile이라는 규격으로 관리한다. Dockerfile 안에 기반 이미지와 애플리케이션과 의존성을 설치하기 위한 단계를 순차적으로 기술할 수 있으며, 이렇게 기술한 단계는 컨테이너 이미지를 빌드할 때 실행되고 단계별 결과는 캐시에 보관되어 추후 동일한 이미지를 빌드할 때 반복된 작업을 줄여준다. 또한 호스트 운영체제의 커널을 공유하면서 각 운영체제의 자원 격리 기술을 이용하는데, Linux의 경우에는 cgroups와 네임스페이스, Windows의 경우에는 Job Objects를 사용하여 낭비되는 리소스를 최대한 줄인다.

꽤 오래 전 Shakr에서 가상 서버 여러 대를 운영하고 있었을 때, 각 서버의 libcurl 버전에 차이가 있어 애플리케이션 엔드포인트가 동작하는 방식이 서버에 따라 달라져 디버깅에 애를 먹은 적이 있다. 중간에 호스트의 운영체제와 라이브러리 버전에 관련 없이 격리된 환경 안에서 사용자가 지정한 라이브러리와 함께 애플리케이션을 구동함으로써, 미연에 생길 수 있는 호스트간 환경 불일치를 막을 수 있다. 또한 프로덕션 환경과 동일한 환경을 개발 환경에서도 재현할 수 있어, 보다 예외가 적은 디버깅을 할 수 있다.

초기 Docker가 출시되었을 때는 AUFS와 같은 리눅스 커널에 내장되지 않은 파일시스템 형식을 사용하여 설치하는 과정이 매우 복잡했지만, 이후 btrfs나 overlayfs, devicemapper 등 여러 저장소 드라이버를 지원하게 되어 대부분의 Linux 시스템에 간단하게 설치하고 애플리케이션을 배포할 수 있다.

References

--

--