change user to root for airflow images

This commit is contained in:
2026-04-15 11:34:56 +03:00
parent 276c1ba6c5
commit 6c0105d85e

View File

@@ -3,7 +3,6 @@ from airflow.providers.standard.operators.python import PythonOperator
import pendulum import pendulum
from datetime import timedelta from datetime import timedelta
WORLD_SIZE = 2
MASTER_PORT = "29500" MASTER_PORT = "29500"
default_args = { default_args = {
@@ -13,153 +12,101 @@ default_args = {
} }
# ── rank 0: стартует, пушит свой hostname в XCom ───────────────────────────── # -------------------------
# RANK 0
# -------------------------
def run_rank_0(**context): def run_rank_0(**context):
import os, socket, torch import os, socket, torch, torch.distributed as dist
import torch.distributed as dist
import torch.nn as nn
import torch.optim as optim
hostname = socket.gethostname() hostname = socket.gethostname()
fqdn = f"{hostname}.airflow-worker-gpu" master_addr = f"{hostname}.airflow-worker-gpu"
print(f"[rank0] hostname={fqdn}")
# Пушим MASTER_ADDR чтобы rank1 знал куда коннектиться # share master with rank1
context["ti"].xcom_push(key="master_addr", value=fqdn) context["ti"].xcom_push(key="master_addr", value=master_addr)
os.environ.update({ os.environ.update({
"MASTER_ADDR": fqdn, "MASTER_ADDR": master_addr,
"MASTER_PORT": MASTER_PORT, "MASTER_PORT": MASTER_PORT,
"WORLD_SIZE": "2", "WORLD_SIZE": "2",
"NCCL_IB_GID_INDEX": "0",
"RANK": "0", "RANK": "0",
"NCCL_SOCKET_IFNAME": "eth0", # bootstrap/rendezvous через eth
"NCCL_IB_DISABLE": "0", # трафик через IB # NCCL (minimal stable IB config)
"NCCL_NET": "IB",
"NCCL_IB_USE_INLINE": "1", # без ibv_reg_mr pinned memory
"NCCL_BUFFSIZE": "1048576",
"NCCL_P2P_DISABLE": "1", # разные ноды — p2p не нужен
"NCCL_SHM_DISABLE": "1", # shm только для локальных рангов
"NCCL_DEBUG": "INFO", "NCCL_DEBUG": "INFO",
"NCCL_DEBUG_SUBSYS": "NET,INIT", "NCCL_SOCKET_IFNAME": "eth0",
"TORCH_NCCL_BLOCKING_WAIT": "1",
# IB stability (IMPORTANT)
"NCCL_IB_GID_INDEX": "0",
"NCCL_IB_USE_INLINE": "0",
"NCCL_IB_TIMEOUT": "22",
"NCCL_IB_RETRY_CNT": "7",
}) })
dist.init_process_group( dist.init_process_group("nccl", init_method="env://")
backend="nccl",
init_method="env://",
rank=0,
world_size=2,
timeout=timedelta(minutes=5),
)
print("[rank0] process group OK")
torch.cuda.set_device(0) torch.cuda.set_device(0)
device = torch.device("cuda:0")
print(f"[rank0] GPU={torch.cuda.get_device_name(0)}")
_train(rank=0, device=device) x = torch.ones(1, device="cuda")
dist.all_reduce(x)
print(f"[rank0] all_reduce result = {x.item()}")
dist.destroy_process_group() dist.destroy_process_group()
print("[rank0] DONE")
# ── rank 1: ждёт XCom от rank0, потом коннектится ──────────────────────────── # -------------------------
# RANK 1
# -------------------------
def run_rank_1(**context): def run_rank_1(**context):
import os, socket, time, torch import os, time, torch, torch.distributed as dist
import torch.distributed as dist
import torch.nn as nn
import torch.optim as optim
# Polling XCom пока rank0 не запишет master_addr # wait for master
master_addr = None master_addr = None
for attempt in range(30): for _ in range(30):
master_addr = context["ti"].xcom_pull( master_addr = context["ti"].xcom_pull(
task_ids="train_rank_0", task_ids="train_rank_0",
key="master_addr", key="master_addr",
) )
if master_addr: if master_addr:
break break
print(f"[rank1] waiting for master_addr (attempt {attempt+1}/30)...") time.sleep(2)
time.sleep(5)
if not master_addr: if not master_addr:
raise RuntimeError("rank1: timeout waiting for master_addr from rank0") raise RuntimeError("master_addr not found")
print(f"[rank1] master_addr={master_addr}")
os.environ.update({ os.environ.update({
"MASTER_ADDR": master_addr, "MASTER_ADDR": master_addr,
"MASTER_PORT": MASTER_PORT, "MASTER_PORT": MASTER_PORT,
"WORLD_SIZE": "2", "WORLD_SIZE": "2",
"RANK": "1", "RANK": "1",
"NCCL_SOCKET_IFNAME": "eth0",
"NCCL_IB_DISABLE": "0", # NCCL (same as rank0)
"NCCL_NET": "IB",
"NCCL_IB_USE_INLINE": "1", # без ibv_reg_mr pinned memory
"NCCL_BUFFSIZE": "1048576",
"NCCL_P2P_DISABLE": "1",
"NCCL_SHM_DISABLE": "1",
"NCCL_DEBUG": "INFO", "NCCL_DEBUG": "INFO",
"NCCL_DEBUG_SUBSYS": "NET,INIT", "NCCL_SOCKET_IFNAME": "eth0",
"TORCH_NCCL_BLOCKING_WAIT": "1",
# IB stability
"NCCL_IB_GID_INDEX": "0",
"NCCL_IB_USE_INLINE": "0",
"NCCL_IB_TIMEOUT": "22",
"NCCL_IB_RETRY_CNT": "7",
}) })
dist.init_process_group( dist.init_process_group("nccl", init_method="env://")
backend="nccl",
init_method="env://",
rank=1,
world_size=2,
timeout=timedelta(minutes=5),
)
print("[rank1] process group OK")
torch.cuda.set_device(0) torch.cuda.set_device(0)
device = torch.device("cuda:0")
print(f"[rank1] GPU={torch.cuda.get_device_name(0)}")
_train(rank=1, device=device) x = torch.ones(1, device="cuda")
dist.all_reduce(x)
print(f"[rank1] all_reduce result = {x.item()}")
dist.destroy_process_group() dist.destroy_process_group()
print("[rank1] DONE")
# ── общий train loop ────────────────────────────────────────────────────────── # -------------------------
def _train(rank: int, device): # DAG
import torch # -------------------------
import torch.distributed as dist
import torch.nn as nn
import torch.optim as optim
# allreduce smoke-test
probe = torch.ones(1, device=device) * rank
dist.all_reduce(probe, op=dist.ReduceOp.SUM)
assert probe.item() == 1.0, f"allreduce mismatch: {probe.item()}"
print(f"[rank{rank}] allreduce smoke-test PASSED")
model = nn.Sequential(
nn.Linear(10, 128),
nn.ReLU(),
nn.Linear(128, 10),
).to(device)
ddp_model = nn.parallel.DistributedDataParallel(model, device_ids=[0])
optimizer = optim.SGD(ddp_model.parameters(), lr=0.001)
loss_fn = nn.MSELoss()
for epoch in range(5):
x = torch.randn(32, 10, device=device)
y = torch.randn(32, 10, device=device)
optimizer.zero_grad()
loss = loss_fn(ddp_model(x), y)
loss.backward()
optimizer.step()
print(f"[rank{rank}] epoch={epoch} loss={loss.item():.4f}")
# ── DAG ───────────────────────────────────────────────────────────────────────
with DAG( with DAG(
dag_id="ddp_ib_xcom", dag_id="ib_ddp_final_test",
start_date=pendulum.today("UTC").add(days=-1), start_date=pendulum.today("UTC").add(days=-1),
schedule=None, schedule=None,
catchup=False, catchup=False,
@@ -172,42 +119,12 @@ with DAG(
task_id="train_rank_0", task_id="train_rank_0",
python_callable=run_rank_0, python_callable=run_rank_0,
queue="gpu", queue="gpu",
executor_config={
"pod_override": {
"spec": {
"containers": [{
"name": "base",
"securityContext": {
"capabilities": {
"add": ["IPC_LOCK", "SYS_RESOURCE"]
}
}
}]
}
}
},
) )
rank1 = PythonOperator( rank1 = PythonOperator(
task_id="train_rank_1", task_id="train_rank_1",
python_callable=run_rank_1, python_callable=run_rank_1,
queue="gpu", queue="gpu",
executor_config={
"pod_override": {
"spec": {
"containers": [{
"name": "base",
"securityContext": {
"capabilities": {
"add": ["IPC_LOCK", "SYS_RESOURCE"]
}
}
}]
}
}
},
) )
# rank1 стартует сразу, но ждёт master_addr через XCom polling rank0 >> rank1
[rank0, rank1]