13년차의 서버실

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

[작성자:] admin

  • [HomeLabs] CheapSecurity vs Frigate: 홈랩 CCTV AI NVR 솔루션 실측 비교

    [HomeLabs] CheapSecurity vs Frigate: 홈랩 CCTV AI NVR 솔루션 실측 비교

    [홈랩] 홈랩 CCTV AI NVR 비교: CheapSecurity vs Frigate 실측 관점 정리

    홈랩 CCTV AI NVR 구성을 고민하시는 분들이 요즘 정말 많습니다. 저도 집에서 카메라 몇 대 붙여놓고 단순 녹화만 하다가, 사람이나 차량 같은 객체를 자동으로 잡아주는 AI NVR이 필요해졌거든요. 처음엔 ‘그냥 싼 장비 하나 사면 끝 아닌가?’ 싶었는데, 실제로 써보니까 저장 방식, RTSP(실시간 스트리밍 프로토콜), 하드웨어 가속(Hardware Acceleration), 객체 탐지(Object Detection) 안정성이 생각보다 크게 갈리더라고요. 그래서 이번 글은 CheapSecurity vs Frigate라는 비교 키워드로 많이 찾는 분들을 위해, 제가 홈랩에서 판단했던 기준과 실제 구축 흐름을 정리해보겠습니다.

    먼저 중요한 점 하나 짚고 갈게요. Frigate는 실존하는 대표적인 오픈소스 AI NVR로 널리 알려져 있지만, CheapSecurity는 제가 확인 가능한 범위에서 구체적인 실존 제품 정보가 명확하지 않았습니다. 그래서 이 글에서는 CheapSecurity를 특정 브랜드나 모델로 단정하지 않고, 저비용 홈랩 CCTV AI NVR 구성 전반을 뜻하는 비교축으로 풀어가겠습니다. 이게 할루시네이션을 피하는 가장 안전한 방식이더라고요.

    홈랩 CCTV AI NVR 전체 아키텍처를 보여주는 이미지

    홈랩 CCTV AI NVR 아키텍처 예시입니다. 카메라, RTSP 스트림, NVR, 저장소, 알림 흐름을 한눈에 보여주는 용도예요.

    왜 홈랩 CCTV AI NVR 비교가 중요한가

    쉽게 말해 CCTV는 이제 녹화만 되는 박스로 끝나지 않습니다. 홈랩에서 직접 운영하면 다음 세 가지가 바로 체감됩니다.

    • 탐지 정확도: 움직임 감지(Motion Detection)만으로는 나뭇잎, 그림자, 비 오는 날 오탐이 너무 많습니다.
    • 저장 효율: 24시간 연속 녹화는 편하지만 디스크를 꾸준히 잡아먹습니다.
    • 운영 편의성: 컨테이너(Docker Container) 기반인지, 설정 파일 중심인지에 따라 유지보수 난도가 확 달라집니다.

    제가 직접 해보니 홈랩 CCTV AI NVR에서 핵심은 단순히 ‘싼가 비싼가’가 아니었습니다. 오탐을 얼마나 줄이느냐, 스트림이 얼마나 안정적이냐, 그리고 장애가 났을 때 내가 직접 고칠 수 있느냐가 훨씬 중요했습니다. 특히 홈랩은 기업 환경처럼 전용 장비를 계속 바꿀 수 있는 구조가 아니잖아요. 이미 갖고 있는 미니 PC, NAS(Network Attached Storage), 중고 카메라를 최대한 활용해야 하니까요.

    CheapSecurity vs Frigate, 개념부터 쉽게 정리해보겠습니다

    저도 처음엔 헷갈렸는데, 여기서는 두 방향으로 이해하면 편합니다.

    1. CheapSecurity 쪽 접근: 저비용 우선입니다. 기존 카메라와 남는 서버를 최대한 재활용하고, 기능은 꼭 필요한 것부터 붙이는 방식이죠.
    2. Frigate 쪽 접근: 객체 탐지(Object Detection) 중심입니다. 단순 모션보다 사람, 차량 같은 이벤트를 기준으로 운영하기 좋습니다.
    3. 결정 포인트: 예산보다 운영 목표가 먼저입니다. ‘녹화 보관’이 목적이면 단순 구성도 충분하고, ‘이벤트 기반 알림’이 목적이면 Frigate류가 훨씬 잘 맞습니다.

    사실 보안 카메라 비교를 할 때 많은 분이 카메라 화질만 먼저 보시는데요, 실제 운영에서는 NVR이 더 중요할 때가 많습니다. 카메라가 RTSP를 안정적으로 뿌려줘도 NVR이 그걸 잘 받아먹지 못하면 녹화 구멍이 생깁니다. 반대로 카메라가 아주 고급형이 아니어도, NVR 구성이 안정적이면 실사용 만족도가 꽤 올라갑니다.

    한눈에 보는 비교 표

    항목 저비용 홈랩 구성(CheapSecurity 관점) Frigate
    주요 목적 최소 비용으로 녹화와 기본 감시 객체 탐지 중심 AI NVR 운영
    구성 난이도 케이스마다 편차 큼 초기 학습은 필요하지만 구조가 비교적 명확함
    탐지 방식 모션 감지 위주로 흐르기 쉬움 사람, 차량 등 객체 기준 운영에 유리
    확장성 개별 조합에 따라 다름 홈 어시스턴트(Home Assistant) 연동 사례가 많음
    트러블슈팅 조합형이라 원인 분리가 어려울 수 있음 설정 파일 중심이라 재현과 수정이 편한 편
    추천 대상 예산이 가장 중요한 입문자 안정적인 이벤트 감시를 원하는 사용자

    실전 구현: 제가 홈랩에서 Frigate 기준으로 잡은 기본 구조

    실제로 써보니까 가장 덜 삽질하는 시작점은 이렇습니다.

    1. 카메라는 RTSP 스트림을 안정적으로 제공하는 모델을 사용합니다.
    2. NVR은 Docker Compose로 올립니다.
    3. 녹화 저장소는 로컬 SSD나 NAS 마운트 경로로 분리합니다.
    4. 탐지용 스트림과 녹화용 스트림을 분리하면 운영이 조금 더 편해집니다.

    여기서 중요한 포인트! 홈랩 CCTV AI NVR은 CPU를 계속 갈아 넣는 구조로 가면 오래 못 버팁니다. 처음엔 ‘이 정도면 버티겠지’ 했는데, 여러 대 붙이니 팬 소음과 발열이 바로 올라오더라고요. 그래서 구성 자체를 단순하게 잡는 게 좋습니다.

    1. 디렉터리 준비

    mkdir -p ~/homelab/frigate/config
    mkdir -p ~/homelab/frigate/media
    cd ~/homelab/frigate

    설정 파일과 녹화 저장소를 분리해두면 백업이나 마이그레이션 할 때 편합니다. 저는 이걸 안 나눠놨다가 나중에 경로 정리하느라 삽질 좀 했습니다 ㅎㅎ

    2. Docker Compose 작성

    services:
      frigate:
        container_name: frigate
        restart: unless-stopped
        image: ghcr.io/blakeblackshear/frigate:stable
        privileged: true
        shm_size: "512mb"
        ports:
          - "5000:5000"
          - "8554:8554"
          - "8555:8555/tcp"
          - "8555:8555/udp"
        volumes:
          - ./config:/config
          - ./media:/media/frigate
          - /etc/localtime:/etc/localtime:ro

    버전 태그를 세세하게 고정하지 않은 이유도 있습니다. 이 글은 특정 시점의 미확인 버전을 단정하기보다, 구조와 운영 포인트에 집중하려고 했거든요.

    3. 기본 설정 파일 예시

    mqtt:
      enabled: false
    
    cameras:
      front_door:
        ffmpeg:
          inputs:
            - path: rtsp://USER:PASS@CAMERA_IP:554/stream1
              roles:
                - detect
                - record
        detect:
          enabled: true
        record:
          enabled: true
        snapshots:
          enabled: true
          timestamp: true
          bounding_box: true

    여기서는 가장 단순한 형태로만 시작하시면 됩니다. 처음부터 카메라 여러 대, 역할 분리, 알림 자동화까지 한 번에 넣으면 어디서 꼬였는지 파악이 안 됩니다. 저도 처음엔 욕심냈다가 결국 한 대만 붙여서 다시 검증했었거든요.

    홈랩 CCTV AI NVR에서 Frigate 설정과 컨테이너 구성을 설명하는 이미지

    Frigate 설정 파일, Docker Compose, 카메라 입력 흐름을 설명하는 구성 이미지가 들어갈 자리입니다.

    4. 컨테이너 실행

    docker compose up -d
    docker compose logs -f frigate

    로그를 바로 보는 습관이 중요합니다. 웹 화면만 보고 있으면 ‘왜 안 되지?’ 상태가 길어집니다. 반면 로그를 보면 RTSP 인증 실패인지, 스트림 경로 문제인지, 디렉터리 권한 문제인지 금방 감이 옵니다.

    CheapSecurity 관점에서 봤을 때의 장점과 한계

    그렇다면 저비용 중심의 구성은 무조건 별로냐? 그건 아닙니다. 오히려 홈랩 입문에서는 아주 현실적인 선택일 수 있습니다.

    • 장점: 이미 가진 장비를 재활용하기 좋습니다.
    • 장점: 작은 규모에서는 투자 부담이 적습니다.
    • 장점: 학습용으로는 충분히 재미있습니다.
    • 한계: 기능이 여기저기 흩어지면 운영 일관성이 떨어집니다.
    • 한계: 객체 탐지 품질을 안정적으로 맞추려면 결국 별도 설계가 필요합니다.

    제가 실제로 느낀 차이는 이거였습니다. 저비용 조합은 처음 시작은 빠른데, 시간이 갈수록 관리 복잡도가 올라갑니다. 반면 Frigate는 초반 설정이 조금 귀찮아도, 구조가 잡히고 나면 비교적 덜 흔들립니다. 특히 보안 카메라 비교를 많이 해보신 분일수록 공감하실 텐데요. 장비가 늘수록 ‘설정이 코드처럼 관리되느냐’가 진짜 중요해집니다.

    ⚠️ 트러블슈팅: 제가 실제로 부딪힌 문제들

    여기서부터가 진짜 실전입니다. 드디어 됐다 싶다가 다시 무너지는 구간이 꼭 오더라고요.

    1. RTSP 연결은 되는데 화면이 불안정한 경우

    대부분 카메라 스트림 프로파일이 원인인 경우가 많았습니다. 메인 스트림은 녹화용, 서브 스트림은 탐지용으로 나누는 방식이 보통 더 안정적이더라고요.

    ffprobe rtsp://USER:PASS@CAMERA_IP:554/stream1

    해결 포인트: 스트림 자체가 흔들리면 NVR을 아무리 만져도 답이 없습니다. 카메라 쪽 인코딩 설정부터 확인하세요.

    2. 저장은 되는데 이벤트 검색이 불편한 경우

    이건 모션 감지 중심 구성에서 자주 느낀 문제였습니다. 비슷한 시간대의 잡이벤트가 너무 많아져서, 막상 필요한 장면을 찾는 시간이 길어지더라고요. 그래서 객체 기반 태깅이 왜 중요한지 뒤늦게 체감했습니다.

    3. CPU 사용률이 계속 높은 경우

    처음엔 이게 뭔가 싶었는데, 탐지와 녹화를 같은 고해상도 스트림 하나로 다 처리하면 부담이 커질 수 있습니다. 특히 홈랩 미니 PC는 여유가 넉넉하지 않거든요.

    • 탐지 스트림 해상도를 낮춰봅니다.
    • 동시 카메라 수를 줄여서 먼저 검증합니다.
    • 디스크 I/O 병목도 함께 봐야 합니다.

    중요: 성능 문제를 무조건 CPU 탓으로만 보면 안 됩니다. 네트워크, 저장소, 스트림 품질이 같이 얽혀 있는 경우가 많습니다.

    4. 권한 문제로 녹화 파일이 안 쌓이는 경우

    이건 진짜 자주 겪습니다. 컨테이너는 떠 있는데 파일이 안 생기면, 경로 권한부터 보셔야 합니다.

    ls -al ~/homelab/frigate
    ls -al ~/homelab/frigate/media

    저는 예전에 NFS(Network File System) 마운트 경로로 연결했다가 권한이 꼬여서 반나절을 날린 적도 있습니다. 이런 날은 정말 허무하죠.

    홈랩 CCTV AI NVR 트러블슈팅과 로그 분석을 표현한 이미지

    로그 분석, RTSP 오류, 저장소 권한 문제 같은 트러블슈팅 장면을 보여주는 이미지 자리입니다.

    검증과 결과: 어떤 기준으로 비교해야 덜 후회하나

    실제로 써보니까 결과를 볼 때는 화려한 기능보다 아래 기준이 훨씬 중요했습니다.

    1. 이벤트 재생이 빠른가
    2. 오탐이 줄었는가
    3. 카메라 추가 시 설정이 반복 가능(repeatable)한가
    4. 문제 발생 시 로그와 설정 파일만으로 복구 가능한가

    여기서 Frigate의 강점은 꽤 분명했습니다. 객체 중심 이벤트 관리를 하려는 순간, 단순 저비용 조합보다 운영 일관성이 좋아지더라고요. 반대로 CheapSecurity식 접근, 즉 기존 자원 재활용 중심 접근은 예산을 많이 아껴야 하는 상황에서 꽤 유효했습니다. 다만 규모가 조금만 커져도 관리 체계가 필요해집니다.

    제가 정리한 결론은 이렇습니다.

    질문 추천 방향
    일단 가장 적은 비용으로 시작하고 싶은가 저비용 홈랩 조합부터 시작
    사람/차량 감지와 이벤트 탐색이 중요한가 Frigate 우선 검토
    나중에 홈 어시스턴트 연동까지 볼 생각인가 Frigate가 더 자연스러움
    운영보다 실험 자체가 목적인가 저비용 조합도 재미있음
    홈랩 CCTV AI NVR 결과 검증과 이벤트 대시보드를 보여주는 이미지

    탐지 이벤트, 썸네일, 타임라인 기반 검증 결과를 보여주는 대시보드 이미지 자리입니다.

    정리: 홈랩 CCTV AI NVR은 목적이 먼저입니다

    이번 비교를 한 문장으로 줄이면 이겁니다. CheapSecurity vs Frigate의 승부는 가격이 아니라 운영 목표에서 갈립니다. 단순 녹화와 저비용이 우선이면 가볍게 시작해도 됩니다. 하지만 나중에 사람 감지, 알림 자동화, 이벤트 검색 효율까지 보게 되면 Frigate 쪽 장점이 점점 더 크게 느껴질 가능성이 높습니다.

    혹시 이런 경험 있으신가요? 처음엔 싸게 시작했는데, 나중에 설정이 쌓이면서 오히려 시간이 더 들어가는 경우요. 저도 딱 그랬습니다. 그래서 지금은 장비 스펙보다도 운영 구조가 단순하고 재현 가능한가를 먼저 봅니다. 홈랩은 재미있어야 오래 가거든요.

    홈랩 CCTV AI NVR에서 CheapSecurity식 구성과 Frigate를 비교 요약한 이미지

    두 접근 방식의 장단점과 추천 상황을 한 장으로 요약한 인포그래픽이 들어갈 자리입니다.

    다음 단계 제안

    • 1단계: 카메라 1대로 RTSP 안정성부터 검증해보세요.
    • 2단계: 그 다음 Frigate 같은 AI NVR로 이벤트 기반 운영을 붙여보세요.
    • 3단계: 마지막에 저장 정책과 알림 정책을 다듬는 게 효율적입니다.

    다음 글에서는 홈 어시스턴트(Home Assistant)와 Frigate 연동이나, 카메라 스트림 분리 전략을 더 자세히 다뤄볼 예정입니다. 이전 글에서 다룬 Docker Compose 운영 팁도 같이 보시면 훨씬 이해가 빨라지실 겁니다.

    자주 묻는 질문 FAQ

    Q1. 홈랩 CCTV AI NVR 입문은 어떤 방향이 좋을까요?

    예산이 빡빡하면 저비용 조합으로 시작하셔도 됩니다. 다만 이벤트 기반 운영을 원하면 초반부터 Frigate를 검토하는 편이 장기적으로 편할 수 있습니다.

    Q2. 보안 카메라 비교에서 가장 먼저 봐야 할 건 뭔가요?

    해상도보다 RTSP 안정성, 스트림 설정 유연성, 그리고 NVR과의 궁합을 먼저 보시는 걸 추천드립니다.

    Q3. Frigate는 누구에게 잘 맞나요?

    단순 녹화보다 사람/차량 감지, 이벤트 검색, 자동화 연동이 중요한 분들께 잘 맞습니다.

    Q4. CheapSecurity라는 이름의 특정 제품을 찾고 있었는데요?

    제가 확인 가능한 범위에서는 특정 실존 제품으로 단정하기 어려웠습니다. 그래서 이 글에서는 저비용 홈랩 CCTV AI NVR 접근 전반을 뜻하는 비교 개념으로 다뤘습니다.

  • [홈랩] MikroTik 라우터 1년 사용 후기와 보안 설정 팁

    [홈랩] MikroTik 라우터 1년 사용 후기와 보안 설정 팁

    [홈랩] MikroTik 라우터 1년 사용 후기와 보안 설정 팁

    홈랩을 굴리다 보면 결국 네트워크가 제일 먼저 발목을 잡습니다. 서버는 잘 뜨는데 외부 접속이 불안정하고, 포트 하나 열었다가 괜히 마음이 찝찝하고, 가족이 같이 쓰는 인터넷까지 느려지면 그때부터는 진짜 골치 아프거든요. 저도 그런 흐름을 몇 번 겪고 나서 MikroTik 라우터 홈랩 구성을 1년 정도 꾸준히 써봤습니다. 결론부터 말하면, 초반 진입장벽은 좀 있지만 네트워크 보안과 세밀한 제어에서는 꽤 만족도가 높았어요.

    특히 MikroTik은 겉으로 보기엔 투박한데, 실제로 써보니까 라우터 설정 자유도가 높아서 홈랩처럼 실험이 많은 환경에 잘 맞더라고요. 반대로 말하면, 기본값만 믿고 쓰면 안 되는 부분도 분명히 있습니다. 저도 처음엔 이게 뭔가 싶었는데, Input(인풋, 라우터 자신으로 들어오는 트래픽) 체인과 Forward(포워드, 내부 네트워크를 통과하는 트래픽) 체인 개념을 제대로 안 잡고 시작했다가 삽질 좀 했습니다 ㅎㅎ

    이번 글에서는 제가 1년 동안 MikroTik 라우터를 홈랩에서 굴리면서 느낀 점, 실제로 적용해 둔 보안 중심 설정, 그리고 자주 겪는 문제 해결 팁까지 한 번에 정리해보겠습니다.

    홈랩 네트워크에서 MikroTik 라우터가 인터넷, 내부 VLAN, 관리망 사이를 어떻게 중재하는지 보여주는 개요 이미지입니다.

    MikroTik 라우터를 홈랩에 쓰면 좋은 이유

    쉽게 말해 MikroTik의 장점은 세밀함입니다. 일반 가정용 공유기는 메뉴가 친절한 대신 할 수 있는 일이 제한적이죠. 반면 MikroTik은 방화벽 규칙, NAT(Network Address Translation, 네트워크 주소 변환), VLAN(Virtual LAN, 가상 랜), Queue(대역폭 제어) 같은 기능을 꽤 촘촘하게 만질 수 있어요.

    • 방화벽 정책을 세분화하기 좋습니다.
    • 홈랩 서버용 네트워크와 가족용 네트워크 분리가 수월합니다.
    • 문제 원인 추적이 비교적 명확합니다.
    • CLI(Command Line Interface, 명령줄 환경)와 GUI 둘 다 쓸 수 있어 관리 방식 선택 폭이 넓습니다.

    제가 직접 해보니 가장 큰 차이는 “열어둔 만큼만 열린다”는 느낌이었습니다. 다른 장비에서는 마법사 기반 설정이 편한 대신 내부 동작이 불투명할 때가 있었는데, MikroTik은 규칙을 내가 만들고 내가 읽을 수 있어서 나중에 점검하기가 편했어요. 홈랩에서는 이게 생각보다 큽니다.

    1년 써보며 정리한 핵심 개념

    여기서 중요한 포인트! MikroTik을 잘 쓰려면 메뉴보다 트래픽 흐름을 먼저 이해해야 합니다.

    1. Input과 Forward를 구분해야 합니다

    이걸 헷갈리면 방화벽을 만든다고 만들었는데 정작 라우터 관리 포트는 열려 있고, 내부 서버는 막혀 있는 이상한 상태가 생깁니다.

    • Input: 라우터 자신에게 들어오는 접속입니다. 예를 들면 WinBox, SSH, DNS 요청 같은 것들입니다.
    • Forward: PC에서 서버로, VLAN에서 인터넷으로 흐르는 통과 트래픽입니다.
    • Output: 라우터가 바깥으로 내보내는 트래픽입니다.

    저도 처음엔 외부 관리 포트를 막는다고 생각하고 Forward 쪽만 만졌었는데, 실제로는 Input을 정리해야 하더라고요. 이거 한 번 헷갈리면 “분명 막았는데 왜 접속되지?”가 반복됩니다.

    2. 기본 허용보다 기본 거부가 편합니다

    홈랩은 서비스가 계속 늘어납니다. NAS 하나 추가하고, 리버스 프록시(Reverse Proxy, 요청을 대신 받아 전달하는 프록시) 붙이고, 테스트용 VM도 늘어나죠. 이런 환경에서는 기본 거부(Default Deny, 기본적으로 막고 필요한 것만 허용)가 훨씬 관리하기 쉬워요.

    3. 관리망을 분리하면 마음이 편합니다

    MikroTik 라우터 홈랩 구성을 오래 유지하려면 관리용 단말과 실험용 서버를 같은 네트워크에 두지 않는 게 좋습니다. 저는 최소한 아래 3개는 분리하는 쪽이 낫다고 봐요.

    1. 관리망: 라우터, 스위치, 하이퍼바이저 관리 페이지
    2. 서버망: NAS, VM, 컨테이너 호스트
    3. 일반 사용자망: 노트북, TV, 모바일 기기

    제가 실제로 잡아둔 홈랩 네트워크 보안 기준

    1년 운영하면서 결국 아래 원칙으로 정리됐어요. 완벽한 정답은 아니지만, 가정용과 홈랩 사이 균형은 괜찮았습니다.

    항목 처음 구성 1년 사용 후 정착한 방식
    관리 접속 어디서나 접속 가능 관리망 또는 허용 IP만 접속
    포트 오픈 서비스별 직접 오픈 필수 포트만 오픈, 가능하면 VPN 우선
    내부 분리 단일 LAN 관리망/서버망/사용자망 분리
    로그 확인 문제 생기면 그때 확인 방화벽 드롭 로그를 주기적으로 점검
    DNS 사용 혼합 사용 내부 클라이언트 경로를 명확히 통일

    특히 외부 공개 서비스는 생각보다 적게 가져가는 게 좋습니다. 홈랩 하다 보면 이것도 열고 싶고 저것도 열고 싶거든요. 근데 실제로 운영해보면 외부 Ingress(인그레스, 외부 트래픽 진입점)는 줄일수록 편해요. 저는 나중에 대부분 VPN 뒤로 넣는 방향으로 바꿨습니다.

    MikroTik 라우터 설정 팁: 처음 세팅할 때 꼭 하는 것

    아래는 제가 새 장비를 잡으면 거의 공통으로 보는 항목입니다. 인터페이스 이름은 환경마다 다르니 그대로 복붙하기보다 구조를 보고 적용하시면 됩니다.

    1. 관리 접속 가능 대역을 먼저 정합니다.
    2. Input 체인에서 불필요한 관리 포트를 막습니다.
    3. Established/Related(이미 수립되었거나 연관된 연결)를 먼저 허용합니다.
    4. Invalid(비정상 상태 연결)를 초반에 드롭합니다.
    5. Forward 체인에서 VLAN 간 접근 범위를 최소화합니다.
    6. NAT와 포트 포워딩은 마지막에 추가합니다.

    예시 1. 관리용 주소 목록 만들기

    /ip firewall address-list
    add list=mgmt-allow address=192.168.10.0/24 comment="management subnet"
    add list=mgmt-allow address=192.168.20.50 comment="admin laptop"
    

    이렇게 Address List(주소 목록)를 먼저 만들어 두면 규칙 읽기가 훨씬 편해요. 나중에 IP가 바뀌어도 룰 전체를 고칠 필요가 없거든요.

    예시 2. Input 체인 기본 보안

    /ip firewall filter
    add chain=input action=accept connection-state=established,related comment="allow established,related"
    add chain=input action=drop connection-state=invalid comment="drop invalid"
    add chain=input action=accept src-address-list=mgmt-allow comment="allow management"
    add chain=input action=accept protocol=icmp comment="allow icmp"
    add chain=input action=drop in-interface=ether1 comment="drop direct WAN access to router"
    

    여기서 핵심은 라우터 자신에 대한 접근을 최소화하는 겁니다. 저는 초반에 WinBox 포트만 생각했는데, 실제로는 Input 전체를 보는 게 맞더라고요.

    MikroTik 라우터 설정과 VLAN 분리, 방화벽 규칙을 설명하는 홈랩 이미지

    주소 목록, Input 규칙, VLAN 분리 흐름을 한눈에 이해할 수 있도록 구성한 설정 개념 이미지입니다.

    예시 3. Forward 체인 최소 허용

    /ip firewall filter
    add chain=forward action=accept connection-state=established,related comment="allow established,related"
    add chain=forward action=drop connection-state=invalid comment="drop invalid"
    add chain=forward action=accept src-address=192.168.30.0/24 dst-address=192.168.50.10 protocol=tcp dst-port=443 comment="allow reverse proxy to app"
    add chain=forward action=drop src-address=192.168.30.0/24 dst-address=192.168.10.0/24 comment="block user net to management net"
    

    이런 식으로 필요한 통신만 열어두면 나중에 서버가 늘어나도 기준이 안 흔들려요. 처음엔 답답해 보여도, 실서비스 비슷하게 운영하려면 이 방식이 결국 편합니다.

    예시 4. 포트 포워딩은 짧고 명확하게

    /ip firewall nat
    add chain=dstnat action=dst-nat protocol=tcp in-interface=ether1 dst-port=443 to-addresses=192.168.50.10 to-ports=443 comment="https to reverse proxy"
    

    포트 포워딩은 간단해 보여도 공격 표면이 바로 늘어나는 지점이에요. 그래서 저는 메모를 꼭 남깁니다. 나중에 “이 규칙 왜 열었지?”가 생각보다 자주 옵니다.

    ⚠️ 1년 동안 실제로 겪은 문제와 해결법

    좋은 얘기만 하면 재미없죠. 실제 운영에서는 꼭 한 번씩 걸리는 포인트가 있어요.

    문제 1. 룰 순서 때문에 허용한 줄 알았는데 계속 차단됨

    MikroTik 방화벽은 위에서 아래로 평가됩니다. 이걸 머리로는 아는데, 작업하다 보면 은근히 놓칩니다. 저도 특정 VLAN에서 NAS 접근을 열어뒀다고 생각했는데, 앞단의 Drop 규칙이 먼저 맞아서 안 되더라고요.

    • 증상: 규칙은 있어 보이는데 트래픽이 통과하지 않음
    • 원인: 더 앞쪽에 포괄적인 Drop 규칙 존재
    • 해결: 허용 규칙을 상단으로 올리고 로그로 재확인

    문제 2. FastTrack 때문에 트래픽 추적이 헷갈림

    성능 최적화용 FastTrack(패스트트랙, 일부 연결을 빠르게 처리하는 기능)은 분명 유용한데, 트러블슈팅할 때는 흐름이 덜 보일 수 있어요. 특히 로그나 세밀한 정책 테스트 중에는 “왜 예상대로 안 보이지?” 싶은 순간이 있습니다.

    저는 정책 검증 단계에서는 단순하게 가는 편이 낫다고 느껴요. 성능이 급한 게 아니라면 먼저 정책을 안정화하고, 그 다음 최적화를 보는 순서가 덜 헷갈립니다.

    문제 3. DNS 경로가 섞여서 내부 접근이 이상해짐

    이건 진짜 많이 겪어요. 일부 장비는 라우터 DNS를 보고, 일부는 외부 DNS를 보고, 내부 도메인은 또 다른 서버를 보면 결과가 제각각이거든요.

    • 증상: 같은 주소인데 PC와 모바일 결과가 다름
    • 원인: DNS 경로 혼재
    • 해결: DHCP에서 기본 DNS 경로를 통일하고, 내부 레코드는 일관된 서버로 처리

    문제 4. 원격 관리 열어두고 마음이 불편해짐

    사실 이건 기술 문제보다 운영 습관 문제에 가까워요. 외부에서 바로 관리 페이지 접속 가능하게 두면 편하긴 한데, 시간이 갈수록 찜찜해요. 저도 한동안은 편의성 때문에 열어뒀다가 결국 VPN 뒤로 옮겼습니다. 그 후로는 심리적으로도 훨씬 편하더라고요.

    검증은 이렇게 했습니다

    설정은 했는데 정말 적용됐는지 확인하는 과정이 중요해요. 라우터 설정은 “저장했다”가 끝이 아니거든요.

    1. 관리망 단말에서만 라우터 관리 포트 접속이 되는지 확인합니다.
    2. 사용자망에서 관리망으로 접근이 차단되는지 테스트합니다.
    3. 외부 공개 포트가 필요한 것만 열려 있는지 스캔합니다.
    4. 로그에서 반복 드롭 이벤트가 있는지 확인합니다.
    5. 장애 시 우회 경로가 없는지 다시 봅니다.
    # 내부 클라이언트에서 경로 확인 예시
    ping 192.168.10.1
    traceroute 192.168.50.10
    
    # 외부 노출 포트 확인 예시
    nmap -Pn example.com
    

    실제로 써보니까 검증은 한 번으로 안 끝나더라고요. 장비 하나 추가할 때마다 정책이 흔들릴 수 있어서, 저는 변경 후 간단 체크리스트를 매번 돌립니다. 이런 습관이 나중에 큰 사고를 줄여주더라고요.

    MikroTik 라우터 홈랩 검증 단계의 방화벽 로그와 네트워크 보안 점검 이미지

    설정 적용 후 방화벽 드롭 로그와 포트 노출 상태를 점검하는 검증 단계의 분위기를 보여주는 이미지입니다.

    1년 사용 후기: 좋았던 점과 아쉬웠던 점

    좋았던 점

    • 정책이 눈에 보여요. 방화벽과 NAT가 명시적이라 운영 판단이 편합니다.
    • 홈랩 확장성이 좋아요. VLAN, 서버망 분리, 테스트망 추가가 자연스럽습니다.
    • 문제 원인을 추적하기 좋습니다. 조금만 익숙해지면 “어느 체인에서 막히는지” 보이기 시작해요.

    아쉬웠던 점

    • 초기 진입장벽이 있어요. 처음엔 메뉴보다 개념이 먼저라서 낯섭니다.
    • 대충 설정하면 오히려 더 헷갈립니다. 규칙 이름, 주소 목록 정리를 안 하면 시간이 갈수록 복잡해져요.
    • 편의 기능보다 원리 이해가 필요해요. 이게 장점이자 단점이더라고요.

    그래도 1년 지나고 보니 저는 만족 쪽입니다. 특히 MikroTik 라우터 홈랩 환경에서는 “작게 시작해서 점점 정교하게 만든다”는 흐름이 잘 맞았어요. 처음엔 어렵지만, 어느 순간부터는 네트워크가 덜 무섭습니다. 이 느낌이 꽤 큽니다.

    처음 시작하는 분께 드리는 설정 순서 추천

    혹시 지금 막 MikroTik으로 넘어오려는 분이라면, 처음부터 모든 기능을 다 만지지 마세요. 저도 욕심내서 한 번에 VLAN, VPN, QoS, 포트포워딩 다 넣었다가 스스로 길을 잃었어요.

    1. 기본 인터넷 연결부터 안정화합니다.
    2. 관리 접속 제한을 먼저 적용합니다.
    3. 내부 네트워크 분리는 2~3개 대역부터 시작합니다.
    4. 외부 공개 서비스는 최소화합니다.
    5. 로그와 주석(comment)을 남깁니다.
    6. 원격 관리는 VPN 우선으로 가져갑니다.

    💡 팁 하나 더 드리면, 규칙 이름을 사람이 읽을 수 있게 적어두세요. 나중에 몇 달 지나서 보면 기억 안 난답니다. 이건 진짜예요.

    자주 묻는 질문 정리

    Q1. 홈랩에서 MikroTik은 초보자에게 너무 어렵지 않나요?

    처음엔 어려워요. 다만 단순히 불친절해서라기보다, 네트워크 원리를 드러내기 때문입니다. 대신 한 번 구조를 익히면 다른 장비를 봐도 이해가 빨라집니다.

    Q2. 포트포워딩보다 VPN이 더 나은가요?

    대부분의 관리 접속은 그래요. 외부에서 꼭 공개해야 하는 서비스가 아니라면 VPN 뒤로 숨기는 편이 보안상 낫고 운영도 편합니다.

    Q3. 꼭 VLAN까지 해야 하나요?

    처음부터는 아니에요. 하지만 홈랩 서버가 늘어나고 IoT 기기가 섞이면 분리의 효과가 확실히 보여요. 최소한 관리망과 일반망 분리는 추천드립니다.

    마무리: 1년 써보니 결국 남는 건 구조였습니다

    이번 MikroTik 라우터 홈랩 1년 사용 후기를 한 줄로 줄이면 이렇습니다. “화려한 기능보다 구조를 이해하게 만드는 장비”였어요. 처음엔 낯설고, 룰 하나 잘못 넣으면 식은땀이 나기도 해요. 근데 그 과정을 지나고 나면 네트워크 보안 관점이 달라집니다. 저도 처음엔 그냥 인터넷만 되면 된다고 생각했었는데, 지금은 트래픽이 어디서 들어오고 어디로 나가는지부터 보게 되더라고요.

    특히 네트워크 보안과 라우터 설정을 장기적으로 관리해야 하는 홈랩이라면, MikroTik은 꽤 괜찮은 선택지였어요. 다만 처음부터 크게 벌이지 말고, 관리 접속 제한과 네트워크 분리부터 차근차근 가시는 걸 추천드립니다.

    다음 글에서는 홈랩에서 VPN 중심 원격 접속 구조를 어떻게 잡는지, 그리고 리버스 프록시 앞단을 어떻게 단순하게 유지하는지 이어서 다뤄볼 예정입니다. 이전 글에서 다뤘던 홈 서버 백업 전략과도 연결되는 내용이라 같이 보시면 흐름이 더 잘 잡히실 겁니다.

    초기 단일망 구성과 현재 분리된 홈랩 구성을 비교하며, 보안과 운영 편의성이 어떻게 달라졌는지 요약한 이미지입니다.

    🎉 한 번에 완벽하게 만들 필요는 없어요. 중요한 건, 내가 왜 이 규칙을 넣었는지 설명할 수 있는 상태로 운영하는 겁니다. 그게 결국 오래 가더라고요.

  • [Cloud] ArgoCD로 Kubernetes에 Ollama 배포: 6개월 운영 후기 및 최적화 전략

    [Cloud] ArgoCD로 Kubernetes에 Ollama 배포: 6개월 운영 후기 및 최적화 전략

    목차

    [Cloud] ArgoCD로 Kubernetes에 Ollama 배포: 6개월 운영 후기 및 최적화 전략

    ArgoCD Ollama 배포를 처음 붙일 때만 해도, 솔직히 저는 “로컬에서 잘 돌던 걸 굳이 Kubernetes(쿠버네티스, 컨테이너 오케스트레이션 플랫폼)까지 올려야 하나?” 싶었습니다. 그런데 팀이나 홈랩에서 여러 워크로드를 같이 운영하다 보면 얘기가 달라지더라고요. 모델 파일은 크고, 노드는 자꾸 바뀌고, 누가 어떤 설정을 바꿨는지 추적도 필요합니다. 결국 ArgoCD(아르고CD, GitOps 배포 도구)와 Ollama(올라마, 로컬/서버 환경에서 LLM을 실행하는 런타임) 조합으로 정리해 두니 운영 피로도가 확 줄었더라고요.

    이번 글은 제가 홈랩과 테스트 클러스터에서 6개월 정도 굴려보면서 정리한 ArgoCD Ollama 배포 운영 후기입니다. 단순 설치 가이드가 아니라, 어디서 삽질했는지, 어떤 최적화가 체감이 컸는지, 그리고 Kubernetes GitOps 관점에서 무엇을 꼭 잡아야 하는지 중심으로 풀어보겠습니다. 비슷하게 LLM 클라우드나 사내 추론 환경을 작게 시작해 보려는 분들께 꽤 현실적인 기준점이 될 겁니다.

    ArgoCD가 Git 저장소의 선언형 설정을 읽어 Kubernetes 클러스터에 Ollama를 배포하고, 내부 서비스와 스토리지를 연결하는 전체 구조 예시입니다.

    왜 ArgoCD Ollama 배포가 생각보다 중요했는지

    쉽게 말해, Ollama 하나만 띄우는 건 어렵지 않습니다. 문제는 운영이거든요. 처음엔 Docker 하나로 시작했었습니다. 그때는 편했습니다. 근데 모델을 추가하고, 스토리지를 옮기고, 노드를 교체하고, Ingress(인그레스, 외부 트래픽 진입점) 뒤에 붙이고, 다시 재현하려고 하니 슬슬 꼬이기 시작했습니다.

    제가 직접 해보니 진짜 차이는 설치가 아니라 재현성(reproducibility, 같은 상태를 다시 만드는 능력)에서 나왔습니다. Git에 원하는 상태를 남기고, ArgoCD가 그 상태를 계속 맞춰 주니까 “어제는 됐는데 오늘은 왜 안 되지” 같은 상황이 확 줄더라고요. 특히 LLM 워크로드는 모델 파일과 디스크 사용량이 크기 때문에, 사람 손으로 운영하면 금방 흔들립니다.

    • 변경 이력 추적: 누가 리소스 제한을 바꿨는지 Git commit으로 남습니다.
    • 복구 속도: 노드가 바뀌어도 선언형 매니페스트로 다시 맞추기 쉽습니다.
    • 운영 표준화: dev, lab, prod 비슷한 구조로 가져가기 좋습니다.
    • 드리프트 방지: kubectl로 급하게 만진 설정이 오래 남지 않습니다.

    핵심 개념 정리: Kubernetes GitOps와 Ollama를 같이 볼 때

    여기서 중요한 포인트가 있습니다. Ollama는 애플리케이션이고, ArgoCD는 상태 관리자입니다. 이 둘의 역할을 섞어서 생각하면 금방 헷갈립니다. 저도 처음엔 ArgoCD가 모델까지 알아서 관리해 주는 느낌으로 생각했었는데, 실제로는 그렇지 않더라고요.

    1. ArgoCD는 원하는 상태를 맞추는 도구입니다

    Git 저장소에 있는 YAML이 기준입니다. Deployment(디플로이먼트, 파드 배포 정의), Service(서비스, 네트워크 노출), PersistentVolumeClaim(PVC, 영구 스토리지 요청) 같은 리소스를 계속 감시하고 맞춰 줍니다.

    2. Ollama는 모델 실행 환경입니다

    모델 파일을 저장하고, 요청을 받아 추론을 수행합니다. 그래서 CPU/GPU보다도 처음엔 디스크와 네트워크가 더 자주 병목이 되기도 합니다. 특히 모델을 여러 번 다시 내려받는 구조가 되면 운영이 굉장히 피곤해집니다.

    3. 모델 관리와 애플리케이션 관리를 분리해야 덜 꼬입니다

    제가 6개월 운영하면서 얻은 결론은 이겁니다. 애플리케이션 배포와 모델 프리로드(preload, 미리 받아두기)를 같은 단계로 억지로 묶지 않는 게 좋습니다. 처음엔 한 번에 끝내고 싶어서 initContainer(초기화 컨테이너) 쪽으로 몰아넣었는데, 재배포 때마다 모델 다운로드가 걸려서 시간도 오래 걸리고 실패 지점도 늘었습니다.

    구분 역할 운영 팁
    ArgoCD Git 기준 상태 동기화 자동 동기화와 self-heal은 켜되, 삭제 정책은 팀 기준에 맞춰 보수적으로
    Kubernetes 파드 실행, 스토리지, 네트워크 관리 리소스 요청값과 스토리지 클래스부터 먼저 정리
    Ollama LLM 실행과 모델 저장 모델 캐시 경로와 영구 볼륨 전략이 핵심
    GitOps 운영 변경 이력과 재현성 확보 핫픽스 후에는 반드시 Git 원복 반영

    실전 구현: 제가 쓰는 ArgoCD Ollama 배포 기본 구조

    이제 본론입니다. 아래 구조는 제가 홈랩에서 가장 무난하게 굴렸던 방식입니다. 아주 화려하진 않지만, 유지보수는 편했습니다. 처음엔 이게 뭔가 싶었는데, 결국 오래 가는 건 단순한 구조더라고요.

    디렉터리 구조

    platform/
      argocd/
        applications/
          ollama.yaml
      apps/
        ollama/
          namespace.yaml
          pvc.yaml
          deployment.yaml
          service.yaml
          kustomization.yaml

    1. Namespace와 PVC부터 고정합니다

    Ollama는 모델 파일이 남아야 의미가 있습니다. 그래서 저는 거의 항상 PVC를 먼저 잡습니다. 여기서 대충 가면 나중에 재배포할 때 모델을 다시 받느라 시간 다 씁니다.

    apiVersion: v1
    kind: Namespace
    metadata:
      name: ollama
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: ollama-data
      namespace: ollama
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 100Gi

    2. Deployment는 최대한 단순하게 갑니다

    실제로 써보니까 처음부터 옵션을 너무 많이 넣는 것보다, 먼저 떠야 합니다. 그리고 그다음에 리소스 제한과 노드 스케줄링을 붙이는 쪽이 덜 위험했습니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ollama
      namespace: ollama
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: ollama
      template:
        metadata:
          labels:
            app: ollama
        spec:
          containers:
            - name: ollama
              image: ollama/ollama:latest
              ports:
                - containerPort: 11434
              volumeMounts:
                - name: ollama-data
                  mountPath: /root/.ollama
              resources:
                requests:
                  cpu: "2"
                  memory: "8Gi"
                limits:
                  cpu: "4"
                  memory: "16Gi"
          volumes:
            - name: ollama-data
              persistentVolumeClaim:
                claimName: ollama-data
    apiVersion: v1
    kind: Service
    metadata:
      name: ollama
      namespace: ollama
    spec:
      selector:
        app: ollama
      ports:
        - name: http
          port: 11434
          targetPort: 11434

    3. Kustomize와 ArgoCD Application으로 묶습니다

    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    namespace: ollama
    resources:
      - namespace.yaml
      - pvc.yaml
      - deployment.yaml
      - service.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: ollama
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://git.example.com/platform.git
        targetRevision: main
        path: apps/ollama
      destination:
        server: https://kubernetes.default.svc
        namespace: ollama
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true
    1. Git 저장소에 Ollama 매니페스트를 커밋합니다.
    2. ArgoCD에 Application을 등록합니다.
    3. 첫 Sync 후 파드와 PVC가 정상 생성되는지 확인합니다.
    4. 그다음 모델 다운로드 전략을 별도로 붙입니다.
    ArgoCD Ollama 배포 설정과 동기화 구성을 표현한 이미지

    ArgoCD에서 애플리케이션이 Synced 상태로 보이고, Ollama Deployment와 PVC가 함께 연결된 구성을 확인하는 장면을 설명하는 이미지 자리입니다.

    4. 초기 검증 명령어

    kubectl get pods -n ollama
    kubectl get pvc -n ollama
    kubectl get svc -n ollama
    kubectl logs -n ollama deploy/ollama

    여기까지 오면 기본 배포는 끝입니다. 드디어 됐다! 싶죠. 근데 진짜 운영은 지금부터입니다 ㅎㅎ

    6개월 운영하면서 효과 컸던 최적화 전략

    이 섹션이 사실 핵심입니다. 단순 배포보다 중요한 건 계속 안정적으로 돌리는 법이거든요. 제가 직접 해보니 아래 네 가지가 체감이 제일 컸습니다.

    스토리지와 모델 캐시를 먼저 설계합니다

    Ollama는 모델 파일 크기 특성상 스토리지 전략이 중요합니다. 저는 초반에 스토리지를 임시 볼륨처럼 다뤘다가, 노드 교체 시 모델을 다시 받느라 시간을 꽤 날렸습니다. 그 뒤로는 모델 저장 경로를 PVC에 고정하고, 이미지 재배포와 모델 캐시를 분리했습니다.

    • PVC 고정: 파드가 재생성돼도 모델 캐시는 유지
    • 노드 디스크 여유 확인: 디스크 압박은 CPU 부족보다 더 먼저 터질 때가 많음
    • 백업 기준 마련: 모델 자체보다 설정과 프롬프트 자산 백업 기준 분리

    리소스 요청값은 보수적으로 시작합니다

    처음부터 크게 잡으면 클러스터 전체 밸런스가 깨집니다. 반대로 너무 작게 잡으면 파드가 뜨더라도 응답이 흔들립니다. 저는 초반에 메모리 요청값을 낮게 잡았다가, 다른 워크로드와 겹치는 시간대에 지연이 튀는 걸 봤습니다. 이후엔 실제 사용 패턴을 보고 천천히 올렸습니다.

    모델 프리로드는 배포 단계와 분리합니다

    이거 진짜 편하더라고요. 배포는 배포대로 끝내고, 모델 다운로드는 Job(잡, 일회성 실행 리소스)이나 운영 스크립트로 분리하니까 실패 지점이 줄었습니다. 특히 ArgoCD Ollama 배포를 여러 환경에 복제할 때 차이가 컸습니다.

    kubectl exec -n ollama deploy/ollama -- ollama pull llama3
    kubectl exec -n ollama deploy/ollama -- ollama list

    모델명은 실제 사용 환경에 맞게 바꾸시면 됩니다. 중요한 건 “어디에서 다운로드를 책임질지”를 명확히 하는 겁니다.

    Ingress와 프록시 타임아웃을 반드시 확인합니다

    LLM 요청은 일반 API보다 응답 시간이 길 수 있습니다. 그래서 Ingress나 리버스 프록시 설정이 보수적이면 중간에서 연결이 끊깁니다. 저는 처음에 앱이 느린 줄 알고 한참 봤는데, 실제 원인은 앞단 타임아웃이었습니다. 이런 건 로그를 함께 봐야 보입니다.

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

    이 부분은 좀 현실적으로 적어보겠습니다. 문서만 보면 다 쉬워 보이는데, 운영에선 꼭 예상 밖 포인트가 나오더라고요.

    문제 1. 파드는 떴는데 모델이 매번 다시 내려받아졌습니다

    원인: 모델 저장 경로가 영구 볼륨에 제대로 붙지 않았거나, 다른 경로를 보고 있었습니다.

    해결: 컨테이너 내부 경로와 volumeMount를 다시 확인했습니다. 그리고 재배포 후에도 같은 PVC가 붙는지 꼭 확인했습니다.

    문제 2. ArgoCD는 Synced인데 실제 동작은 불안정했습니다

    원인: Git 기준 리소스 상태와 애플리케이션 런타임 상태는 다를 수 있습니다. Synced는 선언형 상태 일치이지, 성능 보장까지 해주진 않거든요.

    해결: readiness/liveness보다 먼저 실제 요청 테스트와 로그 수집을 붙였습니다. ArgoCD 상태만 보고 안심하면 안 됩니다.

    문제 3. 노드 이동 후 성능 체감이 달라졌습니다

    원인: 같은 Kubernetes라도 노드 디스크 성능, 메모리 여유, 다른 워크로드 간섭이 다릅니다.

    해결: nodeSelector(노드 셀렉터, 특정 노드 선택)나 taint/toleration(테인트/톨러레이션, 스케줄링 제어)을 검토했고, 최소한 LLM 워크로드가 너무 자주 이사 다니지 않게 잡았습니다.

    문제 4. 급한 핫픽스가 Git과 어긋났습니다

    원인: 운영 중 kubectl edit로 바로 고친 뒤 Git에 반영하지 않았습니다. 며칠 후 ArgoCD 재동기화에서 다시 원래 값으로 돌아가더라고요. 네, 이거 은근 자주 나옵니다.

    해결: 핫픽스 후 바로 Git PR로 반영하는 습관을 들였습니다. Kubernetes GitOps는 결국 Git이 진실 공급원(single source of truth)이니까요.

    ArgoCD Ollama 배포 트러블슈팅 흐름을 설명하는 이미지

    운영 중 자주 만나는 문제인 스토리지 마운트 오류, Git 드리프트, 프록시 타임아웃을 단계별로 추적하는 트러블슈팅 흐름을 설명하는 이미지 자리입니다.

    검증과 결과: 무엇을 기준으로 성공이라고 봤는가

    저는 “파드가 떴다”를 성공으로 보지 않았습니다. 진짜 중요한 건 재배포 후에도 동일하게 동작하는지, 그리고 운영자가 덜 불안한지였거든요.

    제가 보는 검증 체크리스트

    1. ArgoCD에서 애플리케이션이 지속적으로 Synced/Healthy로 유지되는가
    2. 파드 재생성 후에도 기존 모델 캐시가 유지되는가
    3. 간단한 추론 요청이 내부 네트워크에서 안정적으로 응답하는가
    4. 노드 변경이나 롤링 업데이트 후에도 서비스 재현성이 유지되는가
    5. 운영 변경 사항이 모두 Git commit으로 추적되는가
    kubectl rollout restart deploy/ollama -n ollama
    kubectl get pods -n ollama -w
    kubectl exec -n ollama deploy/ollama -- ollama list

    실제로 써보니까, 위 세 줄만으로도 꽤 많은 걸 확인할 수 있었습니다. 롤링 후 파드가 다시 뜨고, 기존 모델이 그대로 보이면 일단 큰 산은 넘은 겁니다. 여기에 사내 서비스나 실험용 앱에서 실제 API 호출까지 붙여보면 더 좋고요.

    ArgoCD Ollama 배포 운영 결과와 안정성을 보여주는 대시보드 이미지

    재배포 이후에도 Ollama 모델 캐시가 유지되고, ArgoCD와 Kubernetes 상태가 안정적으로 보이는 운영 결과 대시보드 이미지 자리입니다.

    운영 관점에서 느낀 장단점

    장점은 명확합니다. ArgoCD Ollama 배포 구조를 한 번 정리해 두면 환경 복제와 복구가 빨라집니다. 특히 여러 사람이 만지는 환경에서는 “누가 뭘 바꿨는지”가 보이는 것만으로도 가치가 큽니다.

    • 장점: 선언형 관리, 재현성, 운영 표준화, 장애 복구 속도
    • 단점: 스토리지와 네트워크를 모르면 초반 진입 장벽이 있음
    • 주의점: Synced 상태와 서비스 품질은 별개라서 모니터링이 반드시 필요

    반대로 단점도 있습니다. 작은 단일 서버 환경에서는 오히려 Kubernetes가 과할 수 있습니다. 그래서 저는 항상 “정말 GitOps가 필요한 규모인가”를 먼저 봅니다. 혼자 잠깐 실험하는 정도라면 Docker Compose로 시작하는 게 더 낫기도 합니다. 하지만 팀이 붙고, 재현성이 필요하고, LLM 클라우드 실험을 계속 이어갈 생각이라면 이야기가 달라집니다.

    정리와 다음 단계

    정리하자면, 6개월 운영 기준으로 가장 중요했던 건 세 가지였습니다. 스토리지 고정, Git 기준 운영, 배포와 모델 관리를 분리. 이 세 가지만 지켜도 안정감이 꽤 올라갑니다. 저도 처음엔 이것저것 한 번에 자동화하려다가 삽질 좀 했습니다 ㅎㅎ 근데 결국 오래 살아남는 구성은 단순하고, 역할이 분리된 구성이더라고요.

    혹시 지금 ArgoCD로 LLM 워크로드를 올리려는 중이신가요? 그렇다면 먼저 작은 범위로 시작해 보세요. Ollama 하나, PVC 하나, Service 하나부터요. 그리고 동작이 확인되면 그다음에 Ingress, 인증, 모니터링을 붙이시면 됩니다. 이전 글에서 다룬 Kubernetes 스토리지 운영 팁과도 연결되는 부분이고, 다음 글에서는 ArgoCD Ollama 배포 뒤에 인증 프록시와 관측성(observability, 관측 가능성) 붙이는 이야기도 정리해보겠습니다.

    배포 전후 운영 복잡도, 모델 캐시 유지 여부, GitOps 적용 효과를 한눈에 비교하는 요약 인포그래픽 이미지 자리입니다.

    자주 묻는 질문

    Q. Ollama를 Kubernetes에 꼭 올려야 하나요?

    아닙니다. 단일 사용자, 단일 서버라면 더 단순한 방법이 맞을 수 있습니다. 다만 재현성과 팀 협업이 중요해지면 Kubernetes와 GitOps가 힘을 발휘합니다.

    Q. GPU가 없으면 의미가 없나요?

    꼭 그렇진 않습니다. 다만 모델 크기와 응답 기대치에 따라 체감이 다릅니다. 저는 처음 검증은 CPU 환경부터 시작했고, 그 과정에서 오히려 스토리지와 운영 흐름을 먼저 정리할 수 있었습니다.

    Q. 운영 후기 기준으로 가장 먼저 볼 지표는 뭔가요?

    저는 순서가 이렇습니다. 파드 상태, PVC 유지 여부, 실제 요청 성공, 재배포 후 재현성. 화려한 대시보드보다 이 네 가지가 먼저입니다.

  • [K8s] Karpenter 온프레미스 도입의 효율성 검증 및 실전 분석

    [K8s] Karpenter 온프레미스 도입의 효율성 검증 및 실전 분석

    [K8s] Karpenter 온프레미스 도입의 효율성 검증 및 실전 분석

    Karpenter 온프레미스가 얼마나 효율적일까—이 질문은 쿠버네티스(Kubernetes, 컨테이너 오케스트레이션)를 운영하다 보면 자연스럽게 나오더라고요. 노드(Node, 작업이 실제로 돌아가는 서버)까지 더 똑똑하게 자동으로 늘리고 줄일 수 없을까 하는 생각 말입니다. 저도 홈랩과 실무에서 이런 고민을 꽤 했었어요. 처음엔 이게 뭔가 싶었는데, 막상 파고들어 보니 Karpenter는 분명 매력적이면서도, 온프레미스에서는 생각보다 전제 조건이 많더라고요.

    결론부터 말씀드리면, Karpenter 온프레미스는 “기술적으로 이름만 가져와서 붙이는 문제”가 아니라 서버 수명주기(lifecycle), 네트워크, IP 주소 할당, OS 부팅 자동화까지 전부 자동화돼 있어야 진정한 효율이 나오는 구조입니다. 이 글에서는 제가 왜 그렇게 판단했는지, 어떤 식으로 검증했고 어디서 막혔는지, 그리고 어떤 팀에 맞고 어떤 팀에는 안 맞는지 정리해보겠습니다.

    Karpenter 온프레미스 도입 검토를 위한 전체 아키텍처 개요 이미지

    온프레미스 쿠버네티스 클러스터에서 스케줄러, Pending Pod, 노드 프로비저닝 흐름을 한눈에 보여주는 개요 이미지가 들어갈 자리입니다.

    Karpenter란 무엇인가, 쉽게 말해

    쉽게 말해 Karpenter는 스케줄링되지 못한 파드(Pod, 쿠버네티스의 실행 단위)를 보고, 거기에 맞는 노드를 빠르게 만들어 붙이는 데 초점을 둔 도구입니다. 기존 Cluster Autoscaler가 노드 그룹(Node Group) 중심으로 움직였다면, Karpenter는 워크로드 요구사항 중심으로 더 유연하게 판단하는 느낌이 훨씬 강하죠.

    공식 문서 기준으로 많이 다뤄지는 프로바이더(provider, 인프라 연동 구현체)는 AWS, Azure, Alibaba Cloud 쪽입니다. 여기서 중요한 포인트! Karpenter의 핵심 아이디어는 범용적이지만, 실제 노드를 만들어내는 동작은 프로바이더 구현에 크게 의존합니다. 즉, 온프레미스에서 “Karpenter를 쓴다”는 말은 결국 내 환경에 맞는 프로비저닝 백엔드를 어떻게 연결하느냐의 문제거든요.

    Karpenter 온프레미스가 매력적으로 보이는 이유

    솔직히 말해서, 아이디어만 보면 정말 좋습니다. 저도 처음엔 “이거 진짜 편하겠는데?” 싶었거든요.

    • 빈 노드 상시 대기를 줄일 수 있습니다.
    • 워크로드별로 CPU, 메모리, 아키텍처, taint/toleration 조건을 세밀하게 나눌 수 있습니다.
    • 배치 작업(Batch Job)이나 CI 러너(Runner)처럼 들쭉날쭉한 부하에 정말 잘 맞습니다.
    • Karpenter 효율성 관점에서 보면, 과하게 큰 노드를 오래 유지하지 않아도 된다는 기대가 생기죠.

    특히 온프레미스는 클라우드처럼 분 단위 과금은 아니어도, 전력, 랙 공간, 라이선스, 예비 장비 운용비가 은근히 큽니다. 그래서 온프레미스 오토스케일링에 관심이 가는 건 아주 자연스러운 흐름이에요.

    하지만 왜 온프레미스에서는 바로 풀리지 않나

    여기서부터 현실이 시작됩니다. 퍼블릭 클라우드에서는 API 한 번으로 가상머신(VM, 가상 서버)을 만들고, 네트워크도 붙이고, IAM 권한도 맞추고, 부팅 후 조인(join)까지 이어집니다. 반면 온프레미스는 보통 이게 한 덩어리로 묶여 있지 않습니다. 제가 직접 검토해보니, 진짜 어려운 건 Karpenter 자체보다 주변 자동화의 빈틈이더라고요.

    항목 퍼블릭 클라우드 온프레미스
    서버 생성 API로 즉시 생성 가상화 플랫폼 또는 베어메탈 절차 연동 필요
    네트워크 기본 VPC/서브넷 모델 존재 VLAN, IPAM, DHCP, DNS를 따로 맞춰야 함
    부팅/조인 이미지와 userdata로 표준화 PXE, 템플릿, 클라우드이닛 대체 절차 필요
    폐기 종료 API로 간단 디스크 정리, 모니터링 해제, CMDB 반영까지 고려
    확장 속도 대체로 빠름 사내 자동화 성숙도에 따라 편차 큼

    그래서 Karpenter 사용 후기를 물어보면, 저는 항상 이렇게 답합니다. “노드를 만드는 API가 이미 예쁘게 정리된 팀이라면 검토할 만하고, 아니면 Karpenter보다 선행 과제가 더 큽니다.”

    실전 구현: 제가 검증할 때 먼저 했던 순서

    저는 Karpenter를 바로 설치해서 결론 내리지 않았습니다. 먼저 정말 노드 자동 확장이 필요한 패턴인지부터 확인했거든요. 이거 안 하고 시작하면 높은 확률로 삽질하게 됩니다.

    1. Pending Pod가 얼마나 자주 생기는지 확인합니다.
    2. 그 Pending이 CPU/메모리 부족인지, taint/toleration 문제인지 구분합니다.
    3. 노드 추가 시간이 실제 서비스 요구와 맞는지 봅니다.
    4. 온프레미스에서 노드 생성 API가 있는지, 없으면 누가 그 역할을 할지 정합니다.

    1. 병목부터 확인하기

    kubectl get pods -A --field-selector=status.phase=Pending
    kubectl get events -A --sort-by=.lastTimestamp
    kubectl describe pod -n default cpu-burner-0
    kubectl top nodes
    

    이 단계에서 FailedScheduling 이벤트를 꼭 봐야 합니다. 실제로 써보니까 “노드가 부족해서”가 아니라 노드 셀렉터(nodeSelector), 어피니티(affinity), 테인트(taint) 때문에 못 붙는 경우가 꽤 많더라고요.

    2. 의도적으로 스케줄 압박 만들기

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: cpu-burner
      namespace: default
    spec:
      replicas: 6
      selector:
        matchLabels:
          app: cpu-burner
      template:
        metadata:
          labels:
            app: cpu-burner
        spec:
          containers:
            - name: pause
              image: registry.k8s.io/pause:3.9
              resources:
                requests:
                  cpu: "4"
                  memory: "4Gi"
                limits:
                  cpu: "4"
                  memory: "4Gi"
    

    이런 식으로 일부러 Pending 상황을 만들고, 노드가 추가됐을 때 실제로 대기 시간이 얼마나 줄어드는지 봤습니다. 이 단계가 없으면 Karpenter 효율성을 논하기가 정말 어렵거든요.

    Karpenter 온프레미스 환경에서 Pending Pod와 노드 증설 흐름을 보여주는 구성 이미지

    Pending Pod 감지, 스케줄러 판단, 노드 프로비저닝 요청 흐름을 시각화한 구성 다이어그램이 들어갈 자리입니다.

    3. 지원 환경에서 기준선 만들기

    여기서 중요한 포인트가 하나 더 있습니다. Karpenter 동작 모델을 이해하려면, 우선 공식적으로 많이 다뤄지는 환경에서 기준선을 봐야 한다는 거예요. 예를 들어 AWS 계열에서는 NodePool, NodeClass 같은 리소스로 요구사항을 선언합니다. 온프레미스에서는 이 선언형 모델을 흉내 내더라도, 실제 서버 생성과 회수는 별도 자동화가 뒷받침돼야 합니다.

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: general
    spec:
      template:
        spec:
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
          expireAfter: 720h
      disruption:
        consolidationPolicy: WhenEmptyOrUnderutilized
    

    이 YAML 자체보다 중요한 건, 온프레미스에서는 이 선언을 받아줄 실제 인프라 제어면(control plane)이 있느냐입니다. VMware vSphere, OpenStack, Proxmox, 베어메탈 PXE 자동화, BMC 연동 같은 것들이 이미 정리돼 있어야 이야기가 될 수 있거든요.

    ⚠️ 트러블슈팅: 제가 막혔던 지점들

    여기는 정말 경험상 중요합니다. 문서만 보면 쉬워 보이는데, 실제 운영에서는 대부분 여기서 시간이 날아갑니다.

    1. 노드 생성보다 노드 조인이 더 오래 걸림

    VM이 떠도 kubelet(큐블릿, 노드를 클러스터에 붙이는 에이전트) 조인이 늦으면 의미가 없습니다. 이미지 템플릿, 컨테이너 런타임, CNI(Container Network Interface, 컨테이너 네트워크), 인증서 부트스트랩이 전부 맞아야 하거든요.

    2. IPAM이 병목이 됨

    온프레미스 오토스케일링에서 자주 빠지는 함정입니다. 서버는 생겼는데 IP가 없거나, DNS 등록이 늦거나, 방화벽 정책 반영이 늦으면 결국 Pod는 계속 Pending 상태가 됩니다. 드디어 됐다! 싶었는데 네트워크 쪽에서 막히는 경우, 진짜 많습니다.

    3. 회수(deprovision) 정책이 더 민감함

    클라우드에서는 종료가 비교적 단순하지만, 온프레미스는 로그 수집, 백업, 모니터링 타깃 해제, 자산관리 반영까지 연결될 수 있습니다. 그래서 비용 최적화보다 운영 일관성이 더 우선인 팀도 많아요.

    4. 베어메탈은 더 신중해야 함

    가상머신은 그래도 템플릿 자동화가 익숙한 편인데, 물리 서버는 전원 제어, PXE, 디스크 초기화, 펌웨어 상태까지 섞입니다. 여기서는 Karpenter보다 먼저 서버 프로비저닝 파이프라인이 안정적이어야 해요.

    검증 결과: 그래서 효율적이었나

    제 결론은 꽤 단순합니다. Karpenter 온프레미스는 “이론상 가능”과 “운영상 효율적” 사이의 간격이 생각보다 크다는 거예요.

    • 효율적인 경우: VM 생성 API가 있고, 네트워크 자동화가 있으며, 노드 조인 시간이 짧고, 워크로드 변동성이 큽니다.
    • 비효율적인 경우: 서버 준비 시간이 길고, 수동 승인 절차가 많고, IP/DNS/보안정책 반영이 느립니다.
    • 애매한 경우: 항상 켜둬야 하는 기본 용량(base capacity)이 큰데, 추가 증설 빈도는 낮은 환경입니다.

    실제로 써보니까 Karpenter 자체는 정말 똑똑한데, 온프레미스에선 그 똑똑함이 발휘될 무대가 준비돼 있어야 하더라고요. 무대가 없으면 결국 “자동화된 척하는 수동 시스템”이 되기 쉽습니다.

    Karpenter 온프레미스 검증 결과를 보여주는 대시보드 이미지

    PoC 전후로 Pending Pod 수, 노드 사용률, 증설 후 조인 시간을 비교하는 대시보드 이미지가 들어갈 자리입니다.

    대안과 비교: 굳이 Karpenter여야 하나

    이 질문도 꼭 해봐야 합니다. 저도 처음엔 Karpenter에 시선이 갔는데, 나중엔 오히려 문제 정의를 다시 하는 게 더 중요하다고 느꼈거든요.

    선택지 잘 맞는 상황 한계
    Karpenter 인프라 API와 노드 자동화가 이미 성숙한 환경 주변 자동화 미성숙 시 효과 반감
    Cluster Autoscaler 기존 노드 그룹 운영이 익숙한 환경 세밀한 유연성은 상대적으로 제한
    고정 노드 + 요청/제한 재정비 부하 패턴이 예측 가능한 환경 유휴 자원 증가 가능
    배치 전용 풀 분리 CI, 렌더링, 분석 잡이 분리 가능한 환경 운영 구조가 다소 복잡해짐

    혹시 이런 경험 있으신가요? 노드가 부족한 줄 알았는데, 알고 보니 리소스 요청값(request)이 너무 공격적으로 잡혀 있었던 경우요. 이런 환경이면 Karpenter보다 먼저 requests/limits, affinity, PDB(PodDisruptionBudget, 중단 허용 범위)를 정리하는 쪽이 체감 효과가 훨씬 크더라고요.

    정리: 제가 내린 판단

    Karpenter 온프레미스는 분명 흥미로운 주제입니다. 다만 “쿠버네티스 노드 오토스케일링 도구 하나 더 설치하면 끝” 같은 그림으로 접근하면 거의 실패합니다. 제가 회고해보면, 성공 조건은 세 가지였어요.

    1. 노드 생성 API가 일관돼 있어야 합니다.
    2. 네트워크/IPAM/보안 정책 반영이 자동화돼 있어야 합니다.
    3. 노드 조인 시간이 서비스 요구를 만족해야 합니다.

    이 세 가지가 되면 Karpenter 효율성은 충분히 검토할 가치가 있습니다. 반대로 이 중 둘 이상이 수동이면, Karpenter는 멋진 이름이고 운영은 더 복잡해질 가능성이 크거든요. 그래서 저는 온프레미스에서 Karpenter를 볼 때 항상 도구보다 자동화 성숙도를 먼저 봅니다.

    Karpenter 온프레미스 도입 적합성과 대안을 정리한 요약 인포그래픽

    Karpenter 도입 적합성 체크리스트와 대안 선택 기준을 한 장으로 요약한 인포그래픽이 들어갈 자리입니다.

    FAQ와 다음 단계

    Q1. 온프레미스에서 Karpenter를 아예 못 쓰는 건가요?

    아예 불가능하다는 뜻은 아닙니다. 다만 공식적으로 널리 소개되는 흐름은 주로 클라우드 프로바이더 중심이고, 온프레미스에서는 프로비저닝 연동을 직접 설계해야 하는 비중이 훨씬 커요.

    Q2. 어떤 팀에 추천하나요?

    가상화 API, 템플릿 배포, DHCP/DNS/IPAM, 쿠버네티스 조인이 이미 자동화된 팀이라면 충분히 검토할 만합니다.

    Q3. 어떤 팀에는 비추천인가요?

    노드 한 대 늘릴 때 승인 절차가 여러 단계거나, 보안 정책 반영이 수동이면 비추천입니다. 이 경우엔 고정 노드 전략과 워크로드 정리가 훨씬 더 현실적이거든요.

    이전 글에서 다뤘던 리소스 요청값 튜닝과 스케줄링 정책 정리도 같이 보시면 정말 도움이 됩니다. 다음 글에서는 온프레미스 오토스케일링 관점에서 Karpenter 대신 어떤 선택지가 더 실용적인지, Cluster Autoscaler와 고정 노드 전략을 비교해볼 예정입니다.

    참고할 공식 자료

  • [k8s] Kubeadm vs ClusterAPI: Kubernetes 클러스터 프로비저닝 도구 비교

    [k8s] Kubeadm vs ClusterAPI: Kubernetes 클러스터 프로비저닝 도구 비교

    Kubeadm vs ClusterAPI: Kubernetes 클러스터 프로비저닝 도구 비교

    Kubeadm과 ClusterAPI를 비교할 때, 많은 분들이 같은 지점에서 막혀 있더라고요. “클러스터 하나 빨리 올리면 되는 건가?”, 아니면 “여러 환경에서 반복 가능하게 클러스터 구축 체계를 만들어야 하나?” 같은 고민 말이에요. 저도 홈랩에서 처음엔 kubeadm(쿠버네티스 클러스터 초기화 도구)만으로 시작했었는데, 노드 수가 늘고 재현 가능한 배포가 필요해지니까 Cluster API(CAPI, 쿠버네티스 방식으로 클러스터 자체를 관리하는 프로젝트) 쪽이 갑자기 눈에 들어오더라고요. 처음엔 이게 뭔가 싶었습니다. 컨트롤러도 많고, 프로바이더도 많고, YAML도 길고요. 근데 구조를 한 번 이해하고 나니 왜 운영팀들이 이 조합을 진지하게 보는지 감이 왔습니다.

    이 글에서는 단순히 기능 나열만 하지 않고, Kubeadm vs ClusterAPI를 실제 운영 관점에서 풀어보겠습니다. 쉽게 말해, kubeadm은 클러스터를 시작하게 해주는 공구에 가깝고, ClusterAPI는 클러스터 생애주기 자체를 선언형으로 다루는 운영 프레임워크에 가깝습니다. 여기서 중요한 포인트! 둘은 경쟁 관계이면서도, 실전에서는 같이 엮이는 경우가 꽤 많습니다.

    Kubeadm ClusterAPI 비교를 보여주는 쿠버네티스 클러스터 프로비저닝 아키텍처 이미지

    kubeadm은 개별 클러스터 부트스트랩 흐름에, Cluster API는 관리 클러스터에서 워크로드 클러스터를 선언형으로 제어하는 흐름에 초점이 있습니다.

    Kubernetes 클러스터 프로비저닝, 뭐가 다른가요?

    쉽게 말해 kubeadm은 노드에 들어가서 클러스터를 만드는 도구입니다. 컨트롤 플레인(클러스터 제어 영역) 초기화, 조인 토큰 생성, 인증서 배포 같은 부트스트랩 작업을 잘 해주죠. 반면 Cluster API는 클러스터를 쿠버네티스 리소스처럼 다루는 방식입니다. 즉, “이런 모양의 클러스터를 만들어줘”라고 선언하면 컨트롤러가 그 상태를 맞추려고 움직입니다.

    제가 직접 해보니 kubeadm은 구조가 비교적 단순해서 학습 진입 장벽이 낮았습니다. 특히 베어메탈(가상화 없이 물리 서버 직접 운영)이나 홈랩처럼 손으로 만질 수 있는 환경에서는 진짜 빠릅니다. 반대로 Cluster API는 관리 클러스터(다른 클러스터를 제어하는 클러스터)라는 개념부터 잡아야 해서 초반 허들이 있습니다. 대신 익숙해지면 반복 배포, 업그레이드, 스케일링, 클러스터 폐기까지 흐름이 꽤 깔끔해집니다.

    • kubeadm: 단일 클러스터 프로비저닝에 강함
    • Cluster API: 여러 클러스터의 선언형 운영과 생애주기 관리에 강함
    • CAPI + kubeadm: Cluster API가 kubeadm 기반 부트스트랩을 활용하는 조합이 현실

    Kubeadm vs ClusterAPI 핵심 구조 비교

    비교 항목 kubeadm Cluster API
    주요 목적 쿠버네티스 클러스터 초기 구성 클러스터 생성, 변경, 업그레이드, 삭제까지 생애주기 관리
    운영 방식 명령형(명령 실행 중심) 선언형(원하는 상태 기술)
    주요 실행 위치 대상 노드 관리 클러스터의 컨트롤러
    멀티 클러스터 운영 수동 설계 필요 기본 개념 자체가 멀티 클러스터 친화적
    인프라 연동 직접 구성 필요 Infrastructure Provider(인프라 프로바이더)로 연동
    업그레이드 자동화 운영 스크립트나 절차 설계 필요 버전 선언 후 컨트롤러 기반 롤링 변경 가능
    초기 난이도 상대적으로 낮음 상대적으로 높음

    여기서 중요한 포인트! Cluster API가 kubeadm을 대체하는 느낌으로 보일 수 있는데, 실제로는 Cluster API 안에서 kubeadm bootstrap provider를 사용하는 사례가 많습니다. 즉, kubeadm은 여전히 중요한 구성요소고, Cluster API는 그걸 더 큰 자동화 체계 안에 넣는 느낌이라고 보시면 이해가 편합니다.

    언제 kubeadm이 더 잘 맞을까요?

    • 처음 쿠버네티스 구조를 익히는 단계일 때
    • 노드 수가 적고 클러스터 수가 많지 않을 때
    • 베어메탈이나 제한된 홈랩 환경에서 빠르게 검증할 때
    • 구성 과정을 손으로 제어해야 안심되는 팀 문화일 때

    언제 Cluster API가 빛날까요?

    • 동일한 형태의 클러스터를 반복 생성해야 할 때
    • 개발, 스테이징, 운영 환경을 비슷한 클러스터 구축 템플릿으로 관리할 때
    • 클러스터 업그레이드와 노드 교체를 체계화하고 싶을 때
    • GitOps(Git 기반 선언형 운영)와 잘 엮고 싶을 때

    실전 구현 1: kubeadm으로 클러스터 올리기

    먼저 kubeadm 흐름부터 보겠습니다. 실제로 써보니까 kubeadm은 클러스터 내부 동작을 이해하는 데 정말 좋았습니다. 인증서, kubelet(노드 에이전트), CNI(컨테이너 네트워크 플러그인) 같은 개념이 어디서 필요한지 몸으로 익히게 되거든요. 다만 그만큼 수동 단계도 많습니다. 삽질 좀 했습니다 ㅎㅎ

    1. 런타임(container runtime)과 kubelet, kubeadm, kubectl 설치
    2. 컨트롤 플레인 노드에서 초기화
    3. CNI 설치
    4. 워커 노드 조인
    5. 상태 검증
    sudo kubeadm init \
      --pod-network-cidr=192.168.0.0/16
    
    mkdir -p $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    kubectl get nodes

    초기화가 끝나면 바로 Ready가 안 뜰 수 있습니다. 여기서 당황 많이 하시더라고요. 대부분은 아직 CNI가 없어서 그렇습니다.

    kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/master/manifests/calico.yaml
    kubectl get pods -A

    워커 노드 조인은 `kubeadm token create –print-join-command`로 받은 명령을 그대로 실행하면 됩니다.

    kubeadm token create --print-join-command

    이 방식의 장점은 명확합니다. 어디서 뭐가 실패하는지 눈에 보입니다. 반대로 단점도 명확해요. 같은 클러스터를 세 번, 네 번 반복해서 만들다 보면 사람이 실수할 지점이 계속 생깁니다. 바로 이 지점에서 “클러스터 구축 도구를 더 선언형으로 가져가야 하나?”라는 생각이 들기 시작하더라고요.

    실전 구현 2: Cluster API로 선언형 클러스터 만들기

    이제 Cluster API 쪽입니다. 처음엔 관리 클러스터가 또 필요하다고 해서 부담스러웠는데, 실제 구조를 이해하고 나니 왜 필요한지 납득됐습니다. Cluster API는 몇 가지 핵심 컴포넌트로 나뉩니다.

    • Core Provider: Cluster API 핵심 리소스와 컨트롤러
    • Bootstrap Provider: 노드 초기화 데이터 생성(kubeadm 활용)
    • Control Plane Provider: 컨트롤 플레인 구성 관리
    • Infrastructure Provider: 실제 VM, 인스턴스, 머신, 네트워크 리소스 생성

    기본 흐름은 이렇습니다. 관리 클러스터에 Cluster API 컴포넌트를 설치하고, 그 위에 워크로드 클러스터(실제 애플리케이션이 올라갈 대상 클러스터)의 정의를 YAML로 적용합니다.

    clusterctl init --infrastructure <provider-name>

    프로바이더 이름은 사용하는 환경에 따라 달라집니다. 예를 들어 퍼블릭 클라우드, 프라이빗 클라우드, 베어메탈 쪽에서 선택지가 갈리죠. 여기서는 원리를 이해하는 데 집중하겠습니다.

    Cluster API 관리 클러스터와 워크로드 클러스터 구조를 설명하는 Kubeadm ClusterAPI 비교 이미지

    관리 클러스터에서 컨트롤러가 동작하고, 선언된 리소스를 바탕으로 워크로드 클러스터의 머신과 컨트롤 플레인을 조립하는 구조입니다.

    apiVersion: cluster.x-k8s.io/v1beta1
    kind: Cluster
    metadata:
      name: demo-cluster
    spec:
      controlPlaneRef:
        apiVersion: controlplane.cluster.x-k8s.io/v1beta1
        kind: KubeadmControlPlane
        name: demo-control-plane
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: GenericInfrastructureCluster
        name: demo-infra
    ---
    apiVersion: controlplane.cluster.x-k8s.io/v1beta1
    kind: KubeadmControlPlane
    metadata:
      name: demo-control-plane
    spec:
      replicas: 1
      version: v1.29.0
      kubeadmConfigSpec:
        clusterConfiguration: {}
        initConfiguration: {}
        joinConfiguration: {}
    ---
    apiVersion: cluster.x-k8s.io/v1beta1
    kind: MachineDeployment
    metadata:
      name: demo-md-0
    spec:
      clusterName: demo-cluster
      replicas: 2
      selector:
        matchLabels: {}
      template:
        spec:
          version: v1.29.0
          clusterName: demo-cluster

    위 YAML은 Cluster API 구조 예시입니다. 실제 배포에서는 인프라 프로바이더 리소스가 더 들어가고, 환경 변수나 자격 증명도 필요합니다. 중요한 건 클러스터를 명령으로 만드는 게 아니라 상태로 선언한다는 점입니다. 이게 Cluster API의 본질이에요.

    kubectl apply -f cluster.yaml
    kubectl get cluster
    kubectl get machinedeployments
    kubectl get machines

    제가 직접 해보니 여기서 재미있는 포인트가 있었습니다. kubeadm은 한 번 만들고 나면 이후 운영 자동화는 별도로 설계해야 하는데, Cluster API는 처음부터 “운영까지 생각한 구조”라는 느낌이 강합니다. 대신 디버깅은 더 추상적입니다. 노드 한 대에서 끝나는 문제가 아니라 컨트롤러, 프로바이더, 템플릿이 다 얽혀 있거든요.

    주의사항과 트러블슈팅: 실제로 많이 막히는 지점

    ⚠️ 여기 진짜 중요합니다. Kubeadm ClusterAPI 비교에서 많은 글이 장점만 말하는데, 실전에서는 막히는 지점이 선택 기준이 되더라고요.

    1. kubeadm은 CNI 전까지 정상처럼 보여도 정상 아닐 수 있습니다

    `kubeadm init`이 끝났다고 클러스터가 다 살아난 건 아닙니다. `kubectl get nodes`에서 `NotReady`가 보이면 CNI부터 의심하세요. 저도 처음엔 인증서 문제인 줄 알고 엉뚱한 로그만 한참 봤었습니다.

    kubectl describe node
    kubectl get pods -A
    journalctl -u kubelet

    2. Cluster API는 실패 지점이 더 분산됩니다

    예를 들어 머신이 안 생기면 인프라 프로바이더 문제인지, bootstrap 데이터 생성 문제인지, control plane reconciliation 문제인지 나눠서 봐야 합니다.

    kubectl get pods -A
    kubectl get clusters,machines,machinedeployments,kubeadmcontrolplanes
    kubectl describe machine <machine-name>
    kubectl logs -n capi-system deployment/capi-controller-manager

    혹시 이런 경험 있으신가요? 리소스는 분명히 생성됐는데 상태가 계속 `Provisioning`에서 안 넘어가는 경우요. 이럴 땐 대부분 인프라 쪽 자격 증명, 이미지 템플릿, 네트워크 전제조건이 빠져 있는 경우가 많았습니다.

    3. kubeadm은 단순한 대신 표준화가 흔들리기 쉽습니다

    운영자가 둘 이상만 돼도 설치 옵션, OS 준비 상태, 네트워크 플러그인 선택이 조금씩 달라집니다. 결국 문서화와 스크립트화가 필수입니다. 즉, kubeadm이 간단하다고 해서 운영 체계까지 단순한 건 아니더라고요.

    4. Cluster API는 관리 클러스터 자체의 안정성이 중요합니다

    관리 클러스터가 흔들리면 워크로드 클러스터 변경 작업도 영향받습니다. 그래서 초기에 “관리 클러스터를 어디에 둘 것인가”를 꽤 신중하게 봐야 합니다. 홈랩에서는 한 대에 몰아넣으면 편하긴 한데, 장애 분리 측면에서는 아쉬움이 남습니다.

    검증과 결과 확인: 뭐가 더 운영 친화적인가

    검증은 단순히 `kubectl get nodes`만 보면 부족합니다. 생성, 변경, 교체, 삭제 흐름까지 봐야 합니다. 이 부분이 Kubernetes 클러스터 프로비저닝 도구를 비교할 때 핵심이거든요.

    1. 클러스터 최초 생성 시간과 절차 복잡도 확인
    2. 워커 노드 확장 방식 확인
    3. 버전 변경 시 운영 개입량 확인
    4. 장애 시 복구 절차 문서화 가능성 확인
    kubectl get nodes -o wide
    kubectl get cluster
    kubectl get machines
    kubectl get events --sort-by=.lastTimestamp
    Kubeadm ClusterAPI 비교 결과를 검증하는 쿠버네티스 상태 확인 이미지

    노드 Ready 상태, Machine 리소스 변화, 이벤트 흐름을 통해 선언한 상태와 실제 상태가 맞아가는지 확인하는 장면을 상상하시면 됩니다.

    실제로 써보니까 결과는 꽤 분명했습니다. 하나의 클러스터를 깊게 이해하고 빠르게 구축하려면 kubeadm이 좋았고, 반복 가능한 다수의 클러스터를 체계적으로 관리하려면 Cluster API가 훨씬 운영 친화적이었습니다. 특히 업그레이드나 머신 교체 시나리오를 생각하면 차이가 더 벌어집니다.

    상황별 선택 가이드: 클러스터 구축 도구 어떤 게 맞을까

    상황 추천 이유
    홈랩에서 쿠버네티스 구조 학습 kubeadm 구성 요소를 직접 체감하기 좋음
    단일 운영 클러스터를 신중하게 수동 관리 kubeadm 절차가 명확하고 제어감이 높음
    여러 환경에 동일한 클러스터 배포 Cluster API 선언형 템플릿 재사용이 쉬움
    업그레이드/교체 자동화를 운영 정책으로 추진 Cluster API 생애주기 관리 철학에 잘 맞음
    베어메탈 자동화까지 포함한 확장 설계 환경에 따라 다름 인프라 프로바이더 성숙도와 운영 역량이 중요
    • 빠른 시작과 학습이 목표면 kubeadm
    • 반복성과 선언형 운영이 목표면 Cluster API
    • 둘 중 하나만 고집할 필요는 없음: kubeadm으로 원리 이해 후 CAPI로 확장하는 경로가 현실적
    Kubeadm ClusterAPI 비교 선택 기준을 정리한 요약 인포그래픽

    팀 규모, 클러스터 수, 자동화 요구사항, 관리 방식에 따라 어떤 도구가 더 어울리는지 한눈에 정리한 비교 이미지입니다.

    자주 묻는 질문

    Q1. Cluster API가 있으면 kubeadm은 이제 안 써도 되나요?

    그렇진 않습니다. 오히려 Cluster API 내부에서 kubeadm 기반 부트스트랩을 활용하는 경우가 흔합니다. kubeadm은 여전히 중요한 기반 기술입니다.

    Q2. 소규모 팀도 Cluster API를 바로 도입해야 할까요?

    반드시 그렇진 않습니다. 클러스터 수가 적고 운영자가 구조를 아직 익히는 단계라면 kubeadm이 더 현실적일 수 있습니다. 자동화 비용보다 복잡도 비용이 더 클 수 있거든요.

    Q3. GitOps와 더 잘 맞는 쪽은 무엇인가요?

    Cluster API가 더 자연스럽습니다. 클러스터 정의 자체를 리소스로 관리하니까 Git 저장소 기반 운영 모델과 궁합이 좋습니다.

    마무리: 현실적인 접근

    정리해보면 이렇습니다. Kubeadm vs ClusterAPI 비교는 단순한 우열 비교가 아니라, 운영 성숙도와 목표가 다른 두 접근입니다. 제가 직접 해보니 kubeadm은 쿠버네티스를 제대로 이해하게 해주는 좋은 선생님이었고, Cluster API는 그 다음 단계에서 운영 자동화를 체계로 바꿔주는 도구였습니다. 둘 중 하나가 무조건 정답은 아니었습니다.

    개인적으로는 이런 순서를 추천드립니다. 처음엔 kubeadm으로 컨트롤 플레인 초기화, CNI 연결, 노드 조인, 인증서 구조를 손으로 한 번 겪어보세요. 그다음 반복 배포가 필요해지는 시점에 Cluster API를 붙이면 훨씬 덜 헷갈립니다. 저도 처음엔 YAML만 보고 멍했었는데, 기반 개념을 알고 나니까 드디어 됐다! 싶은 순간이 오더라고요.

    다음 글에서는 kubeadm으로 만든 클러스터를 운영 표준화하는 방법이나, Cluster API와 GitOps를 연결하는 흐름을 따로 다뤄보겠습니다. 이전 글에서 다룬 네트워크 플러그인 비교나 Ingress(외부 트래픽 진입점) 구성 글과 함께 보시면 더 이해가 잘 되실 겁니다.

    처음엔 수동 구축으로 원리를 익히고, 이후 선언형 운영으로 넘어가는 학습 및 운영 로드맵을 보여주는 마무리 이미지입니다.

  • [k8s] Karpenter 오토스케일링 문제 해결: 노드 프로비저닝 실패 디버깅 가이드

    [k8s] Karpenter 오토스케일링 문제 해결: 노드 프로비저닝 실패 디버깅 가이드

    [Kubernetes] Karpenter 문제 해결: 노드 프로비저닝 실패 디버깅 가이드

    Karpenter 문제 해결 때문에 밤에 이벤트 로그 붙잡고 계신 적, 한 번쯤 있으시죠? 저도 처음엔 “오토스케일링은 붙여놓으면 알아서 되겠지” 했다가, 정작 트래픽이 몰리는 순간 Pod(파드, 쿠버네티스의 최소 배포 단위)는 Pending(펜딩, 스케줄 대기)인데 노드는 안 뜨고, Karpenter 로그는 뭔가 말은 많은데 핵심은 안 보이는 상황을 여러 번 겪었습니다. 특히 Kubernetes 노드 프로비저닝이 실패하면 서비스 지연이 바로 드러나기 때문에, 이 구간은 감으로 보면 안 되더라고요.

    이번 글은 AWS 환경에서 Karpenter를 운영할 때 자주 만나는 오토스케일링 실패 패턴을 기준으로 정리했습니다. 제가 홈랩과 실무 환경에서 직접 부딪히며 정리한 흐름이라, 문서만 읽었을 때보다 훨씬 빠르게 원인을 좁히는 데 도움이 되실 겁니다. 핵심은 하나입니다. Karpenter 디버깅은 “Pending Pod → 스케줄링 조건 → Karpenter 로그 → 클라우드 리소스” 순서로 봐야 한다는 점입니다.

    Karpenter 문제 해결을 위한 오토스케일링 아키텍처 개요 이미지

    Pending Pod, Karpenter controller, AWS 인프라 리소스가 어떻게 연결되는지 보여주는 전체 흐름도입니다.

    Karpenter 문제 해결, 왜 이렇게 헷갈릴까요?

    쉽게 말해 Karpenter는 “Pod가 요구하는 조건에 맞춰 새 노드를 빠르게 계산하고 띄워주는 컨트롤러”입니다. 그런데 실제로는 중간에 확인할 레이어가 꽤 많습니다. Kubernetes scheduler(스케줄러), Karpenter controller(컨트롤러), IAM(권한 체계), subnet/security group(서브넷/보안 그룹), instance type(인스턴스 타입) 조건, 그리고 quota(할당량)까지 다 연결돼 있거든요.

    저도 처음엔 Karpenter 로그만 뒤졌습니다. 근데 실제로 써보니까 문제의 출발점은 의외로 Pod 스펙인 경우가 많더라고요. 예를 들어 CPU 요청량이 과하게 잡혀 있거나, 특정 아키텍처만 허용했거나, taint/toleration(테인트/톨러레이션) 조합이 안 맞으면 Karpenter가 아무리 열심히 계산해도 답이 없습니다.

    • Pod 조건 문제: requests/limits, nodeSelector, affinity, toleration 충돌
    • Karpenter 설정 문제: NodePool(노드풀) 또는 기존 Provisioner(프로비저너) 제약이 너무 빡빡함
    • AWS 리소스 문제: IAM 누락, 태그 미설정, 서브넷 발견 실패
    • 용량 문제: 가용 영역 용량 부족, 인스턴스 타입 제한 과다, 계정 할당량 부족

    여기서 중요한 포인트! Karpenter 문제 해결은 “왜 노드가 안 떴지?”가 아니라 “왜 이 Pod를 만족하는 노드를 계산하지 못했지?”로 질문을 바꾸면 훨씬 빨라집니다.

    Karpenter 디버깅의 기본 원리

    제가 실제로 보는 순서는 거의 고정입니다. 삽질 좀 했습니다 ㅎㅎ 결국 순서를 정해두는 게 제일 낫더라고요.

    1. Pending 상태의 Pod를 확인합니다.
    2. 해당 Pod의 이벤트와 스케줄링 실패 메시지를 읽습니다.
    3. Karpenter controller 로그에서 같은 시점의 에러를 확인합니다.
    4. NodePool/EC2NodeClass 또는 기존 Provisioner/AWSNodeTemplate 조건을 검토합니다.
    5. AWS 측 리소스 발견, 권한, 용량 문제를 점검합니다.

    이 흐름을 안 지키면 로그는 잔뜩 보는데 정작 원인을 놓치기 쉽습니다. 특히 이벤트 메시지 한 줄이 전체 방향을 정해주는 경우가 많아요.

    Karpenter 디버깅 1단계: Pending Pod부터 확인하기

    가장 먼저 해야 할 일은 “안 뜨는 노드”가 아니라 “대기 중인 Pod”를 보는 겁니다. 아래 명령어부터 시작해 보세요.

    kubectl get pods -A --field-selector=status.phase=Pending
    kubectl describe pod -n <namespace> <pod-name>
    kubectl get events -A --sort-by=.lastTimestamp

    여기서 제가 제일 먼저 보는 건 다음 세 가지입니다.

    • Insufficient cpu/memory: 리소스 요청량이 현재/예상 노드와 맞지 않는 경우
    • node affinity mismatch: nodeSelector 또는 affinity 조건이 과도한 경우
    • untolerated taint: 노드 taint를 Pod가 허용하지 않는 경우

    예를 들어 이런 Pod 스펙은 흔히 문제를 만듭니다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: inflate
    spec:
      nodeSelector:
        kubernetes.io/arch: amd64
      containers:
        - name: pause
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: "2"
              memory: "2Gi"

    이 자체는 틀린 YAML은 아닙니다. 다만 NodePool이 arm64만 허용하거나, 너무 작은 인스턴스 타입만 열어놨다면 Karpenter는 새 노드를 못 만듭니다. 결국 Kubernetes 노드 프로비저닝 실패처럼 보이지만, 시작점은 Pod 조건이죠.

    Karpenter 디버깅 2단계: 컨트롤러 로그에서 에러 패턴 찾기

    그다음은 Karpenter 로그입니다. 저는 보통 아래처럼 확인합니다.

    kubectl logs -n karpenter deploy/karpenter --since=30m
    kubectl logs -n karpenter deploy/karpenter --since=30m | grep -i "error"
    kubectl logs -n karpenter deploy/karpenter --since=30m | grep -i "provision"

    로그에서 자주 보는 키워드는 대략 이렇습니다.

    로그/증상 의미 우선 확인할 것
    no instance type met the scheduling requirements 조건에 맞는 인스턴스 타입이 없음 NodePool requirements, Pod requests
    insufficient capacity 가용 영역 또는 타입의 실시간 용량 부족 타입 범위 확대, AZ 분산
    access denied / unauthorized IAM 권한 문제 controller role, instance profile
    subnet/security group not found AWS 리소스 발견 실패 태그, selector 조건

    처음엔 이게 뭔가 싶었는데, 로그를 “읽는” 게 아니라 “분류”하기 시작하니까 속도가 확 달라지더라고요. 특히 access denied 계열은 쿠버네티스 문제가 아니라 거의 AWS 권한 문제였습니다.

    Karpenter 디버깅 과정에서 컨트롤러 로그와 NodePool을 점검하는 이미지

    Karpenter 컨트롤러 로그, NodePool 조건, EC2 관련 리소스를 함께 점검하는 디버깅 흐름 예시입니다.

    실전 구현: NodePool/Provisioner 조건 점검하기

    운영 중인 Karpenter 버전에 따라 리소스 이름이 다를 수 있습니다. 최근 계열은 NodePool/EC2NodeClass 조합을 많이 쓰고, 예전 문서에서는 Provisioner/AWSNodeTemplate 예제가 남아 있습니다. 그래서 제 습관은 “내 클러스터에 실제로 어떤 CRD가 있는지 먼저 보는 것”입니다.

    kubectl api-resources | grep -i karpenter
    kubectl get nodepools
    kubectl get ec2nodeclasses
    kubectl get provisioners

    예를 들어 NodePool이 너무 제한적으로 잡혀 있으면 문제가 생깁니다.

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: default
    spec:
      template:
        spec:
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
            - key: kubernetes.io/os
              operator: In
              values: ["linux"]

    여기서 흔한 실수는 이런 겁니다.

    • 인스턴스 패밀리 조건을 너무 좁게 설정
    • 특정 가용 영역만 허용
    • spot(스팟)만 허용했는데 해당 시점에 용량이 부족
    • daemonset(데몬셋) 오버헤드를 고려하지 않고 너무 작은 타입만 허용

    제가 직접 해보니 조건을 처음부터 정교하게 만드는 것보다, 일단 넉넉하게 열고 정상 프로비저닝을 확인한 뒤 점진적으로 좁히는 방식이 훨씬 덜 고생합니다.

    권장 점검 명령어

    kubectl describe nodepool <nodepool-name>
    kubectl get ec2nodeclass <class-name> -o yaml
    kubectl get provisioner <name> -o yaml

    설정에서 확인할 항목은 다음과 같습니다.

    1. 아키텍처 제약이 Pod와 맞는지
    2. 용량 타입(on-demand/spot) 제약이 과하지 않은지
    3. 서브넷/보안 그룹 선택 조건이 실제 AWS 태그와 일치하는지
    4. 노드 만료, 통합(consolidation) 정책이 과도하게 공격적이지 않은지

    ⚠️ 오토스케일링 실패를 만드는 대표 원인 5가지

    이 섹션은 제가 실제로 많이 봤던 순서대로 적겠습니다. Karpenter 디버깅에서 제일 시간을 많이 잡아먹는 부분이기도 합니다.

    1. IAM(권한 체계) 누락

    컨트롤러가 EC2 인스턴스를 생성하거나 조회할 권한이 없으면 노드가 당연히 안 뜹니다. 로그에 access denied, unauthorized 류 메시지가 보이면 거의 이쪽입니다. EKS에서 IRSA(IAM Roles for Service Accounts, 서비스어카운트용 IAM 역할)를 쓰는 경우, role annotation과 정책 연결 상태를 다시 확인해 보세요.

    2. Subnet/Security Group 태그 문제

    Karpenter는 보통 태그 기반으로 서브넷과 보안 그룹을 찾습니다. 근데 태그가 빠져 있거나 selector 조건과 안 맞으면 리소스를 발견하지 못합니다. 저는 이걸 네트워크 문제로 착각해서 한참 헤맸었는데, 사실은 태그 오타 하나였던 적도 있습니다.

    3. 인스턴스 타입 제한 과다

    예를 들어 특정 세대, 특정 패밀리, 특정 크기만 허용해 놓으면 실시간 수용 용량이 없을 때 바로 막힙니다. 운영에서는 후보군을 넓혀야 합니다. 특히 spot만 쓰는 환경은 더 그렇습니다.

    4. Pod 요청량 과다 또는 DaemonSet 오버헤드 미반영

    실제로 써보니까 작은 워크로드 하나만 보지 말고, CNI(컨테이너 네트워크 인터페이스), 로그 에이전트, 모니터링 에이전트 같은 데몬셋 자원까지 같이 봐야 하더라고요. 남는 것 같아도 실제 스케줄 가능 용량은 더 작습니다.

    5. AWS 계정 할당량 또는 AZ 용량 이슈

    설정이 멀쩡해도 특정 가용 영역에서 타입 수급이 안 될 수 있습니다. 이럴 땐 Karpenter 자체 버그보다 클라우드 용량 이슈일 가능성도 큽니다. 그래서 저는 가능한 한 여러 AZ와 여러 타입을 열어둡니다.

    kubectl get events -A --sort-by=.lastTimestamp
    kubectl describe pod -n <namespace> <pod-name>
    kubectl describe nodepool <nodepool-name>

    설정 예시: 디버깅용으로 조건을 단순화해보기

    문제가 길어질 때는 조건을 줄여서 정상 동작 여부부터 확인하는 게 좋습니다. 아래처럼 Pod를 단순화해서 테스트해 보세요.

    apiVersion: v1
    kind: Pod
    metadata:
      name: karpenter-debug
    spec:
      containers:
        - name: pause
          image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"

    이 Pod조차 스케줄이 안 되면, 애플리케이션 문제가 아니라 Karpenter 또는 인프라 레이어 문제로 보는 게 맞습니다. 반대로 이건 뜨는데 실제 서비스 Pod만 안 뜬다면, 그때는 requests/affinity/toleration을 더 자세히 보시면 됩니다.

    Kubernetes 노드 프로비저닝 점검을 위한 YAML 설정과 kubectl 디버깅 이미지

    디버깅용 Pod YAML, kubectl describe 출력, NodePool 설정을 함께 비교하는 장면을 표현한 이미지입니다.

    검증 방법: 노드가 정말 의도대로 프로비저닝됐는지 확인

    노드가 하나 떴다고 끝이 아닙니다. 저는 아래 순서로 꼭 확인합니다.

    1. 새 노드가 생성됐는지 확인
    2. Pending Pod가 Running으로 전환됐는지 확인
    3. 노드 라벨, taint, allocatable 자원이 기대와 맞는지 확인
    4. 불필요하게 큰 인스턴스가 뜨지 않았는지 확인
    kubectl get nodes
    kubectl get pods -A -o wide
    kubectl describe node <node-name>
    kubectl top nodes

    확인 포인트는 단순합니다.

    • Pod가 실제로 새 노드에 배치됐는가
    • 노드 라벨이 workload 조건과 맞는가
    • CPU/메모리 여유가 너무 적거나 너무 많지 않은가
    • 오토스케일링 실패가 반복되지 않는가

    드디어 됐다! 싶은 순간이 여기인데요, 여기서 끝내면 안 됩니다. 몇 분 후 consolidation이나 만료 정책 때문에 노드가 정리되면서 다시 Pending이 생길 수도 있거든요. 그래서 최소한 짧은 부하 테스트나 재배포 한 번은 꼭 해보시는 걸 권합니다.

    새 노드 생성 이후 Pending Pod가 Running으로 바뀌고 자원 사용량이 안정화된 결과 화면 예시입니다.

    운영 팁: 제가 실제로 적용하는 Karpenter 문제 해결 체크리스트

    혹시 이런 경험 있으신가요? 로그는 많은데 뭘 먼저 봐야 할지 몰라서 계속 왔다 갔다 하게 되는 상황이요. 그래서 저는 아래 체크리스트를 만들어 두고 그대로 봅니다.

    체크 항목 질문 판단 기준
    Pod 이벤트 왜 Pending인가? 스케줄링 실패 메시지가 명확해야 함
    Karpenter 로그 프로비저닝 계산을 했는가? instance type, subnet, 권한 관련 힌트 확인
    설정 제약 NodePool/Provisioner가 너무 좁은가? AZ, 타입, 아키텍처, 용량 타입 검토
    AWS 리소스 태그와 권한이 맞는가? subnet/security group 발견 가능해야 함
    검증 한 번 뜬 뒤 안정적으로 유지되는가? 재배포/부하 시 반복 확인

    이 체크리스트만 잘 지켜도 Karpenter 문제 해결 시간이 꽤 줄어듭니다. 그리고 이전 글에서 다룬 클러스터 리소스 요청량 설계 내용과 같이 보시면 더 이해가 쉬우실 겁니다. 다음 글에서는 Karpenter와 Cluster Autoscaler를 어떤 기준으로 나눠 볼지 정리해보려고 합니다.

    Karpenter 문제 해결 체크리스트를 요약한 인포그래픽 이미지

    Karpenter 문제 해결 순서를 한 장으로 요약한 체크리스트 인포그래픽입니다.

    정리: Karpenter 문제 해결은 순서가 전부입니다

    오늘 내용의 핵심은 복잡하지 않습니다. 오토스케일링 실패가 보이면 바로 AWS 콘솔부터 열지 말고, 먼저 Pending Pod 이벤트를 확인하세요. 그다음 Karpenter 로그, 그리고 NodePool/Provisioner 제약, 마지막으로 IAM과 네트워크 태그를 보는 흐름이 가장 효율적이었습니다.

    저도 처음엔 Karpenter가 너무 똑똑해서 알아서 다 해줄 줄 알았는데, 실제로는 “조건을 어떻게 열고, 어떤 로그를 먼저 읽느냐”가 훨씬 중요하더라고요. 특히 Kubernetes 노드 프로비저닝 이슈는 한 군데만 봐서는 잘 안 풀립니다. 대신 순서를 지키면 생각보다 금방 풀립니다.

    혹시 지금도 Karpenter 디버깅 중이시라면, 오늘 소개한 명령어 세트부터 그대로 실행해 보세요. 그리고 Pod 이벤트 한 줄, Karpenter 로그 한 줄만 정확히 읽어도 방향이 확 잡힐 겁니다. 이거 진짜 편하더라고요.

  • [k8s] ClusterAPI 도입 성공 사례: 온프레미스 Kubernetes 클러스터 자동화 구축기

    [k8s] ClusterAPI 도입 성공 사례: 온프레미스 Kubernetes 클러스터 자동화 구축기

    [인프라] ClusterAPI 도입으로 온프레미스 자동화 성공하기

    온프레미스 Kubernetes 환경을 오래 운영하다 보면 결국 같은 고민을 하게 됩니다. 클러스터 하나 만들 때마다 문서 열고, kubeadm 옵션 다시 확인하고, 네트워크랑 인증서 맞추고, 노드 추가할 때마다 사람 손이 들어가죠. 저도 그랬습니다. 그래서 이번 글에서는 제가 실제로 정리해 본 ClusterAPI 도입 사례를 중심으로, 온프레미스 Kubernetes 환경에서 클러스터 생성과 확장을 어떻게 자동화했는지 풀어보려고 합니다. 특히 여러 팀이 같은 기준으로 클러스터를 만들고 싶을 때, 이 접근이 왜 먹히는지 말씀드릴게요.

    처음엔 솔직히 이게 뭔가 싶었어요. Kubernetes 자체도 복잡한데 관리 클러스터까지 따로 둔다니 더 어려워 보였거든요. 근데 한 번 구조가 잡히고 나니까, “아 이건 사람 손을 줄이기 위한 운영 도구구나” 싶더라고요. 혹시 지금도 새 클러스터 만들 때마다 체크리스트부터 찾고 계신다면, 여기서 중요한 포인트를 같이 보시면 됩니다.

    ClusterAPI 도입 사례를 설명하는 온프레미스 Kubernetes 전체 아키텍처 이미지

    Management Cluster(관리 클러스터), Workload Cluster(운영 클러스터), 인프라 제공자와 GitOps 흐름을 한눈에 보여주는 아키텍처 이미지입니다.

    1. ClusterAPI란 무엇인가요?

    Cluster API(CAPI, 클러스터 수명주기 관리 프로젝트)를 쉽게 말하면, Kubernetes 방식으로 Kubernetes 클러스터를 만드는 도구예요. Deployment(디플로이먼트)를 선언적으로 관리하듯이, 클러스터 자체를 YAML로 선언하고 생성, 확장, 업그레이드까지 다루는 거죠.

    핵심은 세 가지입니다.

    • Management Cluster: 다른 클러스터를 생성하고 관리하는 기준 클러스터입니다.
    • Workload Cluster: 실제 애플리케이션이 올라가는 대상 클러스터입니다.
    • Provider: vSphere(브이스피어, 가상화 플랫폼), OpenStack(오픈스택, 프라이빗 클라우드 플랫폼), Bare Metal(베어메탈, 물리 서버) 같은 인프라와 연결해 주는 구성입니다.

    제가 직접 해보니 중요한 건 “클러스터 생성 절차를 코드로 옮긴다”는 점이었어요. 이게 되면 담당자가 바뀌어도 운영 방식이 흔들리지 않거든요. ClusterAPI 도입 사례가 주목받는 이유도 결국 여기 있습니다.

    2. 왜 온프레미스 Kubernetes 자동화에 잘 맞았나

    클라우드는 원래 API가 잘 되어 있어서 자동화가 비교적 쉬워요. 반면 온프레미스는 가상화 플랫폼, 네트워크, IP 할당 정책, 이미지 관리, 인증서 처리까지 다 제각각인 경우가 많습니다. 그래서 클러스터 자동화가 더 절실한데, 아이러니하게도 더 어렵죠.

    제가 보기에 Cluster API가 온프레미스에서 특히 좋았던 이유는 아래와 같았습니다.

    구분 기존 수동 방식 ClusterAPI 방식
    클러스터 생성 문서 따라 수동 실행 YAML 기반 선언형 생성
    확장 노드별 개별 작업 replica 수 조정 중심
    표준화 작업자 숙련도 영향 큼 템플릿 재사용 가능
    변경 이력 위키나 메모 의존 Git 기반 추적 가능
    복구 재현 어려움 동일 매니페스트 재적용 가능

    특히 여러 환경을 동시에 굴릴 때 차이가 컸어요. 개발, 스테이징, 운영이 따로 있으면 사람이 일일이 맞추다가 꼭 어긋나거든요. 반면 CAPI 구축을 해두면 템플릿 하나에서 변수만 바꿔 환경을 복제할 수 있어서 훨씬 안정적이었습니다.

    3. 제가 잡았던 목표와 설계 기준

    이번 ClusterAPI 도입 사례에서 제가 세운 기준은 과하지 않게 명확했어요.

    1. 클러스터 생성 절차를 문서가 아니라 Git 저장소 기준으로 관리할 것
    2. Control Plane(컨트롤 플레인, 클러스터 제어 영역)과 Worker Node(워커 노드, 실제 워크로드 실행 서버) 확장을 분리할 것
    3. 온프레미스 Kubernetes 환경에서도 동일한 템플릿을 재사용할 것
    4. 장애가 나더라도 다시 만들 수 있는 구조를 우선할 것

    여기서 중요한 포인트는 “처음부터 모든 걸 자동화하려고 하지 않는다”는 점이에요. 저도 처음엔 네트워크, 스토리지, 로드밸런서까지 한 번에 묶으려다가 삽질 좀 했습니다 ㅎㅎ 결국 성공한 방식은 클러스터 수명주기부터 먼저 자동화하고, 그 다음에 GitOps와 운영 도구를 붙이는 순서였어요.

    4. CAPI 구축 전 준비: Management Cluster부터 정리

    실전 구현은 예시로 vSphere 기반 온프레미스 환경을 기준으로 설명드릴게요. 다만 구조 자체는 다른 인프라 제공자에도 비슷하게 적용됩니다. 준비 단계에서 필요한 건 대략 이 정도입니다.

    • kubectl(쿠버네티스 CLI), clusterctl(Cluster API CLI)
    • 부트스트랩용 Kubernetes 클러스터 1개
    • vSphere API 접근 정보 또는 사용하는 온프레미스 인프라 자격 정보
    • 템플릿에서 참조할 VM 이미지와 네트워크 정책
    • 로드밸런서 또는 API 서버 엔드포인트 전략

    저는 먼저 management cluster를 아주 작게 올리고, 여기에 Cluster API Controller(컨트롤러, 상태를 맞추는 구성요소)를 설치했어요. 이 관리 클러스터가 있어야 이후 workload cluster를 선언형으로 찍어낼 수 있거든요.

    export CLUSTER_TOPOLOGY=true
    export EXP_CLUSTER_RESOURCE_SET=true
    
    clusterctl init --infrastructure vsphere
    clusterctl version
    kubectl get pods -A

    이 단계에서 pod가 전부 Running 상태로 올라오는지 꼭 확인하셔야 합니다. 컨트롤러가 불안정하면 뒤 단계에서 증상이 꼬여서 원인 파악이 더 어려워져요.

    5. 실전 구현: 온프레미스 Kubernetes 클러스터 자동화 흐름

    이제 본격적으로 CAPI 구축을 진행합니다. 제 경험상 아래 순서가 가장 덜 꼬였습니다.

    1. 클러스터 템플릿 생성
    2. 환경 변수 주입
    3. 매니페스트 검토
    4. 적용 후 상태 확인
    5. 노드 확장 및 업그레이드 테스트
    export VSPHERE_SERVER=vc.example.local
    export VSPHERE_USERNAME=svc_clusterapi
    export VSPHERE_PASSWORD='change-me'
    export VSPHERE_DATACENTER=DC1
    export VSPHERE_DATASTORE=datastore01
    export VSPHERE_NETWORK=VM_Network
    export VSPHERE_RESOURCE_POOL=/DC1/host/Cluster/Resources
    export VSPHERE_FOLDER=/DC1/vm/k8s
    export VSPHERE_TEMPLATE=ubuntu-k8s-template
    export KUBERNETES_VERSION=v1.29.0
    export CONTROL_PLANE_MACHINE_COUNT=3
    export WORKER_MACHINE_COUNT=3
    export CLUSTER_NAME=onprem-prod-01
    
    clusterctl generate cluster ${CLUSTER_NAME} \
      --kubernetes-version ${KUBERNETES_VERSION} \
      --control-plane-machine-count=${CONTROL_PLANE_MACHINE_COUNT} \
      --worker-machine-count=${WORKER_MACHINE_COUNT} \
      > ${CLUSTER_NAME}.yaml

    생성된 YAML은 바로 적용하지 말고 한 번 읽어보시는 걸 권장합니다. 처음엔 귀찮아 보여도, 여기서 네트워크 이름이나 템플릿 참조가 틀린 걸 많이 잡더라고요.

    ClusterAPI 도입 사례의 템플릿 생성과 컨트롤러 배포 흐름 이미지

    clusterctl로 템플릿을 생성하고, management cluster에서 이를 해석해 workload cluster를 만드는 흐름을 설명하는 이미지입니다.

    apiVersion: cluster.x-k8s.io/v1beta1
    kind: Cluster
    metadata:
      name: onprem-prod-01
    spec:
      clusterNetwork:
        pods:
          cidrBlocks:
            - 192.168.0.0/16
        services:
          cidrBlocks:
            - 10.96.0.0/12
      controlPlaneRef:
        apiVersion: controlplane.cluster.x-k8s.io/v1beta1
        kind: KubeadmControlPlane
        name: onprem-prod-01-control-plane
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: VSphereCluster
        name: onprem-prod-01
    kubectl apply -f onprem-prod-01.yaml
    kubectl get cluster
    kubectl get machines
    kubectl get kubeadmcontrolplane
    kubectl describe cluster onprem-prod-01

    여기서 진짜 편한 점이 나와요. 워커 노드 늘릴 때도 별도 설치 문서가 아니라 replica 값을 조정하는 식으로 접근하게 되거든요. 사람이 SSH 들어가서 하나씩 붙이는 방식에서 벗어나는 거죠.

    apiVersion: cluster.x-k8s.io/v1beta1
    kind: MachineDeployment
    metadata:
      name: onprem-prod-01-md-0
    spec:
      replicas: 5

    이런 식으로 선언 상태를 바꾸면 컨트롤러가 맞춰 줘요. 실제로 써보니까 운영자가 해야 할 일이 “절차 수행”에서 “원하는 상태 정의”로 바뀌더라고요.

    6. ⚠️ 트러블슈팅: 제가 실제로 막혔던 지점들

    자동화라고 해서 처음부터 매끈하진 않았어요. 오히려 수동 설치보다 어디서 실패했는지 감이 안 잡혀서 더 답답할 때도 있었죠. 특히 아래 세 가지는 자주 만났습니다.

    6-1. VM은 생성됐는데 Kubernetes 노드로 안 붙는 문제

    이건 대부분 템플릿 이미지 문제였어요. cloud-init(클라우드 이닛, 초기 설정 자동화) 또는 kubeadm bootstrap 과정이 템플릿과 안 맞는 경우가 있더라고요. 저는 골든 이미지 재생성 후 해결했습니다.

    kubectl get kubeadmconfigs
    kubectl describe machine <machine-name>
    kubectl logs -n capv-system deployment/capv-controller-manager

    6-2. API 엔드포인트는 열렸는데 Control Plane이 Ready로 안 가는 문제

    로드밸런서 health check 조건과 kube-apiserver 준비 상태가 안 맞으면 이런 현상이 생겨요. 겉으로는 포트가 열려 보여도 Cluster API 쪽에서는 준비가 덜 된 걸로 판단할 수 있습니다.

    6-3. 머신 삭제가 느리거나 걸리는 문제

    finalizer(파이널라이저, 정리 작업 보장 메커니즘)와 인프라 자원 정리가 꼬일 때가 있어요. 이때는 무작정 리소스부터 지우지 말고, 어떤 컨트롤러가 막고 있는지 이벤트를 먼저 봐야 합니다.

    • ⚠️ 경고: 수동으로 VM만 먼저 삭제하면 CAPI 상태와 실제 인프라 상태가 어긋날 수 있습니다.
    • 💡 팁: 문제 상황은 이벤트, controller log, Machine 상태를 함께 봐야 빨리 풀려요.
    • 💡 팁: 운영 전 테스트 환경에서 scale-out, scale-in, control plane 교체를 꼭 해보세요.

    저도 처음엔 “왜 선언형인데 선언대로 안 되지?” 싶었는데, 결국 선언형도 기반 인프라와 부트스트랩 준비가 맞아야 돌아가더라고요. 이건 정말 직접 해봐야 감이 와요.

    7. 검증과 결과: 운영 방식이 어떻게 달라졌나

    검증은 단순히 클러스터가 떠 있느냐만 보면 부족합니다. 저는 아래 순서로 확인했어요.

    1. Control Plane 3대가 정상 Ready 상태인지 확인
    2. Worker Node 증설/축소가 선언대로 반영되는지 확인
    3. kubeconfig 추출 후 실제 워크로드 배포 테스트
    4. 클러스터 재생성 시 동일한 결과가 나오는지 확인
    clusterctl get kubeconfig onprem-prod-01 > onprem-prod-01.kubeconfig
    export KUBECONFIG=$(pwd)/onprem-prod-01.kubeconfig
    kubectl get nodes
    kubectl create deployment nginx --image=nginx
    kubectl get pods -o wide

    이 검증이 끝나고 나면 체감이 확 와요. 새 환경이 필요할 때 “며칠 잡아야 하나”가 아니라 “템플릿 변수 뭘 바꿔야 하나”로 대화가 바뀌거든요. 이게 제가 느낀 가장 큰 성과였어요. 숫자를 과장할 필요도 없이, 운영 피로도가 확실히 줄었습니다.

    ClusterAPI 도입 사례에서 생성된 온프레미스 Kubernetes 클러스터 검증 결과 이미지

    노드가 Ready 상태로 정렬되고 테스트 워크로드가 배포된 모습을 보여주는 결과 검증 이미지입니다.

    🎉 성과 정리

    • 클러스터 생성 절차가 사람 의존에서 Git 기반 표준 절차로 바뀜
    • 온프레미스 Kubernetes 환경에서도 재현 가능한 배포 흐름 확보
    • 운영팀 간 작업 편차 감소
    • 추후 GitOps, 모니터링, 정책 관리 연계가 쉬워짐

    8. 정리: ClusterAPI 도입 사례에서 배운 점

    이번 ClusterAPI 도입 사례를 통해 제가 가장 크게 배운 건, 자동화의 핵심은 도구보다 기준이라는 점이었어요. Cluster API는 만능 마법봉은 아니지만, 클러스터 생성과 확장을 운영 표준으로 묶는 데는 정말 강력했습니다. 특히 클러스터 자동화를 사람 손이 아니라 선언 상태 중심으로 옮긴다는 점에서, 장기 운영에 큰 차이를 만들더라고요.

    다만 처음부터 모든 걸 얹지는 마세요. Management cluster 안정화, 템플릿 검증, 네트워크 정책 정리, 골든 이미지 검증 순으로 가는 게 좋아요. 저도 처음엔 욕심냤다가 다시 돌아왔거든요. 근데 그렇게 한 바퀴 돌고 나니 구조가 훨씬 단단해졌습니다.

    ClusterAPI 도입 사례의 도입 전후 운영 방식 비교 이미지

    수동 구축과 선언형 자동화의 차이를 한눈에 보여주는 비교 이미지입니다.

    다음 글에서는 ClusterClass(클래스 기반 클러스터 템플릿)와 GitOps를 묶어서 운영 표준화를 더 밀어붙이는 방법도 다뤄볼 예정입니다. 이전 글에서 정리한 kubeadm 기반 수동 설치 방식과 비교해서 보시면 왜 CAPI 구축이 가치 있는지 더 선명하게 보이실 겁니다.

    FAQ. ClusterAPI 도입 시 자주 묻는 질문

    Q1. 작은 팀도 Cluster API가 필요할까요?

    클러스터 수가 1개뿐이면 당장 절실하지 않을 수 있어요. 다만 환경이 늘어날 계획이 있거나 표준화가 필요하다면 일찍 검토할 가치가 있습니다.

    Q2. 베어메탈에서도 가능한가요?

    가능합니다. 다만 사용하는 인프라 제공자와 네트워크 자동화 수준에 따라 난이도가 확 달라져요. 그래서 처음엔 가상화 환경에서 개념을 먼저 익히는 걸 추천드립니다.

    Q3. 업그레이드도 선언형으로 할 수 있나요?

    네, Kubernetes 버전과 관련 리소스 정의를 통해 관리하는 방식이 일반적이에요. 다만 운영 적용 전에는 테스트 클러스터에서 롤링 동작을 꼭 검증해 보셔야 합니다.

    ✅ 한 줄 정리: 온프레미스 Kubernetes를 반복해서 만들고 운영해야 한다면, Cluster API는 복잡함을 늘리는 도구가 아니라 반복 작업을 구조화하는 도구예요.

  • [Proxmox] proxmox 블루투스 패스스루 성공 사례와 설정 팁

    [Proxmox] proxmox 블루투스 패스스루 성공 사례와 설정 팁

    proxmox 블루투스 패스스루 성공 사례: 홈랩에서 VM에 무선 장치 붙이기

    홈랩을 굴리다 보면 한 번쯤은 proxmox 블루투스 패스스루가 필요해지는 순간이 옵니다. 저도 처음엔 서버는 유선이 기본이지 싶었는데, 막상 써보니까 블루투스 동글 하나를 가상 머신에 넘겨서 무선 장치를 붙여야 할 일이 생기더라고요. 예를 들어 무선 컨트롤러를 테스트한다든지, BLE 센서나 오디오 장치를 분리된 환경에서 다뤄야 할 때요. 물리 머신에 직접 붙이면 간단한데, VM 안에서 안정적으로 잡히게 만드는 과정은 생각보다 체크할 포인트가 많았습니다.

    특히 Proxmox VE에서는 USB 패스스루 자체는 어렵지 않지만, 블루투스 동글은 호스트가 먼저 장치를 초기화하거나 게스트 쪽 도구가 부족하면 바로 안 되는 것처럼 보이기 쉽습니다. 제가 직접 해보니 핵심은 화려한 튜닝보다도 호스트가 장치를 계속 사용하지 않게 하는 것, 그리고 VM 안에서 동글이 독립된 USB 장치로 보이게 만드는 것 이 두 가지였습니다. 이 두 가지만 잡아도 시행착오가 꽤 줄어들더라고요.

    Proxmox 호스트, USB 블루투스 동글, 그리고 게스트 VM 사이의 연결 구조를 한눈에 보여주는 개요 이미지입니다.

    1. 왜 가상 머신 블루투스 구성이 까다로운가

    쉽게 말해 블루투스는 그냥 꽂으면 끝나는 저장장치랑 성격이 다릅니다. 저장장치는 마운트만 안 하면 비교적 조용한 편인데, 블루투스 어댑터는 Linux 호스트가 부팅 직후부터 인식하고 초기화하는 경우가 많거든요. 그러면 내가 의도한 VM이 아니라 Proxmox 호스트 쪽에서 먼저 장치를 만지는 상황이 생길 수 있습니다.

    여기서 중요한 포인트가 있습니다.

    • USB 패스스루는 장치를 VM으로 직접 넘기는 기능입니다.
    • 블루투스 스택(Bluetooth Stack)은 Linux에서 보통 BlueZ가 담당합니다.
    • 호스트가 장치를 먼저 초기화했거나, 게스트에 필요한 도구와 드라이버가 없으면 인식이 불안정해질 수 있습니다.
    • 무선 컨트롤러처럼 재연결이 잦은 장치는 이런 차이를 더 민감하게 탑니다.

    저도 처음엔 “USB 장치 추가했는데 왜 VM 안에서 안 보이지?” 하고 한참 봤었는데, 결국 드라이버 충돌이라기보다 장치 점유와 확인 절차 문제인 경우가 많았습니다. 이 부분을 놓치면 괜히 다른 설정만 계속 만지게 되더라고요.

    2. proxmox 블루투스 패스스루 개념, 쉽게 말해 이겁니다

    proxmox 블루투스 패스스루는 블루투스 동글을 Proxmox 호스트가 아니라 특정 가상 머신이 직접 쓰게 만드는 구성입니다. 즉, VM 입장에서는 “내 서버에 USB 블루투스 동글이 하나 꽂혀 있다”고 느끼는 셈이죠.

    보통 구현 방식은 아래 두 가지로 생각하시면 됩니다.

    방식 설명 장점 주의점
    USB 장치 단위 패스스루 특정 블루투스 동글만 VM에 연결 구성이 단순하고 홈랩 활용에 적합 장치 식별은 버스 번호보다 Vendor ID/Product ID 기준이 안전함
    USB 컨트롤러 단위 패스스루 USB 포트 묶음 전체를 넘김 일부 장치에서 호환성이 더 나을 수 있음 영향 범위가 커서 초보자에겐 부담

    홈랩에서는 대개 USB 장치 단위 패스스루가 현실적입니다. 저도 이 방식으로 구성했습니다. 블루투스 동글 하나만 넘기면 되는데 굳이 USB 컨트롤러 전체를 건드릴 필요는 없었거든요.

    3. proxmox 블루투스 패스스루 전 준비물과 체크 포인트

    실전 들어가기 전에 아래 정도는 확인해두시면 좋습니다.

    1. Proxmox 호스트에서 USB 동글이 인식되는지 확인합니다.
    2. 연결 대상 VM이 정상 동작 중인지 확인합니다.
    3. 게스트 OS가 Linux인지 Windows인지에 따라 드라이버 준비 여부를 봅니다.
    4. 호스트에서 해당 블루투스 동글을 계속 써야 하는 상황은 아닌지 점검합니다.

    제가 실제로 체크했던 명령어는 아래와 비슷합니다.

    lsusb
    qm list
    qm config 101

    lsusb는 USB 장치 식별용이고, qm은 Proxmox의 VM 관리 CLI입니다. 여기서 동글의 Vendor ID:Product ID를 확인해두면 뒤에서 편합니다.

    예시로 확인하는 장치 식별

    lsusb
    
    # 예시 출력 형식
    # Bus 001 Device 004: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle

    위처럼 보인다면 0a12:0001 같은 식별자를 확보한 겁니다. 제품명은 장치마다 다를 수 있으니, 실제 환경에서는 본인 장치 기준으로 확인하시면 됩니다.

    4. 실전 구현: USB 패스스루로 블루투스 동글 넘기기

    이제 본격적으로 설정해보겠습니다. 저는 웹 UI와 CLI를 둘 다 써봤는데, 처음 구성은 UI가 편하고 문제 해결은 CLI가 더 빠르더라고요. 다만 설정을 바꾼 뒤에는 게스트 안에서 단순 재부팅만 보기보다, VM을 완전히 종료한 뒤 다시 시작해서 확인하는 편이 더 확실했습니다.

    4-1. 웹 UI에서 추가하는 방법

    1. Proxmox에서 대상 VM을 선택합니다.
    2. Hardware 메뉴로 들어갑니다.
    3. Add > USB Device를 선택합니다.
    4. 목록에서 블루투스 동글을 고르거나 USB Vendor/Device ID 기준으로 지정합니다.
    5. 설정을 저장한 뒤 VM을 완전히 종료 후 다시 시작합니다.

    처음엔 이게 뭔가 싶었는데, 실제로 써보니까 핵심은 자동으로 바뀌는 버스 번호보다 장치 ID 기준으로 잡는 것 이었습니다. 버스 번호는 재부팅이나 재연결 때 달라질 수 있거든요.

    proxmox 블루투스 패스스루 설정 화면을 설명하는 이미지

    VM의 Hardware 메뉴에서 USB Device를 추가하고 블루투스 동글을 지정하는 흐름을 보여주는 설정 이미지입니다.

    4-2. CLI에서 추가하는 방법

    VM ID가 101이라고 가정하면 아래처럼 설정할 수 있습니다.

    qm set 101 -usb0 host=0a12:0001
    qm config 101

    환경에 따라 USB 포트를 더 써야 하면 -usb1, -usb2 식으로 추가할 수 있습니다. 다만 블루투스 동글 하나만 쓰는 목적이라면 보통 -usb0 하나면 충분했습니다.

    4-3. 게스트 OS 안에서 확인하기

    Linux 게스트라면 먼저 장치 자체가 보이는지 확인합니다.

    lsusb
    rfkill list

    그다음 실제 블루투스 어댑터가 잡혔는지 봅니다. 요즘 배포판에서는 bluetoothctl이나 btmgmt 쪽이 더 익숙하고, hciconfig는 배포판에 따라 빠져 있거나 deprecated 도구로 분리된 경우가 있습니다.

    bluetoothctl list
    bluetoothctl show
    btmgmt info

    btmgmt가 없다면 BlueZ 관련 패키지를 먼저 설치해야 할 수 있습니다. 여기서 어댑터가 보이면 절반은 끝난 겁니다. 저는 이 단계에서 장치가 딱 뜨는 순간, 진짜 끝이 보이더라고요.

    5. 가상 머신 블루투스 활용 예시와 홈랩 활용 포인트

    블루투스를 VM에 붙인다고 해서 모든 상황에 의미가 있는 건 아닙니다. 그런데 맞는 용도에 쓰면 꽤 편합니다. 제가 보기에 현실적인 홈랩 활용은 이 정도입니다.

    • 무선 컨트롤러 테스트: 에뮬레이션 환경이나 게임 스트리밍 실험용
    • BLE(Bluetooth Low Energy) 장치 연동: 센서, 비콘, IoT 실험
    • 분리된 개발 환경 구성: 호스트를 건드리지 않고 VM 안에서만 블루투스 관련 소프트웨어 검증
    • 자동화 실험: Home Assistant 같은 서비스와 연계 테스트

    특히 호스트를 깔끔하게 유지하고 싶을 때 좋습니다. 블루투스 관련 라이브러리나 테스트 도구를 전부 VM 안에 넣고 굴리면, 문제 생겨도 스냅샷 복구가 쉽거든요. 이거 진짜 편하더라고요. 이전에 정리한 VM 네트워크 분리나 VLAN 구성 글이 있다면, 여기서 함께 내부 링크로 묶어주는 것도 흐름이 좋습니다.

    6. 제가 겪었던 문제와 해결법

    이 섹션이 사실 제일 중요합니다. 설정 자체보다 트러블슈팅에서 시간이 더 많이 갔거든요. 저도 여기서 시간을 꽤 썼습니다.

    6-1. 호스트에서는 보이는데 게스트에서는 안 보일 때

    가장 흔한 경우입니다. 보통 아래 순서로 확인하면 됩니다.

    1. USB 장치를 설정에 추가한 뒤 VM을 완전히 종료했다가 다시 시작합니다.
    2. qm config VMID로 실제 설정 반영 여부를 확인합니다.
    3. 게스트 부팅 후 lsusb로 장치가 보이는지 확인합니다.
    4. 안 보이면 게스트 쪽 드라이버, BlueZ 도구, 서비스 상태를 같이 확인합니다.

    여기서 중요한 포인트는, USB 패스스루가 추가되어도 게스트 OS 안에 필요한 드라이버나 사용자 공간 도구가 없으면 “안 되는 것처럼” 보일 수 있다는 점입니다.

    6-2. 재부팅 후 장치가 바뀌는 문제

    버스 번호 기반으로만 보시면 헷갈립니다. 그래서 저는 가능하면 Vendor ID / Product ID 기준으로 잡는 편을 추천드립니다. 물리 포트를 옮겼다가 이름이 바뀌는 경우도 있어서요.

    6-3. 블루투스는 잡히는데 페어링이 불안정할 때

    이 경우는 패스스루 문제라기보다, 무선 환경이나 게스트 OS의 블루투스 서비스 상태 문제일 때도 많습니다. Linux 게스트라면 서비스 상태를 먼저 확인해보세요.

    systemctl status bluetooth
    journalctl -u bluetooth --no-pager

    로그를 보면 생각보다 힌트가 잘 나옵니다. 저도 처음엔 Proxmox 설정이 잘못된 줄 알았는데, 실제로는 게스트 내부 서비스가 제대로 올라오지 않은 적이 있었습니다.

    6-4. 무선 컨트롤러 연결이 끊겼다 붙었다 할 때

    이건 동글 품질, 거리, 전원 관리, 간섭까지 변수가 많아서 한 가지 원인으로 단정하긴 어렵습니다. 다만 USB 3.x 장치 근처 간섭 이야기는 블루투스 환경에서 자주 나옵니다. 그래서 저는 가능하면 짧은 연장 케이블로 동글 위치를 조금 빼서 테스트해보는 편입니다. 무조건 해결된다고 말할 수는 없지만, 체감상 도움이 되는 경우가 있더라고요.

    7. 검증: proxmox 블루투스 패스스루가 정말 성공했는지 확인하는 방법

    설정이 끝났다고 바로 성공으로 보면 안 됩니다. 재시작 후에도 유지되는지, 게스트에서 장치 검색과 연결이 되는지까지 확인해야 합니다.

    1. 필요하면 Proxmox 호스트를 재부팅해 장치 재인식 상태를 확인
    2. 대상 VM 부팅
    3. 게스트에서 블루투스 어댑터 확인
    4. 실제 장치 검색 스캔
    5. 가능하면 한 번 페어링 테스트
    bluetoothctl
    power on
    agent on
    default-agent
    scan on

    스캔이 되고 주변 장치가 보이면 기본 통신은 살아있는 겁니다. 저는 여기서 무선 컨트롤러와 BLE 장치를 각각 한 번씩 잡아보면서 확인했습니다. 모든 환경에서 동일하다고 말할 순 없지만, 적어도 proxmox 블루투스 패스스루 자체는 홈랩 테스트 용도로 충분히 실사용 가능한 구성이었습니다.

    가상 머신 블루투스 검증 결과를 보여주는 proxmox 블루투스 패스스루 이미지

    게스트 VM 안에서 블루투스 어댑터가 인식되고 주변 장치 스캔이 되는 결과를 보여주는 검증 이미지입니다.

    8. 정리 표: 어떤 상황에서 이 구성이 잘 맞는가

    상황 추천 여부 이유
    홈랩에서 BLE 센서 실험 추천 ✅ 호스트를 건드리지 않고 VM 단위로 관리 가능
    무선 컨트롤러 기능 테스트 추천 ✅ 분리된 테스트 환경 구성에 유리
    호스트 자체에서 블루투스를 계속 사용 중 주의 ⚠️ 호스트와 게스트가 동시에 같은 동글을 안정적으로 공유하긴 어려움
    매우 민감한 실시간 오디오 용도 상황별 판단 지연 시간과 연결 안정성은 별도 검증 필요

    이 표만 봐도 방향이 좀 잡히실 겁니다. 사실 가상 머신 블루투스 구성이 만능은 아니지만, 테스트용, 분리 환경용, 홈랩 자동화용으로는 꽤 실용적입니다.

    proxmox 블루투스 패스스루 추천 상황과 주의점을 정리한 이미지

    어떤 상황에서 이 구성이 적합한지 빠르게 판단할 수 있도록 정리한 요약 인포그래픽입니다.

    9. 마무리: 제가 얻은 결론과 다음에 해볼 것

    정리해보면, proxmox 블루투스 패스스루는 생각보다 진입장벽이 높지 않습니다. 다만 USB 장치를 추가하는 것 자체보다, 호스트 점유 문제와 게스트 내부 확인 절차를 놓치지 않는 게 중요했습니다. 저도 처음엔 장치만 붙이면 끝날 줄 알았는데, 실제로 써보니까 재시작 후 유지 여부와 장치 스캔까지 확인해야 진짜 성공이더라고요.

    특히 홈랩 활용 관점에서는 만족도가 높았습니다. 호스트를 건드리지 않고 VM 안에서만 블루투스 실험을 할 수 있으니 실패해도 부담이 적었거든요. 혹시 여러분도 가상 머신 블루투스 구성 때문에 막히고 계셨다면, 오늘 정리한 순서대로 하나씩 점검해보시면 훨씬 수월할 겁니다.

    다음 글에서는 BLE 장치를 Home Assistant와 연동하는 흐름이나, USB 패스스루 대신 다른 분리 전략을 어떻게 잡는지까지 이어서 다뤄볼 예정입니다. 이전 글에서 다뤘던 VM 네트워크 분리나 VLAN 구성과도 연결되는 부분이 있으니, 그쪽에 관심 있으시면 함께 보셔도 좋겠습니다.

    홈랩에서 proxmox 블루투스 패스스루 구축 전후를 보여주는 마무리 이미지

    구축 전후의 차이와 다음 단계 확장 방향을 한 장으로 정리한 마무리 이미지입니다.

    FAQ

    Q1. Proxmox에서 블루투스 동글을 VM에 넘기면 호스트에서는 못 쓰나요?

    보통은 그렇습니다. 같은 USB 블루투스 동글을 호스트와 게스트가 동시에 안정적으로 공유하는 방식으로 보긴 어렵습니다. 하나의 소유권을 어디에 둘지 정하는 개념으로 이해하시면 편합니다.

    Q2. USB 패스스루와 PCI 패스스루 중 무엇이 더 적합한가요?

    블루투스 동글 하나를 붙이는 용도라면 대개 USB 패스스루가 더 단순합니다. PCI 패스스루는 범위가 커서 초기에 접근하기엔 부담이 있습니다.

    Q3. Windows 게스트에서도 가능한가요?

    원리는 같습니다. 다만 게스트 OS에 맞는 드라이버 준비가 중요합니다. 특히 일부 저가형 동글은 Windows에서 제조사 드라이버 의존성이 있을 수 있어서, 장치 인식 후 드라이버 상태를 같이 확인하는 게 좋습니다.

    Q4. 홈랩 활용 관점에서 가장 먼저 테스트할 건 뭔가요?

    장치 인식, VM 재시작 후 유지, 실제 페어링 이 세 가지입니다. 기능 테스트보다 먼저 연결 안정성을 확인하는 게 시간을 아껴줍니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    4-1. netplan 예시

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

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

    sudo netplan generate
    sudo netplan try

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

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

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

    4-2. iptables 적용 예시

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

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

    4-3. DNS 점검 예시

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    8. 자주 묻는 질문과 정리

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

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

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

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

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

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

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

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

    정리하자면 이렇습니다.

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

    [Linux] 리눅스 루트 파티션 마이그레이션으로 새 디스크 안전하게 이전하기

    리눅스 루트 파티션 마이그레이션으로 새 디스크 안전하게 이전하기

    리눅스 파티션 마이그레이션 작업은 평소엔 미루기 쉽지만, 디스크 용량이 부족해지거나 오래된 SSD를 교체해야 할 때는 결국 한 번은 하게 되더라고요. 특히 루트 디스크 이전은 운영체제 자체가 올라가 있는 영역을 옮기는 일이라서, 잘못 건드리면 부팅이 안 되거나 데이터가 꼬일 수 있습니다. 저도 홈랩에서 우분투 서버를 돌리다가 리눅스 디스크 업그레이드를 해야 했던 적이 있었는데, 처음엔 단순히 복사만 하면 끝날 줄 알았습니다. 근데 실제로 해보니까 파티션 구조, 부트로더, UUID, fstab(파일시스템 마운트 설정)까지 확인할 게 정말 많더라고요.

    이번 글에서는 제가 실제로 자주 쓰는 방식 기준으로, 리눅스 루트 파티션을 새 디스크로 안전하게 마이그레이션하는 방법을 정리해보겠습니다. 핵심은 무작정 dd(블록 단위 복제)만 믿지 않고, 상황에 따라 rsync(파일 단위 동기화), gparted(파티션 편집 도구), 그리고 부트 복구 절차를 적절히 조합하는 겁니다. 여기서 중요한 포인트! 빠르게 끝내는 것보다 데이터 손실 방지가 우선입니다.

    리눅스 파티션 마이그레이션 전체 흐름을 보여주는 아키텍처 이미지

    기존 디스크에서 새 디스크로 루트 파티션, EFI 또는 /boot, UUID 확인, 부트 복구까지 이어지는 전체 흐름을 보여주는 개요 이미지입니다.

    1. 왜 리눅스 루트 파티션 마이그레이션이 까다로운가

    쉽게 말해, 일반 데이터 디렉터리를 옮기는 것과 루트 파일시스템 root filesystem(운영체제 핵심 파일이 있는 영역)을 옮기는 건 난이도가 완전 다릅니다. 루트 파티션에는 /etc, /boot, /var, 라이브러리, 서비스 설정, 커널 이미지까지 엮여 있거든요. 여기에 BIOS 부팅인지 UEFI 부팅인지에 따라서 준비할 것도 달라집니다.

    • 데이터만 복사하면 끝나지 않습니다. 부팅 정보도 맞아야 합니다.
    • 파티션 UUID(고유 식별자)가 바뀌면 /etc/fstab 수정이 필요할 수 있습니다.
    • GRUB(그럽, 리눅스 부트로더) 재설치가 필요한 경우가 꽤 많습니다.
    • 실행 중인 시스템을 그대로 옮기면 로그나 캐시 파일이 바뀌어서 복제 일관성이 흔들릴 수 있습니다.

    그래서 저는 가능하면 라이브 환경 Live Environment(USB로 부팅한 임시 OS)에서 작업하는 편입니다. 처음엔 번거로워 보여도, 결과적으로 사고가 덜 납니다.

    2. 리눅스 파티션 마이그레이션 방식 비교: rsync vs dd vs gparted

    많이들 고민하는 게 이겁니다. rsync와 dd 중 뭐가 낫냐는 질문이죠. 결론부터 말하면, 정답은 환경 따라 다릅니다. 저는 아래 기준으로 선택합니다.

    방식 특징 장점 주의점
    rsync 파일 단위 복사 유연하고 파티션 크기 변경이 쉬움 부트 설정과 특수 파일 복사 옵션을 신경 써야 함
    dd 블록 단위 복제 원본 구조를 통째로 복제 대상 디스크가 충분히 커야 하고 실수 시 위험함
    gparted GUI 기반 파티션 조정 시각적으로 확인이 쉬움 복사 자체보다 파티션 정리에 더 적합함

    제가 직접 해보니, 같거나 더 큰 디스크로 통째 복제면 dd가 편할 때도 있습니다. 반대로 더 큰 새 SSD로 옮기면서 파티션 구조를 정리하려면 rsync가 훨씬 낫더라고요. 이 글에서는 가장 범용적인 rsync 기반 리눅스 파티션 마이그레이션을 중심으로 설명하고, 중간에 dd가 맞는 경우도 같이 짚어보겠습니다.

    3. 작업 전 체크리스트: 데이터 손실 방지부터 준비합니다

    이 단계는 정말 중요합니다. 솔직히 말하면, 여기서 대충 하면 나중에 몇 시간 날립니다. 저도 예전에 디스크 이름을 잘못 보고 /dev/sda 대신 /dev/sdb를 건드릴 뻔해서 식은땀 났었거든요 ㅎㅎ

    1. 전체 백업을 먼저 확보합니다. 가능하면 별도 외장 디스크나 NAS에 보관합니다.
    2. 현재 부팅 방식이 UEFI인지 Legacy BIOS인지 확인합니다.
    3. 원본 디스크와 대상 디스크의 장치명을 정확히 확인합니다.
    4. LVM, RAID, LUKS 암호화 사용 여부를 먼저 파악합니다.
    5. 가능하면 라이브 USB로 부팅해 오프라인 상태에서 진행합니다.

    현재 디스크 구조 확인

    lsblk -o NAME,SIZE,FSTYPE,TYPE,MOUNTPOINT,UUID
    sudo fdisk -l
    findmnt /
    cat /etc/fstab
    [ -d /sys/firmware/efi ] && echo UEFI || echo BIOS

    이 명령으로 현재 루트 파티션, EFI System Partition(UEFI 부팅용 파티션), 스왑, UUID를 한 번에 대강 파악할 수 있습니다. 여기서 메모를 꼭 남겨두세요. 나중에 UUID 맞추거나 fstab 수정할 때 정말 도움 됩니다.

    4. 실전 구현: 새 디스크에 루트 파티션 마이그레이션하는 단계

    이제 본격적으로 들어가보겠습니다. 예시는 기존 디스크가 /dev/sda, 새 디스크가 /dev/sdb인 상황으로 가정하겠습니다. 실제 장치명은 환경마다 다르니 꼭 확인하고 바꾸셔야 합니다.

    4-1. 새 디스크 파티션 준비

    UEFI 시스템이라면 보통 아래처럼 구성하면 무난합니다.

    • EFI 파티션: FAT32, 약간의 여유 공간
    • 루트 파티션: ext4
    • 필요 시 swap 파티션 또는 swap 파일
    sudo parted /dev/sdb --script mklabel gpt
    sudo parted /dev/sdb --script mkpart ESP fat32 1MiB 513MiB
    sudo parted /dev/sdb --script set 1 esp on
    sudo parted /dev/sdb --script mkpart primary ext4 513MiB 100%
    
    sudo mkfs.vfat -F32 /dev/sdb1
    sudo mkfs.ext4 /dev/sdb2

    BIOS 환경이라면 EFI 파티션 없이 루트 중심으로 잡는 경우가 많습니다. 다만 기존 구조를 최대한 비슷하게 가져가는 게 삽질을 줄입니다.

    리눅스 파티션 마이그레이션을 위한 새 디스크 파티션 구성 이미지

    새 SSD에 EFI 파티션과 루트 파티션을 만드는 과정, 그리고 임시 마운트 지점을 연결하는 흐름을 보여주는 이미지입니다.

    4-2. 대상 파티션 마운트

    sudo mount /dev/sdb2 /mnt
    sudo mkdir -p /mnt/boot/efi
    sudo mount /dev/sdb1 /mnt/boot/efi

    만약 원본 시스템에 별도 /boot 파티션이 있다면 그것도 따로 만들어서 마운트해야 합니다. 여기서 구조가 틀어지면 복사 후 부팅이 안 될 수 있습니다.

    4-3. rsync로 루트 파일시스템 복사

    제가 가장 많이 쓰는 구간입니다. rsync는 파일 권한, 심볼릭 링크, ACL, 하드링크 등을 꽤 안정적으로 가져갈 수 있어서 좋습니다. 다만 제외 대상은 신중하게 잡아야 합니다.

    sudo rsync -aAXHv \
      --exclude='/dev/*' \
      --exclude='/proc/*' \
      --exclude='/sys/*' \
      --exclude='/tmp/*' \
      --exclude='/run/*' \
      --exclude='/mnt/*' \
      --exclude='/media/*' \
      --exclude='/lost+found' \
      / /mnt

    여기서 -aAXH 옵션이 중요합니다. archive, ACL, xattr, hardlink 보존을 의미하거든요. 처음엔 이게 뭔가 싶었는데, 권한과 메타데이터가 미묘하게 틀어지면 서비스가 이상하게 동작하는 경우가 있어서 저는 거의 습관처럼 넣습니다.

    운영 중 시스템에서 바로 복사했다면, 마지막에 한 번 더 rsync를 돌려 변경분만 맞춰주는 것도 좋습니다. 다운타임을 줄일 때 꽤 유용하더라고요.

    4-4. fstab 수정

    새 디스크의 UUID를 확인한 뒤, 복사된 시스템의 /etc/fstab를 수정합니다.

    sudo blkid /dev/sdb1 /dev/sdb2
    sudo nano /mnt/etc/fstab

    예를 들어 이런 식으로 바뀔 수 있습니다.

    UUID=NEW-ROOT-UUID / ext4 defaults 0 1
    UUID=NEW-EFI-UUID /boot/efi vfat umask=0077 0 1

    이 단계에서 UUID를 잘못 넣으면 부팅 중 emergency mode(긴급 모드)로 떨어질 수 있습니다. 실제로 저는 swap UUID 하나 안 맞아서 부팅이 지연된 적이 있었네요.

    4-5. chroot로 부트 환경 재구성

    chroot(체루트, 임시로 새 루트 환경에 들어가는 방법)로 진입해서 부트로더와 initramfs를 재생성합니다.

    sudo mount --bind /dev /mnt/dev
    sudo mount --bind /proc /mnt/proc
    sudo mount --bind /sys /mnt/sys
    sudo chroot /mnt
    
    grub-install /dev/sdb
    grub-mkconfig -o /boot/grub/grub.cfg
    update-initramfs -u
    exit

    배포판에 따라 grub2-mkconfig 또는 명령 경로가 다를 수 있습니다. UEFI 환경이라면 EFI 경로와 부트 엔트리 생성 여부도 확인해야 합니다. 우분투/데비안 계열은 위 절차가 익숙하고, RHEL 계열은 약간 다를 수 있습니다.

    4-6. EFI 항목 확인

    sudo efibootmgr -v

    새 디스크 기준 부트 엔트리가 제대로 잡혔는지 확인합니다. BIOS/UEFI 설정에서 부팅 순서를 새 디스크로 올리는 작업도 잊지 마세요.

    5. dd와 gparted는 언제 쓰면 좋을까

    모든 상황에서 rsync가 정답은 아닙니다. 기존 디스크 상태가 깔끔하고, 구조를 그대로 복제하고 싶다면 dd가 단순할 때도 있습니다.

    sudo dd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress

    다만 이 명령은 정말 조심해야 합니다. if(입력), of(출력) 순서 뒤바뀌면 끝입니다. 그리고 원본보다 작은 대상 디스크에는 사실상 맞지 않습니다. 복제 후 남는 공간 확장은 gparted나 parted로 추가 작업이 필요하죠.

    gparted는 GUI라서 파티션 확장이나 순서 확인에 좋습니다. 특히 복제 후 루트 파티션이 남는 공간을 못 쓰고 있을 때 시각적으로 확인하기 편하더라고요.

    리눅스 파티션 마이그레이션에서 rsync dd gparted 비교 이미지

    파일 단위 복사와 블록 단위 복제의 차이, 그리고 gparted로 파티션을 확장하는 보조 흐름을 비교한 이미지입니다.

    6. ⚠️ 트러블슈팅: 제가 자주 겪었던 문제들

    여기서는 이론보다 경험담이 더 중요합니다. 저도 처음엔 왜 안 되는지 몰라서 한참 헤맸던 구간들입니다.

    문제 1. 부팅은 되는데 루트 파일시스템을 못 찾는 경우

    • 원인: fstab UUID 불일치, initramfs 미갱신, 잘못된 루트 파라미터
    • 해결: 라이브 USB로 들어가서 새 루트 마운트 후 blkid, /etc/fstab, update-initramfs 재확인

    문제 2. GRUB 프롬프트만 뜨는 경우

    • 원인: GRUB 설치 누락, EFI 엔트리 미등록, 잘못된 부팅 모드
    • 해결: chroot로 들어가 grub-install과 grub-mkconfig 재실행, BIOS/UEFI 모드 일치 확인

    문제 3. rsync 후 권한 문제로 서비스가 실패하는 경우

    • 원인: ACL, xattr 누락 또는 SELinux 컨텍스트 이슈
    • 해결: -A, -X 옵션 포함 여부 확인, 배포판 보안 정책도 점검

    문제 4. 새 디스크로 부팅했는데 예전 디스크가 계속 잡히는 경우

    • 원인: 부팅 순서 미변경, EFI 엔트리 우선순위 문제
    • 해결: 펌웨어 설정에서 새 디스크를 우선 부팅 장치로 변경

    중요 포인트는 문제를 한 번에 다 보려 하지 않는 겁니다. 저는 보통 디스크 인식 → 파티션 UUID → fstab → GRUB → 커널/initramfs 순서로 좁혀갑니다. 이렇게 보면 생각보다 금방 원인이 보입니다.

    7. 검증: 리눅스 디스크 업그레이드 후 꼭 확인할 것

    마이그레이션이 끝났다고 바로 안심하면 안 됩니다. 진짜 완료는 새 디스크 단독 부팅 확인까지 해야 합니다. 저는 아래 순서로 검증합니다.

    1. 기존 디스크를 잠시 분리하거나 BIOS에서 비활성화합니다.
    2. 새 디스크만으로 부팅되는지 확인합니다.
    3. findmnt /, lsblk로 실제 루트가 새 디스크인지 확인합니다.
    4. 서비스가 정상 기동되는지 봅니다. 웹서버, 데이터베이스, 컨테이너 런타임 순으로요.
    5. 로그에 에러가 없는지 확인합니다.
    findmnt /
    lsblk -o NAME,SIZE,MOUNTPOINT,UUID
    systemctl --failed
    journalctl -p err -b

    여기서 systemctl –failed 결과가 비어 있으면 꽤 안심이 됩니다. 저는 홈랩에서는 재부팅도 2~3번 해봅니다. 한 번 성공했다고 끝이 아니더라고요.

    리눅스 파티션 마이그레이션 완료 후 새 디스크 부팅 검증 이미지

    새 디스크에서 정상 부팅된 뒤 lsblk, findmnt, systemctl 결과를 확인하는 검증 장면을 표현한 이미지입니다.

    8. 정리와 FAQ: 리눅스 파티션 마이그레이션에서 기억할 것

    이번 작업의 핵심을 한 줄로 줄이면 이겁니다. 복사보다 검증이 더 중요합니다. 리눅스 파티션 마이그레이션은 명령어 몇 줄로 끝나는 것처럼 보여도, 실제로는 부팅 구조와 파일시스템 이해가 같이 따라와야 안전합니다. 그래도 한 번 제대로 해보면 다음부터는 훨씬 덜 무섭습니다. 저도 처음엔 긴장 많이 했는데, 몇 번 해보니까 체크리스트만 잘 지키면 생각보다 안정적으로 옮겨지더라고요.

    혹시 지금 루트 디스크 이전이나 리눅스 디스크 업그레이드를 준비 중이시라면, 오늘 정리한 순서대로만 가셔도 큰 사고는 피할 가능성이 높습니다. 다음 글에서는 LVM 기반 마이그레이션이나 암호화 디스크 이전도 따로 다뤄보려고 합니다. 이전 글에서 백업 전략을 정리한 내용과도 같이 보면 더 도움이 될 겁니다.

    자주 묻는 질문

    • Q. 온라인 상태에서 옮겨도 되나요?
      가능은 하지만, 일관성 문제가 생길 수 있어서 가능하면 라이브 환경에서 작업하는 편이 안전합니다.
    • Q. rsync와 dd 중 뭐가 더 안전한가요?
      무조건 한쪽이 안전하다고 보긴 어렵습니다. 구조 변경이 있으면 rsync, 동일 복제가 목표면 dd가 맞는 경우가 많습니다.
    • Q. gparted만으로 끝낼 수 있나요?
      파티션 조정엔 좋지만, 루트 디스크 이전 전체를 완성하려면 부트로더와 fstab 확인이 필요합니다.
    • Q. 데이터 손실 방지를 위해 가장 중요한 한 가지는?
      백업입니다. 이건 아무리 강조해도 부족하지 않습니다.
    체크 항목 확인 여부 메모
    원본 백업 완료 필수 외부 저장소 권장
    UUID 확인 필수 fstab와 일치해야 함
    GRUB 재설치 권장 특히 새 디스크 단독 부팅 시 중요
    새 디스크 단독 부팅 테스트 필수 기존 디스크 의존성 제거 확인

    백업, UUID, fstab, GRUB, 단독 부팅 테스트까지 핵심 점검 항목을 한눈에 정리한 요약 이미지입니다.

    🎉 여기까지 마무리하면 기본적인 리눅스 파티션 마이그레이션은 끝입니다. 드디어 됐다! 싶어도 마지막 재부팅 검증은 꼭 해보세요. 그 한 번이 진짜 차이를 만듭니다.