Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

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-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

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‑сервисов. А дальше все как обычно: нагрузочные прогоны, проверки ошибок и мониторинг, чтобы поймать углы именно вашей продовой нагрузки.

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

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

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...