컨테이너와 Docker
가상 머신은 훌륭한 격리를 제공하지만, 각 VM이 완전한 OS를 포함하므로 무겁습니다. 부팅에 수십 초가 걸리고, 수 GB의 디스크를 차지합니다. 컨테이너는 같은 커널을 공유하면서 프로세스를 격리하는 경량 가상화 기술입니다. 2013년 Docker의 등장 이후 컨테이너는 소프트웨어 배포의 표준이 되었습니다.
컨테이너 vs 가상 머신
| 비교 | 가상 머신 | 컨테이너 |
|---|---|---|
| 격리 수준 | OS 전체 격리 (커널 독립) | 프로세스 수준 격리 (커널 공유) |
| 시작 시간 | 수십 초 ~ 수 분 | 수 밀리초 ~ 수 초 |
| 이미지 크기 | 수 GB | 수십 MB ~ 수백 MB |
| 메모리 오버헤드 | OS당 수백 MB | 프로세스 크기 |
| 밀도 | 서버당 수십 개 | 서버당 수백~수천 개 |
| 보안 격리 | 강함 (하이퍼바이저 경계) | 상대적으로 약함 |
컨테이너는 같은 커널을 공유하므로, 커널 취약점이 발견되면 같은 호스트의 컨테이너들이 함께 영향을 받을 수 있습니다. 멀티테넌트 환경에서는 VM을 사용하거나, VM 위에서 컨테이너를 실행하는 방식을 택합니다.
리눅스 컨테이너의 핵심 기술
컨테이너는 완전히 새로운 가상화 기술이라기보다, 리눅스 커널에 이미 있는 격리와 자원 제어 기능을 런타임이 조합해서 제공하는 실행 환경입니다.
네임스페이스 (Namespaces)
프로세스에게 시스템 자원의 독립된 뷰를 제공합니다. 컨테이너 안의 프로세스는 외부를 볼 수 없습니다.
| 네임스페이스 | 격리 대상 | 효과 |
|---|---|---|
| PID | 프로세스 ID | 컨테이너 안에서 PID 1번부터 시작 |
| NET | 네트워크 스택 | 독립된 IP, 포트, 라우팅 테이블 |
| MNT | 파일 시스템 마운트 | 독립된 루트 파일 시스템 |
| UTS | 호스트 이름 | 컨테이너마다 다른 hostname |
| IPC | 프로세스 간 통신 | 독립된 메시지 큐, 세마포어 |
| USER | 사용자/그룹 ID | 컨테이너 안 root ≠ 호스트 root |
| CGROUP | cgroup 뷰 | 자원 제한의 독립적 뷰 |
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <sys/wait.h>
/* 새 네임스페이스에서 실행될 함수 */
int child_func(void *arg) {
printf("자식 PID (네임스페이스 내부): %d\n", getpid()); /* 1 */
char hostname[] = "container";
sethostname(hostname, sizeof(hostname) - 1);
/* 새 네임스페이스에서 셸 실행 */
execlp("/bin/sh", "sh", NULL);
return 0;
}
int main() {
char stack[65536];
/* 새 PID + UTS + NET 네임스페이스로 프로세스 생성 */
pid_t pid = clone(
child_func,
stack + sizeof(stack),
CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET | SIGCHLD,
NULL
);
printf("호스트에서 본 자식 PID: %d\n", pid); /* 실제 PID */
waitpid(pid, NULL, 0);
return 0;
}cgroups (Control Groups)
프로세스 그룹을 계층적으로 묶고 CPU, 메모리, I/O, PID 같은 자원 사용량을 제한하거나 분배합니다.
# cgroup v2에서 메모리 제한 설정 (개념)
# /sys/fs/cgroup/my-container/
# 메모리 제한: 256MB
echo $((256 * 1024 * 1024)) > /sys/fs/cgroup/my-container/memory.max
# CPU 제한: 50% (100ms 중 50ms만 사용)
echo "50000 100000" > /sys/fs/cgroup/my-container/cpu.max
# PID 개수 제한: 최대 100개 프로세스
echo 100 > /sys/fs/cgroup/my-container/pids.max
# 프로세스를 cgroup에 추가
echo $ > /sys/fs/cgroup/my-container/cgroup.procs| 컨트롤러 | 제한 대상 | 설정 파일 | 예시 |
|---|---|---|---|
| memory | 메모리 사용량 | memory.max | 512MB |
| cpu | CPU 시간 | cpu.max | 1.5 코어 |
| io | 디스크 I/O | io.max | 100MB/s |
| pids | 프로세스 수 | pids.max | 100개 |
Docker의 동작 원리
Docker는 컨테이너를 쉽게 빌드, 배포, 실행하기 위한 도구입니다.
이미지와 레이어
Docker 이미지(Image)는 컨테이너의 파일 시스템 스냅샷입니다. 이미지는 레이어(Layer)로 구성됩니다.
이미지 레이어는 읽기 전용이며, 컨테이너가 실행되면 그 위에 쓰기 가능한 레이어가 추가됩니다(Union FS: OverlayFS). 같은 베이스 이미지를 공유하는 컨테이너들은 중복 저장 없이 공통 레이어를 공유하므로 디스크를 절약합니다.
Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# non-root 사용자로 실행 (보안)
RUN useradd -r appuser
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]각 명령어(FROM, RUN, COPY)가 하나의 레이어를 생성합니다. RUN 명령을 합치면 레이어 수를 줄여 이미지 크기를 최적화할 수 있습니다.
Docker 실행
# 이미지 빌드
docker build -t myapp:1.0 .
# 컨테이너 실행 (리소스 제한 + 보안 옵션)
docker run -d \
--name web \
-p 8080:80 \
--memory=512m \
--cpus=1.0 \
--read-only \
--security-opt=no-new-privileges \
nginx:alpine
# 실행 중인 컨테이너 목록
docker ps
# 컨테이너 내부 접속
docker exec -it web /bin/sh
# 리소스 사용량 모니터링
docker stats web
# 컨테이너가 사용하는 네임스페이스 확인
docker inspect --format '{{.State.Pid}}' web
# ls -la /proc/<PID>/ns/--memory=512m은 cgroups로 메모리를 512MB로 제한합니다. 이 한도를 초과하면 OOM Killer가 컨테이너 프로세스를 종료합니다.
컨테이너 격리의 수준과 한계
컨테이너가 같은 커널을 공유한다는 것은 커널 수준의 취약점에 모든 컨테이너가 노출된다는 뜻입니다.
격리 강화 기술
| 기술 | 방식 | 보안 수준 |
|---|---|---|
| rootless 컨테이너 | 엔진을 non-root로 실행 | 중 |
| seccomp | 허용 시스템 콜 제한 | 중상 |
| AppArmor/SELinux | MAC 정책 적용 | 상 |
| user namespace | 컨테이너 root ≠ 호스트 root | 중상 |
| gVisor | 사용자 공간 커널 (시스콜 프록시) | 상 (성능 감소) |
| Kata Containers | 경량 VM 안에서 컨테이너 실행 | 최상 |
| Firecracker | 마이크로 VM (AWS Lambda 사용) | 최상 |
# Docker 기본 seccomp 프로파일은 위험한 시스템 콜 일부를 차단
# 예: mount, reboot, syslog 등 운영체제 전체에 영향을 주는 호출
# 커스텀 seccomp 프로파일 적용
docker run --security-opt seccomp=custom-profile.json myapp실무에서는 보안 요구사항에 따라 적절한 수준을 선택합니다. 일반 웹 서비스는 rootless + seccomp으로 충분하고, 멀티테넌트 환경은 Kata/Firecracker를 고려합니다.
다음 절에서는 컨테이너를 대규모로 관리하는 오케스트레이션과 실무 인프라 선택 기준을 다루겠습니다.