k3-a40-bootstrap / create_pod.py
patdev's picture
Sauvegarde 22/08 : resultats, rapport, docs, scripts, Dockerfile image v3
2454f65 verified
Raw
History Blame Contribute Delete
3.88 kB
"""Recree le pod Ornith A40x2 depuis l'image precompilee du Space HF.
Pourquoi recreer et non redemarrer : Runpod a refuse le `start` avec
"not enough free GPUs on the host machine" -- un pod arrete est lie a son hote,
et l'hote n'a plus deux A40 libres. Le recreer le replace sur un hote qui en a.
Pourquoi un template : `create-pod` n'a pas de champ pour la commande de
demarrage ; elle passe par `dockerStartCmd` du template. Et l'image vient du
registre HF (prive), d'ou `containerRegistryAuthId`.
"""
import json
import os
import sys
import urllib.error
import urllib.request
ICI = os.path.dirname(os.path.abspath(__file__))
K = open(os.path.join(ICI, "rpk")).read().strip()
AUTH = open(os.path.join(ICI, "regauth")).read().strip()
HF_TOKEN = open(os.path.expanduser("~/.cache/huggingface/token")).read().strip()
PUB = open(os.path.join(ICI, "ssh", "pod_key.pub")).read().strip()
PUB_USER = ("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKK2mVCNr6s2S+r9fpX0/SP1gZiceL0pg/"
"NQCTTBA8Rs eira.patrick@gmail.com")
IMAGE = os.environ.get("POD_IMAGE", "registry.hf.space/patdev-ornith-vllm-a40:latest")
GPU = int(os.environ.get("POD_GPUS", "2"))
def rp(path, method="GET", body=None):
r = urllib.request.Request(
"https://rest.runpod.io/v1" + path, method=method,
headers={"Authorization": "Bearer " + K, "Content-Type": "application/json"},
data=json.dumps(body).encode() if body else None)
try:
return json.loads(urllib.request.urlopen(r, timeout=120).read().decode())
except urllib.error.HTTPError as e:
return {"err": e.code, "body": e.read().decode()[:500]}
ENV = {
"HF_TOKEN": HF_TOKEN,
"HF_XET_HIGH_PERFORMANCE": "1",
"HF_HUB_ENABLE_HF_TRANSFER": "0",
"VL_MODEL": "ornith",
"PUBLIC_KEY": PUB_USER + "\n" + PUB,
# Les serveurs MCP node meurent avec un preload herite ; on le neutralise.
"NODE_OPTIONS": "",
}
tpl_name = "ornith-vllm-a40-prebaked"
tpls = rp("/templates")
tpl = next((t for t in tpls if t.get("name") == tpl_name), None) if isinstance(tpls, list) else None
if tpl and tpl.get("imageName") != IMAGE:
rp(f"/templates/{tpl['id']}", "DELETE")
tpl = None
if not tpl:
tpl = rp("/templates", "POST", {
"name": tpl_name,
"imageName": IMAGE,
"containerRegistryAuthId": AUTH,
"containerDiskInGb": 200,
"volumeInGb": 0,
"ports": ["8080/http", "22/tcp"],
"env": ENV,
# /start_pod.sh = sshd en arriere-plan + bootstrap du Hub (rechargement a chaud)
"dockerStartCmd": ["/start_pod.sh"],
"isPublic": False,
})
if "err" in tpl:
sys.exit(f"template: {tpl}")
print("template", tpl["id"], tpl.get("imageName"))
# CUDA 13.0 epingle : sans lui, le meme deploiement marche ou casse selon le
# tirage d'hote. cuda-compat couvre 12.x, mais autant ne pas jouer.
corps = {
"name": "ornith-a40x2",
"templateId": tpl["id"],
"gpuTypeIds": ["NVIDIA A40"],
"gpuCount": GPU,
"cloudType": "SECURE",
"containerDiskInGb": 200,
"volumeInGb": 0,
"ports": ["8080/http", "22/tcp"],
"env": ENV,
"allowedCudaVersions": ["13.0", "12.8"],
"supportPublicIp": True,
}
essais = [dict(corps), {**corps, "dataCenterIds": ["EU-SE-1"]},
{**corps, "dataCenterIds": ["EU-RO-1"]}, {**corps, "dataCenterIds": ["EU-CZ-1"]},
{**corps, "cloudType": "COMMUNITY"}]
for i, c in enumerate(essais, 1):
p = rp("/pods", "POST", c)
if "err" not in p:
print(json.dumps({x: p.get(x) for x in ("id", "name", "desiredStatus", "costPerHr",
"gpuCount", "cudaVersion", "machineId")}, indent=1))
open(os.path.join(ICI, "podid"), "w").write(p["id"])
print("PODID", p["id"])
break
print(f"essai {i} refuse : {p['body'][:200]}")
else:
sys.exit("aucun placement possible")