Failover региона LLM-эндпоинта
Если вы держите один и тот же деплоймент модели в нескольких регионах (Azure OpenAI East US + West Europe, зеркальные эндпоинты у других провайдеров), клиент при сбое сам уводит трафик в живой регион. Работает — но незаметно. А знать о смене активного региона надо: у другого региона своя латентность, свои квоты, иногда другая версия модели. Молчаливый failover сегодня — это сюрприз с квотой завтра.
Push при смене активного региона
Заголовок раздела «Push при смене активного региона»Ротатор помнит текущий активный регион; когда трафик уходит на другой — шлём push один раз на смену, а не на каждый запрос.
import os, json, requestsimport openai
NOTIFLY_URL = os.environ["NOTIFLY_URL"]NOTIFLY_TOKEN = os.environ["NOTIFLY_TOKEN"]
REGIONS = [ # порядок = приоритет {"name": "eastus", "base": "https://my-eastus.openai.azure.com"}, {"name": "westeurope", "base": "https://my-westeu.openai.azure.com"},]STATE = os.path.expanduser("~/.active-region.json")
def notify(title, msg, prio): requests.post(f"{NOTIFLY_URL}/message", params={"token": NOTIFLY_TOKEN}, json={"title": title, "message": msg, "priority": prio}, timeout=5)
def _active(): if os.path.exists(STATE): return json.load(open(STATE)).get("region") return None
def _set_active(name, reason): prev = _active() if name != prev: # регион сменился — сообщаем один раз json.dump({"region": name}, open(STATE, "w")) if prev is not None: notify( f"🌍 Регион сменился: {prev} → {name}", f"Активный LLM-эндпоинт теперь {name}. Причина: {reason}. " f"Проверьте квоты и версию модели в новом регионе.", priority=7, )
def chat(**kwargs): """Пробуем регионы по порядку; на первом живом фиксируем как активный.""" last_err = None for reg in REGIONS: try: client = openai.AzureOpenAI( azure_endpoint=reg["base"], api_key=os.environ["AZURE_KEY"], api_version="2024-10-21") resp = client.chat.completions.create(**kwargs) _set_active(reg["name"], reason="primary ok") return resp except Exception as e: last_err = f"{reg['name']}: {type(e).__name__}" continue raise RuntimeError(f"все регионы недоступны ({last_err})")
resp = chat(model="gpt-4o-mini", messages=[{"role": "user", "content": "ping"}])Push идёт на смену состояния, а не на каждый запрос из нового региона —
иначе после failover телефон разрывался бы. priority=7: не катастрофа
(сервис работает), но вмешаться стоит — свериться с квотами нового региона.
Подстрахуйте это активными мониторами
Заголовок раздела «Подстрахуйте это активными мониторами»Логика в коде видит только те регионы, куда вы реально ходите. Чтобы знать о здоровье всех регионов независимо от вашего трафика, повесьте на health-URL каждого по активному монитору. Тогда падение резервного региона вы увидите до того, как он понадобится под failover, а не в момент, когда основной уже лёг и резерв тоже мёртв.
Не забудьте про возврат (failback)
Заголовок раздела «Не забудьте про возврат (failback)»Смена A→B заметна, а вот тихий возврат B→A часто теряют. Тот же _set_active
уже ловит оба направления — просто убедитесь, что текст алёрта нейтрален
(«регион сменился»), и вы одинаково узнаете и об уходе, и о возвращении на
основной регион.
Что положить в текст алёрта
Заголовок раздела «Что положить в текст алёрта»- откуда и куда ушёл трафик (
eastus → westeurope); - причину переключения (5xx, таймаут, исчерпание квоты региона);
- напоминание сверить квоту и версию модели в новом регионе;
- метку времени — для дедупликации, если failover «дребезжит» A↔B.