В этом случае рассматривается конфигурация подключения к 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)