diff --git a/README.md b/README.md index c973f4a..a1dd28f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ Kubeflow Trainer v2 (v1alpha1)에는 다음 런타임만 기본 제공된다: │ ├── RANK, LOCAL_RANK, WORLD_SIZE 설정 │ │ └── rdzv (rendezvous) 자동 구성 │ │ │ +│ numNodes → replica별 별도 Job 생성 │ +│ Pod hostname: {trainjob}-node-{replica_index}-0 │ +│ │ │ → 학습 코드에서 분산환경 설정 불필요 │ └─────────────────────────────────────────────────────────────────────┘ @@ -35,10 +38,14 @@ Kubeflow Trainer v2 (v1alpha1)에는 다음 런타임만 기본 제공된다: │ │ │ mlPolicy에 framework 없음 → 컨트롤러가 주입하는 것 없음 │ │ │ +│ numNodes → 단일 Job에 completions=numNodes 로 매핑 │ +│ Pod hostname: {trainjob}-node-0-{completion_index} │ +│ completion_index = worker_index │ +│ │ │ 학습 스크립트에서 자체 구성: │ │ ├── Pod hostname 파싱 → TrainJob 이름, worker index 추출 │ -│ ├── Headless Service DNS로 worker 주소 목록 생성 │ -│ ├── TF_CONFIG 환경변수 자동 구성 │ +│ ├── DNS 프로빙으로 워커 자동 감지 │ +│ ├── TF_CONFIG 환경변수 자동 구성 (import tensorflow 전에!) │ │ └── 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 자동 구성 원리 -Trainer v2에서 TrainJob을 생성하면 내부적으로 JobSet이 만들어지고, 각 Pod은 예측 가능한 hostname을 갖는다: +### 1. Pod hostname에서 정보 추출 ``` -mlPolicy에 framework(torch/mpi) 없이 numNodes만 설정하면: - → 단일 Job에 completions=numNodes 로 매핑 - → Pod hostname 패턴: {trainjob}-node-0-{completion_index} - → completion_index = worker_index - -참고) 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) +hostname: tf-distributed-training-node-0-1 + │ │ + │ └─ completion_index (= worker_index = 1) + └─── replica_index (항상 0) ``` -Headless Service DNS와 결합하면: +### 2. Headless Service DNS로 워커 주소 구성 ``` 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 ``` -학습 스크립트가 DNS 프로빙으로 워커를 자동 감지하고 TF_CONFIG를 생성한다: +### 3. DNS 프로빙으로 워커 수 자동 감지 ``` discover_workers() 동작: node-0-0 DNS 조회 → 성공 → workers에 추가 node-0-1 DNS 조회 → 성공 → workers에 추가 node-0-2 DNS 조회 → 실패 → 탐색 종료 → num_workers = 2 + +안정성: 2개 이상 발견 + 연속 2회 동일 결과 → 확정 ``` -**중요: TF_CONFIG는 반드시 `import tensorflow` 전에 설정해야 한다.** -TF는 import 시점에 GPU를 감지하고 TF_CONFIG가 있으면 gRPC 서버를 시작한다. -import 후에 TF_CONFIG를 설정하면 "different incarnation" 에러가 발생한다. - -생성되는 TF_CONFIG: +### 4. TF_CONFIG 생성 ```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 # 이 문서 ├── tensorflow-custom-runtime.yaml # TF Custom Runtime (기본, Volcano 미포함) -├── tensorflow-volcano-trainjob-integration.yaml # TF + Volcano 전체 통합 (ConfigMap + Queue + Runtime + TrainJob) -└── volcano-trainjob-integration.yaml # (참고) PyTorch + Volcano 통합 원본 +└── tensorflow-volcano-trainjob-integration.yaml # TF + Volcano 전체 통합 (ConfigMap + Queue + Runtime + TrainJob) ``` ## 적용 방법 @@ -143,14 +214,14 @@ kubectl get clustertrainingruntime tensorflow-distributed-volcano # TrainJob 상태 확인 kubectl get trainjob tf-distributed-training -# Pod 상태 확인 -kubectl get pods -l batch.kubernetes.io/job-name +# Pod 상태 확인 (namespace 지정 필요) +kubectl get pods -l jobset.sigs.k8s.io/jobset-name=tf-distributed-training -n {namespace} # Volcano PodGroup 확인 kubectl get podgroup # 로그 확인 (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만 변경하여 재실행 @@ -159,7 +230,7 @@ Runtime은 유지하고 TrainJob만 변경할 경우: ```bash # 기존 TrainJob 삭제 -kubectl delete trainjob tf-distributed-training +kubectl delete trainjob tf-distributed-training -n {namespace} # 수정 후 재적용 kubectl apply -f tensorflow-volcano-trainjob-integration.yaml @@ -218,3 +289,53 @@ spec: ``` 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 볼륨 마운트 확인