반응형
좋아, 이제 “실제 운영에 쓰는 풀버전 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 접근 ❌)
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
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
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
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
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")
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
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 보안 분리
👉 이건 “개인 실습” 수준이 아니라
회사 표준 아키텍처 문서로 써도 되는 수준이야.
반응형
'[GPUaaS] > TensorFlow' 카테고리의 다른 글
| [TensorFlow vs PyTorch] GPU 메모리 관리 방식 !! (0) | 2026.02.26 |
|---|---|
| [AI 개발] 딥러닝 프레임워크 6종 !! (0) | 2026.02.26 |
| [중요2] 운영 표준 - [최종] Train.py & TFJob.yaml (라벨/MIG/RWO 적용) (0) | 2026.01.30 |
| [중요2] 운영 표준 - TFJob.yaml (라벨/MIG/RWO/S3 적용) (0) | 2026.01.28 |
| [중요2] 운영 표준 - ☸️ Kubernetes + TensorFlow 구동 원리 (0) | 2026.01.28 |
| [중요2] 운영 표준 - GPU 노드 라벨 세트 (0) | 2026.01.28 |
| [GPU 타입] 운영 무중단 - 라벨 NodePool 등록 (1) | 2026.01.27 |
| [GPU 타입] 신규 라벨 NodePool 등록 (라벨 + Taint + Affinity 세트) (0) | 2026.01.26 |
댓글