13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] cgroup v2

  • [Linux] Linux 커널 vRAM 성능 개선: zswap·zram·PSI 가이드

    [Linux] Linux 커널 vRAM 성능 개선: zswap·zram·PSI 가이드

    Linux 커널 vRAM 성능 개선: zswap·zram·PSI 모니터링 가이드

    Linux 커널 vRAM 성능이라는 표현은 늘 헷갈리더라고요. GPU VRAM 얘기로 흘러가기도 하고, 어떤 분은 swap만 떠올리시죠. 이 글에서는 후자, 그러니까 메모리 압박이 왔을 때 커널이 어떤 계층으로 시간을 벌고 그 대가를 CPU·RAM·I/O 어디에 치르는지를 다룹니다. 홈랩에서 VM 밀도를 올리다 보면 메모리 사용률보다 stall이 먼저 서비스 응답을 무너뜨리는 경우가 꽤 자주 보입니다. 그래서 저는 이제 swappiness부터 만지지 않습니다. 먼저 압박이 어디서 발생하는지 보고, 그다음에 zswap인지 zram인지, 마지막으로 정책값을 조정합니다.

    이 주제가 실무에서 어려운 이유도 여기 있습니다. 기능이 많아서라기보다, 증상은 비슷한데 원인은 다를 때가 많기 때문입니다. SwapUsed가 늘어도 괜찮은 경우가 있고, free 메모리가 남아 보이는데도 reclaim이 꼬여 tail latency가 튀는 경우도 있거든요. 그래서 이 글은 정의 설명보다 관측 순서, 선택 기준, 실패 모드의 근본 원인에 초점을 맞춰 정리해보겠습니다.

    Linux 커널 vRAM 성능, PSI, zswap, zram, swap 디스크의 관계를 한눈에 보여주는 개요 이미지입니다.

    1. Linux 커널 vRAM 성능에서 말하는 vRAM의 정체

    운영 현장에서 말하는 vRAM은 공식 커널 용어가 아닙니다. 보통은 디스크 swap에 닿기 전에 메모리 압박을 완충하는 계층을 묶어 부를 때가 많습니다. 이름보다 중요한 건 계층의 위치입니다.

    • swap: 최종 퇴로입니다. 느린 디스크라면 여기서 지연이 바로 드러납니다.
    • zswap: 기존 swap 앞단에 붙는 압축 캐시입니다. 페이지를 바로 디스크로 내보내지 않고 RAM 안의 압축 풀에 잠깐 머물게 합니다.
    • zram: RAM 안에 만드는 압축 블록 디바이스입니다. 그 장치를 swap으로 써서 디스크 접근을 줄이거나 늦춥니다.
    • PSI: 메모리 부족 여부 자체보다, 그 부족이 실제로 태스크를 얼마나 멈추게 했는지 보여줍니다.

    제 판단 기준은 단순합니다. 문제는 메모리 사용량이 아니라 stall의 비용입니다. 페이지 캐시를 적극적으로 쓰는 서버는 메모리가 꽉 차도 정상일 수 있습니다. 반대로 reclaim scan이 많이 돌고 swap I/O가 겹치면 사용자는 CPU나 디스크 수치보다 먼저 응답 지연으로 체감합니다.

    2. Linux 커널 vRAM 성능 모니터링은 사용률보다 압박 형태가 먼저입니다

    대시보드를 열기 전에 커널 기본 인터페이스부터 보는 편이 훨씬 정확합니다. 아래 다섯 군데만 읽어도 방향이 꽤 선명해집니다.

    1. /proc/pressure/memory: 메모리 때문에 작업이 멈춘 시간 비율
    2. /proc/vmstat: pswpin, pswpout, pgscan, pgsteal 같은 reclaim 흔적
    3. /proc/meminfo: MemAvailable, SwapFree 등 현재 상태
    4. /sys/module/zswap/parameters/*: zswap 활성화 여부와 정책값
    5. /sys/block/zram0/*: zram 압축 효율, I/O 이상 징후, 메모리 오버헤드

    아래 명령 정도만 확인해도 1차 진단은 충분합니다.

    uname -r
    cat /proc/pressure/memory
    grep -E 'MemTotal|MemAvailable|SwapTotal|SwapFree' /proc/meminfo
    grep -E 'pswpin|pswpout|pgscan|pgsteal|pgfault|pgmajfault' /proc/vmstat
    vmstat 1 10

    해석은 사용률 표보다 훨씬 냉정하게 하셔야 합니다.

    • memory full이 부하 시 계속 상승: 시스템 전체가 메모리 때문에 전진하지 못하는 상태에 가깝습니다. 단순 부족이라기보다 thrashing 징후로 보는 편이 맞습니다.
    • pgscan은 큰데 pgsteal이 기대만큼 따라오지 않음: 회수 후보는 많이 뒤지는데 건질 게 적다는 뜻입니다.
    • pswpout 증가 + I/O 대기 증가: 디스크 swap 비용이 사용자 지연으로 번질 가능성이 큽니다.
    • SwapUsed 증가만 있고 PSI는 낮음: 꼭 나쁜 신호는 아닙니다. 커널이 완충 계층을 잘 쓰는 상황일 수도 있습니다.

    실제로 자주 보는 시나리오도 이렇습니다. VM 호스트에서 야간 백업과 압축 작업이 겹치면 free 메모리는 아직 남아 보이는데 memory full이 먼저 튑니다. 이유는 단순 메모리 부족이 아니라 익명 메모리와 캐시가 동시에 압박받으면서 reclaim과 writeback이 서로 발목을 잡기 때문입니다. 이때 swappiness만 낮추면 해결되기보다, 익명 메모리를 디스크로 더 늦게 밀어 결국 stall이 더 커질 때도 많습니다.

    3. zswap과 zram은 비슷해 보여도 선택 기준이 다릅니다

    둘 다 압축을 쓰지만, 운영 관점에서는 거의 다른 도구에 가깝습니다. 차이는 기존 swap 경로를 유지하느냐, RAM을 swap 장치로 재구성하느냐에 있습니다.

    옵션 동작 방식 이럴 때 우선 검토 피해야 할 경우 운영 포인트
    swap only 디스크 swap만 사용 구조 단순성이 더 중요할 때, 메모리 압박이 드물 때 느린 스토리지, burst성 메모리 압박이 잦을 때 PSI와 swap I/O 상관관계 확인이 핵심입니다
    zswap swap 직전 페이지를 RAM 압축 풀에 저장 기존 swap은 유지하되 디스크 쓰기를 줄이고 싶을 때, VM 호스트나 일반 서버 CPU가 이미 바쁘고 압축 이득이 낮은 워크로드 compressor, max_pool_percent, accept_threshold_percent, shrinker_enabled를 같이 봅니다
    zram RAM 기반 압축 블록 디바이스를 swap으로 사용 디스크가 약한 미니 PC, 홈랩, 엣지 노드, 임시 burst 흡수가 필요할 때 RAM 자체가 너무 타이트하고 압축률이 낮은 워크로드 comp_algorithm, disksize, mm_stat, mem_used_total, huge_pages를 봅니다

    제 경험상 판단은 이렇게 가져가면 덜 틀립니다. 디스크 swap은 이미 있고 운영 변경 폭을 작게 가져가야 한다면 zswap, 디스크 병목이 뚜렷하거나 swap 장치를 RAM 안에 고립시키는 편이 낫다면 zram입니다. 둘을 동시에 쓸 수도 있지만, 처음부터 같이 만지면 원인 분리가 어려워집니다.

    4. zswap은 기존 swap 경로를 보존하면서 지연을 깎을 때 유리합니다

    운영 서버에서 먼저 검토하는 쪽은 대체로 zswap입니다. swap 체인을 갈아엎지 않고도 디스크 write pressure를 줄일 수 있기 때문이죠. 특히 SSD swap이 있는 VM 호스트나 컨테이너 밀도가 높은 서버에서 접근성이 좋습니다. 다만 배포판 기본값과 커널 설정에 따라 일부 파라미터 노출 여부가 다를 수 있으니, 먼저 파일 존재 여부부터 확인하는 게 안전합니다.

    # 현재 상태 확인
    cat /sys/module/zswap/parameters/enabled
    cat /sys/module/zswap/parameters/compressor
    cat /sys/module/zswap/parameters/max_pool_percent
    cat /sys/module/zswap/parameters/accept_threshold_percent
    cat /sys/module/zswap/parameters/shrinker_enabled
    
    # 런타임 조정 예시
    sudo sh -c 'echo 1 > /sys/module/zswap/parameters/enabled'
    sudo sh -c 'echo lzo > /sys/module/zswap/parameters/compressor'
    sudo sh -c 'echo 20 > /sys/module/zswap/parameters/max_pool_percent'
    sudo sh -c 'echo 80 > /sys/module/zswap/parameters/accept_threshold_percent'
    sudo sh -c 'echo Y > /sys/module/zswap/parameters/shrinker_enabled'

    각 값은 이렇게 해석하시면 됩니다.

    • compressor: 압축률보다 CPU 비용 대비 전체 지연 관점으로 봐야 합니다. CPU 여유가 적은 노드에서는 조금 덜 압축돼도 빠른 알고리즘이 낫습니다.
    • max_pool_percent: 풀이 커지면 디스크 쓰기는 줄어들 수 있지만 RAM 경쟁이 심해질 수 있습니다.
    • accept_threshold_percent: 풀이 한 번 가득 찬 뒤 언제 다시 페이지를 받기 시작할지 정합니다.
    • shrinker_enabled: 차가운 페이지가 zswap에 오래 쌓이는 환경에서는 메모리를 되돌리는 데 도움이 됩니다.

    여기서 흔한 실패는 세 가지입니다. 첫째, 압축 풀이 성능 향상 수단이 아니라 메모리 은닉 수단으로만 쓰이는 경우입니다. 둘째, CPU가 이미 꽉 찬 노드에서 무거운 압축기를 쓰는 경우입니다. 셋째, writeback을 막아놓고 store failure가 반복되는 경우입니다. 이때는 압축되지 않는 페이지를 계속 거절하면서 reclaim 효율이 떨어질 수 있습니다.

    swappiness도 같은 맥락에서 봐야 합니다. 예전처럼 무조건 낮출 값은 아닙니다. 커널 문서 기준으로 vm.swappiness는 0~200 범위를 쓰며, zram이나 zswap 같은 in-memory swap에서는 100을 넘는 값도 검토할 수 있습니다. 즉, 아래 예시는 출발점일 뿐이고 정답처럼 고정하시면 안 됩니다.

    # 현재 정책 확인
    sysctl vm.swappiness
    cat /proc/sys/vm/swappiness
    
    # 실험용 런타임 변경
    sudo sysctl -w vm.swappiness=100
    
    # 영구 반영 예시
    sudo tee /etc/sysctl.d/99-vm-tuning.conf >/dev/null <<'EOF'
    vm.swappiness = 100
    EOF
    sudo sysctl --system

    권고를 짧게 정리하면 이렇습니다. zswap을 켰다면 swappiness를 너무 낮게 고정하지 말고, PSI와 pswpout 패턴을 보면서 다시 잡으세요. 디스크 swap만 있는 서버에서 쓰던 감각을 그대로 가져오면 의외로 손해를 봅니다.

    Linux 커널 vRAM 성능 개선을 위한 zswap 설정과 PSI 모니터링 이미지

    zswap 활성화, compressor 선택, max_pool_percent 조정, PSI 관측 흐름을 함께 보여주는 구성 이미지입니다.

    5. zram은 느린 디스크를 우회할 때 강하지만 RAM 예산 관리가 더 까다롭습니다

    zram은 홈랩이나 저전력 노드에서 체감이 큰 편입니다. 이거 잘 맞으면 진짜 편하더라고요. 다만 장점이 분명한 만큼 함정도 분명합니다. 디스크 I/O를 아끼는 대신 RAM과 CPU를 더 정교하게 써야 한다는 점입니다. 그래서 저는 zram을 켤 때 늘 압축 이득과 메타데이터·단편화 오버헤드를 같이 봅니다.

    # 새 장치 초기화 전에는 comp_algorithm을 먼저 고릅니다.
    sudo modprobe zram num_devices=1
    cat /sys/block/zram0/comp_algorithm
    sudo sh -c 'echo lz4 > /sys/block/zram0/comp_algorithm'
    
    # 크기를 정한 뒤 swap으로 만듭니다.
    sudo sh -c 'echo 8G > /sys/block/zram0/disksize'
    sudo mkswap /dev/zram0
    sudo swapon -p 100 /dev/zram0
    
    # 상태 확인
    zramctl
    cat /sys/block/zram0/mm_stat
    cat /sys/block/zram0/io_stat

    여기서 놓치기 쉬운 실무 포인트가 있습니다.

    • comp_algorithm은 장치 초기화 뒤에는 변경할 수 없습니다. 이미 disksize를 설정했다면 순서를 다시 잡아야 합니다.
    • disksize는 실제 메모리 사용량이 아닙니다. 논리 크기일 뿐이고, 실제 소비는 mm_stat와 mem_used_total을 같이 봐야 감이 옵니다.
    • 우선순위(swapon -p)를 안 잡으면 기대와 다른 swap 경로로 흘러갈 수 있습니다.

    mm_stat는 꼭 읽어보셔야 합니다.

    • orig_data_size: 원본 데이터 크기
    • compr_data_size: 압축 후 데이터 크기
    • mem_used_total: 실제로 zram이 먹는 메모리. 메타데이터와 단편화 비용이 반영됩니다
    • huge_pages: 압축 이득이 낮은 페이지 수를 가늠할 때 참고할 수 있는 값입니다

    판단법은 단순합니다. orig_data_size 대비 compr_data_size가 의미 있게 줄고, 그에 비해 mem_used_total이 과하게 불어나지 않으면 성공에 가깝습니다. 반대로 huge_pages가 계속 늘면 이 워크로드는 압축 친화적이지 않을 가능성이 큽니다. 그 상태에서 zram 크기만 키우면 메모리 버퍼를 늘린 게 아니라 비효율을 키운 것에 가까워집니다.

    zram을 피하는 편이 나은 경우도 분명합니다. 메모리 자체가 매우 타이트하고, 워크로드가 이미 CPU를 바쁘게 쓰며, 페이지 내용이 압축에 잘 안 걸리는 경우입니다. 이런 노드는 오히려 작은 zswap + 보수적 swappiness가 덜 흔들릴 때가 있습니다.

    6. 트러블슈팅은 증상이 아니라 원인 축으로 봐야 빨리 풀립니다

    제가 실무에서 가장 많이 헷갈렸던 지점도 여기였습니다. 겉으로는 다 메모리 문제처럼 보이는데, 실제로 손대야 할 축이 다르거든요.

    • PSI avg10만 보고 안심: 짧은 spike는 평균보다 total 증가와 순간 지연에서 먼저 드러납니다.
    • swap 사용량 증가를 실패로 해석: swap을 얼마나 썼는지가 아니라, 그 결과로 stall이 줄었는지를 봐야 합니다.
    • zswap 파라미터가 안 보임: 커널 설정, 배포판 기본값, 부팅 옵션 차이일 수 있습니다.
    • zram이 있는데 체감 개선이 없음: 압축률 부족, huge_pages 증가, 우선순위 misconfig, 또는 실제 병목이 메모리보다 CPU일 수 있습니다.
    • writeback을 막았더니 오히려 느려짐: 압축 불가 페이지가 반복 거절되며 reclaim만 더 많이 돌기 때문입니다.

    컨테이너 환경에서는 시스템 평균만 봐서는 자주 틀립니다. 어떤 워크로드 하나가 압박을 만들고 있는데 전체 노드는 멀쩡해 보일 수 있거든요. 그래서 cgroup v2 파일을 같이 읽는 편이 좋습니다. 다만 memory.zswap.* 계열은 비교적 최신 커널과 해당 기능 지원이 필요하니, 파일이 없으면 기능 미지원부터 의심하세요.

    CG=/sys/fs/cgroup/my-workload
    cat $CG/memory.pressure
    cat $CG/memory.current
    cat $CG/memory.high
    cat $CG/memory.swap.max
    cat $CG/memory.zswap.current
    cat $CG/memory.zswap.max
    cat $CG/memory.zswap.writeback

    이 파일들의 조합이 중요한 이유는 시스템 전체 메모리 정책과 워크로드별 완충 정책이 서로 다를 수 있기 때문입니다.

    • memory.high: OOM 전에 압박을 일찍 걸어 reclaim을 유도합니다.
    • memory.swap.max: cgroup 단위 swap 허용 범위를 제한합니다.
    • memory.zswap.max: zswap 사용량의 상한입니다.
    • memory.zswap.writeback: 0으로 두면 zswap writeback과 store failure에 따른 swapout 경로를 막습니다. 대신 반복 거절이 생기면 reclaim 효율이 무너질 수 있습니다.

    권하는 방식은 시스템 전체 평균 대신 문제가 되는 cgroup의 memory.pressure와 zswap 사용량을 같이 보는 것입니다. 멀티테넌트 환경에서는 전체 PSI보다 특정 서비스 cgroup의 압박이 장애와 더 직접적으로 연결될 때가 많습니다.

    7. 검증은 swap 숫자가 아니라 지연 구조가 바뀌었는지로 끝냅니다

    설정 적용 후에 보는 순서도 정해두는 편이 좋습니다. 안 그러면 우연한 캐시 상태를 개선으로 착각하기 쉽습니다.

    1. 부하 전후 /proc/pressure/memory의 some, full, total 비교
    2. /proc/vmstat에서 pswpout, pgscan, pgsteal 패턴 비교
    3. zram이면 mm_stat에서 압축 이득과 실제 메모리 사용량 확인
    4. zswap이면 pool 크기 정책과 cgroup별 사용량, writeback 정책 확인
    5. 마지막으로 애플리케이션 지연 로그나 요청 처리 시간과 맞춰 보기

    좋은 방향의 신호는 대체로 이렇습니다.

    • 메모리 PSI의 full 증가 속도가 둔화됩니다.
    • swap activity가 남아 있어도 응답 지연의 튐이 줄어듭니다.
    • zram의 compr_data_size가 의미 있게 작고, mem_used_total이 통제됩니다.
    • 특정 cgroup의 압박이 다른 워크로드로 덜 번집니다.

    반대로 실패 신호도 분명합니다. swap은 줄었는데 PSI가 그대로 높다, zram 사용량은 늘었는데 huge_pages도 함께 오른다, writeback을 막은 뒤 reclaim scan만 폭증한다. 이런 경우는 설정을 더 밀어붙일 게 아니라, 선택 자체를 다시 봐야 합니다.

    Linux 커널 vRAM 성능 개선 결과를 보여주는 PSI와 zram zswap 대시보드 이미지

    메모리 PSI, swap activity, zram 압축 효율, cgroup 단위 메모리 압박 개선 결과를 시각화한 대시보드 이미지입니다.

    8. 운영 판단을 바로 내릴 수 있게 시나리오별로 끊어보겠습니다

    Q1. swappiness는 낮을수록 좋은가요?

    그렇게 보면 자주 틀립니다. 디스크 swap만 주로 쓰는 서버라면 낮은 값이 도움이 될 수 있습니다. 하지만 zswap이나 zram처럼 메모리 안쪽에 완충 계층이 있을 때는 너무 낮은 값이 익명 메모리를 오래 붙잡아 reclaim 비용을 키울 수 있습니다. 실무에서는 결국 사용률이 아니라 PSI와 지연으로 판단하는 게 덜 틀립니다.

    Q2. zswap과 zram 중 무엇부터 볼까요?

    운영 변경 폭을 작게 가져가야 하는 일반 서버, VM 호스트, SSD swap 환경이면 zswap부터 보시면 됩니다. 디스크가 약한 미니 PC, 홈랩, 엣지 노드라면 zram이 먼저입니다. 둘 다 가능해도 처음엔 한 단계씩 바꾸세요. 그래야 어느 계층이 실제 이득을 냈는지 분리해서 볼 수 있습니다.

    Q3. 컨테이너 환경에서는 무엇이 가장 중요할까요?

    노드 평균보다 cgroup별 pressure와 상한입니다. memory.high, memory.swap.max, memory.zswap.max, memory.zswap.writeback를 같이 보셔야 합니다. 평균이 멀쩡해도 특정 서비스 하나만 느려지는 이유가 여기서 자주 나옵니다.

    Q4. 언제 쓰지 말아야 하나요?

    CPU가 이미 바쁘고, 압축률이 낮고, 메모리 예산도 빠듯한 노드에서는 과한 압축 계층이 손해일 수 있습니다. 이런 경우는 zram을 크게 잡기보다, zswap을 보수적으로 두거나 메모리 상한 정책부터 정리하는 편이 낫습니다.

    Linux 커널 vRAM 성능 선택 기준을 정리한 zswap zram 비교 인포그래픽

    zswap과 zram의 선택 기준, Linux 커널 vRAM 성능 모니터링 체크포인트, 상황별 추천안을 정리한 요약 이미지입니다.

    실무 권고를 딱 잘라 정리하면 이렇습니다.

    • SSD swap이 있고 서버 역할이 많은 시스템: zswap부터 켜고, compressor와 max_pool_percent를 보수적으로 시작한 뒤 PSI와 pswpout으로 확인하세요.
    • 저전력 홈랩, 미니 PC, 느린 디스크: zram swap을 우선 검토하세요. 다만 mm_stat에서 mem_used_total과 huge_pages를 꼭 같이 보셔야 합니다.
    • 컨테이너 밀도가 높은 환경: 시스템 전체 평균보다 memory.pressure, memory.high, memory.swap.max, memory.zswap.*를 우선 보세요.
    • CPU 여유가 적은 노드: 압축률보다 압축 비용을 먼저 계산하세요. 디스크 I/O 절감이 CPU 손실을 상쇄하지 못하면 방향을 다시 잡아야 합니다.

    제가 이 주제에서 끝까지 붙드는 한 문장은 이것입니다. Linux 커널 vRAM 성능 개선은 swap 숫자 줄이기 게임이 아니라, stall이 발생하는 위치를 바꾸고 그 비용을 통제하는 작업이라는 점입니다. 그래서 설정값 자체보다 PSI, reclaim 흔적, cgroup 경계, 압축 계층의 실제 이득이 더 중요합니다.

    관련 글도 함께 보면 이해가 빨라집니다. 예를 들어 Linux 메모리 관리, 커널 튜닝, 성능 모니터링 주제를 내부 문서로 이어두면 운영 기준을 잡는 데 도움이 됩니다. 한 줄 추천으로 마무리하면, 범용 서버는 zswap부터, 느린 디스크 노드는 zram부터, 멀티테넌트 환경은 cgroup pressure부터 보시면 됩니다. 그다음에야 swappiness가 의미를 갖습니다.

  • [Linux] cgroup vs namespace: 컨테이너 격리의 핵심 비교 분석

    [Linux] cgroup vs namespace: 컨테이너 격리의 핵심 비교 분석

    [리눅스 커널] cgroup vs namespace: 컨테이너 격리 핵심 비교

    컨테이너 기술을 조금만 깊게 파보면 결국 cgroup vs namespace 이야기로 귀결되더라고요. 저도 처음 Docker를 쓸 때는 그냥 “컨테이너는 가볍고 빠르다” 정도로만 이해했었는데, 실제로 장애를 따라가다 보니 왜 어떤 프로세스는 CPU를 독식하고, 왜 어떤 프로세스는 다른 프로세스 목록을 못 보는지 여기서 갈리더라고요. 쉽게 말해 namespace(네임스페이스, 리소스를 보이는 범위로 분리)는 “무엇을 볼 수 있느냐”를 나누고, cgroup(컨트롤 그룹, 자원 사용량을 제어하는 기능)은 “얼마나 쓸 수 있느냐”를 관리합니다. 이 차이를 이해하면 컨테이너 기술, 격리, 리눅스 커널의 구조가 한 번에 정리됩니다.

    실제로 홈랩에서 테스트 환경을 만들다가, 프로세스는 잘 분리됐는데 메모리 제한이 안 먹어서 삽질 좀 했습니다 ㅎㅎ 그때 확실히 느꼈어요. 컨테이너 격리는 한 가지 기능으로 되는 게 아니라, 여러 커널 기능을 조합해서 만들어지는 거였거든요.

    cgroup vs namespace 기반 컨테이너 격리 구조를 보여주는 리눅스 커널 아키텍처 이미지

    namespace와 cgroup이 각각 어떤 역할로 컨테이너 격리를 만드는지 보여주는 개요 이미지입니다.

    1. 왜 cgroup과 namespace를 같이 봐야 할까요?

    여기서 중요한 게 있어요. 많은 분들이 컨테이너를 “가벼운 VM”처럼 이해하시는데, 엄밀히 보면 다릅니다. VM은 하이퍼바이저 위에 게스트 OS가 따로 올라가고, 컨테이너는 호스트의 리눅스 커널을 공유합니다. 그러니 격리도 커널 기능에 의존할 수밖에 없죠.

    • namespace: 프로세스가 보는 세계를 분리합니다.
    • cgroup: 프로세스가 쓰는 자원을 제어합니다.
    • capabilities(리눅스 권한 비트 분리), seccomp(시스템 콜 필터링), LSM 계열 기능은 추가 보안 계층으로 붙습니다.

    즉, 컨테이너 기술에서 격리는 한 방에 끝나는 개념이 아니라 층층이 쌓이는 구조예요. 저도 처음엔 namespace만 알면 다 되는 줄 알았는데, 운영 환경에서는 cgroup 쪽을 훨씬 자주 보게 되더라고요. 특히 CPU 폭주나 메모리 OOM(Out Of Memory) 이슈는 거의 cgroup 쪽에서 원인을 찾게 됩니다.

    2. namespace(네임스페이스) 쉽게 이해하기

    쉽게 말해 namespace는 “같은 커널 위에 있지만 서로 다른 방에 들어가 있는 상태”를 만드는 기능입니다. 같은 호스트인데도 프로세스마다 보이는 PID, 네트워크 인터페이스, 마운트 포인트가 달라질 수 있어요.

    대표적인 namespace 종류

    • PID namespace: 프로세스 번호 공간을 분리합니다.
    • NET namespace: 네트워크 인터페이스, 라우팅 테이블, 포트를 분리합니다.
    • MNT namespace: 마운트 지점을 분리합니다.
    • UTS namespace: 호스트명, 도메인명 같은 시스템 식별 정보를 분리합니다.
    • IPC namespace: 프로세스 간 통신 자원을 분리합니다.
    • USER namespace: 사용자와 그룹 ID 매핑을 분리합니다.

    예를 들어 PID namespace 안에서는 프로세스가 자기 자신을 PID 1로 볼 수도 있어요. 컨테이너 안에서 <code>ps를 쳤을 때 호스트 전체 프로세스가 안 보이는 이유가 바로 이겁니다. 실제로 써보니까 이 개념 하나만 이해해도 “왜 컨테이너 안에서는 세상이 작아 보이는지”가 확 들어오더라고요.

    3. cgroup(컨트롤 그룹)은 뭐가 다를까요?

    namespace가 시야를 나눈다면, cgroup은 행동 한도를 정합니다. CPU를 얼마나 쓸지, 메모리를 얼마나 먹을지, I/O를 어느 정도까지 허용할지를 관리하는 쪽이죠. 그래서 cgroup은 격리라기보다 제어와 회계(accounting) 관점이 훨씬 강합니다.

    운영에서 흔히 보는 상황이 이거예요. 애플리케이션 하나가 메모리를 계속 먹습니다. namespace만 있으면 자기 공간 안에서만 보일 뿐이지, 호스트 메모리는 그대로 압박할 수 있거든요. 반대로 cgroup으로 제한을 걸어두면 그 프로세스 그룹은 정해진 범위를 넘기기 어려워집니다. 장애 대응할 때 이 차이가 꽤 크더라고요.

    항목 namespace cgroup
    주요 목적 리소스 가시성 분리 자원 사용량 제어 및 계측
    핵심 질문 무엇을 볼 수 있나? 얼마나 쓸 수 있나?
    예시 다른 PID/네트워크를 못 봄 CPU, 메모리 사용량 제한
    영향 범위 프로세스 관점의 논리적 격리 호스트 자원 배분 정책
    컨테이너에서의 역할 독립된 환경처럼 보이게 함 과도한 자원 사용을 막음

    한 줄 정리: namespace만 있으면 “따로 있는 것처럼 보이는 상태”이고, cgroup까지 있어야 “민폐를 덜 끼치는 상태”가 돼요. 이거 진짜 중요합니다.

    4. 실전 구현: namespace부터 직접 확인해보겠습니다

    제가 직접 해보니 추상 개념으로만 볼 때보다 unshare로 눈으로 확인하는 게 훨씬 빠르더라고요. 아래 예시는 리눅스에서 새로운 UTS, PID, mount namespace를 만들어보는 흐름입니다.

    1. 현재 호스트 이름과 프로세스 상태를 확인합니다.
    2. unshare로 새 namespace를 만듭니다.
    3. 새 환경 안에서 hostname과 프로세스 목록이 어떻게 달라지는지 봅니다.
    hostname
    ps -ef | head
    
    sudo unshare --uts --pid --mount --fork bash
    
    hostname container-lab
    mount -t proc proc /proc
    ps -ef
    hostname

    여기서 --fork를 주는 이유는 새 PID namespace 안에서 새 프로세스를 시작하기 위해서예요. 그리고 mount -t proc proc /proc를 안 하면 ps 출력이 기대와 다를 수 있거든요. 저도 처음엔 이걸 빼먹어서 “왜 분리 안 됐지?” 하고 한참 봤었습니다.

    cgroup vs namespace 실습 중 namespace 분리 셸 구성을 표현한 이미지

    namespace 실습 흐름과 분리된 셸 진입 과정을 보여주는 이미지입니다.

    현재 시스템의 namespace 확인

    lsns

    lsns를 보면 현재 시스템에 어떤 namespace가 잡혀 있는지 확인할 수 있어요. 컨테이너 런타임이 떠 있는 서버에서는 생각보다 다양한 namespace가 보이더라고요.

    5. 실전 구현: cgroup으로 자원 제한 걸어보기

    이제 cgroup입니다. 최근 리눅스 배포판은 보통 cgroup v2 기준으로 보는 게 편해요. 시스템마다 마운트 구조가 조금 다를 수는 있지만, 기본 개념은 비슷합니다. CPU와 메모리 제한을 설정할 수 있고, 프로세스를 특정 cgroup 디렉터리 아래로 넣어 관리하죠.

    mount | grep cgroup
    cat /sys/fs/cgroup/cgroup.controllers
    
    sudo mkdir /sys/fs/cgroup/demo-limit
    echo $$ | sudo tee /sys/fs/cgroup/demo-limit/cgroup.procs

    위처럼 현재 셸을 특정 cgroup에 넣을 수 있어요. 다만 실제 제한을 적용하려면 컨트롤러 활성화와 권한 구조를 이해해야 해서, 운영 서버에서는 systemd 기반 도구를 쓰는 편이 안전합니다.

    systemd-run으로 메모리 제한 테스트

    systemd-run --user --scope -p MemoryMax=200M -p CPUQuota=50% bash
    
    cat /sys/fs/cgroup/$(systemctl --user show --property=ControlGroup --value)/memory.max

    환경에 따라 사용자 세션에서 안 될 수도 있어요. 그런 경우는 시스템 범위에서 실행하거나 테스트 VM에서 보는 쪽이 낫습니다. 핵심은 cgroup이 프로세스를 안 보이게 만드는 기능이 아니라, 자원 사용 정책을 강제하는 기능이라는 점이에요.

    Docker로 보면 훨씬 익숙해요.

    docker run --rm -it --memory=256m --cpus=1 alpine sh
    
    cat /sys/fs/cgroup/memory.max 2>/dev/null || true
    cat /sys/fs/cgroup/cpu.max 2>/dev/null || true

    컨테이너 런타임은 이런 옵션을 받아 내부적으로 cgroup 설정과 namespace 구성을 함께 처리해요. 우리가 편하게 docker run 한 줄로 쓰는 뒤에서 리눅스 커널이 꽤 많은 일을 하는 셈이죠.

    6. cgroup vs namespace 비교를 실무 관점에서 정리해보면

    혹시 이런 경험 있으신가요? 컨테이너는 분명 분리돼 있는데, 한 컨테이너가 CPU를 잡아먹어서 같은 노드의 다른 워크로드까지 느려지는 상황 말이에요. 이럴 때 namespace를 아무리 잘 나눠도 해결이 안 됩니다. 반대로 cgroup만 있고 namespace가 약하면 프로세스 시야가 너무 넓어서 격리 체감이 떨어져요.

    • namespace가 강한 상황: 프로세스, 네트워크, 파일시스템 관점에서 독립된 환경처럼 보입니다.
    • cgroup이 강한 상황: noisy neighbor(시끄러운 이웃, 자원 독식 워크로드) 문제를 줄이기 좋습니다.
    • 둘 다 필요: 컨테이너 기술에서 실용적인 격리는 대부분 둘의 조합이에요.

    제가 홈랩 쿠버네티스 환경을 만질 때도 결국 보는 건 두 가지였어요. “이 파드는 무엇을 볼 수 있지?” 그리고 “이 파드는 얼마나 먹지?” 질문이 딱 namespace와 cgroup으로 나뉘더라고요.

    7. ⚠️ 주의사항과 트러블슈팅

    여기서부터는 제가 실제로 많이 헷갈렸던 부분입니다. 처음엔 개념보다 환경 차이 때문에 더 막히더라고요.

    1) ps 결과가 기대와 다를 때

    PID namespace를 만들었는데도 프로세스가 그대로 보이는 경우가 있어요. 보통 /proc를 새로 마운트하지 않아서 그렇습니다.

    mount -t proc proc /proc

    2) cgroup 디렉터리는 있는데 제한이 안 걸릴 때

    cgroup v1과 v2 차이, systemd 관리 방식, 권한 문제 때문일 수 있어요. 특히 배포판마다 기본 구성이 다르니 무작정 예전 블로그 예제를 복붙하면 안 맞는 경우가 있습니다. 저도 예전 자료 따라 했다가 파일 이름이 달라서 한참 헤맸습니다.

    3) 컨테이너 보안과 자원 제어를 같은 문제로 보면 꼬여요

    이건 실무에서 꽤 중요한 부분이에요.

    • namespace는 보이는 범위를 줄이는 쪽입니다.
    • cgroup은 성능 안정성과 자원 관리 쪽입니다.
    • seccomp, AppArmor, SELinux 같은 보안 계층은 또 별개입니다.

    즉, 보안 사고를 막는 이야기와 리소스 폭주를 막는 이야기를 한 덩어리로 보면 판단이 흐려집니다.

    4) USER namespace는 특히 신중하게 봐야 해요

    USER namespace는 루트 권한 매핑 문제와 연결되기 때문에 환경에 따라 정책 차이가 커요. 실습은 가능하지만, 운영 서버에서는 배포판 기본 정책과 런타임 설정을 반드시 같이 봐야 합니다.

    cgroup vs namespace 비교 중 cgroup v2 자원 제어 구조를 설명하는 이미지

    cgroup v2 구조와 자원 제한이 적용되는 핵심 지점을 시각화한 이미지입니다.

    8. 검증과 결과 확인

    설정은 했는데 실제로 격리가 됐는지 확인을 안 하면 의미가 없죠. 저는 보통 아래처럼 확인합니다.

    1. namespace 분리 여부 확인
    2. cgroup 제한 값 확인
    3. 실제 워크로드를 넣어 동작 확인
    lsns
    
    cat /proc/self/cgroup
    
    cat /sys/fs/cgroup/cpu.max 2>/dev/null || true
    cat /sys/fs/cgroup/memory.max 2>/dev/null || true

    Docker 환경이라면 다음도 자주 봐요.

    docker inspect <container_id>
    docker stats

    검증 포인트는 단순합니다.

    • 프로세스 목록이 호스트와 다르게 보이는가?
    • 호스트명이나 네트워크 인터페이스가 분리되어 보이는가?
    • 메모리/CPU 제한이 파일 시스템이나 런타임 정보에 반영되는가?
    • 부하를 줬을 때 제한 정책이 실제로 동작하는가?

    실제로 써보니까, 격리는 “설정했다”보다 “증명했다”가 더 중요하더라고요. 특히 장애 분석 문서 남길 때 이 확인 단계가 있어야 나중에 팀원과 같은 그림을 볼 수 있어요.

    cgroup vs namespace 검증 결과를 운영 화면처럼 보여주는 이미지

    격리 검증 결과를 확인하는 장면을 담은 이미지입니다.

    9. 정리: 컨테이너 기술에서 둘 중 뭐가 더 중요할까요?

    제 답은 늘 같습니다. 둘 중 하나가 아니라 둘 다예요. namespace는 컨테이너를 컨테이너답게 보이게 만들고, cgroup은 컨테이너를 운영 가능한 상태로 만들어줍니다. 하나는 시야를 분리하고, 다른 하나는 자원을 통제하죠.

    질문 봐야 할 기능 실무 해석
    왜 다른 프로세스가 안 보일까? namespace 격리된 실행 환경
    왜 메모리가 256MB 이상 못 올라갈까? cgroup 자원 제한 정책
    왜 한 컨테이너가 노드 전체를 먹지 못할까? cgroup 멀티테넌시 안정성
    왜 컨테이너 안 hostname이 다를까? namespace 독립된 시스템처럼 보이기

    저도 처음엔 cgroup vs namespace를 경쟁 관계처럼 외웠었는데, 실제로는 역할 분담에 더 가깝거든요. 그래서 컨테이너 기술을 공부할 때는 둘을 따로 암기하기보다, 하나의 워크로드가 커널 위에서 어떻게 분리되고 제한되는지 흐름으로 보는 게 훨씬 이해가 잘 됩니다.

    다음 글에서는 seccomp(시스템 콜 필터링)와 capabilities(권한 비트 세분화)까지 이어서, 왜 “컨테이너는 격리되지만 완전한 보안 경계로 보면 안 된다”는 말이 나오는지 다뤄볼 예정입니다. 이전 글에서 다룬 Docker 기본 구조와 함께 보시면 더 잘 연결되실 겁니다.

    cgroup vs namespace 차이를 한눈에 정리한 비교 인포그래픽 이미지

    cgroup과 namespace의 핵심 차이를 요약한 비교 인포그래픽 이미지입니다.

    자주 묻는 질문(FAQ)

    Q1. namespace만 있으면 컨테이너가 완성되나요?

    아니에요. namespace만으로는 자원 제한이 부족합니다. 실무에서는 cgroup, capabilities, seccomp 같은 요소가 함께 들어갑니다.

    Q2. cgroup은 보안 기능인가요?

    주된 목적은 자원 제어와 계측이에요. 보안에 간접적으로 도움이 될 수는 있지만, 보안 격리 그 자체로 보기엔 부족합니다.

    Q3. 리눅스 커널을 공유하면 위험하지 않나요?

    맞아요. 그래서 컨테이너는 VM과 다른 보안 모델을 가져요. 격리 수준과 운영 목적을 구분해서 설계해야 합니다.

    Q4. 쿠버네티스에서도 결국 같은 원리인가요?

    네, 상위 오케스트레이션이 붙어도 아래에서는 리눅스 커널의 namespace와 cgroup 같은 기능을 활용합니다.