본문 바로가기

개발관련

Docker란?

반응형

 

Overview

  • Container에 대해서 스터디한다.
  • Hypervisor에 대해서 스터디한다.
  • Hypervisor VS Container
  • Docker에 대해서 스터디한다.

Container란?

Container란 어플리케이션이 동작하기 위해서 필요한 요소(실행 파일, 어플리케이션 엔진 등)을 패키지화하고 격리하는 기술

Container를 왜 사용하는걸까?

  • 일관성 있는 환경
    • 개발자는 컨테이너를 이용해, 다른 애플리케이션과 분리된 예측 가능한 환경을 생성할 수 있다. 컨테이너는 애플리케이션에 필요한 소프트웨어 종속 항목(프로그래밍 언어 런타임 및 기타 소프트웨어 라이브러리의 특정 버전 등)도 포함할 수 있다. 개발자의 관점에서 이 모든 요소는 애플리케이션이 배포되는 최종 위치에 관계없이 항상 일관성이 있기 때문에, 그 결과 자연히 생산성이 향상될 수밖에 없다. 개발자와 IT 운영팀이 버그를 잡고 환경 차이를 진단하던 시간을 줄이고 사용자에게 신규 기능을 제공하는 데 집중할 수 있기 때문이다. 또한 개발자가 개발 및 테스트 환경에서 세운 가정이 프로덕션 환경에서 그대로 실현될 것이기 때문에 버그 수 자체도 감소한다.
  • 폭넓은 구동 환경
    • 컨테이너는 Linux, Windows, Mac 운영체제, 가상 머신, 베어메탈, 개발자의 컴퓨터, 데이터 센터, 온프레미스 환경, 퍼블릭 클라우드 등 사실상 어느 환경에서나 구동되므로 개발 및 배포가 크게 쉬워진다. 컨테이너용 Docker 이미지 형식은 워낙 널리 사용되기 때문에 이식성도 매우 뛰어난다. 소프트웨어 구동 환경이 무엇이든 컨테이너를 사용할 수 있다.
  • 격리
    • 컨테이너는 CPU, 메모리, 저장소, 네트워크 리소스를 운영체제 수준에서 가상화하여 개발자에게 기타 애플리케이션으로부터 논리적으로 격리된 운영체제 샌드박스 환경을 제공한다.
    • 따라서 간소화된 배포 및 릴리스 프로세스를 통해 신속하고 자주 배포할 수 있고, 이는 리소스 활용성이 증가함을 의미한다.

Container의 작동 원리

컨테이너는 cgroup과 namespace와 같은 리눅스 커널 기반의 기술을 이용해서 프로세스를 완벽하게 격리하여 분리된 환경을 만들고 실행한다.

cGroup은 각 프로세스 혹은 컨테이너가 소비할 수 있는 호스트OS의 리소스를 공유하고 제한할 수 있게 한다. 이것은 호스트의 하드웨어 리소스의 서비스 거부 공격(denial of sevice attacks)을 방지하므로 리소스 활용과 보안 모두에서 중요하다. Container는 미리 정의된 제약 조건하에서 CPU와 메모리를 공유할 수 있다.

namespace는 운영체제 내에서 프로세스가 가지는 다른 프로세스, 네트워킹, 파일시스템, 사용자 ID 구성 요소 등의 '가시성'을 제한하여 프로세스 상호작용을 따로 구분하는 방식이다. namespace를 통해 컨테이너 프로세스는 동일한 namespace에 있는 것들만 볼 수 있도록 제한된다. 다른 컨테이너에서 만들어진 프로세스나 호스트 프로세스는 컨테이너 프로세스에서 직접 접근할 수 없다.

cGroup?

Control Group의 약자로, 시스템의 CPU 시간, 시스템 메모리, 네트워크 대역폭과 같은 자원을 제한하고 격리할 수 있는 커널 기능

cgroups(control groups의 약자)는 프로세스들의 자원의 사용(CPU, 메모리, 디스크 입출력, 네트워크 등)을 제한하고 격리시키는 리눅스 커널 기능이다.

구글의 엔지니어들이 2006년에 이 기능에 대한 작업에 착수하였고 당시 이름은 "프로세스 컨테이너"(process container)였다.[1] 2007년 말에 리눅스 커널 문맥에서 "컨테이너"라는 용어의 의미가 여러 개이므로 혼란을 방지하기 위해 이름이 "컨트롤 그룹"(control groups)으로 변경되었으며, 컨트롤 그룹 기능은 2008년 1월에 출시된 커널 버전 2.6.24에 리눅스 커널 메인라인으로 병합되었다.[2] 그 뒤로 개발자들은 수많은 새로운 기능과 컨트롤러들을 추가해오고 있는데, 이를테면 kernfs 지원,[3] 방화벽,[4] 통합된 계층구조를 포함한다.[5]

cGroup으로 컨테이너 안의 프로세스에 대해 자원을 제한함으로써 특정 컨테이너가 호스트 OS의 자원을 모두 점유하여 사용하는 일을 막는다.

  • cGroup의 서브시스템
    • blkio - 물리 드라이브 (예: 디스크, 솔리드 스테이트, USB 등)와 같은 블록 장치(Block Device)에 대한 입력/출력 액세스에 제한을 설정한다.
    • cpu - CPU에 cgroup 작업 액세스를 제공하기 위해 스케줄러를 사용한다.
    • cpuacct - cgroup의 작업에 사용된 CPU 자원에 대한 보고서를 자동으로 생성한다.
    • cpuset - 개별 CPU (멀티코어 시스템에서) 및 메모리 노드를 cgroup의 작업에 할당한다.
    • devices - cgroup의 작업 단위로 장치에 대한 액세스를 허용하거나 거부한다.
    • freezer - cgroup의 작업을 일시 중지하거나 다시 시작한다.
    • memory - cgroup의 작업에서 사용되는 메모리에 대한 제한을 설정하고 이러한 작업에서 사용되는 메모리 자원에 대한 보고서를 자동으로 생성한다.
    • net_cls - Linux 트래픽 컨트롤러 (tc)가 특정 cgroup 작업에서 발생하는 패킷을 식별하게 하는 클래식 식별자 (classid)를 사용하여 네트워크 패킷에 태그를 지정한다.
    • net_prio - cgroup의 작업에서 생성되는 네트워크 트래픽의 우선순위를 지정한다.

Namespace?

하나의 시스템에서 프로세스를 격리(Isolation)시킬 수 있는 가상화 기술이다.

Namespace는 커널 인스턴스를 만들지 않고 기존의 리소스들을 필요한 만큼의 Namespace로 분리하여 묶어 관리하는 방법으로 사용한다. 커널이 부팅된 후 관리 자원은 각각의 초기 Default Namespace에서 관리한다. 그 후 사용자의 필요에 따라 Namespace를 추가하여 자원들을 별도로 분리하여 관리할 수 있다.

Namespace는 기존에 잘 알려진 가상화 기술인 Hypervisor 와는 구조적으로 다르다.

Hypervisor는 Hardware resource 를 가상화 한다. Hypervisor 위에 올라가는 Guest OS 에는 가상화 된 형태의 H/W 를 제공하게 되며, 따라서 각각의 Guest OS는 완전한 다른 환경으로 분리된다.

하지만 Namespace의 경우에는 Hardware resource 레벨의 가상화가 아니다. 동일한 OS와 동일한 kernel 에서 작동하게 되며, 단지 각각의 고립된 사용 환경만 제공되는 것이다.

  • Namespace의 종류
    • UTS Namespace : hostname을 변경하고 분할
    • IPC Namespace : 프로세스간 통신 격리
    • PID Namespace : Process ID를 분할 관리
    • NET Namespace : Network Interface, iptables 등 network 리소스와 관련된 정보를 분할
    • User Namespace : user와 group ID를 분할 격리
    • Mount Namespace :

Container의 동작 구조

 

 


Hypervisor란?

하이퍼바이저(hypervisor)는 호스트 컴퓨터에서 다수의 운영 체제(operating system)를 동시에 실행하기 위한 논리적 플랫폼(platform)을 말한다. 가상화 머신 모니터 또는 가상화 머신 매니저(virtual machine monitor 또는 virtual machine manager, 줄여서 VMM)라고도 부른다.

하이퍼 바이저(Hypervisor)는 호스트 컴퓨터 1대에서 다수의 운영체제(OS)를 동시에 실행할 수 있도록 해주는 가상 플랫폼 기술(소프트웨어)을 말한다. 하이퍼 바이저 위에 올라온 운영체제는 게스트 OS 혹은 인스턴스 라고 부른다.

윈도우와 리눅스 가상머신이 동시에 돌아가고있다고 생각해보자.

각 OS는 커널이라는 녀석이 자원관리와 명령 해석을 포함한 거의 모든것을 컨트롤 하는데,

문제는 이 커널에서 사용하는 규칙이 OS마다 다르다는 것이다.

각 가상머신에서는 CPU 혹은 메모리 같은 자원을 사용하기 위해 커널에서 여러가지 명령을 날리는데, 이 규칙이 모두 다른 것이다.

하드웨어는 어느 장단에 맞추어야 할까?

이런 부분을 중재해주는 녀석이 바로 Hypervisor이다.

즉, Hypervisor는 게스트OS들에게 자원을 나눠주며 조율하고 각 게스트 OS들의 커널을 번역해서 하드웨어가 알 수 있도록 도와준다.

Hypervisor의 타입

 

 

  • TYPE 1 : Native 혹은 Bare-metal

호스트 OS가 없이 하이퍼바이저가 하드웨어에 직접 설치되어 운영 체제가 프로그램을 제어하듯이 하이퍼바이저가 해당 하드웨어에서 직접 실행되며 게스트 OS를 설치할 수 있는 여러 개의 가상 머신으로 분할해준다. 게스트 OS는 하드웨어 위에서 2번째 수준으로 실행된다.

Hypervisor 자체에서 직접 하드웨어를 제어하기 때문에 Hosted 방식에 비해 오버헤드가 적다.

종류로는 Xen(반가상화), KVM(Kernel-based Virtual Machine: 커널 기반 가상 머신) 등이 있다.

 

 

  • TYPE 2 : Hosted

하이퍼바이저는 일반 프로그램과 같이 호스트 OS에서 실행되며 VM 내부에서 동작되는 게스트 OS는 하드웨어에서 3번째 수준으로 실행된다.

VM의 대표적인 종류는 VMware Server, VMware Workstation, VMware Fusion, QEMU, 마이크로소프트의 버추얼 PC와 버추얼 서버, Oracle(SUN)의 버추얼박스, SWsoft의 Parallels Workstation과 Parallels Desktop이 있다.

Native 혹은 Bare-metal에 비해 오버헤드가 크지만 게스트 OS에 제약이 적고 도입이 쉽다.

 

추가적으로 TYPE 1의 Bare-metal 방식의 Hypervisor는 DOM0라는 것을 사용하는데 이것에 의해 전가상화, 반가상화로 구분될 수 있다.

 

TYPE 1 - Bare-metal의 가상화

    전가상화(Full Virtualization)
    • 각 게스트OS는 DOM0에게 명령을 전달하고, DOM0에서 이를 해석해 Hypervisor에게 알려준다.
    • 그럼 Hypervisor는 다시 하드웨어에게 해석된 최종 명령을 전달한다.
    • 따라서 모든 게스트OS의 명령에 DOM0이 개입하기 때문에 성능이 느리다.

 

  • 반가상화(Para Virtualization)
    • 전가상화에서 DOM0이 수행했던 '해석'의 역할을 각 게스트OS에서 처리하는 것이다.
    • 각 게스트OS에서 해석된 명령을 DOM0을 통해서가 아닌 Hypervisor에게 바로 전달하기 때문에 전가상화에 비해 성능이 좋다.
    • 이 때 쓰는 명령을 Hyper-Call이라고 하는데, 기본적으로 모든 OS의 커널은 이러한 Hyper-Call에 대해 구현된 부분이 없어 직접 구현 해주어야 한다. 즉, 게스트OS의 커널을 직접 수정해야 한다.
    • 따라서 리눅스 등 오픈소스 OS가 아니면 반가상화를 하기 어렵다.

 

 


Hypervisor vs Container

 

전통적인 배포 시대(Traditional Deployment): 초기 조직은 애플리케이션을 물리 서버에서 실행했었다. 한 물리 서버에서 여러 애플리케이션의 리소스 한계를 정의할 방법이 없었기에, 리소스 할당의 문제가 발생했다. 예를 들어 물리 서버 하나에서 여러 애플리케이션을 실행하면, 리소스 전부를 차지하는 애플리케이션 인스턴스가 있을 수 있고, 결과적으로는 다른 애플리케이션의 성능이 저하될 수 있었다. 이에 대한 해결책은 서로 다른 여러 물리 서버에서 각 애플리케이션을 실행하는 것이 있다. 그러나 이는 리소스가 충분히 활용되지 않는다는 점에서 확장 가능하지 않았으므로, 물리 서버를 많이 유지하기 위해서 조직에게 많은 비용이 들었다.

 

가상화된 배포 시대(Virtualized Deployment): 그 해결책으로 가상화가 도입되었다. 이는 단일 물리 서버의 CPU에서 여러 가상 시스템 (VM)을 실행할 수 있게 한다. 가상화를 사용하면 VM간에 애플리케이션을 격리하고 애플리케이션의 정보를 다른 애플리케이션에서 자유롭게 액세스 할 수 없으므로, 일정 수준의 보안성을 제공할 수 있다.

가상화를 사용하면 물리 서버에서 리소스를 보다 효율적으로 활용할 수 있으며, 쉽게 애플리케이션을 추가하거나 업데이트할 수 있고 하드웨어 비용을 절감할 수 있어 더 나은 확장성을 제공한다.

각 VM은 가상화된 하드웨어 상에서 자체 운영체제를 포함한 모든 구성 요소를 실행하는 전체 시스템이다.

 

컨테이너 개발 시대(Container Deployment): 컨테이너는 VM과 유사하지만 격리 속성을 완화하여 애플리케이션 간에 운영체제(OS)를 공유한다. 그러므로 컨테이너는 가볍다고 여겨진다. VM과 마찬가지로 컨테이너에는 자체 파일 시스템, CPU, 메모리, 프로세스 공간 등이 있다. 기본 인프라와의 종속성을 끊었기 때문에, 클라우드나 OS 배포본에 모두 이식할 수 있다.

컨테이너의 장점의 일부는 다음과 같다.

  • 기민한 애플리케이션 생성과 배포: VM 이미지를 사용하는 것에 비해 컨테이너 이미지 생성이 보다 쉽고 효율적임.
  • 지속적인 개발, 통합 및 배포: 안정적이고 주기적으로 컨테이너 이미지를 빌드해서 배포할 수 있고 (이미지의 불변성 덕에) 빠르고 쉽게 롤백할 수 있다.
  • 개발과 운영의 관심사 분리: 배포 시점이 아닌 빌드/릴리스 시점에 애플리케이션 컨테이너 이미지를 만들기 때문에, 애플리케이션이 인프라스트럭처에서 디커플된다.
  • 가시성은 OS 수준의 정보와 메트릭에 머무르지 않고, 애플리케이션의 헬스와 그 밖의 시그널을 볼 수 있다.
  • 개발, 테스팅 및 운영 환경에 걸친 일관성: 랩탑에서도 클라우드에서와 동일하게 구동된다.
  • 클라우드 및 OS 배포판 간 이식성: Ubuntu, RHEL, CoreOS, on-prem, Google Kubernetes Engine 및 다른 어디에서든 구동된다.
  • 애플리케이션 중심 관리: 가상 하드웨어의 OS에서 애플리케이션을 구동하는 수준에서 OS의 논리적인 자원을 사용하여 애플리케이션을 구동하는 수준으로 추상화 수준이 높아진다.
  • 느슨하게 커플되고, 분산되고, 유연하며, 자유로운 마이크로서비스: 애플리케이션은 단일 목적의 머신에서 모놀리식 스택으로 구동되지 않고 보다 작고 독립적인 단위로 쪼개져서 동적으로 배포되고 관리될 수 있다.
  • 자원 격리: 애플리케이션 성능을 예측할 수 있다.
  • 자원 사용량: 고효율 고집적.

실제 하드웨어인 것처럼 에뮬레이션(emulation)을 하는 VM과 달리 container는 호스트 PC의 자원을 격리(isolation)된 상태로 그대로 활용하기 때문에 VM에 비해 성능 저하가 눈에 띄게 적다.

 

 


 

Docker란?

Container 기반의 오픈소스 가상화 플랫폼

Docker란 컨테이너 기술을 쉽고 간단하게 사용할 수 있도록 다양한 커맨드라인이나 API를 제공해주는 오픈소스이다. Docker를 구성하는 요소들로 Docker Image와 Layer, Dockerfile 그리고 Docker의 Command line Interface 및 API, Docker Hub(Docker의 Git Hub)가 있다.

Docker Image

Container 실행에 필요한 파일과 설정값등을 포함하고 있는 것

 

 

 

이미지는 url 방식으로 관리하며 태그를 붙일 수 있다.

ubuntu 14.04 이미지는 docker.io/library/ubuntu:14.04 또는 docker.io/library/ubuntu:trusty 이고 docker.io/library는 생략 가능하여 ubuntu:14.04 로 사용할 수 있다. 이러한 방식은 이해하기 쉽고 편리하게 사용할 수 있으며 태그 기능을 잘 이용하면 테스트나 롤백도 쉽게 할 수 있다.

# vertx/vertx3 debian version
FROM subicura/vertx3:3.3.1
MAINTAINER chungsub.kim@purpleworks.co.kr

ADD build/distributions/app-3.3.1.tar /
ADD config.template.json /app-3.3.1/bin/config.json
ADD docker/script/start.sh /usr/local/bin/
RUN ln -s /usr/local/bin/start.sh /start.sh

EXPOSE 8080
EXPOSE 7000

CMD ["start.sh"]

도커는 이미지를 만들기 위해 Dockerfile이라는 파일에 자체 DSLDomain-specific language언어를 이용하여 이미지 생성 과정을 적는다. 위 샘플을 보면 그렇게 복잡하지 않다는 걸 알 수 있다.

이것은 굉장히 간단하지만 유용한 아이디어인데, 서버에 어떤 프로그램을 설치하려고 이것저것 의존성 패키지를 설치하고 설정파일을 만들었던 경험이 있다면 더 이상 그 과정을 블로깅 하거나 메모장에 적지 않고 Dockerfile로 관리하면 된다. 이 파일은 소스와 함께 버전 관리 되고 원한다면 누구나 이미지 생성 과정을 보고 수정할 수 있다.

 

Layer 저장 방식

 

 

도커 이미지는 컨테이너를 실행하기 위한 모든 정보를 가지고 있기 때문에 보통 용량이 수백MB에 이른다.

처음 이미지를 다운받을 땐 크게 부담이 안되지만 기존 이미지에 파일 하나 추가했다고 수백MB를 다시 다운받는다면 매우 비효율적일 수 밖에 없다.

도커는 이런 문제를 해결하기 위해 **레이어(layer)**라는 개념을 사용하고 유니온 파일 시스템을 이용하여 여러개의 레이어를 하나의 파일시스템으로 사용할 수 있게 해준다.

이미지는 여러개의 읽기 전용 read only 레이어로 구성되고 파일이 추가되거나 수정되면 새로운 레이어가 생성된다.

ubuntu 이미지가 A + B + C의 집합이라면,

ubuntu 이미지를 베이스로 만든 nginx 이미지는 A + B + C + nginx가 된다.

webapp 이미지를 nginx 이미지 기반으로 만들었다면

예상대로 A + B + C + nginx + source 레이어로 구성된다.

webapp 소스를 수정하면

A, B, C, nginx 레이어를 제외한 새로운 source(v2) 레이어만 다운받으면 되기 때문에 굉장히 효율적으로 이미지를 관리할 수 있다.

컨테이너를 생성할 때도 레이어 방식을 사용하는데 기존의 이미지 레이어 위에 읽기/쓰기(read-write) 레이어를 추가한다. 이미지 레이어를 그대로 사용하면서 컨테이너가 실행 중에 생성하는 파일이나 변경된 내용은 읽기/쓰기 레이어에 저장되므로 여러 개의 컨테이너를 생성해도 최소한의 용량만 사용한다.

가상화의 특성상 이미지 용량이 크고 여러 대의 서버에 배포하는걸 감안하면 단순하지만 엄청나게 영리한 설계이다.

Docker Hub

Docker는 Docker Hub를 통해 공개 이미지를 무료로 관리해준다.

간단한 도커의 커맨드라인 예제


참고

초보를 위한 도커 안내서 - 도커란 무엇인가? : https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html

전가상화 반가상화 : https://blog.naver.com/alice_k106/220218878967

 

반응형