13년차의 서버실

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

[카테고리:] linux

  • [Linux] AppArmor 프로파일 작성 및 디버깅: 리눅스 보안 실전 트러블슈팅

    [Linux] AppArmor 프로파일 작성 및 디버깅: 리눅스 보안 실전 트러블슈팅

    AppArmor 프로파일 작성 및 디버깅: 리눅스 보안 실전 트러블슈팅

    AppArmor 프로파일 작성은 문법 싸움보다 관찰과 축소의 싸움에 더 가깝습니다. 서비스가 멀쩡히 돌다가 배포 직후 파일 접근이 막히고, 로그에는 애매한 <code>DENIED만 남는 경우가 생각보다 자주 나오거든요. 저도 처음엔 규칙 몇 줄만 더하면 끝날 줄 알았는데, 실제 운영에선 /tmp, /run, 동적 라이브러리, 인증서 경로, systemd 런타임 디렉터리처럼 눈에 잘 안 보이는 지점에서 계속 걸리더라고요. 그래서 AppArmor 프로파일 작성은 “무엇을 허용할까”보다 먼저 “프로세스가 실제로 어디를 밟는가”를 확인하는 절차가 더 중요합니다.

    이번 글은 단순 사용법이 아니라, 실무에서 제가 기준으로 삼는 판단 순서를 중심으로 정리했습니다. 어떤 로그를 먼저 믿어야 하는지, 규칙을 어느 단위로 끊어야 나중에 안 무너지는지, AppArmor로 막는 게 맞는 문제와 구조를 먼저 손봐야 하는 문제를 어떻게 구분하는지까지 한 번에 다룹니다. 광고성 정보만 잔뜩 모은 글이 아니라, 읽고 바로 써먹을 수 있게 명령어와 프로파일 예시도 복붙 가능한 수준으로 넣었습니다.

    AppArmor 프로파일 작성 개요와 리눅스 AppArmor 적용 흐름 이미지

    AppArmor가 프로세스와 파일 경로를 기준으로 접근을 제어하는 흐름을 보여주는 개요 이미지입니다.

    AppArmor 프로파일 작성 전에 먼저 이해해야 할 것

    AppArmor는 경로 기반 MAC(Mandatory Access Control)입니다. 같은 리눅스 보안 계층이라도 SELinux처럼 라벨을 설계하는 방식과는 결이 조금 다릅니다. AppArmor 쪽이 초반 진입은 쉬운 편이지만, 반대로 경로가 지저분한 애플리케이션에서는 정책도 금방 지저분해집니다. 그래서 저는 AppArmor를 잘 쓰는 조건을 이렇게 봅니다.

    • 데이터 경로가 분리돼 있다. 예: /var/lib/myapp, /etc/myapp, /run/myapp
    • 외부 프로세스 실행이 적다.
    • 임시 파일, 소켓, PID 파일 위치를 예측할 수 있다.
    • systemd 유닛이 비교적 단순하다.

    반대로 애플리케이션이 실행 중 여기저기 흩어진 경로를 건드리고, 셸을 여러 번 호출하고, 플러그인이나 후처리 스크립트를 동적으로 실행한다면 프로파일 작성보다 애플리케이션의 파일 구조를 먼저 정리하는 편이 비용 대비 효과가 큽니다. 이걸 무시하고 규칙만 늘리면 결국 /var/** rw, 같은 과한 허용으로 흘러가고, 그 시점부터는 보호 가치가 급격히 떨어집니다.

    Enforce와 Complain을 나누는 기준

    • enforce: 정책 위반을 차단합니다. 운영 보호용입니다.
    • complain: 일반 허용 규칙 밖 접근을 차단하지 않고 로그만 남깁니다. 다만 프로파일에 명시한 deny 규칙은 complain 모드에서도 계속 적용됩니다.

    여기서 많이 하는 실수가 “테스트 서버니까 바로 enforce”입니다. 저는 오히려 반대로 갑니다. 테스트 환경일수록 동작 패턴이 덜 안정적이라 예외 경로가 많고, 그래서 처음엔 complain이 낫습니다. enforce는 “정상 경로와 실패 경로를 둘 다 재현했고, 새 deny 로그가 더 이상 의미 있게 안 나온다”는 조건이 붙을 때 올립니다. 운영에서 장애 한 번 나면 AppArmor 자체에 대한 조직 신뢰가 확 꺾이거든요.

    리눅스 AppArmor 운영 전에 알아둘 파일과 도구

    리눅스 AppArmor를 만질 때는 도구를 많이 아는 것보다 언제 무엇을 쓰는지가 더 중요합니다. 아래 정도만 먼저 손에 익혀도 실무 대응 속도가 꽤 빨라집니다.

    • /etc/apparmor.d/: 프로파일 저장 위치
    • aa-status: 로드된 프로파일, enforce/complain 상태 확인
    • aa-complain, aa-enforce: 프로파일 모드 전환
    • apparmor_parser: 문법 검사와 프로파일 재로드
    • journalctl -k, dmesg, /var/log/audit/audit.log: 커널 또는 audit 로그 확인
    • aa-logprof: 로그 기반 규칙 제안
    • systemctl cat: systemd 유닛 정의 확인

    현장에서 바로 보는 기본 점검은 보통 이 정도면 충분합니다.

    sudo systemctl status apparmor
    sudo aa-status
    sudo ls /etc/apparmor.d/
    sudo journalctl -k -g apparmor -b
    

    제가 중요하게 보는 건 숫자보다 맥락입니다. aa-status에서 특정 프로파일이 로드됐는지, 어떤 모드인지 먼저 확인하고, journalctl -k -g apparmor -b로는 현재 부팅에서 실제 deny가 있었는지를 봅니다. auditd를 쓰는 서버라면 로그가 /var/log/audit/audit.log 쪽에 남을 수 있으니 그 점도 같이 확인해두면 덜 헤맵니다.

    AppArmor 프로파일 작성 실전: 작은 서비스부터 잠그는 게 맞습니다

    예시 시나리오를 하나 고정해보겠습니다. /usr/local/bin/report-sync라는 내부 동기화 스크립트가 있고, 설정은 /etc/report-sync/config.yml, 데이터는 /var/lib/report-sync/, 실행 중 락 파일은 /run/report-sync/에 남긴다고 가정하겠습니다. 외부 API 호출은 필요하지만 셸 실행은 필요 없다고 두겠습니다. 이 정도로 경로를 정리해두면 AppArmor 프로파일 작성 난도가 확 내려갑니다.

    1. 프로세스가 밟아야 하는 경로를 먼저 적습니다.
    2. 초안은 짧게 씁니다. 처음부터 다 맞히려 하지 않습니다.
    3. complain 모드에서 정상 시나리오와 실패 시나리오를 둘 다 돌립니다.
    4. deny 로그를 읽고 규칙을 추가하되, 상위 디렉터리를 통째로 열지 않습니다.
    5. 새 deny가 사라진 뒤 enforce로 올립니다.

    1. 프로파일 초안 작성

    sudo tee /etc/apparmor.d/usr.local.bin.report-sync > /dev/null <<'EOF'
    #include <tunables/global>
    
    /usr/local/bin/report-sync {
      #include <abstractions/base>
      #include <abstractions/nameservice>
    
      /usr/local/bin/report-sync r,
      /etc/report-sync/config.yml r,
    
      /var/lib/report-sync/ r,
      /var/lib/report-sync/** rwk,
    
      /run/report-sync/ rw,
      /run/report-sync/** rwk,
    
      network inet tcp,
      network inet6 tcp,
    
      deny /bin/sh x,
      deny /usr/bin/bash x,
      deny /usr/bin/dash x,
    }
    EOF
    

    여기서 몇 가지를 짚고 넘어가야 합니다. r은 읽기, w는 쓰기, k는 파일 잠금(lock), x는 실행입니다. 락 파일이나 PID 파일을 다루는 프로그램은 w만으로 부족할 때가 꽤 있습니다. 실제로 flock 같은 잠금 동작이 들어가면 k가 빠져서 계속 막히는 사례를 자주 봤습니다. 또 디렉터리 자체 권한과 하위 파일 권한은 분리해서 봐야 합니다. /var/lib/report-sync/ r,와 /var/lib/report-sync/** rwk,를 나눠 적는 이유도 그 지점 때문입니다.

    네트워크도 무조건 한 줄로 끝나진 않습니다. 단순 TCP 클라이언트라면 위 예시 정도로 시작할 수 있지만, 유닉스 도메인 소켓이나 raw 소켓을 쓰는 프로그램은 다른 규칙이 필요할 수 있습니다. 그래서 초안은 어디까지나 학습용 가설로 받아들이는 게 맞습니다.

    AppArmor 프로파일 작성 예시와 파일 경로 매핑 이미지

    프로파일 파일, 바이너리 경로, 허용된 데이터 디렉터리의 관계를 보여주는 구성 이미지입니다.

    2. 문법 검사와 complain 전환

    sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.report-sync
    sudo aa-complain /usr/local/bin/report-sync
    sudo aa-status | grep report-sync
    

    이 단계에서는 두 부류의 실패를 분리해서 봐야 합니다. 파서 오류는 프로파일 문법 문제고, 실행 중 deny는 권한 설계 문제입니다. 둘을 섞어 보면 디버깅 시간이 괜히 길어집니다. 파서가 깨질 때는 보통 중괄호 위치, 규칙 끝의 쉼표 누락, include 경로, 권한 토큰 오타 같은 기본 문법이 원인입니다.

    3. 실제 실행과 로그 수집

    sudo /usr/local/bin/report-sync
    sudo journalctl -k -g apparmor --since "10 minutes ago"
    sudo dmesg | grep -i apparmor
    

    로그를 볼 때는 한 줄 전체를 멍하니 읽기보다 핵심 필드만 먼저 잡는 편이 훨씬 빠릅니다.

    • operation=: 무엇을 하려 했는가. 예: open, exec, connect
    • profile=: 어떤 프로파일에 걸렸는가
    • name=: 실제 대상 경로는 무엇인가
    • requested_mask=: 프로세스가 원한 권한은 무엇인가
    • denied_mask=: 실제 막힌 권한은 무엇인가

    예를 들어 operation="open", name="/run/report-sync/lock", denied_mask="k"가 찍히면 이건 단순 쓰기 누락이 아니라 잠금 권한이 빠진 겁니다. 또 operation="exec"가 보이면 파일 접근 문제로만 보면 안 됩니다. 애플리케이션이 내부에서 다른 바이너리를 호출하고 있다는 뜻이라, 그 시점부터는 실행 전이인 ix, px, Px 같은 규칙을 검토해야 합니다.

    트러블슈팅: 실제 운영에서 자주 막히는 지점과 근본 원인

    AppArmor 디버깅은 규칙 추가 게임이 아닙니다. 왜 그 접근이 필요한지 이해하지 못한 채 허용만 늘리면, 나중에 문제를 재현하기가 더 어려워집니다. 아래는 제가 반복해서 본 실패 패턴입니다.

    문제 1. 설정 파일은 열었는데, 여전히 특정 파일 접근이 실패하는 경우

    겉으로는 /etc/myapp/config.yml만 읽는 것처럼 보여도 실제론 CA 인증서, locale 파일, 동적 모듈, 캐시 디렉터리, 소켓 파일을 추가로 건드리는 경우가 많습니다. 특히 HTTPS 통신을 하는 CLI는 /etc/ssl/ 계열 경로를 간접적으로 읽을 수 있습니다. 이럴 때 상위 디렉터리를 크게 열어버리면 빠르긴 해도, 나중에 왜 허용됐는지 설명이 안 됩니다.

    sudo aa-logprof
    

    aa-logprof는 유용하지만, 저는 제안된 규칙을 그대로 다 받지 않습니다. 기준은 단순합니다. 예측 가능한 고정 경로인가, 실패 원인이 해당 경로 하나로 닫히는가, 와일드카드를 쓰지 않아도 되는가를 먼저 봅니다. 예를 들어 설정 파일 1개 때문에 /etc/** r,를 받아들이면 그 순간 관리가 무너집니다.

    문제 2. 수동 실행은 되는데 systemd 서비스로만 실패하는 경우

    이건 정말 흔합니다. 원인은 보통 AppArmor 자체보다 실행 컨텍스트 차이입니다. systemd는 사용자, 작업 디렉터리, 환경 변수, RuntimeDirectory, StateDirectory, 소켓 활성화 여부가 전부 다를 수 있습니다. 수동으로는 안 밟던 /run/... 경로가 서비스 모드에선 갑자기 생기는 이유도 여기 있습니다.

    sudo systemctl cat report-sync.service
    sudo systemctl show report-sync.service -p User -p Group -p RuntimeDirectory -p StateDirectory -p WorkingDirectory
    sudo journalctl -u report-sync.service -b
    

    제가 보는 포인트는 세 가지입니다. 누가 실행하는가, 런타임 파일이 어디에 생기는가, ExecStartPre/ExecStartPost에서 별도 바이너리를 호출하는가입니다. 특히 전처리 스크립트가 있으면 메인 바이너리만 프로파일링해서는 잘 안 끝나더라고요.

    문제 3. deny 로그가 없는데 서비스가 죽는 경우

    이럴 땐 AppArmor만 붙들고 있으면 시간 낭비입니다. deny가 없으면 실제 차단은 없었다는 뜻일 가능성이 높습니다. 애플리케이션 자체 예외, 파일 소유권 문제, 잘못된 경로, systemd 타임아웃, 의존 서비스 미기동 같은 다른 층위를 바로 봐야 합니다.

    상황 우선 확인할 것 근본 원인 후보 제가 권하는 대응
    커널 또는 audit 로그에 AppArmor deny 존재 journalctl -k -g apparmor, /var/log/audit/audit.log 프로파일이 실제 접근을 차단 경로와 권한을 최소 범위로 추가
    deny 없음, 앱 로그에 파일 오류만 존재 애플리케이션 로그와 파일 소유권 경로 오타, 파일 미생성, UNIX 권한 문제 AppArmor보다 앱 설정과 POSIX 권한부터 확인
    수동 실행 성공, systemd 실패 systemctl show, /run, 실행 계정 실행 컨텍스트 차이 런타임 디렉터리와 보조 프로세스 경로를 반영
    규칙이 과도하게 많아짐 프로파일 범위와 파일 구조 애플리케이션 경로 설계가 산만함 정책 추가보다 경로 재구성이나 서비스 분리 검토
    operation="exec" deny 반복 호출되는 하위 바이너리 목록 외부 명령 실행 설계 무분별한 x 허용 대신 실행 전이 정책 검토

    이 표에서 핵심은 “어느 계층부터 볼 것인가”입니다. deny가 있으면 AppArmor부터, deny가 없으면 다른 원인부터 보는 식으로 순서를 고정해두면 추측이 크게 줄어듭니다.

    리눅스 AppArmor deny 로그 분석과 트러블슈팅 이미지

    커널 로그에서 operation, profile, name, denied_mask를 읽는 포인트를 강조한 이미지입니다.

    AppArmor 프로파일 작성 규칙을 다듬는 기준: 파일이 아니라 동작으로 자르세요

    제가 AppArmor 프로파일 작성에서 가장 경계하는 건 “일단 넓게 열고 나중에 줄이자”는 접근입니다. 실제로는 나중에 거의 안 줄어듭니다. 운영에 들어간 허용 규칙은 잘 안 걷히거든요. 그래서 저는 파일 기준보다 동작 기준으로 규칙을 끊습니다.

    • 설정 읽기: /etc/myapp/*.conf r,
    • 상태 저장: /var/lib/myapp/** rwk,
    • 로그 쓰기: /var/log/myapp/** w,
    • 런타임 파일: /run/myapp/** rwk,
    • DNS 조회 필요: #include <abstractions/nameservice>
    • 외부 프로세스 실행 필요: 대상 바이너리별로 별도 검토

    여기서 실행 권한은 특히 조심해야 합니다. x 하나로 끝나는 문제가 아니고, ix, px, Px처럼 실행 시 현재 프로파일을 상속할지, 별도 프로파일로 전환할지에 따라 보안 성질이 달라집니다. 저는 내부 배치가 tar, gzip, rsync 같은 외부 명령을 꼭 호출해야 할 때도 무작정 허용하지 않고, “정말 이 호출이 애플리케이션 내부에 있어야 하나”를 먼저 봅니다. 이거 한번 습관 붙여두면 정책이 훨씬 덜 지저분해집니다.

    언제 AppArmor를 쓰고, 언제 구조를 먼저 손봐야 하는가

    이 질문이 실무에선 더 중요합니다. 모든 서비스에 AppArmor를 똑같이 적용하는 건 좋은 운영이라고 보기 어렵습니다.

    상황 AppArmor 우선 구조 정리 우선
    내부 배치 스크립트, 작은 데몬 권장 보통 불필요
    데이터 경로가 명확한 API 서비스 권장 필요 시 병행
    여러 외부 명령을 연쇄 호출하는 모놀리식 앱 신중 우선 검토
    임시 파일과 소켓 위치가 제각각인 레거시 앱 신중 우선 검토
    서드파티 바이너리 보호 효과 큼 수정 불가라면 AppArmor 쪽이 현실적

    제가 실제로 추천하는 방향은 분명합니다. 작고 경로가 단순한 서비스는 AppArmor를 바로 적용하시고, 경로와 실행 흐름이 복잡한 서비스는 규칙을 늘리기 전에 파일 구조와 런타임 경로를 정리하시는 편이 낫습니다. AppArmor는 엉킨 구조를 마법처럼 정리해주는 도구라기보다, 정리된 구조를 더 안전하게 잠가주는 도구에 가깝습니다.

    검증: complain에서 enforce로 넘기기 전에 꼭 보는 체크리스트

    서비스가 한 번 성공했다고 바로 enforce로 올리면, 야간 배치나 장애 복구 루틴에서 다시 터질 수 있습니다. 그래서 저는 최소한 아래 단계를 거칩니다.

    1. 정상 시나리오를 여러 번 실행합니다.
    2. 재시도, 파일 없음, 로그 회전 직후처럼 실패 시나리오도 일부러 만듭니다.
    3. journalctl -k -g apparmor와 필요 시 audit 로그에 새 deny가 없는지 확인합니다.
    4. 프로파일을 다시 읽고, 목적이 모호한 와일드카드를 줄입니다.
    5. 그 다음에 enforce로 전환합니다.
    sudo aa-enforce /usr/local/bin/report-sync
    sudo aa-status | grep report-sync
    sudo journalctl -k -g apparmor --since "5 minutes ago"
    

    이때 기준은 단순합니다. 기능 성공 + 새 deny 없음 + 규칙 목적이 설명 가능함. 이 세 가지가 맞아야 합니다. deny가 계속 찍히는데 기능이 우연히 통과한 상태라면 아직 끝난 게 아닙니다. 선택 경로나 예외 처리 루틴에서 나중에 터질 가능성이 남아 있습니다.

    AppArmor 프로파일 작성에서 complain 모드와 enforce 모드 비교 이미지

    학습 단계인 complain 모드와 차단 단계인 enforce 모드의 차이를 한눈에 비교하는 이미지입니다.

    제가 많이 헤맸던 함정 하나: /tmp를 열어도 안 풀리는 이유

    예전에 백업 후처리 스크립트를 묶을 때 로그에는 /tmp/backup.lock만 보였습니다. 그래서 /tmp/** rwk,를 추가했는데도 실패가 계속됐습니다. 원인은 다른 데 있었습니다. 서비스가 systemd의 notify 메커니즘을 통해 상태를 알리고 있었고, 실제로는 /run/systemd/notify 계열 소켓 접근이 필요했던 겁니다. 겉으로 드러난 첫 번째 경로만 보고 달려들면 이런 식으로 시간을 잃습니다. 이거 한번 겪고 나면 로그 한 줄만 믿으면 안 된다는 걸 몸으로 배우게 됩니다.

    이 경험 이후로는 deny 로그를 한 줄만 보지 않습니다. 서비스가 어떤 타입으로 실행되는지, PID 파일과 소켓을 어디에 남기는지, 보조 바이너리를 호출하는지를 같이 봅니다. AppArmor 디버깅은 결국 프로세스의 발자국을 복원하는 작업입니다. 그 발자국이 정리되면 규칙은 오히려 짧아집니다.

    자주 묻는 질문

    프로파일은 수동으로 쓰는 게 좋을까요, 도구를 써야 할까요?

    초안은 수동으로 짧게 잡고, 실행 후엔 aa-logprof로 보완하는 방식을 권합니다. 처음부터 도구 제안을 전부 수락하면 과도한 허용이 들어가기 쉽습니다.

    requested_mask와 denied_mask는 어떻게 읽어야 하나요?

    requested_mask는 프로세스가 시도한 권한이고, denied_mask는 그중 실제 막힌 권한입니다. 읽기만 필요한 줄 알았는데 쓰기나 잠금까지 요청한다면, 허용을 늘리기 전에 애플리케이션 동작 자체를 다시 보는 게 맞습니다.

    홈랩과 운영 서버는 접근 방식을 달리해야 할까요?

    그렇습니다. 홈랩은 complain으로 동작 패턴을 학습하기 좋고, 운영은 이미 검증된 프로파일만 enforce로 올리는 편이 안정적입니다. 저는 홈랩에서 deny 패턴을 먼저 모아두고 운영에선 그 결과만 가져가는 방식을 선호합니다.

    실무에서 바로 적용할 추천안

    AppArmor 프로파일 작성이 필요한 상황이라면, 먼저 서비스의 설정 경로, 상태 저장 경로, 런타임 경로를 분리해두세요. 그다음 짧은 프로파일로 시작해서 complain에서 로그를 수집하고, deny를 보고 최소 범위로만 열어주면 됩니다. 이 순서를 지키면 정책이 쓸데없이 커지는 걸 꽤 잘 막을 수 있습니다.

    판단 기준도 분명하게 가져가시면 됩니다. 내부 스크립트, 배치, 작은 데몬, 서드파티 바이너리는 AppArmor 적용 효과가 큽니다. 반면 외부 명령 실행이 많고 경로가 산만한 레거시 서비스는 규칙을 늘리기 전에 구조를 먼저 정리하세요. 관련해서 SELinux 비교 글이나 systemd 보안 옵션 정리 글도 내부 링크로 함께 묶어두면 독자 체류 시간이 꽤 좋아집니다.

  • [Linux] Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전

    [Linux] Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전

    Linux namespace 활용 사례: 격리 환경 구축과 보안 강화 실전

    리눅스 서버를 오래 만지다 보면, 운영 호스트는 그대로 둔 채 어떤 프로세스가 실제로 무엇을 건드리는지 빨리 확인해야 하는 순간이 꼭 옵니다. 패키지를 하나 설치했더니 예상 못 한 소켓을 열고, 에이전트를 하나 올렸더니 부팅 직후 파일을 여기저기 만들고, 테스트 스크립트가 외부망으로 나가 버리는 식이죠. 저도 홈랩이든 실무든 비슷했고요. 그때 가장 손에 익혀둘 가치가 있었던 게 Linux namespace 활용이었습니다. 흔히 컨테이너의 재료 정도로만 설명되지만, 직접 unshare, ip netns, nsenter를 만져보면 이건 단순 개념 설명으로 끝낼 기술이 아니더라고요.

    중요한 건 기대치를 정확히 잡는 겁니다. namespace는 마법 상자가 아닙니다. 커널은 공유하고, CPU·메모리 같은 자원 제한도 기본으로 걸어주지 않습니다. 대신 프로세스가 보는 시야를 분리합니다. 이 차이를 이해하면 “언제 namespace를 직접 쓰고, 언제 컨테이너 런타임이나 VM으로 넘어가야 하는지” 판단이 훨씬 빨라집니다. 오늘은 정의 위주보다, Linux namespace 활용이 실제로 어디에 먹히고 어디서 한계가 드러나는지에 초점을 맞춰 보겠습니다.

    Linux namespace 활용에서 격리 범위를 보여주는 아키텍처 다이어그램

    호스트와 각 namespace가 PID, Network, Mount, UTS 관점에서 어떻게 분리되는지 한눈에 보여주는 개요 이미지입니다.

    1. 왜 Linux namespace 활용이 실무에서 바로 먹히는가

    제가 현장에서 namespace를 꺼내는 순간은 대체로 두 가지입니다. 첫째, 운영 호스트에 패키지나 설정 찌꺼기를 남기고 싶지 않을 때입니다. 둘째, 테스트 대상 프로세스가 어디까지 보이고 어디로 나가는지 통제하고 싶을 때죠. 이건 Dockerfile을 만들 정도로 반복적인 작업은 아닌데, 그렇다고 그냥 호스트에서 돌리기엔 찜찜한 상황에서 특히 유용합니다.

    • 서비스 시작 스크립트가 어떤 파일과 소켓을 만드는지 확인할 때
    • 외부 통신을 막은 채 애플리케이션 기동 여부만 검증할 때
    • 방화벽 정책이나 라우팅 변경 전, 별도 네트워크 공간에서 먼저 재현할 때
    • 문제 프로세스를 호스트 전체와 분리해 관찰할 때

    여기서 VM과 컨테이너, namespace를 같은 선상에서만 비교하면 판단이 흐려집니다. 제가 체감한 기준은 이렇습니다. VM은 커널까지 분리하는 대신 무겁고, 컨테이너는 운영 편의성이 좋지만 준비물이 생기고, namespace 직접 사용은 가장 가볍지만 조립 책임이 사용자에게 있습니다. 즉, namespace는 “기능이 약한 컨테이너”라기보다 “필요한 격리 축만 직접 뽑아 쓰는 커널 도구”에 가깝습니다.

    실무 품질을 좌우하는 포인트도 분명합니다. namespace는 CPU나 메모리 폭주를 막아주지 않습니다. 파일 시스템 쓰기 범위도 자동으로 최소화해 주지 않고요. 그래서 저는 보통 이렇게 선을 그어 설명합니다. namespace는 분리, cgroup은 제한, seccomp는 호출 축소, LSM(AppArmor/SELinux)은 정책 강제. 이걸 구분하지 않고 “격리됐다”라고만 말하면 운영 단계에서 사고 납니다.

    2. Linux namespace 활용 전, 무엇이 분리되는지 먼저 보자

    리눅스 네임스페이스는 종류를 외우는 것보다, 각 종류가 장애 분석과 보안에 어떤 영향을 주는지 아는 편이 더 중요합니다. 아래 표는 제가 실제로 판단할 때 쓰는 관점으로 정리한 겁니다.

    Namespace 종류 분리 대상 실무에서 바로 체감되는 효과 자주 놓치는 포인트 주요 도구
    PID 프로세스 ID 공간 테스트 프로세스를 별도 PID 1처럼 실행 가능 /proc를 새로 준비하지 않으면 ps 결과가 헷갈릴 수 있음 unshare --pid --fork, nsenter --pid
    NET 인터페이스, 라우팅, 포트, 방화벽 외부 송신 차단, veth 기반 통신 테스트, 정책 검증 lo를 올리지 않으면 내부 앱이 예상 밖으로 깨질 수 있음 ip netns, ip link, ip route
    MNT 마운트 포인트와 파일시스템 시야 임시 루트, bind mount, 읽기 전용 뷰 구성 전파 속성(shared/private) 이해 없이 건드리면 의도치 않게 호스트에 반영될 수 있음 unshare --mount, mount, pivot_root
    UTS 호스트명, NIS 도메인명 로그와 프롬프트에서 실험 환경 식별이 쉬워짐 보안 경계 자체는 약하고 주로 식별성 개선용 unshare --uts, hostname
    USER UID/GID 매핑과 일부 권한 범위 비특권 사용자 기반 실험에 유리 배포판 정책, 커널 설정, /etc/subuid//etc/subgid 상태에 따라 동작 차이가 큼 unshare --user --map-root-user
    IPC System V IPC, POSIX 메시지 큐 다른 프로세스와 IPC 충돌 방지 눈에 바로 안 보여서 문제 원인을 놓치기 쉬움 unshare --ipc

    저는 처음부터 전부 묶지 않습니다. 대부분 NET + MNT + PID로 시작하고, 로그 식별이 필요하면 UTS, 비특권 실험이 필요하면 USER를 추가합니다. 이유가 있습니다. namespace는 많이 넣을수록 안전해지는 면도 있지만, 동시에 원인 분리가 어려워집니다. 실패했을 때 어느 층에서 막혔는지 빨리 찾는 편이 실무에선 더 중요하거든요.

    3. 실전 시나리오: 격리된 테스트 환경을 직접 만들어보겠습니다

    이번에는 재현 가능한 시나리오로 가보겠습니다. 상황은 이렇다고 치죠. 새 에이전트를 올리기 전에, 이 프로세스가 외부망으로 나가지 못하게 막은 상태에서 정상 기동하는지 확인하고 싶습니다. 또한 호스트의 프로세스 목록과 파일시스템을 그대로 노출하고 싶지 않습니다. 이럴 때 저는 보통 1단계는 네트워크 분리, 2단계는 PID와 마운트 분리 순으로 갑니다.

    1. 호스트와 분리된 네트워크 namespace를 만듭니다.
    2. veth 페어를 붙여 통신 경로를 의도적으로 만듭니다.
    3. namespace 내부에 서비스 하나를 띄워 실제로 어디에 바인딩되는지 확인합니다.
    4. 그다음 PID, UTS, Mount namespace를 붙여 프로세스와 파일시스템 시야까지 좁힙니다.

    이 순서를 추천하는 이유는 단순합니다. 네트워크 단절, 바인딩 실패, /proc 관찰 오류, 마운트 전파 실수는 각기 보는 포인트가 다릅니다. 한 번에 다 엮으면 증상은 하나인데 원인은 넷 다 될 수 있거든요.

    3-1. 네트워크 namespace 생성

    먼저 가장 자주 쓰는 형태부터 보겠습니다. 아래 예시는 호스트 쪽 veth-host와 namespace 내부 veth-lab를 연결하고, 기본 라우트까지 넣는 최소 구성입니다. 외부 인터넷이 꼭 필요 없다면 default route는 일부러 빼도 괜찮습니다.

    sudo ip netns add labns
    sudo ip link add veth-host type veth peer name veth-lab
    sudo ip link set veth-lab netns labns
    
    sudo ip addr add 10.200.1.1/24 dev veth-host
    sudo ip link set veth-host up
    
    sudo ip netns exec labns ip addr add 10.200.1.2/24 dev veth-lab
    sudo ip netns exec labns ip link set lo up
    sudo ip netns exec labns ip link set veth-lab up
    sudo ip netns exec labns ip route add default via 10.200.1.1
    
    sudo ip netns exec labns ip addr
    sudo ip netns exec labns ip route

    이 단계에서 제일 많이 놓치는 건 두 가지입니다. 하나는 lo를 올리지 않는 것, 다른 하나는 “기본 라우트가 있어야만 내부 테스트가 된다”고 오해하는 겁니다. 사실 동일 서브넷 내 호스트와 namespace 간 통신만 볼 거라면 default route가 없어도 됩니다. 반대로 외부로 내보낼 생각이 없다면 일부러 default route를 넣지 않는 편이 더 안전합니다. 이거 진짜 편하더라고요. 시작부터 길을 다 열어 두면, 통신이 되는 이유가 라우팅인지 NAT인지 애플리케이션 재시도 때문인지 해석이 흐려집니다.

    또 하나, ip netns add는 named network namespace를 /var/run/netns/ 관례 아래에 연결해 관리합니다. 배포판에 따라 실제 경로가 /run/netns/로 보일 수도 있는데, 보통 둘은 같은 런타임 경로 계열로 이해하면 됩니다. 그래서 정리 순서도 중요합니다. 네임스페이스 내부 프로세스가 남아 있으면 삭제가 깔끔하게 안 될 수 있습니다.

    sudo ip netns pids labns
    sudo ip netns exec labns pkill -f http.server || true
    sudo ip netns del labns
    sudo ip link del veth-host || true

    여기서 ip netns pids를 먼저 보는 이유가 있습니다. veth 삭제가 실패하거나 namespace 삭제가 남는 경우, 대개 내부에 살아 있는 프로세스가 원인입니다. 명령어는 간단한데 cleanup을 무시하면 실습이 반복될수록 환경이 꼬입니다.

    3-2. namespace 내부에서 서비스 실행

    이제 namespace 내부에 실제 서비스를 올려 보겠습니다. 예시는 단순하지만, 판단 기준은 실무 그대로 가져갈 수 있습니다. 핵심은 “서비스가 떴는가”가 아니라 어디에 바인딩됐고, 어느 네임스페이스 관점에서 보여야 정상인가를 확인하는 겁니다.

    sudo mkdir -p /srv/labns/www
    printf '%s\n' 'hello from lab namespace' | sudo tee /srv/labns/www/index.html > /dev/null
    
    sudo ip netns exec labns bash -lc 'cd /srv/labns/www && python3 -m http.server 8080 --bind 10.200.1.2'
    
    # 다른 터미널에서 확인
    curl http://10.200.1.2:8080/
    sudo ip netns exec labns ss -lntp
    sudo ip netns exec labns curl -I http://10.200.1.2:8080/

    제가 이 단계에서 항상 같이 보는 건 ss -lntp와 바인딩 주소입니다. 127.0.0.1에만 바인딩된 서비스는 namespace 외부에서 접근되지 않아도 정상일 수 있습니다. 반대로 0.0.0.0에 걸렸다면 namespace 내부에선 모든 인터페이스를 받는다는 뜻이라, 추후 포워딩을 붙이면 생각보다 넓게 열릴 수 있습니다. 즉, 서비스 가시성 문제를 네트워크 문제로 오진하지 않는 것이 중요합니다.

    호스트에서 안 열리는데 namespace 내부에서는 잘 뜨는 경우도 자주 봅니다. 이때는 실패가 아니라, 오히려 격리가 의도대로 작동한 신호일 수 있습니다. 확인 순서는 보통 이렇습니다.

    • ip netns exec labns ss -lntp로 프로세스가 어떤 IP와 포트에 bind 했는지 본다
    • curl을 namespace 내부에서 먼저 날린다
    • 그다음 호스트에서 같은 IP로 접근해 경로가 있는지 확인한다
    • 필요하면 NAT나 포워딩을 추가하되, 마지막 단계로 미룬다
    리눅스 네임스페이스 기반 격리 환경 구축 네트워크 구성도

    호스트의 veth와 namespace 내부 인터페이스가 어떻게 연결되고, 테스트 서비스가 어느 IP에 바인딩되는지 설명하는 구성도입니다.

    3-3. 외부 송신까지 통제하려면 어디를 더 봐야 하나

    네트워크 namespace를 만든 뒤 많은 분들이 “이제 외부 인터넷도 자동으로 막히겠지”라고 생각하시는데, 그건 아닙니다. 경로와 NAT를 어떻게 주느냐에 따라 얼마든지 나갈 수 있습니다. 그래서 보안 검증 목적이면 처음에는 default route를 아예 주지 않거나, 주더라도 포워딩과 NAT를 넣지 않은 상태에서 출발하는 편이 낫습니다.

    외부 통신이 필요한 테스트라면 호스트에 포워딩과 NAT를 붙일 수는 있습니다. 다만 이건 편의성보다 해석 난도가 올라갑니다. 정책 검증이 목적이라면 “닫힌 상태에서 하나씩 여는 방식”이 원인 파악에 훨씬 유리합니다. 최소 예시는 아래처럼 구성합니다. 최근 배포판은 nftables를 기본으로 쓰는 경우도 많으니, 환경에 따라 iptables 명령이 호환 계층인지도 같이 확인하는 편이 좋습니다.

    # 호스트에서 IPv4 forwarding 활성화
    sudo sysctl -w net.ipv4.ip_forward=1
    
    # 예시: labns 대역을 외부 인터페이스 eth0로 NAT
    sudo iptables -t nat -A POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE
    sudo iptables -A FORWARD -i eth0 -o veth-host -m state --state RELATED,ESTABLISHED -j ACCEPT
    sudo iptables -A FORWARD -i veth-host -o eth0 -j ACCEPT

    다만 저는 일회성 분석 용도라면 여기까지는 잘 안 갑니다. NAT가 들어오면 이제 문제 원인이 애플리케이션인지, 라우팅인지, 포워딩 정책인지, 외부 DNS인지 한 층 더 늘어나기 때문입니다. 단순 기동 확인이라면 닫힌 내부망에서 끝내는 쪽이 훨씬 깔끔했습니다.

    3-4. PID, UTS, Mount까지 붙여 더 독립적인 실행 환경 만들기

    네트워크 분리만으로는 부족할 때가 많습니다. 프로세스 목록을 호스트와 분리하고, hostname을 바꾸고, 마운트 시야까지 따로 가져가면 디버깅 노이즈가 크게 줄어듭니다. 제가 자주 쓰는 형태는 아래와 같습니다.

    sudo unshare --fork --pid --mount --uts --ipc bash -lc '
      mount --make-rprivate /
      mount -t proc proc /proc
      hostname lab-host
      echo "[inside namespace] hostname: $(hostname)"
      echo "[inside namespace] process list:"
      ps -ef
      sleep 300
    '

    여기서 핵심은 두 줄입니다. mount --make-rprivate /와 mount -t proc proc /proc입니다. 첫 번째는 마운트 이벤트 전파를 끊어 의도치 않은 공유를 줄이는 역할을 하고, 두 번째는 현재 PID namespace 관점의 /proc을 다시 보여주기 위한 겁니다. 참고로 최근 unshare는 새 mount namespace에서 기본적으로 마운트를 private 쪽으로 돌리는 동작을 하기도 하지만, 명시적으로 적어두면 실습 문서나 운영 절차에서 덜 헷갈립니다.

    특히 PID namespace는 PID 1의 성격 때문에 생각보다 함정이 있습니다. 격리 환경 안에서 PID 1은 일반 프로세스보다 시그널 처리와 좀비 수거에 민감합니다. 실험처럼 짧게 돌릴 땐 괜찮지만, 조금이라도 오래 유지할 프로세스라면 tini 같은 init 래퍼나 별도 supervisor 역할을 고려하는 편이 안전합니다. 이 부분은 컨테이너에서도 똑같이 반복되죠.

    비특권 실험이 필요하면 USER namespace도 검토할 수 있습니다.

    unshare --user --map-root-user --mount --pid --fork bash -lc '
      id
      mount -t proc proc /proc
      ps -ef
      cat /proc/self/uid_map
      cat /proc/self/gid_map
    '

    다만 이건 “되면 좋고 아니면 말고” 식으로 접근하면 안 됩니다. 실제 서버에서는 /proc/sys/user/max_user_namespaces 값, 배포판 보안 정책, 그리고 일부 배포판에서만 제공되는 /proc/sys/kernel/unprivileged_userns_clone 같은 설정 때문에 막히는 경우가 적지 않습니다. 저는 이 단계에서 시간을 오래 쓰기보다, 운영 정책이 금지한 기능이라면 굳이 namespace 직접 조합에 집착하지 않는다는 쪽입니다. 홈랩과 운영 서버가 다르게 반응하는 대표적인 영역이 바로 여기거든요.

    4. Linux namespace 활용과 보안 강화, 어디까지 믿어도 되나

    Linux namespace 활용의 가장 큰 장점은 사고 범위를 줄이는 데 있습니다. 테스트 프로세스가 호스트 전체 프로세스 목록을 못 보고, 네트워크 인터페이스도 제한되고, 마운트 시야까지 좁아지면 “한 번 잘못 돌린 실험이 운영 전체를 어지럽히는 일”이 크게 줄어듭니다. 실수 방지 효과가 꽤 큽니다. 하지만 여기까지입니다. namespace만으로 완전한 샌드박스가 됐다고 보면 위험합니다.

    이유는 단순합니다. 커널은 여전히 공유합니다. 커널 취약점이 있거나 capability를 넓게 줬거나, 마운트와 디바이스 접근을 느슨하게 두면 격리 강도는 금방 낮아집니다. 제가 실무에서 namespace를 단독 보안 장치로 보지 않는 이유도 여기에 있습니다. 보통 아래 조합으로 봅니다.

    • namespace: 프로세스가 보는 세계 분리
    • cgroup: CPU, 메모리, pids 폭주 제한
    • seccomp: 허용 syscall 범위 축소
    • AppArmor 또는 SELinux: 파일·소켓·능력 사용 제약
    • capability drop: 필요 없는 권한 제거

    실무 판단을 더 직접적으로 말하면 이렇습니다. namespace는 “격리의 뼈대”이고, seccomp와 capability 조정이 “탈출 비용을 올리는 장치”이며, cgroup은 “망가질 때 피해 규모를 제한하는 장치”입니다. 하나만 넣고 안심하는 구조가 제일 위험했습니다.

    systemd를 쓰는 환경이라면, 별도 컨테이너 런타임 없이도 서비스 단에서 격리 옵션을 꽤 보강할 수 있습니다. 예를 들면 아래 같은 방향입니다.

    [Service]
    ExecStart=/usr/local/bin/my-agent
    NoNewPrivileges=yes
    PrivateTmp=yes
    ProtectSystem=strict
    ProtectHome=yes
    PrivateDevices=yes
    RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
    CapabilityBoundingSet=
    SystemCallFilter=@system-service

    이건 namespace의 대체재라기보다 보완재에 가깝습니다. 특히 “운영 서비스인데 컨테이너까지는 안 가고 싶다”는 구간에서 생각보다 강력합니다. 저도 네트워크 namespace로 송신 범위를 줄이고, systemd 옵션으로 파일시스템과 권한을 더 잠그는 조합을 자주 씁니다. 관련 옵션을 더 파고들고 싶다면 systemd 보안 설정 정리 글도 함께 보시면 흐름이 더 잘 잡힙니다.

    5. 트러블슈팅: 제가 실제로 자주 겪었던 문제들

    명령어보다 중요한 게 해석 기준입니다. namespace는 증상이 비슷해도 원인이 전혀 다른 경우가 많습니다. 아래는 제가 가장 자주 봤던 실패 패턴과, 그때 실제로 다시 확인하는 순서입니다.

    5-1. ping은 되는데 애플리케이션 통신은 안 되는 경우

    이건 라우팅보다 바인딩 주소 문제인 경우가 많았습니다. 127.0.0.1에만 바인딩된 서비스는 namespace 내부에서는 잘 떠 보이는데, 호스트나 다른 namespace에서는 닿지 않습니다. 반대로 0.0.0.0에 걸렸는데도 안 붙는다면 방화벽이나 경로를 보게 되죠. 저는 이럴 때 순서를 고정해 둡니다.

    • ss -lntp로 listening 주소를 먼저 본다
    • curl을 서비스가 실행 중인 namespace 내부에서 먼저 친다
    • 설정 파일의 listen, bind, host 값을 본다
    • 그다음에야 라우팅과 필터링 규칙을 본다

    이 순서를 추천하는 이유는, 네트워크 문제처럼 보이지만 실제론 프로세스 설정 문제인 경우가 정말 많기 때문입니다. bind 주소 확인 없이 iptables부터 보는 건 대개 시간 낭비였습니다.

    5-2. PID namespace에서 ps 결과가 이상한 경우

    거의 항상 /proc 문제였습니다. 새 PID namespace에 들어갔는데 현재 namespace에 맞는 proc 파일시스템을 보지 않으면, 프로세스 목록이 호스트 기준처럼 보여 혼란이 생깁니다.

    mount -t proc proc /proc
    ps -ef
    cat /proc/1/status
    readlink /proc/self/ns/pid

    /proc/1/status를 보면 현재 namespace 관점의 PID 1이 누구인지 알 수 있고, readlink /proc/self/ns/pid는 실제로 다른 namespace inode를 보고 있는지 확인하는 데 도움이 됩니다. 여기서 “ps가 이상하다”는 증상 자체가 문제의 본질이 아니라 관찰 대상이 잘못됐다는 신호인 경우가 많습니다.

    5-3. 사용자 namespace가 서버마다 다르게 동작하는 경우

    이건 커널 기능 차이보다 운영 정책 차이가 더 크게 체감됐습니다. 특히 비특권 사용자로 unshare --user --map-root-user를 시도할 때 서버마다 결과가 달라집니다.

    cat /proc/sys/user/max_user_namespaces
    [ -f /proc/sys/kernel/unprivileged_userns_clone ] && cat /proc/sys/kernel/unprivileged_userns_clone
    id
    uname -r
    • max_user_namespaces가 낮거나 0이면 생성이 막힐 수 있습니다.
    • unprivileged_userns_clone는 일부 배포판에서만 노출되는 설정이라, 파일이 없다고 이상한 건 아닙니다.
    • 보안 기준이 엄격한 서버는 정책상 의도적으로 비활성화한 경우가 많습니다.

    이럴 때 억지로 우회하는 건 보통 좋은 설계가 아닙니다. 제가 권하는 건 두 가지 중 하나입니다. 운영에서 반드시 필요하면 관리자 정책으로 정식 허용을 받거나, 아니면 컨테이너 런타임이나 별도 테스트 노드로 경로를 바꾸는 것. 운영 정책과 싸우는 형태는 오래 못 갑니다.

    5-4. 마운트는 분리한 줄 알았는데 호스트에 영향이 남는 경우

    이건 mount namespace를 처음 만질 때 특히 많이 겪습니다. 원인은 대개 마운트 전파 속성입니다. namespace를 나눴다고 해서 모든 마운트 이벤트가 자동으로 고립되는 건 아닙니다. 그래서 저는 mount 작업 전에 mount --make-rprivate /를 거의 습관처럼 넣습니다. 이 한 줄을 빼먹으면 bind mount나 임시 마운트가 예상 밖으로 공유돼 버릴 수 있습니다.

    증상은 보통 이렇습니다. 실험 환경 안에서만 붙인 줄 알았던 마운트가 호스트에서도 보이거나, 반대로 호스트 변경이 내부에 그대로 따라 들어옵니다. 이 경우에는 namespace 개념 자체보다 전파 모델을 다시 보는 편이 빠릅니다. 실무에서 mount namespace가 네트워크 namespace보다 까다롭게 느껴지는 이유가 여기 있습니다.

    Linux namespace 활용 점검과 트러블슈팅을 표현한 터미널 스타일 이미지

    ip netns exec, ss -lntp, mount -t proc proc /proc 같은 핵심 점검 포인트를 한 장으로 정리한 이미지입니다.

    6. 검증과 결과 확인: 무엇을 보고 성공이라 판단하나

    명령이 에러 없이 끝났다고 성공은 아닙니다. namespace는 “만들어졌다”와 “의도대로 격리됐다” 사이에 차이가 큽니다. 저는 아래 순서대로 확인합니다.

    1. 인터페이스가 의도한 namespace에 들어갔는지 본다
    2. 프로세스가 내부에서 어떤 PID와 hostname으로 보이는지 본다
    3. 서비스 포트가 기대한 주소에 bind 되었는지 확인한다
    4. 호스트와 namespace 사이 통신 범위를 점검한다
    5. 원치 않은 외부 송신 경로가 없는지 마지막에 확인한다
    ip netns list
    sudo ip netns exec labns ip addr
    sudo ip netns exec labns ip route
    sudo ip netns exec labns ps -ef
    sudo ip netns exec labns ss -lntp
    sudo ip netns exec labns curl -I http://10.200.1.2:8080/
    
    # 호스트에서 접근 확인
    curl -I http://10.200.1.2:8080/
    
    # namespace 내부에서 호스트 방향 확인
    sudo ip netns exec labns ping -c 2 10.200.1.1
    
    # 실제 네임스페이스 분리 여부 확인
    sudo ip netns exec labns readlink /proc/self/ns/net
    readlink /proc/self/ns/net

    결과 해석 기준도 미리 정해 두면 좋습니다.

    • ip netns list에 보이면 생성은 된 겁니다. 하지만 그걸로 끝은 아닙니다.
    • ip addr에 기대한 IP가 없으면 링크 이동이나 주소 할당 단계부터 다시 봐야 합니다.
    • ss -lntp에 프로세스가 없으면 서비스 시작 실패입니다. 이때는 로그보다 실행 명령과 bind 주소를 먼저 봅니다.
    • 호스트에서는 안 보이는데 내부에서는 보이면, 실패가 아니라 격리 성공일 수 있습니다.
    • 외부 통신이 되면 안 되는 시나리오인데 HTTP나 DNS가 나가면 default route, 포워딩, NAT부터 다시 봐야 합니다.

    제가 특히 강조하는 건 마지막 항목입니다. 격리 환경 검증에서 자주 빠지는 게 “열려 있지 않아야 할 경로 확인”입니다. 되는 것만 보면 절반만 본 겁니다. 막혀야 할 것이 실제로 막히는지 확인해야 보안 관점의 검증이 됩니다.

    7. 운영에 붙일 때의 현실적인 추천 조합

    실제로 써보면 모든 namespace를 한 번에 다 쓰는 방식은 종종 과합니다. 목적에 따라 조합을 나누는 편이 유지보수성이 더 좋았습니다. 아래 표는 제가 선택할 때 쓰는 기준입니다.

    상황 추천 조합 이유 굳이 피할 상황
    간단한 네트워크 테스트 NET 설정이 단순하고 효과가 바로 보입니다. 장기 운영 서비스처럼 재현성과 배포 표준화가 중요한 경우
    프로세스 독립 실행 검증 PID + UTS + MNT 프로세스, hostname, 파일시스템 시야를 함께 분리하기 좋습니다. 마운트 전파를 이해하지 못한 상태에서 급히 운영에 넣는 경우
    보안 민감한 임시 실행 USER + NET + MNT + cgroup + seccomp 권한, 시야, 자원, syscall 범위를 함께 줄일 수 있습니다. USER namespace 정책이 금지된 운영 서버
    반복 배포되는 서비스 직접 namespace보다 컨테이너 런타임 검토 이미지 관리, 재시작 정책, 로깅, 표준화 측면에서 유리합니다. 일회성 포렌식 재현이나 빠른 실험처럼 준비 비용이 아까운 경우

    여기서 제 추천은 꽤 명확합니다. 한 번성 검증이면 직접 namespace, 반복 운영이면 컨테이너 런타임이 대체로 맞습니다. 전자는 스패너처럼 빠르게 꺼내 쓰는 도구이고, 후자는 운영 체계에 가깝습니다. 둘 중 무엇이 더 우월하냐의 문제가 아니라, 조립 책임을 누가 지느냐의 차이로 보는 편이 현실적입니다.

    비용 관점에서도 비슷합니다. namespace 직접 조합은 초기 준비가 가볍지만 사람이 더 똑똑해야 합니다. 반면 컨테이너 런타임은 준비물이 늘어나지만 반복성이 좋아집니다. 그래서 팀 단위로 넘어가면 후자가 이기고, 개인 디버깅이나 빠른 재현 실험에선 전자가 여전히 강합니다. 관련해서 컨테이너와 VM 비교 글까지 같이 연결하면 내부 링크 흐름도 자연스럽게 만들 수 있습니다.

    Linux namespace 활용과 컨테이너 선택 기준을 비교한 인포그래픽

    일회성 테스트, 반복 운영, 보안 요구사항에 따라 어떤 선택이 적합한지 비교한 요약 이미지입니다.

    8. 자주 받는 질문과 마지막 권고

    Q1. namespace만 쓰면 컨테이너를 대체할 수 있나요?

    부분적으로는 가능합니다. 특히 빠른 재현, 네트워크 격리 실험, 프로세스 시야 분리 정도는 namespace만으로도 충분히 됩니다. 다만 이미지 관리, 배포 파이프라인, 재시작 정책, 로그 수집 표준화까지 생각하면 컨테이너 런타임이 훨씬 편한 구간이 분명히 있습니다. 저는 보통 “문제 재현과 분석 단계는 namespace”, “반복 서비스화 단계는 컨테이너”로 나눠 봅니다.

    Q2. 보안 강화 목적이라면 무엇부터 붙여야 하나요?

    제가 권하는 시작점은 NET namespace로 통신 범위를 먼저 줄이고, 그다음 MNT와 PID를 분리하는 방식입니다. 여기에 capability 최소화, seccomp, cgroup 제한을 얹는 쪽이 사고를 줄이기 좋았습니다. 처음부터 다 넣으면 강해 보이긴 하지만, 실패 시 원인 분리가 너무 어려워집니다.

    Q3. 홈랩에서도 해볼 가치가 있나요?

    충분합니다. 오히려 홈랩이 제일 좋습니다. 실패해도 운영 영향이 없고, namespace 특유의 함정인 bind 주소, loopback, /proc 준비, mount propagation을 몸으로 익히기 좋거든요. 저도 홈랩에서 먼저 손에 익힌 뒤에야 운영 서버에서 판단 속도가 빨라졌습니다.

    마지막으로 추천을 딱 잘라 말씀드리면 이렇습니다. 단발성 테스트, 포렌식성 재현, 서비스 기동 검증이 목적이면 Linux namespace 활용을 바로 써보는 편이 맞습니다. 반대로 팀 단위 반복 운영, 배포 자동화, 표준 이미지 관리가 목적이면 직접 namespace를 조립하기보다 컨테이너 런타임으로 넘어가는 쪽이 낫습니다. 보안 관점에서도 마찬가지입니다. namespace는 아주 좋은 출발점이지만 종착점은 아닙니다. 네트워크 분리만 믿지 말고, 권한과 syscall, 자원 제한까지 묶어야 실전에서 덜 흔들립니다.

  • [Linux] cgroup v2 vs v1: 리눅스 컨테이너 리소스 제어 차이 분석

    [Linux] cgroup v2 vs v1: 리눅스 컨테이너 리소스 제어 차이 분석

    cgroup v2 vs v1: 리눅스 컨테이너 리소스 제어 차이 분석

    컨테이너 운영에서 cgroup v2 vs v1은 단순한 버전 비교가 아닙니다. 실제 운영에선 “리소스 제한을 걸었다”와 “커널이 의도한 방식으로 그 제한을 집행한다” 사이의 간극이 자주 보이거든요. CPU는 남는데 응답 시간이 흔들리거나, 메모리 제한은 넣었는데 기대한 시점이 아니라 갑자기 OOM으로 터지거나, 블록 I/O가 한쪽에 쏠리면서 옆 워크로드 지연이 커지는 식입니다. 이런 문제를 따라가 보면 결국 cgroup 계층 구조, 컨트롤러 위임 방식, 런타임이 커널과 만나는 지점에서 갈리더라고요.

    현장에서는 아직도 v1 흔적이 남아 있습니다. 다만 신규 배포판, systemd 중심 서버, 최근 컨테이너 런타임 조합이라면 cgroup v2를 기준으로 보는 편이 훨씬 덜 헷갈립니다. 운영 문서나 장애 대응 런북을 정리할 때도 먼저 묻는 건 하나예요. “이 서버는 v1 습관으로 읽어야 하나, 아니면 v2 규칙으로 읽어야 하나?” 이걸 초반에 잘못 잡으면 뒤에 보는 메트릭과 파일 경로 해석이 전부 어긋납니다.

    cgroup v1과 v2 계층 구조 비교 다이어그램

    cgroup v1은 컨트롤러별로 계층이 갈라지고, cgroup v2는 단일 트리에서 CPU·메모리·I/O 정책을 함께 다루는 모습을 보여주는 개요 이미지입니다.

    1. 왜 아직도 cgroup v1과 v2를 따로 봐야 하나요

    커널 기능만 놓고 보면 v1도 충분히 강력했습니다. 문제는 운영 복잡도였죠. v1은 <code>cpu, memory, blkio 같은 컨트롤러가 각자 별도 계층을 가질 수 있어서, 같은 프로세스라도 CPU는 A 경로, 메모리는 B 경로, I/O는 C 경로에 속하는 일이 자연스러웠습니다. 설계 자유도는 높았지만 장애가 나면 운영자가 세 개의 지도를 동시에 펼쳐야 했습니다. 이 구조는 대규모 운영보다는 기능이 먼저 확장된 커널 인터페이스에 더 가깝다고 보는 편이 맞습니다.

    v2는 반대로 갔습니다. 유연성을 조금 덜어내는 대신 정책 일관성, 계층 예측 가능성, systemd와의 결합 안정성을 얻었습니다. 실무 체감도 꽤 분명합니다. v1에서는 “어디를 봐야 하지?”가 먼저였고, v2에서는 “이 값이 왜 이렇게 집행됐지?”가 먼저예요. 전자는 길을 잃기 쉽고, 후자는 원인 분석이 비교적 선형적입니다.

    제가 기준으로 삼는 차이는 세 가지입니다.

    • 관찰 경로 수: v1은 컨트롤러별로 흩어지고, v2는 한 트리에서 따라가기 쉽습니다.
    • 정책 위임 방식: v2는 부모가 자식에게 무엇을 넘겼는지 cgroup.subtree_control로 드러납니다.
    • 운영 도구 친화성: systemd, 최신 런타임, 배포판 기본값과 맞물릴 때 v2가 덜 삐걱거립니다.

    2. cgroup v2 vs v1 핵심 개념: 파일명 차이보다 더 큰 변화

    cgroup v2 vs v1을 파일명 변경 정도로만 기억하면 절반만 이해한 셈입니다. 많이들 memory.limit_in_bytes가 memory.max로 바뀌었다는 식으로 외우는데, 진짜 변화는 “리소스 제한을 어떤 계층 규칙 위에 올릴 것인가”에 있습니다. 이 차이를 이해해야 마이그레이션할 때 덜 흔들립니다.

    v1이 남긴 운영적 특징

    • 컨트롤러마다 별도 마운트와 별도 경로가 가능해서 추적 포인트가 많습니다.
    • 오래된 자동화 스크립트와 호환성이 좋지만, 경로 가정이 강하게 박혀 있는 경우가 많습니다.
    • 성능 이슈가 났을 때 CPU, 메모리, I/O를 같은 구조로 읽기 어렵습니다.

    v2가 바꾼 운영적 특징

    • /sys/fs/cgroup 단일 트리에서 정책과 통계를 같이 읽습니다.
    • cgroup.controllers와 cgroup.subtree_control로 하위 위임 여부가 명시됩니다.
    • cpu.max, memory.max, io.max처럼 이름 규칙이 정돈돼서 자동화 작성이 수월합니다.
    • memory.high, memory.low, memory.min처럼 “강한 상한”과 “보호·완충 구간”을 나눠 설계하기 좋습니다.
    항목 cgroup v1 cgroup v2 실무 해석
    계층 구조 컨트롤러별 분리 가능 통합 계층(unified hierarchy) v2가 장애 분석 경로를 줄입니다.
    CPU 제한 cpu.cfs_quota_us, cpu.cfs_period_us cpu.max v2는 쿼터와 주기를 한 파일에서 읽습니다.
    CPU 가중치 cpu.shares cpu.weight 절대 제한보다 경쟁 상황에서의 우선순위 설계에 중요합니다.
    메모리 상한 memory.limit_in_bytes memory.max 상한만 보면 부족하고, v2에선 memory.high도 같이 봐야 합니다.
    I/O 제어 blkio.* io.* 장치 단위 제한과 관찰 포인트가 v2에서 더 일관적입니다.
    위임 모델 복잡하고 일관성 낮음 명시적 위임 하위 cgroup이 왜 제어 파일을 못 쓰는지 원인 파악이 쉽습니다.
    운영 난이도 도구와 경로 의존성이 큼 상대적으로 단순 신규 표준화는 v2 쪽이 유리합니다.

    실무에서 특히 큰 차이는 v2의 메모리 압박 제어 모델입니다. v1 시절에는 상한(limit) 위주로 사고하는 팀이 많았는데, 상한만으로 운영하면 평소엔 멀쩡하다가 피크 순간에 갑자기 세게 잘리는 일이 생깁니다. v2는 memory.high 같은 완충 구간을 둘 수 있어서, “죽이기 전에 먼저 늦추는” 설계를 하기가 좋습니다. 이거 트래픽 피크가 있는 서비스에선 체감 차이가 꽤 큽니다.

    3. cgroup v2 vs v1 확인: 내 서버가 어떤 계층인지 빠르게 판별하기

    이 단계는 생략하면 안 됩니다. 장애 대응에서도 가장 먼저 보는 지점이에요. 블로그 글이나 사내 문서는 v2 기준인데, 실제 호스트는 hybrid 또는 v1 잔재가 섞여 있으면 설명이 전부 어긋나기 때문입니다.

    1. 파일시스템 타입을 확인합니다.
    2. 마운트 구조를 확인합니다.
    3. 현재 프로세스가 속한 cgroup 경로를 확인합니다.
    4. 대표 제어 파일 이름을 확인합니다.
    # 1) cgroup 파일시스템 타입 확인
    stat -fc %T /sys/fs/cgroup
    
    # 2) 마운트 구조 확인
    mount | grep cgroup
    
    # 3) 현재 셸의 cgroup 소속 확인
    cat /proc/self/cgroup
    
    # 4) v2 대표 파일 확인
    ls /sys/fs/cgroup | grep -E 'cgroup.controllers|cgroup.subtree_control|cpu.max|memory.max|io.max'

    판별 기준은 이렇습니다.

    • stat -fc %T /sys/fs/cgroup 결과가 cgroup2fs면 v2입니다.
    • mount 결과에서 cgroup on /sys/fs/cgroup/cpu처럼 컨트롤러별 마운트가 여럿 보이면 v1일 가능성이 큽니다.
    • /proc/self/cgroup이 단일 계층 형태면 v2, 여러 컨트롤러 라인이 나뉘면 v1 또는 hybrid일 가능성이 큽니다.

    systemd 환경에서는 system.slice, user.slice, kubepods.slice 같은 경로가 보일 수 있습니다. 이건 이상 징후가 아니라 커널 계층을 서비스 관리 단위와 맞춘 흔적입니다. 오히려 경로가 이렇게 구조적으로 보이면 추적이 쉬워집니다.

    다만 여기서 자주 놓치는 함정이 있습니다. 호스트는 v2인데, 오래된 문서나 툴링이 v1 파일명을 기준으로 체크를 돌리는 경우입니다. 그러면 모니터링은 “파일 없음”만 찍고 끝납니다. 그래서 단순 버전 확인에서 멈추지 말고, 실제 자동화가 찾는 파일명까지 같이 검증하는 편이 안전합니다.

    /sys/fs/cgroup 아래 파일 구조와 컨트롤러 예시

    실제 서버에서 확인하는 cgroup 파일 구조 예시와 cgroup.controllers, cpu.max, memory.max 같은 핵심 파일 위치를 보여주는 이미지입니다.

    4. 실전 구현: cgroup v2에서 CPU와 메모리 제한 걸기

    리눅스에서 cgroup을 만지는 방법은 크게 둘입니다. systemd에 맡기거나, 파일시스템을 직접 다루거나입니다. 운영 서버에서는 전자를 먼저 권합니다. 이유는 단순해요. 프로세스 배치, 권한, 정리(clean-up), 서비스 수명주기를 systemd가 같이 책임져 주기 때문입니다. 반대로 원리 이해나 실험에는 후자가 빠릅니다.

    방법 A. systemd-run으로 transient unit 실행

    # 메모리 상한 512M, 메모리 압박 임계값 384M, CPU 쿼터 50%
    sudo systemd-run --unit=cg-demo --scope \
      -p MemoryHigh=384M \
      -p MemoryMax=512M \
      -p CPUQuota=50% \
      -p CPUQuotaPeriodSec=100ms \
      bash -c 'stress-ng --vm 1 --vm-bytes 700M --cpu 2 --timeout 60s'
    
    # systemd 속성 및 매핑 결과 확인
    systemctl status cg-demo.scope
    systemctl show cg-demo.scope \
      -p ControlGroup \
      -p MemoryCurrent \
      -p MemoryHigh \
      -p MemoryMax \
      -p CPUQuotaPerSecUSec \
      -p CPUQuotaPeriodUSec

    여기서 중요한 포인트는 MemoryMax만 보지 않는 겁니다. 서비스가 순간 메모리 피크가 있는 구조라면 먼저 MemoryHigh를 잡고 관찰하는 편이 낫습니다. 상한을 너무 낮게 바로 조이면 OOM으로 가는 길이 짧아지거든요. 반대로 MemoryHigh는 압박 신호를 먼저 주기 때문에, 애플리케이션이 캐시를 줄이거나 GC가 반응할 시간을 벌 수 있습니다.

    CPU도 비슷합니다. CPUQuota=50%는 직관적이지만, 워크로드가 짧은 버스트를 자주 내는 유형이면 평균 사용률은 괜찮아 보여도 nr_throttled가 빠르게 올라갈 수 있습니다. 이럴 때는 쿼터를 높일지, 아예 CPUWeight 중심으로 바꿀지 판단해야 합니다. 절대 제한은 비용 통제엔 좋지만, 지연 민감한 서비스에는 부작용이 더 크게 느껴질 수 있습니다.

    방법 B. cgroup v2 파일을 직접 다뤄보기

    직접 실험할 때는 위임 규칙을 먼저 확인해야 합니다. v2는 파일이 보인다고 다 쓸 수 있는 구조가 아닙니다. 부모 cgroup이 자식에게 컨트롤러를 위임하지 않으면 자식 쪽에 기대한 제어 파일이 나타나지 않거나, 설정이 먹지 않습니다.

    # 루트에서 사용 가능한 컨트롤러 확인
    cat /sys/fs/cgroup/cgroup.controllers
    
    # 하위 cgroup에 cpu, memory 컨트롤러 위임
    echo '+cpu +memory' | sudo tee /sys/fs/cgroup/cgroup.subtree_control
    
    # 테스트용 cgroup 생성
    sudo mkdir /sys/fs/cgroup/lab-demo
    
    # 메모리 압박 임계값과 절대 상한 설정
    echo 402653184 | sudo tee /sys/fs/cgroup/lab-demo/memory.high
    echo 536870912 | sudo tee /sys/fs/cgroup/lab-demo/memory.max
    
    # CPU 최대치 설정: 100ms 주기 중 50ms 사용
    echo '50000 100000' | sudo tee /sys/fs/cgroup/lab-demo/cpu.max
    
    # CPU 상대 우선순위 설정(기본 100, 범위 1~10000)
    echo 200 | sudo tee /sys/fs/cgroup/lab-demo/cpu.weight
    
    # 프로세스를 해당 cgroup에서 실행
    sudo bash -c 'echo $$ > /sys/fs/cgroup/lab-demo/cgroup.procs; exec stress-ng --vm 1 --vm-bytes 700M --cpu 2 --timeout 60s'

    이 방식은 이해에는 좋지만 운영에는 조심해야 합니다. 특히 systemd가 관리하는 호스트에서는 임의 디렉터리를 직접 다루는 방식보다 unit이나 scope로 묶는 편이 덜 위험합니다. 마지막 줄처럼 현재 셸을 직접 옮기는 패턴은 학습용으로는 괜찮아도 실서버에서는 실수 여지가 있습니다. 실제 서버에선 별도 서비스, 별도 scope, 별도 사용자 세션 단위로 분리하는 편이 재현성과 정리 측면에서 훨씬 편하더라고요.

    5. 컨테이너 리소스 제어에서 무엇을 읽어야 하나

    설정보다 더 중요한 건 관찰입니다. 제한을 넣는 건 10초면 끝나지만, 그 제한이 실제로 어떤 방식으로 시스템을 압박하는지 읽는 건 완전히 다른 일입니다. 기본적으로 볼 파일은 memory.current, memory.events, cpu.stat, io.stat이고, 필요하면 memory.pressure와 cpu.pressure까지 같이 봅니다.

    # 메모리 사용량과 이벤트
    cat /sys/fs/cgroup/lab-demo/memory.current
    cat /sys/fs/cgroup/lab-demo/memory.events
    
    # CPU 스로틀링 통계
    cat /sys/fs/cgroup/lab-demo/cpu.stat
    
    # I/O 통계
    cat /sys/fs/cgroup/lab-demo/io.stat
    
    # PSI(Pressure Stall Information) 확인
    cat /sys/fs/cgroup/lab-demo/memory.pressure
    cat /sys/fs/cgroup/lab-demo/cpu.pressure

    읽는 기준은 숫자 자체보다 상관관계입니다.

    • memory.events의 high가 늘면 메모리 압박이 반복되고 있다는 뜻입니다. 바로 장애는 아니어도 응답 시간 악화의 전조일 수 있습니다.
    • memory.events의 max가 늘면 상한에 계속 부딪히고 있다는 뜻입니다. 이 상태가 길어지면 결국 OOM 또는 강한 reclaim으로 이어질 수 있습니다.
    • oom, oom_kill 증가와 애플리케이션 비정상 종료 시점이 맞물리면 원인 범위가 꽤 좁혀집니다.
    • cpu.stat의 nr_throttled, throttled_usec가 빠르게 증가하면 CPU 제한이 실제 처리 지연으로 번질 가능성이 큽니다.
    • memory.pressure가 높고 memory.current는 상한 아래라면, 단순 상한 초과보다 reclaim 비용이 문제일 수 있습니다.
    • io.stat만 보고 끝내면 안 됩니다. I/O는 애플리케이션 레이턴시, 파일시스템 flush, 스토리지 백엔드 상태와 같이 봐야 의미가 생깁니다.

    여기서 많이 생기는 오해가 하나 있습니다. CPU throttling이 보인다고 무조건 나쁜 건 아닙니다. 배치 잡, 백그라운드 인덱싱, 테스트 워커처럼 원래 느려져도 되는 작업이라면 쿼터가 잘 먹고 있다는 뜻일 수 있습니다. 반대로 API 서버나 짧은 요청을 많이 받는 프론트 계층은 같은 throttling이 체감 지연으로 바로 번집니다. 결국 같은 지표라도 워크로드 성격에 따라 해석이 달라집니다.

    systemd-run으로 생성한 cgroup과 메트릭 확인 흐름

    systemd transient unit으로 프로세스를 띄우고 memory.events, cpu.stat, PSI를 확인하는 검증 흐름을 보여주는 구성도입니다.

    6. v1에서 v2로 넘어갈 때 자주 만나는 함정과 근본 원인

    이 부분이 진짜 운영 포인트입니다. 표면적으로는 “파일명이 바뀌었네” 정도로 보이지만, 실제 사고 원인은 더 아래층에 있습니다.

    1) 예전 파일명을 계속 찾는 문제

    현상은 간단합니다. memory.limit_in_bytes가 없고, 스크립트는 실패합니다. 근본 원인은 더 분명합니다. 자동화가 커널 인터페이스를 추상화하지 않고 파일명을 직접 하드코딩했기 때문입니다. 가능하면 내부 도구에 “v1/v2 판별 후 매핑” 레이어를 두는 편이 좋습니다. 운영 자동화는 언젠가 커널 세부 구현과 어긋나거든요.

    2) cgroup.subtree_control를 빼먹는 문제

    현상은 “파일은 있는데 자식에서 못 쓴다”, “생성한 cgroup에 기대한 제어 파일이 없다”입니다. 원인은 v2가 부모의 명시적 위임을 요구하기 때문입니다. v1 습관대로 디렉터리만 만들면 될 거라고 보면 거의 여기서 막힙니다.

    3) no internal process 규칙을 무시하는 문제

    v2는 자원 분배를 담당하는 부모 cgroup과 실제 워크로드가 섞이는 걸 경계합니다. 현상은 구조가 이상하게 꼬이거나, 하위 설계가 불편해지는 것이죠. 원인은 계층을 “정책 노드”와 “실행 노드”로 분리하지 않고 한군데에 몰아넣었기 때문입니다. systemd가 슬라이스와 스코프로 나눠 주는 이유도 여기와 맞닿아 있습니다.

    4) 런타임 드라이버 불일치

    Docker, containerd, kubelet, systemd 조합에서 꽤 자주 만나는 문제입니다. 제한은 건 것 같은데 예상한 경로가 아니라 다른 계층에 정책이 생기거나, 관찰 위치가 어긋납니다. 근본 원인은 cgroup driver와 서비스 관리자 계층이 서로 다른 모델을 가정하기 때문입니다. 이럴 때는 설정 파일 하나만 보지 말고, 실제 서비스 프로세스의 /proc/<pid>/cgroup과 systemd의 ControlGroup를 같이 봐야 합니다.

    5) 메모리 상한만 믿고 압박 제어를 안 하는 문제

    현상은 피크 순간 응답 시간이 급격히 나빠지거나, 갑작스러운 OOM으로 이어지는 것입니다. 원인은 메모리를 “최대치 하나”로만 관리했기 때문입니다. 서비스 성격에 따라 memory.high를 먼저 두고, 정말 넘기면 안 되는 한계만 memory.max로 닫는 구성이 더 안정적으로 먹히는 경우가 많습니다. 비용 통제와 안정성 사이에 완충 구간을 두는 셈이죠.

    7. 재현 가능한 트러블슈팅 시나리오 하나

    홈랩이나 테스트 서버에서 자주 재현하는 건 “제한은 정상인데, 체감 성능이 왜 이렇게 나쁘지?” 유형입니다. 이런 문제는 상한만 보는 습관으로는 잘 안 잡힙니다. 압박 이벤트와 스로틀링을 같이 봐야 감이 옵니다.

    1. MemoryHigh와 MemoryMax를 분리해서 가진 transient unit을 만듭니다.
    2. 그 안에서 메모리를 빠르게 쓰는 프로세스를 실행합니다.
    3. memory.current, memory.events, memory.pressure를 같이 봅니다.
    4. 동시에 journalctl과 애플리케이션 로그 시점을 맞춰 봅니다.
    sudo systemd-run --unit=mem-lab --scope \
      -p MemoryHigh=192M \
      -p MemoryMax=256M \
      bash -c 'stress-ng --vm 1 --vm-bytes 400M --timeout 30s'
    
    CG=$(systemctl show mem-lab.scope -p ControlGroup --value)
    
    cat /sys/fs/cgroup${CG}/memory.current
    cat /sys/fs/cgroup${CG}/memory.events
    cat /sys/fs/cgroup${CG}/memory.pressure
    journalctl -u mem-lab.scope --no-pager

    이 시나리오에서 볼 건 세 가지입니다. 첫째, high 이벤트가 먼저 늘어나는지. 둘째, 그다음에 max나 oom이 붙는지. 셋째, 같은 시점에 애플리케이션 레이턴시나 종료 로그가 어떻게 움직이는지입니다. 여기서 얻는 교훈은 꽤 분명합니다. 메모리 문제는 사용량 숫자 하나로 판단하면 늦습니다. 압박 이벤트가 먼저 오고, 그다음 증상이 사용자에게 보이는 경우가 많습니다.

    비슷한 방식으로 CPU도 재현할 수 있습니다. 쿼터를 낮춘 뒤 cpu.stat에서 nr_throttled가 늘어나는 속도와 애플리케이션 p95 지연을 같이 보면, “제한이 먹는다”와 “서비스가 느려진다”가 언제 연결되는지 보입니다. 실제 튜닝은 그 시점을 찾는 데서 출발하더라고요.

    8. cgroup v2 vs v1 선택 기준: 언제 v2로 가고, 언제 점진 전환할까

    실무 기준으로 말하면 신규 구축은 v2 우선입니다. 다만 “무조건 최신이 좋다”가 아니라, 어떤 운영 비용을 줄일 수 있느냐로 봐야 합니다. v2는 특히 systemd 중심 서버, 표준화된 컨테이너 운영, 내부 자동화 정비를 같이 할 수 있는 팀에 잘 맞습니다. 반대로 오래된 스크립트와 모니터링이 v1 경로에 깊게 묶여 있다면, 이행 비용을 먼저 계산해야 합니다.

    상황 추천 이유 판단 포인트
    신규 리눅스 서버 구축 cgroup v2 우선 통합 계층, systemd 친화성, 운영 단순화 자동화와 모니터링을 처음부터 v2 기준으로 맞출 수 있는지
    systemd 중심 운영 v2 강력 추천 unit 속성과 cgroup 파일 매핑이 자연스럽습니다. systemctl show와 커널 파일을 함께 읽는 절차를 표준화할 수 있는지
    레거시 스크립트가 v1 파일명에 깊게 의존 점진 전환 한 번에 바꾸면 장애 탐지 공백이 생길 수 있습니다. 파일명 매핑 레이어와 모니터링 수정이 끝났는지
    CPU 버스트가 중요한 지연 민감 서비스 v2 + 쿼터 신중 적용 절대 제한이 지연을 키울 수 있습니다. CPUQuota보다 CPUWeight가 더 맞는지
    메모리 피크가 잦은 서비스 v2 + memory.high 활용 갑작스러운 OOM보다 완충형 압박 제어에 유리합니다. 상한 하나가 아니라 압박 임계값과 함께 설계했는지
    장애 분석 중 파일이 안 보여 혼란스러운 상태 먼저 버전 확인 v1/v2 혼동이 원인인 경우가 많습니다. 실제 PID 경로와 서비스 매니저 경로가 일치하는지

    정리하면 선택 기준은 비교적 단순합니다.

    • 이럴 땐 A: 신규 서버, systemd 기본, 컨테이너 런타임 표준화가 가능하면 cgroup v2로 가는 편이 맞습니다.
    • 이럴 땐 B: 레거시 감시 스크립트와 운영 도구가 v1 경로에 박혀 있으면, 즉시 전환보다 매핑 정리 후 점진 이행이 안전합니다.
    • 이럴 땐 C: 비용 통제 목적의 배치성 워크로드는 CPUQuota/MemoryMax를 비교적 적극적으로 써도 됩니다.
    • 이럴 땐 D: 지연 민감 서비스는 CPUWeight + 완만한 메모리 압박 제어를 먼저 검토하고, 절대 상한은 마지막에 닫는 편이 낫습니다.
    cgroup v1과 v2 선택 기준 요약 인포그래픽

    신규 구축, 레거시 유지, systemd 운영, 지연 민감 서비스 등 상황별로 cgroup v1과 v2 선택 기준을 정리한 요약 이미지입니다.

    9. 저는 이렇게 권합니다

    cgroup v2 vs v1을 공부하다 보면 파일명과 옵션 암기에 빠지기 쉽습니다. 그런데 운영 관점에서는 그보다 어떤 워크로드에 어떤 제어 방식을 쓰느냐가 더 중요합니다. 새로 시작하는 환경이라면 cgroup v2를 기본값으로 잡는 편이 좋습니다. 그리고 메모리는 상한 하나만 보지 말고 memory.high와 이벤트 카운터까지 같이 보고, CPU는 무조건 쿼터부터 닫기보다 서비스 성격에 따라 CPUWeight와 병행해서 판단해 보세요.

    반대로 v1 기반 자동화가 이미 깊게 들어가 있다면, 무리하게 한 번에 바꾸는 건 추천하지 않습니다. 이 경우엔 순서가 있습니다. 먼저 버전 판별 로직을 넣고, 그다음 파일명 매핑을 정리하고, 마지막으로 모니터링과 런북을 v2 기준으로 갈아타는 편이 안전합니다. 마이그레이션 실패는 커널 기능 부족보다 운영 습관이 바뀌지 않은 상태에서 인터페이스만 바꾼 경우에 더 자주 나오더라고요.

    한 줄로 정리하면 이렇습니다. 레거시 제약이 없다면 v2, 레거시가 강하면 점진 전환입니다. 그리고 어떤 경우든 설정값 하나만 보지 말고 memory.events, cpu.stat, PSI, 서비스 로그를 같이 읽어야 실제 원인이 보입니다. 관련 글이 있다면 systemd resource-control 가이드나 컨테이너 메모리 튜닝 체크리스트도 이어서 읽어 두세요. 같이 보면 운영 감각이 훨씬 빨리 붙습니다.

  • [Linux] firewalld 보안 체크리스트: Linux 서버 방화벽 설정 모범 사례

    [Linux] firewalld 보안 체크리스트: Linux 서버 방화벽 설정 모범 사례

    firewalld 보안 체크리스트: Linux 서버 방화벽 설정 모범 사례

    리눅스 서버를 오래 운영하다 보면, 서비스는 멀쩡한데 방화벽이 애매하게 열려 있어서 뒤늦게 식은땀이 나는 순간이 꼭 옵니다. 저도 홈랩이랑 운영 서버를 같이 보면서 몇 번이나 겪었거든요. 특히 배포 직후에는 패키지 설치, TLS, 애플리케이션 기동에 신경이 쏠리다 보니 firewalld 보안 체크리스트가 뒤로 밀리기 쉽습니다. 그런데 실제 사고는 대개 여기서 납니다. CVE가 바로 터지지 않더라도, 불필요하게 열린 관리 포트 하나가 공격 표면을 넓히고, 나중에는 “왜 이 포트가 열려 있지?”를 추적하는 운영 비용으로 돌아오더라고요.

    이 글은 명령어만 늘어놓는 글이 아닙니다. 저는 firewalld를 볼 때 “무엇을 열까”보다 “왜 이 경계가 필요한가, 어디서 망가지기 쉬운가, 변경 후 어떻게 검증할까”를 더 중요하게 봅니다. 그래서 이번 글은 Linux 방화벽 설정을 점검하는 순서, 자주 꼬이는 실패 모드의 원인, 단일 NIC 서버와 듀얼 NIC 서버에서 판단이 갈리는 지점까지 실무 기준으로 묶었습니다. 체크박스만 채우는 요약이 아니라, 실제로 복붙해서 점검하고 운영 습관까지 바꿀 수 있는 내용으로 정리해보겠습니다.

    firewalld 보안 체크리스트를 설명하는 Linux 서버 방화벽 아키텍처 이미지

    firewalld의 Zone(존), Service(서비스), Port(포트)가 어떻게 맞물려 동작하는지 한눈에 보여주는 개요 이미지입니다.

    1. 왜 firewalld 보안 체크리스트가 먼저냐

    제가 운영 초기에 가장 많이 본 실수는 “애플리케이션이 정상 응답하니 네트워크도 정리된 줄 아는 상태”였습니다. 실제로는 반대인 경우가 많습니다. 앱은 443만 써야 하는데, 테스트 때 열어둔 8080, 관리용 9090, 배포판 기본 서비스까지 같이 살아 있는 식이죠. firewalld는 이런 상태를 정리하기에 꽤 좋은 도구입니다. 다만 편한 도구라고 해서 자동으로 안전해지지는 않습니다. 규칙을 추가하기 쉬운 도구일수록, 나중에 덜어내는 기준이 더 중요합니다.

    제가 실무에서 기준으로 삼는 건 네 가지입니다. 첫째, 인터넷에 공개할 이유가 없는 포트는 기본적으로 닫는다. 둘째, 관리 트래픽은 서비스 트래픽과 다른 경계로 본다. 셋째, 런타임 실험과 영구 반영을 섞지 않는다. 넷째, 명령 성공이 아니라 실제 패킷 경로 기준으로 검증한다. 방화벽은 설정 자체보다 운영 중 일관성이 더 중요합니다.

    • 최소 허용: 서비스가 실제로 수신해야 하는 프로토콜과 포트만 엽니다.
    • 경계 분리: 공개망, 관리망, 백업망을 같은 존 정책으로 처리하지 않습니다.
    • 상태 분리: 런타임 테스트와 --permanent 반영을 구분합니다.
    • 검증 우선: firewall-cmd 출력만 보지 말고 리슨 상태와 외부 접속 결과까지 확인합니다.

    2. firewalld 보안 체크리스트의 핵심 개념

    처음에 많이 헷갈리는 부분이 Zone, Service, Port, Rich Rule이 각각 뭘 책임지는지입니다. 저는 이걸 “정책의 단위가 다르다”로 이해하는 편이 가장 덜 꼬였습니다. Zone은 경계, Service는 의미 있는 허용 단위, Port는 예외 처리, Rich Rule은 조건부 허용입니다. 이걸 섞어 쓰더라도 각자 맡는 역할이 분명해야 나중에 규칙 해석이 쉬워집니다.

    2-1. Zone(존, 신뢰 수준별 정책 그룹)

    Zone은 인터페이스 또는 소스 대역에 붙는 정책 경계입니다. 중요한 건 “서버 한 대 = 정책 한 개”가 아니라는 점입니다. NIC가 하나여도 출발지 대역이 다르면 다른 경계로 볼 수 있고, NIC가 둘 이상이면 더더욱 분리하는 게 맞습니다. 운영하면서 제일 위험한 패턴은 외부 공개 NIC와 관리자 전용 네트워크를 둘 다 public에 묶어두는 경우였습니다. 이렇게 해두면 나중에 SSH, 백업, 모니터링 예외가 전부 public에 쌓이면서 정책이 지저분해집니다.

    2-2. Service(서비스, 미리 정의된 포트 집합)

    ssh, http, https 같은 표준 서비스는 Service로 여는 편이 낫습니다. 숫자 포트보다 의도가 남기 때문입니다. 나중에 누가 봐도 “이 서버가 왜 이 포트를 열었는지”를 해석하기 쉽습니다. 다만 모든 걸 Service로 해결하려고 하면 오히려 애매해질 수 있습니다. 예를 들어 커스텀 앱 8443/tcp를 장기 운영하면서 이름 없는 포트로만 남겨두면 의미를 잃고, 반대로 잠깐 쓸 포트까지 서비스 XML로 만드는 건 과합니다.

    2-3. Runtime과 Permanent

    여기가 제일 많이 사고 나는 지점입니다. 런타임은 현재 커널에 적용된 메모리 상태이고, 영구 설정은 디스크에 저장된 상태입니다. 즉, 테스트할 때는 런타임이 빠르지만 운영 기준은 결국 영구 설정입니다. 문제는 두 상태가 잠시 어긋나도 눈으로는 잘 안 보인다는 점이죠. 그래서 저는 “테스트는 런타임, 반영은 영구, 마감은 reload 후 재검증” 순서를 고정해둡니다. 이 흐름을 지키면 재부팅 뒤 접속 불가 같은 사고가 확 줄어듭니다.

    선택지 언제 쓰면 좋은가 장점 언제 피해야 하는가 운영상 주의점
    Service 추가 SSH, HTTP, HTTPS 같은 표준 포트 의도가 명확하고 읽기 쉽습니다 실제 포트 구성이 표준과 다를 때 --info-service로 정의 내용을 확인해두면 좋습니다
    Port 직접 허용 커스텀 앱, 단기 테스트, 비표준 포트 즉시 반영이 쉽습니다 장기 운영 포트를 설명 없이 누적할 때 운영 문서에 포트 용도를 같이 남겨야 합니다
    Rich Rule 출발지 IP 제한, 로그, 세밀한 예외 처리 서비스 공개 범위를 정확히 좁힐 수 있습니다 단순한 허용 규칙까지 전부 Rich Rule로 만들 때 규칙이 많아지면 읽기 어려워지므로 목적별로 묶어야 합니다
    Zone 분리 NIC가 둘 이상이거나 관리망/서비스망이 분리된 환경 경계가 명확해지고 예외가 줄어듭니다 작은 단일 NIC 서버에서 과도하게 복잡도를 키울 때 인터페이스 매핑과 소스 대역 매핑을 혼동하지 않아야 합니다
    커스텀 Service XML 장기 운영하는 사내 표준 앱 포트 포트 의미를 이름으로 보존할 수 있습니다 일회성 포트 하나만 잠깐 열 때 /etc/firewalld/services/에 정의를 남기고 변경 이력을 관리하면 편합니다

    3. 실전 체크리스트 1단계: 현재 상태부터 정확히 파악하기

    방화벽 작업에서 제일 위험한 건 규칙 추가 자체가 아니라, 현재 상태를 오해한 채 손대는 겁니다. 원격 SSH 세션 하나로 작업 중인데 현재 세션이 어느 존 정책에 의해 살아 있는지, 같은 서버에 다른 관리 포트가 떠 있는지, 런타임과 영구 설정이 같은지 모르고 건드리면 끊어먹기 쉽습니다. 그래서 저는 항상 “서비스 상태 → 활성 존 → 인터페이스 매핑 → 허용 항목 → 리슨 상태” 순서로 봅니다. 이 순서가 의외로 사고를 많이 줄여줍니다.

    1. firewalld 서비스가 실제로 올라와 있는지 확인합니다.
    2. 기본 Zone과 활성 Zone이 같은지 봅니다.
    3. 어떤 인터페이스가 어느 Zone에 붙었는지 확인합니다.
    4. Zone별 허용 서비스, 포트, Rich Rule을 나눠 봅니다.
    5. 실제 프로세스가 어떤 주소와 포트에 바인딩되어 있는지 비교합니다.
    sudo systemctl status firewalld
    sudo firewall-cmd --state
    sudo firewall-cmd --get-default-zone
    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-all
    sudo firewall-cmd --list-all-zones
    sudo ss -tulpn

    여기서 읽는 기준이 중요합니다. --get-active-zones는 “정책 이름”보다 “어느 인터페이스나 소스가 어디에 매핑됐는지”를 보는 명령이라고 생각하시면 됩니다. --list-all에서는 services, ports, rich rules를 따로 봐야 합니다. 운영 현장에서 진짜 자주 보는 실수가 서비스만 보고 안심하는 겁니다. 예전에 열어둔 포트가 ports:에 남아 있는데 services:만 보고 지나가면 정책 오판이 생깁니다.

    한 가지 더 보셔야 할 게 있습니다. ss -tulpn 결과와 firewalld 허용 목록이 맞는지 비교해야 합니다. 앱이 0.0.0.0:9090에 떠 있는데 방화벽에서 9090이 닫혀 있으면 외부 노출은 막히겠지만, 내부 망이나 로컬 프록시 경로에서는 다른 문제가 생길 수 있습니다. 반대로 앱이 127.0.0.1에만 바인딩되어 있으면 포트를 아무리 열어도 외부 접속은 안 됩니다. 보안 점검과 장애 점검이 이 지점에서 만납니다.

    제가 실무에서 자주 쓰는 확인 패턴은 아래처럼 “방화벽 상태와 실제 리슨 상태를 한 번에 비교 가능한 형태”로 보는 방식입니다.

    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-services
    sudo firewall-cmd --zone=public --list-ports
    sudo firewall-cmd --zone=public --list-rich-rules
    sudo ss -lntp
    sudo ss -lnup
    firewalld 보안 체크리스트 점검을 위한 Linux 방화벽 설정 터미널 이미지

    활성 Zone, 인터페이스 매핑, 허용 서비스와 포트를 점검하는 실제 운영자 시점의 터미널 화면을 표현한 이미지입니다.

    4. 실전 체크리스트 2단계: 필요한 것만 열고 나머지는 닫기

    이 단계부터는 정책을 정리합니다. 기준은 단순합니다. “지금 열려 있는 것”이 아니라 “이 서버 역할에 꼭 필요한 것”만 남기는 겁니다. 웹 서버라면 대개 공개 포트는 80/443 중 필요한 것만 남고, SSH는 관리 경로로만 제한합니다. 이때 중요한 건 보안을 이유로 운영성을 망치지 않는 겁니다. 예를 들어 SSH 전체 차단은 멋있어 보일 수 있지만, 콘솔 대체 수단이 없으면 사고 복구 시간을 키웁니다. 그래서 저는 외부 공개 서버에서 가장 현실적인 기본형을 HTTPS 공개 + SSH 출발지 제한 + 불필요한 기본 서비스 제거로 잡습니다.

    4-1. 기본 서비스만 먼저 허용

    sudo firewall-cmd --permanent --zone=public --add-service=https
    sudo firewall-cmd --permanent --zone=public --add-service=http
    sudo firewall-cmd --permanent --zone=public --remove-service=cockpit
    sudo firewall-cmd --permanent --zone=public --remove-service=dhcpv6-client
    sudo firewall-cmd --reload
    sudo firewall-cmd --zone=public --list-all

    여기서 판단 기준을 분명히 하셔야 합니다. http는 80에서 443으로 리다이렉트하는 프런트가 있거나 ACME HTTP-01 검증이 필요하면 열고, 그렇지 않으면 빼도 됩니다. cockpit은 명시적으로 운영할 때만 여는 편이 안전합니다. 다만 cockpit이나 dhcpv6-client가 실제로 기본 등록되어 있는지는 배포판과 초기 설정에 따라 다를 수 있으니, 제거 전에 --list-all로 현재 상태를 먼저 보세요. 운영 보안은 결국 기본값을 그대로 믿지 않는 데서 시작합니다.

    4-2. SSH는 전체 공개보다 출발지 IP 제한

    SSH는 “열까 말까”보다 “누구에게 열까”가 더 중요합니다. 운영 서버에서 22/tcp 전체 공개는 공격 표면을 넓히는 데 비해 얻는 편의가 적습니다. 고정 관리 IP나 VPN 대역이 있다면 Rich Rule로 제한하는 쪽이 좋습니다. 특히 fail2ban 같은 보조 수단을 쓰더라도, 방화벽에서 먼저 줄이는 게 낫습니다. 로그인 실패를 막는 것보다 애초에 소켓 도달 범위를 좁히는 편이 더 근본적이거든요.

    sudo firewall-cmd --permanent --zone=public \
      --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
    
    sudo firewall-cmd --permanent --zone=public --remove-service=ssh
    sudo firewall-cmd --reload
    sudo firewall-cmd --zone=public --list-rich-rules

    이 규칙의 핵심은 단순합니다. ssh 서비스를 전체 공개하지 않고, 특정 출발지에서만 받아줍니다. 관리 대역이 VPN 서브넷이라면 203.0.113.10/32 대신 실제 CIDR을 넣으시면 됩니다. 다만 여기서 흔한 실패는 “회사 공인 IP라고 생각했던 주소가 실제로는 NAT 뒤에서 바뀌는 주소”인 경우입니다. 그러면 방화벽 문제처럼 보이는데 사실은 출발지 식별 가정이 틀린 겁니다. 그래서 이 단계는 현재 세션을 유지한 채 새 터미널에서 재접속 테스트까지 같이 하시는 게 안전합니다.

    4-3. 커스텀 포트는 무조건 열지 말고, 수명에 따라 처리 방식 나누기

    8443, 9090, 3000 같은 커스텀 포트는 운영에서 자주 나옵니다. 저는 이걸 수명 기준으로 나눕니다. 일시적인 디버그 포트면 짧게 열고 바로 닫습니다. 장기 운영 포트면 처음부터 이름 있는 서비스로 승격할지, 포트로 두되 문서화할지 정합니다. 이걸 안 정하면 몇 달 뒤 포트 목록이 기술 부채가 됩니다.

    sudo firewall-cmd --permanent --zone=public --add-port=8443/tcp
    sudo firewall-cmd --permanent --zone=public --remove-port=9090/tcp
    sudo firewall-cmd --reload
    sudo firewall-cmd --zone=public --list-ports

    장기 운영 포트가 있고 팀원이 여러 명이라면 커스텀 서비스 정의를 고려할 만합니다. 포트를 숫자로만 남겨두면 “8443이 무슨 용도였지?”가 반복됩니다. 반대로 운영 수명이 짧은 포트는 XML 정의까지 만들 필요는 없습니다. 이런 선택 기준이 실무에서 시간을 아껴줍니다.

    <?xml version="1.0" encoding="utf-8"?>
    <service>
      <short>internal-api</short>
      <description>Internal API exposed via TCP 8443</description>
      <port protocol="tcp" port="8443"/>
    </service>

    위 같은 파일을 /etc/firewalld/services/internal-api.xml에 두고 reload한 뒤 --add-service=internal-api로 관리하면, 숫자 포트보다 정책 의미가 오래 남습니다. 저는 포트가 배포 문서, 모니터링, 보안 정책에 반복 등장하기 시작하면 이 단계로 올립니다.

    5. 실전 체크리스트 3단계: Zone 분리와 인터페이스 매핑

    NIC가 둘 이상인 서버에서는 여기서 보안 수준이 꽤 갈립니다. 공개 트래픽과 관리 트래픽을 같은 존에 넣어두면, 나중에 허용 규칙이 죄다 public으로 몰립니다. 그 결과 “관리 편의를 위해 public 예외를 하나 더 추가”하는 패턴이 반복되고, 결국 원래 분리했어야 할 경계가 사라집니다. 제가 듀얼 NIC 서버에서 가장 먼저 정리하는 게 이 부분입니다.

    sudo firewall-cmd --permanent --new-zone=management
    sudo firewall-cmd --permanent --zone=management --add-service=ssh
    sudo firewall-cmd --permanent --zone=management --add-source=192.168.10.0/24
    sudo firewall-cmd --permanent --zone=public --change-interface=eth0
    sudo firewall-cmd --permanent --zone=management --change-interface=eth1
    sudo firewall-cmd --reload
    sudo firewall-cmd --get-active-zones

    이 구성이 좋은 이유는 규칙을 줄여주기 때문입니다. 외부 서비스망 eth0는 공개 포트만 남기고, 내부 관리망 eth1은 SSH, 백업, 모니터링처럼 성격이 다른 트래픽을 따로 받습니다. 운영하면서 예외가 생기더라도 public을 더럽히지 않고 management 쪽에서 해결할 수 있습니다.

    다만 언제나 Zone 분리가 정답은 아닙니다. NIC가 하나뿐이고 관리도 VPN 하나로만 들어온다면, 억지로 존을 많이 만드는 것보다 public 하나에 출발지 제한 Rich Rule을 두는 편이 단순하고 덜 위험합니다. 정책은 강한 것보다 읽기 쉬운 것이 오래 갑니다. 제가 권하는 기준은 이렇습니다. 인터페이스가 다르면 Zone 분리, 인터페이스는 같고 출발지만 다르면 Rich Rule 우선. 이게 대부분의 중소형 서버에서 가장 덜 꼬입니다.

    한 가지 더 실무 팁을 드리면, NetworkManager가 인터페이스-존 매핑을 함께 관리하는 환경에서는 firewalld 쪽 변경과 네트워크 연결 프로필 설정이 엇갈리지 않게 맞춰야 합니다. 그래서 저는 관리망이 명확한 환경에서는 “관리 NIC는 management로 고정, VPN이나 사내 대역은 source로 보강” 정도까지만 씁니다. 규칙 해석이 복잡해지는 순간, 보안 자체보다 운영 오류가 더 큰 리스크가 됩니다.

    Linux 방화벽 설정에서 firewalld 존 분리를 보여주는 서버 보안 강화 이미지

    외부 서비스망과 내부 관리망을 분리한 듀얼 NIC 환경의 Zone 설계를 이해하기 위한 다이어그램입니다.

    6. 실제로 많이 꼬이는 포인트와 트러블슈팅

    이 섹션은 공식 문서보다 운영에서 더 많이 필요합니다. 명령 자체는 맞는데 결과가 기대와 다를 때, 대개 원인은 문법보다 가정이 틀린 데 있습니다. 제가 실제로 자주 본 실패 모드만 추렸습니다. 이 부분을 미리 알고 들어가면 대응 속도가 확실히 빨라집니다.

    6-1. 재부팅 후 규칙이 사라진다

    대부분 원인은 단순합니다. 런타임에만 넣고 영구 반영을 안 했거나, 반대로 런타임과 영구 상태가 이미 달라져 있었는데 작업자가 둘을 같은 것으로 착각한 경우입니다. 근본 원인은 “테스트 상태와 배포 상태를 구분하지 않는 습관”입니다.

    sudo firewall-cmd --list-all
    sudo firewall-cmd --permanent --zone=public --list-all
    sudo firewall-cmd --runtime-to-permanent
    sudo firewall-cmd --reload

    --runtime-to-permanent는 편하지만 무심코 쓰면 임시로 열어둔 포트까지 저장해버릴 수 있습니다. 그래서 저는 이 명령을 “좋은 상태가 이미 런타임에 올라와 있다는 걸 확인한 뒤”에만 씁니다. 확신이 없으면 아예 영구 설정에 다시 명시하는 편이 낫습니다.

    6-2. SSH 접속이 갑자기 끊긴다

    이건 규칙 문법 문제보다 작업 순서 문제입니다. 보통은 --remove-service=ssh를 너무 일찍 적용했거나, 허용해야 할 출발지 IP를 잘못 넣었거나, 현재 접속 세션이 예상과 다른 경로를 타고 있었던 경우입니다. 예를 들어 평소엔 VPN으로 들어오지만 오늘은 외부 회선에서 접속한 상태라면, 머릿속 관리 IP와 실제 출발지 IP가 다를 수 있습니다.

    • 현재 SSH 세션을 끊지 말고, 새 터미널에서 재접속 테스트를 먼저 합니다.
    • 허용 규칙 추가 후 --remove-service=ssh를 마지막에 실행합니다.
    • 클라우드 서버면 웹 콘솔이나 시리얼 콘솔 경로를 먼저 확보합니다.
    • who, ss -tnp | grep :22 같은 보조 확인으로 현재 세션 상태를 같이 봅니다.

    6-3. 애플리케이션은 떠 있는데 외부 접속이 안 된다

    이럴 때 방화벽만 붙잡고 있으면 오래 갑니다. 실제 원인은 앱이 127.0.0.1에만 바인딩되어 있거나, 컨테이너 프록시와 호스트 포트가 다르거나, 다른 존에 인터페이스가 붙어 있어서 예상한 정책을 안 타는 경우가 많습니다. 즉, 근본 원인은 “포트 개방”과 “서비스 바인딩”을 같은 것으로 보는 데 있습니다.

    sudo ss -tulpn
    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-ports
    sudo firewall-cmd --zone=public --list-services

    읽는 기준은 이렇습니다. ss에서 앱이 실제 외부 IP 또는 0.0.0.0에 바인딩되어 있어야 하고, 해당 포트가 맞는 존에서 허용되어 있어야 합니다. 둘 중 하나라도 빠지면 접속이 안 됩니다. 간단해 보이는데, 장애 대응에서는 이 두 층을 분리해서 보지 않아서 시간이 오래 가더라고요.

    6-4. 규칙은 맞는 것 같은데 이상하게 다른 트래픽도 통과한다

    이건 종종 방화벽이 아니라 경로 문제입니다. 예를 들어 애플리케이션이 같은 호스트의 로컬 프록시를 통해 우회하고 있거나, 컨테이너 네트워크 규칙이 별도로 적용되거나, 클라우드 보안 그룹이 firewalld보다 앞단에서 이미 허용하거나 차단하고 있을 수 있습니다. firewalld는 유일한 경계가 아닙니다. 제가 운영 점검할 때 항상 같이 묻는 질문이 “이 트래픽이 진짜 호스트 ingress 경로를 타는가?”입니다.

    7. firewalld 보안 체크리스트의 검증 방법

    보안 설정은 명령 성공으로 끝나면 안 됩니다. 핵심은 “허용된 대상에게만, 필요한 포트만, 예상한 존을 통해 열려 있는가”입니다. 저는 검증을 네 층으로 나눕니다. 방화벽 상태, 프로세스 리슨 상태, 출발지별 접속 결과, 운영 문서와의 일치 여부입니다. 이 네 개가 맞아야 비로소 설정이 끝난 겁니다.

    1. 서버 내부에서 활성 Zone과 허용 목록을 확인합니다.
    2. 프로세스가 실제 어떤 주소/포트에 리슨하는지 확인합니다.
    3. 허용된 네트워크와 비허용 네트워크에서 각각 접속 테스트를 합니다.
    4. 문서화된 서비스 목록과 실제 오픈 포트가 일치하는지 봅니다.
    sudo firewall-cmd --get-active-zones
    sudo firewall-cmd --zone=public --list-all
    sudo firewall-cmd --zone=management --list-all
    sudo ss -tulpn

    외부 테스트는 같은 서버 안이 아니라 관리 PC, 점프 호스트, VPN 외부 클라이언트처럼 서로 다른 출발지에서 해보는 게 좋습니다. 예를 들어 HTTPS는 어디서나 접속되어야 하지만, SSH는 허용된 관리 IP에서만 붙어야 합니다. 허용되지 않은 네트워크에서 SSH가 붙는다면 Rich Rule이나 Zone 매핑이 기대와 다르게 동작하는 겁니다. 반대로 HTTPS가 내부에서만 되고 외부에서 안 되면 firewalld보다 앞단 LB, 보안 그룹, 라우팅까지 같이 봐야 합니다.

    검증 결과 해석 기준도 애매하게 두지 않는 편이 좋습니다.

    • list-all에 문서화하지 않은 서비스나 포트가 보이면 정리 대상입니다.
    • public Zone에 SSH, Cockpit, DB 포트가 남아 있으면 노출 과다로 봅니다.
    • ss -tulpn에서 외부 바인딩된 포트가 의도보다 많으면 앱 설정부터 줄여야 합니다.
    • 허용 대상이 아닌 출발지에서도 접속되면 Zone 매핑, Rich Rule, 앞단 네트워크 정책을 다시 확인합니다.
    • 재부팅 후 결과가 바뀌면 영구 설정 관리에 구멍이 있는 겁니다.

    실무에서는 “포트가 열렸는가”보다 “왜 열려 있는가”를 물어야 합니다. 이 질문이 빠지면 방화벽은 언젠가 예외 규칙 창고가 됩니다. 관련 글로 SELinux, SSH 하드닝, Nginx TLS 점검 가이드도 내부 링크로 함께 묶어두면 검색 유입과 체류 시간 관리에 꽤 도움이 됩니다.

    firewalld 보안 체크리스트 검증 결과를 보여주는 서버 보안 강화 이미지

    허용된 서비스만 남고 관리 포트는 제한된 상태를 검증하는 결과 중심의 대시보드/터미널 이미지입니다.

    8. 운영하면서 유지보수할 때 체크할 항목

    방화벽은 한 번 잠갔다고 끝나는 장치가 아닙니다. 서비스가 늘고 담당자가 바뀌고, 장애 대응 중 임시 예외가 생기면 규칙은 반드시 불어납니다. 그래서 저는 월간 점검이나 배포 체크리스트에 firewalld 보안 체크리스트 항목을 따로 넣습니다. 보안을 강화하는 가장 싼 방법은 새로운 솔루션을 들이는 게 아니라, 이미 열린 예외를 줄이는 겁니다.

    • 사용하지 않는 서비스 제거: 예전에 열었던 포트가 아직 실제 트래픽을 받는지 확인합니다.
    • 출발지 제한 재검토: 퇴역한 사무실 IP, 종료된 VPN 대역이 남아 있지 않은지 봅니다.
    • Zone-인터페이스 매핑 확인: NIC 추가, 이름 변경, 가상 인터페이스 생성 후 매핑이 흐트러지지 않았는지 확인합니다.
    • 앱 배포 문서 동기화: 애플리케이션 포트 변경이 방화벽 정책과 함께 업데이트됐는지 맞춰봅니다.
    • reload 후 재검증: 설정 반영 뒤 실제 접속 테스트까지 끝내야 점검 완료로 봅니다.
    • 커스텀 서비스 정리: /etc/firewalld/services/ 아래 정의가 현재 운영과 맞는지 점검합니다.

    작은 팀일수록 문서화가 귀찮게 느껴질 수 있는데, 방화벽은 문서화하지 않으면 팀이 바뀌는 순간 바로 리스크가 됩니다. 특히 포트를 숫자로만 열어둔 환경은 인수인계 비용이 큽니다. 저는 장기 운영 서비스라면 “서비스명, 포트, 출발지, 이유” 네 가지는 최소한 남겨두는 편이 좋다고 봅니다. 이거 해두면 나중에 진짜 편합니다.

    운영 중 반복 점검해야 할 항목을 한 장으로 압축한 요약 인포그래픽 이미지입니다.

    9. 자주 묻는 질문과 바로 적용할 추천안

    9-1. SSH는 아예 닫는 게 맞을까요?

    콘솔 대체 수단이 안정적으로 있고, 배포 자동화와 장애 복구 루틴이 충분히 성숙했다면 가능합니다. 하지만 대부분의 서버 운영에서는 완전 차단보다 출발지 제한이 더 현실적입니다. 보안과 복구 시간을 같이 봐야 하니까요. 제 추천은 단순합니다. 인터넷 전체 공개는 피하고, 고정 IP 또는 VPN 대역으로 묶으세요.

    9-2. 서비스 추가와 포트 추가 중 뭐가 더 좋나요?

    표준 포트면 서비스가 낫고, 커스텀 앱이면 포트가 빠릅니다. 다만 커스텀 포트가 장기 운영 대상이라면 서비스 XML로 승격하거나 최소한 운영 문서에 의미를 남기세요. “지금 빨리 열기”와 “나중에 이해 가능하기”는 다른 문제입니다.

    9-3. 서버 보안 강화를 시작하는 최소 기준은?

    제가 운영 서버에서 최소선으로 잡는 기준은 이렇습니다. 외부 공개는 HTTPS 중심, SSH는 특정 출발지 제한, 관리망은 공개망과 분리, 불필요한 기본 서비스 제거, reload 후 외부 검증. 이 다섯 개만 지켜도 방화벽 상태는 훨씬 예측 가능해집니다.

    환경별로 추천을 딱 정리해보면 이렇습니다. 단일 NIC 소형 서버라면 public Zone 하나 + HTTPS 공개 + SSH Rich Rule 제한 조합이 가장 실용적입니다. 규칙이 단순하고 운영자가 바뀌어도 이해가 쉽습니다. 반대로 NIC가 둘 이상이거나 관리망이 분리된 서버라면 Zone 분리를 바로 적용하는 편이 낫습니다. public에는 서비스 트래픽만, management에는 관리 트래픽만 남기세요. 이 구조가 예외 규칙 누적을 가장 잘 막아줍니다.

    한 문장으로 요약하면, firewalld 보안 체크리스트의 핵심은 “포트를 여는 기술”이 아니라 “열린 이유를 끝까지 설명할 수 있는 상태를 유지하는 것”입니다. 저는 방화벽을 잘 짠 서버가 보안적으로만 좋은 게 아니라 운영도 덜 아프다고 봅니다. 필요한 것만 열고, 경계를 분리하고, 재부팅 후에도 같은 결과가 나오게 만들고, 마지막엔 실제 접속으로 확인하세요. 이 네 가지를 습관으로 만들면 리눅스 서버는 눈에 띄게 단단해집니다.

  • [Linux] Debian Snap 비교: APT와 Snap 장단점 분석

    [Linux] Debian Snap 비교: APT와 Snap 장단점 분석

    Debian Snap 비교: Debian 환경에서 Snap 사용, 장점과 단점 분석

    Debian Snap 비교를 찾는 분들은 대개 비슷한 고민을 합니다. 시스템은 Debian답게 안정적으로 굴리고 싶은데, 필요한 앱은 APT 저장소 버전이 느리거나 설치 가이드가 Snap 기준으로 적혀 있는 경우가 있거든요. 그래서 결론도 단순하지 않습니다. Snap은 Debian에서 못 쓰는 선택지가 아니라, 운영 방식이 달라지는 선택지에 더 가깝습니다.

    중요한 건 설치 성공 여부보다 그다음입니다. 업데이트를 누가 주도하는지, 장애가 났을 때 어디부터 봐야 하는지, 패키지 하나 추가했을 때 디스크와 서비스 구조가 어떻게 바뀌는지 같은 부분이 운영 체감에 더 크게 남더라고요. 이 글에서는 공식 문서와 실제 운영 관점 기준으로, APT 중심 Debian에 Snap을 얹었을 때 달라지는 지점만 추려서 정리해보겠습니다.

    Debian Snap 비교를 설명하는 APT와 Snap 동작 구조 개요 이미지

    Debian 기본 패키지 관리와 Snap 런타임이 함께 동작하는 전체 구조를 보여주는 개요 이미지입니다.

    왜 Debian Snap 비교가 필요한가

    APT와 Snap의 차이는 패키지 형식 정도로 끝나지 않습니다. APT는 Debian 시스템 전체의 일관성을 전제로 움직입니다. 라이브러리를 공유하고, 관리자가 원하는 시점에 업데이트를 묶어서 적용하고, 서비스 구성도 전통적인 리눅스 흐름과 잘 맞습니다. 반면 Snap은 애플리케이션 단위로 배포와 갱신을 분리하는 방식에 가깝습니다.

    이 구조가 빛나는 순간은 분명 있습니다. 특정 GUI 도구나 개발자 유틸리티를 빨리 검증해야 할 때는 저장소 추가와 충돌 검토 부담을 줄이고 바로 테스트에 들어가기 좋거든요. 반대로 서버 운영에서는 얘기가 달라집니다. 패키지 하나를 설치했는데 <code>snapd 런타임, 자동 갱신 정책, 별도 마운트, 권한 인터페이스까지 함께 관리해야 해서 생각보다 손이 갑니다.

    Snap 패키지 관리란 무엇인가

    Debian에서 Snap을 쓰는 흐름은 보통 이렇습니다. 먼저 apt로 snapd를 설치하고, 이후 애플리케이션 설치와 갱신, 채널 선택, 연결 상태 확인은 snap 명령으로 처리합니다. 즉, Debian에 Snap을 도입한다는 건 APT 위에 별도 배포 계층을 하나 더 올리는 일입니다.

    • APT: 시스템 패키지와 서비스 운영의 기본 축입니다.
    • snapd: Snap 설치, 마운트, 갱신, 인터페이스 연결을 담당하는 데몬입니다.
    • Confinement: 앱이 파일, 장치, 소켓 등에 접근하는 범위를 제한하는 모델입니다.
    • Channel: stable, candidate, beta, edge처럼 배포 리듬을 선택하는 축입니다.

    실무적으로는 정의보다 판단 기준이 더 중요합니다. APT는 시스템에 자연스럽게 녹아드는지 보면 되고, Snap은 이 앱을 시스템과 어느 정도 분리해서 다뤄도 되는지 보면 됩니다. 저는 보통 이 질문 하나로 정리합니다. 운영체제 일부처럼 다룰 패키지인가, 독립 앱처럼 다룰 패키지인가. 전자면 APT, 후자면 Snap 쪽이 더 잘 맞습니다.

    Debian Snap 비교: APT와 뭐가 다를까

    아래 표는 기능 자체보다 운영 영향 중심으로 정리했습니다. 현장에서는 이 차이가 더 크게 느껴집니다.

    항목 APT Snap
    패키지 출처 Debian 저장소, 서드파티 APT 저장소 Snap Store 중심
    업데이트 주도권 관리자가 apt update, apt upgrade 시점을 제어 기본값은 자동 갱신, 필요 시 hold 또는 timer 정책 조정
    의존성 모델 시스템 라이브러리 공유 앱별 번들 또는 base snap 의존
    장애 분석 시작점 apt, dpkg, systemd 로그 snap changes, snap tasks, sudo snap logs, journalctl -u snapd
    디스크 사용 경향 중복이 적어 효율적 리비전 보관과 번들 구조로 커지기 쉬움
    권한 제어 방식 전통적 유닉스 권한과 서비스 계정 중심 인터페이스 연결과 confinement 정책 확인 필요
    서비스 배치감 시스템 패키지와 동일 흐름 별도 마운트와 snap 서비스 단위가 추가됨
    적합한 용도 장기 운영 서버, 일관성 중시 환경 최신 앱 확보, 빠른 검증, 데스크톱 도구

    Debian Snap 비교에서 가장 크게 봐야 할 건 속도보다 통제력입니다. Debian은 원래 패키지 흐름이 예측 가능한 편입니다. 반면 Snap은 최신성 확보가 쉬운 대신, 관리자가 구조를 모르면 왜 이 시점에 갱신됐는지, 왜 설치는 됐는데 장치 접근은 막히는지 같은 질문이 생기기 쉽습니다.

    실전 구현: Debian에 Snap 설치하고 기본 동작 확인하기

    설치 자체는 어렵지 않습니다. 다만 공식 문서 기준으로는 설치 후 경로 반영을 위해 로그아웃 후 다시 로그인하거나 시스템을 재시작하는 단계를 꼭 염두에 두는 게 좋습니다. 이거 빼먹으면 설치는 됐는데 명령이 안 잡혀서 괜히 헷갈리더라고요.

    1. snapd 설치: Debian에 Snap 데몬을 올립니다.
    2. 서비스 상태 확인: 런타임이 정상인지 먼저 봅니다.
    3. 경로 반영 확인: /snap/bin이 PATH에 들어왔는지 확인합니다.
    4. 테스트 Snap 실행: 설치뿐 아니라 실행까지 검증합니다.
    sudo apt update
    sudo apt install -y snapd
    sudo systemctl enable --now snapd.socket
    systemctl status snapd.socket --no-pager
    snap version

    여기서 systemctl status snapd.socket는 최소한 Loaded와 Active를 확인하는 용도로 쓰면 좋습니다. 다만 설치 직후 명령 경로가 바로 반영되지 않을 수 있으니, 세션을 다시 열거나 재부팅한 뒤 테스트하는 편이 더 깔끔합니다. 필요하면 공식 권장 흐름처럼 최신 기능 확보를 위해 아래처럼 snapd snap 자체를 한 번 더 설치하는 방법도 있습니다.

    sudo snap install snapd

    경로 반영은 이렇게 확인하면 됩니다.

    echo "$PATH"
    ls -ld /snap || true
    ls /snap/bin || true
    command -v snap
    command -v hello-world || true

    그다음은 테스트 패키지입니다.

    sudo snap install hello-world
    snap list
    snap run hello-world

    중요한 건 snap list에 이름이 보이는지만 보는 게 아니라, 실제 실행까지 확인하는 겁니다. 설치 메타데이터는 생겼는데 실행 경로나 마운트 단계에서 막히는 경우가 있어서요. 이 단계까지 통과해야 최소 검증이 끝났다고 봐도 무난합니다.

    Debian Snap 비교 실습을 위한 snapd 설치와 검증 터미널 이미지

    Debian 터미널에서 snapd 설치, 서비스 활성화, 테스트 패키지 검증을 수행하는 흐름을 보여주는 이미지입니다.

    조금 더 깊게: 채널, 서비스, 연결 상태 확인

    Snap을 한두 번만 쓸 게 아니라면 install보다 info, services, connections, changes를 익혀두는 편이 훨씬 낫습니다. 설치는 대부분 됩니다. 문제는 그다음에 이상 증상을 어떻게 읽느냐입니다.

    snap info hello-world
    snap services
    snap connections hello-world
    snap changes
    snap refresh --time
    • snap info: 현재 어떤 채널을 추적하는지 확인합니다.
    • snap services: 서비스형 Snap이 어떤 상태인지 봅니다.
    • snap connections: 파일, 네트워크, 장치 접근에 필요한 인터페이스 연결 상태를 점검합니다.
    • snap changes: 설치나 갱신 작업이 중간에 멈췄는지 추적합니다.
    • snap refresh --time: 자동 갱신 주기를 확인합니다.

    여기서 많이 하는 오해가 하나 있습니다. 설치가 성공했으니 실행 문제는 앱 버그라고 단정하는 경우죠. 실제로는 기능 일부만 안 되면 인터페이스 연결부터 의심하는 쪽이 훨씬 빠르고, 설치나 갱신이 걸려 있으면 snap changes와 snap tasks를 먼저 보는 게 맞습니다.

    장점: 언제 Snap이 꽤 쓸 만하냐

    Snap의 장점은 단순히 최신 앱을 빨리 설치할 수 있다는 데서 끝나지 않습니다. 더 중요한 건 배포판 수명주기와 애플리케이션 수명주기를 분리해준다는 점입니다. Debian이 보수적으로 움직여도 특정 앱만 별도 리듬으로 가져갈 수 있다는 뜻이죠. 이거, 데스크톱이나 검증 환경에서는 진짜 편할 때가 있습니다.

    • 최신 버전 확보가 빠릅니다: 배포판 릴리스 주기와 무관하게 앱 공급자가 업데이트를 밀어줄 수 있습니다.
    • 실험 비용이 낮습니다: 저장소 우선순위와 라이브러리 충돌 부담을 줄이고 테스트하기 좋습니다.
    • 앱 단위 회수가 단순합니다: 검증 후 제거 흐름이 비교적 명확합니다.
    • 권한을 구조적으로 나눠 보기 쉽습니다: 모든 보안 문제를 해결하진 않지만, 접근 범위를 한 번 더 점검하게 만듭니다.

    특히 데스크톱 도구, 개발자 유틸리티, 빠른 검증이 필요한 앱에서는 효율이 좋습니다. 반대로 시스템 깊숙이 붙는 핵심 서비스는 Snap의 장점이 생각보다 줄어듭니다. 설치가 쉬운 것과 운영이 쉬운 건 다른 얘기라서요.

    단점: Debian에서 더 크게 느껴지는 지점

    Snap 단점은 Debian에서 더 선명하게 드러나는 편입니다. Ubuntu 계열에서는 비교적 자연스러운 선택지로 보일 수 있지만, Debian에서는 원래 질서 위에 별도 런타임을 추가하는 셈이라 이질감이 있습니다.

    • 업데이트 통제력이 약해질 수 있습니다: 기본값이 자동 갱신이라 운영 창구를 별도로 설계해야 합니다.
    • 디스크 사용이 커지기 쉽습니다: 이전 리비전 보관, base snap, 번들 구조가 겹칩니다.
    • 실행 체감이 일정하지 않을 수 있습니다: 첫 실행이나 초기 마운트 구간에서 전통 패키지와 감각이 다릅니다.
    • 디버깅 동선이 늘어납니다: APT와 systemd만 보던 흐름에서 snapd와 인터페이스 상태까지 확인해야 합니다.
    • 시스템 일관성이 흐려질 수 있습니다: Debian 특유의 단순한 운영 감각을 중시하면 거슬릴 수 있습니다.

    서버에서 특히 보수적으로 보게 되는 이유도 여기에 있습니다. 문제를 빨리 푸는 사람에게는 도구 하나 더 늘어나는 일이 감당 가능한 수준일 수 있습니다. 하지만 장기 운영은 결국 문제가 적게 생기는 구조가 더 강합니다.

    Debian Snap 비교에서 장단점을 보여주는 APT와 Snap 비교 이미지

    APT와 Snap의 업데이트, 격리, 디스크 사용, 운영 편의성을 비교하는 시각 자료입니다.

    운영 관점에서 꼭 점검할 설정

    Snap을 Debian에 올렸다면 설치보다 먼저 갱신 정책과 보관 정책을 확인하는 편이 좋습니다. 특히 서버나 장시간 켜 두는 워크스테이션이라면 기본 자동 갱신을 그대로 둘지 의식적으로 결정해야 합니다.

    snap refresh --time
    sudo snap refresh --hold=24h
    sudo snap set system refresh.timer=mon,02:00-04:00
    sudo snap set system refresh.retain=2
    sudo snap get system refresh.timer
    sudo snap get system refresh.retain

    데스크톱은 기본 자동 갱신을 크게 건드리지 않고, 업무 시간 충돌이 싫을 때 refresh.timer만 조정해도 충분한 경우가 많습니다. 서버는 아예 방치하기보다 refresh.timer로 창을 좁히거나, 검증 전까지 snap refresh --hold를 걸어두는 편이 더 낫습니다. 중요한 건 Snap을 쓰면서도 운영 창구를 직접 만든다는 감각입니다.

    디스크 측면에서는 refresh.retain도 꽤 중요합니다. Snap은 이전 리비전을 남겨 롤백 가능성을 확보하는 대신, 오래 두면 공간을 제법 먹습니다. 저장공간이 빠듯한 VM에서는 초기에 이 값부터 점검하는 게 편하더라고요.

    트러블슈팅: Debian에서 자주 막히는 포인트

    이 섹션은 증상보다 원인 축 위주로 보시면 좋습니다. Debian에서 Snap이 막히는 경우는 대체로 런타임 준비 부족, PATH 문제, 자동 갱신 충돌, 인터페이스 미연결 쪽으로 정리됩니다.

    1. classic confinement 패키지가 설치되지 않는 경우

    일부 Snap은 --classic이 필요합니다. 이때 일부 배포판이나 환경에서는 classic Snap이 /snap 경로를 기대하기 때문에, 해당 경로가 없으면 설치가 막힐 수 있습니다. Debian에서도 환경에 따라 확인할 가치가 있습니다.

    ls -ld /snap
    sudo ln -s /var/lib/snapd/snap /snap
    sudo snap install <package-name> --classic

    다만 이건 항상 필요한 고정 절차는 아닙니다. 먼저 /snap 경로가 이미 있는지 확인하고, classic 설치 오류가 실제로 발생할 때만 적용하는 편이 안전합니다.

    2. 명령은 설치됐는데 실행 파일을 못 찾는 경우

    이건 대개 PATH 반영 문제입니다. 특히 기존 셸 세션을 계속 쓰는 경우 자주 보입니다. 원인은 단순합니다. 링크는 생겼는데 현재 세션의 환경 변수에 /snap/bin이 반영되지 않은 겁니다.

    echo "$PATH"
    ls /snap/bin
    command -v hello-world
    snap run hello-world

    ls /snap/bin에 실행 링크가 있는데 command -v가 비면 PATH 문제일 가능성이 큽니다. 이 경우는 재로그인이나 셸 재시작으로 정리되는 일이 많습니다. 반대로 snap run도 실패하면 경로가 아니라 런타임이나 권한, 마운트 쪽을 봐야 합니다.

    3. 설치나 업데이트가 중간에 걸려 있는 경우

    이럴 때는 감으로 넘기지 말고 작업 이력을 따라가면 됩니다. 원인은 보통 네트워크 문제, 이전 작업 미완료, 마운트 실패, 데몬 상태 이상 쪽입니다.

    snap changes
    snap tasks <change-id>
    journalctl -u snapd --no-pager -n 100
    sudo snap logs snapd -n=50

    snap changes에서 오래 머무는 작업을 찾고, 해당 change-id를 snap tasks로 내려가면 어느 단계에서 멈췄는지 보입니다. 로그는 그냥 읽기보다 store, mount, interface, timeout 같은 키워드로 먼저 분류하면 훨씬 빨리 감이 잡힙니다.

    4. 앱은 실행되는데 일부 기능만 안 되는 경우

    이 경우는 거의 항상 인터페이스나 confinement 쪽입니다. 파일 열기, USB 장치 접근, 특정 시스템 리소스 접근이 안 되는 식으로 보일 때가 많습니다. 얼핏 보면 앱 버그 같지만, 실제로는 필요 권한이 자동 연결되지 않았거나 strict 제약 안에서 막힌 경우가 많습니다.

    snap connections <package-name>
    snap interfaces
    snap info <package-name>

    이 증상이 보이면 재설치부터 반복하기보다 권한 모델을 먼저 확인하는 편이 훨씬 낫습니다. 설치 성공 여부와 기능 완전성은 별개거든요.

    검증: 설치가 끝난 뒤 무엇을 확인해야 하나

    운영 환경에서는 한 번 실행된 걸로 끝내면 부족합니다. 적어도 아래 네 줄기는 보고 넘어가는 편이 안전합니다.

    1. 런타임 준비: systemctl status snapd.socket, 필요 시 snap version
    2. 설치 결과: snap list로 패키지, 채널, 리비전 확인
    3. 업데이트 흐름: snap refresh --time, snap changes
    4. 권한과 실행 경로: snap connections <패키지명>, command -v <명령어>

    합격 기준도 꽤 단순합니다. 설치가 완료됐는지, CLI 또는 GUI가 실제로 실행되는지, 필요 기능이 권한 제약 없이 동작하는지, 재부팅 뒤에도 같은 상태가 유지되는지. 이 네 가지 중 하나라도 흔들리면, 특히 서버에서는 Snap 유지 여부를 다시 보는 편이 낫습니다.

    Debian Snap 비교 후 설치 결과를 검증하는 운영 점검 대시보드 이미지

    설치 후 패키지 목록, 서비스 상태, 변경 이력을 검증하는 운영 점검 화면 이미지입니다.

    어떤 경우에 Snap을 선택하면 좋을까

    여기서는 애매하게 말하지 않겠습니다. Debian Snap 비교의 결론은 의외로 분명합니다.

    상황 추천 이유
    Debian 데스크톱에서 최신 앱이 급함 Snap 우선 검토 배포판 주기와 분리된 최신 버전 확보가 빠름
    짧은 검증용 도구를 올렸다 내릴 예정 Snap 적합 저장소 추가와 충돌 검토 부담이 적음
    장기 운영 서버에서 변경 통제가 최우선 APT 우선 업데이트 타이밍과 시스템 일관성을 잡기 쉬움
    디스크 효율과 단순한 장애 분석이 중요 APT 우선 중복 보관과 별도 런타임 관리 부담이 적음
    앱 단위 격리와 독립 배포가 필요 Snap 후보 confinement와 채널 전략을 함께 가져가기 좋음

    한 줄로 정리하면 이렇습니다. 시스템 일부처럼 다뤄야 하는 패키지는 APT, 독립 앱처럼 다뤄도 되는 패키지는 Snap. 이 기준이 있어야 나중에 운영 기록을 봐도 왜 그 패키지를 Snap으로 골랐는지 설명이 됩니다.

    마무리: 제 추천은 이렇게 정리됩니다

    Debian에 Snap을 붙이는 건 충분히 현실적인 선택입니다. 다만 기본값으로 넓게 쓰기보다는, 필요한 앱에 한해 선택적으로 도입하는 편이 더 안정적입니다. Debian의 장점은 원래 패키지 관리가 조용하고 예측 가능하다는 데 있는데, Snap을 넓게 쓰기 시작하면 최신성은 얻는 대신 그 흐름이 조금 흐려지거든요.

    추천은 명확합니다. 최신 데스크톱 앱, 개발 도구, 빠른 실험이 목적이면 Snap이 꽤 유용합니다. 대신 장기 운영 서버, 작은 디스크, 강한 변경 통제, 단순한 장애 분석이 중요하면 APT를 기본 축으로 두는 편이 낫습니다. 관련해서 패키징 비교 글이 더 필요하시면, 다음에는 Debian 기준 Flatpak과 Snap 비교도 함께 읽어보시면 흐름이 더 잘 잡힙니다.

    Debian Snap 비교를 바탕으로 APT 우선 Snap 보조 전략을 요약한 이미지

    Debian 운영에서 APT를 기본으로 두고 Snap을 보조적으로 선택하는 권장 전략을 요약한 이미지입니다.

  • [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] sudo 권한 관리 베스트 프랙티스: 최소 권한 원칙으로 Linux 보안 강화

    [Linux] sudo 권한 관리 베스트 프랙티스: 최소 권한 원칙으로 Linux 보안 강화

    sudo 권한 관리 베스트 프랙티스: 최소 권한 원칙으로 Linux 보안 강화

    운영 서버를 오래 보다 보면 sudo 권한 관리는 늘 뒤늦게 문제를 일으키더라고요. 패치나 방화벽은 눈에 잘 띄는데, sudo는 장애가 없으면 계속 방치되기 쉽거든요. 그런데 실제 사고는 여기서 자주 납니다. 제가 현장에서 가장 위험하다고 느낀 상태는 권한이 없어서 일이 막히는 서버가 아니라, 누가 어디까지 할 수 있는지 팀 누구도 정확히 모르는 서버였습니다.

    특히 배포 계정, 운영자 개인 계정, CI 계정이 한 서버에 섞이기 시작하면 권한이 기능 단위가 아니라 사람 사정대로 붙습니다. 그래서 저는 sudo를 단순한 편의 기능이 아니라 root 권한을 잘게 쪼개는 정책 엔진으로 봅니다. 이 관점으로 보면 문법 암기보다 더 중요한 게 보입니다. 어떤 명령은 열어도 되고 어떤 명령은 닫아야 하는지, 왜 예외가 쌓이는지, 로그를 어떻게 읽어야 실제 오남용을 잡는지가 핵심이거든요.

    sudo 권한 관리와 최소 권한 원칙 개요 이미지

    sudo 권한 관리와 최소 권한 원칙이 어떻게 연결되는지 보여주는 개요 이미지입니다.

    1. 왜 sudo 권한 관리가 Linux 보안 강화의 핵심인지

    sudo는 흔히 root 비밀번호를 공유하지 않게 해주는 도구로만 설명되지만, 실무에서는 그보다 훨씬 큰 의미가 있습니다. 운영팀 입장에서 sudo는 권한 위임의 경계면입니다. 이 경계가 흐려지면 기술적으로는 root를 공유하지 않아도 운영 방식은 사실상 root 공유와 비슷해집니다. 예를 들어 웹 운영 담당자에게 <code>ALL=(ALL) ALL을 준 뒤 “개인 계정으로 쓰니까 추적은 된다”라고 생각하면, 추적만 남고 제어는 거의 사라진 상태가 됩니다.

    현장에서 자주 본 실패 패턴도 비슷했습니다. 장애 대응 속도를 이유로 권한을 넓게 열고, 정상화 후 줄이자고 했는데 결국 안 줄어들더라고요. 이유는 단순합니다. 권한 회수는 장애 복구보다 덜 급하고, 누가 무슨 이유로 예외를 받았는지 기록이 없기 때문입니다. 그래서 sudo 문제의 근본 원인은 문법 미숙보다 권한을 임시로 열어도 된다고 여기는 운영 습관인 경우가 많았습니다.

    2. 최소 권한 원칙을 sudoers 설정에 적용할 때 보는 5가지 기준

    최소 권한 원칙은 그냥 “적게 주자”가 아닙니다. 운영이 멈추지 않으면서도 우회 경로까지 포함해 범위를 닫는 설계를 뜻합니다. sudoers 설정을 읽을 때 저는 아래 다섯 축으로 봅니다.

    • 주체: 개인 사용자에게 직접 줄지, 그룹이나 역할로 묶을지
    • 대상 계정: 무조건 root로 올릴지, 특정 서비스 계정으로 제한할지
    • 명령 범위: 전체 바이너리인지, 특정 서브커맨드와 인자까지 좁힐지
    • 실행 맥락: 비밀번호 요구, 환경 변수 전달, 작업 디렉터리 영향이 있는지
    • 검증 가능성: 나중에 누가 왜 그 명령을 실행했는지 로그와 리뷰로 설명 가능한지

    여기서 많이 놓치는 게 세 번째와 네 번째입니다. 예를 들어 nginx 재시작만 주려는 의도인데 실제 sudoers에는 systemctl 전체가 들어가 있으면, 사용자는 다른 유닛까지 건드릴 수 있습니다. 반대로 절대 경로 없이 systemctl restart nginx처럼 적으면 정책 검토가 흐려집니다. sudo 권한 관리에서 중요한 건 “대충 맞는 권한”이 아니라 운영자가 리뷰 가능한 권한입니다.

    3. sudo 권한 관리 체크리스트

    아래 순서로 보면 대부분의 운영 서버에서 문제 지점이 금방 드러납니다. 신규 서버 인수인계, 보안 점검, 장애 후 재정비 때 이 순서가 꽤 잘 먹히더라고요.

    1. 직접 root 로그인 금지와 개인 계정 사용 원칙이 지켜지는지 확인합니다.
    2. /etc/sudoers 본문이 비대해지지 않았는지, 역할별 파일이 /etc/sudoers.d로 분리돼 있는지 봅니다.
    3. 권한 부여 단위가 사람 이름이 아니라 역할 또는 그룹인지 확인합니다.
    4. ALL=(ALL) ALL, ALL=(root) ALL, 광범위한 NOPASSWD가 남아 있는지 찾습니다.
    5. 허용 명령이 절대 경로와 가능한 범위의 인자 제한까지 포함하는지 봅니다.
    6. vi, vim, less, man, python, perl, find, tar처럼 쉘 탈출 또는 임의 실행으로 이어질 수 있는 명령이 열려 있는지 확인합니다.
    7. 서비스 재시작 권한이 필요할 뿐인데 systemctl 전체를 준 식의 과도한 추상화가 있는지 봅니다.
    8. sudo -l로 대상 사용자 기준의 실제 해석 결과를 확인합니다. 가능하면 관리자 계정에서 sudo -l -U 사용자명 또는 해당 사용자로 직접 로그인해 함께 봅니다.
    9. 로그가 남는지뿐 아니라, 실패 로그와 거부 로그를 누가 주기적으로 읽는지 확인합니다.
    10. 퇴사자, 역할 변경자, 오래된 자동화 계정 권한을 회수하는 절차가 있는지 봅니다.

    이 체크리스트의 핵심은 “설정이 있는가”가 아니라 “권한이 시간이 지나도 통제 가능한가”입니다. sudo는 한 번 열고 끝나는 설정이 아니라, 조직 변화에 따라 계속 부패하는 데이터라고 보는 편이 현실적입니다.

    4. 실전 구현 1: 현재 sudo 권한부터 인벤토리하기

    설정을 고치기 전에 먼저 현황을 뽑아야 합니다. 의외로 많은 팀이 이 단계를 건너뛰고 바로 정책부터 만지는데, 그러면 예외 권한이 왜 생겼는지 맥락을 놓치기 쉽습니다. 그래서 저는 사용자, 그룹, 설정 파일, 실제 해석 결과를 같이 봅니다.

    4-1. 사용자별 sudo 권한 확인

    # 주요 운영 계정의 실제 허용 권한 확인
    sudo -l -U alice
    sudo -l -U deploy
    sudo -l -U gitlab-runner
    
    # 배포판별 관리자 그룹 확인
    getent group sudo
    getent group wheel
    
    # 현재 로그인 사용자의 그룹 포함 관계 확인
    id alice
    id deploy

    여기서 저는 출력 자체보다 출력 패턴을 봅니다. ALL이 반복되면 과권한 가능성이 높고, 사용자별 직접 부여 규칙이 여러 줄 섞여 있으면 운영 이력이 누적된 상태일 가능성이 큽니다. 특히 원래는 nginx 재시작만 필요했던 계정에서 /bin/bash, /usr/bin/su, /usr/bin/vim, /usr/bin/python3 같은 명령이 보이면 바로 재검토 대상입니다.

    왜 이런 명령이 위험하냐면, 문제는 바이너리 이름이 아니라 그 안에서 추가 명령 실행이나 파일 접근 확장이 가능하다는 점입니다. 예를 들어 편집기나 페이저는 쉘 호출이 가능할 수 있고, 인터프리터는 임의 파일 읽기나 명령 실행으로 이어지기 쉽습니다. 그래서 sudo 권한 관리에서 “조회용 명령인데요”라는 말은 생각보다 안심 재료가 아닙니다.

    4-2. 기존 설정 파일 문법과 include 구조 검사

    # 메인 설정과 include된 파일까지 전체 문법 검사
    sudo visudo -c
    
    # 개별 파일 단위 문법 검사
    sudo visudo -cf /etc/sudoers
    sudo visudo -cf /etc/sudoers.d/ops-web
    
    # include 디렉터리 파일 목록과 권한 확인
    sudo ls -l /etc/sudoers.d/
    sudo stat /etc/sudoers /etc/sudoers.d/*

    실무에서 흔한 실패 모드는 문법 오류 그 자체보다 파일 분리 규칙이 엉킨 상태입니다. 같은 사용자가 여러 파일에서 다른 규칙을 받고 있는데 누구도 우선순위를 설명하지 못하는 경우가 많습니다. sudoers.d는 보통 사전식 정렬 순서로 읽히기 때문에 파일 이름 규칙도 중요합니다. 파일이 20개 넘고 이름 규칙이 제각각이면, 대체로 정책보다 예외가 더 많은 환경이더라고요.

    sudo 권한 관리용 sudoers.d 분리 구성 다이어그램

    /etc/sudoers.d 분리 구조와 역할 기반 권한 부여 흐름을 설명하는 이미지입니다.

    5. 실전 구현 2: sudoers 설정을 역할 기반으로 분리하기

    제 권장 방식은 개인 계정에 직접 권한을 붙이지 않고, 역할별 그룹을 먼저 만들고, 각 역할을 /etc/sudoers.d 파일 하나로 대응시키는 겁니다. 이렇게 해야 누가 빠지고 들어와도 정책이 흔들리지 않습니다. 웹 운영자가 nginx만 다룬다면 그 범위만 열어야지, 시스템 운영 전체를 열어두면 결국 나중에 문제가 납니다.

    5-1. 그룹 생성과 사용자 배치

    # 역할 그룹 생성
    sudo groupadd ops-web
    
    # 사용자 배치
    sudo usermod -aG ops-web alice
    sudo usermod -aG ops-web bob
    
    # 반영 확인
    id alice
    getent group ops-web

    여기서 중요한 건 그룹 이름을 업무 기준으로 짓는 겁니다. alice-admin 같은 식으로 사람 기준 이름을 만들면 나중에 그대로 기술 부채가 됩니다. 반면 ops-web, ops-db-readonly, deploy-api처럼 역할이 드러나면 리뷰와 회수가 쉬워집니다. 이거 실제로 인수인계할 때 진짜 편하더라고요.

    5-2. /etc/sudoers.d에 역할 파일 생성

    sudo visudo -f /etc/sudoers.d/ops-web

    파일 예시는 아래처럼 가져가면 됩니다. 단, systemctl 경로는 배포판마다 다를 수 있으니 아래 예시는 반드시 command -v systemctl 결과에 맞춰 바꿔 넣으세요.

    # /etc/sudoers.d/ops-web
    User_Alias OPS_WEB = %ops-web
    Runas_Alias WEB_RUNAS = root
    Cmnd_Alias WEB_CTL = /usr/bin/systemctl status nginx, /usr/bin/systemctl reload nginx, /usr/bin/systemctl restart nginx
    
    Defaults:OPS_WEB logfile=/var/log/sudo-ops-web.log
    Defaults:OPS_WEB env_reset
    
    OPS_WEB ALL=(WEB_RUNAS) WEB_CTL

    이 구성이 좋은 이유는 범위가 명확하기 때문입니다. User_Alias로 주체를 묶고, Runas_Alias로 누구 권한으로 실행하는지 닫고, Cmnd_Alias로 실제 허용 명령을 좁힙니다. 한 줄로 축약해서 쓸 수도 있지만, 역할이 커질수록 alias를 분리해두는 편이 리뷰와 diff 관리에 훨씬 유리합니다.

    여기서 많이 하는 실수도 비슷합니다. 세 줄 쓰기 귀찮다고 /usr/bin/systemctl 전체를 허용해버리는 거죠. 이건 관리 편의가 아니라 권한 포기입니다. 누군가 다른 유닛 조작이나 예상 밖의 서브커맨드를 실행할 수 있게 되면, 원래 의도와 완전히 달라집니다.

    5-3. 명령 경로 확인 후 반영

    command -v systemctl
    command -v nginx
    command -v journalctl
    readlink -f "$(command -v systemctl)"

    이 단계가 사소해 보여도 실제로 중요합니다. 배포판이나 패키징 방식에 따라 경로가 다를 수 있고, 심볼릭 링크를 타는 경우도 있기 때문입니다. sudoers는 쉘 별칭이나 PATH 검색이 아니라 정확히 매칭되는 경로와 인자를 기준으로 판단한다고 생각하시면 운영 사고를 꽤 줄일 수 있습니다.

    6. 어떤 권한 모델이 더 나은지 비교해보기

    권한 모델 언제 선택하나 장점 치명적인 약점 제 추천
    개인 계정 직접 부여 실험용 VM, 1~2명 단기 운영 설정이 빠름 사람이 바뀌면 정책 의미가 사라짐 장기 운영에는 비추천
    그룹 기반 역할 부여 대부분의 운영 서버 리뷰, 회수, 인수인계가 쉬움 초기 역할 설계가 필요함 기본 선택지로 권장
    광범위한 ALL=(ALL) ALL 긴급 복구 중 아주 짧은 예외 즉시 대응 가능 예외가 상시 권한으로 굳어지기 쉬움 만료 계획 없으면 쓰지 않음
    NOPASSWD + 제한 명령 CI/CD, 배치, 무인 자동화 자동화 안정성이 좋음 명령 범위를 넓게 잡으면 계정 탈취 시 피해가 큼 사람 계정보다 자동화 계정에 적합
    전용 래퍼 스크립트만 허용 인자 검증이 필요한 반복 작업 허용 범위를 가장 좁게 만들기 좋음 스크립트 품질이 낮으면 우회점이 생김 복잡한 운영 절차에는 가장 실용적

    제 판단 기준은 이렇습니다. 단순 명령 몇 개면 그룹 기반 + 절대 경로 명령 제한으로 충분합니다. 그런데 인자 검증이 중요하거나 사람이 실수하기 쉬운 작업이면 sudoers에서 바이너리를 직접 열지 말고 래퍼 스크립트 하나만 허용하는 편이 더 낫습니다. 예를 들어 서비스 재시작 전에 설정 테스트를 강제해야 한다면, 운영자에게 systemctl과 nginx를 각각 주는 것보다 검증을 포함한 스크립트를 주는 쪽이 안정적입니다.

    7. 주의사항과 자주 겪는 트러블슈팅

    여기부터가 문법보다 더 중요합니다. 실제 장애나 오남용은 대개 sudoers 문법을 몰라서가 아니라, 권한 모델이 명령의 성질을 과소평가해서 생깁니다.

    7-1. visudo 없이 직접 편집하지 않기

    /etc/sudoers나 /etc/sudoers.d/*를 일반 편집기로 바로 열면 저장은 쉬워도 검증이 약합니다. 항상 visudo를 쓰는 이유는 단순 문법 체크 때문만이 아닙니다. 운영 서버에서 sudo가 깨지면 복구 경로가 원격 콘솔 하나로 줄어드는 경우가 많아서, 사소한 오타도 비용이 꽤 큽니다.

    7-2. 쉘 탈출 가능한 명령을 읽기 전용으로 착각하지 않기

    이 항목이 가장 자주 과소평가됩니다. less, man, vi, vim, awk, find, python3, perl, tar는 설정과 사용 방식에 따라 추가 명령 실행, 파일 쓰기, 파일 읽기 확대로 이어질 수 있습니다. 즉, 문제는 그 명령이 관리자용이냐 아니냐가 아니라 더 넓은 실행권으로 이어질 수 있느냐입니다. sudo 권한 검토 때 저는 허용할 명령의 기능보다 탈출 표면을 먼저 봅니다.

    7-3. NOPASSWD는 자동화 전용에 가깝습니다

    사람 계정에 넓은 NOPASSWD를 주면 작업 승인감이 사라집니다. 비밀번호 입력 자체가 완벽한 보안 장치는 아니어도, 적어도 “지금 관리자 권한을 쓰고 있다”는 마찰은 줍니다. 자동화 계정은 그 마찰이 필요 없지만 사람은 필요하더라고요. 그래서 제 기준은 간단합니다. 사람 계정은 기본적으로 비밀번호 요구 유지, 자동화 계정만 좁은 NOPASSWD 허용입니다.

    7-4. include 순서와 파일 권한이 엉키면 정책보다 예외가 앞섭니다

    /etc/sudoers.d에 파일이 많아지면 운영자가 논리적 우선순위와 파일 이름 우선순위를 혼동하기 쉽습니다. 거기에 권한까지 제각각이면 감사 때 설명이 어려워집니다. 저는 역할 파일 이름에 접두어를 붙여 정렬 순서를 드러내는 편입니다. 예를 들어 10-base, 20-ops-web, 90-breakglass처럼 나누면 긴급 예외가 일반 역할보다 눈에 잘 띕니다.

    7-5. 환경 변수 전달은 꼭 필요한 것만 예외로 열기

    sudo는 기본적으로 환경 변수를 정리합니다. 이 기본값은 불편해서가 아니라 안전해서 존재합니다. 특정 자동화 때문에 환경 변수를 넘겨야 하면, 먼저 왜 필요한지 확인하고, 가능하면 애플리케이션 설정 파일이나 systemd unit 쪽으로 옮기는 게 낫습니다. 환경 변수 전달을 습관적으로 열면 실행 맥락이 흐려지고, 장애 재현도 어려워집니다.

    7-6. 인자 제어가 애매하면 래퍼 스크립트로 닫는 편이 더 안전합니다

    sudoers에서 명령과 인자를 정교하게 제한하려다 보면 운영자가 오히려 규칙을 우회할 틈이 생기기도 합니다. 이럴 때는 스크립트 하나를 만들고 그 스크립트만 허용하는 쪽이 낫습니다. 제가 실무에서 자주 쓰는 방식도 이겁니다. 예를 들어 “nginx 설정 검사 후 reload만 허용” 같은 요구는 sudoers 한 줄보다 스크립트가 더 명확합니다.

    # /usr/local/sbin/nginx-safe-reload
    #!/bin/sh
    set -eu
    
    /usr/sbin/nginx -t
    exec /usr/bin/systemctl reload nginx
    # /etc/sudoers.d/ops-web
    User_Alias OPS_WEB = %ops-web
    Cmnd_Alias NGINX_SAFE = /usr/local/sbin/nginx-safe-reload
    
    OPS_WEB ALL=(root) NGINX_SAFE

    단, 위 경로도 배포판에 따라 다를 수 있으니 command -v nginx, command -v systemctl로 실제 경로를 확인한 뒤 반영하세요. 이 방식의 장점은 권한 정책과 실행 절차를 같이 묶을 수 있다는 점입니다. 반대로 래퍼 스크립트 자체 권한, 경로, 수정 권한이 허술하면 오히려 우회 지점이 됩니다. 그래서 전용 디렉터리, root 소유, 쓰기 권한 제한이 같이 따라가야 합니다.

    sudo 권한 관리 검증을 위한 로그 확인 이미지

    sudo 실행 로그를 확인하고 과도한 권한을 탐지하는 과정을 보여주는 이미지입니다.

    8. 검증과 로그 읽기: 설정 후 꼭 확인할 것

    sudo 정책은 작성보다 검증이 더 중요합니다. 파일이 예쁘게 나뉘어 있어도 실제 해석 결과가 의도와 다르면 의미가 없습니다. 저는 항상 허용 경로와 거부 경로를 둘 다 테스트합니다.

    1. 대상 사용자 기준으로 sudo -l를 실행해 해석 결과를 봅니다.
    2. 허용한 명령이 실제로 성공하는지 확인합니다.
    3. 허용하지 않은 명령과 인자가 거부되는지 확인합니다.
    4. 로그에 누가, 언제, 무엇을 실행했는지 남는지 확인합니다.
    5. 거부 로그가 반복되는 계정은 정책 누락인지 우회 시도인지 구분합니다.

    8-1. 권한 검증 예시

    su - alice
    sudo -l
    sudo /usr/bin/systemctl status nginx
    sudo /usr/bin/systemctl reload nginx
    sudo /usr/bin/systemctl restart nginx
    sudo /usr/bin/systemctl restart sshd
    sudo /bin/bash

    제가 보는 기준은 명확합니다. 의도한 세 명령만 성공하고, 다른 유닛 재시작이나 셸 실행이 거부되면 정책은 대체로 맞습니다. 반대로 허용하지 않은 명령이 실행되거나, 같은 바이너리인데 인자만 바꿔도 통과하면 범위를 너무 넓게 잡은 겁니다.

    8-2. 로그 확인 예시

    # systemd 저널에서 sudo 관련 로그 확인
    sudo journalctl _COMM=sudo
    
    # 배포판별 전통 로그 파일 확인
    sudo grep sudo /var/log/auth.log
    sudo grep sudo /var/log/secure
    
    # 역할별 전용 로그 확인
    sudo tail -f /var/log/sudo-ops-web.log

    로그를 읽을 때는 성공 로그보다 거부 로그와 반복 패턴이 더 유용합니다. 예를 들어 동일 사용자가 여러 번 다른 유닛 재시작을 시도하면 권한 설계가 업무 흐름과 안 맞는 걸 수 있고, 야간에 자동화 계정이 예상치 못한 명령을 반복하면 잡아야 할 이상 징후일 수 있습니다. 저는 운영팀에 늘 이렇게 말합니다. sudo 로그는 감사용 문서가 아니라 권한 설계 품질을 보여주는 운영 데이터라고요.

    또 하나 중요한 기준이 있습니다. 실패 로그가 많다고 무조건 사용자 잘못은 아닙니다. 실제로는 필요한 작업 범위를 정책이 못 따라가서, 현장이 우회 시도로 배우는 경우가 더 많았습니다. 그래서 거부 로그는 처벌 자료보다 권한 재설계 신호로 먼저 보는 편이 좋습니다.

    9. FAQ와 마무리: 이런 경우엔 이렇게 가시면 됩니다

    9-1. 자주 받는 질문

    Q. sudoers는 한 파일에 몰아넣는 게 낫나요?
    아닙니다. 역할별로 /etc/sudoers.d에 나누는 편이 훨씬 낫습니다. 파일이 나뉘어야 리뷰, 인수인계, 롤백 포인트가 분명해집니다.

    Q. 운영자가 많으면 개인 계정마다 직접 권한을 주면 안 되나요?
    초반에는 편해 보여도 오래 못 갑니다. 사람이 아니라 역할을 기준으로 권한을 설계해야 팀이 커져도 관리가 됩니다.

    Q. 자동화 계정은 비밀번호 없이 써도 되나요?
    됩니다. 대신 NOPASSWD는 좁은 명령 집합에만 붙이고, 가능하면 전용 래퍼 스크립트 한 개만 허용하는 쪽이 더 안전합니다.

    Q. 서비스 재시작 정도면 systemctl 전체를 열어도 되지 않나요?
    저는 권하지 않습니다. 서비스 하나만 필요하면 그 유닛의 status, reload, restart만 분리해 두는 편이 맞습니다.

    9-2. 제가 권하는 최종 운영 기준

    • 개인 실험 서버라도 visudo 사용, /etc/sudoers.d 분리, 절대 경로 지정은 기본으로 가져가세요.
    • 팀 운영 서버라면 개인 계정 직접 부여보다 그룹 기반 역할 부여를 기본 정책으로 두세요.
    • 명령이 단순할 때는 sudoers에서 절대 경로 명령을 직접 제한하고, 인자 검증이나 절차 강제가 필요할 때는 래퍼 스크립트 하나만 허용하세요.
    • 사람 계정에는 비밀번호 요구를 유지하고, CI/CD 같은 자동화 계정에만 좁은 NOPASSWD를 쓰세요.
    • 긴급 복구용 광역 권한이 필요하면 만료 시점과 회수 담당자를 같이 정하세요. 회수 계획 없는 예외는 거의 항상 상시 권한이 됩니다.
    • 감사 대응이나 보안 민감 환경이라면 로그 저장보다 로그 검토 루틴을 먼저 운영 프로세스에 넣으세요.

    제가 운영과 보안을 같이 보면서 내린 결론은 이겁니다. sudo 권한 관리는 문법의 문제가 아니라 권한을 업무 단위로 얼마나 잘게 쪼개고, 그 조각을 얼마나 꾸준히 회수할 수 있느냐의 문제였습니다. 실제로는 권한을 넓게 주는 쪽이 단기적으로 편해 보여도, 시간이 지나면 장애 분석과 감사 대응 비용이 더 커집니다. 반대로 역할 기반으로 잘라 두면 누가 뭘 할 수 있는지가 선명해져서 운영 속도도 더 안정됩니다.

    정리하면 이렇습니다. 대부분의 서버는 “그룹 기반 역할 부여 + 절대 경로 명령 제한”으로 시작하고, 인자 통제나 절차 강제가 필요하면 “래퍼 스크립트만 허용”으로 넘어가세요. 이 조합이 보안, 운영 편의, 리뷰 가능성 사이에서 가장 오래 버팁니다. sudo 권한 관리 외에도 계정 분리나 로그 감사 체계를 함께 보고 싶다면 관련 Linux 보안 강화 글도 이어서 읽어보시는 걸 권합니다.

    최소 권한 적용 전후 차이와 운영 체크리스트를 한눈에 정리한 요약 이미지입니다.

  • [Linux] LVM 스냅샷 백업으로 Linux 서버 무중단 백업과 복구

    [Linux] LVM 스냅샷 백업으로 Linux 서버 무중단 백업과 복구

    [Linux] LVM 스냅샷 백업으로 Linux 서버 무중단 백업과 복구

    운영 중인 Linux 서버 백업은 늘 같은 딜레마가 있습니다. 서비스를 내리면 백업은 편하지만 운영 현실과는 좀 멀어지죠. 서비스를 계속 열어두면 백업 시점이 흔들리고요. 그래서 저는 LVM 스냅샷 백업을 자주 씁니다. 이 방식이 만능이라서가 아니라, 백업이 읽어야 할 기준 시점을 짧고 또렷하게 고정해주기 때문입니다.

    실무에서 진짜 중요한 건 “스냅샷을 만들었다”가 아닙니다. 스냅샷이 살아 있는 동안 백업을 끝낼 수 있는지, 복구 때 권한과 속성이 그대로 돌아오는지, DB처럼 파일시스템 바깥의 일관성 문제를 따로 통제했는지가 더 중요하거든요. 이 글에서는 LVM 스냅샷 백업을 운영 관점에서 어떻게 설계하고, 어떻게 복구까지 이어가는지 한 흐름으로 정리해보겠습니다. 명령어만 던지는 글보다는, 어디서 자주 깨지는지까지 같이 짚어볼게요.

    LVM 스냅샷 백업 기반 Linux 서버 무중단 백업 아키텍처

    LVM 스냅샷, 운영 볼륨, 백업 저장소, 복구 흐름을 한눈에 보여주는 개요 이미지입니다.

    LVM 스냅샷 백업이 실무에서 통하는 이유와, 안 맞는 상황

    이 방식을 높게 보는 이유는 구조가 단순해서입니다. 운영 볼륨에서 직접 백업을 읽지 않고, 스냅샷을 읽기 전용 기준점으로 삼아 백업 작업을 분리합니다. 서비스는 원본 LV에서 계속 쓰고, 백업 프로세스는 스냅샷에서 읽습니다. 그러면 백업 도중 파일이 바뀌어서 생기는 중간 상태를 줄이기 훨씬 수월합니다.

    다만 많이 오해하는 부분도 있습니다. LVM 스냅샷은 백업 사본이 아닙니다. 스냅샷 LV만 만들어두고 안심하면 안 됩니다. 스냅샷은 원본 변경 블록을 붙잡아두는 임시 장치라서 공간이 차면 무효화될 수 있고, 원본 스토리지 장애까지 막아주지도 못합니다. 그래서 제 기준에선 스냅샷은 “백업을 뜨는 동안만 잠깐 존재해야 하는 작업용 안전장치”에 더 가깝습니다. 이 점을 놓치면 설계가 금방 흔들리더라고요.

    상황 추천 방식 이유 제가 피하는 경우
    일반 웹 서버, 파일 서버 LVM 스냅샷 + rsync 구현이 단순하고 파일 단위 복구가 빠릅니다 백업 시간이 길고 변경량이 큰 대용량 쓰기 워크로드
    MySQL, PostgreSQL 같은 DB 서버 DB 네이티브 백업 + 필요 시 LVM 스냅샷 보조 파일시스템 일관성과 트랜잭션 일관성은 다릅니다 LVM 스냅샷만 믿고 복구 가능하다고 보는 설계
    스토리지 기능이 강한 SAN/NAS 환경 스토리지 스냅샷 우선, LVM은 보조 대규모 환경에선 오프로드 이점이 큽니다 호스트 레벨 작업이 병목이 되는 구조
    단일 디스크 장애까지 대비해야 하는 경우 LVM 스냅샷 + 원격/별도 저장소 복제 같은 VG 안 스냅샷만으로는 재해 대응이 안 됩니다 로컬 디스크 안에만 백업을 두는 구성

    제가 먼저 보는 판단 기준: 스냅샷이 버티는가, 읽기 마운트가 안전한가

    실제 운영에서 핵심은 이 두 가지입니다.

    1. 백업이 끝날 때까지 COW 영역이 살아남는가
    2. 스냅샷을 읽기 전용으로 마운트할 때 파일시스템 특성에 맞는 옵션을 썼는가

    첫 번째는 용량 문제이고, 두 번째는 복구 품질 문제입니다. 스냅샷이 중간에 터지면 백업은 실패입니다. 반대로 스냅샷이 살아 있어도 ACL, xattr, SELinux 컨텍스트가 복구 후 어긋나면 운영 입장에선 실패나 다름없습니다.

    파일시스템별로 마운트 습관도 조금 달라야 합니다. 예를 들어 XFS 스냅샷을 같은 호스트에 읽기 전용 마운트할 때는 보통 <code>nouuid를 같이 고려합니다. 원본과 동일 UUID를 가진 파일시스템을 같은 시스템에서 마운트하려다 막히는 경우가 많기 때문입니다. ext4는 일반적인 파일 복사 백업이라면 ro만으로 충분한 경우가 많지만, 저널 재생 없이 조사성으로 확인해야 하는 작업이라면 noload를 같이 검토합니다. 이런 차이를 모르고 들어가면 “마운트가 왜 안 되지?”에서 시간을 꽤 쓰게 됩니다.

    LVM, 스냅샷, COW를 실무 관점으로 이해해보기

    스냅샷을 깊게 파고들 필요는 없지만, 한 가지만은 정확히 보셔야 합니다. 스냅샷 용량은 원본 크기가 아니라 스냅샷 생성 후 변경될 블록량을 감당하는 공간입니다. 그래서 2TB 원본 볼륨이라고 해서 스냅샷도 무조건 거대해야 하는 건 아닙니다. 반대로 100GB 볼륨이라도 백업 시간 동안 쓰기 폭주가 있으면 작은 스냅샷은 금방 무너집니다.

    • 원본 LV: 서비스가 계속 쓰는 볼륨입니다.
    • 스냅샷 LV: 백업 프로세스가 읽을 시점 기준점입니다.
    • COW 영역: 원본 블록이 바뀌기 전에 기존 내용을 보존하는 공간입니다.

    현장에서 가장 자주 보는 실패는 “원본이 크니까 스냅샷도 크게 잡아야 한다”가 아닙니다. 오히려 “백업은 금방 끝나겠지”라고 가볍게 보고 스냅샷을 너무 작게 잡는 경우가 더 많습니다. 원인은 거의 항상 같습니다. 백업 소요 시간과 그 시간 동안의 쓰기량을 계산하지 않았기 때문입니다. 로그 서버, 업로드가 몰리는 애플리케이션 서버, 배치나 VACUUM이 도는 DB 서버는 이 부분이 특히 민감합니다.

    LVM 스냅샷 백업 구현 전 체크리스트

    작업 전에 확인하는 항목은 아래 다섯 가지입니다. 이 단계가 허술하면 백업보다 복구 때 더 고생합니다.

    1. 대상 마운트가 정말 LVM LV 위에 올라가 있는지 확인합니다.
    2. VG의 VFree가 스냅샷과 메타데이터를 감당할 만큼 남아 있는지 봅니다.
    3. 파일시스템이 ext4인지 XFS인지 확인하고 마운트 옵션을 다르게 잡습니다.
    4. DB나 메시지 큐처럼 쓰기 일관성이 중요한 프로세스는 flush, lock, checkpoint 또는 native backup 절차를 따로 준비합니다.
    5. 백업 결과를 같은 VG가 아닌 별도 디스크나 원격 저장소로 보낼지 결정합니다.
    lsblk -o NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT
    pvs -o pv_name,vg_name,pv_size,pv_free
    vgs -o vg_name,vg_size,vg_free
    lvs -a -o lv_name,vg_name,lv_size,origin,data_percent,metadata_percent,lv_attr
    findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /data

    여기서 vgs의 vg_free는 그냥 “남은 공간”이 아니라, 스냅샷이 쓸 생존 예산이라고 보시면 편합니다. findmnt도 꽤 중요합니다. 운영에선 심볼릭 링크, bind mount, 컨테이너 볼륨 때문에 “내가 백업한다고 생각한 경로”와 “실제 파일시스템 경로”가 어긋나는 일이 생각보다 자주 생기거든요.

    실전 구현 1: 스냅샷 생성 전에 정합성 수준부터 정합니다

    이 작업에서 먼저 정하는 건 명령어가 아니라 어느 수준의 정합성을 요구하는지입니다.

    대상 권장 기준 실무 판단 추가 작업
    정적 파일, 문서, 업로드 디렉터리 파일시스템 크래시 일관성 LVM 스냅샷만으로도 충분한 경우가 많습니다 읽기 전용 마운트와 권한 보존 확인
    Nginx/Apache 설정, 일반 앱 배포 파일 파일시스템 일관성 + 서비스 검증 복구 후 설정 테스트가 중요합니다 nginx -t, 서비스 기동 확인
    MySQL/PostgreSQL 데이터 파일 애플리케이션 일관성 LVM 단독으론 불충분할 수 있습니다 flush, checkpoint, dump, native backup 병행

    파일시스템 스냅샷은 어디까지나 파일시스템 관점입니다. DB는 쓰기 캐시, 체크포인트, WAL이나 redo 로그, 버퍼 상태가 얽혀 있어서 “파일이 멈춰 보인다”와 “복구 가능한 상태다”가 같지 않습니다. 그래서 DB 서버에선 LVM 스냅샷을 주력 백업으로 두기보다, 네이티브 백업을 주력으로 두고 LVM은 빠른 파일 보조 복구 수단으로 쓰는 편이 안전합니다.

    실전 구현 2: LVM 스냅샷 생성과 읽기 전용 마운트

    예시는 /dev/vgdata/lvdata를 운영 볼륨으로 가정하겠습니다. 스냅샷은 lvdata_snap, 마운트 지점은 /mnt/backup_snap으로 두겠습니다. ext4와 XFS는 마운트 옵션을 다르게 예시하겠습니다.

    # 공통: 스냅샷 생성
    lvcreate -L 10G -s -n lvdata_snap /dev/vgdata/lvdata
    mkdir -p /mnt/backup_snap
    
    # ext4 예시
    mount -t ext4 -o ro /dev/vgdata/lvdata_snap /mnt/backup_snap
    
    # XFS 예시: 같은 호스트에서 원본과 함께 마운트할 때 nouuid 고려
    # mount -t xfs -o ro,nouuid /dev/vgdata/lvdata_snap /mnt/backup_snap
    
    lvs -a -o lv_name,origin,lv_size,data_percent,metadata_percent,lv_attr /dev/vgdata

    여기서 -L 10G는 샘플일 뿐입니다. 실무에선 이 숫자가 제일 중요합니다. 기준은 “원본 크기”가 아니라 백업이 끝날 때까지 바뀔 블록량입니다. 백업이 40분 걸리고 그 시간 동안 로그 파일과 업로드 파일이 많이 늘어난다면, 스냅샷은 생각보다 빨리 찹니다.

    한 가지는 꼭 바로잡고 싶습니다. 일반적인 LVM 스냅샷 백업에서는 수동 fsfreeze를 기본 절차로 넣지 않는 편이 안전합니다. LVM이 스냅샷 생성 시 파일시스템 freeze를 자동으로 처리하는 경우가 많아서, 이미 얼린 상태에서 다시 lvcreate를 호출하면 대기 상태에 빠질 수 있거든요. 대신 DB처럼 애플리케이션 정합성이 더 중요한 대상은 파일시스템 freeze보다 서비스별 체크포인트나 잠금 절차를 먼저 설계하는 쪽이 낫습니다.

    # 애플리케이션 정합성이 필요할 때는
    # 파일시스템 전체 freeze보다 서비스별 절차를 먼저 검토합니다.
    # 예: PostgreSQL CHECKPOINT, MySQL native backup 또는 flush 전략 확인
    lvcreate -L 10G -s -n lvdata_snap /dev/vgdata/lvdata

    그리고 ACL, xattr, 소유자 보존이 중요한 서버라면 백업과 복구 작업을 root 권한으로 수행하는지도 같이 확인하세요. 옵션만 넣어두고 권한이 부족하면 기대한 복구 품질이 안 나올 수 있습니다.

    LVM 스냅샷 백업 생성과 마운트 절차 이미지

    원본 LV에서 스냅샷을 생성하고 읽기 전용으로 마운트하는 흐름을 설명하는 이미지입니다.

    실전 구현 3: 스냅샷에서 백업을 뜰 때 옵션을 왜 그렇게 주는지

    스냅샷을 마운트했으면 백업은 그 지점에서 읽습니다. 파일 복구 비중이 높은 환경에서는 rsync를 많이 씁니다. 이유는 단순합니다. 권한, 타임스탬프, 하드링크, ACL, xattr를 비교적 일관되게 가져가기 좋고, 복구도 부분 단위로 끊어 하기 쉽습니다. 이 조합은 실제 운영에서 꽤 편하더라고요.

    BACKUP_DIR=/backup/$(date +%F)
    mkdir -p "$BACKUP_DIR"
    
    rsync -aHAX --numeric-ids \
      --info=stats2,progress2 \
      /mnt/backup_snap/ "$BACKUP_DIR/"
    
    sync

    -aHAX는 그냥 관성적으로 붙이는 옵션이 아닙니다. -H는 하드링크, -A는 ACL, -X는 xattr입니다. SELinux를 쓰거나 ACL 기반 권한을 쓰는 서버에선 이 차이가 꽤 큽니다. --numeric-ids도 실무에선 중요할 때가 많습니다. 복원 대상 서버에서 사용자 이름 매핑이 다를 수 있기 때문입니다.

    초안처럼 날짜별 새 디렉터리를 매번 만드는 구조라면 --delete는 보통 불필요합니다. 대상 디렉터리가 매일 새로 생기는데 --delete를 붙여도 얻는 이득이 거의 없습니다. 반대로 같은 미러 디렉터리를 계속 갱신하는 구조라면 --delete가 필요할 수 있지만, 잘못된 경로를 넣었을 때 파괴 범위도 같이 커집니다. 그래서 날짜별 보관과 미러 보관은 목적을 분리하는 편이 안전합니다.

    백업이 끝났으면 스냅샷은 바로 정리합니다.

    umount /mnt/backup_snap
    lvremove -y /dev/vgdata/lvdata_snap

    이건 습관처럼 가져가시는 게 좋습니다. 스냅샷을 오래 들고 가면 원본 쓰기마다 COW 부담이 쌓이고, 결국 성능과 안정성 둘 다 애매해집니다. 짧게 만들고, 빨리 읽고, 바로 제거하는 흐름이 제일 덜 사고 납니다.

    자동화 예시: 실패 시 정리까지 되는 백업 스크립트

    자동화는 단순히 cron이나 timer에 걸어두는 걸 말하지 않습니다. 중간 실패 시 스냅샷과 마운트를 어떻게 치울지까지 포함해야 합니다. 운영에서 진짜 귀찮은 건 백업 실패보다, 실패 뒤에 남은 찌꺼기입니다. 마운트가 남고 스냅샷이 남아 쓰기 성능을 갉아먹는 경우가 생각보다 많습니다.

    #!/usr/bin/env bash
    set -euo pipefail
    
    VG="vgdata"
    ORIGIN="lvdata"
    SNAP="lvdata_snap"
    SNAP_SIZE="10G"
    SOURCE_MNT="/data"
    SNAP_MNT="/mnt/backup_snap"
    BACKUP_ROOT="/backup"
    TODAY="$(date +%F)"
    BACKUP_DIR="$BACKUP_ROOT/$TODAY"
    LV_PATH="/dev/$VG/$ORIGIN"
    SNAP_PATH="/dev/$VG/$SNAP"
    FSTYPE="$(findmnt -no FSTYPE "$SOURCE_MNT")"
    VG_FREE_BYTES="$(vgs --noheadings --units b --nosuffix -o vg_free "$VG" | tr -d ' ')"
    SNAP_SIZE_BYTES="$(numfmt --from=iec "$SNAP_SIZE")"
    
    cleanup() {
      mountpoint -q "$SNAP_MNT" && umount "$SNAP_MNT" || true
      lvs "$SNAP_PATH" >/dev/null 2>&1 && lvremove -y "$SNAP_PATH" || true
    }
    trap cleanup EXIT
    
    if [ "$VG_FREE_BYTES" -le "$SNAP_SIZE_BYTES" ]; then
      echo "Not enough free space in VG $VG" >&2
      exit 1
    fi
    
    mkdir -p "$SNAP_MNT" "$BACKUP_DIR"
    
    lvcreate -L "$SNAP_SIZE" -s -n "$SNAP" "$LV_PATH"
    
    case "$FSTYPE" in
      xfs)
        mount -t xfs -o ro,nouuid "$SNAP_PATH" "$SNAP_MNT"
        ;;
      ext4)
        mount -t ext4 -o ro "$SNAP_PATH" "$SNAP_MNT"
        ;;
      *)
        echo "Unsupported filesystem: $FSTYPE" >&2
        exit 1
        ;;
    esac
    
    rsync -aHAX --numeric-ids --info=stats2 "$SNAP_MNT/" "$BACKUP_DIR/"
    sync
    
    lvs -a -o lv_name,origin,data_percent,metadata_percent,lv_attr "$VG"

    이 스크립트에서 꼭 넣는 건 세 가지입니다. set -euo pipefail, trap cleanup EXIT, 그리고 스냅샷 생성 전 여유 공간 검사입니다. 특히 cleanup trap은 중요합니다. rsync가 중간에 실패하더라도 스냅샷과 마운트가 남지 않게 막아줍니다.

    systemd 타이머는 그대로 써도 되지만, 서비스 유닛에 실패 로그를 남기기 쉽게 구성해두면 운영 추적이 편합니다. 이 블로그의 rsync 증분 백업 가이드나 systemd timer 운영 글이 있다면 내부 링크로 같이 묶어두는 것도 추천합니다. 검색 유입 이후에 다음 글로 자연스럽게 넘어가더라고요.

    # /etc/systemd/system/lvm-snapshot-backup.service
    [Unit]
    Description=LVM snapshot backup job
    After=local-fs.target
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/sbin/lvm-snapshot-backup.sh
    
    # /etc/systemd/system/lvm-snapshot-backup.timer
    [Unit]
    Description=Run LVM snapshot backup daily
    
    [Timer]
    OnCalendar=*-*-* 02:30:00
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    chmod +x /usr/local/sbin/lvm-snapshot-backup.sh
    systemctl daemon-reload
    systemctl enable --now lvm-snapshot-backup.timer
    systemctl status lvm-snapshot-backup.timer
    systemctl list-timers --all | grep lvm-snapshot-backup

    systemctl status와 list-timers를 둘 다 보는 이유도 단순합니다. 타이머가 로드됐는지와 다음 실행 시각이 잡혔는지는 따로 확인하는 편이 실수를 줄여주거든요.

    주의사항과 트러블슈팅: LVM 스냅샷 백업에서 자주 깨지는 지점

    운영에서 자주 깨지는 지점은 대체로 아래 다섯 가지였습니다. 겉증상보다 원인을 먼저 보셔야 대응이 빨라집니다.

    • VG 여유 공간 부족: 원인은 단순히 디스크 부족이 아니라, 스냅샷 크기 산정이 쓰기량 기준이 아니었기 때문입니다.
    • 스냅샷 Data% 급상승: 백업이 느리거나, 백업 시간대의 쓰기 패턴을 과소평가한 경우가 많습니다.
    • XFS 스냅샷 마운트 실패: 같은 UUID 파일시스템 중복 마운트 문제를 놓친 경우가 흔합니다.
    • 복구 후 권한이나 컨텍스트 이상: rsync 옵션에서 ACL이나 xattr 보존을 빼먹었거나, 복구 대상 시스템 정책이 달랐던 경우입니다.
    • 백업 성공, 복구 실패: 파일 복사는 끝났지만 애플리케이션 기동 검증이 없었던 경우입니다.

    자주 보는 모니터링 명령은 아래입니다.

    lvs -a -o lv_name,origin,lv_size,data_percent,metadata_percent,lv_attr /dev/vgdata
    journalctl -u lvm-snapshot-backup.service -n 50 --no-pager
    1. data_percent가 계속 오르면 스냅샷이 변경 블록을 빠르게 소비하고 있다는 뜻입니다.
    2. data_percent가 100에 가까워지면 스냅샷 무효화 위험이 큽니다. 이때는 스냅샷 크기를 키우거나, 백업 속도를 올리거나, 백업 시간대를 바꾸는 쪽으로 접근합니다.
    3. metadata_percent는 스냅샷 유형과 LVM 버전에 따라 표시되지 않거나 의미가 다를 수 있으니, 항상 data_percent와 함께 해석합니다.
    4. lv_attr 값이 평소와 다르면, 특히 snapshot 관련 속성이 예상과 다르면 스냅샷 상태 이상부터 의심합니다.

    제 판단은 꽤 단순합니다. 스냅샷 크기를 무작정 키우는 것보다, 백업 시간을 줄이거나 쓰기 피크를 피하는 게 먼저입니다. 스냅샷을 크게 잡는 건 임시 처방이지 구조적 해결은 아닙니다.

    그리고 파일 복구와 볼륨 롤백은 완전히 다른 작업입니다. 이 둘을 섞어 생각하시면 운영 반영 시점에서 사고가 납니다.

    복구 방식 적합한 상황 주요 도구 리스크 포인트
    파일 단위 복구 설정 파일, 업로드 디렉터리, 일부 데이터만 되살릴 때 rsync, cp, tar 권한, ACL, xattr, 서비스 재기동 검증 누락
    스냅샷 병합 롤백 볼륨 전체를 특정 시점으로 되돌려야 할 때 lvconvert –merge 운영 중인 origin 반영 시점, 재활성화나 재부팅 절차
    LVM 스냅샷 백업 Data 퍼센트 모니터링 대시보드

    스냅샷 사용량 증가와 경고 상태를 관찰하는 모니터링 예시 이미지입니다.

    LVM 복구 시나리오 1: 파일 단위 복원

    실제 장애는 대부분 전체 롤백까지 갈 필요가 없습니다. 설정 파일 하나, 업로드 경로 일부, 잘못 덮어쓴 정적 자산 몇 개처럼 부분 복구가 더 많습니다. 이럴 때 LVM 스냅샷 기반 백업은 꽤 강합니다. 백업을 통째로 되돌리지 않고 필요한 경로만 살릴 수 있어서 영향 범위를 줄이기 쉽습니다.

    rsync -aHAX --numeric-ids /backup/2026-08-16/etc/nginx/ /etc/nginx/
    rsync -aHAX --numeric-ids /backup/2026-08-16/data/uploads/ /data/uploads/
    
    nginx -t
    systemctl reload nginx

    복구 직후엔 파일 존재 여부만 보면 부족합니다. 최소한 설정 문법 검사, 서비스 reload 또는 재기동, 실제 read/write 동작 확인까지 보셔야 합니다. 파일은 돌아왔는데 애플리케이션 권한이 막혀서 실패하는 경우도 생각보다 많습니다.

    LVM 복구 시나리오 2: 스냅샷 병합으로 시점 롤백

    볼륨 전체를 특정 시점으로 되돌려야 한다면 lvconvert --merge를 씁니다. 다만 이건 영향도가 큽니다. 가볍게 권하기 어려운 이유도 명확합니다. merge는 파일 몇 개를 되돌리는 작업이 아니라, origin LV 전체 상태를 되감는 작업이기 때문입니다.

    umount /data
    lvchange -an /dev/vgdata/lvdata
    lvconvert --merge /dev/vgdata/lvdata_snap
    lvchange -ay /dev/vgdata/lvdata
    mount /dev/vgdata/lvdata /data

    환경에 따라 merge 반영 시점은 즉시가 아니라 다음 활성화 시점이 될 수 있습니다. 특히 origin LV가 열려 있으면 더 조심하셔야 합니다. 루트 파일시스템이 걸린 경우라면 유지보수 창, rescue 모드, 재부팅 절차까지 포함해서 반드시 사전 검증하는 편이 안전합니다.

    검증: 백업 성공보다 복구 성공 신호를 봅니다

    운영에선 “백업 로그가 성공으로 끝났다”보다 “복구 검증이 통과했다”가 훨씬 중요합니다. 최소 기준으로 잡는 항목은 아래 네 가지입니다.

    1. rsync 종료 코드와 systemd 서비스 종료 상태를 확인합니다.
    2. 백업 사본에서 샘플 파일을 실제로 열어 읽어봅니다.
    3. 권한, 소유자, 심볼릭 링크, ACL, xattr가 유지됐는지 확인합니다.
    4. 복구 테스트 환경에서 서비스가 떠서 실제 요청을 처리하는지 확인합니다.
    echo $?
    getfacl /data/somefile
    getfattr -d /data/somefile || true
    find /backup/$(date +%F) -maxdepth 2 | head
    systemctl status lvm-snapshot-backup.service --no-pager

    echo $?가 0이 아니면 백업 작업은 실패로 보는 편이 맞습니다. getfacl이나 getfattr 결과가 기대와 다르면, 복구 후 접근 제어 문제가 날 가능성이 큽니다. 그리고 복구 테스트는 프로세스가 떴는지만 보면 부족합니다. 웹 서비스라면 실제 요청을 보내보고, 업로드 경로라면 테스트 파일 생성과 삭제까지 해보는 쪽이 훨씬 현실적입니다.

    LVM 스냅샷 백업 검증과 복구 테스트 체크리스트

    백업 성공 여부보다 복구 가능성을 점검하는 체크리스트를 요약한 이미지입니다.

    실무 추천 시나리오: LVM 스냅샷 백업은 언제 쓰면 좋을까

    운영에서 내리는 추천은 비교적 분명합니다.

    • 일반 웹 서버, 파일 서버, 홈랩: LVM 스냅샷 + rsync 조합이 가장 균형이 좋습니다. 구현 난이도 대비 복구 유연성이 좋습니다.
    • 트랜잭션 정합성이 중요한 DB 서버: DB 네이티브 백업을 주력으로 두고, LVM 스냅샷은 파일 단위 보조 복구나 빠른 시점 확보 수단으로 쓰는 편이 안전합니다.
    • 백업 창이 길고 쓰기량이 높은 서버: 스냅샷 크기를 키우기 전에 백업 시간대 조정, 대상 분리, 백업 속도 개선부터 보시는 게 맞습니다.
    • 디스크 장애나 호스트 장애까지 대비해야 하는 환경: 로컬 스냅샷만으로 끝내지 말고 원격 저장소 복제를 붙이셔야 합니다.

    실무에서 느끼는 건 늘 비슷합니다. 빠르게 시점을 고정하는 기술, 사본을 오래 보관하는 기술, 서비스를 다시 살리는 기술은 서로 다릅니다. LVM 스냅샷은 첫 번째에 강합니다. 그래서 잘 쓰면 진짜 편하지만, 혼자 모든 걸 해결해주진 않습니다.

    추천을 한 줄로 좁히면 이렇습니다. 운영 파일 백업이라면 LVM 스냅샷을 짧게 만들고 rsync로 빠르게 뽑으세요. DB라면 그 위에 네이티브 백업을 얹으세요. 그리고 어떤 경우든 복구 리허설을 백업 성공보다 우선순위 높게 두세요.

    자주 묻는 질문

    LVM 스냅샷 백업만 있으면 충분한가요?

    아닙니다. 스냅샷은 시점 확보 장치이고, 실제 백업 사본과 복구 검증이 함께 있어야 의미가 있습니다. 같은 VG 안에만 결과를 두는 구성도 재해 대응 관점에선 부족합니다.

    XFS에서도 쓸 수 있나요?

    가능합니다. 다만 같은 호스트에 원본과 스냅샷을 함께 마운트할 때는 nouuid를 검토하셔야 합니다. 파일시스템 일관성과 애플리케이션 정합성은 별개라는 점도 그대로 유효합니다.

    운영 중 성능 영향은 없나요?

    영향이 0은 아닙니다. 스냅샷 유지 시간이 길수록, 그리고 원본 쓰기량이 많을수록 COW 부담이 커집니다. 그래서 “짧게 생성, 빠르게 백업, 즉시 제거”가 가장 안전한 운영 패턴입니다.

  • [Linux] Wayland X11 비교: Linux 데스크톱 선택 가이드

    [Linux] Wayland X11 비교: Linux 데스크톱 선택 가이드

    Wayland X11 비교: Linux 데스크톱 선택 가이드와 성능 체크

    Wayland X11 비교는 요즘 Linux 데스크톱을 만지는 분들이 결국 한 번은 부딪히는 주제입니다. 같은 노트북, 같은 외부 모니터, 같은 브라우저를 써도 세션이 Wayland냐 X11이냐에 따라 느낌이 꽤 다르거든요. 어떤 날은 제스처와 스케일링이 아주 자연스럽고, 또 어떤 날은 화면 공유 하나 붙이자마자 워크플로가 흔들립니다. 겉으로는 둘 다 데스크톱이지만, 운영 관점에서 보면 장애 포인트가 분명히 다릅니다.

    핵심은 단순한 신구 대결이 아닙니다. Wayland는 최신 Linux 데스크톱의 일상 사용감과 보안 모델에서 강점이 크고, X11은 도구 생태계와 자동화 호환성에서 여전히 강합니다. 문제는 많은 비교 글이 여기서 끝난다는 점이죠. 실제 운영에서는 “무엇이 더 현대적인가”보다 “내가 자주 쓰는 작업이 어디서 덜 깨지는가”가 더 중요하더라고요. 이 글은 그 기준으로 보겠습니다. 명령어, 로그, 설정 파일, 실패 모드, 선택 기준까지 실무적으로 정리해보겠습니다.

    Wayland X11 비교를 위한 Linux 디스플레이 서버 아키텍처 개요 이미지

    Wayland compositor와 X.Org server 구조 차이를 한눈에 보여주는 개요 이미지입니다.

    1. Wayland X11 비교가 아직 중요한 이유

    요즘 배포판은 Wayland를 기본 세션으로 제공하는 경우가 많습니다. GNOME은 Wayland 중심으로 다듬어졌고, KDE Plasma도 Wayland 완성도가 많이 올라왔죠. 그래서 겉보기에는 이미 승부가 끝난 것처럼 보일 수 있습니다. 그런데 실제로는 그렇지 않습니다. 데스크톱은 브라우저 창만 띄우는 환경이 아니라, 화면 공유, 캡처, 원격 접속, GUI 자동화, 멀티 모니터, 혼합 배율, 게임 런처, 오버레이, 입력기까지 다 엮인 운영 체계이기 때문입니다.

    Wayland X11 비교에서 먼저 볼 건 FPS 하나가 아닙니다. 아래 네 가지가 더 중요합니다.

    • 입력과 표시의 일관성: 스크롤, 제스처, 프랙셔널 스케일링, 티어링 억제
    • 도구 호환성: 화면 녹화, 원격 제어, GUI 자동화, 컬러 피커, 오버레이
    • 장애 분리 난이도: 문제가 세션 구조인지, 드라이버인지, 포털인지 빨리 구분되는가
    • 보안 모델의 비용: 화면 접근을 막아 얻는 안전성과, 그 대가로 잃는 편의성

    실무에서는 이 네 가지가 계속 충돌합니다. 예를 들어 Wayland는 앱이 다른 앱의 화면이나 입력에 임의로 접근하기 어렵게 설계돼 있어 보안상 유리합니다. 대신 예전 X11 방식에 기대던 캡처 도구나 매크로 도구는 여기서 막히기 쉽습니다. 반대로 X11은 도구가 잘 붙지만, 앱 간 경계가 느슨해서 “되는 게 많다”는 장점이 그대로 관리 포인트가 되기도 합니다.

    2. Wayland와 X11 구조 차이: 어디서 문제가 생길까

    디스플레이 서버를 교과서식으로 길게 볼 필요는 없습니다. 운영에 필요한 만큼만 잡고 가면 됩니다. 핵심은 입력과 화면 합성, 그리고 앱의 화면 접근 권한을 누가 쥐고 있느냐입니다.

    Wayland 구조에서 생기는 특징

    Wayland에서는 compositor가 중심입니다. GNOME의 Mutter, KDE의 KWin이 대표적이죠. 앱은 compositor와 직접 프로토콜을 주고받고, 오래된 X11 앱은 대개 XWayland를 통해 실행됩니다. 이 구조 덕분에 최신 노트북에서 제스처, 프랙셔널 스케일링, 모니터별 배율 같은 부분은 Wayland가 더 자연스럽게 느껴지는 경우가 많습니다. 노트북과 4K 외부 모니터를 함께 쓰는 환경에서도 이 차이가 먼저 체감되는 편입니다.

    대신 비용도 분명합니다. Wayland는 “앱이 화면 전체를 마음대로 읽는다”는 오래된 전제를 기본값으로 허용하지 않습니다. 그래서 스크린샷, 화면 녹화, 원격 제어, 매크로 자동화가 포털(xdg-desktop-portal), PipeWire, compositor 구현에 더 의존합니다. 구조는 깔끔한데, 실제 운영에서는 확인해야 할 계층이 한 단계 바뀐 셈입니다.

    X11 구조에서 생기는 특징

    X11은 생태계가 넓고 역사가 길어서, 오래된 도구가 정말 많습니다. `xdotool`, 좌표 기반 클릭, 창 트리 조작, 구형 캡처 도구, 일부 원격 툴은 여전히 X11 전제를 깔고 있습니다. 이런 도구를 업무 핵심으로 쓰는 환경이라면 X11이 더 편할 때가 많습니다. GUI 회귀 테스트나 반복 입력 자동화가 필요한 장비에서 X11을 남겨두는 이유도 보통 여기 있습니다.

    문제는 구성 편차입니다. X.Org server, window manager, compositor, 드라이버 설정이 조합에 따라 달라져서 같은 “X11”이라도 결과가 제각각일 수 있습니다. 특히 멀티 모니터, 혼합 DPI, 티어링 제어는 배포판 기본값과 compositor 설정 영향을 많이 받습니다. 그래서 X11은 익숙하고 유연하지만, 운영자가 책임져야 할 면적이 넓습니다.

    판단 축 Wayland X11
    창 합성과 입력 처리 compositor가 더 직접 담당 X 서버와 WM/compositor 조합 편차가 큼
    앱 간 화면/입력 접근 제한이 강한 편 전통적으로 넓음
    혼합 배율·HiDPI 최신 DE에서 유리한 편 환경별 편차와 설정 부담이 큼
    스크립트 자동화 제약이 많음 기존 도구가 잘 붙음
    화면 공유·녹화 portal/PipeWire 상태에 좌우됨 기존 X11 API 도구와 친화적
    장애 분리 포털·compositor·드라이버를 같이 봐야 함 도구 호환성과 드라이버 분리가 비교적 직관적
    먼저 권하기 좋은 대상 일반 데스크톱, 노트북, 최신 GNOME/KDE 자동화, 레거시 툴, 특수 원격 환경
    Wayland X11 비교에서 세션 선택 과정을 보여주는 Linux 데스크톱 이미지

    로그인 화면에서 Wayland 세션과 X11 세션을 선택하는 흐름을 설명하는 이미지입니다.

    3. Wayland X11 비교 전 확인: 지금 세션이 무엇인지 보는 방법

    현장에서는 “분명 Wayland로 로그인한 줄 알았는데 아니었네”가 꽤 흔합니다. 로그인 화면의 문구만 믿지 말고, 실제 세션과 프로세스를 같이 보는 편이 안전합니다. 특히 원격 접속, GDM 설정, NVIDIA 드라이버 조합, 가상 환경에서는 생각보다 자주 엇나가거든요.

    1차 확인: 세션 타입과 기본 환경 변수

    echo "$XDG_SESSION_TYPE"
    loginctl show-session "$XDG_SESSION_ID" -p Type -p Class -p Name -p Remote -p State
    printf 'WAYLAND_DISPLAY=%s\nDISPLAY=%s\nXDG_CURRENT_DESKTOP=%s\n' \
      "$WAYLAND_DISPLAY" "$DISPLAY" "$XDG_CURRENT_DESKTOP"
    ls -l "$XDG_RUNTIME_DIR"/wayland-* /tmp/.X11-unix 2>/dev/null

    여기서는 이렇게 읽으면 됩니다.

    1. `XDG_SESSION_TYPE=wayland`면 Wayland 세션, `x11`이면 X11 세션입니다.
    2. `WAYLAND_DISPLAY`와 `DISPLAY`가 둘 다 잡혀 있어도 이상한 건 아닙니다. Wayland 세션 위에서 XWayland 앱을 함께 돌리면 흔한 상태입니다.
    3. `Remote=yes`라면 로컬 콘솔 세션과 동작이 다를 수 있습니다. 이 경우 입력 지연이나 화면 공유 결과를 같은 기준으로 비교하면 오판하기 쉽습니다.

    2차 확인: 실제 프로세스와 앱 백엔드

    ps -e -o pid,comm | grep -E 'Xorg|Xwayland|gnome-shell|kwin_wayland|mutter|sway'
    pgrep -a Xwayland
    loginctl session-status "$XDG_SESSION_ID"

    여기서 중요한 포인트가 하나 있습니다. `Xwayland`가 떠 있다고 해서 X11 세션이라는 뜻은 아닙니다. Wayland 세션에서 구형 X11 앱을 돌리기 위한 호환 레이어일 수 있습니다. 반대로 X11 세션에서는 `Xorg`가 중심으로 떠 있고, 앱 대부분이 `DISPLAY`를 통해 붙습니다.

    앱 단위로 백엔드를 강제로 바꿔보는 것도 문제 분리에 꽤 유용합니다. 예를 들어 GTK 앱이 Wayland 네이티브로 돌 때만 이상하다면, 같은 앱을 X11 백엔드로 띄워 차이를 볼 수 있습니다. 이거 실제로 해보면 원인 분리가 꽤 빨라집니다.

    GDK_BACKEND=x11 gedit
    GDK_BACKEND=wayland gedit
    QT_QPA_PLATFORM=xcb kate
    QT_QPA_PLATFORM=wayland kate

    세션 전체를 갈아엎지 않고도 “문제가 앱 백엔드인지, 세션 전체인지”를 빠르게 가를 수 있다는 점이 포인트입니다.

    4. Wayland X11 성능 비교: 숫자보다 패턴을 봐야 하는 이유

    Wayland X11 비교에서 가장 흔한 실수는 “게임 FPS가 비슷하니 차이 없다” 혹은 “스크롤이 부드러우니 Wayland가 무조건 낫다” 식으로 단정하는 겁니다. 데스크톱 성능은 단일 수치로 설명하기 어렵습니다. 실제로는 아래 다섯 가지를 같이 봐야 판단이 덜 흔들립니다.

    • 입력 지연: 클릭, 스크롤, 제스처가 즉시 반응하는가
    • 프레임 일관성: 창 이동, 워크스페이스 전환, 전체화면 전환에서 끊김이 반복되는가
    • 티어링과 깜빡임: 특히 외부 모니터, VRR, 혼합 주사율 환경에서 재현되는가
    • 스케일링 품질: 100%와 150% 모니터를 섞었을 때 글자와 커서가 어색하지 않은가
    • 도구 부하: 화면 공유, 녹화, 오버레이를 붙였을 때 compositor나 Xorg가 비정상적으로 치솟는가

    현장에서 느끼는 차이는 대체로 이렇습니다.

    • Wayland 장점: 최신 GNOME/KDE 기준으로 입력과 화면 합성의 일관성이 좋고, 혼합 DPI 환경에서 덜 거슬리는 경우가 많습니다.
    • X11 장점: 오래된 도구와 API가 잘 붙어서 우회 없이 바로 되는 일이 많습니다.
    • Wayland 주의점: 문제가 생기면 앱 자체보다 portal, PipeWire, compositor, 드라이버 층을 같이 봐야 해서 진단이 한 단계 더 필요합니다.
    • X11 주의점: 잘 되는 대신 설정 편차가 커서, 장비나 모니터 조합이 바뀌면 다른 종류의 스트레스를 받기 쉽습니다.

    로그와 리소스로 보는 기본 점검

    pids=$(pgrep -d',' -x gnome-shell -x kwin_wayland -x Xorg -x Xwayland)
    [ -n "$pids" ] && top -H -p "$pids"
    journalctl -b --no-pager | grep -Ei 'wayland|xorg|xwayland|mutter|kwin|drm|amdgpu|nvidia|nouveau|i915|intel'
    journalctl --user -b --no-pager | grep -Ei 'pipewire|portal|screencast|wireplumber'

    숫자를 볼 때도 해석 기준이 있어야 합니다.

    • 유휴 상태에서 compositor CPU가 계속 높으면 확장 기능, 화면 녹화, 오버레이, 브라우저 가속, 잘못된 VRR 조합을 먼저 의심합니다.
    • `drm`, `amdgpu`, `nvidia`, `i915` 관련 경고가 반복되면 세션 종류보다 드라이버 문제가 더 상위 원인일 수 있습니다.
    • 화면 공유를 켜는 순간부터 문제가 시작되면 Wayland 자체를 탓하기 전에 `xdg-desktop-portal`과 `PipeWire` 상태부터 보는 편이 정확합니다.
    • 문제가 창 전환이나 전체화면 진입 때만 터진다면 compositor 설정이나 주사율 협상 쪽일 가능성이 큽니다.

    Wayland가 빠르다, X11이 안정적이다 같은 문장은 반만 맞습니다. 실제로는 “어떤 작업에서, 어떤 드라이버와 compositor 조합으로, 어떤 도구를 붙였을 때 덜 깨지느냐”가 답에 더 가깝습니다.

    5. 실전 테스트 시나리오: 세션 교체 전에 꼭 해볼 체크리스트

    막연하게 감으로 고르지 말고, 같은 작업을 양쪽 세션에서 재현해보는 게 가장 정확합니다. 아래 순서는 장애 분리에도 잘 맞습니다.

    1. 로그인 화면에서 Wayland 세션과 X11 세션을 각각 선택합니다.
    2. 같은 외부 모니터, 같은 전원 상태, 같은 주사율, 같은 브라우저 탭 수로 맞춥니다.
    3. 창 여러 개 이동, 워크스페이스 전환, 전체화면 전환, 스크린샷, 화면 녹화, 원격 접속, 동영상 재생을 반복합니다.
    4. 세션별로 `journalctl -b`와 사용자 저널을 따로 저장해 비교합니다.
    5. 문제가 앱 전체인지 특정 앱인지 보려고 같은 앱을 Wayland/X11 백엔드로 각각 띄워봅니다.

    이때 많이 놓치는 게 하나 있습니다. 같은 앱이라도 내부 백엔드가 다를 수 있다는 점입니다. 브라우저 화면 공유는 portal 경로를 타고, 구형 녹화 도구는 X11 API를 기대하고, Electron 앱은 버전과 패키징에 따라 Wayland 처리 방식이 다를 수 있습니다. 그래서 “브라우저는 괜찮은데 녹화 도구만 안 된다” 같은 결과가 생깁니다.

    테스트 결과를 남길 때는 감상보다 조건을 적어두는 편이 훨씬 좋습니다. 예를 들어 “Wayland가 더 부드러움”보다 “Wayland 세션 + 외부 4K 150% + 브라우저 화면 공유 켠 상태에서는 정상, X11 세션에서는 전체화면 전환 시 깜빡임 재현”처럼 기록해야 다음 장애 대응이 빨라집니다. 이런 식으로 써두면 나중에 진짜 큰 도움이 됩니다.

    Wayland X11 비교를 위해 세션 타입과 로그를 점검하는 Linux 터미널 이미지

    세션 타입 확인과 로그 점검 명령어를 실행하는 실제 운영 흐름을 보여주는 이미지입니다.

    6. 트러블슈팅: Wayland와 X11에서 자주 만나는 문제

    이 섹션은 “왜 안 되지?”에서 끝나지 않도록 원인 층위를 같이 적겠습니다. 같은 증상이어도 뿌리가 다르면 해법도 달라집니다.

    문제 1. Wayland에서 스크린샷·화면 녹화·화면 공유가 들쭉날쭉하다

    이건 대개 Wayland의 보안 모델과 portal 경로 이해가 부족해서 생깁니다. Wayland에서는 앱이 화면 전체를 직접 읽는 방식이 기본값이 아닙니다. 대신 데스크톱 포털과 PipeWire가 중간에서 권한과 스트림을 다룹니다. 예전처럼 앱이 바로 가져가는 구조가 아니라는 점이 핵심입니다.

    systemctl --user status pipewire wireplumber xdg-desktop-portal \
      xdg-desktop-portal-gnome xdg-desktop-portal-kde
    journalctl --user -b -u pipewire -u wireplumber -u xdg-desktop-portal --no-pager
    busctl --user tree org.freedesktop.portal.Desktop 2>/dev/null | head

    여기서 보는 기준은 단순합니다.

    • 포털 서비스가 아예 안 떠 있으면 화면 공유가 불안정하거나 시작조차 안 될 수 있습니다.
    • GNOME 환경인데 KDE용 포털만 살아 있거나, 반대로 KDE 환경에 GNOME 포털만 섞여 있으면 선택 창이 이상하거나 캡처 경로가 꼬일 수 있습니다.
    • 문제가 특정 앱에서만 나면 앱이 portal 경로를 제대로 쓰는지, 패키지 형태(Flatpak, Snap, distro package) 차이도 같이 봐야 합니다.

    이 경우 Wayland 자체를 포기하기보다 먼저 portal 계층부터 바로잡는 편이 낫습니다. 원인이 세션이 아니라 사용자 세션 서비스인 경우가 생각보다 많거든요.

    문제 2. X11에서는 되던 `xdotool`·매크로·좌표 클릭 자동화가 Wayland에서 안 된다

    이건 버그라기보다 설계 차이에 가깝습니다. X11은 앱 간 입력 주입과 창 조작이 넓게 허용되어 왔고, Wayland는 그 가정을 기본값으로 인정하지 않습니다. 그래서 GUI 자동화가 핵심인 장비라면 Wayland에 억지로 맞추는 것보다 X11 세션을 남겨두는 편이 현실적입니다.

    이 상황에서는 기준을 명확히 잡는 게 좋습니다.

    • 사무용 개인 데스크톱: Wayland 우선
    • 테스트 자동화용 데스크톱: X11 유지
    • 업무 앱이 하나라도 X11 전용 도구에 깊게 묶여 있음: X11 우선 검토

    “최신이니까 옮긴다”는 판단이 제일 비쌀 때가 많습니다. 바꾸고 나서 자동화 파이프라인이 흔들리면 되돌리는 비용이 더 크거든요.

    문제 3. 멀티 모니터에서 배율, 커서, 전체화면 전환이 어색하다

    이 문제는 단순히 Wayland가 낫다, X11이 낫다로 자르기 어렵습니다. 다만 혼합 DPI 환경에서는 Wayland가 더 덜 거슬리는 편이라는 평가가 많습니다. 반대로 X11은 모니터별 스케일링이 깔끔하지 않거나, 특정 조합에서 티어링 제어가 설정 의존적인 경우가 있습니다.

    체크할 때는 세션 종류만 보지 말고, 도구도 구분해서 보셔야 합니다. 아래 명령은 환경에 따라 일부만 설치돼 있을 수 있습니다.

    xrandr --listmonitors 2>/dev/null
    xrandr --query 2>/dev/null
    wlr-randr 2>/dev/null
    kscreen-doctor -o 2>/dev/null

    `xrandr`는 X11 쪽 확인에는 유용하지만 Wayland 전체를 대표하지는 않습니다. Wayland에서는 compositor별 도구가 갈립니다. 그래서 GNOME, KDE, wlroots 계열이 같은 방식으로 보이지 않는다고 해서 이상한 건 아닙니다. 여기서 많이 헷갈립니다.

    문제 4. NVIDIA 환경에서 로그인 세션이 기대와 다르게 올라온다

    이건 배포판, 디스플레이 매니저, 드라이버 조합 영향을 많이 받습니다. 여기서 중요한 건 소문이 아니라 실제 세션 타입입니다. 로그인 화면 메뉴에 Wayland처럼 보여도 실제로는 X11로 오른 경우가 있고, 반대도 있습니다. 그래서 반드시 로그인 후 `echo $XDG_SESSION_TYPE`와 `loginctl show-session`으로 확인하는 편이 안전합니다.

    GNOME 계열에서 GDM이 Wayland를 비활성화했는지 점검할 때는 보통 아래 파일을 봅니다. 다만 배포판에 따라 경로와 기본값은 조금 다를 수 있습니다.

    grep -nE '^[# ]*WaylandEnable=' /etc/gdm/custom.conf 2>/dev/null
    grep -nE '^[# ]*DefaultSession=' /etc/gdm/custom.conf 2>/dev/null

    `WaylandEnable=false`가 있으면 GDM에서 Wayland가 비활성화돼 Xorg 세션으로 기울 수 있습니다. 물론 이 파일 하나로 모든 경우가 설명되지는 않지만, “왜 계속 X11로만 뜨지?” 할 때 가장 먼저 볼 만한 지점입니다.

    문제 5. 원격 제어는 되는데 화면 공유만 불안정하거나, 반대로 공유는 되는데 입력 전달이 이상하다

    이 경우 세션보다 원격 도구가 무엇을 전제로 설계됐는지를 먼저 보는 편이 맞습니다. X11 전용 방식에 기대는 도구는 Wayland에서 입력 주입이 제한될 수 있고, Wayland 친화적인 도구는 portal/PipeWire가 정상일 때 훨씬 깔끔하게 동작하기도 합니다. 원격 업무 비중이 높다면, 세션 선택 전에 쓰는 도구 목록부터 적어두고 지원 범위를 확인하는 게 훨씬 현실적입니다.

    7. 검증과 결과 확인: 무엇이 잘 된 상태인가

    세션을 바꾸고 “대충 되는 것 같네요”로 끝내면 다시 같은 장애를 만납니다. 아래 기준을 통과해야 정상으로 보는 편이 안전합니다.

    • 세션 타입이 의도한 값으로 일관되게 올라온다.
    • 브라우저 화면 공유, 스크린샷, 녹화가 같은 절차로 반복 재현된다.
    • 창 이동, 전체화면 전환, 외부 모니터 hotplug 시 깜빡임이나 멈춤이 반복되지 않는다.
    • `journalctl -b`와 `journalctl –user -b`에 같은 그래픽/portal 오류가 누적되지 않는다.
    • X11 전용 앱은 XWayland 또는 X11 세션에서 예측 가능한 방식으로 동작한다.

    운영 검증용으로는 이런 식의 미니 점검 스크립트도 자주 씁니다. 새 장비나 배포판을 올린 뒤 결과를 남기기 좋아요.

    echo "session=$XDG_SESSION_TYPE desktop=$XDG_CURRENT_DESKTOP"
    loginctl show-session "$XDG_SESSION_ID" -p Type -p Remote -p State
    ps -e -o comm | grep -E 'Xorg|Xwayland|gnome-shell|kwin_wayland' || true
    systemctl --user --no-pager --full status pipewire xdg-desktop-portal | sed -n '1,20p'
    journalctl --user -b --no-pager | grep -Ei 'portal|pipewire|screencast' | tail -n 20

    실제로 써보면 Wayland는 “평소 데스크톱 사용감”이 좋고, X11은 “도구가 예상대로 움직이는 범위”가 넓습니다. 어느 쪽이든 로그가 조용하고, 자주 쓰는 워크플로가 재현 가능하게 유지되는 쪽이 오래 갑니다.

    Wayland X11 비교 결과와 검증 항목을 요약한 Linux 데스크톱 대시보드 이미지

    세션 종류, 로그 상태, 화면 공유, 멀티 모니터 안정성을 검증하는 결과 요약 이미지입니다.

    8. 선택 가이드: 이런 경우엔 Wayland, 이런 경우엔 X11

    여기서는 애매하게 말하지 않겠습니다. 실제 선택 기준이 있어야 운영이 편합니다. 아래 표는 Linux 데스크톱을 세팅할 때 바로 써먹기 좋은 판단표입니다.

    사용 시나리오 추천 이유 같이 점검할 것
    노트북, 터치패드 제스처, HiDPI, 최신 GNOME/KDE Wayland 일상 사용감과 혼합 배율 대응이 좋음 `XDG_SESSION_TYPE`, 외부 모니터 연결, portal 기반 화면 공유
    GUI 자동화, `xdotool`, 좌표 클릭 매크로, 구형 테스트 도구 X11 기존 자동화 생태계와 충돌이 적음 Xorg 세션 고정 여부, 도구별 창 제어 재현성
    일반 개발용 워크스테이션 Wayland 우선 브라우저, IDE, 터미널 중심이면 장점이 큼 화면 공유, 녹화, Electron/GTK/Qt 앱 혼용 상태
    특정 원격 제어 솔루션이 업무 핵심 X11 우선 검토 입력 전달과 캡처 방식 제약이 적은 경우가 많음 원격 도구의 Wayland 지원 범위, 세션 자동 전환 여부
    NVIDIA 조합에서 세션이 자꾸 바뀌거나 깜빡임이 있음 교차 테스트 후 결정 세션보다 드라이버/DM 조합 영향이 클 수 있음 GDM 설정, 실제 세션 타입, 커널/사용자 저널 경고
    문제 원인 분리가 안 되는 초기 장애 대응 Wayland와 X11 둘 다 유지 세션 구조 문제와 드라이버 문제를 분리하기 쉬움 동일 작업 재현, 로그 차이, 앱 백엔드 강제 실행

    정리하면 선택 기준은 꽤 분명합니다.

    • 개인용 Linux 노트북이나 일반 데스크톱이면 Wayland부터 시작하는 편이 맞습니다.
    • 업무 핵심이 GUI 자동화, 특수 원격 도구, 오래된 캡처 툴이라면 X11을 기본값으로 두는 편이 덜 피곤합니다.
    • 둘 중 하나로 못 박기 어렵다면, 로그인 화면에서 두 세션을 모두 선택 가능하게 남겨두는 게 좋습니다. 장애 대응 속도가 꽤 달라집니다.

    관련 글로 Linux 디스플레이 트러블슈팅 가이드도 함께 보시면 원인 분리가 훨씬 빨라집니다.

    사용자 유형별로 Wayland와 X11 중 어떤 선택이 더 맞는지 요약한 이미지입니다.

    9. 마무리: 운영 리스크 기준으로 고르면 덜 흔들립니다

    데스크톱도 결국 “취향”보다 “재현 가능한 안정성”으로 봐야 합니다. 새 기술이라고 무조건 옮기면 다른 층위의 장애를 떠안을 수 있고, 익숙한 방식만 고집하면 현대 하드웨어의 장점을 놓칠 수도 있습니다. Wayland와 X11도 정확히 그 구도입니다.

    일반적인 Linux 데스크톱, 노트북, 멀티 모니터, 최신 GNOME/KDE라면 Wayland를 먼저 써보는 편이 좋습니다. 입력과 스케일링, 일상 사용감, 보안 모델까지 종합하면 장점이 분명하거든요. 대신 화면 녹화, 원격 제어, GUI 자동화, 구형 업무 앱이 핵심이라면 X11을 유지하는 편이 더 합리적입니다. 이건 보수적인 선택이라기보다, 장애 비용을 줄이는 선택에 가깝습니다.

    결국 기준은 단순합니다. 평소 작업이 더 부드러운 쪽이 아니라, 자주 쓰는 워크플로가 덜 깨지는 쪽을 고르면 됩니다. Wayland X11 비교에서 이 기준만 놓치지 않으면, 괜한 종교전 없이 자기 환경에 맞는 답을 찾기 쉬워집니다.

    FAQ

    Wayland가 무조건 더 빠른가요?

    아닙니다. 최신 데스크톱에서 입력과 스케일링 체감이 좋은 경우가 많지만, 실제 운영 만족도는 compositor, 드라이버, portal, 앱 호환성에 따라 달라집니다.

    X11은 이제 완전히 버려도 되는 기술인가요?

    그렇게 보긴 어렵습니다. 레거시 도구, GUI 자동화, 특정 원격 제어, 오래된 업무 앱에서는 여전히 실용적입니다. 업무가 거기에 걸려 있다면 X11이 더 좋은 선택일 수 있습니다.

    무엇부터 점검하면 가장 덜 헤맬까요?

    `echo $XDG_SESSION_TYPE`, `loginctl show-session`, 프로세스 확인, portal/PipeWire 상태, 같은 작업의 교차 재현 테스트 순서로 보면 됩니다. 이 순서가 세션 문제와 드라이버 문제를 분리하는 데 꽤 유용합니다.

  • [Linux] 리눅스 네트워크 설정 실패 회고: 1년 운영 경험으로 배운 베스트 프랙티스

    [Linux] 리눅스 네트워크 설정 실패 회고: 1년 운영 경험으로 배운 베스트 프랙티스

    리눅스 네트워크 설정 실패 회고: 1년 운영 경험으로 배운 베스트 프랙티스

    리눅스 서버를 1년 정도 꾸준히 운영하다 보면, 결국 한 번쯤은 리눅스 네트워크 설정 실패를 겪게 되더라고요. 저도 홈랩에서 Ubuntu Server, Rocky Linux 계열, Debian 계열을 번갈아 굴리면서 꽤 여러 번 삽질했습니다 ㅎㅎ 특히 원격으로 붙어 있는 서버에서 네트워크를 잘못 건드리면, 그 순간 SSH가 끊기고 화면 앞에서 멍해지는 경험을 하게 됩니다. 이 글은 그런 실수들을 그냥 흑역사로 남기지 않고, 리눅스 서버 운영 관점에서 무엇을 조심해야 하는지 정리한 회고입니다.

    이번 글에서는 제가 실제로 자주 부딪혔던 iptables(아이피테이블즈, 리눅스 패킷 필터/방화벽) 실수, DNS(Domain Name System) 설정 꼬임, netplan(넷플랜, Ubuntu 계열 네트워크 설정 도구) 문제를 중심으로 풀어보겠습니다. 화려한 이론보다, 운영하면서 왜 망가졌는지와 어떻게 복구했는지가 더 중요하거든요.

    리눅스 네트워크 설정 실패를 설명하는 홈랩 서버 네트워크 구성 이미지

    홈랩에서 라우터, 스위치, 리눅스 서버, 관리용 노트북이 연결된 전체 네트워크 개요를 보여주는 이미지입니다.

    1. 왜 리눅스 네트워크 설정 실패가 운영에서 치명적인가

    서버에서 네트워크는 그냥 연결만 되면 끝나는 영역처럼 보이는데, 실제로는 아닙니다. 서비스 장애의 시작점이 되는 경우가 많습니다. CPU나 메모리는 눈에 보이게 올라가지만, 네트워크는 조용히 잘못되는 경우가 많거든요.

    • SSH는 되는데 외부 패키지 저장소 접근이 안 되는 경우
    • IP는 붙었는데 DNS 조회가 안 돼서 애플리케이션이 죽는 경우
    • 방화벽 규칙이 꼬여서 특정 포트만 막히는 경우
    • 재부팅 후 설정이 다르게 올라오는 경우

    여기서 중요한 포인트! 리눅스 네트워크 설정 실패는 대부분 한 번에 크게 터지지 않습니다. 처음엔 “어? 왜 이렇게 느리지?” 정도로 시작하다가, 나중엔 서비스가 안 뜨는 식으로 번지더라고요. 저도 처음엔 이게 뭔가 싶었는데, 결국 원인은 기본값을 너무 믿었던 데 있었습니다.

    2. 쉽게 말해 보는 핵심 개념: IP, Gateway, DNS, Firewall

    복잡해 보여도 네트워크는 몇 가지만 분리해서 보면 정리가 됩니다. 쉽게 말해, 서버가 네트워크에서 길을 찾고, 이름을 해석하고, 누굴 통과시킬지 결정하는 과정입니다.

    구성 요소 역할 리눅스 네트워크 설정 실패 증상
    IP Address 서버 자신의 주소 같은 대역 충돌, 접속 불가
    Gateway 다른 네트워크로 나가는 출구 외부 통신 실패
    DNS 이름을 IP로 바꾸는 해석기 도메인 접근 실패, 업데이트 실패
    Firewall 들어오고 나가는 트래픽 제어 특정 포트만 차단, 간헐적 장애

    운영 입장에서 보면 순서도 중요합니다. 보통은 링크(Link, 물리/가상 NIC 상태)가 살아 있는지 확인하고, 그다음 IP, 라우팅, DNS, 방화벽 순으로 봐야 합니다. 근데 저도 예전에는 바로 iptables부터 의심했었거든요. 실제로는 게이트웨이 한 줄이 빠진 경우가 더 많았습니다.

    3. 1년 운영하면서 가장 많이 했던 리눅스 네트워크 설정 실패 세 가지

    3-1. iptables 실수: 기본 정책부터 DROP으로 바꿨다가 SSH 차단

    iptables 실수는 진짜 한 번은 꼭 겪습니다. 저도 “보안을 좀 더 깔끔하게 하자”는 생각으로 INPUT 기본 정책을 DROP으로 바꿨다가, SSH 허용 규칙 적용 순서를 잘못 넣어서 바로 접속이 끊긴 적이 있습니다. 콘솔이 붙어 있어서 망정이지, 원격 장비였으면 더 골치 아팠을 겁니다.

    3-2. DNS 설정 꼬임: ping은 되는데 apt와 curl이 실패

    이건 초보 때보다 오히려 익숙해진 뒤에 더 자주 터지더라고요. IP 통신은 되는데 저장소 접근이 안 되면 대개 DNS 설정 문제였습니다. 특히 systemd-resolved를 쓰는 환경에서 /etc/resolv.conf를 직접 고쳐 버리면, 재부팅이나 네트워크 재시작 때 다시 꼬이는 경우가 있었습니다.

    3-3. netplan 문제: YAML 들여쓰기 하나로 네트워크가 안 올라옴

    netplan 문제는 문법 자체는 단순한데, YAML이 공백에 민감해서 생각보다 자주 발목을 잡습니다. 제가 직접 해보니, 설정을 급하게 바꾸다가 NIC 이름을 잘못 쓰거나 들여쓰기를 틀리면 부팅 후 네트워크가 예상과 다르게 올라오더라고요. 특히 ens18과 eth0를 혼동하는 케이스가 많았습니다.

    4. 실전 구현: 안전하게 네트워크 설정 바꾸는 절차

    여기서는 제가 지금도 지키는 최소한의 변경 절차를 정리해보겠습니다. 핵심은 한 번에 바꾸지 않고, 검증 가능한 작은 단위로 진행하는 겁니다.

    1. 현재 상태를 백업합니다.
    2. 활성 NIC 이름과 라우팅 테이블을 확인합니다.
    3. IP, Gateway, DNS를 한 번에 다 바꾸지 말고 순서대로 적용합니다.
    4. 원격 작업이면 세션을 하나 더 열어 둡니다.
    5. 방화벽은 허용 규칙을 먼저 넣고 기본 정책을 나중에 바꿉니다.
    6. 적용 후 즉시 ping, ss, resolvectl, journalctl로 검증합니다.
    ip -brief address
    ip route
    resolvectl status
    ss -tulpen
    sudo cp /etc/netplan/01-netcfg.yaml /etc/netplan/01-netcfg.yaml.bak
    sudo iptables-save > ~/iptables-backup.rules

    이 정도만 해도 복구 속도가 확실히 빨라집니다. 저는 예전엔 백업 없이 바로 수정했었는데, 그게 제일 큰 실수였습니다.

    4-1. netplan 예시

    network:
      version: 2
      renderer: networkd
      ethernets:
        ens18:
          dhcp4: false
          addresses:
            - 192.168.0.50/24
          routes:
            - to: default
              via: 192.168.0.1
          nameservers:
            addresses:
              - 1.1.1.1
              - 8.8.8.8

    적용 전에 꼭 문법을 다시 보셔야 합니다. 원격 서버라면 특히 더요.

    sudo netplan generate
    sudo netplan try

    netplan try는 정말 유용합니다. 잘못 적용하면 자동으로 이전 상태로 되돌릴 수 있어서, 저처럼 리눅스 네트워크 설정 실패를 많이 겪은 사람에게는 안전벨트 같은 기능이거든요.

    리눅스 네트워크 설정 실패 대응을 위한 netplan 설정 검증 이미지

    netplan YAML 파일과 터미널에서 ip, route, resolvectl로 확인하는 과정을 함께 보여주는 이미지입니다.

    4-2. iptables 적용 예시

    sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
    sudo iptables -A INPUT -i lo -j ACCEPT
    sudo iptables -P INPUT DROP
    sudo iptables -P FORWARD DROP
    sudo iptables -P OUTPUT ACCEPT

    여기서 순서가 중요합니다. 허용 규칙을 먼저 넣고 마지막에 정책을 조정해야 합니다. 저는 예전에 이 순서를 반대로 했다가 SSH가 끊겼습니다. iptables 실수의 전형적인 사례죠. “드디어 됐다!” 싶어서 정책부터 바꾸면, 바로 사고 납니다.

    4-3. DNS 점검 예시

    ping -c 2 8.8.8.8
    getent hosts example.com
    resolvectl query example.com
    cat /etc/resolv.conf

    IP로는 통신되는데 도메인만 안 되면 DNS 설정을 보시면 됩니다. 반대로 DNS 설정이 정상인데도 외부가 안 되면 라우팅이나 방화벽 쪽일 가능성이 높습니다.

    5. ⚠️ 실제 리눅스 네트워크 설정 실패 사례와 복구 과정

    이 섹션은 좀 더 현실적인 회고입니다. 보기엔 사소한데, 운영에선 꽤 아픈 문제들이었습니다.

    5-1. 게이트웨이 누락으로 내부망만 되고 외부망은 불가

    서버 간 통신은 되는데 패키지 업데이트가 안 됐습니다. 처음엔 DNS 문제인 줄 알았는데, 실제로 써보니까 기본 게이트웨이(default gateway)가 빠져 있더라고요. 내부망은 같은 서브넷이라 통신되지만, 외부로 나갈 출구가 없으니 당연히 실패한 겁니다.

    • ip route에 default 경로가 있는지 확인
    • 게이트웨이 IP가 실제 라우터 주소와 일치하는지 확인
    • 정적 라우트가 있으면 우선순위도 함께 점검

    5-2. NIC 이름 오인으로 netplan 적용 실패

    가상화 환경을 옮기고 나서 기존 설정을 그대로 썼는데 NIC 이름이 달랐습니다. 예전엔 eth0였는데 새 환경에서는 ens18로 올라왔거든요. 문법은 맞는데 적용이 안 되니 한참 헤맸습니다. 이것도 리눅스 네트워크 설정 실패의 전형적인 경우죠.

    • ip -brief link로 실제 인터페이스 이름 확인
    • 클라우드 이미지나 VM 템플릿 복제 시 이름이 바뀔 수 있음
    • 문법보다 장치 식별이 먼저라는 점을 기억

    5-3. 방화벽 저장 누락으로 재부팅 후 규칙 유실

    이것도 흔합니다. 세션 중에는 잘 되는데 재부팅하면 다시 열려 있거나 다시 막혀 있죠. 이유는 간단합니다. 런타임 규칙과 영구 저장 규칙을 분리해서 이해하지 않았기 때문입니다. 배포판마다 iptables-persistent나 별도 서비스 관리 방식이 다를 수 있으니, 현재 서버가 어떤 방식으로 규칙을 유지하는지 먼저 확인하셔야 합니다.

    6. 네트워크 베스트 프랙티스: 제가 지금은 이렇게 운영합니다

    네트워크 베스트 프랙티스는 거창한 게 아닙니다. 사고를 줄이는 습관에 가깝습니다. 1년 동안 리눅스 서버를 굴려보니 아래 원칙이 가장 효과가 좋았습니다.

    항목 예전 방식 지금 방식
    설정 변경 한 번에 수정 단계별 변경 후 즉시 검증
    방화벽 정책부터 변경 허용 규칙 먼저 적용
    DNS 안 되면 아무 파일이나 수정 현재 resolver 구조부터 확인
    netplan 바로 apply generate, try 후 적용
    운영 기록 기억에 의존 변경 로그를 간단히 남김
    • 변경 전 현재 상태를 텍스트로 저장해 둡니다.
    • 운영 서버는 콘솔 접근 수단을 반드시 확보합니다.
    • DNS와 라우팅을 분리해서 테스트합니다.
    • 보안 강화를 할 때는 서비스 영향도를 먼저 봅니다.
    • 재부팅 후에도 유지되는지 꼭 확인합니다.

    혹시 이런 경험 있으신가요? 설정은 맞는 것 같은데, 재부팅 한 번 하고 나면 갑자기 안 되는 상황이요. 그런 경우는 대부분 “현재 세션에만 반영된 상태”와 “영구 설정 파일”이 다를 때가 많습니다. 저도 처음엔 헷갈렸는데, 이 구분만 해도 리눅스 네트워크 설정 실패 문제 절반은 줄어듭니다.

    iptables 실수 방지를 위한 리눅스 네트워크 설정 실패 예방 이미지

    SSH 허용 규칙을 먼저 넣고 기본 정책을 나중에 적용하는 안전한 iptables 흐름을 설명하는 이미지입니다.

    7. 검증 방법: 적용 후 무엇을 확인해야 하나

    설정이 들어갔다고 끝이 아닙니다. 검증이 빠지면 다음 장애 때 원인을 다시 처음부터 찾게 됩니다.

    1. 링크 상태: 인터페이스가 UP인지 확인합니다.
    2. 주소 확인: IP와 서브넷이 의도대로 붙었는지 봅니다.
    3. 라우팅 확인: default route가 맞는지 확인합니다.
    4. 이름 해석: DNS 질의가 정상인지 테스트합니다.
    5. 포트 확인: 서비스가 실제로 바인딩됐는지 확인합니다.
    6. 외부 접속: 다른 장비에서 실제 접속 테스트를 합니다.
    ip -brief address
    ip route
    getent hosts github.com
    ss -tulpen
    journalctl -u systemd-networkd --since "10 minutes ago"

    저는 여기에 하나를 더 합니다. 바로 “재부팅 검증”입니다. 지금 당장 되느냐보다, 다음 부팅에서도 그대로 올라오느냐가 운영에서는 더 중요하거든요.

    IP, 라우팅, DNS, 포트 상태를 체크리스트 형태로 확인하는 검증 결과 이미지를 넣는 자리입니다.

    8. 자주 묻는 질문과 정리

    Q1. ping이 되면 네트워크는 정상 아닌가요?

    꼭 그렇지는 않습니다. ICMP는 되는데 TCP 포트가 막혀 있을 수 있고, IP는 되는데 DNS 설정이 안 될 수도 있습니다. 그래서 계층별로 봐야 합니다.

    Q2. netplan만 쓰면 리눅스 네트워크 설정 문제가 다 해결되나요?

    아닙니다. netplan은 선언형 설정 도구일 뿐이고, 실제 렌더러가 networkd인지 NetworkManager인지도 봐야 합니다. 도구를 맹신하면 오히려 원인 파악이 늦어집니다.

    Q3. iptables와 nftables 중 무엇을 써야 하나요?

    배포판과 운영 환경에 따라 다릅니다. 중요한 건 이름보다도 현재 시스템이 어떤 프레임워크를 실제로 쓰는지 확인하는 겁니다. 혼용된 상태에서 규칙을 만지는 게 더 위험하더라고요.

    리눅스 네트워크 설정 실패를 줄이는 가장 좋은 방법은 천재적인 설정이 아니라, 평범한 검증 습관입니다. 저도 1년 동안 서버를 굴리면서 별별 문제를 다 겪었는데, 결국 살아남는 방법은 백업, 단계별 적용, 즉시 검증이었습니다. 다음 글에서는 방화벽 정책을 서비스별로 나누는 방법이나, 홈랩 기준 VLAN 분리 전략도 다뤄볼 예정입니다. 이전 글에서 다뤘던 로그 확인 습관과 함께 보시면 더 도움이 되실 겁니다.

    리눅스 서버 운영에서 네트워크 베스트 프랙티스를 한눈에 정리한 요약 인포그래픽 이미지입니다.

    정리하자면 이렇습니다.

    • 리눅스 네트워크 설정 실패는 대부분 기본 개념보다 적용 순서와 검증 부족에서 시작됩니다.
    • iptables 실수는 규칙 순서와 영구 저장 여부를 먼저 확인하셔야 합니다.
    • DNS 설정은 resolver 구조를 이해하고 나서 건드려야 덜 꼬입니다.
    • netplan 문제는 YAML 문법과 NIC 이름 확인이 핵심입니다.
    • 네트워크 베스트 프랙티스는 결국 안전한 변경 절차를 습관으로 만드는 일입니다.