Rewrite README with latest findings and troubleshooting
- Pod 네이밍 패턴 상세 설명 (torch replica vs custom completion) - TF_CONFIG 설정 순서 주의사항 (import 전 필수) - CIFAR-10 로컬 로딩 설명 - InfiniBand + NCCL 통신 구조 - 트러블슈팅 섹션 추가 (incarnation 에러, DNS 감지, 데이터 다운로드) - env 오버라이드 예시 - volcano-trainjob-integration.yaml 파일 구조에서 제거 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9b1cd02a87
commit
1c915cab65
177
README.md
177
README.md
|
|
@ -27,6 +27,9 @@ Kubeflow Trainer v2 (v1alpha1)에는 다음 런타임만 기본 제공된다:
|
||||||
│ ├── RANK, LOCAL_RANK, WORLD_SIZE 설정 │
|
│ ├── RANK, LOCAL_RANK, WORLD_SIZE 설정 │
|
||||||
│ └── rdzv (rendezvous) 자동 구성 │
|
│ └── rdzv (rendezvous) 자동 구성 │
|
||||||
│ │
|
│ │
|
||||||
|
│ numNodes → replica별 별도 Job 생성 │
|
||||||
|
│ Pod hostname: {trainjob}-node-{replica_index}-0 │
|
||||||
|
│ │
|
||||||
│ → 학습 코드에서 분산환경 설정 불필요 │
|
│ → 학습 코드에서 분산환경 설정 불필요 │
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
@ -35,10 +38,14 @@ Kubeflow Trainer v2 (v1alpha1)에는 다음 런타임만 기본 제공된다:
|
||||||
│ │
|
│ │
|
||||||
│ mlPolicy에 framework 없음 → 컨트롤러가 주입하는 것 없음 │
|
│ mlPolicy에 framework 없음 → 컨트롤러가 주입하는 것 없음 │
|
||||||
│ │
|
│ │
|
||||||
|
│ numNodes → 단일 Job에 completions=numNodes 로 매핑 │
|
||||||
|
│ Pod hostname: {trainjob}-node-0-{completion_index} │
|
||||||
|
│ completion_index = worker_index │
|
||||||
|
│ │
|
||||||
│ 학습 스크립트에서 자체 구성: │
|
│ 학습 스크립트에서 자체 구성: │
|
||||||
│ ├── Pod hostname 파싱 → TrainJob 이름, worker index 추출 │
|
│ ├── Pod hostname 파싱 → TrainJob 이름, worker index 추출 │
|
||||||
│ ├── Headless Service DNS로 worker 주소 목록 생성 │
|
│ ├── DNS 프로빙으로 워커 자동 감지 │
|
||||||
│ ├── TF_CONFIG 환경변수 자동 구성 │
|
│ ├── TF_CONFIG 환경변수 자동 구성 (import tensorflow 전에!) │
|
||||||
│ └── tf.distribute.MultiWorkerMirroredStrategy 사용 │
|
│ └── tf.distribute.MultiWorkerMirroredStrategy 사용 │
|
||||||
│ │
|
│ │
|
||||||
│ 필수 설정: │
|
│ 필수 설정: │
|
||||||
|
|
@ -47,46 +54,60 @@ Kubeflow Trainer v2 (v1alpha1)에는 다음 런타임만 기본 제공된다:
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Pod 네이밍 패턴 (중요)
|
||||||
|
|
||||||
|
mlPolicy에 framework(torch/mpi) 설정 여부에 따라 Pod 네이밍이 달라진다:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─ mlPolicy.torch 사용 (PyTorch) ──────────────────────────────────┐
|
||||||
|
│ numNodes: 2 → replicatedJob replicas=2 → Job 2개 생성 │
|
||||||
|
│ │
|
||||||
|
│ Job: {trainjob}-node-0 → Pod: {trainjob}-node-0-0 │
|
||||||
|
│ Job: {trainjob}-node-1 → Pod: {trainjob}-node-1-0 │
|
||||||
|
│ ↑ replica_index ↑ completion=0 │
|
||||||
|
└────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
┌─ mlPolicy에 framework 없음 (TensorFlow custom) ─────────────────┐
|
||||||
|
│ numNodes: 2 → 단일 Job에 completions=2 로 매핑 │
|
||||||
|
│ │
|
||||||
|
│ Job: {trainjob}-node-0 → Pod: {trainjob}-node-0-0 │
|
||||||
|
│ Pod: {trainjob}-node-0-1 │
|
||||||
|
│ ↑ replica=0 ↑ completion_index │
|
||||||
|
│ │
|
||||||
|
│ completion_index = worker_index │
|
||||||
|
└────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
## TF_CONFIG 자동 구성 원리
|
## TF_CONFIG 자동 구성 원리
|
||||||
|
|
||||||
Trainer v2에서 TrainJob을 생성하면 내부적으로 JobSet이 만들어지고, 각 Pod은 예측 가능한 hostname을 갖는다:
|
### 1. Pod hostname에서 정보 추출
|
||||||
|
|
||||||
```
|
```
|
||||||
mlPolicy에 framework(torch/mpi) 없이 numNodes만 설정하면:
|
hostname: tf-distributed-training-node-0-1
|
||||||
→ 단일 Job에 completions=numNodes 로 매핑
|
│ │
|
||||||
→ Pod hostname 패턴: {trainjob}-node-0-{completion_index}
|
│ └─ completion_index (= worker_index = 1)
|
||||||
→ completion_index = worker_index
|
└─── replica_index (항상 0)
|
||||||
|
|
||||||
참고) mlPolicy.torch 사용 시:
|
|
||||||
→ replica별 별도 Job 생성
|
|
||||||
→ Pod hostname 패턴: {trainjob}-node-{replica_index}-0
|
|
||||||
|
|
||||||
예시 (TrainJob: tf-distributed-training, numNodes: 2):
|
|
||||||
Worker 0: tf-distributed-training-node-0-0 (completion_index=0)
|
|
||||||
Worker 1: tf-distributed-training-node-0-1 (completion_index=1)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Headless Service DNS와 결합하면:
|
### 2. Headless Service DNS로 워커 주소 구성
|
||||||
|
|
||||||
```
|
```
|
||||||
Worker 0: tf-distributed-training-node-0-0.tf-distributed-training.{namespace}.svc.cluster.local:12345
|
Worker 0: tf-distributed-training-node-0-0.tf-distributed-training.{namespace}.svc.cluster.local:12345
|
||||||
Worker 1: tf-distributed-training-node-0-1.tf-distributed-training.{namespace}.svc.cluster.local:12345
|
Worker 1: tf-distributed-training-node-0-1.tf-distributed-training.{namespace}.svc.cluster.local:12345
|
||||||
```
|
```
|
||||||
|
|
||||||
학습 스크립트가 DNS 프로빙으로 워커를 자동 감지하고 TF_CONFIG를 생성한다:
|
### 3. DNS 프로빙으로 워커 수 자동 감지
|
||||||
|
|
||||||
```
|
```
|
||||||
discover_workers() 동작:
|
discover_workers() 동작:
|
||||||
node-0-0 DNS 조회 → 성공 → workers에 추가
|
node-0-0 DNS 조회 → 성공 → workers에 추가
|
||||||
node-0-1 DNS 조회 → 성공 → workers에 추가
|
node-0-1 DNS 조회 → 성공 → workers에 추가
|
||||||
node-0-2 DNS 조회 → 실패 → 탐색 종료 → num_workers = 2
|
node-0-2 DNS 조회 → 실패 → 탐색 종료 → num_workers = 2
|
||||||
|
|
||||||
|
안정성: 2개 이상 발견 + 연속 2회 동일 결과 → 확정
|
||||||
```
|
```
|
||||||
|
|
||||||
**중요: TF_CONFIG는 반드시 `import tensorflow` 전에 설정해야 한다.**
|
### 4. TF_CONFIG 생성
|
||||||
TF는 import 시점에 GPU를 감지하고 TF_CONFIG가 있으면 gRPC 서버를 시작한다.
|
|
||||||
import 후에 TF_CONFIG를 설정하면 "different incarnation" 에러가 발생한다.
|
|
||||||
|
|
||||||
생성되는 TF_CONFIG:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -100,14 +121,64 @@ import 후에 TF_CONFIG를 설정하면 "different incarnation" 에러가 발생
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 5. TF_CONFIG 설정 순서 (중요)
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 반드시 이 순서를 지켜야 한다:
|
||||||
|
setup_tf_config() # 1. TF_CONFIG 환경변수 설정
|
||||||
|
import tensorflow as tf # 2. 그 다음 tensorflow import
|
||||||
|
|
||||||
|
# 이유:
|
||||||
|
# TF는 import 시점에 GPU를 감지하고, TF_CONFIG가 있으면 gRPC 서버를 시작한다.
|
||||||
|
# import 후에 TF_CONFIG를 설정하면 gRPC 서버가 잘못된 상태로 시작되어
|
||||||
|
# "different incarnation" 에러가 발생한다.
|
||||||
|
```
|
||||||
|
|
||||||
|
## CIFAR-10 데이터 로딩
|
||||||
|
|
||||||
|
`tf.keras.datasets.cifar10.load_data()`는 외부 URL에서 다운로드를 시도한다.
|
||||||
|
에어갭 환경이나 빠른 로딩을 위해 로컬 hostPath를 마운트하여 pickle 파일에서 직접 로드한다.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Runtime에서 hostPath 볼륨 마운트
|
||||||
|
volumeMounts:
|
||||||
|
- name: cifar-data
|
||||||
|
mountPath: /workspace/data/cifar-10-batches-py
|
||||||
|
volumes:
|
||||||
|
- name: cifar-data
|
||||||
|
hostPath:
|
||||||
|
path: /home/ubuntu/cifar-10-batches-py # 노드에 미리 준비
|
||||||
|
type: Directory
|
||||||
|
```
|
||||||
|
|
||||||
|
## 통신 구조 (InfiniBand + NCCL)
|
||||||
|
|
||||||
|
TensorFlow도 PyTorch와 동일하게 NCCL + InfiniBand를 사용할 수 있다.
|
||||||
|
|
||||||
|
```
|
||||||
|
PyTorch: torchrun → torch.distributed → NCCL → InfiniBand
|
||||||
|
TensorFlow: gRPC (조정) → MultiWorkerMirroredStrategy → NCCL → InfiniBand
|
||||||
|
```
|
||||||
|
|
||||||
|
NCCL은 프레임워크 독립적인 라이브러리이므로, 동일한 환경변수가 양쪽 모두에 적용된다:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
- name: NCCL_IB_DISABLE
|
||||||
|
value: "0" # InfiniBand 활성화
|
||||||
|
- name: NCCL_SOCKET_IFNAME
|
||||||
|
value: "net" # OOB 통신용 인터페이스
|
||||||
|
- name: NCCL_DEBUG
|
||||||
|
value: "INFO" # NCCL 디버그 로그
|
||||||
|
```
|
||||||
|
|
||||||
## 파일 구조
|
## 파일 구조
|
||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
├── README.md # 이 문서
|
├── README.md # 이 문서
|
||||||
├── tensorflow-custom-runtime.yaml # TF Custom Runtime (기본, Volcano 미포함)
|
├── tensorflow-custom-runtime.yaml # TF Custom Runtime (기본, Volcano 미포함)
|
||||||
├── tensorflow-volcano-trainjob-integration.yaml # TF + Volcano 전체 통합 (ConfigMap + Queue + Runtime + TrainJob)
|
└── tensorflow-volcano-trainjob-integration.yaml # TF + Volcano 전체 통합 (ConfigMap + Queue + Runtime + TrainJob)
|
||||||
└── volcano-trainjob-integration.yaml # (참고) PyTorch + Volcano 통합 원본
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 적용 방법
|
## 적용 방법
|
||||||
|
|
@ -143,14 +214,14 @@ kubectl get clustertrainingruntime tensorflow-distributed-volcano
|
||||||
# TrainJob 상태 확인
|
# TrainJob 상태 확인
|
||||||
kubectl get trainjob tf-distributed-training
|
kubectl get trainjob tf-distributed-training
|
||||||
|
|
||||||
# Pod 상태 확인
|
# Pod 상태 확인 (namespace 지정 필요)
|
||||||
kubectl get pods -l batch.kubernetes.io/job-name
|
kubectl get pods -l jobset.sigs.k8s.io/jobset-name=tf-distributed-training -n {namespace}
|
||||||
|
|
||||||
# Volcano PodGroup 확인
|
# Volcano PodGroup 확인
|
||||||
kubectl get podgroup
|
kubectl get podgroup
|
||||||
|
|
||||||
# 로그 확인 (worker 0)
|
# 로그 확인 (worker 0)
|
||||||
kubectl logs -l batch.kubernetes.io/job-name=tf-distributed-training-node-0 -f
|
kubectl -n {namespace} logs -l job-name=tf-distributed-training-node-0 -f
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3단계: TrainJob만 변경하여 재실행
|
### 3단계: TrainJob만 변경하여 재실행
|
||||||
|
|
@ -159,7 +230,7 @@ Runtime은 유지하고 TrainJob만 변경할 경우:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 기존 TrainJob 삭제
|
# 기존 TrainJob 삭제
|
||||||
kubectl delete trainjob tf-distributed-training
|
kubectl delete trainjob tf-distributed-training -n {namespace}
|
||||||
|
|
||||||
# 수정 후 재적용
|
# 수정 후 재적용
|
||||||
kubectl apply -f tensorflow-volcano-trainjob-integration.yaml
|
kubectl apply -f tensorflow-volcano-trainjob-integration.yaml
|
||||||
|
|
@ -218,3 +289,53 @@ spec:
|
||||||
```
|
```
|
||||||
|
|
||||||
PyTorch runtime에서는 torchrun의 rdzv(rendezvous) 메커니즘이 이를 처리하므로 불필요하다.
|
PyTorch runtime에서는 torchrun의 rdzv(rendezvous) 메커니즘이 이를 처리하므로 불필요하다.
|
||||||
|
|
||||||
|
### TrainJob env 오버라이드
|
||||||
|
|
||||||
|
Runtime에 정의된 환경변수는 TrainJob의 `trainer.env`에서 오버라이드할 수 있다:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: trainer.kubeflow.org/v1alpha1
|
||||||
|
kind: TrainJob
|
||||||
|
spec:
|
||||||
|
runtimeRef:
|
||||||
|
name: tensorflow-distributed-volcano
|
||||||
|
trainer:
|
||||||
|
env:
|
||||||
|
- name: TF_WORKER_PORT # Runtime 기본값 "12345" → 오버라이드
|
||||||
|
value: "23456"
|
||||||
|
- name: EPOCHS # Runtime 기본값 "10" → 오버라이드
|
||||||
|
value: "50"
|
||||||
|
- name: NCCL_DEBUG # NCCL 디버그 레벨 변경
|
||||||
|
value: "WARN"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 트러블슈팅
|
||||||
|
|
||||||
|
### "different incarnation" 에러
|
||||||
|
|
||||||
|
```
|
||||||
|
E tensorflow/...coordination_service.cc: /job:worker/replica:0/task:0
|
||||||
|
unexpectedly tried to connect with a different incarnation. It has likely restarted.
|
||||||
|
```
|
||||||
|
|
||||||
|
**원인**: `TF_CONFIG`가 `import tensorflow` 이후에 설정됨
|
||||||
|
**해결**: 학습 스크립트에서 `setup_tf_config()`를 `import tensorflow` 전에 호출
|
||||||
|
|
||||||
|
### DNS 프로빙에서 워커를 1개만 발견
|
||||||
|
|
||||||
|
```
|
||||||
|
[Discovery] attempt 1/60: found 1 workers, retrying in 5s...
|
||||||
|
```
|
||||||
|
|
||||||
|
**원인**: 다른 워커 Pod이 아직 시작되지 않았거나 DNS 전파 지연
|
||||||
|
**해결**: Volcano gang scheduling 사용 시 모든 Pod이 동시에 스케줄링되지만, DNS 전파에 시간이 걸릴 수 있다. `discover_workers()`가 연속 2회 동일 결과를 확인한 후 진행하므로 정상적으로 기다린다.
|
||||||
|
|
||||||
|
### CIFAR-10 데이터 다운로드 시도
|
||||||
|
|
||||||
|
```
|
||||||
|
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
**원인**: `tf.keras.datasets.cifar10.load_data()` 사용 또는 hostPath 볼륨 미마운트
|
||||||
|
**해결**: `load_cifar10_local()` 함수 사용 + cifar-data hostPath 볼륨 마운트 확인
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue