Revert "Revert "Remove NUM_WORKERS env var, auto-detect workers via DNS probing""
This reverts commit 5f6dec6799.
This commit is contained in:
parent
5f6dec6799
commit
9e92f2e3d4
44
README.md
44
README.md
|
|
@ -43,7 +43,7 @@ Kubeflow Trainer v2 (v1alpha1)에는 다음 런타임만 기본 제공된다:
|
||||||
│ │
|
│ │
|
||||||
│ 필수 설정: │
|
│ 필수 설정: │
|
||||||
│ ├── network.publishNotReadyAddresses: true (DNS 조기 해석) │
|
│ ├── network.publishNotReadyAddresses: true (DNS 조기 해석) │
|
||||||
│ └── NUM_WORKERS 환경변수 = mlPolicy.numNodes │
|
│ └── 워커 수는 DNS 프로빙으로 자동 감지 (수동 설정 불필요) │
|
||||||
└─────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -66,7 +66,16 @@ Worker 0: tf-distributed-training-node-0-0.tf-distributed-training.default.svc.c
|
||||||
Worker 1: tf-distributed-training-node-1-0.tf-distributed-training.default.svc.cluster.local:12345
|
Worker 1: tf-distributed-training-node-1-0.tf-distributed-training.default.svc.cluster.local:12345
|
||||||
```
|
```
|
||||||
|
|
||||||
학습 스크립트가 이를 파싱하여 자동으로 TF_CONFIG를 생성한다:
|
학습 스크립트가 DNS 프로빙으로 워커를 자동 감지하고 TF_CONFIG를 생성한다:
|
||||||
|
|
||||||
|
```
|
||||||
|
discover_workers() 동작:
|
||||||
|
node-0 DNS 조회 → 성공 → workers에 추가
|
||||||
|
node-1 DNS 조회 → 성공 → workers에 추가
|
||||||
|
node-2 DNS 조회 → 실패 → 탐색 종료 → num_workers = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
생성되는 TF_CONFIG:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|
@ -147,24 +156,10 @@ kubectl apply -f tensorflow-volcano-trainjob-integration.yaml
|
||||||
|
|
||||||
## 주의사항
|
## 주의사항
|
||||||
|
|
||||||
### NUM_WORKERS와 numNodes 동기화
|
### 워커 수 자동 감지 (DNS 프로빙)
|
||||||
|
|
||||||
`NUM_WORKERS` 환경변수는 `mlPolicy.numNodes` (또는 TrainJob의 `trainer.numNodes`)와 **반드시 일치**해야 한다.
|
학습 스크립트의 `discover_workers()` 함수가 DNS 프로빙으로 워커 수를 자동 감지한다.
|
||||||
|
`NUM_WORKERS` 같은 환경변수를 수동으로 관리할 필요가 없으므로, TrainJob에서 `numNodes`만 변경하면 된다:
|
||||||
PyTorch runtime은 컨트롤러가 `WORLD_SIZE`를 자동 주입하지만, 커스텀 TF runtime은 이를 수동으로 관리해야 한다.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# Runtime에서 기본값 설정
|
|
||||||
spec:
|
|
||||||
mlPolicy:
|
|
||||||
numNodes: 2 # ← 이 값과
|
|
||||||
...
|
|
||||||
env:
|
|
||||||
- name: NUM_WORKERS
|
|
||||||
value: "2" # ← 이 값이 일치해야 함
|
|
||||||
```
|
|
||||||
|
|
||||||
TrainJob에서 numNodes를 오버라이드할 경우, env도 함께 오버라이드해야 한다:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: trainer.kubeflow.org/v1alpha1
|
apiVersion: trainer.kubeflow.org/v1alpha1
|
||||||
|
|
@ -173,12 +168,15 @@ spec:
|
||||||
runtimeRef:
|
runtimeRef:
|
||||||
name: tensorflow-distributed-volcano
|
name: tensorflow-distributed-volcano
|
||||||
trainer:
|
trainer:
|
||||||
numNodes: 4 # 4노드로 변경
|
numNodes: 4 # 이것만 변경하면 됨. 스크립트가 자동으로 4개 워커 감지.
|
||||||
env:
|
|
||||||
- name: NUM_WORKERS
|
|
||||||
value: "4" # 반드시 함께 변경
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
비교:
|
||||||
|
| | PyTorch Runtime | TensorFlow Custom Runtime |
|
||||||
|
|---|---|---|
|
||||||
|
| 워커 수 감지 | 컨트롤러가 `WORLD_SIZE` 자동 주입 | 스크립트가 DNS 프로빙으로 자동 감지 |
|
||||||
|
| numNodes 변경 시 | 추가 작업 없음 | 추가 작업 없음 |
|
||||||
|
|
||||||
### InfiniBand 설정
|
### InfiniBand 설정
|
||||||
|
|
||||||
클러스터에 InfiniBand가 없는 경우 다음 항목을 제거해야 한다:
|
클러스터에 InfiniBand가 없는 경우 다음 항목을 제거해야 한다:
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,6 @@ spec:
|
||||||
- name: node
|
- name: node
|
||||||
image: tensorflow/tensorflow:2.16.1-gpu
|
image: tensorflow/tensorflow:2.16.1-gpu
|
||||||
env:
|
env:
|
||||||
- name: NUM_WORKERS
|
|
||||||
value: "1"
|
|
||||||
- name: TF_WORKER_PORT
|
- name: TF_WORKER_PORT
|
||||||
value: "12345"
|
value: "12345"
|
||||||
- name: NAMESPACE
|
- name: NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,42 @@ data:
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def discover_workers(job_name, namespace, port, max_retries=60, retry_interval=5):
|
||||||
|
"""
|
||||||
|
DNS 프로빙으로 워커 수를 자동 감지.
|
||||||
|
NUM_WORKERS 환경변수 없이도 numNodes 변경에 자동 대응.
|
||||||
|
|
||||||
|
동작 방식:
|
||||||
|
node-0, node-1, node-2, ... 순서로 DNS를 조회하여
|
||||||
|
해석되지 않는 인덱스가 나오면 그 직전까지를 워커 목록으로 사용.
|
||||||
|
모든 워커가 아직 생성 전일 수 있으므로 retry 포함.
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
workers = []
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
workers = []
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
worker_host = f"{job_name}-node-{i}-0.{job_name}.{namespace}.svc.cluster.local"
|
||||||
|
try:
|
||||||
|
socket.getaddrinfo(worker_host, int(port))
|
||||||
|
workers.append(f"{worker_host}:{port}")
|
||||||
|
i += 1
|
||||||
|
except socket.gaierror:
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(workers) >= 2 or (len(workers) == 1 and attempt >= 5):
|
||||||
|
# 최소 2개 워커 발견 또는 단일 노드 학습으로 판단
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"[Discovery] attempt {attempt+1}/{max_retries}: "
|
||||||
|
f"found {len(workers)} workers, retrying in {retry_interval}s...")
|
||||||
|
time.sleep(retry_interval)
|
||||||
|
|
||||||
|
return workers
|
||||||
|
|
||||||
|
|
||||||
def setup_tf_config():
|
def setup_tf_config():
|
||||||
"""
|
"""
|
||||||
Kubeflow Trainer v2의 Pod 네이밍 규칙을 이용하여 TF_CONFIG를 자동 구성.
|
Kubeflow Trainer v2의 Pod 네이밍 규칙을 이용하여 TF_CONFIG를 자동 구성.
|
||||||
|
|
@ -32,13 +68,10 @@ data:
|
||||||
Pod hostname 패턴: {trainjob-name}-node-{replica_index}-{completion_index}
|
Pod hostname 패턴: {trainjob-name}-node-{replica_index}-{completion_index}
|
||||||
Headless Service DNS: {hostname}.{trainjob-name}.{namespace}.svc.cluster.local
|
Headless Service DNS: {hostname}.{trainjob-name}.{namespace}.svc.cluster.local
|
||||||
|
|
||||||
환경변수:
|
워커 수는 DNS 프로빙으로 자동 감지하므로 NUM_WORKERS 환경변수 불필요.
|
||||||
- NUM_WORKERS: 총 워커 수 (runtime의 numNodes와 일치해야 함)
|
numNodes가 변경되어도 학습 스크립트 수정 없이 자동 대응.
|
||||||
- TF_WORKER_PORT: gRPC 통신 포트 (기본값: 12345)
|
|
||||||
- NAMESPACE: Pod이 실행되는 namespace (Downward API)
|
|
||||||
"""
|
"""
|
||||||
hostname = socket.gethostname()
|
hostname = socket.gethostname()
|
||||||
num_workers = int(os.environ.get('NUM_WORKERS', '1'))
|
|
||||||
port = os.environ.get('TF_WORKER_PORT', '12345')
|
port = os.environ.get('TF_WORKER_PORT', '12345')
|
||||||
namespace = os.environ.get('NAMESPACE', 'default')
|
namespace = os.environ.get('NAMESPACE', 'default')
|
||||||
|
|
||||||
|
|
@ -53,14 +86,10 @@ data:
|
||||||
job_name = os.environ.get('TRAINJOB_NAME', 'unknown')
|
job_name = os.environ.get('TRAINJOB_NAME', 'unknown')
|
||||||
worker_index = 0
|
worker_index = 0
|
||||||
|
|
||||||
# Headless Service DNS를 이용한 worker 주소 목록 생성
|
# DNS 프로빙으로 워커 목록 자동 감지
|
||||||
workers = []
|
workers = discover_workers(job_name, namespace, port)
|
||||||
for i in range(num_workers):
|
num_workers = len(workers)
|
||||||
worker_host = (
|
print(f"[Discovery] Discovered {num_workers} workers via DNS")
|
||||||
f"{job_name}-node-{i}-0.{job_name}.{namespace}"
|
|
||||||
f".svc.cluster.local:{port}"
|
|
||||||
)
|
|
||||||
workers.append(worker_host)
|
|
||||||
|
|
||||||
tf_config = {
|
tf_config = {
|
||||||
"cluster": {"worker": workers},
|
"cluster": {"worker": workers},
|
||||||
|
|
@ -200,7 +229,7 @@ spec:
|
||||||
# - mlPolicy에 torch/mpi 필드 없음 → 컨트롤러가 torchrun/MPI 주입하지 않음
|
# - mlPolicy에 torch/mpi 필드 없음 → 컨트롤러가 torchrun/MPI 주입하지 않음
|
||||||
# - network.publishNotReadyAddresses: true → Pod간 DNS 조기 해석 가능
|
# - network.publishNotReadyAddresses: true → Pod간 DNS 조기 해석 가능
|
||||||
# - TF_CONFIG는 학습 스크립트에서 hostname + Headless Service DNS로 자동 구성
|
# - TF_CONFIG는 학습 스크립트에서 hostname + Headless Service DNS로 자동 구성
|
||||||
# - NUM_WORKERS 환경변수가 numNodes와 반드시 일치해야 함
|
# - 워커 수는 DNS 프로빙으로 자동 감지 (NUM_WORKERS 환경변수 불필요)
|
||||||
apiVersion: trainer.kubeflow.org/v1alpha1
|
apiVersion: trainer.kubeflow.org/v1alpha1
|
||||||
kind: ClusterTrainingRuntime
|
kind: ClusterTrainingRuntime
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -258,9 +287,7 @@ spec:
|
||||||
- name: NCCL_SOCKET_IFNAME
|
- name: NCCL_SOCKET_IFNAME
|
||||||
value: "net"
|
value: "net"
|
||||||
# --- TF_CONFIG 자동 구성용 환경변수 ---
|
# --- TF_CONFIG 자동 구성용 환경변수 ---
|
||||||
# NUM_WORKERS는 mlPolicy.numNodes와 반드시 일치해야 함
|
# 워커 수는 DNS 프로빙으로 자동 감지 (NUM_WORKERS 불필요)
|
||||||
- name: NUM_WORKERS
|
|
||||||
value: "2"
|
|
||||||
- name: TF_WORKER_PORT
|
- name: TF_WORKER_PORT
|
||||||
value: "12345"
|
value: "12345"
|
||||||
- name: NAMESPACE
|
- name: NAMESPACE
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue