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