본문 바로가기
[GPUaaS]/TensorFlow

[중요3] 운영 표준 - [최종] KServer & NAS & S3 & TFJob.yaml (라벨/MIG/RWO/RWM 적용)

by METAVERSE STORY 2026. 2. 1.
반응형

 

 

좋아, 이제 “실제 운영에 쓰는 풀버전 TFJob 설계” 단계로 제대로 가보자.
아래 설명은 요약 없음, 처음부터 끝까지,
👉 왜 이렇게 해야 하는지 → 어디서 실행되는지 → 각 필드의 의미까지 전부 풀어줄게.


0️⃣ 먼저 전체 흐름부터 (이걸 머리에 그려야 YAML이 이해됨)

 
(사람/CI)
  kubectl apply tfjob-train-prod.yaml
        ↓
[TFJob Controller]
  ├─ Chief Pod (GPU)
  │    ├─ NAS: 데이터셋 + 결과 공유
  │    └─ 학습 완료 시 DONE 생성
  │
  ├─ Worker Pod들 (GPU)
  │    └─ NAS: 데이터셋 읽기 전용
  │
  └─ Uploader Sidecar (Chief만)
       └─ NAS 결과 → Object Storage(S3)
        ↓
[Object Storage]
  └─ 모델 저장
        ↓
[KServe]
  └─ S3에서 모델 로드 (NAS 접근 ❌)
 
 
 
 
 

👉 이 구조가 “정석”
(공공/금융/멀티테넌시 전부 이 패턴)


1️⃣ 질문 1에 대한 명확한 답부터

❓ Worker도 데이터셋 공유하려면 NAS 연동해야 해?

✅ 정답: YES, 반드시 필요

이유:

  • TF Distributed Training에서
    • Chief만 데이터 읽고 Worker에게 전달 ❌ (병목)
    • Chief + Worker 모두 동일 데이터셋 접근 ⭕

그래서 실무에서는:

  • NAS (RWX)
    • /dataset : 읽기 전용
    • /output : Chief만 쓰기

2️⃣ 스토리지 역할 분리 (이게 핵심 설계)

경로 스토리지 접근 주체
/dataset NAS (RWX) Chief + Worker (RO)
/output NAS (RWX) Chief (RW)
S3 Object Storage Uploader → KServe

📌 Worker는 결과 저장 안 함
📌 KServe는 NAS 접근 완전 차단


3️⃣ tfjob-train-prod.yaml 풀버전 (운영 기준)

아래 YAML은 그대로 파일로 저장해서 apply 가능한 형태야.


📄 tfjob-train-prod.yaml (FULL)

 
apiVersion: kubeflow.org/v1
kind: TFJob
metadata:
  name: tf-mnist-train
  namespace: ml-train

spec:
  runPolicy:
    cleanPodPolicy: None

  tfReplicaSpecs:

    ################################
    # 1️⃣ Chief (학습 + 결과 관리)
    ################################
    Chief:
      replicas: 1
      restartPolicy: OnFailure

      template:
        spec:
          nodeSelector:
            gpu.vendor: nvidia
            gpu.model: A100
            gpu.mem: 80gb
            gpu.mig: enabled
            gpu.mig.profile: 1g.10gb
            gpu.role: chief
            gpu.pool: train

          containers:
          # 🔹 Trainer (실제 학습)
          - name: trainer
            image: myrepo/tf-mnist-train:latest
            command: ["python", "train.py"]

            resources:
              requests:
                nvidia.com/mig-1g.10gb: 1
              limits:
                nvidia.com/mig-1g.10gb: 1

            volumeMounts:
            - name: dataset
              mountPath: /dataset
              readOnly: true
            - name: output
              mountPath: /output

          # 🔹 Uploader (결과 S3 업로드)
          - name: uploader
            image: amazon/aws-cli
            command: ["/bin/sh", "-c"]
            args:
              - |
                echo "Waiting for training completion..."
                while true; do
                  if [ -f /output/DONE ]; then
                    echo "Training done. Uploading to S3"
                    aws s3 sync /output \
                      s3://tf-result-bucket/tf-mnist-train/ \
                      --endpoint-url $AWS_ENDPOINT_URL
                    exit 0
                  fi
                  sleep 30
                done

            envFrom:
            - secretRef:
                name: objstore-cred

            resources:
              requests:
                cpu: "100m"
                memory: "128Mi"
              limits:
                cpu: "500m"
                memory: "512Mi"

            volumeMounts:
            - name: output
              mountPath: /output

          volumes:
          - name: dataset
            persistentVolumeClaim:
              claimName: dataset-nas-pvc
          - name: output
            persistentVolumeClaim:
              claimName: train-output-nas-pvc

    ################################
    # 2️⃣ Worker (연산 전용)
    ################################
    Worker:
      replicas: 4
      restartPolicy: OnFailure

      template:
        spec:
          nodeSelector:
            gpu.vendor: nvidia
            gpu.model: A100
            gpu.mem: 80gb
            gpu.mig: enabled
            gpu.mig.profile: 1g.10gb
            gpu.role: worker
            gpu.pool: train

          containers:
          - name: trainer
            image: myrepo/tf-mnist-train:latest
            command: ["python", "train.py"]

            resources:
              requests:
                nvidia.com/mig-1g.10gb: 1
              limits:
                nvidia.com/mig-1g.10gb: 1

            volumeMounts:
            - name: dataset
              mountPath: /dataset
              readOnly: true

          volumes:
          - name: dataset
            persistentVolumeClaim:
              claimName: dataset-nas-pvc
 
 
 
 
 
 

4️⃣ NAS PVC는 사전에 반드시 있어야 함

📄 dataset-nas-pvc.yaml

 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dataset-nas-pvc
  namespace: ml-train
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ncp-nas-company-a
  resources:
    requests:
      storage: 2Ti
 
 
 
 
 

📄 train-output-nas-pvc.yaml

 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: train-output-nas-pvc
  namespace: ml-train
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ncp-nas-company-a
  resources:
    requests:
      storage: 1Ti
 
 
 
 
 
kubectl apply -f dataset-nas-pvc.yaml
kubectl apply -f train-output-nas-pvc.yaml
 
 
 

5️⃣ 학습 코드에서 반드시 해야 할 것

train.py 마지막

 
model.save("/output/model")

with open("/output/DONE", "w") as f:
    f.write("done")
 
 
 
 
 

📌 이 DONE 파일이 Uploader 트리거

 

 

 


6️⃣ 결과를 KServe에서 사용하는 방법

📄 InferenceService (Serving 전용)

 
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: mnist
  namespace: ml-serving
spec:
  predictor:
    tensorflow:
      storageUri: s3://tf-result-bucket/tf-mnist-train/model
 
 
 
 
 

📌 중요

  • KServe namespace에는
    • NAS PVC ❌
    • NAS StorageClass ❌
  • 오직 S3만 접근

7️⃣ 이 구조의 장점 (운영 관점)

항목 이유
Worker NAS 사용 데이터 중복 제거
결과 S3 이동 Serving 분리
Uploader Sidecar 학습 코드 단순화
NAS ↔ KServe 분리 보안 사고 차단

8️⃣ 지금 이 수준이면 어디까지 왔냐면

✔ GPU MIG 운영
✔ TFJob 분산 학습
✔ NAS 공유 설계
✔ S3 승인 파이프라인
✔ KServe 보안 분리

👉 이건 “개인 실습” 수준이 아니라
회사 표준 아키텍처 문서로 써도 되는 수준
이야.

 

 

 

반응형

댓글