Блог ·
Перевстановлення контейнера є заміною
У контейнера немає ані диска шаблона, ані cloud-init, тож перевстановити його означає знищити й побудувати наново. Це змінює те, що ви винні клієнтові.
Автор: Provibr
Перевстановити віртуальну машину на Proxmox це майже нудно. Спинити її, видалити, клонувати шаблон на той самий ідентифікатор, дати cloud-init зробити решту. Шаблон несе диск, а диск несе операційну систему, тож «перевстановлення» насправді означає «зробити нову копію відомої доброї речі».
У контейнера Linux немає ні того, ні того. Його розпаковують з архіву tar, і все, чим він є після цього, ядра, памʼять, swap, розмір кореневої файлової системи, мережевий інтерфейс, резолвер, імʼя хоста, живе у файлі конфігурації на гіпервізорі й більше ніде. Немає образу, який можна клонувати згори. Тож перевстановлення контейнера є знищенням із наступним створенням, і щойно ви скажете це вголос, з нього випливає набір обовʼязків, яких у перевстановлення VM ніколи не було.
Ось як ми це побудували, і найдовше тривала не частина зі знищенням.
Дві фази, бо платформа не знає, що змонтовано
Потік розділено. Перша фаза лише читає й нічого не змінює: агент забирає конфігурацію контейнера й віддає її назад. Друга фаза спиняє, знищує, створює й запускає, використовуючи прочитане першою фазою як передумову.
Очевидна альтернатива це одна фаза з попереднім переглядом, побудованим із того, що платформа вже знає. Тут вона недоступна. Огляд обладнання для контейнерів не реалізовано, бо парсер, який для цього використовувався б, читає ключі конфігурації QEMU, тож ніде в нашій базі даних немає збереженого знімка точок монтування контейнера. Попередній перегляд, який позначає кожну точку монтування як «невідому», не є переглядом, це застереження.
Пʼять слів для того, що станеться з даними
Кожна точка монтування в конфігурації отримує рівно один ярлик, і ярлик стосується даних, а не монтування.
| Ярлик | Значення |
|---|---|
erased | том знищується разом із контейнером; коренева файлова система, завжди |
excluded | том належить іншому гостеві, тож Proxmox його пропускає і нічого не чіпає |
externally_managed | bind mount, шлях на хості; каталог виживає, монтування не повертається |
unknown | рядок не вдалося прочитати, і це єдиний ярлик, який блокує всю операцію |
preserved | дані лишаються і монтування повертається |
Останній рядок є в списку і ніколи не виробляється. Зберегти том через знищення означає відʼєднати його, видалити контейнер, не прибираючи дисків без посилань, і потім приєднати його назад: три кроки, середній з яких при відмові лишає том, яким більше ніхто не володіє. Це окрема робота з власним циклом прибирання. Значення існує у словнику, щоб пізнішій версії не довелося тихо підшивати його під іншою назвою, функція заявляє, що воно поки не пропонується, а тест стверджує, що класифікатор ніколи його не повертає.
Важлива відмінність саме та, що в першому реченні. Дані точки монтування можуть вижити, і монтування все одно не повернеться, бо виклик створення, який ми надсилаємо, завжди задає тільки кореневу файлову систему. Кожне інше монтування зникає з нового контейнера незалежно від того, що сталося з його байтами. Зрозуміти це навпаки означає породити найнебезпечніше речення в усій цій функції: preserved поруч із монтуванням, якого потім не існуватиме.
Власність записана в ідентифікаторі тому
Proxmox записує том в одній із двох форм, і обидві кажуть, який гість ним володіє. local-lvm:vm-5001-disk-0 і local:5001/vm-5001-disk-0.raw обидва належать контейнерові 5001. Bind mount не є ні тим, ні тим: це абсолютний шлях, і саме провідну похилу риску перевіряє сам Proxmox.
Той розбір вирішує excluded проти erased. Точку монтування в конфігурації цього контейнера, що вказує на том іншого гостя, знищення пропускає, бо Proxmox перевіряє власність, тож ті дані не витираються. Без порівняння попередній перегляд сказав би erased про дані, які лишаються. Хтось після цього відновив би резервну копію, якої не потребував, а це менша катастрофа, ніж зворотна, але все одно спричинена нами.
Попередній перегляд є передумовою, а не картинкою
Те, що побачив клієнт, їде назад разом із командою застосування у вигляді списку підтверджень, і агент читає конфігурацію ще раз безпосередньо перед знищенням. Якщо вона відрізняється, додано точку монтування, під тим самим ключем інший том, він відмовляє.
Між переглядом і натисканням стоїть людина, і за цей час адміністратор може приєднати диск в інтерфейсі Proxmox. Твердження «bind mount і керовані хостом точки монтування не входять автоматично до перевстановлення» є істинним лише тоді, коли щось його забезпечує.
Кожне підтвердження є ключем і томом і навмисно не ярликом. Ярлик є судженням платформи, і на дальньому кінці йому нема чого вирішувати; якби агент отримав його як частину того, з чим він порівнює, зʼявилася б гілка, що вибирає за ним, і тоді правила жили б у двох місцях. Ярлик усе одно записується, поруч із підтвердженнями в записі команди й у рядку наміру, бо це бік аудиту, і там має бути записано рівно те, що показали клієнтові.
Лише збільшення, порівняння в байтах, показ у гібібайтах
Перевстановлення не може зменшити кореневу файлову систему. Порівняння виконується на байтах, а екран округлює вгору до цілих ГіБ.
Округлення вгору є консервативним напрямком, і контрприклад каже чому. Округліть на екрані 8,5 ГіБ униз до «8 ГіБ», прийміть проти цього запит на 8 ГіБ, і зникне пів гібібайта, якого ніхто не просив втрачати. У вимірюванні на перебудованому Proxmox контейнер, що звітує 16 ГіБ, тоді як план просив 8, отримав відмову.
Шаром нижче є пастка. Proxmox записує розміри як 8G, 512M або як голе число, а голе число це кібібайти. Прочитане як байти, 8388608 дорівнює 8 МіБ; прочитане правильно, це 8 ГіБ. Парсер, який тут вгадає неправильно, не кидає помилки, він просто схвалює кожне зменшення, яке йому колись покажуть.
Девʼять причин відмовити
Перш ніж станеться щось незворотне, є девʼять іменованих блокувальників: контейнера немає, встановлено protection=1, Proxmox тримає блокування на гостеві, виконується одне з наших власних завдань резервного копіювання чи відновлення, існує захищена точка відновлення, коренева файлова система зменшилася б, її поточний розмір не вдалося прочитати, точку монтування не вдалося класифікувати, або перевстановлення вже триває.
Кожен із них є типізованим значенням із власним перекладеним реченням девʼятьма мовами, тож причина, якої немає в списку, речення отримати не може. Їх перевіряють двічі на нашому боці, один раз при малюванні попереднього перегляду і ще раз при натисканні кнопки підтвердження, і втретє їх перевіряє агент проти конфігурації, яку читає сам. Три замикання, бо це незворотний крок розділу.
Пароль root запитується у другій фазі, а не в першій, і їде як тимчасові дані, а не зберігається. Секрет, який ви вводите безпосередньо перед незворотним кроком, не проводить пів години в укладці браузера.
«Потребує звірки» є третім результатом, і цей стан тримає блокування
Якщо щось падає між знищенням і створенням, запуск не стає «невдалим». Він стає «потребує звірки» і несе ідентифікатор контейнера, крок, на якому помер, і те, чи контейнер ще існував, коли агент здався. Останнє поле може бути null, а null не є false: «я не зміг цього визначити» не має читатися як «ідентифікатор вільний».
Чи є відмова руйнівною, виводиться з позиції кроку у впорядкованому списку етапів, від знищення й далі. Саме тому етапи є списком, а не набором вільних рядків: питання «чи сталося вже щось незворотне» отримує відповідь рівно в одному місці.
Запуск у такому стані тримає блокування на сервері закритим. Почати другу заміну на ідентифікаторі, де ніхто не знає, чи сидить там контейнер, це рівно те, що ця конструкція забороняє, і закрити таке має людина.
Той, хто програв compare and swap, мусить перечитати
Немає жодного робітника, який оновлює ці рядки. Стан просувається тоді, коли хтось його читає: читання дивиться на команду, до якої прикріплено намір, і рухає рядок уперед, із compare and swap на лічильнику поколінь, тож два одночасні читачі разом виконують кожен перехід рівно один раз.
Це було спроєктовано. Не було спроєктовано те, що впіймав браузерний тест: що має повертати той, хто програв цей обмін.
Панель опитує раз на секунду після запиту попереднього перегляду, і те опитування виконує той самий перехід. Якщо клієнт завантажує сторінку в ту саму секунду, опитування виграє обмін, сторінка віддає власний, уже застарілий рядок, і екран далі каже, що чекає на агента, тоді як попередній перегляд уже в базі даних. Одна навігація, один запит і екран, що показує стан, якого більше немає. Полагодило це лише друге перезавантаження.
Виправлення завдовжки в одне речення і його варто засвоїти: програти compare and swap означає, за побудовою, що хтось інший щойно зробив рівно той перехід, якого хотіли ви. Правильною відповіддю є тому його рядок, і коштує це один select. Повернути власний є інтуїтивним вчинком, і він завжди хибний.
Що ми б записали
- Якщо в речі, яку ви замінюєте, немає образу, називайте це заміною. Слово змінює те, що ви винні клієнтові ще до початку.
- Позначайте дані й монтування окремо. Це два питання, і поєднання правдивої відповіді на одне з хибною відповіддю на друге є найгіршим виходом, який система здатна видати.
- Зробіть так, щоб попередній перегляд їхав назад як умова, перечитуйте його на дальньому кінці й відмовляйте за будь-якої відмінності. Між тим натиснула людина.
- Округлюйте консервативно, коли похибка округлення здатна лише руйнувати.
- Дайте наполовину виконаній руйнівній операції власний стан, тримайте блокування закритим і ніколи не давайте їй впасти ні в успіх, ні в невдачу.
- Перечитуйте після програного compare and swap, завжди.
Підтримку контейнерів на Proxmox розгортають етапами, і перевстановлення стоїть у тому порядку пізно, з наведених вище причин. Якщо ви раніше в тій послідовності, компроміси між двома типами гостей є в VM чи контейнер на Proxmox, а причина, з якої успішний виклик API не є завершеною роботою, є в чому 200 від Proxmox не є результатом.
З роботи над Proxmox у Provibr.