Container?, Docker?

컨테이너란 무엇이고 왜 필요하고 무엇을 할 수 있을까요?
- 컨테이너의 목표는 소프트웨어의 구성 요소와 종속성을 하나로 묶어 이식 가능한 형식으로 캡슐화해 모든 시스템에 추가 종속성 없이 실행할 수 있도록 하는 것
컨테이너를 알기 전 리눅스의 OS Architecture와 가상화에 대한 이해가 필요합니다.
1. 리눅스 OS Architecture
GNU(OS Project)
1985년에 Free Software Foundation 설립되어 운영
gcc, glibc, GDB, coreutils, binutuils, bash 등의 SW가 제작되었으며
1989년에 GNU GPL(General Public License)을 발표하였고
다양한 Kernel(linux, FreeBSD, Linux-Libre, GNU-Hurd)과 조합해 OS를 운영
Linux OS
1991년 Linus Torvalds가 Linux kernel에 GNU GPLv2 라이선스를 사용하여 배포

-
하드웨어(HW)
- 하드웨어는 CPU, 메모리, 저장 장치, 네트워크 장치 등 물리적인 컴퓨터 부품을 의미합니다. 운영체제가 하드웨어와 직접적으로 소통하지는 않고, 커널이 하드웨어 자원을 관리하여 애플리케이션이 이를 사용할 수 있도록 지원합니다.
-
커널(Kernel)
- 커널은 운영체제의 핵심 부분으로, 하드웨어와 직접 소통하며 리소스를 관리하는 역할을 합니다. 커널은 하드웨어 자원을 애플리케이션에 할당하고, 메모리 관리, 프로세스 관리, 파일 시스템 접근, 네트워크 관리 등의 기능을 제공합니다. 이 계층은 하드웨어와 상위 계층인 쉘 및 애플리케이션 프로그램을 연결하는 중간다리 역할을 합니다.
-
쉘(Shell)
- 쉘은 사용자가 커널과 상호작용할 수 있는 인터페이스입니다. 명령어를 통해 커널에 직접 명령을 전달할 수 있으며, 사용자가 입력한 명령어를 해석하여 커널로 보내는 역할을 합니다. 쉘은 CLI(Command Line Interface) 형태로 사용자와 커널 사이의 소통을 가능하게 하며, 쉘 스크립트를 사용하여 복잡한 명령어를 자동으로 처리할 수도 있습니다.
애플리케이션 프로그램(Application Program)
- 쉘은 사용자가 커널과 상호작용할 수 있는 인터페이스입니다. 명령어를 통해 커널에 직접 명령을 전달할 수 있으며, 사용자가 입력한 명령어를 해석하여 커널로 보내는 역할을 합니다. 쉘은 CLI(Command Line Interface) 형태로 사용자와 커널 사이의 소통을 가능하게 하며, 쉘 스크립트를 사용하여 복잡한 명령어를 자동으로 처리할 수도 있습니다.
-
애플리케이션 프로그램
- 사용자가 원하는 작업을 수행하는 소프트웨어로, 웹 브라우저, 텍스트 편집기, 데이터베이스 관리 시스템 등이 여기에 속합니다. 애플리케이션은 쉘이나 GUI를 통해 사용자 명령을 받아 커널을 통해 하드웨어 자원을 사용하게 됩니다.

처리 방식
- 사용자는 애플리케이션 프로그램을 통해 작업을 요청합니다.
- 애플리케이션 프로그램은 쉘을 통해 명령어를 커널에 전달하거나, 시스템 호출을 통해 커널과 직접 상호작용합니다.
- 커널은 하드웨어와 소통하여 필요한 자원을 애플리케이션에 할당합니다. 예를 들어, 메모리나 CPU 시간을 할당하거나, 파일 시스템을 통해 데이터를 저장하고 로드하는 작업을 수행합니다.
- 하드웨어는 커널의 지시에 따라 애플리케이션이 요청한 작업을 처리합니다.
이 과정이 상호 연동되어 Linux OS가 효율적으로 작동하며, 사용자는 이 계층 구조를 통해 하드웨어를 간접적으로 제어할 수 있게 됩니다.
커널의 상세

Arch-dependent Code (아키텍처 종속 코드)
시스템의 하드웨어 아키텍처에 따라 다르게 설계되는 코드, Linux 커널은 다양한 하드웨어 플랫폼을 지원하기 위해 설계되었기 때문에, 각 아키텍처마다 필요한 고유한 코드가 필요합니다. 이 코드들은 주로 CPU 아키텍처별로 나뉘어져 있으며, 각 플랫폼에 맞는 메모리 관리, 인터럽트 처리, 프로세서 레지스터 초기화 등 하드웨어와 밀접하게 연관된 기능을 수행합니다.
주요 기능: 메모리 관리, 인터럽트 처리, 시스템 부트 로딩, I/O 연산
예시: x86, ARM, MIPS, PowerPC 등과 같은 아키텍처의 특성에 맞춘 코드
파일 위치: Linux 커널 소스 코드의 arch/ 디렉토리에 위치하며, 각 하위 폴더는 특정 아키텍처를 위한 코드로 구성됩니다 (예: arch/x86, arch/arm).
Drivers and Dynamic Modules
Linux 커널에서 드라이버와 다이나믹 모듈은 하드웨어 장치를 운영체제가 인식하고 제어할 수 있도록 하는 코드를 포함합니다. 드라이버는 기본적으로 하드웨어의 동작을 제어하는 소프트웨어로, CPU, 네트워크 카드, 그래픽 카드, USB 장치 등 다양한 하드웨어를 지원합니다. Linux 커널에서 드라이버는 모듈 형태로 구현되며, 필요에 따라 동적으로 로드되고 제거될 수 있습니다.
주요 기능: 하드웨어 초기화, 데이터 전송 제어, 장치와 커널 간의 인터페이스 제공
드라이버 유형:
블록 장치 드라이버: 디스크와 같은 저장 장치를 위한 드라이버 (예: SCSI, SATA)
네트워크 장치 드라이버: 네트워크 장치에 대한 지원 (예: Ethernet)
문자 장치 드라이버: 키보드, 마우스, 시리얼 포트 등 문자 기반 장치 (예: 콘솔)
가상 파일 시스템 드라이버: 다양한 파일 시스템을 지원 (예: ext4, NTFS )
예시:
네트워크 카드 드라이버: e1000.ko (Intel 네트워크 카드)
그래픽 드라이버: nvidia.ko (NVIDIA 그래픽 카드용 드라이버)
USB 드라이버: usb-storage.ko (USB 저장 장치용 드라이버)
동적 로딩 및 제거: modprobe 명령을 사용하여 모듈을 로드하거나 제거할 수 있으며, 이를 통해 필요할 때만 드라이버를 불러와 메모리를 효율적으로 사용할 수 있습니다.
2. 가상화 란?
HW, 스토리지, N/W등의 물리 장치들을 가상 버전인 논리 장치로 구현하는 것
그렇다면 가상화가 왜 필요하게 되었을까요? 다양한 이유가 있지만 핵심은
- 물리적 자원의 낭비를 줄이고
- 데이터센터의 관리 및 운영을 효율화하며
- 운영비용 절감과 클라우드 서비스의 확장성 기여 하기위해 필요 합니다.
-
서버 가상화
- 물리서버를 여러가지 형태로 구현
- 전체 HW, 특정 요소의 논리적 추상화, OS 실행을 위한 기능만 구현한 형태 등
-
서버 가상화 방법
- HW-Based Prtition Mode : Superdom 등
- SW(VM)-Based VM model : VMware, Openstack ... 등
VM-Based 가상화(Full, Para, OS Level, Type1, 2)전 가상화(Full Virtualization)
- HW 완벽히 추상화, 게스트 OS가 실제 하드웨어에서 동작 인식 가상화 방식
- 하이퍼바이저가 하드웨어 자원을 에뮬레이션하여 게스트 OS에 제공
- 시스템의 모든 것이 가상화 됨: CPU, Storage, Networking, etc...
- 에뮬레이션으로 인한 성능 저하가 발생할 수 있습니다.
반 가상화(Paravirtualization)
- 게스트 OS가 하이퍼바이저와 상호작용할 수 있도록 일부 수정된 상태에서 동작
- HW 에뮬레이션하지 않고, 하이퍼바이저 제공 API로 게스트 OS와 직접 통신
- 특정 OS에만 적용할 수 있습니다.
- VM Guest를 수정함(Virtual HW and/or OS) : VM의 I/O 성능이 개선됨

(VMware VM Tools를 통해 반 가상화(Paravirtualization)의 개념을 부분적으로 적용하는 기능을 제공해 하드웨어 에뮬레이션의 오버헤드를 줄임)
Type1, Type2 가상화Type1, Type2 가상화
- Type1(Native/Bare-metal Hypervisor) : 하이퍼바이저는 하드웨어에서 직접 실행되는 하이퍼바이저로, 물리서버 위에 설치되며 운영체제 역할을 대신합니다.
하이퍼바이저가 곧 OS이기 때문에 리소스를 직접 제어하고 분배할 수 있어 매우 효율적입니다. - Type2(Hosted Hypervisor) : 하이퍼바이저는 일반적인 OS 위에서 애플리케이션 형태로 실행되는 하이퍼바이저입니다.
호스트 OS가 하드웨어를 관리하고, 하이퍼바이저는 그 위에서 동작하면서 가상 머신을 실행합니다. 개인 사용자를 위한 가상화 환경에서 주로 사용됩니다.

3. OS, APP Container?
- OS level Virtualiztion(OS 수준 가상화, OS 컨테이너)
- HW나 OS는 그대로이며, APP이 OS를 바라보는 환경만 격리 되는 기술
LXC(Linux Container) : OS 컨테이너
- 가상화 및 APP 격리 요구로 등장한 경량 컨테이너 기술
- 기존 하드웨어 가상화와 달리 커널 레벨에서 프로세스를 격리하고 리소스 사용량을 제한, 독립된 시스템처럼 동작
- 오버헤드가 적고 더 많은 APP을 컨테이너로 동작시켜 동시에 운영할 수 있게 되어 효율성이 크게 향상
cgroups과 namespace
- cgroups, namespace 리눅스 커널 기능 사용, 컨테이너 간 자원 격리 및 관리 제공
- cgroups(Control Groups)
- CPU, 메모리, DISK I/O, N/W 대역폭 등 시스템 리소스를 제한, 관리 기능
- namespaces
- 프로세스가 시스템 리소스를 자신만의 독립된 뷰를 가지도록 하는 기술
- 프로세스는 namespaces를 통해 FS, PID, N/W, userID 등을 독립적 사용
- PID, 네트워크, 마운트, IPC , UTS NameSpace가 주로 사용
- cgroups(Control Groups)
4. Docker Architecture
Docker 아키텍처는 컨테이너화된 애플리케이션의 생성, 배포, 구축을 가능하게 하는 클라이언트-서버 모델
High-Level Architecture
Docker의 상위 레벨 아키텍처는 클라이언트-서버 모델을 중심으로 돌아가며, 여기서 클라이언트는 Docker 데몬 (서버)과 상호 작용하여 컨테이너와 관련 리소스를 관리합니다. Docker는 핵심적으로 클라이언트, 데몬, 이미지라는 세 가지 핵심 구성 요소로 구성됩니다. 클라이언트와 데몬은 REST API, UNIX 소켓 또는 네트워크 인터페이스를 사용하여 통신합니다.
-
Docker Client
- 사용자가 명령을 내리고 Docker 리소스를 관리하기 위해 상호 작용하는 명령줄 도구, API 또는 그래픽 인터페이스입니다. 클라이언트는 Docker 데몬에 요청을 보내고, 데몬은 해당 명령의 실행을 조율합니다.
-
HOST(Docker Daemon)
- Docker Engine이라고도 알려진 이 서비스는 호스트 머신에서 실행되는 백그라운드 서비스이자 장기 실행 프로세스로, 실제로 컨테이너와 컨테이너 이미지를 실행하고 관리하는 작업을 수행합니다.
- Docker Daemon은 컨테이너의 수명 주기를 관리하고 작업을 조정하는 역할을 합니다.
- Docker 클라이언트의 요청을 수신하고, 컨테이너를 관리하고, 다양한 Docker 작업을 조정합니다. 이 데몬은 호스트 운영 체제의 커널과 상호 작용하고 컨테이너화, 네트워킹 및 스토리지를 위해 커널 기능과 모듈을 활용합니다.
-
Docker Registry
- 컨테이너 이미지를 저장하는 레지스트리 입니다.
- Docker Hub는 누구나 사용할 수 있는 공개 레지스트리이며 Docker는 기본적으로 Docker Hub에서 이미지를 찾도록 구성되어 있습니다.
Low-Level Architecture
- Docker 클라이언트는 RESTful API, CLI를 통해 Docker 데몬 (dockerd) 과 상호 작용
- 컨테이너 런타임으로 Containerd를 사용합니다. Containerd는 물리적 또는 가상 머신에서 컨테이너의 수명 주기를 관리하는 산업 표준 런타임입니다. 컨테이너를 생성, 시작, 중지 및 파괴하는 데몬 프로세스입니다.
- Containerd 플러그인을 containerd에 추가하면 기능을 확장할 수 있습니다.
- 컨테이너가 시작되면 containerd의 일부인 shim(런타임 v2) API가 containerd와 OCI 런타임 사이의 중개자 역할을 합니다 .
- OCI 런타임은 컨테이너의 네임스페이스, 제어 그룹(cgroup), 기능 및 컨테이너화에 필요한 기타 설정을 설정하는 역할을 합니다. Linux 커널의 기능을 활용하여 리소스를 격리하고 제어하고, 보안을 강화하고, 컨테이너의 동작을 관리합니다. cgroup은 프로세스에 대한 세분화된 리소스 할당 및 제어를 허용하는 Linux 커널 기능입니다. Linux 커널 내의 기능은 컨테이너에 권한을 제공하여 다양한 시스템 리소스에 대한 권한과 액세스를 정의합니다.
컨테이너 런타임 인터페이스(CRI)
- 쿠버네티스가 컨테이너 런타임과 통신하기 위한 인터페이스로, Docker와 직접적인 상관은 없으나, Docker를 쿠버네티스에서 사용하고자 할 때는 CRI를 통한 연결이 필요합니다.
- Docker를 쿠버네티스에서 사용하기 위해 cri-dockerd와 같은 플러그인이 필요하며, 이는 쿠버네티스에서 CRI를 통해 Docker 런타임에 요청을 보내도록 합니다.
- containerd도 자체적으로 CRI 호환 인터페이스를 제공하므로, 쿠버네티스는 CRI를 통해 Docker나 containerd와 통신하고, 이 런타임들이 Linux Kernel과의 통신을 담당하게 됩니다.
Docker CLI
명령을 Docker의 REST API 요청으로 변환하여 dockerd
에 전송합니다. 이 요청은 HTTP를 통해 전달됩니다.
Dockerd
Docker 데몬(dockerd)은 Docker 엔진의 핵심 컴포넌트로, Docker API 요청을 처리하고 컨테이너 관리를 수행
Containerd
물리적 또는 가상 머신에서 컨테이너의 수명 주기를 관리하는 산업 표준 런타임입니다. 컨테이너를 생성, 시작, 중지 및 파괴하는 데몬 프로세스입니다.
Docker Shim
- containerd-shim은 containerd와 runc 사이에서 작동하여 컨테이너의 상태 관리와 데몬과의 독립성을 제공합니다.
- 컨테이너가 생성된 후 runc가 종료되면 shim이 컨테이너의 프로세스를 관리하게 되며, 이로 인해 containerd나 Docker Daemon이 재시작되더라도 컨테이너가 종료되지 않고 유지될 수 있습니다.
- Shim은 containerd와의 연결을 최소화하고 자원을 절약하면서도 컨테이너 상태를 안정적으로 유지할 수 있게 돕습니다.
Runc
Open Container Initiative (OCI) 표준을 준수하는 가벼운 컨테이너 런타임입니다. Docker 엔진의 핵심 부분 중 하나로, 컨테이너를 실행하고 관리하는 데 사용됩니다
Docker Registry
Docker 이미지를 저장하고 배포하는 시스템으로, 컨테이너화된 애플리케이션을 개발하고 배포하는 데 필수적인 역할을 합니다. Docker Registry는 이미지의 버전 관리, 검색 및 관리 기능을 제공하여 개발자와 운영 팀이 효율적으로 작업할 수 있도록 지원
-
Docker Registry의 역할
-
이미지 저장: Docker Registry는 컨테이너 이미지를 저장하는 중앙 저장소 역할을 합니다. 개발자는 애플리케이션을 컨테이너화한 이미지를 Registry에 푸시(push)하고, 다른 개발자는 이를 풀(pull)하여 사용할 수 있습니다.
-
버전 관리: Docker 이미지는 태그(tag)를 사용하여 다양한 버전을 관리할 수 있습니다. 예를 들어, myapp:latest와 myapp:v1.0 같은 방식으로 서로 다른 버전을 구분할 수 있습니다.
-
보안: Docker Registry는 인증 및 권한 부여 기능을 제공하여, 안전하게 이미지를 관리할 수 있도록 합니다. 이를 통해 인증된 사용자만 이미지에 접근할 수 있습니다.
-
모니터링 및 관리: 이미지를 관리하고 상태를 모니터링하는 도구를 제공하여, 사용자가 이미지를 효율적으로 관리할 수 있도록 도와줍니다.
-
-
Docker Registry는 내부(프라이빗) Registry와 외부(퍼블릭) Registry로 나눌 수 있습니다.
-
내부 Registry (프라이빗 Registry)
- 내부 Registry는 조직 내에서 자체적으로 운영되는 저장소로, 보안성과 통제성을 높일 수 있습니다. 일반적으로 회사의 방화벽 뒤에서 운영되며, 내부 개발자만 접근할 수 있도록 설정됩니다.
- 예시:
Harbor: 오픈 소스 컨테이너 이미지 저장소로, 프라이빗 Registry의 기능을 강화하고 사용자 관리, 레포지토리 관리, 이미지 스캔 기능을 추가합니다.
Docker Registry: Docker의 기본 제공 Registry로, 간단하게 설정하여 내부 네트워크에서 사용할 수 있습니다.
-
외부 Registry (퍼블릭 Registry)
- 외부 Registry는 인터넷을 통해 접근할 수 있는 공개된 저장소로, 누구나 이미지에 접근할 수 있으며, 일반적으로 오픈 소스 프로젝트나 공유가 필요한 이미지를 저장하는 데 사용됩니다.
- 예시:
Docker Hub: 가장 널리 사용되는 퍼블릭 Registry로, 다양한 공식 이미지와 커뮤니티 이미지를 호스팅합니다. 사용자는 자신의 이미지를 Docker Hub에 푸시하거나, 다른 사용자의 이미지를 풀할 수 있습니다.
Quay.io: Red Hat에서 운영하는 퍼블릭 Registry로, 고급 보안 및 자동화 기능을 제공하여 기업 환경에서 선호됩니다.
-
Docker Registry는 기본적으로 다음과 같은 구성 요소로 이루어져 있습니다
- Repository: 이미지의 저장 단위로, 서로 관련된 여러 태그를 포함합니다. 예를 들어, myapp이라는 Repository는 myapp:latest, myapp:v1.0과 같은 태그를 가질 수 있습니다.
- Tag: 이미지의 특정 버전을 나타내는 레이블로, 이를 통해 사용자는 원하는 버전을 쉽게 찾고 사용할 수 있습니다.
- Manifest: Registry에 저장된 이미지의 메타데이터로, 이미지의 구성 요소(레이어 및 태그)와 관련된 정보를 포함합니다.
Docker Registry는 Docker 이미지를 저장하고 관리하는 핵심 컴포넌트로, 내부와 외부 Registry 모두 각각의 용도와 특성을 가지고 있습니다. 내부 Registry는 보안과 통제성을 중시하는 환경에 적합하고, 외부 Registry는 공개된 이미지 공유와 커뮤니티 활용에 유리합니다. 이러한 Registry를 통해 개발자와 운영 팀은 효과적으로 컨테이너화된 애플리케이션을 배포하고 관리할 수 있습니다.
Docker Image
이미지의 이름은 [registry / location /] image(repo name) [:tag] 기술
- registry/location 생략될 경우 기본값은 docker.io(Docker Hub)
- tag 생략 기본값은
- latest(repository 상의 다른 이미지의 버전 정보 및 작성시점 등 상관X)
Docker Hub에 이미지 공유를 위해
- Docker Hub repository 생성
- Repository Property 속성 (Public/Private)설정
- 생성한 이미지에 tag한 뒤 Docker Hub Push
- Docker Hub 이미지 Pull 하여 이미지 실행
가상화, LXC, Docker 비교

-
가상화: 서버 전체를 가상화하여 OS 레벨의 가상화 서비스를 제공합니다. 하이퍼바이저를 통해 여러 가상 머신이 각각의 운영 체제를 실행합니다.
-
LXC: 호스트의 커널을 공유하면서 OS 레벨의 가상화와 리소스 관리를 제공합니다. 네임스페이스와 cgroups를 사용하여 격리된 환경을 만듭니다.
-
Docker: 호스트의 커널을 공유하면서 OS 레벨 가상화보다는 애플리케이션 서비스의 격리에 중점을 둡니다. 이미지 기반으로 컨테이너를 배포하고 관리합니다.

마지막으로 컨테이너는 프로세스인가?
컨테이너는 호스트 OS에서 실행되는 격리된 프로세스라고 할 수 있습니다. 기술적으로는 Linux 네임스페이스와 cgroups를 통해 격리된 상태로 실행되므로, 실제로는 OS의 프로세스로 존재하지만, 마치 독립된 시스템처럼 동작합니다.
즉, 컨테이너는 VM처럼 보일 수 있지만, 이는 격리 기술과 Docker 같은 관리 도구 덕분이지 실제로 VM은 아닙니다. 컨테이너는 OS에서 실행되는 하나의 프로세스이지만, 높은 수준의 추상화와 격리 덕분에 독립적인 환경으로 느껴지는 것입니다.
따라서, 엄밀히 말하면 컨테이너는 프로세스이지만, 격리된 구조 덕분에 단순한 프로세스 이상의 개념으로 취급됩니다.