# 3. OSMO 설치 (Helm)

OSMO는 여러 마이크로서비스로 구성됩니다. Helm 차트를 사용해 이 서비스들을 Kubernetes에 배포합니다.

**OSMO 아키텍처:**

```
┌─ osmo-minimal 네임스페이스 ─────────────────────────┐
│  osmo-service  — CLI/API 엔드포인트                  │
│  osmo-agent    — 내부 작업 조율                      │
│  osmo-logger   — 워크플로 로그 수집 (ctrl sidecar)   │
│  osmo-worker   — 백그라운드 작업 처리                │
└──────────────────────────────────────────────────────┘
┌─ osmo-operator 네임스페이스 ─────────────────────────┐
│  backend-operator — 워크플로 Pod 생성/관리            │
└──────────────────────────────────────────────────────┘
┌─ kai-scheduler 네임스페이스 ─────────────────────────┐
│  kai-scheduler — NVIDIA GPU 스케줄러                  │
│  (Queue CRD로 GPU 쿼터 관리)                         │
└──────────────────────────────────────────────────────┘
┌─ osmo-workflows 네임스페이스 ────────────────────────┐
│  (사용자 워크플로 Pod가 여기서 실행됨)               │
└──────────────────────────────────────────────────────┘
```

***

### 3.1 네임스페이스 생성

각 컴포넌트가 실행될 Kubernetes 네임스페이스를 만듭니다.

```bash
kubectl create namespace osmo-minimal
kubectl create namespace osmo-operator
kubectl create namespace osmo-workflows
```

***

### 3.2 Helm 레포 추가

NVIDIA NGC 레지스트리에서 OSMO Helm 차트를 받을 수 있도록 레포를 등록합니다.

```bash
helm repo add osmo https://helm.ngc.nvidia.com/nvidia/osmo \
  --username '$oauthtoken' --password "${NGC_API_KEY}"
helm repo update
```

{% hint style="warning" %}
`NGC_API_KEY`가 환경 변수로 설정되어 있어야 합니다. [NGC 포털](https://org.ngc.nvidia.com/)에서 발급받으세요.
{% endhint %}

***

### 3.3 인프라 엔드포인트 확인

CDK가 생성한 RDS, Redis 주소를 가져옵니다. OSMO가 이 데이터베이스들에 연결해야 하므로 주소가 필요합니다.

```bash
POSTGRES_HOST=$(aws rds describe-db-instances \
  --query "DBInstances[?DBInstanceIdentifier=='osmo-postgres'].Endpoint.Address" \
  --output text --region us-west-2)

REDIS_HOST=$(aws elasticache describe-replication-groups \
  --query "ReplicationGroups[?ReplicationGroupId=='osmo-redis'].NodeGroups[0].PrimaryEndpoint.Address" \
  --output text --region us-west-2)

echo "PostgreSQL: $POSTGRES_HOST"
echo "Redis: $REDIS_HOST"
```

***

### 3.4 Kubernetes Secret 생성

**왜 필요한가?** 데이터베이스 패스워드 같은 민감 정보는 Helm values에 직접 넣지 않고 Kubernetes Secret으로 따로 관리합니다. OSMO Pod가 실행될 때 이 Secret을 참조합니다.

```bash
# PostgreSQL 접속 패스워드
kubectl create secret generic db-secret \
  --from-literal=db-password="YOUR_DB_PASSWORD" \
  --namespace osmo-minimal

# Redis 접속 패스워드
kubectl create secret generic redis-secret \
  --from-literal=redis-password="YOUR_REDIS_PASSWORD" \
  --namespace osmo-minimal

# Master Encryption Key (MEK) — OSMO 내부 토큰 암호화에 사용
MEK_KEY=$(openssl rand -base64 32 | tr -d '\n')
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: mek-config
  namespace: osmo-minimal
data:
  mek.yaml: |
    currentMek: key1
    meks:
      key1: '{"k":"${MEK_KEY}","kid":"key1","kty":"oct"}'
EOF
```

{% hint style="info" %}
`YOUR_DB_PASSWORD` / `YOUR_REDIS_PASSWORD`는 CDK 배포 시 `cdk.json` 또는 환경 변수로 지정한 값을 사용하세요.
{% endhint %}

***

### 3.5 NGC Pull Secret 생성

**왜 필요한가?** OSMO 컨테이너 이미지는 NVIDIA의 프라이빗 레지스트리(nvcr.io)에 호스팅됩니다. Kubernetes가 이미지를 pull하려면 인증 정보가 필요합니다.

```bash
# OSMO 서비스 네임스페이스
kubectl create secret docker-registry nvcr-secret \
  --namespace osmo-minimal \
  --docker-server=nvcr.io \
  --docker-username='$oauthtoken' \
  --docker-password="${NGC_API_KEY}"

# 워크플로 네임스페이스 (워크플로 Pod도 OSMO init-container를 pull함)
kubectl create secret docker-registry nvcr-secret \
  --namespace osmo-workflows \
  --docker-server=nvcr.io \
  --docker-username='$oauthtoken' \
  --docker-password="${NGC_API_KEY}"
```

***

### 3.6 OSMO Service 배포

OSMO의 핵심 서비스들을 배포합니다. values 파일로 RDS/Redis 연결 정보를 전달합니다.

```bash
cat > /tmp/service_values.yaml <<EOF
global:
  osmoImageLocation: nvcr.io/nvidia/osmo
  osmoImageTag: latest
  imagePullSecret: nvcr-secret

services:
  configFile:
    enabled: true

  postgres:
    enabled: false
    serviceName: ${POSTGRES_HOST}
    port: 5432
    db: osmo
    user: postgres
    passwordSecretName: db-secret
    passwordSecretKey: db-password

  redis:
    enabled: false
    serviceName: ${REDIS_HOST}
    port: 6379
    tlsEnabled: true

  agent:
    scaling:
      minReplicas: 1
      maxReplicas: 1

  logger:
    scaling:
      minReplicas: 1
      maxReplicas: 1

podMonitor:
  enabled: false
EOF

helm install osmo-minimal osmo/service \
  --namespace osmo-minimal \
  --values /tmp/service_values.yaml \
  --wait --timeout 10m
```

{% hint style="info" %}
`postgres.enabled: false`와 `redis.enabled: false`는 "OSMO가 자체 DB/Redis를 배포하지 않고, 외부(CDK가 만든) 것을 사용한다"는 뜻입니다.
{% endhint %}

***

### 3.7 Backend Operator 배포

**왜 필요한가?** Backend Operator는 사용자가 워크플로를 제출하면 실제 Kubernetes Pod를 생성하는 역할을 합니다. OSMO Service와 통신하기 위한 인증 토큰이 필요합니다.

```bash
# OSMO Service에 접속하여 operator용 토큰 발급
kubectl port-forward service/osmo-service 9001:9001 -n osmo-minimal &
sleep 5

# OSMO CLI 설치 및 로그인
pip install nvidia-osmo
osmo login --url http://localhost:9001 --dev-login

# Backend Operator가 사용할 서비스 토큰 생성
OPERATOR_TOKEN=$(osmo token set backend-token \
  --expires-at 2027-01-01 \
  --description "Backend Operator Token" \
  --service \
  --roles osmo-backend \
  -t json | jq -r '.token')

kill %1  # port-forward 종료

# 토큰을 Kubernetes Secret으로 저장
kubectl create secret generic osmo-operator-token \
  --from-literal=token="${OPERATOR_TOKEN}" \
  --namespace osmo-operator

# Operator 배포
cat > /tmp/operator_values.yaml <<EOF
global:
  osmoImageLocation: nvcr.io/nvidia/osmo
  osmoImageTag: latest
  accountTokenSecret: osmo-operator-token
  agentNamespace: osmo-operator
  backendName: default
  backendNamespace: osmo-workflows
  loginMethod: token
  serviceUrl: http://osmo-agent.osmo-minimal.svc.cluster.local

podMonitor:
  enabled: false

sidecars:
  otel:
    enabled: false
EOF

helm install osmo-operator osmo/backend-operator \
  --namespace osmo-operator \
  --values /tmp/operator_values.yaml \
  --wait --timeout 5m
```

***

### 3.8 Kai Scheduler 설치

**왜 필요한가?** NVIDIA Kai Scheduler는 여러 워크플로가 동시에 GPU를 요청할 때 공정하게 분배하는 스케줄러입니다. Queue CRD를 통해 팀/프로젝트별 GPU 쿼터를 설정할 수 있습니다.

```bash
helm install kai-scheduler osmo/kai-scheduler \
  --namespace kai-scheduler --create-namespace \
  --version 0.13.4
```

***

### 3.9 배포 확인

Helm 릴리스와 Pod 상태를 확인합니다.

```bash
# Helm 릴리스 확인 — 3개 차트가 모두 deployed 상태여야 합니다
helm list --all-namespaces
```

**예상 출력:**

```
NAME             NAMESPACE       STATUS     CHART
osmo-minimal     osmo-minimal    deployed   service-x.x.x
osmo-operator    osmo-operator   deployed   backend-operator-x.x.x
kai-scheduler    kai-scheduler   deployed   kai-scheduler-0.13.4
```

```bash
# OSMO 서비스 Pod (5개)
kubectl get pods -n osmo-minimal
```

**예상 출력:**

```
NAME                                        READY   STATUS    AGE
osmo-agent-xxx                              2/2     Running   2m
osmo-delayed-job-monitor-xxx                1/1     Running   2m
osmo-logger-xxx                             2/2     Running   2m
osmo-service-xxx                            2/2     Running   2m
osmo-worker-xxx                             1/1     Running   2m
```

```bash
# Kai Scheduler
kubectl get pods -n kai-scheduler

# Backend Operator
kubectl get pods -n osmo-operator
```

{% hint style="warning" %}
모든 Pod가 Running이 아니라면, `kubectl describe pod <pod-name> -n <namespace>`로 이벤트를 확인하세요.
{% endhint %}

***

### Troubleshooting

| 증상                     | 원인          | 해결                                 |
| ---------------------- | ----------- | ---------------------------------- |
| Helm install timeout   | DB 연결 실패    | `POSTGRES_HOST`, `REDIS_HOST` 값 확인 |
| Pod ImagePullBackOff   | NGC 인증 실패   | nvcr-secret의 `NGC_API_KEY` 확인      |
| osmo-service CrashLoop | DB 패스워드 불일치 | db-secret 내용 확인                    |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hi-space.gitbook.io/physical-ai-on-aws/physical-ai-on-aws-guide/nvidia-osmo-on-aws/3.-osmo-install.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
