13년차의 서버실

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

[태그:] 클라우드 인프라

  • [OpenStack] OpenStack 플레이버 설계: 워크로드별 VM 구성 기준

    [OpenStack] OpenStack 플레이버 설계: 워크로드별 VM 구성 기준

    OpenStack 플레이버 설계: 워크로드별 VM 구성 기준

    OpenStack 플레이버 설계가 중요한 이유

    OpenStack 플레이버 설계는 단순히 vCPU 몇 개, RAM 몇 GB를 고르는 일이 아닙니다. 실제 운영 환경에서는 이 선택 하나 때문에 웹 서버는 멀쩡한데 DB만 느려지거나, 작은 배치 작업 하나가 Compute Node(컴퓨트 노드, 가상 머신을 실제로 실행하는 호스트)의 리소스를 오래 붙잡는 상황이 생기거든요.

    저도 처음 홈랩에서 OpenStack을 만졌을 때는 “그냥 small, medium, large 만들면 되겠지”라고 생각했습니다. 그런데 실제로 써보니 꽤 다르더라고요. 같은 4 vCPU VM(Virtual Machine, 가상 머신)이라도 웹 워크로드와 데이터베이스 워크로드가 원하는 리소스 성격은 완전히 달랐습니다. CPU가 순간적으로 치솟는 서비스도 있고, 메모리를 꾸준히 쓰는 서비스도 있고, 디스크 I/O(Input/Output, 입출력) 때문에 전체가 버벅이는 경우도 있었습니다.

    VM 사양은 넉넉해 보이는데 애플리케이션은 느리고, 반대로 어떤 VM은 리소스를 거의 안 쓰는데 너무 큰 플레이버를 물고 있는 상황을 본 적 있으신가요? 이 글에서는 Nova 플레이버를 워크로드 기준으로 어떻게 나누고, 어떤 명령어로 만들고, 운영 중 어떤 지표를 보고 조정할지 실무 관점으로 정리해보겠습니다.

    OpenStack에서 플레이버가 VM 크기뿐 아니라 워크로드 배치 전략의 출발점이 된다는 흐름을 보여주는 개요 이미지입니다.

    Nova 플레이버 개념: VM의 표준 사이즈표

    OpenStack에서 Flavor(플레이버)는 VM을 만들 때 선택하는 리소스 묶음입니다. vCPU, Memory(메모리), Disk(디스크) 같은 기본값을 정의하고, 필요하면 Extra Specs(추가 속성)를 붙여 스케줄링, CPU 정책, 메모리 페이지 정책까지 조정합니다.

    쉽게 말해 옷 사이즈표와 비슷합니다. small, medium, large라는 이름만 있다고 좋은 게 아니라, 우리 서비스에 맞는 치수표를 만들어야 합니다. 운영자가 미리 합리적인 사이즈를 만들어두면 사용자는 매번 VM 사양을 고민하지 않아도 되고, 인프라 입장에서는 리소스 낭비를 줄일 수 있습니다.

    • vCPU: 가상 CPU 개수입니다. CPU 연산이 많은 워크로드에서 중요합니다.
    • RAM: VM에 할당되는 메모리입니다. 캐시, DB, JVM 기반 서비스에서 특히 민감합니다.
    • Root Disk: 기본 부팅 디스크 크기입니다. 이미지 기반 배포에서는 너무 크게 잡지 않는 편이 관리가 쉽습니다.
    • Ephemeral Disk: VM 삭제 시 함께 사라지는 임시 디스크입니다.
    • Extra Specs: CPU 고정, Huge Pages(휴즈 페이지, 큰 메모리 페이지), NUMA(Non-Uniform Memory Access, 비균일 메모리 접근) 같은 고급 옵션을 지정할 때 씁니다.

    중요한 포인트는 하나입니다. 플레이버는 “많이 주면 좋다”가 아닙니다. 워크로드 분석을 먼저 하고, 그 다음에 VM 모양을 맞춰야 합니다. 이 순서가 바뀌면 나중에 튜닝이 꽤 피곤해집니다.

    워크로드 분석 기준: CPU, 메모리, 디스크, 네트워크

    실제 운영에서는 워크로드를 크게 네 부류로 나눠 보는 게 편했습니다. 웹/API 서버, 데이터베이스, 배치/분석 작업, 네트워크 장비형 VM입니다. 이름은 단순하지만 확인해야 할 지표는 서로 다릅니다.

    워크로드 유형 주요 병목 플레이버 설계 방향 확인할 지표
    웹/API 서버 순간 CPU, 네트워크 중간 vCPU, 적당한 RAM, 수평 확장 고려 CPU 사용률, Load Average, 요청 지연
    DB 서버 메모리, 디스크 I/O RAM 여유, 빠른 볼륨, 필요 시 전용 CPU iowait, 디스크 await, 캐시 히트율
    배치/분석 CPU 지속 사용, 메모리 피크 작업 시간대와 동시 실행 수 기준으로 분리 pidstat, sar, 메모리 스왑 여부
    네트워크 장비형 VM 패킷 처리, 인터럽트 CPU 정책과 네트워크 드라이버 확인 pps, 인터페이스 오류, softirq

    실무에서는 평균값보다 피크 패턴을 더 자주 봅니다. CPU 평균이 낮아도 특정 시간대에 요청이 몰리면 체감 성능은 나빠집니다. 반대로 하루에 한 번만 도는 배치 작업에 항상 큰 플레이버를 붙여두면 리소스가 아깝습니다. VM 수가 늘어나면 이 차이가 진짜 크게 느껴지더라고요.

    OpenStack 플레이버 설계 실전: CLI로 생성하기

    이제 실제 명령어로 가보겠습니다. 아래 예시는 OpenStackClient 기준입니다. 환경마다 인증 방식은 다르지만, 보통은 OpenRC 파일을 source 한 뒤 실행합니다.

    source ~/admin-openrc.sh
    
    openstack flavor create m1.web.small \
      --vcpus 2 \
      --ram 4096 \
      --disk 20
    
    openstack flavor create m1.api.medium \
      --vcpus 4 \
      --ram 8192 \
      --disk 30
    
    openstack flavor create m1.db.memory \
      --vcpus 4 \
      --ram 16384 \
      --disk 40
    
    openstack flavor list

    여기서 RAM 단위는 MB입니다. 4096은 4GB, 8192는 8GB로 보면 됩니다. 처음엔 저도 이 단위를 대충 보고 넘겼다가 “왜 생각보다 작게 잡혔지?” 하고 한참 봤던 기억이 있습니다. 은근히 자주 하는 실수입니다.

    DB나 지연 시간에 민감한 워크로드는 Extra Specs(추가 속성)를 고민할 수 있습니다. 예를 들어 CPU를 공유 정책으로 둘지, dedicated(전용 할당) 정책을 쓸지 판단해야 합니다. 다만 hw:cpu_policy=dedicated 같은 설정은 Compute Node의 전용 CPU 집합, Placement 리소스, Nova 설정이 맞아야 의미가 있습니다. 명령어만 넣는다고 마법처럼 빨라지지는 않더라고요.

    Nova 플레이버 생성과 Extra Specs 설정 흐름도

    OpenStack CLI로 기본 플레이버를 만들고 Extra Specs를 붙여 워크로드별로 구분하는 과정을 나타낸 구성 이미지입니다.

    openstack flavor create m1.db.dedicated \
      --vcpus 4 \
      --ram 16384 \
      --disk 40
    
    openstack flavor set m1.db.dedicated \
      --property hw:cpu_policy=dedicated \
      --property hw:mem_page_size=large
    
    openstack flavor show m1.db.dedicated

    hw:cpu_policy=dedicated는 전용 pCPU(Physical CPU, 물리 CPU)에 vCPU를 고정하는 정책이고, hw:mem_page_size=large는 큰 메모리 페이지 사용을 요청합니다. 단, Huge Pages는 호스트 커널과 libvirt/KVM 쪽 준비가 필요합니다. 지원되지 않는 환경에서 무작정 넣으면 VM 생성이 실패할 수 있습니다.

    가상 머신 최적화: 작은 VM 여러 대 vs 큰 VM 한 대

    가상 머신 최적화에서 자주 나오는 고민이 있습니다. “2 vCPU VM 여러 대가 나을까, 8 vCPU VM 한 대가 나을까?” 정답은 워크로드에 따라 다릅니다. 웹/API 서버처럼 stateless(상태를 서버에 많이 저장하지 않는 구조)에 가까우면 작은 VM을 여러 대 두고 로드밸런서로 나누는 편이 운영상 편했습니다. 장애 격리도 좋고, 배포 롤백도 덜 무섭습니다.

    반대로 DB처럼 상태가 크고 디스크 I/O가 중요한 서비스는 무작정 쪼개기 어렵습니다. 이 경우에는 메모리와 디스크 경로를 먼저 보고, 필요하면 전용 CPU 정책이나 빠른 Cinder Volume(신더 볼륨, OpenStack 블록 스토리지)을 붙이는 식으로 접근합니다.

    1. 먼저 애플리케이션의 병목 후보를 정합니다. CPU인지, 메모리인지, 디스크인지 구분합니다.
    2. 작은 기본 플레이버로 시작하되, 모니터링 지표를 붙입니다.
    3. 피크 시간대에 CPU iowait, 메모리 swap, 디스크 await 같은 값을 확인합니다.
    4. 증설이 필요하면 같은 계열의 다음 플레이버로 올립니다.
    5. 계속 같은 병목이 반복되면 플레이버 문제가 아니라 애플리케이션 구조나 스토리지 설계를 다시 봅니다.

    추천하는 방식은 “처음부터 대형 플레이버”가 아니라 계열을 나눠 점진적으로 올리는 방식입니다. 예를 들어 web.small → web.medium → web.large처럼 같은 성격 안에서만 키우면 운영자가 판단하기 쉽습니다. 이거 진짜 편하더라고요.

    점검 명령어: VM 내부와 OpenStack 외부를 같이 보기

    플레이버가 적절한지 보려면 VM 내부 지표와 OpenStack 외부 상태를 같이 봐야 합니다. VM 안에서는 리눅스 성능 도구를 씁니다. 아래 명령어는 대부분의 리눅스 서버에서 sysstat 패키지를 설치하면 사용할 수 있습니다.

    # CPU 사용률과 iowait 확인
    sar -u 1 5
    
    # 디스크 서비스 시간, 대기열, 사용률 확인
    iostat -x 1 5
    
    # 프로세스별 CPU/메모리/디스크 사용 확인
    pidstat -urd 1 5
    
    # 메모리와 swap 확인
    free -h

    해석은 이렇게 합니다. sar에서 %iowait가 계속 눈에 띄게 높다면 CPU가 부족하다기보다 디스크 응답을 기다리는 쪽을 의심합니다. iostat -x에서는 await, %util, r/s, w/s를 함께 봅니다. %util이 계속 높은데 await도 같이 늘면 스토리지 병목 가능성이 커집니다. free -h에서 swap 사용량이 증가하고 줄지 않는다면 메모리 플레이버를 올리거나 애플리케이션 메모리 설정을 줄여야 합니다.

    OpenStack 쪽에서는 현재 하이퍼바이저 자원 상태와 VM 배치를 확인합니다. 일부 명령은 관리자 권한이 필요할 수 있습니다.

    openstack hypervisor list
    openstack hypervisor stats show
    openstack server list --all-projects --long
    openstack server show <SERVER_ID>

    여기서 보는 핵심은 “한 Compute Node에 특정 유형 VM이 몰렸는가”입니다. CPU 집약형 VM이 한 노드에 너무 몰리면 개별 VM 스펙은 정상이어도 전체 성능이 흔들릴 수 있습니다. 이때는 Availability Zone(가용 영역), Host Aggregate(호스트 집합), Flavor Extra Specs를 조합해서 배치 정책을 나누는 쪽을 검토합니다.

    주의사항과 트러블슈팅: 자주 헷갈리는 부분

    OpenStack 플레이버 설계에서 가장 많이 하는 실수는 “플레이버 이름만 그럴듯하게 만든 것”입니다. m1.large라는 이름은 익숙하지만, 정작 어떤 워크로드용인지 아무도 모르면 시간이 지나면서 엉망이 됩니다. 그래서 이름에 목적을 넣는 편이 좋습니다. 예를 들면 m1.web.small, m1.db.memory, m1.batch.cpu처럼요.

    • VM 생성 실패: Extra Specs가 호스트 기능과 맞지 않을 때 발생합니다. flavor show와 nova-compute 로그를 같이 봅니다.
    • 디스크가 느림: vCPU 증설로 해결되지 않습니다. Cinder 백엔드, 볼륨 타입, 게스트 파일시스템, iostat 지표를 확인합니다.
    • 메모리가 부족함: swap이 늘면 체감 성능이 급격히 나빠집니다. RAM 증설 또는 애플리케이션 heap 설정을 조정합니다.
    • CPU를 많이 줬는데도 느림: CPU steal, iowait, 스레드 병렬성, 애플리케이션 락을 같이 봐야 합니다.

    재현 가능한 시나리오 하나를 들어보겠습니다. 웹 서버 VM에 2 vCPU, 4GB RAM을 주고 운영했는데 피크 시간에 응답이 느려졌습니다. 처음에는 CPU가 부족한 줄 알고 4 vCPU로 올렸습니다. 그런데 개선이 애매했습니다. 다시 보니 sar에서 iowait가 계속 보였고, iostat에서는 디스크 대기가 늘어났습니다. 원인은 로그가 같은 루트 디스크에 과하게 쌓이는 구조였고, 별도 볼륨으로 로그 경로를 분리하니 훨씬 안정됐습니다.

    검증과 결과: 플레이버 변경 후 확인할 것

    플레이버를 바꿨다면 “VM이 켜졌다”에서 끝내면 안 됩니다. 저는 최소한 아래 순서로 확인합니다. 특히 resize 이후에는 환경에 따라 confirm 단계가 필요할 수 있으니 운영 정책도 같이 확인하세요.

    1. VM 생성 또는 resize(크기 변경)가 정상 완료됐는지 확인합니다.
    2. 게스트 OS에서 CPU, 메모리, 디스크가 기대한 값으로 보이는지 확인합니다.
    3. 애플리케이션 로그에 오류나 타임아웃이 늘지 않았는지 봅니다.
    4. 피크 시간대 지표를 이전과 비교합니다.
    5. 동일 계열 VM 여러 대의 편차가 크지 않은지 확인합니다.
    openstack server resize --flavor m1.api.medium <SERVER_ID>
    openstack server resize --confirm <SERVER_ID>
    
    # VM 내부에서 확인
    nproc
    free -h
    lsblk

    결과 해석 기준은 간단하게 잡아두면 좋습니다. CPU 사용률이 피크 시간에만 높고 요청 지연이 없다면 굳이 올리지 않아도 됩니다. 메모리는 swap이 생기는지, 디스크는 iowait와 await가 함께 나빠지는지 봅니다. 네트워크는 대역폭뿐 아니라 인터페이스 오류, retransmission(재전송)도 같이 봐야 합니다.

    OpenStack 플레이버 변경 전후 가상 머신 최적화 지표 대시보드

    CPU, 메모리, 디스크 I/O 지표를 기준으로 플레이버 변경 전후를 비교하는 검증용 대시보드 예시입니다.

    자주 묻는 질문: OpenStack 플레이버 설계 FAQ

    Q1. 플레이버는 몇 종류로 시작하는 게 좋나요?

    처음부터 너무 많이 만들지 않는 걸 추천합니다. 웹/API용 2~3개, 메모리 중심 1~2개, CPU 중심 1~2개 정도로 시작하고 실제 사용 패턴을 보면서 늘리는 편이 관리하기 쉽습니다.

    Q2. dedicated CPU는 무조건 좋은 선택인가요?

    아닙니다. 지연 시간에 민감하거나 성능 격리가 중요한 VM에는 도움이 될 수 있지만, 전체 클러스터 효율은 떨어질 수 있습니다. 호스트의 전용 CPU 구성과 운영 정책이 준비된 경우에만 쓰는 게 좋습니다.

    Q3. 디스크 크기를 크게 잡으면 성능도 좋아지나요?

    대부분의 경우 단순 디스크 용량과 성능은 별개로 봐야 합니다. 성능은 스토리지 백엔드, 볼륨 타입, 캐시 정책, I/O 패턴 영향을 더 많이 받습니다.

    마무리: 워크로드별 추천 기준

    OpenStack에서 VM 구성을 잘 잡고 싶다면 플레이버를 “사이즈”가 아니라 “운영 정책”으로 봐야 합니다. 이 관점이 잡히면 OpenStack 플레이버 설계가 훨씬 쉬워집니다.

    • 웹/API 서버라면 작은 VM 여러 대와 수평 확장을 우선 추천합니다.
    • DB 서버라면 메모리, 디스크 I/O, 볼륨 타입을 먼저 보고 필요할 때 전용 CPU를 검토하세요.
    • 배치 작업이라면 실행 시간대와 동시성 기준으로 CPU형 플레이버를 따로 두는 게 좋습니다.
    • 네트워크 장비형 VM이라면 CPU 정책, 인터럽트, 드라이버, 패킷 처리량을 함께 봐야 합니다.

    제 기준에서는 기본 플레이버를 작고 명확하게 시작한 뒤, 관측 지표를 보고 계열을 늘리는 방식이 가장 덜 아팠습니다. 한 번에 완벽한 표준안을 만들려고 하면 오히려 오래 걸리더라고요. 다음 글에서는 Host Aggregate와 Availability Zone을 이용해서 특정 플레이버를 특정 Compute Node 그룹에 배치하는 방법을 다룰 예정입니다. 이전 글에서 다룬 OpenStack 네트워크 기본 구조도 함께 참고하면 흐름이 더 잘 이어질 겁니다.

    워크로드별 OpenStack 플레이버 설계 선택 기준 요약

    웹, DB, 배치, 네트워크형 VM을 어떤 기준으로 나눌지 한눈에 정리한 요약 이미지입니다.

  • [Cloud] Cosmos DB 마이그레이션 가이드: 기존 DB에서 NoSQL로 안전하게 전환하기

    [Cloud] Cosmos DB 마이그레이션 가이드: 기존 DB에서 NoSQL로 안전하게 전환하기

    안녕하세요, 13년차 인프라 엔지니어입니다. 오늘은 저처럼 관계형 데이터베이스(Relational Database) 환경에 익숙한 분들이 NoSQL로 전환할 때 겪는 Cosmos DB 마이그레이션 경험담을 풀어볼까 합니다. 특히 기존 DB에서 NoSQL로 넘어갈 때 마주치는 문제들과 제가 삽질 끝에 찾아낸 해결 방안들을 여과 없이 공유하려고 해요.

    최근 서비스가 빠르게 성장하면서 기존 RDB로는 감당하기 어려운 트래픽과 데이터 규모에 직면하는 경우가 많아졌습니다. 저도 직접 운영하는 서비스에서 비슷한 고민을 했거든요. 이럴 때 확장성과 유연성이 뛰어난 NoSQL 데이터베이스, 특히 Azure Cosmos DB 같은 글로벌 분산 데이터베이스가 매력적인 대안으로 떠오릅니다. 하지만 익숙한 환경을 떠나 새로운 패러다임으로 넘어가는 건 결코 쉽지 않죠. 😅

    기존 관계형 데이터베이스에서 Azure Cosmos DB로 마이그레이션하는 전체 아키텍처 개요도

    Cosmos DB와 NoSQL 마이그레이션, 왜 필요한가?

    Cosmos DB는 Microsoft Azure에서 제공하는 완전 관리형(fully managed) NoSQL 데이터베이스 서비스입니다. 전 세계 어디서든 낮은 지연 시간(low-latency)으로 데이터를 읽고 쓸 수 있으며, 필요에 따라 자동으로 확장(auto-scale)되는 것이 가장 큰 장점이죠. 특히 여러 NoSQL API를 지원해서(SQL API, MongoDB API, Cassandra API, Gremlin API 등) 기존에 사용하던 NoSQL 시스템과 호환성을 유지하면서 Cosmos DB 마이그레이션을 진행할 수 있다는 점이 매력적입니다.

    그렇다면 왜 굳이 NoSQL 마이그레이션을 해야 할까요? 제가 직접 경험해보니 몇 가지 확실한 이유가 있더라고요.

    • 확장성 (Scalability): 폭발적으로 증가하는 데이터와 트래픽을 RDB의 수직 확장(vertical scaling)만으로는 감당하기 어렵습니다. NoSQL은 수평 확장(horizontal scaling)에 유리하죠.
    • 유연한 스키마 (Flexible Schema): RDB는 엄격한 스키마를 요구해서 데이터 모델 변경 시 복잡한 마이그레이션 작업이 필요합니다. NoSQL은 스키마가 유연해서 빠르게 변화하는 비즈니스 요구사항에 대응하기 좋습니다.
    • 글로벌 분산 (Global Distribution): 전 세계 사용자에게 서비스를 제공해야 할 때, Cosmos DB는 몇 번의 클릭만으로 전 세계 리전에 데이터를 복제하고 동기화할 수 있습니다.

    이런 장점들 때문에 데이터베이스 전환을 고민하게 되지만, 막상 시작하면 만만치 않은 벽에 부딪힙니다. 가장 큰 벽은 바로 데이터 모델링과 마이그레이션 전략 수립이더군요.

    마이그레이션 준비: 데이터 모델링이 반이다

    관계형 데이터베이스는 정규화(Normalization)를 통해 데이터 중복을 최소화하고 데이터 무결성을 유지합니다. 하지만 NoSQL은 읽기 성능(read performance)을 최적화하기 위해 비정규화(Denormalization)를 적극적으로 활용하는 경우가 많습니다. 이게 처음엔 정말 낯설더라고요.

    Cosmos DB에서는 파티션 키 (Partition Key) 선정이 정말 중요합니다. 파티션 키는 데이터를 논리적 파티션으로 나누는 기준이 되는데, 이 키를 어떻게 정하느냐에 따라 성능과 비용이 천차만별로 달라지거든요. 잘못 정하면 ‘핫 파티션(Hot Partition)’이 생겨서 특정 파티션에만 부하가 몰려 성능 저하를 겪게 됩니다. 제가 이 부분에서 정말 크게 삽질했어요. ㅎㅎ

    💡 팁: 파티션 키는 카디널리티(Cardinality, 고유한 값의 수)가 높고, 읽기/쓰기 작업이 고르게 분산될 수 있는 필드를 선택하는 것이 좋습니다. 예를 들어, 사용자 ID나 주문 ID 같은 필드가 좋은 후보가 될 수 있죠.

    실전 마이그레이션: 데이터 이동 전략

    이제 실제로 데이터를 옮겨볼 차례입니다. Cosmos DB로 데이터를 마이그레이션하는 방법은 여러 가지가 있습니다.

    1. Azure Cosmos DB Data Migration Tool: GUI 기반의 도구로, CSV, JSON 파일, MongoDB, SQL Server 등 다양한 소스에서 데이터를 가져올 수 있습니다. 간단한 초기 마이그레이션에 유용합니다.
    2. Azure Data Factory (ADF): 대규모 데이터 마이그레이션, ETL(Extract, Transform, Load) 파이프라인 구축에 적합합니다. 다양한 데이터 소스와 싱크를 지원하며, 데이터 변환 로직을 추가할 수 있습니다.
    3. 커스텀 스크립트: Python, .NET 등 SDK를 이용해 직접 스크립트를 작성하여 데이터를 마이그레이션하는 방식입니다. 복잡한 변환 로직이나 실시간 동기화가 필요할 때 유용하죠.

    저는 초기 마이그레이션에는 Data Migration Tool을 사용했지만, 복잡한 데이터 변환이 필요해서 결국 Python 스크립트를 직접 작성했습니다. 기존 관계형 DB에서 데이터를 읽어와 NoSQL 모델에 맞게 변환한 뒤 Cosmos DB에 삽입하는 방식이었거든요.

    먼저 Azure CLI를 이용해 Cosmos DB 계정과 데이터베이스, 컨테이너를 생성하는 기본적인 명령어부터 살펴보겠습니다.

    # Azure 리소스 그룹 생성 (이미 있다면 생략)
    az group create --name myCosmosResourceGroup --location koreacentral
    
    # Cosmos DB 계정 생성 (API 종류는 필요에 따라 선택, 여기서는 SQL API)
    az cosmosdb create \
      --name mycosmosdbaccount13year \
      --resource-group myCosmosResourceGroup \
      --default-consistency-level Session \
      --locations regionName=koreacentral failoverPriority=0 \
      --kind GlobalDocumentDB
    
    # 데이터베이스 생성
    az cosmosdb sql database create \
      --account-name mycosmosdbaccount13year \
      --name myNoSQLDatabase \
      --resource-group myCosmosResourceGroup
    
    # 컨테이너 생성 (파티션 키는 /userId 로 예시)
    az cosmosdb sql container create \
      --account-name mycosmosdbaccount13year \
      --database-name myNoSQLDatabase \
      --name myItemsContainer \
      --partition-key-path /userId \
      --throughput 400 \
      --resource-group myCosmosResourceGroup
    

    그리고 Python으로 데이터를 읽어와 삽입하는 예시 코드입니다. 실제 환경에서는 데이터 변환 로직이 훨씬 복잡하겠죠.

    import os
    import json
    from azure.cosmos import CosmosClient
    
    # Cosmos DB 설정
    COSMOS_DB_ENDPOINT = os.environ.get("COSMOS_DB_ENDPOINT")
    COSMOS_DB_KEY = os.environ.get("COSMOS_DB_KEY")
    DATABASE_NAME = "myNoSQLDatabase"
    CONTAINER_NAME = "myItemsContainer"
    
    # Cosmos DB 클라이언트 초기화
    client = CosmosClient(COSMOS_DB_ENDPOINT, COSMOS_DB_KEY)
    database = client.get_database_client(DATABASE_NAME)
    container = database.get_container_client(CONTAINER_NAME)
    
    def migrate_data(data_list):
        print(f"Migrating {len(data_list)} items...")
        for item in data_list:
            try:
                # 기존 RDB 데이터를 NoSQL 모델에 맞게 변환 (예시)
                # item_transformed = {"id": str(item["old_id"]), "userId": item["user_id"], "productName": item["name"], ...}
                # 실제 데이터 변환 로직은 여기에 구현
                item_transformed = item # 단순 복사 (예시)
                item_transformed["id"] = str(item_transformed["id"]) # Cosmos DB item id는 문자열이어야 함
    
                container.upsert_item(body=item_transformed)
                # print(f"Inserted/Updated item: {item_transformed['id']}")
            except Exception as e:
                print(f"Error processing item {item.get('id', 'N/A')}: {e}")
    
    if __name__ == "__main__":
        # 예시 데이터 (실제로는 RDB에서 조회)
        sample_data = [
            {"id": 1, "userId": "userA", "productName": "Laptop", "price": 1200},
            {"id": 2, "userId": "userB", "productName": "Mouse", "price": 50},
            {"id": 3, "userId": "userA", "productName": "Keyboard", "price": 100},
            {"id": 4, "userId": "userC", "productName": "Monitor", "price": 300},
        ]
        migrate_data(sample_data)
        print("Migration complete!")
    
    Python 스크립트를 활용한 데이터 마이그레이션 과정 개념도

    Python 스크립트를 활용한 데이터 마이그레이션 과정 개념도

    삽질 경험: 파티션 키 잘못 설계했다가 고생했던 이야기 ⚠️

    제가 가장 크게 삽질했던 부분이 바로 파티션 키 (Partition Key) 설계였습니다. 처음에는 무심코 productId를 파티션 키로 잡았거든요. 저희 서비스는 특정 인기 상품에 대한 조회수가 압도적으로 높았는데, 아니나 다를까 얼마 지나지 않아 productId가 높은 특정 파티션에만 요청이 몰려서 Latency(지연 시간)가 급증하고 Request Unit(RU) 소모가 비정상적으로 치솟는 문제가 발생했습니다. 이게 바로 핫 파티션 (Hot Partition) 문제였죠. 😱

    Cosmos DB는 RU라는 개념으로 처리량을 측정하고 비용을 부과하는데, 핫 파티션이 발생하면 특정 파티션의 RU가 한계치에 도달하여 요청이 제한(throttling)될 수 있습니다. 전체 컨테이너의 RU가 여유 있어도 특정 파티션만 과부하가 걸리는 상황이 발생하더라고요. 이때 정말 당황했었습니다.

    해결책은 파티션 키를 userId와 같이 고르게 분산될 수 있는 값으로 변경하는 것이었습니다. 기존 데이터를 다시 마이그레이션해야 하는 대규모 작업이었지만, 장기적인 안정성을 위해서는 필수적인 선택이었죠. 이 경험을 통해 파티션 키 설계가 얼마나 중요한지 뼈저리게 느꼈습니다.

    RU (Request Unit)는 Cosmos DB에서 모든 데이터베이스 작업(읽기, 쓰기, 쿼리 등)에 대한 성능 단위입니다. 1 RU는 1KB 크기의 항목을 읽는 비용과 같습니다. 복잡한 쿼리나 큰 항목을 쓰면 더 많은 RU가 소모되죠. RU 사용량을 모니터링하면서 핫 파티션이 있는지 주기적으로 확인하는 것이 중요합니다.

    마이그레이션 후 검증 및 최적화

    데이터 마이그레이션이 끝났다고 끝이 아닙니다. 오히려 이때부터가 진짜 시작이죠! 마이그레이션 후에는 반드시 다음 사항들을 꼼꼼하게 검증하고 최적화해야 합니다.

    1. 데이터 일관성 (Data Consistency): 원본 데이터와 Cosmos DB의 데이터가 일치하는지 확인해야 합니다. 간단한 스크립트를 통해 레코드 수, 일부 필드의 합계 등을 비교하는 방법이 효과적입니다.
    2. 성능 벤치마킹 (Performance Benchmarking): 예상했던 성능이 나오는지 부하 테스트를 진행해야 합니다. 특히 핵심 비즈니스 로직에 대한 읽기/쓰기 Latency와 RU 소모량을 면밀히 관찰해야 합니다.
    3. 비용 모니터링 (Cost Monitoring): Cosmos DB는 사용량 기반 과금(pay-as-you-go)이므로, 예상치 못한 RU 소모로 비용 폭탄을 맞지 않도록 모니터링 대시보드를 설정하는 것이 중요합니다. Azure Portal에서 RU 사용량을 실시간으로 확인할 수 있습니다.

    Azure Portal에서 Cosmos DB 계정의 ‘메트릭 (Metrics)’ 섹션을 보면 RU 사용량, 지연 시간, 요청 수 등을 그래프로 볼 수 있습니다. 여기서 특정 파티션 키에 대한 요청이 급증하는 패턴이 보인다면 핫 파티션을 의심해봐야 합니다.

    Azure Portal의 Cosmos DB RU 사용량 및 성능 메트릭 대시보드 예시

    Azure Portal의 Cosmos DB RU 사용량 및 성능 메트릭 대시보드 예시

    Cosmos DB 마이그레이션 전략 선택 가이드

    다양한 상황에 따라 마이그레이션 전략도 달라질 수 있습니다. 제가 경험했던 것들을 바탕으로 몇 가지 시나리오별 추천 전략을 정리해봤습니다.

    시나리오 고려 사항 추천 마이그레이션 전략 주의사항
    소규모 초기 마이그레이션
    (데이터 양 < 100GB)
    빠른 전환, 데이터 모델 단순 Azure Cosmos DB Data Migration Tool 대규모 데이터에는 부적합, 복잡한 변환 어려움
    대규모 데이터 마이그레이션
    (데이터 양 > 100GB, 복잡한 ETL 필요)
    데이터 변환, 증분 동기화 Azure Data Factory (ADF) 활용 ADF 파이프라인 설계 및 운영 복잡성 증가
    실시간/Near Real-time 동기화
    (서비스 중단 최소화)
    무중단 전환, 이중 쓰기(Dual-write) 전략 커스텀 스크립트 + Change Feed 활용 이중 쓰기 로직 구현 및 동기화 일관성 관리 복잡
    기존 NoSQL (예: MongoDB) 에서 전환 API 호환성, 스키마 유사성 MongoDB API for Cosmos DB + Mongo Shell 또는 Data Migration Tool 일부 기능/성능 차이 고려
    Cosmos DB 마이그레이션 전략 선택 가이드 인포그래픽

    Cosmos DB 마이그레이션 전략 선택 가이드 인포그래픽

    마무리: 새로운 패러다임으로의 전환

    Cosmos DB 마이그레이션은 단순한 데이터 이동을 넘어, 데이터베이스 패러다임을 전환하는 일입니다. 제가 직접 겪어보니, 기존 관계형 DB의 사고방식에서 벗어나 NoSQL의 특성과 장점을 이해하는 것이 가장 중요하더라고요. 특히 파티션 키 설계나 RU 최적화 같은 부분은 충분한 학습과 테스트 없이는 큰 시행착오를 겪을 수밖에 없습니다.

    하지만 제대로만 설계하고 구현한다면, Cosmos DB는 여러분의 서비스에 강력한 확장성과 유연성을 제공할 것입니다. 이 글이 기존 DB에서 NoSQL로의 전환을 고민하는 분들께 작은 도움이 되었으면 좋겠네요. 다음에 기회가 된다면 Cosmos DB의 Change Feed를 활용한 실시간 데이터 처리 아키텍처에 대해서도 한번 다뤄보겠습니다. 궁금한 점이 있다면 언제든 댓글로 남겨주세요! 😊

  • [OpenStack] OpenStack Ceph 마이그레이션 결정 기준과 절차

    [OpenStack] OpenStack Ceph 마이그레이션 결정 기준과 절차

    OpenStack Ceph 마이그레이션 결정 기준과 절차

    OpenStack Ceph 마이그레이션을 검토할 때 저는 늘 같은 질문부터 던집니다. "지금 바꾸면 뭐가 좋아지고, 대신 무엇을 새로 운영해야 하나"라는 질문이죠. 핵심은 단순한 저장소 교체가 아니라 운영 모델의 변경입니다. Cinder 볼륨만 옮길지, Glance 이미지 저장소까지 묶을지, Nova의 에페메럴 디스크까지 Ceph RBD로 정리할지에 따라 난이도와 장애 반경이 완전히 달라지거든요. 실제 운영에서는 성능 수치보다 스케줄링 일관성, Ceph 재배치 부담, 인증 권한, 롤백 가능성이 더 자주 발목을 잡습니다.

    이 글은 OpenStack과 Ceph의 일반적인 운영 패턴을 기준으로 정리했습니다. 특정 벤더 확장 기능이나 과장된 수치 없이, 현장에서 일정 잡을 때 실제로 보는 판단 기준과 절차만 남겼습니다. 한 줄로 줄이면 이렇습니다. 기존 백엔드를 덮어쓰지 말고, 새 백엔드를 병행 등록한 뒤 신규 유입과 기존 자산 이동을 분리해서 진행하는 편이 안전합니다. 관련해서 볼륨 타입 설계나 Ceph 풀 설계 글도 함께 보시면 운영 그림이 훨씬 빨리 잡힙니다.

    OpenStack Ceph 마이그레이션 전체 아키텍처 다이어그램

    OpenStack 서비스 계층과 Ceph 풀(pool) 구조, 데이터 이동 흐름을 한눈에 보여주는 개요 이미지입니다.

    1. OpenStack Ceph 마이그레이션을 고민하는 대표적인 이유

    운영 현장에서는 보통 "Ceph가 좋아 보여서"가 아니라 아래 같은 이유로 움직입니다.

    • 풀 구조가 성장 패턴을 못 따라갈 때: 같은 풀에 서로 다른 I/O 성격의 볼륨이 섞이면 운영 판단이 흐려집니다.
    • 볼륨 타입 정책을 분리해야 할 때: 테넌트별, 워크로드별, 복제 정책별로 다른 백엔드가 필요해집니다.
    • 증설과 장애 대응의 표준화가 필요할 때: 하드웨어별 절차보다 Ceph 기준으로 운영 일관성을 맞추고 싶을 때가 많습니다.
    • 기존 백엔드가 OpenStack 스케줄러와 잘 안 맞을 때: 사용자는 타입을 골랐는데 실제 배치가 정책대로 안 되는 경우가 반복됩니다.

    제가 실무에서 특히 중요하게 보는 건 "왜 옮기느냐"보다 어디까지 옮기느냐입니다. Cinder만 옮기면 블록 스토리지 정책 문제로 한정되지만, Glance까지 함께 들어가면 이미지 캐시와 부팅 경로가 바뀝니다. Nova 에페메럴까지 손대는 순간부터는 스토리지 변경이라기보다 컴퓨트 설계 변경에 가깝습니다.

    2. OpenStack Ceph 마이그레이션 결정 기준은 성능보다 운영 책임입니다

    Ceph 도입 여부를 성능 하나로만 판단하면 나중에 꼬이기 쉽습니다. 더 정확한 질문은 이겁니다. "이 팀이 Ceph의 재배치, 권한, 풀 정책, 장애 메시지를 자기 책임으로 읽고 운영할 준비가 됐는가"예요. 이 기준이 생각보다 훨씬 현실적입니다.

    판단 항목 기존 유지가 더 나은 경우 Ceph 전환이 더 나은 경우 실무 체크 포인트
    운영 복잡도 현재 절차가 단순하고 장애 대응 경로가 고정돼 있음 확장, 복제, 장애 도메인 관리를 한 체계로 묶고 싶음 클라우드 팀이 Ceph 경고 메시지와 재배치 상태를 직접 해석할 수 있는지
    볼륨 타입 정책 사실상 단일 티어로도 충분함 테넌트/업무군별로 다른 풀과 정책이 필요함 volume_backend_name, extra_specs, 타입 명명 규칙을 먼저 설계했는지
    장애 허용 구조 외부 스토리지가 이미 안정적으로 이중화됨 CRUSH 규칙과 failure domain을 직접 설계할 가치가 있음 복제 단위가 host인지 rack인지, 기존 가용영역 설계와 충돌하지 않는지
    마이그레이션 방식 짧은 점검창도 확보하기 어려움 신규/기존을 분리하고 몇 주에 걸쳐 점진 전환 가능 붙어 있는(in-use) 볼륨의 정책 변경 허용 여부와 드라이버 지원 범위
    운영 비용 전담 인력이 없고 스토리지 운영을 단순화해야 함 학습 비용을 들여도 장기적으로 통합 운영 이득이 큼 모니터링, 알림, 장애 복구 문서가 Ceph 기준으로 이미 준비됐는지

    짧게 정리하면 이렇습니다. 볼륨 타입 분리와 점진 전환이 필요하면 Ceph 쪽이 유리하고, 운영 역량이 아직 약하면 기존 백엔드를 남겨둔 병행 운영이 맞습니다. 반대로 다운타임 제로만 바라보면서 한 번에 덮어쓰는 방식은 실무에서 꽤 위험하더라고요.

    3. OpenStack 스토리지 교체 범위를 자르면 난이도가 확 줄어듭니다

    OpenStack 스토리지 교체는 기술 문제라기보다 범위 관리 문제인 경우가 많습니다. 보통 아래 셋으로 끊습니다.

    1. Cinder 볼륨만 전환: 가장 현실적이고, 실패해도 영향 반경을 제한하기 쉽습니다.
    2. Glance 이미지까지 전환: 이미지 업로드, 캐시, 부팅 경로를 함께 봐야 합니다.
    3. Nova 에페메럴 디스크까지 전환: 성능과 일관성 이득은 크지만 컴퓨트 노드 영향이 급격히 커집니다.

    제가 추천하는 순서는 거의 같습니다. Cinder -> Glance -> Nova예요. 이유는 단순합니다. Cinder는 볼륨 타입이라는 절연층이 있지만, Nova 루트/에페메럴까지 한 번에 건드리면 인스턴스 부팅 실패가 곧바로 서비스 장애로 번지기 쉽습니다.

    실무에서 자주 보는 장면도 있습니다. 기존에는 외부 SAN을 쓰다가 "이번에 Ceph도 붙였으니 볼륨도 이미지도 같이 넘기자"고 범위를 넓히는 경우죠. 이때 Cinder 신규 볼륨은 정상 생성되는데, 부팅 지연이나 이미지 캐시 미스 때문에 운영팀이 Glance 문제를 Ceph 문제로 오해하는 일이 꽤 많습니다. 그래서 저는 첫 전환 주기에는 신규 볼륨 경로와 이미지 경로를 일부러 분리합니다. 원인 분리가 정말 편해집니다.

    4. OpenStack Ceph 마이그레이션 사전 점검

    이 단계는 체크리스트라기보다 진입 금지선에 가깝습니다. 상태가 좋지 않으면 일정부터 미루는 편이 맞습니다. 마이그레이션 자체보다, 진행 중 나타난 문제의 원인이 Ceph인지 OpenStack인지 분리하기 어려워지기 때문입니다.

    ceph -s
    ceph health detail
    ceph osd stat
    ceph osd tree
    ceph osd df tree
    ceph osd pool ls detail
    rados df
    openstack volume service list
    openstack volume type list --long
    openstack volume list --status in-use --long

    여기서는 보통 이렇게 읽습니다.

    • ceph -s: 클러스터 전체 상태와 recovery/backfill 진행 여부를 먼저 봅니다. 단순 HEALTH_WARN보다 경고 원인이 재배치인지 용량 임계치인지가 더 중요합니다.
    • ceph health detail: degraded, undersized, inactive, nearfull 계열 메시지가 보이면 이동 작업을 미룹니다.
    • ceph osd df tree: 특정 OSD만 유독 가득 찼다면 마이그레이션 중 backfill이 겹치면서 체감 성능이 흔들릴 가능성이 큽니다.
    • ceph osd pool ls detail: 새 풀의 복제 정책, CRUSH rule, autoscale 상태를 봅니다. 풀 이름만 맞고 규칙이 다르면 기대한 장애 도메인이 깨질 수 있습니다.
    • openstack volume service list: 대상 cinder-volume 서비스가 up이고 enabled인지 확인합니다.
    • openstack volume type list --long: 현재 타입과 백엔드 이름 연결이 살아 있는지 확인합니다.
    • openstack volume list --status in-use --long: 붙어 있는 볼륨 비중이 높다면 온라인 정책 변경이 가능한지부터 따져야 합니다.

    실제 실패 모드도 비슷합니다. 새 타입은 잘 만들었는데 Ceph 쪽에서 이미 backfill이 길게 돌고 있던 상황이죠. OpenStack API는 성공으로 보이는데 사용자는 I/O 지연을 바로 체감합니다. OpenStack의 성공 응답이 Ceph 내부 재배치 비용까지 끝났다는 뜻은 아니라는 점, 이 부분은 꼭 분리해서 봐야 합니다.

    OpenStack Ceph 마이그레이션 사전 점검과 볼륨 타입 매핑 이미지

    Ceph 클러스터 헬스 체크, 풀 상태, Cinder 볼륨 타입과 백엔드 이름 매핑 관계를 설명하는 이미지입니다.

    5. 백엔드 설계는 pool보다 volume type을 먼저 잡아야 합니다

    운영에서 더 오래 가는 설계는 "풀 이름 중심"이 아니라 볼륨 타입 중심입니다. 사용자는 타입을 고르고, Cinder 스케줄러는 타입의 extra_specs를 보고 백엔드를 고르거든요. 그래서 이름 규칙이 중요합니다.

    • 백엔드 섹션 이름: 운영자가 구분하기 쉬운 내부 이름
    • volume_backend_name: 타입과 연결되는 스케줄링 식별자
    • Ceph 풀 이름: 실제 데이터가 놓이는 물리적 대상

    많이 헷갈리는 지점이 하나 있습니다. [rbd-new] 같은 설정 섹션 이름과 volume_backend_name=rbd-new는 같은 개념이 아닙니다. 우연히 같게 맞출 수는 있지만, 스케줄러가 직접 참조하는 값은 섹션명이 아니라 백엔드 이름입니다. 여기 오타가 나면 타입은 멀쩡해 보여도 No valid host 오류가 나기 쉽습니다.

    5-1. OpenStack Ceph 마이그레이션용 cinder.conf 예시

    [DEFAULT]
    enabled_backends = rbd-old,rbd-new
    
    [backend_defaults]
    rbd_ceph_conf = /etc/ceph/ceph.conf
    rbd_user = cinder
    rbd_secret_uuid = 11111111-2222-3333-4444-555555555555
    volume_driver = cinder.volume.drivers.rbd.RBDDriver
    
    [rbd-old]
    volume_backend_name = rbd-old
    rbd_pool = volumes_old
    
    [rbd-new]
    volume_backend_name = rbd-new
    rbd_pool = volumes_new

    현재 Cinder 샘플 설정에서도 공통 백엔드 옵션은 [backend_defaults] 섹션을 사용합니다. 여기서 중요한 건 세 가지예요. enabled_backends에 백엔드 섹션이 모두 들어가 있는지, 각 섹션의 volume_backend_name이 타입의 extra_specs와 정확히 일치하는지, 공통 설정을 쓴다면 [backend_defaults]와 개별 섹션의 덮어쓰기 관계를 이해하고 있는지입니다.

    기존 단일 백엔드 서비스에 다중 백엔드를 붙이는 경우, Cinder 서비스 호스트명이 host에서 host@backend 형태로 바뀌는 환경도 있습니다. 이런 경우 기존 볼륨의 host 메타데이터를 갱신하지 않으면 운영이 꼬일 수 있어서, 사전에 cinder-manage volume update_host 대상 여부를 꼭 확인하는 편이 좋습니다.

    5-2. Ceph 풀과 인증 준비

    새 풀만 만들고 끝내면 안 됩니다. RBD로 쓸 풀은 초기화와 권한까지 맞아야 합니다. 저는 신규 백엔드를 붙이기 전에 아래 항목을 먼저 확인합니다.

    ceph osd pool ls detail
    rbd pool init volumes_new
    ceph auth get-or-create client.cinder \
      mon 'profile rbd' \
      osd 'profile rbd pool=volumes_new, profile rbd pool=vms, profile rbd-read-only pool=images' \
      mgr 'profile rbd pool=volumes_new, profile rbd pool=vms'
    ceph auth get-or-create client.glance \
      mon 'profile rbd' \
      osd 'profile rbd pool=images' \
      mgr 'profile rbd pool=images'

    근본 원인은 늘 비슷합니다. 풀은 만들었는데 rbd pool init이 빠졌거나, client.cinder가 새 풀에 대한 cap을 갖고 있지 않거나, Glance와 Cinder 권한을 뭉뚱그려 잡아두는 식이죠. 이런 상태에선 볼륨 생성은 되는데 attach에서 실패하거나, 이미지 기반 볼륨 생성만 유독 실패하는 식으로 증상이 갈라집니다.

    5-3. 새 볼륨 타입 생성

    openstack volume type create ceph-rbd-new
    openstack volume type set --property volume_backend_name=rbd-new ceph-rbd-new
    openstack volume type show ceph-rbd-new
    openstack volume type list --long

    운영상 의미는 명확합니다. 신규 생성 경로를 먼저 새 백엔드로 돌리고, 기존 자산 이동은 그다음에 따로 한다는 거예요. 이 분리가 되면 실패했을 때 되돌리기도 쉬워집니다.

    6. OpenStack Ceph 마이그레이션 절차: 신규 유입과 기존 자산을 분리하세요

    실무 절차는 의외로 단순합니다. 대신 순서를 어기면 비용이 바로 커집니다.

    1. 새 백엔드를 등록하고 타입을 만든다: 아직 기존 타입은 그대로 둡니다.
    2. 신규 볼륨만 새 타입으로 생성하게 유도한다: 새 데이터 유입을 먼저 분리합니다.
    3. 테스트 프로젝트에서 생성, attach, detach, 삭제까지 한 사이클을 검증한다: 생성만 확인하면 부족합니다.
    4. 저위험 볼륨부터 정책 변경 또는 마이그레이션을 시작한다: 백업성 볼륨, 비핵심 업무부터 갑니다.
    5. Ceph 재배치 상태를 보며 배치를 늘린다: API 성공률이 아니라 클러스터 안정성을 기준으로 속도를 조절합니다.
    6. 기존 타입의 신규 생성을 막는다: 잔여 볼륨만 남기고 정리 단계로 들어갑니다.

    CLI는 환경에 따라 조금씩 다르게 씁니다. 현재 openstackclient에서는 볼륨 타입 변경을 openstack volume set --type로 수행할 수 있고, 일부 운영 환경은 여전히 cinder retype를 사용합니다. 핵심은 명령 이름보다 정책 변경이 실제 데이터 이동을 유발하는지를 테스트 볼륨으로 먼저 확인하는 겁니다.

    # 현재 openstackclient 계열에서 많이 쓰는 방식
    openstack volume set \
      --type ceph-rbd-new \
      --migration-policy on-demand \
      8f1f2f0d-1111-2222-3333-444444444444
    openstack volume show 8f1f2f0d-1111-2222-3333-444444444444
    
    # 환경에 따라 여전히 사용하는 cinder client 방식
    cinder retype \
      --migration-policy on-demand \
      8f1f2f0d-1111-2222-3333-444444444444 \
      ceph-rbd-new

    관리자가 목적지를 더 강하게 통제해야 한다면 백엔드 호스트를 직접 지정하는 방식도 씁니다.

    openstack volume migrate \
      --host host01@rbd-new#volumes_new \
      8f1f2f0d-1111-2222-3333-444444444444
    openstack volume show 8f1f2f0d-1111-2222-3333-444444444444 -f yaml

    저는 보통 이렇게 고릅니다. 볼륨 타입 정책까지 함께 정리하려면 retype 계열이 낫고, 특정 백엔드 호스트나 풀을 명시적으로 통제해야 하면 migrate가 더 직접적입니다. 다만 in-use 볼륨의 재타입은 마이그레이션이 동반되면 보통 관리자 권한이 필요하고, 암호화된 in-use 볼륨은 재타입 마이그레이션이 지원되지 않는 경우가 있습니다. 이런 케이스는 무리해서 실시간 전환하지 말고 점검창을 잡는 편이 더 안전합니다.

    OpenStack Ceph 마이그레이션 절차와 데이터 이동 흐름 이미지

    신규 생성 분리, 테스트 볼륨 이동, 운영 볼륨 확장 전환 순서를 보여주는 절차형 이미지입니다.

    7. OpenStack Ceph 마이그레이션에서 자주 부딪히는 실패 모드

    증상은 비슷해 보여도 원인은 다릅니다. 저는 아래 네 가지부터 먼저 자릅니다.

    7-1. 타입은 맞는데 스케줄링이 안 됩니다

    대부분은 volume_backend_name 불일치, 백엔드 서비스 비활성화, 드라이버 초기화 실패 셋 중 하나입니다.

    grep -E 'enabled_backends|volume_backend_name|rbd_pool|rbd_user' /etc/cinder/cinder.conf
    openstack volume service list
    journalctl -u openstack-cinder-scheduler -n 200 --no-pager
    journalctl -u openstack-cinder-volume -n 200 --no-pager

    배포판에 따라 systemd 유닛명은 다를 수 있으니 서비스 이름은 현 환경 기준으로 바꿔서 보시면 됩니다. 근본 원인은 흔히 두 가지예요. 첫째, 타입은 rbd-new를 보는데 실제 백엔드는 rbd_new처럼 다른 이름으로 등록된 경우. 둘째, 드라이버가 Ceph 인증 실패로 초기화되지 않았는데 운영자가 프로세스만 살아 있다고 착각하는 경우입니다.

    7-2. Ceph 인증 문제로 생성 또는 attach가 막힙니다

    이 경우는 생성 단계와 attach 단계의 증상이 달라서 더 헷갈립니다. 새 풀에 대한 cap이 없거나, Nova와 Cinder, Glance가 참조하는 사용자명과 시크릿, 키링 경로가 어긋나 있는 경우가 많습니다. 특히 Cinder가 볼륨을 만들 수 있는 권한과 Nova/libvirt가 그 볼륨을 실제로 매핑할 수 있는 권한은 완전히 같은 문제가 아닐 수 있습니다.

    7-3. 이동은 됐는데 체감 성능이 흔들립니다

    이건 마이그레이션 명령의 성공 여부보다 Ceph 내부 상태를 먼저 봐야 합니다. recovery, backfill, nearfull 경고가 길게 이어지면 배치를 줄이거나 윈도우를 다시 잡는 편이 맞습니다. 현장에서 자주 보는 패턴은 이렇습니다. 낮 시간대에 작은 볼륨 몇 개는 멀쩡해서 속도를 올렸는데, 저녁 배치에서 대용량 볼륨과 Ceph 재배치가 겹치며 지연이 튀는 거죠. 이거 진짜 자주 나옵니다.

    7-4. 소스 풀에 고아 이미지가 남습니다

    실패한 이동, 중단된 작업, 관리자의 수동 정리 이력 때문에 원본 풀에 RBD 이미지가 남을 수 있습니다. 그렇다고 바로 지우면 안 됩니다. 먼저 OpenStack DB가 가리키는 위치와 실제 풀 위치가 일치하는지 확인해야 합니다. 저는 최소 하루 이상 운영 검증을 둔 뒤 정리합니다.

    8. 검증은 보이는지보다 원하는 위치에서 실제로 쓰는지 봐야 합니다

    마이그레이션 후 검증은 세 겹으로 합니다. OpenStack 상태, Ceph 실체, 워크로드 체감입니다.

    1. 볼륨 메타데이터 확인: 타입, 상태, migration 관련 필드 확인
    2. 대상 풀의 실제 RBD 이미지 확인: 새 풀에 이미지가 생겼는지 확인
    3. attach 후 읽기/쓰기 확인: 실제 인스턴스에서 I/O 검증
    4. 소스 풀 잔존물 확인: 즉시 삭제하지 말고 정리 후보만 분리
    openstack volume show 8f1f2f0d-1111-2222-3333-444444444444
    rbd ls volumes_new
    rbd info volumes_new/volume-8f1f2f0d-1111-2222-3333-444444444444
    rbd ls volumes_old

    저는 운영 확인을 여기서 끝내지 않습니다. 인스턴스에 붙여 파일 생성, 재마운트, 재부팅 후 재연결까지 확인합니다. 볼륨이 새 풀에 존재하는 것과, 서비스가 그 볼륨을 문제없이 소비하는 것은 다른 검증이거든요. 여기까지 봐야 마음이 놓입니다.

    OpenStack Ceph 마이그레이션 완료 검증 대시보드 이미지

    볼륨 상태 확인, 대상 풀의 RBD 이미지 존재 여부, 운영 검증 체크리스트를 보여주는 결과 이미지입니다.

    9. 언제 Ceph 스토리지 전환을 고르고, 언제 병행 운영을 고를까

    현장 추천은 꽤 분명합니다.

    • 다운타임이 거의 없고 운영 안정성이 최우선이다: 기존 백엔드를 유지한 병행 운영 후, 새 타입으로 신규를 먼저 분리하고 기존은 점진적으로 옮기세요.
    • 목표가 스토리지 통합 운영과 볼륨 정책 세분화다: Ceph 백엔드를 붙이되, 처음 범위는 Cinder로 제한하세요.
    • 팀이 Ceph 장애 메시지와 재배치를 아직 익숙하게 다루지 못한다: 바로 전면 전환하지 말고 신규 볼륨만 새 백엔드로 보내며 운영 감각부터 쌓는 편이 낫습니다.
    • 이미 Ceph를 쓰고 있고 풀 구조만 재정비하고 싶다: 풀 직접 교체보다 새 백엔드와 새 타입을 따로 만들고 정책 전환으로 정리하는 편이 안전합니다.
    • Nova 에페메럴까지 한 번에 바꾸고 싶다: 말리고 싶습니다. Cinder 경로가 안정화된 뒤 별도 프로젝트로 다루는 편이 훨씬 덜 아픕니다.

    여러 번 겪어보면 결국 덜 아픈 방법은 비슷합니다. 새 백엔드를 먼저 붙이고, 신규 생성 경로를 갈라놓고, 기존 볼륨은 업무 중요도와 Ceph 상태를 보면서 천천히 옮기는 방식이죠. 빠른 전환보다 되돌릴 수 있는 전환이 훨씬 값집니다.

    FAQ

    Q. retype와 migrate 중 무엇을 먼저 고려하면 될까요?

    볼륨 타입 정책 자체를 바꾸는 게 목적이면 retype 계열이 더 자연스럽습니다. 특정 목적지 호스트와 풀을 명시적으로 통제해야 하면 migrate가 더 직접적입니다. 다만 둘 다 실제 데이터 복사 여부는 현재 배치와 드라이버 동작에 좌우되니, 테스트 볼륨 검증부터 해보는 게 맞습니다.

    Q. 기존 백엔드는 언제 제거하는 게 좋을까요?

    신규 생성이 완전히 차단되고, 잔여 볼륨과 소스 풀의 정리 후보가 분리되고, 운영 검증 기간을 지난 뒤가 적절합니다. 저는 최소 한 템포 두고 소스 풀을 정리합니다.

    Q. Ceph가 항상 정답인가요?

    아닙니다. Ceph의 장점은 기능 자체보다 운영 일관성에 있습니다. 팀이 그 일관성을 감당할 준비가 안 돼 있다면, Ceph 도입은 기술 선택이 아니라 운영 부채가 될 수도 있습니다.

    OpenStack Ceph 마이그레이션 결정 기준 요약 이미지

    어떤 상황에서 병행 운영, 점진 전환, 즉시 전환 중 무엇을 고를지 요약한 마무리 인포그래픽입니다.

  • [OpenStack] OpenStack VXLAN 오버레이 네트워크 성능 벤치마크 가이드

    [OpenStack] OpenStack VXLAN 오버레이 네트워크 성능 벤치마크 가이드

    OpenStack VXLAN 오버레이 네트워크 성능 벤치마크 가이드

    OpenStack VXLAN 환경에서 성능이 얼마나 나오는지 궁금하신 분들 많으실 겁니다. VM은 잘 뜨는데, 막상 테넌트 네트워크를 여러 개 나누고 나면 트래픽 처리량이 생각보다 안 나오거나, 지연시간이 튀는 경우가 있거든요. 저도 처음엔 “VXLAN이면 그냥 캡슐화 하나 더 되는 거니까 조금만 느리겠지” 정도로 생각했는데, 실제로 홈랩과 운영 비슷한 구조에서 만져보니 **MTU, 오프로딩(offloading, NIC 하드웨어 처리), OVS(Open vSwitch, 가상 스위치) 경로**에 따라 체감이 꽤 달라지더라고요.

    이번 글에서는 OpenStack VXLAN 기반 오버레이 네트워크를 어떻게 벤치마크하면 좋을지, 그리고 Neutron VXLAN 환경에서 테넌트 격리와 처리량을 어떤 기준으로 봐야 하는지 정리해보겠습니다. 숫자를 지어내는 벤치마크 글은 의미가 없으니까, 이 글은 재현 가능한 측정 방법과 해석 기준에 초점을 맞췄습니다. 지금 OpenStack 네트워크 튜닝 중이시라면 꽤 바로 써먹으실 수 있을 거예요.

    컨트롤러, 컴퓨트 노드, 테넌트 네트워크, 터널 엔드포인트가 보이는 OpenStack VXLAN 개요 이미지입니다.

    1. 왜 OpenStack VXLAN 성능 벤치마크가 중요한가요?

    쉽게 말해 VXLAN(Virtual Extensible LAN, 가상 확장 LAN)은 물리 네트워크 위에 가상 네트워크를 하나 더 얹는 방식입니다. 멀티테넌트 환경에서는 정말 편하거든요. VLAN ID 제한에 덜 묶이고, 테넌트별 네트워크를 유연하게 분리할 수 있으니까요. 근데 여기서 중요한 포인트가 있어요. 편리함과 성능은 항상 같은 방향으로 가지는 않는다는 점 말이에요.

    • 캡슐화(encapsulation, 패킷을 한 번 더 감싸는 작업) 오버헤드가 생깁니다.
    • MTU 설정이 맞지 않으면 단편화(fragmentation, 패킷 쪼개짐)로 성능이 무너질 수 있습니다.
    • OVS나 Linux bridge 경로에 따라 CPU 사용률이 크게 달라집니다.
    • east-west traffic(이스트-웨스트 트래픽, VM 간 내부 통신)과 north-south traffic(노스-사우스 트래픽, 외부와의 통신)을 나눠서 봐야 합니다.

    실제로 써보니까 성능 저하 자체보다 더 무서운 건 병목 지점을 모른 채 감으로 튜닝하는 상황이었어요. 이게 제일 오래 갑니다. 삽질 좀 했습니다 ㅎㅎ

    2. OpenStack VXLAN과 Neutron VXLAN 개념, 쉽게 정리해보겠습니다

    OpenStack 네트워크에서 VXLAN은 보통 Neutron(뉴트론, OpenStack 네트워크 서비스)의 오버레이 메커니즘으로 사용돼요. 각 컴퓨트 노드는 터널 종단점 VTEP(VXLAN Tunnel Endpoint, VXLAN 터널 끝점) 역할을 하고, 테넌트 네트워크 트래픽을 UDP 기반 VXLAN 패킷으로 캡슐화해 전달합니다.

    2-1. 데이터 경로를 이해하면 벤치마크 포인트가 보입니다

    1. VM에서 패킷이 나갑니다.
    2. 가상 NIC를 통해 보안 그룹과 가상 스위치를 통과합니다.
    3. 로컬 브리지에서 VXLAN 캡슐화가 이뤄집니다.
    4. 물리 underlay(언더레이, 실제 네트워크)로 전송됩니다.
    5. 상대 노드에서 디캡슐화(decapsulation, 캡슐 해제) 후 대상 VM으로 들어갑니다.

    여기서 성능 포인트는 명확해요. 가상 스위치 처리, 터널 캡슐화, 물리 NIC, MTU 이 네 군데를 봐야 합니다.

    2-2. VLAN과 VXLAN 비교

    항목 VLAN VXLAN
    격리 방식 L2 태그 기반 오버레이 터널 기반
    확장성 상대적으로 제한적 멀티테넌트 확장에 유리
    설계 복잡도 비교적 단순 터널, MTU, 오프로딩 고려 필요
    성능 변수 상대적으로 적음 캡슐화 오버헤드 영향 있음
    OpenStack 활용 Provider network에 자주 사용 Tenant network에 자주 사용

    벤치마크를 할 때는 “VXLAN이 느리다”가 아니라, 어떤 경로에서 얼마나 손실이 생기는지를 봐야 합니다.

    3. 벤치마크 설계: 오버레이 네트워크 성능을 정확히 측정하려면

    제가 직접 해보니 벤치마크가 실패하는 가장 흔한 이유는 측정 항목이 너무 단순하다는 거였어요. `iperf3` 한 번 돌려보고 끝내면, 그건 네트워크 테스트라기보다 순간 샘플에 가까우니까요.

    3-1. 최소 측정 항목

    • 처리량(throughput, 초당 전송량): TCP/UDP 각각 확인
    • 지연시간(latency, 왕복 시간): ping 또는 fping으로 확인
    • 패킷 손실(packet loss): UDP 테스트에서 특히 중요
    • CPU 사용률: 컴퓨트 노드의 `ovs-vswitchd`, `qemu-kvm`, `softirq` 확인
    • MTU 일관성: 인스턴스, TAP, 브리지, 물리 NIC가 연결되는지 확인

    3-2. 테스트 시나리오

    1. 같은 컴퓨트 노드 내 VM 간 통신
    2. 다른 컴퓨트 노드 간 VM 통신
    3. 같은 테넌트 네트워크 내 다수 VM 동시 통신
    4. 서로 다른 테넌트 간 격리 검증
    5. floating IP 또는 router를 통한 외부 통신

    여기서 두 번째와 세 번째가 핵심이에요. 첫 번째만 보면 터널 영향이 덜 보일 수 있거든요. 반대로 크로스-노드(cross-node, 노드 간) 테스트를 해보면 오버레이 네트워크 성능 차이가 확실히 드러납니다.

    4. 실전 구현: OpenStack VXLAN 테스트 환경 준비

    이제 실제로 측정 가능한 구조를 만들어보겠습니다. 예시는 OpenStack CLI 기준으로 적겠습니다. 배포판은 다양하지만, 개념은 거의 비슷해요.

    4-1. 테넌트 네트워크 생성

    openstack network create demo-vxlan-net
    openstack subnet create demo-vxlan-subnet \
      --network demo-vxlan-net \
      --subnet-range 10.10.10.0/24
    
    openstack router create demo-router
    openstack router add subnet demo-router demo-vxlan-subnet

    환경에 따라 외부 네트워크 연결도 필요합니다.

    openstack router set demo-router --external-gateway public

    4-2. 테스트용 인스턴스 2대 이상 배치

    openstack server create vxlan-test-1 \
      --flavor m1.small \
      --image ubuntu \
      --network demo-vxlan-net \
      --key-name mykey
    
    openstack server create vxlan-test-2 \
      --flavor m1.small \
      --image ubuntu \
      --network demo-vxlan-net \
      --key-name mykey

    여기서 가능하면 서로 다른 컴퓨트 노드에 올리는 게 좋아요. 스케줄러 힌트나 가용성 영역을 활용하면 더 명확하게 분리할 수 있습니다.

    4-3. ML2 VXLAN 설정 확인

    배포 환경마다 파일 위치가 다를 수 있지만, 핵심은 ML2 plugin(ML2 플러그인)과 VXLAN 타입 드라이버가 활성화되어 있는지에요.

    [ml2]
    type_drivers = flat,vlan,vxlan
    tenant_network_types = vxlan
    mechanism_drivers = openvswitch,l2population
    
    [ml2_type_vxlan]
    vni_ranges = 1001:2000

    `l2population`은 브로드캐스트/플러딩을 줄이는 데 도움이 되는 기능으로 잘 알려져 있어요. 대규모 환경일수록 의미가 커집니다.

    Neutron ML2 설정, VNI 범위, 각 컴퓨트 노드의 VXLAN 터널 연결 관계를 보여주는 이미지입니다.

    4-4. MTU 확인은 꼭 하셔야 합니다

    VXLAN은 추가 헤더가 붙기 때문에 underlay MTU보다 tenant MTU를 조금 보수적으로 잡는 경우가 많아요. 이 부분을 놓치면 `iperf3`는 애매하게 나오고, 대용량 전송에서만 문제가 재현되기도 합니다. 저도 처음엔 보안 그룹만 의심했는데, 결국 MTU 문제였던 적이 있었어요.

    ip link show
    ip addr
    ping -M do -s 1472 <target_ip>

    `-M do`는 경로 MTU를 강제로 확인할 때 유용해요. 패킷 크기를 조금씩 조정해보면 어디서 단편화가 발생하는지 감이 옵니다.

    5. 성능 측정 방법: iperf3, ping, 시스템 지표를 같이 보세요

    5-1. 기본 처리량 측정

    테스트 VM 한쪽에서는 서버를 띄우고, 다른 쪽에서는 클라이언트로 붙습니다.

    # server
    iperf3 -s
    
    # client
    iperf3 -c 10.10.10.20 -t 30 -P 4

    여기서 `-P 4`처럼 병렬 스트림(parallel stream, 동시 세션)을 주는 이유는 단일 세션만으로는 경로 활용도를 다 못 볼 수 있기 때문이에요.

    5-2. UDP와 손실률 확인

    iperf3 -c 10.10.10.20 -u -b 1G -t 30

    UDP는 대역폭을 강제로 밀어붙이니까 손실과 지터(jitter, 지연 변동폭)를 보기 좋아요. 다만 숫자만 보고 “실사용도 이렇다”고 단정하면 안 돼요. 어디까지나 한계 구간을 찾는 용도에 가깝습니다.

    5-3. 지연시간과 격리 검증

    ping -c 20 10.10.10.20
    
    openstack network create isolated-net
    openstack subnet create isolated-subnet \
      --network isolated-net \
      --subnet-range 10.20.20.0/24

    서로 다른 테넌트 네트워크에서 직접 통신이 안 되는지 확인해보세요. 테넌트 격리는 보안 기능이자 성능 분석의 기준선이에요. 격리가 흐트러지면 벤치마크 이전에 설계부터 다시 봐야 합니다.

    5-4. 호스트 측 관찰 포인트

    top
    htop
    sar -n DEV 1
    ip -d link show
    ovs-vsctl show
    ovs-ofctl dump-ports br-tun

    성능이 기대보다 안 나오는데 VM 내부는 멀쩡하다면, 호스트의 softirq나 OVS 포트 카운터를 같이 봐야 해요. 근데 여기서 중요한 건, 네트워크만 보지 말고 CPU를 같이 보라는 점이에요. 오버레이는 생각보다 CPU 영향을 받거든요.

    6. ⚠️ 트러블슈팅: 실제로 자주 막히는 지점

    이 섹션은 정말 중요해요. 제가 홈랩에서 OpenStack 네트워크 만질 때 제일 오래 잡아먹은 부분들이거든요.

    6-1. MTU 불일치

    • 증상: TCP는 되는데 대용량 전송에서 처리량이 애매하게 떨어짐
    • 원인: VXLAN 캡슐화 헤더를 고려하지 않은 MTU 설정
    • 해결: 인스턴스 NIC, TAP, `br-int`, `br-tun`, 물리 NIC까지 경로 전체 MTU 점검

    6-2. 오프로딩 설정 이슈

    • 증상: CPU 사용률이 과하게 올라가고 처리량이 들쭉날쭉함
    • 원인: NIC offload와 가상화 경로 조합 문제
    • 해결: `ethtool -k`로 현재 상태를 보고 변경 전후 비교 측정
    ethtool -k eth0

    이건 환경마다 정답이 달라요. 무조건 켜라, 무조건 꺼라 식으로 가면 위험해요. 그래서 반드시 변경 전후를 같은 조건으로 비교해야 합니다.

    6-3. 보안 그룹과 conntrack 병목

    • 증상: 연결 수가 늘면 응답이 갑자기 무거워짐
    • 원인: stateful filtering(상태 기반 필터링)과 connection tracking 부담
    • 해결: 테스트 시나리오를 단순화하고, 보안 정책 영향도 분리해서 확인

    6-4. 같은 노드와 다른 노드 성능 차이

    같은 컴퓨트 노드 안에서는 생각보다 잘 나오는데, 다른 노드로 넘어가는 순간 성능이 달라지는 경우가 많아요. 이럴 때는 거의 대부분 터널 경로 또는 물리 underlay를 의심하게 됩니다.

    VM 간 iperf3 측정, 호스트 CPU 사용률, MTU 점검 포인트가 함께 보이는 실전 운영 이미지입니다.

    7. 검증과 결과 해석: 숫자보다 중요한 것

    벤치마크 결과를 볼 때 저는 보통 아래 순서로 해석해요. 숫자 하나만 보고 결론 내리면 나중에 꼭 후회하더라고요.

    1. 같은 노드와 다른 노드 간 격차가 큰가
    2. TCP와 UDP 결과 차이가 과도한가
    3. 지연시간 평균보다 편차가 큰가
    4. 호스트 CPU 사용률이 병목처럼 보이는가
    5. 테넌트 수 증가 시 성능 하락 패턴이 급격한가

    예를 들어 처리량이 조금 낮아도 지연시간이 안정적이고 손실이 적다면, 실서비스에서는 충분히 좋은 결과일 수 있어요. 반대로 순간 최대 처리량이 높아도 CPU가 포화되고 지터가 심하면 운영 입장에서는 불안한 구조죠.

    검증 항목 좋은 신호 주의 신호
    처리량 반복 측정 시 편차가 작음 테스트마다 편차가 큼
    지연시간 평균과 최대값 차이가 적당함 간헐적 스파이크 발생
    패킷 손실 UDP 부하에서도 통제 가능 부하 시 급격히 증가
    CPU 사용률 여유가 있음 ovs-vswitchd 또는 softirq 집중
    격리 상태 테넌트 간 통신 차단 명확 정책 누락 또는 라우팅 혼선

    오버레이 네트워크 성능은 결국 설계와 운영 습관의 결과물이에요. 그래서 결과 보고서에는 숫자만 넣지 말고, 테스트 조건도 같이 남기시는 걸 권장합니다. 다음에 다시 비교할 때 진짜 큰 도움이 돼요.

    OpenStack VXLAN 벤치마크 결과와 지연시간 처리량을 보여주는 대시보드 이미지

    처리량, 지연시간, CPU 사용률을 함께 보여주는 OpenStack VXLAN 벤치마크 결과 시각화 이미지입니다.

    8. 정리와 다음 단계: OpenStack 네트워크 튜닝은 이렇게 이어가시면 됩니다

    정리해보면 OpenStack VXLAN 벤치마크에서 중요한 건 단순 최고 속도가 아니에요. Neutron VXLAN 환경에서 테넌트 격리가 제대로 되는지, 크로스-노드 트래픽이 안정적인지, MTU와 OVS 경로가 일관적인지, 그리고 호스트 CPU가 어디서 쓰이는지를 같이 봐야 합니다.

    제가 직접 해보니 가장 효과가 컸던 접근은 이것이었어요.

    1. 같은 노드와 다른 노드 결과를 분리해서 기록합니다.
    2. TCP, UDP, ping을 각각 따로 봅니다.
    3. 문제가 보이면 MTU와 오프로딩부터 확인합니다.
    4. 그 다음에 보안 그룹, conntrack, underlay를 봅니다.

    이 순서로 가면 삽질을 꽤 줄일 수 있어요. 드디어 됐다! 싶은 순간이 오거든요.

    OpenStack VXLAN 성능 최적화 체크리스트와 비교 요약 인포그래픽

    MTU, 오프로딩, OVS, 테넌트 격리, 처리량 검증 항목을 한눈에 정리한 요약 인포그래픽입니다.

    자주 묻는 질문

    Q1. VXLAN이면 무조건 VLAN보다 느린가요?

    무조건 그렇지는 않아요. 캡슐화 오버헤드는 있지만, 실제 체감은 NIC 성능, CPU, OVS 구성, underlay 품질에 따라 달라집니다.

    Q2. 벤치마크는 어떤 도구부터 쓰면 좋을까요?

    가볍게는 `ping`, `iperf3`, `sar`, `ethtool`, `ovs-vsctl` 정도면 시작하기 좋아요. 도구보다 중요한 건 같은 조건으로 반복 측정하는 습관입니다.

    Q3. 테넌트 격리는 어떻게 검증하나요?

    서로 다른 테넌트 네트워크에 VM을 두고 직접 통신 가능 여부, 라우터 연결 여부, 보안 그룹 정책을 분리해서 확인하시면 돼요.

    다음 글에서는 Open vSwitch 기반에서 `br-int`, `br-tun`, provider network 경로를 좀 더 깊게 파서, 어디서 병목이 생기는지 보는 방법을 다뤄볼 예정입니다. 이전 글에서 다룬 Linux 네트워크 기본기와 함께 보시면 훨씬 이해가 잘 될 거예요.

  • [OpenStack] OpenStack Manila 도입 1년 회고

    [OpenStack] OpenStack Manila 도입 1년 회고

    [OpenStack] OpenStack Manila 도입 1년 회고

    프라이빗 클라우드 파일 스토리지 이야기를 하면, 많은 분들이 처음엔 Cinder(신더, 블록 스토리지)나 Swift(스위프트, 오브젝트 스토리지)부터 떠올리시더라고요. 저도 그랬습니다. 그런데 VM(가상머신) 여러 대와 컨테이너 워크로드가 같이 굴러가기 시작하면, 결국 팀에서 꼭 묻는 질문이 하나 나옵니다. “공유 폴더처럼 쓸 수 있는 스토리지는 없나요?” 바로 그 지점에서 OpenStack Manila가 등장합니다.

    저는 지난 1년 동안 홈랩과 사내와 유사한 형태의 프라이빗 환경에서 Manila 운영 방식을 꽤 집요하게 다듬어봤습니다. 처음엔 “이게 Nova(노바, 컴퓨트)나 Neutron(뉴트론, 네트워크)처럼 그냥 붙이면 되겠지” 했었는데요. 막상 들어가 보니 기능은 명확한데, 프라이빗 클라우드 파일 스토리지 운영할 때 고려할 포인트가 생각보다 많더라고요. 특히 Manila로 파일 스토리지를 운영하면 자동화가 편해지는 대신, 네트워크와 백엔드 설계를 대충 하면 나중에 꼭 되돌아오니까요. 오늘은 OpenStack Manila 도입 1년 회고라는 관점에서, 좋았던 점과 아쉬웠던 점을 솔직하게 정리해보겠습니다.

    OpenStack Manila, 컴퓨트 노드, 스토리지 백엔드, 사용자 네트워크가 어떻게 연결되는지 한눈에 보여주는 아키텍처 이미지가 들어갈 자리입니다.

    1. 왜 OpenStack Manila를 보게 되었는가

    처음 문제는 단순했습니다. 프로젝트마다 VM 안에 데이터를 따로 넣어두다 보니, 배치 작업 서버와 애플리케이션 서버가 같은 데이터를 봐야 하는 상황에서 계속 복사본이 생겼거든요. 이러면 데이터 일관성도 깨지고, 백업 정책도 꼬이고, 장애가 나면 복구 지점도 애매해집니다.

    프라이빗 클라우드 파일 스토리지 요구가 계속 나오면서 선택지를 세 가지로 놓고 봤습니다.

    • VM 내부 디스크를 각자 관리한다
    • Cinder 볼륨을 붙여서 우회한다
    • 공유 파일 스토리지 자체를 서비스로 제공한다

    세 번째가 바로 OpenStack Manila, 즉 프라이빗 클라우드 파일 스토리지 서비스의 자리였습니다. 실제로 써보니까, 파일 공유를 사람 손으로 매번 만들어주는 방식보다 API(에이피아이, 프로그램 호출 인터페이스) 기반으로 표준화하는 게 훨씬 관리가 잘 되더라고요.

    2. OpenStack Manila란 무엇인가

    쉽게 말해 OpenStack Manila는 공유 파일 시스템을 서비스 형태로 제공하는 컴포넌트입니다. Cinder가 블록 디바이스를 내주는 역할이라면, Manila는 NFS(엔에프에스, 네트워크 파일 시스템)나 SMB(에스엠비, 파일 공유 프로토콜) 같은 형태의 공유 스토리지를 만들어주는 쪽에 가깝습니다.

    처음엔 이름이 좀 낯설죠. 저도 처음엔 “왜 이름이 Manila지?” 싶었는데, 기능 자체는 꽤 직관적입니다. 핵심 개념만 잡아두면 어렵지 않거든요.

    핵심 개념 한 번에 정리

    • Share: 실제로 사용자에게 제공되는 파일 공유 자원
    • Share Type: 성능, 백엔드, 기능 정책을 구분하는 템플릿
    • Share Network: 공유 스토리지가 붙을 네트워크 정보
    • Backend Driver: CephFS(세프에프에스), NetApp, Generic driver 같은 실제 구현체와 연결되는 계층
    • Access Rule: 어떤 IP나 사용자에게 접근을 허용할지 정하는 규칙

    여기서 중요한 포인트! Manila 자체가 스토리지를 저장하는 제품은 아닙니다. 정확히는 백엔드 파일 스토리지를 OpenStack 방식으로 관리해주는 제어 계층이라고 생각하시면 돼요. 이 차이를 이해 못 하면 설계가 꼬입니다.

    다른 스토리지와 무엇이 다른가

    구분 주 용도 접근 방식 운영 시 느낌
    Ephemeral Disk 인스턴스 로컬 작업 VM 내부 로컬 디스크 빠르지만 수명과 이동성 제약이 크다
    Cinder 데이터베이스, 단일 서버 디스크 블록 디바이스 마운트 명확하지만 다중 공유에는 부적합
    Swift 백업, 아카이브, 객체 저장 HTTP API 확장성은 좋지만 파일시스템처럼 쓰긴 어렵다
    Manila (프라이빗 클라우드 파일 스토리지) 공유 파일 스토리지 NFS/SMB 등 협업과 공용 데이터셋에 강하다

    3. 도입 전에 꼭 봐야 했던 설계 포인트

    제가 1년 운영하면서 느낀 건, Manila는 설치보다 설계가 더 중요하다는 점입니다. 특히 아래 세 가지를 먼저 정리해야 나중에 덜 고생합니다.

    1. 백엔드 선택: CephFS처럼 분산 파일 시스템을 붙일지, 전통적인 NAS(나스, 네트워크 스토리지)를 붙일지 결정해야 한다
    2. 네트워크 분리: 스토리지 트래픽과 테넌트 트래픽을 어느 정도 분리할지 판단해야 한다
    3. 운영 권한 모델: 누가 share를 만들고, 누가 접근 규칙을 열 수 있는지 기준을 세워야 한다

    사실 여기서 많이들 놓치는 게 두 번째입니다. 프라이빗 클라우드 파일 스토리지는 결국 네트워크 영향을 강하게 받습니다. 스토리지 백엔드는 멀쩡한데도 MTU(엠티유, 최대 전송 단위)나 라우팅, 보안그룹 정책 때문에 체감 성능이 무너지는 경우를 꽤 봤거든요.

    4. OpenStack Manila 실전 구현: 제가 실제로 잡았던 기본 흐름

    이제 구현 얘기를 해보겠습니다. 환경마다 패키지 설치 방식은 다를 수 있으니, 여기서는 운영 개념과 명령 흐름 위주로 정리하겠습니다. 저는 처음에 모든 설정을 한 번에 넣으려다가 꼬여서, 결국 가장 단순한 형태부터 올리는 방식으로 갔습니다. 이게 훨씬 낫더라고요.

    1) 서비스 상태 확인

    openstack share service list
    openstack endpoint list --service manila
    openstack network list
    

    맨 처음엔 화려한 기능보다 서비스가 보이느냐부터 확인했습니다. 생각보다 기본 서비스 등록이나 엔드포인트 누락이 자주 나옵니다.

    2) share type 생성

    openstack share type create default_share false
    openstack share type set --extra-specs snapshot_support=true default_share
    

    Share Type은 나중에 Manila 운영 정책의 중심이 됩니다. 성능 클래스, 스냅샷 허용 여부, 백엔드 매핑 기준을 여기에 녹여두면 사용자 설명이 쉬워지거든요.

    3) share network 생성

    openstack share network create \
      --name manila-share-net \
      --neutron-net-id <tenant-network-id> \
      --neutron-subnet-id <tenant-subnet-id>
    

    여기서 저도 한 번 크게 삽질했습니다. 인스턴스가 붙은 네트워크와 share network 관계를 대충 맞추면 될 줄 알았는데, 실제로는 접근 경로와 라우팅이 명확해야 마운트 단계에서 덜 막힌다더라고요.

    4) 공유 스토리지 생성

    openstack share create \
      --name project-data \
      --share-type default_share \
      --share-network manila-share-net \
      --size 100 \
      NFS
    

    생성 직후 바로 쓰려고 하면 안 됩니다. 상태가 available이 될 때까지 기다려야 합니다.

    openstack share list
    openstack share show project-data
    
    OpenStack Manila share type과 share network 구성 흐름 이미지

    Share type, share network, backend driver, tenant network가 어떤 순서로 연결되는지 보여주는 구성 다이어그램이 들어갈 자리입니다.

    5) 접근 제어 추가

    openstack share access create project-data ip 10.10.20.15
    openstack share access list project-data
    

    Manila 운영하면서 가장 체감이 좋았던 부분 중 하나가 이겁니다. 예전엔 파일 공유를 열 때 네트워크팀, 스토리지팀, 운영팀이 각각 손대야 했었는데요. Manila를 붙이니 최소한 권한과 절차가 API로 정리되니까 일이 많이 단순해졌습니다.

    6) 인스턴스에서 마운트

    sudo mkdir -p /mnt/project-data
    sudo mount -t nfs <share-export-location> /mnt/project-data
    df -h
    mount | grep project-data
    

    여기서 export location이 올바르게 나오는지 확인해야 합니다. 실제 사용자는 이 지점만 보기 때문에, 앞단 자동화가 아무리 좋아도 마운트 실패하면 평가가 박해집니다. 냉정하죠 ㅎㅎ

    7) 설정 예시

    [DEFAULT]
    default_share_type = default_share
    enabled_share_backends = cephfsnfs
    
    [cephfsnfs]
    share_backend_name = CEPHFSNFS
    driver_handles_share_servers = false
    

    설정은 백엔드마다 달라지니 그대로 복붙하시면 안 되고, 운영 중인 백엔드 문서와 정확히 맞춰야 합니다. 다만 관점 자체는 비슷합니다. 어떤 백엔드를 활성화할지, 드라이버가 share server를 직접 다룰지 여부를 정하는 구조니까요.

    5. 1년 운영하며 좋았던 점: 명(明)

    OpenStack Manila 운영을 1년쯤 해보니까, 분명 장점이 있습니다. 이건 꽤 명확했습니다.

    • 셀프서비스화: 사용 부서가 요청만 하면 표준 절차로 공유 스토리지를 만들 수 있다
    • 정책 통일: share type 중심으로 Manila 운영 기준을 맞추기 쉽다
    • 접근 제어 가시성: 누가 어떤 share에 접근 가능한지 추적이 쉬워진다
    • 자동화 친화적: Terraform(테라폼, 인프라 선언형 자동화)이나 내부 포털과 엮기 좋다

    특히 프로젝트 단위로 수명주기를 관리할 때 편했습니다. 인스턴스는 바뀌어도 데이터 공유 계층은 유지할 수 있으니, 작업 서버를 새로 올려도 공유 데이터를 다시 설계할 필요가 없거든요. 이거 진짜 편하더라고요.

    또 하나는 운영 책임 구간이 분리된다는 점입니다. 예전엔 “공유 폴더 느려요”라는 말이 나오면 원인 범위가 너무 넓었는데, Manila 도입 후에는 적어도 API 계층, 네트워크 계층, 백엔드 계층으로 잘게 나눠서 볼 수 있었습니다.

    6. 1년 운영하며 아쉬웠던 점: 암(暗)

    근데 여기서 중요한 게 있습니다. Manila는 만능이 아닙니다. 오히려 기대치를 너무 높게 잡으면 실망하기 쉽습니다.

    첫째, 문제의 절반은 결국 백엔드와 네트워크입니다

    사용자 입장에서는 OpenStack Manila가 파일 스토리지를 제공하는 것처럼 보이지만, 실제 체감 품질은 백엔드 스토리지와 네트워크 설계에 크게 좌우됩니다. 즉, Manila가 느린 게 아니라 뒤쪽이 느린 경우가 많습니다. 이걸 분리해서 설명하는 데 시간이 꽤 들었습니다.

    둘째, 권한 설계가 느슨하면 금방 복잡해집니다

    처음엔 “필요한 사람에게 IP만 열어주면 되지” 했었는데요. 몇 달 지나면 예외 규칙이 누적됩니다. 접근 규칙을 누가 승인하는지, 만료 기준은 뭔지, 프로젝트 종료 시 어떻게 닫을지 초반에 정해야 합니다.

    셋째, 장애 분석이 생각보다 단순하지 않습니다

    마운트 실패 하나만 놓고 봐도 원인이 다양합니다. DNS(디엔에스, 이름 해석), 라우팅, 보안그룹, export location, 백엔드 상태, 커널 NFS 클라이언트 옵션까지 봐야 하거든요. 처음엔 “왜 이렇게 복잡하지?” 싶었는데, 결국 파일 공유라는 게 여러 계층이 맞물리는 서비스라 그렇더라고요.

    7. ⚠️ 실제로 겪었던 트러블슈팅

    이 섹션은 좀 현실적으로 적어볼게요. 저도 처음엔 문서만 보면 금방 될 줄 알았는데, 실제 Manila 운영에선 아래 이슈를 자주 만났습니다.

    문제 1. share는 생성됐는데 마운트가 안 되는 경우

    • 증상: `mount.nfs: access denied by server`
    • 원인 후보: 접근 규칙 누락, 클라이언트 IP 변경, 잘못된 네트워크 경로
    • 해결: `openstack share access list`로 허용 규칙 확인 후, 실제 클라이언트 IP와 비교
    openstack share access list project-data
    ip addr
    ip route
    

    생각보다 NAT(엔에이티, 주소 변환)나 점프 구간 때문에 사용자가 보는 IP와 실제 서버가 보이는 IP가 다른 경우가 있었습니다.

    문제 2. 생성 속도는 괜찮은데 체감 성능이 들쭉날쭉한 경우

    • 증상: 어떤 VM은 빠르고 어떤 VM은 유독 느림
    • 원인 후보: 네트워크 경로 차이, MTU 불일치, 혼잡 구간 존재
    • 해결: 경로별 성능 차이를 먼저 분리 측정하고, Manila 문제로 단정하지 않기

    여기서 제가 배운 건 하나입니다. 스토리지 문제를 API 문제로 착각하지 말 것. 관리 평면(control plane)과 데이터 평면(data plane)을 분리해서 봐야 합니다.

    문제 3. 공유는 살아 있는데 운영 기록이 남지 않는 경우

    • 증상: 누가 왜 접근 규칙을 열었는지 추적이 어려움
    • 원인 후보: 수동 작업, 표준 요청 절차 부재
    • 해결: 변경 요청 번호나 티켓 번호를 share 이름 또는 메타데이터 규칙에 반영

    이건 기술 이슈 같지 않지만 실제 Manila 운영에선 꽤 큽니다. 나중에 감사나 보안 점검 때 힘들어지거든요.

    문제 4. 기대보다 운영 난도가 높게 느껴지는 경우

    사실 OpenStack 스토리지는 모두 그렇지만, Manila도 “기능 추가”보다 “운영 모델 정리”가 더 어렵습니다. 백엔드 드라이버 특성, 네트워크 토폴로지, 접근 정책이 모두 연결되니까요. 그래서 제가 추천하는 방식은 이겁니다.

    1. 처음엔 딱 한 가지 share type만 운영한다
    2. 접근 제어는 가장 보수적으로 시작한다
    3. 사용자 수요가 확인된 뒤에 스냅샷, 멀티 백엔드, 성능 클래스 분리를 확장한다

    한 번에 다 하려 하지 않는 것, 이게 제일 중요했습니다.

    8. 검증과 운영 결과: 무엇이 달라졌나

    그럼 1년 운영해서 뭐가 남았냐, 이 질문이 제일 중요하겠죠. 제가 직접 해보니 결과는 꽤 분명했습니다.

    • 프라이빗 클라우드 파일 스토리지 개설 절차가 표준화됐다
    • 프로젝트별 데이터 공유 방식이 단순해졌다
    • 수동 NAS 작업이 줄어들었다
    • 장애 시 원인 구간을 더 빨리 좁힐 수 있게 됐다

    반대로, 운영 문서와 접근 정책이 없으면 Manila 운영은 금방 복잡해진다는 것도 확인했습니다. 즉, OpenStack Manila의 성공 조건은 설치가 아니라 운영 규칙이에요.

    openstack share list
    openstack share service list
    openstack share type list
    openstack share network list
    

    검증할 때는 단순히 리소스가 보이는지보다, 아래 항목을 같이 확인했습니다.

    1. Share 상태가 `available`인지
    2. 접근 규칙이 기대한 대상에만 열려 있는지
    3. 실제 인스턴스에서 마운트와 읽기/쓰기가 되는지
    4. 장애 시 로그와 변경 이력이 추적 가능한지
    OpenStack Manila 운영 결과와 검증 체크리스트 대시보드 이미지

    Share 상태, 접근 규칙, 마운트 성공 여부, Manila 운영 지표를 한 화면에서 점검하는 대시보드 이미지가 들어갈 자리입니다.

    9. FAQ와 마무리: 다음 단계는 어떻게 가면 좋을까

    자주 묻는 질문

    • Q. Manila는 모든 환경에서 꼭 필요한가요?
      아닙니다. 프라이빗 클라우드 파일 스토리지 요구가 적고 운영 단순성이 더 중요하면 굳이 넣지 않는 게 나을 수도 있습니다.
    • Q. 프라이빗 클라우드 파일 스토리지로 바로 도입해도 될까요?
      가능은 하지만, 먼저 한두 팀 대상 파일 공유 서비스로 시작해 Manila 운영 모델을 검증하는 걸 추천합니다.
    • Q. OpenStack 스토리지 중 우선순위는 어떻게 잡아야 하나요?
      블록, 오브젝트, 파일의 요구가 다르니 워크로드 기준으로 판단해야 합니다. “공유 파일”이 핵심이면 Manila가 후보가 됩니다.

    정리해보면 이렇습니다. OpenStack Manila는 프라이빗 클라우드 파일 스토리지 운영을 표준화하는 데 꽤 좋은 도구입니다. 다만 제품만 올린다고 끝나는 게 아니고, 백엔드 선택과 네트워크 설계, 접근 정책, 운영 문서화까지 같이 가야 진짜 효과가 납니다.

    저도 처음엔 이게 뭔가 싶었는데, 1년쯤 지나고 나니 보이더라고요. Manila 운영의 핵심은 “기능 활성화”가 아니라 “운영 경계 정의”였습니다. 이걸 빨리 이해할수록 삽질이 줄어들더라고요.

    다음 글에서는 OpenStack Manila 운영 시 백엔드 선택 기준, 예를 들면 CephFS 계열과 전통적인 NAS 연동 관점에서 무엇을 봐야 하는지 더 구체적으로 다뤄볼 예정입니다. 이전 글에서 다뤘던 OpenStack 네트워크 설계 내용과도 연결해서 보시면 훨씬 이해가 쉬우실 겁니다.

    OpenStack Manila 도입 전후 장단점 비교 인포그래픽

    도입 전 수동 운영 방식과 도입 후 표준화된 프라이빗 클라우드 파일 스토리지 운영 방식을 비교하는 요약 인포그래픽이 들어갈 자리입니다.

    혹시 지금 OpenStack Manila 도입을 고민 중이시라면, 제 경험상 가장 먼저 할 일은 하나입니다. “누가 어떤 데이터를 어떤 방식으로 공유해야 하는가”를 먼저 적어보세요. 그 다음에 Manila를 붙이면 훨씬 덜 헤맵니다. 이 순서, 생각보다 정말 중요합니다. 🎉

  • [OpenStack] OpenStack Placement 벤치마크: 대규모 자원 할당 성능 분석

    [OpenStack] OpenStack Placement 벤치마크: 대규모 자원 할당 성능 분석

    [OpenStack] OpenStack Placement 벤치마크: 대규모 자원 할당 성능 분석

    OpenStack Placement 벤치마크를 처음 제대로 돌려본 건, 스케줄러가 이상하게 느려지는 순간 때문이었습니다. VM 몇 대 수준에서는 티가 안 나는데, 컴퓨트 노드 수가 늘고 자원 요청(Resource Request)이 복잡해지면 갑자기 API 응답이 늘어지고, 결국 사용자는 “왜 인스턴스 생성이 이렇게 오래 걸리죠?”라고 묻게 되거든요. 저도 처음엔 Nova Scheduler(노바 스케줄러) 쪽만 의심했었는데, 실제로 뜯어보니 Placement 서비스가 병목으로 보이는 구간이 있더라고요. 그래서 이번 글에서는 OpenStack Placement 벤치마크를 어떤 식으로 설계하고, 무엇을 봐야 하며, 결과를 어떻게 해석해야 하는지 제 경험 기준으로 정리해보겠습니다.

    특히 자원 할당 성능, OpenStack 인프라, Placement 서비스를 운영 관점에서 보고 싶은 분들께 도움이 될 겁니다. 숫자를 지어내는 식의 벤치마크는 의미가 없어서, 이번 글은 재현 가능한 테스트 절차와 해석 포인트 위주로 풀어보겠습니다. 혹시 운영 환경에서 “CPU는 남는데 스케줄링이 느리다” 같은 경험 있으신가요? 여기서 중요한 포인트가 바로 Placement입니다.

    Placement 서비스, Nova Scheduler, 데이터베이스, 컴퓨트 노드가 연결된 전체 벤치마크 아키텍처 개요입니다.

    1. 왜 OpenStack Placement 벤치마크가 중요한가

    쉽게 말해 Placement는 “이 요청을 만족할 수 있는 자원이 어디에 있나”를 빠르게 찾는 역할을 합니다. 예전에는 자원 조회 로직이 여러 군데 흩어져 있어서 복잡했는데, Placement가 그 책임을 상당 부분 분리해줬죠. 문제는 조회 대상이 커지면, 즉 Resource Provider(리소스 프로바이더, 자원 제공 주체) 수가 많아지고 Traits(트레이트, 자원 특성)나 Aggregate(애그리게이트, 그룹화 정보) 조건이 겹치면 성능 특성이 확 달라진다는 게 핵심이더라고요.

    제가 직접 해보니 단순 조회는 꽤 잘 버티는데, 아래 조건이 섞이면 체감 성능이 달라졌습니다.

    • 컴퓨트 노드가 많아져 Resource Provider 수가 크게 증가한 경우
    • NUMA, PCI passthrough, SR-IOV 같은 추가 조건이 붙는 경우
    • 동시 API 요청이 몰리는 경우
    • Inventory(인벤토리, 제공 가능한 자원량)와 Allocation(할당 상태) 갱신이 계속되는 경우

    운영에서는 이게 곧 사용자 경험으로 이어집니다. 인스턴스 생성 지연, 오토스케일 지연, 배치 작업 실패로 연결될 수 있거든요. 그래서 OpenStack Placement 벤치마크는 단순한 성능 놀이가 아니라, 실제 운영 여유치(headroom)를 확인하는 절차라고 보는 게 맞습니다.

    2. Placement 서비스 핵심 개념 정리

    저도 처음엔 용어 때문에 좀 헷갈렸는데, 이 부분만 정리되면 뒤가 훨씬 쉬워집니다.

    2-1. Resource Provider와 Inventory

    Resource Provider는 CPU, RAM, DISK 같은 자원을 제공하는 대상입니다. 보통 컴퓨트 노드가 대표적이지만, 구조상 더 세분화된 단위도 가능합니다. Inventory는 “이 노드가 얼마나 제공 가능한가”에 대한 정보고, Allocation은 “그 중 얼마가 이미 사용 중인가”에 가깝습니다.

    2-2. Trait와 Aggregate

    Trait는 특성 태그라고 생각하면 편합니다. 예를 들어 SSD, 특정 CPU 기능, 가상화 확장 여부 같은 성격을 표현할 때 씁니다. Aggregate는 여러 Resource Provider를 묶는 논리 그룹입니다. Availability Zone과 비슷하게 느껴질 수 있는데 쓰임은 조금 다르죠.

    2-3. Allocation Candidate

    벤치마크에서 자주 보게 되는 개념입니다. Allocation Candidate(할당 후보)는 요청 조건을 만족하는 자원 조합 후보인데, 결국 스케줄링 전에 “가능한 곳 리스트”를 만든다고 보면 됩니다. 이 후보 계산이 커질수록 Placement 서비스의 응답 시간도 영향을 받습니다.

    개념 쉽게 말한 의미 성능에 미치는 영향
    Resource Provider 자원을 제공하는 대상 대상이 많아질수록 조회 범위 증가
    Inventory 제공 가능한 총 자원 갱신 빈도와 조회 비용에 영향
    Allocation 이미 할당된 자원 상태 동시성 충돌과 상태 일관성에 영향
    Trait 자원 특성 태그 필터 조건 증가로 쿼리 복잡도 상승 가능
    Aggregate 리소스 그룹 묶음 후보군 축소 또는 조인 비용 증가 가능
    Allocation Candidate 요청 만족 후보 세트 대규모 환경에서 핵심 병목 포인트

    3. 벤치마크 설계: 뭘 측정해야 의미가 있나

    여기서 제일 많이 하는 실수가, 단순히 초당 요청 수만 보는 겁니다. 물론 Requests Per Second(RPS, 초당 요청 수)도 중요하지만, 운영 관점에서는 그것만 보면 안 되더라고요. 저는 보통 아래 항목을 함께 봅니다.

    1. 응답 시간 분포: 평균보다 P95, P99 같은 꼬리 지연(tail latency)이 더 중요합니다.
    2. 동시 요청 처리량: 단일 요청보다 burst 상황을 봐야 합니다.
    3. 오류율: 5xx 응답, 타임아웃, 충돌 응답 여부를 확인합니다.
    4. DB 부하: Placement 자체보다 백엔드 DB가 먼저 한계에 닿는 경우가 많습니다.
    5. 스케줄링 체감: Placement API만 빠르고 실제 인스턴스 생성은 느릴 수도 있습니다.

    벤치마크 시나리오도 분리하는 게 좋습니다. 제가 추천하는 기준은 이렇습니다.

    • 단순 조회 시나리오: traits 없이 기본 자원만 요청
    • 조건부 조회 시나리오: trait, aggregate, resource class 조건 추가
    • 대규모 후보 시나리오: 후보군이 매우 많도록 구성
    • 갱신 혼합 시나리오: allocation/inventory 업데이트와 조회를 동시에 수행

    사실 운영 장애는 마지막 시나리오에서 많이 튀어나옵니다. 읽기만 있는 테스트는 예쁘게 나오는데, 쓰기와 섞는 순간 이야기가 달라지거든요.

    OpenStack Placement 벤치마크의 리소스 프로바이더 토폴로지 이미지

    리소스 프로바이더 토폴로지와 요청 흐름, 후보 계산 경로를 보여주는 구성 다이어그램입니다.

    4. 홈랩 기준 실전 벤치마크 환경 구성

    제 홈랩에서도 완전히 똑같은 운영 환경을 만들 수는 없었지만, 패턴을 확인하는 데는 충분했습니다. 중요한 건 절대적인 숫자보다 병목이 어디서 시작되는지를 보는 겁니다.

    4-1. 테스트 전 체크리스트

    • Placement API 엔드포인트 접근 가능 여부 확인
    • 테스트용 프로젝트와 인증 토큰 준비
    • 백엔드 DB 상태 확인
    • 벤치마크 중 로그 레벨을 너무 과하게 올리지 않기
    • 테스트 시간대 분리: 운영 트래픽과 겹치지 않게

    4-2. 기본 확인 명령

    source admin-openrc.sh
    openstack endpoint list --service placement
    openstack resource provider list
    openstack --os-placement-api-version 1.0 resource provider list
    

    CLI가 환경마다 조금 다를 수 있어서, 저는 먼저 엔드포인트와 인증이 정상인지부터 확인합니다. 여기서 막히면 뒤 테스트는 전부 헛수고가 되더라고요. 삽질 좀 했습니다 ㅎㅎ

    4-3. API 직접 조회 예시

    export TOKEN=$(openstack token issue -f value -c id)
    export PLACEMENT_URL=$(openstack endpoint list --service placement -f value -c URL | head -n 1)
    
    curl -s -H "X-Auth-Token: ${TOKEN}" \
         -H "OpenStack-API-Version: placement 1.0" \
         "${PLACEMENT_URL}/resource_providers" | jq .
    

    이렇게 직접 보면 중간 계층 없이 Placement 서비스의 응답만 확인하기 좋습니다. 벤치마크는 가능하면 레이어를 나눠서 보세요. Nova까지 한 번에 보면 편하긴 한데, 원인 분리가 어려워집니다.

    4-4. 간단한 부하 스크립트 예시

    제가 자주 쓰는 방식은 Python(파이썬)으로 요청 수, 동시성, 헤더를 명시하는 간단한 스크립트를 먼저 만드는 겁니다. 전문 부하도구를 써도 되지만, 초기에 API 동작 확인은 직접 짠 스크립트가 오히려 빠를 때가 많습니다.

    import os
    import time
    import statistics
    import concurrent.futures
    import requests
    
    TOKEN = os.environ["TOKEN"]
    PLACEMENT_URL = os.environ["PLACEMENT_URL"].rstrip("/")
    HEADERS = {
        "X-Auth-Token": TOKEN,
        "OpenStack-API-Version": "placement 1.0",
    }
    URL = f"{PLACEMENT_URL}/allocation_candidates?resources=VCPU:2,MEMORY_MB:4096,DISK_GB:20"
    
    
    def fetch():
        start = time.perf_counter()
        r = requests.get(URL, headers=HEADERS, timeout=10)
        elapsed = time.perf_counter() - start
        return r.status_code, elapsed
    
    
    def run(total=100, workers=10):
        results = []
        with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as ex:
            futures = [ex.submit(fetch) for _ in range(total)]
            for f in concurrent.futures.as_completed(futures):
                results.append(f.result())
    
        latencies = [elapsed for status, elapsed in results if status == 200]
        errors = [status for status, _ in results if status != 200]
    
        print("total=", len(results))
        print("success=", len(latencies))
        print("errors=", len(errors))
        if latencies:
            print("avg=", round(statistics.mean(latencies), 4))
            print("max=", round(max(latencies), 4))
    
    
    if __name__ == "__main__":
        run(total=300, workers=30)
    

    이 스크립트는 아주 기본형입니다. 운영에서 쓰려면 요청 경로를 더 나누고, P95/P99 계산도 붙이고, 결과를 파일로 남기면 좋습니다.

    5. 단계별 OpenStack Placement 벤치마크 진행 방법

    이제 실제 진행 순서입니다. 여기서는 벤치마크 결과를 왜곡하지 않도록, 시나리오를 점진적으로 키우는 방식이 좋습니다.

    1. 기준선(Baseline) 측정
      아무 조건 없는 조회부터 시작합니다. Resource Provider 수가 적은 상태에서 먼저 응답 시간을 봅니다.
    2. 조건 추가
      Trait와 Aggregate 조건을 하나씩 늘려봅니다. 어떤 조건이 급격한 지연을 만드는지 확인합니다.
    3. 동시성 증가
      workers 값을 점진적으로 올립니다. 갑자기 10배로 올리면 원인 파악이 힘듭니다.
    4. 데이터 규모 증가
      리소스 프로바이더와 할당 데이터를 늘린 상태에서 다시 측정합니다.
    5. 읽기/쓰기 혼합
      조회만이 아니라 할당 갱신 요청과 함께 테스트합니다.

    여기서 중요한 포인트! 한 번에 하나씩만 바꾸세요. 저도 예전에 노드 수, 동시성, 조건을 한꺼번에 바꿨다가 뭐가 원인인지 한참 못 찾았거든요.

    5-1. allocation_candidates 요청 예시

    curl -s -H "X-Auth-Token: ${TOKEN}" \
         -H "OpenStack-API-Version: placement 1.0" \
         "${PLACEMENT_URL}/allocation_candidates?resources=VCPU:4,MEMORY_MB:8192,DISK_GB:40" | jq .
    

    5-2. trait 조건 포함 예시

    curl -s -H "X-Auth-Token: ${TOKEN}" \
         -H "OpenStack-API-Version: placement 1.0" \
         "${PLACEMENT_URL}/allocation_candidates?resources=VCPU:4,MEMORY_MB:8192&required=CUSTOM_SSD" | jq .
    

    5-3. 결과 저장 예시

    for w in 1 5 10 20 30; do
      echo "workers=${w}" >> placement-bench.txt
      python3 placement_bench.py --workers ${w} --total 200 >> placement-bench.txt
      sleep 2
    done
    

    실제로 써보니까 중간중간 쿨다운을 조금 넣는 게 결과 해석에 편했습니다. 연속 폭격만 하면 캐시, DB 커넥션, 시스템 부하가 뒤섞여서 패턴이 흐려질 수 있거든요.

    OpenStack Placement 벤치마크 실행 과정 이미지

    터미널에서 Placement API 벤치마크를 실행하는 장면과 요청 흐름을 시각화한 이미지입니다.

    6. ⚠️ 실제로 겪은 문제와 트러블슈팅

    이 섹션은 진짜 중요합니다. 벤치마크는 돌렸는데 결과를 믿을 수 없는 경우가 생각보다 많거든요.

    6-1. 인증 토큰 만료

    장시간 테스트에서 은근 자주 나옵니다. 처음엔 성능 저하인 줄 알았는데, 알고 보니 중간부터 401이 섞이더라고요. 그래서 저는 토큰 재발급 루틴을 넣거나, 테스트 시간을 짧게 끊어서 돌립니다.

    6-2. DB가 먼저 병목이 되는 경우

    Placement 서비스만 보고 있으면 놓치기 쉽습니다. API 프로세스 CPU는 여유 있는데 응답 시간이 늘어난다면, DB 슬로우 쿼리(slow query)를 꼭 보셔야 합니다. 특히 후보 계산 관련 조회가 커지는 구간에서 차이가 보일 수 있습니다.

    6-3. 로그 레벨 때문에 성능이 왜곡되는 경우

    디버그 로그를 켜고 벤치마크 돌리면, 그 자체가 부하가 됩니다. 저도 “왜 오늘따라 이렇게 느리지?” 했다가 로그 설정부터 다시 봤던 적이 있습니다. 디버깅과 성능 측정은 분리하는 게 맞습니다.

    6-4. 캐시 효과로 첫 번째와 두 번째 결과가 다른 경우

    이건 꽤 흔합니다. 첫 실행과 반복 실행 결과를 구분해서 기록해두세요. 워밍업(warm-up) 구간을 따로 두는 이유가 있습니다.

    증상 의심 포인트 확인 방법
    응답 시간 급증 DB 병목 DB 모니터링, 슬로우 쿼리 로그 확인
    간헐적 실패 토큰 만료 또는 타임아웃 HTTP 상태 코드 분리 기록
    반복할수록 빨라짐 캐시 또는 워밍업 효과 초기 구간과 본 측정 구간 분리
    노드 수 증가 후 급격히 느려짐 후보 계산 복잡도 증가 조건별 시나리오 재실행

    7. 검증과 결과 해석: 숫자보다 패턴을 보세요

    벤치마크 결과를 볼 때 저는 보통 세 가지 질문을 던집니다.

    1. 동시성이 올라갈 때 지연 시간이 선형적으로 늘어나는가?
    2. 특정 조건(trait, aggregate)에서만 갑자기 악화되는가?
    3. Placement API 응답 시간과 실제 인스턴스 생성 지연이 같이 움직이는가?

    예를 들어 평균 응답 시간은 괜찮아 보여도, P99가 튄다면 운영 체감은 이미 나빠졌을 수 있습니다. 반대로 수치가 조금 높아도 일관되게 유지되면 운영상 더 다루기 쉽습니다. 결국 중요한 건 안정성 있는 자원 할당 성능입니다.

    제가 직접 해보니 결과 해석은 아래 순서가 제일 실용적이었습니다.

    • API 응답 시간 분포 확인
    • 오류율 확인
    • DB와 서비스 프로세스 리소스 사용량 확인
    • 실제 스케줄링 체감과 비교
    grep -E "avg|max|errors|workers" placement-bench.txt
    

    가능하다면 결과는 시각화해두세요. 작은 차이도 그래프로 보면 패턴이 보입니다. 특히 worker 수 증가에 따른 지연 변화는 선 그래프로 보면 바로 감이 오더라고요.

    OpenStack Placement 벤치마크 결과 대시보드 이미지

    응답 시간, 오류율, 동시성 변화가 한눈에 보이는 벤치마크 결과 대시보드 이미지입니다.

    8. 정리와 다음 단계

    OpenStack Placement 벤치마크는 단순히 API를 두드려보는 테스트가 아닙니다. 대규모 OpenStack 인프라에서 실제 스케줄링 여유치를 확인하고, 병목이 Placement 서비스 자체인지, DB인지, 아니면 상위 스케줄링 로직인지 구분하는 과정에 가깝습니다. 처음엔 좀 복잡해 보여도, 시나리오를 잘게 나누고 한 번에 하나씩 바꾸면 훨씬 명확해집니다.

    이번 글의 핵심만 다시 묶어보면 이렇습니다.

    • 평균값보다 분포를 보세요. 특히 꼬리 지연이 중요합니다.
    • 조회와 갱신을 분리해서도 보고, 섞어서도 보세요.
    • Placement만 보지 말고 DB와 실제 스케줄링 체감도 함께 보세요.
    • 결과 숫자보다 병목이 시작되는 패턴을 찾으세요.

    다음 글에서는 Nova Scheduler(노바 스케줄러)와 Placement 서비스의 상호작용을 조금 더 깊게 다뤄볼 예정입니다. 이전 글에서 다뤘던 API 응답 시간 분석 방법이 있다면 같이 보셔도 흐름이 잘 연결될 겁니다. 혹시 지금 운영 중인 환경에서 Placement 성능이 의심된다면, 오늘 소개한 최소 구성 벤치마크부터 먼저 돌려보세요. 생각보다 빨리 단서가 나옵니다. 드디어 됐다! 하는 순간이 오더라고요 🎉

    벤치마크 시나리오별 관찰 포인트와 튜닝 우선순위를 요약한 인포그래픽입니다.

    FAQ: 자주 묻는 질문

    Q1. Placement API만 빠르면 스케줄링도 무조건 빠른가요?

    그건 아닙니다. Placement는 중요한 구성요소지만 전부는 아니거든요. Nova Scheduler, MQ, DB, 하이퍼바이저 상태까지 같이 봐야 합니다.

    Q2. 작은 환경에서도 벤치마크가 의미가 있나요?

    네, 있습니다. 절대 수치보다 패턴을 보는 데 의미가 큽니다. 홈랩에서도 병목 시작 지점을 찾는 연습이 충분히 됩니다.

    Q3. 어느 수치가 정상인가요?

    환경마다 다르기 때문에 단일 기준을 말하기는 어렵습니다. 그래서 더더욱 동일 환경에서 조건별 상대 비교가 중요합니다.

    Q4. 튜닝은 어디부터 시작하면 될까요?

    제 경험상 무작정 서비스 파라미터부터 건드리기보다, 요청 패턴 분리, DB 관찰, 후보 계산이 무거운 조건 파악부터 하는 게 훨씬 효율적이었습니다.

  • [인프라] Crossplane 장애 사례로 배우는 멀티 클라우드 인프라 관리

    [인프라] Crossplane 장애 사례로 배우는 멀티 클라우드 인프라 관리

    [인프라] Crossplane 장애 사례로 배우는 멀티 클라우드 인프라 관리

    Crossplane 장애 사례를 한 번이라도 겪어보신 분들은 아실 겁니다. 처음엔 “쿠버네티스(Kubernetes, 컨테이너 오케스트레이션 플랫폼)처럼 리소스를 선언형으로 관리하면 인프라도 깔끔해지겠네?” 싶거든요. 저도 홈랩하고 업무 환경에서 멀티 클라우드 관리 구조를 정리하려고 Crossplane을 붙였었는데, 막상 운영에 들어가니까 컨트롤 플레인(Control Plane, 전체 상태를 조정하는 중앙 제어 계층) 특성 때문에 장애가 생각보다 교묘하게 터지더라고요. 특히 인프라스트럭처 코드(Infrastructure as Code, 코드로 인프라를 정의하는 방식)와 쿠버네티스 API 감각이 섞이면서 원인을 잘못 짚으면 복구가 더 늦어집니다.

    이번 글은 제품 소개보다는 troubleshooting 중심입니다. 제가 직접 해보니 Crossplane은 잘만 쓰면 멀티 클라우드 관리 복잡도를 꽤 줄여주는데, 장애가 났을 때는 “어디서 상태가 꼬였는지”를 읽는 눈이 정말 중요하더라고요. 그래서 오늘은 Crossplane 장애 사례를 바탕으로 어떤 식으로 문제가 드러났고 어떻게 풀어갔는지, 그리고 운영하면서 꼭 챙겨야 할 포인트를 정리해보겠습니다.

    Crossplane 장애 사례를 이해하기 위한 멀티 클라우드 관리 아키텍처 개요

    Crossplane이 여러 클라우드 리소스를 쿠버네티스 컨트롤 플레인으로 관리하는 전체 흐름을 보여주는 이미지입니다.

    1. Crossplane을 왜 멀티 클라우드 관리에 쓰는가

    쉽게 말해 Crossplane은 쿠버네티스 API로 외부 인프라를 다루게 해주는 도구입니다. AWS, GCP, Azure 같은 퍼블릭 클라우드 자원을 쿠버네티스 리소스처럼 선언하고, 원하는 상태(desired state)와 실제 상태(actual state)를 맞추도록 계속 reconcile(리컨실, 상태를 일치시키는 반복 제어)하는 구조죠.

    이게 왜 좋냐면요. 클라우드마다 콘솔도 다르고 권한 체계도 다르고 Terraform 상태 파일 관리도 따로 고민해야 하는데, Crossplane은 적어도 운영 관점에서 제어면을 하나로 모으는 효과가 있습니다. 특히 팀 단위로 표준화된 Composite Resource(복합 리소스)나 Claim(클레임, 사용자 요청 객체)을 만들어두면 개발팀은 세부 클라우드 차이를 몰라도 공통 인터페이스로 인프라를 요청할 수 있거든요.

    • 장점 1: 멀티 클라우드 관리 진입점이 쿠버네티스로 통일돼요.
    • 장점 2: 인프라스트럭처 코드와 GitOps 흐름을 연결하기 좋습니다.
    • 장점 3: 플랫폼 팀이 정책과 표준 구성을 감싸서 제공하기 좋습니다.

    반대로 단점도 분명합니다. Crossplane 자체가 또 하나의 컨트롤 플레인이기 때문에 장애 포인트가 사라지는 게 아니라 다른 계층으로 이동하는 느낌이 있어요. 저도 처음엔 이게 뭔가 싶었는데, 실제로 써보니까 “리소스를 만드는 일”보다 “상태를 해석하는 일”이 더 중요하더라고요.

    2. 핵심 개념 정리: Provider, Composition, Reconciliation

    Crossplane 장애 사례를 이해하려면 구조를 먼저 아주 간단히 잡고 가는 게 좋아요.

    개념 쉽게 말하면 운영 시 체크 포인트
    Provider 클라우드 API와 통신하는 드라이버 인증 정보, 권한, CRD 설치 상태
    Managed Resource 실제 클라우드 리소스와 매핑되는 객체 Ready 조건, 외부 이름, 이벤트
    Composition 여러 리소스를 묶는 설계도 패치, 참조, 필드 연결 오류
    Claim 사용자가 요청하는 추상화된 리소스 상위 상태는 정상인데 하위가 실패할 수 있음
    Reconciliation 원하는 상태로 계속 맞추는 루프 반복 에러, 드리프트, 재시도 패턴

    여기서 중요한 포인트! 겉으로 보이는 Claim이 멀쩡해 보여도 하위 Managed Resource가 실패 중일 수 있어요. 반대로 하위 리소스 하나가 계속 에러를 내면서 전체 Composition이 완료되지 않는 경우도 흔합니다. 저는 초반에 상위 객체만 보고 “왜 안 되지?” 하다가 삽질 좀 했습니다 ㅎㅎ

    3. 실전 구현: 기본 구성과 관찰 포인트

    아래 예시는 개념 설명용으로 단순화한 구조입니다. 특정 클라우드 벤더 기능을 깊게 파기보다는 Crossplane troubleshooting 흐름을 보는 데 집중하시면 됩니다.

    3-1. Provider와 인증 Secret 준비

    1. Crossplane과 Provider가 설치되어 있는지 확인하세요.
    2. 클라우드 인증 정보가 담긴 Secret(시크릿, 민감 정보 저장 객체)을 만듭니다.
    3. ProviderConfig(프로바이더 설정)가 Secret을 올바르게 참조하는지 봅시다.
    kubectl get pods -n crossplane-system
    kubectl get providers
    kubectl get providerconfigs
    kubectl get secrets -n crossplane-system

    제가 실제로 써보니까 첫 장애는 생각보다 단순했어요. 리소스 생성 로직이 아니라 인증 Secret 네임스페이스(namespace, 쿠버네티스 논리적 격리 단위)가 어긋나 있었거든요. 이벤트를 보기 전까지는 Composition 문제인 줄 알았습니다.

    apiVersion: v1
    kind: Secret
    metadata:
      name: cloud-creds
      namespace: crossplane-system
    type: Opaque
    stringData:
      creds: |
        {
          "example": "replace-with-real-credentials"
        }
    ---
    apiVersion: pkg.crossplane.io/v1
    kind: Provider
    metadata:
      name: example-provider
    spec:
      package: xpkg.example/provider
    ---
    apiVersion: example.crossplane.io/v1beta1
    kind: ProviderConfig
    metadata:
      name: default
    spec:
      credentials:
        source: Secret
        secretRef:
          namespace: crossplane-system
          name: cloud-creds
          key: creds

    3-2. Composite Resource와 Claim 정의

    플랫폼 팀이 표준 리소스를 만들 때는 보통 Composition을 씁니다. 예를 들어 네트워크, 데이터베이스, 스토리지를 조합해서 하나의 “애플리케이션용 환경”처럼 제공하는 식이죠.

    apiVersion: apiextensions.crossplane.io/v1
    kind: Composition
    metadata:
      name: xappenvs.platform.example.org
    spec:
      compositeTypeRef:
        apiVersion: platform.example.org/v1alpha1
        kind: XAppEnv
      resources:
        - name: bucket
          base:
            apiVersion: storage.example.crossplane.io/v1beta1
            kind: Bucket
            spec:
              forProvider:
                region: us-east-1
              providerConfigRef:
                name: default
          patches:
            - fromFieldPath: "spec.parameters.region"
              toFieldPath: "spec.forProvider.region"
    apiVersion: platform.example.org/v1alpha1
    kind: AppEnv
    metadata:
      name: demo-appenv
    spec:
      parameters:
        region: us-east-1

    여기서부터는 단순 생성보다 관찰이 중요해요.

    kubectl get appenv
    kubectl describe appenv demo-appenv
    kubectl get managed
    kubectl get events --sort-by=.metadata.creationTimestamp
    Crossplane 장애 사례 분석용 Claim과 Managed Resource 연결 구조

    Claim에서 Composition을 거쳐 실제 Managed Resource가 생성되는 연결 관계를 보여주는 구성도입니다.

    4. Crossplane 장애 사례 1: 리소스는 생성됐는데 Ready가 안 올라오는 경우

    이 사례는 꽤 자주 봐요. 클라우드 콘솔에서는 리소스가 보이는데 쿠버네티스 쪽 상태는 계속 Creating 또는 NotReady에 머무는 거죠. 처음엔 “분명 만들어졌는데 왜 실패지?” 싶었습니다.

    제가 겪었던 원인은 크게 세 가지였어요.

    • 외부 리소스 식별자(external-name) 불일치
    • Provider 권한 부족
    • 후속 조회 API 실패

    Crossplane은 생성만 하는 게 아니라 이후에도 상태를 조회하고 맞춰야 해요. 그래서 create 권한만 있고 read 또는 describe 계열 권한이 빠져 있으면 리소스는 생겨도 Ready 조건이 정상으로 못 올라올 수 있거든요.

    kubectl describe <managed-resource-kind> <resource-name>
    kubectl get <managed-resource-kind> <resource-name> -o yaml

    이때 꼭 볼 부분은 아래입니다.

    1. status.conditions: Ready, Synced 상태가 어떻게 찍히는지
    2. metadata.annotations: external-name 같은 외부 식별자
    3. Events: API 호출 실패 메시지

    실제로 써보니까 “리소스가 존재하니 성공”이라고 보면 안 되더라고요. Crossplane은 상태 일치가 끝나야 진짜 성공이에요.

    5. Crossplane 장애 사례 2: Composition 패치 오류로 엉뚱한 값이 들어간 경우

    이건 진짜 많이 헷갈려요. Claim에 값을 넣었는데 하위 리소스에 반영이 안 되거나 전혀 다른 필드로 들어가는 경우죠. 문법 에러가 아니라서 더 무섭습니다. YAML은 적용됐는데 결과가 이상하거든요.

    제가 처음 삽질했던 포인트는 fromFieldPath와 toFieldPath 오타였어요. 한 글자만 틀려도 조용히 의도와 다르게 흘러갈 수 있습니다. 그리고 일부 필드는 하위 리소스 스키마에 실제로 존재해야 하니까 Crossplane 문제처럼 보여도 사실은 CRD 필드 구조를 잘못 이해한 경우도 많아요.

    kubectl get composition
    kubectl describe composition xappenvs.platform.example.org
    kubectl get xr
    kubectl describe xr <composite-resource-name>

    제가 정리한 확인 순서는 이렇습니다.

    1. Claim의 spec 값이 기대한 형태인지 확인하세요.
    2. Composite Resource(XR)에 값이 전달됐는지 봅시다.
    3. Managed Resource spec에 최종 반영됐는지 확인해요.
    4. 필드 타입이 문자열인지 배열인지, 맵인지 다시 봅시다.

    근데 여기서 중요한 건 Crossplane은 선언형이라 “중간 단계”를 하나씩 따라가야 한다는 점이에요. Terraform처럼 plan 출력 하나 보고 감 잡는 방식과는 결이 좀 다르더라고요.

    5-1. 문제를 줄이는 운영 팁

    • Composition 변수명 규칙을 팀 내에서 고정하세요.
    • region, size, class 같은 공통 필드는 네이밍을 통일해요.
    • 복잡한 패치는 처음부터 크게 만들지 말고 작은 단위로 검증합니다.
    • 변경 후에는 테스트용 Claim을 바로 적용해 이벤트를 확인하세요.
    Crossplane 장애 사례를 추적하는 이벤트 로그 기반 트러블슈팅 장면

    Crossplane 리소스 이벤트와 상태 조건을 보면서 원인을 추적하는 troubleshooting 상황을 표현한 이미지입니다.

    6. Crossplane 장애 사례 3: 멀티 클라우드 관리 환경에서 권한과 한도 이슈가 섞여 터진 경우

    멀티 클라우드 관리가 어려운 이유는 에러 형태가 벤더마다 다르게 보인다는 데 있어요. 어떤 곳은 권한 부족이 명확하게 찍히고, 어떤 곳은 rate limit(요청 제한)이나 quota(할당량) 문제처럼 보이다가 결국 재시도만 반복하기도 하거든요.

    제가 겪은 케이스는 이랬습니다. 한 클라우드에서는 리소스가 잘 만들어졌는데 다른 쪽은 동일한 Claim 패턴으로 계속 실패했어요. 처음엔 Composition 차이인 줄 알았는데 알고 보니 Provider가 쓰는 계정의 권한 범위가 환경마다 달랐습니다. 즉, 코드가 아니라 운영 계정 표준화가 문제였던 거죠.

    증상 겉으로 보이는 현상 실제 원인 후보
    계속 Pending 상위 Claim만 오래 대기 하위 Managed Resource 생성 실패
    반복 재시도 이벤트가 주기적으로 누적 권한 부족, API 제한, 잘못된 참조
    일부만 생성 네트워크는 되고 DB는 실패 Composition 내 특정 리소스 설정 누락
    삭제 지연 오브젝트는 지웠는데 외부 리소스 잔존 finalizer, 외부 API 에러, 종속성 문제

    이런 상황에서는 쿠버네티스 내부만 보면 안 돼요. 클라우드 측 감사 로그나 API 에러도 같이 봐야 합니다. 컨트롤 플레인이 하나라고 해서 장애 원인까지 하나로 줄어드는 건 아니더라고요. 이건 정말 운영하면서 체감했습니다.

    7. ⚠️ 삭제가 안 되는 장애: Finalizer와 외부 리소스 정리 실패

    개인적으로 제일 식은땀 나는 건 삭제 문제였어요. 리소스를 지웠는데 오브젝트가 계속 Terminating 상태로 남아 있고 외부 클라우드 리소스도 깔끔하게 정리되지 않는 경우요. 비용도 문제고 나중에 이름 충돌이나 의존성 꼬임으로 이어지기도 합니다.

    Crossplane은 finalizer(파이널라이저, 삭제 전에 정리 작업을 보장하는 메커니즘)를 사용해서 외부 리소스를 정리한 뒤 객체를 제거해요. 따라서 외부 API 호출이 실패하거나 참조 관계가 꼬이면 삭제가 길어질 수 있습니다.

    kubectl get <managed-resource-kind> <resource-name> -o yaml
    kubectl describe <managed-resource-kind> <resource-name>
    kubectl get events --sort-by=.metadata.creationTimestamp

    여기서 제가 배운 건 무작정 finalizer를 건드리면 안 된다는 점이에요. 물론 정말 예외적인 복구 상황은 있겠지만 먼저 확인해야 합니다.

    1. 외부 리소스가 실제로 삭제 가능한 상태인지 봅시다.
    2. 연결된 종속 리소스가 남아 있는지 확인하세요.
    3. Provider 권한이 delete와 observe까지 포함하는지 다시 봐요.
    4. 이벤트 로그에서 반복되는 에러 메시지를 확인하세요.

    ⚠️ 운영 팁: 삭제 장애는 생성 장애보다 복구 비용이 커요. 그래서 테스트 환경에서 생성뿐 아니라 삭제 시나리오까지 꼭 검증해야 합니다. 저도 예전엔 만드는 데만 집중했었는데 실제로는 지우는 흐름이 더 중요하더라고요.

    8. 검증과 결과: 어디까지 자동화됐는지 확인하는 방법

    문제를 고치고 나면 “이제 됐다”로 끝내면 안 돼요. 다시 같은 Crossplane 장애 사례가 반복되는지 봐야 하거든요. 저는 아래 체크리스트로 검증합니다.

    1. Claim 생성부터 Ready까지 걸리는 흐름을 확인하세요.
    2. 하위 Managed Resource가 모두 Synced 상태인지 봅시다.
    3. 외부 클라우드 콘솔에서도 리소스 속성이 기대값과 같은지 확인해요.
    4. 삭제 테스트까지 수행하세요.
    5. 이벤트 로그에 경고가 남는지 다시 봅니다.
    kubectl get appenv
    kubectl get xr
    kubectl get managed
    kubectl get events --sort-by=.metadata.creationTimestamp

    제가 직접 해보니 결과가 눈에 보이게 달라졌어요. 장애가 완전히 사라진다기보다 문제가 생겨도 어디를 봐야 하는지 감이 생긴다는 게 커요. 이건 운영 피로도를 많이 줄여줍니다. 특히 멀티 클라우드 관리 환경에서는 “도구를 더 넣는 것”보다 “상태를 읽는 기준을 팀이 공유하는 것”이 훨씬 중요하더라고요.

    Crossplane 장애 사례 해결 후 안정화된 멀티 클라우드 관리 결과

    문제 해결 후 Crossplane 리소스들이 Ready 상태로 정렬되고 운영 지표가 안정된 결과를 보여주는 이미지입니다.

    9. 정리: Crossplane은 편한 도구가 아니라, 잘 설계해야 편해지는 도구입니다

    정리해보면 Crossplane은 멀티 클라우드 관리와 인프라스트럭처 코드 표준화에 꽤 강력해요. 다만 처음 붙일 때 “쿠버네티스로 클라우드를 다룬다”는 멋진 그림만 보면 안 돼요. 실제 운영에선 Provider 인증, Composition 패치, 상태 조건, 삭제 흐름, 권한 범위 같은 현실 이슈가 계속 튀어나오거든요.

    저도 처음엔 Crossplane 장애 사례를 겪을 때마다 도구 자체를 의심했었는데 실제로 써보니까 대부분은 관찰 포인트 부족이나 운영 표준 미정리에서 시작하더라고요. 드디어 됐다! 싶은 순간도 있었고, 반대로 “왜 어제 되던 게 오늘 안 되지?” 하면서 로그만 한참 본 날도 있었어요. 근데 그런 삽질이 쌓이니까 구조가 보이더군요.

    • 💡 기억할 점 1: 상위 Claim만 보지 말고 XR과 Managed Resource까지 따라가세요.
    • 💡 기억할 점 2: 이벤트와 조건(status.conditions)은 가장 먼저 봐야 합니다.
    • 💡 기억할 점 3: 생성 성공보다 삭제 성공까지 확인해야 진짜 운영 준비가 끝납니다.
    • 💡 기억할 점 4: 멀티 클라우드 관리는 도구보다 표준화가 먼저에요.

    혹시 지금 Crossplane 장애 사례 때문에 막혀 계신가요? 그러면 가장 먼저 객체 계층을 위에서 아래로, 그리고 이벤트를 시간순으로 보시는 걸 추천드립니다. 이 흐름만 익혀도 troubleshooting 속도가 꽤 빨라집니다. 이전 글에서 다뤘던 쿠버네티스 운영 체크리스트와도 연결되는 이야기고 다음 글에서는 Composition 설계를 어떻게 단순화하면 장애를 줄일 수 있는지 이어서 다뤄볼 예정입니다.

    장애 원인 파악 순서와 운영 체크포인트를 한눈에 정리한 요약 인포그래픽 이미지입니다.

  • [OpenStack] OpenStack Nova 성능 최적화: CPU 스케줄링 및 리소스 할당 분석

    [OpenStack] OpenStack Nova 성능 최적화: CPU 스케줄링 및 리소스 할당 분석

    [OpenStack] OpenStack Nova 성능 최적화: CPU 스케줄링 및 리소스 할당 분석

    OpenStack Nova 성능 최적화 이야기는 결국 운영하면서 한 번쯤 꼭 부딪히는 문제로 이어집니다. 가상 머신은 충분히 띄웠는데, 어떤 인스턴스는 반응이 빠르고 어떤 인스턴스는 같은 flavor(플레이버, 가상 머신 자원 정의)인데도 체감 성능이 들쭉날쭉하거든요. 저도 처음엔 “스토리지가 느린가?” 하고 엉뚱한 데를 먼저 봤었는데, 실제로 파고 들어가 보니 Nova CPU 스케줄링과 리소스 할당 방식이 병목의 핵심인 경우가 많았습니다. 특히 benchmark(벤치마크, 성능 측정) 결과를 비교할 때 CPU 오버커밋(overcommit, 실제 물리 자원보다 더 많이 논리 할당)과 NUMA(Non-Uniform Memory Access, 메모리 접근 거리가 다른 구조) 인식 여부가 결과를 꽤 크게 흔들더라고요.

    이번 글에서는 제가 홈랩과 테스트 환경에서 반복적으로 확인했던 관찰 포인트를 기준으로, OpenStack Nova 성능 최적화를 어디서부터 봐야 하는지 정리해보겠습니다. 숫자를 지어내거나 과장된 벤치마크를 보여드리기보다는, 무엇을 설정하고 무엇을 검증해야 재현 가능한 성능 분석이 되는지에 초점을 맞추겠습니다.

    Nova 스케줄러와 컴퓨트 노드, 하이퍼바이저, NUMA 토폴로지 관계를 한눈에 보여주는 개요 이미지입니다.

    1. 왜 OpenStack Nova 성능 최적화가 생각보다 까다로운가

    쉽게 말해 Nova는 “가상 머신을 어디에 올릴지”만 결정하는 도구가 아닙니다. CPU pinning(피닝, vCPU를 특정 pCPU에 고정), emulator thread(에뮬레이터 스레드), huge page(휴즈페이지, 큰 메모리 페이지), NUMA topology(토폴로지, 자원 배치 구조) 같은 요소가 다 엮여 있습니다. 그래서 표면적으로는 인스턴스 생성이 잘 되더라도, 실제 가상 머신 성능은 스케줄링 정책에 따라 크게 달라질 수 있습니다.

    제가 직접 해보니 가장 흔한 착각은 이겁니다. “vCPU 개수만 맞으면 성능도 비슷하겠지”라는 생각이요. 근데 현실은 그렇지 않더라고요. 같은 4 vCPU 인스턴스라도 어느 소켓(socket, CPU 패키지) 위에 배치됐는지, 이웃한 워크로드가 얼마나 시끄러운지(noisy neighbor, 자원 경쟁을 유발하는 인접 워크로드), CPU 공유 정책이 어떤지에 따라 결과가 달라집니다.

    • CPU overcommit ratio가 높으면 밀도는 올라가지만 지연 시간이 튈 수 있습니다.
    • Dedicated CPU policy를 쓰면 예측 가능성은 좋아지지만 수용량은 줄어듭니다.
    • NUMA mismatch가 나면 메모리 접근 비용이 늘어나 체감 성능이 떨어질 수 있습니다.
    • Benchmark 결과는 테스트 시간대와 이웃 인스턴스 상태에 따라서도 흔들립니다.

    2. Nova CPU 스케줄링 개념, 쉽게 말해 이런 구조입니다

    여기서 중요한 포인트! Nova CPU 스케줄링은 단순한 라운드로빈(round-robin, 순환 배치)이 아닙니다. 스케줄러는 호스트 필터(filter, 후보 선별 규칙)와 weighers(가중치 계산기)를 사용해서 컴퓨트 노드를 고릅니다. 그 다음 실제 하이퍼바이저 계층에서 vCPU와 pCPU 관계가 정해지죠.

    2-1. Shared CPU와 Dedicated CPU 차이

    구분 설명 장점 주의점
    Shared CPU 여러 VM이 물리 CPU 시간을 공유 집적도 높음 성능 편차 가능
    Dedicated CPU 특정 pCPU를 인스턴스에 고정 할당 예측 가능한 성능 운영 유연성 감소
    CPU Pinning vCPU를 지정 코어에 매핑 지터 감소 NUMA 설계 필요

    실제로 써보니까 데이터베이스나 NFV(Network Functions Virtualization, 네트워크 기능 가상화)처럼 지연 시간에 민감한 워크로드는 Shared CPU보다 Dedicated CPU 쪽이 훨씬 해석하기 편했습니다. 반대로 일반 웹 애플리케이션은 적당한 오버커밋이 더 경제적일 때가 많았고요.

    2-2. 리소스 할당에서 꼭 같이 봐야 할 요소

    • vCPU allocation ratio: 논리적으로 얼마나 더 많이 잡을지
    • RAM allocation ratio: 메모리 과할당 정책
    • Reserved host memory: 호스트 OS와 에이전트가 쓸 여유 공간
    • NUMA affinity: CPU와 메모리 지역성 유지
    • Huge pages: TLB 부담 감소에 유리

    저도 처음엔 헷갈렸는데, CPU만 최적화하면 끝이 아니더라고요. 메모리 배치가 틀어지면 CPU pinning을 해도 기대만큼 안 나오는 경우가 있습니다.

    3. 실전 기준선 만들기: benchmark 전에 먼저 해야 할 것

    OpenStack Nova 성능 최적화를 하겠다고 바로 설정부터 바꾸면 나중에 비교가 안 됩니다. 삽질 좀 했습니다 ㅎㅎ 결국 가장 먼저 해야 하는 건 기준선(baseline, 비교 기준)을 만드는 일입니다.

    1. 테스트 대상 flavor를 고정합니다.
    2. 테스트용 이미지와 커널 상태를 동일하게 맞춥니다.
    3. 동일한 시간대에 반복 실행합니다.
    4. 가능하면 noisy neighbor 영향을 줄인 별도 호스트를 씁니다.
    5. 인스턴스 내부 지표와 호스트 지표를 같이 수집합니다.

    제가 권장하는 최소 수집 항목은 아래 정도입니다.

    # Compute node
    lscpu
    numactl --hardware
    virsh vcpuinfo INSTANCE_DOMAIN
    virsh emulatorpin INSTANCE_DOMAIN
    virsh dumpxml INSTANCE_DOMAIN
    
    # Guest VM
    grep -E 'processor|physical id|core id' /proc/cpuinfo
    lscpu
    uptime
    mpstat -P ALL 1 5
    

    이 정도만 모아도 “스케줄링은 잘 됐는지”, “게스트가 기대한 토폴로지를 보고 있는지” 정도는 꽤 빨리 감이 옵니다.

    4. Nova CPU 스케줄링 설정 확인 포인트

    이제 본론입니다. Nova CPU 스케줄링과 리소스 할당을 볼 때 저는 보통 nova.conf의 CPU 정책부터 확인합니다. 환경마다 값은 다를 수 있지만, 아래와 같은 항목들이 자주 등장합니다.

    [DEFAULT]
    cpu_allocation_ratio=4.0
    ram_allocation_ratio=1.0
    reserved_host_memory_mb=4096
    
    [compute]
    cpu_shared_set=2-15
    cpu_dedicated_set=16-31
    
    [libvirt]
    virt_type=kvm
    emulator_threads_policy=share
    

    위 예시는 구조 설명용입니다. 특정 값이 정답이라는 뜻은 아닙니다. 다만 운영 의도는 분명해야 합니다. 예를 들어 cpu_shared_set와 cpu_dedicated_set를 섞어서 쓴다면, 어떤 워크로드를 공유형으로 둘지, 어떤 워크로드를 전용형으로 둘지 먼저 정해야 하거든요.

    중요한 건 설정 파일의 숫자보다 정책 일관성입니다. 한 호스트는 전용 CPU 정책, 다른 호스트는 공유 정책인데 같은 aggregate(집합, 스케줄링 그룹)로 묶여 있으면 benchmark 결과가 해석 불가능해질 수 있습니다.

    OpenStack Nova 성능 최적화 설정에서 CPU 정책과 NUMA 배치를 설명하는 이미지

    공유 CPU 세트, 전용 CPU 세트, 에뮬레이터 스레드 정책이 어떻게 나뉘는지 보여주는 설정 이미지입니다.

    5. 리소스 할당 전략: 워크로드별로 다르게 가져가야 합니다

    이 부분이 현업에서 진짜 중요합니다. 모든 인스턴스에 같은 정책을 적용하면 편하긴 한데, 성능과 밀도 둘 다 애매해지기 쉽습니다.

    5-1. 일반 웹/배치 워크로드

    • Shared CPU 기반 운영이 보통 효율적입니다.
    • 적절한 오버커밋으로 집적도를 높일 수 있습니다.
    • 대신 benchmark는 피크 시간과 비피크 시간을 나눠 봐야 합니다.

    5-2. DB, 메시지 큐, 지연 민감 서비스

    • Dedicated CPU 또는 CPU pinning 검토가 필요합니다.
    • NUMA 정렬과 huge page를 함께 보는 편이 좋습니다.
    • 가상 머신 성능 비교 시 평균값보다 tail latency(꼬리 지연)를 더 중시해야 합니다.

    5-3. 혼합 클러스터 운영 팁

    제 경험상 host aggregate(호스트 애그리게이트)와 flavor extra spec(플레이버 추가 속성)을 같이 쓰는 방식이 운영 설명력이 좋았습니다. 예를 들면 성능형 호스트 풀과 일반형 호스트 풀을 나누고, 성능형 flavor만 전용 CPU 정책으로 보내는 식이죠.

    openstack flavor set perf.large \
      --property hw:cpu_policy=dedicated \
      --property hw:mem_page_size=large
    
    openstack flavor set general.medium \
      --property hw:cpu_policy=shared
    

    이렇게 해두면 나중에 문제 생겼을 때 추적이 쉬워집니다. “왜 이 인스턴스는 빠르지?” 혹은 “왜 이건 느리지?”를 설명할 근거가 남거든요.

    6. ⚠️ 실제로 자주 겪는 트러블슈팅

    여기서는 제가 실제로 많이 부딪혔던 문제 위주로 적어보겠습니다. 문서만 보면 단순해 보이는데, 현장에서는 꼭 꼬입니다.

    6-1. Pinning은 했는데 성능이 기대보다 안 나오는 경우

    가장 먼저 NUMA 배치를 의심해보세요. vCPU는 한 소켓 쪽에 몰렸는데 메모리는 다른 NUMA 노드에서 잡히면 메모리 접근이 꼬일 수 있습니다. 이럴 때는 인스턴스 XML과 호스트 NUMA 정보를 같이 확인해야 합니다.

    virsh dumpxml INSTANCE_DOMAIN | grep -i -E 'numa|vcpu|cputune|emulatorpin'
    numactl --hardware
    

    6-2. 동일한 벤치마크인데 결과 편차가 큰 경우

    이건 noisy neighbor 가능성이 큽니다. 특히 공유형 CPU 정책에서는 배치 타이밍에 따라 차이가 꽤 납니다. 저도 처음엔 테스트 툴 문제인 줄 알았는데, 같은 호스트에 다른 VM이 CPU burst를 치고 있었더라고요.

    6-3. 호스트는 여유 있는데 스케줄링이 실패하는 경우

    Placement(플레이스먼트, 자원 추적 및 배치 서비스) 관점에서 자원 클래스(resource class, 자원 분류)나 trait(특성)이 안 맞는 경우가 있습니다. 숫자상 여유와 스케줄링 가능 여부는 다를 수 있습니다. 그래서 단순히 top만 보는 식으로는 원인 파악이 안 됩니다.

    6-4. 에뮬레이터 스레드가 의외의 병목이 되는 경우

    이거 놓치기 쉽습니다. 전용 CPU만 신경 쓰다가 emulator thread가 같은 코어에 얹혀서 변동성이 생기기도 하거든요. 성능 민감 워크로드라면 이 부분도 꼭 확인해보세요.

    OpenStack Nova 성능 최적화 트러블슈팅과 벤치마크 편차 분석 이미지

    vCPU pinning, NUMA 노드, noisy neighbor 분석 지표를 함께 보는 트러블슈팅 예시 이미지입니다.

    7. 검증은 이렇게 합니다: 가상 머신 성능 확인 절차

    설정 바꾸고 느낌상 빨라진 것 같다고 끝내면 안 됩니다. 검증 절차를 남겨야 다음 변경 때도 비교가 되거든요. 저는 보통 아래 순서로 봅니다.

    1. 호스트 토폴로지 확인
    2. 인스턴스 배치 정책 확인
    3. 게스트 내부 CPU 인식 상태 확인
    4. 동일 조건으로 benchmark 반복 실행
    5. 평균값보다 편차와 최저 구간을 함께 비교
    # Example benchmark flow inside guest
    stress-ng --cpu 4 --timeout 60s --metrics-brief
    sysbench cpu --threads=4 run
    

    여기서 조심할 점은, 특정 툴의 점수 자체보다 반복 실행 시 일관성입니다. OpenStack Nova 성능 최적화가 잘 된 환경은 최고점만 높은 게 아니라, 여러 번 돌려도 편차가 상대적으로 줄어드는 경우가 많았습니다.

    검증 항목 좋은 신호 경계 신호
    CPU 사용률 분포 예상 코어에 집중 임의 분산, 잦은 변동
    Benchmark 반복성 결과 편차 작음 실행마다 차이 큼
    NUMA 정렬 CPU/메모리 지역성 유지 원격 메모리 접근 증가
    호스트 안정성 예약 자원 충분 호스트 자체가 바쁨

    드디어 됐다! 싶었던 순간도 사실 이런 표를 정리한 뒤에야 왔습니다. 그냥 체감이 아니라, 왜 결과가 좋아졌는지 설명이 되어야 다음 운영 변경에도 자신이 생기더라고요.

    OpenStack Nova 성능 최적화 전후 가상 머신 성능 비교 대시보드 이미지

    반복 벤치마크 결과의 편차 감소와 CPU 배치 안정성을 비교하는 결과 시각화 이미지입니다.

    8. 정리와 다음 단계: 무엇부터 손대면 좋을까

    정리해보면 OpenStack Nova 성능 최적화는 결국 세 가지입니다. 정책을 분리하고, 배치를 검증하고, 결과를 반복 측정하는 것이죠. Nova CPU 스케줄링은 단순 옵션 튜닝이 아니라, 워크로드 성격에 맞는 리소스 할당 전략을 설계하는 일에 가깝습니다.

    • 일반 워크로드는 공유 정책과 적절한 오버커밋으로 효율을 봅니다.
    • 민감한 워크로드는 전용 CPU, NUMA 정렬, 메모리 정책을 같이 봅니다.
    • 가상 머신 성능 평가는 평균 점수보다 재현성과 편차를 함께 봐야 합니다.
    • 벤치마크는 반드시 배치 정보와 같이 기록해야 의미가 있습니다.

    혹시 이런 경험 있으신가요? 분명 스펙은 같은데 어떤 VM만 유독 느린 상황이요. 그런 경우라면 애플리케이션을 의심하기 전에 CPU 정책과 배치 상태부터 보시는 걸 추천드립니다. 생각보다 거기서 답이 나오는 경우가 많거든요.

    다음 글에서는 Placement와 flavor extra spec을 조금 더 깊게 들어가서, 스케줄링 의도를 어떻게 코드처럼 관리할지 다뤄볼 예정입니다. 이전 글에서 다뤘던 가상화 호스트 기본 점검 내용과 함께 보시면 흐름이 더 잘 잡히실 겁니다.

    OpenStack Nova 성능 최적화 운영 체크리스트와 CPU 정책 요약 이미지

    공유 CPU와 전용 CPU 선택 기준, NUMA 체크포인트, 검증 절차를 요약한 인포그래픽 이미지입니다.

    자주 묻는 질문

    Q1. CPU overcommit을 무조건 낮추면 성능이 좋아지나요?

    항상 그렇진 않습니다. 집적도가 너무 낮아져 운영 효율이 떨어질 수 있고, 워크로드 특성상 공유 정책으로도 충분한 경우가 있습니다.

    Q2. Nova CPU 스케줄링만 조정하면 끝인가요?

    아닙니다. libvirt 설정, NUMA 구조, 메모리 정책, 게스트 OS 튜닝까지 함께 봐야 합니다.

    Q3. benchmark 결과가 매번 다르면 무엇부터 확인해야 하나요?

    테스트 시간대, noisy neighbor, 배치 호스트, NUMA 정렬, 에뮬레이터 스레드 정책 순으로 점검해보시면 됩니다.

  • [OpenStack] OpenStack Keystone 인증 시스템, 1년 운영 후기 및 보안 강화 팁

    [OpenStack] OpenStack Keystone 인증 시스템, 1년 운영 후기 및 보안 강화 팁

    [OpenStack] OpenStack Keystone 운영 후기와 보안 강화 팁

    OpenStack Keystone 운영 후기 이야기를 해보려고 합니다. 클라우드를 처음 올릴 때는 Nova(노바, 컴퓨트), Neutron(뉴트론, 네트워크), Cinder(신더, 블록 스토리지) 쪽이 더 눈에 잘 들어오는데요. 실제로 1년 정도 굴려보면 장애의 시작점이 생각보다 Keystone(키스톤, 인증 및 권한 관리 서비스)인 경우가 꽤 많습니다. 로그인은 되는데 토큰이 안 맞는다든지, 서비스 계정 권한이 꼬인다든지, 외부에 노출된 엔드포인트(endpoint, 서비스 접속 지점) 관리가 애매해진다든지요. 저도 처음엔 이게 뭔가 싶었는데, 막상 운영해보니 Keystone은 단순한 로그인 서버가 아니라 OpenStack 전체의 신뢰 기준점이더라고요.

    특히 홈랩과 사내 테스트 환경을 같이 운영하면서 느낀 건, Keystone 보안과 인증 시스템 관리가 느슨하면 나머지 서비스도 같이 흔들린다는 점이었습니다. 이번 글에서는 제가 직접 겪은 OpenStack Keystone 운영 후기와 함께, 실제로 효과 있었던 OpenStack 보안 강화 팁을 정리해보겠습니다.

    OpenStack Keystone 운영 후기를 설명하는 전체 인증 아키텍처 다이어그램

    Keystone이 사용자, 프로젝트, 역할, 토큰, 각 OpenStack 서비스 사이에서 어떻게 동작하는지 보여주는 전체 구조 예시입니다.

    1. 왜 Keystone이 운영에서 그렇게 중요할까요?

    쉽게 말해 Keystone은 누가, 어디까지, 어떤 방식으로 접근할 수 있는지를 판단하는 관문입니다. 사용자는 인증(authentication, 본인 확인)을 받고, 그 다음 권한 부여(authorization, 접근 허용 범위 결정)를 통해 프로젝트(project, 테넌트 성격의 작업 공간)와 역할(role, 권한 묶음)에 맞는 작업만 하게 됩니다.

    문제는 여기서 한 번 꼬이면 영향 범위가 넓다는 겁니다. 예를 들어:

    • 대시보드 Horizon(호라이즌) 로그인 실패
    • CLI에서 토큰 발급 실패
    • Nova나 Glance 같은 서비스 간 인증 실패
    • 내부 서비스 계정 비밀번호 만료 또는 잘못된 role 할당
    • 엔드포인트 URL 혼선으로 internal/public 트래픽 분리 실패

    제가 실제로 써보니까, 컴퓨트 노드 한 대 죽는 것보다 Keystone 설정 하나 잘못 들어간 게 복구 피로도가 더 높을 때도 있었습니다. 왜냐하면 장애가 여러 서비스에 퍼져 보이거든요. 겉으로는 Nova 오류처럼 보여도 원인은 Keystone 토큰 검증 실패인 경우가 있었습니다.

    2. Keystone 핵심 개념, 쉽게 말해 이렇게 이해하면 됩니다

    처음 접하시면 용어가 좀 많습니다. 저도 처음엔 헷갈렸는데, 아래처럼 묶어서 이해하니까 훨씬 편하더라고요.

    개념 영문 쉽게 말한 뜻
    사용자 User OpenStack에 로그인하거나 API를 호출하는 주체
    프로젝트 Project 리소스를 묶는 작업 공간
    도메인 Domain 사용자와 프로젝트를 더 큰 범위로 구분하는 단위
    역할 Role 허용된 권한 세트
    토큰 Token 인증 후 발급되는 일시적 접근 증표
    엔드포인트 Endpoint 서비스 API가 열려 있는 주소
    카탈로그 Service Catalog 사용 가능한 서비스 목록과 접속 정보

    여기서 중요한 포인트가 있습니다. Keystone 보안은 단순히 비밀번호를 복잡하게 만드는 수준이 아닙니다. 토큰 수명, 키 회전(rotation), 서비스 계정 분리, TLS(전송 구간 암호화), 로그 감사(audit)까지 같이 봐야 합니다.

    토큰 방식은 왜 신경 써야 할까요?

    운영하면서 체감이 컸던 부분이 토큰입니다. 예전에는 UUID 토큰 이야기도 많이 나왔지만, 실제 운영에선 Fernet(퍼넷, 대칭키 기반의 서명된 토큰 포맷) 쪽이 관리 포인트를 줄이는 데 도움이 됐습니다. DB 조회 부담이 줄고, 토큰 검증 구조가 단순해지는 장점이 있거든요. 다만 그 대신 Fernet 키 관리를 제대로 해야 합니다. 키를 안 돌리면 보안상 찜찜하고, 반대로 무작정 돌리면 토큰 검증 이슈가 생길 수 있습니다.

    3. 제가 1년 운영하면서 정착한 기본 구성

    제 기준의 안정적인 기본 원칙은 아래였습니다.

    1. 관리용 admin 접근과 일반 사용자 접근을 논리적으로 분리합니다.
    2. TLS(전송 계층 보안)는 반드시 앞단 프록시나 로드밸런서에서라도 적용합니다.
    3. 서비스 계정(service account)은 사람 계정과 섞지 않습니다.
    4. 권한은 최소 권한(least privilege)으로 시작합니다.
    5. Fernet 키 회전 주기와 시간 동기화(NTP/Chrony)를 같이 관리합니다.
    6. 관리 네트워크와 외부 노출 네트워크를 분리합니다.

    말은 쉬운데, 실제 운영에선 이걸 꾸준히 지키는 게 어렵습니다. 특히 테스트 환경에서 급하게 계정 하나 더 만들고, role 하나 더 붙이고, endpoint를 임시로 public에 노출해두면 나중에 꼭 빚처럼 돌아오더라고요. 삽질 좀 했습니다 ㅎㅎ

    4. 실전 구현: Keystone 기본 설정과 보안 초기값

    아래 예시는 배포판이나 패키지 구성에 따라 경로가 조금 다를 수 있지만, 큰 흐름은 비슷합니다. 핵심은 Fernet 초기화, 부트스트랩, 서비스 확인입니다.

    4-1. Fernet 키 초기화

    keystone-manage fernet_setup --keystone-user keystone --keystone-group keystone
    keystone-manage credential_setup --keystone-user keystone --keystone-group keystone

    이 단계는 토큰과 자격 증명 암호화의 기반이 됩니다. 여기서 파일 권한이 어긋나면 나중에 토큰 검증에서 조용히 말썽을 부리기도 합니다. 저는 한 번 소유권이 꼬여서 Apache(아파치, 웹 서버) 프로세스는 살아 있는데 Keystone 응답만 이상하게 나오는 상황을 겪었습니다.

    4-2. bootstrap 실행

    keystone-manage bootstrap \
      --bootstrap-password 'CHANGE_ME_STRONG_PASSWORD' \
      --bootstrap-admin-url https://keystone.example.internal:5000/v3/ \
      --bootstrap-internal-url https://keystone.example.internal:5000/v3/ \
      --bootstrap-public-url https://keystone.example.com:5000/v3/ \
      --bootstrap-region-id RegionOne

    여기서 public/internal URL을 어떻게 나눌지 미리 정해두는 게 정말 중요합니다. 처음엔 대충 같은 주소 넣어도 되지 않나 싶었는데, 운영 환경이 커지면 인증 시스템 관리가 바로 복잡해집니다. 외부 접속용, 내부 서비스 통신용, 운영자 점검용을 구분해두면 나중에 정책 적용이 쉬워집니다.

    Keystone 보안과 인증 시스템 관리에 필요한 엔드포인트 분리 구성 다이어그램

    public, internal, admin 성격의 접근 경로를 나누고 Fernet 키와 서비스 계정을 관리하는 설정 흐름 예시입니다.

    4-3. 기본 환경 변수 설정

    export OS_AUTH_URL=https://keystone.example.com:5000/v3
    export OS_USERNAME=admin
    export OS_PASSWORD='CHANGE_ME_STRONG_PASSWORD'
    export OS_PROJECT_NAME=admin
    export OS_USER_DOMAIN_NAME=Default
    export OS_PROJECT_DOMAIN_NAME=Default
    export OS_IDENTITY_API_VERSION=3

    운영 중에는 shell history(셸 기록)에 민감 정보가 남지 않도록 조심하셔야 합니다. 저는 실제로 테스트 서버에서 급하게 붙어 작업하다가 환경 변수 관리가 느슨해진 적이 있었는데요. 이후에는 별도 openrc 파일 권한 제한과 비밀값 주입 방식 통일로 정리했습니다.

    4-4. 프로젝트, 사용자, 역할 생성

    openstack project create --domain default --description "Service Project" service
    openstack project create --domain default --description "Operations Project" ops
    openstack user create --domain default --password 'STRONG_SERVICE_PASSWORD' glance
    openstack role create reader
    openstack role add --project service --user glance admin

    여기서 흔히 하는 실수가 사람 계정과 서비스 계정을 같은 패턴으로 관리하는 겁니다. 저는 초반에 편하다고 운영자 계정 하나로 이것저것 테스트했었는데, 나중에 로그를 보면 누가 무슨 작업을 했는지 흐려집니다. 사람은 사람 계정, 서비스는 서비스 계정. 이 원칙이 감사(audit) 대응에도 좋았습니다.

    4-5. 설정 파일에서 꼭 보는 항목

    [token]
    provider = fernet
    
    [cache]
    enabled = true
    
    [security_compliance]
    lockout_failure_attempts = 5
    lockout_duration = 1800
    unique_last_password_count = 5
    password_expires_days = 90

    배포판과 릴리스에 따라 지원 항목 차이가 있을 수 있으니, 실제 적용 전에는 사용 중인 문서를 꼭 확인하셔야 합니다. 다만 운영 원칙은 분명합니다. 토큰 방식 명확화, 캐시 전략 점검, 로그인 실패 잠금, 비밀번호 정책은 반드시 챙기셔야 합니다.

    5. Keystone 보안, 실제로 효과 있었던 강화 팁

    이 섹션은 정말 체감 위주입니다. 스펙 표보다 운영 안정성에 더 직접적인 것들만 추렸습니다.

    5-1. TLS 적용은 선택이 아니었습니다

    내부망이라 괜찮겠지 싶을 수 있는데, 실제로는 내부망이 제일 오래 방치됩니다. 프록시 계층에서라도 TLS를 적용하고, 인증서 갱신 주기를 운영 절차에 넣는 게 좋습니다. 특히 Horizon과 Keystone이 같이 얽혀 있을 때 mixed content나 redirect 문제도 정리되더라고요.

    5-2. Fernet 키 회전 자동화

    keystone-manage fernet_rotate

    이 명령 자체는 단순합니다. 문제는 언제, 어떤 순서로, 몇 개의 키를 유지할지입니다. 제가 운영하면서 얻은 교훈은, 키 회전은 cron만 걸어두고 끝낼 일이 아니라는 점이었습니다. 여러 컨트롤러 노드가 있다면 키 동기화 순서와 배포 타이밍을 같이 관리해야 합니다.

    5-3. 시간 동기화는 사소해 보여도 필수입니다

    토큰이 유효한데도 간헐적으로 인증이 튀는 경우가 있었습니다. 처음엔 Keystone 자체 문제인 줄 알았는데, 알고 보니 노드 간 시간이 조금씩 어긋나 있었어요. 이거 진짜 흔합니다. chrony(크로니, 시간 동기화 도구)나 NTP 상태를 같이 점검하세요.

    5-4. 서비스 카탈로그와 엔드포인트 정리

    OpenStack 보안을 이야기할 때 의외로 자주 놓치는 게 endpoint 정리입니다. 안 쓰는 public endpoint를 남겨두면 관리 부담이 생기고, 내부 서비스가 public URL을 타기 시작하면 네트워크 정책도 꼬입니다.

    openstack endpoint list
    openstack service list

    분기마다 한 번씩이라도 정리해보세요. 저는 운영 6개월 차쯤부터 정기 점검 항목으로 넣었습니다.

    5-5. Application Credential 적극 활용

    자동화 스크립트나 CI에서 사람 계정 비밀번호를 직접 쓰는 건 피하시는 게 좋습니다. 가능하면 Application Credential(애플리케이션 자격 증명) 같은 방식을 고려해 분리하세요. 스크립트 유출 범위를 줄이는 데 도움이 됩니다.

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

    여기서부터는 OpenStack Keystone 운영 후기다운 내용입니다. 책에는 짧게 나오는데, 현장에서는 꽤 귀찮은 문제들입니다.

    6-1. 토큰이 가끔만 실패하는 문제

    증상: 어떤 요청은 되고, 어떤 요청은 401이 나옵니다.

    원인: 시간 동기화 불일치, 캐시 반영 지연, 다중 노드 환경에서 키 동기화 문제.

    해결:

    1. 컨트롤러 노드 시간 차이 확인
    2. Fernet 키 디렉터리 소유권과 동기화 상태 확인
    3. 웹 서버 재로드 전후 캐시 반영 점검

    처음엔 네트워크 문제로 의심했는데, 결국 시계가 문제였던 적이 있었습니다. 정말 허탈하더라고요.

    6-2. Horizon 로그인은 되는데 일부 메뉴가 비정상인 문제

    증상: 로그인은 되는데 프로젝트 리소스가 안 보이거나 API 호출이 실패합니다.

    원인: role 할당 누락, project scope 설정 문제, endpoint 카탈로그 불일치.

    해결:

    openstack token issue
    openstack role assignment list --user admin --names
    openstack catalog list

    이 세 개를 같이 보면 방향이 잡히는 경우가 많았습니다. 특히 토큰 발급은 되는데 scope가 예상과 다르면, 사용자 입장에서는 그냥 고장처럼 보이거든요.

    6-3. 서비스 계정 비밀번호 변경 후 연쇄 장애

    Glance, Nova, Neutron 같은 서비스 설정 파일에 들어간 자격 증명을 한쪽만 바꾸면 바로 연쇄적으로 인증 실패가 납니다. 이건 진짜 조심하셔야 합니다. 제가 한 번 maintenance window(점검 시간) 없이 낮에 바꿨다가 로그 쫓아다니느라 고생했습니다.

    팁: 비밀번호 변경은 다음 순서를 추천합니다.

    1. Keystone에서 사용자 비밀번호 변경
    2. 각 서비스 설정 파일 동시 반영
    3. 서비스 재시작 또는 재로드
    4. 토큰 발급과 서비스 API 호출 검증

    7. 검증: 운영 상태는 이렇게 확인했습니다

    설정을 바꿨으면 반드시 검증해야 합니다. 저는 아래 체크리스트를 반복적으로 썼습니다.

    1. 관리자 토큰 발급이 되는지 확인
    2. 일반 사용자 계정도 동일하게 인증되는지 확인
    3. 서비스 계정으로 API 인증이 되는지 확인
    4. public/internal endpoint가 의도대로 동작하는지 확인
    5. 로그에 경고나 반복 실패가 없는지 확인
    openstack token issue
    openstack endpoint list
    openstack catalog list
    openstack user list
    journalctl -u apache2 -n 100 --no-pager

    배포판에 따라 웹 서버 서비스명이 `httpd`일 수도 있습니다. 중요한 건 CLI 성공 여부와 서버 로그를 같이 보는 겁니다. 둘 중 하나만 보면 놓치는 게 생깁니다.

    OpenStack Keystone 운영 후기의 검증 단계에서 사용하는 토큰 발급 및 카탈로그 확인 이미지

    토큰 발급, 서비스 카탈로그, 엔드포인트 점검 결과를 한눈에 확인하는 운영 검증 예시입니다.

    1년 정도 운영하고 나니 확실히 느낀 점이 있습니다. Keystone은 화려한 서비스는 아니지만, 안정적으로 굴러가면 전체 OpenStack이 훨씬 단정해집니다. 반대로 인증 시스템 관리가 흐트러지면 장애 분석 시간도 길어지고, 보안 리스크도 조용히 쌓입니다.

    8. Fernet, 계정 관리, 엔드포인트 운영 기준 정리

    항목 권장 운영 방식 이유
    토큰 Fernet 사용, 키 회전 자동화 검증 구조 단순화와 보안성 확보
    계정 사람/서비스 계정 분리 감사 추적과 사고 범위 축소
    권한 최소 권한 부여 오남용 방지
    엔드포인트 public/internal 역할 분리 네트워크 정책 단순화
    시간 모든 노드 동기화 토큰 오류 예방
    전송 보안 TLS 적용 자격 증명 노출 방지

    9. 마무리: Keystone은 결국 운영 습관 싸움입니다

    정리해보면, OpenStack Keystone 운영 후기에서 가장 크게 남은 건 기술보다 습관이었습니다. 처음엔 설정만 맞으면 끝이라고 생각했는데, 실제로는 키 회전, 시간 동기화, 계정 분리, 엔드포인트 정리, 로그 확인을 꾸준히 반복해야 하더라고요. 드디어 됐다 싶어도 몇 달 지나면 또 운영 기준이 흐트러집니다. 그래서 체크리스트와 주기 작업이 중요합니다.

    혹시 지금 Keystone을 막 구축하셨거나, 이미 돌아가는 환경인데 어딘가 불안하신가요? 그럴 땐 거창한 개편보다 아래 5가지만 먼저 점검해보세요.

    • Fernet 키 회전이 되고 있는가
    • 시간 동기화가 정확한가
    • 서비스 계정과 사람 계정이 분리되어 있는가
    • 안 쓰는 public endpoint가 남아 있지 않은가
    • 토큰 발급 검증을 정기적으로 하고 있는가

    이 다섯 가지만 잡아도 체감이 꽤 큽니다. 다음 글에서는 Horizon과 Keystone 연동 시 세션/쿠키 문제, 또는 OpenStack 보안 관점에서 서비스 계정 순환(rotation) 자동화를 다뤄볼 예정입니다. 이전 글에서 다룬 네트워크 분리와 reverse proxy 구성이 있다면 같이 참고하시면 흐름이 더 잘 잡히실 겁니다.

    Fernet 키 회전, 최소 권한, TLS, 시간 동기화, 엔드포인트 정리를 요약한 운영 체크리스트입니다.

    자주 묻는 질문

    Keystone만 안정적이면 나머지 OpenStack 서비스도 괜찮을까요?

    완전히 그렇진 않지만, 최소한 인증 관련 장애 분석 범위를 크게 줄일 수 있습니다. 운영 체감상 출발점이 훨씬 깔끔해집니다.

    OpenStack 보안에서 가장 먼저 손댈 부분은 뭔가요?

    제가 직접 해보니 TLS, 계정 분리, Fernet 키 회전, 시간 동기화 이 네 가지가 우선순위가 높았습니다.

    인증 시스템 관리는 얼마나 자주 점검하면 좋을까요?

    주간 점검으로 토큰 발급과 로그 확인, 월간 점검으로 endpoint/role/서비스 계정 상태를 보는 식이 현실적이었습니다.

  • [Cloud] AWX를 활용한 클라우드 인프라 자동화 1년 회고: 운영 효율성과 도전 과제

    [Cloud] AWX를 활용한 클라우드 인프라 자동화 1년 회고: 운영 효율성과 도전 과제

    AWX 클라우드 인프라 자동화 1년 회고: 운영 효율성과 도전 과제

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 제가 지난 1년간 AWX를 활용한 클라우드 인프라 자동화를 경험하며 느꼈던 점들을 솔직하게 회고해 보려고 해요. 클라우드 환경이 복잡해질수록 수동 작업의 한계를 뼈저리게 느끼곤 하잖아요? 저도 처음엔 정말 막막했습니다. 혹시 여러분도 이런 고민 해보신 적 있으신가요? “수동 작업 줄이고 싶은데, Ansible 플레이북은 많아지고 관리도 어렵고…” 딱 제가 그랬거든요. 그래서 이 녀석, AWX를 도입하게 됐습니다.

    AWX 클라우드 자동화 아키텍처 개요 다이어그램

    AWX 클라우드 자동화 아키텍처 개요 다이어그램

    AWX, 너는 누구니? (핵심 개념)

    자, 그럼 AWX가 뭔지부터 간단히 짚고 넘어갈게요. AWX는 Ansible Tower의 오픈소스 버전이라고 생각하시면 됩니다. 쉽게 말해, Ansible(앤서블) 플레이북(Playbook)을 웹 UI(User Interface)로 관리하고 실행할 수 있게 해주는 도구예요. 단순히 플레이북 실행뿐만 아니라, 인벤토리(Inventory, 관리 대상 호스트 목록), 크리덴셜(Credential, 자격 증명), 프로젝트(Project, 플레이북 저장소), 작업 템플릿(Job Template, 실행 단위), 그리고 워크플로우(Workflow, 여러 작업 템플릿 연결)까지 체계적으로 관리할 수 있게 해줍니다. 💡 특히 팀 단위로 Ansible을 활용할 때, 누가 어떤 작업을 언제 실행했는지, 성공 여부는 어땠는지 한눈에 파악할 수 있어서 정말 편하더라고요.

    AWX 도입 및 초기 셋업 경험

    저도 처음엔 “이거 설치부터 만만치 않겠는데?” 하고 살짝 긴장했었는데, 생각보다 어렵지 않았어요. 저는 주로 Docker Compose(도커 컴포즈)를 이용해서 컨테이너 환경에 AWX를 배포했습니다. 공식 문서에 잘 나와 있어서 따라 하는 데 큰 무리는 없었죠. 하지만 역시 초반 삽질은 피해 갈 수 없더라고요. 😅

    • 인벤토리 구성: 클라우드 환경이다 보니, 정적인 인벤토리보다는 동적 인벤토리(Dynamic Inventory) 스크립트를 활용해야 했습니다. AWS EC2 같은 경우는 플러그인을 활용해서 현재 떠 있는 인스턴스 목록을 자동으로 가져오게 설정했죠. 처음엔 필터링 규칙 잡는 게 좀 헷갈렸는데, 몇 번 해보니 감이 오더라고요.
    • 자격 증명(Credential) 관리: SSH 키나 클라우드 API 키 같은 민감 정보를 안전하게 관리하는 게 중요했습니다. AWX의 크리덴셜 기능을 사용해서 암호화된 형태로 저장하고, 필요한 작업 템플릿에만 연결해서 사용했습니다.
    • Git(깃) 연동: 저희 팀은 모든 Ansible 플레이북을 Git 저장소에 관리하고 있었거든요. AWX 프로젝트와 Git을 연동해서, Git에 푸시(Push)만 하면 AWX가 자동으로 최신 플레이북을 가져오도록 설정했습니다. 이 부분이 정말 편리했어요!

    클라우드 인프라 자동화, 이렇게 해봤습니다

    AWX를 도입하고 나서 저희 팀의 클라우드 인프라 자동화는 날개를 달았습니다. 지난 1년간 다양한 작업들을 AWX를 통해 자동화했는데요, 몇 가지 대표적인 사례를 소개해 드릴게요.

    1. EC2(혹은 VM) 프로비저닝 및 초기 설정 자동화: 새로운 서버를 띄울 때마다 수동으로 OS 설정, 보안 설정, 기본 에이전트 설치 등을 하는 게 큰일이었거든요. AWX에 작업 템플릿을 만들어두고, 인스턴스 ID만 입력하면 모든 초기 설정이 자동으로 완료되도록 했습니다.
    2. 보안 그룹(Security Group) 변경 자동화: 개발자들이 특정 IP를 열어달라고 요청할 때마다 일일이 AWS 콘솔에 들어가서 작업했었는데, 이젠 AWX 작업 템플릿에 IP와 설명을 입력하고 실행하면 끝! 휴먼 에러도 줄고, 작업 속도도 훨씬 빨라졌죠.
    3. 로드밸런서(Load Balancer) 대상 그룹(Target Group) 등록/해제: 배포 시 서비스 인스턴스를 로드밸런서에서 빼고 다시 넣는 작업을 AWX 워크플로우로 묶어 자동화했습니다.
    4. 주기적인 서버 패치 및 재부팅 관리: 매달 특정 요일에 정해진 서버 그룹에 OS 패치를 적용하고 필요시 재부팅하는 작업을 스케줄러(Scheduler) 기능을 활용해서 자동화했습니다.

    간단한 Ansible 플레이북 예시를 하나 보여드릴게요. 특정 EC2 인스턴스의 특정 포트를 보안 그룹에 추가하는 플레이북입니다.

    - name: Add a specific IP to security group
      hosts: localhost
      connection: local
      gather_facts: no
    
      vars:
        instance_id: 'i-xxxxxxxxxxxxxxxxx'
        port_to_open: 8080
        ip_to_allow: '192.168.1.1/32'
        description: 'Allow access from specific IP'
    
      tasks:
        - name: Get security group ID from instance
          amazon.aws.ec2_instance_info:
            instance_ids: "{{ instance_id }}"
          register: ec2_info
    
        - name: Set security group ID fact
          set_fact:
            sg_id: "{{ ec2_info.instances[0].security_groups[0].group_id }}"
    
        - name: Authorize ingress rule
          amazon.aws.ec2_group:
            group_id: "{{ sg_id }}"
            region: ap-northeast-2
            rules:
              - proto: tcp
                ports:
                  - "{{ port_to_open }}"
                cidr_ip: "{{ ip_to_allow }}"
                rule_desc: "{{ description }}"
            purge_rules: no
    

    이런 플레이북을 AWX에 등록하고, instance_id, port_to_open, ip_to_allow 같은 변수들만 작업 템플릿에서 설정하면 되는 거죠. 정말 편하더라고요! 🚀

    AWX 작업 템플릿 설정 화면 예시

    AWX 작업 템플릿 설정 화면 예시

    1년 회고: AWX가 가져온 운영 효율성

    지난 1년간 AWX를 운영하면서 가장 크게 체감한 건 바로 운영 효율성의 극대화였습니다. 🎉

    • 수동 작업 시간 획기적으로 감소: 과거에 1시간씩 걸리던 수동 설정 작업들이 이제는 AWX 작업 템플릿 한 번 클릭으로 5분 안에 끝나는 마법을 경험했습니다.
    • 휴먼 에러(Human Error) 감소: 정형화된 작업 템플릿을 사용하니, 사람이 실수할 여지가 거의 없어졌습니다. 특히 야간 작업이나 긴급 상황에서 빛을 발하더라고요.
    • 작업 표준화 및 가시성 확보: 모든 자동화 작업이 AWX를 통해 이루어지면서, 어떤 팀원이든 동일한 절차로 작업을 수행할 수 있게 되었고, 대시보드에서 모든 작업 이력을 한눈에 볼 수 있게 되었습니다.
    • 팀원 간 협업 용이: Ansible 플레이북을 공유하고, 각자의 권한에 맞춰 작업 템플릿을 실행할 수 있으니 팀원 간의 협업이 훨씬 부드러워졌어요.

    AWX 대시보드를 보면 성공률이 압도적으로 높은 걸 확인할 수 있는데, 정말 뿌듯하더라고요. 👍

    AWX 대시보드 자동화 작업 성공률 통계

    AWX 대시보드 자동화 작업 성공률 통계

    마주했던 도전 과제와 삽질 (Feat. 해결 과정)

    물론 AWX와 함께한 1년이 장밋빛만 있었던 건 아닙니다. 저도 꽤 많은 삽질을 했습니다. ⚠️ 특히 다음과 같은 부분에서 도전 과제가 있었어요.

    1. 자격 증명(Credential) 관리의 복잡성: 초기에는 AWS IAM(Identity and Access Management) 역할(Role)을 AWX에 연동하는 데 애를 먹었습니다. AWX가 EC2 인스턴스에서 실행될 때 IAM 역할을 상속받아 사용하는 방식과, 직접 AWS Access Key를 등록하는 방식 중 보안과 편리성을 저울질하는 데 시간이 걸렸죠. 결국, IAM 역할 기반 인증을 적극 활용하는 방향으로 정착했습니다.
    2. 동적 인벤토리(Dynamic Inventory) 스크립트 작성의 어려움: 클라우드 환경은 인스턴스가 수시로 뜨고 꺼지기 때문에, 항상 최신 인벤토리 정보를 유지하는 게 중요합니다. AWX에 내장된 클라우드 플러그인들을 최대한 활용했지만, 특정 태그(Tag)나 조건에 맞는 인스턴스만 필터링하는 복잡한 스크립트를 작성할 때는 시행착오가 많았습니다. 파이썬(Python)으로 직접 스크립트를 짜면서 디버깅하는 과정이 꽤 힘들었네요.
    3. 워크플로우(Workflow) 설계의 난이도: 여러 작업 템플릿을 연결해서 복잡한 배포 파이프라인(Pipeline)이나 운영 절차를 자동화할 때, 조건부 실행이나 실패 시 롤백(Rollback) 같은 로직을 AWX 워크플로우로 구현하는 게 생각보다 쉽지 않았습니다. 처음에는 너무 복잡하게 얽혀서 디버깅도 어려웠는데, 점차 작은 단위의 작업 템플릿으로 쪼개고, 명확한 성공/실패 조건을 정의하면서 해결해 나갔습니다.
    4. AWX 업그레이드의 험난함: 오픈소스 프로젝트이다 보니, 새로운 버전이 나올 때마다 업그레이드 가이드라인이 명확하지 않거나, 의존성 문제로 애를 먹는 경우가 있었습니다. 특히 컨테이너 환경에서 볼륨(Volume)이나 데이터베이스 마이그레이션(Migration) 과정에서 여러 번 백업/복구를 반복하며 땀 좀 흘렸습니다. 💦

    이 모든 과정들이 저를 더 단단하게 만들어주었네요. 역시 인프라는 삽질하면서 배우는 게 최고인 것 같습니다! ㅎㅎ

    AWX, 앞으로의 방향은? (마무리)

    지난 1년간 AWX 클라우드 자동화를 경험하면서 정말 많은 것을 배우고, 팀의 운영 효율성을 크게 향상시킬 수 있었습니다. 수동 작업을 줄이고, 휴먼 에러를 방지하며, 표준화된 절차를 통해 안정적인 서비스 운영을 할 수 있게 된 것이 가장 큰 성과라고 생각합니다.

    물론 아직 가야 할 길이 멀지만, 앞으로는 AWX를 CI/CD(Continuous Integration/Continuous Deployment) 파이프라인에 더욱 깊숙이 통합하고, GitOps(깃옵스) 철학을 도입하여 모든 인프라 변경 사항을 Git을 통해 관리하는 시스템을 구축하고 싶습니다. IaC(Infrastructure as Code, 코드형 인프라)를 넘어, 모든 것이 코드로 정의되고 자동으로 배포되는 이상적인 환경을 꿈꾸고 있거든요.

    혹시 여러분도 AWX를 활용한 클라우드 자동화 경험이 있으시다면, 어떤 점이 좋았고 어떤 어려움이 있었는지 댓글로 공유해 주시면 좋겠습니다. 다음 글에서는 AWX와 CI/CD 연동에 대한 좀 더 구체적인 내용을 다뤄볼까 합니다. 기대해주세요! 😊

    AWX와 클라우드 인프라 자동화의 미래 비전 인포그래픽

    AWX와 클라우드 인프라 자동화의 미래 비전 인포그래픽