Linux 심화: 낮은 수준 이해
Linux 커널의 낮은 수준 동작 원리: 시스템콜, 네임스페이스, cgroups, 컨테이너 기초
Linux 커널의 낮은 수준 동작 원리: 시스템콜, 네임스페이스, cgroups, 컨테이너 기초
목표: 커널 동작을 이해하고, 컨테이너 기술의 근간을 설명할 수 있는 수준
🧠 커널 아키텍처
사용자 공간 vs 커널 공간
┌─────────────────────────────────────┐
│ 사용자 공간 (User Space) │
│ ┌─────────┐ ┌─────────┐ ┌────────┐ │
│ │ 쉘 │ │ 앱 │ │ 라이브러리 │ │
│ │ bash │ │ nginx │ │ glibc │ │
│ └───┬─────┘ └────┬────┘ └───┬────┘ │
│ │ │ │ │
│ └────────────┴──────────┘ │
│ 시스템콜 인터페이스 │
│ │ │
├───────────────────┼─────────────────┤
│ ▼ │
│ 커널 공간 (Kernel Space) │
│ ┌─────────────────────────────┐ │
│ │ 시스템콜 처리 (Syscalls) │ │
│ │ VFS (가상 파일시스템) │ │
│ │ 프로세스 스케줄러 │ │
│ │ 메모리 관리자 │ │
│ │ 네트워크 스택 │ │
│ │ 장치 드라이버 │ │
│ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ 하드웨어 │
└─────────────────────────────────────┘
시스템콜 (System Calls)
사용자 공간이 커널 서비스를 요청하는 인터페이스
# 시스템콜 추적
strace -c ls # 시스템콜 통계
strace -e open,read,write ls # 특정 시스템콜만
strace -p PID # 실행 중인 프로세스
strace -f -o output.txt ./app # 자식 프로세스 포함
주요 시스템콜:
| 시스템콜 | 기능 | C 함수 |
|---|---|---|
read | 파일 읽기 | read() |
write | 파일 쓰기 | write() |
open | 파일 열기 | open() |
close | 파일 닫기 | close() |
fork | 프로세스 복제 | fork() |
execve | 프로그램 실행 | execve() |
mmap | 메모리 매핑 | mmap() |
brk | 힙 확장 | brk() |
socket | 소켓 생성 | socket() |
clone | 스레드/프로세스 생성 | clone() |
🏗️ 네임스페이스 (Namespaces)
네임스페이스 개요
프로세스별 시스템 리소스 격리 메커니즘
| 네임스페이스 | 플래그 | 격리 대상 | 도입 버전 |
|---|---|---|---|
| Mount | CLONE_NEWNS | 마운트 포인트 | 2.4.19 |
| UTS | CLONE_NEWUTS | 호스트명/도메인명 | 2.6.19 |
| IPC | CLONE_NEWIPC | System V IPC, POSIX MQ | 2.6.19 |
| PID | CLONE_NEWPID | 프로세스 ID | 2.6.24 |
| Network | CLONE_NEWNET | 네트워크 장치, 스택, 포트 | 2.6.24 |
| User | CLONE_NEWUSER | 사용자/그룹 ID | 3.8 |
| Cgroup | CLONE_NEWCGROUP | cgroup 루트 디렉토리 | 4.6 |
| Time | CLONE_NEWTIME | 시스템 시간 | 5.6 |
네임스페이스 실험
Mount 네임스페이스
# 새로운 마운트 네임스페이스 생성
sudo unshare --mount /bin/bash
# 낮에 tmpfs 마운트 (부모에는 영향 없음)
mount -t tmpfs none /mnt
ls /mnt
# exit 후 부모에서 확인
exit
ls /mnt # 비어있음 (마운트 안됨)
PID 네임스페이스
# 새로운 PID 네임스페이스
sudo unshare --pid --fork --mount-proc /bin/bash
# PID 1 확인
echo $$
ps aux
# PID TTY STAT TIME COMMAND
# 1 pts/0 S 0:00 /bin/bash
# 10 pts/0 R+ 0:00 ps aux
# 부모 프로세스 확인 (호스트에서)
ls -la /proc/$$/ns/pid
Network 네임스페이스
# 새로운 네트워크 네임스페이스
sudo ip netns add testns
# 네임스페이스 목록
ip netns list
# 네임스페이스에서 실행
sudo ip netns exec testns ip addr
# 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
# link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 가상 인터페이스 연결
sudo ip link add veth0 type veth peer name veth1
sudo ip link set veth1 netns testns
sudo ip addr add 10.0.0.1/24 dev veth0
sudo ip link set veth0 up
sudo ip netns exec testns ip addr add 10.0.0.2/24 dev veth1
sudo ip netns exec testns ip link set veth1 up
sudo ip netns exec testns ip link set lo up
# 연결 테스트
ping 10.0.0.2
sudo ip netns exec testns ping 10.0.0.1
# 정리
sudo ip netns del testns
User 네임스페이스
# 사용자 네임스페이스 (루트 불필요!)
unshare --user --map-root-user /bin/bash
# 루트인 것처럼 보임
id
# uid=0(root) gid=0(root) groups=0(root)
# 하지만 실제 권한은 제한됨
whoami # root
# cat /etc/shadow # Permission denied (호스트의 실제 권한)
unshare 명령어
# 여러 네임스페이스 동시 생성
sudo unshare --mount --uts --ipc --net --pid --fork --user --map-root-user /bin/bash
# Docker 컨테이너와 유사한 환경
sudo unshare --mount --uts --ipc --net --pid --fork --user --map-root-user \
--mount-proc /bin/bash
⚖️ cgroups (Control Groups)
cgroups v1 vs v2
| 특징 | cgroups v1 | cgroups v2 |
|---|---|---|
| 계층 | 여러 계층 (메모리, CPU 분리) | 단일 계층 |
| 컨트롤러 | 개별 마운트 | 통합 |
| 프로세스 | 여러 그룹 소속 가능 | 단일 그룹 |
| 루트 제어 | 필요 | 덜 엄격 |
| 기본값 | 대부분의 시스템 | 최신 시스템 |
cgroups v2 실습
# cgroups v2 확인
mount | grep cgroup2
# cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# 새로운 cgroup 생성
sudo mkdir /sys/fs/cgroup/mygroup
# 리소스 제한 설정
# CPU 제한 (예: 50%)
echo "50000 100000" | sudo tee /sys/fs/cgroup/mygroup/cpu.max
# 메모리 제한 (예: 100MB)
echo "100000000" | sudo tee /sys/fs/cgroup/mygroup/memory.max
# 프로세스 추가
echo $$ | sudo tee /sys/fs/cgroup/mygroup/cgroup.procs
# 테스트
stress --cpu 2 --timeout 10s # CPU 50%로 제한됨
stress --vm 1 --vm-bytes 200M --timeout 10s # OOM 발생
# 정리
sudo rmdir /sys/fs/cgroup/mygroup
systemd와 cgroups
# 서비스의 cgroup 확인
systemctl status nginx
# CGroup: /system.slice/nginx.service
# cgroup 리소스 확인
cat /sys/fs/cgroup/system.slice/nginx.service/cpu.stat
cat /sys/fs/cgroup/system.slice/nginx.service/memory.current
# 서비스에 리소스 제한
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
CPUQuota=50%
MemoryMax=512M
TasksMax=100
📦 컨테이너 기초 (맨손 컨테이너)
컨테이너 = 네임스페이스 + cgroups + 루트 파일시스템
#!/bin/bash
# simple_container.sh
CONTAINER_NAME="mycontainer"
ROOTFS="/var/lib/simple-containers/$CONTAINER_NAME"
# 1. 루트 파일시스템 준비 (Alpine Linux)
mkdir -p $ROOTFS
cd $ROOTFS
wget https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/alpine-minirootfs-3.18.4-x86_64.tar.gz
tar xzf alpine-minirootfs-3.18.4-x86_64.tar.gz
# 2. cgroup 생성
CGROUP="/sys/fs/cgroup/$CONTAINER_NAME"
sudo mkdir -p $CGROUP
echo "100000000" | sudo tee $CGROUP/memory.max # 100MB
echo "50000 100000" | sudo tee $CGROUP/cpu.max # 50% CPU
# 3. 컨테이너 실행
sudo unshare \
--mount \
--uts \
--ipc \
--net \
--pid \
--fork \
--user \
--map-root-user \
--mount-proc \
/bin/bash -c "
# cgroup에 프로세스 추가
echo \$$ > $CGROUP/cgroup.procs
# 루트 변경
mount --bind $ROOTFS $ROOTFS
pivot_root $ROOTFS $ROOTFS/mnt
cd /
umount -l /mnt
# 호스트네임 설정
hostname $CONTAINER_NAME
# 쉘 실행
/bin/sh
"
# 4. 정리
sudo rmdir $CGROUP
chroot vs pivot_root
# chroot (단순 루트 변경)
sudo chroot /var/lib/chroot /bin/bash
# 단점: /proc, /sys 마운트 필요, 프로세스 탈출 가능
# pivot_root (컨테이너용)
# chroot보다 안전, 마운트 네임스페이스와 함께 사용
sudo unshare --mount /bin/bash
mkdir -p /newroot/oldroot
mount --bind /var/lib/container /newroot
pivot_root /newroot /newroot/oldroot
exec /bin/bash
🔍 /proc 파일시스템
프로세스 정보
# 현재 프로세스 정보
ls /proc/$$
cat /proc/$$/cmdline
cat /proc/$$/environ
cat /proc/$$/status
cat /proc/$$/maps # 메모리 매핑
cat /proc/$$/fd/ # 파일 디스크립터
시스템 정보
# CPU 정보
cat /proc/cpuinfo
# 메모리 정보
cat /proc/meminfo
# 커널 파라미터
cat /proc/sys/kernel/hostname
sysctl kernel.hostname
# 네트워크
cat /proc/net/dev # 인터페이스 통계
cat /proc/net/tcp # TCP 소켓
cat /proc/net/route # 라우팅 테이블
커널 파라미터 조정
# 현재 값 확인
cat /proc/sys/vm/swappiness
# 임시 변경 (재부팅 시 초기화)
echo 10 | sudo tee /proc/sys/vm/swappiness
# 영구 변경
sudo sysctl -w vm.swappiness=10
# /etc/sysctl.conf
sudo nano /etc/sysctl.conf
vm.swappiness=10
vm.dirty_ratio=40
# 적용
sudo sysctl -p
🐛 커널 디버깅
dmesg
# 커널 로그
dmesg
dmesg | tail -20
dmesg | grep -i error
dmesg -T # 사람이 읽기 쉬운 시간
# 실시간
dmesg -w
/sys 디버깅
# 장치 정보
ls /sys/class/net/ # 네트워크 인터페이스
ls /sys/block/ # 블록 장치
cat /sys/class/net/eth0/speed
# 커널 모듈
ls /sys/module/
cat /sys/module/nf_conntrack/parameters/hashsize
ftrace
# ftrace 활성화
cd /sys/kernel/debug/tracing
# 함수 추적
echo function > current_tracer
echo __do_page_fault > set_ftrace_filter
echo 1 > tracing_on
# ... 작업 수행 ...
cat trace
echo 0 > tracing_on
📋 체크리스트
- 시스템콜 추적 (strace)
- 네임스페이스 실험 (unshare)
- cgroups v2 설정
- 맨손 컨테이너 생성
- /proc, /sys 탐색
- 커널 파라미터 조정
- 커널 로그 분석
🔗 관련 노트
- 01-04-프로세스-관리 — 프로세스 관리
- 02-02-서비스-관리-systemd — systemd와 cgroups
- 03-00-Linux-내부동작-자가학습 — Docker 기초
- 03-02-성능-모니터링 — 시스템 모니터링
- 03-00-Linux-내부동작-자가학습 — 추가 실험
마지막 업데이트: 2026-06-01