docs: refresh translations, and translate pages in parallel (#3458)

This commit is contained in:
Max 2026-09-07 13:35:53 +01:00 • committed by GitHub
commit fd66270926
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,6 +1,6 @@
---
translation:
sections: [28221886b198784f, f88ea1f1614f3a1d, ce926d686730b6d0, 3be24f8ad8bb5ab9, 3fad24032b2224ff, f25a7f860e579ecb, e758745df6fb7b0a]
sections: [28221886b198784f, f88ea1f1614f3a1d, 2e76f5cb9df15042, ce926d686730b6d0, 3be24f8ad8bb5ab9, 3fad24032b2224ff, f25a7f860e579ecb, 697b01d95080880d]
tool: 1
---
# Развёртывание и масштабирование {#deploy-scale}
@ -47,6 +47,22 @@ translation:
Свежеразвёрнутый сервер, который отклоняет все подключения, — это список разрешённых Host,
пока не доказано обратное. **[Устранение неполадок](../troubleshooting.md)** тоже начинается отсюда.
## За прокси, терминирующим TLS {#behind-a-tls-terminating-proxy}
Если TLS завершается на прокси (ingress, балансировщик нагрузки, Caddy, nginx), а uvicorn за ним отдаёт обычный HTTP, велите uvicorn доверять заголовкам `X-Forwarded-*` от прокси:
```console
uvicorn server:app --proxy-headers --forwarded-allow-ips='<proxy address>'
```
Без этого приложение считает, что его отдают по `http://`, и любое перенаправление, которое оно выдаёт (обычно это `/mcp` → `/mcp/`), указывает на `http://…`. Python-клиент отказывается переходить с HTTPS-эндпоинта на обычный HTTP и прямо об этом говорит:
```text
MCPError: Redirect to http://mcp.example.com/mcp/ not followed: it would downgrade this HTTPS endpoint to plain HTTP.
```
Временное решение на стороне клиента — указать точный URL, который обслуживает сервер (`https://mcp.example.com/mcp/`, со слэшем на конце), чтобы перенаправления не было вовсе. Настоящее решение — флаг выше. `FORWARDED_ALLOW_IPS` — то же самое в виде переменной окружения; `*` доверяет каждому узлу по пути, что правильно, только если до uvicorn не может добраться никто, кроме прокси.
## Рабочие процессы и кому нужна привязка {#workers-and-who-has-to-be-sticky}
Как только доменное имя отвечает, поставьте за ним больше одного рабочего процесса. В SDK для этого нет никакой ручки; приложение Starlette масштабируется так же, как любое ASGI-приложение: объект передаётся тому, кто умеет порождать процессы:
@ -171,6 +187,7 @@ python -c "import secrets; print(secrets.token_hex(32))"
## Итоги {#recap}
* По умолчанию приложение отвечает только на запросы, адресованные localhost. `transport_security=TransportSecuritySettings(allowed_hosts=[...], allowed_origins=[...])` — это ворота в продакшен: пока вы его не передадите, каждый запрос за настоящим доменным именем получает `421`, а причина есть только в логе сервера.
* За прокси, терминирующим TLS, запускайте uvicorn с `--proxy-headers --forwarded-allow-ips=...`, иначе его перенаправления указывают на `http://`, и клиент их отклоняет.
* На 2026-07-28 нет сессии, и балансировщику не к чему привязываться. `stateless_http=True` — ручка только для старого поколения, потому что современный запрос маршрутизируется и получает ответ раньше, чем этот флаг вообще читается.
* Ключ `requestState` по умолчанию — `os.urandom(32)`, выпускаемый в каждом процессе. Многораундовый повтор, попавший к другому рабочему процессу, падает с `-32602` *«Invalid or expired requestState»*.
* Решение — `RequestStateSecurity(keys=[...])` **и** одно и то же имя сервера на каждом экземпляре. Имя — это audience claim токена по умолчанию. Одни ключи, одно имя.