SM.K

Blog

OrbStack으로 시작하는 Kubernetes

OrbStack을 이용해 로컬 Kubernetes 클러스터를 구축하고 nginx를 배포하는 과정을 알아봅니다.

로컬 개발 환경에서 Kubernetes를 다루는 일은 언제나 딜레마였습니다. Docker Desktop은 너무 무겁고, minikube는 설정이 번거롭고, kind는 네트워킹이 복잡합니다. 이런 상황에서 OrbStack이 등장했습니다. 과연 이 도구가 로컬 Kubernetes 개발의 새로운 표준이 될 수 있을까요?

Docker Desktop의 한계

Docker Desktop은 오랫동안 macOS 개발자들의 사실상 표준이었습니다. 하지만 시간이 갈수록 그 무게감이 부담스러워졌습니다. 유휴 상태에서도 1GB 이상의 메모리를 점유하고, 시작하는 데만 30초 이상이 걸립니다. Kubernetes를 활성화하면 상황은 더 악화됩니다.

문제는 단순히 성능만이 아닙니다. Docker Desktop의 라이선스 정책 변경으로 많은 기업들이 대안을 찾기 시작했습니다. 개인 사용자에게는 무료지만, 일정 규모 이상의 기업은 유료 구독이 필요합니다. 이는 개발 도구 선택에 있어 비기술적 요소를 고려해야 한다는 불편한 현실을 만들었습니다.

OrbStack의 등장

OrbStack은 이런 맥락에서 등장했습니다. macOS 네이티브로 설계된 이 도구는 Docker Desktop과 동일한 기능을 제공하면서도 놀라울 정도로 가볍습니다.

숫자로 말하자면, 유휴 상태 메모리 사용량은 50MB 수준입니다. Docker Desktop의 1/20입니다. 시작 시간은 1초 미만입니다. 이는 단순한 최적화의 결과가 아니라, 아키텍처 자체가 다르기 때문입니다. OrbStack은 macOS의 Virtualization Framework를 직접 활용하여 불필요한 레이어를 제거했습니다.

개인 사용은 영구 무료입니다. 기업 사용은 유료지만, Docker Desktop보다 저렴합니다. 라이선스 문제에서도 상대적으로 자유롭습니다.

Kubernetes 환경 구축

OrbStack의 진가는 Kubernetes를 다룰 때 드러납니다. 별도의 설치나 복잡한 설정 없이, 토글 하나로 Kubernetes 클러스터를 활성화할 수 있습니다.

orb k8s start

10초 후, 로컬 Kubernetes 클러스터가 준비됩니다. Docker Desktop의 Kubernetes가 1-2분 걸리는 것과 대조적입니다.

kubectl get nodes

orbstack이라는 단일 노드가 Ready 상태로 나타납니다. 프로덕션 환경은 아니지만, 로컬 개발과 학습에는 충분합니다.

nginx 배포: 이론과 실제

Kubernetes에서 애플리케이션을 실행하는 기본 단위는 Pod입니다. 하지만 실무에서 Pod를 직접 생성하는 경우는 거의 없습니다. 대신 Deployment를 사용합니다. Deployment는 Pod의 생명주기를 관리하고, 자가 치유(self-healing)와 스케일링 기능을 제공합니다.

nginx-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80

여기서 replicas: 3은 단순히 세 개의 인스턴스를 실행한다는 의미가 아닙니다. 이는 desired state를 선언하는 것입니다. Kubernetes는 항상 이 상태를 유지하려 합니다. 하나의 Pod가 죽으면, 자동으로 새로운 Pod를 생성합니다. 이것이 선언적 관리(declarative management)의 핵심입니다.

kubectl apply -f nginx-deployment.yaml
kubectl get pods

몇 초 후, 세 개의 nginx Pod가 Running 상태로 나타납니다. 이는 이미지 다운로드, 컨테이너 시작, 헬스체크를 모두 포함한 시간입니다.

Service를 통한 네트워크 추상화

Pod는 일시적입니다. 언제든 죽고 다시 생성될 수 있으며, 그때마다 IP 주소가 바뀝니다. 그렇다면 어떻게 안정적으로 Pod에 접근할 수 있을까요? 이것이 Service가 해결하는 문제입니다.

nginx-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80

Service는 label selector를 통해 Pod를 찾습니다. app: nginx 레이블을 가진 모든 Pod에 트래픽을 분산합니다. Pod가 추가되거나 제거되어도, Service는 자동으로 엔드포인트를 업데이트합니다.

kubectl apply -f nginx-service.yaml

여기서 OrbStack의 차별점이 다시 드러납니다. LoadBalancer 타입의 Service는 본래 클라우드 환경(AWS ELB, GCP Load Balancer 등)을 위한 것입니다. 로컬 환경에서는 일반적으로 작동하지 않습니다. minikube는 minikube tunnel을, kind는 별도의 ingress 컨트롤러를 요구합니다.

하지만 OrbStack은 자동으로 localhost에 매핑합니다. 추가 설정이 필요 없습니다. http://localhost로 바로 접근할 수 있습니다. 이는 작은 차이처럼 보이지만, 개발 경험에 큰 영향을 미칩니다.

스케일링: 선언적 관리의 힘

Kubernetes의 철학은 “무엇을(what)“을 선언하면, 시스템이 “어떻게(how)“를 처리한다는 것입니다.

kubectl scale deployment nginx-deployment --replicas=5

이 명령은 단순히 Pod를 두 개 더 시작하는 것이 아닙니다. Deployment의 desired state를 3에서 5로 변경하는 것입니다. Kubernetes controller가 이를 감지하고, 필요한 조치를 취합니다. 새로운 Pod를 생성하고, 스케줄링하고, Service의 엔드포인트에 추가합니다.

kubectl scale deployment nginx-deployment --replicas=2

반대로 줄일 때도 마찬가지입니다. Kubernetes는 graceful shutdown을 수행합니다. SIGTERM 신호를 보내고, 일정 시간 대기한 후, 필요하다면 SIGKILL로 강제 종료합니다. 이 과정에서 Service는 이미 해당 Pod를 엔드포인트에서 제거하여, 트래픽이 종료 중인 Pod로 가지 않도록 합니다.

ConfigMap을 통한 설정 주입

컨테이너의 불변성(immutability)은 장점이자 단점입니다. 설정을 변경하려면 이미지를 다시 빌드해야 할까요? 그렇지 않습니다. ConfigMap을 사용하면 설정을 외부화할 수 있습니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-html
data:
  index.html: |
    <!DOCTYPE html>
    <html>
    <head>
        <title>OrbStack + K8s</title>
        <style>
            body {
                font-family: system-ui;
                display: flex;
                justify-content: center;
                align-items: center;
                height: 100vh;
                margin: 0;
                background: #0a0a0a;
                color: #ededed;
            }
            h1 { color: #fbbf24; }
        </style>
    </head>
    <body>
        <div>
            <h1>🚀 OrbStack + Kubernetes</h1>
            <p>It works!</p>
        </div>
    </body>
    </html>

이 ConfigMap을 Deployment에 마운트하면:

spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
        - containerPort: 80
      volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
  volumes:
    - name: html
      configMap:
        name: nginx-html

컨테이너 내부의 /usr/share/nginx/html 디렉토리가 ConfigMap의 내용으로 채워집니다. 이미지를 다시 빌드하지 않고도 HTML을 변경할 수 있습니다.

하지만 주의할 점이 있습니다. ConfigMap의 변경은 자동으로 Pod에 반영되지 않습니다. Pod를 재시작해야 합니다. 이는 Kubernetes의 설계 결정입니다. 자동 재시작은 예측 불가능한 동작을 유발할 수 있기 때문입니다.

kubectl apply -f nginx-configmap.yaml
kubectl rollout restart deployment nginx-deployment

이제 브라우저를 새로고침하면 커스텀 페이지가 나타납니다.

한계와 고려사항

OrbStack이 완벽한가요? 그렇지 않습니다.

첫째, 단일 노드 클러스터입니다. 멀티 노드 환경의 스케줄링, 네트워크 정책, 노드 간 통신 같은 것들은 테스트할 수 없습니다. 프로덕션 시나리오를 정확히 재현하기에는 한계가 있습니다.

둘째, macOS 전용입니다. Linux나 Windows 사용자는 대안을 찾아야 합니다. 팀 전체가 동일한 환경을 사용하기 어렵다는 의미입니다.

셋째, 상대적으로 새로운 도구입니다. Docker Desktop의 성숙도와 커뮤니티 크기를 따라잡으려면 시간이 필요합니다. 예상치 못한 버그나 호환성 문제를 만날 가능성이 있습니다.

하지만 이런 한계에도 불구하고, 로컬 개발 환경으로서 OrbStack의 가치는 명확합니다. 빠르고, 가볍고, 사용하기 쉽습니다. 대부분의 로컬 개발 시나리오에서 이것만으로 충분합니다.

정리

kubectl delete -f nginx-service.yaml
kubectl delete -f nginx-deployment.yaml
kubectl delete -f nginx-configmap.yaml
orb k8s stop

네 줄의 명령으로 모든 것이 깔끔하게 정리됩니다.

결론

OrbStack은 로컬 Kubernetes 개발의 새로운 가능성을 보여줍니다. Docker Desktop의 무거움을 벗어나면서도, minikube의 복잡함을 피할 수 있습니다. 완벽하지는 않지만, 대부분의 경우 충분히 좋습니다.

Kubernetes를 배우는 사람에게, 로컬에서 빠르게 실험하고 싶은 개발자에게, OrbStack은 좋은 선택입니다. 도구가 방해가 되지 않을 때, 우리는 본질에 집중할 수 있습니다. Kubernetes의 본질은 클러스터 관리가 아니라, 애플리케이션의 desired state를 선언하고 시스템이 그것을 유지하도록 하는 것입니다.

OrbStack은 그 여정을 더 쉽게 만들어줍니다.