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)

네임스페이스 개요

프로세스별 시스템 리소스 격리 메커니즘

네임스페이스플래그격리 대상도입 버전
MountCLONE_NEWNS마운트 포인트2.4.19
UTSCLONE_NEWUTS호스트명/도메인명2.6.19
IPCCLONE_NEWIPCSystem V IPC, POSIX MQ2.6.19
PIDCLONE_NEWPID프로세스 ID2.6.24
NetworkCLONE_NEWNET네트워크 장치, 스택, 포트2.6.24
UserCLONE_NEWUSER사용자/그룹 ID3.8
CgroupCLONE_NEWCGROUPcgroup 루트 디렉토리4.6
TimeCLONE_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 v1cgroups 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