13년차의 서버실

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

[태그:] 인프라 운영

  • [인프라] OpenStack Ironic으로 베어메탈 서버 자동 프로비저닝 실전 사례

    [인프라] OpenStack Ironic으로 베어메탈 서버 자동 프로비저닝 실전 사례

    [인프라] OpenStack Ironic으로 베어메탈 서버 자동 프로비저닝 실전 사례

    OpenStack Ironic 베어메탈 프로비저닝을 처음 제대로 붙잡았던 이유는 단순했습니다. 가상머신은 너무 익숙했는데, 정작 성능이 중요한 워크로드는 결국 물리 서버에 올려야 했거든요. 문제는 여기서부터였습니다. 서버를 한 대씩 BIOS 확인하고, PXE 부팅 잡고, 운영체제 이미지 밀어 넣고, 네트워크 맞추고, 다시 검증하는 과정을 반복하다 보니 사람이 할 일이 너무 많아지더라고요. 특히 서버가 3대, 5대, 10대로 늘어나면 그때부터는 실수도 같이 늘어납니다. 그래서 저는 OpenStack Ironic(아이로닉, 베어메탈 프로비저닝 서비스)을 도입해서 베어메탈 자동화 흐름을 만들었고, 실제로 운영 편의성이 얼마나 달라지는지 체감했습니다.

    혹시 이런 경험 있으신가요? 분명 같은 모델 서버인데도 한 대는 잘 올라오고, 한 대는 PXE에서 멈추고, 또 다른 한 대는 디스크 클리닝(cleaning) 단계에서 실패하는 상황이요. 저도 처음엔 이게 뭔가 싶었는데, 흐름을 한 번 제대로 잡고 나니까 서버 프로비저닝이 훨씬 예측 가능해졌습니다. 이번 글에서는 제가 직접 해보면서 정리한 OpenStack Ironic 베어메탈 프로비저닝 실전 사례를 기준으로, 개념부터 실제 등록과 배포, 그리고 삽질 포인트까지 차근차근 풀어보겠습니다.

    OpenStack Ironic, PXE 네트워크, 이미지 서비스, 관리 네트워크, 물리 서버 간의 전체 흐름을 한눈에 보여주는 아키텍처 예시입니다.

    1. 왜 베어메탈 자동화가 필요했는가

    제가 처음 자동화를 검토했던 환경은 대규모 클라우드까지는 아니고, 홈랩과 소규모 사내 테스트 랙(rack, 서버 장비 거치대)이 섞인 형태였습니다. 쿠버네티스 워커 노드, 스토리지 노드, 네트워크 기능 테스트 장비가 계속 늘어나는데, 설치 방법은 여전히 수동이었죠. 이러면 반복 작업이 많아질 뿐 아니라, 누가 언제 어떤 이미지로 설치했는지 추적도 어려워집니다.

    • 반복 작업 제거: 동일 OS 이미지와 네트워크 정책을 여러 서버에 일관되게 적용
    • 실수 감소: 수동 설치 중 IP, RAID, 부트 모드 입력 오류 감소
    • 재현성 확보: 장애 후 재배포(re-deploy) 시간이 짧아짐
    • 운영 표준화: 서버 모델이 달라도 등록 절차를 표준 흐름으로 묶기 쉬움

    여기서 중요한 포인트가 있습니다. 베어메탈 자동화는 단순히 설치를 편하게 하는 수준이 아니라, 물리 서버를 가상 리소스처럼 다루기 시작하는 첫 단계라는 점입니다. 실제로 써보니까 운영 관점이 바뀌더라고요. 서버를 장비가 아니라 풀(pool, 자원 집합)로 보게 됩니다.

    2. OpenStack Ironic 개념을 쉽게 풀어보면

    쉽게 말해 Ironic은 물리 서버를 API로 관리해 주는 서비스입니다. 가상머신을 Nova(노바, 컴퓨트 서비스)로 다루듯이, 베어메탈 서버는 Ironic으로 전원 제어, 부팅 제어, 이미지 배포, 상태 전환을 처리합니다.

    처음엔 용어가 좀 낯설 수 있습니다. 저도 처음엔 conductor가 뭐고 inspector가 뭐고 헷갈렸거든요. 실무에서는 아래 정도만 먼저 잡아도 흐름이 보입니다.

    구성 요소 역할 현장에서 이해한 방식
    Ironic API 베어메탈 요청 접수 사용자나 자동화 도구가 호출하는 입구
    Ironic Conductor 실제 작업 실행 전원 제어, 배포, 상태 변경을 수행하는 엔진
    PXE / iPXE 네트워크 부팅 로컬 디스크 없이 초기 부팅을 유도하는 통로
    IPA Ironic Python Agent 부팅 후 디스크 작업과 배포를 수행하는 에이전트
    BMC Baseboard Management Controller IPMI나 Redfish로 원격 전원/부트 제어
    Glance 이미지 저장소 배포할 운영체제 이미지를 보관하는 위치

    Ironic 베어메탈 프로비저닝 활용 사례로는 쿠버네티스 노드 자동 공급, Ceph(세프, 분산 스토리지) 스토리지 노드 배포, 테스트 장비 재설치 자동화가 대표적입니다. 특히 성능 오버헤드가 민감한 데이터 처리 워크로드에서는 가상화보다 베어메탈이 낫다고 판단하는 경우가 꽤 있죠.

    3. 실전 사례: OpenStack Ironic 베어메탈 프로비저닝 구성 방식

    이번 사례는 특정 벤더 기능에 기대지 않는, 비교적 범용적인 구성입니다. 관리 네트워크(management network)와 프로비저닝 네트워크(provisioning network)를 분리하고, BMC는 Redfish(레드피시, 표준 하드웨어 관리 API) 또는 IPMI 기반으로 접근했습니다. 디스크는 로컬 SSD를 사용했고, 운영체제 이미지는 일반적인 리눅스 클라우드 이미지 계열을 기준으로 배포했습니다.

    1. 서버 랙 장착 및 BMC IP 할당
    2. Ironic에 노드 등록
    3. 포트(MAC 주소) 연결
    4. 하드웨어 introspection(인트로스펙션, 자동 탐지) 또는 수동 속성 입력
    5. deploy image 연결
    6. manageable → available 상태 전환
    7. 베어메탈 서버 deploy 실행 및 부팅 검증

    이 흐름을 한 번 만들어 두면 신규 서버 반입 때 정말 편합니다. 예전에는 체크리스트를 사람이 읽으면서 따라갔는데, 이제는 등록 정보만 맞으면 나머지는 훨씬 기계적으로 흘러갑니다. 드디어 됐다 싶었던 순간이 여기였네요.

    4. OpenStack Ironic 베어메탈 프로비저닝 구현 단계

    4-1. 네트워크와 부트 방식 먼저 정리

    여기서 가장 많이 꼬입니다. Ironic 자체보다 PXE 네트워크가 더 큰 함정일 때가 많거든요. DHCP 범위, TFTP 또는 HTTP 부트 경로, UEFI/Legacy BIOS 모드가 서로 안 맞으면 배포가 시작도 안 됩니다. 저는 가능하면 UEFI 기준으로 통일하려고 했고, 스위치 포트 VLAN도 먼저 점검했습니다.

    openstack network list
    openstack subnet list
    openstack baremetal driver list
    openstack baremetal node list

    처음엔 단순히 노드만 등록하면 될 줄 알았는데, 실제로는 부팅 경로와 네트워크 경로가 먼저 안정화되어야 하더라고요. 특히 ToR(Top of Rack, 랙 상단 스위치) 쪽 포트 프로파일이 꼬여 있으면 증상이 굉장히 애매합니다.

    OpenStack Ironic 베어메탈 프로비저닝 PXE 부팅 흐름 이미지

    프로비저닝 VLAN, DHCP, HTTP/TFTP 부트, Ironic Conductor, 대상 서버 사이의 실제 데이터 흐름을 설명하는 이미지입니다.

    4-2. 노드 등록과 BMC 정보 입력

    다음은 물리 서버를 Ironic에 등록하는 단계입니다. 아래 예시는 개념을 보여주기 위한 형태이고, 환경에 맞게 driver와 driver-info 항목은 조정하시면 됩니다.

    openstack baremetal node create \
      --driver redfish \
      --name bm-node-01
    openstack baremetal node set bm-node-01 \
      --driver-info redfish_address=https://192.0.2.10/redfish/v1/Systems/1 \
      --driver-info redfish_username=admin \
      --driver-info redfish_password='REDACTED' \
      --property cpus=16 \
      --property memory_mb=65536 \
      --property local_gb=480 \
      --resource-class baremetal-general

    여기서 팁 하나요. 저는 초반에 서버 스펙을 전부 수동 입력했었는데, 장비가 늘어나면 이게 은근히 실수를 부릅니다. introspection을 쓸 수 있는 환경이라면 자동 탐지를 적극 활용하는 편이 낫습니다. 다만 자동 탐지 결과도 100% 맹신하면 안 되고, 디스크 수나 NIC 매핑은 꼭 눈으로 한 번 보셔야 합니다.

    4-3. 포트 연결과 배포 이미지 지정

    openstack baremetal port create aa:bb:cc:dd:ee:ff \
      --node bm-node-01
    openstack baremetal node set bm-node-01 \
      --instance-info image_source=<IMAGE_UUID> \
      --instance-info root_gb=50

    이미지 소스는 보통 Glance 이미지 UUID를 사용합니다. 저 같은 경우에는 운영체제 이미지 템플릿을 최소화해서 관리했는데, 이게 나중에 패치 기준점 맞추기가 편하더라고요. 이미지가 너무 많으면 자동화가 아니라 이미지 스파게티가 됩니다.

    4-4. 상태 전환과 배포 실행

    openstack baremetal node manage bm-node-01
    openstack baremetal node provide bm-node-01
    openstack baremetal node deploy bm-node-01

    Ironic은 상태(state)가 중요합니다. manageable, available, active 같은 상태 전환을 이해하지 못하면 왜 명령이 거부되는지 감이 안 옵니다. 저도 처음엔 deploy가 왜 안 되나 한참 봤는데, 앞단 상태가 안 맞았던 적이 꽤 많았습니다. 이 부분은 로그와 상태를 같이 보면서 익숙해지는 수밖에 없어요.

    5. OpenStack Ironic 베어메탈 프로비저닝: 운영 기준과 설정 예시

    실전에서는 단순 명령 몇 줄보다 운영 기준이 더 중요합니다. 어떤 노드를 어떤 용도로 쓸지, cleaning(클리닝, 디스크 초기화) 정책을 어디까지 강제할지, RAID 구성은 수동으로 둘지 자동으로 둘지 같은 정책이 먼저 있어야 자동화가 깔끔해집니다.

    nodes:
      - name: bm-node-01
        driver: redfish
        resource_class: baremetal-general
        boot_mode: uefi
        bmc:
          address: https://192.0.2.10/redfish/v1/Systems/1
          username: admin
        network:
          provisioning_mac: aa:bb:cc:dd:ee:ff
        deploy:
          image: ubuntu-base
          root_gb: 50

    저는 내부적으로 노드 인벤토리를 YAML 형태로 관리해 두고, 이후 등록 자동화 스크립트에서 읽어 쓰는 방식으로 정리했습니다. 이게 생각보다 큽니다. 사람이 콘솔에서 매번 입력하면 언젠가 오타가 나거든요.

    • 리소스 클래스(resource class)를 미리 정의해 워크로드 분류를 단순화
    • 부트 모드 통일로 장애 포인트 감소
    • 이미지 수 최소화로 운영 복잡도 감소
    • 인벤토리 코드화로 반복 등록 절차 표준화
    OpenStack Ironic 노드 등록과 상태 전환 운영 화면 이미지

    노드 이름, 드라이버, BMC 정보, 포트, 상태 전환 흐름이 한 번에 보이도록 구성한 운영 화면 예시입니다.

    6. ⚠️ 실제로 겪었던 문제와 해결 방법

    이 섹션이 아마 제일 현실적일 겁니다. 문서만 보면 다 잘 될 것 같은데, 실제 현장에서는 작은 불일치가 줄줄이 터집니다. 삽질 좀 했습니다 ㅎㅎ

    6-1. PXE는 되는데 베어메탈 배포가 중간에 멈추는 문제

    증상은 간단했습니다. 서버가 네트워크 부팅까지는 들어오는데, 에이전트가 올라온 뒤 이미지 배포 단계에서 멈추는 겁니다. 처음엔 이미지 문제인 줄 알았는데, 실제 원인은 프로비저닝 네트워크의 MTU와 업링크 설정 차이였습니다. 패킷 손실이 눈에 띄게 보이지 않아서 더 헷갈렸습니다.

    • 에이전트가 올라오는지 확인
    • Conductor 로그에서 타임아웃 여부 확인
    • 프로비저닝 VLAN 경로 MTU 점검
    • 이미지 다운로드 경로의 HTTP 접근성 확인

    6-2. BMC 연결은 되는데 전원 제어가 불안정한 문제

    이건 장비별 구현 차이 영향이 컸습니다. 같은 표준을 써도 Redfish 구현이 미묘하게 다르더라고요. 그래서 저는 가능하면 펌웨어 상태를 먼저 맞추고, 드라이버 타입을 혼용하지 않도록 정리했습니다. 환경에 따라 IPMI보다 Redfish가 더 안정적일 때도 있었고, 반대 경우도 있었습니다. 결국 중요한 건 표준 이름보다 내 장비에서 실제로 일관되게 동작하느냐였습니다.

    6-3. 디스크 선택이 꼬여 OS가 원하지 않는 장치에 설치되는 문제

    NVMe와 SATA SSD가 같이 있는 서버에서 이 문제가 나왔습니다. 에이전트가 잡는 장치 순서와 사람이 기대한 순서가 다를 수 있거든요. 그래서 배포 정책에서 디스크 선택 기준을 명확히 정하고, 가능하면 장치 특성 기반으로 선택하도록 운영 기준을 잡았습니다. 여기서 중요한 포인트! 베어메탈 자동화는 결국 하드웨어 다양성을 어떻게 통제하느냐의 싸움입니다.

    7. 검증과 결과: 서버 프로비저닝이 얼마나 달라졌나

    배포 후에는 단순히 active 상태만 보면 안 됩니다. 저는 최소한 아래 항목은 꼭 확인했습니다.

    1. Ironic 상태가 active로 전환되었는지 확인
    2. 운영체제가 정상 부팅되고 SSH 접근이 되는지 확인
    3. 의도한 NIC와 IP 정책이 적용되었는지 확인
    4. 디스크 레이아웃과 파일시스템이 기대값과 맞는지 확인
    5. 재배포 시 동일 절차가 반복 가능했는지 확인
    openstack baremetal node show bm-node-01
    openstack baremetal node validate bm-node-01

    실제로 써보니까 가장 큰 변화는 속도보다도 예측 가능성이었습니다. 예전에는 “이번에도 잘 되겠지”에 가까웠다면, 자동화 이후에는 “어디가 실패하면 무엇을 보면 된다”로 바뀌었습니다. 운영자는 이 차이를 굉장히 크게 느낍니다. 장애 대응 시간도 줄고, 신규 서버 투입 부담도 확실히 낮아졌습니다.

    OpenStack Ironic 베어메탈 프로비저닝 결과 검증 대시보드 이미지

    active 상태 전환, 배포 성공, 네트워크 연결, 운영체제 부팅 검증 결과를 시각적으로 요약한 이미지입니다.

    8. 정리: OpenStack Ironic 도입 전에 꼭 체크할 것

    OpenStack Ironic 베어메탈 프로비저닝은 분명 강력합니다. 하지만 설치 툴 하나 추가하는 수준으로 보면 실망할 수 있습니다. 이건 물리 서버 운영 체계를 재정리하는 일에 가깝거든요. 제가 직접 해보니 아래 네 가지가 성공 확률을 많이 좌우했습니다.

    • 네트워크 표준화: 프로비저닝 경로와 VLAN 정책을 먼저 안정화할 것
    • 하드웨어 편차 관리: 서버 모델, NIC, 디스크 구성을 가능한 한 단순화할 것
    • 상태 기반 운영: node state와 로그를 함께 보는 습관을 들일 것
    • 인벤토리 자동화: 등록 정보를 코드처럼 관리할 것
    도입 전 방식 Ironic 도입 후 방식
    서버별 수동 설치 API 기반 반복 배포
    작업자 숙련도 의존 절차 표준화
    문제 원인 추적 어려움 상태와 로그 기반 추적
    재설치 시간 편차 큼 재현성 높은 서버 프로비저닝

    혹시 지금 물리 서버를 계속 수동으로 설치하고 계시다면, 작은 랩 환경에서라도 먼저 시도해 보시는 걸 권합니다. 처음엔 헷갈립니다. 저도 그랬습니다. 근데 한 번 흐름을 이해하고 나면 이거 진짜 편하더라고요. 다음 글에서는 Ironic과 Inspector(인스펙터, 하드웨어 자동 탐지)를 엮어서 장비 등록을 더 줄이는 방향도 다뤄볼 예정입니다. 이전 글에서 다뤘던 PXE 네트워크 설계와 함께 보시면 훨씬 연결이 잘 되실 겁니다.

    OpenStack Ironic 도입 전후 비교 요약 인포그래픽

    수동 설치와 자동화된 베어메탈 프로비저닝의 차이, 그리고 도입 전 체크리스트를 한 장으로 정리한 요약 이미지입니다.

    9. 자주 묻는 질문

    Q1. Ironic은 꼭 OpenStack 전체를 다 써야 하나요?

    반드시 그렇지는 않습니다. 다만 실제 운영에서는 네트워크, 이미지, 인증 체계와의 연동이 중요해서 보통은 OpenStack 구성 요소와 함께 보는 편이 자연스럽습니다.

    Q2. 베어메탈 자동화가 작은 환경에도 의미가 있나요?

    의미 있습니다. 특히 재설치가 잦거나, 쿠버네티스 노드를 자주 교체하는 환경이라면 규모가 작아도 체감이 큽니다.

    Q3. Ironic 베어메탈 프로비저닝 활용 사례에서 가장 먼저 검증할 것은 뭔가요?

    저는 네트워크 부팅 경로와 BMC 안정성을 먼저 봅니다. 이 두 가지가 흔들리면 나머지 자동화가 전부 불안정해지거든요.

  • [Kubernetes] Karpenter 오토스케일러 1년 사용 후기: 비용 절감과 성능 최적화 회고

    [Kubernetes] Karpenter 오토스케일러 1년 사용 후기: 비용 절감과 성능 최적화 회고

    [Kubernetes] Karpenter 오토스케일러 1년 사용 후기

    Karpenter 오토스케일러 후기를 한 줄로 먼저 말씀드리면, EKS 운영에서 노드 증설 속도와 비용 통제가 동시에 좋아졌습니다. 저도 처음엔 “Cluster Autoscaler(클러스터 오토스케일러)로도 되는데 굳이?” 싶었거든요. 그런데 서비스가 늘고, 배치 작업이 섞이고, 팀별로 워크로드 특성이 달라지니까 기존 방식이 점점 답답해지더라고요. 특히 Kubernetes 오토스케일링을 노드 그룹 중심으로만 운영하면 빈 자원이 남아도는 순간이 꽤 자주 생깁니다. 실무에서 결국 비용은 숫자로 돌아오니까요.

    제가 지난 1년 동안 홈랩과 업무 환경에서 비슷한 패턴으로 실험하고 운영해보니, Karpenter는 “노드를 미리 크게 잡아두는 습관”을 줄여주는 쪽에 강점이 있었습니다. 다만 마법은 아닙니다. 설정을 대충 하면 오히려 인스턴스 종류가 너무 넓게 열리거나, 반대로 너무 좁아서 스케일이 안 붙는 일도 생깁니다. 오늘 글에서는 제가 실제로 겪었던 삽질까지 포함해서, 왜 도입했고 어떻게 운영했고 무엇을 조심해야 하는지 정리해보겠습니다.

    Karpenter 오토스케일러 후기용 EKS 아키텍처 개요 이미지

    Amazon EKS 환경에서 Karpenter가 대기 중인 Pod를 보고 적절한 EC2 노드를 프로비저닝하는 전체 흐름을 보여주는 아키텍처 이미지입니다.

    Karpenter란 무엇인가: Kubernetes 오토스케일링을 더 세밀하게

    쉽게 말해 Karpenter는 Pod(파드)의 요구사항을 보고 바로 노드를 만들어주는 노드 프로비저너(node provisioner, 노드 생성기)입니다. 기존 Cluster Autoscaler는 미리 만들어둔 Auto Scaling Group(오토 스케일링 그룹)이나 Managed Node Group(관리형 노드 그룹)을 기준으로 증설을 판단합니다. 반면 Karpenter는 “지금 이 파드가 CPU, 메모리, 아키텍처, 스팟 여부를 이렇게 요구하네? 그럼 여기에 맞는 노드를 하나 올리자”에 더 가깝습니다.

    처음엔 이게 뭔가 싶었는데, 실제로 써보니까 포인트가 명확하더라고요. 노드 그룹을 여러 개 미리 쪼개서 관리하던 부담이 줄어듭니다. 그리고 워크로드에 맞는 인스턴스 타입을 더 유연하게 고를 수 있어서 AWS EKS 비용 절감 관점에서 꽤 체감이 됐습니다.

    항목 Cluster Autoscaler Karpenter
    기준 노드 그룹 중심 파드 요구사항 중심
    유연성 사전 정의된 그룹 범위 내 인스턴스 선택 폭이 넓음
    비용 최적화 그룹 설계에 크게 의존 빈 자원 축소에 유리
    운영 포인트 노드 그룹 관리 요구사항과 제약 조건 설계

    왜 도입했는가: 노드 프로비저닝 병목이 보이기 시작했습니다

    제가 Karpenter를 본격적으로 보기 시작한 이유는 세 가지였습니다.

    • 배치와 실시간 서비스가 같은 클러스터에 섞이면서 노드 성격이 달라졌습니다.
    • Managed Node Group을 늘릴수록 운영 포인트가 많아졌습니다.
    • 스팟(Spot)과 온디맨드(On-Demand)를 상황에 따라 유연하게 섞고 싶었습니다.

    예전에는 팀별로 노드 그룹을 하나씩 나누면 깔끔해 보였거든요. 근데 시간이 지나면 태그, 라벨, 테인트(Taint, 특정 워크로드 제한), AMI, 서브넷, 보안 그룹까지 관리 포인트가 계속 늘어납니다. 결국 “지금 이 파드가 왜 여기 못 올라가지?”를 찾는 시간이 길어지더라고요. Karpenter는 그 문제를 완전히 없애주진 않지만, 적어도 노드 프로비저닝을 워크로드 관점으로 다시 정리하게 만들어줍니다.

    실전 구현: EKS에 Karpenter 붙이면서 제가 잡았던 기준

    여기서 중요한 포인트! Karpenter를 처음 붙일 때는 욕심내서 모든 워크로드를 한 번에 옮기지 않는 게 좋습니다. 저도 처음엔 한 번에 바꾸려다가 삽질 좀 했습니다 ㅎㅎ 작은 NodePool부터 시작해서 점진적으로 옮기는 게 훨씬 안전했습니다.

    1. 기본 EKS 클러스터와 워커 노드를 준비합니다.
    2. Karpenter용 IAM 권한과 인터럽션 처리 큐를 구성합니다.
    3. Karpenter 컨트롤러를 설치합니다.
    4. NodeClass와 NodePool을 최소 범위로 선언합니다.
    5. 특정 워크로드만 먼저 태워서 동작을 검증합니다.

    1. 컨트롤러 설치 전 확인할 것

    • 클러스터의 OIDC provider가 연결되어 있는지
    • 서브넷과 보안 그룹에 Karpenter가 찾을 수 있는 태그가 있는지
    • 기존 CNI, CoreDNS, metrics-server 같은 필수 애드온이 정상인지

    특히 태그는 정말 중요합니다. 이거 하나 빠지면 로그만 보고 한참 헤맵니다.

    2. 설치 예시

    export CLUSTER_NAME=my-eks
    export AWS_REGION=ap-northeast-2
    export KARPENTER_NAMESPACE=karpenter
    
    helm repo add eks https://aws.github.io/eks-charts
    helm repo update
    
    helm upgrade --install karpenter eks/karpenter \
      --namespace ${KARPENTER_NAMESPACE} \
      --create-namespace \
      --set settings.clusterName=${CLUSTER_NAME} \
      --set settings.interruptionQueue=${CLUSTER_NAME} \
      --set serviceAccount.create=true

    환경마다 IAM Role for Service Account(IRSA, 서비스어카운트용 IAM 역할) 구성 방식은 다를 수 있습니다. 그래서 설치 커맨드 자체보다도, 컨트롤러가 EC2 인스턴스 생성과 조회 권한을 제대로 갖고 있는지를 먼저 보는 게 중요합니다.

    3. NodeClass / NodePool 예시

    아래 예시는 운영에서 자주 쓰는 방향을 단순화한 것입니다. 핵심은 “너무 넓지도, 너무 좁지도 않게” 시작하는 겁니다.

    apiVersion: karpenter.k8s.aws/v1beta1
    kind: EC2NodeClass
    metadata:
      name: default
    spec:
      amiFamily: AL2
      subnetSelectorTerms:
        - tags:
            karpenter.sh/discovery: my-eks
      securityGroupSelectorTerms:
        - tags:
            karpenter.sh/discovery: my-eks
      role: KarpenterNodeRole-my-eks
    ---
    apiVersion: karpenter.sh/v1beta1
    kind: NodePool
    metadata:
      name: general
    spec:
      template:
        metadata:
          labels:
            workload: general
        spec:
          nodeClassRef:
            name: default
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
            - key: karpenter.sh/capacity-type
              operator: In
              values: ["spot", "on-demand"]
            - key: node.kubernetes.io/instance-type
              operator: In
              values: ["m5.large", "m5.xlarge", "m6i.large", "m6i.xlarge"]
      disruption:
        consolidationPolicy: WhenUnderutilized
      limits:
        cpu: "100"

    제가 직접 해보니 처음부터 인스턴스 타입을 수십 개 열어두는 것보다, 검증된 계열 몇 개로 시작하는 편이 좋았습니다. 나중에 확장하는 건 쉽지만, 처음부터 범위를 넓히면 왜 그 타입이 선택됐는지 추적이 어려워지거든요.

    Karpenter 오토스케일러 후기의 NodePool 구성 이미지

    EC2NodeClass와 NodePool이 연결되어 서브넷, 보안 그룹, 인스턴스 타입, capacity type을 결정하는 구조를 보여주는 구성 이미지입니다.

    4. 테스트용 워크로드로 검증

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: inflate
    spec:
      replicas: 6
      selector:
        matchLabels:
          app: inflate
      template:
        metadata:
          labels:
            app: inflate
        spec:
          nodeSelector:
            workload: general
          containers:
            - name: inflate
              image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
              resources:
                requests:
                  cpu: "500m"
                  memory: "512Mi"

    이렇게 요청 리소스(requests, 예약 자원)를 명시한 테스트 파드를 올려보면 스케줄링이 안 되는 순간 Karpenter가 새 노드를 붙이는 흐름을 직관적으로 볼 수 있습니다. 드디어 됐다! 하는 순간이 여기서 나옵니다.

    운영하면서 체감한 장점: 비용 절감과 성능 최적화 포인트

    Karpenter 오토스케일러 후기에서 가장 많이 물어보는 게 “그래서 돈이 진짜 줄었나요?”인데요, 제 경우엔 AWS EKS 비용 절감 효과를 숫자 하나로 단정하긴 어려웠습니다. 워크로드 패턴이 계속 바뀌니까요. 다만 분명했던 변화는 있었습니다.

    • 대기 중인 파드 처리 속도가 좋아졌습니다. 노드 그룹 단위보다 반응이 직관적이었습니다.
    • 빈 자원이 남는 노드가 줄었습니다. 특히 야간 배치 종료 후 차이가 컸습니다.
    • 스팟과 온디맨드 혼합 전략을 잡기 쉬웠습니다.
    • 팀별 노드 그룹 난립을 줄이면서 운영 복잡도가 낮아졌습니다.

    성능 최적화라는 게 꼭 CPU 사용률 그래프만 예쁘게 만드는 건 아니더라고요. 스케줄링 지연이 줄고, 워크로드 특성에 맞는 노드가 더 빨리 생기고, 과하게 큰 노드를 덜 쓰게 되는 것 자체가 운영 품질 향상입니다. 실제로 써보니까 이 부분이 더 크게 다가왔습니다.

    ⚠️ 주의사항과 트러블슈팅: 여기서 많이 막혔습니다

    이 섹션은 좀 중요합니다. Karpenter는 잘 되면 정말 편한데, 안 될 때는 로그와 이벤트를 꼼꼼히 봐야 하거든요.

    1. 서브넷/보안 그룹 태그 누락

    가장 흔했습니다. 컨트롤러는 떠 있는데 노드가 안 생깁니다. 원인은 대개 discovery 태그 누락이었습니다.

    kubectl logs -n karpenter deploy/karpenter
    kubectl describe nodepool general
    kubectl get events -A --sort-by=.lastTimestamp

    해결: 서브넷과 보안 그룹 선택 조건에 맞는 태그가 있는지 다시 확인했습니다. 이건 문서보다 실제 AWS 리소스 태그 화면이 더 빠르더라고요.

    2. 인스턴스 요구 조건을 너무 빡빡하게 잡음

    저도 처음엔 특정 세대, 특정 사이즈만 고집했습니다. 그러면 순간적으로 가용한 인스턴스가 없을 때 스케일이 멈춥니다. 특히 스팟만 강제하면 더 민감해집니다.

    • 인스턴스 패밀리를 1개만 두지 말 것
    • 사이즈를 한 단계 이상 열어둘 것
    • 스팟만 고정하지 말고 온디맨드 fallback을 고려할 것

    3. DaemonSet(데몬셋) 자원 계산을 과소평가

    이거 진짜 많이 놓칩니다. 노드 하나가 올라오면 CNI, 로그 에이전트, 모니터링 에이전트 같은 DaemonSet이 먼저 자리를 먹거든요. 파드 request를 딱 맞춰 잡아두면 생각보다 노드가 더 필요해집니다.

    해결: 시스템 DaemonSet이 차지하는 CPU/메모리를 감안해서 워크로드 request를 다시 계산했습니다. 제가 직접 해보니 이 조정만으로도 불필요한 추가 스케일링이 꽤 줄었습니다.

    4. Consolidation(통합 축소) 설정이 너무 공격적일 때

    유휴 노드를 잘 정리해주는 기능은 좋지만, 워크로드 패턴이 들쭉날쭉하면 노드가 자주 교체될 수 있습니다. 배치가 짧게 반복되는 환경에서는 오히려 흔들릴 수 있겠더라고요.

    해결: 처음엔 보수적으로 운영하고, 패턴이 보인 뒤에 consolidation 정책을 조정했습니다. 이건 정답이 하나가 아니라 워크로드 성격을 타는 부분입니다.

    검증과 결과 확인: 무엇을 보면 잘 되고 있다고 판단할까

    Karpenter 오토스케일러 후기를 쓰면서 가장 조심한 부분이 여기입니다. 비용 수치나 처리량 수치를 함부로 적으면 안 되거든요. 그래서 저는 운영에서 다음 지표를 기준으로 판단했습니다.

    1. Pending Pod가 줄어드는 속도
    2. 피크 시간 이후 유휴 노드가 정리되는지
    3. 스팟 중단 이후 재스케줄링이 안정적인지
    4. 특정 노드 그룹에 과도하게 몰리던 패턴이 줄었는지
    kubectl get nodes
    kubectl get pods -A -o wide
    kubectl top nodes
    kubectl top pods -A
    kubectl describe pod <pending-pod-name>

    제가 실제로 써보니까, 가장 눈에 띄는 건 노드 수 자체보다도 대기 중인 파드가 오래 머무르지 않는 것이었습니다. 클러스터가 바빠질 때 운영자가 덜 초조해집니다. 이건 체감이 꽤 큽니다.

    AWS EKS 비용 절감과 Karpenter 오토스케일링 결과 이미지

    파드 증가 시 노드가 빠르게 늘고, 부하가 줄면 유휴 노드가 정리되는 모습을 대시보드 형태로 보여주는 결과 이미지입니다.

    정리 표: 어떤 환경에 특히 잘 맞았나

    환경 Karpenter 적합도 이유
    배치와 웹 서비스 혼합 높음 워크로드별 노드 요구사항 차이가 큼
    스팟 적극 활용 높음 capacity type 전략을 유연하게 설계 가능
    소규모 고정 트래픽 보통 정적 노드 그룹만으로도 충분할 수 있음
    규제가 강한 고정 인스턴스 정책 보통 이하 유연성 장점이 줄어듦

    자주 묻는 질문: 도입 전에 많이 받았던 질문

    Q1. Cluster Autoscaler 대신 무조건 Karpenter가 답인가요?

    그건 아닙니다. 워크로드가 단순하고 노드 그룹이 적다면 기존 방식도 충분히 안정적입니다. 다만 노드 그룹이 많아지고 Kubernetes 오토스케일링 설계가 복잡해질수록 Karpenter의 장점이 커졌습니다.

    Q2. 스팟만 써도 되나요?

    중단 허용성이 높은 워크로드라면 가능하지만, 핵심 서비스는 온디맨드 fallback을 함께 두는 게 마음이 편합니다. 저도 처음엔 공격적으로 갔다가 다시 섞어서 운영했습니다.

    Q3. 어떤 팀이 먼저 도입해보면 좋을까요?

    배치 작업, CI 러너, 일시적인 이벤트성 워크로드가 있는 팀부터 추천드립니다. 효과가 빨리 보입니다.

    Kubernetes 오토스케일링 비교를 보여주는 Karpenter 요약 이미지

    Cluster Autoscaler와 Karpenter의 차이점, 추천 사용 시나리오, 운영 포인트를 한 장에 요약한 비교 인포그래픽 이미지입니다.

    마무리: 1년 써보니 결국 설계가 반이었습니다

    Karpenter는 분명 좋은 도구입니다. 하지만 설치했다고 끝나는 종류는 아니었습니다. 어떤 인스턴스를 허용할지, 어떤 워크로드를 어디에 태울지, 스팟과 온디맨드를 어떻게 섞을지, consolidation을 얼마나 공격적으로 둘지 같은 정책 설계가 훨씬 중요했습니다. 저도 처음엔 도구가 다 알아서 해줄 줄 알았는데, 실제로 써보니까 “잘 설계된 제약 조건”이 핵심이더라고요.

    그래도 1년 기준으로 돌아보면, Karpenter 오토스케일러 후기는 꽤 긍정적입니다. 노드 프로비저닝이 더 민첩해졌고, 불필요한 여유 자원을 줄이는 방향으로 운영 습관이 바뀌었습니다. 혹시 지금 EKS에서 노드 그룹이 점점 복잡해지고 있다면, 작은 워크로드 하나부터 붙여보셔도 좋겠습니다. 다음 글에서는 HPA(Horizontal Pod Autoscaler, 파드 수평 확장)와 Karpenter를 같이 운영할 때 생기는 타이밍 이슈도 다뤄볼 예정입니다. 이전 글의 EKS 운영 체크리스트와 함께 보시면 흐름이 더 잘 잡히실 겁니다.

    1년 운영 후 배운 점, 추천 도입 순서, 주의할 설정 포인트를 요약한 마무리 이미지입니다.

  • [Cloud] Cloudflare WAF vs AWS WAF: 장애 사례와 트러블슈팅

    [Cloud] Cloudflare WAF vs AWS WAF: 장애 사례와 트러블슈팅

    Cloudflare WAF vs AWS WAF: 실제 장애 사례와 트러블슈팅 가이드

    Cloudflare WAF vs AWS WAF를 비교해 달라는 요청을 받으면, 저는 기능표부터 꺼내지 않습니다. 실무에서는 웹 방화벽 자체보다도 장애가 났을 때 얼마나 빨리 원인을 좁히고, 우회하고, 복구하느냐가 더 중요하거든요. 저도 초반에는 “둘 다 룰만 잘 넣으면 되는 거 아닌가?” 싶었는데, 운영을 해보면 보는 지점도 다르고 디버깅 방식도 꽤 다르더라고요. 특히 보안 장애는 애플리케이션 오류처럼 로그 한 줄로 바로 드러나지 않아서 초기에 헤매기 쉽습니다.

    이번 글은 Cloudflare WAF vs AWS WAF를 단순 스펙 비교가 아니라, 보안 장애와 트러블슈팅 관점에서 정리한 글입니다. 정상 사용자가 막혔을 때 어디부터 볼지, 403이 떴을 때 WAF 문제인지 앱 문제인지 어떻게 가를지, Cloudflare 앞단과 AWS 원본 구성이 함께 있을 때 무엇을 먼저 확인해야 하는지 실무 기준으로 풀어보겠습니다.

    Cloudflare 엣지와 AWS 내부 구간에서 WAF가 어떻게 배치되는지 한눈에 보여주는 아키텍처 이미지가 들어갈 자리입니다.

    1. 왜 Cloudflare WAF vs AWS WAF 비교가 실무에서 중요한가

    두 제품 모두 HTTP 요청을 검사하고 차단하지만, 트래픽을 관찰하는 위치와 운영자가 원인을 추적하는 방식이 다릅니다. Cloudflare WAF는 보통 사용자 요청이 원본 서버에 도달하기 전에 Cloudflare 엣지에서 먼저 평가합니다. AWS WAF는 CloudFront, Application Load Balancer(ALB), API Gateway 같은 AWS 보호 대상 리소스에 연결해 정책을 적용하죠.

    그래서 같은 403 응답이어도 의미가 완전히 같지는 않습니다.

    • Cloudflare WAF: 원본까지 요청이 가지 않았을 가능성이 큽니다.
    • AWS WAF: AWS 리소스 경계까지는 도달했지만 연결된 Web ACL에서 차단됐을 수 있습니다.
    • 애플리케이션 403: WAF가 아니라 인증, 인가, 세션, 경로 권한 로직 문제일 수 있습니다.

    이 구분을 초반에 못 하면 개발팀은 앱 로그만 보고, 보안팀은 룰만 보고, 인프라팀은 CDN만 보게 됩니다. 장애 대응은 결국 관찰 지점을 분리하는 데서 시작됩니다. 이거 하나만 정리해도 대응 속도가 눈에 띄게 달라지더라고요.

    2. Cloudflare WAF vs AWS WAF 핵심 차이

    쉽게 말해 Cloudflare WAF는 인터넷 입구 쪽에 더 가깝고, AWS WAF는 AWS 리소스 보호에 더 밀착돼 있습니다. 둘 다 관리형 룰, 커스텀 룰, IP 제어, 레이트 리밋 같은 공통 기능은 있지만 운영 감각은 꽤 다릅니다. 특히 로그를 어디서 보고, 어떤 헤더를 기준으로 클라이언트를 식별하는지가 실무에서는 정말 중요합니다.

    항목 Cloudflare WAF AWS WAF
    주요 배치 위치 Cloudflare 엣지 CloudFront, ALB, API Gateway 등 AWS 리소스 앞단
    장애 체감 지점 사용자 입장에서 즉시 차단 체감 AWS 서비스 경계에서 정책 적용
    원인 추적 포인트 Security Events, 커스텀 룰, 관리형 룰, 봇/레이트 리밋 정책 Web ACL, Rule priority, sampled requests, 로그 대상
    복합 구성 시 주의점 원본 IP 전달 헤더와 프록시 체인 확인 커스텀 IP/레이트 룰의 forwarded IP 설정 점검

    제가 현업에서 자주 강조하는 포인트가 하나 있습니다. WAF는 보안 장비이면서 동시에 트래픽 해석기라는 점입니다. 어떤 헤더를 보고 판단하는지, 어떤 경로와 메서드에 민감한지, 차단이 어느 단계에서 일어나는지 모르면 운영이 금방 꼬입니다.

    Cloudflare WAF가 잘 맞는 경우

    • 전 세계 사용자 대상 서비스라 엣지 차단 효과가 중요한 경우
    • DDoS 완화, 봇 제어, 캐시와 함께 운영하는 경우
    • 원본 서버까지 악성 요청을 최대한 보내고 싶지 않은 경우

    AWS WAF가 잘 맞는 경우

    • AWS 인프라 중심으로 서비스가 구성된 경우
    • CloudFront, ALB, API Gateway와 일관되게 묶어 관리하고 싶은 경우
    • 변경 이력과 IaC로 정책을 통제하고 싶은 경우

    3. 실전 구현: 장애 추적을 쉽게 만드는 기본 구성

    보안 장애에서 제일 아쉬운 순간은 로그를 안 남겼다는 사실을 뒤늦게 깨달을 때입니다. 처음엔 번거로워 보여도, WAF는 차단보다 관측 체계를 먼저 만들어두는 편이 훨씬 낫습니다. 저는 보통 아래 원칙으로 시작합니다.

    1. WAF 정책을 처음부터 전부 block으로 넣지 않습니다.
    2. 가능하면 count, log, sampled requests 중심으로 먼저 동작을 봅니다.
    3. 원본 IP, 요청 경로, 메서드, User-Agent, Host 헤더를 함께 확인합니다.
    4. Cloudflare와 AWS를 같이 쓸 때는 어느 계층에서 막혔는지 구분 가능한 로그 기준을 먼저 정합니다.

    Cloudflare 앞단 + AWS 원본 구조 테스트 예시

    아래 명령은 공개 엔드포인트에서 상태 코드와 응답 헤더를 빠르게 비교할 때 많이 씁니다. 다만 프록시 환경의 원본 IP 판별은 단순 curl 한 번으로 결론내리기보다, Cloudflare 이벤트와 AWS WAF 로그를 함께 맞춰 보는 편이 안전합니다.

    # 기본 응답 헤더와 상태 코드 확인
    curl -s -o /dev/null -D - https://example.com/
    
    # 로그인 경로 확인
    curl -s -o /dev/null -D - https://example.com/login
    
    # 헬스체크 경로 확인
    curl -s -o /dev/null -D - https://example.com/health
    

    단순해 보여도 꽤 유용합니다. 정상 경로와 문제 경로를 나눠 보면 어느 계층에서 튕겼는지 감이 잡히거든요. 여기에 시간대까지 맞춰서 WAF 이벤트를 보면 훨씬 빨라집니다.

    AWS WAF Web ACL 설계 예시

    아래 YAML은 개념 설명용 예시입니다. 콘솔, CloudFormation, Terraform 문법과 1:1로 동일한 배포 파일은 아니고, 룰 우선순위와 관측 포인트를 이해하기 위한 샘플로 보시면 됩니다.

    webAcl:
      name: prod-web-acl
      scope: REGIONAL
      defaultAction:
        allow: {}
      rules:
        - name: block-known-bad-ip
          priority: 10
          action:
            block: {}
        - name: rate-limit-login
          priority: 20
          action:
            block: {}
        - name: managed-common-rules
          priority: 30
          overrideAction:
            none: {}
      visibilityConfig:
        cloudWatchMetricsEnabled: true
        sampledRequestsEnabled: true
        metricName: prod-web-acl
    

    실무에서는 priority 때문에 생각보다 많이 헷갈립니다. 저도 처음엔 관리형 룰이 뒤에서 잡을 줄 알았는데, 앞단 커스텀 룰에서 이미 차단되고 있었던 적이 있었습니다. 그래서 장애가 나면 저는 항상 룰 순서부터 먼저 봅니다.

    Cloudflare WAF vs AWS WAF 규칙 우선순위 비교 이미지

    AWS WAF의 Web ACL 우선순위와 Cloudflare 규칙 평가 흐름을 비교해 보여주는 이미지가 들어갈 자리입니다.

    4. 실제 장애 사례 1: 정상 사용자만 403이 뜨는 상황

    개발팀에서는 로그인 잘 된다고 하는데, 특정 사용자군만 403이 뜨는 경우가 있습니다. 처음엔 브라우저 문제나 쿠키 문제처럼 보여도, 실제로는 정상 트래픽이 봇이나 의심 요청으로 오탐되는 경우가 제법 있습니다. 이런 케이스는 특히 배포 직후나 로그인 경로 변경 직후에 잘 나타납니다.

    Cloudflare 쪽에서는 보통 아래 순서로 보는 게 빨랐습니다.

    1. 해당 시간대 Security Events에서 URI와 IP를 확인합니다.
    2. 차단 룰이 관리형 룰인지, 커스텀 룰인지 분리합니다.
    3. 특정 국가, ASN, User-Agent, 봇 정책 조건이 걸려 있는지 봅니다.
    4. 원본 서버 로그가 비어 있는지 함께 확인해 앞단 차단인지 가릅니다.

    AWS WAF에서는 접근 방식이 조금 다릅니다.

    1. 연결된 Web ACL과 보호 대상 리소스를 먼저 확인합니다.
    2. sampled requests에서 어떤 룰이 매치됐는지 확인합니다.
    3. ALB 액세스 로그나 애플리케이션 로그와 시간대를 맞춰 봅니다.
    4. 원본 서버에 요청이 실제로 도달했는지 확인합니다.

    핵심은 WAF 403과 앱 403을 분리하는 겁니다. 앱 로그가 전혀 없으면 앞단 차단 가능성이 높고, 앱 로그에 인증 실패나 권한 오류가 남으면 애플리케이션 이슈일 수 있습니다. 당연한 이야기 같지만, 장애 상황에서는 이 기본 분기가 제일 강력합니다.

    5. 실제 장애 사례 2: Cloudflare 뒤에 AWS WAF를 붙였더니 원본 IP 판단이 꼬인 경우

    이 케이스도 자주 나옵니다. Cloudflare가 프록시 역할을 하고 뒤에 AWS WAF가 또 있는 구조였는데, 로그인 경로의 레이트 리밋이 이상하게 동작하는 상황이었습니다. 사용자 한 명이 과도 요청을 보낸 것도 아닌데 여러 사용자가 묶여 차단되는 느낌이었죠.

    원인은 대개 단순합니다. AWS WAF 커스텀 IP 기반 룰이나 rate-based rule이 실제 클라이언트 IP가 아니라 프록시 구간의 주소를 기준으로 평가하면 의도와 다르게 동작할 수 있습니다. 이럴 때는 원본 IP 전달 방식과 forwarded IP 설정을 같이 점검해야 합니다.

    • CF-Connecting-IP 또는 X-Forwarded-For를 원본에서 어떻게 수집하는지 확인합니다.
    • AWS WAF의 커스텀 IP/Geo/ASN/rate-based rule이 어떤 헤더를 기준으로 평가하는지 확인합니다.
    • 프록시 체인이 여러 단계라면 어느 주소가 first IP로 해석되는지 테스트합니다.
    • 레이트 리밋은 바로 차단보다 관찰 모드로 먼저 검증합니다.
    # 응답 헤더와 상태 코드 확인
    curl -s -o /dev/null -D - https://example.com/login
    
    # 특정 경로 반복 호출로 제한 반응 확인
    for i in $(seq 1 5); do
      curl -s -o /dev/null -w "%{http_code}\n" https://example.com/api/login
    done
    

    직접 겪어보면, 이 문제는 기능 차이보다도 배치 구조를 정확히 이해하지 못한 상태에서 둘을 같이 붙일 때 더 자주 터집니다. 그래서 Cloudflare WAF vs AWS WAF를 비교할 때는 누가 더 좋으냐보다, 누가 어디서 무엇을 보고 차단하느냐를 먼저 정리해야 합니다.

    6. 주의사항: 트러블슈팅할 때 자주 놓치는 포인트

    이 섹션은 실제 장애 대응에서 체감이 큽니다. 아래 항목만 체크해도 원인 추적 속도가 훨씬 빨라집니다.

    • 규칙 우선순위: 먼저 매치된 룰에서 이미 차단됐을 수 있습니다.
    • 허용 목록: 관리자 IP만 열어두고 모바일 회선, VPN, 사내 NAT 대역을 빼먹는 경우가 많습니다.
    • 경로 예외 처리: /health, /callback, /api/upload 같은 경로는 성격이 달라 별도 예외가 필요할 수 있습니다.
    • 메서드 차이: GET은 통과하는데 POST만 막히는 경우가 생각보다 자주 있습니다.
    • 헤더 기반 탐지: User-Agent 누락, 비정상 Content-Type, 특이한 Host 헤더 때문에 오탐이 날 수 있습니다.
    • 배포 타이밍: 앱 변경과 WAF 룰 변경이 겹치면 원인 추적이 훨씬 어려워집니다.

    장애 중에는 룰을 무작정 끄기보다 영향 범위가 좁은 예외부터 적용하는 편이 안전합니다. 예를 들어 전체 관리형 룰을 비활성화하기보다 특정 URI나 특정 파라미터에 한해 임시 예외를 주는 식이죠. 이렇게 해야 보안 노출을 최소화하면서 서비스는 살릴 수 있습니다.

    Cloudflare WAF vs AWS WAF 403 보안 장애 트러블슈팅 이미지

    403 응답이 났을 때 어떤 순서로 헤더, 로그, 룰 매치를 따라가야 하는지 보여주는 트러블슈팅 이미지가 들어갈 자리입니다.

    7. 검증 방법: 해결됐는지 어떻게 확인할까

    문제가 해결된 것처럼 보여도 검증이 약하면 같은 장애가 반복됩니다. 저는 보통 아래 순서로 확인합니다.

    1. 차단되던 실제 요청 패턴을 다시 재현합니다.
    2. 정상 사용자 시나리오와 악성 의심 시나리오를 둘 다 테스트합니다.
    3. WAF 로그에서 의도한 룰만 매치되는지 확인합니다.
    4. 애플리케이션 로그와 응답 시간도 같이 봅니다.
    5. 임시 예외를 넣었다면 만료 계획과 원복 일정을 남깁니다.

    코드 블록은 아주 기본적인 확인용입니다. 여기서 중요한 건 상태 코드만 볼 게 아니라, 어느 레이어에서 응답이 만들어졌는지도 같이 보는 겁니다.

    # 정상 페이지 확인
    curl -I https://example.com/
    
    # 로그인 엔드포인트 확인
    curl -X POST -d "username=test&password=test" https://example.com/login -i
    
    # 헬스체크 경로 확인
    curl -I https://example.com/health
    

    검증에서 진짜 중요한 건 200 OK가 나왔는지만 보는 게 아닙니다. 원래 막아야 할 요청이 계속 막히는지도 꼭 같이 봐야 합니다. 서비스는 살렸는데 방화벽이 사실상 꺼진 상태가 되는 게 가장 위험하거든요.

    Cloudflare WAF vs AWS WAF 검증 결과 대시보드 이미지

    장애 조치 전후로 차단 이벤트와 정상 응답 비율이 어떻게 달라졌는지 보여주는 결과 대시보드 이미지가 들어갈 자리입니다.

    8. Cloudflare WAF vs AWS WAF, 무엇을 기준으로 선택할까

    정리하면 이렇습니다. 두 제품은 기능 몇 개만 비교해서 고를 대상이 아닙니다. 운영 팀의 시야, 로그 수집 체계, 서비스 배치 구조, 장애 대응 프로세스를 함께 봐야 선택이 훨씬 정확해집니다.

    질문 Cloudflare WAF 쪽이 유리한 경우 AWS WAF 쪽이 유리한 경우
    트래픽을 어디서 먼저 걸러야 하나? 인터넷 엣지에서 최대한 먼저 AWS 리소스 경계에서 정교하게
    운영 중심이 어디인가? CDN, 엣지 보안, 글로벌 트래픽 AWS 네이티브 리소스 중심
    트러블슈팅 포인트는? 원본 도달 전 차단 여부 Web ACL, sampled requests, 리소스 연결 상태
    복합 환경 난이도는? AWS와 조합 시 헤더와 원본 IP 해석 주의 프록시 뒤 커스텀 룰의 forwarded IP 설정 주의

    도입을 검토 중이라면 제품 비교표만 보지 말고 장애 시나리오를 먼저 적어보는 걸 권합니다. “로그인 POST가 갑자기 막히면 누가 어디서 무엇을 확인할까?”, “Cloudflare와 AWS WAF를 함께 쓰면 어느 계층에서 예외를 줄까?” 같은 질문이 실제 운영 품질을 좌우합니다.

    9. 마무리: 웹 방화벽은 도입보다 운영이 어렵습니다

    이번 글에서는 Cloudflare WAF vs AWS WAF를 웹 방화벽 비교 차원이 아니라, 보안 장애와 트러블슈팅 중심으로 정리했습니다. 직접 운영해보면 좋은 제품을 고르는 일보다 관찰 가능성을 확보하는 일이 더 중요하다는 걸 금방 느끼게 됩니다. 로그를 남기고, 룰 우선순위를 정리하고, 원본 IP 해석 기준을 분명히 해두면 장애 대응이 훨씬 편해집니다.

    핵심만 짚으면 아래 네 가지입니다.

    • Cloudflare WAF는 엣지에서 빠르게 차단하는 데 강점이 있습니다.
    • AWS WAF는 AWS 리소스와의 통합 운영에 강점이 있습니다.
    • 403 장애는 WAF, 프록시, 애플리케이션을 분리해서 봐야 빨리 해결됩니다.
    • 복합 구성에서는 헤더와 원본 IP 해석이 가장 자주 문제를 일으킵니다.

    다음 글에서는 WAF 예외 처리 패턴과 헬스체크, 로그인, API 업로드 경로를 안전하게 분리하는 방법도 다뤄보겠습니다. 이전에 정리한 리버스 프록시와 원본 IP 전달 방식 글도 함께 보시면 이해가 훨씬 빨라집니다.

    Cloudflare WAF vs AWS WAF 선택 기준 요약 인포그래픽

    도입 환경, 장애 대응 포인트, 운영 난이도를 기준으로 두 WAF를 요약 비교한 인포그래픽 이미지가 들어갈 자리입니다.

    자주 묻는 질문

    Cloudflare WAF와 AWS WAF를 같이 써도 되나요?

    됩니다. 다만 어디서 차단됐는지 구분할 수 있도록 로그, 헤더, 원본 IP 해석 기준을 먼저 정리해두는 게 좋습니다.

    보안 장애가 나면 가장 먼저 뭘 봐야 하나요?

    응답 코드만 보지 말고 원본 서버 로그 유무와 WAF 이벤트를 함께 보셔야 합니다. 그 분기만 정확히 잡아도 시간을 많이 줄일 수 있습니다.

    처음 도입할 때 바로 차단 정책으로 가도 될까요?

    권장하지 않습니다. 가능하면 count, log, sampled requests 중심으로 먼저 관찰하고 오탐 여부를 확인한 뒤 점진적으로 차단 강도를 올리는 편이 안전합니다.

  • [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교

    [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교

    [Linux 보안] SELinux vs AppArmor: 선택 가이드와 실전 비교

    리눅스 서버를 운영하다 보면 결국 한 번은 붙잡게 되는 주제가 있습니다. 바로 SELinux AppArmor 비교입니다. 방화벽만 잘 열고 닫는다고 끝나는 게 아니더라고요. 실제 운영 환경에서는 프로세스가 어디까지 접근할 수 있는지, 침해 사고가 났을 때 피해 범위를 얼마나 좁힐 수 있는지가 정말 중요합니다. 저도 홈랩에서 웹 서버, 컨테이너, 파일 공유 서비스를 이것저것 올려보면서 Linux 보안을 강화하려다가 SELinux(Security-Enhanced Linux)와 AppArmor(Application Armor)를 번갈아 만져봤습니다. 처음엔 둘 다 비슷해 보였는데, 직접 써보니까 운영 철학부터 관리 포인트까지 꽤 다르더라고요.

    혹시 이런 경험 있으신가요? 서비스는 분명 정상인데 권한 거부 때문에 접속이 안 되고, 로그를 보면 더 헷갈리는 상황 말입니다. 저도 처음엔 이게 뭔가 싶었는데, 기준만 잡고 보면 선택이 꽤 쉬워집니다. 이번 글에서는 SELinux vs AppArmor를 비교하면서, 어떤 환경에서 무엇을 선택하면 좋은지 경험 기준으로 풀어보겠습니다.

    SELinux AppArmor 비교를 보여주는 리눅스 보안 개요 다이어그램

    SELinux와 AppArmor가 커널 보안 계층에서 어떻게 동작하는지 보여주는 개요 이미지입니다.

    1. 왜 SELinux와 AppArmor가 중요한가

    쉽게 말해 둘 다 MAC(Mandatory Access Control, 강제 접근 통제) 계열입니다. 일반적인 파일 권한처럼 사용자가 알아서 결정하는 DAC(Discretionary Access Control, 임의 접근 통제)보다 훨씬 강하게 제어하는 방식이거든요. 프로세스가 root 권한을 가졌다고 해도, 보안 정책이 막고 있으면 파일을 읽거나 네트워크 동작을 하지 못하게 되는 겁니다.

    이게 왜 중요하냐면, 서비스 하나가 뚫렸을 때 전체 서버로 번지는 걸 줄여주기 때문이에요. 예를 들어 웹 서버 프로세스가 취약점으로 악용되더라도, 원래 접근하면 안 되는 디렉터리나 소켓을 막아두면 피해 확산을 줄일 수 있습니다. 제가 홈랩에서 리버스 프록시, 파일 서버, 테스트용 앱을 굴릴 때도 결국 마지막 안전장치 역할은 이런 보안 정책이 하더라고요. 평소엔 귀찮아 보여도, 문제 생기면 존재감이 정말 확실합니다.

    2. SELinux와 AppArmor 개념 설명

    2-1. SELinux는 라벨 기반입니다

    SELinux는 객체와 프로세스에 보안 컨텍스트(context, 보안 문맥) 라벨을 붙여서 제어합니다. 파일, 포트, 프로세스가 각각 어떤 타입인지 보고 허용 여부를 판단하는 방식이죠. 처음엔 정말 어렵습니다. 저도 파일 권한만 보다가 갑자기 컨텍스트를 보라니까 삽질 좀 했어요. 그런데 익숙해지면 정책이 꽤 정교하고 체계적이더라고요.

    • 파일과 디렉터리에 보안 라벨을 부여합니다.
    • 프로세스도 특정 도메인(domain, 실행 문맥)으로 실행됩니다.
    • 정책이 세밀해서 대규모 운영 환경에 잘 맞는 편입니다.

    2-2. AppArmor는 경로 기반입니다

    AppArmor는 파일 경로(path, 경로)를 기준으로 제어합니다. 그래서 사람이 읽고 이해하기가 상대적으로 훨씬 쉽습니다. 특정 실행 파일이 어느 경로를 읽고 쓸 수 있는지 프로파일(profile, 정책 파일)로 정의하는 방식이거든요. Ubuntu 계열에서 처음 보안 강화 기능을 접할 때 부담이 덜한 이유가 바로 여기에 있습니다.

    • 실행 파일별 프로파일을 적용합니다.
    • 경로 기반이라 정책을 읽기 쉽습니다.
    • 처음 도입할 때 학습 곡선이 비교적 완만합니다.

    3. SELinux와 AppArmor 비교: 무엇이 다른가

    여기서 중요한 포인트! SELinux AppArmor 비교를 할 때 단순히 “누가 더 강한가”로 보면 답이 잘 안 나옵니다. 실제로는 운영 방식에 맞는지가 훨씬 더 중요하거든요.

    항목 SELinux AppArmor
    정책 기준 라벨 기반 경로 기반
    학습 난이도 상대적으로 높음 상대적으로 낮음
    정책 세밀도 매우 세밀함 직관적이고 단순한 편
    대표 사용 환경 RHEL 계열에서 자주 사용 Ubuntu 계열에서 자주 사용
    초기 운영 체감 로그 분석이 중요 프로파일 관리가 중요
    적합한 상황 엄격한 통제, 장기 운영 빠른 도입, 단순한 서비스 보호

    제 경험상 정리하면 이렇습니다.

    • SELinux: 처음엔 어렵지만, 표준화된 운영과 세밀한 정책이 필요한 환경에서 정말 강합니다.
    • AppArmor: 빠르게 적용하고 이해하기 쉬워서 소규모 서비스나 초기 구축에 부담이 훨씬 적습니다.
    • 보안 강도만 볼 게 아니라, 팀이 정책을 유지할 수 있는지가 더 중요한 선택 기준이 됩니다.

    4. 실전 구현: SELinux 기본 확인과 적용

    이제 실전으로 가보겠습니다. 제가 직접 해보니 무조건 정책부터 건드리기보다, 현재 상태를 먼저 보는 게 훨씬 낫습니다. SELinux는 특히 더 그렇습니다. 상태 확인 없이 파일만 만지면 나중에 왜 막혔는지 추적이 훨씬 어려워지거든요.

    1. SELinux 상태를 확인합니다.
    2. 서비스 파일 컨텍스트를 점검합니다.
    3. 필요하면 적절한 컨텍스트를 다시 지정합니다.
    4. 로그를 보고 실제 차단 원인을 확인합니다.
    getenforce
    seststatus
    ls -Z /var/www
    ps -eZ | grep httpd

    getenforce는 현재 모드를 빠르게 보여줍니다. Enforcing이면 정책이 실제로 강제 적용 중이고, Permissive면 차단 대신 로그만 남깁니다. 처음 테스트할 땐 Permissive 모드에서 원인부터 보는 게 훨씬 편하더라고요.

    sudo semanage fcontext -a -t httpd_sys_content_t "/srv/myweb(/.*)?"  
    sudo restorecon -Rv /srv/myweb

    이 명령은 웹 서버 콘텐츠 디렉터리에 적절한 타입을 지정하는 예시입니다. 여기서 정말 중요한 건 chmod로 해결하려고 하지 말고 컨텍스트를 봐야 한다는 점이에요. 저도 예전에 권한은 맞는데 계속 403이 떠서 한참 헤맸는데, 알고 보니 파일 라벨이 안 맞았던 경우가 정말 많았거든요.

    sudo ausearch -m avc -ts recent
    sudo journalctl -t setroubleshoot --since today

    SELinux는 로그를 보는 습관이 정말 핵심입니다. 거부 로그를 보면 무엇이 막혔는지 힌트를 얻을 수 있어요. 드디어 됐다! 하는 순간이 대부분 여기서 옵니다.

    SELinux AppArmor 비교 글의 SELinux 컨텍스트 적용 흐름 이미지

    SELinux에서 컨텍스트를 확인하고 복구하는 흐름을 단계별로 보여주는 이미지입니다.

    5. 실전 구현: AppArmor 기본 확인과 프로파일 적용

    AppArmor는 상대적으로 진입 장벽이 훨씬 낮습니다. 실제로 써보니까 정책 파일이 사람이 읽기 쉬워서, 빠르게 감을 잡기 정말 좋더라고요. 특히 단일 서비스 보호 용도로는 꽤 편했습니다.

    1. AppArmor가 활성화되어 있는지 확인합니다.
    2. 현재 로드된 프로파일과 적용 상태를 봅니다.
    3. 필요한 프로파일을 enforce 모드로 전환합니다.
    4. 로그를 보고 누락된 권한을 보완합니다.
    sudo aa-status
    sudo apparmor_status

    배포판에 따라 둘 중 하나를 쓰게 되는 경우가 있습니다. 출력에서 어떤 프로파일이 enforce 모드인지, complain 모드인지 확인하면 됩니다. complain은 차단 대신 기록만 남기는 모드라서 테스트할 때 정말 유용해요.

    sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
    sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx

    AppArmor는 이렇게 프로파일 단위로 전환하는 방식이 직관적입니다. 경로 기반이라 “이 서비스가 이 디렉터리를 왜 못 읽지?”를 추적할 때 상대적으로 수월했어요. 다만 경로 변경이 잦은 환경에서는 프로파일 관리가 생각보다 귀찮을 수 있습니다.

    sudo journalctl -k | grep DENIED
    sudo dmesg | grep apparmor

    차단 로그도 꼭 봐야 합니다. AppArmor는 쉬워 보이지만, 결국 로그를 안 보면 감으로 수정하게 되거든요. 그럼 나중에 정책이 지저분해져서 관리가 힘들어집니다.

    6. 어떤 환경에서 무엇을 선택할까

    SELinux AppArmor 비교의 결론은 기술 우열보다 운영 적합성에 있습니다. 제가 홈랩과 실무성 테스트 환경에서 느낀 기준을 정리해보면 이렇습니다.

    • SELinux를 권하고 싶은 경우: RHEL 계열을 쓰고 있고, 정책 일관성과 세밀한 제어가 중요할 때
    • AppArmor를 권하고 싶은 경우: Ubuntu 계열을 쓰고 있고, 빠르게 프로파일을 읽고 조정해야 할 때
    • 팀 운영 관점: 로그 분석과 정책 유지에 익숙한 팀이면 SELinux도 충분히 잘 굴릴 수 있습니다.
    • 개인 홈랩 관점: 처음 시작할 땐 AppArmor가 덜 부담스러울 수 있어요.

    사실 제가 직접 써봤을 때 느낀 체감은 이랬습니다. SELinux는 한 번 체계가 잡히면 든든하고, AppArmor는 처음 붙을 때 편하다는 거죠. 둘 다 좋은 도구인데, 도구보다 운영자의 숙련도가 더 크게 작용하더라고요.

    SELinux AppArmor 비교 선택 가이드를 위한 의사결정 이미지

    배포판, 운영 인력, 정책 복잡도에 따라 어떤 선택이 맞는지 보여주는 결정 흐름도입니다.

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

    이 섹션은 정말 중요합니다. 저도 처음엔 보안 기능을 꺼버리고 끝내고 싶었던 순간이 정말 많았거든요. 근데 그 습관 들이면 나중에 훨씬 더 힘들어집니다.

    7-1. 서비스가 안 되면 바로 비활성화하지 마세요

    가장 흔한 실수가 “일단 꺼서 되게 만들자”라는 생각입니다. 테스트 중 잠깐 상태를 완화하는 건 가능하지만, 영구 비활성화는 정말 신중해야 합니다. 문제 원인을 안 보고 끄면 이후에 보안 사고 대응력이 확 떨어지거든요.

    7-2. 파일 권한과 보안 정책을 혼동하지 마세요

    SELinux에서는 파일 권한이 맞아도 컨텍스트가 틀리면 접근이 막힙니다. AppArmor에서는 프로세스가 파일 경로 접근 권한을 갖고 있는지 별도로 봐야 합니다. 즉, chmod와 chown만으로는 완벽하게 해결되는 게 아닙니다.

    7-3. 로그 없는 수정은 거의 실패합니다

    • SELinux: AVC 거부 로그 확인
    • AppArmor: DENIED 로그 확인
    • 정책 변경 전후로 무엇이 달라졌는지 기록

    제가 실무 비슷한 환경에서 자주 하는 방식은 간단합니다. 먼저 로그를 보고, 그다음 가장 좁은 범위로 수정합니다. 한 번에 크게 열어버리면 편하긴 한데, 나중에 왜 열었는지 기억이 안 나더라고요.

    8. 검증과 결과 확인

    설정을 끝냈다면 반드시 검증해야 합니다. 적용했다고 믿는 것과 실제로 동작하는 건 정말 다르거든요. 여기서 확인할 건 세 가지입니다.

    1. 서비스가 정상 동작하는지 확인합니다.
    2. 보안 정책이 실제로 enforce 중인지 확인합니다.
    3. 거부 로그가 불필요하게 반복되지 않는지 봅니다.
    curl -I http://localhost
    getenforce
    sudo aa-status
    sudo journalctl -k --since "10 minutes ago"

    정상 응답이 오고, 보안 모드가 의도한 상태이며, 로그에 반복적인 거부가 없다면 1차 검증은 통과입니다. ✅ 여기까지 오면 운영 가능한 상태에 꽤 가까워진 겁니다. 저도 이 단계까지 정리해두면 이후 서비스 추가가 훨씬 수월했어요.

    SELinux AppArmor 비교 적용 후 검증 결과를 보여주는 대시보드 이미지

    서비스 정상 응답, 정책 적용 상태, 로그 감소 여부를 한눈에 확인하는 결과 이미지입니다.

    9. 정리와 FAQ

    SELinux vs AppArmor를 한 줄로 요약하면 이렇습니다. 세밀한 통제와 표준 운영이면 SELinux, 빠른 이해와 간결한 관리면 AppArmor를 선택하는 게 맞습니다. 물론 현실에선 배포판 기본값과 팀 경험이 선택에 큰 영향을 줍니다. 그래서 제 추천은 무조건 하나를 고집하기보다, 현재 운영 체계와 문제 해결 방식에 맞추는 거예요.

    다음 단계로는 컨테이너 환경에서의 보안 프로파일, systemd 서비스 하드닝, seccomp(시스템 콜 필터링) 같은 주제까지 이어가면 좋습니다. 이 부분은 다음 글에서 다룰 예정입니다. 이전 글에서 다뤘던 리눅스 권한 구조와 함께 보시면 흐름이 더 잘 잡히실 거예요.

    자주 묻는 질문

    • Q. 둘 중 하나만 꼭 써야 하나요?
      A. 보통은 배포판과 운영 표준에 맞춰 하나를 중심으로 관리하는 편이 현실적입니다.
    • Q. 초보자에게는 뭐가 더 쉬운가요?
      A. 대체로 AppArmor가 더 직관적입니다. 다만 장기 운영 기준에선 SELinux를 배워둘 가치가 정말 큽니다.
    • Q. 보안 기능이 성능을 크게 떨어뜨리나요?
      A. 일반적인 서버 운영에서 체감보다 정책 정확성이 더 큰 이슈인 경우가 많았습니다.
    SELinux AppArmor 비교 장단점을 정리한 인포그래픽

    선택 기준, 장단점, 추천 시나리오를 한 장으로 정리한 요약 인포그래픽입니다.

    마지막으로, SELinux AppArmor 비교를 하다 보면 자꾸 정답 하나를 찾게 되는데요. 실제로 써보니까 정답은 환경마다 달랐습니다. 중요한 건 꺼두는 게 아니라 이해하고 운영하는 거예요. 처음엔 헷갈려도, 로그를 읽고 정책을 조금씩 줄여가다 보면 감이 옵니다. 그 과정이 조금 번거롭긴 해도, 서버를 오래 안정적으로 굴리려면 결국 한 번은 넘어야 할 산이더라고요. 이거 정말 중요합니다.

  • [k8s] Karpenter 오토스케일로 EKS 비용 절감 검증하는 방법

    [k8s] Karpenter 오토스케일로 EKS 비용 절감 검증하는 방법

    Karpenter 오토스케일로 EKS 비용 절감 검증하는 방법

    Karpenter 오토스케일을 검토하시는 분들은 비슷한 지점에서 막히곤 합니다. HPA로 파드 수는 잘 늘고 줄어드는데, 월말 청구서를 보면 노드가 생각보다 오래 남아 있어서 클라우드 비용 절감 체감이 약하거든요. 특히 AWS EKS를 운영하다 보면 파드 오토스케일링과 노드 비용 최적화가 완전히 같은 문제가 아니라는 점을 금방 느끼게 됩니다.

    저도 초반에는 “HPA만 잘 잡으면 끝나는 거 아닌가?” 싶었는데, 실제로는 노드 단위 빈 공간이 더 큰 병목이었습니다. 이럴 때 Karpenter 오토스케일은 파드 요구사항에 맞춰 노드를 더 유연하게 만들고, 유휴 노드를 정리하는 데 강점이 있습니다. 이번 글에서는 Kubernetes 오토스케일링 관점에서 Karpenter가 왜 비용 최적화에 유리한지, 그리고 AWS EKS 비용 최적화 효과를 어떤 지표로 검증해야 하는지 정리해보겠습니다.

    Karpenter 오토스케일 기반 EKS 아키텍처 개요 이미지

    Karpenter가 스케줄링되지 못한 파드를 감지하고 적절한 EC2 노드를 만들고, 유휴 노드를 정리하는 흐름을 보여주는 이미지입니다.

    Karpenter 오토스케일이 비용 절감에 유리한 이유

    쉽게 말해 Karpenter는 파드가 실제로 필요로 하는 리소스에 맞춰 노드를 바로 만들어주는 도구입니다. 기존 Cluster Autoscaler도 노드 수를 늘리고 줄일 수 있지만, 보통 미리 정의한 노드 그룹을 중심으로 확장하다 보니 리소스가 남는 구간이 생기기 쉽습니다.

    반면 Karpenter는 Pending 상태의 파드를 보고 CPU, 메모리, 아키텍처, 용량 유형(온디맨드/스팟) 같은 조건에 맞는 인스턴스를 더 유연하게 고릅니다. 운영에서 보면 이 차이가 꽤 큽니다. 특정 인스턴스 몇 개만 반복해서 늘리는 대신, 워크로드 특성에 맞춰 c 계열, m 계열, r 계열을 섞고 스팟까지 활용하기 쉬워지거든요.

    항목 Cluster Autoscaler Karpenter
    확장 기준 기존 노드 그룹 중심 파드 요구사항 중심
    인스턴스 선택 유연성 상대적으로 제한적 높음
    빈 리소스 최소화 노드 그룹 크기에 영향 받음 상황별 최적화에 유리
    스팟 활용 구성 복잡도 있음 정책 설계가 비교적 직관적
    비용 절감 포인트 노드 수 축소 노드 크기와 종류까지 최적화

    핵심은 단순히 노드를 빨리 만드는 데 있지 않습니다. Karpenter 오토스케일의 진짜 장점은 불필요하게 큰 노드를 오래 붙잡고 있지 않게 만드는 구조에 있습니다. 결국 비용은 시간당 단가와 실행 시간의 곱이라서, 둘을 같이 줄여야 체감이 나더라고요.

    Karpenter 오토스케일 도입 전에 먼저 봐야 할 비용 지표

    Karpenter를 붙였는데도 “그래서 얼마가 절감됐지?”를 설명하지 못하면 운영팀 설득이 어렵습니다. 감으로 보면 꼭 해석이 꼬입니다. 최소한 아래 지표는 같은 기간 기준으로 같이 보는 편이 좋습니다.

    • 노드 평균 사용률: CPU와 메모리 요청량 대비 실제 사용률
    • 유휴 시간: 야간 또는 비업무 시간에 노드가 얼마나 오래 남는지
    • 파드 Pending 빈도: 확장 지연이 발생하는지
    • 인스턴스 타입 다양성: 특정 타입에 과도하게 묶여 있는지
    • 스팟 비중: 안정성과 비용 균형이 맞는지

    메트릭은 CloudWatch, Prometheus, Grafana 조합으로 많이 보고, 비용은 AWS Cost Explorer 또는 CUR로 맞춰보면 됩니다. 포인트는 하나입니다. 메트릭과 비용 데이터를 반드시 같은 기간으로 맞춰서 비교해야 한다는 점입니다.

    Kubernetes 오토스케일링 구성, 이렇게 잡으면 시작이 편합니다

    실전 구현 자체는 아주 복잡하지 않습니다. 다만 EKS 클러스터가 이미 있어야 하고, IRSA 또는 EKS Pod Identity 같은 AWS 권한 구성이 가능해야 합니다. 그리고 Karpenter가 파드 요청값을 기준으로 판단하므로 워크로드의 resources.requests가 비어 있으면 기대한 최적화가 잘 나오지 않습니다. 이 부분은 정말 중요합니다.

    1. EKS 클러스터 이름과 리전을 환경 변수로 설정합니다.
    2. Karpenter 컨트롤러용 IAM 역할과 중단 이벤트용 큐를 준비합니다.
    3. Helm으로 Karpenter를 설치합니다.
    4. NodePool과 EC2NodeClass를 정의합니다.
    5. 워크로드의 requests/limits를 다시 점검합니다.
    export CLUSTER_NAME=my-eks
    export AWS_DEFAULT_REGION=ap-northeast-2
    export KARPENTER_NAMESPACE=karpenter
    export KARPENTER_VERSION=1.14.0
    export KARPENTER_IAM_ROLE_ARN=arn:aws:iam::123456789012:role/KarpenterControllerRole
    
    aws eks update-kubeconfig --name $CLUSTER_NAME --region $AWS_DEFAULT_REGION
    kubectl config current-context
    kubectl get nodes
    

    클러스터 연결부터 먼저 확인해두세요. 여기서 컨텍스트가 꼬이면 다른 클러스터에 배포하는 사고가 의외로 자주 납니다. 한 번만 겪어도 이 단계가 왜 중요한지 바로 느껴지더라고요.

    Karpenter 설치 예시

    helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
      --version $KARPENTER_VERSION \
      --namespace $KARPENTER_NAMESPACE \
      --create-namespace \
      --set settings.clusterName=$CLUSTER_NAME \
      --set settings.interruptionQueue=$CLUSTER_NAME \
      --set serviceAccount.annotations.eks\.amazonaws\.com/role-arn=$KARPENTER_IAM_ROLE_ARN \
      --wait
    
    kubectl -n $KARPENTER_NAMESPACE get pods
    

    설치가 끝나면 컨트롤러 파드가 정상 기동하는지 먼저 확인합니다. 여기서 CrashLoopBackOff가 보이면 IAM 권한, 서브넷 태그, 보안 그룹 태그, 이벤트 큐 설정부터 보는 게 빠릅니다.

    Karpenter 오토스케일 설정과 NodePool 연결 구조 이미지

    Karpenter 컨트롤러, NodePool, EC2NodeClass, 워크로드 파드가 어떻게 연결되는지 보여주는 구성 다이어그램입니다.

    NodePool과 EC2NodeClass 예시

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: default
    spec:
      template:
        spec:
          nodeClassRef:
            group: karpenter.k8s.aws
            kind: EC2NodeClass
            name: default
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
            - key: kubernetes.io/os
              operator: In
              values: ["linux"]
            - key: karpenter.sh/capacity-type
              operator: In
              values: ["spot", "on-demand"]
            - key: node.kubernetes.io/instance-type
              operator: In
              values: ["c6i.large", "m6i.large", "r6i.large"]
          expireAfter: 720h
      disruption:
        consolidationPolicy: WhenEmptyOrUnderutilized
        consolidateAfter: 5m
      limits:
        cpu: "200"
    ---
    apiVersion: karpenter.k8s.aws/v1
    kind: EC2NodeClass
    metadata:
      name: default
    spec:
      role: KarpenterNodeRole-my-eks
      amiSelectorTerms:
        - alias: al2023@latest
      subnetSelectorTerms:
        - tags:
            karpenter.sh/discovery: my-eks
      securityGroupSelectorTerms:
        - tags:
            karpenter.sh/discovery: my-eks
    

    여기서 핵심은 세 가지입니다. 첫째, NodePool에는 nodeClassRef가 필요합니다. 둘째, 최근 EKS 기준으로는 AL2보다 AL2023 계열 AMI 선택이 더 자연스럽습니다. 셋째, consolidation을 켜야 유휴 노드 정리가 제대로 일어납니다. 실제 비용 절감은 스케일 아웃보다 이 정리 단계에서 더 크게 체감되는 경우가 많습니다.

    테스트용 워크로드 배포

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: inflate
    spec:
      replicas: 0
      selector:
        matchLabels:
          app: inflate
      template:
        metadata:
          labels:
            app: inflate
        spec:
          terminationGracePeriodSeconds: 0
          containers:
            - name: inflate
              image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
              resources:
                requests:
                  cpu: 1
    
    kubectl apply -f inflate.yaml
    kubectl scale deployment inflate --replicas 5
    kubectl get pods -w
    

    이렇게 테스트하면 Pending 상태였던 파드 수요를 Karpenter가 보고 새 노드를 띄우는 흐름을 확인할 수 있습니다. 이후 replicas를 다시 줄이거나 배포를 삭제해보면 노드 통합 정리까지 관찰할 수 있습니다. 작은 워크로드부터 붙여보는 방식이 운영 리스크도 낮고 결과도 해석하기 편합니다.

    AWS EKS 비용 최적화를 위한 운영 포인트

    이 섹션은 꼭 짚고 넘어가야 합니다. Karpenter를 도입했다고 자동으로 클라우드 비용 절감이 되진 않습니다. 정책을 어떻게 잡느냐에 따라 결과가 꽤 다르게 나옵니다.

    • requests를 현실적으로 작성: 과장된 요청값은 큰 노드를 계속 부릅니다.
    • 스팟과 온디맨드 혼합: 서비스 성격에 따라 풀을 나누는 편이 안정적입니다.
    • 워크로드 분리: API 서버 계열과 배치 계열을 같은 NodePool에 몰지 않는 게 좋습니다.
    • 스케일 인 여유 시간 확인: 너무 공격적으로 줄이면 재기동이 잦아질 수 있습니다.
    • PodDisruptionBudget 검토: 축소가 막혀 노드가 안 내려가는 경우가 생각보다 많습니다.

    처음부터 모든 워크로드를 하나의 NodePool에 넣으면 정책 충돌이 생기기 쉽습니다. 최소한 안정성 우선 풀과 비용 우선 풀 정도는 분리해두면 운영이 훨씬 편합니다. 이거 진짜 편하더라고요.

    ⚠️ 실제로 많이 겪는 문제와 해결법

    문서만 보면 금방 될 것 같아도 운영 환경에서는 자잘한 함정이 많습니다. 특히 권한, 태그, 요청값 같은 기본 설정에서 많이 막힙니다.

    1. 노드가 아예 생성되지 않는 경우

    대부분은 IAM 또는 태그 문제입니다. 서브넷과 보안 그룹에 karpenter.sh/discovery 태그가 빠져 있으면 인프라 리소스를 찾지 못합니다.

    kubectl describe nodepool default
    kubectl -n karpenter logs deploy/karpenter
    

    로그에서 subnet, security group, instance profile, access denied 관련 메시지를 먼저 확인하면 원인이 빨리 보입니다.

    2. 노드는 생기는데 비용이 생각보다 안 줄어드는 경우

    이 경우도 자주 나옵니다. 원인은 대개 세 가지입니다.

    • 파드 requests가 과하게 큼
    • PodDisruptionBudget 때문에 빈 노드를 못 비움
    • 스팟 허용 범위가 너무 좁음

    Karpenter가 덜 똑똑해서가 아니라 입력값이 비효율적인 경우가 많습니다. 결국 스케줄러와 오토스케일러도 선언된 요청값을 기준으로 움직이니까요.

    3. 스팟 중단 대응이 불안한 경우

    스팟은 비용 면에서 매력적이지만 서비스 성격에 따라 조심해야 합니다. 중단 알림을 받았을 때 드레이닝과 재스케줄링이 자연스럽게 되도록 준비가 필요합니다. 중요한 서비스는 무조건 스팟으로 몰기보다 온디맨드와 섞는 편이 안전합니다.

    Karpenter 오토스케일 비용 절감 결과 대시보드 이미지

    파드 수 증가에 따라 노드가 늘고, 유휴 시간대에 통합 정리로 노드 수가 줄어드는 메트릭 대시보드 예시입니다.

    Karpenter 오토스케일 비용 절감 효과, 어떻게 검증해야 할까

    여기서 중요한 건 “노드 수가 줄었다”로 끝내지 않는 겁니다. 노드 수는 줄었는데 더 비싼 타입을 오래 썼다면 의미가 없거든요. 저는 아래 순서로 비교하면 결과 해석이 가장 깔끔했습니다.

    1. 도입 전 2주와 도입 후 2주를 비교합니다.
    2. 같은 요일, 같은 시간대 기준으로 워크로드 패턴을 맞춥니다.
    3. EC2 비용, 노드 평균 개수, 평균 vCPU 사용률을 함께 봅니다.
    4. 배포 실패, Pending 시간, 재스케줄링 지연이 늘지 않았는지 확인합니다.
    5. 스팟 비중 증가가 장애로 이어지지 않았는지 점검합니다.

    특히 비용 / 요청 처리량 같은 지표가 설명력이 좋습니다. 같은 트래픽을 처리하는데 필요한 EC2 비용이 줄었는지 보면, 단순 총액보다 훨씬 설득력이 있습니다. 팀 내 공유 문서에도 이 지표를 같이 적어두면 의사결정이 빨라집니다.

    kubectl top nodes
    kubectl top pods -A
    aws ce get-cost-and-usage \
      --time-period Start=2026-07-01,End=2026-07-15 \
      --granularity DAILY \
      --metrics UnblendedCost \
      --group-by Type=DIMENSION,Key=SERVICE
    

    위 명령은 비용 추세를 보는 기본 예시입니다. 참고로 Cost Explorer의 종료일은 보통 마지막 날짜 다음 날로 잡아야 비교가 깔끔합니다. 운영에서는 CUR를 Athena로 조회하거나 Grafana에 연결해 시계열로 보는 쪽이 더 편합니다.

    해석할 때 주의할 점

    • 도입 직후 며칠은 캐시, 배포 주기, 스팟 수급 영향으로 흔들릴 수 있습니다.
    • 트래픽 자체가 줄어서 비용이 감소한 것을 Karpenter 효과로 착각하면 안 됩니다.
    • RI나 Savings Plans 영향도 함께 봐야 합니다.

    결국 비교 기준은 같은 부하 조건이어야 합니다. 이 기준만 지켜도 해석 오류가 꽤 줄어듭니다.

    정리: 이런 환경이라면 Karpenter 도입 가치가 큽니다

    다음 조건에 해당하면 Karpenter 도입 효과가 비교적 잘 나옵니다.

    • 트래픽 변동폭이 크다
    • 워크로드 종류가 다양하다
    • EKS에서 인스턴스 타입 선택 폭을 넓게 가져갈 수 있다
    • 스팟 활용 여지가 있다
    • 현재 노드 유휴 시간이 길다

    반대로 워크로드가 매우 단순하고 부하가 거의 고정되어 있다면 체감 효과가 크지 않을 수도 있습니다. 그래서 무조건 도입부터 하기보다, 현재 비효율이 어디서 생기는지 먼저 확인하는 편이 맞습니다. 그다음 작은 서비스에 시범 적용하고 결과를 비교하면 훨씬 덜 위험합니다.

    Karpenter 오토스케일과 기존 오토스케일링 비교 인포그래픽

    노드 그룹 기반 확장과 파드 요구사항 기반 확장의 차이를 한눈에 요약한 비교 이미지입니다.

    마무리

    이번 글의 핵심은 간단합니다. Karpenter 오토스케일은 단순한 증설 도구라기보다, AWS EKS 비용 최적화를 운영 레벨에서 밀어주는 도구에 가깝습니다. 다만 효과는 설치 여부보다 requests 품질, NodePool 정책, 스팟 전략, 검증 방식에 더 크게 좌우됩니다.

    지금 EKS 비용이 애매하게 새고 있다고 느끼신다면, 전면 도입부터 하기보다 작은 워크로드에 먼저 붙여보세요. 그리고 이전 글의 HPA/VPA 튜닝 가이드도 함께 보시면 파드 스케일링과 노드 스케일링을 한 흐름으로 이해하는 데 도움이 됩니다. 다음 글에서는 스팟과 온디맨드 혼합 정책을 어떻게 설계해야 장애 없이 비용을 줄일 수 있는지 이어서 다뤄보겠습니다.

    자주 묻는 질문

    Karpenter만 도입하면 비용이 바로 줄어드나요?

    아닙니다. requests 값, NodePool 정책, 스팟 허용 범위가 같이 맞아야 의미 있는 절감이 나옵니다.

    Cluster Autoscaler를 꼭 버려야 하나요?

    꼭 그렇진 않습니다. 다만 인스턴스 선택 유연성과 운영 단순성이 중요하다면 Karpenter가 더 잘 맞는 환경이 많습니다.

    비용 절감 효과는 무엇으로 확인하나요?

    총 EC2 비용만 보지 말고, 같은 트래픽 대비 비용, 노드 평균 사용률, 유휴 시간, Pending 감소 여부를 함께 보시면 됩니다.

  • [모니터링] Grafana Cloud 비용 분석, 자체 Prometheus로 돌아간 이유

    [모니터링] Grafana Cloud 비용 분석, 자체 Prometheus로 돌아간 이유

    [모니터링] Grafana Cloud 비용 분석, 자체 Prometheus로 돌아간 이유

    Grafana Cloud 비용이 예전보다 훨씬 민감하게 느껴지기 시작하면, 그때부터는 단순히 ‘관리형이라 편하다’만으로 설명이 안 되더라고요. 저도 홈랩과 소규모 운영 환경에서 Grafana Cloud를 꽤 편하게 썼었는데, 메트릭(Metrics, 시계열 지표)과 로그(Logs, 시스템/애플리케이션 기록)가 늘어나면서 청구 구조를 다시 뜯어보게 됐습니다. 특히 Grafana Cloud 비용은 사용량이 조금만 늘어도 체감상 훅 올라갈 수 있어서, 모니터링 비용을 어디까지 SaaS로 맡길지 진지하게 다시 보게 되거든요.

    이번 글은 ‘무조건 자체 호스팅이 낫다’는 이야기가 아닙니다. Grafana Cloud 공식 가격 구조를 기준으로 왜 비용이 커지는지, 그리고 어떤 조건에서 자체 호스팅 ROI(Return on Investment, 투자 대비 효과)가 맞아떨어지는지 제가 정리한 기준을 공유해보려는 글입니다. 중간에 실제로 바로 올려볼 수 있는 Prometheus(프로메테우스, 메트릭 수집기) + Grafana(그라파나, 시각화 도구) 구성 예시도 넣어둘게요.

    Grafana Cloud와 자체 Prometheus 구성을 비용 관점에서 비교한 전체 아키텍처 예시입니다.

    1. 왜 Grafana Cloud 비용이 갑자기 아프게 느껴질까

    처음엔 진짜 편합니다. 가입하고, 에이전트 붙이고, 대시보드 몇 개 열면 끝이거든요. 저도 처음엔 이게 뭔가 싶을 정도로 진입 장벽이 낮아서 아주 만족했었습니다. 근데 여기서 중요한 포인트가 있습니다. 편한 구조일수록 과금 단위가 눈에 잘 안 들어온다는 점입니다.

    Grafana 공식 가격 페이지를 보면, Pro 플랜은 월 플랫폼 요금(platform fee)에 더해 사용량 기반 과금이 붙습니다. 메트릭은 active series(액티브 시리즈, 실제로 수집 중인 시계열 개수) 기준이고, 로그와 트레이스는 process / write / retain처럼 단계별 과금 구조가 걸려 있더라고요. 쉽게 말해, 데이터를 많이 넣는 환경에서는 ‘조금 늘었네’라고 생각했는데 청구서는 전혀 조금이 아닐 수 있다는 뜻입니다.

    • 메트릭: active series가 늘면 비용이 바로 반응합니다.
    • 로그: 수집량뿐 아니라 처리와 저장이 분리돼 보여서 체감이 더 큽니다.
    • 트레이스: 장애 분석엔 좋지만, 운영 습관 없이 켜두면 금방 비싸집니다.
    • 보존 기간(retention, 데이터 보관 기간): 길어질수록 관리형 서비스의 장점은 커지지만 비용도 같이 올라갑니다.

    이런 구조 때문에 클라우드 가격이 ‘갑자기 2배 오른 느낌’으로 다가오는 경우가 많습니다. 꼭 단가만 오른 게 아니라, 내가 보내는 데이터 패턴과 과금 기준이 맞물리면서 총액이 급격히 커지는 거죠. 실제로 써보니까, 비용 문제는 제품 성능보다 카디널리티(cardinality, 라벨 조합 폭발) 관리 실패에서 먼저 터지는 경우가 많더라고요.

    2. Grafana Cloud 가격 구조를 쉽게 풀어보면

    공식 가격 페이지 기준으로 현재 눈에 띄는 포인트는 이렇습니다. 글을 읽는 시점에 바뀔 수 있으니, 실제 계약 전에 공식 페이지는 꼭 다시 확인하셔야 합니다.

    항목 공식 페이지 기준 포인트 운영자가 봐야 할 부분
    Metrics Free는 월 10k active series, 14일 보관 라벨 설계가 나쁘면 금방 초과합니다.
    Metrics Pro 월 플랫폼 요금 + 사용량 기반 가격 노드 수보다 series 폭증이 더 위험합니다.
    Logs Free 월 50GB, 14일 보관 테스트 환경은 괜찮지만 운영 로그는 빠듯할 수 있습니다.
    Logs Pro process, write, retain 기반 사용량 과금 수집, 저장, 보관을 따로 봐야 합니다.
    Retention Pro 기준 metrics 13개월, logs/traces 30일 장기 보관이 필요한 조직엔 장점이 큽니다.

    쉽게 말해 이렇습니다. Grafana Cloud 비용은 서버 대수보다도 ‘얼마나 많은 시계열과 데이터를 계속 보내고 있는가’에 더 민감합니다. VM 몇 대 수준에서는 괜찮아 보여도, 쿠버네티스(Kubernetes, 컨테이너 오케스트레이션) 들어가고 라벨이 늘고, 로그를 구조화해서 많이 쌓기 시작하면 얘기가 달라집니다.

    그래서 저는 비용 분석할 때 아래 세 가지부터 봅니다.

    1. active series가 왜 늘고 있는지
    2. scrape interval(스크랩 주기, 수집 간격)이 과한지
    3. 꼭 SaaS에 남겨야 하는 데이터와 로컬에 둬도 되는 데이터를 구분했는지

    3. 자체 호스팅 ROI, 저는 이렇게 계산합니다

    여기서 많이들 실수하는 게 있습니다. 자체 Prometheus로 돌린다고 해서 무조건 싸지 않거든요. 저도 처음엔 ‘VM 하나면 끝 아닌가?’ 싶었는데, 막상 해보면 백업, 디스크, 알람, 업그레이드, 장애 대응까지 다 내 일이 됩니다. 삽질 좀 했습니다 ㅎㅎ

    그래도 자체 호스팅 ROI가 좋아지는 구간은 분명히 있습니다.

    • 메트릭은 많은데 장기 보관이 꼭 필요하지 않을 때
    • 운영 인력이 있어 기본 유지보수를 감당할 수 있을 때
    • 로그와 트레이스를 전부 관리형으로 둘 필요가 없을 때
    • 홈랩, 사내 관제, 내부 서비스처럼 외부 SLA보다 비용 통제가 더 중요할 때

    반대로 아래 조건이면 Grafana Cloud가 여전히 훨씬 편할 수 있습니다.

    • 24×7 대응과 장기 보관이 중요할 때
    • 멀티 리전, 팀 협업, 권한 관리가 이미 복잡할 때
    • 장애 나면 관제 스택을 직접 고칠 사람이 없을 때

    제가 실제로는 이렇게 따집니다. 월 청구서 증가분과 직접 운영할 때 들어가는 VM/디스크/백업 비용 + 관리 시간을 같이 봅니다. 숫자를 억지로 예쁘게 만들 필요는 없고요. 한 달에 두세 번만 대시보드 보고, 보관 기간도 15일~30일이면, 자체 Prometheus 쪽이 생각보다 금방 수지가 맞는 경우가 있습니다.

    4. 그래서 저는 Prometheus + Grafana 조합으로 다시 갔습니다

    이번 선택의 핵심은 단순했습니다. 메트릭은 자체 호스팅, 필요하면 나중에 일부만 외부로 보낸다. Prometheus는 구조가 명확하고, Grafana는 익숙하고, 둘 다 문서가 탄탄합니다. 무엇보다 제가 어디서 비용이 나는지 눈으로 볼 수 있더라고요.

    Prometheus 공식 문서를 보면 로컬 저장소(local storage)는 기본적으로 15일 retention입니다. 그리고 저장소 크기를 제어하려면 --storage.tsdb.retention.time 또는 --storage.tsdb.retention.size를 잡을 수 있습니다. 또 중요한 경고가 하나 있는데, NFS 같은 비POSIX 환경은 권장되지 않습니다. 이거 모르고 네트워크 스토리지에 올렸다가 애매한 문제 겪는 경우 진짜 많습니다.

    Grafana Cloud 비용 절감을 위해 구성한 자체 Prometheus 홈랩 다이어그램

    Docker Compose 기반으로 Prometheus와 Grafana를 엮은 홈랩 구성 예시입니다.

    5. 실전 구현: Docker Compose로 10분 안에 올리기

    복잡하게 시작할 필요 없습니다. 가장 단순하고 관리하기 쉬운 형태로 먼저 띄우는 게 좋습니다. 아래 예시는 Prometheus와 Grafana를 같은 Docker Compose 네트워크에 올리는 방식입니다.

    5-1. 디렉터리 구조

    monitoring/
    ├── docker-compose.yaml
    ├── prometheus/
    │   └── prometheus.yml
    └── grafana/
        └── provisioning/
            └── datasources/
                └── prometheus.yaml

    5-2. docker-compose.yaml

    services:
      prometheus:
        image: prom/prometheus:latest
        container_name: prometheus
        restart: unless-stopped
        command:
          - --config.file=/etc/prometheus/prometheus.yml
          - --storage.tsdb.path=/prometheus
          - --storage.tsdb.retention.time=15d
          - --storage.tsdb.retention.size=20GB
          - --web.enable-lifecycle
        ports:
          - "9090:9090"
        volumes:
          - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
          - prometheus-data:/prometheus
    
      grafana:
        image: grafana/grafana:latest
        container_name: grafana
        restart: unless-stopped
        ports:
          - "3000:3000"
        volumes:
          - grafana-data:/var/lib/grafana
          - ./grafana/provisioning:/etc/grafana/provisioning:ro
        depends_on:
          - prometheus
    
    volumes:
      prometheus-data:
      grafana-data:

    여기서 중요한 포인트는 두 가지입니다. 첫째, retention.time과 retention.size를 둘 다 넣어 디스크 폭주를 막는 것. 둘째, 영속 볼륨(persistent volume)을 꼭 쓰는 것입니다. 안 그러면 컨테이너 다시 뜰 때 데이터가 날아가거든요.

    5-3. prometheus.yml

    global:
      scrape_interval: 15s
      evaluation_interval: 15s
    
    scrape_configs:
      - job_name: prometheus
        static_configs:
          - targets:
              - prometheus:9090

    Prometheus 공식 시작 가이드의 가장 기본적인 예시는 자기 자신을 스크랩하는 구성입니다. 저는 처음에 딱 여기서 시작하시는 걸 추천합니다. 이것만으로도 Prometheus가 정상 수집 중인지 바로 검증할 수 있거든요.

    5-4. Grafana 데이터소스 프로비저닝

    apiVersion: 1
    
    datasources:
      - name: Prometheus
        type: prometheus
        access: proxy
        url: http://prometheus:9090
        isDefault: true
        jsonData:
          httpMethod: POST

    Grafana 공식 문서에도 나오지만, 같은 Compose 네트워크라면 http://prometheus:9090처럼 서비스 이름으로 붙이는 방식이 제일 깔끔합니다. 많은 분들이 여기서 localhost:9090 넣고 왜 안 되지? 하는데, 컨테이너 안에서 localhost는 자기 자신이거든요. 저도 처음엔 여기서 한 번 헷갈렸습니다.

    5-5. 실행

    cd monitoring
    docker compose up -d
    
    docker compose ps
    curl http://localhost:9090/-/ready
    curl http://localhost:3000/api/health

    Prometheus는 http://localhost:9090, Grafana는 http://localhost:3000으로 들어가면 됩니다. Grafana 데이터소스가 자동으로 붙어 있으면 거의 다 된 겁니다. 드디어 됐다! 하는 순간이 여기서 오더라고요.

    Grafana Cloud 비용 대안으로 자체 Prometheus를 연결한 Grafana 설정 이미지

    Grafana에서 Prometheus 데이터소스가 정상 연결된 상태를 보여주는 설정 화면 예시입니다.

    6. 운영하면서 꼭 챙겨야 하는 비용 절감 포인트

    자체 호스팅으로 돌아왔다고 모니터링 비용 문제가 자동 해결되진 않습니다. 그냥 청구서 대신 디스크와 리소스가 문제를 말해줄 뿐이죠. 그래서 아래 기준은 꼭 잡고 가는 편이 좋습니다.

    1. scrape interval을 무작정 5초로 두지 않습니다. 15초나 30초면 충분한 경우가 많습니다.
    2. high cardinality 라벨을 줄입니다. 요청 ID, 세션 ID 같은 값은 거의 독입니다.
    3. 장기 보관이 필요하면 Prometheus 단독보다 Thanos, Mimir 같은 별도 구조를 검토합니다.
    4. 메트릭과 로그를 분리해서 생각합니다. 둘을 한 번에 SaaS에서 빼면 운영 복잡도가 급격히 올라갈 수 있습니다.

    특히 모니터링 비용은 수집량보다도 라벨 설계에서 무너지는 경우가 많습니다. 예를 들어 pod 이름, request path, 사용자별 식별자가 섞이면 active series가 순식간에 불어납니다. 클라우드 가격이 비싸다고 느껴질 때는 서비스 갈아타기 전에 먼저 series 폭증 원인을 보는 게 맞습니다. 이게 정말 중요한 포인트입니다!

    7. ⚠️ 실제로 많이 겪는 트러블슈팅

    • Grafana에서 데이터가 안 보임
      원인: 데이터소스 URL을 localhost:9090으로 넣은 경우가 많습니다. 컨테이너 분리 환경이면 서비스명이나 내부 네트워크 주소를 써야 합니다.
    • 디스크가 생각보다 빨리 참
      원인: retention은 시간만 잡고 size 제한을 안 둔 경우가 많습니다. --storage.tsdb.retention.size를 같이 설정하세요.
    • Prometheus 재시작 후 데이터가 날아감
      원인: 볼륨 마운트가 빠졌을 가능성이 큽니다. 특히 테스트하다가 귀찮아서 빼면 꼭 나중에 후회합니다.
    • NFS에 올렸더니 이상하게 불안정함
      원인: Prometheus 공식 문서에서도 로컬 파일시스템 사용을 강하게 권장합니다. 네트워크 파일시스템은 피하는 편이 안전합니다.

    저는 한때 백업 편하겠다고 네트워크 스토리지를 먼저 떠올렸었는데, 이건 운영 편의보다 안정성이 우선이더라고요. 결국은 로컬 디스크 + 스냅샷/백업 전략으로 다시 정리했습니다.

    8. 검증 결과: 무엇이 좋아졌고 무엇을 잃었나

    직접 운영으로 바꾸고 나면 제일 먼저 느끼는 건 비용 예측 가능성입니다. Grafana Cloud 비용처럼 사용량 급등에 따라 청구서가 튀는 대신, 이제는 VM 크기와 디스크 크기, retention 정책으로 상한선을 제가 결정할 수 있습니다. 이거 진짜 편하더라고요. 적어도 월말에 놀랄 일은 줄어듭니다.

    반대로 잃는 것도 분명합니다.

    • 관리형 서비스의 즉시성
    • 긴 retention과 팀 기능의 편의성
    • 장애 시 벤더에 기대는 심리적 안정감

    그래서 결론은 단순합니다. 메트릭 위주의 소규모~중간 규모 환경이라면 자체 Prometheus는 충분히 매력적입니다. 반면 로그, 트레이스, 장기 보관, 멀티팀 협업이 핵심이면 Grafana Cloud가 여전히 좋은 선택일 수 있습니다. 결국 핵심은 도구 종교가 아니라 ROI와 운영 책임 범위입니다.

    Grafana Cloud 비용 분석 후 자체 Prometheus 전환 결과 대시보드 이미지

    자체 Prometheus 전환 후 Grafana 대시보드와 리소스 사용량을 함께 확인하는 결과 예시입니다.

    9. 정리와 FAQ

    자주 묻는 질문

    • Q. Grafana Cloud를 완전히 버려야 하나요?
      아닙니다. 메트릭만 자체 호스팅하고, 로그나 외부 공유 대시보드는 관리형을 유지하는 혼합형도 충분히 좋습니다.
    • Q. 자체 호스팅 ROI는 언제 좋아지나요?
      청구서 증가 속도보다 VM/스토리지/운영 시간 증가 속도가 느릴 때입니다. 특히 retention이 짧고 운영 범위가 작을수록 유리합니다.
    • Q. 다음 단계는 뭘 보면 좋을까요?
      다음 글에서는 Node Exporter, Alertmanager, 그리고 카디널리티 줄이는 실전 팁을 정리해볼 예정입니다. 이전 글에서 다뤘던 홈랩 스토리지 설계와도 연결해서 보시면 더 이해가 쉬우실 겁니다.

    정리하면 이렇습니다. Grafana Cloud 비용이 부담스럽다고 느껴지는 순간이 오면, 무조건 해지부터 할 필요는 없습니다. 먼저 내 사용량 구조를 보고, 어떤 데이터가 비용을 끌어올리는지 파악한 뒤, 메트릭부터 단계적으로 분리해보세요. 저처럼 한 번 직접 돌려보면, 어떤 환경에서 자체 호스팅이 맞는지 감이 꽤 선명해집니다. 처음엔 헷갈려도, 막상 해보면 생각보다 어렵지 않습니다.

    Grafana Cloud 비용과 자체 호스팅 ROI를 비교한 인포그래픽

    Grafana Cloud와 자체 호스팅 ROI를 한눈에 정리한 비교 인포그래픽 예시입니다.

    참고한 공식 문서

  • [OpenStack] 오픈스택 플레이버 최적화로 유휴 자원 줄이기

    [OpenStack] 오픈스택 플레이버 최적화로 유휴 자원 줄이기

    [OpenStack] 오픈스택 플레이버 최적화로 유휴 자원 줄이기

    프라이빗 클라우드 운영을 하다 보면 CPU는 남는데 메모리가 먼저 바닥나거나, 반대로 스토리지는 넉넉한데 작은 워크로드가 큰 VM 사양을 계속 잡아먹는 상황을 자주 보게 됩니다. 저도 홈랩과 업무 환경에서 이런 패턴을 여러 번 겪었고, 결국 문제의 중심에는 OpenStack 플레이버 설계가 있더라고요. 처음엔 인스턴스만 잘 뜨면 된다고 생각했는데, 실제로 써보니까 플레이버가 조금만 느슨해도 유휴 자원(idle resource)이 금방 쌓이고, 그게 곧 비용 압박으로 이어졌거든요.

    특히 프라이빗 클라우드 운영에서는 퍼블릭 클라우드처럼 청구서가 바로 날아오지 않아서 체감이 늦습니다. 근데 여기서 방심하면 안 됩니다. 서버 증설, 전력, 랙 공간, 백업, 운영 시간까지 다 합치면 결국 내부 비용이 꽤 커지거든요. 그래서 오늘은 OpenStack 플레이버를 어떻게 다듬어야 자원 최적화와 비용 절감을 동시에 가져갈 수 있는지, 제가 삽질했던 포인트까지 포함해서 정리해보겠습니다.

    OpenStack 플레이버가 프라이빗 클라우드 자원 배분에 미치는 구조를 보여주는 아키텍처 이미지

    플레이버 정의가 컴퓨트 노드 자원 사용률에 어떤 영향을 주는지 한눈에 보여주는 개요 이미지입니다.

    왜 OpenStack 플레이버 최적화가 중요한가

    쉽게 말해 플레이버는 VM의 기본 체급표입니다. vCPU, RAM, root disk 같은 자원 크기를 미리 정해두고, 사용자는 그중 하나를 골라 인스턴스를 만들게 되죠. 문제는 이 체급표가 현실과 안 맞을 때 생기더라고요.

    • 너무 큰 플레이버만 있으면 작은 서비스도 과하게 큰 자원을 점유합니다.
    • 너무 많은 플레이버가 있으면 운영 기준이 흐려지고, 사용자도 뭘 골라야 할지 헷갈립니다.
    • 이름 규칙이 제각각이면 자동화 스크립트와 정책 관리가 꼬이기 쉽습니다.
    • 워크로드 특성에 맞지 않는 비율로 설계하면 CPU overcommit(오버커밋)이나 메모리 낭비가 반복됩니다.

    저도 처음엔 부서 요청이 들어올 때마다 플레이버를 하나씩 추가했었습니다. 그때는 빨리 만들어주는 게 친절이라고 생각했는데요. 시간이 지나니까 m1-medium-v2-final 같은 이름이 생기고, 용도는 겹치고, 어떤 VM은 메모리만 남고 어떤 VM은 CPU만 묶여버리는 이상한 상태가 되더라고요. 그때부터 기준을 다시 세웠고, 드디어 좀 정리가 됐습니다.

    OpenStack 플레이버 개념, 쉽게 말해 이런 겁니다

    Flavor(플레이버, 가상머신 자원 템플릿)는 Nova(노바, OpenStack의 컴퓨트 서비스)에서 인스턴스 크기를 정의하는 단위입니다. 일반적으로 아래 항목을 포함합니다.

    • vCPU: 가상 CPU 개수
    • RAM: 메모리 크기(MB 단위)
    • Disk: 루트 디스크 크기(GB 단위)
    • Ephemeral Disk: 임시 디스크가 필요한 경우 사용하는 추가 디스크
    • Swap: 스왑 영역
    • extra_specs: 스케줄링, CPU 정책, NUMA 같은 추가 속성

    여기서 중요한 포인트! 플레이버는 단순히 숫자 몇 개를 묶어놓은 게 아닙니다. 실제로는 스케줄러(scheduler, 어느 컴퓨트 노드에 올릴지 결정하는 구성요소)와 운영 정책의 기준점 역할도 해요. 예를 들어 특정 플레이버에 dedicated CPU policy(전용 CPU 정책)나 huge pages(대용량 메모리 페이지) 같은 조건을 넣으면, 같은 4 vCPU / 8GB라도 완전히 다른 자원 소비 패턴이 됩니다.

    그래서 자원 최적화를 하려면 단순히 작은 플레이버를 늘리는 게 아니라, 어떤 워크로드를 어떤 비율로 태울지를 먼저 봐야 합니다. 사실 이걸 건너뛰면 플레이버만 바꾸고 결과는 그대로인 경우가 많더라고요.

    현재 상태부터 점검해보세요: 유휴 자원은 숫자로 봐야 합니다

    제가 직접 해보니 제일 먼저 해야 할 일은 플레이버를 새로 만드는 게 아니라, 지금 어떤 플레이버가 실제로 얼마나 쓰이는지 확인하는 일이었습니다. 감으로 보면 꼭 틀립니다. 특히 오래된 환경일수록 더 그렇거든요.

    1. 현재 등록된 플레이버 목록을 확인합니다.
    2. 인스턴스별로 어떤 플레이버가 얼마나 사용 중인지 집계합니다.
    3. 컴퓨트 노드별 vCPU, 메모리 사용률과 배치 불균형을 함께 봅니다.
    4. 실사용 대비 과대 할당된 표준 플레이버를 후보로 뽑습니다.
    openstack flavor list --long
    
    openstack server list --all-projects -f value -c ID -c Name -c Flavor
    
    openstack hypervisor list
    openstack hypervisor stats show

    CLI(Command Line Interface, 명령줄 인터페이스)로 보면 좀 투박하긴 한데, 오히려 현실이 잘 보여요. 예를 들어 플레이버는 12개인데 실제로 자주 쓰는 건 4개뿐인 경우가 꽤 많습니다. 반대로 이름은 비슷한데 메모리만 조금씩 다른 플레이버가 잔뜩 있는 환경도 있었고요.

    제가 운영하던 환경에서는 2 vCPU / 8GB 계열 플레이버가 유독 많았는데, 실제 앱은 메모리 3~4GB 수준만 쓰는 경우가 대부분이었습니다. 결국 메모리 단편화(fragmentation, 자원이 애매하게 쪼개져 남는 현상)가 심해졌고, 새 인스턴스를 띄울 때 특정 노드만 계속 부족하다는 알람이 뜨더라고요. CPU는 남는데 메모리 때문에 못 올리는 상황, 이거 꽤 자주 본답니다.

    OpenStack 플레이버 사용 편중과 자원 불균형을 보여주는 운영 대시보드 이미지

    실제 운영에서 자주 보게 되는 플레이버 사용 편중과 노드별 자원 불균형 예시를 보여주는 이미지입니다.

    표준 플레이버를 줄이고, 비율을 맞추는 게 비용 절감의 시작입니다

    여기서 중요한 건 플레이버 개수를 무조건 늘리는 게 아니라 표준화(standardization, 기준을 통일하는 작업)입니다. 저는 보통 워크로드를 세 가지로 나눠서 봅니다.

    • 범용형: 웹, API, 배치처럼 평균적인 CPU/메모리 비율
    • 메모리 집중형: 캐시, JVM, 분석 도구처럼 메모리 사용량이 큰 유형
    • 컴퓨트 집중형: 빌드, 인코딩, 일부 계산 작업처럼 CPU 사용량이 높은 유형

    이걸 바탕으로 플레이버를 단순하게 재구성하면 선택이 쉬워지고, 배치도 예측 가능해집니다. 아래는 많이 쓰는 정리 방식 예시입니다.

    구분 예시 이름 용도 설계 포인트
    범용형 gp.small / gp.medium 일반 웹, API, 업무 시스템 CPU와 메모리 비율을 균형 있게 유지
    메모리형 mem.small / mem.medium 캐시, 메모리 민감 서비스 같은 vCPU 대비 RAM 비율 확대
    컴퓨트형 cpu.small / cpu.medium 배치, 변환, CI 작업 메모리보다 CPU 효율 우선
    전용형 perf.large 성능 민감 워크로드 extra_specs로 정책 분리

    이름 규칙도 꽤 중요합니다. 저는 나중에 자동화할 걸 생각해서 접두사(prefix)를 꼭 넣어요. 예를 들면 gp, mem, cpu 같이요. 이렇게 해야 Terraform(테라폼, 인프라 코드 도구)이나 Ansible(앤서블, 자동화 도구)에서 분기하기 편하더라고요.

    참고로 플레이버를 설계할 때는 너무 세밀하게 자르는 것보다 20~30% 정도의 여유 구간을 가진 계단형 구성이 관리가 편합니다. 2GB, 3GB, 4GB, 5GB, 6GB 식으로 촘촘히 만들면 한동안은 좋아 보이는데, 운영자가 나중에 감당하기 힘들어진답니다.

    실전 구현: OpenStack 플레이버 재설계와 적용 순서

    이제 실제로 손을 대볼 차례입니다. 제가 보통 쓰는 절차는 아래 순서입니다. 한 번에 다 바꾸려고 하면 사고 나요. 저도 예전에 급하게 정리하다가 사용자한테 왜 목록이 바뀌었냐는 문의를 한꺼번에 받았거든요.

    1. 기존 플레이버 사용 현황을 수집합니다.
    2. 표준 플레이버 목록을 먼저 문서화합니다.
    3. 새 플레이버를 추가하되 기존 것은 바로 삭제하지 않습니다.
    4. 신규 배포부터 새 플레이버를 사용하게 유도합니다.
    5. 사용량이 떨어진 기존 플레이버를 단계적으로 비공개 또는 정리합니다.
    # 범용형 플레이버 생성 예시
    openstack flavor create gp.small \
      --vcpus 2 \
      --ram 4096 \
      --disk 20
    
    openstack flavor create gp.medium \
      --vcpus 4 \
      --ram 8192 \
      --disk 40
    
    # 메모리형 플레이버 생성 예시
    openstack flavor create mem.small \
      --vcpus 2 \
      --ram 8192 \
      --disk 20
    
    # extra_specs 설정 예시
    openstack flavor set perf.large \
      --property hw:cpu_policy=dedicated \
      --property hw:mem_page_size=large

    위 예시는 어디까지나 구조 예시입니다. 수치는 각 환경의 하이퍼바이저(hypervisor, 가상화 호스트) 구성과 워크로드 특성에 맞춰 잡으셔야 해요. 무작정 따라 넣는 건 추천하지 않습니다. 특히 전용 CPU 정책은 호스트 여유가 부족하면 오히려 배치 가능성이 떨어질 수 있거든요.

    실제로 써보니까 신규 플레이버를 만들고 바로 공지하는 것보다, Horizon(호라이즌, OpenStack 웹 대시보드) 또는 내부 포털에서 기본 선택지를 먼저 바꾸는 게 효과가 좋았습니다. 사용자는 기본값을 잘 따라가거든요. 이거 진짜 편하더라고요.

    표준 플레이버 생성과 정책 분리를 적용하는 과정을 시각적으로 보여주는 구성 이미지입니다.

    권장 운영 기준

    • 기존 플레이버는 즉시 삭제하지 말고 일정 기간 공존시키세요.
    • 프로젝트별 예외 요구는 별도 전용 플레이버로 격리하세요.
    • 자동 배포 템플릿에서 플레이버 이름을 하드코딩했다면 사전 점검이 필요합니다.
    • 비용 절감 효과는 VM 개수보다 호스트 증설 지연 효과에서 더 크게 나타날 수 있습니다.

    ⚠️ 제가 실제로 겪은 문제들: 트러블슈팅과 주의사항

    여기서부터가 진짜 운영 이야기예요. 문서에는 잘 안 나오는데, 실제론 이런 부분에서 많이 막힙니다.

    1. 플레이버만 줄였는데도 자원이 안 돌아오는 경우

    기존 인스턴스는 자동으로 새 플레이버를 쓰지 않아요. resize(리사이즈, 인스턴스 사양 변경)나 재배포가 필요합니다. 저도 처음엔 표준 플레이버 정리만 하면 바로 효과가 날 줄 알았는데, 기존 대형 인스턴스가 그대로 남아 있어서 체감 변화가 거의 없었습니다.

    # 인스턴스 리사이즈 예시
    openstack server resize --flavor gp.small <SERVER_ID>
    openstack server resize confirm <SERVER_ID>

    2. 메모리 단편화가 심해 배치가 꼬이는 경우

    플레이버 종류가 많으면 같은 총 메모리 용량이라도 애매하게 쪼개져 남아요. 이럴 때는 큰 플레이버를 무작정 유지하는 것보다, 중간 단계를 줄이고 범용형으로 수렴시키는 편이 낫더라고요.

    3. extra_specs를 너무 공격적으로 쓰는 경우

    특정 성능 요구 때문에 dedicated CPU나 huge pages를 광범위하게 쓰기 시작하면 스케줄링 제약이 커져요. 성능은 좋아질 수 있어도 전체 효율성은 오히려 떨어질 수 있습니다. 성능 민감 워크로드만 별도 클래스로 분리하는 게 안전했습니다.

    4. 이름만 바꾸고 정책은 안 바뀌는 경우

    이건 꽤 흔합니다. 이름을 optimized로 바꿔도 사용자가 여전히 가장 큰 플레이버를 고르면 의미가 없어요. 그래서 quota(쿼터, 프로젝트별 자원 한도), 기본 템플릿, 셀프서비스 포털 가이드까지 같이 손봐야 합니다.

    검증은 이렇게 보시면 됩니다: 자원 최적화가 됐는지 확인하는 법

    변경 후에는 감이 아니라 지표로 보셔야 해요. 저는 보통 아래 네 가지를 같이 봅니다.

    1. 플레이버별 인스턴스 분포가 표준 세트로 모였는지
    2. 컴퓨트 노드별 메모리/CPU 편차가 줄었는지
    3. 새 인스턴스 배치 실패가 감소했는지
    4. 호스트 증설 시점을 뒤로 미룰 수 있는지
    openstack hypervisor stats show
    openstack usage show --project <PROJECT_ID>
    openstack server list --all-projects --long

    제가 직접 해보니 가장 먼저 보이는 변화는 “애매하게 남는 자원”이 줄어드는 점이었어요. 예전에는 노드마다 2GB, 4GB, 6GB씩 찌꺼기처럼 남았는데 실제 배치엔 못 쓰는 경우가 많았거든요. 플레이버를 표준화하고 나니 배치 성공률이 안정되고, 신규 호스트 추가 논의를 한 템포 늦출 수 있었습니다. 프라이빗 클라우드 운영에서는 이 지점이 곧 비용 절감 효과로 이어집니다.

    🎉 특히 showback(쇼백, 내부 비용 가시화)이나 chargeback(차지백, 부서별 비용 배분) 체계가 있는 조직이라면 플레이버 표준화가 훨씬 중요합니다. 기준이 명확해야 부서별 사용 패턴도 설명하기 쉽고, 과도한 요청에 근거 있게 대응할 수 있거든요.

    OpenStack 플레이버 표준화 전후 자원 활용도 개선 결과를 보여주는 대시보드 이미지

    최적화 전후의 자원 활용도와 인스턴스 배치 효율 차이를 보여주는 결과 시각화 이미지입니다.

    자주 묻는 질문

    Q1. OpenStack 플레이버를 많이 만들수록 사용자 만족도가 높아지지 않나요?

    짧게 답하면, 꼭 그렇진 않습니다. 선택지가 너무 많으면 오히려 잘못 고를 확률이 높아져요. 표준 플레이버는 적게, 예외는 분리해서 운영하는 쪽이 장기적으로 효율적이었습니다.

    Q2. 비용 절감 효과를 어떻게 설명하면 좋을까요?

    직접적인 청구 금액보다 호스트 증설 지연, 배치 실패 감소, 유휴 자원 감소 관점으로 설명하는 게 현실적이에요. 프라이빗 클라우드 운영에서는 이게 훨씬 설득력이 있습니다.

    Q3. 기존 인스턴스는 언제 옮기는 게 좋을까요?

    서비스 영향이 적은 점검 창을 잡아서 순차적으로 resize하거나, 재배포 주기에 맞춰 천천히 전환하는 게 안전해요. 한 번에 몰아붙이면 운영팀만 고생합니다. 저도 그렇게 삽질 좀 했습니다.

    정리: 비용 절감은 작은 플레이버가 아니라 좋은 기준에서 시작됩니다

    OpenStack 플레이버 최적화의 핵심은 단순히 VM 사양을 낮추는 게 아니에요. 워크로드를 분류하고, 표준 타입을 정하고, 운영 정책과 연결하는 것이죠. 이 흐름이 잡히면 자원 최적화, 효율성 개선, 비용 절감이 같이 따라옵니다. 반대로 기준 없이 요청마다 플레이버를 추가하면 언젠가 운영 복잡도가 비용으로 돌아와요.

    혹시 지금 환경에서 플레이버 이름이 제각각이거나, 어떤 걸 없애도 되는지 감이 안 오신다면 먼저 사용 현황부터 뽑아보세요. 거기서 답이 보여요. 다음 글에서는 quota 설계와 프로젝트별 자원 정책을 어떻게 묶어야 실제 프라이빗 클라우드 운영이 편해지는지 다뤄볼 예정입니다. 이전 글에서 다룬 하이퍼바이저 자원 모니터링 글과 함께 보셔도 흐름이 더 잘 잡히실 거예요.

    OpenStack 플레이버 설계 원칙과 비용 절감 포인트를 요약한 인포그래픽

    표준화, 운영 정책, 비용 절감 포인트를 한 장으로 정리한 요약 이미지입니다.

    ✅ 오늘 내용만 실무에 적용해도, 플레이버 목록 정리와 기본값 개선만으로 꽤 큰 차이를 느끼실 가능성이 높습니다. 저도 처음엔 이게 뭔가 싶었는데, 막상 정리하고 나니 운영이 훨씬 덜 피곤해졌거든요.

  • [Linux] iptables에서 nftables로 전환: 레거시 방화벽 마이그레이션 가이드

    [Linux] iptables에서 nftables로 전환: 레거시 방화벽 마이그레이션 가이드

    [Linux] iptables에서 nftables로 전환: 레거시 방화벽 마이그레이션 가이드

    리눅스 서버를 오래 운영하다 보면 방화벽 규칙이 점점 덕지덕지 붙는 순간이 오더라고요. 저도 홈랩과 실서버를 같이 굴리면서 iptables nftables 전환 작업을 몇 번 했었는데, 처음엔 “굳이 잘 돌아가는 걸 왜 바꿔?” 싶었어요. 근데 규칙이 늘어나고, IPv4와 IPv6를 따로 관리하고, 서비스별 예외가 계속 생기기 시작하면 이야기가 확 달라져요. 그때부터는 레거시 방화벽 구조가 발목을 잡기 시작하거든요. 이번 글에서는 iptables에서 nftables로 전환할 때 어떤 흐름으로 접근하면 덜 고생하는지, 제가 직접 해보며 삽질했던 포인트까지 묶어서 정리해보겠습니다.

    특히 이 글은 새로운 방화벽을 처음 설계하는 분보다는, 이미 운영 중인 규칙을 가진 상태에서 nftables 마이그레이션을 고민하는 분들께 맞춰 썼습니다. “규칙은 많은데 서비스 중단은 싫다”, “iptables-save 출력은 복잡한데 어떻게 옮겨야 할지 모르겠다” 같은 상황이 딱 여기에 해당합니다.

    iptables nftables 전환 아키텍처를 보여주는 리눅스 방화벽 마이그레이션 이미지

    레거시 체인과 테이블이 nftables의 통합 규칙셋으로 재구성되는 흐름을 보여주는 개요 이미지입니다.

    왜 지금 iptables에서 nftables로 전환해야 할까요

    쉽게 말해, nftables는 Linux 커널의 Netfilter(넷필터, 리눅스 패킷 필터링 프레임워크) 위에서 동작하는 더 현대적인 규칙 관리 방식이에요. 예전에는 iptables, ip6tables, arptables, ebtables처럼 도구가 나뉘어 있었는데, nftables 쪽은 이걸 훨씬 일관되게 다룰 수 있게 정리해둔 느낌이 강합니다.

    제가 직접 운영하면서 체감한 장점은 크게 세 가지였어요. 첫째, 규칙 표현이 훨씬 읽기 좋습니다. 둘째, IPv4와 IPv6를 한 구조 안에서 관리하기가 정말 편해요. 셋째, 집합(Set, 셋)과 맵(Map, 매핑) 같은 기능을 활용하면 반복 규칙이 크게 줄어듭니다. 예전엔 포트 하나 열 때마다 규칙을 늘어놓았는데, nftables에서는 묶어서 다루니까 관리가 훨씬 편하더라고요.

    항목 iptables nftables
    규칙 구조 테이블/체인/룰이 분산되어 장황해지기 쉬움 문법이 비교적 일관적이고 집합 활용이 쉬움
    IPv4/IPv6 관리 보통 분리 관리 inet 패밀리로 통합 관리 가능
    변환 도구 기존 자산 많음 iptables-translate 같은 변환 도구 활용 가능
    장기 운영성 레거시 환경과의 호환성은 좋음 새 규칙 설계와 정리에 유리

    여기서 중요한 포인트가 하나 있어요. iptables를 무조건 즉시 버리라는 뜻은 아닙니다. 실제 운영에서는 배포판 기본값, 커널 버전, 시스템 서비스와의 연동 방식까지 같이 봐야 하거든요. 다만 새로 정리할 기회가 있다면 nftables 쪽이 구조적으로 훨씬 낫다는 건 분명해요.

    iptables에서 nftables로 전환 전에 꼭 확인할 체크리스트

    저도 처음엔 규칙부터 바꾸려고 달려들었는데, 나중에 보니 그게 제일 위험한 접근이었어요. 리눅스 방화벽 교체 전에 아래 항목부터 확인하는 게 훨씬 안전합니다.

    1. 현재 규칙 백업
      현재 적용된 IPv4/IPv6 규칙을 반드시 저장합니다.
    2. 원격 접속 경로 확인
      SSH 관리 포트가 어디서 열려 있는지 먼저 확인합니다. 이거 놓치면 진짜 난감해집니다.
    3. 배포판의 기본 방화벽 스택 확인
      일부 환경은 iptables 명령이 내부적으로 nft 백엔드를 쓰기도 해요. 헷갈리기 쉬운 부분이죠.
    4. 자동 시작 서비스 확인
      재부팅 후 `nftables.service` 또는 배포판별 방화벽 서비스가 어떤 규칙을 로드하는지 봐야 합니다.
    5. 롤백 경로 준비
      문제가 생기면 바로 이전 규칙으로 되돌릴 방법을 준비해둡니다.

    예를 들어 현재 규칙 백업은 이렇게 해두면 돼요.

    sudo iptables-save > /root/iptables-backup.rules
    sudo ip6tables-save > /root/ip6tables-backup.rules

    그리고 nftables 쪽 현재 상태도 함께 확인해보세요.

    sudo nft list ruleset

    출력이 비어 있더라도 괜찮습니다. 중요한 건 지금 시스템이 무엇을 기준으로 패킷을 처리하는지 감을 잡는 거예요.

    nftables 개념, 쉽게 말해 이렇게 이해하면 됩니다

    저도 처음엔 `table`, `chain`, `hook` 같은 용어가 한꺼번에 나와서 좀 헷갈렸는데요. 쉽게 말해 이렇습니다.

    • Table(테이블): 규칙을 묶는 큰 서랍이에요.
    • Chain(체인): 실제 패킷이 지나가며 검사되는 규칙 목록입니다.
    • Hook(훅): 커널 네트워크 경로의 어느 지점에서 체인을 실행할지 정하는 연결점입니다.
    • Set(셋): IP, 포트 같은 값을 묶어 재사용하는 구조예요.

    iptables에서는 INPUT, OUTPUT, FORWARD 체인 중심으로 익숙해져 있다 보니 nftables 문법이 낯설게 느껴지는데, 구조를 이해하고 나면 오히려 더 명확합니다. 특히 `inet` 패밀리를 쓰면 IPv4와 IPv6 규칙을 함께 다룰 수 있어서, 실무에서 규칙 중복이 많이 줄어들어요.

    레거시 규칙을 그대로 옮기기보다 재설계가 필요한 이유

    이 부분이 꽤 중요합니다. iptables-translate는 분명 유용한 도구입니다. 하지만 “기계적으로 변환된 규칙 = 가장 좋은 nftables 규칙”은 아니더라고요. 번역은 되는데, 구조가 지저분하게 남는 경우가 많아요. 그래서 저는 보통 이렇게 접근합니다. 먼저 변환 도구로 초안을 만들고, 그다음 사람이 읽기 좋은 구조로 다시 정리합니다. 처음엔 귀찮아 보여도 장기적으로 훨씬 이득이에요.

    실전: iptables 규칙을 nftables로 마이그레이션하는 순서

    이제 본격적으로 옮겨보겠습니다. 여기서는 가장 흔한 서버 기준으로 설명할게요. SSH는 열어두고, 이미 확립된 연결은 유지하고, 기본 정책은 보수적으로 가져가는 흐름입니다.

    1. 현재 iptables 규칙 확인

    sudo iptables -S
    sudo ip6tables -S

    규칙을 읽어보면서 실제로 필요한 것과 과거 흔적을 구분하는 게 먼저예요. 저 같은 경우 예전에 테스트하다 남긴 포트 허용 규칙이 생각보다 많았습니다. 이런 건 옮기기 전에 정리하는 게 맞아요.

    2. 변환 초안 생성

    단일 규칙 한 줄은 `iptables-translate`로, 전체 저장본은 `iptables-restore-translate`로 보는 식으로 접근하면 편합니다.

    sudo iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
    sudo iptables-save | sudo iptables-restore-translate

    출력 결과를 바로 적용하기보다, 먼저 파일로 저장해서 검토하는 쪽을 권장해요.

    sudo sh -c 'iptables-save | iptables-restore-translate > /root/translated.nft'

    여기서 중요한 포인트! 변환 결과를 그대로 믿지 마시고, 불필요하게 중복된 규칙이나 체인 구조를 꼭 점검하세요.

    iptables nftables 전환 과정에서 iptables-translate를 사용하는 터미널 이미지

    기존 iptables 규칙을 읽고 nftables 초안으로 변환하는 과정의 핵심 명령 흐름을 보여주는 이미지입니다.

    3. 사람이 읽기 좋은 nftables 규칙으로 정리

    제가 실제로는 아래처럼 새 파일을 다시 구성하는 편입니다. 예시는 가장 기본적인 서버용 필터 규칙셋입니다.

    table inet filter {
        chain input {
            type filter hook input priority 0;
            policy drop;
    
            iif lo accept
            ct state established,related accept
            ct state invalid drop
    
            tcp dport 22 accept
            tcp dport 80 accept
            tcp dport 443 accept
    
            ip protocol icmp accept
            ip6 nexthdr ipv6-icmp accept
        }
    
        chain forward {
            type filter hook forward priority 0;
            policy drop;
        }
    
        chain output {
            type filter hook output priority 0;
            policy accept;
        }
    }

    이 규칙은 설명하기도 좋고, 나중에 봐도 의도가 비교적 분명합니다. `ct state established,related accept` 같은 부분은 기존 연결을 유지하는 데 아주 중요해서 빠뜨리면 안 돼요. 저도 초반에 이걸 빼먹고 “왜 응답 패킷이 이상하지?” 하며 한참 들여다봤었습니다.

    4. Set으로 반복 규칙 줄이기

    nftables의 진짜 장점은 이런 반복 제거에서 드러나요.

    table inet filter {
        set allowed_tcp_ports {
            type inet_service
            elements = { 22, 80, 443 }
        }
    
        chain input {
            type filter hook input priority 0;
            policy drop;
    
            iif lo accept
            ct state established,related accept
            tcp dport @allowed_tcp_ports accept
        }
    }

    포트가 많아질수록 이 방식이 진짜 편하더라고요. 나중에 하나 추가하거나 제거할 때도 눈에 잘 들어옵니다.

    5. 문법 검증 후 적용

    실제 적용 전에는 반드시 문법 검사를 먼저 해요.

    sudo nft -c -f /etc/nftables.conf

    이상 없으면 적용합니다.

    sudo nft -f /etc/nftables.conf

    그리고 자동 시작도 확인해둬요.

    sudo systemctl enable nftables
    sudo systemctl restart nftables

    배포판에 따라 서비스 이름이나 기본 설정 경로가 조금 다를 수 있으니, 이 부분은 현재 환경 기준으로 꼭 다시 확인하셔야 해요.

    ⚠️ 트러블슈팅: 제가 실제로 겪었던 문제들

    여기서부터가 실전입니다. 문서만 보면 금방 끝날 것 같지만, 실제로는 작은 차이 때문에 시간이 꽤 들어가요. 저도 삽질 좀 했습니다 ㅎㅎ

    SSH 접속이 갑자기 끊길 뻔한 경우

    가장 흔한 문제입니다. 기본 정책을 `drop`으로 바꿨는데 SSH 허용 규칙 순서가 뒤에 있거나, 아예 빠져 있으면 바로 위험해져요. 그래서 저는 항상 아래 순서를 지킵니다.

    1. 현재 접속 세션을 유지하는 `established,related` 규칙 먼저 추가
    2. SSH 허용 규칙 추가
    3. 그다음 기본 정책 적용

    이 순서를 지키면 사고 확률이 많이 줄어들어요.

    iptables와 nftables가 동시에 있는 것처럼 보이는 경우

    이건 처음 보면 꽤 혼란스러워요. 배포판에 따라 `iptables` 명령이 내부적으로 nft 기반 호환 레이어를 쓰는 경우가 있거든요. 그래서 “분명 nft로 바꿨는데 iptables 명령도 뭔가 보인다?” 같은 상황이 생깁니다. 이럴 땐 감으로 보지 말고, 실제 활성 규칙셋이 무엇인지 `nft list ruleset` 기준으로 확인하는 습관이 필요해요.

    변환은 됐는데 규칙이 너무 지저분한 경우

    이건 거의 반드시 겪어요. `iptables-translate`는 시작점으로는 훌륭하지만, 최종 결과물로 보기엔 장황한 경우가 많습니다. 여기서 시간을 조금 더 써서 `set`, `inet`, 상태 기반 매칭 중심으로 재구성하면 나중에 유지보수가 훨씬 쉬워져요. 제가 직접 써보니 이 단계가 가장 큰 차이를 만들었습니다.

    nftables 마이그레이션 시 set 기반 규칙 구성을 설명하는 리눅스 방화벽 이미지

    반복되는 포트 허용 규칙을 set으로 정리해 유지보수성을 높이는 구성을 시각화한 이미지입니다.

    검증: nftables 마이그레이션 후 무엇을 확인해야 할까요

    규칙 적용이 끝났다고 바로 마무리하면 안 돼요. 실제로 원하는 동작이 나오는지 검증이 꼭 필요합니다.

    기본 검증 명령

    sudo nft list ruleset
    sudo ss -tulpn
    sudo journalctl -u nftables --no-pager

    첫 번째는 현재 규칙셋 확인, 두 번째는 실제 리슨(Listen, 수신 대기) 포트 확인, 세 번째는 서비스 적용 로그 확인입니다. 저는 여기에 외부 호스트에서 실제 접속 테스트도 꼭 붙입니다. 서버 안에서만 보면 놓치는 게 있거든요.

    체크해야 할 항목

    • SSH, 웹, 모니터링 포트가 의도대로 열려 있는지
    • 허용하지 않은 포트가 막혀 있는지
    • 재부팅 후에도 동일한 규칙이 유지되는지
    • IPv6 트래픽도 의도대로 동작하는지

    특히 재부팅 검증은 꼭 해보세요. 적용은 됐는데 부팅 후 원래 규칙이 다시 올라오거나, 반대로 아무 규칙도 안 올라오는 경우가 있거든요. 저도 예전에 이걸 놓쳐서 “어제 분명 됐는데 왜 오늘 다르지?” 하며 로그를 뒤졌던 적이 있습니다.

    iptables nftables 전환 후 규칙 검증과 포트 확인을 보여주는 운영 점검 이미지

    적용된 규칙셋과 실제 서비스 포트 상태를 함께 확인하는 검증 단계의 결과 이미지입니다.

    iptables와 nftables, 어떤 방식으로 운영 정리하는 게 좋을까요

    운영 기준으로 보면 저는 이렇게 정리해요. 기존 시스템이 매우 안정적으로 돌고 있고 외부 의존성이 강하면, 한 번에 크게 바꾸기보다 단계적으로 옮기는 게 맞습니다. 반대로 규칙이 이미 복잡하고, 중복이 많고, 앞으로도 계속 확장될 예정이라면 nftables로 정리할 가치가 충분해요.

    운영 상황 권장 접근
    단순한 단일 서비스 서버 기본 규칙을 nftables로 재작성 후 빠르게 전환
    규칙이 많은 오래된 서버 iptables 백업 후 변환 초안 생성, 단계적 검증
    IPv4/IPv6 동시 운영 inet 테이블 중심으로 통합 설계
    반복 포트/주소 규칙이 많음 set과 map 활용으로 구조 단순화

    혹시 이런 경험 있으신가요? 규칙은 분명 맞는 것 같은데, 몇 달 뒤 다시 보면 왜 이렇게 짰는지 본인도 기억이 안 나는 경우요. 사실 리눅스 방화벽은 “작동만 하면 된다”가 아니라, 나중에 다시 읽어도 이해되는 구조가 정말 중요합니다. nftables는 바로 그 지점에서 큰 장점이 있어요.

    정리: 리눅스 방화벽 교체는 번역보다 재설계가 핵심입니다

    이번 iptables nftables 전환의 핵심은 단순 치환이 아닙니다. 리눅스 방화벽 교체를 한다고 생각하면, 기존 규칙을 점검하고, 필요한 것만 남기고, nftables 문법과 구조에 맞게 다시 정리하는 과정이 훨씬 중요합니다. `iptables-translate`는 좋은 출발점이지만, 최종 목적지는 결국 사람이 관리하기 좋은 규칙셋이어야 해요.

    제가 직접 해보니 가장 중요한 건 세 가지였습니다. 백업, 순서, 검증입니다. 백업 없이 시작하지 말 것. SSH 같은 필수 허용 규칙을 먼저 둘 것. 적용 후에는 반드시 외부에서 검증할 것. 이 세 가지만 지켜도 큰 사고를 많이 줄일 수 있어요.

    다음 글에서는 `nftables`에서 NAT(Network Address Translation, 네트워크 주소 변환) 규칙과 포트 포워딩을 정리하는 방법도 다뤄볼까 합니다. 홈랩 돌리시는 분들은 그쪽도 꽤 자주 쓰시거든요. 이전 글에서 다룬 리눅스 네트워크 기본 흐름과 함께 보시면 훨씬 이해가 잘 되실 겁니다.

    iptables nftables 전환 핵심 체크리스트를 요약한 인프라 운영 인포그래픽

    전환 이유, 절차, 검증 포인트를 한 번에 복습할 수 있도록 요약한 마무리 인포그래픽입니다.

    자주 묻는 질문

    iptables 규칙을 그대로 자동 변환해서 써도 될까요?

    가능은 하지만 권장하지는 않아요. 초안 생성에는 좋지만, 장기 운영을 생각하면 사람이 읽기 좋은 형태로 정리하는 게 훨씬 낫습니다.

    nftables 마이그레이션 시 가장 위험한 실수는 뭔가요?

    원격 접속 허용 규칙보다 먼저 기본 차단 정책을 적용하는 거예요. SSH 세션이 끊기면 복구가 번거로워집니다.

    IPv6를 안 쓰는 것 같으면 무시해도 될까요?

    환경에 따라 다르지만, 실제로는 IPv6가 살아 있는 경우가 적지 않아요. 인터페이스와 서비스 노출 상태를 먼저 확인해보는 게 안전합니다.

    netfilter를 꼭 깊게 알아야 하나요?

    커널 내부까지 깊게 파고들 필요는 없지만, 패킷이 어느 훅(hook)을 지나고 어느 체인에서 처리되는지 정도는 이해해두면 문제 해결이 훨씬 빨라져요.

  • [OpenStack] OpenStack Trove vs RDS 마이그레이션, 왜 AWS RDS로 옮겼나

    [OpenStack] OpenStack Trove vs RDS 마이그레이션, 왜 AWS RDS로 옮겼나

    OpenStack Trove vs RDS 마이그레이션, 제가 AWS RDS로 옮긴 이유

    OpenStack Trove vs RDS 마이그레이션을 고민하는 분들이 생각보다 많더라고요. 저도 한동안 OpenStack 환경에서 관리형 데이터베이스를 직접 운영해봤습니다. 처음엔 '우리 클라우드 안에서 끝내면 제일 깔끔하지 않을까?' 싶었는데, 막상 들어가 보니 기능보다 운영 복잡도가 더 크게 다가왔습니다. 결국 저는 Trove를 접고 AWS RDS로 옮겼습니다.

    이 글은 'Trove가 무조건 나쁘다'는 얘기를 하려는 건 아닙니다. 다만 OpenStack 관리형 DB의 한계가 어디서 드러나는지, 그리고 어떤 조직에서는 왜 AWS RDS가 더 현실적인 선택이 되는지 제 경험 기준으로 정리해보려 합니다. 지금 관리형 데이터베이스 선택 때문에 고민 중이라면, 아마 중간중간 꽤 공감되실 겁니다.

    온프레미스 OpenStack 영역과 AWS RDS 영역을 나란히 보여주는 비교 아키텍처 예시입니다.

    1. OpenStack Trove vs RDS 마이그레이션 이야기가 자주 나오는 이유

    Trove는 OpenStack 안에서 DBaaS(Database as a Service, 서비스형 데이터베이스)를 제공하는 프로젝트입니다. 사용자는 직접 VM을 띄우고 MySQL이나 PostgreSQL을 손으로 설치하지 않아도 되고, API나 CLI로 인스턴스를 만들고 관리할 수 있습니다. 개념만 보면 꽤 매력적입니다.

    문제는 여기서부터였어요. 관리형이라는 단어에 기대하는 수준이 사람마다 다르거든요. 사용자는 보통 이렇게 기대합니다.

    • 장애가 나도 복구가 빠를 것
    • 백업과 복원이 일관되게 동작할 것
    • 버전 업그레이드가 예측 가능할 것
    • 모니터링과 알림 구성이 어렵지 않을 것
    • 운영자가 밤에 덜 깨울 것

    그런데 실제 운영에 들어가면 Trove는 '완성형 관리 서비스'라기보다 OpenStack 위에 얹은 DB 배포·운영 프레임워크에 더 가깝게 느껴질 때가 있습니다. 반면 AWS RDS는 오랫동안 다듬어진 상용 관리형 서비스라서, 운영 경험이 이미 서비스 안에 녹아 있다는 느낌이 있었어요. 결국 클라우드 DB 서비스 비교를 할 때 핵심은 기능표보다도 '누가 운영 책임을 얼마나 가져가 주느냐'였습니다.

    2. OpenStack Trove vs RDS를 쉽게 설명해보면

    2-1. Trove는 어떤 성격인가

    Trove는 OpenStack 프로젝트 중 하나이고, DBaaS를 목표로 합니다. 인스턴스 생성, 데이터베이스와 사용자 관리, 백업과 복원 같은 기본 기능은 분명 있습니다. 다만 구조를 뜯어보면 Nova, Neutron, Cinder, Keystone, Swift 같은 여러 OpenStack 컴포넌트와 꽤 강하게 얽혀 있습니다. 운영 환경에 따라서는 관리 네트워크, guest agent, datastore 이미지까지 챙겨야 해서 생각보다 손이 많이 가더라고요.

    2-2. RDS는 어떤 성격인가

    AWS RDS(Relational Database Service, AWS의 관리형 관계형 DB 서비스)는 관계형 데이터베이스 운영 작업을 크게 줄여주는 서비스입니다. 인스턴스 생성, 자동 백업, 스냅샷, 시점 복구, 파라미터 그룹, 보안 그룹 연계 같은 운영 레일이 비교적 잘 정리돼 있습니다. 물론 완전 자동은 아닙니다. 설계와 튜닝은 여전히 사람이 해야 합니다. 그래도 기본 운영 레일이 잘 깔려 있다는 점은 확실히 컸습니다.

    2-3. 제가 느낀 가장 큰 차이

    항목 OpenStack Trove AWS RDS
    도입 목적 자체 OpenStack 환경에서 DBaaS 제공 AWS에서 검증된 관리형 DB 운영
    운영 난이도 인프라 팀 숙련도와 표준화 수준에 크게 좌우 상대적으로 낮고 표준화가 잘 됨
    문제 발생 시 원인 추적 컴퓨트, 네트워크, 스토리지, 이미지까지 넓게 봐야 함 서비스 경계가 비교적 명확함
    백업/복원 체감 구성과 저장소 정책에 따라 편차가 큼 자동 백업과 스냅샷 흐름이 비교적 일관적임
    확장성과 표준 운영 경험 구현 수준에 따라 다름 문서와 사례가 풍부함

    여기서 중요한 포인트는 이겁니다. Trove의 한계는 '기능이 없다'가 아니라, 운영 품질을 일정하게 유지하기가 어렵다는 데 있었습니다.

    3. 제가 Trove를 포기하게 된 실제 이유

    3-1. 장애 경계가 너무 넓었습니다

    실제로 써보면 DB 하나 문제 생겼을 때 DB 엔진만 보면 끝나는 경우가 거의 없었습니다. 인스턴스 상태, 스토리지 연결, 네트워크 정책, guest agent 동작, 이미지 상태까지 같이 봐야 했거든요. 이게 한두 번이면 괜찮은데 반복되면 운영 피로가 확 올라갑니다. 결국 Trove 운영 비용은 라이선스보다 사람 시간에서 크게 나오더라고요.

    3-2. '관리형' 기대치와 현실의 차이

    사용자 입장에서는 RDS처럼 몇 가지 옵션만 정하면 끝나는 경험을 기대합니다. 그런데 Trove는 환경에 따라 준비해야 할 게 많습니다. 이미지 관리도 신경 써야 하고, Swift 백업 저장소 정책도 따져야 하고, 네트워크 경로도 챙겨야 합니다. 인프라 팀이 강하면 운영할 수는 있지만, 결국 특정 담당자 의존도가 높아졌습니다.

    3-3. 마이그레이션 이후 운영 피로도가 확 줄었습니다

    이건 정말 체감이 컸습니다. RDS로 옮긴 뒤에는 DB 엔진 자체보다 애플리케이션 성능, 쿼리 튜닝, 파라미터 최적화에 시간을 더 쓰게 됐어요. 예전에는 '왜 인스턴스가 이 상태지?'를 먼저 봤다면, 옮긴 뒤에는 '이 쿼리가 왜 느리지?'를 먼저 보게 되더라고요. 운영자가 봐야 하는 층이 줄어드니 훨씬 낫습니다.

    4. OpenStack Trove vs RDS 마이그레이션, 저는 이렇게 진행했습니다

    아래는 제가 보통 권장하는 흐름입니다. 엔진은 예시로 MySQL 계열을 가정하되, 핵심은 절차입니다.

    1. 현재 Trove 인스턴스 상태 점검: 버전, 파라미터, 사용자, 백업 정책, 연결 애플리케이션 목록 정리
    2. RDS 목표 사양 설계: 엔진 계열, 인스턴스 클래스, 스토리지 유형, 백업 보존, 네트워크, 보안 그룹 정리
    3. 사전 덤프 테스트: 운영 전과 동일한 방식으로 내보내기와 가져오기를 리허설
    4. 애플리케이션 연결 문자열 분리: 엔드포인트 전환이 가능하도록 설정 외부화
    5. 점검 창 확보 후 최종 데이터 이전
    6. 읽기/쓰기 검증 후 DNS 또는 설정 전환

    4-1. Trove 쪽 사전 점검

    openstack database instance list
    openstack database instance show mydb
    openstack database backup list
    

    명령어는 단순하지만, 여기서 확인할 게 많습니다. 인스턴스 이름만 보지 말고 실제 엔진 버전, 백업 상태, 연결 중인 애플리케이션 목록을 같이 정리하셔야 합니다. 저도 처음엔 대충 보고 넘어갔다가 예전 접속 정보를 계속 물고 있는 애플리케이션 하나 때문에 꽤 헤맸습니다.

    4-2. 데이터 덤프

    mysqldump -h TROVE_DB_HOST -u migration_user -p --databases appdb > appdb.sql
    

    여기서는 덤프가 되느냐보다 복원 시간이 어느 정도인지를 꼭 재보셔야 합니다. OpenStack Trove vs RDS 마이그레이션 관련 검색으로 들어오신 분들이 가장 많이 놓치는 부분이 이거예요. 덤프는 되는데 복원이 너무 오래 걸리면 점검 창이 바로 무너집니다.

    OpenStack Trove vs RDS 마이그레이션 데이터 이전 흐름 이미지

    Trove 소스 DB에서 덤프를 만들고 AWS RDS 대상으로 복원하는 흐름을 나타낸 구성도입니다.

    4-3. RDS 대상 준비

    aws rds create-db-instance \
      --db-instance-identifier appdb-prod \
      --db-instance-class db.t3.micro \
      --engine mysql \
      --master-username admin \
      --manage-master-user-password \
      --allocated-storage 20 \
      --backup-retention-period 7
    

    실무에서는 보통 콘솔과 IaC(Infrastructure as Code, 코드형 인프라)를 같이 씁니다. 위 예시는 CLI 흐름만 간단히 보여드린 겁니다. 실제로는 VPC, DB subnet group, VPC security group, DB parameter group까지 함께 설계하셔야 해요. 이 부분을 건너뛰면 서비스는 떠도 운영은 금방 불편해집니다.

    4-4. 복원 및 검증

    mysql -h RDS_ENDPOINT -u admin -p < appdb.sql
    mysql -h RDS_ENDPOINT -u app_user -p -e "SHOW DATABASES;"
    

    저는 여기서 단순 접속 확인만 하지 않고, 애플리케이션이 실제로 사용하는 주요 쿼리도 몇 개 꼭 돌려봤습니다. 문자셋이나 권한 차이 때문에 예상 못 한 오류가 나는 경우가 있거든요. 이런 문제는 전환 직후에 터지면 꽤 곤란합니다.

    5. 설정 화면보다 중요한 설계 포인트

    관리형 데이터베이스 선택에서 자주 놓치는 게 있습니다. 서비스 이름보다 주변 설계를 먼저 봐야 한다는 점입니다.

    • 네트워크 경계: 퍼블릭 접근 여부, 배스천 호스트 사용 여부
    • 보안: 최소 권한 계정 분리, 운영 계정과 애플리케이션 계정 분리
    • 백업/복구: 자동 백업 보존 기간, 스냅샷 주기, 복구 리허설 여부
    • 관측성: CPU, 연결 수, 지연 시간, 슬로우 쿼리 관찰 체계
    • 전환 전략: DNS 전환인지 애플리케이션 설정 전환인지 미리 결정

    사실 서비스 자체보다 이런 운영 설계가 결과를 좌우합니다. RDS가 편한 건 맞지만, 설계를 대충 하면 어디서든 사고 납니다. 이전 글에서 다룬 백업 자동화 내용이 있다면 이 부분과 같이 보는 게 훨씬 도움이 됩니다.

    관리형 데이터베이스 선택 시 확인하는 AWS RDS 운영 설정 이미지

    RDS 운영에서 자주 만지는 설정 요소들을 한 화면 개념도로 묶어 보여주는 예시입니다.

    6. 실제로 겪었던 문제와 해결법

    6-1. 애플리케이션 타임아웃

    복원 후 애플리케이션 연결은 되는데 응답이 느린 경우가 있었습니다. 처음엔 DB 성능 문제인 줄 알았는데, 실제로는 보안 그룹과 네트워크 경로가 꼬여 있어서 우회 홉이 생긴 적도 있었어요. 이건 DB만 들여다보면 잘 안 잡힙니다.

    6-2. 권한 차이

    Trove에서 쓰던 계정 권한과 RDS에서 허용되는 범위가 완전히 같다고 생각하면 안 됩니다. 특히 슈퍼유저처럼 넓게 쓰던 계정이 있었다면 전환하면서 정리하는 편이 낫습니다. 저도 이참에 권한을 쪼개서 다시 만들었는데, 번거롭긴 해도 결과적으로 훨씬 안정적이었습니다.

    6-3. 백업이 있다고 복구가 쉬운 건 아니었습니다

    이 부분은 정말 중요합니다. 백업이 존재하는 것과 정해진 시간 안에 복구가 끝나는 것은 다른 얘기거든요. 저도 예전에 백업만 믿고 있다가 복원 속도 때문에 식은땀 흘린 적이 있습니다. 그 뒤로는 무조건 복구 리허설을 같이 했습니다.

    6-4. 운영 문서가 없으면 또 같은 삽질을 합니다

    OpenStack Trove vs RDS 마이그레이션을 마치면 다 끝난 것 같지만, 실제로는 그때부터 운영 기준을 굳혀야 합니다. 엔드포인트, 계정, 비밀번호 회전 정책, 알람 기준을 문서화하지 않으면 다음 장애 때 같은 문제를 반복하게 돼요. 저는 위키에 runbook을 따로 남겨뒀습니다.

    7. 검증은 이렇게 했습니다

    1. 애플리케이션 읽기 기능 확인
    2. 쓰기 기능 확인
    3. 배치 작업 및 스케줄러 실행 확인
    4. 모니터링 지표 수집 여부 확인
    5. 백업 생성 및 복원 테스트 계획 확인

    검증 단계에서는 '접속된다'만 보면 안 됩니다. 실제 업무 흐름이 정상인지를 봐야 하거든요. 저는 최소한 로그인, 조회, 저장, 배치, 알람까지 묶어서 확인했습니다. 그렇게 하니 전환 직후 불안감이 훨씬 줄더라고요.

    OpenStack Trove vs RDS 마이그레이션 검증 결과 대시보드 이미지

    전환 후 연결 수, 지연 시간, 백업 상태 등을 점검하는 결과 대시보드 예시입니다.

    8. 결국 왜 RDS였나: 비용보다 운영 예측 가능성

    많은 분이 RDS를 얘기하면 바로 비용부터 떠올리십니다. 맞아요. 퍼블릭 클라우드 비용은 늘 예민하죠. 그런데 제가 직접 운영해보니, Trove 운영 비용은 단순 인프라 비용표만으로 잘 안 보였습니다. 장애 대응 시간, 특정 인력 의존도, 야간 대응 피로도, 복구 리허설 부담까지 다 포함해야 하거든요.

    반대로 RDS는 비용이 비교적 눈에 잘 보이고, 운영 품질도 어느 정도 예측 가능합니다. 제 기준에서는 이 '예측 가능성'이 정말 컸습니다. 물론 모든 환경이 AWS로 가야 한다는 뜻은 아닙니다. 규제, 데이터 위치, 기존 OpenStack 투자 규모 때문에 Trove나 자체 구축이 더 맞는 조직도 분명히 있습니다.

    이런 경우 추천 방향
    OpenStack 전문 인력이 충분하고 내부 표준화가 잘 된 경우 Trove 유지 검토 가능
    소수 인력으로 안정적 운영이 더 중요한 경우 RDS 같은 상용 관리형 서비스 검토
    장애 대응 시간과 복구 예측 가능성이 핵심인 경우 RDS 쪽 장점이 큼
    커스터마이징보다 운영 단순화가 우선인 경우 RDS가 유리한 편

    9. 정리와 FAQ

    정리하자면, 제가 Trove를 포기하고 AWS RDS로 간 이유는 화려한 기능 차이보다 운영 복잡도와 책임 범위 때문이었습니다. 처음에는 OpenStack 안에서 다 해결하면 멋있어 보였는데, 실제로는 DB 운영보다 플랫폼 운영 이슈를 더 많이 다루게 되더라고요. 그 순간 '이건 우리가 풀어야 할 문제가 조금 다르다'는 생각이 들었습니다.

    다음 글에서는 RDS 전환 후 파라미터 그룹 정리, 백업 점검 루틴, 슬로우 쿼리 확인 흐름을 따로 다뤄보려 합니다. 이전 글에서 홈랩 백업 자동화 얘기를 보셨다면 그 연장선으로 같이 읽으셔도 좋습니다.

    자주 묻는 질문

    • Q. Trove는 쓰면 안 되나요?
      아닙니다. 조직 역량과 표준화 수준이 충분하면 의미 있습니다. 다만 기대하는 '관리형' 수준을 먼저 맞춰보셔야 합니다.
    • Q. RDS로 가면 운영이 완전히 끝나나요?
      그건 아닙니다. 스키마 설계, 쿼리 튜닝, 접근 제어, 비용 관리는 여전히 중요합니다.
    • Q. 마이그레이션에서 제일 중요한 한 가지는 뭔가요?
      복원 리허설입니다. 백업 존재 여부보다 복원 가능 시간이 더 중요합니다.
    클라우드 DB 서비스 비교와 Trove 운영 비용 요약 이미지

    Trove 유지와 RDS 전환 중 어떤 선택이 맞는지 빠르게 판단할 수 있는 요약 인포그래픽입니다.

  • [보안] VPN 비용 분석: 매니지드 VPN과 WireGuard 자체 호스팅 3년 TCO 비교

    [보안] VPN 비용 분석: 매니지드 VPN과 WireGuard 자체 호스팅 3년 TCO 비교

    VPN 비용 분석: 매니지드 VPN과 WireGuard 자체 호스팅 3년 TCO 비교

    VPN 비용 분석을 제대로 해보면, 월 구독료만 보는 방식이 얼마나 위험한지 금방 느끼게 됩니다. 특히 팀 규모가 조금만 커져도 매니지드 VPN은 편하긴 한데 생각보다 예산을 빨리 잡아먹고, 반대로 WireGuard 자체 호스팅은 싸게 시작했다가 운영 시간이 숨어 있는 비용으로 돌아오더라고요. 저도 홈랩에서 시작해서 실제 업무 환경처럼 사용자 수를 늘려가며 비교해봤는데, 처음엔 “당연히 직접 올리는 게 싸지” 싶었거든요. 근데 3년 총 소유 비용(TCO, Total Cost of Ownership) 관점으로 보니 얘기가 좀 달라졌습니다.

    이번 글은 특정 서비스의 가격표를 늘어놓기보다, 실제로 검증 가능한 비용 항목을 기준으로 매니지드 VPN과 WireGuard 자체 호스팅 VPN을 비교합니다. 지금 보안 예산을 짜고 계시거나, 네트워크 보안 관점에서 어떤 선택이 덜 아픈지 고민 중이시라면 꽤 도움이 될 거예요.

    매니지드 VPN과 자체 호스팅 VPN의 구성 요소, 운영 책임, 비용 발생 지점을 한눈에 보여주는 개요 이미지입니다.

    1. 왜 VPN 비용 분석은 월 요금표만 보면 안 될까요

    쉽게 말해, 매니지드 VPN은 돈으로 운영 복잡도를 사는 구조고, WireGuard 자체 호스팅은 운영 시간을 써서 현금 지출을 줄이는 구조입니다. 둘 다 맞는 선택이 될 수 있어요. 문제는 이걸 단순히 “서비스 A는 월 얼마, VPS는 월 얼마” 식으로 비교하면 거의 항상 판단이 틀어진다는 점입니다.

    • 매니지드 VPN: 계정 관리, 접속 정책, 로그, 고가용성(HA, High Availability), 지원 체계를 서비스 사업자가 대신 봐줍니다.
    • WireGuard 자체 호스팅 VPN: 서버, 키 관리, 모니터링, 백업, 장애 대응, 운영 문서화까지 직접 책임져야 합니다.
    • 숨은 비용: 장애 한 번 났을 때 누가 새벽에 일어나는지, 이게 진짜 비용이거든요.

    직접 해보니 작은 팀에서는 자체 호스팅이 엄청 매력적으로 보여요. 설정도 단순하고 속도도 좋거든요. 그런데 사용자 온보딩(Onboarding, 신규 사용자 등록), 키 회전(Key Rotation, 키 교체), 감사 로그(Audit Log, 추적 로그)까지 붙기 시작하면 운영 난도가 확 올라갑니다.

    2. 매니지드 VPN과 WireGuard 자체 호스팅, 기본 개념부터 정리하겠습니다

    2-1. 매니지드 VPN이 제공하는 것

    매니지드 VPN은 서비스 제공자가 제어면(Control Plane)을 직접 운영합니다. 관리 콘솔, 사용자 초대, 디바이스 승인, 정책 적용, 상태 확인 기능이 모두 포함되어 있어요. 여기서 중요한 포인트! 여러분이 사는 건 단순한 터널링(Tunneling, 트래픽 캡슐화) 기능이 아니라 운영 자동화입니다.

    2-2. WireGuard 자체 호스팅이 제공하는 것

    WireGuard는 커널 레벨 또는 경량 구현으로 동작하는 VPN 프로토콜로 잘 알려져 있고, 설정 구조가 비교적 단순해요. 그래서 홈랩이나 소규모 팀의 자체 호스팅 VPN으로 많이 선택됩니다. 처음 설정 파일 몇 개로 동작이 잡히는 걸 보고 꽤 인상적이었어요. 다만 WireGuard 그 자체는 운영 포털이 아니라는 게 핵심입니다. 접속은 되는데 관리 체계는 직접 만들어야 하는 경우가 대부분입니다.

    2-3. 3년 총 소유 비용에 들어가는 항목

    항목 매니지드 VPN WireGuard 자체 호스팅
    초기 구축 낮음 중간
    월 고정비 중간~높음 낮음~중간
    운영 시간 낮음 중간~높음
    장애 대응 책임 일부 사업자 부담 대부분 내부 부담
    감사/정책 관리 상대적으로 쉬움 직접 설계 필요
    확장성 사용자 증가 시 비용 증가 설계에 따라 효율적일 수 있음

    3. 3년 VPN 비용 분석: 실제 계산 방식

    VPN 비용 분석에서 저는 아래 항목을 꼭 분리합니다. 숫자를 지어내면 오히려 판단을 망치기 때문에, 각 조직의 실제 단가를 넣는 방식이 가장 안전해요.

    1. 서비스/인프라 비용: 구독료, VPS, 스토리지, 백업, 트래픽 비용
    2. 구축 시간 비용: 초기 설치, 테스트, 문서화
    3. 운영 시간 비용: 계정 관리, 키 재발급, 점검, 패치
    4. 장애 비용: 접속 불가, 복구 시간, 업무 손실
    5. 보안 비용: 로그 보존, 접근 통제, 감사 대응

    예를 들면 이렇게 계산합니다.

    # 3년 총 소유 비용(TCO) 단순 계산 예시
    managed_tco = (월구독료 * 36) + 초기구축인건비 + 운영인건비 + 추가보안옵션비용
    selfhosted_tco = (월인프라비 * 36) + 초기구축인건비 + 운영인건비 + 백업/모니터링비용 + 장애대응비용

    여기서 핵심은 운영인건비를 빼먹지 않는 것입니다. 사실 이게 제일 자주 빠져요. 저도 예전엔 VPS 비용만 보고 “이 정도면 끝”이라고 생각했는데, 키 분실 대응하고, 신규 노트북 교체하고, MTU 꼬인 거 잡고 나니까 생각이 확 달라지더라고요.

    VPN 비용 분석의 3년 총 소유 비용 항목 비교 이미지

    초기 구축비, 월 고정비, 운영 인건비, 장애 대응비를 항목별로 나눠 보여주는 비용 구조 시각화입니다.

    4. WireGuard 자체 호스팅 VPN 실전 구현 예시

    이 글의 목적은 VPN 비용 분석이지만, 실제로 한 번 올려봐야 어떤 항목에서 시간이 나가는지 감이 와요. 그래서 최소 구성 예시를 함께 정리해봤습니다. 저는 홈랩에서 먼저 검증하고, 그다음 업무 환경 체크리스트로 옮기는 식으로 많이 합니다.

    4-1. 키 생성

    umask 077
    wg genkey | tee server_private.key | wg pubkey > server_public.key
    wg genkey | tee client_private.key | wg pubkey > client_public.key

    4-2. 서버 설정 예시

    [Interface]
    Address = 10.10.0.1/24
    ListenPort = 51820
    PrivateKey = SERVER_PRIVATE_KEY
    PostUp = sysctl -w net.ipv4.ip_forward=1
    PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
    PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
    PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    
    [Peer]
    PublicKey = CLIENT_PUBLIC_KEY
    AllowedIPs = 10.10.0.2/32

    4-3. 클라이언트 설정 예시

    [Interface]
    Address = 10.10.0.2/24
    PrivateKey = CLIENT_PRIVATE_KEY
    DNS = 1.1.1.1
    
    [Peer]
    PublicKey = SERVER_PUBLIC_KEY
    Endpoint = vpn.example.com:51820
    AllowedIPs = 0.0.0.0/0
    PersistentKeepalive = 25

    4-4. 컨테이너 기반으로 올리고 싶다면

    services:
      wireguard:
        image: lscr.io/linuxserver/wireguard:latest
        container_name: wireguard
        cap_add:
          - NET_ADMIN
          - SYS_MODULE
        environment:
          - PUID=1000
          - PGID=1000
          - TZ=Etc/UTC
          - SERVERURL=vpn.example.com
          - SERVERPORT=51820
          - PEERS=3
          - PEERDNS=1.1.1.1
          - INTERNAL_SUBNET=10.10.0.0
        volumes:
          - ./config:/config
          - /lib/modules:/lib/modules
        ports:
          - 51820:51820/udp
        sysctls:
          - net.ipv4.conf.all.src_valid_mark=1
        restart: unless-stopped

    여기까지만 보면 “어? VPN 자체 호스팅 할 만한데요?” 싶어요. 맞습니다. 소규모 환경에서는 진짜 할 만해요. 근데 이제부터가 운영이거든요.

    WireGuard 자체 호스팅 VPN 구성과 연결 흐름 이미지

    WireGuard 인터페이스, 피어 설정, NAT 흐름을 함께 보여주는 실전 구성 이미지입니다.

    5. ⚠️ 실제로 많이 겪는 문제와 숨은 비용

    삽질 경험을 좀 솔직하게 적어보겠습니다. 자체 호스팅 VPN의 비용은 서버비보다 예외 상황 처리 시간에서 크게 나와요.

    • 키 관리: 사용자가 노트북을 바꾸거나 스마트폰을 초기화하면 재배포 작업이 생깁니다.
    • MTU 문제: 연결은 되는데 특정 사이트만 느리거나 끊기는 경우가 있어요.
    • NAT/방화벽: Ingress(인그레스, 외부 트래픽 진입점) 경로가 꼬이면 원인 추적에 시간이 꽤 듭니다.
    • 로그 부족: 누가 언제 왜 안 붙는지 보기가 불편하면 장애 시간이 길어집니다.
    • 문서화 부재: 담당자가 휴가 가면 남은 사람이 고생해요.

    제가 자주 썼던 점검 명령어도 남겨보겠습니다.

    sudo wg show
    sudo ip addr show wg0
    sudo ip route
    sudo ss -lunp | grep 51820
    sudo journalctl -u wg-quick@wg0 --since today

    wg show에서 최신 핸드셰이크(latest handshake)와 전송 바이트가 보여요. 여기서 상태가 멈춰 있으면 키 문제인지 방화벽 문제인지 방향을 빨리 잡을 수 있습니다. 드디어 됐다! 하는 순간이 오긴 오는데, 그 전에 꽤 헤맬 수 있어요.

    6. 어떤 조직에서 무엇이 더 유리할까요

    3년 총 소유 비용 기준으로 자주 권하는 판단 방식을 정리해봤습니다.

    상황 추천 방향 이유
    1인 개발자, 홈랩, 소규모 팀 WireGuard 자체 호스팅 구축 난도 대비 비용 효율이 좋음
    보안 정책/감사 요구가 큰 조직 매니지드 VPN 운영 표준화와 추적성이 중요함
    24×7 대응 인력이 없는 팀 매니지드 VPN 장애 대응 부담을 줄일 수 있음
    네트워크 엔지니어가 직접 운영 가능한 팀 WireGuard 자체 호스팅 내부 역량을 비용 절감으로 전환 가능

    여기서 중요한 포인트! 자체 호스팅이 무조건 저렴한 건 아닙니다. 월 인프라비는 낮아도 운영자가 드문드문 1시간씩 계속 쓰는 구조면, 3년 누적으로 꽤 커져요. 반대로 매니지드 VPN은 비싸 보여도 예산이 예측 가능해서 경영진 설득이 훨씬 쉬운 장점이 있어요.

    7. 검증 방법: 숫자 없이 현실적인 VPN 비용 분석을 하는 법

    정확한 단가를 공개하기 어려운 조직도 많으니까, 아래 체크리스트로 비교표를 먼저 만들어보세요.

    1. 사용자 수를 현재/1년 후/3년 후로 나눕니다.
    2. 접속 기기 수를 사용자당 평균으로 잡습니다.
    3. 월 운영 시간을 실제 담당자 기준으로 추정합니다.
    4. 장애 발생 시 평균 복구 시간을 보수적으로 잡습니다.
    5. 감사 로그, 접근 제어, 백업 요구사항을 따로 체크합니다.

    그리고 최종적으로 아래처럼 정리하면 됩니다.

    # 예시 질문
    - 신규 사용자 추가에 몇 분 걸리는가?
    - 담당자 1명이 없으면 다른 사람이 운영 가능한가?
    - 접속 이력 확인이 필요한가?
    - 보안 예산에서 인건비가 보이는가, 숨겨져 있는가?
    - 3년 뒤 사용자 수가 2배가 되어도 같은 구조로 버틸 수 있는가?

    이 질문에 답하다 보면, 네트워크 보안 설계가 단순히 기술 선택이 아니라 운영 모델 선택이라는 게 명확해져요. 이거 정말 편하더라고요. 숫자가 없어도 방향성이 훨씬 또렷해집니다.

    매니지드 VPN과 자체 호스팅 VPN의 운영 부담 비교 결과 이미지

    사용자 수와 운영 부담에 따라 매니지드 VPN과 자체 호스팅 VPN 중 어느 쪽이 유리한지 보여주는 결과 이미지입니다.

    8. 자주 묻는 질문

    Q1. WireGuard 자체 호스팅 VPN은 보안상 불리한가요?

    반드시 그렇진 않아요. 다만 프로토콜 자체와 별개로, 운영 보안이 중요합니다. 키 배포, 키 폐기, 서버 패치, 백업 관리가 허술하면 위험해집니다.

    Q2. 매니지드 VPN은 왜 비싸게 느껴질까요?

    직접 눈에 보이는 건 월 구독료라서 그래요. 하지만 사용자 관리와 장애 대응 시간을 절약해주는 부분까지 합치면 오히려 합리적인 경우가 많습니다.

    Q3. 보안 예산이 작은 팀은 무조건 자체 호스팅이 맞을까요?

    꼭 그렇진 않아요. 보안 예산이 작아도 운영 담당자가 부족하면 매니지드 VPN이 더 싸게 끝나는 경우가 있습니다. 이 부분은 다음 글에서 ZTNA(Zero Trust Network Access, 제로 트러스트 네트워크 접근)와 함께 비교해볼 예정입니다.

    9. 마무리: 결국 비용보다 운영 책임을 먼저 봐야 합니다

    정리하면, 이번 VPN 비용 분석의 핵심은 단순해요. 매니지드 VPN은 돈으로 복잡도를 줄이고, WireGuard 자체 호스팅은 시간을 써서 현금 지출을 줄입니다. 어느 쪽이 더 낫다는 정답은 없고, 조직의 인력 구조와 운영 성숙도에 따라 달라집니다.

    저는 개인적으로 홈랩이나 소규모 팀에서는 WireGuard 자체 호스팅 VPN을 꽤 좋아해요. 직접 만져보면 배울 것도 많고, 네트워크 보안 감각도 빨리 올라오거든요. 반면 사용자 수가 늘고 감사 대응이 중요해지면, 매니지드 VPN이 훨씬 편해집니다. 사실 편한 게 제일 중요할 때가 많아요.

    VPN 비용 분석 요약 인포그래픽: 매니지드 VPN과 WireGuard 자체 호스팅 비교

    비용, 운영 시간, 보안 정책, 팀 규모를 기준으로 두 방식을 요약 비교한 마무리 인포그래픽입니다.

    지금 자체 호스팅 VPN을 붙일지, 매니지드 VPN으로 갈지 고민 중이라면 먼저 3년 기준으로 운영 시간을 숫자로 적어보세요. 그 한 줄이 의사결정을 거의 다 끝내줍니다. 이전 글의 홈랩 방화벽 구성 편도 함께 보시면 흐름이 더 잘 잡힐 겁니다.