13년차의 서버실

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

[태그:] 인프라 최적화

  • [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 유지 여부, 실제 요청 성공, 재배포 후 재현성. 화려한 대시보드보다 이 네 가지가 먼저입니다.

  • [Cloud] Spot Instance 활용 극대화: 비용 절감과 안정성 확보 전략

    [Cloud] Spot Instance 활용 극대화: 비용 절감과 안정성 확보 전략

    [클라우드] Spot Instance 활용 전략: 비용 절감과 안정성 확보

    Spot Instance 활용 전략은 클라우드 비용 절감을 고민하는 팀이라면 한 번쯤은 반드시 붙잡게 되는 주제입니다. 온디맨드(On-Demand, 필요 시 즉시 쓰는 기본 과금 방식)만으로 운영하면 편하긴 한데, 배치 작업이 늘고 개발 환경이 많아질수록 월말 청구서가 꽤 아프거든요. 저도 홈랩이랑 실제 운영 환경에서 비슷한 고민을 오래 했었습니다. 처음엔 “싸면 좋은 거 아닌가?” 하고 덤볐다가, 중간에 인스턴스가 회수(eviction, 클라우드 제공자가 자원을 다시 가져감)되면서 삽질 좀 했습니다 ㅎㅎ 그런데 구조를 조금만 바꾸니까, 비용 절감과 가용성 관리 두 마리 토끼를 꽤 안정적으로 잡을 수 있더라고요.

    이번 글에서는 특정 신제품이나 불확실한 기능 얘기 말고, 이미 널리 알려진 Spot 개념과 검증된 운영 패턴 위주로 정리해보겠습니다. 특히 클라우드 비용 절감, 탄력적 컴퓨팅, 가용성 관리 관점에서 어떤 워크로드에 Spot을 붙여야 하는지, 어디서 사고가 나는지, 그리고 어떻게 안전장치를 걸어야 하는지를 경험 기반으로 풀어볼게요.

    Spot Instance 활용 전략 기반 비용 최적화 아키텍처 개요

    Spot Instance와 On-Demand, Auto Scaling, 작업 큐, 모니터링이 연결된 전체 구성 예시입니다.

    1. 왜 Spot Instance 활용 전략이 중요한가

    쉽게 말해 Spot Instance는 “남는 자원을 할인해서 쓰는 방식”입니다. 다만 조건이 있죠. 클라우드 사업자가 해당 자원이 다시 필요해지면 인스턴스를 종료할 수 있습니다. 이 특징 때문에 아무 데나 넣으면 안 되고, 중단을 감당할 수 있는 구조에 넣어야 합니다.

    제가 직접 해보니, Spot은 단순히 인스턴스 타입 하나 바꿔서 끝나는 문제가 아니었습니다. 워크로드 분리, 상태 저장 위치, 종료 신호 처리, 용량 분산, 모니터링까지 같이 가야 비로소 전략이 되더라고요. 여기서 중요한 포인트는 딱 하나입니다. Spot을 믿지 말고, 시스템 설계를 믿어야 합니다.

    • 비용에 민감한 배치(Batch, 일괄 처리) 작업에 유리합니다.
    • 수평 확장(Horizontal Scaling, 인스턴스를 여러 대 늘리는 방식) 구조와 잘 맞습니다.
    • 상태 비저장(Stateless, 개별 서버에 중요한 상태를 남기지 않는 구조) 서비스에서 특히 강합니다.
    • 반대로 단일 노드 데이터베이스처럼 중단을 허용하기 어려운 워크로드에는 정말 조심해야 해요.

    2. Spot Instance 개념 설명: 쉽게 말해 어떤 자원인가

    Spot Instance는 할인 폭이 큰 대신, 언제든 회수될 수 있는 가변 자원입니다. AWS에서는 EC2 Spot Instances가 대표적이고, 다른 클라우드에도 비슷한 개념이 있습니다. 이름은 조금씩 달라도 핵심은 같습니다. 싸지만 보장되지 않는 자원입니다.

    저도 처음엔 헷갈렸는데, 아래처럼 구분하면 훨씬 이해가 잘 돼요.

    구분 On-Demand Reserved/Savings 계열 Spot
    비용 기본 장기 약정 시 절감 높은 할인 가능
    가용성 높음 높음 회수 가능성 있음
    적합한 워크로드 핵심 서비스 예측 가능한 상시 부하 배치, CI, 렌더링, 워커
    운영 난이도 낮음 중간 높음

    Spot Instance 활용 전략의 핵심은 “Spot만 쓰겠다”가 아니라, 기본 용량은 안정적인 방식으로 확보하고, 변동 용량만 Spot으로 가져가는 것입니다. 이 조합이 실제로 가장 덜 아픕니다.

    3. 어떤 워크로드에 Spot을 붙여야 하나

    여기서 방향을 잘못 잡으면 비용은 줄었는데 장애 대응 시간만 늘어납니다. 실제로 써보니까 아래 기준이 꽤 명확했습니다.

    잘 맞는 경우

    • 큐(Queue, 작업 대기열) 기반 워커
    • CI 빌드 에이전트
    • 로그 처리, 이미지 변환, 데이터 분석 배치
    • 오토스케일링이 잘 되는 웹 애플리케이션의 일부 용량
    • 개발/테스트 환경

    조심해야 하는 경우

    • 단일 인스턴스 데이터베이스
    • 세션(Session, 사용자 상태)이 로컬 메모리에 강하게 묶인 서비스
    • 종료 전 정리 시간이 거의 없는 실시간 처리
    • 라이선스나 초기화 시간이 긴 상용 소프트웨어

    혹시 이런 경험 있으신가요? CPU는 넉넉한데 서버 비용이 계속 오르는 상황이요. 그런 경우 대부분은 피크 부하 대응용 여유분이 상시 켜져 있는 패턴이 많습니다. 이런 부분을 Spot으로 치환하면 효과가 잘 납니다. 대신 핵심 트래픽 바닥 용량(base capacity)은 On-Demand나 예약형으로 두는 게 안전합니다.

    4. 실전 구현 1: Auto Scaling으로 Spot과 On-Demand 섞어 쓰기

    실전에서는 혼합 운영이 기본입니다. 예를 들어 웹 워커 풀(worker pool)을 10대로 운영한다고 치면, 최소 3대는 On-Demand로 깔고 나머지 7대는 Spot으로 채우는 식이죠. 이렇게 하면 Spot 회수가 와도 서비스 전체가 바로 흔들리지는 않습니다.

    1. 서비스를 상태 비저장으로 정리합니다. 세션은 Redis 같은 외부 저장소로 뺍니다.
    2. Auto Scaling Group을 구성합니다.
    3. 기본 용량은 On-Demand로 확보합니다.
    4. 추가 확장분은 여러 인스턴스 타입의 Spot으로 분산합니다.
    5. 로드 밸런서와 헬스 체크를 연결합니다.

    아래 예시는 개념 전달용 설정 예시입니다. 핵심은 여러 타입, 여러 가용 영역, 혼합 용량입니다.

    spot_strategy:
      base_on_demand_capacity: 3
      desired_capacity: 10
      spot_pools:
        - c6i.large
        - c5.large
        - m6i.large
        - m5.large
      availability_zones:
        - ap-northeast-2a
        - ap-northeast-2c
      allocation_strategy: capacity-optimized
    

    제가 처음에 했던 실수는 인스턴스 타입을 하나만 넣은 겁니다. 그랬더니 특정 타입 수급이 불안정할 때 대체가 안 되더라고요. 반면 타입과 가용 영역을 넓혀두면 Spot 회수 이벤트가 와도 전체 풀은 훨씬 덜 흔들립니다. 탄력적 컴퓨팅은 결국 선택지를 많이 주는 설계에서 나옵니다.

    aws ec2 create-launch-template \
      --launch-template-name web-spot-template \
      --launch-template-data '{
        "ImageId":"ami-xxxxxxxx",
        "InstanceType":"m5.large",
        "SecurityGroupIds":["sg-xxxxxxxx"],
        "IamInstanceProfile":{"Name":"ec2-app-role"}
      }'
    

    실무에서는 이걸 Terraform이나 CloudFormation 같은 IaC(Infrastructure as Code, 코드형 인프라)로 관리하는 편이 더 낫습니다. 사람이 콘솔에서 몇 번 누르다 보면 언젠가 drift(설정 불일치)가 생기거든요.

    Spot Instance 활용 전략을 위한 혼합 오토스케일링 구성 다이어그램

    기본 용량은 On-Demand로 유지하고, 확장분을 여러 Spot 풀로 분산하는 구성 예시입니다.

    5. 실전 구현 2: Spot 종료 신호 처리와 안전한 Drain

    Spot을 오래 쓰려면 종료 신호를 꼭 다뤄야 합니다. AWS의 경우 Spot interruption notice(중단 예고 알림)를 메타데이터에서 확인할 수 있는 패턴이 널리 알려져 있습니다. 이 신호를 감지하면 인스턴스는 새 작업을 받지 않고, 처리 중인 작업을 정리한 뒤 빠지도록 만들어야 합니다.

    특히 큐 기반 워커에서는 효과가 큽니다. 작업을 꺼내기 전에 락(lock)이나 가시성 타임아웃(visibility timeout)을 잘 설정해두면, 종료 중인 노드가 빠져도 다른 워커가 이어받을 수 있거든요. 이거 진짜 편하더라고요.

    #!/usr/bin/env bash
    set -euo pipefail
    
    TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
      -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
    
    while true; do
      ACTION=$(curl -sf -H "X-aws-ec2-metadata-token: ${TOKEN}" \
        http://169.254.169.254/latest/meta-data/spot/instance-action || true)
    
      if [ -n "${ACTION}" ]; then
        echo "Spot interruption detected: ${ACTION}"
        touch /tmp/stop-accepting-jobs
        systemctl stop my-worker.service
        sleep 20
        shutdown -h now
      fi
    
      sleep 5
    done
    

    웹 서비스라면 로드 밸런서에서 먼저 빼고, 워커라면 큐 소비를 중단하는 식으로 접근하시면 됩니다. 쿠버네티스(Kubernetes, 컨테이너 오케스트레이션)를 쓰는 분들은 노드 축출(eviction)과 파드 재스케줄링 쪽도 함께 봐야 하고요. 이 부분은 다음 글에서 따로 다뤄볼 예정입니다.

    if [ -f /tmp/stop-accepting-jobs ]; then
      echo "Node is draining. Skip new job fetch."
      exit 0
    fi
    

    여기서 중요한 포인트! 작업을 중간에 잃어버리지 않도록 재시도 가능(idempotent, 여러 번 실행돼도 결과가 꼬이지 않는)하게 만드는 것이 Spot 운영의 핵심입니다.

    6. ⚠️ 주의사항과 트러블슈팅: 제가 실제로 많이 부딪힌 문제

    Spot은 싸지만, 운영 습관이 잘못 잡혀 있으면 바로 티가 납니다. 아래는 제가 직접 해보니 자주 터졌던 문제들입니다.

    문제 1. 로컬 디스크에 상태를 저장했다가 데이터 유실

    처음엔 캐시 파일 정도는 괜찮겠지 했었는데, 재시작 시 복구 시간이 길어져서 결국 더 느려졌습니다. 해결은 단순했습니다. 중요한 상태는 오브젝트 스토리지(Object Storage, 객체 저장소)나 DB, 공유 스토리지로 빼고, 인스턴스는 최대한 가볍게 만들었습니다.

    문제 2. Spot 비율을 너무 높게 잡아서 회수 이벤트 때 동시 흔들림

    비용 욕심이 과하면 바로 대가를 치릅니다. 핵심 서비스는 최소한의 On-Demand 바닥 용량을 두세요. 저는 초기에 거의 전부 Spot으로 돌렸다가 특정 시간대에 용량 확보가 안 되면서 식은땀 좀 났습니다.

    문제 3. 특정 인스턴스 패밀리만 고집

    컴퓨트 최적화(Compute Optimized)만, 또는 메모리 최적화(Memory Optimized)만 고집하면 대체 폭이 줄어듭니다. 애플리케이션이 허용한다면 비슷한 스펙 계열을 넓게 열어두는 편이 훨씬 안정적입니다.

    문제 4. 모니터링 없이 비용만 봄

    클라우드 비용 절감만 보면 반쪽짜리입니다. 회수 횟수, 재시작 빈도, 작업 실패율, 평균 처리 시간, 큐 적체량도 같이 봐야 합니다. 비용은 내려갔는데 SLA(Service Level Agreement, 서비스 수준 목표)가 망가지면 의미가 없거든요.

    • 권장 메트릭: 인스턴스 교체 횟수, 헬스 체크 실패율, 평균 대기 시간, 작업 재시도 횟수
    • 권장 로그: 종료 예고 감지 시점, drain 시작/종료 시점, 작업 재할당 기록
    • 권장 알람: 원하는 용량 미달, 큐 적체 급증, 에러율 상승

    7. 검증과 결과: 무엇을 확인해야 성공인가

    Spot을 붙이고 나면 “청구서가 줄었다”만 보면 안 됩니다. 저는 보통 아래 3가지를 같이 확인합니다.

    1. 월별 또는 주별 인프라 비용 추이
    2. 장애나 성능 저하 없이 목표 처리량을 유지했는지
    3. 회수 이벤트가 와도 자동 복구가 되는지

    가장 쉬운 검증 방법은 작은 워커 풀부터 시작하는 겁니다. 예를 들어 전체 20대 중 2대만 Spot으로 시작해보세요. 그다음 20%, 40%, 60% 식으로 천천히 올리면서 실패율과 큐 적체를 같이 봅니다. 실제로 써보니까 한 번에 크게 바꾸는 것보다 이 방식이 훨씬 덜 아픕니다.

    검증 항목 확인 포인트 성공 기준 예시
    비용 Spot 도입 전후 비교 유의미한 절감 추세 확인
    안정성 회수 시 자동 복구 여부 수동 개입 최소화
    성능 처리량, 지연 시간 기존 목표 유지
    운영성 알람, 로그, 런북 장애 대응 절차 정립
    Spot Instance 활용 전략 적용 후 비용 절감과 성공률 검증 대시보드

    비용 절감 효과와 함께 작업 성공률, 재시도 횟수, 큐 적체량을 함께 보는 검증 화면 예시입니다.

    🎉 잘 설계된 Spot Instance 활용 전략은 비용만 줄이는 게 아니라, 오히려 아키텍처를 더 탄탄하게 만드는 계기가 되기도 합니다. 중단을 전제로 설계하다 보니 시스템이 전반적으로 단단해지거든요.

    8. 정리와 FAQ: 비용 절감과 안정성 확보, 둘 다 잡으려면

    정리하면 이렇습니다. Spot은 마법의 할인 버튼이 아니라, 중단 가능성을 시스템 설계로 흡수하는 방식입니다. 저도 처음엔 할인율만 보고 달려들었다가, 종료 처리와 상태 분리의 중요성을 몸으로 배웠습니다. 근데 그 과정을 한번 넘고 나니 운영이 훨씬 유연해지더라고요.

    Spot Instance 활용 전략 운영 체크리스트와 도입 우선순위 인포그래픽

    도입 대상 선정, 혼합 비율, 종료 처리, 모니터링까지 한 장으로 정리한 체크리스트 이미지입니다.

    자주 묻는 질문

    • Q. Spot을 핵심 서비스에도 쓸 수 있나요?
      A. 가능합니다. 다만 전체를 맡기기보다는 일부 확장 용량에 제한적으로 넣는 방식이 현실적입니다.
    • Q. 가장 먼저 붙여볼 대상은 뭔가요?
      A. 배치, 워커, CI처럼 재시도가 쉬운 작업이 좋습니다.
    • Q. 가용성 관리는 어떻게 시작하면 좋을까요?
      A. 종료 예고 감지, drain, 재시도, 외부 상태 저장 이 네 가지부터 챙기시면 됩니다.

    마지막으로 추천드리고 싶은 순서는 이렇습니다.

    1. 개발/배치 환경부터 Spot을 적용합니다.
    2. 혼합 비율을 낮게 시작합니다.
    3. 중단 처리 자동화를 먼저 완성합니다.
    4. 모니터링과 런북(runbook, 대응 절차 문서)을 정리합니다.
    5. 그다음에 프로덕션 확장 용량으로 넓힙니다.

    Spot Instance 활용 전략은 결국 “싼 자원을 안전하게 쓰는 설계”입니다. 여기만 잡히면 클라우드 비용 절감이 꽤 현실적으로 보이기 시작합니다. 이전 글에서 다뤘던 오토스케일링 기본기와 함께 보시면 더 이해가 잘 되실 거고, 다음 글에서는 쿠버네티스 환경에서 Spot 노드 운영 팁을 이어서 다뤄보겠습니다.