이번 시리즈에서는 Docker에 대해서 다뤄보려합니다. 도커라고 하면 누구나 한번쯤 들어봤을 법 하지만, 도커에 대해 제대로 설명할 수 있는 사람은 많지 않습니다. 그래서 이번 포스팅은 첫 포스팅인만큼, Docker에 대한 개념부터 명확히 이해하는 것을 목표로 해봅시다!
도커를 한마디로 정의하면, "데이터 또는 프로그램을 격리시키는 기능을 제공하는 소프트웨어"이다. 도커는 주로 서버에서 사용되어, 서버에 설치되어 있는 다양한 프로그램과 데이터를 각각 독립된 환경에 격리시킨다.
조금 더 이해하기 쉽게 설명하자면, 서버의 환경을 마치 코스트코 매장처럼 만드는 것이다. 마치 각각의 팔레트로 물품을 격리시키는 것처럼, 데이터와 프로그램을 컨테이너에 담아 격리한다는 것이다. 그리고 이 때, 이 컨테이너를 다루기 위해 사용하는 소프트웨어가 바로 도커이다.
참고로, 도커는 리눅스 운영체제에서만 사용할 수 있기 때문에 컨테이너에서 동작시킬 프로그램도 리눅스용 프로그램이어야 한다. 물론, 윈도우나 macOS에서도 도커를 사용할 수는 있지만, 이는 내부적으로 리눅스 운영체제를 사용하기에 가능한 것이다.
도커를 사용하려면 먼저 도커의 본체라 할 수 있는 도커 엔진을 설치해야 하는데, 이 도커 엔진이 있어야 컨테이너를 생성하고 구동할 수 있다. 그러나 엄밀히 말하면 도커 엔진만 있다고 해서 컨테이너를 생성할 수 있는 것은 아니고, 도커 엔진과 더불어 컨테이너 이미지도 있어야 한다.
여기서 이미지란, 컨테이너 생성에 필요한 모든 파일과 환경설정을 지니고 있어 별도의 설치나 의존성 추가가 필요하지 않은 상태의 파일을 말한다. EC2를 생성할 때 사용했던 AMI나, VirtualBox에 넣었던 Ubuntu 이미지가 모두 컨테이너 이미지에 해당한다.
도커의 핵심적인 역할은 서버 환경을 격리시키는 것이다. 그렇다면 데이터나 프로그램을 격리해야하는 이유는 무엇일까? 우리가 사용하는 대부분의 프로그램은 단독으로 동작하지 않고, 특정 실행 환경이나 라이브러리, 다른 프로그램을 이용해 동작한다.
쉬운 예시로, 연동되는 프로그램의 버전이 5.0일 때에만 제대로 구동될 수 있는 A라는 시스템이 있다고 하자. 그런데 B라는 시스템은 해당 프로그램의 버전이 8.0 이상일 때에만 사용 가능하다. 이러한 상황에서 B 시스템에 맞추어 프로그램을 업데이트한다면, A 시스템의 동작에 문제가 발생할 것이다.
당연히 연동 프로그램뿐 아니라 실행 환경, 라이브러리, 디렉토리, 설정 파일 등을 공유하는 상황에서도 한쪽 시스템에 맞추어 코드를 수정하다보면, 다른 시스템에서 예상치 못한 오류가 발생할 수 있다. 그 예시는 아래와 같다.
도커의 개념이 워낙 생소하다보니 지금까지 말한 내용이 잘 와닿지 않을 수 있다. 조금 더 쉬운 예시를 들어보겠다. 친한 친구들과 함께 한 방에서 자취를 하는 경우를 생각해보자. 밖에서 만날 때에는 큰 문제 없이 잘 지냈지만, 막상 한 공간에 같이 모여 살다보면 이런 저런 문제가 발생하게 된다.
또한 누군가의 생활 패턴에 억지로 맞추어 살다보면, 나의 생활 패턴에는 수 많은 제약이 가해질 것이다. 당연히 혼자 살 때보다 신경쓸 것도, 불편한 것도 많아진다. 이러한 문제를 해결하는 가장 간단한 방법이 무엇일까? 독립된 환경을 구축하는 것 즉, 각방을 쓰는 것이다.
컨테이너는 다른 컨테이너와 완전히 분리되어 있기 때문에 당연히 컨테이너 안의 프로그램도 다른 프로그램과 완전히 격리된 상태이다. 그러므로 여러 프로그램이 하나의 서버에서 실행됨으로써 발생할 수 있는 대부분의 문제를 도커 컨테이너를 이용해 해결할 수 있다.
위에서 들었던 예시를 다시 생각해보자. A 시스템은 5.0 버전의 프로그램을 필요로 하고, B 시스템은 8.0 버전의 프로그램을 필요로 한다. 한 대의 서버에 두 버전을 모두 설치하는 것은 원칙적으로 불가능하다. 그러나 도커 컨테이너는 완전히 독립된 환경이므로, 여러 컨테이너에서 같은 프로그램을 설치 및 실행해도 전혀 문제가 되지 않는다. 이 때 프로그램의 버전은 컨테이너마다 같아도 되고, 달라도 된다.
이로써 위 문제에 대한 해결 방법은 너무나 명확해진다. 시스템 A와 5.0 버전의 프로그램을 A 시스템의 컨테이너로 묶고, 시스템 B와 8.0 버전의 프로그램은 B 시스템의 컨테이너로 묶으면 된다.
상황에 따라 컨테이너를 사용하여 서버 비용을 절감할 수도 있다. 원칙적으로는 한 대의 서버 컴퓨터에는 하나의 웹 서버(Apache, Nginx 등) 밖에 실행할 수 없다. 요구 사양이 별로 높지 않은 여러 개의 프로젝트가 있다고 생각해보자. 하나의 서버 컴퓨터로 여러 개의 프로젝트를 충분히 감당 가능한 상황이더라도, 단지 웹 서버를 실행하기 위한 이유로 별도의 물리 서버를 구축해야 한다.
이로 인해 고사양의 서버 컴퓨터가 있어도 제대로 활용하기 어렵고, 프로젝트마다 서버를 따로 구축해야하기 때문에 불필요한 비용 손실도 감수해야 한다. 이 때 필요한 것이 바로 컨테이너 기술이다. 컨테이너를 사용하면, 하나의 물리 서버에 여러 웹 서버를 올리는 것이 가능해지기 때문에 수 배의 비용을 절감할 수 있게 된다.
뿐만 아니라, 개발에 필요한 시간적 비용을 절감하는 데에도 큰 도움이 될 수 있다. 웹 서버를 공유하면서 생길 수 있는 수 많은 문제(예를 들어 A 프로젝트 개발자가 B 프로젝트의 환경을 잘못 건드리는 등)를 전혀 걱정하지 않아도 되기 때문에 개발 속도가 빨라질 것이다. 또한 컨테이너는 도커만 설치되어 있으면, 운영체제의 차이, 물리적 환경의 차이, 서버 구성의 차이와 전혀 무관하게 다른 곳으로 옮길 수 있기 때문에 운영 서버와 개발 서버의 환경 차이로 인해 발생할 수 있는 문제도 걱정할 필요가 없다.
도커에 대해 설명할 때 빠지지 않고 등장하는 내용이 바로 가상화 기술과의 비교이다. 도커와 가상화 기술은 모두 컴퓨터 시스템에서 애플리케이션을 격리하고 실행하는 기술이라는 점에서는 동일하지만, 두 기술 사이에는 명확한 차이점이 존재한다.
먼저 VirtualBox, VMware와 같은 가상화 기술은 말 그대로 가상의 물리 서버를 만들어 내는 것이다. 여기서 "가상"이란, CPU, 메모리 등의 하드웨어를 소프트웨어로 대체한다는 의미이다. 실제로는 소프트웨어이지만, 실질적으로는 컴퓨터와 동등한 것이므로, 운영체제를 설치하거나 소프트웨어를 구동하는 것이 가능하다.
VM을 써본 적이 있다면, 아래의 그림을 쉽게 이해할 수 있을 것이다. 기존 OS(윈도우 등) 위에 하이퍼바이저를 두어 Ubuntu와 같은 Guest OS를 설치하는 방식이 아래의 구조에 해당한다.
도커는 방금 설명한 가상화 기술을 토대로 만들어진 새로운 기술이다. 당연히 새로운 기술인만큼 차이점이 존재한다. 아래의 사진을 통해 가상화 기술과 도커의 차이점이 무엇인지 알아보자.
언뜻 보면 비슷한 구조인 듯 하지만, 눈에 띄게 다른 두 가지를 찾아볼 수 있다. 바로 Hypervisor와 Guest OS의 유무이다. 이 두 가지의 차이점으로 인해 도커와 가상화 기술은 서로 다른 특징을 갖게 된다.
① 가상화 수준의 차이
② 리소스 사용 및 성능
※ Overhead
오버헤드란, 작업을 수행하기 위해 추가적으로 필요한 시간, 자원 또는 비용을 의미한다. 주로 성능과 관련된 맥락에서 사용되며, 시스템이나 애플리케이션의 실행과 관련된 부가적인 작업이나 리소스 사용을 설명하기 위해 사용하는 개념이다. 쉽게 말해 A라는 작업을 단순하게 처리하면 10초가 걸리는데, 안전성을 고려하여 부가적인 작업을 추가한 결과 처리시간이 15초가 되었다면, 이 때의 오버헤드는 5초가 된다.
③ 이식성과 확장성
④ 보안과 격리
도커는 데이터 또는 프로그램을 격리시키는 소프트웨어이기 때문에, "어떻게 격리시키는지"와 "왜 격리시켜야 하는지"를 제대로 이해했다면, 도커의 이론을 거의 다 이해한 것이나 마찬가지이다.