Проблемы с подключением к кластеру MongoDB и улучшения конфигурации

Проблемы с подключением к кластеру MongoDB и улучшения конфигурации

В этом случае рассматривается конфигурация подключения к MongoDB в кластерной среде на основе инцидента, вызванного наложением развертывания AWS EKS на обновление версии MongoDB Atlas.развертывание AWS EKS и обновление версии MongoDB Atlas1. Непредвиденный инцидент

1. Непредвиденный инцидент

При развертывании системы с использованием MongoDB Atlas в AWS EKS подключения к некоторым кластерам MongoDB не устанавливались, поэтому новый WAS не смог нормально запуститься. Это не означало, что ошибка возникала немедленно и процесс завершался. Приложение ожидало во время инициализации и не могло обрабатывать фактические запросы.

Поскольку в таком состоянии выполнялись проверка работоспособности Kubernetes и процедура развертывания, существующие экземпляры сервиса были остановлены, из-за чего инцидент расширился. В этом разборе мы проанализировали причину, сосредоточившись на процессе подключения к MongoDB , а не на самой проверке работоспособности. Изучив события, происходившие во время инцидента, мы обнаружили, что одновременно выполнялось обновление версии MongoDB Atlas.

2. Обновление версии Atlas совпало по времени с развертыванием

Кластер MongoDB состоит из нескольких участников, и во время технического обслуживания или обновления версии доступность некоторых участников может временно измениться. Если затронут Primary, также может быть выбран новый Primary. Это нормальное изменение состояния , которое необходимо учитывать при эксплуатации кластера, а драйвер должен уметь определять топологию и выбирать доступный сервер.

В этом случае начальное подключение нового WAS совпало по времени с переходом узлов в Atlas. У существующего WAS уже имелись пул подключений и информация о топологии, и если бы Atlas находился в штатном состоянии, новый WAS, скорее всего, подключился бы как обычно. Поэтому мы не стали связывать причину исключительно с техническим обслуживанием Atlas. Поскольку в кластерной среде могут происходить техническое обслуживание, переключение при отказе и смена Primary, новое приложение также должно уметь работать в состоянии, при котором некоторые участники недоступны.

Мы также рассмотрели возможность того, что проблемы с подключением к некоторым участникам кластера оставались незамеченными и не приводили к отказу сервиса. Если подключение удавалось установить к другому доступному участнику, проблема могла не проявляться до тех пор, пока переход узлов, вызванный обновлением, не совпал с начальным подключением нового WAS и не проявился как проблема при запуске.

3. Выявление проблемы по времени, необходимому для определения сбоя подключения

MongoDB Java Driver имеет тайм-аут подключения, заданный по умолчанию, однако значение по умолчанию не означает, что оно подходит для требований сервиса к запуску и отказоустойчивости. Во время начального подключения последовательно выполняются поиск DNS, TCP-подключение и обработка TLS, обнаружение топологии и выбор сервера. Если ожидание на каждом этапе складывается, WAS может оставаться в состоянии запуска дольше ожидаемого.

В центре анализа был не вопрос «Почему конкретный узел временно стал недоступен?», а «Почему в такой ситуации новый WAS не смог за подходящее время определить, успешно ли установлено подключение или произошел сбой?». Поэтому мы явно настроили тайм-аут подключения и политику выбора сервера.

4. Конфигурация, примененная в рабочей производственной среде

4.1 Настройка времени хранения DNS-кэша JVM

spec:
  template:
    spec:
      containers:
        - name: serviceName
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-Dsun.net.inetaddr.ttl=5" 

В JAVA_TOOL_OPTIONS в deployment.yml мы применили `-Dsun.net.inetaddr.ttl=5`. Этот параметр обеспечивает, чтобы JVM не хранила результаты поиска DNS в течение длительного времени , благодаря чему изменения адреса кластера или состояния DNS могут отражаться относительно быстро. Поскольку `sun.net.inetaddr.ttl` является свойством системы, зависящим от реализации JDK, следует проверить, поддерживается ли оно используемой JDK. В этом случае мы передали JVM только это свойство как свойство системы, не используя отдельное свойство безопасности.

4.2 Настройка тайм-аутов MongoDB Client и политик чтения/записи

public class MongoCustomConfig
        extends AbstractMongoClientConfiguration {

    @Override
    public MongoClient mongoClient() {
        //
        return getUriMongoClient();
    }

    @Override
    public MongoTemplate mongoTemplate(
            MongoDatabaseFactory databaseFactory,
            MappingMongoConverter converter) {
        //
        MongoTemplate mongoTemplate =
                new MongoTemplate(databaseFactory, converter);

        // [데이터 안정성] 과반수 노드 복제 확인으로 Primary 이탈 시 데이터 롤백 가능성 감소
        mongoTemplate.setWriteConcern(WriteConcern.MAJORITY);

        // [읽기 가용성] Primary 우선, 전환 중에는 Secondary 조회 허용
        mongoTemplate.setReadPreference(
                ReadPreference.primaryPreferred());

        return mongoTemplate;
    }

    private MongoClient getUriMongoClient() {
        //
        ConnectionString connectionString =
                new ConnectionString(dbUri);

        MongoClientSettings settings =
            MongoClientSettings.builder()
                .applyConnectionString(connectionString)
                .applyToSocketSettings(socketBuilder ->
                    socketBuilder
                        // TCP 초기 연결 대기 시간 제한
                        .connectTimeout(3, TimeUnit.SECONDS)

                        // 연결 후 응답 대기 시간 제한
                        .readTimeout(5, TimeUnit.SECONDS)
                )
                .applyToClusterSettings(clusterBuilder ->
                    clusterBuilder
                        // 사용 가능한 Replica Set 노드 탐색 대기 시간 제한
                        .serverSelectionTimeout(
                            5, TimeUnit.SECONDS)
                )
                // MongoDB 버전 변경 시 API 호환성 확인
                .serverApi(ServerApi.builder()
                    .version(ServerApiVersion.V1)
                    .strict(true)
                    .deprecationErrors(true)
                    .build())
                .build();

        return MongoClients.create(settings);
    }
}

5. Назначение применения каждого параметра

`connectTimeout`ограничивает время ожидания установления TCP-соединения с каждым отдельным сервером. В этой среде он был установлен в значение 3 секунды, чтобы клиент не ждал слишком долго ответа от недоступного сервера. `readTimeout`ограничивает время ожидания ответа сервера после установления соединения и был установлен в значение 5 секунд. Если обычные запросы часто превышают это значение, перед увеличением Timeout следует проверить запросы и индексы.

`serverSelectionTimeout`ограничивает время, которое драйвер тратит на поиск сервера в текущем кластере для обработки запроса. Он был установлен в значение 5 секунд с учётом переходов Primary или временного выхода некоторых участников. Три Timeout имеют разные области применения. `connectTimeout` управляет подключением к отдельному серверу, `serverSelectionTimeout`управляет выбором сервера внутри кластера, а `readTimeout` — ожиданием ответа после подключения. Использованные здесь значения — 3 и 5 секунд — адаптированы к условиям эксплуатации и не являются рекомендуемыми значениями для каждой системы.

`WriteConcern.MAJORITY`считает запись успешной после её репликации на большинство участников, снижая риск отката данных во время переключения при отказе. `ReadPreference.primaryPreferred()`отдаёт приоритет актуальности данных при обычном чтении, разрешая при этом чтение со Secondary в течение короткого периода недоступности Primary. Однако чтение больших объёмов данных в пакетном и массовом режимепри котором важно распределить нагрузку, создаваемую Primary, может использовать `secondaryPreferred` или другую отдельную политику чтения. Read Preference следует выбирать с учётом характеристик сервиса. Параметры strict/deprecationErrors в Server API V1 не являются функциями переключения при отказе, но помогают выявлять проблемы совместимости API при изменении версии сервера.

6. Расширение области применения на другие задачи обслуживания

В ходе анализа этого случая мы также рассмотрели ситуации обслуживания, не связанные с обновлением версии. Если после удаления большого объёма данных требуется фактически освободить место на диске, в зависимости от среды и версии можно рассмотреть такие операции, как `compact`. Важен не конкретный механизм обслуживания, а то, что во время операции роли или доступность участников кластера могут измениться. Обновление версии, переходы Primary и обслуживание узлов могут восприниматься приложением как временная невозможность подключиться к некоторым серверам.

Поэтому вместо обработки исключений для каждой отдельной операции важно настроить политику подключения так, чтобы сервис либо продолжал работу, когда некоторые участники кластера исключены, либо явно завершал работу в течение заданного времени.

7. Расширенное влияние во время проверки работоспособности и развёртывания

Новый WAS не успел установить соединение с MongoDB до того, как Kubernetes Health Check и развёртывание продолжились, из-за чего существующий сервис был остановлен, а масштаб сбоя увеличился. Однако в ходе фактического реагирования настройки Health Check не изменялись. Непосредственными целями улучшениябыли Timeout подключения к MongoDB, выбор сервера, кэш DNS, а также политики чтения и записи.

8. Результаты применения настроек и выводы из этого случая

После применения настроек та же проблема больше не повторялась. Однако, поскольку это было редкое состояние, возникающее только при совпадении развёртывания с изменениями состояния кластера, одного лишь отсутствия повторных случаев недостаточно для однозначного подтверждения эффективности. Ключевым улучшением стало явное определение политики допустимого для сервиса времени ожидания подключения и изменений состояния кластера вместо опоры только на поведение драйвера и JVM по умолчанию.

Хотя этот инцидент произошёл в Atlas, его выводы не ограничиваются Atlas. В системах MongoDB, состоящих из нескольких участников, таких как Replica Set, обслуживание, переключение при отказе и изменения Primary могут временно сделать некоторые участники недоступными. Использование базы данных с высокой доступностью не означает, что доступность приложения автоматически гарантируется. Клиент должен уметь выбрать подходящий сервер и определить успех или неудачу в течение ограниченного времени. Для этого необходимо совместно проверить Timeout подключения, выбор сервера, кэш DNS, а также политики чтения и записи.

Daniel(K)

Site footer