13년차의 서버실

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

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