Search

Docker란?

Created
2021/03/23
Tags
Docker
Introduction
Virtualization
Container
Image
Docker Toolbox
Docker for Windows
Docker for Mac
Docker 클라이언트
Docker 데몬

Subjects

1. Docker를 찾아보게 된 이유

Mac OS X 환경에 푹 빠져들기 시작하면서 컴퓨터가 필요한 모든 과정에 맥을 이용하기 시작했다. 그리고 학교 커리큘럼에 있는 기계학습딥러닝을 배우면서 맥을 이용하여 딥러닝을 해보고 싶다는 생각을 많이 하게 되었다.
딥러닝에는 주로 nvidiaGPU가 선호되는데, 맥은 nvidia보다는 AMDGPU 친화적으로 아키텍쳐가 구성이 되어 있고 그러다보니 자연스럽게 맥으로 딥러닝을 하는 것에는 한계가 있다는 얘기를 많이 듣게 되었다.
따라서 nvidia2080ti가 장착된 데스크탑을 서버 PC로 두고 ssh 통신을 이용하여 접근 할 수 있도록 구상을 해보았다. 코딩은 맥에서 진행하되 학습은 데스크탑 서버를 이용하는 방식으로 말이다.
이에 대해서는 애매한 점들이 있었다. GPU가 장착된 PC를 학습용 서버로 두기 위해선 Windows보다는 ubuntu 환경이 선호된다는 것이었는데, 데스크탑의 Windows를 종종 이용해야할 때가 있었기 때문이다. 그렇다고 Windows 환경으로 서버를 두자니 ubuntu에 최적화된 것들이 많아서 결국 딥러닝 과정 중에 발생할 수 있는 제한 사항이 많아 보였다.
초기에 내린 해답은 듀얼 부팅이었다. 듀얼 부팅을 설정하고 그 환경을 먼저 탐색해보고자 몇 달 이용을 하게 되었는데 결국 세부 문제가 있었다.
듀얼 부팅을 위한 genome 설정이 까다롭다.
Graphic Driver 업데이트를 해야하는 상황에서는 부팅 오류가 잦았다.
Library, Framework들의 버전이 서로 엉키면서 Dependency 통일화가 어려웠다.
그렇게 해결책을 찾아보던 중 Docker라는 것을 찾게 되었다. Docker가 등장한 배경에는 내가 갖고 있던 문제점도 일부 있었던 것을 볼 수 있었다. 덕분에 흥미롭게 Docker에 대해서 찾아보고 알아가볼 수 있었다.

2. Docker

1) Docker와 가상화 기술

DockerGo 언어로 작성된 가상화 환경을 구축하는데 이용되는 오픈소스 프로젝트이다. 기존에 사용되던 가상화 기술에 비해서 성능 손실이 거의 없어서 많은 인프라 솔루션에 이용된다.
일반적으로 지칭하는 Docker라 함은 상황에 따라 2가지 문맥으로 이용된다. Docker Engine을 의미하거나, Docker의 여러 프로젝트들을 통칭하여 Docker라고 부른다.
Docker Compose, Private Registry, Docker Machine, KinematicDocker에는 여러 프로젝트들이 있다. 이러한 Docker 프로젝트들은 Docker Engine을 조금 더 효율적으로 사용하기 위한 것이므로 결국에 핵심은 Docker Engine이다.
기존 가상화 기술은 Virtual Machine (VM)을 이용하는, Host OS위에 Hypervisor가 존재하고 그 위에 Guest OS들이 존재하는 개념이었다. 여기서 말하는 가상화 라는 것은 현재 사용하고 있는 머신의 공간과는 독립적인 공간을 만드는 것을 의미한다.
Docker도 가상화 기술을 이용하긴 하지만 이전처럼 HypervisorGuest OS를 이용하는 구조는 아니다.
HypervisorGuest OS들을 관리하고, 시스템의 자원을 Guest OS에게 분배할 수 있도록 자원을 가상화하는 역할을 한다. 그 과정들 중에서 Host OS에서 사용되는 I/O와 같은 Device들에 대한 가상화를 수행할 때 많은 성능 저하가 일어난다. 그리고 이처럼 VM을 이용하는 경우에도 Docker를 이용할 때처럼 Image를 만들어 낼 수 있는데, Guest OSKernel을 포함한 채로 Image를 만들어내다보니 Image의 크기가 상당히 방대할 뿐 아니라 Image 생성 시간이 Kernel을 포함된 만큼 더 길다.
이러한 이유들 때문에 IntelXen, LinuxKVM과 같은 반가상화 기술에 대한 다양한 시도를 하게 되었고, 그 이후에는 Container라는 기술이 가상화 기술의 표준으로 자리 매김하게 되었다.
ContainerLinux상에서 이용되고, chroot, namespace, cgroup 등의 Linux 자체 기능만으로 구현되어 프로세스 단위로 독립적인 공간을 만드는 기술이다. DockerContainerLinuxContainer를 이용한 것이고, LinuxContainer에 여러 기능을 추가하여 어플리케이션을 Container로 조금 더 이용하기 쉽게 만들어 둔 것이다.

2) Docker의 장점

Container를 이용하기 위한 기술들은 OpenVZ, LXC, cri-o 등이 존재하지만 그 중에서도 DockerContainer의 표준으로 자리 잡았다. Docker를 이용하면 어떤 점이 좋길래 많은 회사에서 Docker를 이용하고 있을까?

개발 및 운영 환경의 통합

Docker를 이용하여 환경을 구축하고 싶다면, 환경을 구성한 ContainerImage로 만든 뒤 해당 Image를 구축하려는 환경에 전달만 해주면 된다. 이렇게 구축된 환경은 다른 공간에서 이용되는 환경과 별개로 독립적임과 동시에, 동일한 Image를 사용한다면 동일한 환경을 만들 수 있으므로 개발 및 운영 환경에 대한 통합이 가능해진다는 장점이 있다.

빠른 배포 속도

차후에 ContainerImage를 더 자세히 다루겠지만, DockerImage는 여러 Layer로 구성되어 중복되는 Layer에 대해선 재사용할 수 있도록 만들어졌다. 예를 들어서 1번 Image[A, B, C]라는 Layer로 구성되어 있고, 2번 Image[A, B, D]라는 Layer로 구성되어 있다고 해보자. 이 때의 Image[A, B, C][A, B, D]처럼 중복된 Layer의 형태로 존재하는 것이 아니라, [A, B, C, D]의 형태로 Layer를 유지하여 각 Layer를 사용하는 Image의 이름을 LayerTag로 남기는 식으로 이용된다. 따라서 중복된 Layer를 새롭게 만들 필요가 없기 때문에 배포 시에 많은 이점을 갖는다.

작은 Image 용량

위의 VM을 이용한 부분에서도 밝혔듯이, ContainerImage로 만들 때는 Kernel에 대한 내용들을 포함할 필요가 없기 때문에 VM에 비해서 Image의 크기가 작다.

서비스의 독립성, 확장성, 편의성

어플리케이션의 서비스를 유지하는 구조는 MonolithicMicro Service로 나뉜다. 위 그림처럼 서비스에 요구되는 모듈이 A, B, C일 때 이를 하나의 프로그램으로 구동시키는 것이 Monolithic 구조이고, 각 모듈들이 여러 프로그램으로 나뉘어 구동되는 방식이 Micro Service 구조이다. 서비스를 운영할 때 Monolithic 구조를 갖고 있다면 하나의 Container를 이용할 것이고, Micro Service 구조라면 여러 Container를 이용하게 될 것이다.
DockerContainer는 짧은 시간 내에 생성하고 시작시킬 수 있는데다가 독립적인 환경 구축이 가능하므로 여러 Container를 두는 식의 Micro Service 구조를 이용할 때 많이 사용된다. 따라서 Micro Service 구조에서 얻을 수 있는 독립성, 확장성, 편의성이라는 이점을 가질 수 있다.
서비스가 소규모 단위라면 Monolithic 구조가 조금 더 득을 볼 수 있겠지만, 서비스가 거대해질 수록 소프트웨어의 확장성과 유연성이 줄어들게 되므로 이 때는 Monolithic 구조보다는 Micro Service 구조를 더 많이 이용한다.
독립성, 확장성, 편의성은 서비스 관리 단위에서 확인할 수 있다. 각 서비스를 유지하고 있는 Container를 구성할 때, 서비스 단위의 독립적인 공간을 두고 각자의 환경을 만들기 때문에 Container 내부 코드에 대해선 각 언어가 달라도 무방하게 된다. 따라서 특정 언어에 종속되지 않는다는 장점이 있다. 또한 서비스 단위로 Container들을 구성했다는 것은 서비스의 변화가 생길 때 이를 확장하기 쉬워지므로 변화에 민감하게 대처할 수 있게 된다. 즉, 서비스를 편리하게 관리할 수 있게 된다.

3) Docker Toolbox, Docker for Windows / Mac

Docker를 시스템에서 이용하는 방법은 간단하다. DockerLinux 상의 Container를 이용하기 때문에 Linux 환경을 잡아주면 된다. 즉, Window 혹은 Mac OS X를 사용하더라도 별도의 Linux 환경이 필요하다는 것이다. 단, 옛날 버전과 현재 버전에 따라 DockerLinux 환경 구축 방법이 별도로 존재한다.
꽤 옛날 버전의 Docker를 설치할 때, Linux 환경 구축을 위해 VirtualBox를 이용했고, 이를 Docker Toolbox라고 한다. 최신 버전의 Docker를 설치할 때는 Docker for Windows 혹은 Docker for Mac을 이용하는데, 이는 Docker Toolbox와는 달리 별도의 VM을 사용하지 않고 각 운영체제에서 지원하는 가상화 기술을 사용하여 Linux 환경을 구축한다. Windows의 경우에는 Hyper-V라는 가상화 기술을 이용하여 별도의 Linux 환경을 만들어두고, Mac OS X의 경우에는 Xhyve라는 가상화 기술을 이용하여 별도의 Linux 환경을 만듦으로써 Docker 설치 시 Docker Engine을 둘 수 있게 된다.
Docker Toolbox를 이용하여 Linux 환경을 만드는 것과 운영체제 자체에서 지원하는 가상화 기술로 Linux 환경을 만드는데는 어떤 차이가 있을까? Docker Engine이 반드시 Linux 상에서 동작하기 때문에 Linux 환경을 이용한다는 공통점이 있지만, 대표적인 차이점으로는 Image를 이용하여 Container를 만들어 동작시킬 때 네트워크 설정에 있다.
위 그림과 같이 Docker Toolbox를 이용하는 경우에는 VirtualBox를 이용하여 Docker Engine을 두게 되므로 VM에 해당하는 가상 네트워크 1개와 VM에서 동작하는 Container의 가상 네트워크 1개가 생성된다. 따라서 Container에 서비스를 구축하여 외부에서도 접근할 수 있게 하려면 2중 포트 포워딩이 요구된다. (머신과 VM 사이의 포트 포워딩, VMContainer 사이의 포트 포워딩) 이 때, VMContainer 사이의 포트 포워딩은 Container 생성 시 쉽게 설정할 수 있기 때문에 Docker Toolbox를 이용하는 경우에는 머신과 VM 사이의 포트 포워딩 설정에만 유의해주면 된다.
반대로 운영체제 자체에서 지원하는 가상화 기술의 경우에는 VM을 이용하지 않기 때문에 Container에 해당하는 가상 네트워크 1개만 생성된다. 이는 Container 생성 시 쉽게 포트 포워딩에 대한 설정이 가능하다.
운영체제 자체에서 지원하는 가상화 기술을 이용하는 경우에는 VM을 이용할 때처럼 Ubuntu, CentOS와 같은 Kernel이 통째로 이용되지 않는다. Docker Engine을 구동하기 위한 기능만 존재하면 되기 때문에, Linuxkit이라고 불리는 최소한의 기능을 유지하고 있는 Kernel을 이용하여 Linux 환경을 구축한다.
따라서 Docker Toolbox 보다는 이용 중인 운영체제에 해당하는 Docker for Win 혹은 Docker for Mac으로 Docker를 설치하는 것이 더 낫다.

Windows

Mac OS X

4) Docker 클라이언트 & 데몬

Docker를 이용하기 위해 일반적으로 docker라는 명령어를 이용하는데, 위 그림과 같이 확인해보면 docker 명령어의 위치를 확인할 수 있다. docker라는 명령어는 일반 파일로써 유지되고 있는 것을 확인할 수 있다.
반면에 프로세스로 동작하는 Docker를 확인해보면, 이는 dockerd로 동작하는 것을 확인할 수 있다. dockerdDocker 데몬이며, 수행해야할 요청을 받음과 동시에 Docker Engine의 기능을 수행 후 응답하는 프로세스이다.
Daemon Process? 데몬 프로세스의 사전적 정의는 지속적으로 실행되어 서비스 요청에 응답하는 백그라운드 프로세스이다. 데몬 프로세스는 백그라운드 프로세스들 중에서도, 부모 프로세스를 갖지 않아 PPID (Parent Process ID)1이거나 다른 데몬 프로세스를 부모로 갖는 프로세스를 지칭한다. 이는 쉘이 종료되었을 때 쉘에서 실행된 자식 프로세스들이 모두 종료되는 현상으로 데몬 프로세스도 종료되는 현상을 방지하기 위해서이다. 데몬 프로세스는 일반적으로 이름의 끝에 d를 달고 있으며, 시스템이 시작될 때 자동으로 시작하는 경우가 많다.
docker 라는 명령어와 dockerd라는 데몬 프로세스는 어떤 관계가 있을까? Docker는 실제로 클라이언트로 동작하는 Docker와 서버로 동작하는 Docker로 구분된다. 사용자가 명령어를 입력하는 CLI 프로그램이 Docker 클라이언트이며 되고, API를 외부로부터 받아 Container를 생성하고 실행하는 등 전반적인 Docker Engine의 기능을 수행하는 데몬 프로세스Docker 데몬이 된다.
Docker 데몬은 클라이언트와 서버의 관계에서 일종의 서버라고 보면 된다.
Docker 데몬API를 입력을 받아 Docker Engine의 기능을 수행한다고 했는데, 여기서의 API가 곧 Docker 클라이언트에 사용자가 명령어로 입력한 것들이 된다. Docker 클라이언트는 사용자가 입력한 명령어를 읽어들인 뒤, 그대로 Docker 데몬에게 보내게 되고, Docker 데몬은 이를 파싱하여 명령어에 해당하는 Docker Engine의 기능을 수행하게 된다. 이 때 Docker 클라이언트Docker 데몬이 같은 Host에 위치해 있다면 Docker 클라이언트Docker 데몬Unix Socket을 이용하여 소통하게 된다.
동일한 Host에 위치한 클라이언트와 데몬은 Unix Socket을 이용한다고 했는데, 위 그림을 보면 알 수 있듯이 해당 Socket은 시스템 상에 파일로써 유지되고 있다.
그렇다면 동일하지 않은 Host에 각각 Docker 클라이언트Docker 데몬이 위치하고 있다면, 이 때도 Unix Socket을 이용하여 소통하게 되는 것일까? 그렇지 않다. 서로 다른 Host에 각각 클라이언트와 데몬이 있다는 말은 곧 서로 다른 머신에 클라이언트와 데몬이 존재한다는 말과 동일하므로 Unix Socket으로 상호 통신이 불가능하다. 따라서 이 때는 TCP라는 네트워크 통신을 이용하여 명령어를 전달하게 된다.
동일한 HostDocker 클라이언트Docker 데몬이 위치한 경우에 대한 구조는 Local Host의 부분처럼 나타나고, 명령어에 대한 처리 흐름은 아래와 같다.
1.
사용자가 docker 관련 명령어를 입력한다.
2.
Docker 클라이언트/var/run/docker.sock이라는 Unix Socket을 이용하여 Docker 데몬에게 명령어를 전달한다.
3.
Docker 데몬은 전달 받은 명령어를 파싱하고 명령어에 해당하는 기능을 Docker Engine을 통해 수행한다.
4.
기능 수행의 결과를 Docker 클라이언트에게 전달한다.
5.
Docker 클라이언트는 전달 받은 결과를 출력하여 사용자가 볼 수 있도록 해준다.
동일하지 않은 Host에 각각 Docker 클라이언트Docker 데몬이 위치한 경우에 대한 구조는 주어진 그림의 Remote Host의 부분과 같으며, Unix Socket을 이용하지 않는 점만 빼면 명령어에 대한 처리 흐름은 동일한 Host에서 동작하는 경우와 같다.

5) Docker 데몬 실행 및 설정

Docker 데몬 실행

Linux 환경에서 Docker 데몬을 실행할 수 있는 방법에는 일반적으로 Docker 데몬을 직접적으로 실행시키는 방법과 그렇지 않은 방법으로 나뉜다. 직접적으로 실행시킨 것은dockerd라는 명령어를 이용한 것이고, 그렇지 않은 경우는 service, systemctl과 같은 명령어를 통해 실행시키는 것이다. service, systemctl과 같은 명령어로 Docker 데몬을 실행하면 이는 Linux 서비스로써 관리하게 된다. dockerd라는 명령어로 직접 Docker 데몬을 실행하게 되면 데몬 프로세스가 포그라운드로 동작하기 때문에 Docker의 디버깅 및 트러블 슈팅에는 도움이 될 수 있지만 운영 및 관리에 적합한 방법은 아니므로 운영 환경에서는 dockerd라는 명령어로 직접 실행하는 것보다는 Linux 서비스로 관리하는 것이 더 좋다.
Docker 데몬이 실행 중에 어떤 문제가 생기는지 디버깅 및 트러블 슈팅을 하는 것이 실제 서비스 운영에서는 중요하기 때문에 데몬 프로세스로 구동하면서도 docker events와 같이 Docker Engine 자체에서 제공하는 Docker 데몬Logging 툴 혹은 외부 솔루션으로 제공되는 Docker 데몬을 위한 Logging 툴을 이용하는 것도 중요하다.
현재 글에서는 Docker for Mac을 이용하고 있기 때문에 위처럼 Linux의 명령어를 이용한 것이 아닌, Docker Desktop을 이용하여 Docker 데몬을 다루는 방법을 다뤄보고자 한다.
Docker for Mac을 설치하면 위 그림에서 보이는 것처럼 Docker Desktop이 함께 설치되는데, 이를 실행하게 되면 Docker 데몬도 함께 실행된다. Docker Desktop이 실행되지 않은채로 명령어를 실행하게 되면 아래 그림처럼 unix:///var/run/docker.sock에 연결할 수 없어 요청이 불가능한 것을 확인할 수 있다.
Docker Desktop을 실행하면 그 과정에서 Docker Engine도 함께 실행된다. 따라서 아래 그림처럼 명령어를 실행하면 그제서야 요청이 잘 전달되어 그 결과를 확인할 수 있게 된다.

Docker 데몬 설정

dockerd 명령어에는 정말 다양한 옵션이 있고, 이를 통해 Docker 데몬의 기본 설정 값을 바꿀 수 있다. 하지만 이는 Linux 상에서의 설정이므로 Docker for Mac을 이용할 때는 Docker Desktop을 통해 Docker 데몬의 설정 값을 조작할 수 있다.
Preferences 탭을 열게 되면 다양한 설정이 가능한데, 주로 Docker에서 이용할 자원 제한 및 Docker Engine에 대한 설정을 할 수 있다. 그 중에서도 Docker Engine에 대한 설정이 곧 Docker 데몬에 대한 설정이 되며, json 형태로 설정 항목 및 값을 입력 후 Apply & Restart를 누르면 Docker 데몬이 재시작되면서 설정이 반영된다.
설정 항목 및 값은 위 링크에서 상세하게 확인할 수 있으며, Docker Desktop에서 이용하는 daemon.json 파일은 ~/.docker/daemon.json으로 유지되고 있다. Mac OS의 경우에 Linux에서 사용할 수 있는 항목 중 일부 제한된다.

3. Commands

Docker의 명령어는 그 대상에 따라서 명령어 구성이 바뀐다. 예를 들면, Container를 대상으로 하는 명령어는 docker container ... 과 같은 형태가 되고, Image를 대상으로 하는 명령어는 docker image ... 과 같은 형태를 갖는다. 이를 Management Commands라고 한다. 일반적인 명령어는 docker ... 같은 형태를 가지며 아래에서 소개하는 명령어 외에도 정말 많은 명령어들이 있기 때문에 정확한 정보들은 아래 링크의 공식 도큐멘트의 도움을 받도록 하자.

1) 버전 확인

docker version docker -v

2) Docker Hub 로그인

docker login
Docker Hub에서 Image를 업로드 혹은 다운로드 하기 위해서 Docker Hub에 반드시 로그인을 해야 한다.

3) Docker Hub 로그아웃

docker logout

4. Reference