Блог¶
Ласкаво просимо до мого блогу! Тут ви знайдете статті про DevOps, AWS, хмарну архітектуру та багато іншого.
Останні пости¶
Embedded vs External DB: BoltDB vs etcd trade-offs¶
5 вересня 2026 - Kubernetes тримає стан кластера в etcd - розподіленому key-value сховищі на 3-5 нод з Raft-консенсусом. Shepherd тримає свій у BoltDB - один файл, один виклик bolt.Open() і нуль інфраструктури. Обидва - key-value, і різниця виринає лише тоді, коли щось ламається. Порівняння йде по розгортанню, розміру, транзакціях, реплікації, watch і бекапу, але справжня прірва - саме у watch: у нас це Go-канал у пам'яті з буфером на 64 і select/default, тому рестарт знищує всі підписки, а сплеск подій тихо їх дропає; в etcd watch несе revision, тому клієнт після реконекту догоняє все пропущене. Саме ця різниця й пояснює, чому контролери в нас опитують стан по тікеру, а watch - лише прискорювач, а не джерело істини: виявляється, це не "відсталий" дизайн, а чесно визнана eventual consistency. Чесно про дірки з обох боків: у BoltDB рівно один writer, тому довгий Update() блокує всі інші записи; bolt.Open() бере ексклюзивний file lock, тому standalone і server на одному --data-dir підвішують другий процес; cp гарячого файлу може дати неконсистентну копію (правильно - Tx.WriteTo() всередині read-транзакції); файл не зменшується сам; а забутий довгий View() тримає сторінки і роздуває базу. etcd натомість потребує кворуму, робить два disk sync на кожен запис і буває складнішим за сам кластер. Ширший контекст: etcd сам використовує bbolt як локальний storage engine, тобто кожна нода etcd всередині - той самий BoltDB, поверх якого накручено Raft, MVCC і gRPC; оригінальний boltdb/bolt Бен Джонсон заморозив, і світ перейшов на форк, який підтримує команда etcd; парний розмір кластера etcd витримує не більше відмов, ніж непарний на одиницю менший; дефолтна квота 2 GB переводить кластер у read-only alarm NOSPACE, який треба знімати руками; ErrCompacted означає, що навіть гарантована доставка watch гарантована лише в межах вікна retention; дві мета-сторінки на початку .db файлу - це те, що робить крах посеред комміту безпечним; а k3s возить із собою kine - etcd API поверх SQLite, Postgres або MySQL, тому Kubernetes можна запустити взагалі без etcd. Тридцята частина серії Sheep & Shepherd.
Two-Mode Architecture: server + agent¶
30 серпня 2026 - Один бінарник, три режими: --mode server піднімає control plane, --mode agent - воркер, --mode standalone - обидва в одному процесі. Той самий код, різна гілка в main() - і все, що попередні частини будували окремо (API Server, BoltDB, шедулер, три контролери, агент ноди), нарешті сходиться в одній точці входу. Найцікавіше рішення - у standalone: агент ходить до API Server через HTTP, хоча вони в одному процесі. Прямий виклик Go-функції був би швидшим, але створив би другий code path, який ніхто не тестує, і баги жили б саме там; похід через localhost робить standalone окремим випадком multi-node, а не окремим продуктом. Саме через це агенту потрібен retry на реєстрації - в одному процесі API Server може просто ще не слухати порт, і це чесне відображення того, що в розподіленій системі ніхто не гарантує порядок старту. Чесно про дірки: standalone - це single point of failure (впав процес - впав кластер) і спільна машина, тому контейнер, який з'їв усю пам'ять, забирає з собою control plane; retry обмежений 10 спробами (~27.5с) і здається мовчки; actualAddr[0] індексує рядок без перевірки довжини, тому --addr "" панікує; а switch по --mode не має default, тому --mode serevr стартує, нічого не пише і виходить з кодом 0 - друкарська помилка виглядає точнісінько як успішний запуск. Ширший контекст: Kubernetes віддає ту саму кодову базу чотирма окремими бінарниками, k3s запихає її в один із сабкомандами server/agent, а k0s кладе всередину єдиного статичного файлу ще й containerd з runc; Docker пішов у протилежний бік, розібравши один демон на dockerd/containerd/runc, щоб рестарт демона не вбивав контейнери; control plane Kubernetes бутстрапить сам себе через static pods - kubelet піднімає той API Server, у якому потім реєструється; HTTP через loopback дешевший, ніж звучить, бо в lo MTU 65536 байт і трафік не торкається драйвера; backoff 500ms/1s/1.5s тут лінійний, а не експоненційний, і саме така форма дає thundering herd, щойно агентів стане п'ятдесят; а сама ідея "один бінарник, багато особистостей" стара, як busybox, що обирає поведінку за argv[0]. Двадцять дев'ята частина серії Sheep & Shepherd.
Graceful Shutdown: signals, channels, cleanup¶
22 серпня 2026 - Shepherd запускає з півдесятка goroutines (scheduler, три контролери, API server, agent), і коли прилітає SIGTERM, усі мають зупинитися акуратно. Увесь фокус - один close(stopCh): закритий канал віддає zero value необмежену кількість разів, тому кожна goroutine у своєму select прокидається одночасно. Ось чому close кращий за send (який будить рівно одного слухача) і чому ідіома - це chan struct{} нульового розміру: значення не несе сенсу, важлива лише подія. Чому звичайний канал тут чистіший за context.WithCancel для простого сигналу "все зупиняється" і як <-sigCh блокує main до сигналу в standalone-режимі. Чесно про дірки: ми не чекаємо, поки reconcile завершиться, тому контролер посеред запису в BoltDB можна обірвати; close(stopCh) двічі панікує, тому закривати має рівно один власник; а api.Shutdown(context.Background()) без deadline зависає назавжди на живому keep-alive. Ширший контекст: SIGKILL і SIGSTOP - два сигнали, які не перехопити; terminationGracePeriodSeconds у Kubernetes (30с) і docker stop (10с) - це той самий ввічливий сценарій SIGTERM-очікування-SIGKILL, який наш close імітує без таймера; PID 1 повністю ігнорує необроблений SIGTERM - саме заради цього існує tini; net/http має Server.Shutdown ще з Go 1.8; а signal.NotifyContext (Go 1.16) згортає обробник сигналу в context, тому ручний sigCh зникає. Двадцять восьма частина серії Sheep & Shepherd.
Async Scheduling: чому Pod створюється як Pending¶
15 серпня 2026 - sheepctl apply повертає 201 Created за дві мілісекунди, і под, який щойно створено, не запущений - він навіть не запланований. Він Pending, завжди, без винятків. Це не баг, а вся модель відмов: API Server записує об'єкт, кидає go scheduler.SchedulePod() і забуває про нього, а reconcile-loop, що тікає щодві секунди, підбирає все, що ця goroutine загубила. Синхронна альтернатива блокує HTTP-worker на секунди, віддає timeout, коли нод немає, і - гірше - бреше: 200, який обіцяє Running, знецінюється тієї ж миті, коли agent падає посеред запуску. Чому pod.Spec.NodeName - єдине, що відрізняє "scheduler ще не дивився" від "agent ще не запустив", і повний таймлайн від apply до Running (~3.5 секунди, з яких 3 - просто очікування тіку агента). Чесно про дірки: fast-path goroutine і reconcile-loop можуть схопити один под одночасно і призначити його двічі без перевірки-захоплення на NodeName, под, який нікуди не вміщується, висить Pending вічно і мовчки, бо причина ніколи не пишеться в Status, а без Watch API немає wait --for=Ready - тільки sleep і polling. Ширший контекст: шедулер Kubernetes навіть не робить PATCH на под, він POSTить в окремий підресурс /binding, щоб RBAC міг дозволити розміщення, не дозволяючи запис у spec; його крок assume позначає под як розміщений у локальному кеші до того, як bind повернеться, інакше він продав би ту саму пам'ять ноди десять разів, поки летить один запит; Pending - це родина причин, справжня з яких живе в умові PodScheduled=False, а не у фазі; percentageOfNodesToScore міняє якість розміщення на латентність, бо оцінювати 5000 нод задорого; а SLO від SIG Scalability обіцяє старт 99% подів за 5 секунд - із свідомо виключеним pull образу. Двадцять сьома частина серії Sheep & Shepherd.
Label Selectors: як зв'язати ресурси без foreign keys¶
8 серпня 2026 - У SQL ти зв'язуєш таблиці через foreign keys. У Kubernetes зв'язок між ресурсами - через labels. Deployment не має поля "мої поди" - він має selector: "всі поди з labels app=web." matchLabels - це 8 рядків Go, які реалізують AND-логіку над map[string]string, і ці 8 рядків зв'язують Deployments із Pods, Services із Pods і Pods із Nodes через nodeSelector. Чому Kubernetes свідомо відмовився від foreign keys: под ефемерний, народжується і вмирає десятки разів на день із новим UID - посилання за ID протухне миттєво, а labels переживають перестворення. Три типи зв'язків через labels (Deployment→Pods, Service→Pods, Pod→Node), як mergeLabels копіює labels із template і selector на нові поди, і порівняльна таблиця foreign keys vs labels для CRUD-операцій. Чесно про дірки: label matching - повний перебір на кожному reconcile (informers і кеші вирішують це в справжньому Kubernetes), немає валідації labels, а перетин selectors дозволяє двом Deployments воювати за одні поди. Двадцять шоста частина серії Sheep & Shepherd.
Desired State vs Actual State: парадигма Kubernetes¶
1 серпня 2026 - Вся архітектура Kubernetes тримається на одній ідеї: ти описуєш, чого хочеш, а система нескінченно виправляє реальність, поки та не збіжиться. Imperative каже "створи A, потім B, потім C" і не має що відповісти, коли B впаде; declarative каже "має бути три", і наступний цикл просто бачить, що є два. Три контролери в Shepherd - replication, service, node - виявляються одним і тим самим тілом observe-compare-act, лише з різними іменниками. Чесно про дірки: eventual consistency означає, що под ніколи не стає Running миттєво, reconcile із порівнянням через == вічно флапає проти дефолтів, які додав сервер, backoff'у немає - тому act, що постійно падає, крутиться на повну, спалюючи CPU і логи, а "самовідновлення" нічого не відновлює: видалений под не повертається, його місце займає інший - з новим ім'ям, новим IP і порожнім диском. Ширший контекст: ідея старша за Kubernetes на два десятиліття (CFEngine 1993, convergent operators, Promise Theory, а вже потім Puppet і Terraform), metadata.generation проти status.observedGeneration - це вбудована відповідь на питання "контролер узагалі бачив мою зміну?", і саме на ній тримається kubectl rollout status, spec і status - фізично різні ендпоінти з різними RBAC-правами, тому контролер не може записати spec, справжні контролери взагалі не опитують API (informer плюс watch, а resync міряється годинами і потрібен лише як страховка від загубленої події), найпоширеніший інцидент у GitOps - це HPA і Argo CD, які по черзі переписують spec.replicas до нескінченності, а kubectl apply колись зберігав увесь твій попередній YAML цілим JSON-рядком в анотації, щоб three-way merge взагалі працював. Двадцять п'ята частина серії Sheep & Shepherd і перша стаття блоку Distributed Systems Patterns.
Push в свій реєстр: layer creation і manifest upload¶
29 липня 2026 - Pull - це завантаження, а push - це збирання OCI-образу з локального rootfs і відправка його в реєстр. Чотири кроки: запакувати rootfs у tar.gz, рахуючи SHA256 стиснутих байтів на льоту, залити його як blob, залити config JSON як ще один blob, а потім PUT маніфест, який їх зв'язує. Чому digest рахується по стиснутих байтах, а config'у потрібен хеш нестиснутого tar (diffID) - два хеші на один шар, і вони ніколи не збігаються. Чесно про дірки: один шар на весь rootfs (тому HEAD-дедуплікація не спрацьовує ніколи), порожні DiffIDs, які відкидають строгіші валідатори типу Harbor чи cosign, відсутній слеш у /blobs/uploads/, на який частина реєстрів відповідає 404, проігнорований 202 Accepted, що означає «ти лише почав» і вилазить через три кроки як MANIFEST_BLOB_UNKNOWN, body у вигляді *os.File, який тихо стає Transfer-Encoding: chunked, бо Go виводить Content-Length лише для byte-readerів, і mtime у tar плюс рівень gzip, через які той самий rootfs хешується двічі по-різному. Ширший контекст: cross-repository blob mount - це справжній ендпоінт (?mount=&from=), і саме він стоїть за Mounted from library/nginx, digest маніфесту рахується по конкретних байтах, тому вивід з відступами ламає pull за digest, sha256:44136fa3... - це просто два символи {}, і OCI 1.1 дав цьому ім'я, щоб SBOM і Helm-чарти могли заповнити обов'язкове поле config, повторний push тега не перезаписує старий маніфест, а осиротює його, спека обмежує маніфест 4 MiB, а overlay2 - 128 шарами, zstd-шари існують тому, що pull впирається у розпакування, а cosign імітував приєднання підписів через імена тегів sha256-<hex>.sig, поки не з'явився Referrers API. У фіналі - повний образ, запушений руками лише через curl, tar і shasum, який потім спокійно тягне docker pull. Двадцять четверта частина серії Sheep & Shepherd.
Pull з registry: auth, manifest lists, multi-arch¶
25 липня 2026 - sheep pull nginx - це одна команда, за якою чотири кроки: розпарсити reference, отримати bearer-токен, звести manifest list до однієї платформи, а потім стягнути й розпакувати шари. Чому nginx насправді library/nginx і як анонімний auth розділяє auth.docker.io і registry-1.docker.io. Чесно про дірки: шари тягнуться послідовно, токен може протухнути посеред pull'у і ніхто його не перезапитує, linux/amd64 захардкоджений (тихий exec format error на Apple Silicon чи Graviton), у порівнянні платформи проігноровано variant, fallback Manifests[0] може вибрати attestation-маніфест, який запустити неможливо, а адреса auth-сервера захардкоджена замість пошуку через WWW-Authenticate - тому приватний GHCR або ECR не працюватиме. Ширший контекст: розкодуй анонімний токен - і побачиш власну квоту всередині нього (pull_limit: 100 на 6 годин, підписані реєстром), Docker Hub віддає blob'и через 307 на підписаний CDN-URL (і працює це лише тому, що Go викидає Authorization при переході на інший хост), половина записів в індексі nginx:latest - це SBOM і provenance від BuildKit із platform: unknown/unknown, Windows-образи несуть foreign-шари, яких реєстр взагалі не зберігає, видалення файлу в шарі - це насправді створення whiteout-файлу .wh., а підписані через JWS маніфести schema 1 викинули на користь звичайного content-addressing. Двадцять третя частина серії Sheep & Shepherd.
Content-Addressable Storage: SHA256 як ключ¶
22 липня 2026 - У Meadow ім'я файлу blob'у - це і є SHA256 його вмісту, і саме це безкоштовно дає дедуплікацію (однакові layer'и зберігаються один раз) і верифікацію (хеш не збігся = дані пошкоджені). Як io.MultiWriter(tmpFile, h) за один прохід одночасно пише на диск і рахує хеш, чому це змушує до танцю temp-файл → atomic os.Rename (фінальне ім'я невідоме, поки невідомий хеш), і як маніфест зберігається двічі - за тегом і за digest - з sidecar-файлом .content-type, бо ФС не тримає MIME-тип. Чесно про мінуси: немає garbage collection (видалений маніфест лишає осиротілі blob'и), os.Rename атомарний лише в межах однієї ФС, і залежність content-addressing від міцного хеша (SHA-1 зламали - тому OCI прив'язаний до SHA256). Ширший контекст: Git робить це з 2005 року, назву дало залізо CAM, Docker перейшов на content-addressable ID образів лише у 1.10, а IPFS/Nix/ZFS масштабують ту саму ідею на мережі, пакети й блоки. Двадцять друга частина серії Sheep & Shepherd.
OCI Distribution Spec: пишемо свій Docker Registry¶
18 липня 2026 - Docker Registry - це просто HTTP-сервер із конкретним набором endpoints: Meadow реалізує OCI Distribution Spec за 370 рядків одним /v2/-хендлером, що маршрутизує за шляхом. Blob upload із перевіркою digest після запису (не збігся → видалення і помилка), manifest push/pull за тегом і digest, перелічений формат помилок OCI (BLOB_UNKNOWN, MANIFEST_UNKNOWN, DIGEST_INVALID), який клієнти парсять за кодом, а не повідомленням, і чому маршрутизація через LastIndex виживає з іменами репо зі слешами. Де ламається: monolithic-only upload тримає весь blob у пам'яті, немає chunked PATCH, немає пагінації. Порівняння з ширшою екосистемою: /v2/ як хендшейк v1→v2, що пережив Registry v1, специфікація, витягнута з Docker HTTP API v2, 202 Accepted у значенні "продовжуй", розподіл transport-vs-image-spec, який дозволяє тому самому API возити Helm-чарти й SBOM'и (OCI artifacts / ORAS), cross-repository blob mount, і чому продакшн пінить за digest, а не за тегом. Двадцять перша частина серії Sheep & Shepherd.
Event System: audit trail для кластера¶
15 липня 2026 - Коли щось відбувається в кластері, потрібно знати що, коли і чому. Як Event system Shepherd-а записує Normal і Warning події від API Server, Scheduler, NodeController і ReplicationController - з ключем із nanosecond timestamp для безкоштовного хронологічного порядку в BoltDB. Чому events прив'язані до об'єктів (pod/web-0), а не до процесів, і чим це відрізняється від stdout-логів. Порівняння зі справжнім Kubernetes: TTL 1 година й агрегація з count, окремий Audit Logging для безпеки, involvedObject і kubectl describe, міграція на events.k8s.io/v1, і чому Events - це сповіщення, а не Event Sourcing. Фінальна частина серії Orchestrator. Двадцята частина серії Sheep & Shepherd.
Pod Lifecycle: від Pending до Running¶
11 липня 2026 - Повний асинхронний шлях поду від sheepctl apply до Running - через API Server, Scheduler, Agent і runtime, де жоден компонент ніколи не чекає на інший. Чому той самий Pending означає "чекає на Scheduler" без NodeName і "чекає на Agent" - з ним, як eventual consistency (та сама модель, що й у git) робить латентність у 5-15 секунд прогнозованою сумою незалежних ticker-ів, а не багом, чому перехід Running → Failed односторонній, і як health-check loop переводить под у Failed, коли контейнер зупиняється. Порівняння зі справжнім Kubernetes: phase як навмисно груба абстракція проти справжньої деталізації в conditions, фаза Pulling, де поди зависають на повільному registry, initContainers, RestartPolicy і CrashLoopBackOff, terminationGracePeriodSeconds, і чому readiness/liveness probes - це те, чого бракує. Дев'ятнадцята частина серії Sheep & Shepherd.
Node Agent: kubelet¶
8 липня 2026 - Agent - це kubelet Shepherd-а: реєстрація з retry, heartbeats кожні 10 секунд, reconciliation подів кожні 3 секунди і запуск контейнерів для призначених подів. Структура, детекція ресурсів з /proc/meminfo, reconcile loop, що переводить Pending у Running, і чому окремий IP на контейнер у Shepherd - спрощення порівняно зі спільним network namespace у Kubernetes. Порівняння зі справжнім Kubernetes: CRI замість прямих викликів runtime, pause infra container, syncLoop і PLEG, static pods, TLS bootstrap і чому 110 max-pods - це навантаження на control plane. Вісімнадцята частина серії Sheep & Shepherd.
Node Health: heartbeat і failure detection¶
4 липня 2026 - Як control plane знає, що нода жива? Через heartbeats - агент шле його кожні 10 секунд, і якщо 30 секунд тиша, NodeController переводить ноду в NotReady, а scheduler перестає ставити на неї поди. Дві сторони механізму (агент, що пише "я живий", і окремий контролер, що вирішує "ти мертвий"), чому heartbeat - це не перевірка живості, а домовленість про timeout, проблема split-brain, коли губиться лише heartbeat, і пастки clock skew та last-write-wins. Порівняння зі справжнім Kubernetes: об'єкт Lease, що замінив переписування повного статусу, taint-based виселення з обмеженням швидкості, і як Φ Accrual detector та SWIM gossip виходять за межі єдиного захардкодженого timeout. За зразком heartbeat-ів нод Kubernetes. Сімнадцята частина серії Sheep & Shepherd.
Service Discovery: як Service знаходить Pods¶
1 липня 2026 - Поди приходять і йдуть, їхні IP змінюються при кожному рестарті, а Service є стабільною точкою входу, яка завжди знає, де потрібні поди. Як ServiceController кожні 5 секунд перебудовує список endpoints через label selectors, чому под має збігтися лейблами, бути Running і мати IP, щоб потрапити в список, і чому Service - це насправді просто запис у базі, який хтось перераховує, а не проксі. Порівняння зі справжнім Kubernetes: ClusterIP як віртуальний IP, якого немає на жодному інтерфейсі, режими iptables/IPVS у kube-proxy, EndpointSlice, headless-сервіси та альтернатива на eBPF (Cilium). За зразком Kubernetes Services. Шістнадцята частина серії Sheep & Shepherd.
Replication Controller: scale up і scale down¶
27 червня 2026 - Deployment каже "хочу 3 репліки" - як ReplicationController це забезпечує. Створення подів з template, слабкий зв'язок через label-selector app=web замість списку власних подів, scale up і scale down, оновлення ReadyReplicas і весь асинхронний шлях від sheepctl до запущеного контейнера. Чому індексні імена (web-0) і перетин селекторів кусаються, і де тут місце kubectl scale, HorizontalPodAutoscaler та scale-to-zero. За зразком Kubernetes ReplicaSet. П'ятнадцята частина серії Sheep & Shepherd.
Reconciliation Loop: серце Shepherd¶
24 червня 2026 - Reconciliation loop - серце будь-якого оркестратора: ти описуєш бажаний стан, а контролери постійно звіряють його з реальним і виправляють різницю. Паттерн observe-compare-act, чому він робить систему самовідновлюваною, три контролери, що працюють паралельно в Shepherd, ідемпотентність як вимога виживання і чому level-triggered кращий за edge-triggered. За зразком контролерів Kubernetes. Чотирнадцята частина серії Sheep & Shepherd.
Scheduler: як вибрати ноду для поду¶
20 червня 2026 - Свіжостворений под - Pending і без ноди. Як шедулер Shepherd вибирає найкращу ноду у дві фази: filter (відсіяти непридатні) і score (віддати перевагу найменш завантаженій). Перевірка ресурсів, збіг лейблів, скоринг least-loaded і чому под лишається Pending навіть після планування. За зразком шедулера Kubernetes. Тринадцята частина серії Sheep & Shepherd.
BoltDB замість etcd: embedded state store¶
17 червня 2026 - Kubernetes тримається на etcd, але для навчального проекту це overkill. Як Shepherd зберігає весь стан кластера в BoltDB - вбудованому key-value сховищі в одному файлі. Buckets як таблиці, ключі з namespace, транзакції на читання/запис, watch-канали для сповіщень про зміни і журнал подій. Чому це ідеально для навчання і де воно поступається etcd. Дванадцята частина серії Sheep & Shepherd.
Kubernetes API Server за 300 рядків¶
13 червня 2026 - Kubernetes API Server - центр всього кластера. Як Shepherd реалізує REST API з CRUD для pods, services, deployments та nodes за ~300 рядків Go, використовуючи тільки net/http. Асинхронне планування, namespaced ресурси та logging middleware. Одинадцята частина серії Sheep & Shepherd.
Docker CLI за 500 рядків Go¶
10 червня 2026 - Subcommand routing, flag parsing і форматований вивід - все без CLI-фреймворків. Як Sheep реалізує run, ps, stop, rm та ще 11 Docker-подібних команд в одному файлі тільки на стандартній бібліотеці Go. Десята частина серії Sheep & Shepherd.
Container Lifecycle: state machine від Created до Removed¶
7 червня 2026 - Контейнер проходить три стани - created, running, stopped. Як state machine керує Create/Start/Stop/Remove, чому кожен перехід зберігається в state.json, і як signal 0 перевіряє, чи пережив контейнер рестарт демона. Дев'ята частина серії Sheep & Shepherd.
Image Management: tar-архів → rootfs → контейнер¶
2 червня 2026 - Образ контейнера - це по суті архів із файловою системою. Як tar-архів перетворюється на rootfs через import і bootstrap, як OCI whiteout-файли видаляють файли між шарами, і чому Sheep зберігає повний rootfs замість шарів. Восьма частина серії Sheep & Shepherd.
NAT і iptables: як контейнер бачить інтернет¶
30 травня 2026 - Адреса контейнера 10.20.0.x приватна - її не маршрутизує жоден роутер. Як ip_forward і одне правило MASQUERADE дають пакетам вийти в інтернет і повернутися назад через conntrack. Сьома частина серії Sheep & Shepherd.
Bridge Networking: даємо контейнеру IP-адресу¶
25 травня 2026 - Контейнер у новому network namespace не має мережі - навіть loopback не піднятий. Як Linux bridge, veth пари і трохи NAT дають йому IP і маршрут в інтернет. Шоста частина серії Sheep & Shepherd.
OverlayFS: copy-on-write шари як у Docker¶
20 травня 2026 - Як OverlayFS складає read-only шар образу і per-container read-write шар в одну файлову систему, і чому copy-up дозволяє 10 контейнерам nginx ділити 100MB замість того, щоб кожен тягнув свою копію. П'ята частина серії Sheep & Shepherd.
Cgroups v2: обмежуємо пам'ять, CPU та PIDs¶
15 травня 2026 - Namespace'и ізолюють, але не обмежують. Як memory.max, cpu.max і pids.max накладають ліміти на ресурси контейнера через віртуальну файлову систему cgroups v2 - четверта частина серії Sheep & Shepherd.
pivot_root: як контейнер отримує свою файлову систему¶
9 травня 2026 - Як pivot_root(2) міняє кореневу директорію процесу на рівні mount namespace, і чому це правильний примітив ізоляції замість chroot. Третя частина серії Sheep & Shepherd.
Re-Exec Pattern: чому Go і clone() не дружать¶
2 травня 2026 - Go threading model конфліктує з clone(). Self re-exec вирішує проблему - друга частина серії Sheep & Shepherd.
Linux Namespaces: ізолюємо процес за 50 рядків Go¶
28 квітня 2026 - Контейнер - це процес з обмеженим видом на систему. Як ізолювати процес за допомогою Linux namespaces за 50 рядків Go - перша частина серії Sheep & Shepherd.
AI-суверенітет: своя модель на DGX Spark замість API¶
18 квітня 2026 - Як я перестав платити OpenAI і переніс інференс на власну коробку DGX Spark з vLLM. Про залізо, проблеми з CUDA/PyTorch, порівняння з Ollama і простий веб-інтерфейс для керування.
EMM: трейси LangGraph у Phoenix¶
11 квітня 2026 - Один виклик при старті покриває 15 LangGraph-агентів. Ручні span-и розширюють покриття на voice (Gemini Live tools), avatar (Runway сесії) та Izabella chat (OpenAI/Ollama/Google + MCP tool loop).
A2A Inspector у моєму application і MCP до нього¶
8 квітня 2026 - Вбудований UI плюс MCP-сервер: перегляд Agent Card, tasks/submit і tasks/status з IDE без виходу з монорепи.
EMM A2A Phase 4: Auth, Rate Limiting, Observability¶
4 квітня 2026 - X-API-Key, rate limiting, структуроване логування. A2A endpoints тепер захищені як інші API.
AI Reliability Engineering: сертифікація від fwdays¶
31 березня 2026 - Завершив курс AI Reliability Engineering від fwdays. Чому надійність AI-систем для мене в одному ряду з класичним SRE.
EMM A2A Phase 3: Stream task status¶
28 березня 2026 - SSE замість polling. GET /api/a2a/tasks/{id}/stream. Теорія, схеми, capabilities.streaming.
EMM A2A Phase 2+: TaskStore і tasks/status¶
21 березня 2026 - Task lifecycle в A2A: submit → taskId → poll status. InMemoryTaskStore, TTL 1 год. Схеми.
Технологічні ризики у production: як не програти, коли проект застаріє¶
14 березня 2026 - PostgreSQL, MinIO, lakeFS: коли вибираєш технології для production, думай не тільки про features - що буде через 5 років?
EMM A2A Phase 2: Task Manager (list_board)¶
14 березня 2026 - Другий skill - list_board. Маршрутизація по skillId, схеми взаємодії, що змінилось.
EMM A2A Phase 1: Process Manager як A2A сервер¶
7 березня 2026 - Process Manager - перший агент з A2A інтерфейсом. Теорія протоколу, діаграми взаємодії, що реалізовано.
Як я став AWS Community Builder¶
5 березня 2026 - Кілька років тому це був звичайний DevOps - деплої, скрипти. Потім почав думати кластерами замість серверів. Ось як це привело до AWS Community Builders.
Розробка та тестування AI-агентів: від LangGraph до production¶
12 лютого 2026 - Як писати, тестувати і дебажити LangGraph агентів? Які patterns працюють для StateGraph? Чому pytest fixtures критичні? Розповідаю про development workflow від першого коду до production deployment.
Версіонування даних в AI-агентах: практичний досвід з lakeFS¶
8 лютого 2026 - Коли AI-агенти починають класифікувати і переміщувати твої файли, питання 'а як відкотити назад?' стає дуже практичним. Розповідаю про інтеграцію lakeFS - системи версіонування для об'єктного сховища.
Kubernetes deployment для AI-агентів: практичний досвід з LangGraph¶
3 лютого 2026 - Коли AI-агенти переїжджають з локального Docker Compose в Kubernetes, виникають питання про service discovery, кеш-інг, secrets management. Розповідаю як я задеплоїв 7 microservices з мінімальним downtime.
Створення агента - мультисайтового парсера оголошень квартир: Патерни проєктування та архітектура¶
Tarot AI Agent: Інноваційний підхід до оцінки ризиків через штучний інтелект¶
September 11, 2025 - Tarot AI Agent: Інноваційний підхід до оцінки ризиків через штучний інтелект.
Використання AWS ECR як універсального OCI-репозиторію¶
10 липня 2025 - Використання AWS ECR як універсального OCI-репозиторію для зберігання різних типів артефактів.
Використання AWS ECR як універсального OCI-репозиторію (УК)¶
10 липня 2025 - Використання AWS ECR як універсального OCI-репозиторію для зберігання різних типів артефактів (українська версія).
Cert Manager в Kubernetes¶
3 червня 2025 - Налаштування та використання Cert Manager для автоматичного управління SSL сертифікатами в Kubernetes.
Cert Manager в Kubernetes (УК)¶
3 червня 2025 - Налаштування та використання Cert Manager для автоматичного управління SSL сертифікатами в Kubernetes (українська версія).
Нова архітектура для Ізабелли - C4 діаграми¶
14 травня 2025 - Розробка нової архітектури системи з використанням C4 діаграм.
Нова архітектура для Ізабелли - Структура¶
14 травня 2025 - Детальний аналіз структури нової архітектури Ізабелли.
Нова архітектура для Ізабелли¶
10 травня 2025 - Огляд нового дизайну архітектури для проекту Ізабелла.
Waste Resources - Фінансова оптимізація в хмарі¶
14 квітня 2025 - Аналіз та оптимізація витрат на хмарні ресурси.
Redis Backup на AWS S3¶
19 березня 2025 - Автоматизація резервного копіювання Redis на AWS S3.
EMM A2A Phase 1 (EN)¶
March 7, 2026 - Process Manager is the first agent with an A2A interface. Protocol theory, interaction diagrams, what's implemented.
RDS Import з Terraform¶
17 лютого 2025 - Імпорт існуючих RDS інстансів в Terraform.
RDS Import з Terraform (EN)¶
17 лютого 2025 - Імпорт існуючих RDS інстансів в Terraform (англійська версія).
AWS Lambda Cost Optimization¶
26 лютого 2025 - Стратегії оптимізації витрат на AWS Lambda.
Kubernetes Onboarding з Flux¶
10 березня 2025 - Автоматизація онбордингу в Kubernetes з використанням Flux.
RDS Migration Cases¶
9 лютого 2025 - Різні сценарії міграції баз даних на AWS RDS.
SPA Deployment на S3 з CloudFront¶
12 лютого 2025 - Розгортання Single Page Application на AWS S3 з CloudFront.
Karpenter Properties¶
8 грудня 2024 - Властивості та налаштування Karpenter для Kubernetes.
AI Stable Diffusion¶
8 лютого 2025 - Використання Stable Diffusion для генерації зображень.
ARM vs AMD¶
5 грудня 2024 - Порівняння ARM та AMD архітектур для хмарних рішень.
My First Blog Post¶
27 квітня 2024 - Привітання та вступ до блогу.