Выберите продукт

gRPC за Nginx: proxy_pass grpc://, HTTP/2, таймауты и ошибки

Разбираем, как запустить gRPC за Nginx: чем отличается от обычного HTTP-прокси, зачем нужен HTTP/2, как прописать grpc_pass и upstream, какие таймауты задать для потоков и длинных RPC, и как разбирать типовые ошибки 502/504/431 в логах.
gRPC за Nginx: proxy_pass grpc://, HTTP/2, таймауты и ошибки

gRPC стабильно уходит в прод, а вместе с ним — потребность ставить реверс‑прокси перед backend’ами. Чаще всего это Nginx: он принимает TLS, держит единый вход, балансирует, пишет логи и режет всплески. Но gRPC — не обычный HTTP/1.1: он живет поверх HTTP/2, использует мультиплексные потоки, трейлеры и строгое поведение по таймаутам. В этой статье соберем рабочую конфигурацию grpc_pass, разберем таймауты, балансировку и типовые ошибки.

Что особенного в gRPC за Nginx

Ключевое отличие: gRPC требует HTTP/2 и бинарные протоколы поверх него. Для клиента и Nginx это значит ALPN с параметром h2 на TLS‑входе. Для связи Nginx → backend — тоже HTTP/2, но это уже забота модуля ngx_http_grpc_module. Поэтому конфигурация должна явно включать http2 на listen и использовать grpc_pass или grpcs к апстриму. Дополнительно освежить знания по HTTP/2 можно в материале про приоритеты и push в HTTP/2.

Если забыть http2 на фронте, первый запрос клиента будет содержать «PRI * HTTP/2.0», а Nginx ответит 400 или 421. Если промахнуться портом/протоколом до backend’а, получите 502 с сообщением в ошибках про «invalid HTTP/2 header» или «prematurely closed connection».

Минимальная конфигурация: TLS и один backend

Базовый вариант: принимаем TLS‑трафик по HTTP/2 и проксируем на локальный gRPC‑сервер. Для публичного периметра заранее позаботьтесь о валидных SSL-сертификатах.

http {
    log_format grpc_access '$remote_addr - $host "$request" $status '
                           'grpc:$grpc_status $upstream_status '
                           '$upstream_response_time $request_time';
    access_log /var/log/nginx/grpc_access.log grpc_access;
    error_log  /var/log/nginx/grpc_error.log warn;

    upstream grpc_backend {
        server 127.0.0.1:50051;
        keepalive 32;
    }

    server {
        listen 443 ssl http2;
        server_name example.org;

        ssl_certificate /etc/ssl/example.crt;
        ssl_certificate_key /etc/ssl/example.key;

        # Важно для некоторых клиентов и прокси
        location / {
            grpc_set_header TE trailers;
            grpc_pass grpc://grpc_backend;

            # Таймауты к апстриму
            grpc_connect_timeout 3s;
            grpc_read_timeout 1m;
            grpc_send_timeout 1m;
        }
    }
}

Пояснения:

  • listen ... http2 — обязательно для клиентов gRPC поверх TLS.
  • grpc_set_header TE trailers — безопасная совместимость с реализацией трейлеров. В HTTP/2 это формальность, но иногда спасает от «неожиданных» 415/мутных ошибок клиентов.
  • keepalive в upstream — сокращает накладные расходы на установку HTTP/2 сессий до backend’а.

Схема балансировки gRPC в Nginx: upstream, least_conn, таймауты

h2c (без TLS) и внутренняя балансировка

Если фронт доступен только во внутренней сети, можно принимать HTTP/2 без TLS (h2c). Учтите: многие клиенты по умолчанию ожидают TLS и ALPN, поэтому h2c — это story для доверенных сетей и сервис‑to‑сервис.

server {
    listen 80 http2;
    server_name internal.example;

    location / {
        grpc_pass grpc://127.0.0.1:50051;
        grpc_connect_timeout 1s;
        grpc_read_timeout 5m;
        grpc_send_timeout 5m;
    }
}

Для балансировки:

upstream grpc_pool {
    least_conn;
    server 10.0.0.11:50051 max_fails=3 fail_timeout=10s;
    server 10.0.0.12:50051 max_fails=3 fail_timeout=10s;
    keepalive 64;
}

server {
    listen 443 ssl http2;
    server_name api.example.org;

    ssl_certificate /etc/ssl/api.crt;
    ssl_certificate_key /etc/ssl/api.key;

    location / {
        grpc_set_header TE trailers;
        grpc_pass grpc://grpc_pool;
        grpc_next_upstream error timeout http_502 http_504;
        grpc_next_upstream_tries 3;
        grpc_next_upstream_timeout 5s;
    }
}

Здесь least_conn помогает при долгоживущих потоках (стримы, bidi RPC) равномернее распределять нагрузку.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

TLS до backend’а: grpcs и доверие к сертификату

Если backend слушает поверх TLS, используйте grpcs:// и укажите доверенный корневой сертификат. При необходимости — SNI и имя сервера. Если вы переносите входной домен или настраиваете HSTS, пригодится обзор по миграции: переезд домена, 301 и HSTS.

upstream grpc_tls {
    server backend.example.internal:443;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name edge.example.org;

    ssl_certificate /etc/ssl/edge.crt;
    ssl_certificate_key /etc/ssl/edge.key;

    location / {
        grpc_pass grpcs://grpc_tls;
        grpc_ssl_name backend.example.internal;
        grpc_ssl_server_name on;
        grpc_ssl_trusted_certificate /etc/ssl/certs/ca-bundle.pem;
        grpc_connect_timeout 2s;
        grpc_read_timeout 2m;
        grpc_send_timeout 2m;
    }
}

Типичная ошибка при grpcs — 502 с сообщением о TLS‑рукопожатии. Проверяйте цепочку доверия, имя хоста и соответствие сертификата. Если фронт размещаете на отдельном узле, удобнее поднять Nginx на VDS, а публичную защищенную точку входа закрыть валидными SSL-сертификатами.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Таймауты: как не «убить» стримы и дедлайны

gRPC активно использует дедлайны, которые клиенты передают в заголовке gRPC-Timeout. Nginx сам по себе не интерпретирует этот дедлайн: он продолжит удерживать прокси‑соединение, пока не сработают собственные таймауты или backend не закроет поток. Потому важно настроить именно прокси‑таймауты.

  • grpc_connect_timeout — время на установление соединения с backend’ом. Короткое (1–3s) выявляет недоступные инстансы.
  • grpc_read_timeout — время ожидания следующего байта от backend’а. Для потоковых RPC ставьте с запасом (минуты или часы), иначе ловите 504 в тишине.
  • grpc_send_timeout — время на отправку данных backend’у. При медленном апстриме увеличивайте.

Практические пресеты:

  • Синхронные короткие RPC: connect=1s, read=10s, send=10s.
  • Генерация отчетов/долгие unary: connect=2s, read=2m, send=1m.
  • Стримы/наблюдения: connect=2s, read=1h, send=1m.

Отдельно про заголовки/метаданные: если получаете 431 на фронте, смотрите лимиты заголовков HTTP/2. Полезно увеличить http2_max_field_size и http2_max_header_size глобально, а также, при необходимости, large_client_header_buffers (хотя это больше про HTTP/1.1).

http {
    http2_max_field_size 32k;
    http2_max_header_size 128k;
}

Буферы и размеры сообщений

gRPC ограничивает размер сообщений на уровне приложения; Nginx не «склеивает» и не разрезает protobuf‑фреймы. Если backend или клиент жалуются на «message too large», ищите параметр максимального размера сообщения в сервере/клиенте gRPC. На стороне Nginx имеет смысл лишь отрегулировать grpc_buffer_size для сетевых операций и метаданных.

location / {
    grpc_pass grpc://grpc_backend;
    grpc_buffer_size 64k;
}

Помните: client_max_body_size на gRPC не влияет.

Логи и наблюдаемость gRPC за Nginx

Чтобы понимать, что произошло на уровне gRPC, включайте в формат логов переменную $grpc_status. Она содержит код статуса протокола (например, 0 OK, 4 DEADLINE_EXCEEDED, 14 UNAVAILABLE), который приходит в трейлерах. Параллельно полезны стандартные $status, $upstream_status, $upstream_response_time.

log_format grpc_json '{"ts":"$time_iso8601","remote":"$remote_addr",'
                     '"host":"$host","req":"$request",'
                     '"http":"$status","grpc":"$grpc_status",'
                     '"up":"$upstream_status",'
                     '"urt":"$upstream_response_time","rt":"$request_time"}';
access_log /var/log/nginx/grpc_access.json grpc_json;

Для «сложных» случаев на ограниченное время повышайте уровень error_log до info или debug и сузьте его до нужного server/location, чтобы не утонуть в объеме.

Логи Nginx для gRPC: $grpc_status, upstream_status, response_time

Типовые ошибки и как их лечить

502 Bad Gateway: upstream sent no valid HTTP/2 header

Чаще всего означает, что вы попали в HTTP/1.1 сервис либо в неверный порт. Проверьте порт backend’а, что там действительно gRPC, а не HTTP‑REST/сторонний демо‑сервер. Для grpcs — смотрите TLS/ALPN и цепочку доверия.

502 Bad Gateway: upstream prematurely closed connection

Backend закрыл соединение до того, как Nginx получил трейлеры/заголовки ответа. Причины: падение процесса, лимиты keepalive на сервере, агрессивный idle‑timeout на стороне backend’а, несовпадение протокола h2/h2c. Лечится увеличением таймаутов и проверкой логов сервиса.

504 Gateway Timeout

Сработал grpc_read_timeout или grpc_send_timeout. Если у вас стримы и могут быть длительные паузы, увеличьте read_timeout. Проверьте, что клиентский дедлайн не короче — иначе клиент оборвет поток раньше.

431 Request Header Fields Too Large

Метаданные gRPC распухли (корреляционные id, токены и пр.). Поднимите лимиты HTTP/2 заголовков в секции http и проверьте, что вы не передаете избыточные поля на каждый RPC.

400/421 на входе: PRI * HTTP/2.0

На фронте не включен http2 на listen, либо клиент пришел h2c на 443/TLS. В первом случае добавьте параметр http2, во втором — используйте 80 http2 (h2c) или заставьте клиента говорить TLS+ALPN.

415 Unsupported Media Type

Редко, но встречается при странной комбинации прокси/клиента. Убедитесь, что Content-Typeapplication/grpc, и добавьте grpc_set_header TE trailers на всякий случай.

Балансировка и устойчивость

gRPC‑соединения долгие, поэтому:

  • Используйте least_conn вместо round_robin, чтобы не «забилдить» один инстанс длинными потоками.
  • Задайте max_fails/fail_timeout и grpc_next_upstream, чтобы не ждать провалившийся backend дольше разумного.
  • Добавьте keepalive в апстрим, чтобы повторные RPC не тратили время на рукопожатия.

Если нужен стейт, который привязан к конкретному серверу (редко для gRPC, но бывает), можно рассмотреть ip_hash — однако это ухудшит распределение при больших диапазонах клиентов. В идеале — храните состояние вне процесса сервера или используйте токены сеансов.

gRPC‑Web — это другое

Браузерные клиенты часто используют gRPC‑Web (HTTP/1.1 или HTTP/2 с иными заголовками). Nginx не переводит gRPC‑Web в «настоящий» gRPC: для этого нужен отдельный шлюз. Если браузер — ваш основной клиент, убедитесь, что у вас есть совместимый гейтвей или сервер, умеющий оба протокола.

Контроль ресурсоемкости

gRPC поверх HTTP/2 активно мультиплексирует в одном TCP‑соединении много потоков. На фронте это означает:

  • worker_connections и лимиты файловых дескрипторов должны быть с запасом, особенно при тысячах клиентов с долгими стримами.
  • keepalive_timeout на фронте не стоит делать слишком коротким — иначе будете терять конвейеризацию и плодить handshakes.
  • Следите за CPU (шифрование TLS, HPACK компрессия заголовков) и памятью (буферы и окна потока). При необходимости масштабируйте фронты горизонтально.

Чек‑лист перед продом

  • Nginx собран с ngx_http_grpc_module, фронт слушает listen ... http2.
  • Путь до backend’а корректен: grpc:// или grpcs://, SNI и доверенный корень заданы при TLS.
  • Выставлены адекватные grpc_connect_timeout/grpc_read_timeout/grpc_send_timeout под ваш профиль RPC.
  • Включены логи с $grpc_status и метриками апстрима.
  • Балансировка учитывает длительность потоков: least_conn и keepalive в upstream.
  • Лимиты HTTP/2 заголовков увеличены при необходимости (http2_max_field_size, http2_max_header_size).
  • Проверены ошибки 502/504/431 на стенде и отработаны сценарии отказов (grpc_next_upstream).

Итого

Настройка gRPC за Nginx упирается в три вещи: строго включенный HTTP/2 на фронте, корректный grpc_pass до backend’ов и таймауты, соответствующие профилю трафика. Добавьте к этому продуманную балансировку и наблюдаемость с $grpc_status — и у вас получится устойчивый и предсказуемый слой входа для gRPC‑сервисов. А дальше все как обычно: нагрузочные прогоны, проверки ошибок и мониторинг, чтобы поймать углы именно вашей продовой нагрузки.

Поделиться статьей

Вам будет интересно

Linux ENOSPC из-за inode: диагностика df -i и быстрые способы исправления OpenAI Статья написана AI (GPT 5)

Linux ENOSPC из-за inode: диагностика df -i и быстрые способы исправления

ENOSPC «No space left on device» появляется даже при свободных гигабайтах, если закончились inode. Разберём проверку df -i, поиск ...
systemd: journalctl, systemctl status и лимиты запуска StartLimit/Timeout* без боли OpenAI Статья написана AI (GPT 5)

systemd: journalctl, systemctl status и лимиты запуска StartLimit/Timeout* без боли

Если сервис в systemd уходит в restart loop, падает с failed to start или «не успевает» подняться — почти всегда дело в логах и ли ...
Systemd-boot и UKI: unified kernel images для Secure Boot-ready VDS на Debian/Ubuntu OpenAI Статья написана AI (GPT 5)

Systemd-boot и UKI: unified kernel images для Secure Boot-ready VDS на Debian/Ubuntu

Практический разбор UKI (unified kernel image) и systemd-boot для Debian/Ubuntu на VDS: проверяем UEFI и ESP, ставим загрузчик, со ...