[{"data":1,"prerenderedAt":16599},["ShallowReactive",2],{"navigation_docs":3,"-tools-kubernetes-deployment-basics":3099,"-tools-kubernetes-deployment-basics-surround":16594},[4,1657,1810,2264,2445,2652,2774,2824,2881,2915,3041,3058,3095],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","/csharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1346,1636,1653],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp/index",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","/csharp/fundamentals","01.csharp/01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","/csharp/fundamentals/introduction-to-ecosystem","01.csharp/01.fundamentals/01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","/csharp/fundamentals/program-structure","01.csharp/01.fundamentals/02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","/csharp/fundamentals/variables-data-types","01.csharp/01.fundamentals/03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","/csharp/fundamentals/arrays","01.csharp/01.fundamentals/04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","/csharp/fundamentals/strings-text-handling","01.csharp/01.fundamentals/05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","/csharp/fundamentals/dates-time-handling","01.csharp/01.fundamentals/06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","/csharp/fundamentals/control-flow","01.csharp/01.fundamentals/07.control-flow",{"title":48,"path":49,"stem":50},"Методи","/csharp/fundamentals/methods","01.csharp/01.fundamentals/08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","/csharp/fundamentals/debugging-basics","01.csharp/01.fundamentals/09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","/csharp/fundamentals/interactive-console","01.csharp/01.fundamentals/10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","/csharp/oop","01.csharp/02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","/csharp/oop/package-management","01.csharp/02.oop/01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","/csharp/oop/classes-objects","01.csharp/02.oop/02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","/csharp/oop/properties-fields","01.csharp/02.oop/03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","/csharp/oop/oop-pillars","01.csharp/02.oop/04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","/csharp/oop/advanced-types","01.csharp/02.oop/05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","/csharp/oop/namespaces","01.csharp/02.oop/06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","/csharp/advanced-core","01.csharp/03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","/csharp/advanced-core/generics","01.csharp/03.advanced-core/01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","/csharp/advanced-core/delegates-events-lambdas","01.csharp/03.advanced-core/02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","/csharp/advanced-core/interfaces-deep-dive","01.csharp/03.advanced-core/03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","/csharp/advanced-core/exception-handling","01.csharp/03.advanced-core/04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","/csharp/advanced-core/pattern-matching","01.csharp/03.advanced-core/05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","/csharp/advanced-core/additional-features","01.csharp/03.advanced-core/06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","/csharp/architecture-best-practices","01.csharp/04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","/csharp/architecture-best-practices/software-design-principles","01.csharp/04.architecture-best-practices/01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","/csharp/architecture-best-practices/design-patterns","01.csharp/04.architecture-best-practices/02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","/csharp/architecture-best-practices/design-patterns/creational","01.csharp/04.architecture-best-practices/02.design-patterns/creational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","/csharp/architecture-best-practices/design-patterns/creational/singleton","01.csharp/04.architecture-best-practices/02.design-patterns/creational/01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","/csharp/architecture-best-practices/design-patterns/creational/builder","01.csharp/04.architecture-best-practices/02.design-patterns/creational/02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","/csharp/architecture-best-practices/building-professional-clis","01.csharp/04.architecture-best-practices/03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","/csharp/architecture-best-practices/validation-flow-control","01.csharp/04.architecture-best-practices/04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","/csharp/architecture-best-practices/modern-dotnet-host","01.csharp/04.architecture-best-practices/05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","/csharp/architecture-best-practices/data-mapper-part1","01.csharp/04.architecture-best-practices/06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","/csharp/architecture-best-practices/data-mapper-part2","01.csharp/04.architecture-best-practices/07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","/csharp/architecture-best-practices/di-ioc","01.csharp/04.architecture-best-practices/08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","/csharp/architecture-best-practices/di-ioc/the-dependency-problem","01.csharp/04.architecture-best-practices/08.di-ioc/01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","/csharp/architecture-best-practices/di-ioc/build-your-own-container","01.csharp/04.architecture-best-practices/08.di-ioc/02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","/csharp/architecture-best-practices/di-ioc/service-locator-pattern","01.csharp/04.architecture-best-practices/08.di-ioc/03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","/csharp/architecture-best-practices/di-ioc/dependency-injection-patterns","01.csharp/04.architecture-best-practices/08.di-ioc/04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","/csharp/architecture-best-practices/di-ioc/microsoft-di-deep-dive","01.csharp/04.architecture-best-practices/08.di-ioc/05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","/csharp/architecture-best-practices/di-ioc/service-lifetimes-and-scopes","01.csharp/04.architecture-best-practices/08.di-ioc/06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","/csharp/architecture-best-practices/di-ioc/di-anti-patterns-and-best-practices","01.csharp/04.architecture-best-practices/08.di-ioc/07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","/csharp/standard-library","01.csharp/05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","/csharp/standard-library/collections","01.csharp/05.standard-library/01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","/csharp/standard-library/high-performance-types","01.csharp/05.standard-library/02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","/csharp/standard-library/linq","01.csharp/05.standard-library/03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","/csharp/system-internals-concurrency","01.csharp/06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","/csharp/system-internals-concurrency/memory-management","01.csharp/06.system-internals-concurrency/01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","/csharp/system-internals-concurrency/reflection-fundamentals","01.csharp/06.system-internals-concurrency/02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","/csharp/system-internals-concurrency/attributes-dynamic","01.csharp/06.system-internals-concurrency/03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","/csharp/system-internals-concurrency/expression-trees-compiled","01.csharp/06.system-internals-concurrency/04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","/csharp/system-internals-concurrency/source-generators","01.csharp/06.system-internals-concurrency/05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","/csharp/system-internals-concurrency/multithreading-fundamentals","01.csharp/06.system-internals-concurrency/06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","/csharp/system-internals-concurrency/synchronization-primitives","01.csharp/06.system-internals-concurrency/07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","/csharp/system-programming-windows","01.csharp/07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","/csharp/system-programming-windows/how-os-works","01.csharp/07.system-programming-windows/01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","/csharp/system-programming-windows/processes-in-dotnet","01.csharp/07.system-programming-windows/02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","/csharp/system-programming-windows/02a.processes-ipc","01.csharp/07.system-programming-windows/02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","/csharp/system-programming-windows/appdomains-assemblies","01.csharp/07.system-programming-windows/03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","/csharp/system-programming-windows/03a.appdomains-plugin-system","01.csharp/07.system-programming-windows/03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","/csharp/system-programming-windows/thread-fundamentals","01.csharp/07.system-programming-windows/04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","/csharp/system-programming-windows/04a.thread-lifecycle-priorities","01.csharp/07.system-programming-windows/04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","/csharp/system-programming-windows/shared-state-problems","01.csharp/07.system-programming-windows/05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","/csharp/system-programming-windows/05a.shared-state-memory-model","01.csharp/07.system-programming-windows/05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","/csharp/system-programming-windows/synchronization-fundamentals","01.csharp/07.system-programming-windows/06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","/csharp/system-programming-windows/06a.synchronization-walkthrough","01.csharp/07.system-programming-windows/06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","/csharp/system-programming-windows/synchronization-advanced","01.csharp/07.system-programming-windows/07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","/csharp/system-programming-windows/07a.synchronization-advanced-walkthrough","01.csharp/07.system-programming-windows/07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","/csharp/system-programming-windows/interlocked-cas-lockfree","01.csharp/07.system-programming-windows/08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","/csharp/system-programming-windows/08a.volatile-memory-model","01.csharp/07.system-programming-windows/08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","/csharp/system-programming-windows/thread-pool","01.csharp/07.system-programming-windows/09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","/csharp/system-programming-windows/09a.thread-pool-advanced","01.csharp/07.system-programming-windows/09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","/csharp/system-programming-windows/concurrent-collections","01.csharp/07.system-programming-windows/10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","/csharp/system-programming-windows/tpl-parallel-plinq","01.csharp/07.system-programming-windows/11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","/csharp/system-programming-windows/11a.tpl-parallel-plinq-advanced","01.csharp/07.system-programming-windows/11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async/Await — Фундамент Асинхронного Програмування","/csharp/system-programming-windows/async-fundamentals","01.csharp/07.system-programming-windows/12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","/csharp/system-programming-windows/async-context-configureawait","01.csharp/07.system-programming-windows/13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","/csharp/system-programming-windows/async-advanced","01.csharp/07.system-programming-windows/14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","/csharp/system-programming-windows/channels","01.csharp/07.system-programming-windows/15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","/csharp/system-programming-windows/async-synchronization","01.csharp/07.system-programming-windows/16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","/csharp/system-programming-windows/unsafe-code","01.csharp/07.system-programming-windows/17.unsafe-code",{"title":364,"path":365,"stem":366},"P/Invoke та Windows API — Міст між .NET та Native Code","/csharp/system-programming-windows/pinvoke-winapi","01.csharp/07.system-programming-windows/18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","/csharp/system-programming-windows/windows-registry","01.csharp/07.system-programming-windows/19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","/csharp/system-programming-windows/windows-hooks-services","01.csharp/07.system-programming-windows/20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","/csharp/system-programming-windows/implementation_plan","01.csharp/07.system-programming-windows/implementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","/csharp/io","01.csharp/08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","/csharp/io/file-system-basics","01.csharp/08.io/01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","/csharp/io/streams-serialization","01.csharp/08.io/02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","/csharp/io/json-serialization","01.csharp/08.io/03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","/csharp/io/xml-serialization","01.csharp/08.io/04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","/csharp/io/binary-serialization","01.csharp/08.io/05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","/csharp/ado-net","01.csharp/09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","/csharp/ado-net/introduction-to-adonet","01.csharp/09.ado-net/01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","/csharp/ado-net/connection","01.csharp/09.ado-net/02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","/csharp/ado-net/command-and-queries","01.csharp/09.ado-net/03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","/csharp/ado-net/datareader","01.csharp/09.ado-net/04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","/csharp/ado-net/parameters-and-sql-injection","01.csharp/09.ado-net/05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","/csharp/ado-net/transactions","01.csharp/09.ado-net/06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","/csharp/ado-net/provider-factory","01.csharp/09.ado-net/07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","/csharp/ado-net/async-data-access","01.csharp/09.ado-net/08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","/csharp/ado-net/disconnected-mode-dataset","01.csharp/09.ado-net/09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","/csharp/ado-net/data-adapter","01.csharp/09.ado-net/10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","/csharp/ado-net/data-mapper-repository","01.csharp/09.ado-net/11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","/csharp/ado-net/advanced-patterns","01.csharp/09.ado-net/12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","/csharp/ef-core","01.csharp/10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","/csharp/ef-core/what-is-orm","01.csharp/10.ef-core/01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","/csharp/ef-core/first-project","01.csharp/10.ef-core/02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","/csharp/ef-core/dbcontext-deep-dive","01.csharp/10.ef-core/03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","/csharp/ef-core/database-providers","01.csharp/10.ef-core/04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","/csharp/ef-core/conventions","01.csharp/10.ef-core/05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","/csharp/ef-core/fluent-api-vs-annotations","01.csharp/10.ef-core/06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","/csharp/ef-core/relationships-basics","01.csharp/10.ef-core/07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","/csharp/ef-core/relationships-advanced","01.csharp/10.ef-core/08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","/csharp/ef-core/property-configuration-part1","01.csharp/10.ef-core/09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","/csharp/ef-core/property-configuration-part2","01.csharp/10.ef-core/09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","/csharp/ef-core/complex-types-owned-part1","01.csharp/10.ef-core/10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","/csharp/ef-core/complex-types-owned-part2","01.csharp/10.ef-core/10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","/csharp/ef-core/json-columns-part1","01.csharp/10.ef-core/11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","/csharp/ef-core/json-columns-part2","01.csharp/10.ef-core/11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","/csharp/ef-core/inheritance-part1","01.csharp/10.ef-core/12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","/csharp/ef-core/inheritance-part2","01.csharp/10.ef-core/12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","/csharp/ef-core/indexes-constraints-part1","01.csharp/10.ef-core/13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","/csharp/ef-core/indexes-constraints-part2","01.csharp/10.ef-core/13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","/csharp/ef-core/seeding-part1","01.csharp/10.ef-core/14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","/csharp/ef-core/seeding-part2","01.csharp/10.ef-core/14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","/csharp/ef-core/global-query-filters-part1","01.csharp/10.ef-core/15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","/csharp/ef-core/global-query-filters-part2","01.csharp/10.ef-core/15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","/csharp/ef-core/linq-queries-part1","01.csharp/10.ef-core/16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","/csharp/ef-core/linq-queries-part2","01.csharp/10.ef-core/16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","/csharp/ef-core/loading-related-data-part1","01.csharp/10.ef-core/17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","/csharp/ef-core/loading-related-data-part2","01.csharp/10.ef-core/17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","/csharp/ef-core/raw-sql-part1","01.csharp/10.ef-core/18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","/csharp/ef-core/raw-sql-part2","01.csharp/10.ef-core/18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","/csharp/ef-core/advanced-queries-part1","01.csharp/10.ef-core/19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","/csharp/ef-core/advanced-queries-part2","01.csharp/10.ef-core/19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","/csharp/ef-core/change-tracking-part1","01.csharp/10.ef-core/20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","/csharp/ef-core/change-tracking-part2","01.csharp/10.ef-core/20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","/csharp/ef-core/saving-data-part1","01.csharp/10.ef-core/21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","/csharp/ef-core/saving-data-part2","01.csharp/10.ef-core/21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","/csharp/ef-core/concurrency-part1","01.csharp/10.ef-core/22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","/csharp/ef-core/concurrency-part2","01.csharp/10.ef-core/22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","/csharp/ef-core/migrations-basics-part1","01.csharp/10.ef-core/23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","/csharp/ef-core/migrations-basics-part2","01.csharp/10.ef-core/23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","/csharp/ef-core/migrations-advanced-part1","01.csharp/10.ef-core/24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","/csharp/ef-core/migrations-advanced-part2","01.csharp/10.ef-core/24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","/csharp/ef-core/schema-management-part1","01.csharp/10.ef-core/25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","/csharp/ef-core/schema-management-part2","01.csharp/10.ef-core/25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","/csharp/ef-core/performance-fundamentals-part1","01.csharp/10.ef-core/26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","/csharp/ef-core/interceptors-part1","01.csharp/10.ef-core/29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","/csharp/ef-core/interceptors-part2","01.csharp/10.ef-core/29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","/csharp/ef-core/implementation_plan","01.csharp/10.ef-core/implementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","/csharp/aspnet","01.csharp/11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","/csharp/aspnet/minimal-api","01.csharp/11.aspnet/01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","/csharp/aspnet/minimal-api/introduction","01.csharp/11.aspnet/01.minimal-api/01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","/csharp/aspnet/minimal-api/first-application","01.csharp/11.aspnet/01.minimal-api/02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","/csharp/aspnet/minimal-api/webapplication-builder","01.csharp/11.aspnet/01.minimal-api/03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","/csharp/aspnet/minimal-api/request-pipeline-middleware","01.csharp/11.aspnet/01.minimal-api/04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","/csharp/aspnet/minimal-api/routing-basics","01.csharp/11.aspnet/01.minimal-api/05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","/csharp/aspnet/minimal-api/routing-advanced","01.csharp/11.aspnet/01.minimal-api/06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","/csharp/aspnet/minimal-api/static-files","01.csharp/11.aspnet/01.minimal-api/07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","/csharp/aspnet/minimal-api/static-assets","01.csharp/11.aspnet/01.minimal-api/08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","/csharp/aspnet/minimal-api/configuration-fundamentals","01.csharp/11.aspnet/01.minimal-api/09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","/csharp/aspnet/minimal-api/configuration-options","01.csharp/11.aspnet/01.minimal-api/10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","/csharp/aspnet/minimal-api/logging-basics","01.csharp/11.aspnet/01.minimal-api/11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","/csharp/aspnet/minimal-api/logging-advanced","01.csharp/11.aspnet/01.minimal-api/12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","/csharp/aspnet/minimal-api/state-management","01.csharp/11.aspnet/01.minimal-api/13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","/csharp/aspnet/minimal-api/session-state","01.csharp/11.aspnet/01.minimal-api/14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","/csharp/aspnet/minimal-api/project-structure","01.csharp/11.aspnet/01.minimal-api/15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","/csharp/aspnet/minimal-api/scalar-openapi-fluent","01.csharp/11.aspnet/01.minimal-api/16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger / Swashbuckle у Minimal API: окремий класичний шлях","/csharp/aspnet/minimal-api/swagger-swashbuckle","01.csharp/11.aspnet/01.minimal-api/17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","/csharp/aspnet/api","01.csharp/11.aspnet/02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","/csharp/aspnet/api/what-is-api","01.csharp/11.aspnet/02.api/01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","/csharp/aspnet/api/data-formats","01.csharp/11.aspnet/02.api/02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","/csharp/aspnet/api/api-paradigms-rest","01.csharp/11.aspnet/02.api/03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","/csharp/aspnet/api/http-methods-status-codes","01.csharp/11.aspnet/02.api/04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","/csharp/aspnet/api/rest-organizing","01.csharp/11.aspnet/02.api/05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","/csharp/aspnet/api/url-nomenclature-crud","01.csharp/11.aspnet/02.api/06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","/csharp/aspnet/api/api-design-naming","01.csharp/11.aspnet/02.api/07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","/csharp/aspnet/api/api-design-validation","01.csharp/11.aspnet/02.api/08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","/csharp/aspnet/api/error-handling-http","01.csharp/11.aspnet/02.api/09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","/csharp/aspnet/api/idempotency-sync","01.csharp/11.aspnet/02.api/10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","/csharp/aspnet/api/pagination-lists","01.csharp/11.aspnet/02.api/11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","/csharp/aspnet/api/security-auth","01.csharp/11.aspnet/02.api/12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","/csharp/aspnet/api/api-design-process","01.csharp/11.aspnet/02.api/13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","/csharp/aspnet/api/openapi","01.csharp/11.aspnet/02.api/14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","/csharp/aspnet/auth","01.csharp/11.aspnet/03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","/csharp/aspnet/auth/auth-fundamentals","01.csharp/11.aspnet/03.auth/01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","/csharp/aspnet/auth/jwt-authentication","01.csharp/11.aspnet/03.auth/02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","/csharp/aspnet/auth/authorization-policies","01.csharp/11.aspnet/03.auth/03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","/csharp/aspnet/auth/cookie-auth-identity","01.csharp/11.aspnet/03.auth/04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","/csharp/aspnet/auth/04b.identity-auth-jwt","01.csharp/11.aspnet/03.auth/04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","/csharp/aspnet/auth/identity-email-confirmation","01.csharp/11.aspnet/03.auth/05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","/csharp/aspnet/auth/identity-two-factor","01.csharp/11.aspnet/03.auth/06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","/csharp/aspnet/auth/identity-internals","01.csharp/11.aspnet/03.auth/07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","/csharp/aspnet/auth/oauth-external-providers","01.csharp/11.aspnet/03.auth/08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","/csharp/aspnet/auth/security-hardening","01.csharp/11.aspnet/03.auth/09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","/csharp/aspnet/auth/oauth-theory","01.csharp/11.aspnet/03.auth/10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","/csharp/aspnet/auth/oidc-keycloak","01.csharp/11.aspnet/03.auth/10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","/csharp/aspnet/auth/api-keys","01.csharp/11.aspnet/03.auth/11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","/csharp/aspnet/auth/rate-limiting","01.csharp/11.aspnet/03.auth/12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","/csharp/aspnet/auth/refresh-token-rotation","01.csharp/11.aspnet/03.auth/13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","/csharp/aspnet/auth/certificate-auth","01.csharp/11.aspnet/03.auth/14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","/csharp/aspnet/auth/rbac-abac-rebac","01.csharp/11.aspnet/03.auth/15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","/csharp/aspnet/auth/multi-tenancy","01.csharp/11.aspnet/03.auth/16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","/csharp/aspnet/notifications","01.csharp/11.aspnet/04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","/csharp/aspnet/notifications/in-app-database-notifications","01.csharp/11.aspnet/04.notifications/01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","/csharp/aspnet/notifications/polling","01.csharp/11.aspnet/04.notifications/02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","/csharp/aspnet/notifications/server-sent-events","01.csharp/11.aspnet/04.notifications/03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","/csharp/aspnet/notifications/websockets","01.csharp/11.aspnet/04.notifications/04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","/csharp/aspnet/notifications/signalr","01.csharp/11.aspnet/04.notifications/05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","/csharp/aspnet/notifications/background-services","01.csharp/11.aspnet/04.notifications/06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","/csharp/aspnet/notifications/web-push","01.csharp/11.aspnet/04.notifications/07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","/csharp/aspnet/notifications/email-notifications","01.csharp/11.aspnet/04.notifications/08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","/csharp/aspnet/notifications/choosing-the-right-approach","01.csharp/11.aspnet/04.notifications/09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","/csharp/aspnet/notifications/hangfire","01.csharp/11.aspnet/04.notifications/10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","/csharp/aspnet/notifications/hangfire-image-webp","01.csharp/11.aspnet/04.notifications/11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","/csharp/aspnet/notifications/hangfire-video-hls","01.csharp/11.aspnet/04.notifications/12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","/csharp/aspnet/notifications/telegram-notifications","01.csharp/11.aspnet/04.notifications/13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","/csharp/aspnet/i18n","01.csharp/11.aspnet/05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","/csharp/aspnet/i18n/internationalization","01.csharp/11.aspnet/05.i18n/01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","/csharp/aspnet/i18n/humanizer","01.csharp/11.aspnet/05.i18n/02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","/csharp/aspnet/caching","01.csharp/11.aspnet/06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","/csharp/aspnet/caching/caching","01.csharp/11.aspnet/06.caching/01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","/csharp/aspnet/caching/memory-cache","01.csharp/11.aspnet/06.caching/02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","/csharp/aspnet/caching/distributed-cache","01.csharp/11.aspnet/06.caching/03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","/csharp/aspnet/caching/response-cache","01.csharp/11.aspnet/06.caching/04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","/csharp/aspnet/caching/output-cache","01.csharp/11.aspnet/06.caching/05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","/csharp/aspnet/testing","01.csharp/11.aspnet/07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","/csharp/aspnet/testing/what-is-testing","01.csharp/11.aspnet/07.testing/01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","/csharp/aspnet/testing/testing-pyramid","01.csharp/11.aspnet/07.testing/02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","/csharp/aspnet/testing/testing-schools","01.csharp/11.aspnet/07.testing/03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","/csharp/aspnet/testing/tdd-and-bdd","01.csharp/11.aspnet/07.testing/04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","/csharp/aspnet/testing/what-to-test","01.csharp/11.aspnet/07.testing/05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","/csharp/aspnet/testing/test-frameworks","01.csharp/11.aspnet/07.testing/06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","/csharp/aspnet/testing/xunit-basics","01.csharp/11.aspnet/07.testing/07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","/csharp/aspnet/testing/xunit-advanced","01.csharp/11.aspnet/07.testing/08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","/csharp/aspnet/testing/mocking-with-moq","01.csharp/11.aspnet/07.testing/09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","/csharp/aspnet/testing/database-testing","01.csharp/11.aspnet/07.testing/10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","/csharp/aspnet/testing/integration-testing","01.csharp/11.aspnet/07.testing/11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","/csharp/aspnet/testing/11a.integration-testing-practice","01.csharp/11.aspnet/07.testing/11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","/csharp/aspnet/testing/integration-testing-advanced","01.csharp/11.aspnet/07.testing/12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","/csharp/aspnet/testing/postman-professional","01.csharp/11.aspnet/07.testing/13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","/csharp/aspnet/testing/httpclient-testing","01.csharp/11.aspnet/07.testing/14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","/csharp/aspnet/testing/wiremock-net","01.csharp/11.aspnet/07.testing/15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","/csharp/aspnet/testing/testing-patterns","01.csharp/11.aspnet/07.testing/16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","/csharp/aspnet/testing/advanced-testing-tools","01.csharp/11.aspnet/07.testing/17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","/csharp/aspnet/testing/architecture-testing","01.csharp/11.aspnet/07.testing/18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","/csharp/aspnet/testing/performance-testing","01.csharp/11.aspnet/07.testing/19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","/csharp/aspnet/testing/remaining_plan","01.csharp/11.aspnet/07.testing/remaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","/csharp/aspnet/payments","01.csharp/11.aspnet/08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","/csharp/aspnet/payments/payment-fundamentals","01.csharp/11.aspnet/08.payments/01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","/csharp/aspnet/payments/payment-methods-ukraine","01.csharp/11.aspnet/08.payments/02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","/csharp/aspnet/payments/pci-dss-security","01.csharp/11.aspnet/08.payments/03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","/csharp/aspnet/payments/payment-architecture","01.csharp/11.aspnet/08.payments/04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","/csharp/aspnet/payments/liqpay-integration","01.csharp/11.aspnet/08.payments/05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","/csharp/aspnet/payments/monobank-acquiring","01.csharp/11.aspnet/08.payments/06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","/csharp/aspnet/payments/stripe-integration","01.csharp/11.aspnet/08.payments/07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","/csharp/aspnet/payments/webhooks-deep-dive","01.csharp/11.aspnet/08.payments/08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","/csharp/aspnet/payments/subscriptions-recurring","01.csharp/11.aspnet/08.payments/09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","/csharp/aspnet/payments/refunds-disputes","01.csharp/11.aspnet/08.payments/10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","/csharp/aspnet/payments/testing-payments","01.csharp/11.aspnet/08.payments/11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","/csharp/aspnet/payments/production-checklist","01.csharp/11.aspnet/08.payments/12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","/csharp/aspnet/libraries","01.csharp/11.aspnet/09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","/csharp/aspnet/libraries/fluent-validation","01.csharp/11.aspnet/09.libraries/01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","/csharp/aspnet/libraries/mapster","01.csharp/11.aspnet/09.libraries/02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","/csharp/aspnet/libraries/erroror-result-pattern","01.csharp/11.aspnet/09.libraries/03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","/csharp/aspnet/libraries/serilog","01.csharp/11.aspnet/09.libraries/04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","/csharp/aspnet/libraries/mediatr","01.csharp/11.aspnet/09.libraries/05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","/csharp/aspnet/libraries/polly","01.csharp/11.aspnet/09.libraries/06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","/csharp/aspnet/libraries/health-checks","01.csharp/11.aspnet/09.libraries/07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","/csharp/aspnet/libraries/feature-management","01.csharp/11.aspnet/09.libraries/08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","/csharp/aspnet/libraries/fluent-email","01.csharp/11.aspnet/09.libraries/09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","/csharp/aspnet/libraries/quest-pdf","01.csharp/11.aspnet/09.libraries/10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","/csharp/aspnet/libraries/bogus","01.csharp/11.aspnet/09.libraries/11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","/csharp/aspnet/libraries/humanizer-guard","01.csharp/11.aspnet/09.libraries/12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","/csharp/aspnet/libraries/plan","01.csharp/11.aspnet/09.libraries/plan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","/csharp/aspnet/razor-pages","01.csharp/11.aspnet/10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","/csharp/aspnet/razor-pages/from-minimal-api","01.csharp/11.aspnet/10.razor-pages/01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","/csharp/aspnet/razor-pages/page-model","01.csharp/11.aspnet/10.razor-pages/02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","/csharp/aspnet/razor-pages/razor-syntax","01.csharp/11.aspnet/10.razor-pages/03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","/csharp/aspnet/razor-pages/tag-helpers","01.csharp/11.aspnet/10.razor-pages/04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","/csharp/aspnet/razor-pages/forms-validation","01.csharp/11.aspnet/10.razor-pages/05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","/csharp/aspnet/razor-pages/project-task-manager","01.csharp/11.aspnet/10.razor-pages/06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","/csharp/aspnet/mvc","01.csharp/11.aspnet/11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","/csharp/aspnet/mvc/mvc-pattern","01.csharp/11.aspnet/11.mvc/01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","/csharp/aspnet/mvc/from-razor-pages","01.csharp/11.aspnet/11.mvc/02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","/csharp/aspnet/mvc/controllers-actions","01.csharp/11.aspnet/11.mvc/03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","/csharp/aspnet/mvc/routing-mvc","01.csharp/11.aspnet/11.mvc/04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","/csharp/aspnet/mvc/model-binding","01.csharp/11.aspnet/11.mvc/05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","/csharp/aspnet/mvc/views-viewdata-tempdata","01.csharp/11.aspnet/11.mvc/06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","/csharp/aspnet/mvc/filters","01.csharp/11.aspnet/11.mvc/07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","/csharp/aspnet/mvc/areas","01.csharp/11.aspnet/11.mvc/08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","/csharp/aspnet/mvc/view-components","01.csharp/11.aspnet/11.mvc/09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","/csharp/aspnet/mvc/display-editor-templates","01.csharp/11.aspnet/11.mvc/10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","/csharp/aspnet/mvc/validation-advanced","01.csharp/11.aspnet/11.mvc/11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","/csharp/aspnet/mvc/htmx","01.csharp/11.aspnet/11.mvc/12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","/csharp/aspnet/mvc/ajax-htmx-mvc","01.csharp/11.aspnet/11.mvc/13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","/csharp/aspnet/mvc/htmx-project","01.csharp/11.aspnet/11.mvc/14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","/csharp/aspnet/mvc/file-upload","01.csharp/11.aspnet/11.mvc/15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","/csharp/aspnet/mvc/globalization-localization","01.csharp/11.aspnet/11.mvc/16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","/csharp/aspnet/mvc/mvc-project","01.csharp/11.aspnet/11.mvc/17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","/csharp/aspnet/mvc/plan","01.csharp/11.aspnet/11.mvc/plan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","/csharp/aspnet/web-api","01.csharp/11.aspnet/12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","/csharp/aspnet/web-api/from-minimal-api-to-controllers","01.csharp/11.aspnet/12.web-api/01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","/csharp/aspnet/web-api/controller-base-actionresult","01.csharp/11.aspnet/12.web-api/02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","/csharp/aspnet/web-api/content-negotiation","01.csharp/11.aspnet/12.web-api/03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","/csharp/aspnet/web-api/api-versioning","01.csharp/11.aspnet/12.web-api/04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","/csharp/aspnet/web-api/problemdetails-error-handling","01.csharp/11.aspnet/12.web-api/05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","/csharp/aspnet/web-api/filters-for-api","01.csharp/11.aspnet/12.web-api/06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","/csharp/aspnet/web-api/pagination-filtering-sorting","01.csharp/11.aspnet/12.web-api/07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","/csharp/aspnet/web-api/hateoas-resource-expansion","01.csharp/11.aspnet/12.web-api/08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","/csharp/aspnet/web-api/minimal-api-vs-controllers-hybrid","01.csharp/11.aspnet/12.web-api/09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","/csharp/aspnet/web-api/api-documentation-generation","01.csharp/11.aspnet/12.web-api/10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","/csharp/aspnet/web-api/health-checks-monitoring","01.csharp/11.aspnet/12.web-api/11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","/csharp/aspnet/web-api/web-api-project","01.csharp/11.aspnet/12.web-api/12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","/csharp/aspnet/web-api/plan","01.csharp/11.aspnet/12.web-api/plan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Desktop UI","i-lucide-app-window","/csharp/desktop-ui","01.csharp/12.desktop-ui",[1352,1356,1360,1364,1368,1372,1376,1380,1384,1388,1392,1396,1400,1404,1408,1412,1416,1420,1424,1428,1432,1436,1440,1444,1448,1452,1456,1460,1464,1468,1472,1476,1480,1484,1488,1492,1496,1500,1504,1508,1512,1516,1520,1524,1528,1532,1536,1540,1544,1548,1552,1556,1560,1564,1568,1572,1576,1580,1584,1588,1592,1596,1600,1604,1608,1612,1616,1620,1624,1628,1632],{"title":1353,"path":1354,"stem":1355},"Що таке десктопна розробка?","/csharp/desktop-ui/what-is-desktop-dev","01.csharp/12.desktop-ui/01.what-is-desktop-dev",{"title":1357,"path":1358,"stem":1359},"Архітектура WPF — як влаштований графічний інтерфейс","/csharp/desktop-ui/wpf-architecture","01.csharp/12.desktop-ui/02.wpf-architecture",{"title":1361,"path":1362,"stem":1363},"Перший WPF-проєкт — від нуля до вікна","/csharp/desktop-ui/first-wpf-app","01.csharp/12.desktop-ui/03.first-wpf-app",{"title":1365,"path":1366,"stem":1367},"Перший Avalonia-проєкт: WPF для всіх платформ","/csharp/desktop-ui/03a.first-avalonia-app","01.csharp/12.desktop-ui/03a.first-avalonia-app",{"title":1369,"path":1370,"stem":1371},"XAML: декларативний інтерфейс","/csharp/desktop-ui/xaml-basics","01.csharp/12.desktop-ui/04.xaml-basics",{"title":1373,"path":1374,"stem":1375},"Fluent UI у WPF — сучасний дизайн Windows 11","/csharp/desktop-ui/04a.wpf-fluent-ui","01.csharp/12.desktop-ui/04a.wpf-fluent-ui",{"title":1377,"path":1378,"stem":1379},"WPF UI — сучасна бібліотека Fluent контролів","/csharp/desktop-ui/04b.wpf-ui-library","01.csharp/12.desktop-ui/04b.wpf-ui-library",{"title":1381,"path":1382,"stem":1383},"HandyControl — велика бібліотека UI контролів для WPF","/csharp/desktop-ui/04c.handycontrol-library","01.csharp/12.desktop-ui/04c.handycontrol-library",{"title":1385,"path":1386,"stem":1387},"Простори імен та ресурси XAML","/csharp/desktop-ui/xaml-namespaces-resources","01.csharp/12.desktop-ui/05.xaml-namespaces-resources",{"title":1389,"path":1390,"stem":1391},"XAML в Avalonia: ключові відмінності від WPF","/csharp/desktop-ui/05a.avalonia-xaml-differences","01.csharp/12.desktop-ui/05a.avalonia-xaml-differences",{"title":1393,"path":1394,"stem":1395},"Розширення розмітки XAML (Markup Extensions)","/csharp/desktop-ui/xaml-markup-extensions","01.csharp/12.desktop-ui/06.xaml-markup-extensions",{"title":1397,"path":1398,"stem":1399},"Панелі Layout: StackPanel, WrapPanel, DockPanel","/csharp/desktop-ui/layout-panels-part1","01.csharp/12.desktop-ui/07.layout-panels-part1",{"title":1401,"path":1402,"stem":1403},"Grid, Canvas, UniformGrid","/csharp/desktop-ui/layout-panels-part2","01.csharp/12.desktop-ui/07.layout-panels-part2",{"title":1405,"path":1406,"stem":1407},"Просунуті техніки Layout","/csharp/desktop-ui/layout-advanced","01.csharp/12.desktop-ui/08.layout-advanced",{"title":1409,"path":1410,"stem":1411},"Адаптивний Layout та найкращі практики","/csharp/desktop-ui/layout-responsive","01.csharp/12.desktop-ui/09.layout-responsive",{"title":1413,"path":1414,"stem":1415},"Layout в Avalonia: відмінності та нові можливості","/csharp/desktop-ui/09a.layout-avalonia","01.csharp/12.desktop-ui/09a.layout-avalonia",{"title":1417,"path":1418,"stem":1419},"Button, Image, ProgressBar та інші базові контроли","/csharp/desktop-ui/basic-controls","01.csharp/12.desktop-ui/10.basic-controls",{"title":1421,"path":1422,"stem":1423},"Контроли в Avalonia: відмінності від WPF","/csharp/desktop-ui/10a.controls-avalonia","01.csharp/12.desktop-ui/10a.controls-avalonia",{"title":1425,"path":1426,"stem":1427},"Текстові контроли — TextBlock, TextBox, RichTextBox","/csharp/desktop-ui/text-controls","01.csharp/12.desktop-ui/11.text-controls",{"title":1429,"path":1430,"stem":1431},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","/csharp/desktop-ui/selection-controls","01.csharp/12.desktop-ui/12.selection-controls",{"title":1433,"path":1434,"stem":1435},"Content Model — GroupBox, Expander, TabControl, StatusBar","/csharp/desktop-ui/content-controls","01.csharp/12.desktop-ui/13.content-controls",{"title":1437,"path":1438,"stem":1439},"UI/UX принципи десктопних застосунків","/csharp/desktop-ui/13a.ui-ux-principles","01.csharp/12.desktop-ui/13a.ui-ux-principles",{"title":1441,"path":1442,"stem":1443},"Dependency Properties — Концепція та Value Resolution","/csharp/desktop-ui/dependency-properties-part1","01.csharp/12.desktop-ui/14.dependency-properties-part1",{"title":1445,"path":1446,"stem":1447},"Avalonia Property System — StyledProperty та DirectProperty","/csharp/desktop-ui/14a.avalonia-property-system","01.csharp/12.desktop-ui/14a.avalonia-property-system",{"title":1449,"path":1450,"stem":1451},"Attached Properties — Властивості без меж","/csharp/desktop-ui/attached-properties","01.csharp/12.desktop-ui/15.attached-properties",{"title":1453,"path":1454,"stem":1455},"Routed Events — Маршрутизація подій у WPF","/csharp/desktop-ui/routed-events","01.csharp/12.desktop-ui/16.routed-events",{"title":1457,"path":1458,"stem":1459},"Data Binding — Від Code-Behind до Декларативності","/csharp/desktop-ui/data-binding-basics-part1","01.csharp/12.desktop-ui/17.data-binding-basics-part1",{"title":1461,"path":1462,"stem":1463},"INotifyPropertyChanged — Живе оновлення UI","/csharp/desktop-ui/data-binding-basics-part2","01.csharp/12.desktop-ui/17.data-binding-basics-part2",{"title":1465,"path":1466,"stem":1467},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","/csharp/desktop-ui/17a.avalonia-compiled-bindings","01.csharp/12.desktop-ui/17a.avalonia-compiled-bindings",{"title":1469,"path":1470,"stem":1471},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","/csharp/desktop-ui/data-binding-advanced","01.csharp/12.desktop-ui/18.data-binding-advanced",{"title":1473,"path":1474,"stem":1475},"Value Converters — Перетворення типів даних у Data Binding","/csharp/desktop-ui/value-converters","01.csharp/12.desktop-ui/19.value-converters",{"title":1477,"path":1478,"stem":1479},"Data Templates — Візуалізація об'єктів у WPF","/csharp/desktop-ui/data-templates","01.csharp/12.desktop-ui/20.data-templates",{"title":1481,"path":1482,"stem":1483},"Collections Binding Part 1 — ObservableCollection та ItemsControl","/csharp/desktop-ui/collections-binding-part1","01.csharp/12.desktop-ui/21.collections-binding-part1",{"title":1485,"path":1486,"stem":1487},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","/csharp/desktop-ui/collections-binding-part2","01.csharp/12.desktop-ui/21.collections-binding-part2",{"title":1489,"path":1490,"stem":1491},"MVVM Pattern — Від Spaghetti Code до архітектури","/csharp/desktop-ui/mvvm-pattern","01.csharp/12.desktop-ui/22.mvvm-pattern",{"title":1493,"path":1494,"stem":1495},"ViewModel Implementation — Від BaseViewModel до валідації","/csharp/desktop-ui/viewmodel-implementation","01.csharp/12.desktop-ui/23.viewmodel-implementation",{"title":1497,"path":1498,"stem":1499},"Commands — Від event handlers до декларативних команд","/csharp/desktop-ui/commands","01.csharp/12.desktop-ui/24.commands",{"title":1501,"path":1502,"stem":1503},"MVVM Toolkit — MVVM без boilerplate через Source Generators","/csharp/desktop-ui/mvvm-toolkit","01.csharp/12.desktop-ui/25.mvvm-toolkit",{"title":1505,"path":1506,"stem":1507},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","/csharp/desktop-ui/messenger-pattern","01.csharp/12.desktop-ui/26.messenger-pattern",{"title":1509,"path":1510,"stem":1511},"Стилі WPF — CSS для десктопу","/csharp/desktop-ui/styles-basics","01.csharp/12.desktop-ui/27.styles-basics",{"title":1513,"path":1514,"stem":1515},"CSS-like стилі Avalonia","/csharp/desktop-ui/27a.avalonia-css-styling","01.csharp/12.desktop-ui/27a.avalonia-css-styling",{"title":1517,"path":1518,"stem":1519},"Control Templates — Частина 1. Концепція та TemplateBinding","/csharp/desktop-ui/control-templates-part1","01.csharp/12.desktop-ui/28.control-templates-part1",{"title":1521,"path":1522,"stem":1523},"Control Templates — Частина 2. Named Parts та ContentPresenter","/csharp/desktop-ui/control-templates-part2","01.csharp/12.desktop-ui/28.control-templates-part2",{"title":1525,"path":1526,"stem":1527},"Control Themes в Avalonia — нова ера стилізації","/csharp/desktop-ui/28a.avalonia-control-themes","01.csharp/12.desktop-ui/28a.avalonia-control-themes",{"title":1529,"path":1530,"stem":1531},"Triggers та Visual State Manager у WPF","/csharp/desktop-ui/triggers-visual-states","01.csharp/12.desktop-ui/29.triggers-visual-states",{"title":1533,"path":1534,"stem":1535},"Pseudo-classes в Avalonia — замість WPF Triggers","/csharp/desktop-ui/29a.avalonia-pseudo-classes","01.csharp/12.desktop-ui/29a.avalonia-pseudo-classes",{"title":1537,"path":1538,"stem":1539},"Теми та ресурсні словники у WPF","/csharp/desktop-ui/resources-themes","01.csharp/12.desktop-ui/30.resources-themes",{"title":1541,"path":1542,"stem":1543},"Avalonia Themes — Fluent Design та система тематизації","/csharp/desktop-ui/30a.avalonia-themes-fluent","01.csharp/12.desktop-ui/30a.avalonia-themes-fluent",{"title":1545,"path":1546,"stem":1547},"Контроли колекцій — глибоке занурення","/csharp/desktop-ui/collection-controls","01.csharp/12.desktop-ui/31.collection-controls",{"title":1549,"path":1550,"stem":1551},"DataGrid — колонки та базове відображення","/csharp/desktop-ui/datagrid-part1","01.csharp/12.desktop-ui/32.datagrid-part1",{"title":1553,"path":1554,"stem":1555},"DataGrid — сортування, фільтрація, редагування","/csharp/desktop-ui/datagrid-part2","01.csharp/12.desktop-ui/32.datagrid-part2",{"title":1557,"path":1558,"stem":1559},"TreeView та GridView","/csharp/desktop-ui/treeview-listview","01.csharp/12.desktop-ui/33.treeview-listview",{"title":1561,"path":1562,"stem":1563},"Меню, Toolbar, ContextMenu, StatusBar","/csharp/desktop-ui/menus-toolbars","01.csharp/12.desktop-ui/34.menus-toolbars",{"title":1565,"path":1566,"stem":1567},"Навігація та керування вікнами. Частина 1: вікна та сторінки","/csharp/desktop-ui/navigation-windows-part1","01.csharp/12.desktop-ui/35.navigation-windows-part1",{"title":1569,"path":1570,"stem":1571},"Навігація та керування вікнами. Частина 2: MVVM-навігація","/csharp/desktop-ui/navigation-windows-part2","01.csharp/12.desktop-ui/35.navigation-windows-part2",{"title":1573,"path":1574,"stem":1575},"Avalonia — Навігація та діалоги","/csharp/desktop-ui/35a.avalonia-navigation-dialogs","01.csharp/12.desktop-ui/35a.avalonia-navigation-dialogs",{"title":1577,"path":1578,"stem":1579},"Діалоги та File Pickers у WPF","/csharp/desktop-ui/dialogs-file-pickers","01.csharp/12.desktop-ui/36.dialogs-file-pickers",{"title":1581,"path":1582,"stem":1583},"UserControl: компонентний підхід у WPF","/csharp/desktop-ui/user-controls","01.csharp/12.desktop-ui/37.user-controls",{"title":1585,"path":1586,"stem":1587},"Custom Controls: Lookless Controls у WPF","/csharp/desktop-ui/custom-controls","01.csharp/12.desktop-ui/38.custom-controls",{"title":1589,"path":1590,"stem":1591},"Avalonia TemplatedControl — Lookless Controls","/csharp/desktop-ui/38a.avalonia-templated-controls","01.csharp/12.desktop-ui/38a.avalonia-templated-controls",{"title":1593,"path":1594,"stem":1595},"Анімації у WPF: Storyboard та Easing Functions","/csharp/desktop-ui/animations-transitions","01.csharp/12.desktop-ui/39.animations-transitions",{"title":1597,"path":1598,"stem":1599},"Анімації в Avalonia","/csharp/desktop-ui/39a.avalonia-animations","01.csharp/12.desktop-ui/39a.avalonia-animations",{"title":1601,"path":1602,"stem":1603},"2D Графіка та Мультимедіа у WPF","/csharp/desktop-ui/media-graphics","01.csharp/12.desktop-ui/40.media-graphics",{"title":1605,"path":1606,"stem":1607},"Dependency Injection у WPF та Avalonia","/csharp/desktop-ui/di-integration","01.csharp/12.desktop-ui/41.di-integration",{"title":1609,"path":1610,"stem":1611},"SQLite та EF Core у десктопних додатках","/csharp/desktop-ui/data-persistence-part1","01.csharp/12.desktop-ui/42.data-persistence-part1",{"title":1613,"path":1614,"stem":1615},"Repository Pattern та Unit of Work","/csharp/desktop-ui/data-persistence-part2","01.csharp/12.desktop-ui/43.data-persistence-part2",{"title":1617,"path":1618,"stem":1619},"Тестування ViewModels","/csharp/desktop-ui/viewmodel-testing","01.csharp/12.desktop-ui/44.viewmodel-testing",{"title":1621,"path":1622,"stem":1623},"Avalonia Headless Testing — тестування UI без вікон","/csharp/desktop-ui/44a.avalonia-headless-testing","01.csharp/12.desktop-ui/44a.avalonia-headless-testing",{"title":1625,"path":1626,"stem":1627},"Кросплатформна розробка з Avalonia","/csharp/desktop-ui/avalonia-cross-platform","01.csharp/12.desktop-ui/45.avalonia-cross-platform",{"title":1629,"path":1630,"stem":1631},"Пакування та розгортання Avalonia додатків","/csharp/desktop-ui/avalonia-packaging-deployment","01.csharp/12.desktop-ui/46.avalonia-packaging-deployment",{"title":1633,"path":1634,"stem":1635},"Розгортання WPF застосунків","/csharp/desktop-ui/wpf-packaging-deployment","01.csharp/12.desktop-ui/47.wpf-packaging-deployment",{"title":1637,"icon":658,"path":1638,"stem":1639,"children":1640,"page":59},"Network Programming","/csharp/network-programming","01.csharp/13.network-programming",[1641,1645,1649],{"title":1642,"path":1643,"stem":1644},"Основи комп'ютерних мереж","/csharp/network-programming/foundations","01.csharp/13.network-programming/01.foundations",{"title":1646,"path":1647,"stem":1648},"Модель OSI та стек TCP/IP","/csharp/network-programming/osi-model","01.csharp/13.network-programming/02.osi-model",{"title":1650,"path":1651,"stem":1652},"IP-протокол та адресація","/csharp/network-programming/ip-addressing","01.csharp/13.network-programming/03.ip-addressing",{"title":1654,"path":1655,"stem":1656},"C# & .NET: The Ultimate Roadmap","/csharp/roadmap","01.csharp/roadmap",{"title":1658,"icon":1659,"path":1660,"stem":1661,"children":1662,"page":59},"C++","i-devicon-cplusplus","/cpp","02.cpp",[1663,1667,1671,1675,1679,1683,1687,1691,1695,1698,1702,1706,1710,1714,1718,1722,1726,1730,1734,1738,1742,1746,1750,1754,1758,1762,1766,1770,1774,1778,1782,1786,1790,1794,1798,1802,1806],{"title":1664,"path":1665,"stem":1666},"Вступ у програмування та алгоритми","/cpp/intro-algorithms","02.cpp/01.intro-algorithms",{"title":1668,"path":1669,"stem":1670},"Code Style: угоди про оформлення коду","/cpp/code-style","02.cpp/02.code-style",{"title":1672,"path":1673,"stem":1674},"Середовище розробки та перший проєкт","/cpp/ide-setup","02.cpp/03.ide-setup",{"title":1676,"path":1677,"stem":1678},"Вивід даних на екран","/cpp/data-output","02.cpp/04.data-output",{"title":1680,"path":1681,"stem":1682},"Типи даних, змінні та константи","/cpp/data-types-variables","02.cpp/05.data-types-variables",{"title":1684,"path":1685,"stem":1686},"Ввід даних з клавіатури","/cpp/data-input","02.cpp/06.data-input",{"title":1688,"path":1689,"stem":1690},"Оператори, перетворення типів та логічні операції","/cpp/operators-type-conversion","02.cpp/07.operators-type-conversion",{"title":1692,"path":1693,"stem":1694},"Цикли","/cpp/loops","02.cpp/08.loops",{"title":32,"path":1696,"stem":1697},"/cpp/arrays","02.cpp/09.arrays",{"title":1699,"path":1700,"stem":1701},"Алгоритми сортування та аналіз складності","/cpp/sorting","02.cpp/10.sorting",{"title":1703,"path":1704,"stem":1705},"Алгоритми пошуку","/cpp/searching","02.cpp/11.searching",{"title":1707,"path":1708,"stem":1709},"Функції: основи","/cpp/functions-basics","02.cpp/12.functions-basics",{"title":1711,"path":1712,"stem":1713},"Функції: прототипи, область видимості та додаткові можливості","/cpp/functions-scope","02.cpp/13.functions-scope",{"title":1715,"path":1716,"stem":1717},"Функції: перевантаження та шаблони","/cpp/functions-overloading-templates","02.cpp/14.functions-overloading-templates",{"title":1719,"path":1720,"stem":1721},"Вказівники: основи","/cpp/pointers-basics","02.cpp/15.pointers-basics",{"title":1723,"path":1724,"stem":1725},"Посилання (References)","/cpp/references","02.cpp/16.references",{"title":1727,"path":1728,"stem":1729},"Вказівники, const і масиви","/cpp/pointers-const-arrays","02.cpp/17.pointers-const-arrays",{"title":1731,"path":1732,"stem":1733},"Адресна арифметика","/cpp/pointer-arithmetic","02.cpp/18.pointer-arithmetic",{"title":1735,"path":1736,"stem":1737},"Динамічна пам'ять","/cpp/dynamic-memory","02.cpp/19.dynamic-memory",{"title":1739,"path":1740,"stem":1741},"Вказівники типу void","/cpp/void-pointers","02.cpp/20.void-pointers",{"title":1743,"path":1744,"stem":1745},"Вказівники на вказівники","/cpp/pointers-to-pointers","02.cpp/21.pointers-to-pointers",{"title":1747,"path":1748,"stem":1749},"Оператор доступу до членів через вказівник (->)","/cpp/member-access-operator","02.cpp/22.member-access-operator",{"title":1751,"path":1752,"stem":1753},"Цикл for-each (Range-based for)","/cpp/foreach-loop","02.cpp/23.foreach-loop",{"title":1755,"path":1756,"stem":1757},"Вказівники на функції","/cpp/function-pointers","02.cpp/24.function-pointers",{"title":1759,"path":1760,"stem":1761},"Лямбда-вирази","/cpp/lambdas","02.cpp/25.lambdas",{"title":1763,"path":1764,"stem":1765},"Лямбда-захоплення","/cpp/lambda-captures","02.cpp/26.lambda-captures",{"title":1767,"path":1768,"stem":1769},"Еліпсис","/cpp/ellipsis","02.cpp/27.ellipsis",{"title":1771,"path":1772,"stem":1773},"Аргументи командного рядка","/cpp/command-line-arguments","02.cpp/28.command-line-arguments",{"title":1775,"path":1776,"stem":1777},"Перерахування (enum)","/cpp/enum","02.cpp/29.enum",{"title":1779,"path":1780,"stem":1781},"Класи-перерахування (enum class)","/cpp/enum-class","02.cpp/30.enum-class",{"title":1783,"path":1784,"stem":1785},"Псевдоніми типів (typedef і using)","/cpp/type-aliases","02.cpp/31.type-aliases",{"title":1787,"path":1788,"stem":1789},"Системи числення та двійкова арифметика","/cpp/number-systems","02.cpp/32.number-systems",{"title":1791,"path":1792,"stem":1793},"Структури (struct): агрегування даних","/cpp/struct","02.cpp/33.struct",{"title":1795,"path":1796,"stem":1797},"Структури у функціях","/cpp/struct-functions","02.cpp/34.struct-functions",{"title":1799,"path":1800,"stem":1801},"Масиви структур і вкладені структури","/cpp/struct-arrays","02.cpp/35.struct-arrays",{"title":1803,"path":1804,"stem":1805},"Патерни struct та межі застосування","/cpp/struct-patterns","02.cpp/36.struct-patterns",{"title":1807,"path":1808,"stem":1809},"План навчання: Курс C++ — Продовження (Статті 29–60+)","/cpp/curriculum-plan","02.cpp/curriculum-plan",{"title":1811,"icon":1812,"path":1813,"stem":1814,"children":1815,"page":59},"JavaScript","i-devicon-javascript","/javascript","03.javascript",[1816,1842,1896,1918,2222,2260],{"title":1817,"icon":1818,"path":1819,"stem":1820,"children":1821,"page":59},"Events","i-lucide-mouse-pointer-click","/javascript/events","03.javascript/01.events",[1822,1826,1830,1834,1838],{"title":1823,"path":1824,"stem":1825},"Вступ до подій браузера","/javascript/events/intro","03.javascript/01.events/01.intro",{"title":1827,"path":1828,"stem":1829},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","/javascript/events/bubbling-capturing","03.javascript/01.events/02.bubbling-capturing",{"title":1831,"path":1832,"stem":1833},"Делегування подій (Event Delegation)","/javascript/events/delegate-events","03.javascript/01.events/03.delegate-events",{"title":1835,"path":1836,"stem":1837},"Типові дії браузера та preventDefault()","/javascript/events/prevent-default","03.javascript/01.events/04.prevent-default",{"title":1839,"path":1840,"stem":1841},"Запуск користувацьких подій (Custom Events)","/javascript/events/custom-events","03.javascript/01.events/05.custom-events",{"title":1843,"icon":1844,"path":1845,"stem":1846,"children":1847,"page":59},"Network","i-lucide-globe","/javascript/network","03.javascript/02.network",[1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892],{"title":1849,"path":1850,"stem":1851},"Fetch API - Сучасний підхід до HTTP-запитів","/javascript/network/01-fetch-api","03.javascript/02.network/01-fetch-api",{"title":1853,"path":1854,"stem":1855},"FormData - Робота з формами та файлами","/javascript/network/02-formdata","03.javascript/02.network/02-formdata",{"title":1857,"path":1858,"stem":1859},"Відстеження прогресу завантаження","/javascript/network/03-download-progress","03.javascript/02.network/03-download-progress",{"title":1861,"path":1862,"stem":1863},"Переривання fetch-запитів","/javascript/network/04-abort-requests","03.javascript/02.network/04-abort-requests",{"title":1865,"path":1866,"stem":1867},"CORS - Запити між різними джерелами","/javascript/network/05-cors","03.javascript/02.network/05-cors",{"title":1869,"path":1870,"stem":1871},"Fetch API - Повний довідник опцій","/javascript/network/06-fetch-options","03.javascript/02.network/06-fetch-options",{"title":1873,"path":1874,"stem":1875},"URL Objects - Робота з посиланнями","/javascript/network/07-url-objects","03.javascript/02.network/07-url-objects",{"title":1877,"path":1878,"stem":1879},"XMLHttpRequest - AJAX та низькорівневі запити","/javascript/network/08-xmlhttprequest","03.javascript/02.network/08-xmlhttprequest",{"title":1881,"path":1882,"stem":1883},"Відновлюване завантаження файлів","/javascript/network/09-resumable-upload","03.javascript/02.network/09-resumable-upload",{"title":1885,"path":1886,"stem":1887},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","/javascript/network/10-cookies","03.javascript/02.network/10-cookies",{"title":1889,"path":1890,"stem":1891},"js-cookie: Керування Cookies без Болю","/javascript/network/11-js-cookie","03.javascript/02.network/11-js-cookie",{"title":1893,"path":1894,"stem":1895},"Axios: Потужний HTTP-клієнт для JavaScript","/javascript/network/12-axios","03.javascript/02.network/12-axios",{"title":1897,"icon":1898,"path":1899,"stem":1900,"children":1901,"page":59},"Bom","i-lucide-monitor","/javascript/bom","03.javascript/03.bom",[1902,1906,1910,1914],{"title":1903,"path":1904,"stem":1905},"LocalStorage, SessionStorage та patterns збереження даних","/javascript/bom/01-localstorage","03.javascript/03.bom/01-localstorage",{"title":1907,"path":1908,"stem":1909},"Location Object - Керування адресою сторінки","/javascript/bom/02-location-object","03.javascript/03.bom/02-location-object",{"title":1911,"path":1912,"stem":1913},"History API - Керування історією браузера","/javascript/bom/03-history-api","03.javascript/03.bom/03-history-api",{"title":1915,"path":1916,"stem":1917},"Navigator Object - Ідентифікація та Можливості Пристрою","/javascript/bom/04-navigator-object","03.javascript/03.bom/04-navigator-object",{"title":1919,"icon":1920,"path":1921,"stem":1922,"children":1923},"React","i-devicon-react","/javascript/react","03.javascript/04.react/index",[1924,1925,1929,1933,1937,1941,2004,2039,2191],{"title":1919,"path":1921,"stem":1922},{"title":1926,"path":1927,"stem":1928},"Робота з Формами в React","/javascript/react/react-forms","03.javascript/04.react/01.react-forms",{"title":1930,"path":1931,"stem":1932},"React Hook Form: Професійна Робота з Формами","/javascript/react/react-hook-form","03.javascript/04.react/02.react-hook-form",{"title":1934,"path":1935,"stem":1936},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","/javascript/react/react-hook-form-new","03.javascript/04.react/02.react-hook-form-new",{"title":1938,"path":1939,"stem":1940},"Axios та React: Професійна Архітектура Запитів","/javascript/react/data-fetching-axios","03.javascript/04.react/03.data-fetching-axios",{"title":1942,"icon":132,"path":1943,"stem":1944,"children":1945},"Tanstack Query","/javascript/react/tanstack-query","03.javascript/04.react/04.tanstack-query/index",[1946,1948,1952,1956,1960,1964,1968,1972,1976,1980,1984,1988,1992,1996,2000],{"title":1947,"path":1943,"stem":1944},"TanStack Query: Майстерність Керування Станом Сервера",{"title":1949,"path":1950,"stem":1951},"Парадигма Server State: Чому useEffect недостатньо","/javascript/react/tanstack-query/server-state-paradigm","03.javascript/04.react/04.tanstack-query/01.server-state-paradigm",{"title":1953,"path":1954,"stem":1955},"Встановлення та Налаштування: Фундамент","/javascript/react/tanstack-query/installation-and-devtools","03.javascript/04.react/04.tanstack-query/02.installation-and-devtools",{"title":1957,"path":1958,"stem":1959},"Основи Запитів та Магія Ключів","/javascript/react/tanstack-query/query-basics-and-keys","03.javascript/04.react/04.tanstack-query/03.query-basics-and-keys",{"title":1961,"path":1962,"stem":1963},"Синхронізація Даних: Життєвий Цикл Запиту","/javascript/react/tanstack-query/data-synchronization","03.javascript/04.react/04.tanstack-query/04.data-synchronization",{"title":1965,"path":1966,"stem":1967},"Мутації та Інвалідація: Зміна Даних","/javascript/react/tanstack-query/mutations-and-invalidation","03.javascript/04.react/04.tanstack-query/05.mutations-and-invalidation",{"title":1969,"path":1970,"stem":1971},"Оптимістичні Оновлення: Швидше за Світло","/javascript/react/tanstack-query/optimistic-updates","03.javascript/04.react/04.tanstack-query/06.optimistic-updates",{"title":1973,"path":1974,"stem":1975},"Пагінація та Infinite Scroll","/javascript/react/tanstack-query/pagination-and-load-more","03.javascript/04.react/04.tanstack-query/07.pagination-and-load-more",{"title":1977,"path":1978,"stem":1979},"Просунуті Патерни та Оптимізація","/javascript/react/tanstack-query/advanced-patterns","03.javascript/04.react/04.tanstack-query/08.advanced-patterns",{"title":1981,"path":1982,"stem":1983},"Архітектура та Best Practices","/javascript/react/tanstack-query/architecture-and-best-practices","03.javascript/04.react/04.tanstack-query/09.architecture-and-best-practices",{"title":1985,"path":1986,"stem":1987},"Server-Side Rendering (SSR) та Гідратація","/javascript/react/tanstack-query/server-side-rendering","03.javascript/04.react/04.tanstack-query/10.server-side-rendering",{"title":1989,"path":1990,"stem":1991},"Стратегії Тестування","/javascript/react/tanstack-query/testing-strategies","03.javascript/04.react/04.tanstack-query/11.testing-strategies",{"title":1993,"path":1994,"stem":1995},"Аутентифікація та Обробка Помилок","/javascript/react/tanstack-query/authentication-and-errors","03.javascript/04.react/04.tanstack-query/12.authentication-and-errors",{"title":1997,"path":1998,"stem":1999},"React Suspense та Майбутнє","/javascript/react/tanstack-query/react-suspense","03.javascript/04.react/04.tanstack-query/13.react-suspense",{"title":2001,"path":2002,"stem":2003},"Глибоке Занурення в Продуктивність","/javascript/react/tanstack-query/performance-deep-dive","03.javascript/04.react/04.tanstack-query/14.performance-deep-dive",{"title":2005,"icon":1920,"path":2006,"stem":2007,"children":2008},"React Router","/javascript/react/react-router","03.javascript/04.react/05.react-router/index",[2009,2011,2015,2019,2023,2027,2031,2035],{"title":2010,"path":2006,"stem":2007},"React Router: Навігаційна система сучасного вебу",{"title":2012,"path":2013,"stem":2014},"Налаштування та Базовий Роутинг","/javascript/react/react-router/setup-and-basic-routing","03.javascript/04.react/05.react-router/01.setup-and-basic-routing",{"title":2016,"path":2017,"stem":2018},"Динамічна Навігація","/javascript/react/react-router/navigation-and-links","03.javascript/04.react/05.react-router/02.navigation-and-links",{"title":2020,"path":2021,"stem":2022},"Вкладені Маршрути та Макети","/javascript/react/react-router/nested-routes-and-layouts","03.javascript/04.react/05.react-router/03.nested-routes-and-layouts",{"title":2024,"path":2025,"stem":2026},"Динамічні Маршрути та Параметри","/javascript/react/react-router/dynamic-routing","03.javascript/04.react/05.react-router/04.dynamic-routing",{"title":2028,"path":2029,"stem":2030},"Data APIs: Loaders та Actions","/javascript/react/react-router/data-loading","03.javascript/04.react/05.react-router/05.data-loading",{"title":2032,"path":2033,"stem":2034},"Просунуті Патерни","/javascript/react/react-router/advanced-patterns","03.javascript/04.react/05.react-router/06.advanced-patterns",{"title":2036,"path":2037,"stem":2038},"Legacy Routing: Компонентний підхід","/javascript/react/react-router/legacy-routing","03.javascript/04.react/05.react-router/07.legacy-routing",{"title":2040,"icon":132,"path":2041,"stem":2042,"children":2043},"Redux","/javascript/react/redux","03.javascript/04.react/06.redux/index",[2044,2046,2062,2091,2100,2121,2137,2166],{"title":2045,"path":2041,"stem":2042},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2047,"stem":2048,"children":2049,"page":59},"/javascript/react/redux/fundamentals","03.javascript/04.react/06.redux/01.fundamentals",[2050,2054,2058],{"title":2051,"path":2052,"stem":2053},"Вступ до State Management","/javascript/react/redux/fundamentals/intro-state-management","03.javascript/04.react/06.redux/01.fundamentals/01.intro-state-management",{"title":2055,"path":2056,"stem":2057},"Філософія Redux та Три Принципи","/javascript/react/redux/fundamentals/redux-philosophy","03.javascript/04.react/06.redux/01.fundamentals/02.redux-philosophy",{"title":2059,"path":2060,"stem":2061},"Чисті функції та Іммутабельність","/javascript/react/redux/fundamentals/pure-functions-immutability","03.javascript/04.react/06.redux/01.fundamentals/03.pure-functions-immutability",{"title":2063,"icon":132,"path":2064,"stem":2065,"children":2066,"page":59},"Classic Redux","/javascript/react/redux/classic-redux","03.javascript/04.react/06.redux/02.classic-redux",[2067,2071,2075,2079,2083,2087],{"title":2068,"path":2069,"stem":2070},"Створення Store (Classic Redux)","/javascript/react/redux/classic-redux/store-setup","03.javascript/04.react/06.redux/02.classic-redux/01.store-setup",{"title":2072,"path":2073,"stem":2074},"Actions, Constants та Action Creators","/javascript/react/redux/classic-redux/actions-constants","03.javascript/04.react/06.redux/02.classic-redux/02.actions-constants",{"title":2076,"path":2077,"stem":2078},"Логіка Reducers","/javascript/react/redux/classic-redux/reducers","03.javascript/04.react/06.redux/02.classic-redux/03.reducers",{"title":2080,"path":2081,"stem":2082},"Комбінування Reducers (Root Reducer)","/javascript/react/redux/classic-redux/data-flow","03.javascript/04.react/06.redux/02.classic-redux/04.data-flow",{"title":2084,"path":2085,"stem":2086},"Підключення до React (React-Redux)","/javascript/react/redux/classic-redux/react-redux-connection","03.javascript/04.react/06.redux/02.classic-redux/05.react-redux-connection",{"title":2088,"path":2089,"stem":2090},"Middleware та Асинхронність (Redux Thunk)","/javascript/react/redux/classic-redux/middleware-thunk","03.javascript/04.react/06.redux/02.classic-redux/06.middleware-thunk",{"title":2092,"icon":132,"path":2093,"stem":2094,"children":2095,"page":59},"Transition To Rtk","/javascript/react/redux/transition-to-rtk","03.javascript/04.react/06.redux/03.transition-to-rtk",[2096],{"title":2097,"path":2098,"stem":2099},"Проблеми класичного Redux","/javascript/react/redux/transition-to-rtk/problems-with-classic","03.javascript/04.react/06.redux/03.transition-to-rtk/01.problems-with-classic",{"title":2101,"icon":132,"path":2102,"stem":2103,"children":2104,"page":59},"Redux Toolkit","/javascript/react/redux/redux-toolkit","03.javascript/04.react/06.redux/04.redux-toolkit",[2105,2109,2113,2117],{"title":2106,"path":2107,"stem":2108},"Налаштування Store з configureStore","/javascript/react/redux/redux-toolkit/configure-store","03.javascript/04.react/06.redux/04.redux-toolkit/01.configure-store",{"title":2110,"path":2111,"stem":2112},"createSlice: Революція в Redux","/javascript/react/redux/redux-toolkit/create-slice","03.javascript/04.react/06.redux/04.redux-toolkit/02.create-slice",{"title":2114,"path":2115,"stem":2116},"Асинхронність з createAsyncThunk","/javascript/react/redux/redux-toolkit/async-thunks","03.javascript/04.react/06.redux/04.redux-toolkit/03.async-thunks",{"title":2118,"path":2119,"stem":2120},"04. Entity Adapter: Керування нормалізованим станом","/javascript/react/redux/redux-toolkit/entity-adapter","03.javascript/04.react/06.redux/04.redux-toolkit/04.entity-adapter",{"title":2122,"icon":92,"path":2123,"stem":2124,"children":2125,"page":59},"Advanced","/javascript/react/redux/advanced","03.javascript/04.react/06.redux/05.advanced",[2126,2130,2134],{"title":2127,"path":2128,"stem":2129},"Мемоізація та Селектори: Повний Гайд по Reselect","/javascript/react/redux/advanced/selectors-reselect","03.javascript/04.react/06.redux/05.advanced/01.selectors-reselect",{"title":2131,"path":2132,"stem":2133},"RTK Query: Архітектура Серверного Кешу","/javascript/react/redux/advanced/rtk-query-intro","03.javascript/04.react/06.redux/05.advanced/02.rtk-query-intro",{"title":1981,"path":2135,"stem":2136},"/javascript/react/redux/advanced/architecture-best-practices","03.javascript/04.react/06.redux/05.advanced/03.architecture-best-practices",{"title":2138,"icon":132,"path":2139,"stem":2140,"children":2141,"page":59},"Project Kanban","/javascript/react/redux/project-kanban","03.javascript/04.react/06.redux/06.project-kanban",[2142,2146,2150,2154,2158,2162],{"title":2143,"path":2144,"stem":2145},"Проєкт: Kanban Board (Trello Clone)","/javascript/react/redux/project-kanban/project-overview","03.javascript/04.react/06.redux/06.project-kanban/01.project-overview",{"title":2147,"path":2148,"stem":2149},"Налаштування та Типізація","/javascript/react/redux/project-kanban/setup-and-types","03.javascript/04.react/06.redux/06.project-kanban/02.setup-and-types",{"title":2151,"path":2152,"stem":2153},"Board Slice: Серце Дошки","/javascript/react/redux/project-kanban/board-slice","03.javascript/04.react/06.redux/06.project-kanban/03.board-slice",{"title":2155,"path":2156,"stem":2157},"Логіка Drag & Drop","/javascript/react/redux/project-kanban/drag-and-drop-logic","03.javascript/04.react/06.redux/06.project-kanban/04.drag-and-drop-logic",{"title":2159,"path":2160,"stem":2161},"Інтеграція з RTK Query","/javascript/react/redux/project-kanban/rtk-query-integration","03.javascript/04.react/06.redux/06.project-kanban/05.rtk-query-integration",{"title":2163,"path":2164,"stem":2165},"Optimistic Updates","/javascript/react/redux/project-kanban/optimistic-updates","03.javascript/04.react/06.redux/06.project-kanban/06.optimistic-updates",{"title":2167,"icon":132,"path":2168,"stem":2169,"children":2170,"page":59},"Testing","/javascript/react/redux/testing","03.javascript/04.react/06.redux/07.testing",[2171,2175,2179,2183,2187],{"title":2172,"path":2173,"stem":2174},"Тестування Redux","/javascript/react/redux/testing/intro-testing","03.javascript/04.react/06.redux/07.testing/01.intro-testing",{"title":2176,"path":2177,"stem":2178},"Тестування Reducers","/javascript/react/redux/testing/testing-reducers","03.javascript/04.react/06.redux/07.testing/02.testing-reducers",{"title":2180,"path":2181,"stem":2182},"Тестування Селекторів","/javascript/react/redux/testing/testing-selectors","03.javascript/04.react/06.redux/07.testing/03.testing-selectors",{"title":2184,"path":2185,"stem":2186},"Тестування Компонентів (Integration)","/javascript/react/redux/testing/testing-components","03.javascript/04.react/06.redux/07.testing/04.testing-components",{"title":2188,"path":2189,"stem":2190},"Тестування Async Thunks","/javascript/react/redux/testing/testing-thunks","03.javascript/04.react/06.redux/07.testing/05.testing-thunks",{"title":2192,"icon":132,"path":2193,"stem":2194,"children":2195},"Ui Libraries","/javascript/react/ui-libraries","03.javascript/04.react/07.ui-libraries/index",[2196,2198,2202,2206,2210,2214,2218],{"title":2197,"path":2193,"stem":2194},"UI Бібліотеки в React",{"title":2199,"path":2200,"stem":2201},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","/javascript/react/ui-libraries/introduction-to-ui-libraries","03.javascript/04.react/07.ui-libraries/01.introduction-to-ui-libraries",{"title":2203,"path":2204,"stem":2205},"Філософія shadcn/ui: \"Not a Component Library\"","/javascript/react/ui-libraries/shadcn-philosophy","03.javascript/04.react/07.ui-libraries/02.shadcn-philosophy",{"title":2207,"path":2208,"stem":2209},"Установка та Налаштування shadcn/ui","/javascript/react/ui-libraries/shadcn-installation","03.javascript/04.react/07.ui-libraries/03.shadcn-installation",{"title":2211,"path":2212,"stem":2213},"Базові Компоненти shadcn/ui: Фундамент Інтерфейсу","/javascript/react/ui-libraries/shadcn-components-basics","03.javascript/04.react/07.ui-libraries/04.shadcn-components-basics",{"title":2215,"path":2216,"stem":2217},"Компоненти Форм: Побудова Інтерактивних Form","/javascript/react/ui-libraries/shadcn-components-forms","03.javascript/04.react/07.ui-libraries/05.shadcn-components-forms",{"title":2219,"path":2220,"stem":2221},"Складні Компоненти: Dialog, Dropdown, Table та Command","/javascript/react/ui-libraries/shadcn-components-advanced","03.javascript/04.react/07.ui-libraries/06.shadcn-components-advanced",{"title":2223,"icon":2224,"path":2225,"stem":2226,"children":2227,"page":59},"TypeScript","i-devicon-typescript","/javascript/typescript","03.javascript/05.typescript",[2228,2232,2236,2240,2244,2248,2252,2256],{"title":2229,"path":2230,"stem":2231},"TypeScript: Броня для вашого коду","/javascript/typescript/intro-and-basic-types","03.javascript/05.typescript/01.intro-and-basic-types",{"title":2233,"path":2234,"stem":2235},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","/javascript/typescript/interfaces-and-advanced-types","03.javascript/05.typescript/02.interfaces-and-advanced-types",{"title":2237,"path":2238,"stem":2239},"Алхімія Типів: Generics та Utility Types","/javascript/typescript/generics-and-utilities","03.javascript/05.typescript/03.generics-and-utilities",{"title":2241,"path":2242,"stem":2243},"Архітектура та Шаблони: Класи в TypeScript","/javascript/typescript/classes-and-oop","03.javascript/05.typescript/04.classes-and-oop",{"title":2245,"path":2246,"stem":2247},"Продакшн та Екосистема: Advanced Config & Workflow","/javascript/typescript/advanced-patterns-and-config","03.javascript/05.typescript/05.advanced-patterns-and-config",{"title":2249,"path":2250,"stem":2251},"TypeScript у світі React","/javascript/typescript/react-basics","03.javascript/05.typescript/06.react-basics",{"title":2253,"path":2254,"stem":2255},"React + TypeScript: Продвинуті патерни","/javascript/typescript/react-advanced","03.javascript/05.typescript/07.react-advanced",{"title":2257,"path":2258,"stem":2259},"React + TypeScript: Екосистема та бібліотеки","/javascript/typescript/react-ecosystem","03.javascript/05.typescript/08.react-ecosystem",{"title":2261,"path":2262,"stem":2263},"Atomic Design","/javascript/atomic-design","03.javascript/2.atomic-design",{"title":2265,"icon":2266,"path":2267,"stem":2268,"children":2269,"page":59},"Java","i-devicon-java","/java","04.java",[2270,2273,2276,2280,2284,2288,2292],{"title":162,"path":2271,"stem":2272},"/java/data-mapper-part1","04.java/01.data-mapper-part1",{"title":166,"path":2274,"stem":2275},"/java/data-mapper-part2","04.java/02.data-mapper-part2",{"title":2277,"path":2278,"stem":2279},"Service Layer: Організація бізнес-логіки","/java/service-layer","04.java/03.service-layer",{"title":2281,"path":2282,"stem":2283},"Rich Domain Model та State Pattern","/java/rich-domain-model","04.java/04.rich-domain-model",{"title":2285,"path":2286,"stem":2287},"Патерни для складної бізнес-логіки","/java/business-logic-patterns","04.java/05.business-logic-patterns",{"title":2289,"path":2290,"stem":2291},"Обробка помилок та валідація","/java/error-handling-validation","04.java/06.error-handling-validation",{"title":2293,"path":2294,"stem":2295,"children":2296,"page":59},"Проектування баз даних","/java/pr2","04.java/pr2",[2297,2301,2305,2309,2313,2317,2321,2325,2329,2333,2337,2341,2345,2349,2353,2357,2361,2365,2369,2373,2377,2381,2385,2389,2393,2397,2401,2405,2409,2413,2417,2421,2425,2429,2433,2437,2441],{"title":2298,"path":2299,"stem":2300},"Концептуальне моделювання: Мистецтво розуміння предметної області","/java/pr2/conceptual-modeling","04.java/pr2/01.conceptual-modeling",{"title":2302,"path":2303,"stem":2304},"Логічне моделювання: Від бізнес-ідей до структур даних","/java/pr2/logical-modeling","04.java/pr2/02.logical-modeling",{"title":2306,"path":2307,"stem":2308},"Нормалізація: Гігієна даних та боротьба з аномаліями","/java/pr2/normalization","04.java/pr2/03.normalization",{"title":2310,"path":2311,"stem":2312},"Фізична схема: Від абстракції до DDL","/java/pr2/physical-schema","04.java/pr2/04.physical-schema",{"title":2314,"path":2315,"stem":2316},"Архітектурна класифікація таблиць","/java/pr2/table-classification","04.java/pr2/05.table-classification",{"title":2318,"path":2319,"stem":2320},"Database Migrations: Версіонування схеми з Flyway","/java/pr2/database-migrations","04.java/pr2/06.database-migrations",{"title":2322,"path":2323,"stem":2324},"А що, якби це була не реляційна БД?","/java/pr2/beyond-relational","04.java/pr2/07.beyond-relational",{"title":2326,"path":2327,"stem":2328},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","/java/pr2/impedance-mismatch","04.java/pr2/09.impedance-mismatch",{"title":2330,"path":2331,"stem":2332},"JDBC: Перший контакт із базою даних","/java/pr2/jdbc-fundamentals","04.java/pr2/10.jdbc-fundamentals",{"title":2334,"path":2335,"stem":2336},"Якість коду: Spotless, SpotBugs та SonarQube","/java/pr2/10a.code-quality","04.java/pr2/10a.code-quality",{"title":2338,"path":2339,"stem":2340},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","/java/pr2/connection-pool","04.java/pr2/11.connection-pool",{"title":2342,"path":2343,"stem":2344},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","/java/pr2/row-data-gateway","04.java/pr2/12.row-data-gateway",{"title":2346,"path":2347,"stem":2348},"Table Data Gateway: Фасад таблиці як архітектурний відступ","/java/pr2/table-data-gateway","04.java/pr2/13.table-data-gateway",{"title":2350,"path":2351,"stem":2352},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","/java/pr2/repository-data-mapper","04.java/pr2/14.repository-data-mapper",{"title":2354,"path":2355,"stem":2356},"Identity Map: Кешування сутностей у рамках сесії","/java/pr2/identity-map","04.java/pr2/15.identity-map",{"title":2358,"path":2359,"stem":2360},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","/java/pr2/unit-of-work","04.java/pr2/16.unit-of-work",{"title":2362,"path":2363,"stem":2364},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","/java/pr2/strategy-sql","04.java/pr2/17.strategy-sql",{"title":2366,"path":2367,"stem":2368},"Proxy: Lazy Loading для One-To-Many колекцій","/java/pr2/proxy-lazy-loading","04.java/pr2/18.proxy-lazy-loading",{"title":2370,"path":2371,"stem":2372},"Generic Repository через Java Reflection: анотації та динамічний SQL","/java/pr2/generic-repository-reflection","04.java/pr2/19.generic-repository-reflection",{"title":2374,"path":2375,"stem":2376},"Specification Pattern: Композиція бізнес-правил для складних запитів","/java/pr2/specification-pattern","04.java/pr2/20.specification-pattern",{"title":2378,"path":2379,"stem":2380},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","/java/pr2/20a.advanced-specifications","04.java/pr2/20a.advanced-specifications",{"title":2382,"path":2383,"stem":2384},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","/java/pr2/asynchronous-jdbc","04.java/pr2/21.asynchronous-jdbc",{"title":2386,"path":2387,"stem":2388},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","/java/pr2/integration-testing-h2","04.java/pr2/22.integration-testing-h2",{"title":2390,"path":2391,"stem":2392},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","/java/pr2/integration-testing-testcontainers","04.java/pr2/23.integration-testing-testcontainers",{"title":2394,"path":2395,"stem":2396},"Google Guice: Впровадження залежностей у JavaFX-проєкті","/java/pr2/dependency-injection-guice","04.java/pr2/24.dependency-injection-guice",{"title":2398,"path":2399,"stem":2400},"JavaFX: Основи побудови графічних інтерфейсів","/java/pr2/javafx-fundamentals","04.java/pr2/25.javafx-fundamentals",{"title":2402,"path":2403,"stem":2404},"Properties та Bindings: Реактивність у JavaFX","/java/pr2/javafx-properties-bindings","04.java/pr2/26.javafx-properties-bindings",{"title":2406,"path":2407,"stem":2408},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","/java/pr2/ui-architecture-patterns","04.java/pr2/27.ui-architecture-patterns",{"title":2410,"path":2411,"stem":2412},"MVVM на практиці: Побудова ViewModel","/java/pr2/mvvm-viewmodel-implementation","04.java/pr2/28.mvvm-viewmodel-implementation",{"title":2414,"path":2415,"stem":2416},"View та Controller: Зв'язування з ViewModel через FXML","/java/pr2/mvvm-view-controller","04.java/pr2/29.mvvm-view-controller",{"title":2418,"path":2419,"stem":2420},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","/java/pr2/mvvm-guice-integration","04.java/pr2/30.mvvm-guice-integration",{"title":2422,"path":2423,"stem":2424},"Валідація та обробка помилок у MVVM","/java/pr2/mvvm-validation-error-handling","04.java/pr2/31.mvvm-validation-error-handling",{"title":2426,"path":2427,"stem":2428},"Навігація та управління екранами у JavaFX MVVM","/java/pr2/mvvm-navigation-screen-management","04.java/pr2/32.mvvm-navigation-screen-management",{"title":2430,"path":2431,"stem":2432},"Тестування JavaFX MVVM-додатків","/java/pr2/mvvm-testing","04.java/pr2/33.mvvm-testing",{"title":2434,"path":2435,"stem":2436},"Стилізація та теми у JavaFX: CSS та User Experience","/java/pr2/javafx-styling-themes","04.java/pr2/34.javafx-styling-themes",{"title":2438,"path":2439,"stem":2440},"AtlantaFX: Сучасні теми для JavaFX додатків","/java/pr2/atlantafx-modern-themes","04.java/pr2/35.atlantafx-modern-themes",{"title":2442,"path":2443,"stem":2444},"Пакування та розповсюдження JavaFX-додатків","/java/pr2/jar-packaging-distribution","04.java/pr2/36.jar-packaging-distribution",{"title":2446,"icon":2447,"path":2448,"stem":2449,"children":2450,"page":59},"Бази даних","i-lucide-database","/databases","06.databases",[2451,2481,2504,2541,2570,2588,2622,2634,2643],{"title":2452,"icon":2453,"path":2454,"stem":2455,"children":2456,"page":59},"Intro","i-lucide-play","/databases/intro","06.databases/01.intro",[2457,2461,2465,2469,2473,2477],{"title":2458,"path":2459,"stem":2460},"Введення в теорію баз даних","/databases/intro/introduction-to-databases","06.databases/01.intro/01.introduction-to-databases",{"title":2462,"path":2463,"stem":2464},"Реляційна модель даних","/databases/intro/relational-model-theory","06.databases/01.intro/02.relational-model-theory",{"title":2466,"path":2467,"stem":2468},"ER-моделювання","/databases/intro/er-modeling","06.databases/01.intro/03.er-modeling",{"title":2470,"path":2471,"stem":2472},"Логічне проектування БД","/databases/intro/logical-schema","06.databases/01.intro/04.logical-schema",{"title":2474,"path":2475,"stem":2476},"Класифікація таблиць","/databases/intro/table-classification","06.databases/01.intro/05.table-classification",{"title":2478,"path":2479,"stem":2480},"PlantUML для баз даних","/databases/intro/plantuml-diagrams","06.databases/01.intro/06.plantuml-diagrams",{"title":2482,"icon":2447,"path":2483,"stem":2484,"children":2485,"page":59},"MS SQL Server Start","/databases/ms-sql-server-start","06.databases/02.ms-sql-server-start",[2486,2490,2496,2500],{"title":2487,"path":2488,"stem":2489},"Типи даних у MS SQL Server","/databases/ms-sql-server-start/data-types","06.databases/02.ms-sql-server-start/01.data-types",{"title":2491,"path":2492,"stem":2493,"children":2494},"Індекси у MS SQL Server","/databases/ms-sql-server-start/sql-indexes","06.databases/02.ms-sql-server-start/02.sql-indexes",[2495],{"title":2491,"path":2492,"stem":2493},{"title":2497,"path":2498,"stem":2499},"Системні бази даних MS SQL Server","/databases/ms-sql-server-start/system-databases","06.databases/02.ms-sql-server-start/03.system-databases",{"title":2501,"path":2502,"stem":2503},"Огляд мови SQL та запитів","/databases/ms-sql-server-start/sql-queries-overview","06.databases/02.ms-sql-server-start/04.sql-queries-overview",{"title":2505,"icon":2447,"path":2506,"stem":2507,"children":2508,"page":59},"SQL","/databases/sql","06.databases/03.sql",[2509,2513,2517,2521,2525,2529,2533,2537],{"title":2510,"path":2511,"stem":2512},"Налаштування демонстраційної бази даних","/databases/sql/sample-database-setup","06.databases/03.sql/00.sample-database-setup",{"title":2514,"path":2515,"stem":2516},"DDL - Створення таблиць (CREATE TABLE)","/databases/sql/ddl-create-table","06.databases/03.sql/01.ddl-create-table",{"title":2518,"path":2519,"stem":2520},"DDL - Зміна та видалення таблиць (ALTER, DROP)","/databases/sql/ddl-alter-drop-table","06.databases/03.sql/02.ddl-alter-drop-table",{"title":2522,"path":2523,"stem":2524},"SELECT запити - Основи","/databases/sql/select-queries-fundamentals","06.databases/03.sql/03.select-queries-fundamentals",{"title":2526,"path":2527,"stem":2528},"SELECT запити - Розширені можливості","/databases/sql/select-queries-advanced","06.databases/03.sql/04.select-queries-advanced",{"title":2530,"path":2531,"stem":2532},"INSERT запити - Додавання даних","/databases/sql/insert-queries","06.databases/03.sql/05.insert-queries",{"title":2534,"path":2535,"stem":2536},"UPDATE та DELETE запити","/databases/sql/update-delete-queries","06.databases/03.sql/06.update-delete-queries",{"title":2538,"path":2539,"stem":2540},"Транзакції в SQL","/databases/sql/transactions","06.databases/03.sql/07.transactions",{"title":2542,"icon":2447,"path":2543,"stem":2544,"children":2545,"page":59},"Multi Table Databases","/databases/multi-table-databases","06.databases/04.multi-table-databases",[2546,2550,2554,2558,2562,2566],{"title":2547,"path":2548,"stem":2549},"Зв'язки та нормалізація БД","/databases/multi-table-databases/relationships-and-normalization","06.databases/04.multi-table-databases/00.relationships-and-normalization",{"title":2551,"path":2552,"stem":2553},"INNER JOIN - З'єднання таблиць","/databases/multi-table-databases/inner-join","06.databases/04.multi-table-databases/01.inner-join",{"title":2555,"path":2556,"stem":2557},"OUTER JOINs - LEFT, RIGHT, FULL","/databases/multi-table-databases/outer-joins","06.databases/04.multi-table-databases/02.outer-joins",{"title":2559,"path":2560,"stem":2561},"CROSS та SELF JOINs","/databases/multi-table-databases/cross-self-joins","06.databases/04.multi-table-databases/03.cross-self-joins",{"title":2563,"path":2564,"stem":2565},"Підзапити (Subqueries)","/databases/multi-table-databases/subqueries","06.databases/04.multi-table-databases/04.subqueries",{"title":2567,"path":2568,"stem":2569},"Агрегації з JOIN","/databases/multi-table-databases/aggregations-with-joins","06.databases/04.multi-table-databases/05.aggregations-with-joins",{"title":2571,"icon":2572,"path":2573,"stem":2574,"children":2575,"page":59},"Aggregate Functions","i-lucide-calculator","/databases/aggregate-functions","06.databases/05.aggregate-functions",[2576,2580,2584],{"title":2577,"path":2578,"stem":2579},"Функції агрегування в MS SQL Server","/databases/aggregate-functions/introduction-aggregate-functions","06.databases/05.aggregate-functions/01.introduction-aggregate-functions",{"title":2581,"path":2582,"stem":2583},"Групування даних в MS SQL Server","/databases/aggregate-functions/grouping-data","06.databases/05.aggregate-functions/02.grouping-data",{"title":2585,"path":2586,"stem":2587},"Підзапити з агрегатними функціями","/databases/aggregate-functions/subqueries-aggregates","06.databases/05.aggregate-functions/03.subqueries-aggregates",{"title":2589,"icon":2590,"path":2591,"stem":2592,"children":2593,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","/databases/triggers-stored-procedures","06.databases/07.triggers-stored-procedures",[2594,2598,2602,2606,2610,2614,2618],{"title":2595,"path":2596,"stem":2597},"DML-тригери","/databases/triggers-stored-procedures/dml-triggers","06.databases/07.triggers-stored-procedures/01.dml-triggers",{"title":2599,"path":2600,"stem":2601},"DDL-тригери","/databases/triggers-stored-procedures/ddl-triggers","06.databases/07.triggers-stored-procedures/02.ddl-triggers",{"title":2603,"path":2604,"stem":2605},"Transact-SQL розширення","/databases/triggers-stored-procedures/transact-sql-extensions","06.databases/07.triggers-stored-procedures/03.transact-sql-extensions",{"title":2607,"path":2608,"stem":2609},"Транзакції","/databases/triggers-stored-procedures/transactions","06.databases/07.triggers-stored-procedures/04.transactions",{"title":2611,"path":2612,"stem":2613},"Зберігаємі процедури","/databases/triggers-stored-procedures/stored-procedures","06.databases/07.triggers-stored-procedures/05.stored-procedures",{"title":2615,"path":2616,"stem":2617},"Користувацькі функції","/databases/triggers-stored-procedures/user-defined-functions","06.databases/07.triggers-stored-procedures/06.user-defined-functions",{"title":2619,"path":2620,"stem":2621},"Безпека баз даних","/databases/triggers-stored-procedures/security","06.databases/07.triggers-stored-procedures/08.security",{"title":2619,"icon":793,"path":2623,"stem":2624,"children":2625,"page":59},"/databases/security","06.databases/08.security",[2626,2630],{"title":2627,"path":2628,"stem":2629},"Вступ до безпеки баз даних","/databases/security/introduction","06.databases/08.security/01.introduction",{"title":2631,"path":2632,"stem":2633},"Системні представлення та метадані","/databases/security/system-views","06.databases/08.security/02.system-views",{"title":2635,"icon":2636,"path":2637,"stem":2638,"children":2639,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","/databases/backup-recovery","06.databases/09.backup-recovery",[2640],{"title":2635,"path":2641,"stem":2642},"/databases/backup-recovery/backup-restore","06.databases/09.backup-recovery/01.backup-restore",{"title":2644,"icon":2645,"path":2646,"stem":2647,"children":2648,"page":59},"Повнотекстовий пошук","i-lucide-search","/databases/full-text-search","06.databases/10.full-text-search",[2649],{"title":2644,"path":2650,"stem":2651},"/databases/full-text-search/full-text-search","06.databases/10.full-text-search/01.full-text-search",{"title":2653,"icon":2654,"path":2655,"stem":2656,"children":2657,"page":59},"Tools","i-lucide-wrench","/tools","07.tools",[2658,2734],{"title":2659,"icon":2660,"path":2661,"stem":2662,"children":2663},"Docker","i-simple-icons-docker","/tools/docker","07.tools/01.docker/index",[2664,2666,2670,2674,2678,2682,2686,2690,2694,2698,2702,2706,2710,2714,2718,2722,2726,2730],{"title":2665,"path":2661,"stem":2662},"Docker: від нуля до production",{"title":2667,"path":2668,"stem":2669},"Контейнеризація — від проблеми до рішення","/tools/docker/containerization-concept","07.tools/01.docker/01.containerization-concept",{"title":2671,"path":2672,"stem":2673},"Docker — що це і навіщо?","/tools/docker/docker-what-and-why","07.tools/01.docker/02.docker-what-and-why",{"title":2675,"path":2676,"stem":2677},"Архітектура Docker Engine","/tools/docker/docker-architecture","07.tools/01.docker/03.docker-architecture",{"title":2679,"path":2680,"stem":2681},"Встановлення Docker","/tools/docker/installation","07.tools/01.docker/04.installation",{"title":2683,"path":2684,"stem":2685},"Перший контейнер — docker run","/tools/docker/first-container","07.tools/01.docker/05.first-container",{"title":2687,"path":2688,"stem":2689},"Життєвий цикл контейнера","/tools/docker/container-lifecycle","07.tools/01.docker/06.container-lifecycle",{"title":2691,"path":2692,"stem":2693},"Docker Images — фундаментальні концепції","/tools/docker/docker-images-fundamentals","07.tools/01.docker/07.docker-images-fundamentals",{"title":2695,"path":2696,"stem":2697},"Dockerfile — основи","/tools/docker/dockerfile-basics","07.tools/01.docker/08.dockerfile-basics",{"title":2699,"path":2700,"stem":2701},"Dockerfile — просунуті техніки","/tools/docker/dockerfile-advanced","07.tools/01.docker/09.dockerfile-advanced",{"title":2703,"path":2704,"stem":2705},"Build Context та кешування шарів","/tools/docker/build-context-and-cache","07.tools/01.docker/10.build-context-and-cache",{"title":2707,"path":2708,"stem":2709},"Реєстри Docker-образів","/tools/docker/image-registries","07.tools/01.docker/11.image-registries",{"title":2711,"path":2712,"stem":2713},"Контейнеризація .NET додатків","/tools/docker/dotnet-containerization","07.tools/01.docker/12.dotnet-containerization",{"title":2715,"path":2716,"stem":2717},"Томи та збереження даних","/tools/docker/volumes-and-data","07.tools/01.docker/13.volumes-and-data",{"title":2719,"path":2720,"stem":2721},"Основи мережі в Docker","/tools/docker/networking-basics","07.tools/01.docker/14.networking-basics",{"title":2723,"path":2724,"stem":2725},"Змінні оточення та конфігурація","/tools/docker/environment-and-configuration","07.tools/01.docker/15.environment-and-configuration",{"title":2727,"path":2728,"stem":2729},"Docker Compose — оркестрація контейнерів","/tools/docker/docker-compose-basics","07.tools/01.docker/16.docker-compose-basics",{"title":2731,"path":2732,"stem":2733},"Docker Compose — Multi-Service застосунки","/tools/docker/compose-multi-service","07.tools/01.docker/17.compose-multi-service",{"title":2735,"icon":2736,"path":2737,"stem":2738,"children":2739},"Kubernetes","simple-icons:kubernetes","/tools/kubernetes","07.tools/02.kubernetes/index",[2740,2742,2746,2750,2754,2758,2762,2766,2770],{"title":2741,"path":2737,"stem":2738},"Kubernetes: від розробки до production",{"title":2743,"path":2744,"stem":2745},"Kubernetes — коли Docker Compose більше не вистачає","/tools/kubernetes/why-kubernetes","07.tools/02.kubernetes/01.why-kubernetes",{"title":2747,"path":2748,"stem":2749},"Архітектура Kubernetes — анатомія кластера","/tools/kubernetes/kubernetes-architecture","07.tools/02.kubernetes/02.kubernetes-architecture",{"title":2751,"path":2752,"stem":2753},"Локальне середовище — minikube, kind та k3s","/tools/kubernetes/local-environment","07.tools/02.kubernetes/03.local-environment",{"title":2755,"path":2756,"stem":2757},"Pod — атомарна одиниця Kubernetes","/tools/kubernetes/pods-and-containers","07.tools/02.kubernetes/04.pods-and-containers",{"title":2759,"path":2760,"stem":2761},"Патерни використання Pod","/tools/kubernetes/pod-patterns","07.tools/02.kubernetes/05.pod-patterns",{"title":2763,"path":2764,"stem":2765},"Deployment — декларативне управління Pod","/tools/kubernetes/deployment-basics","07.tools/02.kubernetes/06.deployment-basics",{"title":2767,"path":2768,"stem":2769},"Rolling Updates та управління життєвим циклом Deployment","/tools/kubernetes/deployment-rolling-updates","07.tools/02.kubernetes/07.deployment-rolling-updates",{"title":2771,"path":2772,"stem":2773},"Service — мережева абстракція для Pod","/tools/kubernetes/services-networking","07.tools/02.kubernetes/08.services-networking",{"title":2775,"icon":2776,"path":2777,"stem":2778,"children":2779,"page":59},"Software Engineering","i-lucide-code-2","/software-engineering","09.software-engineering",[2780,2784,2788,2792,2796,2800,2804,2808,2812,2816,2820],{"title":2781,"path":2782,"stem":2783},"1. Аналіз предметної області. Експертні знання та складність","/software-engineering/intro-subdomains","09.software-engineering/01.intro-subdomains",{"title":2785,"path":2786,"stem":2787},"2. Обмежені контексти. Інтеграція обмежених контекстів","/software-engineering/integrating-limited-contexts","09.software-engineering/02.integrating-limited-contexts",{"title":2789,"path":2790,"stem":2791},"3. Реалізація простої бізнес-логіки","/software-engineering/simple","09.software-engineering/03.simple",{"title":2793,"path":2794,"stem":2795},"4. Опрацювання складної бізнес-логіки","/software-engineering/complex-business-logic","09.software-engineering/04.complex-business-logic",{"title":2797,"path":2798,"stem":2799},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","/software-engineering/modelling-the-time-factor","09.software-engineering/05.modelling-the-time-factor",{"title":2801,"path":2802,"stem":2803},"6. Архітектурні патерни","/software-engineering/architectural-patterns","09.software-engineering/06.architectural-patterns",{"title":2805,"path":2806,"stem":2807},"Паттерни взаємодії","/software-engineering/patterns-of-interaction","09.software-engineering/07.patterns-of-interaction",{"title":2809,"path":2810,"stem":2811},"Евристика проєктування","/software-engineering/design-heuristics","09.software-engineering/08.design-heuristics",{"title":2813,"path":2814,"stem":2815},"Еволюція проєктних рішень","/software-engineering/evolution-of-design-solutions","09.software-engineering/09.evolution-of-design-solutions",{"title":2817,"path":2818,"stem":2819},"EventStorming","/software-engineering/eventstorming","09.software-engineering/10.eventstorming",{"title":2821,"path":2822,"stem":2823},"DDD на практиці","/software-engineering/ddd-in-practice","09.software-engineering/11.ddd-in-practice",{"title":2825,"icon":943,"path":2826,"stem":2827,"children":2828,"page":59},"DDD","/ddd","10.ddd",[2829,2833,2837,2841,2845,2849,2853,2857,2861,2865,2869,2873,2877],{"title":2830,"path":2831,"stem":2832},"Аналіз предметної області","/ddd/domain-analysis","10.ddd/01.domain-analysis",{"title":2834,"path":2835,"stem":2836},"Експертні знання про предметну область","/ddd/domain-expert-knowledge","10.ddd/02.domain-expert-knowledge",{"title":2838,"path":2839,"stem":2840},"Як осмислити складність предметної області","/ddd/managing-domain-complexity","10.ddd/03.managing-domain-complexity",{"title":2842,"path":2843,"stem":2844},"Інтеграція обмежених контекстів","/ddd/bounded-context-integration","10.ddd/04.bounded-context-integration",{"title":2846,"path":2847,"stem":2848},"Реалізація простої бізнес-логіки","/ddd/simple-business-logic","10.ddd/05.simple-business-logic",{"title":2850,"path":2851,"stem":2852},"Обробка складної бізнес-логіки","/ddd/complex-business-logic","10.ddd/06.complex-business-logic",{"title":2854,"path":2855,"stem":2856},"Моделювання фактора часу","/ddd/time-modeling","10.ddd/07.time-modeling",{"title":2858,"path":2859,"stem":2860},"Глава 8. Архітектурні Патерни","/ddd/architectural-patterns","10.ddd/08.architectural-patterns",{"title":2862,"path":2863,"stem":2864},"Глава 9. Патерни Взаємодії","/ddd/interaction-patterns","10.ddd/09.interaction-patterns",{"title":2866,"path":2867,"stem":2868},"Глава 10. Проектні Евристики","/ddd/design-heuristics","10.ddd/10.design-heuristics",{"title":2870,"path":2871,"stem":2872},"Глава 11. Еволюція Проектних Рішень","/ddd/evolution-of-design-decisions","10.ddd/11.evolution-of-design-decisions",{"title":2874,"path":2875,"stem":2876},"Глава 12. EventStorming","/ddd/event-storming","10.ddd/12.event-storming",{"title":2878,"path":2879,"stem":2880},"Глава 13. DDD на Практиці","/ddd/ddd-in-practice","10.ddd/13.ddd-in-practice",{"title":2882,"icon":2883,"path":2884,"stem":2885,"children":2886,"page":59},"Media Streaming","i-lucide-video","/media-streaming","11.media-streaming",[2887,2891,2895,2899,2903,2907,2911],{"title":2888,"path":2889,"stem":2890},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","/media-streaming/introduction","11.media-streaming/01.introduction",{"title":2892,"path":2893,"stem":2894},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","/media-streaming/audio-video-anatomy","11.media-streaming/02.audio-video-anatomy",{"title":2896,"path":2897,"stem":2898},"03. The Gym: FFmpeg Deep Dive","/media-streaming/ffmpeg-gym","11.media-streaming/03.ffmpeg-gym",{"title":2900,"path":2901,"stem":2902},"04. HLS Protocol: HTTP Live Streaming у Деталях","/media-streaming/hls-protocol","11.media-streaming/04.hls-protocol",{"title":2904,"path":2905,"stem":2906},"05. DASH Protocol: Відкритий Стандарт","/media-streaming/dash-protocol","11.media-streaming/05.dash-protocol",{"title":2908,"path":2909,"stem":2910},"06. Масштабування: CDN та Adaptive Bitrate","/media-streaming/cdn-and-adaptive-bitrate","11.media-streaming/06.cdn-and-adaptive-bitrate",{"title":2912,"path":2913,"stem":2914},"07. Війна із Затримкою (Latency)","/media-streaming/realtime-latency","11.media-streaming/07.realtime-latency",{"title":2916,"icon":2917,"path":2918,"stem":2919,"children":2920,"page":59},"HTML & CSS","i-devicon-html5","/html-css","12.html-css",[2921,2925,2929,2933,2937,2941,2945,2949,2953,2957,2961,2965,2969,2973,2977,2981,2985,2989,2993,2997,3001,3005,3009,3013,3017,3021,3025,3029,3033,3037],{"title":2922,"path":2923,"stem":2924},"Вступ до HTML. Структура документа","/html-css/intro-html-structure","12.html-css/01.intro-html-structure",{"title":2926,"path":2927,"stem":2928},"Форматування тексту в HTML","/html-css/html-text-formatting","12.html-css/02.html-text-formatting",{"title":2930,"path":2931,"stem":2932},"Посилання та зображення в HTML","/html-css/html-links-images","12.html-css/03.html-links-images",{"title":2934,"path":2935,"stem":2936},"Списки та таблиці в HTML","/html-css/html-lists-tables","12.html-css/04.html-lists-tables",{"title":2938,"path":2939,"stem":2940},"Форми в HTML","/html-css/html-forms","12.html-css/05.html-forms",{"title":2942,"path":2943,"stem":2944},"Семантичні елементи HTML5","/html-css/html-semantic-elements","12.html-css/06.html-semantic-elements",{"title":2946,"path":2947,"stem":2948},"Мультимедіа та розширені елементи HTML","/html-css/html-multimedia-advanced","12.html-css/07.html-multimedia-advanced",{"title":2950,"path":2951,"stem":2952},"Мікророзмітка та SEO в HTML","/html-css/html-microdata-seo","12.html-css/08.html-microdata-seo",{"title":2954,"path":2955,"stem":2956},"Вступ до CSS. Селектори та специфічність","/html-css/css-intro-selectors","12.html-css/09.css-intro-selectors",{"title":2958,"path":2959,"stem":2960},"Блокова модель CSS. Відступи. Box Sizing","/html-css/css-box-model","12.html-css/10.css-box-model",{"title":2962,"path":2963,"stem":2964},"Розміри у CSS: повний довідник одиниць і ключових слів","/html-css/10a.css-sizing","12.html-css/10a.css-sizing",{"title":2966,"path":2967,"stem":2968},"Типографіка в CSS. Шрифти та текст","/html-css/css-typography","12.html-css/11.css-typography",{"title":2970,"path":2971,"stem":2972},"Кольори та фони в CSS","/html-css/css-colors-backgrounds","12.html-css/12.css-colors-backgrounds",{"title":2974,"path":2975,"stem":2976},"Тіні та фільтри в CSS","/html-css/12b.css-shadows-filters","12.html-css/12b.css-shadows-filters",{"title":2978,"path":2979,"stem":2980},"CSS Flexbox: Фундамент гнучких макетів","/html-css/css-flexbox-fundamentals","12.html-css/13.css-flexbox-fundamentals",{"title":2982,"path":2983,"stem":2984},"CSS Flexbox: Вирівнювання та Позиціонування","/html-css/css-flexbox-alignment-sizing-and-patterns","12.html-css/14.css-flexbox-alignment-sizing-and-patterns",{"title":2986,"path":2987,"stem":2988},"CSS Grid. Двовимірний макет. Частина 1","/html-css/css-layout-grid","12.html-css/15.css-layout-grid",{"title":2990,"path":2991,"stem":2992},"CSS Grid. Двовимірний макет. Частина 2","/html-css/css-layout-grid-advanced","12.html-css/16.css-layout-grid-advanced",{"title":2994,"path":2995,"stem":2996},"Позиціонування в CSS. Z-index. Stacking Context","/html-css/css-positioning","12.html-css/17.css-positioning",{"title":2998,"path":2999,"stem":3000},"CSS Анімації та Переходи","/html-css/css-animations-transitions","12.html-css/18.css-animations-transitions",{"title":3002,"path":3003,"stem":3004},"Адаптивний дизайн. Media Queries. Частина 1","/html-css/css-responsive-media-queries","12.html-css/19.css-responsive-media-queries",{"title":3006,"path":3007,"stem":3008},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","/html-css/css-responsive-advanced","12.html-css/20.css-responsive-advanced",{"title":3010,"path":3011,"stem":3012},"CSS Custom Properties. Методології. Сучасний CSS","/html-css/css-variables-methodologies","12.html-css/21.css-variables-methodologies",{"title":3014,"path":3015,"stem":3016},"Сучасний CSS 2023–2025: Нові можливості","/html-css/css-modern-features","12.html-css/22.css-modern-features",{"title":3018,"path":3019,"stem":3020},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","/html-css/22a.css-nesting-modern-syntax","12.html-css/22a.css-nesting-modern-syntax",{"title":3022,"path":3023,"stem":3024},"CSS для форм та інтерактивних станів","/html-css/css-forms-interactive-states","12.html-css/23.css-forms-interactive-states",{"title":3026,"path":3027,"stem":3028},"Доступність у CSS (CSS Accessibility)","/html-css/css-accessibility","12.html-css/24.css-accessibility",{"title":3030,"path":3031,"stem":3032},"CSS-функції та сучасні sizing primitives","/html-css/css-functions-sizing","12.html-css/25.css-functions-sizing",{"title":3034,"path":3035,"stem":3036},"Rendering Pipeline і CSS Performance","/html-css/css-rendering-performance","12.html-css/26.css-rendering-performance",{"title":3038,"path":3039,"stem":3040},"CSS Best Practices: типові ситуації та правильні рішення","/html-css/css-best-practices","12.html-css/27.css-best-practices",{"title":3042,"path":3043,"stem":3044,"children":3045,"page":59},"AWS","/aws","13.aws",[3046,3050,3054],{"title":3047,"path":3048,"stem":3049},"Реєстрація AWS акаунту та студентські програми","/aws/account-registration","13.aws/00.account-registration",{"title":3051,"path":3052,"stem":3053},"Вступ до хмарних обчислень та AWS","/aws/introduction-to-cloud","13.aws/01.introduction-to-cloud",{"title":3055,"path":3056,"stem":3057},"AWS IAM — Identity and Access Management","/aws/iam","13.aws/02.iam",{"title":3059,"path":3060,"stem":3061,"children":3062,"page":59},"Tailwind","/tailwind","21.tailwind",[3063,3067,3071,3075,3079,3083,3087,3091],{"title":3064,"path":3065,"stem":3066},"Що таке Tailwind CSS і навіщо він потрібен","/tailwind/tailwind-intro-philosophy","21.tailwind/01.tailwind-intro-philosophy",{"title":3068,"path":3069,"stem":3070},"Встановлення та налаштування Tailwind CSS v4","/tailwind/tailwind-installation-setup","21.tailwind/02.tailwind-installation-setup",{"title":3072,"path":3073,"stem":3074},"Utility-класи: основи та система Tailwind","/tailwind/tailwind-utility-classes-core","21.tailwind/03.tailwind-utility-classes-core",{"title":3076,"path":3077,"stem":3078},"Layout: Flexbox та Grid через Tailwind","/tailwind/tailwind-flexbox-grid","21.tailwind/04.tailwind-flexbox-grid",{"title":3080,"path":3081,"stem":3082},"Кастомізація теми через @theme у Tailwind v4","/tailwind/tailwind-theme-customization","21.tailwind/05.tailwind-theme-customization",{"title":3084,"path":3085,"stem":3086},"Варіанти: hover, focus, responsive, dark mode та нові v4","/tailwind/tailwind-variants-states","21.tailwind/06.tailwind-variants-states",{"title":3088,"path":3089,"stem":3090},"Типографіка та система кольорів у Tailwind v4","/tailwind/tailwind-typography-colors","21.tailwind/07.tailwind-typography-colors",{"title":3092,"path":3093,"stem":3094},"Компоненти та повторюваність: @apply, @utility та патерни","/tailwind/tailwind-components-patterns","21.tailwind/08.tailwind-components-patterns",{"title":3096,"path":3097,"stem":3098},"Тестування компонентів діаграм","/test-components","98.test-components",{"id":3100,"title":2763,"body":3101,"description":16588,"extension":16589,"links":16590,"meta":16591,"navigation":3516,"path":2764,"seo":16592,"stem":2765,"__hash__":16593},"docs/07.tools/02.kubernetes/06.deployment-basics.md",{"type":3102,"value":3103,"toc":16520},"minimark",[3104,3108,3113,3122,3125,3130,3133,3167,3170,3297,3302,3331,3337,3341,3344,3376,3381,3384,3388,3405,3433,3437,3470,3474,3481,3811,3816,3827,3834,3845,3847,3851,3854,3858,3861,4018,4021,4132,4136,4149,4214,4219,4251,4256,4320,4334,4346,4350,4353,4358,4657,4682,4686,4740,4864,4908,4912,4964,5039,5043,5046,5450,5453,5473,5475,5479,5482,5486,5492,5648,5652,5655,5675,5679,5682,5701,5706,5750,5754,5757,5784,5789,5795,5815,5818,5822,5825,5933,5936,5950,5952,5956,5962,5966,5975,5978,6225,6229,6232,6246,6249,6268,6272,6306,6329,6331,6335,6342,6346,6349,6365,6368,6394,6405,6429,6432,6458,6468,6504,6508,6511,6833,6838,6864,6866,6870,6877,6881,6884,6904,6907,6911,6914,6931,6934,6936,6940,6945,6963,6966,6999,7009,7011,7044,7047,7052,7055,7073,7107,7114,7153,7158,7178,7183,7206,7208,7212,7215,7226,7371,7377,7394,7405,7411,7428,7431,7436,7461,7466,7480,7508,7510,7514,7517,7536,7542,7565,7578,7588,7591,7596,7616,7621,7640,7676,7678,7682,7754,7756,7760,7763,8270,8275,8320,8326,8328,8332,8335,8339,8342,8591,8595,8598,8603,8611,8616,10176,10181,10347,10373,10377,10380,10385,10627,10677,10681,10684,10788,10824,10827,10843,10847,10850,10855,11347,11350,11489,11493,11496,11671,11676,11775,11969,11973,11976,11994,11997,12015,12022,12040,12043,12070,12092,12095,12254,12257,12261,12268,12287,12294,12530,12539,12631,12635,12638,12654,12656,12682,12691,12716,12719,12723,12726,12744,12777,12780,12814,12817,12834,12868,12874,12878,12881,12909,12944,12947,12951,12954,12996,13003,13028,13031,13089,13095,13097,13101,13104,13123,13126,13136,13139,13155,13158,13193,13195,13199,13202,13206,13212,13217,13254,13259,13332,13828,13830,13834,13839,13843,13898,13902,13905,14293,14295,14299,14304,14308,14336,14340,14343,14605,14607,14611,14616,14620,14644,14648,14651,15219,15221,15225,15230,15234,15267,15271,15274,15778,15780,15784,15790,15843,15847,15891,15895,15902,15922,15925,15927,15931,15934,16475,16477,16481,16506,16508,16516],[3105,3106,2763],"h1",{"id":3107},"deployment-декларативне-управління-pod",[3109,3110,3112],"h2",{"id":3111},"проблема-чому-pod-не-підходить-для-production","Проблема: чому Pod не підходить для production",[3114,3115,3116,3117,3121],"p",{},"У попередніх статтях ми детально вивчили Pod — атомарну одиницю Kubernetes. Ми навчилися створювати Pod, налаштовувати контейнери, використовувати volumes, init-контейнери та sidecar-патерни. Але наприкінці статті про Pod ми зробили важливий висновок: ",[3118,3119,3120],"strong",{},"Pod не слід створювати напряму у production",".",[3114,3123,3124],{},"Чому? Давайте розглянемо типовий сценарій розгортання веб-застосунку.",[3126,3127,3129],"h3",{"id":3128},"сценарій-розгортання-aspnet-core-web-api","Сценарій: розгортання ASP.NET Core Web API",[3114,3131,3132],{},"Уявіть, що ви розгортаєте ASP.NET Core Web API у Kubernetes. Ваші вимоги:",[3134,3135,3136,3143,3149,3155,3161],"ol",{},[3137,3138,3139,3142],"li",{},[3118,3140,3141],{},"Три репліки"," для балансування навантаження та відмовостійкості",[3137,3144,3145,3148],{},[3118,3146,3147],{},"Автоматичне відновлення"," — якщо одна репліка падає, система має створити нову автоматично",[3137,3150,3151,3154],{},[3118,3152,3153],{},"Оновлення без downtime"," — нова версія застосунку має розгортатись поступово, без зупинки сервісу",[3137,3156,3157,3160],{},[3118,3158,3159],{},"Можливість rollback"," — якщо нова версія має критичний баг, потрібно швидко повернутись до попередньої версії",[3137,3162,3163,3166],{},[3118,3164,3165],{},"Масштабування"," — можливість швидко збільшити або зменшити кількість реплік залежно від навантаження",[3114,3168,3169],{},"Якби ви використовували Pod напряму, вам довелося б:",[3171,3172,3174,3185,3196,3205,3214,3217,3224,3233,3236,3243,3251,3254,3261,3270,3279,3288],"terminal-preview",{"title":3173},"Ручне управління Pod",[3175,3176,3179],"div",{"className":3177},[3178],"line",[3180,3181,3184],"span",{"className":3182},[3183],"opacity-40","# Створити 3 Pod вручну",[3175,3186,3188,3192,3193],{"className":3187},[3178],[3180,3189,3191],{"className":3190},[3183],"$"," ",[3118,3194,3195],{},"kubectl apply -f api-pod-1.yaml",[3175,3197,3199,3192,3202],{"className":3198},[3178],[3180,3200,3191],{"className":3201},[3183],[3118,3203,3204],{},"kubectl apply -f api-pod-2.yaml",[3175,3206,3208,3192,3211],{"className":3207},[3178],[3180,3209,3191],{"className":3210},[3183],[3118,3212,3213],{},"kubectl apply -f api-pod-3.yaml",[3175,3215],{"className":3216},[3178],[3175,3218,3220],{"className":3219},[3178],[3180,3221,3223],{"className":3222},[3183],"# Постійно моніторити їхній стан",[3175,3225,3227,3192,3230],{"className":3226},[3178],[3180,3228,3191],{"className":3229},[3183],[3118,3231,3232],{},"kubectl get pods -w",[3175,3234],{"className":3235},[3178],[3175,3237,3239],{"className":3238},[3178],[3180,3240,3242],{"className":3241},[3183],"# Якщо один Pod впав — вручну створити новий",[3175,3244,3246,3192,3249],{"className":3245},[3178],[3180,3247,3191],{"className":3248},[3183],[3118,3250,3195],{},[3175,3252],{"className":3253},[3178],[3175,3255,3257],{"className":3256},[3178],[3180,3258,3260],{"className":3259},[3183],"# Для оновлення — видалити старі та створити нові (з downtime!)",[3175,3262,3264,3192,3267],{"className":3263},[3178],[3180,3265,3191],{"className":3266},[3183],[3118,3268,3269],{},"kubectl delete pod api-pod-1 api-pod-2 api-pod-3",[3175,3271,3273,3192,3276],{"className":3272},[3178],[3180,3274,3191],{"className":3275},[3183],[3118,3277,3278],{},"kubectl apply -f api-pod-1-v2.yaml",[3175,3280,3282,3192,3285],{"className":3281},[3178],[3180,3283,3191],{"className":3284},[3183],[3118,3286,3287],{},"kubectl apply -f api-pod-2-v2.yaml",[3175,3289,3291,3192,3294],{"className":3290},[3178],[3180,3292,3191],{"className":3293},[3183],[3118,3295,3296],{},"kubectl apply -f api-pod-3-v2.yaml",[3114,3298,3299],{},[3118,3300,3301],{},"Проблеми цього підходу:",[3303,3304,3305,3311,3316,3321,3326],"card-group",{},[3306,3307,3310],"card",{"icon":3308,"title":3309},"i-heroicons-x-circle","Немає self-healing","Якщо Pod падає або вузол виходить з ладу — Pod просто зникає. Kubernetes не створить новий автоматично. Вам потрібно вручну моніторити стан та відновлювати Pod.",[3306,3312,3315],{"icon":3313,"title":3314},"i-heroicons-arrows-pointing-out","Немає масштабування","Щоб збільшити кількість реплік з 3 до 5, потрібно вручну створити 2 нові Pod. Щоб зменшити — вручну видалити зайві. Немає автоматизації.",[3306,3317,3320],{"icon":3318,"title":3319},"i-heroicons-arrow-down-circle","Downtime при оновленні","Щоб оновити версію застосунку, потрібно спочатку видалити всі старі Pod, потім створити нові. Є момент, коли жоден Pod не працює — це downtime.",[3306,3322,3325],{"icon":3323,"title":3324},"i-heroicons-clock","Немає історії версій","Якщо нова версія має баг, немає простого способу повернутись до попередньої. Доведеться вручну змінювати маніфести та застосовувати знову.",[3306,3327,3330],{"icon":3328,"title":3329},"i-heroicons-wrench","Ручна робота","Кожна операція вимагає вашого втручання. Це не масштабується — якщо у вас 10 застосунків по 5 реплік кожен, це 50 Pod для ручного управління.",[3114,3332,3333,3334,3121],{},"Це неприйнятно для production. Саме тому існує ",[3118,3335,3336],{},"Deployment",[3126,3338,3340],{"id":3339},"що-потрібно-замість-ручного-управління","Що потрібно замість ручного управління",[3114,3342,3343],{},"Нам потрібен механізм, який:",[3134,3345,3346,3352,3358,3364,3370],{},[3137,3347,3348,3351],{},[3118,3349,3350],{},"Автоматично підтримує задану кількість реплік"," — якщо Pod падає, система сама створює новий",[3137,3353,3354,3357],{},[3118,3355,3356],{},"Дозволяє декларативно описати бажаний стан"," — \"хочу 3 репліки версії 1.0.0\", а система сама досягає цього стану",[3137,3359,3360,3363],{},[3118,3361,3362],{},"Виконує оновлення без downtime"," — поступово замінює старі Pod новими, завжди залишаючи працюючі репліки",[3137,3365,3366,3369],{},[3118,3367,3368],{},"Зберігає історію версій"," — можна швидко повернутись до попередньої версії однією командою",[3137,3371,3372,3375],{},[3118,3373,3374],{},"Легко масштабується"," — змінити кількість реплік можна однією командою або автоматично",[3114,3377,3378,3379,3121],{},"Саме це і робить ",[3118,3380,3336],{},[3382,3383],"hr",{},[3109,3385,3387],{"id":3386},"що-таке-deployment-формальне-визначення","Що таке Deployment: формальне визначення",[3114,3389,3390,3392,3393,3396,3397,3400,3401,3404],{},[3118,3391,3336],{}," — це ресурс Kubernetes, який забезпечує ",[3118,3394,3395],{},"декларативне управління"," набором ідентичних Pod. Ви описуєте ",[3118,3398,3399],{},"бажаний стан"," (скільки реплік, яка версія образу, які ресурси), а Kubernetes ",[3118,3402,3403],{},"автоматично"," підтримує цей стан.",[3406,3407,3408,3425],"note",{},[3114,3409,3410,3413,3414,3417,3418,3420,3421,3424],{},[3118,3411,3412],{},"Ключова ідея:"," Deployment працює за принципом ",[3118,3415,3416],{},"reconciliation loop"," (цикл узгодження). Kubernetes постійно порівнює ",[3118,3419,3399],{}," (описаний у YAML) з ",[3118,3422,3423],{},"поточним станом"," (що насправді працює в кластері) та виконує дії для усунення розбіжностей.",[3114,3426,3427,3428,3432],{},"Приклад: Ви вказали ",[3429,3430,3431],"code",{},"replicas: 3",", але працює лише 2 Pod (один впав). Deployment Controller виявляє розбіжність та створює третій Pod. Ви нічого не робите вручну — система сама усуває проблему.",[3126,3434,3436],{"id":3435},"основні-можливості-deployment","Основні можливості Deployment",[3303,3438,3439,3444,3452,3457,3465],{},[3306,3440,3443],{"icon":3441,"title":3442},"i-heroicons-arrow-path","Self-healing","Якщо Pod видаляється, падає або вузол виходить з ладу — Deployment автоматично створює новий Pod, підтримуючи задану кількість реплік. Це відбувається без вашого втручання.",[3306,3445,3447,3448,3451],{"icon":3313,"title":3446},"Декларативне масштабування","Змінити кількість реплік можна однією командою (",[3429,3449,3450],{},"kubectl scale",") або редагуванням YAML. Kubernetes сам створить або видалить Pod для досягнення бажаної кількості.",[3306,3453,3456],{"icon":3454,"title":3455},"i-heroicons-arrow-up-circle","Rolling Updates","Оновлення відбувається поступово: нові Pod створюються, перевіряються на готовність, і лише після цього старі видаляються. Завжди є працюючі репліки — немає downtime.",[3306,3458,3461,3462,3121],{"icon":3459,"title":3460},"i-heroicons-arrow-uturn-left","Rollback","Kubernetes зберігає історію версій Deployment (за замовчуванням 10 останніх). Повернутись до попередньої версії можна однією командою ",[3429,3463,3464],{},"kubectl rollout undo",[3306,3466,3469],{"icon":3467,"title":3468},"i-heroicons-document-duplicate","Версіонування","Кожна зміна у специфікації Pod (образ, змінні оточення, ресурси) створює нову ревізію. Ви можете переглянути історію та повернутись до будь-якої версії.",[3126,3471,3473],{"id":3472},"архітектура-deployment-replicaset-pod","Архітектура: Deployment → ReplicaSet → Pod",[3114,3475,3476,3477,3480],{},"Deployment не створює Pod напряму. Він використовує проміжний ресурс — ",[3118,3478,3479],{},"ReplicaSet",":",[3482,3483,3484],"plant-uml",{},[3485,3486,3491],"pre",{"className":3487,"code":3488,"language":3489,"meta":3490,"style":3490},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Kubernetes Control Plane\" #f8f9fa {\n    component \"Deployment Controller\" as dc #e3f2fd\n    component \"ReplicaSet Controller\" as rsc #fff3e0\n}\n\npackage \"Cluster\" #f8f9fa {\n    component \"Deployment\\n(myapp)\" as dep #e3f2fd {\n        [replicas: 3]\n        [image: aspnetapp]\n    }\n    \n    component \"ReplicaSet\\n(myapp-abc123)\" as rs #fff3e0 {\n        [replicas: 3]\n        [template: aspnetapp]\n    }\n    \n    package \"Pods\" #e8f5e9 {\n        component \"Pod 1\" as p1\n        component \"Pod 2\" as p2\n        component \"Pod 3\" as p3\n    }\n}\n\ndc --> dep : manages\ndep --> rs : creates\nrsc --> rs : manages\nrs --> p1 : creates\nrs --> p2 : creates\nrs --> p3 : creates\n\nnote right of dep\n    Deployment описує\n    бажаний стан:\n    - Скільки реплік\n    - Який образ\n    - Стратегія оновлення\nend note\n\nnote right of rs\n    ReplicaSet підтримує\n    задану кількість Pod\n    з певним шаблоном\nend note\n\nnote right of p1\n    Pod — це те,\n    що насправді\n    виконується\nend note\n\n@enduml\n","plantuml","",[3429,3492,3493,3499,3505,3511,3518,3524,3530,3536,3542,3547,3553,3559,3565,3571,3577,3583,3589,3594,3600,3605,3610,3616,3622,3628,3634,3639,3644,3649,3655,3661,3667,3673,3679,3685,3690,3696,3702,3708,3714,3720,3726,3732,3737,3743,3749,3755,3761,3766,3771,3777,3783,3789,3795,3800,3805],{"__ignoreMap":3490},[3180,3494,3496],{"class":3178,"line":3495},1,[3180,3497,3498],{},"@startuml\n",[3180,3500,3502],{"class":3178,"line":3501},2,[3180,3503,3504],{},"skinparam style plain\n",[3180,3506,3508],{"class":3178,"line":3507},3,[3180,3509,3510],{},"skinparam backgroundColor #ffffff\n",[3180,3512,3514],{"class":3178,"line":3513},4,[3180,3515,3517],{"emptyLinePlaceholder":3516},true,"\n",[3180,3519,3521],{"class":3178,"line":3520},5,[3180,3522,3523],{},"package \"Kubernetes Control Plane\" #f8f9fa {\n",[3180,3525,3527],{"class":3178,"line":3526},6,[3180,3528,3529],{},"    component \"Deployment Controller\" as dc #e3f2fd\n",[3180,3531,3533],{"class":3178,"line":3532},7,[3180,3534,3535],{},"    component \"ReplicaSet Controller\" as rsc #fff3e0\n",[3180,3537,3539],{"class":3178,"line":3538},8,[3180,3540,3541],{},"}\n",[3180,3543,3545],{"class":3178,"line":3544},9,[3180,3546,3517],{"emptyLinePlaceholder":3516},[3180,3548,3550],{"class":3178,"line":3549},10,[3180,3551,3552],{},"package \"Cluster\" #f8f9fa {\n",[3180,3554,3556],{"class":3178,"line":3555},11,[3180,3557,3558],{},"    component \"Deployment\\n(myapp)\" as dep #e3f2fd {\n",[3180,3560,3562],{"class":3178,"line":3561},12,[3180,3563,3564],{},"        [replicas: 3]\n",[3180,3566,3568],{"class":3178,"line":3567},13,[3180,3569,3570],{},"        [image: aspnetapp]\n",[3180,3572,3574],{"class":3178,"line":3573},14,[3180,3575,3576],{},"    }\n",[3180,3578,3580],{"class":3178,"line":3579},15,[3180,3581,3582],{},"    \n",[3180,3584,3586],{"class":3178,"line":3585},16,[3180,3587,3588],{},"    component \"ReplicaSet\\n(myapp-abc123)\" as rs #fff3e0 {\n",[3180,3590,3592],{"class":3178,"line":3591},17,[3180,3593,3564],{},[3180,3595,3597],{"class":3178,"line":3596},18,[3180,3598,3599],{},"        [template: aspnetapp]\n",[3180,3601,3603],{"class":3178,"line":3602},19,[3180,3604,3576],{},[3180,3606,3608],{"class":3178,"line":3607},20,[3180,3609,3582],{},[3180,3611,3613],{"class":3178,"line":3612},21,[3180,3614,3615],{},"    package \"Pods\" #e8f5e9 {\n",[3180,3617,3619],{"class":3178,"line":3618},22,[3180,3620,3621],{},"        component \"Pod 1\" as p1\n",[3180,3623,3625],{"class":3178,"line":3624},23,[3180,3626,3627],{},"        component \"Pod 2\" as p2\n",[3180,3629,3631],{"class":3178,"line":3630},24,[3180,3632,3633],{},"        component \"Pod 3\" as p3\n",[3180,3635,3637],{"class":3178,"line":3636},25,[3180,3638,3576],{},[3180,3640,3642],{"class":3178,"line":3641},26,[3180,3643,3541],{},[3180,3645,3647],{"class":3178,"line":3646},27,[3180,3648,3517],{"emptyLinePlaceholder":3516},[3180,3650,3652],{"class":3178,"line":3651},28,[3180,3653,3654],{},"dc --> dep : manages\n",[3180,3656,3658],{"class":3178,"line":3657},29,[3180,3659,3660],{},"dep --> rs : creates\n",[3180,3662,3664],{"class":3178,"line":3663},30,[3180,3665,3666],{},"rsc --> rs : manages\n",[3180,3668,3670],{"class":3178,"line":3669},31,[3180,3671,3672],{},"rs --> p1 : creates\n",[3180,3674,3676],{"class":3178,"line":3675},32,[3180,3677,3678],{},"rs --> p2 : creates\n",[3180,3680,3682],{"class":3178,"line":3681},33,[3180,3683,3684],{},"rs --> p3 : creates\n",[3180,3686,3688],{"class":3178,"line":3687},34,[3180,3689,3517],{"emptyLinePlaceholder":3516},[3180,3691,3693],{"class":3178,"line":3692},35,[3180,3694,3695],{},"note right of dep\n",[3180,3697,3699],{"class":3178,"line":3698},36,[3180,3700,3701],{},"    Deployment описує\n",[3180,3703,3705],{"class":3178,"line":3704},37,[3180,3706,3707],{},"    бажаний стан:\n",[3180,3709,3711],{"class":3178,"line":3710},38,[3180,3712,3713],{},"    - Скільки реплік\n",[3180,3715,3717],{"class":3178,"line":3716},39,[3180,3718,3719],{},"    - Який образ\n",[3180,3721,3723],{"class":3178,"line":3722},40,[3180,3724,3725],{},"    - Стратегія оновлення\n",[3180,3727,3729],{"class":3178,"line":3728},41,[3180,3730,3731],{},"end note\n",[3180,3733,3735],{"class":3178,"line":3734},42,[3180,3736,3517],{"emptyLinePlaceholder":3516},[3180,3738,3740],{"class":3178,"line":3739},43,[3180,3741,3742],{},"note right of rs\n",[3180,3744,3746],{"class":3178,"line":3745},44,[3180,3747,3748],{},"    ReplicaSet підтримує\n",[3180,3750,3752],{"class":3178,"line":3751},45,[3180,3753,3754],{},"    задану кількість Pod\n",[3180,3756,3758],{"class":3178,"line":3757},46,[3180,3759,3760],{},"    з певним шаблоном\n",[3180,3762,3764],{"class":3178,"line":3763},47,[3180,3765,3731],{},[3180,3767,3769],{"class":3178,"line":3768},48,[3180,3770,3517],{"emptyLinePlaceholder":3516},[3180,3772,3774],{"class":3178,"line":3773},49,[3180,3775,3776],{},"note right of p1\n",[3180,3778,3780],{"class":3178,"line":3779},50,[3180,3781,3782],{},"    Pod — це те,\n",[3180,3784,3786],{"class":3178,"line":3785},51,[3180,3787,3788],{},"    що насправді\n",[3180,3790,3792],{"class":3178,"line":3791},52,[3180,3793,3794],{},"    виконується\n",[3180,3796,3798],{"class":3178,"line":3797},53,[3180,3799,3731],{},[3180,3801,3803],{"class":3178,"line":3802},54,[3180,3804,3517],{"emptyLinePlaceholder":3516},[3180,3806,3808],{"class":3178,"line":3807},55,[3180,3809,3810],{},"@enduml\n",[3114,3812,3813],{},[3118,3814,3815],{},"Чому потрібен ReplicaSet?",[3114,3817,3818,3819,3822,3823,3826],{},"ReplicaSet відповідає за ",[3118,3820,3821],{},"підтримку заданої кількості реплік",". Його єдине завдання — гарантувати, що завжди працює рівно ",[3429,3824,3825],{},"N"," Pod з певним шаблоном.",[3114,3828,3829,3830,3833],{},"Deployment використовує ReplicaSet для реалізації ",[3118,3831,3832],{},"rolling updates",": при оновленні створюється новий ReplicaSet з новим шаблоном Pod, а старий поступово зменшується до нуля. Це дозволяє виконувати оновлення без downtime.",[3835,3836,3837,3840,3841,3844],"warning",{},[3118,3838,3839],{},"Важливо:"," Ви ",[3118,3842,3843],{},"ніколи"," не створюєте ReplicaSet напряму. Завжди використовуйте Deployment, який автоматично керує ReplicaSet. Пряме створення ReplicaSet позбавляє вас можливості rolling updates та rollback.",[3382,3846],{},[3109,3848,3850],{"id":3849},"анатомія-deployment-структура-yaml","Анатомія Deployment: структура YAML",[3114,3852,3853],{},"Тепер розглянемо, як описати Deployment у YAML. Почнемо з мінімального прикладу та поступово додаватимемо поля.",[3126,3855,3857],{"id":3856},"мінімальний-deployment","Мінімальний Deployment",[3114,3859,3860],{},"Найпростіший Deployment містить лише обов'язкові поля:",[3485,3862,3866],{"className":3863,"code":3864,"language":3865,"meta":3490,"style":3490},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus","apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx-deployment\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n","yaml",[3429,3867,3868,3882,3892,3900,3910,3917,3928,3935,3942,3952,3959,3966,3973,3982,3989,3996,4008],{"__ignoreMap":3490},[3180,3869,3870,3874,3878],{"class":3178,"line":3495},[3180,3871,3873],{"class":3872},"sKtos","apiVersion",[3180,3875,3877],{"class":3876},"sHH4Y",": ",[3180,3879,3881],{"class":3880},"su9tN","apps/v1\n",[3180,3883,3884,3887,3889],{"class":3178,"line":3501},[3180,3885,3886],{"class":3872},"kind",[3180,3888,3877],{"class":3876},[3180,3890,3891],{"class":3880},"Deployment\n",[3180,3893,3894,3897],{"class":3178,"line":3507},[3180,3895,3896],{"class":3872},"metadata",[3180,3898,3899],{"class":3876},":\n",[3180,3901,3902,3905,3907],{"class":3178,"line":3513},[3180,3903,3904],{"class":3872},"  name",[3180,3906,3877],{"class":3876},[3180,3908,3909],{"class":3880},"nginx-deployment\n",[3180,3911,3912,3915],{"class":3178,"line":3520},[3180,3913,3914],{"class":3872},"spec",[3180,3916,3899],{"class":3876},[3180,3918,3919,3922,3924],{"class":3178,"line":3526},[3180,3920,3921],{"class":3872},"  replicas",[3180,3923,3877],{"class":3876},[3180,3925,3927],{"class":3926},"sJj4R","3\n",[3180,3929,3930,3933],{"class":3178,"line":3532},[3180,3931,3932],{"class":3872},"  selector",[3180,3934,3899],{"class":3876},[3180,3936,3937,3940],{"class":3178,"line":3538},[3180,3938,3939],{"class":3872},"    matchLabels",[3180,3941,3899],{"class":3876},[3180,3943,3944,3947,3949],{"class":3178,"line":3544},[3180,3945,3946],{"class":3872},"      app",[3180,3948,3877],{"class":3876},[3180,3950,3951],{"class":3880},"nginx\n",[3180,3953,3954,3957],{"class":3178,"line":3549},[3180,3955,3956],{"class":3872},"  template",[3180,3958,3899],{"class":3876},[3180,3960,3961,3964],{"class":3178,"line":3555},[3180,3962,3963],{"class":3872},"    metadata",[3180,3965,3899],{"class":3876},[3180,3967,3968,3971],{"class":3178,"line":3561},[3180,3969,3970],{"class":3872},"      labels",[3180,3972,3899],{"class":3876},[3180,3974,3975,3978,3980],{"class":3178,"line":3567},[3180,3976,3977],{"class":3872},"        app",[3180,3979,3877],{"class":3876},[3180,3981,3951],{"class":3880},[3180,3983,3984,3987],{"class":3178,"line":3573},[3180,3985,3986],{"class":3872},"    spec",[3180,3988,3899],{"class":3876},[3180,3990,3991,3994],{"class":3178,"line":3579},[3180,3992,3993],{"class":3872},"      containers",[3180,3995,3899],{"class":3876},[3180,3997,3998,4001,4004,4006],{"class":3178,"line":3585},[3180,3999,4000],{"class":3876},"        - ",[3180,4002,4003],{"class":3872},"name",[3180,4005,3877],{"class":3876},[3180,4007,3951],{"class":3880},[3180,4009,4010,4013,4015],{"class":3178,"line":3591},[3180,4011,4012],{"class":3872},"          image",[3180,4014,3877],{"class":3876},[3180,4016,4017],{"class":3880},"nginx:1.27\n",[3114,4019,4020],{},"Розберемо кожне поле детально.",[4022,4023,4024,4042,4048,4056,4062,4083,4100,4114,4125],"field-group",{},[4025,4026,4029,4030,4033,4034,4037,4038,4041],"field",{"name":3873,"type":4027,"required":4028},"string","true","Для Deployment використовується ",[3429,4031,4032],{},"apps/v1"," (не просто ",[3429,4035,4036],{},"v1",", як для Pod). Це означає, що Deployment належить до групи API ",[3429,4039,4040],{},"apps",". Ця група містить ресурси для управління застосунками: Deployment, StatefulSet, DaemonSet, ReplicaSet.",[4025,4043,4044,4045,4047],{"name":3886,"type":4027,"required":4028},"Тип ресурсу — ",[3429,4046,3336],{},". Це вказує Kubernetes, що ви створюєте саме Deployment, а не Pod або інший ресурс.",[4025,4049,4051,4052,4055],{"name":4050,"type":4027,"required":4028},"metadata.name","Унікальне ім'я Deployment у межах namespace. Має відповідати DNS-стандарту: малі літери, цифри, дефіси. Максимум 253 символи. Це ім'я буде використовуватись у командах ",[3429,4053,4054],{},"kubectl"," та як префікс для імен ReplicaSet та Pod.",[4025,4057,4061],{"name":4058,"type":4059,"default":4060},"spec.replicas","integer","1","Бажана кількість реплік Pod. Deployment Controller постійно підтримує саме цю кількість. Якщо Pod падає — створюється новий. Якщо реплік більше, ніж задано — зайві видаляються. Якщо не вказано — за замовчуванням створюється 1 репліка.",[4025,4063,4066,4067,4070,4071,4074,4075,4078,4079,4082],{"name":4064,"type":4065,"required":4028},"spec.selector","object","Селектор для вибору Pod, якими керує цей Deployment. ",[3118,4068,4069],{},"Критично важливо:"," мітки у ",[3429,4072,4073],{},"selector.matchLabels"," мають ",[3118,4076,4077],{},"точно збігатись"," з мітками у ",[3429,4080,4081],{},"template.metadata.labels",". Якщо мітки не збігаються — Deployment не зможе знайти свої Pod і буде постійно створювати нові.",[4025,4084,4087,4088,4091,4092,4095,4096,4099],{"name":4085,"type":4086,"required":4028},"spec.selector.matchLabels","map","Набір міток (key-value пар), за якими Deployment ідентифікує свої Pod. Deployment вибирає всі Pod, які мають ",[3118,4089,4090],{},"всі"," вказані мітки. Наприклад, ",[3429,4093,4094],{},"app: nginx"," означає \"вибрати всі Pod з міткою ",[3429,4097,4098],{},"app=nginx","\".",[4025,4101,4103,4104,4107,4108,4110,4111,4113],{"name":4102,"type":4065,"required":4028},"spec.template","Шаблон Pod, який буде створюватись Deployment. Це ",[3118,4105,4106],{},"повна специфікація Pod"," (як у статті про Pod), але без полів ",[3429,4109,3873],{}," та ",[3429,4112,3886],{},". Всі Pod, створені цим Deployment, будуть ідентичними копіями цього шаблону.",[4025,4115,4117,4118,4121,4122,4124],{"name":4116,"type":4086,"required":4028},"spec.template.metadata.labels","Мітки, які будуть додані до кожного створеного Pod. ",[3118,4119,4120],{},"Мають збігатись"," з ",[3429,4123,4085],{},". Це критично важливо для роботи Deployment.",[4025,4126,4128,4129,4131],{"name":4127,"type":4065,"required":4028},"spec.template.spec","Специфікація Pod — контейнери, volumes, init-контейнери тощо. Це те саме поле ",[3429,4130,3914],{},", яке ви використовували при створенні Pod напряму. Всі поля з статті про Pod доступні тут.",[3126,4133,4135],{"id":4134},"важливість-selector-та-labels","Важливість selector та labels",[3114,4137,4138,4139,4110,4142,4144,4145,4148],{},"Зв'язок між ",[3429,4140,4141],{},"selector",[3429,4143,4081],{}," — це ",[3118,4146,4147],{},"найважливіша"," частина Deployment. Давайте розберемо детально:",[3485,4150,4152],{"className":3863,"code":4151,"language":3865,"meta":3490,"style":3490},"spec:\n  selector:\n    matchLabels:\n      app: nginx  # ← Deployment шукає Pod з цією міткою\n  template:\n    metadata:\n      labels:\n        app: nginx  # ← Pod створюються з цією міткою\n",[3429,4153,4154,4160,4166,4172,4185,4191,4197,4203],{"__ignoreMap":3490},[3180,4155,4156,4158],{"class":3178,"line":3495},[3180,4157,3914],{"class":3872},[3180,4159,3899],{"class":3876},[3180,4161,4162,4164],{"class":3178,"line":3501},[3180,4163,3932],{"class":3872},[3180,4165,3899],{"class":3876},[3180,4167,4168,4170],{"class":3178,"line":3507},[3180,4169,3939],{"class":3872},[3180,4171,3899],{"class":3876},[3180,4173,4174,4176,4178,4181],{"class":3178,"line":3513},[3180,4175,3946],{"class":3872},[3180,4177,3877],{"class":3876},[3180,4179,4180],{"class":3880},"nginx",[3180,4182,4184],{"class":4183},"spJ8K","  # ← Deployment шукає Pod з цією міткою\n",[3180,4186,4187,4189],{"class":3178,"line":3520},[3180,4188,3956],{"class":3872},[3180,4190,3899],{"class":3876},[3180,4192,4193,4195],{"class":3178,"line":3526},[3180,4194,3963],{"class":3872},[3180,4196,3899],{"class":3876},[3180,4198,4199,4201],{"class":3178,"line":3532},[3180,4200,3970],{"class":3872},[3180,4202,3899],{"class":3876},[3180,4204,4205,4207,4209,4211],{"class":3178,"line":3538},[3180,4206,3977],{"class":3872},[3180,4208,3877],{"class":3876},[3180,4210,4180],{"class":3880},[3180,4212,4213],{"class":4183},"  # ← Pod створюються з цією міткою\n",[3114,4215,4216],{},[3118,4217,4218],{},"Що відбувається:",[3134,4220,4221,4230,4238,4245],{},[3137,4222,4223,4224,4226,4227,4229],{},"Deployment створює Pod з міткою ",[3429,4225,4094],{}," (з ",[3429,4228,4081],{},")",[3137,4231,4232,4233,4235,4236,4229],{},"Deployment Controller постійно шукає Pod з міткою ",[3429,4234,4094],{}," (через ",[3429,4237,4073],{},[3137,4239,4240,4241,4244],{},"Якщо знайдено менше Pod, ніж ",[3429,4242,4243],{},"replicas"," — створює нові",[3137,4246,4247,4248,4250],{},"Якщо знайдено більше Pod, ніж ",[3429,4249,4243],{}," — видаляє зайві",[3114,4252,4253],{},[3118,4254,4255],{},"Типова помилка новачків:",[3485,4257,4259],{"className":3863,"code":4258,"language":3865,"meta":3490,"style":3490},"spec:\n  selector:\n    matchLabels:\n      app: nginx  # ← Шукає Pod з app=nginx\n  template:\n    metadata:\n      labels:\n        app: web  # ← Створює Pod з app=web (ПОМИЛКА!)\n",[3429,4260,4261,4267,4273,4279,4290,4296,4302,4308],{"__ignoreMap":3490},[3180,4262,4263,4265],{"class":3178,"line":3495},[3180,4264,3914],{"class":3872},[3180,4266,3899],{"class":3876},[3180,4268,4269,4271],{"class":3178,"line":3501},[3180,4270,3932],{"class":3872},[3180,4272,3899],{"class":3876},[3180,4274,4275,4277],{"class":3178,"line":3507},[3180,4276,3939],{"class":3872},[3180,4278,3899],{"class":3876},[3180,4280,4281,4283,4285,4287],{"class":3178,"line":3513},[3180,4282,3946],{"class":3872},[3180,4284,3877],{"class":3876},[3180,4286,4180],{"class":3880},[3180,4288,4289],{"class":4183},"  # ← Шукає Pod з app=nginx\n",[3180,4291,4292,4294],{"class":3178,"line":3520},[3180,4293,3956],{"class":3872},[3180,4295,3899],{"class":3876},[3180,4297,4298,4300],{"class":3178,"line":3526},[3180,4299,3963],{"class":3872},[3180,4301,3899],{"class":3876},[3180,4303,4304,4306],{"class":3178,"line":3532},[3180,4305,3970],{"class":3872},[3180,4307,3899],{"class":3876},[3180,4309,4310,4312,4314,4317],{"class":3178,"line":3538},[3180,4311,3977],{"class":3872},[3180,4313,3877],{"class":3876},[3180,4315,4316],{"class":3880},"web",[3180,4318,4319],{"class":4183},"  # ← Створює Pod з app=web (ПОМИЛКА!)\n",[3114,4321,4322,4323,4326,4327,4329,4330,4333],{},"У цьому випадку Deployment створить Pod з міткою ",[3429,4324,4325],{},"app: web",", але шукатиме Pod з міткою ",[3429,4328,4094],{},". Він не знайде жодного Pod і буде ",[3118,4331,4332],{},"нескінченно створювати нові",", бо вважатиме, що реплік недостатньо.",[3835,4335,4336,4338,4339,4110,4341,4074,4343,4345],{},[3118,4337,4069],{}," Мітки у ",[3429,4340,4073],{},[3429,4342,4081],{},[3118,4344,4077],{},". Це не рекомендація — це вимога. Kubernetes навіть не дозволить створити Deployment з неспівпадаючими мітками (отримаєте помилку валідації).",[3126,4347,4349],{"id":4348},"повна-специфікація-deployment","Повна специфікація Deployment",[3114,4351,4352],{},"Тепер розглянемо всі доступні поля Deployment. Почнемо з базових, потім перейдемо до розширених.",[4354,4355,4357],"h4",{"id":4356},"базові-поля-обовязкові-та-найчастіше-використовувані","Базові поля (обов'язкові та найчастіше використовувані)",[3485,4359,4361],{"className":3863,"code":4360,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: myapp-deployment\n  namespace: default\n  labels:\n    app: myapp\n    version: \"1.0\"\n  annotations:\n    description: \"My application deployment\"\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: myapp\n  template:\n    metadata:\n      labels:\n        app: myapp\n        version: \"1.0\"\n    spec:\n      containers:\n        - name: app\n          image: mcr.microsoft.com/dotnet/samples:aspnetapp\n          ports:\n            - containerPort: 8080\n          env:\n            - name: ENV\n              value: \"production\"\n          resources:\n            requests:\n              memory: \"128Mi\"\n              cpu: \"100m\"\n            limits:\n              memory: \"256Mi\"\n              cpu: \"500m\"\n",[3429,4362,4363,4371,4379,4385,4394,4404,4411,4421,4432,4439,4449,4455,4463,4469,4475,4483,4489,4495,4501,4509,4518,4524,4530,4541,4550,4557,4570,4577,4588,4598,4605,4612,4622,4632,4639,4648],{"__ignoreMap":3490},[3180,4364,4365,4367,4369],{"class":3178,"line":3495},[3180,4366,3873],{"class":3872},[3180,4368,3877],{"class":3876},[3180,4370,3881],{"class":3880},[3180,4372,4373,4375,4377],{"class":3178,"line":3501},[3180,4374,3886],{"class":3872},[3180,4376,3877],{"class":3876},[3180,4378,3891],{"class":3880},[3180,4380,4381,4383],{"class":3178,"line":3507},[3180,4382,3896],{"class":3872},[3180,4384,3899],{"class":3876},[3180,4386,4387,4389,4391],{"class":3178,"line":3513},[3180,4388,3904],{"class":3872},[3180,4390,3877],{"class":3876},[3180,4392,4393],{"class":3880},"myapp-deployment\n",[3180,4395,4396,4399,4401],{"class":3178,"line":3520},[3180,4397,4398],{"class":3872},"  namespace",[3180,4400,3877],{"class":3876},[3180,4402,4403],{"class":3880},"default\n",[3180,4405,4406,4409],{"class":3178,"line":3526},[3180,4407,4408],{"class":3872},"  labels",[3180,4410,3899],{"class":3876},[3180,4412,4413,4416,4418],{"class":3178,"line":3532},[3180,4414,4415],{"class":3872},"    app",[3180,4417,3877],{"class":3876},[3180,4419,4420],{"class":3880},"myapp\n",[3180,4422,4423,4426,4428],{"class":3178,"line":3538},[3180,4424,4425],{"class":3872},"    version",[3180,4427,3877],{"class":3876},[3180,4429,4431],{"class":4430},"sbdoH","\"1.0\"\n",[3180,4433,4434,4437],{"class":3178,"line":3544},[3180,4435,4436],{"class":3872},"  annotations",[3180,4438,3899],{"class":3876},[3180,4440,4441,4444,4446],{"class":3178,"line":3549},[3180,4442,4443],{"class":3872},"    description",[3180,4445,3877],{"class":3876},[3180,4447,4448],{"class":4430},"\"My application deployment\"\n",[3180,4450,4451,4453],{"class":3178,"line":3555},[3180,4452,3914],{"class":3872},[3180,4454,3899],{"class":3876},[3180,4456,4457,4459,4461],{"class":3178,"line":3561},[3180,4458,3921],{"class":3872},[3180,4460,3877],{"class":3876},[3180,4462,3927],{"class":3926},[3180,4464,4465,4467],{"class":3178,"line":3567},[3180,4466,3932],{"class":3872},[3180,4468,3899],{"class":3876},[3180,4470,4471,4473],{"class":3178,"line":3573},[3180,4472,3939],{"class":3872},[3180,4474,3899],{"class":3876},[3180,4476,4477,4479,4481],{"class":3178,"line":3579},[3180,4478,3946],{"class":3872},[3180,4480,3877],{"class":3876},[3180,4482,4420],{"class":3880},[3180,4484,4485,4487],{"class":3178,"line":3585},[3180,4486,3956],{"class":3872},[3180,4488,3899],{"class":3876},[3180,4490,4491,4493],{"class":3178,"line":3591},[3180,4492,3963],{"class":3872},[3180,4494,3899],{"class":3876},[3180,4496,4497,4499],{"class":3178,"line":3596},[3180,4498,3970],{"class":3872},[3180,4500,3899],{"class":3876},[3180,4502,4503,4505,4507],{"class":3178,"line":3602},[3180,4504,3977],{"class":3872},[3180,4506,3877],{"class":3876},[3180,4508,4420],{"class":3880},[3180,4510,4511,4514,4516],{"class":3178,"line":3607},[3180,4512,4513],{"class":3872},"        version",[3180,4515,3877],{"class":3876},[3180,4517,4431],{"class":4430},[3180,4519,4520,4522],{"class":3178,"line":3612},[3180,4521,3986],{"class":3872},[3180,4523,3899],{"class":3876},[3180,4525,4526,4528],{"class":3178,"line":3618},[3180,4527,3993],{"class":3872},[3180,4529,3899],{"class":3876},[3180,4531,4532,4534,4536,4538],{"class":3178,"line":3624},[3180,4533,4000],{"class":3876},[3180,4535,4003],{"class":3872},[3180,4537,3877],{"class":3876},[3180,4539,4540],{"class":3880},"app\n",[3180,4542,4543,4545,4547],{"class":3178,"line":3630},[3180,4544,4012],{"class":3872},[3180,4546,3877],{"class":3876},[3180,4548,4549],{"class":3880},"mcr.microsoft.com/dotnet/samples:aspnetapp\n",[3180,4551,4552,4555],{"class":3178,"line":3636},[3180,4553,4554],{"class":3872},"          ports",[3180,4556,3899],{"class":3876},[3180,4558,4559,4562,4565,4567],{"class":3178,"line":3641},[3180,4560,4561],{"class":3876},"            - ",[3180,4563,4564],{"class":3872},"containerPort",[3180,4566,3877],{"class":3876},[3180,4568,4569],{"class":3926},"8080\n",[3180,4571,4572,4575],{"class":3178,"line":3646},[3180,4573,4574],{"class":3872},"          env",[3180,4576,3899],{"class":3876},[3180,4578,4579,4581,4583,4585],{"class":3178,"line":3651},[3180,4580,4561],{"class":3876},[3180,4582,4003],{"class":3872},[3180,4584,3877],{"class":3876},[3180,4586,4587],{"class":3880},"ENV\n",[3180,4589,4590,4593,4595],{"class":3178,"line":3657},[3180,4591,4592],{"class":3872},"              value",[3180,4594,3877],{"class":3876},[3180,4596,4597],{"class":4430},"\"production\"\n",[3180,4599,4600,4603],{"class":3178,"line":3663},[3180,4601,4602],{"class":3872},"          resources",[3180,4604,3899],{"class":3876},[3180,4606,4607,4610],{"class":3178,"line":3669},[3180,4608,4609],{"class":3872},"            requests",[3180,4611,3899],{"class":3876},[3180,4613,4614,4617,4619],{"class":3178,"line":3675},[3180,4615,4616],{"class":3872},"              memory",[3180,4618,3877],{"class":3876},[3180,4620,4621],{"class":4430},"\"128Mi\"\n",[3180,4623,4624,4627,4629],{"class":3178,"line":3681},[3180,4625,4626],{"class":3872},"              cpu",[3180,4628,3877],{"class":3876},[3180,4630,4631],{"class":4430},"\"100m\"\n",[3180,4633,4634,4637],{"class":3178,"line":3687},[3180,4635,4636],{"class":3872},"            limits",[3180,4638,3899],{"class":3876},[3180,4640,4641,4643,4645],{"class":3178,"line":3692},[3180,4642,4616],{"class":3872},[3180,4644,3877],{"class":3876},[3180,4646,4647],{"class":4430},"\"256Mi\"\n",[3180,4649,4650,4652,4654],{"class":3178,"line":3698},[3180,4651,4626],{"class":3872},[3180,4653,3877],{"class":3876},[3180,4655,4656],{"class":4430},"\"500m\"\n",[4022,4658,4659,4667,4678],{},[4025,4660,4662,4663,4666],{"name":4661,"type":4027,"default":3516},"metadata.namespace","Namespace, у якому буде створено Deployment. Якщо не вказано — використовується ",[3429,4664,4665],{},"default",". Namespace — це спосіб ізоляції ресурсів у Kubernetes. Різні команди або проєкти можуть мати свої namespace.",[4025,4668,4670,4671,4674,4675,3121],{"name":4669,"type":4086},"metadata.labels","Мітки для самого Deployment (не для Pod!). Використовуються для організації та фільтрації Deployment. Наприклад, можна додати мітку ",[3429,4672,4673],{},"team: backend"," та потім знайти всі Deployment команди backend через ",[3429,4676,4677],{},"kubectl get deployments -l team=backend",[4025,4679,4681],{"name":4680,"type":4086},"metadata.annotations","Анотації — це довільні метадані, які не використовуються для вибору ресурсів (на відміну від labels). Використовуються для зберігання додаткової інформації: опис, посилання на документацію, контакти відповідальної особи тощо.",[4354,4683,4685],{"id":4684},"розширені-поля-стратегія-оновлення","Розширені поля: стратегія оновлення",[3485,4687,4689],{"className":3863,"code":4688,"language":3865,"meta":3490,"style":3490},"spec:\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxUnavailable: 1\n      maxSurge: 1\n",[3429,4690,4691,4697,4704,4714,4721,4731],{"__ignoreMap":3490},[3180,4692,4693,4695],{"class":3178,"line":3495},[3180,4694,3914],{"class":3872},[3180,4696,3899],{"class":3876},[3180,4698,4699,4702],{"class":3178,"line":3501},[3180,4700,4701],{"class":3872},"  strategy",[3180,4703,3899],{"class":3876},[3180,4705,4706,4709,4711],{"class":3178,"line":3507},[3180,4707,4708],{"class":3872},"    type",[3180,4710,3877],{"class":3876},[3180,4712,4713],{"class":3880},"RollingUpdate\n",[3180,4715,4716,4719],{"class":3178,"line":3513},[3180,4717,4718],{"class":3872},"    rollingUpdate",[3180,4720,3899],{"class":3876},[3180,4722,4723,4726,4728],{"class":3178,"line":3520},[3180,4724,4725],{"class":3872},"      maxUnavailable",[3180,4727,3877],{"class":3876},[3180,4729,4730],{"class":3926},"1\n",[3180,4732,4733,4736,4738],{"class":3178,"line":3526},[3180,4734,4735],{"class":3872},"      maxSurge",[3180,4737,3877],{"class":3876},[3180,4739,4730],{"class":3926},[4022,4741,4742,4756,4775,4832],{},[4025,4743,4745,4746,4748,4749,4752,4753,3121],{"name":4744,"type":4065},"spec.strategy","Стратегія оновлення Pod при зміні ",[3429,4747,4102],{},". Визначає, як Kubernetes замінює старі Pod новими. Є два типи: ",[3429,4750,4751],{},"RollingUpdate"," (за замовчуванням) та ",[3429,4754,4755],{},"Recreate",[4025,4757,4759,4762],{"name":4758,"type":4027,"default":4751},"spec.strategy.type",[3114,4760,4761],{},"Тип стратегії оновлення:",[4763,4764,4765,4770],"ul",{},[3137,4766,4767,4769],{},[3429,4768,4751],{}," — поступове оновлення: нові Pod створюються, старі видаляються. Завжди є працюючі репліки.",[3137,4771,4772,4774],{},[3429,4773,4755],{}," — спочатку видаляються всі старі Pod, потім створюються нові. Є момент downtime.",[4025,4776,4780,4803,4819],{"name":4777,"type":4778,"default":4779},"spec.strategy.rollingUpdate.maxUnavailable","integer | string","25%",[3114,4781,4782,4783,4786,4787,4789,4790,4793,4794,4796,4797,4789,4799,4802],{},"Максимальна кількість Pod, які можуть бути ",[3118,4784,4785],{},"недоступними"," під час оновлення. Може бути абсолютним числом (",[3429,4788,4060],{},", ",[3429,4791,4792],{},"2",") або відсотком від ",[3429,4795,4243],{}," (",[3429,4798,4779],{},[3429,4800,4801],{},"50%",").",[3114,4804,4805,4808,4809,4110,4812,4815,4816,4802],{},[3118,4806,4807],{},"Приклад:"," Якщо ",[3429,4810,4811],{},"replicas: 10",[3429,4813,4814],{},"maxUnavailable: 2",", то під час оновлення мінімум 8 Pod мають бути доступними (",[3429,4817,4818],{},"10 - 2 = 8",[3114,4820,4821,4808,4824,4110,4826,4829,4830,4802],{},[3118,4822,4823],{},"Приклад з відсотком:",[3429,4825,4811],{},[3429,4827,4828],{},"maxUnavailable: 25%",", то під час оновлення мінімум 7-8 Pod мають бути доступними (25% від 10 = 2.5, округлюється вниз до 2, тому ",[3429,4831,4818],{},[4025,4833,4835,4845,4858],{"name":4834,"type":4778,"default":4779},"spec.strategy.rollingUpdate.maxSurge",[3114,4836,4837,4838,4841,4842,4844],{},"Максимальна кількість ",[3118,4839,4840],{},"додаткових"," Pod, які можуть бути створені понад ",[3429,4843,4243],{}," під час оновлення. Може бути абсолютним числом або відсотком.",[3114,4846,4847,4808,4849,4110,4851,4854,4855,4802],{},[3118,4848,4807],{},[3429,4850,4811],{},[3429,4852,4853],{},"maxSurge: 2",", то під час оновлення максимум 12 Pod можуть існувати одночасно (",[3429,4856,4857],{},"10 + 2 = 12",[3114,4859,4860,4863],{},[3118,4861,4862],{},"Навіщо це потрібно:"," Додаткові Pod дозволяють швидше виконати оновлення. Нові Pod створюються паралельно зі старими, і лише після того, як нові стануть готовими, старі видаляються.",[4865,4866,4867,4872],"tip",{},[3114,4868,4869],{},[3118,4870,4871],{},"Як вибрати maxUnavailable та maxSurge:",[4763,4873,4874,4886,4897],{},[3137,4875,4876,3192,4879,4789,4882,4885],{},[3118,4877,4878],{},"Швидке оновлення, більше ресурсів:",[3429,4880,4881],{},"maxUnavailable: 0",[3429,4883,4884],{},"maxSurge: 50%"," — створюються багато нових Pod одразу, старі видаляються лише після готовності нових. Потребує більше ресурсів (CPU, пам'ять).",[3137,4887,4888,3192,4891,4789,4893,4896],{},[3118,4889,4890],{},"Повільне оновлення, менше ресурсів:",[3429,4892,4828],{},[3429,4894,4895],{},"maxSurge: 0"," — старі Pod видаляються, потім створюються нові. Економить ресурси, але оновлення триватиме довше.",[3137,4898,4899,3192,4902,4789,4904,4907],{},[3118,4900,4901],{},"Збалансований підхід (за замовчуванням):",[3429,4903,4828],{},[3429,4905,4906],{},"maxSurge: 25%"," — компроміс між швидкістю та ресурсами.",[4354,4909,4911],{"id":4910},"розширені-поля-контроль-життєвого-циклу","Розширені поля: контроль життєвого циклу",[3485,4913,4915],{"className":3863,"code":4914,"language":3865,"meta":3490,"style":3490},"spec:\n  revisionHistoryLimit: 10\n  progressDeadlineSeconds: 600\n  minReadySeconds: 0\n  paused: false\n",[3429,4916,4917,4923,4933,4943,4953],{"__ignoreMap":3490},[3180,4918,4919,4921],{"class":3178,"line":3495},[3180,4920,3914],{"class":3872},[3180,4922,3899],{"class":3876},[3180,4924,4925,4928,4930],{"class":3178,"line":3501},[3180,4926,4927],{"class":3872},"  revisionHistoryLimit",[3180,4929,3877],{"class":3876},[3180,4931,4932],{"class":3926},"10\n",[3180,4934,4935,4938,4940],{"class":3178,"line":3507},[3180,4936,4937],{"class":3872},"  progressDeadlineSeconds",[3180,4939,3877],{"class":3876},[3180,4941,4942],{"class":3926},"600\n",[3180,4944,4945,4948,4950],{"class":3178,"line":3513},[3180,4946,4947],{"class":3872},"  minReadySeconds",[3180,4949,3877],{"class":3876},[3180,4951,4952],{"class":3926},"0\n",[3180,4954,4955,4958,4960],{"class":3178,"line":3520},[3180,4956,4957],{"class":3872},"  paused",[3180,4959,3877],{"class":3876},[3180,4961,4963],{"class":4962},"su1O8","false\n",[4022,4965,4966,4982,5001,5024],{},[4025,4967,4970,4971,4973,4974,4977,4978,4981],{"name":4968,"type":4059,"default":4969},"spec.revisionHistoryLimit","10","Кількість старих ReplicaSet, які зберігаються для можливості rollback. Кожна зміна у ",[3429,4972,4102],{}," створює нову ревізію (новий ReplicaSet). Старі ReplicaSet зберігаються з ",[3429,4975,4976],{},"replicas: 0"," для історії. Якщо встановити ",[3429,4979,4980],{},"0"," — rollback буде неможливий.",[4025,4983,4986,4996],{"name":4984,"type":4059,"default":4985},"spec.progressDeadlineSeconds","600",[3114,4987,4988,4989,4992,4993,3121],{},"Максимальний час (у секундах), протягом якого Deployment має досягти прогресу під час оновлення. Якщо за цей час жоден новий Pod не стане готовим — оновлення вважається невдалим, і Deployment отримає статус ",[3429,4990,4991],{},"Progressing: False"," з причиною ",[3429,4994,4995],{},"ProgressDeadlineExceeded",[3114,4997,4998,5000],{},[3118,4999,4862],{}," Запобігає ситуації, коли оновлення \"зависає\" нескінченно (наприклад, новий образ не може завантажитись або Pod не проходить health checks).",[4025,5002,5004,5007,5019],{"name":5003,"type":4059,"default":4980},"spec.minReadySeconds",[3114,5005,5006],{},"Мінімальний час (у секундах), протягом якого новий Pod має бути готовим (без падінь) перед тим, як він вважатиметься доступним. Це додаткова перевірка стабільності.",[3114,5008,5009,4808,5011,5014,5015,5018],{},[3118,5010,4807],{},[3429,5012,5013],{},"minReadySeconds: 30",", то після того, як Pod стане ",[3429,5016,5017],{},"Ready",", Kubernetes чекатиме ще 30 секунд. Якщо за цей час Pod не впаде — він вважається доступним і оновлення продовжується. Якщо впаде — оновлення призупиняється.",[3114,5020,5021,5023],{},[3118,5022,4862],{}," Запобігає ситуації, коли Pod стартує успішно, але падає через кілька секунд (наприклад, через помилку підключення до бази даних).",[4025,5025,5029,5030,5032,5033,5035,5036,3121],{"name":5026,"type":5027,"default":5028},"spec.paused","boolean","false","Якщо ",[3429,5031,4028],{}," — Deployment призупинено. Зміни у ",[3429,5034,4102],{}," не застосовуються автоматично. Використовується для ручного контролю оновлень (canary deployments). Щоб продовжити оновлення, потрібно встановити ",[3429,5037,5038],{},"paused: false",[3126,5040,5042],{"id":5041},"повний-приклад-з-усіма-полями","Повний приклад з усіма полями",[3114,5044,5045],{},"Тепер об'єднаємо все, що ми вивчили, в один Deployment з усіма важливими полями:",[3485,5047,5049],{"className":3863,"code":5048,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: myapp-deployment\n  namespace: default\n  labels:\n    app: myapp\n    tier: backend\n    version: \"1.0\"\n  annotations:\n    description: \"Production deployment for MyApp API\"\n    contact: \"backend-team@example.com\"\nspec:\n  # Кількість реплік\n  replicas: 3\n  \n  # Селектор для вибору Pod\n  selector:\n    matchLabels:\n      app: myapp\n  \n  # Стратегія оновлення\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxUnavailable: 1\n      maxSurge: 1\n  \n  # Контроль життєвого циклу\n  revisionHistoryLimit: 10\n  progressDeadlineSeconds: 600\n  minReadySeconds: 5\n  \n  # Шаблон Pod\n  template:\n    metadata:\n      labels:\n        app: myapp\n        version: \"1.0\"\n    spec:\n      containers:\n        - name: app\n          image: mcr.microsoft.com/dotnet/samples:aspnetapp\n          ports:\n            - containerPort: 8080\n              name: http\n          env:\n            - name: ASPNETCORE_ENVIRONMENT\n              value: \"Production\"\n          resources:\n            requests:\n              memory: \"128Mi\"\n              cpu: \"100m\"\n            limits:\n              memory: \"256Mi\"\n              cpu: \"500m\"\n",[3429,5050,5051,5059,5067,5073,5081,5089,5095,5103,5113,5121,5127,5136,5146,5152,5157,5165,5170,5175,5181,5187,5195,5199,5204,5210,5218,5224,5232,5240,5244,5249,5257,5265,5274,5278,5283,5289,5295,5301,5309,5317,5323,5329,5339,5347,5353,5363,5373,5379,5390,5399,5405,5411,5419,5427,5433,5441],{"__ignoreMap":3490},[3180,5052,5053,5055,5057],{"class":3178,"line":3495},[3180,5054,3873],{"class":3872},[3180,5056,3877],{"class":3876},[3180,5058,3881],{"class":3880},[3180,5060,5061,5063,5065],{"class":3178,"line":3501},[3180,5062,3886],{"class":3872},[3180,5064,3877],{"class":3876},[3180,5066,3891],{"class":3880},[3180,5068,5069,5071],{"class":3178,"line":3507},[3180,5070,3896],{"class":3872},[3180,5072,3899],{"class":3876},[3180,5074,5075,5077,5079],{"class":3178,"line":3513},[3180,5076,3904],{"class":3872},[3180,5078,3877],{"class":3876},[3180,5080,4393],{"class":3880},[3180,5082,5083,5085,5087],{"class":3178,"line":3520},[3180,5084,4398],{"class":3872},[3180,5086,3877],{"class":3876},[3180,5088,4403],{"class":3880},[3180,5090,5091,5093],{"class":3178,"line":3526},[3180,5092,4408],{"class":3872},[3180,5094,3899],{"class":3876},[3180,5096,5097,5099,5101],{"class":3178,"line":3532},[3180,5098,4415],{"class":3872},[3180,5100,3877],{"class":3876},[3180,5102,4420],{"class":3880},[3180,5104,5105,5108,5110],{"class":3178,"line":3538},[3180,5106,5107],{"class":3872},"    tier",[3180,5109,3877],{"class":3876},[3180,5111,5112],{"class":3880},"backend\n",[3180,5114,5115,5117,5119],{"class":3178,"line":3544},[3180,5116,4425],{"class":3872},[3180,5118,3877],{"class":3876},[3180,5120,4431],{"class":4430},[3180,5122,5123,5125],{"class":3178,"line":3549},[3180,5124,4436],{"class":3872},[3180,5126,3899],{"class":3876},[3180,5128,5129,5131,5133],{"class":3178,"line":3555},[3180,5130,4443],{"class":3872},[3180,5132,3877],{"class":3876},[3180,5134,5135],{"class":4430},"\"Production deployment for MyApp API\"\n",[3180,5137,5138,5141,5143],{"class":3178,"line":3561},[3180,5139,5140],{"class":3872},"    contact",[3180,5142,3877],{"class":3876},[3180,5144,5145],{"class":4430},"\"backend-team@example.com\"\n",[3180,5147,5148,5150],{"class":3178,"line":3567},[3180,5149,3914],{"class":3872},[3180,5151,3899],{"class":3876},[3180,5153,5154],{"class":3178,"line":3573},[3180,5155,5156],{"class":4183},"  # Кількість реплік\n",[3180,5158,5159,5161,5163],{"class":3178,"line":3579},[3180,5160,3921],{"class":3872},[3180,5162,3877],{"class":3876},[3180,5164,3927],{"class":3926},[3180,5166,5167],{"class":3178,"line":3585},[3180,5168,5169],{"class":3876},"  \n",[3180,5171,5172],{"class":3178,"line":3591},[3180,5173,5174],{"class":4183},"  # Селектор для вибору Pod\n",[3180,5176,5177,5179],{"class":3178,"line":3596},[3180,5178,3932],{"class":3872},[3180,5180,3899],{"class":3876},[3180,5182,5183,5185],{"class":3178,"line":3602},[3180,5184,3939],{"class":3872},[3180,5186,3899],{"class":3876},[3180,5188,5189,5191,5193],{"class":3178,"line":3607},[3180,5190,3946],{"class":3872},[3180,5192,3877],{"class":3876},[3180,5194,4420],{"class":3880},[3180,5196,5197],{"class":3178,"line":3612},[3180,5198,5169],{"class":3876},[3180,5200,5201],{"class":3178,"line":3618},[3180,5202,5203],{"class":4183},"  # Стратегія оновлення\n",[3180,5205,5206,5208],{"class":3178,"line":3624},[3180,5207,4701],{"class":3872},[3180,5209,3899],{"class":3876},[3180,5211,5212,5214,5216],{"class":3178,"line":3630},[3180,5213,4708],{"class":3872},[3180,5215,3877],{"class":3876},[3180,5217,4713],{"class":3880},[3180,5219,5220,5222],{"class":3178,"line":3636},[3180,5221,4718],{"class":3872},[3180,5223,3899],{"class":3876},[3180,5225,5226,5228,5230],{"class":3178,"line":3641},[3180,5227,4725],{"class":3872},[3180,5229,3877],{"class":3876},[3180,5231,4730],{"class":3926},[3180,5233,5234,5236,5238],{"class":3178,"line":3646},[3180,5235,4735],{"class":3872},[3180,5237,3877],{"class":3876},[3180,5239,4730],{"class":3926},[3180,5241,5242],{"class":3178,"line":3651},[3180,5243,5169],{"class":3876},[3180,5245,5246],{"class":3178,"line":3657},[3180,5247,5248],{"class":4183},"  # Контроль життєвого циклу\n",[3180,5250,5251,5253,5255],{"class":3178,"line":3663},[3180,5252,4927],{"class":3872},[3180,5254,3877],{"class":3876},[3180,5256,4932],{"class":3926},[3180,5258,5259,5261,5263],{"class":3178,"line":3669},[3180,5260,4937],{"class":3872},[3180,5262,3877],{"class":3876},[3180,5264,4942],{"class":3926},[3180,5266,5267,5269,5271],{"class":3178,"line":3675},[3180,5268,4947],{"class":3872},[3180,5270,3877],{"class":3876},[3180,5272,5273],{"class":3926},"5\n",[3180,5275,5276],{"class":3178,"line":3681},[3180,5277,5169],{"class":3876},[3180,5279,5280],{"class":3178,"line":3687},[3180,5281,5282],{"class":4183},"  # Шаблон Pod\n",[3180,5284,5285,5287],{"class":3178,"line":3692},[3180,5286,3956],{"class":3872},[3180,5288,3899],{"class":3876},[3180,5290,5291,5293],{"class":3178,"line":3698},[3180,5292,3963],{"class":3872},[3180,5294,3899],{"class":3876},[3180,5296,5297,5299],{"class":3178,"line":3704},[3180,5298,3970],{"class":3872},[3180,5300,3899],{"class":3876},[3180,5302,5303,5305,5307],{"class":3178,"line":3710},[3180,5304,3977],{"class":3872},[3180,5306,3877],{"class":3876},[3180,5308,4420],{"class":3880},[3180,5310,5311,5313,5315],{"class":3178,"line":3716},[3180,5312,4513],{"class":3872},[3180,5314,3877],{"class":3876},[3180,5316,4431],{"class":4430},[3180,5318,5319,5321],{"class":3178,"line":3722},[3180,5320,3986],{"class":3872},[3180,5322,3899],{"class":3876},[3180,5324,5325,5327],{"class":3178,"line":3728},[3180,5326,3993],{"class":3872},[3180,5328,3899],{"class":3876},[3180,5330,5331,5333,5335,5337],{"class":3178,"line":3734},[3180,5332,4000],{"class":3876},[3180,5334,4003],{"class":3872},[3180,5336,3877],{"class":3876},[3180,5338,4540],{"class":3880},[3180,5340,5341,5343,5345],{"class":3178,"line":3739},[3180,5342,4012],{"class":3872},[3180,5344,3877],{"class":3876},[3180,5346,4549],{"class":3880},[3180,5348,5349,5351],{"class":3178,"line":3745},[3180,5350,4554],{"class":3872},[3180,5352,3899],{"class":3876},[3180,5354,5355,5357,5359,5361],{"class":3178,"line":3751},[3180,5356,4561],{"class":3876},[3180,5358,4564],{"class":3872},[3180,5360,3877],{"class":3876},[3180,5362,4569],{"class":3926},[3180,5364,5365,5368,5370],{"class":3178,"line":3757},[3180,5366,5367],{"class":3872},"              name",[3180,5369,3877],{"class":3876},[3180,5371,5372],{"class":3880},"http\n",[3180,5374,5375,5377],{"class":3178,"line":3763},[3180,5376,4574],{"class":3872},[3180,5378,3899],{"class":3876},[3180,5380,5381,5383,5385,5387],{"class":3178,"line":3768},[3180,5382,4561],{"class":3876},[3180,5384,4003],{"class":3872},[3180,5386,3877],{"class":3876},[3180,5388,5389],{"class":3880},"ASPNETCORE_ENVIRONMENT\n",[3180,5391,5392,5394,5396],{"class":3178,"line":3773},[3180,5393,4592],{"class":3872},[3180,5395,3877],{"class":3876},[3180,5397,5398],{"class":4430},"\"Production\"\n",[3180,5400,5401,5403],{"class":3178,"line":3779},[3180,5402,4602],{"class":3872},[3180,5404,3899],{"class":3876},[3180,5406,5407,5409],{"class":3178,"line":3785},[3180,5408,4609],{"class":3872},[3180,5410,3899],{"class":3876},[3180,5412,5413,5415,5417],{"class":3178,"line":3791},[3180,5414,4616],{"class":3872},[3180,5416,3877],{"class":3876},[3180,5418,4621],{"class":4430},[3180,5420,5421,5423,5425],{"class":3178,"line":3797},[3180,5422,4626],{"class":3872},[3180,5424,3877],{"class":3876},[3180,5426,4631],{"class":4430},[3180,5428,5429,5431],{"class":3178,"line":3802},[3180,5430,4636],{"class":3872},[3180,5432,3899],{"class":3876},[3180,5434,5435,5437,5439],{"class":3178,"line":3807},[3180,5436,4616],{"class":3872},[3180,5438,3877],{"class":3876},[3180,5440,4647],{"class":4430},[3180,5442,5444,5446,5448],{"class":3178,"line":5443},56,[3180,5445,4626],{"class":3872},[3180,5447,3877],{"class":3876},[3180,5449,4656],{"class":4430},[3114,5451,5452],{},"Цей Deployment:",[4763,5454,5455,5461,5464,5467,5470],{},[3137,5456,5457,5458],{},"Створює 3 репліки Pod з образом ",[3429,5459,5460],{},"mcr.microsoft.com/dotnet/samples:aspnetapp",[3137,5462,5463],{},"Використовує rolling update з обережними налаштуваннями (максимум 1 недоступний Pod)",[3137,5465,5466],{},"Зберігає історію 10 останніх версій для rollback",[3137,5468,5469],{},"Чекає 5 секунд після готовності Pod перед продовженням оновлення",[3137,5471,5472],{},"Має таймаут 600 секунд для оновлення",[3382,5474],{},[3109,5476,5478],{"id":5477},"створення-першого-deployment","Створення першого Deployment",[3114,5480,5481],{},"Тепер створимо реальний Deployment та подивимося, як він працює.",[3126,5483,5485],{"id":5484},"крок-1-створення-yaml-маніфесту","Крок 1: Створення YAML маніфесту",[3114,5487,5488,5489,3480],{},"Створіть файл ",[3429,5490,5491],{},"nginx-deployment.yaml",[3485,5493,5495],{"className":3863,"code":5494,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx-deployment\n  labels:\n    app: nginx\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n          ports:\n            - containerPort: 80\n",[3429,5496,5497,5505,5513,5519,5527,5533,5541,5547,5555,5561,5567,5575,5581,5587,5593,5601,5607,5613,5623,5631,5637],{"__ignoreMap":3490},[3180,5498,5499,5501,5503],{"class":3178,"line":3495},[3180,5500,3873],{"class":3872},[3180,5502,3877],{"class":3876},[3180,5504,3881],{"class":3880},[3180,5506,5507,5509,5511],{"class":3178,"line":3501},[3180,5508,3886],{"class":3872},[3180,5510,3877],{"class":3876},[3180,5512,3891],{"class":3880},[3180,5514,5515,5517],{"class":3178,"line":3507},[3180,5516,3896],{"class":3872},[3180,5518,3899],{"class":3876},[3180,5520,5521,5523,5525],{"class":3178,"line":3513},[3180,5522,3904],{"class":3872},[3180,5524,3877],{"class":3876},[3180,5526,3909],{"class":3880},[3180,5528,5529,5531],{"class":3178,"line":3520},[3180,5530,4408],{"class":3872},[3180,5532,3899],{"class":3876},[3180,5534,5535,5537,5539],{"class":3178,"line":3526},[3180,5536,4415],{"class":3872},[3180,5538,3877],{"class":3876},[3180,5540,3951],{"class":3880},[3180,5542,5543,5545],{"class":3178,"line":3532},[3180,5544,3914],{"class":3872},[3180,5546,3899],{"class":3876},[3180,5548,5549,5551,5553],{"class":3178,"line":3538},[3180,5550,3921],{"class":3872},[3180,5552,3877],{"class":3876},[3180,5554,3927],{"class":3926},[3180,5556,5557,5559],{"class":3178,"line":3544},[3180,5558,3932],{"class":3872},[3180,5560,3899],{"class":3876},[3180,5562,5563,5565],{"class":3178,"line":3549},[3180,5564,3939],{"class":3872},[3180,5566,3899],{"class":3876},[3180,5568,5569,5571,5573],{"class":3178,"line":3555},[3180,5570,3946],{"class":3872},[3180,5572,3877],{"class":3876},[3180,5574,3951],{"class":3880},[3180,5576,5577,5579],{"class":3178,"line":3561},[3180,5578,3956],{"class":3872},[3180,5580,3899],{"class":3876},[3180,5582,5583,5585],{"class":3178,"line":3567},[3180,5584,3963],{"class":3872},[3180,5586,3899],{"class":3876},[3180,5588,5589,5591],{"class":3178,"line":3573},[3180,5590,3970],{"class":3872},[3180,5592,3899],{"class":3876},[3180,5594,5595,5597,5599],{"class":3178,"line":3579},[3180,5596,3977],{"class":3872},[3180,5598,3877],{"class":3876},[3180,5600,3951],{"class":3880},[3180,5602,5603,5605],{"class":3178,"line":3585},[3180,5604,3986],{"class":3872},[3180,5606,3899],{"class":3876},[3180,5608,5609,5611],{"class":3178,"line":3591},[3180,5610,3993],{"class":3872},[3180,5612,3899],{"class":3876},[3180,5614,5615,5617,5619,5621],{"class":3178,"line":3596},[3180,5616,4000],{"class":3876},[3180,5618,4003],{"class":3872},[3180,5620,3877],{"class":3876},[3180,5622,3951],{"class":3880},[3180,5624,5625,5627,5629],{"class":3178,"line":3602},[3180,5626,4012],{"class":3872},[3180,5628,3877],{"class":3876},[3180,5630,4017],{"class":3880},[3180,5632,5633,5635],{"class":3178,"line":3607},[3180,5634,4554],{"class":3872},[3180,5636,3899],{"class":3876},[3180,5638,5639,5641,5643,5645],{"class":3178,"line":3612},[3180,5640,4561],{"class":3876},[3180,5642,4564],{"class":3872},[3180,5644,3877],{"class":3876},[3180,5646,5647],{"class":3926},"80\n",[3126,5649,5651],{"id":5650},"крок-2-застосування-маніфесту","Крок 2: Застосування маніфесту",[3114,5653,5654],{},"Застосуйте маніфест до кластера:",[3171,5656,5658,5667],{"title":5657},"kubectl apply",[3175,5659,5661,3192,5664],{"className":5660},[3178],[3180,5662,3191],{"className":5663},[3183],[3118,5665,5666],{},"kubectl apply -f nginx-deployment.yaml",[3175,5668,5670],{"className":5669},[3178],[3180,5671,5674],{"className":5672},[5673],"text-green-400","deployment.apps/nginx-deployment created",[3126,5676,5678],{"id":5677},"крок-3-перевірка-створеного-deployment","Крок 3: Перевірка створеного Deployment",[3114,5680,5681],{},"Переглянемо список Deployment:",[3171,5683,5685,5693,5697],{"title":5684},"kubectl get deployments",[3175,5686,5688,3192,5691],{"className":5687},[3178],[3180,5689,3191],{"className":5690},[3183],[3118,5692,5684],{},[3175,5694,5696],{"className":5695},[3178],"NAME               READY   UP-TO-DATE   AVAILABLE   AGE",[3175,5698,5700],{"className":5699},[3178],"nginx-deployment   3/3     3            3           15s",[3114,5702,5703],{},[3118,5704,5705],{},"Що означають колонки:",[4763,5707,5708,5716,5727,5736,5744],{},[3137,5709,5710,5713,5714,4229],{},[3118,5711,5712],{},"NAME",": Ім'я Deployment (з ",[3429,5715,4050],{},[3137,5717,5718,3877,5721,5724,5725,4229],{},[3118,5719,5720],{},"READY",[3429,5722,5723],{},"3/3"," — 3 з 3 реплік готові до роботи (стан ",[3429,5726,5017],{},[3137,5728,5729,3877,5732,5735],{},[3118,5730,5731],{},"UP-TO-DATE",[3429,5733,5734],{},"3"," — 3 репліки відповідають поточній версії шаблону (актуальний образ, змінні оточення тощо)",[3137,5737,5738,3877,5741,5743],{},[3118,5739,5740],{},"AVAILABLE",[3429,5742,5734],{}," — 3 репліки доступні для обслуговування трафіку (пройшли readiness probe, якщо він налаштований)",[3137,5745,5746,5749],{},[3118,5747,5748],{},"AGE",": Час з моменту створення Deployment",[3126,5751,5753],{"id":5752},"крок-4-перевірка-створених-pod","Крок 4: Перевірка створених Pod",[3114,5755,5756],{},"Deployment автоматично створив Pod. Подивимося на них:",[3171,5758,5760,5768,5772,5776,5780],{"title":5759},"kubectl get pods",[3175,5761,5763,3192,5766],{"className":5762},[3178],[3180,5764,3191],{"className":5765},[3183],[3118,5767,5759],{},[3175,5769,5771],{"className":5770},[3178],"NAME                                READY   STATUS    RESTARTS   AGE",[3175,5773,5775],{"className":5774},[3178],"nginx-deployment-7d6b8c9f4d-8xk2p   1/1     Running   0          20s",[3175,5777,5779],{"className":5778},[3178],"nginx-deployment-7d6b8c9f4d-m5n7q   1/1     Running   0          20s",[3175,5781,5783],{"className":5782},[3178],"nginx-deployment-7d6b8c9f4d-z9w3r   1/1     Running   0          20s",[3114,5785,5786],{},[3118,5787,5788],{},"Структура імені Pod:",[3114,5790,5791,5792],{},"Ім'я Pod складається з трьох частин: ",[3429,5793,5794],{},"nginx-deployment-7d6b8c9f4d-8xk2p",[3134,5796,5797,5803,5809],{},[3137,5798,5799,5802],{},[3429,5800,5801],{},"nginx-deployment"," — ім'я Deployment",[3137,5804,5805,5808],{},[3429,5806,5807],{},"7d6b8c9f4d"," — хеш ReplicaSet (унікальний ідентифікатор версії шаблону Pod)",[3137,5810,5811,5814],{},[3429,5812,5813],{},"8xk2p"," — унікальний суфікс Pod (генерується випадково)",[3114,5816,5817],{},"Це дозволяє легко ідентифікувати, до якого Deployment належить Pod, та яку версію шаблону він використовує.",[3126,5819,5821],{"id":5820},"крок-5-детальна-інформація-про-deployment","Крок 5: Детальна інформація про Deployment",[3114,5823,5824],{},"Переглянемо детальну інформацію:",[3171,5826,5828,5837,5841,5845,5849,5853,5857,5861,5865,5869,5873,5877,5881,5885,5889,5893,5897,5901,5905,5909,5913,5917,5921,5925,5929],{"title":5827},"kubectl describe deployment",[3175,5829,5831,3192,5834],{"className":5830},[3178],[3180,5832,3191],{"className":5833},[3183],[3118,5835,5836],{},"kubectl describe deployment nginx-deployment",[3175,5838,5840],{"className":5839},[3178],"Name:                   nginx-deployment",[3175,5842,5844],{"className":5843},[3178],"Namespace:              default",[3175,5846,5848],{"className":5847},[3178],"CreationTimestamp:      Fri, 09 May 2026 20:20:00 +0000",[3175,5850,5852],{"className":5851},[3178],"Labels:                 app=nginx",[3175,5854,5856],{"className":5855},[3178],"Selector:               app=nginx",[3175,5858,5860],{"className":5859},[3178],"Replicas:               3 desired | 3 updated | 3 total | 3 available",[3175,5862,5864],{"className":5863},[3178],"StrategyType:           RollingUpdate",[3175,5866,5868],{"className":5867},[3178],"MinReadySeconds:        0",[3175,5870,5872],{"className":5871},[3178],"RollingUpdateStrategy:  25% max unavailable, 25% max surge",[3175,5874,5876],{"className":5875},[3178],"Pod Template:",[3175,5878,5880],{"className":5879},[3178],"  Labels:  app=nginx",[3175,5882,5884],{"className":5883},[3178],"  Containers:",[3175,5886,5888],{"className":5887},[3178],"   nginx:",[3175,5890,5892],{"className":5891},[3178],"    Image:        nginx:1.27",[3175,5894,5896],{"className":5895},[3178],"    Port:         80/TCP",[3175,5898,5900],{"className":5899},[3178],"Conditions:",[3175,5902,5904],{"className":5903},[3178],"  Type           Status  Reason",[3175,5906,5908],{"className":5907},[3178],"  ----           ------  ------",[3175,5910,5912],{"className":5911},[3178],"  Available      True    MinimumReplicasAvailable",[3175,5914,5916],{"className":5915},[3178],"  Progressing    True    NewReplicaSetAvailable",[3175,5918,5920],{"className":5919},[3178],"Events:",[3175,5922,5924],{"className":5923},[3178],"  Type    Reason             Age   Message",[3175,5926,5928],{"className":5927},[3178],"  ----    ------             ----  -------",[3175,5930,5932],{"className":5931},[3178],"  Normal  ScalingReplicaSet  30s   Scaled up replica set nginx-deployment-7d6b8c9f4d to 3",[3114,5934,5935],{},"Тут ви бачите:",[4763,5937,5938,5941,5944,5947],{},[3137,5939,5940],{},"Конфігурацію Deployment (replicas, selector, strategy)",[3137,5942,5943],{},"Шаблон Pod (образ, порти)",[3137,5945,5946],{},"Умови (Conditions) — стан Deployment",[3137,5948,5949],{},"Події (Events) — що відбувалося з Deployment (створення ReplicaSet, масштабування)",[3382,5951],{},[3109,5953,5955],{"id":5954},"replicaset-проміжний-шар-між-deployment-та-pod","ReplicaSet: проміжний шар між Deployment та Pod",[3114,5957,5958,5959,5961],{},"Ми згадували, що Deployment не створює Pod напряму. Він використовує ",[3118,5960,3479],{},". Давайте розберемося детально, що це і навіщо.",[3126,5963,5965],{"id":5964},"що-таке-replicaset","Що таке ReplicaSet",[3114,5967,5968,3392,5970,3822,5973,3826],{},[3118,5969,3479],{},[3118,5971,5972],{},"підтримку заданої кількості ідентичних Pod",[3429,5974,3825],{},[3114,5976,5977],{},"Коли ви створюєте Deployment, він автоматично створює ReplicaSet:",[3482,5979,5980],{},[3485,5981,5983],{"className":3487,"code":5982,"language":3489,"meta":3490,"style":3490},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Користувач\" as user\nparticipant \"kubectl\" as kubectl\nparticipant \"API Server\" as api\nparticipant \"Deployment Controller\" as dc\nparticipant \"ReplicaSet Controller\" as rsc\nparticipant \"Scheduler\" as sched\nparticipant \"Kubelet\" as kubelet\n\nuser -> kubectl: kubectl apply -f deployment.yaml\nkubectl -> api: Створити Deployment\napi -> dc: Подія: новий Deployment\nactivate dc\ndc -> api: Створити ReplicaSet\ndeactivate dc\n\napi -> rsc: Подія: новий ReplicaSet\nactivate rsc\nrsc -> api: Створити Pod 1\nrsc -> api: Створити Pod 2\nrsc -> api: Створити Pod 3\ndeactivate rsc\n\napi -> sched: Подія: нові Pod\nactivate sched\nsched -> api: Призначити Pod вузлам\ndeactivate sched\n\napi -> kubelet: Подія: Pod призначено вузлу\nactivate kubelet\nkubelet -> kubelet: Завантажити образ\nkubelet -> kubelet: Запустити контейнер\nkubelet -> api: Pod Running\ndeactivate kubelet\n\nnote right of dc\n  Deployment Controller\n  створює та керує\n  ReplicaSet\nend note\n\nnote right of rsc\n  ReplicaSet Controller\n  створює та керує\n  Pod\nend note\n\n@enduml\n",[3429,5984,5985,5989,5993,5997,6001,6006,6011,6016,6021,6026,6031,6036,6040,6045,6050,6055,6060,6065,6070,6074,6079,6084,6089,6094,6099,6104,6108,6113,6118,6123,6128,6132,6137,6142,6147,6152,6157,6162,6166,6171,6176,6181,6186,6190,6194,6199,6204,6208,6213,6217,6221],{"__ignoreMap":3490},[3180,5986,5987],{"class":3178,"line":3495},[3180,5988,3498],{},[3180,5990,5991],{"class":3178,"line":3501},[3180,5992,3504],{},[3180,5994,5995],{"class":3178,"line":3507},[3180,5996,3510],{},[3180,5998,5999],{"class":3178,"line":3513},[3180,6000,3517],{"emptyLinePlaceholder":3516},[3180,6002,6003],{"class":3178,"line":3520},[3180,6004,6005],{},"actor \"Користувач\" as user\n",[3180,6007,6008],{"class":3178,"line":3526},[3180,6009,6010],{},"participant \"kubectl\" as kubectl\n",[3180,6012,6013],{"class":3178,"line":3532},[3180,6014,6015],{},"participant \"API Server\" as api\n",[3180,6017,6018],{"class":3178,"line":3538},[3180,6019,6020],{},"participant \"Deployment Controller\" as dc\n",[3180,6022,6023],{"class":3178,"line":3544},[3180,6024,6025],{},"participant \"ReplicaSet Controller\" as rsc\n",[3180,6027,6028],{"class":3178,"line":3549},[3180,6029,6030],{},"participant \"Scheduler\" as sched\n",[3180,6032,6033],{"class":3178,"line":3555},[3180,6034,6035],{},"participant \"Kubelet\" as kubelet\n",[3180,6037,6038],{"class":3178,"line":3561},[3180,6039,3517],{"emptyLinePlaceholder":3516},[3180,6041,6042],{"class":3178,"line":3567},[3180,6043,6044],{},"user -> kubectl: kubectl apply -f deployment.yaml\n",[3180,6046,6047],{"class":3178,"line":3573},[3180,6048,6049],{},"kubectl -> api: Створити Deployment\n",[3180,6051,6052],{"class":3178,"line":3579},[3180,6053,6054],{},"api -> dc: Подія: новий Deployment\n",[3180,6056,6057],{"class":3178,"line":3585},[3180,6058,6059],{},"activate dc\n",[3180,6061,6062],{"class":3178,"line":3591},[3180,6063,6064],{},"dc -> api: Створити ReplicaSet\n",[3180,6066,6067],{"class":3178,"line":3596},[3180,6068,6069],{},"deactivate dc\n",[3180,6071,6072],{"class":3178,"line":3602},[3180,6073,3517],{"emptyLinePlaceholder":3516},[3180,6075,6076],{"class":3178,"line":3607},[3180,6077,6078],{},"api -> rsc: Подія: новий ReplicaSet\n",[3180,6080,6081],{"class":3178,"line":3612},[3180,6082,6083],{},"activate rsc\n",[3180,6085,6086],{"class":3178,"line":3618},[3180,6087,6088],{},"rsc -> api: Створити Pod 1\n",[3180,6090,6091],{"class":3178,"line":3624},[3180,6092,6093],{},"rsc -> api: Створити Pod 2\n",[3180,6095,6096],{"class":3178,"line":3630},[3180,6097,6098],{},"rsc -> api: Створити Pod 3\n",[3180,6100,6101],{"class":3178,"line":3636},[3180,6102,6103],{},"deactivate rsc\n",[3180,6105,6106],{"class":3178,"line":3641},[3180,6107,3517],{"emptyLinePlaceholder":3516},[3180,6109,6110],{"class":3178,"line":3646},[3180,6111,6112],{},"api -> sched: Подія: нові Pod\n",[3180,6114,6115],{"class":3178,"line":3651},[3180,6116,6117],{},"activate sched\n",[3180,6119,6120],{"class":3178,"line":3657},[3180,6121,6122],{},"sched -> api: Призначити Pod вузлам\n",[3180,6124,6125],{"class":3178,"line":3663},[3180,6126,6127],{},"deactivate sched\n",[3180,6129,6130],{"class":3178,"line":3669},[3180,6131,3517],{"emptyLinePlaceholder":3516},[3180,6133,6134],{"class":3178,"line":3675},[3180,6135,6136],{},"api -> kubelet: Подія: Pod призначено вузлу\n",[3180,6138,6139],{"class":3178,"line":3681},[3180,6140,6141],{},"activate kubelet\n",[3180,6143,6144],{"class":3178,"line":3687},[3180,6145,6146],{},"kubelet -> kubelet: Завантажити образ\n",[3180,6148,6149],{"class":3178,"line":3692},[3180,6150,6151],{},"kubelet -> kubelet: Запустити контейнер\n",[3180,6153,6154],{"class":3178,"line":3698},[3180,6155,6156],{},"kubelet -> api: Pod Running\n",[3180,6158,6159],{"class":3178,"line":3704},[3180,6160,6161],{},"deactivate kubelet\n",[3180,6163,6164],{"class":3178,"line":3710},[3180,6165,3517],{"emptyLinePlaceholder":3516},[3180,6167,6168],{"class":3178,"line":3716},[3180,6169,6170],{},"note right of dc\n",[3180,6172,6173],{"class":3178,"line":3722},[3180,6174,6175],{},"  Deployment Controller\n",[3180,6177,6178],{"class":3178,"line":3728},[3180,6179,6180],{},"  створює та керує\n",[3180,6182,6183],{"class":3178,"line":3734},[3180,6184,6185],{},"  ReplicaSet\n",[3180,6187,6188],{"class":3178,"line":3739},[3180,6189,3731],{},[3180,6191,6192],{"class":3178,"line":3745},[3180,6193,3517],{"emptyLinePlaceholder":3516},[3180,6195,6196],{"class":3178,"line":3751},[3180,6197,6198],{},"note right of rsc\n",[3180,6200,6201],{"class":3178,"line":3757},[3180,6202,6203],{},"  ReplicaSet Controller\n",[3180,6205,6206],{"class":3178,"line":3763},[3180,6207,6180],{},[3180,6209,6210],{"class":3178,"line":3768},[3180,6211,6212],{},"  Pod\n",[3180,6214,6215],{"class":3178,"line":3773},[3180,6216,3731],{},[3180,6218,6219],{"class":3178,"line":3779},[3180,6220,3517],{"emptyLinePlaceholder":3516},[3180,6222,6223],{"class":3178,"line":3785},[3180,6224,3810],{},[3126,6226,6228],{"id":6227},"навіщо-потрібен-replicaset","Навіщо потрібен ReplicaSet",[3114,6230,6231],{},"ReplicaSet виконує дві ключові функції:",[3134,6233,6234,6240],{},[3137,6235,6236,6239],{},[3118,6237,6238],{},"Підтримка кількості реплік"," — якщо Pod падає, ReplicaSet створює новий",[3137,6241,6242,6245],{},[3118,6243,6244],{},"Версіонування для rolling updates"," — при оновленні Deployment створює новий ReplicaSet, а старий залишається для rollback",[3114,6247,6248],{},"Переглянемо ReplicaSet, створений нашим Deployment:",[3171,6250,6252,6260,6264],{"title":6251},"kubectl get replicasets",[3175,6253,6255,3192,6258],{"className":6254},[3178],[3180,6256,3191],{"className":6257},[3183],[3118,6259,6251],{},[3175,6261,6263],{"className":6262},[3178],"NAME                          DESIRED   CURRENT   READY   AGE",[3175,6265,6267],{"className":6266},[3178],"nginx-deployment-7d6b8c9f4d   3         3         3       2m",[3114,6269,6270],{},[3118,6271,5705],{},[4763,6273,6274,6279,6288,6294,6301],{},[3137,6275,6276,6278],{},[3118,6277,5712],{},": Ім'я ReplicaSet (ім'я Deployment + хеш шаблону Pod)",[3137,6280,6281,6284,6285,6287],{},[3118,6282,6283],{},"DESIRED",": Бажана кількість Pod (з ",[3429,6286,4058],{}," Deployment)",[3137,6289,6290,6293],{},[3118,6291,6292],{},"CURRENT",": Поточна кількість Pod (скільки насправді існує)",[3137,6295,6296,6298,6299,4229],{},[3118,6297,5720],{},": Кількість готових Pod (стан ",[3429,6300,5017],{},[3137,6302,6303,6305],{},[3118,6304,5748],{},": Час з моменту створення ReplicaSet",[3406,6307,6308,6315,6326],{},[3114,6309,6310,3840,6312,6314],{},[3118,6311,3839],{},[3118,6313,3843],{}," не створюєте ReplicaSet напряму. Завжди використовуйте Deployment. Пряме створення ReplicaSet позбавляє вас можливості:",[4763,6316,6317,6320,6323],{},[3137,6318,6319],{},"Rolling updates (поступового оновлення)",[3137,6321,6322],{},"Rollback (повернення до попередньої версії)",[3137,6324,6325],{},"Історії версій",[3114,6327,6328],{},"ReplicaSet — це низькорівневий примітив, на якому будується Deployment. Використовуйте Deployment для управління застосунками.",[3382,6330],{},[3109,6332,6334],{"id":6333},"self-healing-у-дії","Self-healing у дії",[3114,6336,6337,6338,6341],{},"Тепер продемонструємо головну перевагу Deployment — ",[3118,6339,6340],{},"автоматичне відновлення"," (self-healing).",[3126,6343,6345],{"id":6344},"експеримент-видалення-pod","Експеримент: видалення Pod",[3114,6347,6348],{},"Видалимо один Pod вручну та подивимося, що станеться:",[3171,6350,6352,6361],{"title":6351},"kubectl delete pod",[3175,6353,6355,3192,6358],{"className":6354},[3178],[3180,6356,3191],{"className":6357},[3183],[3118,6359,6360],{},"kubectl delete pod nginx-deployment-7d6b8c9f4d-8xk2p",[3175,6362,6364],{"className":6363},[3178],"pod \"nginx-deployment-7d6b8c9f4d-8xk2p\" deleted",[3114,6366,6367],{},"Одразу перевіримо список Pod:",[3171,6369,6370,6378,6382,6386,6390],{"title":5759},[3175,6371,6373,3192,6376],{"className":6372},[3178],[3180,6374,3191],{"className":6375},[3183],[3118,6377,5759],{},[3175,6379,6381],{"className":6380},[3178],"NAME                                READY   STATUS              RESTARTS   AGE",[3175,6383,6385],{"className":6384},[3178],"nginx-deployment-7d6b8c9f4d-m5n7q   1/1     Running             0          3m",[3175,6387,6389],{"className":6388},[3178],"nginx-deployment-7d6b8c9f4d-z9w3r   1/1     Running             0          3m",[3175,6391,6393],{"className":6392},[3178],"nginx-deployment-7d6b8c9f4d-p4k8t   0/1     ContainerCreating   0          2s",[3114,6395,6396,6397,6400,6401,6404],{},"Бачимо новий Pod з іменем ",[3429,6398,6399],{},"p4k8t"," у стані ",[3429,6402,6403],{},"ContainerCreating",". Що сталося?",[3134,6406,6407,6412,6418,6423,6426],{},[3137,6408,6409,6410],{},"Ми видалили Pod ",[3429,6411,5813],{},[3137,6413,6414,6415,4229],{},"ReplicaSet Controller виявив, що реплік менше, ніж задано (",[3429,6416,6417],{},"2 \u003C 3",[3137,6419,6420,6421],{},"ReplicaSet автоматично створив новий Pod ",[3429,6422,6399],{},[3137,6424,6425],{},"Scheduler призначив Pod вузлу",[3137,6427,6428],{},"Kubelet завантажує образ та запускає контейнер",[3114,6430,6431],{},"Через кілька секунд:",[3171,6433,6435,6443,6446,6450,6454],{"title":6434},"kubectl get pods (через 10 секунд)",[3175,6436,6438,3192,6441],{"className":6437},[3178],[3180,6439,3191],{"className":6440},[3183],[3118,6442,5759],{},[3175,6444,5771],{"className":6445},[3178],[3175,6447,6449],{"className":6448},[3178],"nginx-deployment-7d6b8c9f4d-m5n7q   1/1     Running   0          3m15s",[3175,6451,6453],{"className":6452},[3178],"nginx-deployment-7d6b8c9f4d-z9w3r   1/1     Running   0          3m15s",[3175,6455,6457],{"className":6456},[3178],"nginx-deployment-7d6b8c9f4d-p4k8t   1/1     Running   0          15s",[3114,6459,6460,6461,6464,6465,6467],{},"Знову три репліки! Це і є ",[3118,6462,6463],{},"self-healing"," — система сама усуває розбіжності між бажаним станом (",[3429,6466,3431],{},") та поточним станом (2 працюючі Pod).",[4865,6469,6470,6475,6478,6498],{},[3114,6471,6472],{},[3118,6473,6474],{},"Reconciliation Loop (цикл узгодження):",[3114,6476,6477],{},"Deployment Controller та ReplicaSet Controller працюють за принципом reconciliation loop:",[3134,6479,6480,6486,6489,6492,6495],{},[3137,6481,6482,6483,4229],{},"Читають бажаний стан з API Server (",[3429,6484,6485],{},"spec.replicas: 3",[3137,6487,6488],{},"Читають поточний стан з API Server (скільки Pod насправді працює)",[3137,6490,6491],{},"Порівнюють бажаний та поточний стан",[3137,6493,6494],{},"Якщо є розбіжності — виконують дії для їх усунення (створюють або видаляють Pod)",[3137,6496,6497],{},"Повторюють цикл кожні кілька секунд",[3114,6499,6500,6501,6503],{},"Це фундаментальний принцип роботи Kubernetes — ",[3118,6502,3395],{}," через постійне узгодження стану.",[3126,6505,6507],{"id":6506},"візуалізація-reconciliation-loop","Візуалізація reconciliation loop",[3114,6509,6510],{},"Давайте детально розглянемо, як працює цикл узгодження при видаленні Pod:",[3482,6512,6513],{},[3485,6514,6516],{"className":3487,"code":6515,"language":3489,"meta":3490,"style":3490},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nparticipant \"ReplicaSet\\nController\" as rsc\nparticipant \"API Server\" as api\nparticipant \"etcd\" as etcd\nparticipant \"Scheduler\" as sched\nparticipant \"Kubelet\" as kubelet\n\n== Початковий стан: 3 Pod працюють ==\n\nrsc -> api: GET /pods?labelSelector=app=nginx\napi -> etcd: Читати Pod\netcd --> api: 3 Pod (Running)\napi --> rsc: 3 Pod знайдено\nrsc -> rsc: Порівняти: desired=3, current=3\\n✓ Стан узгоджено\n\n== Користувач видаляє Pod ==\n\nnote over api: kubectl delete pod nginx-xxx\napi -> etcd: Видалити Pod nginx-xxx\netcd --> api: OK\n\n== Наступна ітерація reconciliation loop ==\n\nrsc -> api: GET /pods?labelSelector=app=nginx\napi -> etcd: Читати Pod\netcd --> api: 2 Pod (Running)\napi --> rsc: 2 Pod знайдено\nrsc -> rsc: Порівняти: desired=3, current=2\\n✗ Розбіжність виявлено!\n\nrsc -> api: POST /pods (створити новий Pod)\napi -> etcd: Зберегти новий Pod\netcd --> api: OK\napi --> rsc: Pod створено\n\n== Scheduler призначає Pod вузлу ==\n\napi -> sched: Подія: новий Pod без вузла\nsched -> api: PATCH /pods/nginx-new (nodeName=node-1)\napi -> etcd: Оновити Pod\netcd --> api: OK\n\n== Kubelet запускає Pod ==\n\napi -> kubelet: Подія: Pod призначено node-1\nkubelet -> kubelet: Завантажити образ nginx:1.27\nkubelet -> kubelet: Запустити контейнер\nkubelet -> api: PATCH /pods/nginx-new (status=Running)\napi -> etcd: Оновити статус Pod\netcd --> api: OK\n\n== Наступна ітерація reconciliation loop ==\n\nrsc -> api: GET /pods?labelSelector=app=nginx\napi -> etcd: Читати Pod\netcd --> api: 3 Pod (Running)\napi --> rsc: 3 Pod знайдено\nrsc -> rsc: Порівняти: desired=3, current=3\\n✓ Стан узгоджено\n\nnote right of rsc\n  Reconciliation loop\n  виконується кожні\n  5-10 секунд\nend note\n\n@enduml\n",[3429,6517,6518,6522,6526,6530,6534,6539,6543,6548,6552,6556,6560,6565,6569,6574,6579,6584,6589,6594,6598,6603,6607,6612,6617,6622,6626,6631,6635,6639,6643,6648,6653,6658,6662,6667,6672,6676,6681,6685,6690,6694,6699,6704,6709,6713,6717,6722,6726,6731,6736,6740,6745,6750,6754,6758,6762,6766,6770,6775,6780,6785,6790,6795,6800,6806,6812,6818,6823,6828],{"__ignoreMap":3490},[3180,6519,6520],{"class":3178,"line":3495},[3180,6521,3498],{},[3180,6523,6524],{"class":3178,"line":3501},[3180,6525,3504],{},[3180,6527,6528],{"class":3178,"line":3507},[3180,6529,3510],{},[3180,6531,6532],{"class":3178,"line":3513},[3180,6533,3517],{"emptyLinePlaceholder":3516},[3180,6535,6536],{"class":3178,"line":3520},[3180,6537,6538],{},"participant \"ReplicaSet\\nController\" as rsc\n",[3180,6540,6541],{"class":3178,"line":3526},[3180,6542,6015],{},[3180,6544,6545],{"class":3178,"line":3532},[3180,6546,6547],{},"participant \"etcd\" as etcd\n",[3180,6549,6550],{"class":3178,"line":3538},[3180,6551,6030],{},[3180,6553,6554],{"class":3178,"line":3544},[3180,6555,6035],{},[3180,6557,6558],{"class":3178,"line":3549},[3180,6559,3517],{"emptyLinePlaceholder":3516},[3180,6561,6562],{"class":3178,"line":3555},[3180,6563,6564],{},"== Початковий стан: 3 Pod працюють ==\n",[3180,6566,6567],{"class":3178,"line":3561},[3180,6568,3517],{"emptyLinePlaceholder":3516},[3180,6570,6571],{"class":3178,"line":3567},[3180,6572,6573],{},"rsc -> api: GET /pods?labelSelector=app=nginx\n",[3180,6575,6576],{"class":3178,"line":3573},[3180,6577,6578],{},"api -> etcd: Читати Pod\n",[3180,6580,6581],{"class":3178,"line":3579},[3180,6582,6583],{},"etcd --> api: 3 Pod (Running)\n",[3180,6585,6586],{"class":3178,"line":3585},[3180,6587,6588],{},"api --> rsc: 3 Pod знайдено\n",[3180,6590,6591],{"class":3178,"line":3591},[3180,6592,6593],{},"rsc -> rsc: Порівняти: desired=3, current=3\\n✓ Стан узгоджено\n",[3180,6595,6596],{"class":3178,"line":3596},[3180,6597,3517],{"emptyLinePlaceholder":3516},[3180,6599,6600],{"class":3178,"line":3602},[3180,6601,6602],{},"== Користувач видаляє Pod ==\n",[3180,6604,6605],{"class":3178,"line":3607},[3180,6606,3517],{"emptyLinePlaceholder":3516},[3180,6608,6609],{"class":3178,"line":3612},[3180,6610,6611],{},"note over api: kubectl delete pod nginx-xxx\n",[3180,6613,6614],{"class":3178,"line":3618},[3180,6615,6616],{},"api -> etcd: Видалити Pod nginx-xxx\n",[3180,6618,6619],{"class":3178,"line":3624},[3180,6620,6621],{},"etcd --> api: OK\n",[3180,6623,6624],{"class":3178,"line":3630},[3180,6625,3517],{"emptyLinePlaceholder":3516},[3180,6627,6628],{"class":3178,"line":3636},[3180,6629,6630],{},"== Наступна ітерація reconciliation loop ==\n",[3180,6632,6633],{"class":3178,"line":3641},[3180,6634,3517],{"emptyLinePlaceholder":3516},[3180,6636,6637],{"class":3178,"line":3646},[3180,6638,6573],{},[3180,6640,6641],{"class":3178,"line":3651},[3180,6642,6578],{},[3180,6644,6645],{"class":3178,"line":3657},[3180,6646,6647],{},"etcd --> api: 2 Pod (Running)\n",[3180,6649,6650],{"class":3178,"line":3663},[3180,6651,6652],{},"api --> rsc: 2 Pod знайдено\n",[3180,6654,6655],{"class":3178,"line":3669},[3180,6656,6657],{},"rsc -> rsc: Порівняти: desired=3, current=2\\n✗ Розбіжність виявлено!\n",[3180,6659,6660],{"class":3178,"line":3675},[3180,6661,3517],{"emptyLinePlaceholder":3516},[3180,6663,6664],{"class":3178,"line":3681},[3180,6665,6666],{},"rsc -> api: POST /pods (створити новий Pod)\n",[3180,6668,6669],{"class":3178,"line":3687},[3180,6670,6671],{},"api -> etcd: Зберегти новий Pod\n",[3180,6673,6674],{"class":3178,"line":3692},[3180,6675,6621],{},[3180,6677,6678],{"class":3178,"line":3698},[3180,6679,6680],{},"api --> rsc: Pod створено\n",[3180,6682,6683],{"class":3178,"line":3704},[3180,6684,3517],{"emptyLinePlaceholder":3516},[3180,6686,6687],{"class":3178,"line":3710},[3180,6688,6689],{},"== Scheduler призначає Pod вузлу ==\n",[3180,6691,6692],{"class":3178,"line":3716},[3180,6693,3517],{"emptyLinePlaceholder":3516},[3180,6695,6696],{"class":3178,"line":3722},[3180,6697,6698],{},"api -> sched: Подія: новий Pod без вузла\n",[3180,6700,6701],{"class":3178,"line":3728},[3180,6702,6703],{},"sched -> api: PATCH /pods/nginx-new (nodeName=node-1)\n",[3180,6705,6706],{"class":3178,"line":3734},[3180,6707,6708],{},"api -> etcd: Оновити Pod\n",[3180,6710,6711],{"class":3178,"line":3739},[3180,6712,6621],{},[3180,6714,6715],{"class":3178,"line":3745},[3180,6716,3517],{"emptyLinePlaceholder":3516},[3180,6718,6719],{"class":3178,"line":3751},[3180,6720,6721],{},"== Kubelet запускає Pod ==\n",[3180,6723,6724],{"class":3178,"line":3757},[3180,6725,3517],{"emptyLinePlaceholder":3516},[3180,6727,6728],{"class":3178,"line":3763},[3180,6729,6730],{},"api -> kubelet: Подія: Pod призначено node-1\n",[3180,6732,6733],{"class":3178,"line":3768},[3180,6734,6735],{},"kubelet -> kubelet: Завантажити образ nginx:1.27\n",[3180,6737,6738],{"class":3178,"line":3773},[3180,6739,6151],{},[3180,6741,6742],{"class":3178,"line":3779},[3180,6743,6744],{},"kubelet -> api: PATCH /pods/nginx-new (status=Running)\n",[3180,6746,6747],{"class":3178,"line":3785},[3180,6748,6749],{},"api -> etcd: Оновити статус Pod\n",[3180,6751,6752],{"class":3178,"line":3791},[3180,6753,6621],{},[3180,6755,6756],{"class":3178,"line":3797},[3180,6757,3517],{"emptyLinePlaceholder":3516},[3180,6759,6760],{"class":3178,"line":3802},[3180,6761,6630],{},[3180,6763,6764],{"class":3178,"line":3807},[3180,6765,3517],{"emptyLinePlaceholder":3516},[3180,6767,6768],{"class":3178,"line":5443},[3180,6769,6573],{},[3180,6771,6773],{"class":3178,"line":6772},57,[3180,6774,6578],{},[3180,6776,6778],{"class":3178,"line":6777},58,[3180,6779,6583],{},[3180,6781,6783],{"class":3178,"line":6782},59,[3180,6784,6588],{},[3180,6786,6788],{"class":3178,"line":6787},60,[3180,6789,6593],{},[3180,6791,6793],{"class":3178,"line":6792},61,[3180,6794,3517],{"emptyLinePlaceholder":3516},[3180,6796,6798],{"class":3178,"line":6797},62,[3180,6799,6198],{},[3180,6801,6803],{"class":3178,"line":6802},63,[3180,6804,6805],{},"  Reconciliation loop\n",[3180,6807,6809],{"class":3178,"line":6808},64,[3180,6810,6811],{},"  виконується кожні\n",[3180,6813,6815],{"class":3178,"line":6814},65,[3180,6816,6817],{},"  5-10 секунд\n",[3180,6819,6821],{"class":3178,"line":6820},66,[3180,6822,3731],{},[3180,6824,6826],{"class":3178,"line":6825},67,[3180,6827,3517],{"emptyLinePlaceholder":3516},[3180,6829,6831],{"class":3178,"line":6830},68,[3180,6832,3810],{},[3114,6834,6835],{},[3118,6836,6837],{},"Ключові моменти:",[3134,6839,6840,6846,6852,6858],{},[3137,6841,6842,6845],{},[3118,6843,6844],{},"Постійний моніторинг"," — ReplicaSet Controller не чекає на події. Він постійно (кожні 5-10 секунд) перевіряє стан Pod.",[3137,6847,6848,6851],{},[3118,6849,6850],{},"Декларативність"," — контролер не знає, чому Pod зникли (видалення, збій вузла, OOMKilled). Він просто бачить розбіжність між бажаним та поточним станом і усуває її.",[3137,6853,6854,6857],{},[3118,6855,6856],{},"Ідемпотентність"," — якщо стан уже узгоджено, контролер нічого не робить. Це безпечно викликати reconciliation багато разів.",[3137,6859,6860,6863],{},[3118,6861,6862],{},"Розподілена робота"," — ReplicaSet Controller створює Pod, Scheduler призначає вузол, Kubelet запускає контейнер. Кожен компонент відповідає за свою частину.",[3382,6865],{},[3109,6867,6869],{"id":6868},"масштабування-deployment","Масштабування Deployment",[3114,6871,6872,6873,6876],{},"Одна з найважливіших можливостей Deployment — ",[3118,6874,6875],{},"масштабування"," (scaling). Це зміна кількості реплік Pod для адаптації до навантаження.",[3126,6878,6880],{"id":6879},"навіщо-потрібне-масштабування","Навіщо потрібне масштабування",[3114,6882,6883],{},"Уявіть, що ваш веб-застосунок отримує різне навантаження протягом дня:",[4763,6885,6886,6892,6898],{},[3137,6887,6888,6891],{},[3118,6889,6890],{},"Ніч (02:00-06:00):"," 100 запитів/хвилину — достатньо 2 реплік",[3137,6893,6894,6897],{},[3118,6895,6896],{},"День (09:00-18:00):"," 1000 запитів/хвилину — потрібно 5 реплік",[3137,6899,6900,6903],{},[3118,6901,6902],{},"Пікове навантаження (12:00-13:00):"," 5000 запитів/хвилину — потрібно 10 реплік",[3114,6905,6906],{},"Без масштабування вам довелося б постійно тримати 10 реплік, витрачаючи ресурси навіть коли вони не потрібні. З масштабуванням ви можете динамічно змінювати кількість реплік.",[3126,6908,6910],{"id":6909},"три-способи-масштабування","Три способи масштабування",[3114,6912,6913],{},"Kubernetes надає три способи змінити кількість реплік Deployment:",[3303,6915,6916,6921,6926],{},[3306,6917,6920],{"icon":6918,"title":6919},"i-heroicons-command-line","1. Команда kubectl scale","Найшвидший спосіб — одна команда. Ідеально для ручного масштабування або експериментів.",[3306,6922,6925],{"icon":6923,"title":6924},"i-heroicons-document-text","2. Редагування YAML та kubectl apply","Декларативний підхід — змінюєте файл, застосовуєте. Зміни зберігаються у версійному контролі (Git).",[3306,6927,6930],{"icon":6928,"title":6929},"i-heroicons-pencil","3. Команда kubectl edit","Інтерактивне редагування — відкриває редактор з поточною конфігурацією. Зручно для швидких змін без локальних файлів.",[3114,6932,6933],{},"Розглянемо кожен спосіб детально.",[3382,6935],{},[3126,6937,6939],{"id":6938},"спосіб-1-kubectl-scale","Спосіб 1: kubectl scale",[3114,6941,6942,6943,3480],{},"Найпростіший спосіб — команда ",[3429,6944,3450],{},[3171,6946,6947,6956],{"title":3450},[3175,6948,6950,3192,6953],{"className":6949},[3178],[3180,6951,3191],{"className":6952},[3183],[3118,6954,6955],{},"kubectl scale deployment nginx-deployment --replicas=5",[3175,6957,6959],{"className":6958},[3178],[3180,6960,6962],{"className":6961},[5673],"deployment.apps/nginx-deployment scaled",[3114,6964,6965],{},"Перевіримо результат:",[3171,6967,6968,6976,6979,6983,6987,6991,6995],{"title":5759},[3175,6969,6971,3192,6974],{"className":6970},[3178],[3180,6972,3191],{"className":6973},[3183],[3118,6975,5759],{},[3175,6977,6381],{"className":6978},[3178],[3175,6980,6982],{"className":6981},[3178],"nginx-deployment-7d6b8c9f4d-m5n7q   1/1     Running             0          10m",[3175,6984,6986],{"className":6985},[3178],"nginx-deployment-7d6b8c9f4d-z9w3r   1/1     Running             0          10m",[3175,6988,6990],{"className":6989},[3178],"nginx-deployment-7d6b8c9f4d-p4k8t   1/1     Running             0          7m",[3175,6992,6994],{"className":6993},[3178],"nginx-deployment-7d6b8c9f4d-x2n9k   0/1     ContainerCreating   0          2s",[3175,6996,6998],{"className":6997},[3178],"nginx-deployment-7d6b8c9f4d-q7m4p   0/1     ContainerCreating   0          2s",[3114,7000,7001,7002,4110,7005,7008],{},"Kubernetes створив 2 нові Pod (",[3429,7003,7004],{},"x2n9k",[3429,7006,7007],{},"q7m4p",") для досягнення бажаної кількості 5 реплік.",[3114,7010,6431],{},[3171,7012,7013,7021,7024,7028,7032,7036,7040],{"title":6434},[3175,7014,7016,3192,7019],{"className":7015},[3178],[3180,7017,3191],{"className":7018},[3183],[3118,7020,5759],{},[3175,7022,5771],{"className":7023},[3178],[3175,7025,7027],{"className":7026},[3178],"nginx-deployment-7d6b8c9f4d-m5n7q   1/1     Running   0          10m",[3175,7029,7031],{"className":7030},[3178],"nginx-deployment-7d6b8c9f4d-z9w3r   1/1     Running   0          10m",[3175,7033,7035],{"className":7034},[3178],"nginx-deployment-7d6b8c9f4d-p4k8t   1/1     Running   0          7m",[3175,7037,7039],{"className":7038},[3178],"nginx-deployment-7d6b8c9f4d-x2n9k   1/1     Running   0          15s",[3175,7041,7043],{"className":7042},[3178],"nginx-deployment-7d6b8c9f4d-q7m4p   1/1     Running   0          15s",[3114,7045,7046],{},"Тепер усі 5 реплік працюють.",[3114,7048,7049],{},[3118,7050,7051],{},"Зменшення кількості реплік:",[3114,7053,7054],{},"Так само легко зменшити кількість реплік:",[3171,7056,7058,7067],{"title":7057},"kubectl scale (зменшення)",[3175,7059,7061,3192,7064],{"className":7060},[3178],[3180,7062,3191],{"className":7063},[3183],[3118,7065,7066],{},"kubectl scale deployment nginx-deployment --replicas=2",[3175,7068,7070],{"className":7069},[3178],[3180,7071,6962],{"className":7072},[5673],[3171,7074,7075,7083,7087,7091,7095,7099,7103],{"title":5759},[3175,7076,7078,3192,7081],{"className":7077},[3178],[3180,7079,3191],{"className":7080},[3183],[3118,7082,5759],{},[3175,7084,7086],{"className":7085},[3178],"NAME                                READY   STATUS        RESTARTS   AGE",[3175,7088,7090],{"className":7089},[3178],"nginx-deployment-7d6b8c9f4d-m5n7q   1/1     Running       0          12m",[3175,7092,7094],{"className":7093},[3178],"nginx-deployment-7d6b8c9f4d-z9w3r   1/1     Running       0          12m",[3175,7096,7098],{"className":7097},[3178],"nginx-deployment-7d6b8c9f4d-p4k8t   1/1     Terminating   0          9m",[3175,7100,7102],{"className":7101},[3178],"nginx-deployment-7d6b8c9f4d-x2n9k   1/1     Terminating   0          2m",[3175,7104,7106],{"className":7105},[3178],"nginx-deployment-7d6b8c9f4d-q7m4p   1/1     Terminating   0          2m",[3114,7108,7109,7110,7113],{},"Kubernetes видаляє 3 зайві Pod. Статус ",[3429,7111,7112],{},"Terminating"," означає, що Pod отримав сигнал завершення (SIGTERM) і має 30 секунд (за замовчуванням) для graceful shutdown.",[3406,7115,7116,7121,7124,7150],{},[3114,7117,7118],{},[3118,7119,7120],{},"Який Pod буде видалено першим?",[3114,7122,7123],{},"Kubernetes використовує наступний порядок пріоритетів при виборі Pod для видалення:",[3134,7125,7126,7132,7138,7144],{},[3137,7127,7128,7131],{},[3118,7129,7130],{},"Unscheduled Pod"," (ще не призначені вузлу) — видаляються першими",[3137,7133,7134,7137],{},[3118,7135,7136],{},"Pending Pod"," (чекають на ресурси) — наступні",[3137,7139,7140,7143],{},[3118,7141,7142],{},"Running Pod на вузлах з більшою кількістю реплік"," — для балансування навантаження",[3137,7145,7146,7149],{},[3118,7147,7148],{},"Новіші Pod"," (за часом створення) — старіші Pod зберігаються, бо вони довше працюють без проблем",[3114,7151,7152],{},"Це гарантує, що масштабування не порушить балансування навантаження та стабільність.",[3114,7154,7155],{},[3118,7156,7157],{},"Переваги kubectl scale:",[4763,7159,7160,7166,7172],{},[3137,7161,7162,7165],{},[3118,7163,7164],{},"Швидкість"," — одна команда, миттєвий результат",[3137,7167,7168,7171],{},[3118,7169,7170],{},"Простота"," — не потрібно редагувати файли",[3137,7173,7174,7177],{},[3118,7175,7176],{},"Ідеально для експериментів"," — швидко збільшити/зменшити репліки для тестування",[3114,7179,7180],{},[3118,7181,7182],{},"Недоліки kubectl scale:",[4763,7184,7185,7191,7200],{},[3137,7186,7187,7190],{},[3118,7188,7189],{},"Не зберігається у Git"," — зміна не відображена у YAML файлах",[3137,7192,7193,7196,7197,7199],{},[3118,7194,7195],{},"Можна забути"," — якщо пізніше застосуєте старий YAML з ",[3429,7198,3431],{},", масштабування скасується",[3137,7201,7202,7205],{},[3118,7203,7204],{},"Не підходить для production"," — краще використовувати декларативний підхід (YAML)",[3382,7207],{},[3126,7209,7211],{"id":7210},"спосіб-2-редагування-yaml-та-kubectl-apply","Спосіб 2: Редагування YAML та kubectl apply",[3114,7213,7214],{},"Декларативний підхід — змінюєте YAML файл та застосовуєте його:",[3114,7216,7217,7220,7221,7223,7224,3480],{},[3118,7218,7219],{},"Крок 1:"," Відкрийте ",[3429,7222,5491],{}," та змініть ",[3429,7225,4243],{},[3485,7227,7229],{"className":3863,"code":7228,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx-deployment\nspec:\n  replicas: 7  # ← Було 3, стало 7\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n          ports:\n            - containerPort: 80\n",[3429,7230,7231,7239,7247,7253,7261,7267,7279,7285,7291,7299,7305,7311,7317,7325,7331,7337,7347,7355,7361],{"__ignoreMap":3490},[3180,7232,7233,7235,7237],{"class":3178,"line":3495},[3180,7234,3873],{"class":3872},[3180,7236,3877],{"class":3876},[3180,7238,3881],{"class":3880},[3180,7240,7241,7243,7245],{"class":3178,"line":3501},[3180,7242,3886],{"class":3872},[3180,7244,3877],{"class":3876},[3180,7246,3891],{"class":3880},[3180,7248,7249,7251],{"class":3178,"line":3507},[3180,7250,3896],{"class":3872},[3180,7252,3899],{"class":3876},[3180,7254,7255,7257,7259],{"class":3178,"line":3513},[3180,7256,3904],{"class":3872},[3180,7258,3877],{"class":3876},[3180,7260,3909],{"class":3880},[3180,7262,7263,7265],{"class":3178,"line":3520},[3180,7264,3914],{"class":3872},[3180,7266,3899],{"class":3876},[3180,7268,7269,7271,7273,7276],{"class":3178,"line":3526},[3180,7270,3921],{"class":3872},[3180,7272,3877],{"class":3876},[3180,7274,7275],{"class":3926},"7",[3180,7277,7278],{"class":4183},"  # ← Було 3, стало 7\n",[3180,7280,7281,7283],{"class":3178,"line":3532},[3180,7282,3932],{"class":3872},[3180,7284,3899],{"class":3876},[3180,7286,7287,7289],{"class":3178,"line":3538},[3180,7288,3939],{"class":3872},[3180,7290,3899],{"class":3876},[3180,7292,7293,7295,7297],{"class":3178,"line":3544},[3180,7294,3946],{"class":3872},[3180,7296,3877],{"class":3876},[3180,7298,3951],{"class":3880},[3180,7300,7301,7303],{"class":3178,"line":3549},[3180,7302,3956],{"class":3872},[3180,7304,3899],{"class":3876},[3180,7306,7307,7309],{"class":3178,"line":3555},[3180,7308,3963],{"class":3872},[3180,7310,3899],{"class":3876},[3180,7312,7313,7315],{"class":3178,"line":3561},[3180,7314,3970],{"class":3872},[3180,7316,3899],{"class":3876},[3180,7318,7319,7321,7323],{"class":3178,"line":3567},[3180,7320,3977],{"class":3872},[3180,7322,3877],{"class":3876},[3180,7324,3951],{"class":3880},[3180,7326,7327,7329],{"class":3178,"line":3573},[3180,7328,3986],{"class":3872},[3180,7330,3899],{"class":3876},[3180,7332,7333,7335],{"class":3178,"line":3579},[3180,7334,3993],{"class":3872},[3180,7336,3899],{"class":3876},[3180,7338,7339,7341,7343,7345],{"class":3178,"line":3585},[3180,7340,4000],{"class":3876},[3180,7342,4003],{"class":3872},[3180,7344,3877],{"class":3876},[3180,7346,3951],{"class":3880},[3180,7348,7349,7351,7353],{"class":3178,"line":3591},[3180,7350,4012],{"class":3872},[3180,7352,3877],{"class":3876},[3180,7354,4017],{"class":3880},[3180,7356,7357,7359],{"class":3178,"line":3596},[3180,7358,4554],{"class":3872},[3180,7360,3899],{"class":3876},[3180,7362,7363,7365,7367,7369],{"class":3178,"line":3602},[3180,7364,4561],{"class":3876},[3180,7366,4564],{"class":3872},[3180,7368,3877],{"class":3876},[3180,7370,5647],{"class":3926},[3114,7372,7373,7376],{},[3118,7374,7375],{},"Крок 2:"," Застосуйте зміни:",[3171,7378,7379,7387],{"title":5657},[3175,7380,7382,3192,7385],{"className":7381},[3178],[3180,7383,3191],{"className":7384},[3183],[3118,7386,5666],{},[3175,7388,7390],{"className":7389},[3178],[3180,7391,7393],{"className":7392},[5673],"deployment.apps/nginx-deployment configured",[3114,7395,7396,7397,7400,7401,7404],{},"Зверніть увагу на слово ",[3118,7398,7399],{},"configured"," (а не ",[3118,7402,7403],{},"created","). Це означає, що Deployment уже існував, і Kubernetes застосував зміни.",[3114,7406,7407,7410],{},[3118,7408,7409],{},"Крок 3:"," Перевірте результат:",[3171,7412,7413,7421,7424],{"title":5684},[3175,7414,7416,3192,7419],{"className":7415},[3178],[3180,7417,3191],{"className":7418},[3183],[3118,7420,5684],{},[3175,7422,5696],{"className":7423},[3178],[3175,7425,7427],{"className":7426},[3178],"nginx-deployment   7/7     7            7           20m",[3114,7429,7430],{},"Тепер працює 7 реплік.",[3114,7432,7433],{},[3118,7434,7435],{},"Переваги kubectl apply:",[4763,7437,7438,7443,7449,7455],{},[3137,7439,7440,7442],{},[3118,7441,6850],{}," — YAML файл — це єдине джерело правди (single source of truth)",[3137,7444,7445,7448],{},[3118,7446,7447],{},"Версійний контроль"," — зміни зберігаються у Git, можна відстежити історію",[3137,7450,7451,7454],{},[3118,7452,7453],{},"Відтворюваність"," — можна застосувати той самий YAML на іншому кластері",[3137,7456,7457,7460],{},[3118,7458,7459],{},"Production-ready"," — рекомендований підхід для production",[3114,7462,7463],{},[3118,7464,7465],{},"Недоліки kubectl apply:",[4763,7467,7468,7474],{},[3137,7469,7470,7473],{},[3118,7471,7472],{},"Повільніше"," — потрібно редагувати файл, зберегти, застосувати",[3137,7475,7476,7479],{},[3118,7477,7478],{},"Потрібен локальний файл"," — якщо ви на іншій машині без файлів, доведеться спочатку отримати YAML",[4865,7481,7482,7491],{},[3114,7483,7484,7487,7488,7490],{},[3118,7485,7486],{},"Best practice:"," Завжди використовуйте ",[3429,7489,5657],{}," для production. Зберігайте всі YAML файли у Git. Це дозволяє:",[4763,7492,7493,7496,7502,7505],{},[3137,7494,7495],{},"Відстежувати, хто і коли змінив конфігурацію",[3137,7497,7498,7499],{},"Повернутись до попередньої версії через ",[3429,7500,7501],{},"git revert",[3137,7503,7504],{},"Автоматизувати розгортання через CI/CD (GitHub Actions, GitLab CI)",[3137,7506,7507],{},"Мати документацію інфраструктури у вигляді коду (Infrastructure as Code)",[3382,7509],{},[3126,7511,7513],{"id":7512},"спосіб-3-kubectl-edit","Спосіб 3: kubectl edit",[3114,7515,7516],{},"Інтерактивне редагування — відкриває редактор з поточною конфігурацією:",[3171,7518,7520,7529],{"title":7519},"kubectl edit",[3175,7521,7523,3192,7526],{"className":7522},[3178],[3180,7524,3191],{"className":7525},[3183],[3118,7527,7528],{},"kubectl edit deployment nginx-deployment",[3175,7530,7532],{"className":7531},[3178],[3180,7533,7535],{"className":7534},[3183],"# Відкриється редактор (vim/nano) з YAML",[3114,7537,7538,7539,7541],{},"У редакторі ви побачите повну конфігурацію Deployment (включно з полями, які додав Kubernetes автоматично). Знайдіть рядок ",[3429,7540,4243],{}," та змініть значення:",[3485,7543,7545],{"className":3863,"code":7544,"language":3865,"meta":3490,"style":3490},"spec:\n  replicas: 4  # ← Змініть на потрібне значення\n",[3429,7546,7547,7553],{"__ignoreMap":3490},[3180,7548,7549,7551],{"class":3178,"line":3495},[3180,7550,3914],{"class":3872},[3180,7552,3899],{"class":3876},[3180,7554,7555,7557,7559,7562],{"class":3178,"line":3501},[3180,7556,3921],{"class":3872},[3180,7558,3877],{"class":3876},[3180,7560,7561],{"class":3926},"4",[3180,7563,7564],{"class":4183},"  # ← Змініть на потрібне значення\n",[3114,7566,7567,7568,7571,7572,4789,7575,4802],{},"Збережіть та закрийте редактор (у vim: ",[3429,7569,7570],{},":wq",", у nano: ",[3429,7573,7574],{},"Ctrl+O",[3429,7576,7577],{},"Ctrl+X",[3171,7579,7581],{"title":7580},"kubectl edit (результат)",[3175,7582,7584],{"className":7583},[3178],[3180,7585,7587],{"className":7586},[5673],"deployment.apps/nginx-deployment edited",[3114,7589,7590],{},"Kubernetes одразу застосує зміни.",[3114,7592,7593],{},[3118,7594,7595],{},"Переваги kubectl edit:",[4763,7597,7598,7604,7610],{},[3137,7599,7600,7603],{},[3118,7601,7602],{},"Не потрібен локальний файл"," — редагуєте конфігурацію безпосередньо у кластері",[3137,7605,7606,7609],{},[3118,7607,7608],{},"Швидше за kubectl apply"," — не потрібно шукати файл, редагувати, зберігати",[3137,7611,7612,7615],{},[3118,7613,7614],{},"Бачите всі поля"," — включно з тими, що додав Kubernetes автоматично",[3114,7617,7618],{},[3118,7619,7620],{},"Недоліки kubectl edit:",[4763,7622,7623,7628,7634],{},[3137,7624,7625,7627],{},[3118,7626,7189],{}," — зміни не відображені у локальних файлах",[3137,7629,7630,7633],{},[3118,7631,7632],{},"Складніше для новачків"," — потрібно вміти користуватись vim/nano",[3137,7635,7636,7639],{},[3118,7637,7638],{},"Можна зробити помилку"," — якщо випадково змінити щось інше, можна зламати Deployment",[3835,7641,7642,7647,7656,7667],{},[3114,7643,7644],{},[3118,7645,7646],{},"Обережно з kubectl edit у production!",[3114,7648,7649,7651,7652,7655],{},[3429,7650,7519],{}," зручний для швидких експериментів, але ",[3118,7653,7654],{},"не рекомендується для production",". Причини:",[3134,7657,7658,7661,7664],{},[3137,7659,7660],{},"Зміни не зберігаються у Git — немає історії",[3137,7662,7663],{},"Легко зробити помилку — випадково змінити щось інше",[3137,7665,7666],{},"Немає code review — зміни застосовуються одразу без перевірки колегами",[3114,7668,7669,7670,7672,7673,7675],{},"Використовуйте ",[3429,7671,7519],{}," лише для debugging або швидких тестів. Для production завжди використовуйте ",[3429,7674,5657],{}," з YAML файлами у Git.",[3382,7677],{},[3126,7679,7681],{"id":7680},"порівняння-способів-масштабування","Порівняння способів масштабування",[3303,7683,7684,7708,7731],{},[3306,7685,7687,7692,7703],{"icon":7686,"title":3450},"i-heroicons-bolt",[3114,7688,7689],{},[3118,7690,7691],{},"Коли використовувати:",[4763,7693,7694,7697,7700],{},[3137,7695,7696],{},"Швидкі експерименти",[3137,7698,7699],{},"Тимчасове масштабування під час інциденту",[3137,7701,7702],{},"Локальна розробка (Minikube)",[3114,7704,7705,7707],{},[3118,7706,4807],{}," Раптовий сплеск трафіку — швидко збільшити репліки, поки не з'ясуєте причину.",[3306,7709,7711,7715,7726],{"icon":7710,"title":5657},"i-heroicons-document-check",[3114,7712,7713],{},[3118,7714,7691],{},[4763,7716,7717,7720,7723],{},[3137,7718,7719],{},"Production розгортання",[3137,7721,7722],{},"CI/CD pipelines",[3137,7724,7725],{},"Будь-які зміни, які мають зберігатись",[3114,7727,7728,7730],{},[3118,7729,4807],{}," Планове збільшення реплік перед маркетинговою кампанією — змінюєте YAML, робите commit, застосовуєте через CI/CD.",[3306,7732,7734,7738,7749],{"icon":7733,"title":7519},"i-heroicons-pencil-square",[3114,7735,7736],{},[3118,7737,7691],{},[4763,7739,7740,7743,7746],{},[3137,7741,7742],{},"Debugging",[3137,7744,7745],{},"Немає доступу до локальних файлів",[3137,7747,7748],{},"Швидкі тести",[3114,7750,7751,7753],{},[3118,7752,4807],{}," Ви на сервері без Git репозиторію, потрібно швидко змінити конфігурацію для перевірки гіпотези.",[3382,7755],{},[3126,7757,7759],{"id":7758},"візуалізація-процесу-масштабування","Візуалізація процесу масштабування",[3114,7761,7762],{},"Давайте подивимося, що відбувається всередині Kubernetes при масштабуванні з 3 до 5 реплік:",[3482,7764,7765],{},[3485,7766,7768],{"className":3487,"code":7767,"language":3489,"meta":3490,"style":3490},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nparticipant \"kubectl\" as kubectl\nparticipant \"API Server\" as api\nparticipant \"etcd\" as etcd\nparticipant \"Deployment\\nController\" as dc\nparticipant \"ReplicaSet\\nController\" as rsc\nparticipant \"Scheduler\" as sched\nparticipant \"Kubelet\\n(node-1)\" as kubelet1\nparticipant \"Kubelet\\n(node-2)\" as kubelet2\n\n== Користувач виконує масштабування ==\n\nkubectl -> api: PATCH /deployments/nginx-deployment\\n{spec: {replicas: 5}}\napi -> etcd: Оновити Deployment (replicas: 3 → 5)\netcd --> api: OK\napi --> kubectl: deployment.apps/nginx-deployment scaled\n\n== Deployment Controller виявляє зміну ==\n\napi -> dc: Подія: Deployment змінено\nactivate dc\ndc -> api: GET /replicasets?owner=nginx-deployment\napi -> etcd: Читати ReplicaSet\netcd --> api: ReplicaSet nginx-xxx (replicas: 3)\napi --> dc: ReplicaSet знайдено\n\ndc -> api: PATCH /replicasets/nginx-xxx\\n{spec: {replicas: 5}}\napi -> etcd: Оновити ReplicaSet (replicas: 3 → 5)\netcd --> api: OK\ndeactivate dc\n\n== ReplicaSet Controller виявляє зміну ==\n\napi -> rsc: Подія: ReplicaSet змінено\nactivate rsc\nrsc -> api: GET /pods?labelSelector=app=nginx\napi -> etcd: Читати Pod\netcd --> api: 3 Pod (Running)\napi --> rsc: 3 Pod знайдено\n\nrsc -> rsc: Порівняти: desired=5, current=3\\nПотрібно створити 2 Pod\n\nrsc -> api: POST /pods (Pod 4)\napi -> etcd: Зберегти Pod 4\netcd --> api: OK\n\nrsc -> api: POST /pods (Pod 5)\napi -> etcd: Зберегти Pod 5\netcd --> api: OK\ndeactivate rsc\n\n== Scheduler призначає Pod вузлам ==\n\napi -> sched: Подія: 2 нові Pod без вузла\nactivate sched\nsched -> api: GET /nodes\napi -> etcd: Читати вузли\netcd --> api: node-1 (CPU: 50%), node-2 (CPU: 30%)\napi --> sched: 2 вузли доступні\n\nsched -> sched: Вибрати вузли:\\nPod 4 → node-2 (менше навантаження)\\nPod 5 → node-1\n\nsched -> api: PATCH /pods/nginx-xxx-4 (nodeName=node-2)\napi -> etcd: Оновити Pod 4\netcd --> api: OK\n\nsched -> api: PATCH /pods/nginx-xxx-5 (nodeName=node-1)\napi -> etcd: Оновити Pod 5\netcd --> api: OK\ndeactivate sched\n\n== Kubelet запускають Pod ==\n\napi -> kubelet2: Подія: Pod 4 призначено node-2\nactivate kubelet2\nkubelet2 -> kubelet2: Завантажити образ nginx:1.27\nkubelet2 -> kubelet2: Запустити контейнер\nkubelet2 -> api: PATCH /pods/nginx-xxx-4\\n(status=Running)\napi -> etcd: Оновити статус Pod 4\netcd --> api: OK\ndeactivate kubelet2\n\napi -> kubelet1: Подія: Pod 5 призначено node-1\nactivate kubelet1\nkubelet1 -> kubelet1: Образ вже є (кешовано)\nkubelet1 -> kubelet1: Запустити контейнер\nkubelet1 -> api: PATCH /pods/nginx-xxx-5\\n(status=Running)\napi -> etcd: Оновити статус Pod 5\netcd --> api: OK\ndeactivate kubelet1\n\nnote right of dc\n  Весь процес займає\n  5-15 секунд залежно від:\n  - Швидкості завантаження образу\n  - Доступності ресурсів\n  - Швидкості старту застосунку\nend note\n\n@enduml\n",[3429,7769,7770,7774,7778,7782,7786,7790,7794,7798,7803,7807,7811,7816,7821,7825,7830,7834,7839,7844,7848,7853,7857,7862,7866,7871,7875,7880,7885,7890,7895,7899,7904,7909,7913,7917,7921,7926,7930,7935,7939,7943,7947,7951,7955,7959,7964,7968,7973,7978,7982,7986,7991,7996,8000,8004,8008,8013,8017,8022,8026,8031,8036,8041,8046,8050,8055,8059,8064,8069,8073,8078,8084,8090,8095,8100,8105,8111,8116,8122,8128,8134,8140,8146,8152,8157,8163,8168,8174,8180,8186,8192,8198,8204,8209,8215,8220,8225,8231,8237,8243,8249,8255,8260,8265],{"__ignoreMap":3490},[3180,7771,7772],{"class":3178,"line":3495},[3180,7773,3498],{},[3180,7775,7776],{"class":3178,"line":3501},[3180,7777,3504],{},[3180,7779,7780],{"class":3178,"line":3507},[3180,7781,3510],{},[3180,7783,7784],{"class":3178,"line":3513},[3180,7785,3517],{"emptyLinePlaceholder":3516},[3180,7787,7788],{"class":3178,"line":3520},[3180,7789,6010],{},[3180,7791,7792],{"class":3178,"line":3526},[3180,7793,6015],{},[3180,7795,7796],{"class":3178,"line":3532},[3180,7797,6547],{},[3180,7799,7800],{"class":3178,"line":3538},[3180,7801,7802],{},"participant \"Deployment\\nController\" as dc\n",[3180,7804,7805],{"class":3178,"line":3544},[3180,7806,6538],{},[3180,7808,7809],{"class":3178,"line":3549},[3180,7810,6030],{},[3180,7812,7813],{"class":3178,"line":3555},[3180,7814,7815],{},"participant \"Kubelet\\n(node-1)\" as kubelet1\n",[3180,7817,7818],{"class":3178,"line":3561},[3180,7819,7820],{},"participant \"Kubelet\\n(node-2)\" as kubelet2\n",[3180,7822,7823],{"class":3178,"line":3567},[3180,7824,3517],{"emptyLinePlaceholder":3516},[3180,7826,7827],{"class":3178,"line":3573},[3180,7828,7829],{},"== Користувач виконує масштабування ==\n",[3180,7831,7832],{"class":3178,"line":3579},[3180,7833,3517],{"emptyLinePlaceholder":3516},[3180,7835,7836],{"class":3178,"line":3585},[3180,7837,7838],{},"kubectl -> api: PATCH /deployments/nginx-deployment\\n{spec: {replicas: 5}}\n",[3180,7840,7841],{"class":3178,"line":3591},[3180,7842,7843],{},"api -> etcd: Оновити Deployment (replicas: 3 → 5)\n",[3180,7845,7846],{"class":3178,"line":3596},[3180,7847,6621],{},[3180,7849,7850],{"class":3178,"line":3602},[3180,7851,7852],{},"api --> kubectl: deployment.apps/nginx-deployment scaled\n",[3180,7854,7855],{"class":3178,"line":3607},[3180,7856,3517],{"emptyLinePlaceholder":3516},[3180,7858,7859],{"class":3178,"line":3612},[3180,7860,7861],{},"== Deployment Controller виявляє зміну ==\n",[3180,7863,7864],{"class":3178,"line":3618},[3180,7865,3517],{"emptyLinePlaceholder":3516},[3180,7867,7868],{"class":3178,"line":3624},[3180,7869,7870],{},"api -> dc: Подія: Deployment змінено\n",[3180,7872,7873],{"class":3178,"line":3630},[3180,7874,6059],{},[3180,7876,7877],{"class":3178,"line":3636},[3180,7878,7879],{},"dc -> api: GET /replicasets?owner=nginx-deployment\n",[3180,7881,7882],{"class":3178,"line":3641},[3180,7883,7884],{},"api -> etcd: Читати ReplicaSet\n",[3180,7886,7887],{"class":3178,"line":3646},[3180,7888,7889],{},"etcd --> api: ReplicaSet nginx-xxx (replicas: 3)\n",[3180,7891,7892],{"class":3178,"line":3651},[3180,7893,7894],{},"api --> dc: ReplicaSet знайдено\n",[3180,7896,7897],{"class":3178,"line":3657},[3180,7898,3517],{"emptyLinePlaceholder":3516},[3180,7900,7901],{"class":3178,"line":3663},[3180,7902,7903],{},"dc -> api: PATCH /replicasets/nginx-xxx\\n{spec: {replicas: 5}}\n",[3180,7905,7906],{"class":3178,"line":3669},[3180,7907,7908],{},"api -> etcd: Оновити ReplicaSet (replicas: 3 → 5)\n",[3180,7910,7911],{"class":3178,"line":3675},[3180,7912,6621],{},[3180,7914,7915],{"class":3178,"line":3681},[3180,7916,6069],{},[3180,7918,7919],{"class":3178,"line":3687},[3180,7920,3517],{"emptyLinePlaceholder":3516},[3180,7922,7923],{"class":3178,"line":3692},[3180,7924,7925],{},"== ReplicaSet Controller виявляє зміну ==\n",[3180,7927,7928],{"class":3178,"line":3698},[3180,7929,3517],{"emptyLinePlaceholder":3516},[3180,7931,7932],{"class":3178,"line":3704},[3180,7933,7934],{},"api -> rsc: Подія: ReplicaSet змінено\n",[3180,7936,7937],{"class":3178,"line":3710},[3180,7938,6083],{},[3180,7940,7941],{"class":3178,"line":3716},[3180,7942,6573],{},[3180,7944,7945],{"class":3178,"line":3722},[3180,7946,6578],{},[3180,7948,7949],{"class":3178,"line":3728},[3180,7950,6583],{},[3180,7952,7953],{"class":3178,"line":3734},[3180,7954,6588],{},[3180,7956,7957],{"class":3178,"line":3739},[3180,7958,3517],{"emptyLinePlaceholder":3516},[3180,7960,7961],{"class":3178,"line":3745},[3180,7962,7963],{},"rsc -> rsc: Порівняти: desired=5, current=3\\nПотрібно створити 2 Pod\n",[3180,7965,7966],{"class":3178,"line":3751},[3180,7967,3517],{"emptyLinePlaceholder":3516},[3180,7969,7970],{"class":3178,"line":3757},[3180,7971,7972],{},"rsc -> api: POST /pods (Pod 4)\n",[3180,7974,7975],{"class":3178,"line":3763},[3180,7976,7977],{},"api -> etcd: Зберегти Pod 4\n",[3180,7979,7980],{"class":3178,"line":3768},[3180,7981,6621],{},[3180,7983,7984],{"class":3178,"line":3773},[3180,7985,3517],{"emptyLinePlaceholder":3516},[3180,7987,7988],{"class":3178,"line":3779},[3180,7989,7990],{},"rsc -> api: POST /pods (Pod 5)\n",[3180,7992,7993],{"class":3178,"line":3785},[3180,7994,7995],{},"api -> etcd: Зберегти Pod 5\n",[3180,7997,7998],{"class":3178,"line":3791},[3180,7999,6621],{},[3180,8001,8002],{"class":3178,"line":3797},[3180,8003,6103],{},[3180,8005,8006],{"class":3178,"line":3802},[3180,8007,3517],{"emptyLinePlaceholder":3516},[3180,8009,8010],{"class":3178,"line":3807},[3180,8011,8012],{},"== Scheduler призначає Pod вузлам ==\n",[3180,8014,8015],{"class":3178,"line":5443},[3180,8016,3517],{"emptyLinePlaceholder":3516},[3180,8018,8019],{"class":3178,"line":6772},[3180,8020,8021],{},"api -> sched: Подія: 2 нові Pod без вузла\n",[3180,8023,8024],{"class":3178,"line":6777},[3180,8025,6117],{},[3180,8027,8028],{"class":3178,"line":6782},[3180,8029,8030],{},"sched -> api: GET /nodes\n",[3180,8032,8033],{"class":3178,"line":6787},[3180,8034,8035],{},"api -> etcd: Читати вузли\n",[3180,8037,8038],{"class":3178,"line":6792},[3180,8039,8040],{},"etcd --> api: node-1 (CPU: 50%), node-2 (CPU: 30%)\n",[3180,8042,8043],{"class":3178,"line":6797},[3180,8044,8045],{},"api --> sched: 2 вузли доступні\n",[3180,8047,8048],{"class":3178,"line":6802},[3180,8049,3517],{"emptyLinePlaceholder":3516},[3180,8051,8052],{"class":3178,"line":6808},[3180,8053,8054],{},"sched -> sched: Вибрати вузли:\\nPod 4 → node-2 (менше навантаження)\\nPod 5 → node-1\n",[3180,8056,8057],{"class":3178,"line":6814},[3180,8058,3517],{"emptyLinePlaceholder":3516},[3180,8060,8061],{"class":3178,"line":6820},[3180,8062,8063],{},"sched -> api: PATCH /pods/nginx-xxx-4 (nodeName=node-2)\n",[3180,8065,8066],{"class":3178,"line":6825},[3180,8067,8068],{},"api -> etcd: Оновити Pod 4\n",[3180,8070,8071],{"class":3178,"line":6830},[3180,8072,6621],{},[3180,8074,8076],{"class":3178,"line":8075},69,[3180,8077,3517],{"emptyLinePlaceholder":3516},[3180,8079,8081],{"class":3178,"line":8080},70,[3180,8082,8083],{},"sched -> api: PATCH /pods/nginx-xxx-5 (nodeName=node-1)\n",[3180,8085,8087],{"class":3178,"line":8086},71,[3180,8088,8089],{},"api -> etcd: Оновити Pod 5\n",[3180,8091,8093],{"class":3178,"line":8092},72,[3180,8094,6621],{},[3180,8096,8098],{"class":3178,"line":8097},73,[3180,8099,6127],{},[3180,8101,8103],{"class":3178,"line":8102},74,[3180,8104,3517],{"emptyLinePlaceholder":3516},[3180,8106,8108],{"class":3178,"line":8107},75,[3180,8109,8110],{},"== Kubelet запускають Pod ==\n",[3180,8112,8114],{"class":3178,"line":8113},76,[3180,8115,3517],{"emptyLinePlaceholder":3516},[3180,8117,8119],{"class":3178,"line":8118},77,[3180,8120,8121],{},"api -> kubelet2: Подія: Pod 4 призначено node-2\n",[3180,8123,8125],{"class":3178,"line":8124},78,[3180,8126,8127],{},"activate kubelet2\n",[3180,8129,8131],{"class":3178,"line":8130},79,[3180,8132,8133],{},"kubelet2 -> kubelet2: Завантажити образ nginx:1.27\n",[3180,8135,8137],{"class":3178,"line":8136},80,[3180,8138,8139],{},"kubelet2 -> kubelet2: Запустити контейнер\n",[3180,8141,8143],{"class":3178,"line":8142},81,[3180,8144,8145],{},"kubelet2 -> api: PATCH /pods/nginx-xxx-4\\n(status=Running)\n",[3180,8147,8149],{"class":3178,"line":8148},82,[3180,8150,8151],{},"api -> etcd: Оновити статус Pod 4\n",[3180,8153,8155],{"class":3178,"line":8154},83,[3180,8156,6621],{},[3180,8158,8160],{"class":3178,"line":8159},84,[3180,8161,8162],{},"deactivate kubelet2\n",[3180,8164,8166],{"class":3178,"line":8165},85,[3180,8167,3517],{"emptyLinePlaceholder":3516},[3180,8169,8171],{"class":3178,"line":8170},86,[3180,8172,8173],{},"api -> kubelet1: Подія: Pod 5 призначено node-1\n",[3180,8175,8177],{"class":3178,"line":8176},87,[3180,8178,8179],{},"activate kubelet1\n",[3180,8181,8183],{"class":3178,"line":8182},88,[3180,8184,8185],{},"kubelet1 -> kubelet1: Образ вже є (кешовано)\n",[3180,8187,8189],{"class":3178,"line":8188},89,[3180,8190,8191],{},"kubelet1 -> kubelet1: Запустити контейнер\n",[3180,8193,8195],{"class":3178,"line":8194},90,[3180,8196,8197],{},"kubelet1 -> api: PATCH /pods/nginx-xxx-5\\n(status=Running)\n",[3180,8199,8201],{"class":3178,"line":8200},91,[3180,8202,8203],{},"api -> etcd: Оновити статус Pod 5\n",[3180,8205,8207],{"class":3178,"line":8206},92,[3180,8208,6621],{},[3180,8210,8212],{"class":3178,"line":8211},93,[3180,8213,8214],{},"deactivate kubelet1\n",[3180,8216,8218],{"class":3178,"line":8217},94,[3180,8219,3517],{"emptyLinePlaceholder":3516},[3180,8221,8223],{"class":3178,"line":8222},95,[3180,8224,6170],{},[3180,8226,8228],{"class":3178,"line":8227},96,[3180,8229,8230],{},"  Весь процес займає\n",[3180,8232,8234],{"class":3178,"line":8233},97,[3180,8235,8236],{},"  5-15 секунд залежно від:\n",[3180,8238,8240],{"class":3178,"line":8239},98,[3180,8241,8242],{},"  - Швидкості завантаження образу\n",[3180,8244,8246],{"class":3178,"line":8245},99,[3180,8247,8248],{},"  - Доступності ресурсів\n",[3180,8250,8252],{"class":3178,"line":8251},100,[3180,8253,8254],{},"  - Швидкості старту застосунку\n",[3180,8256,8258],{"class":3178,"line":8257},101,[3180,8259,3731],{},[3180,8261,8263],{"class":3178,"line":8262},102,[3180,8264,3517],{"emptyLinePlaceholder":3516},[3180,8266,8268],{"class":3178,"line":8267},103,[3180,8269,3810],{},[3114,8271,8272],{},[3118,8273,8274],{},"Ключові етапи масштабування:",[3134,8276,8277,8286,8292,8298,8304,8310],{},[3137,8278,8279,8282,8283,8285],{},[3118,8280,8281],{},"Оновлення Deployment"," — користувач змінює ",[3429,8284,4243],{}," через kubectl",[3137,8287,8288,8291],{},[3118,8289,8290],{},"Deployment Controller"," — виявляє зміну та оновлює ReplicaSet",[3137,8293,8294,8297],{},[3118,8295,8296],{},"ReplicaSet Controller"," — виявляє розбіжність (3 \u003C 5) та створює 2 нові Pod",[3137,8299,8300,8303],{},[3118,8301,8302],{},"Scheduler"," — призначає нові Pod вузлам з найменшим навантаженням",[3137,8305,8306,8309],{},[3118,8307,8308],{},"Kubelet"," — завантажує образи та запускає контейнери",[3137,8311,8312,8315,8316,8319],{},[3118,8313,8314],{},"Готовність"," — Pod переходять у стан ",[3429,8317,8318],{},"Running"," та стають доступними",[3114,8321,8322,8323,8325],{},"Весь процес повністю автоматичний — ви лише змінюєте одне число (",[3429,8324,4243],{},"), а Kubernetes виконує всю роботу.",[3382,8327],{},[3109,8329,8331],{"id":8330},"практичний-приклад-aspnet-core-todoapi-з-deployment","Практичний приклад: ASP.NET Core TodoApi з Deployment",[3114,8333,8334],{},"Тепер створимо реальний застосунок ASP.NET Core та розгорнемо його у Kubernetes через Deployment. Це буде простий TodoApi з трьома репліками.",[3126,8336,8338],{"id":8337},"архітектура-застосунку","Архітектура застосунку",[3114,8340,8341],{},"Наш TodoApi матиме наступну структуру:",[3482,8343,8344],{},[3485,8345,8347],{"className":3487,"code":8346,"language":3489,"meta":3490,"style":3490},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Kubernetes Cluster\" #f8f9fa {\n    component \"Deployment\\n(todoapi)\" as dep #e3f2fd {\n        [replicas: 3]\n        [image: todoapi:1.0.0]\n    }\n    \n    component \"ReplicaSet\\n(todoapi-abc123)\" as rs #fff3e0 {\n        [replicas: 3]\n    }\n    \n    package \"Pods\" #e8f5e9 {\n        component \"Pod 1\\n10.244.1.10\" as p1 {\n            [todoapi:1.0.0]\n            [Port: 8080]\n        }\n        component \"Pod 2\\n10.244.1.11\" as p2 {\n            [todoapi:1.0.0]\n            [Port: 8080]\n        }\n        component \"Pod 3\\n10.244.1.12\" as p3 {\n            [todoapi:1.0.0]\n            [Port: 8080]\n        }\n    }\n}\n\nactor \"Користувач\" as user\n\ndep --> rs : manages\nrs --> p1 : creates\nrs --> p2 : creates\nrs --> p3 : creates\n\nuser --> p1 : HTTP GET /todos\nuser --> p2 : HTTP POST /todos\nuser --> p3 : HTTP GET /todos/1\n\nnote right of dep\n    Deployment гарантує:\n    - 3 репліки завжди працюють\n    - Автоматичне відновлення\n    - Можливість масштабування\nend note\n\nnote bottom of p1\n    Кожен Pod — незалежна\n    копія застосунку з власною\n    пам'яттю та IP-адресою\nend note\n\n@enduml\n",[3429,8348,8349,8353,8357,8361,8365,8370,8375,8379,8384,8388,8392,8397,8401,8405,8409,8413,8418,8423,8428,8433,8438,8442,8446,8450,8455,8459,8463,8467,8471,8475,8479,8483,8487,8492,8496,8500,8504,8508,8513,8518,8523,8527,8531,8536,8541,8546,8551,8555,8559,8564,8569,8574,8579,8583,8587],{"__ignoreMap":3490},[3180,8350,8351],{"class":3178,"line":3495},[3180,8352,3498],{},[3180,8354,8355],{"class":3178,"line":3501},[3180,8356,3504],{},[3180,8358,8359],{"class":3178,"line":3507},[3180,8360,3510],{},[3180,8362,8363],{"class":3178,"line":3513},[3180,8364,3517],{"emptyLinePlaceholder":3516},[3180,8366,8367],{"class":3178,"line":3520},[3180,8368,8369],{},"package \"Kubernetes Cluster\" #f8f9fa {\n",[3180,8371,8372],{"class":3178,"line":3526},[3180,8373,8374],{},"    component \"Deployment\\n(todoapi)\" as dep #e3f2fd {\n",[3180,8376,8377],{"class":3178,"line":3532},[3180,8378,3564],{},[3180,8380,8381],{"class":3178,"line":3538},[3180,8382,8383],{},"        [image: todoapi:1.0.0]\n",[3180,8385,8386],{"class":3178,"line":3544},[3180,8387,3576],{},[3180,8389,8390],{"class":3178,"line":3549},[3180,8391,3582],{},[3180,8393,8394],{"class":3178,"line":3555},[3180,8395,8396],{},"    component \"ReplicaSet\\n(todoapi-abc123)\" as rs #fff3e0 {\n",[3180,8398,8399],{"class":3178,"line":3561},[3180,8400,3564],{},[3180,8402,8403],{"class":3178,"line":3567},[3180,8404,3576],{},[3180,8406,8407],{"class":3178,"line":3573},[3180,8408,3582],{},[3180,8410,8411],{"class":3178,"line":3579},[3180,8412,3615],{},[3180,8414,8415],{"class":3178,"line":3585},[3180,8416,8417],{},"        component \"Pod 1\\n10.244.1.10\" as p1 {\n",[3180,8419,8420],{"class":3178,"line":3591},[3180,8421,8422],{},"            [todoapi:1.0.0]\n",[3180,8424,8425],{"class":3178,"line":3596},[3180,8426,8427],{},"            [Port: 8080]\n",[3180,8429,8430],{"class":3178,"line":3602},[3180,8431,8432],{},"        }\n",[3180,8434,8435],{"class":3178,"line":3607},[3180,8436,8437],{},"        component \"Pod 2\\n10.244.1.11\" as p2 {\n",[3180,8439,8440],{"class":3178,"line":3612},[3180,8441,8422],{},[3180,8443,8444],{"class":3178,"line":3618},[3180,8445,8427],{},[3180,8447,8448],{"class":3178,"line":3624},[3180,8449,8432],{},[3180,8451,8452],{"class":3178,"line":3630},[3180,8453,8454],{},"        component \"Pod 3\\n10.244.1.12\" as p3 {\n",[3180,8456,8457],{"class":3178,"line":3636},[3180,8458,8422],{},[3180,8460,8461],{"class":3178,"line":3641},[3180,8462,8427],{},[3180,8464,8465],{"class":3178,"line":3646},[3180,8466,8432],{},[3180,8468,8469],{"class":3178,"line":3651},[3180,8470,3576],{},[3180,8472,8473],{"class":3178,"line":3657},[3180,8474,3541],{},[3180,8476,8477],{"class":3178,"line":3663},[3180,8478,3517],{"emptyLinePlaceholder":3516},[3180,8480,8481],{"class":3178,"line":3669},[3180,8482,6005],{},[3180,8484,8485],{"class":3178,"line":3675},[3180,8486,3517],{"emptyLinePlaceholder":3516},[3180,8488,8489],{"class":3178,"line":3681},[3180,8490,8491],{},"dep --> rs : manages\n",[3180,8493,8494],{"class":3178,"line":3687},[3180,8495,3672],{},[3180,8497,8498],{"class":3178,"line":3692},[3180,8499,3678],{},[3180,8501,8502],{"class":3178,"line":3698},[3180,8503,3684],{},[3180,8505,8506],{"class":3178,"line":3704},[3180,8507,3517],{"emptyLinePlaceholder":3516},[3180,8509,8510],{"class":3178,"line":3710},[3180,8511,8512],{},"user --> p1 : HTTP GET /todos\n",[3180,8514,8515],{"class":3178,"line":3716},[3180,8516,8517],{},"user --> p2 : HTTP POST /todos\n",[3180,8519,8520],{"class":3178,"line":3722},[3180,8521,8522],{},"user --> p3 : HTTP GET /todos/1\n",[3180,8524,8525],{"class":3178,"line":3728},[3180,8526,3517],{"emptyLinePlaceholder":3516},[3180,8528,8529],{"class":3178,"line":3734},[3180,8530,3695],{},[3180,8532,8533],{"class":3178,"line":3739},[3180,8534,8535],{},"    Deployment гарантує:\n",[3180,8537,8538],{"class":3178,"line":3745},[3180,8539,8540],{},"    - 3 репліки завжди працюють\n",[3180,8542,8543],{"class":3178,"line":3751},[3180,8544,8545],{},"    - Автоматичне відновлення\n",[3180,8547,8548],{"class":3178,"line":3757},[3180,8549,8550],{},"    - Можливість масштабування\n",[3180,8552,8553],{"class":3178,"line":3763},[3180,8554,3731],{},[3180,8556,8557],{"class":3178,"line":3768},[3180,8558,3517],{"emptyLinePlaceholder":3516},[3180,8560,8561],{"class":3178,"line":3773},[3180,8562,8563],{},"note bottom of p1\n",[3180,8565,8566],{"class":3178,"line":3779},[3180,8567,8568],{},"    Кожен Pod — незалежна\n",[3180,8570,8571],{"class":3178,"line":3785},[3180,8572,8573],{},"    копія застосунку з власною\n",[3180,8575,8576],{"class":3178,"line":3791},[3180,8577,8578],{},"    пам'яттю та IP-адресою\n",[3180,8580,8581],{"class":3178,"line":3797},[3180,8582,3731],{},[3180,8584,8585],{"class":3178,"line":3802},[3180,8586,3517],{"emptyLinePlaceholder":3516},[3180,8588,8589],{"class":3178,"line":3807},[3180,8590,3810],{},[3126,8592,8594],{"id":8593},"крок-1-створення-aspnet-core-minimal-api","Крок 1: Створення ASP.NET Core Minimal API",[3114,8596,8597],{},"Створимо простий TodoApi з використанням ASP.NET Core Minimal API (без Entity Framework для простоти):",[3114,8599,8600],{},[3118,8601,8602],{},"Структура проєкту:",[3485,8604,8609],{"className":8605,"code":8607,"language":8608},[8606],"language-text","TodoApi/\n├── Program.cs\n├── TodoApi.csproj\n├── Dockerfile\n└── k8s/\n    └── deployment.yaml\n","text",[3429,8610,8607],{"__ignoreMap":3490},[3114,8612,8613],{},[3118,8614,8615],{},"Program.cs:",[3485,8617,8621],{"className":8618,"code":8619,"language":8620,"meta":3490,"style":3490},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","using System.Collections.Concurrent;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// Додаємо Swagger для документації API\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\n// In-memory сховище (для демонстрації)\nvar todos = new ConcurrentDictionary\u003Cint, Todo>();\nvar nextId = 1;\n\nvar app = builder.Build();\n\n// Swagger UI (доступний лише у Development)\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\n// Health check endpoint (для Kubernetes)\napp.MapGet(\"/health\", () => Results.Ok(new { status = \"healthy\", timestamp = DateTime.UtcNow }))\n   .WithName(\"HealthCheck\")\n   .WithOpenApi();\n\n// GET /todos - отримати всі завдання\napp.MapGet(\"/todos\", () => \n{\n    var hostname = Environment.GetEnvironmentVariable(\"HOSTNAME\") ?? \"unknown\";\n    return Results.Ok(new \n    { \n        todos = todos.Values.ToList(),\n        servedBy = hostname,\n        count = todos.Count\n    });\n})\n.WithName(\"GetAllTodos\")\n.WithOpenApi();\n\n// GET /todos/{id} - отримати завдання за ID\napp.MapGet(\"/todos/{id:int}\", (int id) =>\n{\n    if (todos.TryGetValue(id, out var todo))\n    {\n        var hostname = Environment.GetEnvironmentVariable(\"HOSTNAME\") ?? \"unknown\";\n        return Results.Ok(new { todo, servedBy = hostname });\n    }\n    return Results.NotFound(new { error = \"Todo not found\", id });\n})\n.WithName(\"GetTodoById\")\n.WithOpenApi();\n\n// POST /todos - створити нове завдання\napp.MapPost(\"/todos\", (CreateTodoRequest request) =>\n{\n    var id = Interlocked.Increment(ref nextId);\n    var todo = new Todo\n    {\n        Id = id,\n        Title = request.Title,\n        IsCompleted = false,\n        CreatedAt = DateTime.UtcNow\n    };\n    \n    todos[id] = todo;\n    \n    var hostname = Environment.GetEnvironmentVariable(\"HOSTNAME\") ?? \"unknown\";\n    return Results.Created($\"/todos/{id}\", new { todo, servedBy = hostname });\n})\n.WithName(\"CreateTodo\")\n.WithOpenApi();\n\n// PUT /todos/{id} - оновити завдання\napp.MapPut(\"/todos/{id:int}\", (int id, UpdateTodoRequest request) =>\n{\n    if (!todos.TryGetValue(id, out var todo))\n    {\n        return Results.NotFound(new { error = \"Todo not found\", id });\n    }\n    \n    todo.Title = request.Title ?? todo.Title;\n    todo.IsCompleted = request.IsCompleted ?? todo.IsCompleted;\n    \n    var hostname = Environment.GetEnvironmentVariable(\"HOSTNAME\") ?? \"unknown\";\n    return Results.Ok(new { todo, servedBy = hostname });\n})\n.WithName(\"UpdateTodo\")\n.WithOpenApi();\n\n// DELETE /todos/{id} - видалити завдання\napp.MapDelete(\"/todos/{id:int}\", (int id) =>\n{\n    if (todos.TryRemove(id, out var todo))\n    {\n        var hostname = Environment.GetEnvironmentVariable(\"HOSTNAME\") ?? \"unknown\";\n        return Results.Ok(new { message = \"Todo deleted\", todo, servedBy = hostname });\n    }\n    return Results.NotFound(new { error = \"Todo not found\", id });\n})\n.WithName(\"DeleteTodo\")\n.WithOpenApi();\n\napp.Run();\n\n// Моделі\nrecord Todo\n{\n    public int Id { get; set; }\n    public required string Title { get; set; }\n    public bool IsCompleted { get; set; }\n    public DateTime CreatedAt { get; set; }\n}\n\nrecord CreateTodoRequest(string Title);\nrecord UpdateTodoRequest(string? Title, bool? IsCompleted);\n","csharp",[3429,8622,8623,8646,8650,8680,8684,8689,8707,8722,8726,8731,8760,8773,8777,8795,8799,8804,8827,8832,8843,8854,8858,8862,8867,8925,8941,8950,8954,8959,8975,8979,9009,9028,9033,9056,9069,9083,9088,9093,9106,9114,9118,9123,9147,9151,9184,9189,9214,9246,9250,9281,9285,9298,9306,9310,9315,9338,9342,9367,9380,9384,9395,9412,9423,9437,9442,9446,9463,9467,9491,9537,9541,9554,9562,9566,9571,9599,9603,9630,9634,9662,9666,9670,9698,9725,9729,9753,9781,9785,9798,9806,9810,9815,9836,9840,9867,9871,9895,9933,9937,9965,9969,9982,9990,9994,10006,10011,10017,10025,10030,10056,10080,10101,10122,10127,10132,10148],{"__ignoreMap":3490},[3180,8624,8625,8629,8633,8635,8638,8640,8643],{"class":3178,"line":3495},[3180,8626,8628],{"class":8627},"sCDza","using",[3180,8630,8632],{"class":8631},"sN1BT"," System",[3180,8634,3121],{"class":3876},[3180,8636,8637],{"class":8631},"Collections",[3180,8639,3121],{"class":3876},[3180,8641,8642],{"class":8631},"Concurrent",[3180,8644,8645],{"class":3876},";\n",[3180,8647,8648],{"class":3178,"line":3501},[3180,8649,3517],{"emptyLinePlaceholder":3516},[3180,8651,8652,8655,8659,8662,8665,8667,8671,8674,8677],{"class":3178,"line":3507},[3180,8653,8654],{"class":4962},"var",[3180,8656,8658],{"class":8657},"siwwj"," builder",[3180,8660,8661],{"class":3876}," = ",[3180,8663,8664],{"class":8657},"WebApplication",[3180,8666,3121],{"class":3876},[3180,8668,8670],{"class":8669},"s8Opu","CreateBuilder",[3180,8672,8673],{"class":3876},"(",[3180,8675,8676],{"class":8657},"args",[3180,8678,8679],{"class":3876},");\n",[3180,8681,8682],{"class":3178,"line":3513},[3180,8683,3517],{"emptyLinePlaceholder":3516},[3180,8685,8686],{"class":3178,"line":3520},[3180,8687,8688],{"class":4183},"// Додаємо Swagger для документації API\n",[3180,8690,8691,8694,8696,8699,8701,8704],{"class":3178,"line":3526},[3180,8692,8693],{"class":8657},"builder",[3180,8695,3121],{"class":3876},[3180,8697,8698],{"class":8657},"Services",[3180,8700,3121],{"class":3876},[3180,8702,8703],{"class":8669},"AddEndpointsApiExplorer",[3180,8705,8706],{"class":3876},"();\n",[3180,8708,8709,8711,8713,8715,8717,8720],{"class":3178,"line":3532},[3180,8710,8693],{"class":8657},[3180,8712,3121],{"class":3876},[3180,8714,8698],{"class":8657},[3180,8716,3121],{"class":3876},[3180,8718,8719],{"class":8669},"AddSwaggerGen",[3180,8721,8706],{"class":3876},[3180,8723,8724],{"class":3178,"line":3538},[3180,8725,3517],{"emptyLinePlaceholder":3516},[3180,8727,8728],{"class":3178,"line":3544},[3180,8729,8730],{"class":4183},"// In-memory сховище (для демонстрації)\n",[3180,8732,8733,8735,8738,8740,8743,8746,8749,8752,8754,8757],{"class":3178,"line":3549},[3180,8734,8654],{"class":4962},[3180,8736,8737],{"class":8657}," todos",[3180,8739,8661],{"class":3876},[3180,8741,8742],{"class":4962},"new",[3180,8744,8745],{"class":8631}," ConcurrentDictionary",[3180,8747,8748],{"class":3876},"\u003C",[3180,8750,8751],{"class":4962},"int",[3180,8753,4789],{"class":3876},[3180,8755,8756],{"class":8631},"Todo",[3180,8758,8759],{"class":3876},">();\n",[3180,8761,8762,8764,8767,8769,8771],{"class":3178,"line":3555},[3180,8763,8654],{"class":4962},[3180,8765,8766],{"class":8657}," nextId",[3180,8768,8661],{"class":3876},[3180,8770,4060],{"class":3926},[3180,8772,8645],{"class":3876},[3180,8774,8775],{"class":3178,"line":3561},[3180,8776,3517],{"emptyLinePlaceholder":3516},[3180,8778,8779,8781,8784,8786,8788,8790,8793],{"class":3178,"line":3567},[3180,8780,8654],{"class":4962},[3180,8782,8783],{"class":8657}," app",[3180,8785,8661],{"class":3876},[3180,8787,8693],{"class":8657},[3180,8789,3121],{"class":3876},[3180,8791,8792],{"class":8669},"Build",[3180,8794,8706],{"class":3876},[3180,8796,8797],{"class":3178,"line":3573},[3180,8798,3517],{"emptyLinePlaceholder":3516},[3180,8800,8801],{"class":3178,"line":3579},[3180,8802,8803],{"class":4183},"// Swagger UI (доступний лише у Development)\n",[3180,8805,8806,8809,8811,8814,8816,8819,8821,8824],{"class":3178,"line":3585},[3180,8807,8808],{"class":8627},"if",[3180,8810,4796],{"class":3876},[3180,8812,8813],{"class":8657},"app",[3180,8815,3121],{"class":3876},[3180,8817,8818],{"class":8657},"Environment",[3180,8820,3121],{"class":3876},[3180,8822,8823],{"class":8669},"IsDevelopment",[3180,8825,8826],{"class":3876},"())\n",[3180,8828,8829],{"class":3178,"line":3591},[3180,8830,8831],{"class":3876},"{\n",[3180,8833,8834,8836,8838,8841],{"class":3178,"line":3596},[3180,8835,4415],{"class":8657},[3180,8837,3121],{"class":3876},[3180,8839,8840],{"class":8669},"UseSwagger",[3180,8842,8706],{"class":3876},[3180,8844,8845,8847,8849,8852],{"class":3178,"line":3602},[3180,8846,4415],{"class":8657},[3180,8848,3121],{"class":3876},[3180,8850,8851],{"class":8669},"UseSwaggerUI",[3180,8853,8706],{"class":3876},[3180,8855,8856],{"class":3178,"line":3607},[3180,8857,3541],{"class":3876},[3180,8859,8860],{"class":3178,"line":3612},[3180,8861,3517],{"emptyLinePlaceholder":3516},[3180,8863,8864],{"class":3178,"line":3618},[3180,8865,8866],{"class":4183},"// Health check endpoint (для Kubernetes)\n",[3180,8868,8869,8871,8873,8876,8878,8881,8884,8887,8889,8892,8894,8896,8899,8902,8904,8907,8909,8912,8914,8917,8919,8922],{"class":3178,"line":3624},[3180,8870,8813],{"class":8657},[3180,8872,3121],{"class":3876},[3180,8874,8875],{"class":8669},"MapGet",[3180,8877,8673],{"class":3876},[3180,8879,8880],{"class":4430},"\"/health\"",[3180,8882,8883],{"class":3876},", () => ",[3180,8885,8886],{"class":8657},"Results",[3180,8888,3121],{"class":3876},[3180,8890,8891],{"class":8669},"Ok",[3180,8893,8673],{"class":3876},[3180,8895,8742],{"class":4962},[3180,8897,8898],{"class":3876}," { ",[3180,8900,8901],{"class":8657},"status",[3180,8903,8661],{"class":3876},[3180,8905,8906],{"class":4430},"\"healthy\"",[3180,8908,4789],{"class":3876},[3180,8910,8911],{"class":8657},"timestamp",[3180,8913,8661],{"class":3876},[3180,8915,8916],{"class":8657},"DateTime",[3180,8918,3121],{"class":3876},[3180,8920,8921],{"class":8657},"UtcNow",[3180,8923,8924],{"class":3876}," }))\n",[3180,8926,8927,8930,8933,8935,8938],{"class":3178,"line":3630},[3180,8928,8929],{"class":3876},"   .",[3180,8931,8932],{"class":8669},"WithName",[3180,8934,8673],{"class":3876},[3180,8936,8937],{"class":4430},"\"HealthCheck\"",[3180,8939,8940],{"class":3876},")\n",[3180,8942,8943,8945,8948],{"class":3178,"line":3636},[3180,8944,8929],{"class":3876},[3180,8946,8947],{"class":8669},"WithOpenApi",[3180,8949,8706],{"class":3876},[3180,8951,8952],{"class":3178,"line":3641},[3180,8953,3517],{"emptyLinePlaceholder":3516},[3180,8955,8956],{"class":3178,"line":3646},[3180,8957,8958],{"class":4183},"// GET /todos - отримати всі завдання\n",[3180,8960,8961,8963,8965,8967,8969,8972],{"class":3178,"line":3651},[3180,8962,8813],{"class":8657},[3180,8964,3121],{"class":3876},[3180,8966,8875],{"class":8669},[3180,8968,8673],{"class":3876},[3180,8970,8971],{"class":4430},"\"/todos\"",[3180,8973,8974],{"class":3876},", () => \n",[3180,8976,8977],{"class":3178,"line":3657},[3180,8978,8831],{"class":3876},[3180,8980,8981,8984,8987,8989,8991,8993,8996,8998,9001,9004,9007],{"class":3178,"line":3663},[3180,8982,8983],{"class":4962},"    var",[3180,8985,8986],{"class":8657}," hostname",[3180,8988,8661],{"class":3876},[3180,8990,8818],{"class":8657},[3180,8992,3121],{"class":3876},[3180,8994,8995],{"class":8669},"GetEnvironmentVariable",[3180,8997,8673],{"class":3876},[3180,8999,9000],{"class":4430},"\"HOSTNAME\"",[3180,9002,9003],{"class":3876},") ?? ",[3180,9005,9006],{"class":4430},"\"unknown\"",[3180,9008,8645],{"class":3876},[3180,9010,9011,9014,9017,9019,9021,9023,9025],{"class":3178,"line":3669},[3180,9012,9013],{"class":8627},"    return",[3180,9015,9016],{"class":8657}," Results",[3180,9018,3121],{"class":3876},[3180,9020,8891],{"class":8669},[3180,9022,8673],{"class":3876},[3180,9024,8742],{"class":4962},[3180,9026,9027],{"class":3876}," \n",[3180,9029,9030],{"class":3178,"line":3675},[3180,9031,9032],{"class":3876},"    { \n",[3180,9034,9035,9038,9040,9043,9045,9048,9050,9053],{"class":3178,"line":3681},[3180,9036,9037],{"class":8657},"        todos",[3180,9039,8661],{"class":3876},[3180,9041,9042],{"class":8657},"todos",[3180,9044,3121],{"class":3876},[3180,9046,9047],{"class":8657},"Values",[3180,9049,3121],{"class":3876},[3180,9051,9052],{"class":8669},"ToList",[3180,9054,9055],{"class":3876},"(),\n",[3180,9057,9058,9061,9063,9066],{"class":3178,"line":3687},[3180,9059,9060],{"class":8657},"        servedBy",[3180,9062,8661],{"class":3876},[3180,9064,9065],{"class":8657},"hostname",[3180,9067,9068],{"class":3876},",\n",[3180,9070,9071,9074,9076,9078,9080],{"class":3178,"line":3692},[3180,9072,9073],{"class":8657},"        count",[3180,9075,8661],{"class":3876},[3180,9077,9042],{"class":8657},[3180,9079,3121],{"class":3876},[3180,9081,9082],{"class":8657},"Count\n",[3180,9084,9085],{"class":3178,"line":3698},[3180,9086,9087],{"class":3876},"    });\n",[3180,9089,9090],{"class":3178,"line":3704},[3180,9091,9092],{"class":3876},"})\n",[3180,9094,9095,9097,9099,9101,9104],{"class":3178,"line":3710},[3180,9096,3121],{"class":3876},[3180,9098,8932],{"class":8669},[3180,9100,8673],{"class":3876},[3180,9102,9103],{"class":4430},"\"GetAllTodos\"",[3180,9105,8940],{"class":3876},[3180,9107,9108,9110,9112],{"class":3178,"line":3716},[3180,9109,3121],{"class":3876},[3180,9111,8947],{"class":8669},[3180,9113,8706],{"class":3876},[3180,9115,9116],{"class":3178,"line":3722},[3180,9117,3517],{"emptyLinePlaceholder":3516},[3180,9119,9120],{"class":3178,"line":3728},[3180,9121,9122],{"class":4183},"// GET /todos/{id} - отримати завдання за ID\n",[3180,9124,9125,9127,9129,9131,9133,9136,9139,9141,9144],{"class":3178,"line":3734},[3180,9126,8813],{"class":8657},[3180,9128,3121],{"class":3876},[3180,9130,8875],{"class":8669},[3180,9132,8673],{"class":3876},[3180,9134,9135],{"class":4430},"\"/todos/{id:int}\"",[3180,9137,9138],{"class":3876},", (",[3180,9140,8751],{"class":4962},[3180,9142,9143],{"class":8657}," id",[3180,9145,9146],{"class":3876},") =>\n",[3180,9148,9149],{"class":3178,"line":3739},[3180,9150,8831],{"class":3876},[3180,9152,9153,9156,9158,9160,9162,9165,9167,9170,9172,9175,9178,9181],{"class":3178,"line":3745},[3180,9154,9155],{"class":8627},"    if",[3180,9157,4796],{"class":3876},[3180,9159,9042],{"class":8657},[3180,9161,3121],{"class":3876},[3180,9163,9164],{"class":8669},"TryGetValue",[3180,9166,8673],{"class":3876},[3180,9168,9169],{"class":8657},"id",[3180,9171,4789],{"class":3876},[3180,9173,9174],{"class":4962},"out",[3180,9176,9177],{"class":4962}," var",[3180,9179,9180],{"class":8657}," todo",[3180,9182,9183],{"class":3876},"))\n",[3180,9185,9186],{"class":3178,"line":3751},[3180,9187,9188],{"class":3876},"    {\n",[3180,9190,9191,9194,9196,9198,9200,9202,9204,9206,9208,9210,9212],{"class":3178,"line":3757},[3180,9192,9193],{"class":4962},"        var",[3180,9195,8986],{"class":8657},[3180,9197,8661],{"class":3876},[3180,9199,8818],{"class":8657},[3180,9201,3121],{"class":3876},[3180,9203,8995],{"class":8669},[3180,9205,8673],{"class":3876},[3180,9207,9000],{"class":4430},[3180,9209,9003],{"class":3876},[3180,9211,9006],{"class":4430},[3180,9213,8645],{"class":3876},[3180,9215,9216,9219,9221,9223,9225,9227,9229,9231,9234,9236,9239,9241,9243],{"class":3178,"line":3763},[3180,9217,9218],{"class":8627},"        return",[3180,9220,9016],{"class":8657},[3180,9222,3121],{"class":3876},[3180,9224,8891],{"class":8669},[3180,9226,8673],{"class":3876},[3180,9228,8742],{"class":4962},[3180,9230,8898],{"class":3876},[3180,9232,9233],{"class":8657},"todo",[3180,9235,4789],{"class":3876},[3180,9237,9238],{"class":8657},"servedBy",[3180,9240,8661],{"class":3876},[3180,9242,9065],{"class":8657},[3180,9244,9245],{"class":3876}," });\n",[3180,9247,9248],{"class":3178,"line":3768},[3180,9249,3576],{"class":3876},[3180,9251,9252,9254,9256,9258,9261,9263,9265,9267,9270,9272,9275,9277,9279],{"class":3178,"line":3773},[3180,9253,9013],{"class":8627},[3180,9255,9016],{"class":8657},[3180,9257,3121],{"class":3876},[3180,9259,9260],{"class":8669},"NotFound",[3180,9262,8673],{"class":3876},[3180,9264,8742],{"class":4962},[3180,9266,8898],{"class":3876},[3180,9268,9269],{"class":8657},"error",[3180,9271,8661],{"class":3876},[3180,9273,9274],{"class":4430},"\"Todo not found\"",[3180,9276,4789],{"class":3876},[3180,9278,9169],{"class":8657},[3180,9280,9245],{"class":3876},[3180,9282,9283],{"class":3178,"line":3779},[3180,9284,9092],{"class":3876},[3180,9286,9287,9289,9291,9293,9296],{"class":3178,"line":3785},[3180,9288,3121],{"class":3876},[3180,9290,8932],{"class":8669},[3180,9292,8673],{"class":3876},[3180,9294,9295],{"class":4430},"\"GetTodoById\"",[3180,9297,8940],{"class":3876},[3180,9299,9300,9302,9304],{"class":3178,"line":3791},[3180,9301,3121],{"class":3876},[3180,9303,8947],{"class":8669},[3180,9305,8706],{"class":3876},[3180,9307,9308],{"class":3178,"line":3797},[3180,9309,3517],{"emptyLinePlaceholder":3516},[3180,9311,9312],{"class":3178,"line":3802},[3180,9313,9314],{"class":4183},"// POST /todos - створити нове завдання\n",[3180,9316,9317,9319,9321,9324,9326,9328,9330,9333,9336],{"class":3178,"line":3807},[3180,9318,8813],{"class":8657},[3180,9320,3121],{"class":3876},[3180,9322,9323],{"class":8669},"MapPost",[3180,9325,8673],{"class":3876},[3180,9327,8971],{"class":4430},[3180,9329,9138],{"class":3876},[3180,9331,9332],{"class":8631},"CreateTodoRequest",[3180,9334,9335],{"class":8657}," request",[3180,9337,9146],{"class":3876},[3180,9339,9340],{"class":3178,"line":5443},[3180,9341,8831],{"class":3876},[3180,9343,9344,9346,9348,9350,9353,9355,9358,9360,9363,9365],{"class":3178,"line":6772},[3180,9345,8983],{"class":4962},[3180,9347,9143],{"class":8657},[3180,9349,8661],{"class":3876},[3180,9351,9352],{"class":8657},"Interlocked",[3180,9354,3121],{"class":3876},[3180,9356,9357],{"class":8669},"Increment",[3180,9359,8673],{"class":3876},[3180,9361,9362],{"class":4962},"ref",[3180,9364,8766],{"class":8657},[3180,9366,8679],{"class":3876},[3180,9368,9369,9371,9373,9375,9377],{"class":3178,"line":6777},[3180,9370,8983],{"class":4962},[3180,9372,9180],{"class":8657},[3180,9374,8661],{"class":3876},[3180,9376,8742],{"class":4962},[3180,9378,9379],{"class":8631}," Todo\n",[3180,9381,9382],{"class":3178,"line":6782},[3180,9383,9188],{"class":3876},[3180,9385,9386,9389,9391,9393],{"class":3178,"line":6787},[3180,9387,9388],{"class":8657},"        Id",[3180,9390,8661],{"class":3876},[3180,9392,9169],{"class":8657},[3180,9394,9068],{"class":3876},[3180,9396,9397,9400,9402,9405,9407,9410],{"class":3178,"line":6792},[3180,9398,9399],{"class":8657},"        Title",[3180,9401,8661],{"class":3876},[3180,9403,9404],{"class":8657},"request",[3180,9406,3121],{"class":3876},[3180,9408,9409],{"class":8657},"Title",[3180,9411,9068],{"class":3876},[3180,9413,9414,9417,9419,9421],{"class":3178,"line":6797},[3180,9415,9416],{"class":8657},"        IsCompleted",[3180,9418,8661],{"class":3876},[3180,9420,5028],{"class":4962},[3180,9422,9068],{"class":3876},[3180,9424,9425,9428,9430,9432,9434],{"class":3178,"line":6802},[3180,9426,9427],{"class":8657},"        CreatedAt",[3180,9429,8661],{"class":3876},[3180,9431,8916],{"class":8657},[3180,9433,3121],{"class":3876},[3180,9435,9436],{"class":8657},"UtcNow\n",[3180,9438,9439],{"class":3178,"line":6808},[3180,9440,9441],{"class":3876},"    };\n",[3180,9443,9444],{"class":3178,"line":6814},[3180,9445,3582],{"class":3876},[3180,9447,9448,9451,9454,9456,9459,9461],{"class":3178,"line":6820},[3180,9449,9450],{"class":8657},"    todos",[3180,9452,9453],{"class":3876},"[",[3180,9455,9169],{"class":8657},[3180,9457,9458],{"class":3876},"] = ",[3180,9460,9233],{"class":8657},[3180,9462,8645],{"class":3876},[3180,9464,9465],{"class":3178,"line":6825},[3180,9466,3582],{"class":3876},[3180,9468,9469,9471,9473,9475,9477,9479,9481,9483,9485,9487,9489],{"class":3178,"line":6830},[3180,9470,8983],{"class":4962},[3180,9472,8986],{"class":8657},[3180,9474,8661],{"class":3876},[3180,9476,8818],{"class":8657},[3180,9478,3121],{"class":3876},[3180,9480,8995],{"class":8669},[3180,9482,8673],{"class":3876},[3180,9484,9000],{"class":4430},[3180,9486,9003],{"class":3876},[3180,9488,9006],{"class":4430},[3180,9490,8645],{"class":3876},[3180,9492,9493,9495,9497,9499,9502,9504,9507,9511,9513,9516,9519,9521,9523,9525,9527,9529,9531,9533,9535],{"class":3178,"line":8075},[3180,9494,9013],{"class":8627},[3180,9496,9016],{"class":8657},[3180,9498,3121],{"class":3876},[3180,9500,9501],{"class":8669},"Created",[3180,9503,8673],{"class":3876},[3180,9505,9506],{"class":4430},"$\"/todos/",[3180,9508,9510],{"class":9509},"sD7JJ","{",[3180,9512,9169],{"class":8657},[3180,9514,9515],{"class":9509},"}",[3180,9517,9518],{"class":4430},"\"",[3180,9520,4789],{"class":3876},[3180,9522,8742],{"class":4962},[3180,9524,8898],{"class":3876},[3180,9526,9233],{"class":8657},[3180,9528,4789],{"class":3876},[3180,9530,9238],{"class":8657},[3180,9532,8661],{"class":3876},[3180,9534,9065],{"class":8657},[3180,9536,9245],{"class":3876},[3180,9538,9539],{"class":3178,"line":8080},[3180,9540,9092],{"class":3876},[3180,9542,9543,9545,9547,9549,9552],{"class":3178,"line":8086},[3180,9544,3121],{"class":3876},[3180,9546,8932],{"class":8669},[3180,9548,8673],{"class":3876},[3180,9550,9551],{"class":4430},"\"CreateTodo\"",[3180,9553,8940],{"class":3876},[3180,9555,9556,9558,9560],{"class":3178,"line":8092},[3180,9557,3121],{"class":3876},[3180,9559,8947],{"class":8669},[3180,9561,8706],{"class":3876},[3180,9563,9564],{"class":3178,"line":8097},[3180,9565,3517],{"emptyLinePlaceholder":3516},[3180,9567,9568],{"class":3178,"line":8102},[3180,9569,9570],{"class":4183},"// PUT /todos/{id} - оновити завдання\n",[3180,9572,9573,9575,9577,9580,9582,9584,9586,9588,9590,9592,9595,9597],{"class":3178,"line":8107},[3180,9574,8813],{"class":8657},[3180,9576,3121],{"class":3876},[3180,9578,9579],{"class":8669},"MapPut",[3180,9581,8673],{"class":3876},[3180,9583,9135],{"class":4430},[3180,9585,9138],{"class":3876},[3180,9587,8751],{"class":4962},[3180,9589,9143],{"class":8657},[3180,9591,4789],{"class":3876},[3180,9593,9594],{"class":8631},"UpdateTodoRequest",[3180,9596,9335],{"class":8657},[3180,9598,9146],{"class":3876},[3180,9600,9601],{"class":3178,"line":8113},[3180,9602,8831],{"class":3876},[3180,9604,9605,9607,9610,9612,9614,9616,9618,9620,9622,9624,9626,9628],{"class":3178,"line":8118},[3180,9606,9155],{"class":8627},[3180,9608,9609],{"class":3876}," (!",[3180,9611,9042],{"class":8657},[3180,9613,3121],{"class":3876},[3180,9615,9164],{"class":8669},[3180,9617,8673],{"class":3876},[3180,9619,9169],{"class":8657},[3180,9621,4789],{"class":3876},[3180,9623,9174],{"class":4962},[3180,9625,9177],{"class":4962},[3180,9627,9180],{"class":8657},[3180,9629,9183],{"class":3876},[3180,9631,9632],{"class":3178,"line":8124},[3180,9633,9188],{"class":3876},[3180,9635,9636,9638,9640,9642,9644,9646,9648,9650,9652,9654,9656,9658,9660],{"class":3178,"line":8130},[3180,9637,9218],{"class":8627},[3180,9639,9016],{"class":8657},[3180,9641,3121],{"class":3876},[3180,9643,9260],{"class":8669},[3180,9645,8673],{"class":3876},[3180,9647,8742],{"class":4962},[3180,9649,8898],{"class":3876},[3180,9651,9269],{"class":8657},[3180,9653,8661],{"class":3876},[3180,9655,9274],{"class":4430},[3180,9657,4789],{"class":3876},[3180,9659,9169],{"class":8657},[3180,9661,9245],{"class":3876},[3180,9663,9664],{"class":3178,"line":8136},[3180,9665,3576],{"class":3876},[3180,9667,9668],{"class":3178,"line":8142},[3180,9669,3582],{"class":3876},[3180,9671,9672,9675,9677,9679,9681,9683,9685,9687,9690,9692,9694,9696],{"class":3178,"line":8148},[3180,9673,9674],{"class":8657},"    todo",[3180,9676,3121],{"class":3876},[3180,9678,9409],{"class":8657},[3180,9680,8661],{"class":3876},[3180,9682,9404],{"class":8657},[3180,9684,3121],{"class":3876},[3180,9686,9409],{"class":8657},[3180,9688,9689],{"class":3876}," ?? ",[3180,9691,9233],{"class":8657},[3180,9693,3121],{"class":3876},[3180,9695,9409],{"class":8657},[3180,9697,8645],{"class":3876},[3180,9699,9700,9702,9704,9707,9709,9711,9713,9715,9717,9719,9721,9723],{"class":3178,"line":8154},[3180,9701,9674],{"class":8657},[3180,9703,3121],{"class":3876},[3180,9705,9706],{"class":8657},"IsCompleted",[3180,9708,8661],{"class":3876},[3180,9710,9404],{"class":8657},[3180,9712,3121],{"class":3876},[3180,9714,9706],{"class":8657},[3180,9716,9689],{"class":3876},[3180,9718,9233],{"class":8657},[3180,9720,3121],{"class":3876},[3180,9722,9706],{"class":8657},[3180,9724,8645],{"class":3876},[3180,9726,9727],{"class":3178,"line":8159},[3180,9728,3582],{"class":3876},[3180,9730,9731,9733,9735,9737,9739,9741,9743,9745,9747,9749,9751],{"class":3178,"line":8165},[3180,9732,8983],{"class":4962},[3180,9734,8986],{"class":8657},[3180,9736,8661],{"class":3876},[3180,9738,8818],{"class":8657},[3180,9740,3121],{"class":3876},[3180,9742,8995],{"class":8669},[3180,9744,8673],{"class":3876},[3180,9746,9000],{"class":4430},[3180,9748,9003],{"class":3876},[3180,9750,9006],{"class":4430},[3180,9752,8645],{"class":3876},[3180,9754,9755,9757,9759,9761,9763,9765,9767,9769,9771,9773,9775,9777,9779],{"class":3178,"line":8170},[3180,9756,9013],{"class":8627},[3180,9758,9016],{"class":8657},[3180,9760,3121],{"class":3876},[3180,9762,8891],{"class":8669},[3180,9764,8673],{"class":3876},[3180,9766,8742],{"class":4962},[3180,9768,8898],{"class":3876},[3180,9770,9233],{"class":8657},[3180,9772,4789],{"class":3876},[3180,9774,9238],{"class":8657},[3180,9776,8661],{"class":3876},[3180,9778,9065],{"class":8657},[3180,9780,9245],{"class":3876},[3180,9782,9783],{"class":3178,"line":8176},[3180,9784,9092],{"class":3876},[3180,9786,9787,9789,9791,9793,9796],{"class":3178,"line":8182},[3180,9788,3121],{"class":3876},[3180,9790,8932],{"class":8669},[3180,9792,8673],{"class":3876},[3180,9794,9795],{"class":4430},"\"UpdateTodo\"",[3180,9797,8940],{"class":3876},[3180,9799,9800,9802,9804],{"class":3178,"line":8188},[3180,9801,3121],{"class":3876},[3180,9803,8947],{"class":8669},[3180,9805,8706],{"class":3876},[3180,9807,9808],{"class":3178,"line":8194},[3180,9809,3517],{"emptyLinePlaceholder":3516},[3180,9811,9812],{"class":3178,"line":8200},[3180,9813,9814],{"class":4183},"// DELETE /todos/{id} - видалити завдання\n",[3180,9816,9817,9819,9821,9824,9826,9828,9830,9832,9834],{"class":3178,"line":8206},[3180,9818,8813],{"class":8657},[3180,9820,3121],{"class":3876},[3180,9822,9823],{"class":8669},"MapDelete",[3180,9825,8673],{"class":3876},[3180,9827,9135],{"class":4430},[3180,9829,9138],{"class":3876},[3180,9831,8751],{"class":4962},[3180,9833,9143],{"class":8657},[3180,9835,9146],{"class":3876},[3180,9837,9838],{"class":3178,"line":8211},[3180,9839,8831],{"class":3876},[3180,9841,9842,9844,9846,9848,9850,9853,9855,9857,9859,9861,9863,9865],{"class":3178,"line":8217},[3180,9843,9155],{"class":8627},[3180,9845,4796],{"class":3876},[3180,9847,9042],{"class":8657},[3180,9849,3121],{"class":3876},[3180,9851,9852],{"class":8669},"TryRemove",[3180,9854,8673],{"class":3876},[3180,9856,9169],{"class":8657},[3180,9858,4789],{"class":3876},[3180,9860,9174],{"class":4962},[3180,9862,9177],{"class":4962},[3180,9864,9180],{"class":8657},[3180,9866,9183],{"class":3876},[3180,9868,9869],{"class":3178,"line":8222},[3180,9870,9188],{"class":3876},[3180,9872,9873,9875,9877,9879,9881,9883,9885,9887,9889,9891,9893],{"class":3178,"line":8227},[3180,9874,9193],{"class":4962},[3180,9876,8986],{"class":8657},[3180,9878,8661],{"class":3876},[3180,9880,8818],{"class":8657},[3180,9882,3121],{"class":3876},[3180,9884,8995],{"class":8669},[3180,9886,8673],{"class":3876},[3180,9888,9000],{"class":4430},[3180,9890,9003],{"class":3876},[3180,9892,9006],{"class":4430},[3180,9894,8645],{"class":3876},[3180,9896,9897,9899,9901,9903,9905,9907,9909,9911,9914,9916,9919,9921,9923,9925,9927,9929,9931],{"class":3178,"line":8233},[3180,9898,9218],{"class":8627},[3180,9900,9016],{"class":8657},[3180,9902,3121],{"class":3876},[3180,9904,8891],{"class":8669},[3180,9906,8673],{"class":3876},[3180,9908,8742],{"class":4962},[3180,9910,8898],{"class":3876},[3180,9912,9913],{"class":8657},"message",[3180,9915,8661],{"class":3876},[3180,9917,9918],{"class":4430},"\"Todo deleted\"",[3180,9920,4789],{"class":3876},[3180,9922,9233],{"class":8657},[3180,9924,4789],{"class":3876},[3180,9926,9238],{"class":8657},[3180,9928,8661],{"class":3876},[3180,9930,9065],{"class":8657},[3180,9932,9245],{"class":3876},[3180,9934,9935],{"class":3178,"line":8239},[3180,9936,3576],{"class":3876},[3180,9938,9939,9941,9943,9945,9947,9949,9951,9953,9955,9957,9959,9961,9963],{"class":3178,"line":8245},[3180,9940,9013],{"class":8627},[3180,9942,9016],{"class":8657},[3180,9944,3121],{"class":3876},[3180,9946,9260],{"class":8669},[3180,9948,8673],{"class":3876},[3180,9950,8742],{"class":4962},[3180,9952,8898],{"class":3876},[3180,9954,9269],{"class":8657},[3180,9956,8661],{"class":3876},[3180,9958,9274],{"class":4430},[3180,9960,4789],{"class":3876},[3180,9962,9169],{"class":8657},[3180,9964,9245],{"class":3876},[3180,9966,9967],{"class":3178,"line":8251},[3180,9968,9092],{"class":3876},[3180,9970,9971,9973,9975,9977,9980],{"class":3178,"line":8257},[3180,9972,3121],{"class":3876},[3180,9974,8932],{"class":8669},[3180,9976,8673],{"class":3876},[3180,9978,9979],{"class":4430},"\"DeleteTodo\"",[3180,9981,8940],{"class":3876},[3180,9983,9984,9986,9988],{"class":3178,"line":8262},[3180,9985,3121],{"class":3876},[3180,9987,8947],{"class":8669},[3180,9989,8706],{"class":3876},[3180,9991,9992],{"class":3178,"line":8267},[3180,9993,3517],{"emptyLinePlaceholder":3516},[3180,9995,9997,9999,10001,10004],{"class":3178,"line":9996},104,[3180,9998,8813],{"class":8657},[3180,10000,3121],{"class":3876},[3180,10002,10003],{"class":8669},"Run",[3180,10005,8706],{"class":3876},[3180,10007,10009],{"class":3178,"line":10008},105,[3180,10010,3517],{"emptyLinePlaceholder":3516},[3180,10012,10014],{"class":3178,"line":10013},106,[3180,10015,10016],{"class":4183},"// Моделі\n",[3180,10018,10020,10023],{"class":3178,"line":10019},107,[3180,10021,10022],{"class":4962},"record",[3180,10024,9379],{"class":8631},[3180,10026,10028],{"class":3178,"line":10027},108,[3180,10029,8831],{"class":3876},[3180,10031,10033,10036,10039,10042,10044,10047,10050,10053],{"class":3178,"line":10032},109,[3180,10034,10035],{"class":4962},"    public",[3180,10037,10038],{"class":4962}," int",[3180,10040,10041],{"class":8657}," Id",[3180,10043,8898],{"class":3876},[3180,10045,10046],{"class":4962},"get",[3180,10048,10049],{"class":3876},"; ",[3180,10051,10052],{"class":4962},"set",[3180,10054,10055],{"class":3876},"; }\n",[3180,10057,10059,10061,10064,10067,10070,10072,10074,10076,10078],{"class":3178,"line":10058},110,[3180,10060,10035],{"class":4962},[3180,10062,10063],{"class":4962}," required",[3180,10065,10066],{"class":4962}," string",[3180,10068,10069],{"class":8657}," Title",[3180,10071,8898],{"class":3876},[3180,10073,10046],{"class":4962},[3180,10075,10049],{"class":3876},[3180,10077,10052],{"class":4962},[3180,10079,10055],{"class":3876},[3180,10081,10083,10085,10088,10091,10093,10095,10097,10099],{"class":3178,"line":10082},111,[3180,10084,10035],{"class":4962},[3180,10086,10087],{"class":4962}," bool",[3180,10089,10090],{"class":8657}," IsCompleted",[3180,10092,8898],{"class":3876},[3180,10094,10046],{"class":4962},[3180,10096,10049],{"class":3876},[3180,10098,10052],{"class":4962},[3180,10100,10055],{"class":3876},[3180,10102,10104,10106,10109,10112,10114,10116,10118,10120],{"class":3178,"line":10103},112,[3180,10105,10035],{"class":4962},[3180,10107,10108],{"class":8631}," DateTime",[3180,10110,10111],{"class":8657}," CreatedAt",[3180,10113,8898],{"class":3876},[3180,10115,10046],{"class":4962},[3180,10117,10049],{"class":3876},[3180,10119,10052],{"class":4962},[3180,10121,10055],{"class":3876},[3180,10123,10125],{"class":3178,"line":10124},113,[3180,10126,3541],{"class":3876},[3180,10128,10130],{"class":3178,"line":10129},114,[3180,10131,3517],{"emptyLinePlaceholder":3516},[3180,10133,10135,10137,10140,10142,10144,10146],{"class":3178,"line":10134},115,[3180,10136,10022],{"class":4962},[3180,10138,10139],{"class":8631}," CreateTodoRequest",[3180,10141,8673],{"class":3876},[3180,10143,4027],{"class":4962},[3180,10145,10069],{"class":8657},[3180,10147,8679],{"class":3876},[3180,10149,10151,10153,10156,10158,10160,10163,10165,10167,10170,10172,10174],{"class":3178,"line":10150},116,[3180,10152,10022],{"class":4962},[3180,10154,10155],{"class":8631}," UpdateTodoRequest",[3180,10157,8673],{"class":3876},[3180,10159,4027],{"class":4962},[3180,10161,10162],{"class":3876},"? ",[3180,10164,9409],{"class":8657},[3180,10166,4789],{"class":3876},[3180,10168,10169],{"class":4962},"bool",[3180,10171,10162],{"class":3876},[3180,10173,9706],{"class":8657},[3180,10175,8679],{"class":3876},[3114,10177,10178],{},[3118,10179,10180],{},"TodoApi.csproj:",[3485,10182,10186],{"className":10183,"code":10184,"language":10185,"meta":3490,"style":3490},"language-xml shiki shiki-themes light-plus dark-plus dark-plus","\u003CProject Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net8.0\u003C/TargetFramework>\n    \u003CNullable>enable\u003C/Nullable>\n    \u003CImplicitUsings>enable\u003C/ImplicitUsings>\n  \u003C/PropertyGroup>\n\n  \u003CItemGroup>\n    \u003CPackageReference Include=\"Swashbuckle.AspNetCore\" Version=\"6.5.0\" />\n  \u003C/ItemGroup>\n\n\u003C/Project>\n","xml",[3429,10187,10188,10209,10213,10223,10244,10262,10279,10288,10292,10301,10327,10335,10339],{"__ignoreMap":3490},[3180,10189,10190,10193,10196,10200,10203,10206],{"class":3178,"line":3495},[3180,10191,8748],{"class":10192},"s0P7L",[3180,10194,10195],{"class":3872},"Project",[3180,10197,10199],{"class":10198},"sa4r_"," Sdk",[3180,10201,10202],{"class":3876},"=",[3180,10204,10205],{"class":3880},"\"Microsoft.NET.Sdk.Web\"",[3180,10207,10208],{"class":10192},">\n",[3180,10210,10211],{"class":3178,"line":3501},[3180,10212,3517],{"emptyLinePlaceholder":3516},[3180,10214,10215,10218,10221],{"class":3178,"line":3507},[3180,10216,10217],{"class":10192},"  \u003C",[3180,10219,10220],{"class":3872},"PropertyGroup",[3180,10222,10208],{"class":10192},[3180,10224,10225,10228,10231,10234,10237,10240,10242],{"class":3178,"line":3513},[3180,10226,10227],{"class":10192},"    \u003C",[3180,10229,10230],{"class":3872},"TargetFramework",[3180,10232,10233],{"class":10192},">",[3180,10235,10236],{"class":3876},"net8.0",[3180,10238,10239],{"class":10192},"\u003C/",[3180,10241,10230],{"class":3872},[3180,10243,10208],{"class":10192},[3180,10245,10246,10248,10251,10253,10256,10258,10260],{"class":3178,"line":3520},[3180,10247,10227],{"class":10192},[3180,10249,10250],{"class":3872},"Nullable",[3180,10252,10233],{"class":10192},[3180,10254,10255],{"class":3876},"enable",[3180,10257,10239],{"class":10192},[3180,10259,10250],{"class":3872},[3180,10261,10208],{"class":10192},[3180,10263,10264,10266,10269,10271,10273,10275,10277],{"class":3178,"line":3526},[3180,10265,10227],{"class":10192},[3180,10267,10268],{"class":3872},"ImplicitUsings",[3180,10270,10233],{"class":10192},[3180,10272,10255],{"class":3876},[3180,10274,10239],{"class":10192},[3180,10276,10268],{"class":3872},[3180,10278,10208],{"class":10192},[3180,10280,10281,10284,10286],{"class":3178,"line":3532},[3180,10282,10283],{"class":10192},"  \u003C/",[3180,10285,10220],{"class":3872},[3180,10287,10208],{"class":10192},[3180,10289,10290],{"class":3178,"line":3538},[3180,10291,3517],{"emptyLinePlaceholder":3516},[3180,10293,10294,10296,10299],{"class":3178,"line":3544},[3180,10295,10217],{"class":10192},[3180,10297,10298],{"class":3872},"ItemGroup",[3180,10300,10208],{"class":10192},[3180,10302,10303,10305,10308,10311,10313,10316,10319,10321,10324],{"class":3178,"line":3549},[3180,10304,10227],{"class":10192},[3180,10306,10307],{"class":3872},"PackageReference",[3180,10309,10310],{"class":10198}," Include",[3180,10312,10202],{"class":3876},[3180,10314,10315],{"class":3880},"\"Swashbuckle.AspNetCore\"",[3180,10317,10318],{"class":10198}," Version",[3180,10320,10202],{"class":3876},[3180,10322,10323],{"class":3880},"\"6.5.0\"",[3180,10325,10326],{"class":10192}," />\n",[3180,10328,10329,10331,10333],{"class":3178,"line":3555},[3180,10330,10283],{"class":10192},[3180,10332,10298],{"class":3872},[3180,10334,10208],{"class":10192},[3180,10336,10337],{"class":3178,"line":3561},[3180,10338,3517],{"emptyLinePlaceholder":3516},[3180,10340,10341,10343,10345],{"class":3178,"line":3567},[3180,10342,10239],{"class":10192},[3180,10344,10195],{"class":3872},[3180,10346,10208],{"class":10192},[3406,10348,10349,10357,10370],{},[3114,10350,10351],{},[3118,10352,10353,10354,10356],{},"Чому ",[3429,10355,9238],{}," у відповідях?",[3114,10358,10359,10360,10362,10363,10366,10367,3121],{},"Кожна відповідь API містить поле ",[3429,10361,9238],{}," з hostname Pod. Це дозволяє побачити, який саме Pod обробив запит. У Kubernetes кожен Pod має унікальне ім'я (наприклад, ",[3429,10364,10365],{},"todoapi-7d6b8c9f4d-x2n9k","), яке зберігається у змінній оточення ",[3429,10368,10369],{},"HOSTNAME",[3114,10371,10372],{},"Це корисно для демонстрації балансування навантаження — ви побачите, що різні запити обробляються різними Pod.",[3126,10374,10376],{"id":10375},"крок-2-створення-dockerfile","Крок 2: Створення Dockerfile",[3114,10378,10379],{},"Створимо multi-stage Dockerfile для оптимізації розміру образу:",[3114,10381,10382],{},[3118,10383,10384],{},"Dockerfile:",[3485,10386,10390],{"className":10387,"code":10388,"language":10389,"meta":3490,"style":3490},"language-dockerfile shiki shiki-themes light-plus dark-plus dark-plus","# Stage 1: Build\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nWORKDIR /src\n\n# Копіюємо .csproj та відновлюємо залежності (кешування шарів)\nCOPY TodoApi.csproj .\nRUN dotnet restore\n\n# Копіюємо решту файлів та збираємо\nCOPY . .\nRUN dotnet publish -c Release -o /app/publish\n\n# Stage 2: Runtime\nFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime\nWORKDIR /app\n\n# Створюємо non-root користувача для безпеки\nRUN addgroup --system --gid 1000 appgroup && \\\n    adduser --system --uid 1000 --ingroup appgroup appuser\n\n# Копіюємо зібраний застосунок\nCOPY --from=build /app/publish .\n\n# Змінюємо власника файлів\nRUN chown -R appuser:appgroup /app\n\n# Перемикаємось на non-root користувача\nUSER appuser\n\n# Відкриваємо порт 8080 (non-privileged port)\nEXPOSE 8080\n\n# Налаштовуємо ASP.NET Core для прослуховування на порту 8080\nENV ASPNETCORE_URLS=http://+:8080\n\n# Запускаємо застосунок\nENTRYPOINT [\"dotnet\", \"TodoApi.dll\"]\n","dockerfile",[3429,10391,10392,10397,10411,10419,10423,10428,10436,10444,10448,10453,10460,10467,10471,10476,10488,10495,10499,10504,10511,10516,10520,10525,10532,10536,10541,10548,10552,10557,10565,10569,10574,10582,10586,10591,10599,10603,10608],{"__ignoreMap":3490},[3180,10393,10394],{"class":3178,"line":3495},[3180,10395,10396],{"class":4183},"# Stage 1: Build\n",[3180,10398,10399,10402,10405,10408],{"class":3178,"line":3501},[3180,10400,10401],{"class":4962},"FROM",[3180,10403,10404],{"class":3876}," mcr.microsoft.com/dotnet/sdk:8.0 ",[3180,10406,10407],{"class":4962},"AS",[3180,10409,10410],{"class":3876}," build\n",[3180,10412,10413,10416],{"class":3178,"line":3507},[3180,10414,10415],{"class":4962},"WORKDIR",[3180,10417,10418],{"class":3876}," /src\n",[3180,10420,10421],{"class":3178,"line":3513},[3180,10422,3517],{"emptyLinePlaceholder":3516},[3180,10424,10425],{"class":3178,"line":3520},[3180,10426,10427],{"class":4183},"# Копіюємо .csproj та відновлюємо залежності (кешування шарів)\n",[3180,10429,10430,10433],{"class":3178,"line":3526},[3180,10431,10432],{"class":4962},"COPY",[3180,10434,10435],{"class":3876}," TodoApi.csproj .\n",[3180,10437,10438,10441],{"class":3178,"line":3532},[3180,10439,10440],{"class":4962},"RUN",[3180,10442,10443],{"class":3876}," dotnet restore\n",[3180,10445,10446],{"class":3178,"line":3538},[3180,10447,3517],{"emptyLinePlaceholder":3516},[3180,10449,10450],{"class":3178,"line":3544},[3180,10451,10452],{"class":4183},"# Копіюємо решту файлів та збираємо\n",[3180,10454,10455,10457],{"class":3178,"line":3549},[3180,10456,10432],{"class":4962},[3180,10458,10459],{"class":3876}," . .\n",[3180,10461,10462,10464],{"class":3178,"line":3555},[3180,10463,10440],{"class":4962},[3180,10465,10466],{"class":3876}," dotnet publish -c Release -o /app/publish\n",[3180,10468,10469],{"class":3178,"line":3561},[3180,10470,3517],{"emptyLinePlaceholder":3516},[3180,10472,10473],{"class":3178,"line":3567},[3180,10474,10475],{"class":4183},"# Stage 2: Runtime\n",[3180,10477,10478,10480,10483,10485],{"class":3178,"line":3573},[3180,10479,10401],{"class":4962},[3180,10481,10482],{"class":3876}," mcr.microsoft.com/dotnet/aspnet:8.0 ",[3180,10484,10407],{"class":4962},[3180,10486,10487],{"class":3876}," runtime\n",[3180,10489,10490,10492],{"class":3178,"line":3579},[3180,10491,10415],{"class":4962},[3180,10493,10494],{"class":3876}," /app\n",[3180,10496,10497],{"class":3178,"line":3585},[3180,10498,3517],{"emptyLinePlaceholder":3516},[3180,10500,10501],{"class":3178,"line":3591},[3180,10502,10503],{"class":4183},"# Створюємо non-root користувача для безпеки\n",[3180,10505,10506,10508],{"class":3178,"line":3596},[3180,10507,10440],{"class":4962},[3180,10509,10510],{"class":3876}," addgroup --system --gid 1000 appgroup && \\\n",[3180,10512,10513],{"class":3178,"line":3602},[3180,10514,10515],{"class":3876},"    adduser --system --uid 1000 --ingroup appgroup appuser\n",[3180,10517,10518],{"class":3178,"line":3607},[3180,10519,3517],{"emptyLinePlaceholder":3516},[3180,10521,10522],{"class":3178,"line":3612},[3180,10523,10524],{"class":4183},"# Копіюємо зібраний застосунок\n",[3180,10526,10527,10529],{"class":3178,"line":3618},[3180,10528,10432],{"class":4962},[3180,10530,10531],{"class":3876}," --from=build /app/publish .\n",[3180,10533,10534],{"class":3178,"line":3624},[3180,10535,3517],{"emptyLinePlaceholder":3516},[3180,10537,10538],{"class":3178,"line":3630},[3180,10539,10540],{"class":4183},"# Змінюємо власника файлів\n",[3180,10542,10543,10545],{"class":3178,"line":3636},[3180,10544,10440],{"class":4962},[3180,10546,10547],{"class":3876}," chown -R appuser:appgroup /app\n",[3180,10549,10550],{"class":3178,"line":3641},[3180,10551,3517],{"emptyLinePlaceholder":3516},[3180,10553,10554],{"class":3178,"line":3646},[3180,10555,10556],{"class":4183},"# Перемикаємось на non-root користувача\n",[3180,10558,10559,10562],{"class":3178,"line":3651},[3180,10560,10561],{"class":4962},"USER",[3180,10563,10564],{"class":3876}," appuser\n",[3180,10566,10567],{"class":3178,"line":3657},[3180,10568,3517],{"emptyLinePlaceholder":3516},[3180,10570,10571],{"class":3178,"line":3663},[3180,10572,10573],{"class":4183},"# Відкриваємо порт 8080 (non-privileged port)\n",[3180,10575,10576,10579],{"class":3178,"line":3669},[3180,10577,10578],{"class":4962},"EXPOSE",[3180,10580,10581],{"class":3876}," 8080\n",[3180,10583,10584],{"class":3178,"line":3675},[3180,10585,3517],{"emptyLinePlaceholder":3516},[3180,10587,10588],{"class":3178,"line":3681},[3180,10589,10590],{"class":4183},"# Налаштовуємо ASP.NET Core для прослуховування на порту 8080\n",[3180,10592,10593,10596],{"class":3178,"line":3687},[3180,10594,10595],{"class":4962},"ENV",[3180,10597,10598],{"class":3876}," ASPNETCORE_URLS=http://+:8080\n",[3180,10600,10601],{"class":3178,"line":3692},[3180,10602,3517],{"emptyLinePlaceholder":3516},[3180,10604,10605],{"class":3178,"line":3698},[3180,10606,10607],{"class":4183},"# Запускаємо застосунок\n",[3180,10609,10610,10613,10616,10619,10621,10624],{"class":3178,"line":3704},[3180,10611,10612],{"class":4962},"ENTRYPOINT",[3180,10614,10615],{"class":3876}," [",[3180,10617,10618],{"class":4430},"\"dotnet\"",[3180,10620,4789],{"class":3876},[3180,10622,10623],{"class":4430},"\"TodoApi.dll\"",[3180,10625,10626],{"class":3876},"]\n",[4865,10628,10629,10634,10637,10658,10661,10666,10669,10674],{},[3114,10630,10631],{},[3118,10632,10633],{},"Чому multi-stage build?",[3114,10635,10636],{},"Multi-stage Dockerfile має дві стадії:",[3134,10638,10639,10649],{},[3137,10640,10641,10644,10645,10648],{},[3118,10642,10643],{},"Build stage"," — використовує ",[3429,10646,10647],{},"dotnet/sdk:8.0"," (розмір ~1.2 GB) для компіляції застосунку",[3137,10650,10651,10644,10654,10657],{},[3118,10652,10653],{},"Runtime stage",[3429,10655,10656],{},"dotnet/aspnet:8.0"," (розмір ~200 MB) лише з runtime",[3114,10659,10660],{},"Результат: фінальний образ містить лише runtime та скомпільований застосунок, без SDK та проміжних файлів. Це зменшує розмір образу з ~1.2 GB до ~220 MB.",[3114,10662,10663],{},[3118,10664,10665],{},"Чому non-root користувач?",[3114,10667,10668],{},"Запуск контейнера від root — це ризик безпеки. Якщо зловмисник зламає застосунок, він матиме root-доступ до контейнера. Non-root користувач обмежує можливості атакуючого.",[3114,10670,10671],{},[3118,10672,10673],{},"Чому порт 8080, а не 80?",[3114,10675,10676],{},"Порти \u003C 1024 вимагають root-привілеїв. Використання порту 8080 дозволяє запускати застосунок від non-root користувача.",[3126,10678,10680],{"id":10679},"крок-3-збірка-та-завантаження-образу-у-minikube","Крок 3: Збірка та завантаження образу у Minikube",[3114,10682,10683],{},"Тепер зберемо Docker образ та завантажимо його у Minikube:",[3171,10685,10687,10696,10699,10706,10715,10718,10725,10734,10738,10742,10746,10750,10754,10758,10762,10766,10770,10774,10781],{"title":10686},"Збірка образу",[3175,10688,10690,3192,10693],{"className":10689},[3178],[3180,10691,3191],{"className":10692},[3183],[3118,10694,10695],{},"cd TodoApi",[3175,10697],{"className":10698},[3178],[3175,10700,10702],{"className":10701},[3178],[3180,10703,10705],{"className":10704},[3183],"# Налаштовуємо Docker для використання Minikube registry",[3175,10707,10709,3192,10712],{"className":10708},[3178],[3180,10710,3191],{"className":10711},[3183],[3118,10713,10714],{},"eval $(minikube docker-env)",[3175,10716],{"className":10717},[3178],[3175,10719,10721],{"className":10720},[3178],[3180,10722,10724],{"className":10723},[3183],"# Збираємо образ",[3175,10726,10728,3192,10731],{"className":10727},[3178],[3180,10729,3191],{"className":10730},[3183],[3118,10732,10733],{},"docker build -t todoapi:1.0.0 .",[3175,10735,10737],{"className":10736},[3178],"[+] Building 45.2s (15/15) FINISHED",[3175,10739,10741],{"className":10740},[3178]," => [build 1/5] FROM mcr.microsoft.com/dotnet/sdk:8.0",[3175,10743,10745],{"className":10744},[3178]," => [build 2/5] COPY TodoApi.csproj .",[3175,10747,10749],{"className":10748},[3178]," => [build 3/5] RUN dotnet restore",[3175,10751,10753],{"className":10752},[3178]," => [build 4/5] COPY . .",[3175,10755,10757],{"className":10756},[3178]," => [build 5/5] RUN dotnet publish -c Release -o /app/publish",[3175,10759,10761],{"className":10760},[3178]," => [runtime 1/4] FROM mcr.microsoft.com/dotnet/aspnet:8.0",[3175,10763,10765],{"className":10764},[3178]," => [runtime 2/4] RUN addgroup --system --gid 1000 appgroup",[3175,10767,10769],{"className":10768},[3178]," => [runtime 3/4] COPY --from=build /app/publish .",[3175,10771,10773],{"className":10772},[3178]," => [runtime 4/4] RUN chown -R appuser:appgroup /app",[3175,10775,10777],{"className":10776},[3178],[3180,10778,10780],{"className":10779},[5673]," => exporting to image",[3175,10782,10784],{"className":10783},[3178],[3180,10785,10787],{"className":10786},[5673]," => => naming to docker.io/library/todoapi:1.0.0",[3406,10789,10790,10798,10801,10804,10818],{},[3114,10791,10792],{},[3118,10793,10794,10795,10797],{},"Що робить ",[3429,10796,10714],{},"?",[3114,10799,10800],{},"Ця команда налаштовує ваш локальний Docker CLI для роботи з Docker daemon всередині Minikube. Після цього всі образи, які ви збираєте, зберігаються безпосередньо у Minikube, і їх не потрібно завантажувати окремо.",[3114,10802,10803],{},"Без цієї команди вам довелося б:",[3134,10805,10806,10809,10812],{},[3137,10807,10808],{},"Зібрати образ локально",[3137,10810,10811],{},"Зберегти його у tar-файл",[3137,10813,10814,10815],{},"Завантажити у Minikube через ",[3429,10816,10817],{},"minikube image load",[3114,10819,10820,10821,10823],{},"З ",[3429,10822,10714],{}," образ одразу доступний у Minikube.",[3114,10825,10826],{},"Перевіримо, що образ створено:",[3171,10828,10830,10839],{"title":10829},"Перевірка образу",[3175,10831,10833,3192,10836],{"className":10832},[3178],[3180,10834,3191],{"className":10835},[3183],[3118,10837,10838],{},"docker images | grep todoapi",[3175,10840,10842],{"className":10841},[3178],"todoapi      1.0.0     a1b2c3d4e5f6   2 minutes ago   218MB",[3126,10844,10846],{"id":10845},"крок-4-створення-deployment-yaml","Крок 4: Створення Deployment YAML",[3114,10848,10849],{},"Створимо маніфест Deployment для TodoApi:",[3114,10851,10852],{},[3118,10853,10854],{},"k8s/deployment.yaml:",[3485,10856,10858],{"className":3863,"code":10857,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: todoapi\n  labels:\n    app: todoapi\n    version: \"1.0.0\"\nspec:\n  # Три репліки для балансування навантаження\n  replicas: 3\n  \n  # Селектор для вибору Pod\n  selector:\n    matchLabels:\n      app: todoapi\n  \n  # Шаблон Pod\n  template:\n    metadata:\n      labels:\n        app: todoapi\n        version: \"1.0.0\"\n    spec:\n      containers:\n        - name: todoapi\n          image: todoapi:1.0.0\n          imagePullPolicy: Never  # Використовувати локальний образ (для Minikube)\n          \n          ports:\n            - name: http\n              containerPort: 8080\n              protocol: TCP\n          \n          # Змінні оточення\n          env:\n            - name: ASPNETCORE_ENVIRONMENT\n              value: \"Production\"\n            - name: ASPNETCORE_URLS\n              value: \"http://+:8080\"\n          \n          # Ресурси (requests та limits)\n          resources:\n            requests:\n              memory: \"128Mi\"\n              cpu: \"100m\"\n            limits:\n              memory: \"256Mi\"\n              cpu: \"500m\"\n          \n          # Liveness probe - перевірка, чи застосунок живий\n          livenessProbe:\n            httpGet:\n              path: /health\n              port: 8080\n            initialDelaySeconds: 10\n            periodSeconds: 10\n            timeoutSeconds: 5\n            failureThreshold: 3\n          \n          # Readiness probe - перевірка, чи застосунок готовий приймати трафік\n          readinessProbe:\n            httpGet:\n              path: /health\n              port: 8080\n            initialDelaySeconds: 5\n            periodSeconds: 5\n            timeoutSeconds: 3\n            failureThreshold: 3\n",[3429,10859,10860,10868,10876,10882,10891,10897,10905,10914,10920,10925,10933,10937,10941,10947,10953,10961,10965,10969,10975,10981,10987,10995,11003,11009,11015,11025,11034,11047,11052,11058,11068,11077,11087,11091,11096,11102,11112,11120,11131,11140,11144,11149,11155,11161,11169,11177,11183,11191,11199,11203,11208,11215,11222,11232,11241,11250,11259,11268,11277,11281,11286,11293,11299,11307,11315,11323,11331,11339],{"__ignoreMap":3490},[3180,10861,10862,10864,10866],{"class":3178,"line":3495},[3180,10863,3873],{"class":3872},[3180,10865,3877],{"class":3876},[3180,10867,3881],{"class":3880},[3180,10869,10870,10872,10874],{"class":3178,"line":3501},[3180,10871,3886],{"class":3872},[3180,10873,3877],{"class":3876},[3180,10875,3891],{"class":3880},[3180,10877,10878,10880],{"class":3178,"line":3507},[3180,10879,3896],{"class":3872},[3180,10881,3899],{"class":3876},[3180,10883,10884,10886,10888],{"class":3178,"line":3513},[3180,10885,3904],{"class":3872},[3180,10887,3877],{"class":3876},[3180,10889,10890],{"class":3880},"todoapi\n",[3180,10892,10893,10895],{"class":3178,"line":3520},[3180,10894,4408],{"class":3872},[3180,10896,3899],{"class":3876},[3180,10898,10899,10901,10903],{"class":3178,"line":3526},[3180,10900,4415],{"class":3872},[3180,10902,3877],{"class":3876},[3180,10904,10890],{"class":3880},[3180,10906,10907,10909,10911],{"class":3178,"line":3532},[3180,10908,4425],{"class":3872},[3180,10910,3877],{"class":3876},[3180,10912,10913],{"class":4430},"\"1.0.0\"\n",[3180,10915,10916,10918],{"class":3178,"line":3538},[3180,10917,3914],{"class":3872},[3180,10919,3899],{"class":3876},[3180,10921,10922],{"class":3178,"line":3544},[3180,10923,10924],{"class":4183},"  # Три репліки для балансування навантаження\n",[3180,10926,10927,10929,10931],{"class":3178,"line":3549},[3180,10928,3921],{"class":3872},[3180,10930,3877],{"class":3876},[3180,10932,3927],{"class":3926},[3180,10934,10935],{"class":3178,"line":3555},[3180,10936,5169],{"class":3876},[3180,10938,10939],{"class":3178,"line":3561},[3180,10940,5174],{"class":4183},[3180,10942,10943,10945],{"class":3178,"line":3567},[3180,10944,3932],{"class":3872},[3180,10946,3899],{"class":3876},[3180,10948,10949,10951],{"class":3178,"line":3573},[3180,10950,3939],{"class":3872},[3180,10952,3899],{"class":3876},[3180,10954,10955,10957,10959],{"class":3178,"line":3579},[3180,10956,3946],{"class":3872},[3180,10958,3877],{"class":3876},[3180,10960,10890],{"class":3880},[3180,10962,10963],{"class":3178,"line":3585},[3180,10964,5169],{"class":3876},[3180,10966,10967],{"class":3178,"line":3591},[3180,10968,5282],{"class":4183},[3180,10970,10971,10973],{"class":3178,"line":3596},[3180,10972,3956],{"class":3872},[3180,10974,3899],{"class":3876},[3180,10976,10977,10979],{"class":3178,"line":3602},[3180,10978,3963],{"class":3872},[3180,10980,3899],{"class":3876},[3180,10982,10983,10985],{"class":3178,"line":3607},[3180,10984,3970],{"class":3872},[3180,10986,3899],{"class":3876},[3180,10988,10989,10991,10993],{"class":3178,"line":3612},[3180,10990,3977],{"class":3872},[3180,10992,3877],{"class":3876},[3180,10994,10890],{"class":3880},[3180,10996,10997,10999,11001],{"class":3178,"line":3618},[3180,10998,4513],{"class":3872},[3180,11000,3877],{"class":3876},[3180,11002,10913],{"class":4430},[3180,11004,11005,11007],{"class":3178,"line":3624},[3180,11006,3986],{"class":3872},[3180,11008,3899],{"class":3876},[3180,11010,11011,11013],{"class":3178,"line":3630},[3180,11012,3993],{"class":3872},[3180,11014,3899],{"class":3876},[3180,11016,11017,11019,11021,11023],{"class":3178,"line":3636},[3180,11018,4000],{"class":3876},[3180,11020,4003],{"class":3872},[3180,11022,3877],{"class":3876},[3180,11024,10890],{"class":3880},[3180,11026,11027,11029,11031],{"class":3178,"line":3641},[3180,11028,4012],{"class":3872},[3180,11030,3877],{"class":3876},[3180,11032,11033],{"class":3880},"todoapi:1.0.0\n",[3180,11035,11036,11039,11041,11044],{"class":3178,"line":3646},[3180,11037,11038],{"class":3872},"          imagePullPolicy",[3180,11040,3877],{"class":3876},[3180,11042,11043],{"class":3880},"Never",[3180,11045,11046],{"class":4183},"  # Використовувати локальний образ (для Minikube)\n",[3180,11048,11049],{"class":3178,"line":3651},[3180,11050,11051],{"class":3876},"          \n",[3180,11053,11054,11056],{"class":3178,"line":3657},[3180,11055,4554],{"class":3872},[3180,11057,3899],{"class":3876},[3180,11059,11060,11062,11064,11066],{"class":3178,"line":3663},[3180,11061,4561],{"class":3876},[3180,11063,4003],{"class":3872},[3180,11065,3877],{"class":3876},[3180,11067,5372],{"class":3880},[3180,11069,11070,11073,11075],{"class":3178,"line":3669},[3180,11071,11072],{"class":3872},"              containerPort",[3180,11074,3877],{"class":3876},[3180,11076,4569],{"class":3926},[3180,11078,11079,11082,11084],{"class":3178,"line":3675},[3180,11080,11081],{"class":3872},"              protocol",[3180,11083,3877],{"class":3876},[3180,11085,11086],{"class":3880},"TCP\n",[3180,11088,11089],{"class":3178,"line":3681},[3180,11090,11051],{"class":3876},[3180,11092,11093],{"class":3178,"line":3687},[3180,11094,11095],{"class":4183},"          # Змінні оточення\n",[3180,11097,11098,11100],{"class":3178,"line":3692},[3180,11099,4574],{"class":3872},[3180,11101,3899],{"class":3876},[3180,11103,11104,11106,11108,11110],{"class":3178,"line":3698},[3180,11105,4561],{"class":3876},[3180,11107,4003],{"class":3872},[3180,11109,3877],{"class":3876},[3180,11111,5389],{"class":3880},[3180,11113,11114,11116,11118],{"class":3178,"line":3704},[3180,11115,4592],{"class":3872},[3180,11117,3877],{"class":3876},[3180,11119,5398],{"class":4430},[3180,11121,11122,11124,11126,11128],{"class":3178,"line":3710},[3180,11123,4561],{"class":3876},[3180,11125,4003],{"class":3872},[3180,11127,3877],{"class":3876},[3180,11129,11130],{"class":3880},"ASPNETCORE_URLS\n",[3180,11132,11133,11135,11137],{"class":3178,"line":3716},[3180,11134,4592],{"class":3872},[3180,11136,3877],{"class":3876},[3180,11138,11139],{"class":4430},"\"http://+:8080\"\n",[3180,11141,11142],{"class":3178,"line":3722},[3180,11143,11051],{"class":3876},[3180,11145,11146],{"class":3178,"line":3728},[3180,11147,11148],{"class":4183},"          # Ресурси (requests та limits)\n",[3180,11150,11151,11153],{"class":3178,"line":3734},[3180,11152,4602],{"class":3872},[3180,11154,3899],{"class":3876},[3180,11156,11157,11159],{"class":3178,"line":3739},[3180,11158,4609],{"class":3872},[3180,11160,3899],{"class":3876},[3180,11162,11163,11165,11167],{"class":3178,"line":3745},[3180,11164,4616],{"class":3872},[3180,11166,3877],{"class":3876},[3180,11168,4621],{"class":4430},[3180,11170,11171,11173,11175],{"class":3178,"line":3751},[3180,11172,4626],{"class":3872},[3180,11174,3877],{"class":3876},[3180,11176,4631],{"class":4430},[3180,11178,11179,11181],{"class":3178,"line":3757},[3180,11180,4636],{"class":3872},[3180,11182,3899],{"class":3876},[3180,11184,11185,11187,11189],{"class":3178,"line":3763},[3180,11186,4616],{"class":3872},[3180,11188,3877],{"class":3876},[3180,11190,4647],{"class":4430},[3180,11192,11193,11195,11197],{"class":3178,"line":3768},[3180,11194,4626],{"class":3872},[3180,11196,3877],{"class":3876},[3180,11198,4656],{"class":4430},[3180,11200,11201],{"class":3178,"line":3773},[3180,11202,11051],{"class":3876},[3180,11204,11205],{"class":3178,"line":3779},[3180,11206,11207],{"class":4183},"          # Liveness probe - перевірка, чи застосунок живий\n",[3180,11209,11210,11213],{"class":3178,"line":3785},[3180,11211,11212],{"class":3872},"          livenessProbe",[3180,11214,3899],{"class":3876},[3180,11216,11217,11220],{"class":3178,"line":3791},[3180,11218,11219],{"class":3872},"            httpGet",[3180,11221,3899],{"class":3876},[3180,11223,11224,11227,11229],{"class":3178,"line":3797},[3180,11225,11226],{"class":3872},"              path",[3180,11228,3877],{"class":3876},[3180,11230,11231],{"class":3880},"/health\n",[3180,11233,11234,11237,11239],{"class":3178,"line":3802},[3180,11235,11236],{"class":3872},"              port",[3180,11238,3877],{"class":3876},[3180,11240,4569],{"class":3926},[3180,11242,11243,11246,11248],{"class":3178,"line":3807},[3180,11244,11245],{"class":3872},"            initialDelaySeconds",[3180,11247,3877],{"class":3876},[3180,11249,4932],{"class":3926},[3180,11251,11252,11255,11257],{"class":3178,"line":5443},[3180,11253,11254],{"class":3872},"            periodSeconds",[3180,11256,3877],{"class":3876},[3180,11258,4932],{"class":3926},[3180,11260,11261,11264,11266],{"class":3178,"line":6772},[3180,11262,11263],{"class":3872},"            timeoutSeconds",[3180,11265,3877],{"class":3876},[3180,11267,5273],{"class":3926},[3180,11269,11270,11273,11275],{"class":3178,"line":6777},[3180,11271,11272],{"class":3872},"            failureThreshold",[3180,11274,3877],{"class":3876},[3180,11276,3927],{"class":3926},[3180,11278,11279],{"class":3178,"line":6782},[3180,11280,11051],{"class":3876},[3180,11282,11283],{"class":3178,"line":6787},[3180,11284,11285],{"class":4183},"          # Readiness probe - перевірка, чи застосунок готовий приймати трафік\n",[3180,11287,11288,11291],{"class":3178,"line":6792},[3180,11289,11290],{"class":3872},"          readinessProbe",[3180,11292,3899],{"class":3876},[3180,11294,11295,11297],{"class":3178,"line":6797},[3180,11296,11219],{"class":3872},[3180,11298,3899],{"class":3876},[3180,11300,11301,11303,11305],{"class":3178,"line":6802},[3180,11302,11226],{"class":3872},[3180,11304,3877],{"class":3876},[3180,11306,11231],{"class":3880},[3180,11308,11309,11311,11313],{"class":3178,"line":6808},[3180,11310,11236],{"class":3872},[3180,11312,3877],{"class":3876},[3180,11314,4569],{"class":3926},[3180,11316,11317,11319,11321],{"class":3178,"line":6814},[3180,11318,11245],{"class":3872},[3180,11320,3877],{"class":3876},[3180,11322,5273],{"class":3926},[3180,11324,11325,11327,11329],{"class":3178,"line":6820},[3180,11326,11254],{"class":3872},[3180,11328,3877],{"class":3876},[3180,11330,5273],{"class":3926},[3180,11332,11333,11335,11337],{"class":3178,"line":6825},[3180,11334,11263],{"class":3872},[3180,11336,3877],{"class":3876},[3180,11338,3927],{"class":3926},[3180,11340,11341,11343,11345],{"class":3178,"line":6830},[3180,11342,11272],{"class":3872},[3180,11344,3877],{"class":3876},[3180,11346,3927],{"class":3926},[3114,11348,11349],{},"Розберемо нові поля детально.",[4022,11351,11352,11388,11400,11421,11453,11472],{},[4025,11353,11356,11359,11377],{"name":11354,"type":4027,"default":11355},"spec.template.spec.containers[].imagePullPolicy","IfNotPresent",[3114,11357,11358],{},"Політика завантаження образу:",[4763,11360,11361,11367,11372],{},[3137,11362,11363,11366],{},[3429,11364,11365],{},"Always"," — завжди завантажувати образ з registry (навіть якщо він є локально)",[3137,11368,11369,11371],{},[3429,11370,11355],{}," — завантажувати лише якщо образу немає локально",[3137,11373,11374,11376],{},[3429,11375,11043],{}," — ніколи не завантажувати, використовувати лише локальний образ (для Minikube)",[3114,11378,11379,11380,11382,11383,11385,11386,3121],{},"Для Minikube використовуємо ",[3429,11381,11043],{},", бо образ зібрано локально. Для production використовуйте ",[3429,11384,11365],{}," або ",[3429,11387,11355],{},[4025,11389,11391,11392,11395,11396,11399],{"name":11390,"type":4065},"spec.template.spec.containers[].resources","Ресурси CPU та пам'яті для контейнера. Має два підполя: ",[3429,11393,11394],{},"requests"," (мінімум, який гарантується) та ",[3429,11397,11398],{},"limits"," (максимум, який дозволено).",[4025,11401,11403,11410],{"name":11402,"type":4065},"spec.template.spec.containers[].resources.requests",[3114,11404,11405,11406,11409],{},"Мінімальні ресурси, які Kubernetes ",[3118,11407,11408],{},"гарантує"," контейнеру. Scheduler використовує це значення для вибору вузла — Pod буде призначено лише вузлу, який має достатньо вільних ресурсів.",[3114,11411,11412,3192,11414,11417,11418,3121],{},[3118,11413,4807],{},[3429,11415,11416],{},"memory: \"128Mi\""," означає, що Pod потребує мінімум 128 MiB пам'яті. Якщо жоден вузол не має 128 MiB вільної пам'яті, Pod залишиться у стані ",[3429,11419,11420],{},"Pending",[4025,11422,11424,11431,11445],{"name":11423,"type":4065},"spec.template.spec.containers[].resources.limits",[3114,11425,11426,11427,11430],{},"Максимальні ресурси, які контейнер ",[3118,11428,11429],{},"може використати",". Якщо контейнер спробує використати більше:",[4763,11432,11433,11439],{},[3137,11434,11435,11438],{},[3118,11436,11437],{},"CPU:"," контейнер буде throttled (обмежено) — він працюватиме повільніше",[3137,11440,11441,11444],{},[3118,11442,11443],{},"Memory:"," контейнер буде killed (OOMKilled — Out Of Memory Killed) та перезапущено",[3114,11446,11447,3192,11449,11452],{},[3118,11448,4807],{},[3429,11450,11451],{},"memory: \"256Mi\""," означає, що якщо контейнер спробує використати більше 256 MiB, Kubernetes його вб'є.",[4025,11454,11456,11467],{"name":11455,"type":4065},"spec.template.spec.containers[].livenessProbe",[3114,11457,11458,11459,11462,11463,11466],{},"Перевірка, чи застосунок ",[3118,11460,11461],{},"живий"," (не завис, не deadlock). Якщо liveness probe fails (не проходить) ",[3429,11464,11465],{},"failureThreshold"," разів підряд — Kubernetes перезапускає контейнер.",[3114,11468,11469,11471],{},[3118,11470,4862],{}," Іноді застосунок може \"зависнути\" — процес працює, але не відповідає на запити (deadlock, infinite loop). Liveness probe виявляє такі ситуації та перезапускає контейнер.",[4025,11473,11475,11484],{"name":11474,"type":4065},"spec.template.spec.containers[].readinessProbe",[3114,11476,11458,11477,11480,11481,3121],{},[3118,11478,11479],{},"готовий"," приймати трафік. Якщо readiness probe fails — Pod виключається з балансування навантаження (не отримує трафік), але ",[3118,11482,11483],{},"не перезапускається",[3114,11485,11486,11488],{},[3118,11487,4862],{}," Під час старту застосунку може знадобитись час для ініціалізації (підключення до БД, завантаження конфігурації). Readiness probe гарантує, що трафік надходить лише на повністю готові Pod.",[3126,11490,11492],{"id":11491},"різниця-між-liveness-та-readiness-probe","Різниця між Liveness та Readiness Probe",[3114,11494,11495],{},"Це важлива концепція, яку часто плутають новачки. Давайте розберемо детально:",[3482,11497,11498],{},[3485,11499,11501],{"className":3487,"code":11500,"language":3489,"meta":3490,"style":3490},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nstate \"Pod Lifecycle\" as plc {\n    state \"Container Starting\" as starting #fff3e0\n    state \"Initializing\\n(підключення до БД,\\nзавантаження конфігурації)\" as init #fff3e0\n    state \"Ready\\n(приймає трафік)\" as ready #e8f5e9\n    state \"Unhealthy\\n(тимчасова проблема)\" as unhealthy #ffebee\n    state \"Dead\\n(deadlock, crash)\" as dead #ffebee\n    state \"Restarting\" as restart #fff3e0\n    \n    [*] --> starting\n    starting --> init : Container started\n    init --> ready : Readiness probe ✓\n    ready --> unhealthy : Readiness probe ✗\n    unhealthy --> ready : Readiness probe ✓\n    ready --> dead : Liveness probe ✗\\n(3 рази підряд)\n    unhealthy --> dead : Liveness probe ✗\\n(3 рази підряд)\n    dead --> restart : Kubernetes kills container\n    restart --> starting : Container restarted\n}\n\nnote right of init\n    Readiness probe fails\n    → Pod не отримує трафік\n    → Контейнер НЕ перезапускається\nend note\n\nnote right of dead\n    Liveness probe fails\n    → Kubernetes kills container\n    → Контейнер перезапускається\nend note\n\n@enduml\n",[3429,11502,11503,11507,11511,11515,11519,11524,11529,11534,11539,11544,11549,11554,11558,11563,11568,11573,11578,11583,11588,11593,11598,11603,11607,11611,11616,11621,11626,11631,11635,11639,11644,11649,11654,11659,11663,11667],{"__ignoreMap":3490},[3180,11504,11505],{"class":3178,"line":3495},[3180,11506,3498],{},[3180,11508,11509],{"class":3178,"line":3501},[3180,11510,3504],{},[3180,11512,11513],{"class":3178,"line":3507},[3180,11514,3510],{},[3180,11516,11517],{"class":3178,"line":3513},[3180,11518,3517],{"emptyLinePlaceholder":3516},[3180,11520,11521],{"class":3178,"line":3520},[3180,11522,11523],{},"state \"Pod Lifecycle\" as plc {\n",[3180,11525,11526],{"class":3178,"line":3526},[3180,11527,11528],{},"    state \"Container Starting\" as starting #fff3e0\n",[3180,11530,11531],{"class":3178,"line":3532},[3180,11532,11533],{},"    state \"Initializing\\n(підключення до БД,\\nзавантаження конфігурації)\" as init #fff3e0\n",[3180,11535,11536],{"class":3178,"line":3538},[3180,11537,11538],{},"    state \"Ready\\n(приймає трафік)\" as ready #e8f5e9\n",[3180,11540,11541],{"class":3178,"line":3544},[3180,11542,11543],{},"    state \"Unhealthy\\n(тимчасова проблема)\" as unhealthy #ffebee\n",[3180,11545,11546],{"class":3178,"line":3549},[3180,11547,11548],{},"    state \"Dead\\n(deadlock, crash)\" as dead #ffebee\n",[3180,11550,11551],{"class":3178,"line":3555},[3180,11552,11553],{},"    state \"Restarting\" as restart #fff3e0\n",[3180,11555,11556],{"class":3178,"line":3561},[3180,11557,3582],{},[3180,11559,11560],{"class":3178,"line":3567},[3180,11561,11562],{},"    [*] --> starting\n",[3180,11564,11565],{"class":3178,"line":3573},[3180,11566,11567],{},"    starting --> init : Container started\n",[3180,11569,11570],{"class":3178,"line":3579},[3180,11571,11572],{},"    init --> ready : Readiness probe ✓\n",[3180,11574,11575],{"class":3178,"line":3585},[3180,11576,11577],{},"    ready --> unhealthy : Readiness probe ✗\n",[3180,11579,11580],{"class":3178,"line":3591},[3180,11581,11582],{},"    unhealthy --> ready : Readiness probe ✓\n",[3180,11584,11585],{"class":3178,"line":3596},[3180,11586,11587],{},"    ready --> dead : Liveness probe ✗\\n(3 рази підряд)\n",[3180,11589,11590],{"class":3178,"line":3602},[3180,11591,11592],{},"    unhealthy --> dead : Liveness probe ✗\\n(3 рази підряд)\n",[3180,11594,11595],{"class":3178,"line":3607},[3180,11596,11597],{},"    dead --> restart : Kubernetes kills container\n",[3180,11599,11600],{"class":3178,"line":3612},[3180,11601,11602],{},"    restart --> starting : Container restarted\n",[3180,11604,11605],{"class":3178,"line":3618},[3180,11606,3541],{},[3180,11608,11609],{"class":3178,"line":3624},[3180,11610,3517],{"emptyLinePlaceholder":3516},[3180,11612,11613],{"class":3178,"line":3630},[3180,11614,11615],{},"note right of init\n",[3180,11617,11618],{"class":3178,"line":3636},[3180,11619,11620],{},"    Readiness probe fails\n",[3180,11622,11623],{"class":3178,"line":3641},[3180,11624,11625],{},"    → Pod не отримує трафік\n",[3180,11627,11628],{"class":3178,"line":3646},[3180,11629,11630],{},"    → Контейнер НЕ перезапускається\n",[3180,11632,11633],{"class":3178,"line":3651},[3180,11634,3731],{},[3180,11636,11637],{"class":3178,"line":3657},[3180,11638,3517],{"emptyLinePlaceholder":3516},[3180,11640,11641],{"class":3178,"line":3663},[3180,11642,11643],{},"note right of dead\n",[3180,11645,11646],{"class":3178,"line":3669},[3180,11647,11648],{},"    Liveness probe fails\n",[3180,11650,11651],{"class":3178,"line":3675},[3180,11652,11653],{},"    → Kubernetes kills container\n",[3180,11655,11656],{"class":3178,"line":3681},[3180,11657,11658],{},"    → Контейнер перезапускається\n",[3180,11660,11661],{"class":3178,"line":3687},[3180,11662,3731],{},[3180,11664,11665],{"class":3178,"line":3692},[3180,11666,3517],{"emptyLinePlaceholder":3516},[3180,11668,11669],{"class":3178,"line":3698},[3180,11670,3810],{},[3114,11672,11673],{},[3118,11674,11675],{},"Приклад сценаріїв:",[3303,11677,11678,11709,11742],{},[3306,11679,11682,11686,11703],{"icon":11680,"title":11681},"i-heroicons-rocket-launch","Сценарій 1: Старт застосунку",[3114,11683,11684],{},[3118,11685,4218],{},[3134,11687,11688,11691,11694,11697,11700],{},[3137,11689,11690],{},"Контейнер стартує",[3137,11692,11693],{},"Застосунок підключається до БД (5 секунд)",[3137,11695,11696],{},"Readiness probe fails → Pod не отримує трафік",[3137,11698,11699],{},"Підключення встановлено",[3137,11701,11702],{},"Readiness probe ✓ → Pod починає отримувати трафік",[3114,11704,11705,11708],{},[3118,11706,11707],{},"Результат:"," Трафік надходить лише після повної готовності.",[3306,11710,11713,11717,11737],{"icon":11711,"title":11712},"i-heroicons-exclamation-triangle","Сценарій 2: Тимчасова проблема з БД",[3114,11714,11715],{},[3118,11716,4218],{},[3134,11718,11719,11722,11725,11728,11731,11734],{},[3137,11720,11721],{},"Pod працює нормально",[3137,11723,11724],{},"БД тимчасово недоступна (мережева проблема)",[3137,11726,11727],{},"Readiness probe fails → Pod виключається з балансування",[3137,11729,11730],{},"Liveness probe ✓ → контейнер НЕ перезапускається",[3137,11732,11733],{},"БД знову доступна",[3137,11735,11736],{},"Readiness probe ✓ → Pod знову отримує трафік",[3114,11738,11739,11741],{},[3118,11740,11707],{}," Трафік не надходить на проблемний Pod, але контейнер не перезапускається (бо проблема тимчасова).",[3306,11743,11745,11749,11770],{"icon":3308,"title":11744},"Сценарій 3: Deadlock у застосунку",[3114,11746,11747],{},[3118,11748,4218],{},[3134,11750,11751,11753,11756,11758,11761,11764,11767],{},[3137,11752,11721],{},[3137,11754,11755],{},"Deadlock у коді — застосунок завис",[3137,11757,11727],{},[3137,11759,11760],{},"Liveness probe fails (3 рази підряд)",[3137,11762,11763],{},"Kubernetes kills контейнер",[3137,11765,11766],{},"Контейнер перезапускається",[3137,11768,11769],{},"Після старту readiness probe ✓ → Pod знову працює",[3114,11771,11772,11774],{},[3118,11773,11707],{}," Завислий контейнер автоматично перезапущено.",[3835,11776,11777,11781,11784,11888,11897,11909],{},[3114,11778,11779],{},[3118,11780,4255],{},[3114,11782,11783],{},"Використання однакових налаштувань для liveness та readiness probe:",[3485,11785,11787],{"className":3863,"code":11786,"language":3865,"meta":3490,"style":3490},"livenessProbe:\n  httpGet:\n    path: /health\n    port: 8080\n  initialDelaySeconds: 5\n  periodSeconds: 5\n\nreadinessProbe:\n  httpGet:\n    path: /health\n    port: 8080\n  initialDelaySeconds: 5\n  periodSeconds: 5\n",[3429,11788,11789,11796,11803,11812,11821,11830,11839,11843,11850,11856,11864,11872,11880],{"__ignoreMap":3490},[3180,11790,11791,11794],{"class":3178,"line":3495},[3180,11792,11793],{"class":3872},"livenessProbe",[3180,11795,3899],{"class":3876},[3180,11797,11798,11801],{"class":3178,"line":3501},[3180,11799,11800],{"class":3872},"  httpGet",[3180,11802,3899],{"class":3876},[3180,11804,11805,11808,11810],{"class":3178,"line":3507},[3180,11806,11807],{"class":3872},"    path",[3180,11809,3877],{"class":3876},[3180,11811,11231],{"class":3880},[3180,11813,11814,11817,11819],{"class":3178,"line":3513},[3180,11815,11816],{"class":3872},"    port",[3180,11818,3877],{"class":3876},[3180,11820,4569],{"class":3926},[3180,11822,11823,11826,11828],{"class":3178,"line":3520},[3180,11824,11825],{"class":3872},"  initialDelaySeconds",[3180,11827,3877],{"class":3876},[3180,11829,5273],{"class":3926},[3180,11831,11832,11835,11837],{"class":3178,"line":3526},[3180,11833,11834],{"class":3872},"  periodSeconds",[3180,11836,3877],{"class":3876},[3180,11838,5273],{"class":3926},[3180,11840,11841],{"class":3178,"line":3532},[3180,11842,3517],{"emptyLinePlaceholder":3516},[3180,11844,11845,11848],{"class":3178,"line":3538},[3180,11846,11847],{"class":3872},"readinessProbe",[3180,11849,3899],{"class":3876},[3180,11851,11852,11854],{"class":3178,"line":3544},[3180,11853,11800],{"class":3872},[3180,11855,3899],{"class":3876},[3180,11857,11858,11860,11862],{"class":3178,"line":3549},[3180,11859,11807],{"class":3872},[3180,11861,3877],{"class":3876},[3180,11863,11231],{"class":3880},[3180,11865,11866,11868,11870],{"class":3178,"line":3555},[3180,11867,11816],{"class":3872},[3180,11869,3877],{"class":3876},[3180,11871,4569],{"class":3926},[3180,11873,11874,11876,11878],{"class":3178,"line":3561},[3180,11875,11825],{"class":3872},[3180,11877,3877],{"class":3876},[3180,11879,5273],{"class":3926},[3180,11881,11882,11884,11886],{"class":3178,"line":3567},[3180,11883,11834],{"class":3872},[3180,11885,3877],{"class":3876},[3180,11887,5273],{"class":3926},[3114,11889,11890,11893,11894,3121],{},[3118,11891,11892],{},"Проблема:"," Якщо застосунок стартує довго (наприклад, 30 секунд), liveness probe почне fails ще до завершення старту та вб'є контейнер. Контейнер перезапуститься, знову не встигне стартувати, знову буде вбитий — ",[3118,11895,11896],{},"crash loop",[3114,11898,11899,11902,11903,11906,11907,3121],{},[3118,11900,11901],{},"Рішення:"," Liveness probe має більший ",[3429,11904,11905],{},"initialDelaySeconds",", ніж час старту застосунку. Readiness probe може мати менший ",[3429,11908,11905],{},[3485,11910,11912],{"className":3863,"code":11911,"language":3865,"meta":3490,"style":3490},"livenessProbe:\n  initialDelaySeconds: 60  # Достатньо часу для старту\n  periodSeconds: 10\n\nreadinessProbe:\n  initialDelaySeconds: 10  # Швидше виявляє готовність\n  periodSeconds: 5\n",[3429,11913,11914,11920,11932,11940,11944,11950,11961],{"__ignoreMap":3490},[3180,11915,11916,11918],{"class":3178,"line":3495},[3180,11917,11793],{"class":3872},[3180,11919,3899],{"class":3876},[3180,11921,11922,11924,11926,11929],{"class":3178,"line":3501},[3180,11923,11825],{"class":3872},[3180,11925,3877],{"class":3876},[3180,11927,11928],{"class":3926},"60",[3180,11930,11931],{"class":4183},"  # Достатньо часу для старту\n",[3180,11933,11934,11936,11938],{"class":3178,"line":3507},[3180,11935,11834],{"class":3872},[3180,11937,3877],{"class":3876},[3180,11939,4932],{"class":3926},[3180,11941,11942],{"class":3178,"line":3513},[3180,11943,3517],{"emptyLinePlaceholder":3516},[3180,11945,11946,11948],{"class":3178,"line":3520},[3180,11947,11847],{"class":3872},[3180,11949,3899],{"class":3876},[3180,11951,11952,11954,11956,11958],{"class":3178,"line":3526},[3180,11953,11825],{"class":3872},[3180,11955,3877],{"class":3876},[3180,11957,4969],{"class":3926},[3180,11959,11960],{"class":4183},"  # Швидше виявляє готовність\n",[3180,11962,11963,11965,11967],{"class":3178,"line":3532},[3180,11964,11834],{"class":3872},[3180,11966,3877],{"class":3876},[3180,11968,5273],{"class":3926},[3126,11970,11972],{"id":11971},"крок-5-розгортання-у-kubernetes","Крок 5: Розгортання у Kubernetes",[3114,11974,11975],{},"Тепер застосуємо маніфест:",[3171,11977,11978,11987],{"title":5657},[3175,11979,11981,3192,11984],{"className":11980},[3178],[3180,11982,3191],{"className":11983},[3183],[3118,11985,11986],{},"kubectl apply -f k8s/deployment.yaml",[3175,11988,11990],{"className":11989},[3178],[3180,11991,11993],{"className":11992},[5673],"deployment.apps/todoapi created",[3114,11995,11996],{},"Перевіримо стан Deployment:",[3171,11998,11999,12007,12011],{"title":5684},[3175,12000,12002,3192,12005],{"className":12001},[3178],[3180,12003,3191],{"className":12004},[3183],[3118,12006,5684],{},[3175,12008,12010],{"className":12009},[3178],"NAME      READY   UP-TO-DATE   AVAILABLE   AGE",[3175,12012,12014],{"className":12013},[3178],"todoapi   0/3     3            0           5s",[3114,12016,12017,12018,12021],{},"Бачимо ",[3429,12019,12020],{},"0/3"," — жоден Pod ще не готовий. Це нормально — Pod стартують. Почекаємо кілька секунд:",[3171,12023,12025,12033,12036],{"title":12024},"kubectl get deployments (через 15 секунд)",[3175,12026,12028,3192,12031],{"className":12027},[3178],[3180,12029,3191],{"className":12030},[3183],[3118,12032,5684],{},[3175,12034,12010],{"className":12035},[3178],[3175,12037,12039],{"className":12038},[3178],"todoapi   3/3     3            3           20s",[3114,12041,12042],{},"Тепер усі 3 репліки готові! Переглянемо Pod:",[3171,12044,12045,12054,12058,12062,12066],{"title":5759},[3175,12046,12048,3192,12051],{"className":12047},[3178],[3180,12049,3191],{"className":12050},[3183],[3118,12052,12053],{},"kubectl get pods -l app=todoapi",[3175,12055,12057],{"className":12056},[3178],"NAME                       READY   STATUS    RESTARTS   AGE",[3175,12059,12061],{"className":12060},[3178],"todoapi-7d6b8c9f4d-x2n9k   1/1     Running   0          25s",[3175,12063,12065],{"className":12064},[3178],"todoapi-7d6b8c9f4d-q7m4p   1/1     Running   0          25s",[3175,12067,12069],{"className":12068},[3178],"todoapi-7d6b8c9f4d-k5t8w   1/1     Running   0          25s",[3406,12071,12072,12080,12087],{},[3114,12073,12074],{},[3118,12075,12076,12077,3480],{},"Прапорець ",[3429,12078,12079],{},"-l app=todoapi",[3114,12081,12082,12083,12086],{},"Це label selector — фільтрує Pod за міткою ",[3429,12084,12085],{},"app=todoapi",". Без нього ви побачите всі Pod у namespace, включно з Pod інших застосунків.",[3114,12088,12089,12090,3121],{},"Це той самий селектор, який використовує Deployment у ",[3429,12091,4085],{},[3114,12093,12094],{},"Переглянемо детальну інформацію про один Pod:",[3171,12096,12098,12107,12111,12115,12119,12123,12127,12131,12135,12139,12143,12147,12151,12155,12159,12163,12167,12171,12175,12179,12183,12187,12191,12195,12199,12203,12207,12211,12215,12219,12223,12227,12230,12234,12238,12242,12246,12250],{"title":12097},"kubectl describe pod",[3175,12099,12101,3192,12104],{"className":12100},[3178],[3180,12102,3191],{"className":12103},[3183],[3118,12105,12106],{},"kubectl describe pod todoapi-7d6b8c9f4d-x2n9k",[3175,12108,12110],{"className":12109},[3178],"Name:             todoapi-7d6b8c9f4d-x2n9k",[3175,12112,12114],{"className":12113},[3178],"Namespace:        default",[3175,12116,12118],{"className":12117},[3178],"Priority:         0",[3175,12120,12122],{"className":12121},[3178],"Service Account:  default",[3175,12124,12126],{"className":12125},[3178],"Node:             minikube/192.168.49.2",[3175,12128,12130],{"className":12129},[3178],"Start Time:       Fri, 09 May 2026 20:35:00 +0000",[3175,12132,12134],{"className":12133},[3178],"Labels:           app=todoapi",[3175,12136,12138],{"className":12137},[3178],"                  version=1.0.0",[3175,12140,12142],{"className":12141},[3178],"Status:           Running",[3175,12144,12146],{"className":12145},[3178],"IP:               10.244.0.15",[3175,12148,12150],{"className":12149},[3178],"Controlled By:    ReplicaSet/todoapi-7d6b8c9f4d",[3175,12152,12154],{"className":12153},[3178],"Containers:",[3175,12156,12158],{"className":12157},[3178],"  todoapi:",[3175,12160,12162],{"className":12161},[3178],"    Image:          todoapi:1.0.0",[3175,12164,12166],{"className":12165},[3178],"    Port:           8080/TCP",[3175,12168,12170],{"className":12169},[3178],"    State:          Running",[3175,12172,12174],{"className":12173},[3178],"      Started:      Fri, 09 May 2026 20:35:05 +0000",[3175,12176,12178],{"className":12177},[3178],"    Ready:          True",[3175,12180,12182],{"className":12181},[3178],"    Restart Count:  0",[3175,12184,12186],{"className":12185},[3178],"    Limits:",[3175,12188,12190],{"className":12189},[3178],"      cpu:     500m",[3175,12192,12194],{"className":12193},[3178],"      memory:  256Mi",[3175,12196,12198],{"className":12197},[3178],"    Requests:",[3175,12200,12202],{"className":12201},[3178],"      cpu:        100m",[3175,12204,12206],{"className":12205},[3178],"      memory:     128Mi",[3175,12208,12210],{"className":12209},[3178],"    Liveness:     http-get http://:8080/health delay=10s timeout=5s period=10s",[3175,12212,12214],{"className":12213},[3178],"    Readiness:    http-get http://:8080/health delay=5s timeout=3s period=5s",[3175,12216,12218],{"className":12217},[3178],"    Environment:",[3175,12220,12222],{"className":12221},[3178],"      ASPNETCORE_ENVIRONMENT:  Production",[3175,12224,12226],{"className":12225},[3178],"      ASPNETCORE_URLS:         http://+:8080",[3175,12228,5920],{"className":12229},[3178],[3175,12231,12233],{"className":12232},[3178],"  Type    Reason     Age   Message",[3175,12235,12237],{"className":12236},[3178],"  ----    ------     ----  -------",[3175,12239,12241],{"className":12240},[3178],"  Normal  Scheduled  30s   Successfully assigned default/todoapi-7d6b8c9f4d-x2n9k to minikube",[3175,12243,12245],{"className":12244},[3178],"  Normal  Pulled     28s   Container image \"todoapi:1.0.0\" already present on machine",[3175,12247,12249],{"className":12248},[3178],"  Normal  Created    28s   Created container todoapi",[3175,12251,12253],{"className":12252},[3178],"  Normal  Started    27s   Started container todoapi",[3114,12255,12256],{},"Тут ви бачите всю інформацію про Pod: образ, порти, ресурси, probes, змінні оточення та події.",[3126,12258,12260],{"id":12259},"крок-6-тестування-api-через-port-forward","Крок 6: Тестування API через port-forward",[3114,12262,12263,12264,12267],{},"Щоб протестувати API, використаємо ",[3429,12265,12266],{},"kubectl port-forward"," для перенаправлення трафіку з локальної машини на Pod:",[3171,12269,12270,12279,12283],{"title":12266},[3175,12271,12273,3192,12276],{"className":12272},[3178],[3180,12274,3191],{"className":12275},[3183],[3118,12277,12278],{},"kubectl port-forward deployment/todoapi 8080:8080",[3175,12280,12282],{"className":12281},[3178],"Forwarding from 127.0.0.1:8080 -> 8080",[3175,12284,12286],{"className":12285},[3178],"Forwarding from [::1]:8080 -> 8080",[3114,12288,12289,12290,12293],{},"Тепер API доступний на ",[3429,12291,12292],{},"http://localhost:8080",". Відкрийте новий термінал та протестуйте:",[3171,12295,12297,12304,12313,12317,12320,12327,12336,12343,12349,12352,12356,12360,12364,12368,12372,12376,12380,12383,12386,12393,12401,12406,12412,12415,12418,12422,12426,12429,12433,12436,12440,12443,12446,12453,12462,12465,12469,12473,12477,12481,12485,12489,12493,12496,12500,12504,12507,12511,12515,12519,12523,12527],{"title":12296},"Тестування API",[3175,12298,12300],{"className":12299},[3178],[3180,12301,12303],{"className":12302},[3183],"# Перевірка health endpoint",[3175,12305,12307,3192,12310],{"className":12306},[3178],[3180,12308,3191],{"className":12309},[3183],[3118,12311,12312],{},"curl http://localhost:8080/health",[3175,12314,12316],{"className":12315},[3178],"{\"status\":\"healthy\",\"timestamp\":\"2026-05-09T20:36:15.123Z\"}",[3175,12318],{"className":12319},[3178],[3175,12321,12323],{"className":12322},[3178],[3180,12324,12326],{"className":12325},[3183],"# Створення першого todo",[3175,12328,12330,3192,12333],{"className":12329},[3178],[3180,12331,3191],{"className":12332},[3183],[3118,12334,12335],{},"curl -X POST http://localhost:8080/todos \\",[3175,12337,12339,12340],{"className":12338},[3178],"  ",[3118,12341,12342],{},"-H \"Content-Type: application/json\" \\",[3175,12344,12339,12346],{"className":12345},[3178],[3118,12347,12348],{},"-d '{\"title\":\"Вивчити Kubernetes Deployment\"}'",[3175,12350,9510],{"className":12351},[3178],[3175,12353,12355],{"className":12354},[3178],"  \"todo\": {",[3175,12357,12359],{"className":12358},[3178],"    \"id\": 1,",[3175,12361,12363],{"className":12362},[3178],"    \"title\": \"Вивчити Kubernetes Deployment\",",[3175,12365,12367],{"className":12366},[3178],"    \"isCompleted\": false,",[3175,12369,12371],{"className":12370},[3178],"    \"createdAt\": \"2026-05-09T20:36:20.456Z\"",[3175,12373,12375],{"className":12374},[3178],"  },",[3175,12377,12379],{"className":12378},[3178],"  \"servedBy\": \"todoapi-7d6b8c9f4d-x2n9k\"",[3175,12381,9515],{"className":12382},[3178],[3175,12384],{"className":12385},[3178],[3175,12387,12389],{"className":12388},[3178],[3180,12390,12392],{"className":12391},[3183],"# Створення другого todo",[3175,12394,12396,3192,12399],{"className":12395},[3178],[3180,12397,3191],{"className":12398},[3183],[3118,12400,12335],{},[3175,12402,12339,12404],{"className":12403},[3178],[3118,12405,12342],{},[3175,12407,12339,12409],{"className":12408},[3178],[3118,12410,12411],{},"-d '{\"title\":\"Протестувати масштабування\"}'",[3175,12413,9510],{"className":12414},[3178],[3175,12416,12355],{"className":12417},[3178],[3175,12419,12421],{"className":12420},[3178],"    \"id\": 2,",[3175,12423,12425],{"className":12424},[3178],"    \"title\": \"Протестувати масштабування\",",[3175,12427,12367],{"className":12428},[3178],[3175,12430,12432],{"className":12431},[3178],"    \"createdAt\": \"2026-05-09T20:36:25.789Z\"",[3175,12434,12375],{"className":12435},[3178],[3175,12437,12439],{"className":12438},[3178],"  \"servedBy\": \"todoapi-7d6b8c9f4d-q7m4p\"",[3175,12441,9515],{"className":12442},[3178],[3175,12444],{"className":12445},[3178],[3175,12447,12449],{"className":12448},[3178],[3180,12450,12452],{"className":12451},[3183],"# Отримання всіх todos",[3175,12454,12456,3192,12459],{"className":12455},[3178],[3180,12457,3191],{"className":12458},[3183],[3118,12460,12461],{},"curl http://localhost:8080/todos",[3175,12463,9510],{"className":12464},[3178],[3175,12466,12468],{"className":12467},[3178],"  \"todos\": [",[3175,12470,12472],{"className":12471},[3178],"    {",[3175,12474,12476],{"className":12475},[3178],"      \"id\": 1,",[3175,12478,12480],{"className":12479},[3178],"      \"title\": \"Вивчити Kubernetes Deployment\",",[3175,12482,12484],{"className":12483},[3178],"      \"isCompleted\": false,",[3175,12486,12488],{"className":12487},[3178],"      \"createdAt\": \"2026-05-09T20:36:20.456Z\"",[3175,12490,12492],{"className":12491},[3178],"    },",[3175,12494,12472],{"className":12495},[3178],[3175,12497,12499],{"className":12498},[3178],"      \"id\": 2,",[3175,12501,12503],{"className":12502},[3178],"      \"title\": \"Протестувати масштабування\",",[3175,12505,12484],{"className":12506},[3178],[3175,12508,12510],{"className":12509},[3178],"      \"createdAt\": \"2026-05-09T20:36:25.789Z\"",[3175,12512,12514],{"className":12513},[3178],"    }",[3175,12516,12518],{"className":12517},[3178],"  ],",[3175,12520,12522],{"className":12521},[3178],"  \"servedBy\": \"todoapi-7d6b8c9f4d-k5t8w\",",[3175,12524,12526],{"className":12525},[3178],"  \"count\": 2",[3175,12528,9515],{"className":12529},[3178],[3114,12531,12532,12533,12535,12536,12538],{},"Зверніть увагу на поле ",[3429,12534,9238],{}," — кожен запит обробляється різним Pod! Це демонструє, що ",[3429,12537,12266],{}," автоматично балансує навантаження між репліками.",[3835,12540,12541,12546,12557,12562,12565,12570,12628],{},[3114,12542,12543],{},[3118,12544,12545],{},"Важливо про in-memory сховище:",[3114,12547,12548,12549,12552,12553,12556],{},"Наш TodoApi використовує ",[3429,12550,12551],{},"ConcurrentDictionary"," для зберігання даних у пам'яті. Це означає, що ",[3118,12554,12555],{},"кожен Pod має власне сховище",". Якщо ви створите todo через Pod 1, а потім запитаєте список через Pod 2 — ви не побачите цей todo.",[3114,12558,12559],{},[3118,12560,12561],{},"Чому так?",[3114,12563,12564],{},"Кожен Pod — це окремий процес з власною пам'яттю. Вони не діляться даними між собою. У реальному застосунку ви б використовували зовнішню базу даних (PostgreSQL, MongoDB), до якої підключаються всі Pod.",[3114,12566,12567],{},[3118,12568,12569],{},"Демонстрація проблеми:",[3485,12571,12575],{"className":12572,"code":12573,"language":12574,"meta":3490,"style":3490},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# Створюємо todo (обробляє Pod 1)\ncurl -X POST http://localhost:8080/todos -d '{\"title\":\"Test\"}'\n# Відповідь: servedBy: todoapi-xxx-pod1\n\n# Запитуємо список (обробляє Pod 2)\ncurl http://localhost:8080/todos\n# Відповідь: servedBy: todoapi-xxx-pod2, todos: [] (порожньо!)\n","bash",[3429,12576,12577,12582,12602,12607,12611,12616,12623],{"__ignoreMap":3490},[3180,12578,12579],{"class":3178,"line":3495},[3180,12580,12581],{"class":4183},"# Створюємо todo (обробляє Pod 1)\n",[3180,12583,12584,12587,12590,12593,12596,12599],{"class":3178,"line":3501},[3180,12585,12586],{"class":8669},"curl",[3180,12588,12589],{"class":4962}," -X",[3180,12591,12592],{"class":4430}," POST",[3180,12594,12595],{"class":4430}," http://localhost:8080/todos",[3180,12597,12598],{"class":4962}," -d",[3180,12600,12601],{"class":4430}," '{\"title\":\"Test\"}'\n",[3180,12603,12604],{"class":3178,"line":3507},[3180,12605,12606],{"class":4183},"# Відповідь: servedBy: todoapi-xxx-pod1\n",[3180,12608,12609],{"class":3178,"line":3513},[3180,12610,3517],{"emptyLinePlaceholder":3516},[3180,12612,12613],{"class":3178,"line":3520},[3180,12614,12615],{"class":4183},"# Запитуємо список (обробляє Pod 2)\n",[3180,12617,12618,12620],{"class":3178,"line":3526},[3180,12619,12586],{"class":8669},[3180,12621,12622],{"class":4430}," http://localhost:8080/todos\n",[3180,12624,12625],{"class":3178,"line":3532},[3180,12626,12627],{"class":4183},"# Відповідь: servedBy: todoapi-xxx-pod2, todos: [] (порожньо!)\n",[3114,12629,12630],{},"Це нормально для демонстрації. У наступних статтях ми додамо PostgreSQL та побачимо, як Pod діляться даними через зовнішню БД.",[3126,12632,12634],{"id":12633},"крок-7-тестування-self-healing","Крок 7: Тестування self-healing",[3114,12636,12637],{},"Тепер продемонструємо self-healing — видалимо один Pod та подивимося, як Deployment автоматично створить новий:",[3171,12639,12641,12650],{"title":12640},"Видалення Pod",[3175,12642,12644,3192,12647],{"className":12643},[3178],[3180,12645,3191],{"className":12646},[3183],[3118,12648,12649],{},"kubectl delete pod todoapi-7d6b8c9f4d-x2n9k",[3175,12651,12653],{"className":12652},[3178],"pod \"todoapi-7d6b8c9f4d-x2n9k\" deleted",[3114,12655,6367],{},[3171,12657,12658,12666,12670,12674,12678],{"title":5759},[3175,12659,12661,3192,12664],{"className":12660},[3178],[3180,12662,3191],{"className":12663},[3183],[3118,12665,12053],{},[3175,12667,12669],{"className":12668},[3178],"NAME                       READY   STATUS              RESTARTS   AGE",[3175,12671,12673],{"className":12672},[3178],"todoapi-7d6b8c9f4d-q7m4p   1/1     Running             0          5m",[3175,12675,12677],{"className":12676},[3178],"todoapi-7d6b8c9f4d-k5t8w   1/1     Running             0          5m",[3175,12679,12681],{"className":12680},[3178],"todoapi-7d6b8c9f4d-n8p2r   0/1     ContainerCreating   0          3s",[3114,12683,12684,12685,6400,12688,12690],{},"Бачимо новий Pod ",[3429,12686,12687],{},"n8p2r",[3429,12689,6403],{},". Через кілька секунд:",[3171,12692,12693,12701,12704,12708,12712],{"title":6434},[3175,12694,12696,3192,12699],{"className":12695},[3178],[3180,12697,3191],{"className":12698},[3183],[3118,12700,12053],{},[3175,12702,12057],{"className":12703},[3178],[3175,12705,12707],{"className":12706},[3178],"todoapi-7d6b8c9f4d-q7m4p   1/1     Running   0          5m15s",[3175,12709,12711],{"className":12710},[3178],"todoapi-7d6b8c9f4d-k5t8w   1/1     Running   0          5m15s",[3175,12713,12715],{"className":12714},[3178],"todoapi-7d6b8c9f4d-n8p2r   1/1     Running   0          15s",[3114,12717,12718],{},"Знову три репліки! Deployment автоматично відновив бажаний стан.",[3126,12720,12722],{"id":12721},"крок-8-тестування-масштабування","Крок 8: Тестування масштабування",[3114,12724,12725],{},"Тепер збільшимо кількість реплік з 3 до 5:",[3171,12727,12728,12737],{"title":3450},[3175,12729,12731,3192,12734],{"className":12730},[3178],[3180,12732,3191],{"className":12733},[3183],[3118,12735,12736],{},"kubectl scale deployment todoapi --replicas=5",[3175,12738,12740],{"className":12739},[3178],[3180,12741,12743],{"className":12742},[5673],"deployment.apps/todoapi scaled",[3171,12745,12746,12754,12757,12761,12765,12769,12773],{"title":5759},[3175,12747,12749,3192,12752],{"className":12748},[3178],[3180,12750,3191],{"className":12751},[3183],[3118,12753,12053],{},[3175,12755,12669],{"className":12756},[3178],[3175,12758,12760],{"className":12759},[3178],"todoapi-7d6b8c9f4d-q7m4p   1/1     Running             0          6m",[3175,12762,12764],{"className":12763},[3178],"todoapi-7d6b8c9f4d-k5t8w   1/1     Running             0          6m",[3175,12766,12768],{"className":12767},[3178],"todoapi-7d6b8c9f4d-n8p2r   1/1     Running             0          1m",[3175,12770,12772],{"className":12771},[3178],"todoapi-7d6b8c9f4d-m7q3s   0/1     ContainerCreating   0          2s",[3175,12774,12776],{"className":12775},[3178],"todoapi-7d6b8c9f4d-p9k4t   0/1     ContainerCreating   0          2s",[3114,12778,12779],{},"Два нові Pod створюються. Через 10-15 секунд:",[3171,12781,12783,12791,12794,12798,12802,12806,12810],{"title":12782},"kubectl get pods (після масштабування)",[3175,12784,12786,3192,12789],{"className":12785},[3178],[3180,12787,3191],{"className":12788},[3183],[3118,12790,12053],{},[3175,12792,12057],{"className":12793},[3178],[3175,12795,12797],{"className":12796},[3178],"todoapi-7d6b8c9f4d-q7m4p   1/1     Running   0          6m20s",[3175,12799,12801],{"className":12800},[3178],"todoapi-7d6b8c9f4d-k5t8w   1/1     Running   0          6m20s",[3175,12803,12805],{"className":12804},[3178],"todoapi-7d6b8c9f4d-n8p2r   1/1     Running   0          1m20s",[3175,12807,12809],{"className":12808},[3178],"todoapi-7d6b8c9f4d-m7q3s   1/1     Running   0          20s",[3175,12811,12813],{"className":12812},[3178],"todoapi-7d6b8c9f4d-p9k4t   1/1     Running   0          20s",[3114,12815,12816],{},"Тепер працює 5 реплік! Зменшимо назад до 3:",[3171,12818,12819,12828],{"title":7057},[3175,12820,12822,3192,12825],{"className":12821},[3178],[3180,12823,3191],{"className":12824},[3183],[3118,12826,12827],{},"kubectl scale deployment todoapi --replicas=3",[3175,12829,12831],{"className":12830},[3178],[3180,12832,12743],{"className":12833},[5673],[3171,12835,12836,12844,12848,12852,12856,12860,12864],{"title":5759},[3175,12837,12839,3192,12842],{"className":12838},[3178],[3180,12840,3191],{"className":12841},[3183],[3118,12843,12053],{},[3175,12845,12847],{"className":12846},[3178],"NAME                       READY   STATUS        RESTARTS   AGE",[3175,12849,12851],{"className":12850},[3178],"todoapi-7d6b8c9f4d-q7m4p   1/1     Running       0          7m",[3175,12853,12855],{"className":12854},[3178],"todoapi-7d6b8c9f4d-k5t8w   1/1     Running       0          7m",[3175,12857,12859],{"className":12858},[3178],"todoapi-7d6b8c9f4d-n8p2r   1/1     Running       0          2m",[3175,12861,12863],{"className":12862},[3178],"todoapi-7d6b8c9f4d-m7q3s   1/1     Terminating   0          1m",[3175,12865,12867],{"className":12866},[3178],"todoapi-7d6b8c9f4d-p9k4t   1/1     Terminating   0          1m",[3114,12869,12870,12871,12873],{},"Два Pod видаляються (статус ",[3429,12872,7112],{},"). Kubernetes вибрав найновіші Pod для видалення.",[3126,12875,12877],{"id":12876},"крок-9-моніторинг-ресурсів","Крок 9: Моніторинг ресурсів",[3114,12879,12880],{},"Переглянемо, скільки ресурсів використовують Pod:",[3171,12882,12884,12893,12897,12901,12905],{"title":12883},"kubectl top pods",[3175,12885,12887,3192,12890],{"className":12886},[3178],[3180,12888,3191],{"className":12889},[3183],[3118,12891,12892],{},"kubectl top pods -l app=todoapi",[3175,12894,12896],{"className":12895},[3178],"NAME                       CPU(cores)   MEMORY(bytes)",[3175,12898,12900],{"className":12899},[3178],"todoapi-7d6b8c9f4d-q7m4p   2m           45Mi",[3175,12902,12904],{"className":12903},[3178],"todoapi-7d6b8c9f4d-k5t8w   1m           43Mi",[3175,12906,12908],{"className":12907},[3178],"todoapi-7d6b8c9f4d-n8p2r   2m           44Mi",[3406,12910,12911,12916,12922,12941],{},[3114,12912,12913],{},[3118,12914,12915],{},"Якщо команда не працює:",[3114,12917,12918,12921],{},[3429,12919,12920],{},"kubectl top"," вимагає Metrics Server. У Minikube його можна увімкнути:",[3485,12923,12925],{"className":12572,"code":12924,"language":12574,"meta":3490,"style":3490},"minikube addons enable metrics-server\n",[3429,12926,12927],{"__ignoreMap":3490},[3180,12928,12929,12932,12935,12938],{"class":3178,"line":3495},[3180,12930,12931],{"class":8669},"minikube",[3180,12933,12934],{"class":4430}," addons",[3180,12936,12937],{"class":4430}," enable",[3180,12939,12940],{"class":4430}," metrics-server\n",[3114,12942,12943],{},"Почекайте 1-2 хвилини, поки Metrics Server зібре дані, потім спробуйте знову.",[3114,12945,12946],{},"Бачимо, що кожен Pod використовує ~2m CPU (0.002 cores) та ~45 MiB пам'яті. Це значно менше за наші limits (500m CPU, 256Mi memory), тому Pod працюють без обмежень.",[3126,12948,12950],{"id":12949},"крок-10-перегляд-логів","Крок 10: Перегляд логів",[3114,12952,12953],{},"Переглянемо логи одного з Pod:",[3171,12955,12957,12966,12970,12974,12978,12982,12985,12989,12992],{"title":12956},"kubectl logs",[3175,12958,12960,3192,12963],{"className":12959},[3178],[3180,12961,3191],{"className":12962},[3183],[3118,12964,12965],{},"kubectl logs todoapi-7d6b8c9f4d-q7m4p",[3175,12967,12969],{"className":12968},[3178],"info: Microsoft.Hosting.Lifetime[14]",[3175,12971,12973],{"className":12972},[3178],"      Now listening on: http://[::]:8080",[3175,12975,12977],{"className":12976},[3178],"info: Microsoft.Hosting.Lifetime[0]",[3175,12979,12981],{"className":12980},[3178],"      Application started. Press Ctrl+C to shut down.",[3175,12983,12977],{"className":12984},[3178],[3175,12986,12988],{"className":12987},[3178],"      Hosting environment: Production",[3175,12990,12977],{"className":12991},[3178],[3175,12993,12995],{"className":12994},[3178],"      Content root path: /app",[3114,12997,12998,12999,13002],{},"Якщо хочете стежити за логами в реальному часі (як ",[3429,13000,13001],{},"tail -f","):",[3171,13004,13006,13015,13018,13021],{"title":13005},"kubectl logs -f",[3175,13007,13009,3192,13012],{"className":13008},[3178],[3180,13010,3191],{"className":13011},[3183],[3118,13013,13014],{},"kubectl logs -f todoapi-7d6b8c9f4d-q7m4p",[3175,13016,12977],{"className":13017},[3178],[3175,13019,12981],{"className":13020},[3178],[3175,13022,13024],{"className":13023},[3178],[3180,13025,13027],{"className":13026},[3183],"# Тут з'являтимуться нові логи в реальному часі",[3114,13029,13030],{},"Щоб переглянути логи всіх Pod одночасно:",[3171,13032,13034,13043,13051,13054,13057,13060,13067,13070,13073,13076,13083,13086],{"title":13033},"kubectl logs (всі Pod)",[3175,13035,13037,3192,13040],{"className":13036},[3178],[3180,13038,3191],{"className":13039},[3183],[3118,13041,13042],{},"kubectl logs -l app=todoapi --tail=10",[3175,13044,13046],{"className":13045},[3178],[3180,13047,13050],{"className":13048},[13049],"text-blue-400","todoapi-7d6b8c9f4d-q7m4p:",[3175,13052,12977],{"className":13053},[3178],[3175,13055,12981],{"className":13056},[3178],[3175,13058],{"className":13059},[3178],[3175,13061,13063],{"className":13062},[3178],[3180,13064,13066],{"className":13065},[13049],"todoapi-7d6b8c9f4d-k5t8w:",[3175,13068,12977],{"className":13069},[3178],[3175,13071,12981],{"className":13072},[3178],[3175,13074],{"className":13075},[3178],[3175,13077,13079],{"className":13078},[3178],[3180,13080,13082],{"className":13081},[13049],"todoapi-7d6b8c9f4d-n8p2r:",[3175,13084,12977],{"className":13085},[3178],[3175,13087,12981],{"className":13088},[3178],[3114,13090,12076,13091,13094],{},[3429,13092,13093],{},"--tail=10"," показує лише останні 10 рядків з кожного Pod.",[3382,13096],{},[3109,13098,13100],{"id":13099},"очищення-ресурсів","Очищення ресурсів",[3114,13102,13103],{},"Після експериментів видалимо Deployment:",[3171,13105,13107,13116],{"title":13106},"kubectl delete",[3175,13108,13110,3192,13113],{"className":13109},[3178],[3180,13111,3191],{"className":13112},[3183],[3118,13114,13115],{},"kubectl delete deployment todoapi",[3175,13117,13119],{"className":13118},[3178],[3180,13120,13122],{"className":13121},[5673],"deployment.apps \"todoapi\" deleted",[3114,13124,13125],{},"Це автоматично видалить:",[4763,13127,13128,13130,13133],{},[3137,13129,3336],{},[3137,13131,13132],{},"ReplicaSet (створений Deployment)",[3137,13134,13135],{},"Всі Pod (створені ReplicaSet)",[3114,13137,13138],{},"Перевіримо:",[3171,13140,13142,13151],{"title":13141},"kubectl get all",[3175,13143,13145,3192,13148],{"className":13144},[3178],[3180,13146,3191],{"className":13147},[3183],[3118,13149,13150],{},"kubectl get all -l app=todoapi",[3175,13152,13154],{"className":13153},[3178],"No resources found in default namespace.",[3114,13156,13157],{},"Все видалено!",[4865,13159,13160,13165,13172,13190],{},[3114,13161,13162],{},[3118,13163,13164],{},"Альтернативний спосіб — видалення через файл:",[3114,13166,13167,13168,13171],{},"Якщо ви створювали ресурси через ",[3429,13169,13170],{},"kubectl apply -f deployment.yaml",", можете видалити їх тим самим файлом:",[3485,13173,13175],{"className":12572,"code":13174,"language":12574,"meta":3490,"style":3490},"kubectl delete -f k8s/deployment.yaml\n",[3429,13176,13177],{"__ignoreMap":3490},[3180,13178,13179,13181,13184,13187],{"class":3178,"line":3495},[3180,13180,4054],{"class":8669},[3180,13182,13183],{"class":4430}," delete",[3180,13185,13186],{"class":4962}," -f",[3180,13188,13189],{"class":4430}," k8s/deployment.yaml\n",[3114,13191,13192],{},"Це видалить всі ресурси, описані у файлі. Зручно, якщо у файлі кілька ресурсів (Deployment, Service, ConfigMap тощо).",[3382,13194],{},[3109,13196,13198],{"id":13197},"практичні-завдання","Практичні завдання",[3114,13200,13201],{},"Тепер, коли ви розумієте основи Deployment, виконайте наступні завдання для закріплення знань:",[3126,13203,13205],{"id":13204},"завдання-1-deployment-з-різними-образами","Завдання 1: Deployment з різними образами",[3114,13207,13208,13211],{},[3118,13209,13210],{},"Мета:"," Навчитись створювати Deployment з різними образами та порівнювати їх поведінку.",[3114,13213,13214],{},[3118,13215,13216],{},"Завдання:",[3134,13218,13219,13242,13245,13251],{},[3137,13220,13221,13222],{},"Створіть два Deployment:",[4763,13223,13224,13233],{},[3137,13225,13226,13228,13229,13232],{},[3429,13227,5801],{}," з образом ",[3429,13230,13231],{},"nginx:1.27"," (3 репліки)",[3137,13234,13235,13228,13238,13241],{},[3429,13236,13237],{},"httpd-deployment",[3429,13239,13240],{},"httpd:2.4"," (2 репліки)",[3137,13243,13244],{},"Перевірте, що всі Pod працюють",[3137,13246,13247,13248,13250],{},"Використайте ",[3429,13249,12266],{}," для доступу до кожного Deployment та порівняйте відповіді (nginx показує \"Welcome to nginx!\", httpd показує \"It works!\")",[3137,13252,13253],{},"Видаліть один Pod з кожного Deployment та переконайтесь, що вони автоматично відновлюються",[3114,13255,13256],{},[3118,13257,13258],{},"Очікуваний результат:",[3485,13260,13262],{"className":12572,"code":13261,"language":12574,"meta":3490,"style":3490},"kubectl get deployments\n# NAME               READY   UP-TO-DATE   AVAILABLE   AGE\n# nginx-deployment   3/3     3            3           2m\n# httpd-deployment   2/2     2            2           2m\n\nkubectl get pods\n# NAME                                READY   STATUS    RESTARTS   AGE\n# nginx-deployment-xxx-yyy            1/1     Running   0          2m\n# nginx-deployment-xxx-zzz            1/1     Running   0          2m\n# nginx-deployment-xxx-www            1/1     Running   0          2m\n# httpd-deployment-aaa-bbb            1/1     Running   0          2m\n# httpd-deployment-aaa-ccc            1/1     Running   0          2m\n",[3429,13263,13264,13274,13279,13284,13289,13293,13302,13307,13312,13317,13322,13327],{"__ignoreMap":3490},[3180,13265,13266,13268,13271],{"class":3178,"line":3495},[3180,13267,4054],{"class":8669},[3180,13269,13270],{"class":4430}," get",[3180,13272,13273],{"class":4430}," deployments\n",[3180,13275,13276],{"class":3178,"line":3501},[3180,13277,13278],{"class":4183},"# NAME               READY   UP-TO-DATE   AVAILABLE   AGE\n",[3180,13280,13281],{"class":3178,"line":3507},[3180,13282,13283],{"class":4183},"# nginx-deployment   3/3     3            3           2m\n",[3180,13285,13286],{"class":3178,"line":3513},[3180,13287,13288],{"class":4183},"# httpd-deployment   2/2     2            2           2m\n",[3180,13290,13291],{"class":3178,"line":3520},[3180,13292,3517],{"emptyLinePlaceholder":3516},[3180,13294,13295,13297,13299],{"class":3178,"line":3526},[3180,13296,4054],{"class":8669},[3180,13298,13270],{"class":4430},[3180,13300,13301],{"class":4430}," pods\n",[3180,13303,13304],{"class":3178,"line":3532},[3180,13305,13306],{"class":4183},"# NAME                                READY   STATUS    RESTARTS   AGE\n",[3180,13308,13309],{"class":3178,"line":3538},[3180,13310,13311],{"class":4183},"# nginx-deployment-xxx-yyy            1/1     Running   0          2m\n",[3180,13313,13314],{"class":3178,"line":3544},[3180,13315,13316],{"class":4183},"# nginx-deployment-xxx-zzz            1/1     Running   0          2m\n",[3180,13318,13319],{"class":3178,"line":3549},[3180,13320,13321],{"class":4183},"# nginx-deployment-xxx-www            1/1     Running   0          2m\n",[3180,13323,13324],{"class":3178,"line":3555},[3180,13325,13326],{"class":4183},"# httpd-deployment-aaa-bbb            1/1     Running   0          2m\n",[3180,13328,13329],{"class":3178,"line":3561},[3180,13330,13331],{"class":4183},"# httpd-deployment-aaa-ccc            1/1     Running   0          2m\n",[13333,13334,13336,13341,13482,13487,13632,13637],"collapsible",{"title":13335},"Показати рішення",[3114,13337,13338],{},[3118,13339,13340],{},"nginx-deployment.yaml:",[3485,13342,13344],{"className":3863,"code":13343,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx-deployment\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n          ports:\n            - containerPort: 80\n",[3429,13345,13346,13354,13362,13368,13376,13382,13390,13396,13402,13410,13416,13422,13428,13436,13442,13448,13458,13466,13472],{"__ignoreMap":3490},[3180,13347,13348,13350,13352],{"class":3178,"line":3495},[3180,13349,3873],{"class":3872},[3180,13351,3877],{"class":3876},[3180,13353,3881],{"class":3880},[3180,13355,13356,13358,13360],{"class":3178,"line":3501},[3180,13357,3886],{"class":3872},[3180,13359,3877],{"class":3876},[3180,13361,3891],{"class":3880},[3180,13363,13364,13366],{"class":3178,"line":3507},[3180,13365,3896],{"class":3872},[3180,13367,3899],{"class":3876},[3180,13369,13370,13372,13374],{"class":3178,"line":3513},[3180,13371,3904],{"class":3872},[3180,13373,3877],{"class":3876},[3180,13375,3909],{"class":3880},[3180,13377,13378,13380],{"class":3178,"line":3520},[3180,13379,3914],{"class":3872},[3180,13381,3899],{"class":3876},[3180,13383,13384,13386,13388],{"class":3178,"line":3526},[3180,13385,3921],{"class":3872},[3180,13387,3877],{"class":3876},[3180,13389,3927],{"class":3926},[3180,13391,13392,13394],{"class":3178,"line":3532},[3180,13393,3932],{"class":3872},[3180,13395,3899],{"class":3876},[3180,13397,13398,13400],{"class":3178,"line":3538},[3180,13399,3939],{"class":3872},[3180,13401,3899],{"class":3876},[3180,13403,13404,13406,13408],{"class":3178,"line":3544},[3180,13405,3946],{"class":3872},[3180,13407,3877],{"class":3876},[3180,13409,3951],{"class":3880},[3180,13411,13412,13414],{"class":3178,"line":3549},[3180,13413,3956],{"class":3872},[3180,13415,3899],{"class":3876},[3180,13417,13418,13420],{"class":3178,"line":3555},[3180,13419,3963],{"class":3872},[3180,13421,3899],{"class":3876},[3180,13423,13424,13426],{"class":3178,"line":3561},[3180,13425,3970],{"class":3872},[3180,13427,3899],{"class":3876},[3180,13429,13430,13432,13434],{"class":3178,"line":3567},[3180,13431,3977],{"class":3872},[3180,13433,3877],{"class":3876},[3180,13435,3951],{"class":3880},[3180,13437,13438,13440],{"class":3178,"line":3573},[3180,13439,3986],{"class":3872},[3180,13441,3899],{"class":3876},[3180,13443,13444,13446],{"class":3178,"line":3579},[3180,13445,3993],{"class":3872},[3180,13447,3899],{"class":3876},[3180,13449,13450,13452,13454,13456],{"class":3178,"line":3585},[3180,13451,4000],{"class":3876},[3180,13453,4003],{"class":3872},[3180,13455,3877],{"class":3876},[3180,13457,3951],{"class":3880},[3180,13459,13460,13462,13464],{"class":3178,"line":3591},[3180,13461,4012],{"class":3872},[3180,13463,3877],{"class":3876},[3180,13465,4017],{"class":3880},[3180,13467,13468,13470],{"class":3178,"line":3596},[3180,13469,4554],{"class":3872},[3180,13471,3899],{"class":3876},[3180,13473,13474,13476,13478,13480],{"class":3178,"line":3602},[3180,13475,4561],{"class":3876},[3180,13477,4564],{"class":3872},[3180,13479,3877],{"class":3876},[3180,13481,5647],{"class":3926},[3114,13483,13484],{},[3118,13485,13486],{},"httpd-deployment.yaml:",[3485,13488,13490],{"className":3863,"code":13489,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: httpd-deployment\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: httpd\n  template:\n    metadata:\n      labels:\n        app: httpd\n    spec:\n      containers:\n        - name: httpd\n          image: httpd:2.4\n          ports:\n            - containerPort: 80\n",[3429,13491,13492,13500,13508,13514,13523,13529,13538,13544,13550,13559,13565,13571,13577,13585,13591,13597,13607,13616,13622],{"__ignoreMap":3490},[3180,13493,13494,13496,13498],{"class":3178,"line":3495},[3180,13495,3873],{"class":3872},[3180,13497,3877],{"class":3876},[3180,13499,3881],{"class":3880},[3180,13501,13502,13504,13506],{"class":3178,"line":3501},[3180,13503,3886],{"class":3872},[3180,13505,3877],{"class":3876},[3180,13507,3891],{"class":3880},[3180,13509,13510,13512],{"class":3178,"line":3507},[3180,13511,3896],{"class":3872},[3180,13513,3899],{"class":3876},[3180,13515,13516,13518,13520],{"class":3178,"line":3513},[3180,13517,3904],{"class":3872},[3180,13519,3877],{"class":3876},[3180,13521,13522],{"class":3880},"httpd-deployment\n",[3180,13524,13525,13527],{"class":3178,"line":3520},[3180,13526,3914],{"class":3872},[3180,13528,3899],{"class":3876},[3180,13530,13531,13533,13535],{"class":3178,"line":3526},[3180,13532,3921],{"class":3872},[3180,13534,3877],{"class":3876},[3180,13536,13537],{"class":3926},"2\n",[3180,13539,13540,13542],{"class":3178,"line":3532},[3180,13541,3932],{"class":3872},[3180,13543,3899],{"class":3876},[3180,13545,13546,13548],{"class":3178,"line":3538},[3180,13547,3939],{"class":3872},[3180,13549,3899],{"class":3876},[3180,13551,13552,13554,13556],{"class":3178,"line":3544},[3180,13553,3946],{"class":3872},[3180,13555,3877],{"class":3876},[3180,13557,13558],{"class":3880},"httpd\n",[3180,13560,13561,13563],{"class":3178,"line":3549},[3180,13562,3956],{"class":3872},[3180,13564,3899],{"class":3876},[3180,13566,13567,13569],{"class":3178,"line":3555},[3180,13568,3963],{"class":3872},[3180,13570,3899],{"class":3876},[3180,13572,13573,13575],{"class":3178,"line":3561},[3180,13574,3970],{"class":3872},[3180,13576,3899],{"class":3876},[3180,13578,13579,13581,13583],{"class":3178,"line":3567},[3180,13580,3977],{"class":3872},[3180,13582,3877],{"class":3876},[3180,13584,13558],{"class":3880},[3180,13586,13587,13589],{"class":3178,"line":3573},[3180,13588,3986],{"class":3872},[3180,13590,3899],{"class":3876},[3180,13592,13593,13595],{"class":3178,"line":3579},[3180,13594,3993],{"class":3872},[3180,13596,3899],{"class":3876},[3180,13598,13599,13601,13603,13605],{"class":3178,"line":3585},[3180,13600,4000],{"class":3876},[3180,13602,4003],{"class":3872},[3180,13604,3877],{"class":3876},[3180,13606,13558],{"class":3880},[3180,13608,13609,13611,13613],{"class":3178,"line":3591},[3180,13610,4012],{"class":3872},[3180,13612,3877],{"class":3876},[3180,13614,13615],{"class":3880},"httpd:2.4\n",[3180,13617,13618,13620],{"class":3178,"line":3596},[3180,13619,4554],{"class":3872},[3180,13621,3899],{"class":3876},[3180,13623,13624,13626,13628,13630],{"class":3178,"line":3602},[3180,13625,4561],{"class":3876},[3180,13627,4564],{"class":3872},[3180,13629,3877],{"class":3876},[3180,13631,5647],{"class":3926},[3114,13633,13634],{},[3118,13635,13636],{},"Команди:",[3485,13638,13640],{"className":12572,"code":13639,"language":12574,"meta":3490,"style":3490},"# Створення\nkubectl apply -f nginx-deployment.yaml\nkubectl apply -f httpd-deployment.yaml\n\n# Перевірка\nkubectl get deployments\nkubectl get pods\n\n# Тестування nginx\nkubectl port-forward deployment/nginx-deployment 8080:80\ncurl http://localhost:8080\n# Відповідь: Welcome to nginx!\n\n# Тестування httpd (у новому терміналі)\nkubectl port-forward deployment/httpd-deployment 8081:80\ncurl http://localhost:8081\n# Відповідь: \u003Chtml>\u003Cbody>\u003Ch1>It works!\u003C/h1>\u003C/body>\u003C/html>\n\n# Тестування self-healing\nkubectl delete pod \u003Cnginx-pod-name>\nkubectl get pods -w  # Спостерігайте за створенням нового Pod\n\n# Очищення\nkubectl delete deployment nginx-deployment httpd-deployment\n",[3429,13641,13642,13647,13659,13670,13674,13679,13687,13695,13699,13704,13717,13724,13729,13733,13738,13750,13757,13762,13766,13771,13789,13804,13808,13813],{"__ignoreMap":3490},[3180,13643,13644],{"class":3178,"line":3495},[3180,13645,13646],{"class":4183},"# Створення\n",[3180,13648,13649,13651,13654,13656],{"class":3178,"line":3501},[3180,13650,4054],{"class":8669},[3180,13652,13653],{"class":4430}," apply",[3180,13655,13186],{"class":4962},[3180,13657,13658],{"class":4430}," nginx-deployment.yaml\n",[3180,13660,13661,13663,13665,13667],{"class":3178,"line":3507},[3180,13662,4054],{"class":8669},[3180,13664,13653],{"class":4430},[3180,13666,13186],{"class":4962},[3180,13668,13669],{"class":4430}," httpd-deployment.yaml\n",[3180,13671,13672],{"class":3178,"line":3513},[3180,13673,3517],{"emptyLinePlaceholder":3516},[3180,13675,13676],{"class":3178,"line":3520},[3180,13677,13678],{"class":4183},"# Перевірка\n",[3180,13680,13681,13683,13685],{"class":3178,"line":3526},[3180,13682,4054],{"class":8669},[3180,13684,13270],{"class":4430},[3180,13686,13273],{"class":4430},[3180,13688,13689,13691,13693],{"class":3178,"line":3532},[3180,13690,4054],{"class":8669},[3180,13692,13270],{"class":4430},[3180,13694,13301],{"class":4430},[3180,13696,13697],{"class":3178,"line":3538},[3180,13698,3517],{"emptyLinePlaceholder":3516},[3180,13700,13701],{"class":3178,"line":3544},[3180,13702,13703],{"class":4183},"# Тестування nginx\n",[3180,13705,13706,13708,13711,13714],{"class":3178,"line":3549},[3180,13707,4054],{"class":8669},[3180,13709,13710],{"class":4430}," port-forward",[3180,13712,13713],{"class":4430}," deployment/nginx-deployment",[3180,13715,13716],{"class":4430}," 8080:80\n",[3180,13718,13719,13721],{"class":3178,"line":3555},[3180,13720,12586],{"class":8669},[3180,13722,13723],{"class":4430}," http://localhost:8080\n",[3180,13725,13726],{"class":3178,"line":3561},[3180,13727,13728],{"class":4183},"# Відповідь: Welcome to nginx!\n",[3180,13730,13731],{"class":3178,"line":3567},[3180,13732,3517],{"emptyLinePlaceholder":3516},[3180,13734,13735],{"class":3178,"line":3573},[3180,13736,13737],{"class":4183},"# Тестування httpd (у новому терміналі)\n",[3180,13739,13740,13742,13744,13747],{"class":3178,"line":3579},[3180,13741,4054],{"class":8669},[3180,13743,13710],{"class":4430},[3180,13745,13746],{"class":4430}," deployment/httpd-deployment",[3180,13748,13749],{"class":4430}," 8081:80\n",[3180,13751,13752,13754],{"class":3178,"line":3585},[3180,13753,12586],{"class":8669},[3180,13755,13756],{"class":4430}," http://localhost:8081\n",[3180,13758,13759],{"class":3178,"line":3591},[3180,13760,13761],{"class":4183},"# Відповідь: \u003Chtml>\u003Cbody>\u003Ch1>It works!\u003C/h1>\u003C/body>\u003C/html>\n",[3180,13763,13764],{"class":3178,"line":3596},[3180,13765,3517],{"emptyLinePlaceholder":3516},[3180,13767,13768],{"class":3178,"line":3602},[3180,13769,13770],{"class":4183},"# Тестування self-healing\n",[3180,13772,13773,13775,13777,13780,13783,13786],{"class":3178,"line":3607},[3180,13774,4054],{"class":8669},[3180,13776,13183],{"class":4430},[3180,13778,13779],{"class":4430}," pod",[3180,13781,13782],{"class":3876}," \u003C",[3180,13784,13785],{"class":4430},"nginx-pod-nam",[3180,13787,13788],{"class":3876},"e>\n",[3180,13790,13791,13793,13795,13798,13801],{"class":3178,"line":3612},[3180,13792,4054],{"class":8669},[3180,13794,13270],{"class":4430},[3180,13796,13797],{"class":4430}," pods",[3180,13799,13800],{"class":4962}," -w",[3180,13802,13803],{"class":4183},"  # Спостерігайте за створенням нового Pod\n",[3180,13805,13806],{"class":3178,"line":3618},[3180,13807,3517],{"emptyLinePlaceholder":3516},[3180,13809,13810],{"class":3178,"line":3624},[3180,13811,13812],{"class":4183},"# Очищення\n",[3180,13814,13815,13817,13819,13822,13825],{"class":3178,"line":3630},[3180,13816,4054],{"class":8669},[3180,13818,13183],{"class":4430},[3180,13820,13821],{"class":4430}," deployment",[3180,13823,13824],{"class":4430}," nginx-deployment",[3180,13826,13827],{"class":4430}," httpd-deployment\n",[3382,13829],{},[3126,13831,13833],{"id":13832},"завдання-2-експерименти-з-ресурсами","Завдання 2: Експерименти з ресурсами",[3114,13835,13836,13838],{},[3118,13837,13210],{}," Зрозуміти, як працюють resource requests та limits.",[3114,13840,13841],{},[3118,13842,13216],{},[3134,13844,13845,13887,13890,13893],{},[3137,13846,13847,13848],{},"Створіть Deployment з дуже низькими limits:",[3485,13849,13851],{"className":3863,"code":13850,"language":3865,"meta":3490,"style":3490},"resources:\n  limits:\n    memory: \"10Mi\"\n    cpu: \"10m\"\n",[3429,13852,13853,13860,13867,13877],{"__ignoreMap":3490},[3180,13854,13855,13858],{"class":3178,"line":3495},[3180,13856,13857],{"class":3872},"resources",[3180,13859,3899],{"class":3876},[3180,13861,13862,13865],{"class":3178,"line":3501},[3180,13863,13864],{"class":3872},"  limits",[3180,13866,3899],{"class":3876},[3180,13868,13869,13872,13874],{"class":3178,"line":3507},[3180,13870,13871],{"class":3872},"    memory",[3180,13873,3877],{"class":3876},[3180,13875,13876],{"class":4430},"\"10Mi\"\n",[3180,13878,13879,13882,13884],{"class":3178,"line":3513},[3180,13880,13881],{"class":3872},"    cpu",[3180,13883,3877],{"class":3876},[3180,13885,13886],{"class":4430},"\"10m\"\n",[3137,13888,13889],{},"Спостерігайте, що відбувається з Pod (можливо, OOMKilled)",[3137,13891,13892],{},"Поступово збільшуйте limits, поки Pod не стане стабільним",[3137,13894,13247,13895,13897],{},[3429,13896,12883],{}," для моніторингу реального споживання ресурсів",[3114,13899,13900],{},[3118,13901,13258],{},[3114,13903,13904],{},"Ви побачите, як Pod з недостатніми ресурсами постійно перезапускаються (CrashLoopBackOff або OOMKilled), а після збільшення limits стають стабільними.",[13333,13906,13907,13912,14095,14099],{"title":13335},[3114,13908,13909],{},[3118,13910,13911],{},"low-resources-deployment.yaml:",[3485,13913,13915],{"className":3863,"code":13914,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: low-resources-test\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: test\n  template:\n    metadata:\n      labels:\n        app: test\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n          resources:\n            limits:\n              memory: \"10Mi\"  # Занадто мало для nginx!\n              cpu: \"10m\"\n            requests:\n              memory: \"5Mi\"\n              cpu: \"5m\"\n",[3429,13916,13917,13925,13933,13939,13948,13954,13962,13968,13974,13983,13989,13995,14001,14009,14015,14021,14031,14039,14045,14051,14063,14071,14077,14086],{"__ignoreMap":3490},[3180,13918,13919,13921,13923],{"class":3178,"line":3495},[3180,13920,3873],{"class":3872},[3180,13922,3877],{"class":3876},[3180,13924,3881],{"class":3880},[3180,13926,13927,13929,13931],{"class":3178,"line":3501},[3180,13928,3886],{"class":3872},[3180,13930,3877],{"class":3876},[3180,13932,3891],{"class":3880},[3180,13934,13935,13937],{"class":3178,"line":3507},[3180,13936,3896],{"class":3872},[3180,13938,3899],{"class":3876},[3180,13940,13941,13943,13945],{"class":3178,"line":3513},[3180,13942,3904],{"class":3872},[3180,13944,3877],{"class":3876},[3180,13946,13947],{"class":3880},"low-resources-test\n",[3180,13949,13950,13952],{"class":3178,"line":3520},[3180,13951,3914],{"class":3872},[3180,13953,3899],{"class":3876},[3180,13955,13956,13958,13960],{"class":3178,"line":3526},[3180,13957,3921],{"class":3872},[3180,13959,3877],{"class":3876},[3180,13961,4730],{"class":3926},[3180,13963,13964,13966],{"class":3178,"line":3532},[3180,13965,3932],{"class":3872},[3180,13967,3899],{"class":3876},[3180,13969,13970,13972],{"class":3178,"line":3538},[3180,13971,3939],{"class":3872},[3180,13973,3899],{"class":3876},[3180,13975,13976,13978,13980],{"class":3178,"line":3544},[3180,13977,3946],{"class":3872},[3180,13979,3877],{"class":3876},[3180,13981,13982],{"class":3880},"test\n",[3180,13984,13985,13987],{"class":3178,"line":3549},[3180,13986,3956],{"class":3872},[3180,13988,3899],{"class":3876},[3180,13990,13991,13993],{"class":3178,"line":3555},[3180,13992,3963],{"class":3872},[3180,13994,3899],{"class":3876},[3180,13996,13997,13999],{"class":3178,"line":3561},[3180,13998,3970],{"class":3872},[3180,14000,3899],{"class":3876},[3180,14002,14003,14005,14007],{"class":3178,"line":3567},[3180,14004,3977],{"class":3872},[3180,14006,3877],{"class":3876},[3180,14008,13982],{"class":3880},[3180,14010,14011,14013],{"class":3178,"line":3573},[3180,14012,3986],{"class":3872},[3180,14014,3899],{"class":3876},[3180,14016,14017,14019],{"class":3178,"line":3579},[3180,14018,3993],{"class":3872},[3180,14020,3899],{"class":3876},[3180,14022,14023,14025,14027,14029],{"class":3178,"line":3585},[3180,14024,4000],{"class":3876},[3180,14026,4003],{"class":3872},[3180,14028,3877],{"class":3876},[3180,14030,3951],{"class":3880},[3180,14032,14033,14035,14037],{"class":3178,"line":3591},[3180,14034,4012],{"class":3872},[3180,14036,3877],{"class":3876},[3180,14038,4017],{"class":3880},[3180,14040,14041,14043],{"class":3178,"line":3596},[3180,14042,4602],{"class":3872},[3180,14044,3899],{"class":3876},[3180,14046,14047,14049],{"class":3178,"line":3602},[3180,14048,4636],{"class":3872},[3180,14050,3899],{"class":3876},[3180,14052,14053,14055,14057,14060],{"class":3178,"line":3607},[3180,14054,4616],{"class":3872},[3180,14056,3877],{"class":3876},[3180,14058,14059],{"class":4430},"\"10Mi\"",[3180,14061,14062],{"class":4183},"  # Занадто мало для nginx!\n",[3180,14064,14065,14067,14069],{"class":3178,"line":3612},[3180,14066,4626],{"class":3872},[3180,14068,3877],{"class":3876},[3180,14070,13886],{"class":4430},[3180,14072,14073,14075],{"class":3178,"line":3618},[3180,14074,4609],{"class":3872},[3180,14076,3899],{"class":3876},[3180,14078,14079,14081,14083],{"class":3178,"line":3624},[3180,14080,4616],{"class":3872},[3180,14082,3877],{"class":3876},[3180,14084,14085],{"class":4430},"\"5Mi\"\n",[3180,14087,14088,14090,14092],{"class":3178,"line":3630},[3180,14089,4626],{"class":3872},[3180,14091,3877],{"class":3876},[3180,14093,14094],{"class":4430},"\"5m\"\n",[3114,14096,14097],{},[3118,14098,13636],{},[3485,14100,14102],{"className":12572,"code":14101,"language":12574,"meta":3490,"style":3490},"# Створення\nkubectl apply -f low-resources-deployment.yaml\n\n# Спостереження (Pod буде постійно перезапускатись)\nkubectl get pods -w\n# NAME                                 READY   STATUS      RESTARTS   AGE\n# low-resources-test-xxx-yyy           0/1     OOMKilled   1          10s\n# low-resources-test-xxx-yyy           0/1     CrashLoopBackOff   1   20s\n\n# Перегляд причини\nkubectl describe pod \u003Cpod-name>\n# Last State:     Terminated\n#   Reason:       OOMKilled\n#   Exit Code:    137\n\n# Збільшення ресурсів\nkubectl edit deployment low-resources-test\n# Змініть memory limit на 64Mi\n\n# Тепер Pod має стартувати успішно\nkubectl get pods\n# NAME                                 READY   STATUS    RESTARTS   AGE\n# low-resources-test-xxx-zzz           1/1     Running   0          15s\n\n# Перевірка реального споживання\nkubectl top pods\n# NAME                                 CPU(cores)   MEMORY(bytes)\n# low-resources-test-xxx-zzz           1m           8Mi\n\n# Очищення\nkubectl delete deployment low-resources-test\n",[3429,14103,14104,14108,14119,14123,14128,14139,14144,14149,14154,14158,14163,14179,14184,14189,14194,14198,14203,14215,14220,14224,14229,14237,14242,14247,14251,14256,14265,14270,14275,14279,14283],{"__ignoreMap":3490},[3180,14105,14106],{"class":3178,"line":3495},[3180,14107,13646],{"class":4183},[3180,14109,14110,14112,14114,14116],{"class":3178,"line":3501},[3180,14111,4054],{"class":8669},[3180,14113,13653],{"class":4430},[3180,14115,13186],{"class":4962},[3180,14117,14118],{"class":4430}," low-resources-deployment.yaml\n",[3180,14120,14121],{"class":3178,"line":3507},[3180,14122,3517],{"emptyLinePlaceholder":3516},[3180,14124,14125],{"class":3178,"line":3513},[3180,14126,14127],{"class":4183},"# Спостереження (Pod буде постійно перезапускатись)\n",[3180,14129,14130,14132,14134,14136],{"class":3178,"line":3520},[3180,14131,4054],{"class":8669},[3180,14133,13270],{"class":4430},[3180,14135,13797],{"class":4430},[3180,14137,14138],{"class":4962}," -w\n",[3180,14140,14141],{"class":3178,"line":3526},[3180,14142,14143],{"class":4183},"# NAME                                 READY   STATUS      RESTARTS   AGE\n",[3180,14145,14146],{"class":3178,"line":3532},[3180,14147,14148],{"class":4183},"# low-resources-test-xxx-yyy           0/1     OOMKilled   1          10s\n",[3180,14150,14151],{"class":3178,"line":3538},[3180,14152,14153],{"class":4183},"# low-resources-test-xxx-yyy           0/1     CrashLoopBackOff   1   20s\n",[3180,14155,14156],{"class":3178,"line":3544},[3180,14157,3517],{"emptyLinePlaceholder":3516},[3180,14159,14160],{"class":3178,"line":3549},[3180,14161,14162],{"class":4183},"# Перегляд причини\n",[3180,14164,14165,14167,14170,14172,14174,14177],{"class":3178,"line":3555},[3180,14166,4054],{"class":8669},[3180,14168,14169],{"class":4430}," describe",[3180,14171,13779],{"class":4430},[3180,14173,13782],{"class":3876},[3180,14175,14176],{"class":4430},"pod-nam",[3180,14178,13788],{"class":3876},[3180,14180,14181],{"class":3178,"line":3561},[3180,14182,14183],{"class":4183},"# Last State:     Terminated\n",[3180,14185,14186],{"class":3178,"line":3567},[3180,14187,14188],{"class":4183},"#   Reason:       OOMKilled\n",[3180,14190,14191],{"class":3178,"line":3573},[3180,14192,14193],{"class":4183},"#   Exit Code:    137\n",[3180,14195,14196],{"class":3178,"line":3579},[3180,14197,3517],{"emptyLinePlaceholder":3516},[3180,14199,14200],{"class":3178,"line":3585},[3180,14201,14202],{"class":4183},"# Збільшення ресурсів\n",[3180,14204,14205,14207,14210,14212],{"class":3178,"line":3591},[3180,14206,4054],{"class":8669},[3180,14208,14209],{"class":4430}," edit",[3180,14211,13821],{"class":4430},[3180,14213,14214],{"class":4430}," low-resources-test\n",[3180,14216,14217],{"class":3178,"line":3596},[3180,14218,14219],{"class":4183},"# Змініть memory limit на 64Mi\n",[3180,14221,14222],{"class":3178,"line":3602},[3180,14223,3517],{"emptyLinePlaceholder":3516},[3180,14225,14226],{"class":3178,"line":3607},[3180,14227,14228],{"class":4183},"# Тепер Pod має стартувати успішно\n",[3180,14230,14231,14233,14235],{"class":3178,"line":3612},[3180,14232,4054],{"class":8669},[3180,14234,13270],{"class":4430},[3180,14236,13301],{"class":4430},[3180,14238,14239],{"class":3178,"line":3618},[3180,14240,14241],{"class":4183},"# NAME                                 READY   STATUS    RESTARTS   AGE\n",[3180,14243,14244],{"class":3178,"line":3624},[3180,14245,14246],{"class":4183},"# low-resources-test-xxx-zzz           1/1     Running   0          15s\n",[3180,14248,14249],{"class":3178,"line":3630},[3180,14250,3517],{"emptyLinePlaceholder":3516},[3180,14252,14253],{"class":3178,"line":3636},[3180,14254,14255],{"class":4183},"# Перевірка реального споживання\n",[3180,14257,14258,14260,14263],{"class":3178,"line":3641},[3180,14259,4054],{"class":8669},[3180,14261,14262],{"class":4430}," top",[3180,14264,13301],{"class":4430},[3180,14266,14267],{"class":3178,"line":3646},[3180,14268,14269],{"class":4183},"# NAME                                 CPU(cores)   MEMORY(bytes)\n",[3180,14271,14272],{"class":3178,"line":3651},[3180,14273,14274],{"class":4183},"# low-resources-test-xxx-zzz           1m           8Mi\n",[3180,14276,14277],{"class":3178,"line":3657},[3180,14278,3517],{"emptyLinePlaceholder":3516},[3180,14280,14281],{"class":3178,"line":3663},[3180,14282,13812],{"class":4183},[3180,14284,14285,14287,14289,14291],{"class":3178,"line":3669},[3180,14286,4054],{"class":8669},[3180,14288,13183],{"class":4430},[3180,14290,13821],{"class":4430},[3180,14292,14214],{"class":4430},[3382,14294],{},[3126,14296,14298],{"id":14297},"завдання-3-масштабування-під-навантаженням","Завдання 3: Масштабування під навантаженням",[3114,14300,14301,14303],{},[3118,14302,13210],{}," Навчитись масштабувати застосунок залежно від навантаження.",[3114,14305,14306],{},[3118,14307,13216],{},[3134,14309,14310,14313,14325,14328,14333],{},[3137,14311,14312],{},"Розгорніть TodoApi з 2 репліками",[3137,14314,13247,14315,14317,14318,11385,14321,14324],{},[3429,14316,12266],{}," та інструмент навантаження (наприклад, ",[3429,14319,14320],{},"ab",[3429,14322,14323],{},"wrk",") для генерації трафіку",[3137,14326,14327],{},"Під час навантаження збільште кількість реплік до 5",[3137,14329,14330,14331,4229],{},"Спостерігайте за розподілом навантаження через логи (",[3429,14332,12956],{},[3137,14334,14335],{},"Після завершення тесту зменшіть репліки назад до 2",[3114,14337,14338],{},[3118,14339,13258],{},[3114,14341,14342],{},"Ви побачите, як нові Pod створюються під час навантаження та починають обробляти запити, розподіляючи навантаження між усіма репліками.",[13333,14344,14345,14349,14556,14561],{"title":13335},[3114,14346,14347],{},[3118,14348,13636],{},[3485,14350,14352],{"className":12572,"code":14351,"language":12574,"meta":3490,"style":3490},"# Розгортання TodoApi (використовуйте YAML з попереднього прикладу)\nkubectl apply -f k8s/deployment.yaml\nkubectl scale deployment todoapi --replicas=2\n\n# Port-forward\nkubectl port-forward deployment/todoapi 8080:8080 &\n\n# Генерація навантаження (потрібен Apache Bench)\n# macOS: brew install httpd\n# Ubuntu: sudo apt install apache2-utils\nab -n 1000 -c 10 http://localhost:8080/health\n\n# Під час виконання ab — у новому терміналі:\nkubectl scale deployment todoapi --replicas=5\n\n# Спостереження за Pod\nkubectl get pods -l app=todoapi -w\n\n# Перегляд логів (побачите, що запити розподіляються між Pod)\nkubectl logs -l app=todoapi --tail=20\n\n# Після завершення тесту\nkubectl scale deployment todoapi --replicas=2\n\n# Очищення\nkubectl delete deployment todoapi\n",[3429,14353,14354,14359,14369,14384,14388,14393,14408,14412,14417,14422,14427,14446,14450,14455,14468,14472,14477,14493,14497,14502,14516,14520,14525,14537,14541,14545],{"__ignoreMap":3490},[3180,14355,14356],{"class":3178,"line":3495},[3180,14357,14358],{"class":4183},"# Розгортання TodoApi (використовуйте YAML з попереднього прикладу)\n",[3180,14360,14361,14363,14365,14367],{"class":3178,"line":3501},[3180,14362,4054],{"class":8669},[3180,14364,13653],{"class":4430},[3180,14366,13186],{"class":4962},[3180,14368,13189],{"class":4430},[3180,14370,14371,14373,14376,14378,14381],{"class":3178,"line":3507},[3180,14372,4054],{"class":8669},[3180,14374,14375],{"class":4430}," scale",[3180,14377,13821],{"class":4430},[3180,14379,14380],{"class":4430}," todoapi",[3180,14382,14383],{"class":4962}," --replicas=2\n",[3180,14385,14386],{"class":3178,"line":3513},[3180,14387,3517],{"emptyLinePlaceholder":3516},[3180,14389,14390],{"class":3178,"line":3520},[3180,14391,14392],{"class":4183},"# Port-forward\n",[3180,14394,14395,14397,14399,14402,14405],{"class":3178,"line":3526},[3180,14396,4054],{"class":8669},[3180,14398,13710],{"class":4430},[3180,14400,14401],{"class":4430}," deployment/todoapi",[3180,14403,14404],{"class":4430}," 8080:8080",[3180,14406,14407],{"class":3876}," &\n",[3180,14409,14410],{"class":3178,"line":3532},[3180,14411,3517],{"emptyLinePlaceholder":3516},[3180,14413,14414],{"class":3178,"line":3538},[3180,14415,14416],{"class":4183},"# Генерація навантаження (потрібен Apache Bench)\n",[3180,14418,14419],{"class":3178,"line":3544},[3180,14420,14421],{"class":4183},"# macOS: brew install httpd\n",[3180,14423,14424],{"class":3178,"line":3549},[3180,14425,14426],{"class":4183},"# Ubuntu: sudo apt install apache2-utils\n",[3180,14428,14429,14431,14434,14437,14440,14443],{"class":3178,"line":3555},[3180,14430,14320],{"class":8669},[3180,14432,14433],{"class":4962}," -n",[3180,14435,14436],{"class":3926}," 1000",[3180,14438,14439],{"class":4962}," -c",[3180,14441,14442],{"class":3926}," 10",[3180,14444,14445],{"class":4430}," http://localhost:8080/health\n",[3180,14447,14448],{"class":3178,"line":3561},[3180,14449,3517],{"emptyLinePlaceholder":3516},[3180,14451,14452],{"class":3178,"line":3567},[3180,14453,14454],{"class":4183},"# Під час виконання ab — у новому терміналі:\n",[3180,14456,14457,14459,14461,14463,14465],{"class":3178,"line":3573},[3180,14458,4054],{"class":8669},[3180,14460,14375],{"class":4430},[3180,14462,13821],{"class":4430},[3180,14464,14380],{"class":4430},[3180,14466,14467],{"class":4962}," --replicas=5\n",[3180,14469,14470],{"class":3178,"line":3579},[3180,14471,3517],{"emptyLinePlaceholder":3516},[3180,14473,14474],{"class":3178,"line":3585},[3180,14475,14476],{"class":4183},"# Спостереження за Pod\n",[3180,14478,14479,14481,14483,14485,14488,14491],{"class":3178,"line":3591},[3180,14480,4054],{"class":8669},[3180,14482,13270],{"class":4430},[3180,14484,13797],{"class":4430},[3180,14486,14487],{"class":4962}," -l",[3180,14489,14490],{"class":4430}," app=todoapi",[3180,14492,14138],{"class":4962},[3180,14494,14495],{"class":3178,"line":3596},[3180,14496,3517],{"emptyLinePlaceholder":3516},[3180,14498,14499],{"class":3178,"line":3602},[3180,14500,14501],{"class":4183},"# Перегляд логів (побачите, що запити розподіляються між Pod)\n",[3180,14503,14504,14506,14509,14511,14513],{"class":3178,"line":3607},[3180,14505,4054],{"class":8669},[3180,14507,14508],{"class":4430}," logs",[3180,14510,14487],{"class":4962},[3180,14512,14490],{"class":4430},[3180,14514,14515],{"class":4962}," --tail=20\n",[3180,14517,14518],{"class":3178,"line":3612},[3180,14519,3517],{"emptyLinePlaceholder":3516},[3180,14521,14522],{"class":3178,"line":3618},[3180,14523,14524],{"class":4183},"# Після завершення тесту\n",[3180,14526,14527,14529,14531,14533,14535],{"class":3178,"line":3624},[3180,14528,4054],{"class":8669},[3180,14530,14375],{"class":4430},[3180,14532,13821],{"class":4430},[3180,14534,14380],{"class":4430},[3180,14536,14383],{"class":4962},[3180,14538,14539],{"class":3178,"line":3630},[3180,14540,3517],{"emptyLinePlaceholder":3516},[3180,14542,14543],{"class":3178,"line":3636},[3180,14544,13812],{"class":4183},[3180,14546,14547,14549,14551,14553],{"class":3178,"line":3641},[3180,14548,4054],{"class":8669},[3180,14550,13183],{"class":4430},[3180,14552,13821],{"class":4430},[3180,14554,14555],{"class":4430}," todoapi\n",[3114,14557,14558],{},[3118,14559,14560],{},"Альтернатива з wrk (більш потужний інструмент):",[3485,14562,14564],{"className":12572,"code":14563,"language":12574,"meta":3490,"style":3490},"# Встановлення wrk\n# macOS: brew install wrk\n# Ubuntu: sudo apt install wrk\n\n# Генерація навантаження (10 потоків, 100 з'єднань, 30 секунд)\nwrk -t10 -c100 -d30s http://localhost:8080/health\n",[3429,14565,14566,14571,14576,14581,14585,14590],{"__ignoreMap":3490},[3180,14567,14568],{"class":3178,"line":3495},[3180,14569,14570],{"class":4183},"# Встановлення wrk\n",[3180,14572,14573],{"class":3178,"line":3501},[3180,14574,14575],{"class":4183},"# macOS: brew install wrk\n",[3180,14577,14578],{"class":3178,"line":3507},[3180,14579,14580],{"class":4183},"# Ubuntu: sudo apt install wrk\n",[3180,14582,14583],{"class":3178,"line":3513},[3180,14584,3517],{"emptyLinePlaceholder":3516},[3180,14586,14587],{"class":3178,"line":3520},[3180,14588,14589],{"class":4183},"# Генерація навантаження (10 потоків, 100 з'єднань, 30 секунд)\n",[3180,14591,14592,14594,14597,14600,14603],{"class":3178,"line":3526},[3180,14593,14323],{"class":8669},[3180,14595,14596],{"class":4962}," -t10",[3180,14598,14599],{"class":4962}," -c100",[3180,14601,14602],{"class":4962}," -d30s",[3180,14604,14445],{"class":4430},[3382,14606],{},[3126,14608,14610],{"id":14609},"завдання-4-налаштування-health-checks","Завдання 4: Налаштування Health Checks",[3114,14612,14613,14615],{},[3118,14614,13210],{}," Навчитись правильно налаштовувати liveness та readiness probes.",[3114,14617,14618],{},[3118,14619,13216],{},[3134,14621,14622,14627,14630,14633],{},[3137,14623,14624,14625,4229],{},"Створіть Deployment з некоректними health checks (занадто короткий ",[3429,14626,11905],{},[3137,14628,14629],{},"Спостерігайте за CrashLoopBackOff",[3137,14631,14632],{},"Виправте налаштування та досягніть стабільного стану",[3137,14634,14635,14636,4789,14639,4789,14642],{},"Експериментуйте з різними значеннями ",[3429,14637,14638],{},"periodSeconds",[3429,14640,14641],{},"timeoutSeconds",[3429,14643,11465],{},[3114,14645,14646],{},[3118,14647,13258],{},[3114,14649,14650],{},"Ви зрозумієте, як неправильні health checks можуть призвести до постійних перезапусків Pod, та навчитесь підбирати оптимальні значення.",[13333,14652,14653,14658,14908,14912,15077,15082],{"title":13335},[3114,14654,14655],{},[3118,14656,14657],{},"bad-probes-deployment.yaml (некоректні налаштування):",[3485,14659,14661],{"className":3863,"code":14660,"language":3865,"meta":3490,"style":3490},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: bad-probes-test\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: test\n  template:\n    metadata:\n      labels:\n        app: test\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n          ports:\n            - containerPort: 80\n          livenessProbe:\n            httpGet:\n              path: /\n              port: 80\n            initialDelaySeconds: 1  # Занадто мало! nginx не встигне стартувати\n            periodSeconds: 2\n            failureThreshold: 2\n          readinessProbe:\n            httpGet:\n              path: /\n              port: 80\n            initialDelaySeconds: 1\n            periodSeconds: 2\n            failureThreshold: 2\n",[3429,14662,14663,14671,14679,14685,14694,14700,14708,14714,14720,14728,14734,14740,14746,14754,14760,14766,14776,14784,14790,14800,14806,14812,14821,14829,14840,14848,14856,14862,14868,14876,14884,14892,14900],{"__ignoreMap":3490},[3180,14664,14665,14667,14669],{"class":3178,"line":3495},[3180,14666,3873],{"class":3872},[3180,14668,3877],{"class":3876},[3180,14670,3881],{"class":3880},[3180,14672,14673,14675,14677],{"class":3178,"line":3501},[3180,14674,3886],{"class":3872},[3180,14676,3877],{"class":3876},[3180,14678,3891],{"class":3880},[3180,14680,14681,14683],{"class":3178,"line":3507},[3180,14682,3896],{"class":3872},[3180,14684,3899],{"class":3876},[3180,14686,14687,14689,14691],{"class":3178,"line":3513},[3180,14688,3904],{"class":3872},[3180,14690,3877],{"class":3876},[3180,14692,14693],{"class":3880},"bad-probes-test\n",[3180,14695,14696,14698],{"class":3178,"line":3520},[3180,14697,3914],{"class":3872},[3180,14699,3899],{"class":3876},[3180,14701,14702,14704,14706],{"class":3178,"line":3526},[3180,14703,3921],{"class":3872},[3180,14705,3877],{"class":3876},[3180,14707,4730],{"class":3926},[3180,14709,14710,14712],{"class":3178,"line":3532},[3180,14711,3932],{"class":3872},[3180,14713,3899],{"class":3876},[3180,14715,14716,14718],{"class":3178,"line":3538},[3180,14717,3939],{"class":3872},[3180,14719,3899],{"class":3876},[3180,14721,14722,14724,14726],{"class":3178,"line":3544},[3180,14723,3946],{"class":3872},[3180,14725,3877],{"class":3876},[3180,14727,13982],{"class":3880},[3180,14729,14730,14732],{"class":3178,"line":3549},[3180,14731,3956],{"class":3872},[3180,14733,3899],{"class":3876},[3180,14735,14736,14738],{"class":3178,"line":3555},[3180,14737,3963],{"class":3872},[3180,14739,3899],{"class":3876},[3180,14741,14742,14744],{"class":3178,"line":3561},[3180,14743,3970],{"class":3872},[3180,14745,3899],{"class":3876},[3180,14747,14748,14750,14752],{"class":3178,"line":3567},[3180,14749,3977],{"class":3872},[3180,14751,3877],{"class":3876},[3180,14753,13982],{"class":3880},[3180,14755,14756,14758],{"class":3178,"line":3573},[3180,14757,3986],{"class":3872},[3180,14759,3899],{"class":3876},[3180,14761,14762,14764],{"class":3178,"line":3579},[3180,14763,3993],{"class":3872},[3180,14765,3899],{"class":3876},[3180,14767,14768,14770,14772,14774],{"class":3178,"line":3585},[3180,14769,4000],{"class":3876},[3180,14771,4003],{"class":3872},[3180,14773,3877],{"class":3876},[3180,14775,3951],{"class":3880},[3180,14777,14778,14780,14782],{"class":3178,"line":3591},[3180,14779,4012],{"class":3872},[3180,14781,3877],{"class":3876},[3180,14783,4017],{"class":3880},[3180,14785,14786,14788],{"class":3178,"line":3596},[3180,14787,4554],{"class":3872},[3180,14789,3899],{"class":3876},[3180,14791,14792,14794,14796,14798],{"class":3178,"line":3602},[3180,14793,4561],{"class":3876},[3180,14795,4564],{"class":3872},[3180,14797,3877],{"class":3876},[3180,14799,5647],{"class":3926},[3180,14801,14802,14804],{"class":3178,"line":3607},[3180,14803,11212],{"class":3872},[3180,14805,3899],{"class":3876},[3180,14807,14808,14810],{"class":3178,"line":3612},[3180,14809,11219],{"class":3872},[3180,14811,3899],{"class":3876},[3180,14813,14814,14816,14818],{"class":3178,"line":3618},[3180,14815,11226],{"class":3872},[3180,14817,3877],{"class":3876},[3180,14819,14820],{"class":3880},"/\n",[3180,14822,14823,14825,14827],{"class":3178,"line":3624},[3180,14824,11236],{"class":3872},[3180,14826,3877],{"class":3876},[3180,14828,5647],{"class":3926},[3180,14830,14831,14833,14835,14837],{"class":3178,"line":3630},[3180,14832,11245],{"class":3872},[3180,14834,3877],{"class":3876},[3180,14836,4060],{"class":3926},[3180,14838,14839],{"class":4183},"  # Занадто мало! nginx не встигне стартувати\n",[3180,14841,14842,14844,14846],{"class":3178,"line":3636},[3180,14843,11254],{"class":3872},[3180,14845,3877],{"class":3876},[3180,14847,13537],{"class":3926},[3180,14849,14850,14852,14854],{"class":3178,"line":3641},[3180,14851,11272],{"class":3872},[3180,14853,3877],{"class":3876},[3180,14855,13537],{"class":3926},[3180,14857,14858,14860],{"class":3178,"line":3646},[3180,14859,11290],{"class":3872},[3180,14861,3899],{"class":3876},[3180,14863,14864,14866],{"class":3178,"line":3651},[3180,14865,11219],{"class":3872},[3180,14867,3899],{"class":3876},[3180,14869,14870,14872,14874],{"class":3178,"line":3657},[3180,14871,11226],{"class":3872},[3180,14873,3877],{"class":3876},[3180,14875,14820],{"class":3880},[3180,14877,14878,14880,14882],{"class":3178,"line":3663},[3180,14879,11236],{"class":3872},[3180,14881,3877],{"class":3876},[3180,14883,5647],{"class":3926},[3180,14885,14886,14888,14890],{"class":3178,"line":3669},[3180,14887,11245],{"class":3872},[3180,14889,3877],{"class":3876},[3180,14891,4730],{"class":3926},[3180,14893,14894,14896,14898],{"class":3178,"line":3675},[3180,14895,11254],{"class":3872},[3180,14897,3877],{"class":3876},[3180,14899,13537],{"class":3926},[3180,14901,14902,14904,14906],{"class":3178,"line":3681},[3180,14903,11272],{"class":3872},[3180,14905,3877],{"class":3876},[3180,14907,13537],{"class":3926},[3114,14909,14910],{},[3118,14911,13636],{},[3485,14913,14915],{"className":12572,"code":14914,"language":12574,"meta":3490,"style":3490},"# Створення з поганими налаштуваннями\nkubectl apply -f bad-probes-deployment.yaml\n\n# Спостереження (Pod буде постійно перезапускатись)\nkubectl get pods -w\n# NAME                               READY   STATUS    RESTARTS   AGE\n# bad-probes-test-xxx-yyy            0/1     Running   0          2s\n# bad-probes-test-xxx-yyy            0/1     Running   1          5s\n# bad-probes-test-xxx-yyy            0/1     CrashLoopBackOff   1   10s\n\n# Перегляд подій\nkubectl describe pod \u003Cpod-name>\n# Events:\n#   Warning  Unhealthy  10s (x3 over 14s)  kubelet  Liveness probe failed: Get \"http://10.244.0.15:80/\": dial tcp 10.244.0.15:80: connect: connection refused\n#   Warning  BackOff    5s (x2 over 10s)   kubelet  Back-off restarting failed container\n\n# Виправлення (збільшення initialDelaySeconds)\nkubectl edit deployment bad-probes-test\n# Змініть initialDelaySeconds на 10 для обох probes\n\n# Тепер Pod має стартувати успішно\nkubectl get pods\n# NAME                               READY   STATUS    RESTARTS   AGE\n# bad-probes-test-xxx-zzz            1/1     Running   0          15s\n\n# Очищення\nkubectl delete deployment bad-probes-test\n",[3429,14916,14917,14922,14933,14937,14941,14951,14956,14961,14966,14971,14975,14980,14994,14999,15004,15009,15013,15018,15029,15034,15038,15042,15050,15054,15059,15063,15067],{"__ignoreMap":3490},[3180,14918,14919],{"class":3178,"line":3495},[3180,14920,14921],{"class":4183},"# Створення з поганими налаштуваннями\n",[3180,14923,14924,14926,14928,14930],{"class":3178,"line":3501},[3180,14925,4054],{"class":8669},[3180,14927,13653],{"class":4430},[3180,14929,13186],{"class":4962},[3180,14931,14932],{"class":4430}," bad-probes-deployment.yaml\n",[3180,14934,14935],{"class":3178,"line":3507},[3180,14936,3517],{"emptyLinePlaceholder":3516},[3180,14938,14939],{"class":3178,"line":3513},[3180,14940,14127],{"class":4183},[3180,14942,14943,14945,14947,14949],{"class":3178,"line":3520},[3180,14944,4054],{"class":8669},[3180,14946,13270],{"class":4430},[3180,14948,13797],{"class":4430},[3180,14950,14138],{"class":4962},[3180,14952,14953],{"class":3178,"line":3526},[3180,14954,14955],{"class":4183},"# NAME                               READY   STATUS    RESTARTS   AGE\n",[3180,14957,14958],{"class":3178,"line":3532},[3180,14959,14960],{"class":4183},"# bad-probes-test-xxx-yyy            0/1     Running   0          2s\n",[3180,14962,14963],{"class":3178,"line":3538},[3180,14964,14965],{"class":4183},"# bad-probes-test-xxx-yyy            0/1     Running   1          5s\n",[3180,14967,14968],{"class":3178,"line":3544},[3180,14969,14970],{"class":4183},"# bad-probes-test-xxx-yyy            0/1     CrashLoopBackOff   1   10s\n",[3180,14972,14973],{"class":3178,"line":3549},[3180,14974,3517],{"emptyLinePlaceholder":3516},[3180,14976,14977],{"class":3178,"line":3555},[3180,14978,14979],{"class":4183},"# Перегляд подій\n",[3180,14981,14982,14984,14986,14988,14990,14992],{"class":3178,"line":3561},[3180,14983,4054],{"class":8669},[3180,14985,14169],{"class":4430},[3180,14987,13779],{"class":4430},[3180,14989,13782],{"class":3876},[3180,14991,14176],{"class":4430},[3180,14993,13788],{"class":3876},[3180,14995,14996],{"class":3178,"line":3567},[3180,14997,14998],{"class":4183},"# Events:\n",[3180,15000,15001],{"class":3178,"line":3573},[3180,15002,15003],{"class":4183},"#   Warning  Unhealthy  10s (x3 over 14s)  kubelet  Liveness probe failed: Get \"http://10.244.0.15:80/\": dial tcp 10.244.0.15:80: connect: connection refused\n",[3180,15005,15006],{"class":3178,"line":3579},[3180,15007,15008],{"class":4183},"#   Warning  BackOff    5s (x2 over 10s)   kubelet  Back-off restarting failed container\n",[3180,15010,15011],{"class":3178,"line":3585},[3180,15012,3517],{"emptyLinePlaceholder":3516},[3180,15014,15015],{"class":3178,"line":3591},[3180,15016,15017],{"class":4183},"# Виправлення (збільшення initialDelaySeconds)\n",[3180,15019,15020,15022,15024,15026],{"class":3178,"line":3596},[3180,15021,4054],{"class":8669},[3180,15023,14209],{"class":4430},[3180,15025,13821],{"class":4430},[3180,15027,15028],{"class":4430}," bad-probes-test\n",[3180,15030,15031],{"class":3178,"line":3602},[3180,15032,15033],{"class":4183},"# Змініть initialDelaySeconds на 10 для обох probes\n",[3180,15035,15036],{"class":3178,"line":3607},[3180,15037,3517],{"emptyLinePlaceholder":3516},[3180,15039,15040],{"class":3178,"line":3612},[3180,15041,14228],{"class":4183},[3180,15043,15044,15046,15048],{"class":3178,"line":3618},[3180,15045,4054],{"class":8669},[3180,15047,13270],{"class":4430},[3180,15049,13301],{"class":4430},[3180,15051,15052],{"class":3178,"line":3624},[3180,15053,14955],{"class":4183},[3180,15055,15056],{"class":3178,"line":3630},[3180,15057,15058],{"class":4183},"# bad-probes-test-xxx-zzz            1/1     Running   0          15s\n",[3180,15060,15061],{"class":3178,"line":3636},[3180,15062,3517],{"emptyLinePlaceholder":3516},[3180,15064,15065],{"class":3178,"line":3641},[3180,15066,13812],{"class":4183},[3180,15068,15069,15071,15073,15075],{"class":3178,"line":3646},[3180,15070,4054],{"class":8669},[3180,15072,13183],{"class":4430},[3180,15074,13821],{"class":4430},[3180,15076,15028],{"class":4430},[3114,15078,15079],{},[3118,15080,15081],{},"Правильні налаштування:",[3485,15083,15085],{"className":3863,"code":15084,"language":3865,"meta":3490,"style":3490},"livenessProbe:\n  httpGet:\n    path: /\n    port: 80\n  initialDelaySeconds: 10  # Достатньо часу для старту\n  periodSeconds: 10\n  timeoutSeconds: 5\n  failureThreshold: 3\n\nreadinessProbe:\n  httpGet:\n    path: /\n    port: 80\n  initialDelaySeconds: 5   # Може бути менше за liveness\n  periodSeconds: 5\n  timeoutSeconds: 3\n  failureThreshold: 3\n",[3429,15086,15087,15093,15099,15107,15115,15125,15133,15142,15151,15155,15161,15167,15175,15183,15195,15203,15211],{"__ignoreMap":3490},[3180,15088,15089,15091],{"class":3178,"line":3495},[3180,15090,11793],{"class":3872},[3180,15092,3899],{"class":3876},[3180,15094,15095,15097],{"class":3178,"line":3501},[3180,15096,11800],{"class":3872},[3180,15098,3899],{"class":3876},[3180,15100,15101,15103,15105],{"class":3178,"line":3507},[3180,15102,11807],{"class":3872},[3180,15104,3877],{"class":3876},[3180,15106,14820],{"class":3880},[3180,15108,15109,15111,15113],{"class":3178,"line":3513},[3180,15110,11816],{"class":3872},[3180,15112,3877],{"class":3876},[3180,15114,5647],{"class":3926},[3180,15116,15117,15119,15121,15123],{"class":3178,"line":3520},[3180,15118,11825],{"class":3872},[3180,15120,3877],{"class":3876},[3180,15122,4969],{"class":3926},[3180,15124,11931],{"class":4183},[3180,15126,15127,15129,15131],{"class":3178,"line":3526},[3180,15128,11834],{"class":3872},[3180,15130,3877],{"class":3876},[3180,15132,4932],{"class":3926},[3180,15134,15135,15138,15140],{"class":3178,"line":3532},[3180,15136,15137],{"class":3872},"  timeoutSeconds",[3180,15139,3877],{"class":3876},[3180,15141,5273],{"class":3926},[3180,15143,15144,15147,15149],{"class":3178,"line":3538},[3180,15145,15146],{"class":3872},"  failureThreshold",[3180,15148,3877],{"class":3876},[3180,15150,3927],{"class":3926},[3180,15152,15153],{"class":3178,"line":3544},[3180,15154,3517],{"emptyLinePlaceholder":3516},[3180,15156,15157,15159],{"class":3178,"line":3549},[3180,15158,11847],{"class":3872},[3180,15160,3899],{"class":3876},[3180,15162,15163,15165],{"class":3178,"line":3555},[3180,15164,11800],{"class":3872},[3180,15166,3899],{"class":3876},[3180,15168,15169,15171,15173],{"class":3178,"line":3561},[3180,15170,11807],{"class":3872},[3180,15172,3877],{"class":3876},[3180,15174,14820],{"class":3880},[3180,15176,15177,15179,15181],{"class":3178,"line":3567},[3180,15178,11816],{"class":3872},[3180,15180,3877],{"class":3876},[3180,15182,5647],{"class":3926},[3180,15184,15185,15187,15189,15192],{"class":3178,"line":3573},[3180,15186,11825],{"class":3872},[3180,15188,3877],{"class":3876},[3180,15190,15191],{"class":3926},"5",[3180,15193,15194],{"class":4183},"   # Може бути менше за liveness\n",[3180,15196,15197,15199,15201],{"class":3178,"line":3579},[3180,15198,11834],{"class":3872},[3180,15200,3877],{"class":3876},[3180,15202,5273],{"class":3926},[3180,15204,15205,15207,15209],{"class":3178,"line":3585},[3180,15206,15137],{"class":3872},[3180,15208,3877],{"class":3876},[3180,15210,3927],{"class":3926},[3180,15212,15213,15215,15217],{"class":3178,"line":3591},[3180,15214,15146],{"class":3872},[3180,15216,3877],{"class":3876},[3180,15218,3927],{"class":3926},[3382,15220],{},[3126,15222,15224],{"id":15223},"завдання-5-декларативне-управління-через-git","Завдання 5: Декларативне управління через Git",[3114,15226,15227,15229],{},[3118,15228,13210],{}," Навчитись використовувати Git для версійного контролю Kubernetes маніфестів.",[3114,15231,15232],{},[3118,15233,13216],{},[3134,15235,15236,15239,15242,15247,15250,15255,15261],{},[3137,15237,15238],{},"Створіть Git репозиторій для Kubernetes маніфестів",[3137,15240,15241],{},"Додайте Deployment YAML з 3 репліками",[3137,15243,15244,15245],{},"Зробіть commit та застосуйте через ",[3429,15246,5657],{},[3137,15248,15249],{},"Змініть кількість реплік на 5 у YAML, зробіть commit",[3137,15251,15252,15253],{},"Застосуйте зміни через ",[3429,15254,5657],{},[3137,15256,13247,15257,15260],{},[3429,15258,15259],{},"git log"," для перегляду історії змін",[3137,15262,15263,15264,15266],{},"Поверніться до попередньої версії через ",[3429,15265,7501],{}," та застосуйте",[3114,15268,15269],{},[3118,15270,13258],{},[3114,15272,15273],{},"Ви матимете повну історію змін у Git, зможете відстежувати, хто і коли змінював конфігурацію, та легко повертатись до попередніх версій.",[13333,15275,15276,15280,15734,15739],{"title":13335},[3114,15277,15278],{},[3118,15279,13636],{},[3485,15281,15283],{"className":12572,"code":15282,"language":12574,"meta":3490,"style":3490},"# Створення Git репозиторію\nmkdir k8s-manifests\ncd k8s-manifests\ngit init\n\n# Створення першої версії Deployment\ncat > nginx-deployment.yaml \u003C\u003C'YAML'\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx-deployment\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.27\n          ports:\n            - containerPort: 80\nYAML\n\n# Commit\ngit add nginx-deployment.yaml\ngit commit -m \"Initial deployment: 3 replicas\"\n\n# Застосування\nkubectl apply -f nginx-deployment.yaml\n\n# Перевірка\nkubectl get deployments\n# NAME               READY   UP-TO-DATE   AVAILABLE   AGE\n# nginx-deployment   3/3     3            3           10s\n\n# Зміна кількості реплік\nsed -i.bak 's/replicas: 3/replicas: 5/' nginx-deployment.yaml\n\n# Commit\ngit add nginx-deployment.yaml\ngit commit -m \"Scale up to 5 replicas\"\n\n# Застосування\nkubectl apply -f nginx-deployment.yaml\n\n# Перевірка\nkubectl get deployments\n# NAME               READY   UP-TO-DATE   AVAILABLE   AGE\n# nginx-deployment   5/5     5            5           1m\n\n# Перегляд історії\ngit log --oneline\n# a1b2c3d Scale up to 5 replicas\n# d4e5f6g Initial deployment: 3 replicas\n\n# Повернення до попередньої версії\ngit revert HEAD --no-edit\n\n# Застосування\nkubectl apply -f nginx-deployment.yaml\n\n# Перевірка (знову 3 репліки)\nkubectl get deployments\n# NAME               READY   UP-TO-DATE   AVAILABLE   AGE\n# nginx-deployment   3/3     3            3           2m\n\n# Очищення\nkubectl delete -f nginx-deployment.yaml\ncd ..\nrm -rf k8s-manifests\n",[3429,15284,15285,15290,15298,15305,15313,15317,15322,15338,15343,15348,15353,15358,15363,15368,15373,15378,15383,15388,15393,15398,15403,15408,15413,15418,15423,15428,15433,15438,15442,15447,15456,15469,15473,15478,15488,15492,15496,15504,15508,15513,15517,15522,15535,15539,15543,15551,15562,15566,15570,15580,15584,15588,15596,15600,15605,15609,15614,15624,15629,15634,15638,15643,15656,15660,15664,15674,15678,15683,15691,15695,15699,15703,15707,15717,15724],{"__ignoreMap":3490},[3180,15286,15287],{"class":3178,"line":3495},[3180,15288,15289],{"class":4183},"# Створення Git репозиторію\n",[3180,15291,15292,15295],{"class":3178,"line":3501},[3180,15293,15294],{"class":8669},"mkdir",[3180,15296,15297],{"class":4430}," k8s-manifests\n",[3180,15299,15300,15303],{"class":3178,"line":3507},[3180,15301,15302],{"class":8669},"cd",[3180,15304,15297],{"class":4430},[3180,15306,15307,15310],{"class":3178,"line":3513},[3180,15308,15309],{"class":8669},"git",[3180,15311,15312],{"class":4430}," init\n",[3180,15314,15315],{"class":3178,"line":3520},[3180,15316,3517],{"emptyLinePlaceholder":3516},[3180,15318,15319],{"class":3178,"line":3526},[3180,15320,15321],{"class":4183},"# Створення першої версії Deployment\n",[3180,15323,15324,15327,15330,15332,15335],{"class":3178,"line":3532},[3180,15325,15326],{"class":8669},"cat",[3180,15328,15329],{"class":3876}," > ",[3180,15331,5491],{"class":4430},[3180,15333,15334],{"class":3876}," \u003C\u003C",[3180,15336,15337],{"class":3876},"'YAML'\n",[3180,15339,15340],{"class":3178,"line":3538},[3180,15341,15342],{"class":4430},"apiVersion: apps/v1\n",[3180,15344,15345],{"class":3178,"line":3544},[3180,15346,15347],{"class":4430},"kind: Deployment\n",[3180,15349,15350],{"class":3178,"line":3549},[3180,15351,15352],{"class":4430},"metadata:\n",[3180,15354,15355],{"class":3178,"line":3555},[3180,15356,15357],{"class":4430},"  name: nginx-deployment\n",[3180,15359,15360],{"class":3178,"line":3561},[3180,15361,15362],{"class":4430},"spec:\n",[3180,15364,15365],{"class":3178,"line":3567},[3180,15366,15367],{"class":4430},"  replicas: 3\n",[3180,15369,15370],{"class":3178,"line":3573},[3180,15371,15372],{"class":4430},"  selector:\n",[3180,15374,15375],{"class":3178,"line":3579},[3180,15376,15377],{"class":4430},"    matchLabels:\n",[3180,15379,15380],{"class":3178,"line":3585},[3180,15381,15382],{"class":4430},"      app: nginx\n",[3180,15384,15385],{"class":3178,"line":3591},[3180,15386,15387],{"class":4430},"  template:\n",[3180,15389,15390],{"class":3178,"line":3596},[3180,15391,15392],{"class":4430},"    metadata:\n",[3180,15394,15395],{"class":3178,"line":3602},[3180,15396,15397],{"class":4430},"      labels:\n",[3180,15399,15400],{"class":3178,"line":3607},[3180,15401,15402],{"class":4430},"        app: nginx\n",[3180,15404,15405],{"class":3178,"line":3612},[3180,15406,15407],{"class":4430},"    spec:\n",[3180,15409,15410],{"class":3178,"line":3618},[3180,15411,15412],{"class":4430},"      containers:\n",[3180,15414,15415],{"class":3178,"line":3624},[3180,15416,15417],{"class":4430},"        - name: nginx\n",[3180,15419,15420],{"class":3178,"line":3630},[3180,15421,15422],{"class":4430},"          image: nginx:1.27\n",[3180,15424,15425],{"class":3178,"line":3636},[3180,15426,15427],{"class":4430},"          ports:\n",[3180,15429,15430],{"class":3178,"line":3641},[3180,15431,15432],{"class":4430},"            - containerPort: 80\n",[3180,15434,15435],{"class":3178,"line":3646},[3180,15436,15437],{"class":3876},"YAML\n",[3180,15439,15440],{"class":3178,"line":3651},[3180,15441,3517],{"emptyLinePlaceholder":3516},[3180,15443,15444],{"class":3178,"line":3657},[3180,15445,15446],{"class":4183},"# Commit\n",[3180,15448,15449,15451,15454],{"class":3178,"line":3663},[3180,15450,15309],{"class":8669},[3180,15452,15453],{"class":4430}," add",[3180,15455,13658],{"class":4430},[3180,15457,15458,15460,15463,15466],{"class":3178,"line":3669},[3180,15459,15309],{"class":8669},[3180,15461,15462],{"class":4430}," commit",[3180,15464,15465],{"class":4962}," -m",[3180,15467,15468],{"class":4430}," \"Initial deployment: 3 replicas\"\n",[3180,15470,15471],{"class":3178,"line":3675},[3180,15472,3517],{"emptyLinePlaceholder":3516},[3180,15474,15475],{"class":3178,"line":3681},[3180,15476,15477],{"class":4183},"# Застосування\n",[3180,15479,15480,15482,15484,15486],{"class":3178,"line":3687},[3180,15481,4054],{"class":8669},[3180,15483,13653],{"class":4430},[3180,15485,13186],{"class":4962},[3180,15487,13658],{"class":4430},[3180,15489,15490],{"class":3178,"line":3692},[3180,15491,3517],{"emptyLinePlaceholder":3516},[3180,15493,15494],{"class":3178,"line":3698},[3180,15495,13678],{"class":4183},[3180,15497,15498,15500,15502],{"class":3178,"line":3704},[3180,15499,4054],{"class":8669},[3180,15501,13270],{"class":4430},[3180,15503,13273],{"class":4430},[3180,15505,15506],{"class":3178,"line":3710},[3180,15507,13278],{"class":4183},[3180,15509,15510],{"class":3178,"line":3716},[3180,15511,15512],{"class":4183},"# nginx-deployment   3/3     3            3           10s\n",[3180,15514,15515],{"class":3178,"line":3722},[3180,15516,3517],{"emptyLinePlaceholder":3516},[3180,15518,15519],{"class":3178,"line":3728},[3180,15520,15521],{"class":4183},"# Зміна кількості реплік\n",[3180,15523,15524,15527,15530,15533],{"class":3178,"line":3734},[3180,15525,15526],{"class":8669},"sed",[3180,15528,15529],{"class":4962}," -i.bak",[3180,15531,15532],{"class":4430}," 's/replicas: 3/replicas: 5/'",[3180,15534,13658],{"class":4430},[3180,15536,15537],{"class":3178,"line":3739},[3180,15538,3517],{"emptyLinePlaceholder":3516},[3180,15540,15541],{"class":3178,"line":3745},[3180,15542,15446],{"class":4183},[3180,15544,15545,15547,15549],{"class":3178,"line":3751},[3180,15546,15309],{"class":8669},[3180,15548,15453],{"class":4430},[3180,15550,13658],{"class":4430},[3180,15552,15553,15555,15557,15559],{"class":3178,"line":3757},[3180,15554,15309],{"class":8669},[3180,15556,15462],{"class":4430},[3180,15558,15465],{"class":4962},[3180,15560,15561],{"class":4430}," \"Scale up to 5 replicas\"\n",[3180,15563,15564],{"class":3178,"line":3763},[3180,15565,3517],{"emptyLinePlaceholder":3516},[3180,15567,15568],{"class":3178,"line":3768},[3180,15569,15477],{"class":4183},[3180,15571,15572,15574,15576,15578],{"class":3178,"line":3773},[3180,15573,4054],{"class":8669},[3180,15575,13653],{"class":4430},[3180,15577,13186],{"class":4962},[3180,15579,13658],{"class":4430},[3180,15581,15582],{"class":3178,"line":3779},[3180,15583,3517],{"emptyLinePlaceholder":3516},[3180,15585,15586],{"class":3178,"line":3785},[3180,15587,13678],{"class":4183},[3180,15589,15590,15592,15594],{"class":3178,"line":3791},[3180,15591,4054],{"class":8669},[3180,15593,13270],{"class":4430},[3180,15595,13273],{"class":4430},[3180,15597,15598],{"class":3178,"line":3797},[3180,15599,13278],{"class":4183},[3180,15601,15602],{"class":3178,"line":3802},[3180,15603,15604],{"class":4183},"# nginx-deployment   5/5     5            5           1m\n",[3180,15606,15607],{"class":3178,"line":3807},[3180,15608,3517],{"emptyLinePlaceholder":3516},[3180,15610,15611],{"class":3178,"line":5443},[3180,15612,15613],{"class":4183},"# Перегляд історії\n",[3180,15615,15616,15618,15621],{"class":3178,"line":6772},[3180,15617,15309],{"class":8669},[3180,15619,15620],{"class":4430}," log",[3180,15622,15623],{"class":4962}," --oneline\n",[3180,15625,15626],{"class":3178,"line":6777},[3180,15627,15628],{"class":4183},"# a1b2c3d Scale up to 5 replicas\n",[3180,15630,15631],{"class":3178,"line":6782},[3180,15632,15633],{"class":4183},"# d4e5f6g Initial deployment: 3 replicas\n",[3180,15635,15636],{"class":3178,"line":6787},[3180,15637,3517],{"emptyLinePlaceholder":3516},[3180,15639,15640],{"class":3178,"line":6792},[3180,15641,15642],{"class":4183},"# Повернення до попередньої версії\n",[3180,15644,15645,15647,15650,15653],{"class":3178,"line":6797},[3180,15646,15309],{"class":8669},[3180,15648,15649],{"class":4430}," revert",[3180,15651,15652],{"class":4430}," HEAD",[3180,15654,15655],{"class":4962}," --no-edit\n",[3180,15657,15658],{"class":3178,"line":6802},[3180,15659,3517],{"emptyLinePlaceholder":3516},[3180,15661,15662],{"class":3178,"line":6808},[3180,15663,15477],{"class":4183},[3180,15665,15666,15668,15670,15672],{"class":3178,"line":6814},[3180,15667,4054],{"class":8669},[3180,15669,13653],{"class":4430},[3180,15671,13186],{"class":4962},[3180,15673,13658],{"class":4430},[3180,15675,15676],{"class":3178,"line":6820},[3180,15677,3517],{"emptyLinePlaceholder":3516},[3180,15679,15680],{"class":3178,"line":6825},[3180,15681,15682],{"class":4183},"# Перевірка (знову 3 репліки)\n",[3180,15684,15685,15687,15689],{"class":3178,"line":6830},[3180,15686,4054],{"class":8669},[3180,15688,13270],{"class":4430},[3180,15690,13273],{"class":4430},[3180,15692,15693],{"class":3178,"line":8075},[3180,15694,13278],{"class":4183},[3180,15696,15697],{"class":3178,"line":8080},[3180,15698,13283],{"class":4183},[3180,15700,15701],{"class":3178,"line":8086},[3180,15702,3517],{"emptyLinePlaceholder":3516},[3180,15704,15705],{"class":3178,"line":8092},[3180,15706,13812],{"class":4183},[3180,15708,15709,15711,15713,15715],{"class":3178,"line":8097},[3180,15710,4054],{"class":8669},[3180,15712,13183],{"class":4430},[3180,15714,13186],{"class":4962},[3180,15716,13658],{"class":4430},[3180,15718,15719,15721],{"class":3178,"line":8102},[3180,15720,15302],{"class":8669},[3180,15722,15723],{"class":4430}," ..\n",[3180,15725,15726,15729,15732],{"class":3178,"line":8107},[3180,15727,15728],{"class":8669},"rm",[3180,15730,15731],{"class":4962}," -rf",[3180,15733,15297],{"class":4430},[3114,15735,15736],{},[3118,15737,15738],{},"Переваги цього підходу:",[3134,15740,15741,15750,15756,15766,15772],{},[3137,15742,15743,15746,15747,15749],{},[3118,15744,15745],{},"Історія змін"," — ",[3429,15748,15259],{}," показує всі зміни з коментарями",[3137,15751,15752,15755],{},[3118,15753,15754],{},"Code review"," — зміни можна переглянути через Pull Request перед застосуванням",[3137,15757,15758,15760,15761,11385,15763],{},[3118,15759,3460],{}," — легко повернутись до будь-якої версії через ",[3429,15762,7501],{},[3429,15764,15765],{},"git checkout",[3137,15767,15768,15771],{},[3118,15769,15770],{},"Документація"," — Git commit messages документують причини змін",[3137,15773,15774,15777],{},[3118,15775,15776],{},"Collaboration"," — кілька людей можуть працювати над маніфестами одночасно",[3382,15779],{},[3109,15781,15783],{"id":15782},"резюме","Резюме",[3114,15785,15786,15787,15789],{},"У цій статті ми детально вивчили ",[3118,15788,3336],{}," — основний ресурс Kubernetes для управління застосунками у production. Ось що ми розглянули:",[3303,15791,15792,15796,15801,15805,15809,15812,15824,15829,15834,15839],{},[3306,15793,15795],{"icon":11711,"title":15794},"Проблема ручного управління Pod","Чому Pod не підходить для production: немає self-healing, масштабування, rolling updates та історії версій. Кожна операція вимагає ручного втручання.",[3306,15797,15800],{"icon":15798,"title":15799},"i-heroicons-cube","Що таке Deployment","Декларативний ресурс для управління набором ідентичних Pod. Працює за принципом reconciliation loop — постійно узгоджує бажаний стан з поточним.",[3306,15802,15804],{"icon":15803,"title":3473},"i-heroicons-squares-2x2","Deployment створює ReplicaSet, який створює Pod. Це дозволяє реалізувати rolling updates та rollback через управління кількома ReplicaSet.",[3306,15806,15808],{"icon":6923,"title":15807},"Анатомія YAML","Повна специфікація Deployment: metadata, selector, template, strategy, resources, probes. Кожне поле детально розібрано з прикладами.",[3306,15810,15811],{"icon":3441,"title":3442},"Автоматичне відновлення Pod при падінні або видаленні. ReplicaSet Controller постійно підтримує задану кількість реплік без вашого втручання.",[3306,15813,15815,15817,15818,15820,15821,15823],{"icon":3313,"title":15814},"Масштабування (3 способи)",[3429,15816,3450],{}," (швидко), ",[3429,15819,5657],{}," (декларативно), ",[3429,15822,7519],{}," (інтерактивно). Кожен спосіб має свої переваги та недоліки.",[3306,15825,15828],{"icon":15826,"title":15827},"i-heroicons-code-bracket","Практичний приклад: TodoApi","Повний цикл розробки: ASP.NET Core Minimal API, Dockerfile, Deployment YAML, розгортання у Minikube, тестування через port-forward.",[3306,15830,15833],{"icon":15831,"title":15832},"i-heroicons-heart","Health Checks","Liveness probe (перевірка живості) та readiness probe (перевірка готовності). Різниця між ними та правильні налаштування для уникнення CrashLoopBackOff.",[3306,15835,15838],{"icon":15836,"title":15837},"i-heroicons-cpu-chip","Resource Management","Requests (гарантовані ресурси) та limits (максимальні ресурси). Що відбувається при перевищенні: CPU throttling та OOMKilled.",[3306,15840,15842],{"icon":15841,"title":13198},"i-heroicons-academic-cap","5 завдань для закріплення знань: різні образи, експерименти з ресурсами, масштабування під навантаженням, налаштування health checks, Git workflow.",[3126,15844,15846],{"id":15845},"ключові-висновки","Ключові висновки",[3134,15848,15849,15855,15861,15867,15873,15879,15885],{},[3137,15850,15851,15854],{},[3118,15852,15853],{},"Завжди використовуйте Deployment у production"," — ніколи не створюйте Pod напряму. Deployment надає self-healing, масштабування та rolling updates.",[3137,15856,15857,15860],{},[3118,15858,15859],{},"Декларативний підхід"," — описуйте бажаний стан у YAML, а Kubernetes автоматично досягає його. Зберігайте YAML у Git для версійного контролю.",[3137,15862,15863,15866],{},[3118,15864,15865],{},"Reconciliation loop"," — фундаментальний принцип Kubernetes. Контролери постійно порівнюють бажаний стан з поточним та усувають розбіжності.",[3137,15868,15869,15872],{},[3118,15870,15871],{},"ReplicaSet — проміжний шар"," — Deployment не створює Pod напряму. Він використовує ReplicaSet для підтримки кількості реплік та реалізації rolling updates.",[3137,15874,15875,15878],{},[3118,15876,15877],{},"Health checks критично важливі"," — правильно налаштовані liveness та readiness probes гарантують стабільність застосунку. Неправильні налаштування призводять до CrashLoopBackOff.",[3137,15880,15881,15884],{},[3118,15882,15883],{},"Resource requests та limits"," — завжди вказуйте ресурси для Pod. Requests використовуються для scheduling, limits запобігають перевитраті ресурсів.",[3137,15886,15887,15890],{},[3118,15888,15889],{},"Масштабування — це просто"," — змінити кількість реплік можна однією командою або редагуванням YAML. Kubernetes автоматично створює або видаляє Pod.",[3126,15892,15894],{"id":15893},"що-далі","Що далі?",[3114,15896,15897,15898,15901],{},"У наступній статті ",[3118,15899,15900],{},"\"Rolling Updates та управління життєвим циклом Deployment\""," ми детально розглянемо:",[4763,15903,15904,15907,15910,15913,15916,15919],{},[3137,15905,15906],{},"Як працює rolling update (покрокова візуалізація)",[3137,15908,15909],{},"Стратегії оновлення: RollingUpdate vs Recreate",[3137,15911,15912],{},"Параметри: maxSurge, maxUnavailable, progressDeadlineSeconds, minReadySeconds (з математичними розрахунками)",[3137,15914,15915],{},"Rollback та історія версій",[3137,15917,15918],{},"Оновлення TodoApi з v1.0 на v2.0 з реальними змінами в коді",[3137,15920,15921],{},"Troubleshooting типових проблем",[3114,15923,15924],{},"Ви навчитесь виконувати оновлення застосунків без downtime та швидко повертатись до попередніх версій у разі проблем.",[3382,15926],{},[3109,15928,15930],{"id":15929},"корисні-команди","Корисні команди",[3114,15932,15933],{},"Для швидкого доступу — всі команди, які ми використовували у цій статті:",[15935,15936,15937,16068,16143,16240,16310,16376],"code-group",{},[3485,15938,15941],{"className":12572,"code":15939,"filename":15940,"language":12574,"meta":3490,"style":3490},"# Створення Deployment\nkubectl apply -f deployment.yaml\n\n# Перегляд Deployment\nkubectl get deployments\nkubectl get deploy  # скорочена форма\n\n# Детальна інформація\nkubectl describe deployment \u003Cname>\n\n# Перегляд Pod\nkubectl get pods\nkubectl get pods -l app=myapp  # з фільтром за міткою\n\n# Детальна інформація про Pod\nkubectl describe pod \u003Cpod-name>\n","Створення та перегляд",[3429,15942,15943,15948,15959,15963,15968,15976,15988,15992,15997,16012,16016,16021,16029,16045,16049,16054],{"__ignoreMap":3490},[3180,15944,15945],{"class":3178,"line":3495},[3180,15946,15947],{"class":4183},"# Створення Deployment\n",[3180,15949,15950,15952,15954,15956],{"class":3178,"line":3501},[3180,15951,4054],{"class":8669},[3180,15953,13653],{"class":4430},[3180,15955,13186],{"class":4962},[3180,15957,15958],{"class":4430}," deployment.yaml\n",[3180,15960,15961],{"class":3178,"line":3507},[3180,15962,3517],{"emptyLinePlaceholder":3516},[3180,15964,15965],{"class":3178,"line":3513},[3180,15966,15967],{"class":4183},"# Перегляд Deployment\n",[3180,15969,15970,15972,15974],{"class":3178,"line":3520},[3180,15971,4054],{"class":8669},[3180,15973,13270],{"class":4430},[3180,15975,13273],{"class":4430},[3180,15977,15978,15980,15982,15985],{"class":3178,"line":3526},[3180,15979,4054],{"class":8669},[3180,15981,13270],{"class":4430},[3180,15983,15984],{"class":4430}," deploy",[3180,15986,15987],{"class":4183},"  # скорочена форма\n",[3180,15989,15990],{"class":3178,"line":3532},[3180,15991,3517],{"emptyLinePlaceholder":3516},[3180,15993,15994],{"class":3178,"line":3538},[3180,15995,15996],{"class":4183},"# Детальна інформація\n",[3180,15998,15999,16001,16003,16005,16007,16010],{"class":3178,"line":3544},[3180,16000,4054],{"class":8669},[3180,16002,14169],{"class":4430},[3180,16004,13821],{"class":4430},[3180,16006,13782],{"class":3876},[3180,16008,16009],{"class":4430},"nam",[3180,16011,13788],{"class":3876},[3180,16013,16014],{"class":3178,"line":3549},[3180,16015,3517],{"emptyLinePlaceholder":3516},[3180,16017,16018],{"class":3178,"line":3555},[3180,16019,16020],{"class":4183},"# Перегляд Pod\n",[3180,16022,16023,16025,16027],{"class":3178,"line":3561},[3180,16024,4054],{"class":8669},[3180,16026,13270],{"class":4430},[3180,16028,13301],{"class":4430},[3180,16030,16031,16033,16035,16037,16039,16042],{"class":3178,"line":3567},[3180,16032,4054],{"class":8669},[3180,16034,13270],{"class":4430},[3180,16036,13797],{"class":4430},[3180,16038,14487],{"class":4962},[3180,16040,16041],{"class":4430}," app=myapp",[3180,16043,16044],{"class":4183},"  # з фільтром за міткою\n",[3180,16046,16047],{"class":3178,"line":3573},[3180,16048,3517],{"emptyLinePlaceholder":3516},[3180,16050,16051],{"class":3178,"line":3579},[3180,16052,16053],{"class":4183},"# Детальна інформація про Pod\n",[3180,16055,16056,16058,16060,16062,16064,16066],{"class":3178,"line":3585},[3180,16057,4054],{"class":8669},[3180,16059,14169],{"class":4430},[3180,16061,13779],{"class":4430},[3180,16063,13782],{"class":3876},[3180,16065,14176],{"class":4430},[3180,16067,13788],{"class":3876},[3485,16069,16071],{"className":12572,"code":16070,"filename":3165,"language":12574,"meta":3490,"style":3490},"# Масштабування через kubectl scale\nkubectl scale deployment \u003Cname> --replicas=5\n\n# Масштабування через kubectl edit\nkubectl edit deployment \u003Cname>\n\n# Масштабування через kubectl apply\n# (змініть replicas у YAML та застосуйте)\nkubectl apply -f deployment.yaml\n",[3429,16072,16073,16078,16096,16100,16105,16119,16123,16128,16133],{"__ignoreMap":3490},[3180,16074,16075],{"class":3178,"line":3495},[3180,16076,16077],{"class":4183},"# Масштабування через kubectl scale\n",[3180,16079,16080,16082,16084,16086,16088,16090,16093],{"class":3178,"line":3501},[3180,16081,4054],{"class":8669},[3180,16083,14375],{"class":4430},[3180,16085,13821],{"class":4430},[3180,16087,13782],{"class":3876},[3180,16089,16009],{"class":4430},[3180,16091,16092],{"class":3876},"e> ",[3180,16094,16095],{"class":4962},"--replicas=5\n",[3180,16097,16098],{"class":3178,"line":3507},[3180,16099,3517],{"emptyLinePlaceholder":3516},[3180,16101,16102],{"class":3178,"line":3513},[3180,16103,16104],{"class":4183},"# Масштабування через kubectl edit\n",[3180,16106,16107,16109,16111,16113,16115,16117],{"class":3178,"line":3520},[3180,16108,4054],{"class":8669},[3180,16110,14209],{"class":4430},[3180,16112,13821],{"class":4430},[3180,16114,13782],{"class":3876},[3180,16116,16009],{"class":4430},[3180,16118,13788],{"class":3876},[3180,16120,16121],{"class":3178,"line":3526},[3180,16122,3517],{"emptyLinePlaceholder":3516},[3180,16124,16125],{"class":3178,"line":3532},[3180,16126,16127],{"class":4183},"# Масштабування через kubectl apply\n",[3180,16129,16130],{"class":3178,"line":3538},[3180,16131,16132],{"class":4183},"# (змініть replicas у YAML та застосуйте)\n",[3180,16134,16135,16137,16139,16141],{"class":3178,"line":3544},[3180,16136,4054],{"class":8669},[3180,16138,13653],{"class":4430},[3180,16140,13186],{"class":4962},[3180,16142,15958],{"class":4430},[3485,16144,16147],{"className":12572,"code":16145,"filename":16146,"language":12574,"meta":3490,"style":3490},"# Логи одного Pod\nkubectl logs \u003Cpod-name>\n\n# Логи з follow (реальний час)\nkubectl logs -f \u003Cpod-name>\n\n# Логи всіх Pod з міткою\nkubectl logs -l app=myapp --tail=20\n\n# Моніторинг ресурсів\nkubectl top pods\nkubectl top pods -l app=myapp\n","Логи та моніторинг",[3429,16148,16149,16154,16166,16170,16175,16189,16193,16198,16210,16214,16219,16227],{"__ignoreMap":3490},[3180,16150,16151],{"class":3178,"line":3495},[3180,16152,16153],{"class":4183},"# Логи одного Pod\n",[3180,16155,16156,16158,16160,16162,16164],{"class":3178,"line":3501},[3180,16157,4054],{"class":8669},[3180,16159,14508],{"class":4430},[3180,16161,13782],{"class":3876},[3180,16163,14176],{"class":4430},[3180,16165,13788],{"class":3876},[3180,16167,16168],{"class":3178,"line":3507},[3180,16169,3517],{"emptyLinePlaceholder":3516},[3180,16171,16172],{"class":3178,"line":3513},[3180,16173,16174],{"class":4183},"# Логи з follow (реальний час)\n",[3180,16176,16177,16179,16181,16183,16185,16187],{"class":3178,"line":3520},[3180,16178,4054],{"class":8669},[3180,16180,14508],{"class":4430},[3180,16182,13186],{"class":4962},[3180,16184,13782],{"class":3876},[3180,16186,14176],{"class":4430},[3180,16188,13788],{"class":3876},[3180,16190,16191],{"class":3178,"line":3526},[3180,16192,3517],{"emptyLinePlaceholder":3516},[3180,16194,16195],{"class":3178,"line":3532},[3180,16196,16197],{"class":4183},"# Логи всіх Pod з міткою\n",[3180,16199,16200,16202,16204,16206,16208],{"class":3178,"line":3538},[3180,16201,4054],{"class":8669},[3180,16203,14508],{"class":4430},[3180,16205,14487],{"class":4962},[3180,16207,16041],{"class":4430},[3180,16209,14515],{"class":4962},[3180,16211,16212],{"class":3178,"line":3544},[3180,16213,3517],{"emptyLinePlaceholder":3516},[3180,16215,16216],{"class":3178,"line":3549},[3180,16217,16218],{"class":4183},"# Моніторинг ресурсів\n",[3180,16220,16221,16223,16225],{"class":3178,"line":3555},[3180,16222,4054],{"class":8669},[3180,16224,14262],{"class":4430},[3180,16226,13301],{"class":4430},[3180,16228,16229,16231,16233,16235,16237],{"class":3178,"line":3561},[3180,16230,4054],{"class":8669},[3180,16232,14262],{"class":4430},[3180,16234,13797],{"class":4430},[3180,16236,14487],{"class":4962},[3180,16238,16239],{"class":4430}," app=myapp\n",[3485,16241,16244],{"className":12572,"code":16242,"filename":16243,"language":12574,"meta":3490,"style":3490},"# Port-forward на Deployment\nkubectl port-forward deployment/\u003Cname> 8080:8080\n\n# Port-forward на конкретний Pod\nkubectl port-forward pod/\u003Cpod-name> 8080:8080\n\n# Тестування через curl\ncurl http://localhost:8080/health\n","Port-forward та тестування",[3429,16245,16246,16251,16269,16273,16278,16295,16299,16304],{"__ignoreMap":3490},[3180,16247,16248],{"class":3178,"line":3495},[3180,16249,16250],{"class":4183},"# Port-forward на Deployment\n",[3180,16252,16253,16255,16257,16260,16262,16264,16266],{"class":3178,"line":3501},[3180,16254,4054],{"class":8669},[3180,16256,13710],{"class":4430},[3180,16258,16259],{"class":4430}," deployment/",[3180,16261,8748],{"class":3876},[3180,16263,16009],{"class":4430},[3180,16265,16092],{"class":3876},[3180,16267,16268],{"class":4430},"8080:8080\n",[3180,16270,16271],{"class":3178,"line":3507},[3180,16272,3517],{"emptyLinePlaceholder":3516},[3180,16274,16275],{"class":3178,"line":3513},[3180,16276,16277],{"class":4183},"# Port-forward на конкретний Pod\n",[3180,16279,16280,16282,16284,16287,16289,16291,16293],{"class":3178,"line":3520},[3180,16281,4054],{"class":8669},[3180,16283,13710],{"class":4430},[3180,16285,16286],{"class":4430}," pod/",[3180,16288,8748],{"class":3876},[3180,16290,14176],{"class":4430},[3180,16292,16092],{"class":3876},[3180,16294,16268],{"class":4430},[3180,16296,16297],{"class":3178,"line":3526},[3180,16298,3517],{"emptyLinePlaceholder":3516},[3180,16300,16301],{"class":3178,"line":3532},[3180,16302,16303],{"class":4183},"# Тестування через curl\n",[3180,16305,16306,16308],{"class":3178,"line":3538},[3180,16307,12586],{"class":8669},[3180,16309,14445],{"class":4430},[3485,16311,16314],{"className":12572,"code":16312,"filename":16313,"language":12574,"meta":3490,"style":3490},"# Видалення Deployment (також видаляє ReplicaSet та Pod)\nkubectl delete deployment \u003Cname>\n\n# Видалення через файл\nkubectl delete -f deployment.yaml\n\n# Видалення всіх ресурсів з міткою\nkubectl delete all -l app=myapp\n","Видалення",[3429,16315,16316,16321,16335,16339,16344,16354,16358,16363],{"__ignoreMap":3490},[3180,16317,16318],{"class":3178,"line":3495},[3180,16319,16320],{"class":4183},"# Видалення Deployment (також видаляє ReplicaSet та Pod)\n",[3180,16322,16323,16325,16327,16329,16331,16333],{"class":3178,"line":3501},[3180,16324,4054],{"class":8669},[3180,16326,13183],{"class":4430},[3180,16328,13821],{"class":4430},[3180,16330,13782],{"class":3876},[3180,16332,16009],{"class":4430},[3180,16334,13788],{"class":3876},[3180,16336,16337],{"class":3178,"line":3507},[3180,16338,3517],{"emptyLinePlaceholder":3516},[3180,16340,16341],{"class":3178,"line":3513},[3180,16342,16343],{"class":4183},"# Видалення через файл\n",[3180,16345,16346,16348,16350,16352],{"class":3178,"line":3520},[3180,16347,4054],{"class":8669},[3180,16349,13183],{"class":4430},[3180,16351,13186],{"class":4962},[3180,16353,15958],{"class":4430},[3180,16355,16356],{"class":3178,"line":3526},[3180,16357,3517],{"emptyLinePlaceholder":3516},[3180,16359,16360],{"class":3178,"line":3532},[3180,16361,16362],{"class":4183},"# Видалення всіх ресурсів з міткою\n",[3180,16364,16365,16367,16369,16372,16374],{"class":3178,"line":3538},[3180,16366,4054],{"class":8669},[3180,16368,13183],{"class":4430},[3180,16370,16371],{"class":4430}," all",[3180,16373,14487],{"class":4962},[3180,16375,16239],{"class":4430},[3485,16377,16379],{"className":12572,"code":16378,"filename":7742,"language":12574,"meta":3490,"style":3490},"# Перегляд подій\nkubectl get events --sort-by=.metadata.creationTimestamp\n\n# Виконання команди всередині Pod\nkubectl exec -it \u003Cpod-name> -- /bin/bash\n\n# Копіювання файлів з/до Pod\nkubectl cp \u003Cpod-name>:/path/to/file ./local-file\nkubectl cp ./local-file \u003Cpod-name>:/path/to/file\n",[3429,16380,16381,16385,16397,16401,16406,16428,16432,16437,16457],{"__ignoreMap":3490},[3180,16382,16383],{"class":3178,"line":3495},[3180,16384,14979],{"class":4183},[3180,16386,16387,16389,16391,16394],{"class":3178,"line":3501},[3180,16388,4054],{"class":8669},[3180,16390,13270],{"class":4430},[3180,16392,16393],{"class":4430}," events",[3180,16395,16396],{"class":4962}," --sort-by=.metadata.creationTimestamp\n",[3180,16398,16399],{"class":3178,"line":3507},[3180,16400,3517],{"emptyLinePlaceholder":3516},[3180,16402,16403],{"class":3178,"line":3513},[3180,16404,16405],{"class":4183},"# Виконання команди всередині Pod\n",[3180,16407,16408,16410,16413,16416,16418,16420,16422,16425],{"class":3178,"line":3520},[3180,16409,4054],{"class":8669},[3180,16411,16412],{"class":4430}," exec",[3180,16414,16415],{"class":4962}," -it",[3180,16417,13782],{"class":3876},[3180,16419,14176],{"class":4430},[3180,16421,16092],{"class":3876},[3180,16423,16424],{"class":4962},"--",[3180,16426,16427],{"class":4430}," /bin/bash\n",[3180,16429,16430],{"class":3178,"line":3526},[3180,16431,3517],{"emptyLinePlaceholder":3516},[3180,16433,16434],{"class":3178,"line":3532},[3180,16435,16436],{"class":4183},"# Копіювання файлів з/до Pod\n",[3180,16438,16439,16441,16444,16446,16448,16451,16454],{"class":3178,"line":3538},[3180,16440,4054],{"class":8669},[3180,16442,16443],{"class":4430}," cp",[3180,16445,13782],{"class":3876},[3180,16447,14176],{"class":4430},[3180,16449,16450],{"class":3876},"e>",[3180,16452,16453],{"class":4430},":/path/to/file",[3180,16455,16456],{"class":4430}," ./local-file\n",[3180,16458,16459,16461,16463,16466,16468,16470,16472],{"class":3178,"line":3544},[3180,16460,4054],{"class":8669},[3180,16462,16443],{"class":4430},[3180,16464,16465],{"class":4430}," ./local-file",[3180,16467,13782],{"class":3876},[3180,16469,14176],{"class":4430},[3180,16471,16450],{"class":3876},[3180,16473,16474],{"class":4430},":/path/to/file\n",[3382,16476],{},[3109,16478,16480],{"id":16479},"додаткові-ресурси","Додаткові ресурси",[3303,16482,16483,16490,16495,16501],{},[3306,16484,16489],{"icon":16485,"title":16486,"target":16487,"to":16488},"i-heroicons-book-open","Офіційна документація Kubernetes","_blank","https://kubernetes.io/docs/concepts/workloads/controllers/deployment/","Повна документація про Deployment з усіма полями та прикладами.",[3306,16491,16494],{"icon":15826,"title":16492,"target":16487,"to":16493},"API Reference: Deployment","https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/","Детальна специфікація API для Deployment v1 (apps/v1).",[3306,16496,16500],{"icon":16497,"title":16498,"target":16487,"to":16499},"i-heroicons-light-bulb","Best Practices: Resource Management","https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/","Рекомендації щодо налаштування resource requests та limits.",[3306,16502,16505],{"icon":15831,"title":16503,"target":16487,"to":16504},"Configure Liveness, Readiness and Startup Probes","https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/","Детальний гайд з налаштування health checks для Pod.",[3382,16507],{},[3114,16509,16510,3192,16513],{},[3118,16511,16512],{},"Наступна стаття:",[16514,16515,2767],"a",{"href":2768},[16517,16518,16519],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sCDza, html code.shiki .sCDza{--shiki-light:#AF00DB;--shiki-default:#CE92A4;--shiki-dark:#CE92A4}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .s0P7L, html code.shiki .s0P7L{--shiki-light:#800000;--shiki-default:#808080;--shiki-dark:#808080}html pre.shiki code .sa4r_, html code.shiki .sa4r_{--shiki-light:#E50000;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}",{"title":3490,"searchDepth":3501,"depth":3501,"links":16521},[16522,16526,16530,16536,16543,16547,16551,16560,16574,16575,16582,16586,16587],{"id":3111,"depth":3501,"text":3112,"children":16523},[16524,16525],{"id":3128,"depth":3507,"text":3129},{"id":3339,"depth":3507,"text":3340},{"id":3386,"depth":3501,"text":3387,"children":16527},[16528,16529],{"id":3435,"depth":3507,"text":3436},{"id":3472,"depth":3507,"text":3473},{"id":3849,"depth":3501,"text":3850,"children":16531},[16532,16533,16534,16535],{"id":3856,"depth":3507,"text":3857},{"id":4134,"depth":3507,"text":4135},{"id":4348,"depth":3507,"text":4349},{"id":5041,"depth":3507,"text":5042},{"id":5477,"depth":3501,"text":5478,"children":16537},[16538,16539,16540,16541,16542],{"id":5484,"depth":3507,"text":5485},{"id":5650,"depth":3507,"text":5651},{"id":5677,"depth":3507,"text":5678},{"id":5752,"depth":3507,"text":5753},{"id":5820,"depth":3507,"text":5821},{"id":5954,"depth":3501,"text":5955,"children":16544},[16545,16546],{"id":5964,"depth":3507,"text":5965},{"id":6227,"depth":3507,"text":6228},{"id":6333,"depth":3501,"text":6334,"children":16548},[16549,16550],{"id":6344,"depth":3507,"text":6345},{"id":6506,"depth":3507,"text":6507},{"id":6868,"depth":3501,"text":6869,"children":16552},[16553,16554,16555,16556,16557,16558,16559],{"id":6879,"depth":3507,"text":6880},{"id":6909,"depth":3507,"text":6910},{"id":6938,"depth":3507,"text":6939},{"id":7210,"depth":3507,"text":7211},{"id":7512,"depth":3507,"text":7513},{"id":7680,"depth":3507,"text":7681},{"id":7758,"depth":3507,"text":7759},{"id":8330,"depth":3501,"text":8331,"children":16561},[16562,16563,16564,16565,16566,16567,16568,16569,16570,16571,16572,16573],{"id":8337,"depth":3507,"text":8338},{"id":8593,"depth":3507,"text":8594},{"id":10375,"depth":3507,"text":10376},{"id":10679,"depth":3507,"text":10680},{"id":10845,"depth":3507,"text":10846},{"id":11491,"depth":3507,"text":11492},{"id":11971,"depth":3507,"text":11972},{"id":12259,"depth":3507,"text":12260},{"id":12633,"depth":3507,"text":12634},{"id":12721,"depth":3507,"text":12722},{"id":12876,"depth":3507,"text":12877},{"id":12949,"depth":3507,"text":12950},{"id":13099,"depth":3501,"text":13100},{"id":13197,"depth":3501,"text":13198,"children":16576},[16577,16578,16579,16580,16581],{"id":13204,"depth":3507,"text":13205},{"id":13832,"depth":3507,"text":13833},{"id":14297,"depth":3507,"text":14298},{"id":14609,"depth":3507,"text":14610},{"id":15223,"depth":3507,"text":15224},{"id":15782,"depth":3501,"text":15783,"children":16583},[16584,16585],{"id":15845,"depth":3507,"text":15846},{"id":15893,"depth":3507,"text":15894},{"id":15929,"depth":3501,"text":15930},{"id":16479,"depth":3501,"text":16480},"Від ручного управління Pod до автоматизованої оркестрації — self-healing, масштабування та декларативні оновлення","md",null,{},{"title":2763,"description":16588},"0INKxMabF7BR-R7-qEOhfmuYDBIio4hAzrZEvFlyNL8",[16595,16597],{"title":2759,"path":2760,"stem":2761,"description":16596,"children":-1},"Init-контейнери та Sidecar — розв'язання реальних проблем у Kubernetes з практичними прикладами на .NET",{"title":2767,"path":2768,"stem":2769,"description":16598,"children":-1},"Оновлення застосунків без downtime — від теорії до практики з детальною візуалізацією, математичними розрахунками та реальними прикладами",1778489423846]