Home Docker?
Post
Cancel

Docker?

도커(Docker)란 소프트웨어 컨테이너화를 통해 애플리케이션을 더 쉽게 개발, 배포, 실행할 수 있게 해주는 오픈 소스 플랫폼이다. 컨테이너화 기술은 개발자가 코드와 그 코드의 모든 종속성을 함께 묶어 컨테이너(Container)로 배포할 수 있게 해주는 기술로, 간단하게 말하면 컨테이너를 생성하고 관리하기 위한 도구이다.

Container?

컨테이너는 운영 체제 수준의 가상화 기술로 리눅스 커널을 공유하면서 프로세스를 격리된 환경에서 실행하는 기술이다. 간단히 말하면, 애플리케이션과 그 실행에 필요한 모든 것을 포함하는 가볍고 독립적인 실행 환경이다. 즉, 가상 머신(VM)과는 달리 컨테이너는 운영 체제 전체를 포함하는 대신, 호스트 운영 체제의 커널을 공유하여 더 적은 리소스를 사용하고 더 빠르게 실행할 수 있다.

컨테이너는 애플리케이션 코드뿐만 아니라 해당 코드를 실행하는 데 필요한 종속성, 라이브러리, 설정 정보 등을 모두 포함하는 독립적인 실행 패키지이다. 따라서 컨테이너는 코드의 버전 및 실행 환경을 일관되게 유지하여 동일한 동작을 수행하도록 보장한다. 즉, 개발 환경, 테스트 환경, 배포 환경 등 서로 다른 환경에서도 동일한 애플리케이션을 일관되게 실행할 수 있어 환경 차이로 인해 발생하는 많은 문제를 해결할 수 있다.

가상머신과 Container의 차이

가상 머신의 경우 각 머신마다 각각의 독립적인 운영 체제를 가져야 하므로 굉장히 무겁다. 또한 VM 간 자원과 파일을 공유하려면 별도의 설정이 필요하다.

반면, 컨테이너(Container)는 호스트 운영 체제의 커널을 공유하면서 애플리케이션과 그 실행에 필요한 종속성만 포함하는 가벼운 실행 환경을 제공한다. 컨테이너는 애플리케이션을 서비스하기 위해 필요한 요소들만 포함하고 있어 매우 가볍고 빠르게 실행할 수 있고, 가상 머신처럼 운영 체제 전체를 포함하지 않기 때문에 부팅 시간도 짧고 리소스 사용량이 적다.

컨테이너는 서로 격리된 상태로 실행되지만, 같은 호스트의 커널을 공유하므로, 가상 머신과 비교해 더 효율적이며 자원 소모가 적다.

컨테이너의 주요 기술

컨테이너는 리눅스의 여러 기술을 사용하여 운영 체제 수준의 가상화를 이루어낸다. 주요 기술에 대해 알아보자.

Namespace

네임스페이스(Namespace)는 컨테이너의 격리를 담당하는 기술이다. 네임스페이스는 시스템 리소스를 논리적으로 분리하여 각 컨테이너가 다른 컨테이너나 호스트 시스템과 독립적인 환경을 가지는 것처럼 보이게 한다. 이를 통해 컨테이너는 자신만의 프로세스, 네트워크, 파일 시스템 등을 가지며, 격리된 상태에서 실행될 수 있다.

Mount Namespace

  • 파일 시스템 마운트를 격리하고 각 네임스페이스는 자신의 파일 시스템 트리를 가지고 있어 다른 네임스페이스와 독립적으로 파일 시스템을 마운트하거나 언마운트 할 수 있다.
  • 마운트 네임스페이스에 여러 작업을 수행하고 종료하면 작업한 내용을 날려 깨끗한 상태를 유지할 수 있다.

Process ID Namespace

  • 프로세스 ID를 격리하고 각 네임스페이스는 독립된 프로세스 ID 공간을 가지므로, 서로 다른 네임스페이스에서 동일한 PID를 가질 수 있다.
  • 네임스페이스 내에서 PID가 1번인 프로세스를 실행할 수 있으며, 이는 주로 init 프로세스로 사용된다.

Network Namespace

  • 네트워크 장치, IP 주소, 포트 번호 등을 격리한다.
  • 각 네임스페이스는 독립된 네트워크 스택을 가지며, 네임스페이스 간에 네트워크 장치를 독립적으로 설정할 수 있고, 이를 가상 네트워크 인터페이스 및 라우팅 테이블을 구성하는 데 사용할 수 있다.

User Namespace

  • 사용자와 그룹 ID를 격리한다.
  • 각 네임스페이스는 독립된 사용자 ID 및 그룹 ID를 가질 수 있으며, 이를 통해 비루트 사용자가 네임스페이스 내에서 루트 권한을 가질 수 있게 한다.

Cgroups

컨트롤 그룹(Cgroups)은 시스템 자원을 효율적으로 제한하고 관리하는 기능을 제공한다. cgroups는 컨테이너에 할당되는 CPU, 메모리, 디스크 I/O, 네트워크 대역폭 등 다양한 리소스를 제한하거나 우선순위를 설정할 수 있도록 한다. 이를 통해 하나의 컨테이너가 시스템 자원을 과도하게 사용해 다른 컨테이너나 호스트 시스템에 영향을 미치는 것을 방지할 수 있다.

주요 기능

  • 리소스 제한: 프로세스가 사용할 수 있는 자원을 제한하여 시스템 전체의 안정성을 보장한다.
  • 리소스 우선순위: 중요한 프로세스에 더 많은 자원을 할당하고, 덜 중요한 프로세스의 자원을 제한한다.
  • 리소스 계정: 프로세스 그룹별로 자원 사용량을 모니터링하고, 통계 정보를 제공한다.
  • 리소스 격리: 서로 다른 프로세스 그룹 간의 자원 사용을 격리하여, 한 그룹의 자원 사용이 다른 그룹에 영향을 미치지 않도록 한다.

chroot

chroot는 프로세스가 특정 디렉터리를 루트 디렉터리(/) 로 간주하도록 변경하는 기술이다. 이를 통해 컨테이너 내의 프로세스는 그 디렉터리 밖의 파일 시스템에 접근할 수 없게 된다.

단, 네임스페이스와 같은 복잡한 격리 기능이 없는 초기 가상화 기술로, 도커 컨테이너 격리에 있어 네임스페이스와 cgroups만큼 강력한 격리 기능을 제공하지는 않는다. 즉, 기존 루트 파일 시스템이 그대로 남아 있어, 권한이 충분한 경우 이를 우회하거나 탈출(탈옥)하여 접근할 수 있다.

pivot_root

pivot_root는 현재 루트 파일 시스템을 새로운 파일 시스템으로 변경하고, 기존 루트 파일 시스템을 새로운 루트의 하위 디렉터리로 이동시키는 기술이다. pivot_rootMount Namespace와 결합하여 격리된 파일 시스템 환경을 제공하며, 기존 루트를 언마운트하여 기존 루트에 대한 접근을 차단한다. 이를 통해 기존 루트에 대한 접근 가능성을 원천적으로 방지하며, chroot보다 더 강력한 보안과 격리를 제공한다.

chroot와 pivot_root의 차이

기능/특징chrootpivot_root
루트 교체 방식프로세스의 루트 디렉터리를 특정 디렉터리로 상대적으로 변경
(호스트 파일 시스템은 그대로 유지)
현재 루트 파일 시스템을 새로운 디렉터리로 완전히 교체하고,
기존 루트를 새로운 루트의 하위 디렉터리로 이동
보안기존 루트 파일 시스템 접근 가능 (권한 우회 가능성 존재)기존 루트 파일 시스템 접근 차단 (강력한 보안과 격리 제공)
격리 수준논리적 경로 변경으로 제한적 격리 제공파일 시스템을 완전히 분리하여 높은 수준의 격리 제공
도커에서 사용 여부사용하지 않음사용함 (컨테이너의 루트 파일 시스템 전환에 활용)

pivot_root를 사용한 파일 시스템 격리 과정

  1. 새로운 루트 파일 시스템 준비 (마운트)
    • 컨테이너를 실행하기 전, 새로운 루트 파일 시스템을 생성하고 기존의 호스트의 파일 시스템에 마운트(연결).
  2. Mount Namespace 생성 (격리)
    • 새로 마운트한 파일 시스템이 네임스페이스 외부(호스트나 다른 컨테이너)에서는 보이지 않도록 격리한다.
    • Mount Namespace를 사용하면, 해당 네임스페이스 내의 프로세스만 새로운 파일 시스템 트리를 볼 수 있으며, 호스트나 다른 네임스페이스는 영향을 받지 않는다.
  3. pivot_root를 사용하여 루트 디렉터리 변경
    • 새로 마운트된 루트 파일 시스템을 컨테이너의 루트(/)로 전환한다.
    • 기존 루트 파일 시스템은 새로운 루트의 하위 디렉터리로 이동되고, 안정적으로 언마운트된다.
  4. 격리된 환경에서 실행
    • Mount Namespace와 pivot_root를 통해 격리된 파일 시스템 환경을 갖춘 컨테이너가 실행된다.

기존의 루트를 하위로 이동하는 이유: 기존 루트를 언마운트 가능 상태로 만들고, 파일 시스템 전환의 안정성을 보장하기 위함.

Union File System

유니언 파일 시스템(UFS)은 컨테이너의 파일 시스템을 효율적으로 관리하기 위한 기술이다. 도커에서 사용하는 대표적인 파일 시스템인 OverlayFS은 유니언 파일 시스템의 한 종류로, 여러 레이어로 구성된 파일 시스템을 하나의 파일 시스템처럼 사용할 수 있게 해준다.

OverlayFS

UFS의 한 종류로, 리눅스 커널에서 지원하는 파일 시스템으로, 성능과 효율성 면에서 최적화되어 있다. 주로 도커와 같은 컨테이너 기술에서 많이 사용되며, 읽기 전용 파일 시스템에 쓰기 가능한 레이어를 추가하여, 기본 파일 시스템을 변경하지 않고도 파일을 수정하거나 추가할 수 있게 한다.

파일 시스템 구조
  • LowerDir: 읽기 전용 파일 시스템으로, 이미지가 사용하는 디렉터리다.
  • UpperDir: 쓰기 전용 파일 시스템으로, 변경 사항이 저장되는 컨테이너 레이어 전용 디렉터리다.
  • WorkDir: UpperDir에서 발생한 변경 사항을 관리하고, LowerDir과 UpperDir 간의 데이터 동기화 작업을 처리하는 디렉터리다. 파일 수정 시 LowerDir에서 UpperDir로 데이터를 복사할 때 사용된다.
  • Merged: LowerDir과 UpperDir을 합쳐서 만든 현재 상태의 파일 시스템 디렉터리다. 사용자는 이 Merged 디렉터리를 통해 파일 시스템을 작업한다.
작동 방식

모든 작업은 Merged 디렉터리에서 이루어진다. 새로운 파일을 추가하면 UpperDir에만 저장되지만, LowerDir에 이미 파일이 존재하는 파일을 수정할 경우 UpperDir로 복사되어 수정되므로, 이를 통해 일관성이 유지된다.

  • 새로운 파일 생성: UpperDir에 파일이 생성된다.
  • 기존 파일 읽기: 파일이 UpperDir에 있으면 UpperDir에서 읽고, 없으면 LowerDir에서 읽는다.
  • 기존 파일 수정: 파일이 UpperDir에 없으면 LowerDir에서 UpperDir로 복사한 후 수정한다.
  • 파일 삭제: 파일이 UpperDir에 있으면 삭제되고, LowerDir에 있는 경우 UpperDir에 삭제 마커가 생성된다. (실제 삭제는 되지 않고 삭제된 것처럼 보인다)

도커에서는 pivot_root를 사용해 Merged 디렉터리를 컨테이너의 루트 파일 시스템으로 설정한다. 이에 따라 컨테이너 내부에서는 Merged 디렉터리가 실제 루트 디렉터리처럼 인식되며, 이곳에서 모든 작업이 수행된다.

도커가 레이어 구조를 사용하는 이유?

LowerDir은 읽기 전용으로 설정되며, 도커 이미지의 기본 레이어이다. 따라서 여러 컨테이너가 동일한 LowerDir(이미지)을 공유할 수 있다. 즉, 동일한 이미지 레이어를 사용하는 컨테이너가 여러 개라면 LowerDir은 여러 번 복사되거나 중복으로 저장되지 않고 한 번만 저장돼 저장 공간을 절약할 수 있다.

UpperDir은 변경 사항만 저장되므로, 시스템에 어떤 파일이 변경되었는지 쉽게 추적할 수 있어, 원본 파일 시스템을 보존하면서 효율적으로 파일을 관리할 수 있다. 이에 따라 시스템 오류나 실수로 변경된 파일을 손쉽게 복구할 수 있다.

컨테이너 기술을 사용한 도커의 동작 과정

  1. 도커는 컨테이너를 생성할 때 새로운 네임스페이스 사용해 격리된 공간을 생성한다. (컨테이너마다 독립적인 파일 시스템을 가지게 된다)
  2. 도커는 OverlayFS를 사용하여 컨테이너의 파일 시스템을 설정한다.
  3. 도커는 OverlayFS로 병합된 디렉터리를 pivot_root와 Mount Namespace를 사용해 새로운 루트 파일 시스템으로 설정한다.

Docker와 Linux?

도커는 리눅스 커널의 네임스페이스, cgroups 등의 기능을 활용해 애플리케이션을 가볍게 격리하고 실행할 수 있다. Windows나 macOS에서 도커를 실행하려면 리눅스 커널이 필요하다. 이를 위해 가상화를 통해 리눅스 환경을 제공하는 방법이 사용된다.

  • Windows: Windows 10이상에서는 WSL2(Windows Subsystem for Linux2)를 사용해 리눅스 커널을 가상화하고, 그 위에서 도커를 실행한다.
  • macOS: HyperKit, VirtualBox, 또는 QEMU 같은 가상화 소프트웨어를 사용해 가상 머신을 실행한 후 그 위에서 도커를 실행한다. 이러한 가상화 계층을 통해 리눅스 환경이 아니더라도 도커를 사용할 수 있지만, 리눅스 환경에서는 이러한 가상화 단계가 없이 바로 도커가 실행되므로 더 효율적이고 가볍게 동작한다.
This post is licensed under CC BY 4.0 by the author.