Kubernetes v1.37: etcd RangeStream зменшує використання памʼяті при читанні великих списків
Я радий повідомити, що etcd RangeStream переходить у бета-версію в Kubernetes v1.37. У поєднанні з etcd v3.7 це дозволяє зменшити обсяг памʼяті, необхідний API-серверу та etcd для зчитування великих наборів даних, а також робить пікове використання памʼяті більш передбачуваним.
Вартість великих читань
API-сервер обробляє більшість запитів list і watch із свого кешу спостереження, що зберігається в оперативній памʼяті. Для заповнення цього кешу необхідно зчитати повний стан ресурсу з etcd під час запуску та при кожній повторній ініціалізації. Для ресурсів, що містять багато обʼєктів або великі обʼєкти, такі як Podʼи, таке зчитування є витратним.
API-сервер і раніше розбивав ці читання на сторінки, запитуючи у etcd фіксовану кількість ключів за раз, а не всю колекцію одразу. Але сторінка, обмежена кількістю ключів, не має уявлення про розмір обʼєкта, тому сторінка великих обʼєктів все ще може бути дуже великою. Через це важко передбачити використання памʼяті, а невдале поєднання розміру обʼєкта та одночасних операцій читання може стати причиною для OOM. Унарний Range etcd повністю збирає кожну сторінку перед її надсиланням, а API-сервер утримує її під час декодування, тому одне й те саме корисне навантаження одночасно перебуває в памʼяті обох сторін. Більша частина цих витрат припадає на etcd, і саме тут потокова передача даних є найбільш корисною.
Потокове зчитування за допомогою RangeStream
У версії etcd v3.7 додано потокову версію цього запиту — RPC-виклик RangeStream. Він отримує той самий обʼєкт RangeRequest, що й Range, і повертає той самий набір результатів, але замість того, щоб формувати всю відповідь одразу, etcd розділяє її на фрагменти та передає їх потоком. Розмір фрагмента адаптивно налаштовується відповідно до значень, що повертаються, тому набір великих обʼєктів обмежується кількістю байтів, а не кількістю ключів, а памʼять звільняється по мірі просування потоку, а не утримується доти, доки не буде зібрана вся сторінка.
Коли цю можливість увімкнено, API-сервер використовує RangeStream у всіх випадках, коли він зчитує цілу колекцію з etcd. Це стосується як ініціалізації кешу спостереження, так і резервних шляхів, коли запит list не може бути оброблений із кешу і зчитування відбувається безпосередньо з etcd. У будь-якому випадку API-сервер декодує кожен фрагмент у міру його надходження та звільняє його перед завантаженням наступного, тому жодна зі сторін ніколи не зберігає цілу колекцію.
Вимоги
- Kubernetes v1.37 або новіший
- etcd v3.7 або новіший
RangeStream використовується, коли в kube-apiserver увімкнено функціональну можливість EtcdRangeStream, яка у v1.37 має статус бета і типово увімкнена, а etcd має версію v3.7 або новішу. API-сервер визначає підтримку в etcd під час запуску, а також повертається до попереднього шляху під час роботи, якщо виклик повертає Unimplemented, тому API-сервер, що працює зі старішим etcd, сам продовжує використовувати шлях Range з розбиттям на сторінки. Щоб вимкнути RangeStream, вимкніть функціональну можливість:
--feature-gates=EtcdRangeStream=false
Підтвердження використання RangeStream
API-сервер фіксує потокові операції читання у своїх метриках etcd з власною операційною міткою. Ненульове значення цього показника означає, що RangeStream використовується:
etcd_request_duration_seconds_count{operation="listStream"}
Якщо значення залишається нульовим, API-сервер все ще використовує шлях Range з розбиттям на сторінки, найімовірніше, тому що etcd старіший за v3.7.
Дізнатися більше
Якщо у вас є запитання або відгук, приєднуйтесь до каналу #sig-etcd на Kubernetes Slack.