위 그림은 전통적인 컴퓨팅 아키텍처와 가상화된(virtualized) 컴퓨팅 아키텍처를 각각 나타낸 것이다.
전통적인 컴퓨팅 아키텍처를 나타낸 왼쪽 그림에는 하드웨어 위에 하나의 OS가 올라가 있지만, 가상화된 컴퓨팅 아키텍처를 나타낸 오른쪽 그림은 각각의 가상머신(VM) 안에 개별적으로 guest OS가 하나씩 들어가 있다.
Virtualization이 필요한 이유는 application들이 OS, 하드웨어 등에 종속적일 수 있기 때문이다. 왼쪽 그림에서는 특정한 하드웨어와 OS 환경 안에서만 동작할 수 있는 어플리케이션을 올릴 수 있지만, 오른쪽 사례에서는 각각의 virtual machine에서 각기 다른 환경을 구축함으로써, OS와 하드웨어 의존성을 끊어내고 더욱 유연하게 어플리케이션을 동작시킬 수 있다.
이처럼, virtualization이란 서버, 스토리지, 네트워크와 같은 실제의 것을 가상화된 형태로 사용하는 기술을 말하며, 이로써 서비스를 그 밑바탕의 물리적인 의존성으로부터 분리할 수 있다.
서비스를 물리적인 의존성과 분리한다는 것은 또다른 장점을 가져온다. 다양한 어플리케이션을 동작시키기 위해 (윈도우 서버, 리눅스 서버 등을 따로 두는 식으로) 여러 개의 하드웨어/OS를 사용하지 않아도 되니, 더욱 유연하고 효율적인 자원 분배가 가능하다. 서비스의 출렁이는 수요에 따른 scaling도 용이하다. 그에 따라 IT 인프라 비용도 감소시킬 수 있다. 어플리케이션 간 의존성 충돌도 예방할 수 있다.
Virtualization을 위해 virtual machine(VM)을 사용할 수 있지만, VM을 도입함으로써 잃는 비용이 있다. 예를 들면 모든 각각의 VM이 OS 전체를 하나씩 들고 있어야 한다는 점, 중복된 라이브러리와 바이너리 파일들이 생길 수 있다는 점, VM 하이퍼바이저를 운영해야 한다는 점 등에서 컴퓨팅, 메모리, 스토리지, 의존성 관리에 투입되는 비용 등이 생기게 될 것이다.
컨테이너는 코드와 그 모든 의존성을 패키징함으로써, 어플리케이션이 여러 컴퓨팅 환경에서 동작할 수 있도록 하는 소프트웨어의 표준 단위이자 어플리케이션 단위의 추상화이다.
다수의 컨테이너들은 하나의 물리적 머신에서 OS 커널을 공유할 수 있으며, 마치 독립된 개별의 프로세스와 같이 동작한다.
즉, 호스트 머신에서 돌아가는 다른 모든 프로세스들과 독립적이다.
컨테이너가 등장하게 된 배경은 위 언급했던 VM에서의 비용과 일맥상통한다. Guest OS로 인한 오버헤드 때문에, 더 경량화된 virtualization 방법을 찾게 된 것이다.
따라서 container 기술이 등장하였으며, container 기술에서는 각각의 container가 개별적인 OS를 가지지 않고, 하나의 OS 커널을 공유하며 컨테이너 엔진이 자원을 나눠준다.
하지만 컨테이너 기술이라고 해서 장점만 있는 것은 아니다. 경영 관점에서 보면 컨테이너 기술을 잘 모르는 직원이 있다면 컨테이너 기술에 대해 교육해야 하고, 때에 따라서는 컨테이너 기술을 도입함에 따라 어플리케이션의 구조를 변경해야 하는 일들이 있을 수 있다.
docker는 컨테이너 기술을 활용한 플랫폼으로, Go 언어로 작성되었다.
docker는 IaC
개념을 도입해 어플리케이션 아래의 인프라를 쉽게 관리하도록 한다.
IaC란 Infrastructure as Code의 줄임말으로, 코드를 이용해 어플리케이션 로직을 짜듯이, 코드를 이용해 어플리케이션에서 요구되는 인프라 시스템 상태와 구조를 선언적으로 관리해줄 수 있다.
즉, 예를 들어 네트워크 설정하고, DB를 올리고, 톰캣을 켜고, 서버를 올리고, 초기화하는 과정들을 하나하나씩 나열하는 것이 아니라 내가 원하는 인프라 상태만을 configuration 파일에 코드 형태로 적을 수 있는 것이다.
무슨 이야기인지 궁금하다면, 아래 예시 3
에서 compose.yaml
파일이 어떻게 적혀 있는지 잠깐 구경하고 와도 좋을 것이다.
컨테이너 기술을 통해 어플리케이션과 인프라를 분리함으로써, 어플리케이션을 다양한 환경에서 개발하고, 구동시키고, 배포하고, 다른 환경으로 이식하는 일들을 더욱 편리하도록 한다.
또한 VM에 비해 virtualization 비용이 적다.
인프라 구조를 파일로 관리함으로써 관리가 용이하며, 이미 만들어진 docker image를 활용해 인프라를 구축할 수 있어 편리하다.
Kubernetes와 같은 다른 orchestration 소프트웨어와 조합하면, 서비스를 배포, 업데이트, 버전 관리하기 편리하다.
더 적은 하드웨어 스펙을 가지고도 비즈니스 목표를 달성할 수 있다.
다양한 종류의 머신에서 동작할 수 있으며, 거의 실시간으로 scaling을 할 수 있도록 돕는다.
Docker는 client-server 아키텍처를 사용한다.
클라이언트와 서버는 같은 머신일 수도 있고, 다른 머신일 수도 있다.
서버 역할을 하는 docker host에는 docker daemon(dockerd
)이 있으며, 이는 docker client(docker
)로부터 명령어를 받아 그 명령어를 처리한다.
하나의 docker daemon에는 클라이언트 역할을 하는 docker client를 여러 개 붙일 수 있다.
그리고, docker는 아래와 같이 CLI와 GUI 클라이언트 환경을 모두 지원한다. (각각 Docker Engine, Docker Desktop)
위는 Docker의 registry, image, container의 관계를 설명한 그림이다.
Docker registry는 마치 소스코드의 repository와 유사한 개념으로, docker image(이후 더 설명할 예정)의 저장소이다.
가장 대표적인 docker registry는, 위 사진에도 나와 있는 docker hub으로, docker의 공식 registry이다.
아래와 같이 alphine linux, nginx, busybox, ubuntu, python 등 다양한 이미지들을 공식으로 제공하고 있는 것을 확인할 수 있다.
Docker image에는 하나의 프로그램이 돌아가기 위해 필요한 모든 것(코드, 런타임, 라이브러리, 환경변수, 각종 파일, 등등)이 포함된다.
Docker application은 docker image만으로는 실행될 수 없으며, 소스코드를 실행시켜야 프로세스가 되는 것과 같이, docker image를 읽어 docker container로 만들어야만 실행이 가능하다.
Docker image는 Dockerfile
을 통해 build되어 만들어지며, 앞으로 설명할 Dockerfile
과 docker compose의 compose.yaml
등에서 이 이미지를 이용하여 서비스를 정의한다.
도커 이미지가 프로그램이라면, 도커 컨테이너는 그 프로그램을 실행시킨 프로세스에 비유할 수 있다.
Docker container는 위 그림과 같은 lifecycle을 가지며, container의 상태를 바꾸기 위해서는 화살표에 적힌 명령어들을 이용하면 된다.
예를 들어, container를 실행시키려면 docker run <container>
, container를 삭제하려면 docker rm <container>
와 같이 사용하면 된다.
아래와 같이 터미널 명령어를 입력하면 hello-world
컨테이너를 실행시킨다.
> sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1c
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
sudo docker run hello-world
는 hello-world
라는 이름의 이미지를 찾아 실행하라는 명령어이다.
docker run
을 하면 로컬, docker hub 순서대로 image를 찾아본다.
위 사례에서는 로컬에 캐싱된 hello-world
이미지를 찾지 못했기 때문에 docker hub에서 hello-world
이미지가 있는지 찾아보았고, docker hub에 hello-world
이미지가 존재하는 것을 확인한 뒤 이미지를 다운로드(pull)했다.
앞선 예시 1과 같이 docker run <image>
명령어로 container를 실행시킬 수도 있지만, 아래와 같이 사용하고자 하는 image와 실행 환경을 코드의 형태로 표현해줄 수도 있는데, 이것을 Dockerfile
이라고 한다.
Dockerfile
을 사용하기 위해서는 디렉토리에 Dockerfile
이라는 확장자가 없는 파일을 생성한 뒤 그곳에 아래와 같은 명령어들을 적어놓으면 된다.
# Dockerfile
FROM python:3.10.7
LABEL author="yechan24680@gmail.com"
RUN pip3 install django==4.1
COPY ./mysite /app
WORKDIR /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
EXPOSE 8000
위 Dockerfile
을 아주 쉽게 해석하면 다음과 같다. (생략된 것이 많으니 공식 문서를 참고하자.)
FROM python:3.10.7
: python docker image를 사용하여, 이를 기반으로 서비스를 만든다. python의 버전은 3.10.7으로 한다.RUN pip3 install django==4.1
: docker container 안의 가상환경에서 pip3 install django==4.1
이라는 터미널 명령어를 실행하고 이미지 레이어를 만들어라. (이를 통해 django가 4.1 버전으로 설치된다)COPY ./mysite /app
: Dockerfile이 위치한 클라이언트 현재 디렉토리를 기준으로 ./mysite
에 있는 파일들을 docker container 안 가상 환경의 /app
디렉토리에 복사하라.WORKDIR /app
: Dockerfile에서 사용할 working directory를 /app
으로 설정하라.CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
: docker container 안의 가상환경에서 python manage.py runserver 0.0.0.0:8000
이라는 터미널 명령어를 실행하라.EXPOSE 8000
: docker container 안의 가상환경에서 8000번 포트를 열어라.이 터미널 명령어는 Dockerfile
파일이 위치한 디렉토리에서 실행한다.
sudo docker run -d -p 8000:8000 yechan/todolist
이 명령어를 실행한 뒤, http://localhost:8000
을 방문하면 to do list 웹페이지를 확인할 수 있다.
docker compose는 여러 개의 컨테이너로 구성된 docker 어플리케이션을 정의하고 실행하는 도구이다.
docker compose를 통해 여러 개의 컨테이너가 서로 상호작용하는 서비스를 만들 수 있다.
# Dockerfile - db
FROM mariadb
LABEL author="yechan24680@gmail.com"
ENV MARIADB DATABASE todolistdb
ENV MYSQL_USER root
ENV MARIADB_ROOT_PASSWORD root
VOLUME [ "./image/mariadb/data:/var/lib/mysql" ]
VOLUME [ "./image/mariadb/mysqld:/var/run/mysqld" ]
EXPOSE 3306
# Dockerfile - web
FROM python:3.10.7
LABEL author="yechan24680@gmail.com"
RUN pip3 install django==4.1
COPY ./mysite /app
WORKDIR /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
EXPOSE 8000
# compose.yaml
services:
db:
image: yechan/todolistdb
web:
image: yechan/todolist:2
depends_on:
- db
restart: on-failure:20
ports:
- 8000:8000
위와 같이 Dockerfile
두 개를 만들어 registry에 업로드한 뒤, 위와 같은 compose.yaml
을 작성한다.
compose.yaml이 위치한 디렉토리에서 아래 명령어를 실행한다.
sudo docker pull yechan/todolistdb
sudo docker pull yechan/todolist:2
sudo docker compose up -d
그러면 DB가 먼저 실행되고, 그 DB에 웹서버가 접속해 서비스를 시작한다.
A Guide to Virtual Machines(VM)
Virtualization in Cloud Computing and Types
A Comparative Study of Containers and Virtual Machines in Big Data Environment