Medium - A Beginner-Friendly Introduction to Containers, VMs and Docker

원문 - Preethi Kasireddy

Trend 파악을 위한 Medium 기고문 포스팅 - 초심자를위한 컨테이너, VM, 도커 개요

Source: https://flipboard.com/topic/container

여러분이 기술자거나 프로그래머라면 도커에 대해서 들어본 적이 있으실 겁니다. packing, shipping, 그리고 응용프로그램 실행을 컨테이너 내부에서 할 수 있도록 도와주는 도구이죠. 모든 개발자나 시스템 관리자들의 관심이 모아지고 있기 때문에 사용해보지 않을 수가 없죠. 거대기업인 구글과 VMware, 그리고 아마존까지 도커를 지원하기 위한 서비스를 구축하고 있습니다.

여러분의 생각속에 당장 도커의 활용 방안이 떠오르지 않더라도 VM과 비교하여 컨테이너와 관련된 기본 개념을 이해하는 것은 중요하다고 생각합니다. 인터넷에는 도커 사용을 위한 멋진 사용법으로 많지만 초심자에게 친화적인; 개념적인 설명과 컨테이너가 무엇으로 이루어져 있는지 알려주는 가이드는 없었습니다. 그래서 이 포스트가 도움이 되었으면 좋겠습니다. 그럼 VMs와 컨테이너가 뭔지 알아봅시다.

What are “containers” and “VMs”?

컨테이너와 VMs들은 비슷한 목적을 가지고 있습니다; 응용프로그램 자체를 독립적으로 만들어 어디에서나 실행할 수 있도록 만드는 것이죠. 추가적으로 컨테이너와 VMs은 실제적인 하드웨어가 필요없습니다. 따라서 더욱 효율적으로 컴퓨터 자원을 사용할 수 있죠. 에너지 소비나 비용적인 측면에서 말이죠. 컨테이너와 VMs의 주요한 차이점은 그들의 설계적인 접근법에 있습니다. 한번 자세히 보시죠.

Virtual Machines

원래 VM은 실제 컴퓨터처럼 프로그램을 실행시키는 에뮬레이터였습니다. VMs은 hypervisor를 사용해서 실제 컴퓨터의 최상단에서 실행합니다. 하이퍼바이저는 번갈아 가면서 호스트 머신과 bare-metal에서 동작합니다. 전문용어를 좀 더 쉽게 풀어봅시다.

하이퍼바이저는 소프트웨어, 펌웨어, 하드웨어의 조각으로 VMs에서 동작하고 있습니다. 하이퍼바이저는 호스트 머신이라고 언급된 실제 기기에서 자체적으로 돌아갑니다. 호스트머신은 VMs에게 CPU나 램메모리 같은 자원을 제공합니다. 이런 자원들은 VM이 사용하는 것과 분리할 수도 있고 여러분이 원하는 대로 분산시킬 수도 있습니다. 그래서 하나의 VM이 많은 리소스가 필요한 응용프로그램을 실행하고 있다면 같은 호스트머신에서 동작하는 VM보다 더 많은 자원을 할당하셔야 할 것입니다.

VM은 호스트머신에서 하이퍼바이저를 사용해서 돌아가고 게스트 머신이라고도 불립니다. 게스트 모신은 응용프로그램과 응용프로그램 실행에 필요한 것들을 모두 가지고 있습니다(시스템 바이너리, 라이브러리 등) 또한 자체적인 가상 하드웨어 전체를 들고 다니는데 가상 네트워크 어댑터, 저장소, CPU 등으로 구성되며 게스트머신 자체적으로 독립할 수 있는 운영시스템도 가지고 있습니다. 내부적으로 게스트머신은 자신의 자원을 가지고 열심히 동작하는 것처럼 보입니다. 밖에서는 아시다싶이 호스트 머신의 자원을 공유하는 VM이죠.

위에서 언급했지만 게스트 머신은 호스팅된 하이퍼바이저나 bare-metal 하이퍼바이저에서 동작할 수 있습니다. 여기에는 중요한 차이점이 있습니다.

먼저 hosted 가상화 하이퍼바이저는 호스트 머신의 운영체제에서 돌아갑니다. 예를들어 OSX에서 돌아가는 컴퓨터가 운영체제 위에 VM을 설치할 수 있습니다. 그 VM은 하드웨어에 직접 접근할 수 없기 때문에 호스트의 운영체제를 통해 접근해야 합니다.

hosted 하이퍼바이저의 이점은 하드웨어 밑단에 대해서는 덜 중요하다는 것입니다. 호스트 운영체제가 하드웨어 드라이버와 같은 책임을 모두 담당하고 하드웨어 호환성 측면에서 더욱 좋습니다. 반면에 이것은 하드웨어와 하이퍼바이저 사이에 추가적인 레이어를 만들어 자원 오버헤드를 만들고 VM의 성능이 떨어지게 됩니다.

bare metal 하이퍼바이저는 호스트 머신에 VM을 설치하고 하드웨어를 사용해서 발생하는 성능문제를 해결하려고 했습니다. 인터페이스가 직접 하드웨어 밑단으로 갈 수 있기 때문에 하드웨어를 실행하는 데 호스트 운영체제가 필요하지 않습니다. 이 경우에는 호스트 머신의 서버에 운영체제로 설치하는 것이 하이퍼바이저가 될 것입니다. 호스팅된 하이퍼바이저와 달리 bare-metal 하이퍼바이저는 자체적인 장치 드라이버가 있고 컴포넌트와 직접 의사소통을 할 수 있습니다. 이 때문에 성능과 확장성 안정성이 더욱 좋아졌습니다. 그 대가로 하이퍼바이저에 장치 드라이버를 많이 설치해야해서 하드웨어 호환성이 떨어집니다.

하이퍼바이저에 대해 여기까지 얘기하고 나면 아마 여러분은 왜 추가적인 하이퍼바이저 레이어가 호스트머신과 VM사이에 있는지 궁금하실 겁니다. 꼭 있어야 할 필요가 있나??

글쎄요, VM은 자체적인 운영체제를 가지고 있고 하이퍼바이저는 VMs에게 플랫폼을 관리하고 게스트 운영체제를 실행하는 기본적인 역할을 맡고 있습니다. 하이퍼바이저는 호스트컴퓨터가 자원을 VM에게 공유할 수 있도록 해주며 게스트들이 호스트머신 상단에서 실행될 수 있게 해줍니다.

Virtual Machine Diagram

다이어그램에서 보시듯이 VMs는 각각 새로운 VM마다 가상 하드웨어, 커널, 사용자 공간을 가지고 있습니다.

Container

VM이 하드웨어 가상화를 제공하는 것과 달리 컨테이너는 “user space”라는 추상화를 통해 운영체제 레벨의 가상화를 제공합니다. 컨테이너와 관련된 용어를 설명해드리면 이게 무슨말인지 아시게 될겁니다.

모든 의도와 목적에서 컨테이너는 VM처럼 보입니다. 예를들어 컨테이너는 프로세싱을 위한 비밀 공간을 가지고 있고 루트처럼 명령어를 실행할 수 있으며 비공개 네트워크 인터페이스와 IP 주소를 가지고 있어서 커스텀 라우트와 iptable 규칙을 적용할 수 있고 파일시스템을 마운트 할 수도 있습니다.

VMs와 컨테이너 사이의 가장 큰 차이점은 컨테이너는 다른 컨테이너들과 호스트 시스템의 커널을 공유한다는 것입니다.

Container Diagram

다이어 그램에서는 user space만 담고있고 VM처럼 커널이나 가상 하드웨어 같은 것은 없습니다. 각 컨테이너들은 독립적인 유저 스페이스가 있어서 다수의 컨테이너 들이 단일 호스트 머신에서 돌아갈 수 있습니다. 운영체제 레벨의 구조를 보시면 컨테이너들 끼리 공유되고 있다는 것을 볼 수 있습니다. 각각 생성되는 부분은 bins/libs 입니다. 이것 때문에 컨테이너가 매우 가벼워 지는 것이죠.

Where does Docker come in?

도커는 리눅스 컨테이너를 기반으로 한 오픈소스 프로젝트 입니다. 네임스페이스나 그룹 컨트롤 같은 리눅스 커널의 특징을 사용해서 운영체제 위에서 컨테이너를 만들 수 있습니다. 컨테이너는 그렇게 새로운 것은 아닙니다. 구글은 자체적인 컨테이너 기술을 오랜시간동안 보유하고 있었습니다. 솔라리스 존, BSD jails, LXC를 포함해서 다른 리눅스 컨테이너 기술들 또한 예전부터 있었습니다.

그럼 도커가 갑자기 왜 이렇게 핫해진 걸까요?

Ease of use

도커는 개발자, 시스템 관리자, 아키텍쳐 등 누구나 사용하기 쉽게 만들어졌습니다. 그래서 응용프로그램을 쉽게 빌드하고 빠르게 테스트 할 수 있는 것이 컨테이너의 장점이 되었죠. 누구나 자신의 노트북에서 응용프로그램을 패키징 할 수 있고 수정없이 공공 클라우드, 비공개 클라우드, bare metal에서 동작합니다. 만트라는 한번 빌드하면 어디서나 동작된다는 것입니다.

Speed

도커 컨테이너는 매우 가볍고 빠릅니다. 컨테이너들이 그냥 실행 환경을 가지고 커널에서 돌아가는 모래생자와도 같기 때문에 자원을 적게 사용합니다. 몇초만에 도커 컨테이너를 만들고 실행할 수 있으며 VMs와 비교하면 엄청나게 빠른 것입니다. VMs는 매번 가상 운영체제를 부팅해야 하기 때문입니다.

Docker Hub

도커 사용자는 도커 이미지를 위한 앱스토어와 같이 엄청나게 풍부한 도커 허브 환경의 혜택을 받을 수 있습니다. 도커 허브는 커뮤니티에서 생성되고 바로 사용할 수 있는 수천개의 공공 이미지가 있습니다. 여러분의 목적에 맞는 이미지를 찾는 것은 매우 쉬우며 그냥 내려받아서 아무 수정없이 바로 사용하면 됩니다.

Modularity and Scalability

도커는 응용프로그램의 기능을 개별적인 컨테이너에 넣기 쉽도록 만들어 줍니다. 여러분이 하나의 컨테이너에서 돌아가고 있는 Postgres 데이터베이스가 있고 레디스 서버가 다른 곳에있고 또 다른 곳에 Node.js App이 있을 수 있습니다. 도커와 함께라면 이런 컨테이너를 연결해서 여러분의 응용프로그램을 만들기가 매우 쉬우며 확장성과 컴포넌트별로 독립적인 업데이트가 쉽습니다.

마지막으로 누가 이런 파란색 도커 고래를 싫어하겠나요? :D

Source: https://www.docker.com/docker-birthday

Fundamental Docker Concepts

이제 큰 그림이 잡히셨을 테니 도커의 기본 부분을 알아봅시다

Docker Engine

도커 엔진은 도커가 실행되는 레이어입니다. 가볍게 실행되며 컨테이너, 이미지, 빌드 등을 관리합니다. 리눅스 시스템에서 네이티브하게 동작하며 도커 데몬, 도커 클라이언트, REST API로 이루어져 있습니다.

  • 도커 데몬 - 호스트 컴퓨터에서 동작합니다
  • 도커 클라이언트 - 도커 데몬이 명령어를 실행하도록 통신합니다.
  • REST API - 도커 데몬을 원격에서 인터렉팅 합니다.

Docker Client

도커 클라이언트는 여러분과 같은 도커의 엔드 유저와 통신합니다. 도커를 위한 UI라고 생각하셔도 됩니다. $ docker build iampeekay/someImage 라고 입력해서 도커 클라이언트와 통신하면 도커 데몬에게 명령을 내리게 되는 것입니다.

Docker Daemon

도커데몬은 도커 클라이언트에게 보내진 명령을 실제적으로 실행하는 곳입니다. 빌드나 러닝, 컨테이너 분산같은 것들 말이죠. 도커 데몬은 호스트 머신에서 동작하지만 여러분은 user로서 절대 직접적으로 데몬과 통신할 수 없습니다. 도커 클라이언트는 호스트 머신에서 동작할 수 있지만 필수적인 것은 아닙니다. 다른 머신에서 동작하면서도 호스트 머신에서 동작하는 도커 데몬과 통신할 수 있습니다.

Dockerfile

도커 파일은 도커 이미지를 만들 때 지시사항을 작성하는 곳입니다.

  • RUN apt-get y install some-package: 소프트웨어 패키지를 설치합니다
  • EXPOSE 8000: 8000번 포트를 엽니다
  • ENV ANT_HOME /usr/local/apache-ant : 환경 변수를 전달합니다.

한번 도커파일을 설정하면 docker build명령어를 사용해서 이미지를 빌드할 수 있습니다. 다음은 도커파일의 예입니다.

# Start with ubuntu 14.04
FROM ubuntu:14.04

MAINTAINER preethi kasireddy iam.preethi.k@gmail.com

# For SSH access and port redirection
ENV ROOTPASSWORD sample

# Turn off prompts during installations
ENV DEBIAN_FRONTEND noninteractive
RUN echo "debconf shared/accepted-oracle-license-v1-1 select true" | debconf-set-selections
RUN echo "debconf shared/accepted-oracle-license-v1-1 seen true" | debconf-set-selections

# Update packages
RUN apt-get -y update

# Install system tools / libraries
RUN apt-get -y install python3-software-properties \
    software-properties-common \
    bzip2 \
    ssh \
    net-tools \
    vim \
    curl \
    expect \
    git \
    nano \
    wget \
    build-essential \
    dialog \
    make \
    build-essential \
    checkinstall \
    bridge-utils \
    virt-viewer \
    python-pip \
    python-setuptools \
    python-dev

# Install Node, npm
RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
RUN apt-get install -y nodejs

# Add oracle-jdk7 to repositories
RUN add-apt-repository ppa:webupd8team/java

# Make sure the package repository is up to date
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list

# Update apt
RUN apt-get -y update

# Install oracle-jdk7
RUN apt-get -y install oracle-java7-installer

# Export JAVA_HOME variable
ENV JAVA_HOME /usr/lib/jvm/java-7-oracle

# Run sshd
RUN apt-get install -y openssh-server
RUN mkdir /var/run/sshd
RUN echo "root:$ROOTPASSWORD" | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# SSH login fix. Otherwise user is kicked off after login
RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

# Expose Node.js app port
EXPOSE 8000

# Create tap-to-android app directory
RUN mkdir -p /usr/src/my-app
WORKDIR /usr/src/my-app

# Install app dependencies
COPY . /usr/src/my-app
RUN npm install

# Add entrypoint
ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

CMD ["npm", "start"]

Docker Image

도커 이미지는 여러분의 도커파일에 작성한 명령들이 빌드되어 읽기만 가능한 템플릿입니다. 이미지는 여러분이 원하는 응용프로그램 패키지와 의존성이 어떻게 보일지 그리고 실행되었을 때 어떤 프로세스가 실행될 지 정의합니다.

도커 이미지는 도커파일을 사용해서 만들어 집니다. 도커 파일의 각 명령은 새로운 레이어르 ㄹ이미지에 만들어서 레이어 들은 이미지 파일시스템의 일부를 대표하는 것으로 추가되거나 레이어 하단을 교체할 수 있습니다. 레이어들은 도커가 가벼우면서도 강력한 구조가 되도록 하는 핵심요소 입니다. 도커는 통합 파일 시스템을 사용해서 이것을 달성했습니다.

Union File Systems

도커는 통합 파일 시스템을 사용해서 이미지를 빌드합니다. 쌓을 수 있는 파일시스템으로 통합 파일 시스템을 생각하셔도 되는데 파일과 디렉토리가 분리되어 있고 (branch) 단일 파일시스템이 투명한 상태로 놓일 수 있습니다. 디렉토리의 내용은 overlaid 브랜치 내에서는 동일한 경로를 가지며 하나의 통합된 디렉토리처럼 보이고 각 레이어의 분리해서 복사할 필요가 없도록 하기 위함입니다. 특정 레이어가 수정이 필요하다면 복사본을 만들고 로컬 카피를 수정하고 원본은 수정되지 않은채로 놔둡니다. 이게 파일시스템이 쓰기 권한이 없어도 쓰기가 가능한 것처럼 보이는 이유입니다. (다른 말로 copy-on-write 시스템이라고 합니다.)

레이어된 시스템은 두가지 큰 장점이 있습니다.

  • Duplication-free : 레이어는 완전한 파일 집합의 중복을 피하는 것을 도와주며 여러분이 이미지를 사용해서 새로운 컨테이너를 만들 때 마다 도커 컨테이너의 설치는 매우 빠르고 가볍습니다.
  • Layer segregation : 더욱 빠르게 만들어 줍니다, 이미지를 수정하면 도커는 모든 레이어가 아니라 변경이 적용될 레이어들에게만 변경사항을 전파합니다.

Volumes

볼륨은 컨테이너의 데이터 영역으로 컨테이너가 만들어질 때 초기화됩니다. 볼륨은 컨테이너의 데이터를 유지하고 컨테이너끼리 공유할 수 있게 합니다. 데이터 볼륨은 기본 통합 파일시스템과 분리되어 있으며 호스트 파일시스템의 일반 디렉토리와 파일로 존재합니다. 그래서 삭제하고 업데이트하고 컨테이너를 리빌드해도 데이터 볼륨은 그대로 남아있을 것입니다. 볼륨을 업데이트 하고 싶다면 직접적으로 수정을 할 수 있습니다.(추가적으로 데이터볼륨은 다수의 컨테이너들과 공유되어 재사용 될 수 있으며 꽤나 편리합니다.)

Docker Containers

도커 컨테이너는 위에서 말씀드린대로 응용프로그램의 소프트웨어를 보이지않는 박스안에 실행에 필요한 모든 것과 함께 포장합니다. 거기에는 운영체제와 응용프로그램 코드, 런타임, 시스템 도구, 시스템 라이브러리 등등이 포함되죠. 도커 컨테이너는 도커 이미지로부터 만들어 집니다. 이미지가 읽기 전용이기 때문에 도커는 읽기/쓰기 파일시스템을 이미지의 읽기 전용 파일시스템 위에 추가하여 컨테이너를 만듭니다.

Source: Docker

컨테이너를 만들고 나서 도커는 네트워크 인터페이스를 만들어서 로컬호수트와 컨테이너가 얘기할 수 있도록 하고 컨테이너에게 사용 가능한 IP를 붙여주고 도커 이미지에 정의한 대로 응용프로그램을 동작하기 위한 특정 프로세스들을 실행합니다. 한번 컨테이너를 성공적으로 만들고 나면 어떤 환경에서도 수정없이 실행할 수 있습니다.

Double-clicking on “containers”

후, 정말 많은 부분이 있었군요. 제가 항상 궁금해 하던 것은 컨테이너가 실제로 어떻게 구현되어있는지, 특히나 컨테이너 주변의 경계와 인프라를 추상화 하는 것이 없기 때문에 더욱 궁금했습니다.

컨테이너라는 단어는 단지 추상적인 개념으로 컨테이너를 가상화 하여 작업하는 것과는 약간 차이가 있습니다. 그것들에 대해서 빠르게 알아봅시다.

1) Namespaces

네임스페이스는 컨테이너에게 리눅스 시스템의 아랫단을 볼 수 있는 자체적인 뷰를 제공하여 어떻 것이 컨테이너에게 보이고 접근할 수 있는지 제한합니다. 컨테이너를 실행하면 도커는 특정 컨테이너가 사용하게 될 네임스페이스를 만듭니다. 커널에서 여러종류의 네임스페이스가 있으며 도커가 사용하는 것은 다음과 같습니다.

  • Net: 컨테이너에게 시스템의 네트워크 스택에 대한 자체적인 뷰를 제공합니다.(네트워크 장치, IP 주소, 라우팅 테이블 등)

  • PID: PID는 프로세스 아이디로 ps aux를 실행하면 시스템에 동작하고 있는 프로세스들을 볼 수 있습니다. 그 중에 PID 열이 보이실 겁니다. PID 네임스페이스는 컨테이너에게 자체적인 프로세스들의 스코프 뷰를 줘서 그들과 통신할 수 있게 해줍니다.

  • MNT: 컨테이너에게 mounts on the system의 뷰를 제공해 줍니다. 그래서 다른 네임스페이스에 있는 프로세스들은 파일시스템 계층구조에 대한 다른 뷰를 가지게 됩니다.

  • UTS: UTS는 UNITX Timesharing System 입니다. 프로세스가 시스템 식별자를 구분할 수 있게 해줍니다. UTS는 컨테이너에게 자체 호스트 네임과 NIS 도메인 이름을 가질 수 있게 해주며 다른 컨테이너들과 호스트 시스템과 독립적입니다.

  • IPC: IPC는 InterProcess Communication 입니다. IPC 네임스페이스는 각 컨테이너 내부에서 동작하는 프로세스 간에 IPC 자원들을 독립시켜주는 일을 담당하고 있습니다.

  • USER: 이 네임스페이스는 각 컨테이너 내에 사용자를 독립시키는데 사용됩니다. 컨테이너들이 호스트 시스템과 비교하여 다른 범위의 uid,gid 뷰를 갖게 합니다. 결과적으로 프로세스의 uid와 gid는 내부와 외부에서 달라질 수 있으며 프로세스가 컨테이너 내부의 루트 권한을 갖지 않아도 컨테이너 외부에 권한없는 사용자를 가질 수 있게합니다. 도커는 이러한 네임스페이스를 같이 사용해서 컨테이너를 만들고 독립시킵니다. 다음은 컨트롤 그룹이라고 불리는 특징입니다.

2) Control groups

컨트롤 그룹(cgroups)은 리눅스 커널의 피처로써 프로세스 집합에 대해 독립적으로 우선순위를 매겨서 자원을 사용할 수 있도록 해주는 것입니다. 여기에서 cgroup은 도커 컨테이너가 그들이 필요한 자원만 사용하도록 만듭니다. 게다가 컨테이너가 사용할 수 있는 자원을 제한할 수도 있습니다. cgroup은 단일 컨테이너가 이런 자원들을 다 써버려서 전체 시스템이 다운되도록 만들지 않게 합니다. 마지막으로 도커가 사용하는 통합 파일시스템에 대해 알아보겠습니다.

3) Isolated Union file system

도커 이미지 영역에서 모두 설명되어 있습니다. 이것이 도커 컨테이너에 들어있는 모든 것입니다.

Summary

  • 컨테이너와 VMs에 대한 비교, 컨테이너 소프트웨어는 이전에도 많았음,
  • 도커가 좋은 점, 쓰기 쉽고 빠르다
  • 리눅스 커널의 피처들을 활용하여 독립된 공간을 만들어주며 자원을 공유해서 활용할 수 있도록 한다

© 2019. All rights reserved.

Powered by Hydejack v8.1.1