# Docker 베이스 이미지 이해하기: 컨테이너 속 Ubuntu는 진짜 Ubuntu가 아님

> Clean Markdown view of GeekNews topic #25997. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=25997](https://news.hada.io/topic?id=25997)
- GeekNews Markdown: [https://news.hada.io/topic/25997.md](https://news.hada.io/topic/25997.md)
- Type: news
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-01-21T09:46:01+09:00
- Updated: 2026-01-21T09:46:01+09:00
- Original source: [oneuptime.com](https://oneuptime.com/blog/post/2026-01-19-how-os-base-images-work-in-docker/view)
- Points: 71
- Comments: 9

## Summary

Docker 컨테이너의 **Ubuntu 베이스 이미지**는 실제 운영체제가 아니라, Ubuntu의 사용자 공간 도구와 라이브러리만을 담은 파일시스템 스냅샷입니다. 컨테이너 내부에서 실행되는 모든 시스템 콜은 호스트의 **Linux 커널로 직접 전달**되며, 이 구조 덕분에 컨테이너는 밀리초 단위로 시작하고 수십 개 이상을 동시에 실행할 수 있습니다. 그러나 커널을 공유하기 때문에 하나의 취약점이 모든 컨테이너에 영향을 미칠 수 있어, **호스트 커널 보안과 베이스 이미지 선택**이 운영 안정성의 핵심이 됩니다.

## Topic Body

- `docker run ubuntu`를 실행해도 **호스트 Linux 커널을 공유**하며 Ubuntu는 **사용자 공간 도구만 제공**함  
- `uname -r` 결과가 **호스트 커널 버전**으로 표시되고, `/etc/os-release`만 Ubuntu 정보로 나타남  
- VM은 각각 고유한 커널을 가지고 부팅에 수 분이 걸리지만, 컨테이너는 **밀리초 단위로 시작**되고 **하드웨어 가상화 없이 OS 수준 격리**로 호스트 커널을 공유하여 오버헤드가 낮음  
- 리눅스 **시스템 콜 ABI의 안정성**으로 다양한 배포판 컨테이너가 동일 커널에서 동작 가능함  
- 16GB RAM 환경에서 경량 컨테이너는 50-100개, 중간 규모는 10-30개, **대형 컨테이너는 5-10개** 정도가 실용적인 한계  
- 이 아키텍처 이해가 중요한 이유는 커널 취약점이 모든 컨테이너에 영향을 미치고, **베이스 이미지 선택**이 호환성과 보안에 직접적 영향을 주기 때문  
---  
### “Ubuntu를 실행한다”는 의미  
- `docker run ubuntu:22.04` 실행 시 Ubuntu처럼 보이는 bash 프롬프트를 얻고 `apt update`와 패키지 설치가 가능  
- 그러나 컨테이너 내부에서 `uname -r` 실행 시 **호스트 커널 버전**(예: 6.5.0-44-generic)이 표시됨  
- `/etc/os-release` 파일은 Ubuntu 22.04로 표시되지만, **커널은 호스트 머신의 것**이며 "Ubuntu" 부분은 **사용자 공간을 구성하는 파일시스템**에 불과  
  
### 컨테이너 vs 가상 머신: 아키텍처 비교  
- VM은 하드웨어를 가상화하고, 컨테이너는 **운영체제를 가상화**  
- 주요 차이점:  
  - **커널**: VM은 각각 고유 커널 보유, 컨테이너는 호스트 커널 공유  
  - **부팅 시간**: VM은 수 분, 컨테이너는 **밀리초**  
  - **메모리 오버헤드**: VM은 512MB-4GB, 컨테이너는 1-10MB  
  - **디스크 사용량**: VM은 10-100GB, 컨테이너 이미지는 10-500MB  
  - **격리 수준**: VM은 하드웨어 레벨, 컨테이너는 **프로세스 레벨**  
  - **성능**: VM은 약 5-10% 오버헤드, 컨테이너는 **네이티브에 가까운 성능**  
  
### 베이스 이미지의 실제 구성 요소  
- `ubuntu:22.04` 풀 시 다운로드되는 tarball 내용:  
- ## 1. 필수 바이너리 (`/bin`, `/usr/bin`)  
  - `/bin/bash` (셸), `/bin/ls` (파일 목록), `/bin/cat` (파일 표시)  
  - `/usr/bin/apt` (패키지 관리자), `/usr/bin/dpkg` (Debian 패키지 도구)  
- ## 2. 공유 라이브러리 (`/lib`, `/usr/lib`)  
  - **glibc**와 기타 프로그램이 링크하는 공유 라이브러리  
  - `/lib/x86_64-linux-gnu/libc.so.6` (C 라이브러리 - 모든 C 프로그램의 기반)  
  - `libpthread.so.0`, `libm.so.6` 등 필수 라이브러리  
- ## 3. 설정 파일 (`/etc`)  
  - `/etc/apt/sources.list` (패키지 저장소)  
  - `/etc/passwd` (사용자 데이터베이스)  
  - `/etc/resolv.conf` (DNS 설정, 보통 호스트에서 마운트)  
- ## 4. 패키지 데이터베이스  
  - `/var/lib/dpkg/status` (설치된 패키지)  
  - `/var/lib/apt/lists/` (사용 가능한 패키지 캐시)  
- 커널·부트로더·드라이버는 **포함되지 않음**  
  
### 커널은 그대로, 모든 것이 변함  
- Linux 커널이 제공하는 기능: **프로세스 스케줄링, 메모리 관리, 파일시스템 연산, 네트워크 스택, 디바이스 드라이버, 시스템 콜**  
- 컨테이너 프로세스가 `open()`, `read()`, `fork()`를 호출하면 **호스트 커널로 직접 전달**  
- 커널은 해당 프로세스가 "Ubuntu 컨테이너"인지 "Alpine 컨테이너"인지 **알지도 신경 쓰지도 않음**  
- ## 시스템 콜 인터페이스의 안정성  
  - Linux syscall ABI는 매우 안정적  
  - glibc 2.31(Ubuntu 20.04)에서 컴파일된 바이너리가 Ubuntu 24.04 커널에서도 동작하는 이유:  
    - 커널이 **하위 호환성** 유지  
    - 시스템 콜 번호 변경 없음  
    - 새 기능은 추가되지만 기존 기능은 거의 제거되지 않음  
  - 커널 6.5를 실행하는 호스트에서 Ubuntu 18.04 컨테이너 실행이 가능한 이유  
  
### 실습 해보기: 동일 커널, 다른 사용자 공간  
- 여러 베이스 이미지에서 동일한 커널 쿼리 실행 시 모든 이미지가 **호스트 커널을 공유한다는 것**을 볼수 있음  
- ubuntu:22.04, debian:12, alpine:3.19, fedora:39, archlinux:latest 모두 동일한 커널 버전(6.5.0-44-generic)임  
- 컨테이너별 차이는 `uname` 바이너리와 libc 등 **유저랜드 구성**임  
  
### 컨테이너가 매우 효율적인 이유  
- ## 1. 커널 중복 없음  
  - VM은 각각 전체 커널을 메모리에 로드(약 100-500MB)  
  - 10개 VM은 10개 커널이 메모리 소비, 10개 컨테이너는 **커널 하나만 사용**  
- ## 2. 즉시 시작  
  - VM 부팅 순서: BIOS → 부트로더 → 커널 → init 시스템 → 서비스  
  - 컨테이너는 `fork()`와 `exec()` 호출만으로 **밀리초 내 프로세스 존재**  
  - 일반적인 VM 부팅: 30-60초 / 컨테이너 시작: 약 0.347초  
- ## 3. 공유 이미지 레이어  
  - `ubuntu:22.04`에서 100개 컨테이너 실행 시 베이스 이미지 레이어는 **디스크에 한 번만 존재**  
  - 각 컨테이너는 변경 사항을 위한 **얇은 copy-on-write 레이어**만 획득  
- ## 4. 커널을 통한 메모리 공유  
  - 커널의 **페이지 캐시가 공유**됨  
  - 50개 컨테이너가 동일 파일 읽기 시 커널은 한 번만 캐시  
  - 동일한 공유 라이브러리 사용 시 **copy-on-write로 메모리 페이지 공유** 가능  
  
### 컨테이너 실행 가능 한계 계산  
- ## 메모리 분석 (16GB RAM VM 기준)  
  - 총 RAM: 16,384 MB  
  - 호스트 OS 오버헤드: -1,024 MB  
  - Docker 데몬: -256 MB  
  - 컨테이너 런타임 오버헤드: -512 MB  
  - **컨테이너 가용량: 14,592 MB**  
- ## 컨테이너 유형별 메모리 사용량  
  - **최소(sleep)**: 약 1MB  
  - **Alpine + 소형 앱**: 약 25MB  
  - **Ubuntu + Python 앱**: 약 120MB  
  - **Ubuntu + Java 앱**: 약 500MB  
  - **Node.js 서비스**: 약 200MB  
- ## 이론적 최대치  
  - 최소 컨테이너(1MB): 14,592개  
  - Alpine + 소형 앱(25MB): 583개  
  - Ubuntu + Python(120MB): 121개  
  - Java 마이크로서비스(500MB): 29개  
- ## 실제 한계  
  - 메모리 외 고려사항:  
    - **CPU 스케줄링**: 너무 많은 컨테이너 경쟁 시 지연 스파이크 발생  
    - **파일 디스크립터**: 기본 ulimit 1024  
    - **네트워크 포트**: 포트 매핑에 65,535개만 사용 가능  
    - **PIDs**: `/proc/sys/kernel/pid_max` 제한(기본: 32,768)  
    - **디스크 I/O**: OverlayFS 오버헤드, 많은 레이어 탐색 필요  
  - 16GB VM에서 실제 워크로드 실행 시 실용적 한계:  
    - **경량 컨테이너(API, 워커)**: 50-100개  
    - **중간 컨테이너(DB, 캐시)**: 10-30개  
    - **대형 컨테이너(ML 모델, JVM 앱)**: 5-10개  
  
### Linux 배포판 호환성  
- ## 커널 ABI 약속  
  - Linux는 안정적인 syscall 인터페이스 유지  
  - 오래된 커널용으로 컴파일된 바이너리가 새 커널에서 동작  
  - Ubuntu 18.04 바이너리가 커널 6.5에서 정상 실행  
- ## 호환성이 깨지는 경우  
  - **커널 기능 요구사항**: 컨테이너가 커널에 없는 기능 필요 시 (예: **io_uring**은 커널 5.1+ 필요)  
  - **커널 모듈 의존성**: Wireguard는 wireguard 커널 모듈 필요, NVIDIA 컨테이너는 nvidia 커널 드라이버 필요  
  - **Seccomp/capability 제한**: 호스트가 컨테이너에 필요한 syscall 차단 시 (예: ptrace 사용 시 `--cap-add SYS_PTRACE` 필요)  
  
### 베이스 이미지 선택 가이드  
| 베이스 이미지 | 크기 | 패키지 관리자 | 용도 |  
|---|---|---|---|  
| `scratch` | 0 MB | 없음 | **정적 컴파일**된 Go/Rust 바이너리 |  
| `alpine` | 7 MB | apk | 최소 컨테이너, **musl libc** |  
| `distroless` | 20 MB | 없음 | 보안 중심, 셸·패키지 관리자 없음 |  
| `debian-slim` | 80 MB | apt | 크기와 호환성 균형 |  
| `ubuntu` | 78 MB | apt | 개발 친숙성 |  
| `fedora` | 180 MB | dnf | 최신 패키지, SELinux |  
- ## 각 이미지 사용 시점  
  - **scratch**: 정적 컴파일 바이너리용, OS 전혀 없이 바이너리만 포함  
  - **alpine**: 셸 접근이 필요한 최소 이미지, glibc 대신 **musl libc** 사용으로 일부 호환성 문제 가능  
  - **distroless**: 보안 중심 프로덕션 이미지, 셸과 패키지 관리자 없어 디버깅 어렵지만 더 안전  
  
### 사용자 공간-커널 경계  
- ## 베이스 이미지에서 오는 것 (사용자 공간)  
  - 셸 (`/bin/bash`, `/bin/sh`)  
  - C 라이브러리 (**glibc, musl**)  
  - 패키지 관리자 (apt, apk, yum)  
  - 핵심 유틸리티 (ls, cat, grep)  
  - init 시스템 설정 (보통 systemd 자체는 아님)  
  - 기본 사용자와 그룹 (`/etc/passwd`)  
  - 라이브러리 경로와 설정  
- ## 호스트에서 오는 것 (커널)  
  - 프로세스 스케줄링과 메모리 관리  
  - 네트워크 스택 (TCP/IP, 라우팅)  
  - 파일시스템 연산 (읽기, 쓰기, 마운트)  
  - 보안 기능 (**네임스페이스, cgroups, seccomp**)  
  - 디바이스 드라이버 (GPU, 네트워크, 스토리지)  
  - 시간과 클럭 관리  
  - 암호화와 난수 생성  
- ## 네임스페이스가 만들어 내는 환각  
  - 커널이 네임스페이스를 제공하여 컨테이너가 격리된 것처럼 느끼게 함  
  - 컨테이너 내부에서 PID 1로 보이는 프로세스가 호스트에서는 더 높은 PID(예: 45678)로 존재  
  - 커널이 매핑 유지: 컨테이너 PID 1 → 호스트 PID 45678  
  - 가상화 없이 **격리가 작동하는 방식**  
  
### 프로덕션 환경에서의 의미  
- ## 1. 커널 취약점이 모든 컨테이너에 영향  
  - 호스트 커널에 취약점이 있으면 모든 컨테이너가 노출됨  
  - **호스트 패치 유지 필수**  
- ## 2. 호스트 커널이 컨테이너 기능 제한  
  - io_uring 사용하려면 호스트 커널 5.1+ 필요  
  - eBPF 기능은 특정 옵션이 활성화된 커널 4.15+ 필요  
- ## 3. glibc vs musl 중요성  
  - Alpine은 **musl libc** 사용  
  - glibc용으로 컴파일된 일부 바이너리가 동작하지 않을 수 있음  
  - 예: Alpine에서 glibc 바이너리 실행 시 `/lib/x86_64-linux-gnu/libc.so.6` 파일 없음 오류 발생 가능  
- ## 4. 컨테이너 "OS"는 순전히 조직적 개념  
  - 커널 관점에서 "Ubuntu 컨테이너"와 "Debian 컨테이너"는 **차이 없음**  
  - 모두 syscall을 만드는 프로세스일 뿐  
  
### 흔한 오해  
- ❌ "**컨테이너는 경량 VM**": 컨테이너는 고급 격리를 가진 프로세스이며, VM은 하드웨어를 가상화하고 별도 커널 실행  
- ❌ "**각 컨테이너가 고유 커널 보유**": 모든 컨테이너가 호스트 커널 공유, 컨테이너의 "OS"는 사용자 공간 파일일 뿐  
- ❌ "**Ubuntu 컨테이너 실행 = Ubuntu 실행**": 호스트 커널과 Ubuntu 도구 실행, 호스트가 Debian이면 실제로는 Debian 커널 실행  
- ❌ "**베이스 이미지에 완전한 운영체제 포함**": 베이스 이미지는 최소 사용자 공간 도구만 포함, 커널·부트로더·드라이버 없음  
- ❌ "**더 많은 컨테이너 = 더 많은 메모리**": 공유 레이어와 커널 페이지 캐싱으로 컨테이너가 종종 **효율적으로 메모리 공유**  
  
### 핵심 요약  
  
- Docker 베이스 이미지는 Linux 배포판의 **사용자 공간 구성 요소에 대한 파일시스템 스냅샷**   
  - Ubuntu를 Ubuntu처럼 느끼게 하는 바이너리, 라이브러리, 설정  
- 실제 운영체제인 **커널은 호스트와 공유**함  
- 이 아키텍처가 가능하게 하는 것:  
  - **밀리초 단위 시작 시간** (커널 부팅 없음)  
  - **최소한의 메모리 오버헤드** (하나의 커널, 공유 페이지)  
  - **대규모 밀도** (호스트당 수백 개 컨테이너)  
  - **네이티브에 가까운 성능** (커널로의 직접 syscall)  
- 트레이드오프는 VM보다 약한 격리 - 컨테이너가 커널을 공유하므로 **커널 익스플로잇이 모든 컨테이너에 영향**  
- 대부분의 워크로드에서 이 트레이드오프는 가치 있음

## Comments



### Comment 49648

- Author: bbulbum
- Created: 2026-01-22T01:41:36+09:00
- Points: 2

커널 + 도구 = 배포판  
그렇다면 이또한 우분투가 맞는것 아닌가..

### Comment 49619

- Author: sacredshine
- Created: 2026-01-21T16:44:50+09:00
- Points: 2

그래서 리눅스에서 직접 도커 만들어보기 해가지고 디렉토리 격리해서 유저랑 그룹이랑 이래저래 하는 튜토리얼도 있고 그러더라고요.

### Comment 49610

- Author: dongho42
- Created: 2026-01-21T13:24:57+09:00
- Points: 1

유익해요

### Comment 49609

- Author: seunggi
- Created: 2026-01-21T13:19:46+09:00
- Points: 1

https://news.hada.io/topic?id=9531  
  
그래서 좀 과장되게 말해서 chroot + cgroup = docker 이렇게 봐도 되겠더라고요

### Comment 49638

- Author: euphcat
- Created: 2026-01-21T21:13:06+09:00
- Points: 1
- Parent comment: 49609
- Depth: 1

Acktuallly 그건 `systemd-nspawn`이 조금 더 가깝긴 하죠 ☝️🤓

### Comment 49679

- Author: hohemian
- Created: 2026-01-22T12:48:30+09:00
- Points: 1
- Parent comment: 49638
- Depth: 2

Actually

### Comment 49681

- Author: euphcat
- Created: 2026-01-22T13:07:48+09:00
- Points: 2
- Parent comment: 49679
- Depth: 3

Sarcasm / Self-deprecating

### Comment 49600

- Author: crawler
- Created: 2026-01-21T11:56:12+09:00
- Points: 1

정말 신기하네요  
  
본문은 LINUX 기준으로 설명한 거 같지만,  
윈도우에서 실행하는 경우에는 WSL2로 만들어진 가상 커널을 글처럼 공유하게 되는 거겠죠?  
  
만약에 도커에 취약점이 생겨서 커널을 건드릴 수 있게 된다면 리눅스보다 한 번 가상화를 하는 윈도우가 더 보안에 강하다고 봐야 할 지도 궁금하네요.

### Comment 49649

- Author: minsuchae
- Created: 2026-01-22T01:56:42+09:00
- Points: 2
- Parent comment: 49600
- Depth: 1

윗 댓글 반응때매 약간 놀랐네요.  
전 당연히 알고 쓰고 있지 않을까 했었는데요.  
리눅스 커널은 호스트 - 나머진 리눅스 배포판에서 사용되는 툴들을 가져오는 형태여서요.  
  
WSL2 은 제가 알기론 Hyper-V 에서 가상화해서 돌립니다.  
윈도우 - 가상머신의 리눅스 - 그 안에서 다시 Container...  
  
기본적으로 Container 내부에서의 root가 진짜 전체 시스템의 root가 아니기 떄매 기본적으로 커널을 임의 조작하지 못하긴 합니다.  
다만 취약점 생기면 큰일나긴 합니다.  
  
성능적인 측면으로 보면 윈도우가 좀 더 그래서 느린 이유가 가상화를 한 번 더 거치기 때문입니다.
