최근 개발 분야에 상관 없이, 도커가 참 많이 쓰이고 있는 것 같습니다. 그러나 저는 여태 소규모 개발만 해 봤기 때문에 도커를 직접적으로 써 볼 일은 없었습니다. 그런데 이번에 어떤 프로젝트를 기획하면서 도커를 도입해보면 좋을 것 같다는 생각이 들었습니다. 그래서 도커를 공부해봤고, 그 내용을 블로그에 정리하기로 했습니다.
도커란 무엇인가?
도커(Docker)란, 기본적으로 가상화 플랫폼입니다. 즉, 가상 머신과 비슷하게 어떤 컴퓨터 내부에서 독립된 다른 컴퓨터를 구동할 수 있도록 해 주는 플랫폼입니다.
컨테이너와 이미지
도커의 가장 중요한 개념은 컨테이너와 이미지입니다.
컨테이너(Container)
위에서 도커라는 것은 어떤 컴퓨터(A)내에서 다른 컴퓨터(B)를 구동할 수 있도록 해 준다고 했습니다. 이때 컴퓨터 A에 해당하는 컴퓨터를 호스트(host) 컴퓨터라고 부르며, 컴퓨터 B에 해당하는 것을 컨테이너라 부릅니다. 즉, 컨테이너는 도커를 통해 생성한 가상 머신 인스턴스와 비슷한 것이라 볼 수 있습니다. 실제로는 가상 머신과는 다르며, 아래에 그 차이를 서술합니다.
컨테이너는 바로 다음에 설명할 이미지로부터 생성되며, 특정 작업을 완료한 후에는 종료됩니다. 컨테이너를 삭제하면 컨테이너 내부에서 이뤄졌던 모든 내용들은 다 날아가버립니다. 그러므로 도커를 사용하여 개발을 할 때에는 외부 디렉토리를 마운트하여 빌드할 때만 도커를 사용해야 하며, 도커로 서비스를 운영할 경우 DB등 저장이 필요한 부분은 도커 외부에 두어야 합니다.
이미지(Image)
도커에서 이미지는 OS이미지 할 때의 그 이미지와 같은 이미지입니다. 이미지는 컨테이너에 대한 모든 정보를 담고 있는 파일과 같은 것인데, 이미지를 실행(run)하면 컨테이너가 됩니다. 우리가 한 개의 윈도우 이미지로 여러 컴퓨터에 윈도우를 설치할 수 있듯, 한 개의 도커 이미지로 여러 개의 컨테이너를 동시에 실행시킬 수 있습니다. 프로그래밍에 비유하자면, 이미지는 클래스에 해당하며 컨테이너는 인스턴스에 해당합니다.
이미지란 컨테이너에 대한 모든 정보를 담고 있다고 했습니다. 예를 들자면 어떤 프로그램이 설치되어있다거나, 환경 변수 설정, 파일들, 원한다면 터미널 글자 색깔까지 지정해줄 수 있습니다.(...) 이미지는 매우 단순한 방법으로 만들어집니다. 어떤 어떤 OS를 사용할 것인지에 대한 베이스 이미지를 정하고, 여기에 원하는 내용을 추가해나가면 됩니다.
이미지를 만들 때에는 Dockerfile
이라는 파일을 이용합니다. 이것은 그냥 이름만 Dockerfile
이고, 실은 일반적인 텍스트파일입니다. 제가 이번에 만든 도커파일을 보면서 작동 방식을 알아보도록 하겠습니다. 아래 파일에서 #
으로 시작하는 부분은 주석이며, 이미지에 영향을 미치지 않습니다.
# Dockerfile
# 이 파일은 도커 위에서 code-server 프로그램을 돌리기 위한 예제입니다.
# code-server는 vscode를 웹브라우저에서 사용할 수 있도록 한 프로그램입니다.
# FROM 키워드를 사용해 어떤 이미지를 베이스로 할지 정해줍니다.
# 이미지:태그 와 같은 식으로 지정이 가능합니다.
# 태그란 어떤 이미지에 여러 버전이 있을 때 그 버전을 지정하는 역할을 합니다.
# 예를 들어 ubuntu 이미지는 18.04, 20.04, latest등의 버전을 가질 수 있으며,
# 태그를 통해서 구체적인 버전을 지정해줄 수 있습니다.
FROM ubuntu:latest
# 기본 이미지 위에 원하는 설정을 해 줍니다.
# bash에서 실행하는 것과 똑같다고 생각하면 됩니다.
RUN apt-get -y update
RUN apt-get install -y curl
RUN curl -fsSL https://code-server.dev/install.sh | sh
# EXPOSE는 어떤 포트를 사용할지 지정하는 키워드입니다.
# 사실 설정 안 해 줘도 잘 돌아간다고 하던데, 확신은 못하겠습니다.
EXPOSE 8080
# CMD는 컨테이너가 생성되었을 때 실행할 내용입니다.
CMD code-server --host 0.0.0.0 --port 8080 /src
위는 도커 커맨드 중 아주 일부만을 이용한 것이고, 더 많은 도커 커맨드들이 있습니다. 도커 도큐먼트를 찾아봐도 좋고, Subicura님의 초보를 위한 도커 안내서 시리즈에도 정리가 매우 잘 되어 있습니다.
위 도커파일을 빌드하면 도커 이미지가 만들어집니다. 도커 이미지는 매우 효율적이지만 단순한 방법으로 만들어지는데, 다음과 같은 과정을 거칩니다.
- 임시 컨테이너 생성
- 임시 컨테이너에서
Dockerfile
에 있는 커맨드 한 줄 실행 - 임시 컨테이너의 상태를 통째로 이미지로 저장
- 단계 1로 돌아감.
그러므로 위 파일을 빌드하면 다음과 같은 과정이 이루어집니다.
ububtu:latest
이미지를 다운받아서 임시 컨테이너(a라고 하자)를 실행- a를 이미지로 저장
- a를 다시 실행하여 컨테이너를 만들고
apt-get -y update
실행 - 이것을 b로 저장
- b를 다시 실행하여 컨테이너를 만들고
apt-get install -y curl
실행 - 이것을 c로 저장
- c를 다시 실행하여 ....
- 이것을 d로...
- ...
따라서 도커 이미지를 생성할 때에는 Dockerfile
의 각 행에 해당하는 이미지가 모두 생성됩니다. 물론 매번 모든 이미지를 저장하는 것은 아니고, 이전 이미지와의 차이점만을 저장합니다. 이를 레이어라고 부르며, 도커 이미지는 베이스 이미지 위에 여러 레이어를 쌓아서 만들어진다고 볼 수 있습니다. 따라서 Dockerfile
의 어떤 행이 수정될 경우, 그 이전까지의 과정은 모두 레이어로 남아있기 때문에 이후의 과정만이 새로 수행됩니다. 그러므로 Dockerfile
을 작성할 때에는 가장 덜 자주 바뀌는 부분을 위에 두고, 자주 바뀔 것 같은 부분을 아래에 두는 것이 좋습니다.
가상 머신과의 차이점
도커와 가상 머신의 가장 근본적인 차이는 가상화 범위라 볼 수 있습니다. 기존의 가상 머신은 커널과 프로세스를 전부 가상화합니다. 반면, 도커 컨테이너에서는 커널은 호스트 컴퓨터의 것을 그대로 사용하고, 프로세스가 돌아가는 부분만이 가상화됩니다. 즉, 도커는 프로세스를 감싸는(wrapping) 얇은 가상화 계층으로 생각할 수 있으며, 컨테이너는 아주 얇은 가상화 계층으로 둘러싸인 하나의 프로세스로 생각할 수 있습니다.
따라서 도커는 OS를 가상화하지 않아 실행 속도가 아주 빠르며, 장기간의 사용뿐만이 아니라 가벼운 용도로 독립된 실행 공간이 필요할 때 매우 유용합니다. 예를 들자면 빌드된 소스코드를 테스트해야한다고 가정합시다. 테스트를 위해서는 일관된 실행 환경이 중요한데, 도커를 사용하면 매번 새로운 환경을 생성하므로 항상 일관된 실행 환경에서 테스트할 수 있습니다. 특히 도커는 실행 속도가 매우 빠르므로 테스트를 자주 하더라도 전혀 부담이 없습니다.
참고 문헌
- 도커 설치에서 운영까지(Docker Up & Running), 칼 마티아스, 션 P. 케인 지음, 박종영 역
- 초보를 위한 도커 안내서 시리즈, Subicura
- 도커 공식 도큐먼트