🧑‍💻DevDays
10 subscribers
4 photos
29 links
Практичні нотатки про кібербезпеку, мережі, self-hosting, проксі та AI-інфраструктуру українською. Реальні кейси, код і розбори.
Download Telegram
Встановив власний проєкт за власною інструкцією 😅

Transparent Gateway уже працював на домашньому стенді. Після попередніх пригод навіть навчився розгортатися на новій машині.

Але захотілося перевірити простішу й значно кориснішу ситуацію: людина відкрила GitHub, завантажила код і вирішила ним скористатися. Без моїх готових віртуалок і підказок через плече 🤔

Для тих, хто не застав початок: Transparent Gateway — це шлюз між клієнтськими машинами та інтернетом. Програми звертаються до сайтів звичайним способом, а шлюз спрямовує їхні з’єднання через налаштований проксі. Вписувати проксі окремо в браузер чи кожну програму не потрібно.

Маршрут виходить такий:

клієнт → gateway → проксі → сайт


Тому підготував чистий Debian і почав із цілком буденної команди:

git clone https://github.com/Va1b0rt/TransparentGateway.git


Далі потрібно було ввести параметри мережі, додати проксі й запустити сервіси.

Саме цей шлях і захотів зробити нормальним: щоб для першого запуску не доводилося спочатку вивчати внутрішню будову всього проєкту ⚙️

Почав із налаштувань. Бо адреса проксі одна, а файлів, яким щось від неї потрібно, уже декілька.
❤1👏1
Налаштування без екскурсії по конфігах ⚙️

Для першого запуску gateway потрібні досить прості речі: клієнтська мережа, адреса проксі, його порт і, за потреби, логін із паролем.

Але всередині проєкту вони розкладаються по різних файлах. Частина належить мережевим налаштуванням, частина — списку проксі, ще частина — даним доступу.

Вручну це зробити можна. Але змушувати людину розбирати всі ці файли ще до першого запуску якось негостинно 😅

Тому додав до репозиторію scripts/tgctl.py — скрипт для налаштування й керування gateway.

На підготовленій машині, з каталогу проєкту, запускаю:

sudo python3 scripts/tgctl.py configure


Він послідовно запитує потрібні значення. Наприклад:

Protocol ... [http]: socks5
Authorised upstream proxy host or IP: proxy.example.com
Proxy port [3128]: 1080


Тут proxy.example.com — вигаданий приклад. Для роботи вводжу дані свого реального проксі.

Якщо потрібна авторизація, скрипт запитає логін і пароль. Пароль під час введення не відображається 🔑

Відповіді потрапляють у робочі файли з потрібними правами. Після цього окремою командою перевіряю конфігурацію:

sudo python3 scripts/tgctl.py check


У результаті перше знайомство з проєктом уже не починається з ручного редагування JSON-файлів ✅

А от чому пароль узагалі лежить окремо від адреси проксі — там є своя причина.
Мільйон доларів і трохи математики 💸

Прочитав у пості розбір про шанси стати доларовим мільйонером. Для України автор отримав приблизно 0,05%.

Спочатку взяв частку мільйонерів у населенні, потім додав власні припущення про стартові умови та вплив війни. Вийшла цифра, після якої хочеться закрити калькулятор і застрелитись піти зробити чай 😅

У коментарях зачепилися за питання: а чи можна цей відсоток вважати моїм особистим шансом?
Автор пояснив свою думку через можливості. Там, де більший платоспроможний ринок, легше знайти справу, на якій можна заробити великий капітал. Самого бажання багато працювати недостатньо, якщо покупцям нічим платити.

Із цим погоджуюся. Але між «скільки людей уже мають мільйон» і «чи зможу заробити його за життя» є різниця.

Частка мільйонерів показує, скільки людей уже мають такі статки. Але не показує, скільки людей, які сьогодні починають з нуля, дійдуть до мільйона за наступні двадцять років. Це різні питання, хоча відповідь на обидва хочеться отримати одним діленням.

Щоб порахувати саме шанс, потрібно знати, скільки людей починали зі схожих умов, що робили й до чого прийшли за певний час. Самого ділення кількості мільйонерів на населення для цього замало. Звіт Credit Suisse, на який послався автор, описує розподіл статків, а не ймовірність конкретної людини розбагатіти.

Моя перша думка була простішою: «Але ж за ці гроші змагаються не всі».

Не кожен хоче будувати бізнес, продавати свій продукт чи ризикувати заради більшого доходу. Комусь достатньо стабільної роботи — і це нормальний вибір.

Тільки тут теж легко намалювати собі красиву математику. Прибрати зі знаменника всіх, хто «не грає», поділити ще раз — і порадіти новому відсотку. Але скільки насправді людей пробували, скільки не вийшло й за який період це рахувати? Цих даних у мене немає. Тому власні «тепер уже точно 5%» вигадувати не буду 🙂

Натомість є інша важлива деталь: жити в Україні й продавати лише в Україні — не одне й те саме.

Наприклад, можу робити гру або сервіс тут, а продавати людям з інших країн. Тоді можливості мого продукту залежать уже не тільки від того, скільки готові витратити покупці вдома.

Легкою задачу це не робить. За кордоном на мій продукт теж ніхто спеціально не чекає: доведеться знайти покупця й переконати його заплатити.

Тому думку автора про нерівні можливості приймаю. Місце, де живеш і працюєш, справді має значення. Але свої особисті шанси з цього відсотка все одно не вирахую.

Не знаю, наскільки вони вищі за 0,05%. І підміняти неприємну цифру приємнішою тільки заради настрою теж не хочу.

Але між «у цій країні мало мільйонерів» і «у мене майже немає шансів» — висновок, якого ця математика не доводить.

Мільйона поки немає. Але й статистично довести, що варто кидати спроби за нього конкурувати, теж не вдалося. Продовжую працювати 😅
🔥3🤝1
Пароль теж хотів подорожувати 🔑

Під час налаштування ввів адресу проксі та пароль до нього. Здавалося б, записати поруч — і готово.

Але список проксі проходить через кілька сервісів: один збирає адреси, другий перевіряє, третій вибирає придатні для роботи. Результати передаються через Redis.

Якщо покласти пароль поруч з адресою, він поїде тією самою дорогою. Хоча більшості цих сервісів пароль узагалі не потрібен 🫣

Тому в записі про проксі залишив лише мітку:

"credential_ref": "quickstart-proxy"


За нею два сервіси знаходять потрібні дані доступу в окремому файлі.

Validator бере пароль, коли перевіряє проксі. Gateway — коли передає через нього клієнтський трафік. Інші компоненти працюють із міткою й самого пароля не отримують ⚙️

Магічного сейфа тут поки немає: пароль лежить у звичайному JSON-файлі, а користувач root може його прочитати.

Зате він уже не мандрує всім конвеєром. І пізніше зможу підключити сховище секретів, змінивши місце, звідки ці два сервіси отримують пароль.

Так вдалося розділити адресу проксі й доступ до нього без зайвої складності. Що не може не радувати 🙂

Налаштування готові, контейнери запустилися. А потім зникло світло.
Світло знайшло забуту службу 💡

Поки готував перевірку gateway, згасло світло. Машина перезавантажилася.

Після її повернення Docker працював. А шість контейнерів мого проєкту — ні 😬

У налаштуваннях контейнерів стояло:

restart: "no"


І це було навмисно. Запуском мала керувати служба systemd — системний механізм, який запускає програми під час завантаження Linux.

От тільки встановлення самої служби в команду запуску забув додати. Контейнери запустив, автоматичний запуск подумки теж установив. Дуже зручно 😅

Поки машина не перезавантажувалася, помилка себе не показувала.

Виправив tgctl.py up: тепер після успішного першого запуску він установлює та вмикає службу. Під час наступного завантаження вона чекає на Docker, запускає контейнери й перевіряє появу робочого проксі.

Після виправлень уже навмисно перезавантажив машину. Цього разу gateway піднявся без ручної допомоги ✅

Безкоштовне тестування від електромережі виявилося корисним. Але до списку залежностей проєкту його все-таки не додавав. 😜

На цьому проблему запуску закрив. А клієнт тим часом знайшов наступну: адресу сайту він усе ще визначити не міг.
😁1
UGEN пише у своєму пості про проблему, яка висить над новачками (і не тільки) в IT вже роками. 📉

Відкриваєш вакансії — і легко знаходиш junior-позиції з вимогами до вже досвідченого спеціаліста. У їхньому прикладі Junior Recruiter має три роки досвіду в технічному рекрутингу. 🤯

Історія не нова. Вимоги встигнути попрацювати ще до першої роботи зустрічалися задовго до нинішньої конкуренції. Питання, чи справді шукають джуна, чи просто хочуть платити як джуну, теж нікуди не поділося. 💸

UGEN пропонує уточнити робочі моменти: що означає Middle у цій компанії, чи буде ментор, які умови стажування та оплати. 📝

Але UGEN не уточнює, що шанс поставити ці питання буде далеко не у кожного.

За даними Ashby, для технічних позицій до співбесіди доходять близько 3,6% відгуків. Серед тих, хто дістався співбесіди, до оферу доходять 7,3%. 📲

На умовну тисячу відгуків це приблизно 36 запрошень на співбесіду й 2–3 офери. Це не окрема статистика українських джунів і не рекрутерської вакансії з допису UGEN — це масштаб відбору на технічні позиції у вибірці Ashby. 🎯

І вся ця боротьба та відбір на рівні спецназу — за позиції на $500–800.

Для справедливості: далеко не кожен роботодавець виставляє джуну неадекватні вимоги. За моїми спостереженнями, на профільних DOU та Djinni вакансії зазвичай притомніші, ніж на загальних сайтах пошуку роботи.

Але головна конкуренція для джуна — з іншими кандидатами. І вона є всюди, незалежно від сайту. Айтівців стало багато. Із нас тепер можна обирати.

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

От про цей шлях і хочеться читати в каналі для пошуку першої роботи. Реальні кейси: що кандидат умів, які проєкти показав, як отримав запрошення на співбесіду й що допомогло отримати офер.

Бо список питань до роботодавця — це добре. Але до кінця посту так і не зрозумів: як кандидату наблизитися до першої роботи? Питань у нього й до цього вистачало. 🤷‍♂️
❤1
Забув, що gateway теж може бути DNS-сервером 🧩

Після виправлення запуску контейнери працювали, проксі був готовий, а клієнтська перевірка завершувалася так:

CLIENT_ROUTE PASS gateway=192.168.91.1 dev=eth1
CLIENT_CHECK FAIL: [Errno -2] Name or service not known


Перший рядок показував правильний маршрут через gateway. Другий — що клієнт не зміг знайти адресу потрібного сервісу через DNS.

DNS перетворює ім’я на кшталт echo.devdays.net.ua на IP-адресу, до якої вже можна підключитися. Без цього HTTPS-запит навіть не почався 🤷‍♂️

У правилах gateway врахував звернення до 1.1.1.1 і 1.0.0.1: такі DNS-запити передавалися через захищене HTTPS-з’єднання.

А клієнт запитував адресу в самого gateway — 192.168.91.1.

Оцей варіант і пропустив 😅

Додав правило, яке спрямовує DNS-запити до власної адреси gateway в той самий обробник. Тепер клієнт міг використовувати шлюз як DNS-сервер, а той уже знаходив відповідь через зовнішній сервіс.

Після виправлення перевірка пройшла далі й отримала HTTPS-відповідь ✅

Але отримати відповідь — ще половина справи. Залишилося порівняти, з якої адреси публічний сервіс побачив цей запит.
❤1
Перевіряю запит із клієнтської машини 🔎

Gateway запустився, DNS запрацював. Тепер потрібно було пройти весь маршрут звичайним клієнтським запитом.

Для цього використав окрему машину, єдиний вихід якої в інтернет веде через gateway.

Передав їй scripts/client-check.py із репозиторію та запустив перевірку вже на клієнті:

python3 /tmp/tg-client-check.py --gateway 192.168.91.1


Скрипт перевіряє маршрут, знаходить адресу публічного echo-сервісу через DNS і звертається до нього через HTTPS.

Echo повертає адресу, з якої побачив запит. Саме вона й дозволяє порівняти результат із проксі, який вибрав gateway.

Щоб не показувати зовнішні адреси у виводі, обидві перевірки обчислюють для них однакові короткі відбитки. Отримав:

На gateway:
active_exit_ids=0649753926f0

На клієнті:
PUBLIC_EXIT id=0649753926f0


Значення збіглися ✅

Тобто публічний сервіс побачив клієнтський запит з адреси активного проксі. Сам клієнт при цьому знав лише адресу gateway.

Оце вже той результат, заради якого все й затіяв: програма просто відкриває сайт, а потрібний маршрут забезпечує шлюз ⚙️
❤1
Transparent Gateway вже можна встановлювати 🛠️

Нарешті пройшов шлях від репозиторію на GitHub до робочого gateway на чистій Debian-машині 🚀

Поки розгортав, знайшов забуте встановлення служби systemd і пропущений випадок у DNS. Обидві проблеми виправив. Після повторного перезавантаження сервіси запустилися самі, а клієнтський запит пройшов через проксі.

Тепер на підготовленому Debian основні дії виглядають так:

sudo python3 scripts/tgctl.py configure
sudo python3 scripts/tgctl.py check
sudo python3 scripts/tgctl.py up --timeout 300


Перша команда збирає налаштування, друга їх перевіряє, третя запускає gateway й чекає на робочий проксі.

Сам проксі та клієнтську мережу потрібно підготувати заздалегідь — їх скрипт із повітря ще не дістає 😅

Найприємніше тут те, що проєктом уже можна скористатися, не повторюючи всі мої попередні експерименти.

Оновлення теж уже спростив: нова версія встановлюється однією командою, налаштування залишаються на місці, а якщо запуск не вдався — повертається попередня версія. Цю роботу описав окремо:

Оновлення Transparent Gateway без повторного налаштування

Далі — діагностика, щоб при помилці одразу було видно, де саме зупинився запит.

Повний шлях із командами, скрінами та пригодами зі світлом зібрав у статті:

Від GitHub до робочого Transparent Gateway

Код проєкту на GitHub
Поки що №815 на HackerRank Orchestrate 🧑‍💻

З’явилися проміжні результати змагання, про яке писав раніше. Зараз маю 62,6 бала зі 100 і 815-те місце серед майже п’яти тисяч учасників. Як на мене, непогано 🙂

До завершення оцінювання ще приблизно дві доби. Роботи продовжують перевіряти, тому місце й бали ще можуть змінитися. Фінальну табличку поки не малюю.

А завдання було цікаве — перевірити, чи справді компанія видалила персональні дані користувача. 🔍

Людина попросила видалити її дані. Система відзвітувала: «Готово». А потім людина пробує зареєструватися знову й отримує: «Ця пошта вже використовується».

Десь видалили недостатньо 😅

Обліковий запис міг зникнути з основної бази, але залишитися в іншій копії, журналах, вкладеннях підтримки чи старому експорті. А ще якийсь процес синхронізації міг сумлінно повернути вже видалені дані назад.

Потрібно було побудувати AI-агента, який розбирається в цьому господарстві. На вході — таблиці, журнали, PDF, зображення та знімки баз даних. Загалом 518 звернень.

Для кожного агент мав з’ясувати:

• чи людина взагалі просила видалення, а не звернулася з іншим питанням;
• які її дані залишилися, зокрема під старими адресами пошти чи іншими ідентифікаторами;
• чи може синхронізація або відновлення з резервної копії повернути їх назад;
• що потрібно виправити та як потім перевірити результат.

Причому просто запропонувати видалити все підряд теж не можна. За умовою частину записів потрібно зберегти, але прибрати з них дані, які дозволяють упізнати людину. Агент мав підготувати обґрунтований висновок і послідовність дій, а не самостійно влаштувати прибирання в базах. 🧹

Оцінювали результат, код, історію роботи з AI та пояснення рішення на співбесіді з AI-суддею.

І от на останньому посипався — 9 із 30 балів. 🫠

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

Наступного разу буду перепитувати й просити конкретизувати питання. Бо сидіти, намагатися вгадати, що мала на увазі нейронка, а потім ще й пояснювати їй свою здогадку англійською — це вже якесь додаткове змагання 😂

Поки чекаю остаточних результатів. Навіть нинішнє №815 мене радує. А куди пересуне за дві доби — ще побачимо 👀
🔥2
Оновлення без другого знайомства з конфігами 🔄

Трохи випав із серії про Transparent Gateway — брав участь у HackerRank Orchestrate, про який писав вище. Тепер повертаюся до проєкту: встановлення вже розібрав, на черзі — оновлення 🔄

У попередній серії довів установлення Transparent Gateway до нормального вигляду: завантажив код, увів налаштування, запустив. Після перезавантаження він теж повертався сам. Уже приємно 🙂

Але проєкт продовжує змінюватися. З’являються виправлення й нові можливості, які потрібно якось доставити на вже встановлений gateway.

І тут заново вводити адресу проксі, пароль та параметри мережі зовсім не хочеться. Один раз уже натанцювалися з тими налаштуваннями, досить 😅

Тому додав до скрипта керування scripts/tgctl.py команду оновлення. На машині з установленим gateway, із каталогу проєкту:

sudo python3 scripts/tgctl.py update main --timeout 300


main — гілка, з якої команда забере нову версію. --timeout 300 — до п’яти хвилин на перевірку готовності після її запуску.

Код і контейнери замінюються. Налаштування мережі, список проксі, паролі й дані в Redis залишаються на місці. Повторно проходити діалог налаштування не потрібно 🔑

Якщо змінював сам код проєкту, команда покаже змінені файли й зупиниться. Затирати чужі правки заради зручного оновлення — це не наш метод.

Але найцікавіше тут — порядок дій. Спочатку потрібно підготувати нову версію й лише потім зупиняти робочу. Бо залишитися без gateway через образ, який навіть не зібрався, було б якось образливо.

Як це організував — покажу наступним постом ⚙️
Нова версія готується, стара ще працює 🔄

У минулому пості показав команду оновлення Transparent Gateway. Поки вона готує нову версію, поточний gateway продовжує передавати трафік.

Спочатку завантажує новий код в окремий каталог. Робочий gateway продовжує передавати трафік, а поруч готується його заміна ⚙️

Нова версія використовуватиме ті самі налаштування мережі та проксі. Тому перед запуском команда перевіряє, чи вони їй підходять. Наприклад, якщо в коді з’явився новий обов’язковий параметр, краще побачити повідомлення про нього до зупинки gateway, а не після.

Далі Docker збирає образи — пакує новий код разом із залежностями для запуску. Старі контейнери поки працюють.

Увесь цей час попередня версія залишається в роботі.

Коли нові образи готові, команда зберігає попередній код та образи для відкату. Якщо нова версія не запрацює, буде з чого повернути стару 🔙

І лише після підготовки зупиняє старі контейнери, запускає нові та перевіряє їхню роботу:

Завантажити нову версію
↓
Перевірити її з наявними налаштуваннями
↓
Зібрати нові образи
↓
Зберегти стару версію для відкату
↓
Замінити контейнери й перевірити роботу


Так завантаження та збирання не забирають час у працюючого gateway. Якщо на цих етапах щось не вдалося — оновлення зупиниться з помилкою, але поточна версія залишиться працювати.

На стенді саме так і сталося. Перед заміною контейнерів скрипт спробував зберегти старі образи для повернення попередньої версії. Docker відповів, що одного з образів не знайшов. Оновлення зупинилося, gateway продовжив роботу.

З’ясувалося, що образ був на місці, але скрипт шукав його за ідентифікатором, який Docker на цьому стенді не приймав. Що змінив у пошуку — розповім наступним постом 🔧
❤1
№707 із 4090: остаточні результати HackerRank Orchestrate 🧑‍💻

Оцінювання завершилося: 707-ме місце та 62,6 бала зі 100 🎯

Початково учасників було понад п’ять тисяч. Частину дискваліфікували за плагіат, у фінальній таблиці залишилося 4090.

У попередньому пості був №815. Бали не змінилися, а підсумкове місце стало кращим 🙂

Завданням було створити AI-агента, який перевіряє, чи компанія виконала прохання користувача видалити його дані. Якщо щось залишилося в базах, журналах або резервних копіях — агент має знайти це й пояснити, що потрібно зробити.

Найбільше втратив на англомовній співбесіді — 9 із 30. І головна проблема тут — мій рівень англійської: не завжди розумів питання й не міг нормально пояснити рішення. Наступного разу більше перепитуватиму, але й мову потрібно підтягувати. Код себе англійською не захистить 😅

707-ме серед понад чотирьох тисяч — мене радує. Є результат і зрозуміло, над чим працювати далі 💪

Остаточний лідерборд
Виправив збереження образів для відкату 🔧

Перше оновлення Transparent Gateway зупинилося ще до заміни контейнерів. Скрипт не зміг підготувати попередню версію для відкату.

Для цього він мав закріпити за старими образами окремі назви. Так після оновлення можна було б запустити саме їх, а не повторно збирати старий код.

Але Docker відповів, що потрібного образу не знайшов. Хоча запущений контейнер був створений саме з нього 🤔

Причина виявилася в тому, як скрипт шукав образ.

Він брав ідентифікатор із відомостей про контейнер і передавав його команді docker tag, яка додає образу назву. На тестовому Docker 26 це працювало. На Docker 29 зі стенда — ні: отриманий ідентифікатор не підходив для цієї операції.

Тобто образ не зник. Скрипт неправильно до нього звертався.

Замість перевірки «яка тут версія Docker?» додав пошук самого образу:

Спробувати знайти за ідентифікатором контейнера
↓ якщо не вдалося
Знайти за назвою й перевірити відповідність
↓ якщо назва вже вказує на новий образ
Пошукати потрібний серед інших локальних образів


Перевірка відповідності тут потрібна: після збирання назва може вже належати новій версії. Зберегти її замість старої — означає підготувати відкат назад у те саме оновлення 😅

Якщо потрібний образ знайти не вдалося, скрипт зупиняється й називає сервіс, для якого не може підготувати відкат. Робочі контейнери залишаються на місці.

Після виправлення збереглися образи всіх шести сервісів, а повторне оновлення завершилося UPDATE PASS ✅

Залишилося перевірити, чи збережена версія справді повернеться, якщо нова не запрацює. Для цього вже навмисно підготував поламаний запуск. Про нього — наступним постом 🔙
❤1
Один рядок і п’ятнадцять хвилин архітектури 🎮

Гра, над якою працюю як розробник, зараз проходить перевірку на вебплатформі. Тестувальники знайшли баг, виправлення якого мало зайняти один рядок коду.

За задумом, коли антагоніст ловить персонажа, програється катсцена. Після неї гравець має опинитися в безпечному місці й продовжити гру.

Але персонаж з’являвся прямо в руках антагоніста.

Той знову його ловив, катсцена повторювалася, персонаж повертався туди ж. І так, поки не закінчувалися життя.

Для виправлення потрібно було на початку катсцени задати персонажу вже відомі координати безпечного місця. Нічого обчислювати чи шукати не потрібно: викликати метод і передати два готові значення.

Копирсатися самому було лінь, тому доручив Астрі.

Повертаюся через п’ятнадцять хвилин — ще працює. Вирішив подивитися, що там узагалі відбувається 🤔

А там уже ціла система. Агент додав перевірки купи змінних, нові умови в методи й події для передачі сигналів між частинами коду. Частину поведінки персонажа вимикає, його параметрами керує, за змінами стану стежить.

І все це — щоб у потрібний момент змінити положення персонажа.

Пояснив, що достатньо зробити це на початку катсцени. Без усієї цієї конструкції.

У відповідь: «А, ну точно. Це було зайве переускладнення, можна й без цього».

Та невже 😁

Останнім часом часто бачу захоплені розповіді про агентів, які можуть годинами самостійно писати код і збирати цілі проєкти за коротким описом. У мене найдовший безперервний запуск поки був близько сорока хвилин. І після нього теж вистачало помилок, які доводилося виправляти.

Тому сама тривалість роботи мене вже не дуже вражає. За п’ятнадцять хвилин можна знайти складну помилку. А можна обкласти просте виправлення такою кількістю нового коду, що перевіряти доведеться вже його 😅

Добре, що зазирнув через п’ятнадцять хвилин. Ще трохи — і замість виправлення бага отримав би нову ігрову механіку.
❤1
Навмисно зламав запуск, щоб перевірити відкат 🔙

Старі образи для відкату вже зберігаються. Тепер потрібно було перевірити, чи скрипт справді зможе повернути з них робочий gateway.

Щоб перевірити відкат, навмисно зламав запуск gateway у тестовій версії: замість звичайного старту контейнер одразу завершувався з помилкою. Docker міг зібрати образ і створити контейнер, але сам gateway у ньому не працював. Саме таку несправність мала помітити перевірка після оновлення 🧪

Саме її й установив на тестову машину.

Після заміни контейнерів команда запустила перевірку готовності. Вона побачила, що gateway не працює, і замість успішного завершення почався відкат.

Далі скрипт сам:

• повернув попередню версію коду;
• запустив контейнери зі збережених старих образів;
• залишив на місці налаштування, паролі й підключені томи даних;
• повторив перевірку готовності.

Цього разу отримав:

READINESS PASS
ROLLBACK PASS previous_version_running=1


READINESS PASS означає, що відновлена версія пройшла перевірку роботи сервісів, свіжості пулів проксі та запиту через проксі.

previous_version_running=1 — що після відкату працює саме попередня версія.

А фінальний результат самої команди оновлення — усе одно UPDATE FAIL. І це правильно: нову версію встановити не вдалося. Просто тепер ця помилка не залишає мене з непрацюючим gateway 🙂

Є й межа: відкат повертає код та образи, але не перемотує назад вміст Redis. Тому оновлення, які змінюють його конфігурацію або підключення томів, скрипт зараз відхиляє. Для таких змін потрібно окремо продумати збереження й сумісність даних.

У цьому тесті вручну повертати стару версію не довелося. Спеціально встановив поламану, дочекався результату — і знову отримав робочий gateway.

Оце вже помилка оновлення, з якою можна спокійно розбиратися. Без термінового ремонту всього, що щойно працювало 😅
👏3
Оновлення Transparent Gateway — однією командою 🔄

Для переходу на нову версію тепер достатньо запустити з каталогу проєкту:

sudo python3 scripts/tgctl.py update main --timeout 300


Наявні налаштування зберігаються. Тобто після оновлення не потрібно заново вказувати клієнтську мережу, додавати проксі та вводити паролі — gateway продовжить використовувати вже підготовлену конфігурацію 🔑

Команда готує нову версію, замінює контейнери та перевіряє роботу. Якщо нова версія не проходить перевірку — повертає попередню 🔙

По дорозі Docker 29 підкинув проблему зі збереженням старих образів. Виправив пошук, а потім навмисно зламав запуск тестової версії, щоб перевірити відкат. Попередній gateway повернувся без ручного ремонту ✅

Приємно бачити, як проєкт поступово стає інструментом, яким можна користуватися без моїх підказок через плече. Пригоди під час розробки нікуди не поділися, але повторювати їх кожному користувачу вже не обов’язково 😅

Увесь шлях із командами, скрінами та поясненнями зібрав у статті:

Оновлення Transparent Gateway без повторного налаштування

Код проєкту на GitHub

Далі — діагностика. Планую додати команду doctor, яка перевірятиме налаштування, стан сервісів і підключення через проксі та пояснюватиме, на якому етапі виникла проблема.

Щоб після повідомлення про помилку було зрозуміло, де шукати причину, а не доводилося відкривати журнали всіх контейнерів по черзі 🔎
Як добре, що мене ще хтось розуміє 😅

Зашиваюся так, що вже й повідомлення пишу запчастинами. Добре, що хоч хтось збирає їх без інструкції 😂

Нейронки дозволили робити значно більше роботи за той самий час. Те, до чого раніше руки дійшли б через тиждень, тепер уже виконується паралельно з іншими задачами.

Виконаної роботи більше. Помилок в абсолютних числах — теж. Особливо коли встигаєш роздати задачі швидше, ніж перевірити результати 😅

Робота в паралельних проєктах тепер нагадує моє уявлення про життя із СДУГ. Не діагноз, просто асоціація.

Крутиш гайки в одному проєкті — падає база в іншому. Біжиш піднімати базу, бо дав агенту невдалу команду. Дорогою коригуєш агентів у третьому проєкті. Потім перевіряєш оновлення четвертого. До першого повертаєшся з думкою: «Так, а що тут узагалі робив?» 🔄

І так по колу.

А потім іноді вдається підняти голову від помилок і подивитися, скільки всього вже працює. І щиро здивуватися: оце все вдалося зробити за такий короткий час? 🤯

Тому не можу сказати, що з нейронками стало менше роботи. Стало більше можливостей узяти її на себе.

А от перевіряти, чи не набрав зайвого, поки доводиться власною головою. Тією самою, яка вже пише повідомлення запчастинами 😂
Навіщо даним зайвий біт? 🧩

Зараз готуюся до заліків та екзаменів, а отже буде серія розборів фахових речей. Почну з надлишковості.

Цієї теми вже трохи торкався, коли розповідав про формулу Шеннона. Тепер захотілося розібратися, як вона працює на практиці.

Визначення з «Теорії інформації» нічого, крім сонливості, не викликало. Довелося шукати практичні приклади. І знаєте що? Це просто повсюди 😅

Наприклад, серверна оперативна пам’ять використовує ECC — механізм виявлення та виправлення помилок. Для цього разом із даними зберігаються додаткові перевірочні біти.

Беремо вісім бітів даних і додаємо дев’ятий — біт парності. Його значення підбираємо так, щоб загальна кількість одиниць у всіх дев’яти бітах була парною:

Дані       Біт парності
00000000 0
00000001 1
00000011 0
00000111 1


У першому рядку одиниць немає. Нуль — парне число, тому додатковий біт теж нуль.

У другому є одна одиниця. Додаємо ще одну — отримуємо дві.

У третьому вже дві одиниці, нічого додавати не потрібно. У четвертому їх три, тому перевірочний біт дорівнює одиниці — разом буде чотири.

Під час читання даних кількість одиниць перевіряється знову. Якщо вона стала непарною — дані або сам перевірочний біт пошкодилися 🔍

Припустимо, записали:

00000001 | 1


Разом дві одиниці — усе правильно.

А потім один біт змінився, і під час читання отримали:

00000000 | 1


Тепер одиниця лише одна. Парність порушена — отже, сталася помилка.

Але є нюанс: один біт парності не скаже, де саме помилка, і не виправить її. А якщо зміняться два біти, кількість одиниць може знову залишитися парною — перевірка пропустить пошкодження.

Щоб знаходити пошкоджений біт і відновлювати його значення, потрібні складніші перевірки. Як це працює в ECC — розберу наступним постом, теж на конкретних числах.

Оце й був приклад, якого мені не вистачало. Математичне визначення прочитав — і наступного дня вже не пам’ятаю, що там було. А тут сам порахував біти, змінив один і побачив, як знайшлася помилка. Тепер ця інформація зі мною надовго 🙂

Залишилося на заліку пояснити це так само зрозуміло, як нарешті пояснив собі і вам 😅