Недавно я попытался настроить маршрутизацию на основе SNI на HAProxy для mongodb+srv
подключения по протоколу mongodb.
Я заставил это работать, но это не работало, пока я не поставил
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
в моей конфигурации frontend он начал работать правильно
Без них (илитолько с однимиз них) я продолжал получать спорадические сбросы соединения для примерно 70% моих запросов. Обратите внимание, что иногда соединение все же удавалось установить.
В соответствии сhttp://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-tcp-request%20content
Правила на основе содержимого оцениваются в точном порядке их объявления. Если ни одно правило не соответствует или его нет, то действие по умолчанию — принять содержимое. Нет определенного ограничения на количество правил, которые могут быть вставлены.
Так что это должно было работать по умолчанию.
Либо я упускаю какую-то фундаментальную концепцию TCP, либо какую-то конкретную деталь соединения Mongo. Но я заметил, что почти у всех есть одно и то же правило, определенное при переходе на маршрутизацию на основе SNI, так что это должно иметь смысл для большинства людей.
Рабочая конфигурация HAProxy:
frontend fe_mongo-nonprod
bind :27017
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl mongoAtlas-01 req_ssl_sni -i host1.mongodb.net
acl mongoAtlas-02 req_ssl_sni -i host2.mongodb.net
acl mongoAtlas-03 req_ssl_sni -i host3.mongodb.net
use_backend be_mongo-nonprod if mongoAtlas-01 || mongoAtlas-02 || mongoAtlas-03
backend be_mongo-nonprod
mode tcp
acl mongoAtlas-01 req_ssl_sni -i host1.mongodb.net
acl mongoAtlas-02 req_ssl_sni -i host2.mongodb.net
acl mongoAtlas-03 req_ssl_sni -i host3.mongodb.net
use-server server-01 if mongoAtlas-01
use-server server-02 if mongoAtlas-02
use-server server-03 if mongoAtlas-03
server server-01 host1.mongodb.net:27017
server server-02 host2.mongodb.net:27017
server server-03 host3.mongodb.net:27017
Также стоит отметить, что HAProxy развернут в Kubernetes (управляемый Google кластер в GCP), имеет istio-sidecar и доступен через службу Internal LoadBalancer k8s.
Как указано выше, конфигурация работает отлично. Мне интересно, почему tcp-request content accept
это абсолютно необходимо и почему соединение не [всегда] принимается по умолчанию здесь.
решение1
Я попытаюсь ответить на свой собственный вопрос, немного подумав.
Итак, в документации HAProxy также есть следующее упоминание: http://cbonte.github.io/haproxy-dconv/2.0/configuration.html
Если необходимо переключение контента, рекомендуется сначала дождаться полного приветствия клиента (тип 1), как в примере ниже.
Теперь, поскольку данные SNI также содержатся в разделе Client Hello запроса (https://www.rfc-editor.org/rfc/rfc6066#page-6) Мое предположение следующее:
В случае, если Client Hello распределен по нескольким TCP-пакетам и нет tcp-request content accept is configured
, при получении первого пакета HAProxy пытается маршрутизировать его, и происходит одно из двух:
- Если настроен бэкэнд по умолчанию, пакет пересылается на этот бэкэнд.
- Если бэкэнд по умолчанию не настроен (в моем случае), пакет отбрасывается и соединение сбрасывается.
Также обратите внимание, что даже в первом описанном случае сервер HAProxy, на который указывает в разделе backend, может также требовать SNI. Поэтому пересылка неполного запроса без указания SNI также приведет к сбросу соединения.