[{"data":1,"prerenderedAt":19079},["ShallowReactive",2],{"navigation_docs":3,"-tools-kubernetes-deployment-rolling-updates":3099,"-tools-kubernetes-deployment-rolling-updates-surround":19074},[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":2767,"body":3101,"description":19068,"extension":19069,"links":19070,"meta":19071,"navigation":3410,"path":2768,"seo":19072,"stem":2769,"__hash__":19073},"docs/07.tools/02.kubernetes/07.deployment-rolling-updates.md",{"type":3102,"value":3103,"toc":18983},"minimark",[3104,3108,3113,3121,3126,3129,3134,3205,3215,3220,3278,3284,3290,3293,3297,3310,3325,3329,3362,3364,3368,3371,3375,3539,3545,3549,3552,3570,3573,3730,3748,3752,3759,3897,3902,3918,3922,3925,3941,4143,4147,4158,4167,4171,4174,4185,4362,4380,4384,4387,4546,4550,4562,4566,4569,4737,4741,4749,4753,4756,4921,4931,4963,4965,4969,4972,5585,5590,5628,5630,5634,5637,5641,5648,5711,5715,5726,5731,5742,5747,5758,5762,5769,5795,5799,5810,5814,5822,5826,5837,5841,6178,6180,6184,6187,6190,6197,6232,6256,6261,6269,6274,6409,6412,6422,6445,6459,6464,6470,6474,6596,6600,6603,6830,6834,6837,6844,6971,6976,7014,7016,7020,7028,7031,7034,7060,7069,7074,7087,7092,7098,7161,7172,7177,7238,7244,7274,7278,7281,7320,7330,7334,7340,7344,7401,7406,7428,7432,7573,7577,7588,7593,7612,7614,7618,7621,7625,7631,7636,7764,7771,7792,7796,7799,8840,8844,9050,9054,9057,9569,9625,9629,9638,9653,9661,9676,9698,9706,9807,9809,9813,9816,9820,10102,10106,10309,10313,10316,10321,10327,10354,10357,10468,10477,10488,10596,10600,10603,10608,10646,10651,10705,10710,10713,10735,10804,10809,10929,10931,10935,10942,10946,10956,11118,11123,11133,11156,11162,11166,11169,11206,11211,11225,11305,11309,11312,11384,11387,11391,11394,11413,11415,11433,11591,11596,11603,11614,11618,11621,11639,11642,11646,11649,11687,11690,11694,11697,11716,11719,11738,11742,11748,11850,11852,11856,11859,11863,11866,11871,12844,12848,12855,12860,14071,14076,14090,14094,14097,14136,14140,14143,14161,14164,14192,14195,14271,14274,14278,14281,14433,14439,14443,14449,14466,14489,14492,14532,14539,14543,14569,14574,14598,14601,14603,14607,14610,14614,14618,14637,14644,14649,14700,14709,14714,14896,14900,14904,14923,14927,14958,14963,14981,14985,14999,15003,15013,15017,15022,15031,15036,15073,15076,15084,15120,15123,15127,15132,15137,15141,15146,15179,15185,15194,15226,15229,15233,15241,15263,15268,15272,15283,15288,15291,15447,15454,15514,15518,15961,15963,15967,15970,15974,15979,15984,16029,16035,16486,16488,16492,16497,16501,16523,16528,16847,16849,16853,16858,16862,16882,16887,17284,17286,17290,17295,17299,17333,17338,18151,18153,18157,18164,18218,18222,18277,18281,18284,18322,18324,18328,18331,18932,18934,18938,18969,18971,18979],[3105,3106,2767],"h1",{"id":3107},"rolling-updates-та-управління-життєвим-циклом-deployment",[3109,3110,3112],"h2",{"id":3111},"проблема-як-оновити-застосунок-без-downtime","Проблема: як оновити застосунок без downtime?",[3114,3115,3116,3117],"p",{},"У попередній статті ми навчилися створювати Deployment, масштабувати його та використовувати self-healing. Але залишилося найважливіше питання: ",[3118,3119,3120],"strong",{},"як оновити застосунок на нову версію без зупинки сервісу?",[3122,3123,3125],"h3",{"id":3124},"сценарій-оновлення-веб-застосунку-у-production","Сценарій: оновлення веб-застосунку у production",[3114,3127,3128],{},"Уявіть, що ваш TodoApi працює у production з 3 репліками. Ви виправили критичний баг та хочете розгорнути нову версію. Які у вас варіанти?",[3114,3130,3131],{},[3118,3132,3133],{},"Варіант 1: \"Наївний\" підхід (з downtime)",[3135,3136,3138,3149,3160,3168,3171,3178,3187,3194,3197],"terminal-preview",{"title":3137},"Оновлення з downtime",[3139,3140,3143],"div",{"className":3141},[3142],"line",[3144,3145,3148],"span",{"className":3146},[3147],"opacity-40","# Видалити всі старі Pod",[3139,3150,3152,3156,3157],{"className":3151},[3142],[3144,3153,3155],{"className":3154},[3147],"$"," ",[3118,3158,3159],{},"kubectl delete deployment todoapi",[3139,3161,3163],{"className":3162},[3142],[3144,3164,3167],{"className":3165},[3166],"text-yellow-400","⚠ Сервіс недоступний!",[3139,3169],{"className":3170},[3142],[3139,3172,3174],{"className":3173},[3142],[3144,3175,3177],{"className":3176},[3147],"# Створити Deployment з новою версією",[3139,3179,3181,3156,3184],{"className":3180},[3142],[3144,3182,3155],{"className":3183},[3147],[3118,3185,3186],{},"kubectl apply -f todoapi-v2.yaml",[3139,3188,3190],{"className":3189},[3142],[3144,3191,3193],{"className":3192},[3166],"⚠ Чекаємо 30 секунд, поки Pod стартують...",[3139,3195],{"className":3196},[3142],[3139,3198,3200],{"className":3199},[3142],[3144,3201,3204],{"className":3202},[3203],"text-green-400","✓ Сервіс знову доступний",[3114,3206,3207,3210,3211,3214],{},[3118,3208,3209],{},"Проблема:"," Є період (30-60 секунд), коли ",[3118,3212,3213],{},"жоден Pod не працює",". Користувачі отримують помилки 503 Service Unavailable. Це неприйнятно для production.",[3114,3216,3217],{},[3118,3218,3219],{},"Варіант 2: Rolling Update (без downtime)",[3135,3221,3223,3230,3239,3247,3254,3261,3268,3271],{"title":3222},"Rolling Update",[3139,3224,3226],{"className":3225},[3142],[3144,3227,3229],{"className":3228},[3147],"# Змінити версію образу у YAML",[3139,3231,3233,3156,3236],{"className":3232},[3142],[3144,3234,3155],{"className":3235},[3147],[3118,3237,3238],{},"kubectl set image deployment/todoapi todoapi=todoapi:2.0.0",[3139,3240,3242],{"className":3241},[3142],[3144,3243,3246],{"className":3244},[3245],"text-blue-400","→ Створюється новий Pod з версією 2.0.0",[3139,3248,3250],{"className":3249},[3142],[3144,3251,3253],{"className":3252},[3245],"→ Новий Pod стає Ready",[3139,3255,3257],{"className":3256},[3142],[3144,3258,3260],{"className":3259},[3245],"→ Старий Pod видаляється",[3139,3262,3264],{"className":3263},[3142],[3144,3265,3267],{"className":3266},[3245],"→ Повторюється для всіх реплік",[3139,3269],{"className":3270},[3142],[3139,3272,3274],{"className":3273},[3142],[3144,3275,3277],{"className":3276},[3203],"✓ Сервіс працював весь час!",[3114,3279,3280,3283],{},[3118,3281,3282],{},"Переваги:"," Завжди є працюючі Pod. Користувачі не помічають оновлення. Якщо нова версія має баг — можна швидко повернутись до старої.",[3114,3285,3286,3287,3289],{},"Саме це і робить ",[3118,3288,3222],{},".",[3291,3292],"hr",{},[3109,3294,3296],{"id":3295},"що-таке-rolling-update-формальне-визначення","Що таке Rolling Update: формальне визначення",[3114,3298,3299,3301,3302,3305,3306,3309],{},[3118,3300,3222],{}," — це стратегія оновлення Deployment, при якій ",[3118,3303,3304],{},"старі Pod поступово замінюються новими",", завжди залишаючи мінімальну кількість працюючих реплік. Це гарантує ",[3118,3307,3308],{},"zero-downtime deployment"," — оновлення без зупинки сервісу.",[3311,3312,3313,3319],"note",{},[3114,3314,3315,3318],{},[3118,3316,3317],{},"Ключова ідея:"," Kubernetes не видаляє всі старі Pod одразу. Він створює нові Pod, чекає, поки вони стануть готовими (пройдуть readiness probe), і лише після цього видаляє старі. Цей процес повторюється, поки всі Pod не будуть оновлені.",[3114,3320,3321,3324],{},[3118,3322,3323],{},"Аналогія:"," Уявіть, що ви міняєте колеса на автомобілі, який їде. Ви не можете зняти всі колеса одразу — машина впаде. Замість цього ви міняєте по одному колесу, завжди залишаючи мінімум 3 колеса на місці. Так само працює Rolling Update.",[3122,3326,3328],{"id":3327},"основні-характеристики-rolling-update","Основні характеристики Rolling Update",[3330,3331,3332,3338,3343,3357],"card-group",{},[3333,3334,3337],"card",{"icon":3335,"title":3336},"i-heroicons-clock","Zero-downtime","Завжди є мінімальна кількість працюючих Pod. Користувачі не помічають оновлення — сервіс доступний весь час.",[3333,3339,3342],{"icon":3340,"title":3341},"i-heroicons-arrow-trending-up","Поступовість","Pod оновлюються по черзі, а не всі одразу. Це дозволяє виявити проблеми на ранній стадії — якщо перший новий Pod падає, оновлення зупиняється.",[3333,3344,3347,3348,3352,3353,3356],{"icon":3345,"title":3346},"i-heroicons-adjustments-horizontal","Контрольованість","Ви контролюєте швидкість оновлення через параметри ",[3349,3350,3351],"code",{},"maxSurge"," та ",[3349,3354,3355],{},"maxUnavailable",". Можна зробити оновлення швидким (багато Pod одразу) або обережним (по одному Pod).",[3333,3358,3361],{"icon":3359,"title":3360},"i-heroicons-arrow-uturn-left","Автоматичний rollback","Якщо нові Pod не проходять health checks, оновлення автоматично зупиняється. Старі Pod залишаються працювати. Ви можете вручну повернутись до попередньої версії однією командою.",[3291,3363],{},[3109,3365,3367],{"id":3366},"як-працює-rolling-update-покрокова-візуалізація","Як працює Rolling Update: покрокова візуалізація",[3114,3369,3370],{},"Давайте детально розберемо, що відбувається під час Rolling Update. Візьмемо приклад: Deployment з 3 репліками оновлюється з версії 1.0 на версію 2.0.",[3122,3372,3374],{"id":3373},"початковий-стан","Початковий стан",[3376,3377,3378],"plant-uml",{},[3379,3380,3385],"pre",{"className":3381,"code":3382,"language":3383,"meta":3384,"style":3384},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n        [replicas: 3]\n    }\n}\n\npackage \"Pods (версія 1.0)\" #e8f5e9 {\n    component \"Pod 1\\nv1.0\" as p1\n    component \"Pod 2\\nv1.0\" as p2\n    component \"Pod 3\\nv1.0\" as p3\n}\n\nrs1 --> p1\nrs1 --> p2\nrs1 --> p3\n\nnote right of rs1\n    Всі 3 Pod працюють\n    з версією 1.0\nend note\n\n@enduml\n","plantuml","",[3349,3386,3387,3393,3399,3405,3412,3418,3424,3430,3436,3442,3447,3453,3459,3465,3471,3476,3481,3487,3493,3499,3504,3510,3516,3522,3528,3533],{"__ignoreMap":3384},[3144,3388,3390],{"class":3142,"line":3389},1,[3144,3391,3392],{},"@startuml\n",[3144,3394,3396],{"class":3142,"line":3395},2,[3144,3397,3398],{},"skinparam style plain\n",[3144,3400,3402],{"class":3142,"line":3401},3,[3144,3403,3404],{},"skinparam backgroundColor #ffffff\n",[3144,3406,3408],{"class":3142,"line":3407},4,[3144,3409,3411],{"emptyLinePlaceholder":3410},true,"\n",[3144,3413,3415],{"class":3142,"line":3414},5,[3144,3416,3417],{},"package \"Deployment: todoapi\" #e3f2fd {\n",[3144,3419,3421],{"class":3142,"line":3420},6,[3144,3422,3423],{},"    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n",[3144,3425,3427],{"class":3142,"line":3426},7,[3144,3428,3429],{},"        [replicas: 3]\n",[3144,3431,3433],{"class":3142,"line":3432},8,[3144,3434,3435],{},"    }\n",[3144,3437,3439],{"class":3142,"line":3438},9,[3144,3440,3441],{},"}\n",[3144,3443,3445],{"class":3142,"line":3444},10,[3144,3446,3411],{"emptyLinePlaceholder":3410},[3144,3448,3450],{"class":3142,"line":3449},11,[3144,3451,3452],{},"package \"Pods (версія 1.0)\" #e8f5e9 {\n",[3144,3454,3456],{"class":3142,"line":3455},12,[3144,3457,3458],{},"    component \"Pod 1\\nv1.0\" as p1\n",[3144,3460,3462],{"class":3142,"line":3461},13,[3144,3463,3464],{},"    component \"Pod 2\\nv1.0\" as p2\n",[3144,3466,3468],{"class":3142,"line":3467},14,[3144,3469,3470],{},"    component \"Pod 3\\nv1.0\" as p3\n",[3144,3472,3474],{"class":3142,"line":3473},15,[3144,3475,3441],{},[3144,3477,3479],{"class":3142,"line":3478},16,[3144,3480,3411],{"emptyLinePlaceholder":3410},[3144,3482,3484],{"class":3142,"line":3483},17,[3144,3485,3486],{},"rs1 --> p1\n",[3144,3488,3490],{"class":3142,"line":3489},18,[3144,3491,3492],{},"rs1 --> p2\n",[3144,3494,3496],{"class":3142,"line":3495},19,[3144,3497,3498],{},"rs1 --> p3\n",[3144,3500,3502],{"class":3142,"line":3501},20,[3144,3503,3411],{"emptyLinePlaceholder":3410},[3144,3505,3507],{"class":3142,"line":3506},21,[3144,3508,3509],{},"note right of rs1\n",[3144,3511,3513],{"class":3142,"line":3512},22,[3144,3514,3515],{},"    Всі 3 Pod працюють\n",[3144,3517,3519],{"class":3142,"line":3518},23,[3144,3520,3521],{},"    з версією 1.0\n",[3144,3523,3525],{"class":3142,"line":3524},24,[3144,3526,3527],{},"end note\n",[3144,3529,3531],{"class":3142,"line":3530},25,[3144,3532,3411],{"emptyLinePlaceholder":3410},[3144,3534,3536],{"class":3142,"line":3535},26,[3144,3537,3538],{},"@enduml\n",[3114,3540,3541,3544],{},[3118,3542,3543],{},"Стан:"," 3 Pod з версією 1.0 працюють нормально. Сервіс обробляє запити користувачів.",[3122,3546,3548],{"id":3547},"крок-1-користувач-ініціює-оновлення","Крок 1: Користувач ініціює оновлення",[3114,3550,3551],{},"Користувач змінює версію образу у Deployment:",[3135,3553,3555,3563],{"title":3554},"Ініціація оновлення",[3139,3556,3558,3156,3561],{"className":3557},[3142],[3144,3559,3155],{"className":3560},[3147],[3118,3562,3238],{},[3139,3564,3566],{"className":3565},[3142],[3144,3567,3569],{"className":3568},[3203],"deployment.apps/todoapi image updated",[3114,3571,3572],{},"Що відбувається всередині Kubernetes:",[3376,3574,3575],{},[3379,3576,3578],{"className":3381,"code":3577,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Користувач\" as user\nparticipant \"kubectl\" as kubectl\nparticipant \"API Server\" as api\nparticipant \"etcd\" as etcd\nparticipant \"Deployment\\nController\" as dc\n\nuser -> kubectl: kubectl set image deployment/todoapi\\ntodoapi=todoapi:2.0.0\nkubectl -> api: PATCH /deployments/todoapi\\n{spec.template.spec.containers[0].image: \"todoapi:2.0.0\"}\napi -> etcd: Оновити Deployment\netcd --> api: OK\napi --> kubectl: deployment.apps/todoapi image updated\nkubectl --> user: Оновлення ініційовано\n\napi -> dc: Подія: Deployment змінено\nactivate dc\ndc -> dc: Виявлено зміну у spec.template\\n(образ змінився з 1.0.0 на 2.0.0)\ndc -> dc: Потрібно створити новий ReplicaSet\\nдля нової версії Pod\ndeactivate dc\n\nnote right of dc\n    Deployment Controller\n    виявляє, що шаблон Pod\n    змінився, і починає\n    rolling update\nend note\n\n@enduml\n",[3349,3579,3580,3584,3588,3592,3596,3601,3606,3611,3616,3621,3625,3630,3635,3640,3645,3650,3655,3659,3664,3669,3674,3679,3684,3688,3693,3698,3703,3709,3715,3720,3725],{"__ignoreMap":3384},[3144,3581,3582],{"class":3142,"line":3389},[3144,3583,3392],{},[3144,3585,3586],{"class":3142,"line":3395},[3144,3587,3398],{},[3144,3589,3590],{"class":3142,"line":3401},[3144,3591,3404],{},[3144,3593,3594],{"class":3142,"line":3407},[3144,3595,3411],{"emptyLinePlaceholder":3410},[3144,3597,3598],{"class":3142,"line":3414},[3144,3599,3600],{},"actor \"Користувач\" as user\n",[3144,3602,3603],{"class":3142,"line":3420},[3144,3604,3605],{},"participant \"kubectl\" as kubectl\n",[3144,3607,3608],{"class":3142,"line":3426},[3144,3609,3610],{},"participant \"API Server\" as api\n",[3144,3612,3613],{"class":3142,"line":3432},[3144,3614,3615],{},"participant \"etcd\" as etcd\n",[3144,3617,3618],{"class":3142,"line":3438},[3144,3619,3620],{},"participant \"Deployment\\nController\" as dc\n",[3144,3622,3623],{"class":3142,"line":3444},[3144,3624,3411],{"emptyLinePlaceholder":3410},[3144,3626,3627],{"class":3142,"line":3449},[3144,3628,3629],{},"user -> kubectl: kubectl set image deployment/todoapi\\ntodoapi=todoapi:2.0.0\n",[3144,3631,3632],{"class":3142,"line":3455},[3144,3633,3634],{},"kubectl -> api: PATCH /deployments/todoapi\\n{spec.template.spec.containers[0].image: \"todoapi:2.0.0\"}\n",[3144,3636,3637],{"class":3142,"line":3461},[3144,3638,3639],{},"api -> etcd: Оновити Deployment\n",[3144,3641,3642],{"class":3142,"line":3467},[3144,3643,3644],{},"etcd --> api: OK\n",[3144,3646,3647],{"class":3142,"line":3473},[3144,3648,3649],{},"api --> kubectl: deployment.apps/todoapi image updated\n",[3144,3651,3652],{"class":3142,"line":3478},[3144,3653,3654],{},"kubectl --> user: Оновлення ініційовано\n",[3144,3656,3657],{"class":3142,"line":3483},[3144,3658,3411],{"emptyLinePlaceholder":3410},[3144,3660,3661],{"class":3142,"line":3489},[3144,3662,3663],{},"api -> dc: Подія: Deployment змінено\n",[3144,3665,3666],{"class":3142,"line":3495},[3144,3667,3668],{},"activate dc\n",[3144,3670,3671],{"class":3142,"line":3501},[3144,3672,3673],{},"dc -> dc: Виявлено зміну у spec.template\\n(образ змінився з 1.0.0 на 2.0.0)\n",[3144,3675,3676],{"class":3142,"line":3506},[3144,3677,3678],{},"dc -> dc: Потрібно створити новий ReplicaSet\\nдля нової версії Pod\n",[3144,3680,3681],{"class":3142,"line":3512},[3144,3682,3683],{},"deactivate dc\n",[3144,3685,3686],{"class":3142,"line":3518},[3144,3687,3411],{"emptyLinePlaceholder":3410},[3144,3689,3690],{"class":3142,"line":3524},[3144,3691,3692],{},"note right of dc\n",[3144,3694,3695],{"class":3142,"line":3530},[3144,3696,3697],{},"    Deployment Controller\n",[3144,3699,3700],{"class":3142,"line":3535},[3144,3701,3702],{},"    виявляє, що шаблон Pod\n",[3144,3704,3706],{"class":3142,"line":3705},27,[3144,3707,3708],{},"    змінився, і починає\n",[3144,3710,3712],{"class":3142,"line":3711},28,[3144,3713,3714],{},"    rolling update\n",[3144,3716,3718],{"class":3142,"line":3717},29,[3144,3719,3527],{},[3144,3721,3723],{"class":3142,"line":3722},30,[3144,3724,3411],{"emptyLinePlaceholder":3410},[3144,3726,3728],{"class":3142,"line":3727},31,[3144,3729,3538],{},[3114,3731,3732,3735,3736,3739,3740,3743,3744,3747],{},[3118,3733,3734],{},"Важливо:"," Deployment Controller виявляє, що ",[3349,3737,3738],{},"spec.template"," змінився (образ ",[3349,3741,3742],{},"todoapi:1.0.0"," → ",[3349,3745,3746],{},"todoapi:2.0.0","). Це сигнал для створення нового ReplicaSet.",[3122,3749,3751],{"id":3750},"крок-2-створення-нового-replicaset","Крок 2: Створення нового ReplicaSet",[3114,3753,3754,3755,3758],{},"Deployment Controller створює ",[3118,3756,3757],{},"новий ReplicaSet"," для версії 2.0:",[3376,3760,3761],{},[3379,3762,3764],{"className":3381,"code":3763,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n        [replicas: 3]\n    }\n    \n    component \"ReplicaSet v2.0\\n(todoapi-def456)\" as rs2 #fff3e0 {\n        [replicas: 0]\n    }\n}\n\npackage \"Pods (версія 1.0)\" #e8f5e9 {\n    component \"Pod 1\\nv1.0\" as p1\n    component \"Pod 2\\nv1.0\" as p2\n    component \"Pod 3\\nv1.0\" as p3\n}\n\nrs1 --> p1\nrs1 --> p2\nrs1 --> p3\n\nnote right of rs2\n    Новий ReplicaSet створено,\n    але replicas=0\n    (поки що немає Pod)\nend note\n\n@enduml\n",[3349,3765,3766,3770,3774,3778,3782,3786,3790,3794,3798,3803,3808,3813,3817,3821,3825,3829,3833,3837,3841,3845,3849,3853,3857,3861,3865,3870,3875,3880,3885,3889,3893],{"__ignoreMap":3384},[3144,3767,3768],{"class":3142,"line":3389},[3144,3769,3392],{},[3144,3771,3772],{"class":3142,"line":3395},[3144,3773,3398],{},[3144,3775,3776],{"class":3142,"line":3401},[3144,3777,3404],{},[3144,3779,3780],{"class":3142,"line":3407},[3144,3781,3411],{"emptyLinePlaceholder":3410},[3144,3783,3784],{"class":3142,"line":3414},[3144,3785,3417],{},[3144,3787,3788],{"class":3142,"line":3420},[3144,3789,3423],{},[3144,3791,3792],{"class":3142,"line":3426},[3144,3793,3429],{},[3144,3795,3796],{"class":3142,"line":3432},[3144,3797,3435],{},[3144,3799,3800],{"class":3142,"line":3438},[3144,3801,3802],{},"    \n",[3144,3804,3805],{"class":3142,"line":3444},[3144,3806,3807],{},"    component \"ReplicaSet v2.0\\n(todoapi-def456)\" as rs2 #fff3e0 {\n",[3144,3809,3810],{"class":3142,"line":3449},[3144,3811,3812],{},"        [replicas: 0]\n",[3144,3814,3815],{"class":3142,"line":3455},[3144,3816,3435],{},[3144,3818,3819],{"class":3142,"line":3461},[3144,3820,3441],{},[3144,3822,3823],{"class":3142,"line":3467},[3144,3824,3411],{"emptyLinePlaceholder":3410},[3144,3826,3827],{"class":3142,"line":3473},[3144,3828,3452],{},[3144,3830,3831],{"class":3142,"line":3478},[3144,3832,3458],{},[3144,3834,3835],{"class":3142,"line":3483},[3144,3836,3464],{},[3144,3838,3839],{"class":3142,"line":3489},[3144,3840,3470],{},[3144,3842,3843],{"class":3142,"line":3495},[3144,3844,3441],{},[3144,3846,3847],{"class":3142,"line":3501},[3144,3848,3411],{"emptyLinePlaceholder":3410},[3144,3850,3851],{"class":3142,"line":3506},[3144,3852,3486],{},[3144,3854,3855],{"class":3142,"line":3512},[3144,3856,3492],{},[3144,3858,3859],{"class":3142,"line":3518},[3144,3860,3498],{},[3144,3862,3863],{"class":3142,"line":3524},[3144,3864,3411],{"emptyLinePlaceholder":3410},[3144,3866,3867],{"class":3142,"line":3530},[3144,3868,3869],{},"note right of rs2\n",[3144,3871,3872],{"class":3142,"line":3535},[3144,3873,3874],{},"    Новий ReplicaSet створено,\n",[3144,3876,3877],{"class":3142,"line":3705},[3144,3878,3879],{},"    але replicas=0\n",[3144,3881,3882],{"class":3142,"line":3711},[3144,3883,3884],{},"    (поки що немає Pod)\n",[3144,3886,3887],{"class":3142,"line":3717},[3144,3888,3527],{},[3144,3890,3891],{"class":3142,"line":3722},[3144,3892,3411],{"emptyLinePlaceholder":3410},[3144,3894,3895],{"class":3142,"line":3727},[3144,3896,3538],{},[3114,3898,3899,3901],{},[3118,3900,3543],{}," Тепер є два ReplicaSet:",[3903,3904,3905,3912],"ul",{},[3906,3907,3908,3911],"li",{},[3118,3909,3910],{},"Старий (v1.0):"," 3 репліки (працюють)",[3906,3913,3914,3917],{},[3118,3915,3916],{},"Новий (v2.0):"," 0 реплік (поки що порожній)",[3122,3919,3921],{"id":3920},"крок-3-поступове-масштабування-ітерація-1","Крок 3: Поступове масштабування (ітерація 1)",[3114,3923,3924],{},"Deployment Controller починає rolling update:",[3926,3927,3928,3935],"ol",{},[3906,3929,3930,3931,3934],{},"Збільшує ",[3349,3932,3933],{},"replicas"," нового ReplicaSet на 1 (0 → 1)",[3906,3936,3937,3938,3940],{},"Зменшує ",[3349,3939,3933],{}," старого ReplicaSet на 1 (3 → 2)",[3376,3942,3943],{},[3379,3944,3946],{"className":3381,"code":3945,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n        [replicas: 2]\n    }\n    \n    component \"ReplicaSet v2.0\\n(todoapi-def456)\" as rs2 #fff3e0 {\n        [replicas: 1]\n    }\n}\n\npackage \"Pods (версія 1.0)\" #e8f5e9 {\n    component \"Pod 1\\nv1.0\\nRunning\" as p1\n    component \"Pod 2\\nv1.0\\nRunning\" as p2\n    component \"Pod 3\\nv1.0\\nTerminating\" as p3 #ffebee\n}\n\npackage \"Pods (версія 2.0)\" #fff3e0 {\n    component \"Pod 4\\nv2.0\\nContainerCreating\" as p4\n}\n\nrs1 --> p1\nrs1 --> p2\nrs1 -[dashed]-> p3\n\nrs2 --> p4\n\nnote right of p4\n    Новий Pod створюється\n    (завантаження образу,\n    запуск контейнера)\nend note\n\nnote right of p3\n    Старий Pod отримав\n    SIGTERM та завершується\nend note\n\n@enduml\n",[3349,3947,3948,3952,3956,3960,3964,3968,3972,3977,3981,3985,3989,3994,3998,4002,4006,4010,4015,4020,4025,4029,4033,4038,4043,4047,4051,4055,4059,4064,4068,4073,4077,4082,4088,4094,4100,4105,4110,4116,4122,4128,4133,4138],{"__ignoreMap":3384},[3144,3949,3950],{"class":3142,"line":3389},[3144,3951,3392],{},[3144,3953,3954],{"class":3142,"line":3395},[3144,3955,3398],{},[3144,3957,3958],{"class":3142,"line":3401},[3144,3959,3404],{},[3144,3961,3962],{"class":3142,"line":3407},[3144,3963,3411],{"emptyLinePlaceholder":3410},[3144,3965,3966],{"class":3142,"line":3414},[3144,3967,3417],{},[3144,3969,3970],{"class":3142,"line":3420},[3144,3971,3423],{},[3144,3973,3974],{"class":3142,"line":3426},[3144,3975,3976],{},"        [replicas: 2]\n",[3144,3978,3979],{"class":3142,"line":3432},[3144,3980,3435],{},[3144,3982,3983],{"class":3142,"line":3438},[3144,3984,3802],{},[3144,3986,3987],{"class":3142,"line":3444},[3144,3988,3807],{},[3144,3990,3991],{"class":3142,"line":3449},[3144,3992,3993],{},"        [replicas: 1]\n",[3144,3995,3996],{"class":3142,"line":3455},[3144,3997,3435],{},[3144,3999,4000],{"class":3142,"line":3461},[3144,4001,3441],{},[3144,4003,4004],{"class":3142,"line":3467},[3144,4005,3411],{"emptyLinePlaceholder":3410},[3144,4007,4008],{"class":3142,"line":3473},[3144,4009,3452],{},[3144,4011,4012],{"class":3142,"line":3478},[3144,4013,4014],{},"    component \"Pod 1\\nv1.0\\nRunning\" as p1\n",[3144,4016,4017],{"class":3142,"line":3483},[3144,4018,4019],{},"    component \"Pod 2\\nv1.0\\nRunning\" as p2\n",[3144,4021,4022],{"class":3142,"line":3489},[3144,4023,4024],{},"    component \"Pod 3\\nv1.0\\nTerminating\" as p3 #ffebee\n",[3144,4026,4027],{"class":3142,"line":3495},[3144,4028,3441],{},[3144,4030,4031],{"class":3142,"line":3501},[3144,4032,3411],{"emptyLinePlaceholder":3410},[3144,4034,4035],{"class":3142,"line":3506},[3144,4036,4037],{},"package \"Pods (версія 2.0)\" #fff3e0 {\n",[3144,4039,4040],{"class":3142,"line":3512},[3144,4041,4042],{},"    component \"Pod 4\\nv2.0\\nContainerCreating\" as p4\n",[3144,4044,4045],{"class":3142,"line":3518},[3144,4046,3441],{},[3144,4048,4049],{"class":3142,"line":3524},[3144,4050,3411],{"emptyLinePlaceholder":3410},[3144,4052,4053],{"class":3142,"line":3530},[3144,4054,3486],{},[3144,4056,4057],{"class":3142,"line":3535},[3144,4058,3492],{},[3144,4060,4061],{"class":3142,"line":3705},[3144,4062,4063],{},"rs1 -[dashed]-> p3\n",[3144,4065,4066],{"class":3142,"line":3711},[3144,4067,3411],{"emptyLinePlaceholder":3410},[3144,4069,4070],{"class":3142,"line":3717},[3144,4071,4072],{},"rs2 --> p4\n",[3144,4074,4075],{"class":3142,"line":3722},[3144,4076,3411],{"emptyLinePlaceholder":3410},[3144,4078,4079],{"class":3142,"line":3727},[3144,4080,4081],{},"note right of p4\n",[3144,4083,4085],{"class":3142,"line":4084},32,[3144,4086,4087],{},"    Новий Pod створюється\n",[3144,4089,4091],{"class":3142,"line":4090},33,[3144,4092,4093],{},"    (завантаження образу,\n",[3144,4095,4097],{"class":3142,"line":4096},34,[3144,4098,4099],{},"    запуск контейнера)\n",[3144,4101,4103],{"class":3142,"line":4102},35,[3144,4104,3527],{},[3144,4106,4108],{"class":3142,"line":4107},36,[3144,4109,3411],{"emptyLinePlaceholder":3410},[3144,4111,4113],{"class":3142,"line":4112},37,[3144,4114,4115],{},"note right of p3\n",[3144,4117,4119],{"class":3142,"line":4118},38,[3144,4120,4121],{},"    Старий Pod отримав\n",[3144,4123,4125],{"class":3142,"line":4124},39,[3144,4126,4127],{},"    SIGTERM та завершується\n",[3144,4129,4131],{"class":3142,"line":4130},40,[3144,4132,3527],{},[3144,4134,4136],{"class":3142,"line":4135},41,[3144,4137,3411],{"emptyLinePlaceholder":3410},[3144,4139,4141],{"class":3142,"line":4140},42,[3144,4142,3538],{},[3114,4144,4145],{},[3118,4146,3543],{},[3903,4148,4149,4152,4155],{},[3906,4150,4151],{},"2 старі Pod працюють (v1.0)",[3906,4153,4154],{},"1 старий Pod завершується (v1.0)",[3906,4156,4157],{},"1 новий Pod створюється (v2.0)",[3114,4159,4160,4162,4163,4166],{},[3118,4161,3734],{}," Kubernetes ",[3118,4164,4165],{},"не чекає",", поки старий Pod завершиться. Він одразу створює новий Pod паралельно. Це прискорює оновлення.",[3122,4168,4170],{"id":4169},"крок-4-очікування-готовності-нового-pod","Крок 4: Очікування готовності нового Pod",[3114,4172,4173],{},"Новий Pod проходить lifecycle:",[3926,4175,4176,4179,4182],{},[3906,4177,4178],{},"Завантаження образу",[3906,4180,4181],{},"Запуск контейнера",[3906,4183,4184],{},"Проходження readiness probe",[3376,4186,4187],{},[3379,4188,4190],{"className":3381,"code":4189,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nparticipant \"ReplicaSet\\nController\" as rsc\nparticipant \"Scheduler\" as sched\nparticipant \"Kubelet\" as kubelet\nparticipant \"Pod 4\\n(v2.0)\" as pod\nparticipant \"Readiness\\nProbe\" as probe\n\nrsc -> pod: Створити Pod\npod -> sched: Призначити вузол\nsched -> kubelet: Pod призначено node-1\n\nactivate kubelet\nkubelet -> kubelet: Завантажити образ\\ntodoapi:2.0.0\nnote right: 5-10 секунд\n\nkubelet -> kubelet: Запустити контейнер\nnote right: 2-3 секунди\n\nkubelet -> probe: Перевірити readiness\\n(HTTP GET /health)\nprobe --> kubelet: 200 OK\n\nkubelet -> pod: Встановити Ready=True\ndeactivate kubelet\n\npod -> rsc: Pod готовий!\n\nnote right of rsc\n    ReplicaSet Controller\n    бачить, що новий Pod\n    готовий, і може продовжити\n    rolling update\nend note\n\n@enduml\n",[3349,4191,4192,4196,4200,4204,4208,4213,4218,4223,4228,4233,4237,4242,4247,4252,4256,4261,4266,4271,4275,4280,4285,4289,4294,4299,4303,4308,4313,4317,4322,4326,4331,4336,4341,4346,4350,4354,4358],{"__ignoreMap":3384},[3144,4193,4194],{"class":3142,"line":3389},[3144,4195,3392],{},[3144,4197,4198],{"class":3142,"line":3395},[3144,4199,3398],{},[3144,4201,4202],{"class":3142,"line":3401},[3144,4203,3404],{},[3144,4205,4206],{"class":3142,"line":3407},[3144,4207,3411],{"emptyLinePlaceholder":3410},[3144,4209,4210],{"class":3142,"line":3414},[3144,4211,4212],{},"participant \"ReplicaSet\\nController\" as rsc\n",[3144,4214,4215],{"class":3142,"line":3420},[3144,4216,4217],{},"participant \"Scheduler\" as sched\n",[3144,4219,4220],{"class":3142,"line":3426},[3144,4221,4222],{},"participant \"Kubelet\" as kubelet\n",[3144,4224,4225],{"class":3142,"line":3432},[3144,4226,4227],{},"participant \"Pod 4\\n(v2.0)\" as pod\n",[3144,4229,4230],{"class":3142,"line":3438},[3144,4231,4232],{},"participant \"Readiness\\nProbe\" as probe\n",[3144,4234,4235],{"class":3142,"line":3444},[3144,4236,3411],{"emptyLinePlaceholder":3410},[3144,4238,4239],{"class":3142,"line":3449},[3144,4240,4241],{},"rsc -> pod: Створити Pod\n",[3144,4243,4244],{"class":3142,"line":3455},[3144,4245,4246],{},"pod -> sched: Призначити вузол\n",[3144,4248,4249],{"class":3142,"line":3461},[3144,4250,4251],{},"sched -> kubelet: Pod призначено node-1\n",[3144,4253,4254],{"class":3142,"line":3467},[3144,4255,3411],{"emptyLinePlaceholder":3410},[3144,4257,4258],{"class":3142,"line":3473},[3144,4259,4260],{},"activate kubelet\n",[3144,4262,4263],{"class":3142,"line":3478},[3144,4264,4265],{},"kubelet -> kubelet: Завантажити образ\\ntodoapi:2.0.0\n",[3144,4267,4268],{"class":3142,"line":3483},[3144,4269,4270],{},"note right: 5-10 секунд\n",[3144,4272,4273],{"class":3142,"line":3489},[3144,4274,3411],{"emptyLinePlaceholder":3410},[3144,4276,4277],{"class":3142,"line":3495},[3144,4278,4279],{},"kubelet -> kubelet: Запустити контейнер\n",[3144,4281,4282],{"class":3142,"line":3501},[3144,4283,4284],{},"note right: 2-3 секунди\n",[3144,4286,4287],{"class":3142,"line":3506},[3144,4288,3411],{"emptyLinePlaceholder":3410},[3144,4290,4291],{"class":3142,"line":3512},[3144,4292,4293],{},"kubelet -> probe: Перевірити readiness\\n(HTTP GET /health)\n",[3144,4295,4296],{"class":3142,"line":3518},[3144,4297,4298],{},"probe --> kubelet: 200 OK\n",[3144,4300,4301],{"class":3142,"line":3524},[3144,4302,3411],{"emptyLinePlaceholder":3410},[3144,4304,4305],{"class":3142,"line":3530},[3144,4306,4307],{},"kubelet -> pod: Встановити Ready=True\n",[3144,4309,4310],{"class":3142,"line":3535},[3144,4311,4312],{},"deactivate kubelet\n",[3144,4314,4315],{"class":3142,"line":3705},[3144,4316,3411],{"emptyLinePlaceholder":3410},[3144,4318,4319],{"class":3142,"line":3711},[3144,4320,4321],{},"pod -> rsc: Pod готовий!\n",[3144,4323,4324],{"class":3142,"line":3717},[3144,4325,3411],{"emptyLinePlaceholder":3410},[3144,4327,4328],{"class":3142,"line":3722},[3144,4329,4330],{},"note right of rsc\n",[3144,4332,4333],{"class":3142,"line":3727},[3144,4334,4335],{},"    ReplicaSet Controller\n",[3144,4337,4338],{"class":3142,"line":4084},[3144,4339,4340],{},"    бачить, що новий Pod\n",[3144,4342,4343],{"class":3142,"line":4090},[3144,4344,4345],{},"    готовий, і може продовжити\n",[3144,4347,4348],{"class":3142,"line":4096},[3144,4349,3714],{},[3144,4351,4352],{"class":3142,"line":4102},[3144,4353,3527],{},[3144,4355,4356],{"class":3142,"line":4107},[3144,4357,3411],{"emptyLinePlaceholder":3410},[3144,4359,4360],{"class":3142,"line":4112},[3144,4361,3538],{},[3114,4363,4364,4367,4368,4371,4372,4375,4376,4379],{},[3118,4365,4366],{},"Критично важливо:"," Deployment Controller ",[3118,4369,4370],{},"чекає",", поки новий Pod стане ",[3349,4373,4374],{},"Ready"," (пройде readiness probe), перед тим як продовжити оновлення. Якщо Pod не стає готовим протягом ",[3349,4377,4378],{},"progressDeadlineSeconds"," (за замовчуванням 600 секунд) — оновлення зупиняється.",[3122,4381,4383],{"id":4382},"крок-5-продовження-rolling-update-ітерація-2","Крок 5: Продовження rolling update (ітерація 2)",[3114,4385,4386],{},"Після того, як Pod 4 (v2.0) став готовим, Deployment Controller продовжує оновлення:",[3376,4388,4389],{},[3379,4390,4392],{"className":3381,"code":4391,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n        [replicas: 1]\n    }\n    \n    component \"ReplicaSet v2.0\\n(todoapi-def456)\" as rs2 #fff3e0 {\n        [replicas: 2]\n    }\n}\n\npackage \"Pods (версія 1.0)\" #e8f5e9 {\n    component \"Pod 1\\nv1.0\\nRunning\" as p1\n    component \"Pod 2\\nv1.0\\nTerminating\" as p2 #ffebee\n}\n\npackage \"Pods (версія 2.0)\" #fff3e0 {\n    component \"Pod 4\\nv2.0\\nRunning\" as p4\n    component \"Pod 5\\nv2.0\\nContainerCreating\" as p5\n}\n\nrs1 --> p1\nrs1 -[dashed]-> p2\n\nrs2 --> p4\nrs2 --> p5\n\nnote right of p5\n    Другий новий Pod\n    створюється\nend note\n\n@enduml\n",[3349,4393,4394,4398,4402,4406,4410,4414,4418,4422,4426,4430,4434,4438,4442,4446,4450,4454,4458,4463,4467,4471,4475,4480,4485,4489,4493,4497,4502,4506,4510,4515,4519,4524,4529,4534,4538,4542],{"__ignoreMap":3384},[3144,4395,4396],{"class":3142,"line":3389},[3144,4397,3392],{},[3144,4399,4400],{"class":3142,"line":3395},[3144,4401,3398],{},[3144,4403,4404],{"class":3142,"line":3401},[3144,4405,3404],{},[3144,4407,4408],{"class":3142,"line":3407},[3144,4409,3411],{"emptyLinePlaceholder":3410},[3144,4411,4412],{"class":3142,"line":3414},[3144,4413,3417],{},[3144,4415,4416],{"class":3142,"line":3420},[3144,4417,3423],{},[3144,4419,4420],{"class":3142,"line":3426},[3144,4421,3993],{},[3144,4423,4424],{"class":3142,"line":3432},[3144,4425,3435],{},[3144,4427,4428],{"class":3142,"line":3438},[3144,4429,3802],{},[3144,4431,4432],{"class":3142,"line":3444},[3144,4433,3807],{},[3144,4435,4436],{"class":3142,"line":3449},[3144,4437,3976],{},[3144,4439,4440],{"class":3142,"line":3455},[3144,4441,3435],{},[3144,4443,4444],{"class":3142,"line":3461},[3144,4445,3441],{},[3144,4447,4448],{"class":3142,"line":3467},[3144,4449,3411],{"emptyLinePlaceholder":3410},[3144,4451,4452],{"class":3142,"line":3473},[3144,4453,3452],{},[3144,4455,4456],{"class":3142,"line":3478},[3144,4457,4014],{},[3144,4459,4460],{"class":3142,"line":3483},[3144,4461,4462],{},"    component \"Pod 2\\nv1.0\\nTerminating\" as p2 #ffebee\n",[3144,4464,4465],{"class":3142,"line":3489},[3144,4466,3441],{},[3144,4468,4469],{"class":3142,"line":3495},[3144,4470,3411],{"emptyLinePlaceholder":3410},[3144,4472,4473],{"class":3142,"line":3501},[3144,4474,4037],{},[3144,4476,4477],{"class":3142,"line":3506},[3144,4478,4479],{},"    component \"Pod 4\\nv2.0\\nRunning\" as p4\n",[3144,4481,4482],{"class":3142,"line":3512},[3144,4483,4484],{},"    component \"Pod 5\\nv2.0\\nContainerCreating\" as p5\n",[3144,4486,4487],{"class":3142,"line":3518},[3144,4488,3441],{},[3144,4490,4491],{"class":3142,"line":3524},[3144,4492,3411],{"emptyLinePlaceholder":3410},[3144,4494,4495],{"class":3142,"line":3530},[3144,4496,3486],{},[3144,4498,4499],{"class":3142,"line":3535},[3144,4500,4501],{},"rs1 -[dashed]-> p2\n",[3144,4503,4504],{"class":3142,"line":3705},[3144,4505,3411],{"emptyLinePlaceholder":3410},[3144,4507,4508],{"class":3142,"line":3711},[3144,4509,4072],{},[3144,4511,4512],{"class":3142,"line":3717},[3144,4513,4514],{},"rs2 --> p5\n",[3144,4516,4517],{"class":3142,"line":3722},[3144,4518,3411],{"emptyLinePlaceholder":3410},[3144,4520,4521],{"class":3142,"line":3727},[3144,4522,4523],{},"note right of p5\n",[3144,4525,4526],{"class":3142,"line":4084},[3144,4527,4528],{},"    Другий новий Pod\n",[3144,4530,4531],{"class":3142,"line":4090},[3144,4532,4533],{},"    створюється\n",[3144,4535,4536],{"class":3142,"line":4096},[3144,4537,3527],{},[3144,4539,4540],{"class":3142,"line":4102},[3144,4541,3411],{"emptyLinePlaceholder":3410},[3144,4543,4544],{"class":3142,"line":4107},[3144,4545,3538],{},[3114,4547,4548],{},[3118,4549,3543],{},[3903,4551,4552,4555,4557,4560],{},[3906,4553,4554],{},"1 старий Pod працює (v1.0)",[3906,4556,4154],{},[3906,4558,4559],{},"1 новий Pod працює (v2.0)",[3906,4561,4157],{},[3122,4563,4565],{"id":4564},"крок-6-завершення-rolling-update-ітерація-3","Крок 6: Завершення rolling update (ітерація 3)",[3114,4567,4568],{},"Після того, як Pod 5 (v2.0) став готовим:",[3376,4570,4571],{},[3379,4572,4574],{"className":3381,"code":4573,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n        [replicas: 0]\n    }\n    \n    component \"ReplicaSet v2.0\\n(todoapi-def456)\" as rs2 #fff3e0 {\n        [replicas: 3]\n    }\n}\n\npackage \"Pods (версія 1.0)\" #e8f5e9 {\n    component \"Pod 1\\nv1.0\\nTerminating\" as p1 #ffebee\n}\n\npackage \"Pods (версія 2.0)\" #fff3e0 {\n    component \"Pod 4\\nv2.0\\nRunning\" as p4\n    component \"Pod 5\\nv2.0\\nRunning\" as p5\n    component \"Pod 6\\nv2.0\\nContainerCreating\" as p6\n}\n\nrs1 -[dashed]-> p1\n\nrs2 --> p4\nrs2 --> p5\nrs2 --> p6\n\nnote right of rs1\n    Старий ReplicaSet\n    зменшено до 0 реплік,\n    але НЕ видалено\n    (для rollback)\nend note\n\n@enduml\n",[3349,4575,4576,4580,4584,4588,4592,4596,4600,4604,4608,4612,4616,4620,4624,4628,4632,4636,4641,4645,4649,4653,4657,4662,4667,4671,4675,4680,4684,4688,4692,4697,4701,4705,4710,4715,4720,4725,4729,4733],{"__ignoreMap":3384},[3144,4577,4578],{"class":3142,"line":3389},[3144,4579,3392],{},[3144,4581,4582],{"class":3142,"line":3395},[3144,4583,3398],{},[3144,4585,4586],{"class":3142,"line":3401},[3144,4587,3404],{},[3144,4589,4590],{"class":3142,"line":3407},[3144,4591,3411],{"emptyLinePlaceholder":3410},[3144,4593,4594],{"class":3142,"line":3414},[3144,4595,3417],{},[3144,4597,4598],{"class":3142,"line":3420},[3144,4599,3423],{},[3144,4601,4602],{"class":3142,"line":3426},[3144,4603,3812],{},[3144,4605,4606],{"class":3142,"line":3432},[3144,4607,3435],{},[3144,4609,4610],{"class":3142,"line":3438},[3144,4611,3802],{},[3144,4613,4614],{"class":3142,"line":3444},[3144,4615,3807],{},[3144,4617,4618],{"class":3142,"line":3449},[3144,4619,3429],{},[3144,4621,4622],{"class":3142,"line":3455},[3144,4623,3435],{},[3144,4625,4626],{"class":3142,"line":3461},[3144,4627,3441],{},[3144,4629,4630],{"class":3142,"line":3467},[3144,4631,3411],{"emptyLinePlaceholder":3410},[3144,4633,4634],{"class":3142,"line":3473},[3144,4635,3452],{},[3144,4637,4638],{"class":3142,"line":3478},[3144,4639,4640],{},"    component \"Pod 1\\nv1.0\\nTerminating\" as p1 #ffebee\n",[3144,4642,4643],{"class":3142,"line":3483},[3144,4644,3441],{},[3144,4646,4647],{"class":3142,"line":3489},[3144,4648,3411],{"emptyLinePlaceholder":3410},[3144,4650,4651],{"class":3142,"line":3495},[3144,4652,4037],{},[3144,4654,4655],{"class":3142,"line":3501},[3144,4656,4479],{},[3144,4658,4659],{"class":3142,"line":3506},[3144,4660,4661],{},"    component \"Pod 5\\nv2.0\\nRunning\" as p5\n",[3144,4663,4664],{"class":3142,"line":3512},[3144,4665,4666],{},"    component \"Pod 6\\nv2.0\\nContainerCreating\" as p6\n",[3144,4668,4669],{"class":3142,"line":3518},[3144,4670,3441],{},[3144,4672,4673],{"class":3142,"line":3524},[3144,4674,3411],{"emptyLinePlaceholder":3410},[3144,4676,4677],{"class":3142,"line":3530},[3144,4678,4679],{},"rs1 -[dashed]-> p1\n",[3144,4681,4682],{"class":3142,"line":3535},[3144,4683,3411],{"emptyLinePlaceholder":3410},[3144,4685,4686],{"class":3142,"line":3705},[3144,4687,4072],{},[3144,4689,4690],{"class":3142,"line":3711},[3144,4691,4514],{},[3144,4693,4694],{"class":3142,"line":3717},[3144,4695,4696],{},"rs2 --> p6\n",[3144,4698,4699],{"class":3142,"line":3722},[3144,4700,3411],{"emptyLinePlaceholder":3410},[3144,4702,4703],{"class":3142,"line":3727},[3144,4704,3509],{},[3144,4706,4707],{"class":3142,"line":4084},[3144,4708,4709],{},"    Старий ReplicaSet\n",[3144,4711,4712],{"class":3142,"line":4090},[3144,4713,4714],{},"    зменшено до 0 реплік,\n",[3144,4716,4717],{"class":3142,"line":4096},[3144,4718,4719],{},"    але НЕ видалено\n",[3144,4721,4722],{"class":3142,"line":4102},[3144,4723,4724],{},"    (для rollback)\n",[3144,4726,4727],{"class":3142,"line":4107},[3144,4728,3527],{},[3144,4730,4731],{"class":3142,"line":4112},[3144,4732,3411],{"emptyLinePlaceholder":3410},[3144,4734,4735],{"class":3142,"line":4118},[3144,4736,3538],{},[3114,4738,4739],{},[3118,4740,3543],{},[3903,4742,4743,4746],{},[3906,4744,4745],{},"0 старих Pod (останній завершується)",[3906,4747,4748],{},"3 нові Pod (2 працюють, 1 створюється)",[3122,4750,4752],{"id":4751},"крок-7-фінальний-стан","Крок 7: Фінальний стан",[3114,4754,4755],{},"Після того, як Pod 6 (v2.0) став готовим, rolling update завершено:",[3376,4757,4758],{},[3379,4759,4761],{"className":3381,"code":4760,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    component \"ReplicaSet v1.0\\n(todoapi-abc123)\" as rs1 #e8f5e9 {\n        [replicas: 0]\n        [зберігається для rollback]\n    }\n    \n    component \"ReplicaSet v2.0\\n(todoapi-def456)\" as rs2 #fff3e0 {\n        [replicas: 3]\n    }\n}\n\npackage \"Pods (версія 2.0)\" #fff3e0 {\n    component \"Pod 4\\nv2.0\\nRunning\" as p4\n    component \"Pod 5\\nv2.0\\nRunning\" as p5\n    component \"Pod 6\\nv2.0\\nRunning\" as p6\n}\n\nrs2 --> p4\nrs2 --> p5\nrs2 --> p6\n\nnote right of rs1\n    Старий ReplicaSet\n    зберігається з replicas=0\n    для можливості rollback\nend note\n\nnote right of rs2\n    Всі 3 Pod працюють\n    з версією 2.0\n    Rolling update завершено!\nend note\n\n@enduml\n",[3349,4762,4763,4767,4771,4775,4779,4783,4787,4791,4796,4800,4804,4808,4812,4816,4820,4824,4828,4832,4836,4841,4845,4849,4853,4857,4861,4865,4869,4873,4878,4883,4887,4891,4895,4899,4904,4909,4913,4917],{"__ignoreMap":3384},[3144,4764,4765],{"class":3142,"line":3389},[3144,4766,3392],{},[3144,4768,4769],{"class":3142,"line":3395},[3144,4770,3398],{},[3144,4772,4773],{"class":3142,"line":3401},[3144,4774,3404],{},[3144,4776,4777],{"class":3142,"line":3407},[3144,4778,3411],{"emptyLinePlaceholder":3410},[3144,4780,4781],{"class":3142,"line":3414},[3144,4782,3417],{},[3144,4784,4785],{"class":3142,"line":3420},[3144,4786,3423],{},[3144,4788,4789],{"class":3142,"line":3426},[3144,4790,3812],{},[3144,4792,4793],{"class":3142,"line":3432},[3144,4794,4795],{},"        [зберігається для rollback]\n",[3144,4797,4798],{"class":3142,"line":3438},[3144,4799,3435],{},[3144,4801,4802],{"class":3142,"line":3444},[3144,4803,3802],{},[3144,4805,4806],{"class":3142,"line":3449},[3144,4807,3807],{},[3144,4809,4810],{"class":3142,"line":3455},[3144,4811,3429],{},[3144,4813,4814],{"class":3142,"line":3461},[3144,4815,3435],{},[3144,4817,4818],{"class":3142,"line":3467},[3144,4819,3441],{},[3144,4821,4822],{"class":3142,"line":3473},[3144,4823,3411],{"emptyLinePlaceholder":3410},[3144,4825,4826],{"class":3142,"line":3478},[3144,4827,4037],{},[3144,4829,4830],{"class":3142,"line":3483},[3144,4831,4479],{},[3144,4833,4834],{"class":3142,"line":3489},[3144,4835,4661],{},[3144,4837,4838],{"class":3142,"line":3495},[3144,4839,4840],{},"    component \"Pod 6\\nv2.0\\nRunning\" as p6\n",[3144,4842,4843],{"class":3142,"line":3501},[3144,4844,3441],{},[3144,4846,4847],{"class":3142,"line":3506},[3144,4848,3411],{"emptyLinePlaceholder":3410},[3144,4850,4851],{"class":3142,"line":3512},[3144,4852,4072],{},[3144,4854,4855],{"class":3142,"line":3518},[3144,4856,4514],{},[3144,4858,4859],{"class":3142,"line":3524},[3144,4860,4696],{},[3144,4862,4863],{"class":3142,"line":3530},[3144,4864,3411],{"emptyLinePlaceholder":3410},[3144,4866,4867],{"class":3142,"line":3535},[3144,4868,3509],{},[3144,4870,4871],{"class":3142,"line":3705},[3144,4872,4709],{},[3144,4874,4875],{"class":3142,"line":3711},[3144,4876,4877],{},"    зберігається з replicas=0\n",[3144,4879,4880],{"class":3142,"line":3717},[3144,4881,4882],{},"    для можливості rollback\n",[3144,4884,4885],{"class":3142,"line":3722},[3144,4886,3527],{},[3144,4888,4889],{"class":3142,"line":3727},[3144,4890,3411],{"emptyLinePlaceholder":3410},[3144,4892,4893],{"class":3142,"line":4084},[3144,4894,3869],{},[3144,4896,4897],{"class":3142,"line":4090},[3144,4898,3515],{},[3144,4900,4901],{"class":3142,"line":4096},[3144,4902,4903],{},"    з версією 2.0\n",[3144,4905,4906],{"class":3142,"line":4102},[3144,4907,4908],{},"    Rolling update завершено!\n",[3144,4910,4911],{"class":3142,"line":4107},[3144,4912,3527],{},[3144,4914,4915],{"class":3142,"line":4112},[3144,4916,3411],{"emptyLinePlaceholder":3410},[3144,4918,4919],{"class":3142,"line":4118},[3144,4920,3538],{},[3114,4922,4923,4926,4927,4930],{},[3118,4924,4925],{},"Результат:"," Всі Pod оновлені до версії 2.0. Старий ReplicaSet зберігається з ",[3349,4928,4929],{},"replicas: 0"," для можливості швидкого rollback.",[4932,4933,4934,4939,4946,4960],"tip",{},[3114,4935,4936],{},[3118,4937,4938],{},"Чому старий ReplicaSet не видаляється?",[3114,4940,4941,4942,4945],{},"Kubernetes зберігає старі ReplicaSet (за замовчуванням 10 останніх) для можливості ",[3118,4943,4944],{},"швидкого rollback",". Якщо ви виявите баг у версії 2.0 та захочете повернутись до 1.0, Kubernetes просто:",[3926,4947,4948,4954],{},[3906,4949,4950,4951,4953],{},"Збільшить ",[3349,4952,3933],{}," старого ReplicaSet (0 → 3)",[3906,4955,4956,4957,4959],{},"Зменшить ",[3349,4958,3933],{}," нового ReplicaSet (3 → 0)",[3114,4961,4962],{},"Це займає 10-20 секунд, бо образ версії 1.0 вже є на вузлах (кешовано). Без збереження старого ReplicaSet довелося б створювати новий, що займає більше часу.",[3291,4964],{},[3109,4966,4968],{"id":4967},"повна-візуалізація-rolling-update","Повна візуалізація Rolling Update",[3114,4970,4971],{},"Тепер об'єднаємо всі кроки в одну sequence diagram:",[3376,4973,4974],{},[3379,4975,4977],{"className":3381,"code":4976,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nparticipant \"kubectl\" as kubectl\nparticipant \"API Server\" as api\nparticipant \"Deployment\\nController\" as dc\nparticipant \"ReplicaSet v1.0\\nController\" as rsc1\nparticipant \"ReplicaSet v2.0\\nController\" as rsc2\nparticipant \"Scheduler\" as sched\nparticipant \"Kubelet\" as kubelet\n\n== Ініціація оновлення ==\n\nkubectl -> api: PATCH /deployments/todoapi\\n{image: todoapi:2.0.0}\napi -> dc: Подія: Deployment змінено\n\nactivate dc\ndc -> dc: Виявлено зміну spec.template\ndc -> api: Створити ReplicaSet v2.0 (replicas=0)\napi -> rsc2: Подія: новий ReplicaSet\ndeactivate dc\n\n== Ітерація 1: Оновлення першого Pod ==\n\nactivate dc\ndc -> api: PATCH ReplicaSet v2.0 (replicas: 0→1)\ndc -> api: PATCH ReplicaSet v1.0 (replicas: 3→2)\ndeactivate dc\n\napi -> rsc2: Подія: replicas змінено\nactivate rsc2\nrsc2 -> api: Створити Pod 4 (v2.0)\ndeactivate rsc2\n\napi -> rsc1: Подія: replicas змінено\nactivate rsc1\nrsc1 -> api: Видалити Pod 3 (v1.0)\ndeactivate rsc1\n\napi -> sched: Подія: новий Pod 4\nsched -> kubelet: Призначити Pod 4 вузлу\n\nactivate kubelet\nkubelet -> kubelet: Завантажити образ todoapi:2.0.0\nkubelet -> kubelet: Запустити контейнер\nkubelet -> kubelet: Перевірити readiness probe\nkubelet -> api: Pod 4 Ready=True\ndeactivate kubelet\n\n== Ітерація 2: Оновлення другого Pod ==\n\nactivate dc\ndc -> dc: Pod 4 готовий, продовжити\ndc -> api: PATCH ReplicaSet v2.0 (replicas: 1→2)\ndc -> api: PATCH ReplicaSet v1.0 (replicas: 2→1)\ndeactivate dc\n\napi -> rsc2: Подія: replicas змінено\nactivate rsc2\nrsc2 -> api: Створити Pod 5 (v2.0)\ndeactivate rsc2\n\napi -> rsc1: Подія: replicas змінено\nactivate rsc1\nrsc1 -> api: Видалити Pod 2 (v1.0)\ndeactivate rsc1\n\napi -> sched: Подія: новий Pod 5\nsched -> kubelet: Призначити Pod 5 вузлу\n\nactivate kubelet\nkubelet -> kubelet: Образ вже є (кешовано)\nkubelet -> kubelet: Запустити контейнер\nkubelet -> kubelet: Перевірити readiness probe\nkubelet -> api: Pod 5 Ready=True\ndeactivate kubelet\n\n== Ітерація 3: Оновлення третього Pod ==\n\nactivate dc\ndc -> dc: Pod 5 готовий, продовжити\ndc -> api: PATCH ReplicaSet v2.0 (replicas: 2→3)\ndc -> api: PATCH ReplicaSet v1.0 (replicas: 1→0)\ndeactivate dc\n\napi -> rsc2: Подія: replicas змінено\nactivate rsc2\nrsc2 -> api: Створити Pod 6 (v2.0)\ndeactivate rsc2\n\napi -> rsc1: Подія: replicas змінено\nactivate rsc1\nrsc1 -> api: Видалити Pod 1 (v1.0)\ndeactivate rsc1\n\napi -> sched: Подія: новий Pod 6\nsched -> kubelet: Призначити Pod 6 вузлу\n\nactivate kubelet\nkubelet -> kubelet: Образ вже є (кешовано)\nkubelet -> kubelet: Запустити контейнер\nkubelet -> kubelet: Перевірити readiness probe\nkubelet -> api: Pod 6 Ready=True\ndeactivate kubelet\n\n== Завершення ==\n\nactivate dc\ndc -> dc: Всі Pod оновлені\ndc -> api: Встановити Deployment status:\\nAvailable=True, Progressing=False\ndeactivate dc\n\nnote right of dc\n  Rolling update завершено!\n  Час: ~30-60 секунд\n  Downtime: 0 секунд\nend note\n\n@enduml\n",[3349,4978,4979,4983,4987,4991,4995,4999,5003,5007,5012,5017,5021,5025,5029,5034,5038,5043,5047,5051,5055,5060,5065,5070,5074,5078,5083,5087,5091,5096,5101,5105,5109,5114,5119,5124,5129,5133,5138,5143,5148,5153,5157,5162,5167,5172,5177,5183,5188,5194,5200,5205,5210,5216,5221,5226,5232,5238,5244,5249,5254,5259,5264,5270,5275,5280,5285,5290,5296,5301,5306,5312,5318,5323,5328,5334,5339,5344,5350,5355,5360,5366,5371,5376,5382,5388,5394,5399,5404,5409,5414,5420,5425,5430,5435,5440,5446,5451,5456,5462,5468,5473,5478,5483,5488,5493,5499,5504,5509,5515,5520,5525,5531,5537,5542,5547,5552,5558,5564,5570,5575,5580],{"__ignoreMap":3384},[3144,4980,4981],{"class":3142,"line":3389},[3144,4982,3392],{},[3144,4984,4985],{"class":3142,"line":3395},[3144,4986,3398],{},[3144,4988,4989],{"class":3142,"line":3401},[3144,4990,3404],{},[3144,4992,4993],{"class":3142,"line":3407},[3144,4994,3411],{"emptyLinePlaceholder":3410},[3144,4996,4997],{"class":3142,"line":3414},[3144,4998,3605],{},[3144,5000,5001],{"class":3142,"line":3420},[3144,5002,3610],{},[3144,5004,5005],{"class":3142,"line":3426},[3144,5006,3620],{},[3144,5008,5009],{"class":3142,"line":3432},[3144,5010,5011],{},"participant \"ReplicaSet v1.0\\nController\" as rsc1\n",[3144,5013,5014],{"class":3142,"line":3438},[3144,5015,5016],{},"participant \"ReplicaSet v2.0\\nController\" as rsc2\n",[3144,5018,5019],{"class":3142,"line":3444},[3144,5020,4217],{},[3144,5022,5023],{"class":3142,"line":3449},[3144,5024,4222],{},[3144,5026,5027],{"class":3142,"line":3455},[3144,5028,3411],{"emptyLinePlaceholder":3410},[3144,5030,5031],{"class":3142,"line":3461},[3144,5032,5033],{},"== Ініціація оновлення ==\n",[3144,5035,5036],{"class":3142,"line":3467},[3144,5037,3411],{"emptyLinePlaceholder":3410},[3144,5039,5040],{"class":3142,"line":3473},[3144,5041,5042],{},"kubectl -> api: PATCH /deployments/todoapi\\n{image: todoapi:2.0.0}\n",[3144,5044,5045],{"class":3142,"line":3478},[3144,5046,3663],{},[3144,5048,5049],{"class":3142,"line":3483},[3144,5050,3411],{"emptyLinePlaceholder":3410},[3144,5052,5053],{"class":3142,"line":3489},[3144,5054,3668],{},[3144,5056,5057],{"class":3142,"line":3495},[3144,5058,5059],{},"dc -> dc: Виявлено зміну spec.template\n",[3144,5061,5062],{"class":3142,"line":3501},[3144,5063,5064],{},"dc -> api: Створити ReplicaSet v2.0 (replicas=0)\n",[3144,5066,5067],{"class":3142,"line":3506},[3144,5068,5069],{},"api -> rsc2: Подія: новий ReplicaSet\n",[3144,5071,5072],{"class":3142,"line":3512},[3144,5073,3683],{},[3144,5075,5076],{"class":3142,"line":3518},[3144,5077,3411],{"emptyLinePlaceholder":3410},[3144,5079,5080],{"class":3142,"line":3524},[3144,5081,5082],{},"== Ітерація 1: Оновлення першого Pod ==\n",[3144,5084,5085],{"class":3142,"line":3530},[3144,5086,3411],{"emptyLinePlaceholder":3410},[3144,5088,5089],{"class":3142,"line":3535},[3144,5090,3668],{},[3144,5092,5093],{"class":3142,"line":3705},[3144,5094,5095],{},"dc -> api: PATCH ReplicaSet v2.0 (replicas: 0→1)\n",[3144,5097,5098],{"class":3142,"line":3711},[3144,5099,5100],{},"dc -> api: PATCH ReplicaSet v1.0 (replicas: 3→2)\n",[3144,5102,5103],{"class":3142,"line":3717},[3144,5104,3683],{},[3144,5106,5107],{"class":3142,"line":3722},[3144,5108,3411],{"emptyLinePlaceholder":3410},[3144,5110,5111],{"class":3142,"line":3727},[3144,5112,5113],{},"api -> rsc2: Подія: replicas змінено\n",[3144,5115,5116],{"class":3142,"line":4084},[3144,5117,5118],{},"activate rsc2\n",[3144,5120,5121],{"class":3142,"line":4090},[3144,5122,5123],{},"rsc2 -> api: Створити Pod 4 (v2.0)\n",[3144,5125,5126],{"class":3142,"line":4096},[3144,5127,5128],{},"deactivate rsc2\n",[3144,5130,5131],{"class":3142,"line":4102},[3144,5132,3411],{"emptyLinePlaceholder":3410},[3144,5134,5135],{"class":3142,"line":4107},[3144,5136,5137],{},"api -> rsc1: Подія: replicas змінено\n",[3144,5139,5140],{"class":3142,"line":4112},[3144,5141,5142],{},"activate rsc1\n",[3144,5144,5145],{"class":3142,"line":4118},[3144,5146,5147],{},"rsc1 -> api: Видалити Pod 3 (v1.0)\n",[3144,5149,5150],{"class":3142,"line":4124},[3144,5151,5152],{},"deactivate rsc1\n",[3144,5154,5155],{"class":3142,"line":4130},[3144,5156,3411],{"emptyLinePlaceholder":3410},[3144,5158,5159],{"class":3142,"line":4135},[3144,5160,5161],{},"api -> sched: Подія: новий Pod 4\n",[3144,5163,5164],{"class":3142,"line":4140},[3144,5165,5166],{},"sched -> kubelet: Призначити Pod 4 вузлу\n",[3144,5168,5170],{"class":3142,"line":5169},43,[3144,5171,3411],{"emptyLinePlaceholder":3410},[3144,5173,5175],{"class":3142,"line":5174},44,[3144,5176,4260],{},[3144,5178,5180],{"class":3142,"line":5179},45,[3144,5181,5182],{},"kubelet -> kubelet: Завантажити образ todoapi:2.0.0\n",[3144,5184,5186],{"class":3142,"line":5185},46,[3144,5187,4279],{},[3144,5189,5191],{"class":3142,"line":5190},47,[3144,5192,5193],{},"kubelet -> kubelet: Перевірити readiness probe\n",[3144,5195,5197],{"class":3142,"line":5196},48,[3144,5198,5199],{},"kubelet -> api: Pod 4 Ready=True\n",[3144,5201,5203],{"class":3142,"line":5202},49,[3144,5204,4312],{},[3144,5206,5208],{"class":3142,"line":5207},50,[3144,5209,3411],{"emptyLinePlaceholder":3410},[3144,5211,5213],{"class":3142,"line":5212},51,[3144,5214,5215],{},"== Ітерація 2: Оновлення другого Pod ==\n",[3144,5217,5219],{"class":3142,"line":5218},52,[3144,5220,3411],{"emptyLinePlaceholder":3410},[3144,5222,5224],{"class":3142,"line":5223},53,[3144,5225,3668],{},[3144,5227,5229],{"class":3142,"line":5228},54,[3144,5230,5231],{},"dc -> dc: Pod 4 готовий, продовжити\n",[3144,5233,5235],{"class":3142,"line":5234},55,[3144,5236,5237],{},"dc -> api: PATCH ReplicaSet v2.0 (replicas: 1→2)\n",[3144,5239,5241],{"class":3142,"line":5240},56,[3144,5242,5243],{},"dc -> api: PATCH ReplicaSet v1.0 (replicas: 2→1)\n",[3144,5245,5247],{"class":3142,"line":5246},57,[3144,5248,3683],{},[3144,5250,5252],{"class":3142,"line":5251},58,[3144,5253,3411],{"emptyLinePlaceholder":3410},[3144,5255,5257],{"class":3142,"line":5256},59,[3144,5258,5113],{},[3144,5260,5262],{"class":3142,"line":5261},60,[3144,5263,5118],{},[3144,5265,5267],{"class":3142,"line":5266},61,[3144,5268,5269],{},"rsc2 -> api: Створити Pod 5 (v2.0)\n",[3144,5271,5273],{"class":3142,"line":5272},62,[3144,5274,5128],{},[3144,5276,5278],{"class":3142,"line":5277},63,[3144,5279,3411],{"emptyLinePlaceholder":3410},[3144,5281,5283],{"class":3142,"line":5282},64,[3144,5284,5137],{},[3144,5286,5288],{"class":3142,"line":5287},65,[3144,5289,5142],{},[3144,5291,5293],{"class":3142,"line":5292},66,[3144,5294,5295],{},"rsc1 -> api: Видалити Pod 2 (v1.0)\n",[3144,5297,5299],{"class":3142,"line":5298},67,[3144,5300,5152],{},[3144,5302,5304],{"class":3142,"line":5303},68,[3144,5305,3411],{"emptyLinePlaceholder":3410},[3144,5307,5309],{"class":3142,"line":5308},69,[3144,5310,5311],{},"api -> sched: Подія: новий Pod 5\n",[3144,5313,5315],{"class":3142,"line":5314},70,[3144,5316,5317],{},"sched -> kubelet: Призначити Pod 5 вузлу\n",[3144,5319,5321],{"class":3142,"line":5320},71,[3144,5322,3411],{"emptyLinePlaceholder":3410},[3144,5324,5326],{"class":3142,"line":5325},72,[3144,5327,4260],{},[3144,5329,5331],{"class":3142,"line":5330},73,[3144,5332,5333],{},"kubelet -> kubelet: Образ вже є (кешовано)\n",[3144,5335,5337],{"class":3142,"line":5336},74,[3144,5338,4279],{},[3144,5340,5342],{"class":3142,"line":5341},75,[3144,5343,5193],{},[3144,5345,5347],{"class":3142,"line":5346},76,[3144,5348,5349],{},"kubelet -> api: Pod 5 Ready=True\n",[3144,5351,5353],{"class":3142,"line":5352},77,[3144,5354,4312],{},[3144,5356,5358],{"class":3142,"line":5357},78,[3144,5359,3411],{"emptyLinePlaceholder":3410},[3144,5361,5363],{"class":3142,"line":5362},79,[3144,5364,5365],{},"== Ітерація 3: Оновлення третього Pod ==\n",[3144,5367,5369],{"class":3142,"line":5368},80,[3144,5370,3411],{"emptyLinePlaceholder":3410},[3144,5372,5374],{"class":3142,"line":5373},81,[3144,5375,3668],{},[3144,5377,5379],{"class":3142,"line":5378},82,[3144,5380,5381],{},"dc -> dc: Pod 5 готовий, продовжити\n",[3144,5383,5385],{"class":3142,"line":5384},83,[3144,5386,5387],{},"dc -> api: PATCH ReplicaSet v2.0 (replicas: 2→3)\n",[3144,5389,5391],{"class":3142,"line":5390},84,[3144,5392,5393],{},"dc -> api: PATCH ReplicaSet v1.0 (replicas: 1→0)\n",[3144,5395,5397],{"class":3142,"line":5396},85,[3144,5398,3683],{},[3144,5400,5402],{"class":3142,"line":5401},86,[3144,5403,3411],{"emptyLinePlaceholder":3410},[3144,5405,5407],{"class":3142,"line":5406},87,[3144,5408,5113],{},[3144,5410,5412],{"class":3142,"line":5411},88,[3144,5413,5118],{},[3144,5415,5417],{"class":3142,"line":5416},89,[3144,5418,5419],{},"rsc2 -> api: Створити Pod 6 (v2.0)\n",[3144,5421,5423],{"class":3142,"line":5422},90,[3144,5424,5128],{},[3144,5426,5428],{"class":3142,"line":5427},91,[3144,5429,3411],{"emptyLinePlaceholder":3410},[3144,5431,5433],{"class":3142,"line":5432},92,[3144,5434,5137],{},[3144,5436,5438],{"class":3142,"line":5437},93,[3144,5439,5142],{},[3144,5441,5443],{"class":3142,"line":5442},94,[3144,5444,5445],{},"rsc1 -> api: Видалити Pod 1 (v1.0)\n",[3144,5447,5449],{"class":3142,"line":5448},95,[3144,5450,5152],{},[3144,5452,5454],{"class":3142,"line":5453},96,[3144,5455,3411],{"emptyLinePlaceholder":3410},[3144,5457,5459],{"class":3142,"line":5458},97,[3144,5460,5461],{},"api -> sched: Подія: новий Pod 6\n",[3144,5463,5465],{"class":3142,"line":5464},98,[3144,5466,5467],{},"sched -> kubelet: Призначити Pod 6 вузлу\n",[3144,5469,5471],{"class":3142,"line":5470},99,[3144,5472,3411],{"emptyLinePlaceholder":3410},[3144,5474,5476],{"class":3142,"line":5475},100,[3144,5477,4260],{},[3144,5479,5481],{"class":3142,"line":5480},101,[3144,5482,5333],{},[3144,5484,5486],{"class":3142,"line":5485},102,[3144,5487,4279],{},[3144,5489,5491],{"class":3142,"line":5490},103,[3144,5492,5193],{},[3144,5494,5496],{"class":3142,"line":5495},104,[3144,5497,5498],{},"kubelet -> api: Pod 6 Ready=True\n",[3144,5500,5502],{"class":3142,"line":5501},105,[3144,5503,4312],{},[3144,5505,5507],{"class":3142,"line":5506},106,[3144,5508,3411],{"emptyLinePlaceholder":3410},[3144,5510,5512],{"class":3142,"line":5511},107,[3144,5513,5514],{},"== Завершення ==\n",[3144,5516,5518],{"class":3142,"line":5517},108,[3144,5519,3411],{"emptyLinePlaceholder":3410},[3144,5521,5523],{"class":3142,"line":5522},109,[3144,5524,3668],{},[3144,5526,5528],{"class":3142,"line":5527},110,[3144,5529,5530],{},"dc -> dc: Всі Pod оновлені\n",[3144,5532,5534],{"class":3142,"line":5533},111,[3144,5535,5536],{},"dc -> api: Встановити Deployment status:\\nAvailable=True, Progressing=False\n",[3144,5538,5540],{"class":3142,"line":5539},112,[3144,5541,3683],{},[3144,5543,5545],{"class":3142,"line":5544},113,[3144,5546,3411],{"emptyLinePlaceholder":3410},[3144,5548,5550],{"class":3142,"line":5549},114,[3144,5551,3692],{},[3144,5553,5555],{"class":3142,"line":5554},115,[3144,5556,5557],{},"  Rolling update завершено!\n",[3144,5559,5561],{"class":3142,"line":5560},116,[3144,5562,5563],{},"  Час: ~30-60 секунд\n",[3144,5565,5567],{"class":3142,"line":5566},117,[3144,5568,5569],{},"  Downtime: 0 секунд\n",[3144,5571,5573],{"class":3142,"line":5572},118,[3144,5574,3527],{},[3144,5576,5578],{"class":3142,"line":5577},119,[3144,5579,3411],{"emptyLinePlaceholder":3410},[3144,5581,5583],{"class":3142,"line":5582},120,[3144,5584,3538],{},[3114,5586,5587],{},[3118,5588,5589],{},"Ключові моменти:",[3926,5591,5592,5603,5611,5617,5623],{},[3906,5593,5594,5596,5597,5599,5600,5602],{},[3118,5595,3341],{}," — Pod оновлюються по одному (або по кілька, залежно від ",[3349,5598,3351],{},"/",[3349,5601,3355],{},")",[3906,5604,5605,5608,5609],{},[3118,5606,5607],{},"Очікування готовності"," — перед продовженням оновлення Kubernetes чекає, поки новий Pod стане ",[3349,5610,4374],{},[3906,5612,5613,5616],{},[3118,5614,5615],{},"Паралельність"," — створення нового Pod та видалення старого відбуваються паралельно",[3906,5618,5619,5622],{},[3118,5620,5621],{},"Кешування образів"," — після завантаження образу на вузол, наступні Pod стартують швидше",[3906,5624,5625,5627],{},[3118,5626,3336],{}," — завжди є мінімум 2 працюючі Pod (у нашому прикладі)",[3291,5629],{},[3109,5631,5633],{"id":5632},"стратегії-оновлення-rollingupdate-vs-recreate","Стратегії оновлення: RollingUpdate vs Recreate",[3114,5635,5636],{},"Kubernetes підтримує дві стратегії оновлення Deployment:",[3122,5638,5640],{"id":5639},"_1-rollingupdate-за-замовчуванням","1. RollingUpdate (за замовчуванням)",[3114,5642,5643,5644,5647],{},"Поступове оновлення, яке ми щойно розглянули. Це ",[3118,5645,5646],{},"рекомендована стратегія"," для більшості застосунків.",[3379,5649,5653],{"className":5650,"code":5651,"language":5652,"meta":3384,"style":3384},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus","spec:\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 1\n      maxUnavailable: 1\n","yaml",[3349,5654,5655,5665,5672,5684,5691,5702],{"__ignoreMap":3384},[3144,5656,5657,5661],{"class":3142,"line":3389},[3144,5658,5660],{"class":5659},"sKtos","spec",[3144,5662,5664],{"class":5663},"sHH4Y",":\n",[3144,5666,5667,5670],{"class":3142,"line":3395},[3144,5668,5669],{"class":5659},"  strategy",[3144,5671,5664],{"class":5663},[3144,5673,5674,5677,5680],{"class":3142,"line":3401},[3144,5675,5676],{"class":5659},"    type",[3144,5678,5679],{"class":5663},": ",[3144,5681,5683],{"class":5682},"su9tN","RollingUpdate\n",[3144,5685,5686,5689],{"class":3142,"line":3407},[3144,5687,5688],{"class":5659},"    rollingUpdate",[3144,5690,5664],{"class":5663},[3144,5692,5693,5696,5698],{"class":3142,"line":3414},[3144,5694,5695],{"class":5659},"      maxSurge",[3144,5697,5679],{"class":5663},[3144,5699,5701],{"class":5700},"sJj4R","1\n",[3144,5703,5704,5707,5709],{"class":3142,"line":3420},[3144,5705,5706],{"class":5659},"      maxUnavailable",[3144,5708,5679],{"class":5663},[3144,5710,5701],{"class":5700},[3114,5712,5713],{},[3118,5714,3282],{},[3903,5716,5717,5720,5723],{},[3906,5718,5719],{},"Zero-downtime — сервіс доступний весь час",[3906,5721,5722],{},"Поступове виявлення проблем — якщо перший новий Pod падає, оновлення зупиняється",[3906,5724,5725],{},"Можливість rollback — старі Pod ще працюють, можна швидко повернутись",[3114,5727,5728],{},[3118,5729,5730],{},"Недоліки:",[3903,5732,5733,5736,5739],{},[3906,5734,5735],{},"Повільніше за Recreate (потрібен час на поступове оновлення)",[3906,5737,5738],{},"Потребує більше ресурсів (одночасно працюють старі та нові Pod)",[3906,5740,5741],{},"Складніше для застосунків, які не підтримують одночасну роботу різних версій",[3114,5743,5744],{},[3118,5745,5746],{},"Коли використовувати:",[3903,5748,5749,5752,5755],{},[3906,5750,5751],{},"Веб-застосунки (API, frontend)",[3906,5753,5754],{},"Stateless сервіси",[3906,5756,5757],{},"Будь-які застосунки, де downtime неприйнятний",[3122,5759,5761],{"id":5760},"_2-recreate","2. Recreate",[3114,5763,5764,5765,5768],{},"Спочатку видаляються ",[3118,5766,5767],{},"всі"," старі Pod, потім створюються нові. Є період downtime.",[3379,5770,5772],{"className":5650,"code":5771,"language":5652,"meta":3384,"style":3384},"spec:\n  strategy:\n    type: Recreate\n",[3349,5773,5774,5780,5786],{"__ignoreMap":3384},[3144,5775,5776,5778],{"class":3142,"line":3389},[3144,5777,5660],{"class":5659},[3144,5779,5664],{"class":5663},[3144,5781,5782,5784],{"class":3142,"line":3395},[3144,5783,5669],{"class":5659},[3144,5785,5664],{"class":5663},[3144,5787,5788,5790,5792],{"class":3142,"line":3401},[3144,5789,5676],{"class":5659},[3144,5791,5679],{"class":5663},[3144,5793,5794],{"class":5682},"Recreate\n",[3114,5796,5797],{},[3118,5798,3282],{},[3903,5800,5801,5804,5807],{},[3906,5802,5803],{},"Простота — немає складної логіки поступового оновлення",[3906,5805,5806],{},"Менше ресурсів — не потрібно одночасно тримати старі та нові Pod",[3906,5808,5809],{},"Гарантія, що лише одна версія працює — немає проблем з несумісністю версій",[3114,5811,5812],{},[3118,5813,5730],{},[3903,5815,5816,5819],{},[3906,5817,5818],{},"Downtime — є період (30-60 секунд), коли сервіс недоступний",[3906,5820,5821],{},"Ризикованіше — якщо нова версія має баг, користувачі одразу його побачать",[3114,5823,5824],{},[3118,5825,5746],{},[3903,5827,5828,5831,5834],{},[3906,5829,5830],{},"Застосунки, які не підтримують одночасну роботу різних версій (наприклад, через несумісність схеми БД)",[3906,5832,5833],{},"Stateful застосунки з одним екземпляром (наприклад, база даних)",[3906,5835,5836],{},"Внутрішні сервіси, де downtime прийнятний (наприклад, cron jobs)",[3122,5838,5840],{"id":5839},"порівняння-стратегій","Порівняння стратегій",[3376,5842,5843],{},[3379,5844,5846],{"className":3381,"code":5845,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\n!define ROLLING_COLOR #e8f5e9\n!define RECREATE_COLOR #ffebee\n!define DOWNTIME_COLOR #fff3e0\n\nrectangle \"RollingUpdate\" ROLLING_COLOR {\n    rectangle \"t=0s\" as r0 {\n        (Pod 1 v1.0)\n        (Pod 2 v1.0)\n        (Pod 3 v1.0)\n    }\n    \n    rectangle \"t=10s\" as r10 {\n        (Pod 1 v1.0)\n        (Pod 2 v1.0)\n        (Pod 4 v2.0)\n    }\n    \n    rectangle \"t=20s\" as r20 {\n        (Pod 1 v1.0)\n        (Pod 4 v2.0)\n        (Pod 5 v2.0)\n    }\n    \n    rectangle \"t=30s\" as r30 {\n        (Pod 4 v2.0)\n        (Pod 5 v2.0)\n        (Pod 6 v2.0)\n    }\n    \n    r0 -down-> r10\n    r10 -down-> r20\n    r20 -down-> r30\n}\n\nrectangle \"Recreate\" RECREATE_COLOR {\n    rectangle \"t=0s\" as c0 {\n        (Pod 1 v1.0)\n        (Pod 2 v1.0)\n        (Pod 3 v1.0)\n    }\n    \n    rectangle \"t=5s\" as c5 DOWNTIME_COLOR {\n        note \"Downtime!\\nЖодного Pod\" as n1\n    }\n    \n    rectangle \"t=35s\" as c35 {\n        (Pod 4 v2.0)\n        (Pod 5 v2.0)\n        (Pod 6 v2.0)\n    }\n    \n    c0 -down-> c5\n    c5 -down-> c35\n}\n\nnote right of r30\n    RollingUpdate:\n    - Завжди є працюючі Pod\n    - Downtime: 0 секунд\n    - Час оновлення: 30 секунд\nend note\n\nnote right of c35\n    Recreate:\n    - Є період без Pod\n    - Downtime: 30 секунд\n    - Час оновлення: 35 секунд\nend note\n\n@enduml\n",[3349,5847,5848,5852,5856,5860,5864,5869,5874,5879,5883,5888,5893,5898,5903,5908,5912,5916,5921,5925,5929,5934,5938,5942,5947,5951,5955,5960,5964,5968,5973,5977,5981,5986,5990,5994,5999,6004,6009,6013,6017,6022,6027,6031,6035,6039,6043,6047,6052,6057,6061,6065,6070,6074,6078,6082,6086,6090,6095,6100,6104,6108,6113,6118,6123,6128,6133,6137,6141,6146,6151,6156,6161,6166,6170,6174],{"__ignoreMap":3384},[3144,5849,5850],{"class":3142,"line":3389},[3144,5851,3392],{},[3144,5853,5854],{"class":3142,"line":3395},[3144,5855,3398],{},[3144,5857,5858],{"class":3142,"line":3401},[3144,5859,3404],{},[3144,5861,5862],{"class":3142,"line":3407},[3144,5863,3411],{"emptyLinePlaceholder":3410},[3144,5865,5866],{"class":3142,"line":3414},[3144,5867,5868],{},"!define ROLLING_COLOR #e8f5e9\n",[3144,5870,5871],{"class":3142,"line":3420},[3144,5872,5873],{},"!define RECREATE_COLOR #ffebee\n",[3144,5875,5876],{"class":3142,"line":3426},[3144,5877,5878],{},"!define DOWNTIME_COLOR #fff3e0\n",[3144,5880,5881],{"class":3142,"line":3432},[3144,5882,3411],{"emptyLinePlaceholder":3410},[3144,5884,5885],{"class":3142,"line":3438},[3144,5886,5887],{},"rectangle \"RollingUpdate\" ROLLING_COLOR {\n",[3144,5889,5890],{"class":3142,"line":3444},[3144,5891,5892],{},"    rectangle \"t=0s\" as r0 {\n",[3144,5894,5895],{"class":3142,"line":3449},[3144,5896,5897],{},"        (Pod 1 v1.0)\n",[3144,5899,5900],{"class":3142,"line":3455},[3144,5901,5902],{},"        (Pod 2 v1.0)\n",[3144,5904,5905],{"class":3142,"line":3461},[3144,5906,5907],{},"        (Pod 3 v1.0)\n",[3144,5909,5910],{"class":3142,"line":3467},[3144,5911,3435],{},[3144,5913,5914],{"class":3142,"line":3473},[3144,5915,3802],{},[3144,5917,5918],{"class":3142,"line":3478},[3144,5919,5920],{},"    rectangle \"t=10s\" as r10 {\n",[3144,5922,5923],{"class":3142,"line":3483},[3144,5924,5897],{},[3144,5926,5927],{"class":3142,"line":3489},[3144,5928,5902],{},[3144,5930,5931],{"class":3142,"line":3495},[3144,5932,5933],{},"        (Pod 4 v2.0)\n",[3144,5935,5936],{"class":3142,"line":3501},[3144,5937,3435],{},[3144,5939,5940],{"class":3142,"line":3506},[3144,5941,3802],{},[3144,5943,5944],{"class":3142,"line":3512},[3144,5945,5946],{},"    rectangle \"t=20s\" as r20 {\n",[3144,5948,5949],{"class":3142,"line":3518},[3144,5950,5897],{},[3144,5952,5953],{"class":3142,"line":3524},[3144,5954,5933],{},[3144,5956,5957],{"class":3142,"line":3530},[3144,5958,5959],{},"        (Pod 5 v2.0)\n",[3144,5961,5962],{"class":3142,"line":3535},[3144,5963,3435],{},[3144,5965,5966],{"class":3142,"line":3705},[3144,5967,3802],{},[3144,5969,5970],{"class":3142,"line":3711},[3144,5971,5972],{},"    rectangle \"t=30s\" as r30 {\n",[3144,5974,5975],{"class":3142,"line":3717},[3144,5976,5933],{},[3144,5978,5979],{"class":3142,"line":3722},[3144,5980,5959],{},[3144,5982,5983],{"class":3142,"line":3727},[3144,5984,5985],{},"        (Pod 6 v2.0)\n",[3144,5987,5988],{"class":3142,"line":4084},[3144,5989,3435],{},[3144,5991,5992],{"class":3142,"line":4090},[3144,5993,3802],{},[3144,5995,5996],{"class":3142,"line":4096},[3144,5997,5998],{},"    r0 -down-> r10\n",[3144,6000,6001],{"class":3142,"line":4102},[3144,6002,6003],{},"    r10 -down-> r20\n",[3144,6005,6006],{"class":3142,"line":4107},[3144,6007,6008],{},"    r20 -down-> r30\n",[3144,6010,6011],{"class":3142,"line":4112},[3144,6012,3441],{},[3144,6014,6015],{"class":3142,"line":4118},[3144,6016,3411],{"emptyLinePlaceholder":3410},[3144,6018,6019],{"class":3142,"line":4124},[3144,6020,6021],{},"rectangle \"Recreate\" RECREATE_COLOR {\n",[3144,6023,6024],{"class":3142,"line":4130},[3144,6025,6026],{},"    rectangle \"t=0s\" as c0 {\n",[3144,6028,6029],{"class":3142,"line":4135},[3144,6030,5897],{},[3144,6032,6033],{"class":3142,"line":4140},[3144,6034,5902],{},[3144,6036,6037],{"class":3142,"line":5169},[3144,6038,5907],{},[3144,6040,6041],{"class":3142,"line":5174},[3144,6042,3435],{},[3144,6044,6045],{"class":3142,"line":5179},[3144,6046,3802],{},[3144,6048,6049],{"class":3142,"line":5185},[3144,6050,6051],{},"    rectangle \"t=5s\" as c5 DOWNTIME_COLOR {\n",[3144,6053,6054],{"class":3142,"line":5190},[3144,6055,6056],{},"        note \"Downtime!\\nЖодного Pod\" as n1\n",[3144,6058,6059],{"class":3142,"line":5196},[3144,6060,3435],{},[3144,6062,6063],{"class":3142,"line":5202},[3144,6064,3802],{},[3144,6066,6067],{"class":3142,"line":5207},[3144,6068,6069],{},"    rectangle \"t=35s\" as c35 {\n",[3144,6071,6072],{"class":3142,"line":5212},[3144,6073,5933],{},[3144,6075,6076],{"class":3142,"line":5218},[3144,6077,5959],{},[3144,6079,6080],{"class":3142,"line":5223},[3144,6081,5985],{},[3144,6083,6084],{"class":3142,"line":5228},[3144,6085,3435],{},[3144,6087,6088],{"class":3142,"line":5234},[3144,6089,3802],{},[3144,6091,6092],{"class":3142,"line":5240},[3144,6093,6094],{},"    c0 -down-> c5\n",[3144,6096,6097],{"class":3142,"line":5246},[3144,6098,6099],{},"    c5 -down-> c35\n",[3144,6101,6102],{"class":3142,"line":5251},[3144,6103,3441],{},[3144,6105,6106],{"class":3142,"line":5256},[3144,6107,3411],{"emptyLinePlaceholder":3410},[3144,6109,6110],{"class":3142,"line":5261},[3144,6111,6112],{},"note right of r30\n",[3144,6114,6115],{"class":3142,"line":5266},[3144,6116,6117],{},"    RollingUpdate:\n",[3144,6119,6120],{"class":3142,"line":5272},[3144,6121,6122],{},"    - Завжди є працюючі Pod\n",[3144,6124,6125],{"class":3142,"line":5277},[3144,6126,6127],{},"    - Downtime: 0 секунд\n",[3144,6129,6130],{"class":3142,"line":5282},[3144,6131,6132],{},"    - Час оновлення: 30 секунд\n",[3144,6134,6135],{"class":3142,"line":5287},[3144,6136,3527],{},[3144,6138,6139],{"class":3142,"line":5292},[3144,6140,3411],{"emptyLinePlaceholder":3410},[3144,6142,6143],{"class":3142,"line":5298},[3144,6144,6145],{},"note right of c35\n",[3144,6147,6148],{"class":3142,"line":5303},[3144,6149,6150],{},"    Recreate:\n",[3144,6152,6153],{"class":3142,"line":5308},[3144,6154,6155],{},"    - Є період без Pod\n",[3144,6157,6158],{"class":3142,"line":5314},[3144,6159,6160],{},"    - Downtime: 30 секунд\n",[3144,6162,6163],{"class":3142,"line":5320},[3144,6164,6165],{},"    - Час оновлення: 35 секунд\n",[3144,6167,6168],{"class":3142,"line":5325},[3144,6169,3527],{},[3144,6171,6172],{"class":3142,"line":5330},[3144,6173,3411],{"emptyLinePlaceholder":3410},[3144,6175,6176],{"class":3142,"line":5336},[3144,6177,3538],{},[3291,6179],{},[3109,6181,6183],{"id":6182},"параметри-rolling-update-maxsurge-та-maxunavailable","Параметри Rolling Update: maxSurge та maxUnavailable",[3114,6185,6186],{},"Тепер розберемо найважливіші параметри, які контролюють швидкість та безпеку rolling update.",[3122,6188,3355],{"id":6189},"maxunavailable",[3114,6191,6192,6193,6196],{},"Максимальна кількість Pod, які можуть бути ",[3118,6194,6195],{},"недоступними"," (відключеними) під час оновлення.",[3903,6198,6199,6205],{},[3906,6200,6201,6204],{},[3118,6202,6203],{},"Простими словами (з іншого ракурсу):"," Це ваш \"запас міцності\" або допустима втрата потужності. Цей параметр показує, наскільки сильно ви готові тимчасово \"просісти\" по продуктивності заради швидшого оновлення.",[3906,6206,6207,6210,6211,6214,6215,6218,6219,6223,6224,6227,6228,6231],{},[3118,6208,6209],{},"Конкретний приклад:"," Якщо ваш інтернет-магазин під час розпродажу обслуговується 10 контейнерами (",[3349,6212,6213],{},"replicas: 10",") і ви вказуєте ",[3349,6216,6217],{},"maxUnavailable: 30%"," (тобто 3 контейнери), це означає: ",[6220,6221,6222],"em",{},"\"Я згоден, щоб під час оновлення мій сайт тимчасово обслуговували 7 контейнерів замість 10, поки інші 3 перестворюються на нову версію\"",". Якщо ж ви вкажете ",[3349,6225,6226],{},"maxUnavailable: 0"," (або ",[3349,6229,6230],{},"0%","), це означає, що ви за жодних обставин не згодні на тимчасове просідання потужності, і Kubernetes не видалить жодного старого контейнера, поки не переконається, що новий успішно піднявся і готовий приймати трафік.",[3114,6233,6234,6237,6238,6241,6242,6245,6246,6248,6249,6241,6252,6255],{},[3118,6235,6236],{},"Формат:"," Абсолютне число (",[3349,6239,6240],{},"1",", ",[3349,6243,6244],{},"2",") або відсоток від ",[3349,6247,3933],{}," (",[3349,6250,6251],{},"25%",[3349,6253,6254],{},"50%",").",[3114,6257,6258],{},[3118,6259,6260],{},"Формула розрахунку мінімальної кількості доступних Pod:",[3379,6262,6267],{"className":6263,"code":6265,"language":6266},[6264],"language-text","min_available = replicas - maxUnavailable\n","text",[3349,6268,6265],{"__ignoreMap":3384},[3114,6270,6271],{},[3118,6272,6273],{},"Приклади:",[6275,6276,6277,6310,6349,6377],"field-group",{},[6278,6279,6281,6289,6299,6304],"field",{"name":6280},"replicas: 10, maxUnavailable: 2",[3114,6282,6283,3156,6286],{},[3118,6284,6285],{},"Розрахунок:",[3349,6287,6288],{},"min_available = 10 - 2 = 8",[3114,6290,6291,6294,6295,6298],{},[3118,6292,6293],{},"Означає:"," Під час оновлення мінімум ",[3118,6296,6297],{},"8 Pod"," мають бути доступними. Kubernetes може видалити максимум 2 старі Pod одразу.",[3114,6300,6301],{},[3118,6302,6303],{},"Візуалізація:",[3379,6305,6308],{"className":6306,"code":6307,"language":6266},[6264],"Початок:  [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1]  (10 Pod)\nКрок 1:   [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v2] [v2]  (8 v1, 2 v2)\nКрок 2:   [v1] [v1] [v1] [v1] [v1] [v1] [v2] [v2] [v2] [v2]  (6 v1, 4 v2)\n...\nКінець:   [v2] [v2] [v2] [v2] [v2] [v2] [v2] [v2] [v2] [v2]  (10 Pod)\n",[3349,6309,6307],{"__ignoreMap":3384},[6278,6311,6313,6327,6331,6340],{"name":6312},"replicas: 10, maxUnavailable: 25%",[3114,6314,6315,3156,6317,6320,6321,6324,6325],{},[3118,6316,6285],{},[3349,6318,6319],{},"25% від 10 = 2.5"," → округлюється ",[3118,6322,6323],{},"вниз"," до ",[3349,6326,6244],{},[3114,6328,6329],{},[3349,6330,6288],{},[3114,6332,6333,6335,6336,6339],{},[3118,6334,6293],{}," Те саме, що ",[3349,6337,6338],{},"maxUnavailable: 2"," — мінімум 8 Pod доступні.",[3114,6341,6342,6345,6346,6348],{},[3118,6343,6344],{},"Чому округлення вниз?"," Kubernetes завжди округлює ",[3349,6347,3355],{}," вниз для безпеки — краще залишити більше доступних Pod, ніж менше.",[6278,6350,6352,6359,6367,6371],{"name":6351},"replicas: 3, maxUnavailable: 1",[3114,6353,6354,3156,6356],{},[3118,6355,6285],{},[3349,6357,6358],{},"min_available = 3 - 1 = 2",[3114,6360,6361,6294,6363,6366],{},[3118,6362,6293],{},[3118,6364,6365],{},"2 Pod"," доступні. Kubernetes оновлює по одному Pod за раз.",[3114,6368,6369],{},[3118,6370,6303],{},[3379,6372,6375],{"className":6373,"code":6374,"language":6266},[6264],"Початок:  [v1] [v1] [v1]           (3 Pod)\nКрок 1:   [v1] [v1] [v2]           (2 v1, 1 v2)\nКрок 2:   [v1] [v2] [v2]           (1 v1, 2 v2)\nКрок 3:   [v2] [v2] [v2]           (3 Pod)\n",[3349,6376,6374],{"__ignoreMap":3384},[6278,6378,6380,6387,6400],{"name":6379},"replicas: 3, maxUnavailable: 0",[3114,6381,6382,3156,6384],{},[3118,6383,6285],{},[3349,6385,6386],{},"min_available = 3 - 0 = 3",[3114,6388,6389,6391,6392,6395,6396,6399],{},[3118,6390,6293],{}," Під час оновлення ",[3118,6393,6394],{},"всі 3 Pod"," мають бути доступними. Kubernetes ",[3118,6397,6398],{},"не може видалити жодного старого Pod",", поки не створить новий.",[3114,6401,6402,6404,6405,6408],{},[3118,6403,3734],{}," Це вимагає ",[3349,6406,6407],{},"maxSurge > 0",", інакше оновлення неможливе (не можна ні видалити старі, ні створити нові понад ліміт).",[3122,6410,3351],{"id":6411},"maxsurge",[3114,6413,6414,6415,6418,6419,6421],{},"Максимальна кількість ",[3118,6416,6417],{},"додаткових"," Pod, які можуть бути тимчасово створені понад базове значення ",[3349,6420,3933],{}," під час оновлення.",[3903,6423,6424,6429],{},[3906,6425,6426,6428],{},[3118,6427,6203],{}," Це ваш \"кредитний ліміт\" на ресурси серверів. Цей параметр вказує, скільки додаткової оперативної пам'яті та процесорного часу ви готові тимчасово виділити (позичити) у кластера, щоб паралельно запустити нові контейнери поруч зі старими.",[3906,6430,6431,6433,6434,6436,6437,6440,6441,6444],{},[3118,6432,6209],{}," Уявіть, що ви переносите меблі зі старої кімнати в нову. Якщо у вас є вільний коридор (додатковий простір), ви можете винести нові меблі туди, розпакувати, а вже потім заносити (це швидкий варіант, аналог ",[3349,6435,6407],{},"). Якщо ж коридору немає (вільних ресурсів у кластері обмаль, ",[3349,6438,6439],{},"maxSurge: 0","), вам доведеться спочатку викинути старий диван (видалити старий Pod), звільнити місце, і лише потім заносити новий (це повільніше, але економно). З ",[3349,6442,6443],{},"maxSurge: 20%"," для 10 реплік Kubernetes створює 2 нових контейнери нової версії одночасно, навіть не чіпаючи старі, прискорюючи перехід за рахунок тимчасового використання додаткових ресурсів кластера.",[3114,6446,6447,6237,6449,6241,6451,6245,6453,6248,6455,6241,6457,6255],{},[3118,6448,6236],{},[3349,6450,6240],{},[3349,6452,6244],{},[3349,6454,3933],{},[3349,6456,6251],{},[3349,6458,6254],{},[3114,6460,6461],{},[3118,6462,6463],{},"Формула розрахунку максимальної кількості Pod під час оновлення:",[3379,6465,6468],{"className":6466,"code":6467,"language":6266},[6264],"max_pods = replicas + maxSurge\n",[3349,6469,6467],{"__ignoreMap":3384},[3114,6471,6472],{},[3118,6473,6273],{},[6275,6475,6476,6515,6538,6566],{},[6278,6477,6479,6486,6495,6499,6505],{"name":6478},"replicas: 10, maxSurge: 2",[3114,6480,6481,3156,6483],{},[3118,6482,6285],{},[3349,6484,6485],{},"max_pods = 10 + 2 = 12",[3114,6487,6488,6490,6491,6494],{},[3118,6489,6293],{}," Під час оновлення максимум ",[3118,6492,6493],{},"12 Pod"," можуть існувати одночасно. Kubernetes може створити 2 нові Pod понад 10 реплік.",[3114,6496,6497],{},[3118,6498,6303],{},[3379,6500,6503],{"className":6501,"code":6502,"language":6266},[6264],"Початок:  [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1]        (10 Pod)\nКрок 1:   [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v2] [v2]  (12 Pod!)\nКрок 2:   [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v1] [v2] [v2] [v2] [v2]  (12 Pod!)\n...\nКінець:   [v2] [v2] [v2] [v2] [v2] [v2] [v2] [v2] [v2] [v2]        (10 Pod)\n",[3349,6504,6502],{"__ignoreMap":3384},[3114,6506,6507,6510,6511,6514],{},[3118,6508,6509],{},"Навіщо це потрібно?"," Додаткові Pod дозволяють ",[3118,6512,6513],{},"швидше"," виконати оновлення. Нові Pod створюються паралельно зі старими, і лише після готовності нових старі видаляються.",[6278,6516,6518,6525,6530],{"name":6517},"replicas: 10, maxSurge: 50%",[3114,6519,6520,3156,6522],{},[3118,6521,6285],{},[3349,6523,6524],{},"50% від 10 = 5",[3114,6526,6527],{},[3349,6528,6529],{},"max_pods = 10 + 5 = 15",[3114,6531,6532,6490,6534,6537],{},[3118,6533,6293],{},[3118,6535,6536],{},"15 Pod"," можуть існувати одночасно. Дуже швидке оновлення, але потребує багато ресурсів.",[6278,6539,6541,6548,6556,6560],{"name":6540},"replicas: 3, maxSurge: 1",[3114,6542,6543,3156,6545],{},[3118,6544,6285],{},[3349,6546,6547],{},"max_pods = 3 + 1 = 4",[3114,6549,6550,6490,6552,6555],{},[3118,6551,6293],{},[3118,6553,6554],{},"4 Pod"," можуть існувати одночасно.",[3114,6557,6558],{},[3118,6559,6303],{},[3379,6561,6564],{"className":6562,"code":6563,"language":6266},[6264],"Початок:  [v1] [v1] [v1]           (3 Pod)\nКрок 1:   [v1] [v1] [v1] [v2]      (4 Pod! 3 v1, 1 v2)\nКрок 2:   [v1] [v1] [v2] [v2]      (4 Pod! 2 v1, 2 v2)\nКрок 3:   [v1] [v2] [v2] [v2]      (4 Pod! 1 v1, 3 v2)\nКрок 4:   [v2] [v2] [v2]           (3 Pod)\n",[3349,6565,6563],{"__ignoreMap":3384},[6278,6567,6569,6576,6588],{"name":6568},"replicas: 3, maxSurge: 0",[3114,6570,6571,3156,6573],{},[3118,6572,6285],{},[3349,6574,6575],{},"max_pods = 3 + 0 = 3",[3114,6577,6578,6490,6580,6583,6584,6587],{},[3118,6579,6293],{},[3118,6581,6582],{},"3 Pod"," можуть існувати одночасно. Kubernetes ",[3118,6585,6586],{},"не може створити додаткові Pod"," — спочатку має видалити старий, потім створити новий.",[3114,6589,6590,6404,6592,6595],{},[3118,6591,3734],{},[3349,6593,6594],{},"maxUnavailable > 0",", інакше оновлення неможливе.",[3122,6597,6599],{"id":6598},"комбінації-maxsurge-та-maxunavailable","Комбінації maxSurge та maxUnavailable",[3114,6601,6602],{},"Різні комбінації цих параметрів дають різну поведінку оновлення:",[3330,6604,6605,6664,6719,6772],{},[3333,6606,6609,6632,6638,6643,6654,6659],{"icon":6607,"title":6608},"i-heroicons-bolt","Швидке оновлення, багато ресурсів",[3379,6610,6612],{"className":5650,"code":6611,"language":5652,"meta":3384,"style":3384},"maxSurge: 50%\nmaxUnavailable: 0\n",[3349,6613,6614,6623],{"__ignoreMap":3384},[3144,6615,6616,6618,6620],{"class":3142,"line":3389},[3144,6617,3351],{"class":5659},[3144,6619,5679],{"class":5663},[3144,6621,6622],{"class":5682},"50%\n",[3144,6624,6625,6627,6629],{"class":3142,"line":3395},[3144,6626,3355],{"class":5659},[3144,6628,5679],{"class":5663},[3144,6630,6631],{"class":5700},"0\n",[3114,6633,6634,6637],{},[3118,6635,6636],{},"Поведінка:"," Створюються багато нових Pod одразу (до 50% понад replicas), старі видаляються лише після готовності нових. Завжди є всі репліки доступними.",[3114,6639,6640],{},[3118,6641,6642],{},"Приклад (replicas: 10):",[3903,6644,6645,6648,6651],{},[3906,6646,6647],{},"Крок 1: 10 старих + 5 нових = 15 Pod",[3906,6649,6650],{},"Крок 2: 5 старих + 10 нових = 15 Pod",[3906,6652,6653],{},"Крок 3: 0 старих + 10 нових = 10 Pod",[3114,6655,6656,6658],{},[3118,6657,3282],{}," Найшвидше оновлення, zero-downtime гарантовано",[3114,6660,6661,6663],{},[3118,6662,5730],{}," Потребує 150% ресурсів (CPU, пам'ять) під час оновлення",[3333,6665,6668,6690,6695,6699,6709,6714],{"icon":6666,"title":6667},"i-heroicons-arrow-trending-down","Повільне оновлення, мало ресурсів",[3379,6669,6671],{"className":5650,"code":6670,"language":5652,"meta":3384,"style":3384},"maxSurge: 0\nmaxUnavailable: 25%\n",[3349,6672,6673,6681],{"__ignoreMap":3384},[3144,6674,6675,6677,6679],{"class":3142,"line":3389},[3144,6676,3351],{"class":5659},[3144,6678,5679],{"class":5663},[3144,6680,6631],{"class":5700},[3144,6682,6683,6685,6687],{"class":3142,"line":3395},[3144,6684,3355],{"class":5659},[3144,6686,5679],{"class":5663},[3144,6688,6689],{"class":5682},"25%\n",[3114,6691,6692,6694],{},[3118,6693,6636],{}," Спочатку видаляються старі Pod (до 25%), потім створюються нові. Економить ресурси, але є період зниженої доступності.",[3114,6696,6697],{},[3118,6698,6642],{},[3903,6700,6701,6704,6707],{},[3906,6702,6703],{},"Крок 1: 7-8 старих + 2-3 нових = 10 Pod (мінімум 7 доступних)",[3906,6705,6706],{},"Крок 2: 5 старих + 5 нових = 10 Pod",[3906,6708,6653],{},[3114,6710,6711,6713],{},[3118,6712,3282],{}," Не потребує додаткових ресурсів",[3114,6715,6716,6718],{},[3118,6717,5730],{}," Повільніше, є період зниженої доступності (7 замість 10 Pod)",[3333,6720,6723,6744,6749,6753,6762,6767],{"icon":6721,"title":6722},"i-heroicons-scale","Збалансований підхід (за замовчуванням)",[3379,6724,6726],{"className":5650,"code":6725,"language":5652,"meta":3384,"style":3384},"maxSurge: 25%\nmaxUnavailable: 25%\n",[3349,6727,6728,6736],{"__ignoreMap":3384},[3144,6729,6730,6732,6734],{"class":3142,"line":3389},[3144,6731,3351],{"class":5659},[3144,6733,5679],{"class":5663},[3144,6735,6689],{"class":5682},[3144,6737,6738,6740,6742],{"class":3142,"line":3395},[3144,6739,3355],{"class":5659},[3144,6741,5679],{"class":5663},[3144,6743,6689],{"class":5682},[3114,6745,6746,6748],{},[3118,6747,6636],{}," Компроміс між швидкістю та ресурсами. Можна створити до 25% додаткових Pod та видалити до 25% старих одночасно.",[3114,6750,6751],{},[3118,6752,6642],{},[3903,6754,6755,6758,6760],{},[3906,6756,6757],{},"Крок 1: 7-8 старих + 2-3 нових = 10-11 Pod",[3906,6759,6706],{},[3906,6761,6653],{},[3114,6763,6764,6766],{},[3118,6765,3282],{}," Баланс між швидкістю та ресурсами",[3114,6768,6769,6771],{},[3118,6770,5730],{}," Не найшвидше, не найекономніше",[3333,6773,6776,6797,6802,6806,6820,6825],{"icon":6774,"title":6775},"i-heroicons-shield-check","Обережне оновлення (по одному)",[3379,6777,6779],{"className":5650,"code":6778,"language":5652,"meta":3384,"style":3384},"maxSurge: 1\nmaxUnavailable: 0\n",[3349,6780,6781,6789],{"__ignoreMap":3384},[3144,6782,6783,6785,6787],{"class":3142,"line":3389},[3144,6784,3351],{"class":5659},[3144,6786,5679],{"class":5663},[3144,6788,5701],{"class":5700},[3144,6790,6791,6793,6795],{"class":3142,"line":3395},[3144,6792,3355],{"class":5659},[3144,6794,5679],{"class":5663},[3144,6796,6631],{"class":5700},[3114,6798,6799,6801],{},[3118,6800,6636],{}," Оновлення по одному Pod за раз. Завжди є всі репліки доступними. Найбезпечніший підхід.",[3114,6803,6804],{},[3118,6805,6642],{},[3903,6807,6808,6811,6814,6817],{},[3906,6809,6810],{},"Крок 1: 10 старих + 1 новий = 11 Pod",[3906,6812,6813],{},"Крок 2: 9 старих + 2 нових = 11 Pod",[3906,6815,6816],{},"...",[3906,6818,6819],{},"Крок 10: 0 старих + 10 нових = 10 Pod",[3114,6821,6822,6824],{},[3118,6823,3282],{}," Максимальна безпека, легко виявити проблеми на ранній стадії",[3114,6826,6827,6829],{},[3118,6828,5730],{}," Найповільніше оновлення (10 ітерацій для 10 реплік)",[3122,6831,6833],{"id":6832},"математичні-розрахунки-для-різних-сценаріїв","Математичні розрахунки для різних сценаріїв",[3114,6835,6836],{},"Давайте розрахуємо, скільки Pod буде під час оновлення для різних конфігурацій:",[3114,6838,6839,3156,6842],{},[3118,6840,6841],{},"Дано:",[3349,6843,6213],{},[6845,6846,6847,6867],"table",{},[6848,6849,6850],"thead",{},[6851,6852,6853,6856,6858,6861,6864],"tr",{},[6854,6855,3351],"th",{},[6854,6857,3355],{},[6854,6859,6860],{},"min_available",[6854,6862,6863],{},"max_pods",[6854,6865,6866],{},"Діапазон Pod під час оновлення",[6868,6869,6870,6887,6901,6915,6928,6942,6956],"tbody",{},[6851,6871,6872,6876,6878,6881,6884],{},[6873,6874,6875],"td",{},"0",[6873,6877,6240],{},[6873,6879,6880],{},"9",[6873,6882,6883],{},"10",[6873,6885,6886],{},"9-10 Pod",[6851,6888,6889,6891,6893,6896,6898],{},[6873,6890,6875],{},[6873,6892,6251],{},[6873,6894,6895],{},"8",[6873,6897,6883],{},[6873,6899,6900],{},"8-10 Pod",[6851,6902,6903,6905,6907,6909,6912],{},[6873,6904,6240],{},[6873,6906,6875],{},[6873,6908,6883],{},[6873,6910,6911],{},"11",[6873,6913,6914],{},"10-11 Pod",[6851,6916,6917,6919,6921,6923,6925],{},[6873,6918,6240],{},[6873,6920,6240],{},[6873,6922,6880],{},[6873,6924,6911],{},[6873,6926,6927],{},"9-11 Pod",[6851,6929,6930,6932,6934,6936,6939],{},[6873,6931,6251],{},[6873,6933,6251],{},[6873,6935,6895],{},[6873,6937,6938],{},"12",[6873,6940,6941],{},"8-12 Pod",[6851,6943,6944,6946,6948,6950,6953],{},[6873,6945,6254],{},[6873,6947,6875],{},[6873,6949,6883],{},[6873,6951,6952],{},"15",[6873,6954,6955],{},"10-15 Pod",[6851,6957,6958,6961,6963,6965,6968],{},[6873,6959,6960],{},"100%",[6873,6962,6875],{},[6873,6964,6883],{},[6873,6966,6967],{},"20",[6873,6969,6970],{},"10-20 Pod",[3114,6972,6973],{},[3118,6974,6975],{},"Висновки:",[3926,6977,6978,6984,6990,6996,7005],{},[3906,6979,6980,6983],{},[3118,6981,6982],{},"Більший maxSurge"," → швидше оновлення, але більше ресурсів",[3906,6985,6986,6989],{},[3118,6987,6988],{},"Більший maxUnavailable"," → швидше оновлення, але менша доступність",[3906,6991,6992,6995],{},[3118,6993,6994],{},"maxSurge=0, maxUnavailable=0"," → неможливо (оновлення заблоковано)",[3906,6997,6998,3156,7001,7004],{},[3118,6999,7000],{},"Для критичних сервісів:",[3349,7002,7003],{},"maxSurge > 0, maxUnavailable = 0"," (завжди повна доступність)",[3906,7006,7007,3156,7010,7013],{},[3118,7008,7009],{},"Для економії ресурсів:",[3349,7011,7012],{},"maxSurge = 0, maxUnavailable > 0"," (без додаткових Pod)",[3291,7015],{},[3109,7017,7019],{"id":7018},"додаткові-параметри-життєвого-циклу","Додаткові параметри життєвого циклу",[3114,7021,7022,7023,3352,7025,7027],{},"Окрім ",[3349,7024,3351],{},[3349,7026,3355],{},", є ще кілька важливих параметрів:",[3122,7029,4378],{"id":7030},"progressdeadlineseconds",[3114,7032,7033],{},"Максимальний час (у секундах), протягом якого Deployment має показати хоча б якийсь рух вперед (прогрес) під час оновлення чи розгортання.",[3903,7035,7036,7044],{},[3906,7037,7038,7040,7041],{},[3118,7039,6203],{}," Це ваш \"сигнальний таймер терпіння\" для системи моніторингу або CI/CD пайплайну. Він відповідає на запитання: ",[6220,7042,7043],{},"\"Скільки часу ми готові чекати безрезультатного \"висіння\" процесу оновлення, перш ніж офіційно визнати, що щось пішло не так і підняти тривогу?\"",[3906,7045,7046,7048,7049,7052,7053,7055,7056,7059],{},[3118,7047,6209],{}," Уявіть, що ви запустили оновлення застосунку і пішли пити каву. Якщо через помилку в коді новий контейнер не може запуститися й постійно падає, Kubernetes не буде чекати вічно. З ",[3349,7050,7051],{},"progressDeadlineSeconds: 300"," (5 хвилин) система засікає час. Якщо за ці 5 хвилин жоден новий Pod не зміг успішно запуститись і стати ",[3349,7054,4374],{}," (тобто не було жодного прогресу), Kubernetes зупинить безглузді спроби, переведе статус оновлення в ",[3349,7057,7058],{},"ProgressDeadlineExceeded"," (перевищено термін прогресу), що дозволить вашому автоматичному CI/CD скрипту чи ArgoCD одразу зрозуміти проблему та ініціювати відкат (rollback) до попередньої стабільної версії.",[3114,7061,7062,3156,7065,7068],{},[3118,7063,7064],{},"За замовчуванням:",[3349,7066,7067],{},"600"," (10 хвилин)",[3114,7070,7071],{},[3118,7072,7073],{},"Що вважається \"прогресом\"?",[3903,7075,7076,7081,7084],{},[3906,7077,7078,7079],{},"Новий Pod став ",[3349,7080,4374],{},[3906,7082,7083],{},"Старий Pod був видалений",[3906,7085,7086],{},"Будь-яка зміна у кількості доступних реплік",[3114,7088,7089],{},[3118,7090,7091],{},"Що відбувається при перевищенні таймауту?",[3114,7093,7094,7095,7097],{},"Якщо за ",[3349,7096,4378],{}," жоден новий Pod не став готовим, Deployment отримує статус:",[3379,7099,7101],{"className":5650,"code":7100,"language":5652,"meta":3384,"style":3384},"status:\n  conditions:\n    - type: Progressing\n      status: \"False\"\n      reason: ProgressDeadlineExceeded\n      message: \"ReplicaSet 'todoapi-def456' has timed out progressing.\"\n",[3349,7102,7103,7110,7117,7130,7141,7151],{"__ignoreMap":3384},[3144,7104,7105,7108],{"class":3142,"line":3389},[3144,7106,7107],{"class":5659},"status",[3144,7109,5664],{"class":5663},[3144,7111,7112,7115],{"class":3142,"line":3395},[3144,7113,7114],{"class":5659},"  conditions",[3144,7116,5664],{"class":5663},[3144,7118,7119,7122,7125,7127],{"class":3142,"line":3401},[3144,7120,7121],{"class":5663},"    - ",[3144,7123,7124],{"class":5659},"type",[3144,7126,5679],{"class":5663},[3144,7128,7129],{"class":5682},"Progressing\n",[3144,7131,7132,7135,7137],{"class":3142,"line":3407},[3144,7133,7134],{"class":5659},"      status",[3144,7136,5679],{"class":5663},[3144,7138,7140],{"class":7139},"sbdoH","\"False\"\n",[3144,7142,7143,7146,7148],{"class":3142,"line":3414},[3144,7144,7145],{"class":5659},"      reason",[3144,7147,5679],{"class":5663},[3144,7149,7150],{"class":5682},"ProgressDeadlineExceeded\n",[3144,7152,7153,7156,7158],{"class":3142,"line":3420},[3144,7154,7155],{"class":5659},"      message",[3144,7157,5679],{"class":5663},[3144,7159,7160],{"class":7139},"\"ReplicaSet 'todoapi-def456' has timed out progressing.\"\n",[3114,7162,7163,7164,7167,7168,7171],{},"Оновлення ",[3118,7165,7166],{},"зупиняється",", але ",[3118,7169,7170],{},"не відкочується"," автоматично. Старі Pod продовжують працювати.",[3114,7173,7174],{},[3118,7175,7176],{},"Приклад:",[3379,7178,7180],{"className":5650,"code":7179,"language":5652,"meta":3384,"style":3384},"spec:\n  progressDeadlineSeconds: 300  # 5 хвилин\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 1\n      maxUnavailable: 0\n",[3349,7181,7182,7188,7202,7208,7216,7222,7230],{"__ignoreMap":3384},[3144,7183,7184,7186],{"class":3142,"line":3389},[3144,7185,5660],{"class":5659},[3144,7187,5664],{"class":5663},[3144,7189,7190,7193,7195,7198],{"class":3142,"line":3395},[3144,7191,7192],{"class":5659},"  progressDeadlineSeconds",[3144,7194,5679],{"class":5663},[3144,7196,7197],{"class":5700},"300",[3144,7199,7201],{"class":7200},"spJ8K","  # 5 хвилин\n",[3144,7203,7204,7206],{"class":3142,"line":3401},[3144,7205,5669],{"class":5659},[3144,7207,5664],{"class":5663},[3144,7209,7210,7212,7214],{"class":3142,"line":3407},[3144,7211,5676],{"class":5659},[3144,7213,5679],{"class":5663},[3144,7215,5683],{"class":5682},[3144,7217,7218,7220],{"class":3142,"line":3414},[3144,7219,5688],{"class":5659},[3144,7221,5664],{"class":5663},[3144,7223,7224,7226,7228],{"class":3142,"line":3420},[3144,7225,5695],{"class":5659},[3144,7227,5679],{"class":5663},[3144,7229,5701],{"class":5700},[3144,7231,7232,7234,7236],{"class":3142,"line":3426},[3144,7233,5706],{"class":5659},[3144,7235,5679],{"class":5663},[3144,7237,6631],{"class":5700},[3114,7239,7240,7243],{},[3118,7241,7242],{},"Сценарій:"," Новий образ має баг — Pod стартує, але не проходить readiness probe. Через 5 хвилин Kubernetes зупиняє оновлення та повідомляє про проблему.",[7245,7246,7247,7262,7268],"warning",{},[3114,7248,7249,3156,7251,7253,7254,7257,7258,7261],{},[3118,7250,3734],{},[3349,7252,4378],{}," — це ",[3118,7255,7256],{},"не"," загальний час оновлення. Це час ",[3118,7259,7260],{},"між прогресами",". Якщо кожен Pod стартує за 30 секунд, а у вас 10 реплік, загальний час оновлення може бути 5 хвилин, і це нормально (бо є прогрес кожні 30 секунд).",[3114,7263,7264,7267],{},[3118,7265,7266],{},"Неправильне розуміння:"," \"Оновлення має завершитись за 600 секунд\"",[3114,7269,7270,7273],{},[3118,7271,7272],{},"Правильне розуміння:"," \"Між кожним прогресом (новий Pod Ready) має пройти не більше 600 секунд\"",[3122,7275,7277],{"id":7276},"minreadyseconds","minReadySeconds",[3114,7279,7280],{},"Мінімальний час (у секундах), протягом якого новостворений Pod має безперервно працювати у стані \"Ready\" (готовий приймати трафік) без жодних падінь і перезапусків, перш ніж Kubernetes вважатиме його повністю стабільним і продовжить оновлювати інші репліки.",[3903,7282,7283,7292],{},[3906,7284,7285,7287,7288,7291],{},[3118,7286,6203],{}," Це \"карантинний період\" або тест на витривалість для нових контейнерів. Він захищає вас від ситуації, коли контейнер формально запустився, відрапортував ",[6220,7289,7290],{},"\"я готовий!\"",", але через 5 секунд упав через внутрішню помилку ініціалізації чи невірно зчитану конфігурацію.",[3906,7293,7294,7296,7297,7300,7301,7304,7305,7308,7309,7311,7312,7315,7316,7319],{},[3118,7295,6209],{}," Уявіть нового працівника у команді. Якщо ви дасте йому роботу і він у першу ж секунду скаже ",[6220,7298,7299],{},"\"все зрозуміло!\"",", ви не побіжите одразу звільняти старого працівника. Ви почекаєте хоча б день-два, щоб переконатися, що він дійсно справляється. Так само і з ",[3349,7302,7303],{},"minReadySeconds: 30",". Коли новий Pod проходить ",[3349,7306,7307],{},"readiness probe"," (наприклад, віддав HTTP 200 на тестовий запит), Kubernetes не переходить одразу до видалення наступного старого Pod. Він тримає новий Pod \"на карантині\" рівно 30 секунд. Якщо протягом цих 30 секунд новий контейнер не впав, не перезапустився і стабільно тримав статус ",[3349,7310,4374],{},", Kubernetes робить висновок: ",[6220,7313,7314],{},"\"Все чудово, цей контейнер дійсно здоровий та працездатний\"",", маркує його як ",[3349,7317,7318],{},"Available"," і спокійно продовжує оновлення решти кластера.",[3114,7321,7322,3156,7324,7326,7327,5602],{},[3118,7323,7064],{},[3349,7325,6875],{}," (Pod вважається доступним одразу після ",[3349,7328,7329],{},"Ready=True",[3114,7331,7332],{},[3118,7333,6509],{},[3114,7335,7336,7337,7339],{},"Іноді Pod стартує успішно (проходить readiness probe), але падає через кілька секунд (наприклад, через помилку підключення до БД, яка виявляється не одразу). ",[3349,7338,7277],{}," додає додаткову перевірку стабільності.",[3114,7341,7342],{},[3118,7343,7176],{},[3379,7345,7347],{"className":5650,"code":7346,"language":5652,"meta":3384,"style":3384},"spec:\n  minReadySeconds: 30\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 1\n      maxUnavailable: 0\n",[3349,7348,7349,7355,7365,7371,7379,7385,7393],{"__ignoreMap":3384},[3144,7350,7351,7353],{"class":3142,"line":3389},[3144,7352,5660],{"class":5659},[3144,7354,5664],{"class":5663},[3144,7356,7357,7360,7362],{"class":3142,"line":3395},[3144,7358,7359],{"class":5659},"  minReadySeconds",[3144,7361,5679],{"class":5663},[3144,7363,7364],{"class":5700},"30\n",[3144,7366,7367,7369],{"class":3142,"line":3401},[3144,7368,5669],{"class":5659},[3144,7370,5664],{"class":5663},[3144,7372,7373,7375,7377],{"class":3142,"line":3407},[3144,7374,5676],{"class":5659},[3144,7376,5679],{"class":5663},[3144,7378,5683],{"class":5682},[3144,7380,7381,7383],{"class":3142,"line":3414},[3144,7382,5688],{"class":5659},[3144,7384,5664],{"class":5663},[3144,7386,7387,7389,7391],{"class":3142,"line":3420},[3144,7388,5695],{"class":5659},[3144,7390,5679],{"class":5663},[3144,7392,5701],{"class":5700},[3144,7394,7395,7397,7399],{"class":3142,"line":3426},[3144,7396,5706],{"class":5659},[3144,7398,5679],{"class":5663},[3144,7400,6631],{"class":5700},[3114,7402,7403],{},[3118,7404,7405],{},"Що відбувається:",[3926,7407,7408,7411,7416,7422,7425],{},[3906,7409,7410],{},"Новий Pod стартує",[3906,7412,7413,7414],{},"Pod проходить readiness probe → ",[3349,7415,7329],{},[3906,7417,7418,7419],{},"Kubernetes ",[3118,7420,7421],{},"чекає 30 секунд",[3906,7423,7424],{},"Якщо за ці 30 секунд Pod не впав → він вважається доступним, оновлення продовжується",[3906,7426,7427],{},"Якщо Pod впав → оновлення зупиняється",[3114,7429,7430],{},[3118,7431,6303],{},[3376,7433,7434],{},[3379,7435,7437],{"className":3381,"code":7436,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nstate \"Pod створено\" as created\nstate \"Container запущено\" as running\nstate \"Readiness probe ✓\" as ready\nstate \"Очікування minReadySeconds\" as waiting #fff3e0\nstate \"Pod доступний\" as available #e8f5e9\nstate \"Pod впав\" as crashed #ffebee\n\n[*] --> created\ncreated --> running : 5s\nrunning --> ready : 10s\nready --> waiting : Ready=True\n\nwaiting --> available : 30s без падінь\nwaiting --> crashed : Pod впав протягом 30s\n\navailable --> [*] : Оновлення продовжується\ncrashed --> [*] : Оновлення зупиняється\n\nnote right of waiting\n    minReadySeconds = 30\n    Kubernetes чекає 30 секунд\n    після Ready=True\nend note\n\n@enduml\n",[3349,7438,7439,7443,7447,7451,7455,7460,7465,7470,7475,7480,7485,7489,7494,7499,7504,7509,7513,7518,7523,7527,7532,7537,7541,7546,7551,7556,7561,7565,7569],{"__ignoreMap":3384},[3144,7440,7441],{"class":3142,"line":3389},[3144,7442,3392],{},[3144,7444,7445],{"class":3142,"line":3395},[3144,7446,3398],{},[3144,7448,7449],{"class":3142,"line":3401},[3144,7450,3404],{},[3144,7452,7453],{"class":3142,"line":3407},[3144,7454,3411],{"emptyLinePlaceholder":3410},[3144,7456,7457],{"class":3142,"line":3414},[3144,7458,7459],{},"state \"Pod створено\" as created\n",[3144,7461,7462],{"class":3142,"line":3420},[3144,7463,7464],{},"state \"Container запущено\" as running\n",[3144,7466,7467],{"class":3142,"line":3426},[3144,7468,7469],{},"state \"Readiness probe ✓\" as ready\n",[3144,7471,7472],{"class":3142,"line":3432},[3144,7473,7474],{},"state \"Очікування minReadySeconds\" as waiting #fff3e0\n",[3144,7476,7477],{"class":3142,"line":3438},[3144,7478,7479],{},"state \"Pod доступний\" as available #e8f5e9\n",[3144,7481,7482],{"class":3142,"line":3444},[3144,7483,7484],{},"state \"Pod впав\" as crashed #ffebee\n",[3144,7486,7487],{"class":3142,"line":3449},[3144,7488,3411],{"emptyLinePlaceholder":3410},[3144,7490,7491],{"class":3142,"line":3455},[3144,7492,7493],{},"[*] --> created\n",[3144,7495,7496],{"class":3142,"line":3461},[3144,7497,7498],{},"created --> running : 5s\n",[3144,7500,7501],{"class":3142,"line":3467},[3144,7502,7503],{},"running --> ready : 10s\n",[3144,7505,7506],{"class":3142,"line":3473},[3144,7507,7508],{},"ready --> waiting : Ready=True\n",[3144,7510,7511],{"class":3142,"line":3478},[3144,7512,3411],{"emptyLinePlaceholder":3410},[3144,7514,7515],{"class":3142,"line":3483},[3144,7516,7517],{},"waiting --> available : 30s без падінь\n",[3144,7519,7520],{"class":3142,"line":3489},[3144,7521,7522],{},"waiting --> crashed : Pod впав протягом 30s\n",[3144,7524,7525],{"class":3142,"line":3495},[3144,7526,3411],{"emptyLinePlaceholder":3410},[3144,7528,7529],{"class":3142,"line":3501},[3144,7530,7531],{},"available --> [*] : Оновлення продовжується\n",[3144,7533,7534],{"class":3142,"line":3506},[3144,7535,7536],{},"crashed --> [*] : Оновлення зупиняється\n",[3144,7538,7539],{"class":3142,"line":3512},[3144,7540,3411],{"emptyLinePlaceholder":3410},[3144,7542,7543],{"class":3142,"line":3518},[3144,7544,7545],{},"note right of waiting\n",[3144,7547,7548],{"class":3142,"line":3524},[3144,7549,7550],{},"    minReadySeconds = 30\n",[3144,7552,7553],{"class":3142,"line":3530},[3144,7554,7555],{},"    Kubernetes чекає 30 секунд\n",[3144,7557,7558],{"class":3142,"line":3535},[3144,7559,7560],{},"    після Ready=True\n",[3144,7562,7563],{"class":3142,"line":3705},[3144,7564,3527],{},[3144,7566,7567],{"class":3142,"line":3711},[3144,7568,3411],{"emptyLinePlaceholder":3410},[3144,7570,7571],{"class":3142,"line":3717},[3144,7572,3538],{},[3114,7574,7575],{},[3118,7576,5746],{},[3903,7578,7579,7582,7585],{},[3906,7580,7581],{},"Застосунки з повільною ініціалізацією (підключення до БД, завантаження конфігурації)",[3906,7583,7584],{},"Застосунки, які можуть падати через кілька секунд після старту",[3906,7586,7587],{},"Критичні сервіси, де важлива стабільність",[3114,7589,7590],{},[3118,7591,7592],{},"Типові значення:",[3903,7594,7595,7600,7606],{},[3906,7596,7597,7599],{},[3349,7598,6875],{}," — для простих застосунків (за замовчуванням)",[3906,7601,7602,7605],{},[3349,7603,7604],{},"10-30"," — для більшості веб-застосунків",[3906,7607,7608,7611],{},[3349,7609,7610],{},"60-120"," — для складних застосунків з довгою ініціалізацією",[3291,7613],{},[3109,7615,7617],{"id":7616},"health-checks-для-net-застосунків","Health Checks для .NET застосунків",[3114,7619,7620],{},"Тепер розглянемо, як правильно налаштувати health checks для ASP.NET Core застосунків у Kubernetes.",[3122,7622,7624],{"id":7623},"базові-health-checks-у-aspnet-core","Базові health checks у ASP.NET Core",[3114,7626,7627,7628,3289],{},"ASP.NET Core має вбудовану підтримку health checks через пакет ",[3349,7629,7630],{},"Microsoft.Extensions.Diagnostics.HealthChecks",[3114,7632,7633],{},[3118,7634,7635],{},"Простий приклад:",[3379,7637,7641],{"className":7638,"code":7639,"language":7640,"meta":3384,"style":3384},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","var builder = WebApplication.CreateBuilder(args);\n\n// Додаємо health checks\nbuilder.Services.AddHealthChecks();\n\nvar app = builder.Build();\n\n// Endpoint для health checks\napp.MapHealthChecks(\"/health\");\n\napp.Run();\n","csharp",[3349,7642,7643,7674,7678,7683,7701,7705,7723,7727,7732,7749,7753],{"__ignoreMap":3384},[3144,7644,7645,7649,7653,7656,7659,7661,7665,7668,7671],{"class":3142,"line":3389},[3144,7646,7648],{"class":7647},"su1O8","var",[3144,7650,7652],{"class":7651},"siwwj"," builder",[3144,7654,7655],{"class":5663}," = ",[3144,7657,7658],{"class":7651},"WebApplication",[3144,7660,3289],{"class":5663},[3144,7662,7664],{"class":7663},"s8Opu","CreateBuilder",[3144,7666,7667],{"class":5663},"(",[3144,7669,7670],{"class":7651},"args",[3144,7672,7673],{"class":5663},");\n",[3144,7675,7676],{"class":3142,"line":3395},[3144,7677,3411],{"emptyLinePlaceholder":3410},[3144,7679,7680],{"class":3142,"line":3401},[3144,7681,7682],{"class":7200},"// Додаємо health checks\n",[3144,7684,7685,7688,7690,7693,7695,7698],{"class":3142,"line":3407},[3144,7686,7687],{"class":7651},"builder",[3144,7689,3289],{"class":5663},[3144,7691,7692],{"class":7651},"Services",[3144,7694,3289],{"class":5663},[3144,7696,7697],{"class":7663},"AddHealthChecks",[3144,7699,7700],{"class":5663},"();\n",[3144,7702,7703],{"class":3142,"line":3414},[3144,7704,3411],{"emptyLinePlaceholder":3410},[3144,7706,7707,7709,7712,7714,7716,7718,7721],{"class":3142,"line":3420},[3144,7708,7648],{"class":7647},[3144,7710,7711],{"class":7651}," app",[3144,7713,7655],{"class":5663},[3144,7715,7687],{"class":7651},[3144,7717,3289],{"class":5663},[3144,7719,7720],{"class":7663},"Build",[3144,7722,7700],{"class":5663},[3144,7724,7725],{"class":3142,"line":3426},[3144,7726,3411],{"emptyLinePlaceholder":3410},[3144,7728,7729],{"class":3142,"line":3432},[3144,7730,7731],{"class":7200},"// Endpoint для health checks\n",[3144,7733,7734,7737,7739,7742,7744,7747],{"class":3142,"line":3438},[3144,7735,7736],{"class":7651},"app",[3144,7738,3289],{"class":5663},[3144,7740,7741],{"class":7663},"MapHealthChecks",[3144,7743,7667],{"class":5663},[3144,7745,7746],{"class":7139},"\"/health\"",[3144,7748,7673],{"class":5663},[3144,7750,7751],{"class":3142,"line":3444},[3144,7752,3411],{"emptyLinePlaceholder":3410},[3144,7754,7755,7757,7759,7762],{"class":3142,"line":3449},[3144,7756,7736],{"class":7651},[3144,7758,3289],{"class":5663},[3144,7760,7761],{"class":7663},"Run",[3144,7763,7700],{"class":5663},[3114,7765,7766,7767,7770],{},"Це створює endpoint ",[3349,7768,7769],{},"/health",", який повертає:",[3903,7772,7773,7783],{},[3906,7774,7775,7778,7779,7782],{},[3349,7776,7777],{},"200 OK"," + ",[3349,7780,7781],{},"\"Healthy\""," — якщо все добре",[3906,7784,7785,7778,7788,7791],{},[3349,7786,7787],{},"503 Service Unavailable",[3349,7789,7790],{},"\"Unhealthy\""," — якщо є проблеми",[3122,7793,7795],{"id":7794},"розширені-health-checks-з-перевірками","Розширені health checks з перевірками",[3114,7797,7798],{},"Для production потрібні більш детальні перевірки:",[3379,7800,7802],{"className":7638,"code":7801,"language":7640,"meta":3384,"style":3384},"using Microsoft.Extensions.Diagnostics.HealthChecks;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddHealthChecks()\n    // Перевірка підключення до БД\n    .AddNpgSql(\n        connectionString: builder.Configuration.GetConnectionString(\"DefaultConnection\")!,\n        name: \"postgresql\",\n        failureStatus: HealthStatus.Unhealthy,\n        tags: new[] { \"db\", \"sql\" })\n    \n    // Перевірка доступності зовнішнього API\n    .AddUrlGroup(\n        uri: new Uri(\"https://api.example.com/health\"),\n        name: \"external-api\",\n        failureStatus: HealthStatus.Degraded,\n        tags: new[] { \"external\" })\n    \n    // Кастомна перевірка пам'яті\n    .AddCheck\u003CMemoryHealthCheck>(\"memory\");\n\nvar app = builder.Build();\n\n// Liveness endpoint — перевіряє, чи застосунок живий\napp.MapHealthChecks(\"/health/live\", new HealthCheckOptions\n{\n    Predicate = _ => false // Не виконувати жодних перевірок, лише базову\n});\n\n// Readiness endpoint — перевіряє, чи застосунок готовий\napp.MapHealthChecks(\"/health/ready\", new HealthCheckOptions\n{\n    Predicate = check => check.Tags.Contains(\"db\") || check.Tags.Contains(\"external\")\n});\n\n// Детальний endpoint для debugging\napp.MapHealthChecks(\"/health/detailed\", new HealthCheckOptions\n{\n    ResponseWriter = async (context, report) =>\n    {\n        context.Response.ContentType = \"application/json\";\n        var result = System.Text.Json.JsonSerializer.Serialize(new\n        {\n            status = report.Status.ToString(),\n            checks = report.Entries.Select(e => new\n            {\n                name = e.Key,\n                status = e.Value.Status.ToString(),\n                description = e.Value.Description,\n                duration = e.Value.Duration.TotalMilliseconds\n            }),\n            totalDuration = report.TotalDuration.TotalMilliseconds\n        });\n        await context.Response.WriteAsync(result);\n    }\n});\n\napp.Run();\n\n// Кастомна перевірка пам'яті\npublic class MemoryHealthCheck : IHealthCheck\n{\n    public Task\u003CHealthCheckResult> CheckHealthAsync(\n        HealthCheckContext context,\n        CancellationToken cancellationToken = default)\n    {\n        var allocated = GC.GetTotalMemory(forceFullCollection: false);\n        var threshold = 1024L * 1024L * 1024L; // 1 GB\n        \n        var status = allocated \u003C threshold \n            ? HealthStatus.Healthy \n            : HealthStatus.Unhealthy;\n        \n        return Task.FromResult(new HealthCheckResult(\n            status,\n            description: $\"Allocated memory: {allocated / 1024 / 1024} MB\"));\n    }\n}\n",[3349,7803,7804,7832,7836,7856,7860,7875,7880,7891,7918,7931,7948,7972,7976,7981,7990,8010,8021,8036,8051,8055,8060,8081,8085,8101,8105,8110,8130,8135,8154,8159,8163,8168,8187,8191,8238,8242,8246,8251,8270,8274,8297,8302,8324,8362,8367,8389,8417,8422,8438,8462,8482,8505,8510,8528,8533,8557,8561,8565,8569,8579,8583,8588,8605,8609,8630,8639,8654,8658,8686,8713,8718,8739,8753,8766,8770,8791,8797,8832,8836],{"__ignoreMap":3384},[3144,7805,7806,7810,7814,7816,7819,7821,7824,7826,7829],{"class":3142,"line":3389},[3144,7807,7809],{"class":7808},"sCDza","using",[3144,7811,7813],{"class":7812},"sN1BT"," Microsoft",[3144,7815,3289],{"class":5663},[3144,7817,7818],{"class":7812},"Extensions",[3144,7820,3289],{"class":5663},[3144,7822,7823],{"class":7812},"Diagnostics",[3144,7825,3289],{"class":5663},[3144,7827,7828],{"class":7812},"HealthChecks",[3144,7830,7831],{"class":5663},";\n",[3144,7833,7834],{"class":3142,"line":3395},[3144,7835,3411],{"emptyLinePlaceholder":3410},[3144,7837,7838,7840,7842,7844,7846,7848,7850,7852,7854],{"class":3142,"line":3401},[3144,7839,7648],{"class":7647},[3144,7841,7652],{"class":7651},[3144,7843,7655],{"class":5663},[3144,7845,7658],{"class":7651},[3144,7847,3289],{"class":5663},[3144,7849,7664],{"class":7663},[3144,7851,7667],{"class":5663},[3144,7853,7670],{"class":7651},[3144,7855,7673],{"class":5663},[3144,7857,7858],{"class":3142,"line":3407},[3144,7859,3411],{"emptyLinePlaceholder":3410},[3144,7861,7862,7864,7866,7868,7870,7872],{"class":3142,"line":3414},[3144,7863,7687],{"class":7651},[3144,7865,3289],{"class":5663},[3144,7867,7692],{"class":7651},[3144,7869,3289],{"class":5663},[3144,7871,7697],{"class":7663},[3144,7873,7874],{"class":5663},"()\n",[3144,7876,7877],{"class":3142,"line":3420},[3144,7878,7879],{"class":7200},"    // Перевірка підключення до БД\n",[3144,7881,7882,7885,7888],{"class":3142,"line":3426},[3144,7883,7884],{"class":5663},"    .",[3144,7886,7887],{"class":7663},"AddNpgSql",[3144,7889,7890],{"class":5663},"(\n",[3144,7892,7893,7896,7898,7900,7902,7905,7907,7910,7912,7915],{"class":3142,"line":3432},[3144,7894,7895],{"class":7651},"        connectionString",[3144,7897,5679],{"class":5663},[3144,7899,7687],{"class":7651},[3144,7901,3289],{"class":5663},[3144,7903,7904],{"class":7651},"Configuration",[3144,7906,3289],{"class":5663},[3144,7908,7909],{"class":7663},"GetConnectionString",[3144,7911,7667],{"class":5663},[3144,7913,7914],{"class":7139},"\"DefaultConnection\"",[3144,7916,7917],{"class":5663},")!,\n",[3144,7919,7920,7923,7925,7928],{"class":3142,"line":3438},[3144,7921,7922],{"class":7651},"        name",[3144,7924,5679],{"class":5663},[3144,7926,7927],{"class":7139},"\"postgresql\"",[3144,7929,7930],{"class":5663},",\n",[3144,7932,7933,7936,7938,7941,7943,7946],{"class":3142,"line":3444},[3144,7934,7935],{"class":7651},"        failureStatus",[3144,7937,5679],{"class":5663},[3144,7939,7940],{"class":7651},"HealthStatus",[3144,7942,3289],{"class":5663},[3144,7944,7945],{"class":7651},"Unhealthy",[3144,7947,7930],{"class":5663},[3144,7949,7950,7953,7955,7958,7961,7964,7966,7969],{"class":3142,"line":3449},[3144,7951,7952],{"class":7651},"        tags",[3144,7954,5679],{"class":5663},[3144,7956,7957],{"class":7647},"new",[3144,7959,7960],{"class":5663},"[] { ",[3144,7962,7963],{"class":7139},"\"db\"",[3144,7965,6241],{"class":5663},[3144,7967,7968],{"class":7139},"\"sql\"",[3144,7970,7971],{"class":5663}," })\n",[3144,7973,7974],{"class":3142,"line":3455},[3144,7975,3802],{"class":5663},[3144,7977,7978],{"class":3142,"line":3461},[3144,7979,7980],{"class":7200},"    // Перевірка доступності зовнішнього API\n",[3144,7982,7983,7985,7988],{"class":3142,"line":3467},[3144,7984,7884],{"class":5663},[3144,7986,7987],{"class":7663},"AddUrlGroup",[3144,7989,7890],{"class":5663},[3144,7991,7992,7995,7997,7999,8002,8004,8007],{"class":3142,"line":3473},[3144,7993,7994],{"class":7651},"        uri",[3144,7996,5679],{"class":5663},[3144,7998,7957],{"class":7647},[3144,8000,8001],{"class":7812}," Uri",[3144,8003,7667],{"class":5663},[3144,8005,8006],{"class":7139},"\"https://api.example.com/health\"",[3144,8008,8009],{"class":5663},"),\n",[3144,8011,8012,8014,8016,8019],{"class":3142,"line":3478},[3144,8013,7922],{"class":7651},[3144,8015,5679],{"class":5663},[3144,8017,8018],{"class":7139},"\"external-api\"",[3144,8020,7930],{"class":5663},[3144,8022,8023,8025,8027,8029,8031,8034],{"class":3142,"line":3483},[3144,8024,7935],{"class":7651},[3144,8026,5679],{"class":5663},[3144,8028,7940],{"class":7651},[3144,8030,3289],{"class":5663},[3144,8032,8033],{"class":7651},"Degraded",[3144,8035,7930],{"class":5663},[3144,8037,8038,8040,8042,8044,8046,8049],{"class":3142,"line":3489},[3144,8039,7952],{"class":7651},[3144,8041,5679],{"class":5663},[3144,8043,7957],{"class":7647},[3144,8045,7960],{"class":5663},[3144,8047,8048],{"class":7139},"\"external\"",[3144,8050,7971],{"class":5663},[3144,8052,8053],{"class":3142,"line":3495},[3144,8054,3802],{"class":5663},[3144,8056,8057],{"class":3142,"line":3501},[3144,8058,8059],{"class":7200},"    // Кастомна перевірка пам'яті\n",[3144,8061,8062,8064,8067,8070,8073,8076,8079],{"class":3142,"line":3506},[3144,8063,7884],{"class":5663},[3144,8065,8066],{"class":7663},"AddCheck",[3144,8068,8069],{"class":5663},"\u003C",[3144,8071,8072],{"class":7812},"MemoryHealthCheck",[3144,8074,8075],{"class":5663},">(",[3144,8077,8078],{"class":7139},"\"memory\"",[3144,8080,7673],{"class":5663},[3144,8082,8083],{"class":3142,"line":3512},[3144,8084,3411],{"emptyLinePlaceholder":3410},[3144,8086,8087,8089,8091,8093,8095,8097,8099],{"class":3142,"line":3518},[3144,8088,7648],{"class":7647},[3144,8090,7711],{"class":7651},[3144,8092,7655],{"class":5663},[3144,8094,7687],{"class":7651},[3144,8096,3289],{"class":5663},[3144,8098,7720],{"class":7663},[3144,8100,7700],{"class":5663},[3144,8102,8103],{"class":3142,"line":3524},[3144,8104,3411],{"emptyLinePlaceholder":3410},[3144,8106,8107],{"class":3142,"line":3530},[3144,8108,8109],{"class":7200},"// Liveness endpoint — перевіряє, чи застосунок живий\n",[3144,8111,8112,8114,8116,8118,8120,8123,8125,8127],{"class":3142,"line":3535},[3144,8113,7736],{"class":7651},[3144,8115,3289],{"class":5663},[3144,8117,7741],{"class":7663},[3144,8119,7667],{"class":5663},[3144,8121,8122],{"class":7139},"\"/health/live\"",[3144,8124,6241],{"class":5663},[3144,8126,7957],{"class":7647},[3144,8128,8129],{"class":7812}," HealthCheckOptions\n",[3144,8131,8132],{"class":3142,"line":3705},[3144,8133,8134],{"class":5663},"{\n",[3144,8136,8137,8140,8142,8145,8148,8151],{"class":3142,"line":3711},[3144,8138,8139],{"class":7651},"    Predicate",[3144,8141,7655],{"class":5663},[3144,8143,8144],{"class":7651},"_",[3144,8146,8147],{"class":5663}," => ",[3144,8149,8150],{"class":7647},"false",[3144,8152,8153],{"class":7200}," // Не виконувати жодних перевірок, лише базову\n",[3144,8155,8156],{"class":3142,"line":3717},[3144,8157,8158],{"class":5663},"});\n",[3144,8160,8161],{"class":3142,"line":3722},[3144,8162,3411],{"emptyLinePlaceholder":3410},[3144,8164,8165],{"class":3142,"line":3727},[3144,8166,8167],{"class":7200},"// Readiness endpoint — перевіряє, чи застосунок готовий\n",[3144,8169,8170,8172,8174,8176,8178,8181,8183,8185],{"class":3142,"line":4084},[3144,8171,7736],{"class":7651},[3144,8173,3289],{"class":5663},[3144,8175,7741],{"class":7663},[3144,8177,7667],{"class":5663},[3144,8179,8180],{"class":7139},"\"/health/ready\"",[3144,8182,6241],{"class":5663},[3144,8184,7957],{"class":7647},[3144,8186,8129],{"class":7812},[3144,8188,8189],{"class":3142,"line":4090},[3144,8190,8134],{"class":5663},[3144,8192,8193,8195,8197,8200,8202,8204,8206,8209,8211,8214,8216,8218,8221,8223,8225,8227,8229,8231,8233,8235],{"class":3142,"line":4096},[3144,8194,8139],{"class":7651},[3144,8196,7655],{"class":5663},[3144,8198,8199],{"class":7651},"check",[3144,8201,8147],{"class":5663},[3144,8203,8199],{"class":7651},[3144,8205,3289],{"class":5663},[3144,8207,8208],{"class":7651},"Tags",[3144,8210,3289],{"class":5663},[3144,8212,8213],{"class":7663},"Contains",[3144,8215,7667],{"class":5663},[3144,8217,7963],{"class":7139},[3144,8219,8220],{"class":5663},") || ",[3144,8222,8199],{"class":7651},[3144,8224,3289],{"class":5663},[3144,8226,8208],{"class":7651},[3144,8228,3289],{"class":5663},[3144,8230,8213],{"class":7663},[3144,8232,7667],{"class":5663},[3144,8234,8048],{"class":7139},[3144,8236,8237],{"class":5663},")\n",[3144,8239,8240],{"class":3142,"line":4102},[3144,8241,8158],{"class":5663},[3144,8243,8244],{"class":3142,"line":4107},[3144,8245,3411],{"emptyLinePlaceholder":3410},[3144,8247,8248],{"class":3142,"line":4112},[3144,8249,8250],{"class":7200},"// Детальний endpoint для debugging\n",[3144,8252,8253,8255,8257,8259,8261,8264,8266,8268],{"class":3142,"line":4118},[3144,8254,7736],{"class":7651},[3144,8256,3289],{"class":5663},[3144,8258,7741],{"class":7663},[3144,8260,7667],{"class":5663},[3144,8262,8263],{"class":7139},"\"/health/detailed\"",[3144,8265,6241],{"class":5663},[3144,8267,7957],{"class":7647},[3144,8269,8129],{"class":7812},[3144,8271,8272],{"class":3142,"line":4124},[3144,8273,8134],{"class":5663},[3144,8275,8276,8279,8281,8284,8286,8289,8291,8294],{"class":3142,"line":4130},[3144,8277,8278],{"class":7651},"    ResponseWriter",[3144,8280,7655],{"class":5663},[3144,8282,8283],{"class":7647},"async",[3144,8285,6248],{"class":5663},[3144,8287,8288],{"class":7651},"context",[3144,8290,6241],{"class":5663},[3144,8292,8293],{"class":7651},"report",[3144,8295,8296],{"class":5663},") =>\n",[3144,8298,8299],{"class":3142,"line":4135},[3144,8300,8301],{"class":5663},"    {\n",[3144,8303,8304,8307,8309,8312,8314,8317,8319,8322],{"class":3142,"line":4140},[3144,8305,8306],{"class":7651},"        context",[3144,8308,3289],{"class":5663},[3144,8310,8311],{"class":7651},"Response",[3144,8313,3289],{"class":5663},[3144,8315,8316],{"class":7651},"ContentType",[3144,8318,7655],{"class":5663},[3144,8320,8321],{"class":7139},"\"application/json\"",[3144,8323,7831],{"class":5663},[3144,8325,8326,8329,8332,8334,8337,8339,8342,8344,8347,8349,8352,8354,8357,8359],{"class":3142,"line":5169},[3144,8327,8328],{"class":7647},"        var",[3144,8330,8331],{"class":7651}," result",[3144,8333,7655],{"class":5663},[3144,8335,8336],{"class":7651},"System",[3144,8338,3289],{"class":5663},[3144,8340,8341],{"class":7651},"Text",[3144,8343,3289],{"class":5663},[3144,8345,8346],{"class":7651},"Json",[3144,8348,3289],{"class":5663},[3144,8350,8351],{"class":7651},"JsonSerializer",[3144,8353,3289],{"class":5663},[3144,8355,8356],{"class":7663},"Serialize",[3144,8358,7667],{"class":5663},[3144,8360,8361],{"class":7647},"new\n",[3144,8363,8364],{"class":3142,"line":5174},[3144,8365,8366],{"class":5663},"        {\n",[3144,8368,8369,8372,8374,8376,8378,8381,8383,8386],{"class":3142,"line":5179},[3144,8370,8371],{"class":7651},"            status",[3144,8373,7655],{"class":5663},[3144,8375,8293],{"class":7651},[3144,8377,3289],{"class":5663},[3144,8379,8380],{"class":7651},"Status",[3144,8382,3289],{"class":5663},[3144,8384,8385],{"class":7663},"ToString",[3144,8387,8388],{"class":5663},"(),\n",[3144,8390,8391,8394,8396,8398,8400,8403,8405,8408,8410,8413,8415],{"class":3142,"line":5185},[3144,8392,8393],{"class":7651},"            checks",[3144,8395,7655],{"class":5663},[3144,8397,8293],{"class":7651},[3144,8399,3289],{"class":5663},[3144,8401,8402],{"class":7651},"Entries",[3144,8404,3289],{"class":5663},[3144,8406,8407],{"class":7663},"Select",[3144,8409,7667],{"class":5663},[3144,8411,8412],{"class":7651},"e",[3144,8414,8147],{"class":5663},[3144,8416,8361],{"class":7647},[3144,8418,8419],{"class":3142,"line":5190},[3144,8420,8421],{"class":5663},"            {\n",[3144,8423,8424,8427,8429,8431,8433,8436],{"class":3142,"line":5196},[3144,8425,8426],{"class":7651},"                name",[3144,8428,7655],{"class":5663},[3144,8430,8412],{"class":7651},[3144,8432,3289],{"class":5663},[3144,8434,8435],{"class":7651},"Key",[3144,8437,7930],{"class":5663},[3144,8439,8440,8443,8445,8447,8449,8452,8454,8456,8458,8460],{"class":3142,"line":5202},[3144,8441,8442],{"class":7651},"                status",[3144,8444,7655],{"class":5663},[3144,8446,8412],{"class":7651},[3144,8448,3289],{"class":5663},[3144,8450,8451],{"class":7651},"Value",[3144,8453,3289],{"class":5663},[3144,8455,8380],{"class":7651},[3144,8457,3289],{"class":5663},[3144,8459,8385],{"class":7663},[3144,8461,8388],{"class":5663},[3144,8463,8464,8467,8469,8471,8473,8475,8477,8480],{"class":3142,"line":5207},[3144,8465,8466],{"class":7651},"                description",[3144,8468,7655],{"class":5663},[3144,8470,8412],{"class":7651},[3144,8472,3289],{"class":5663},[3144,8474,8451],{"class":7651},[3144,8476,3289],{"class":5663},[3144,8478,8479],{"class":7651},"Description",[3144,8481,7930],{"class":5663},[3144,8483,8484,8487,8489,8491,8493,8495,8497,8500,8502],{"class":3142,"line":5212},[3144,8485,8486],{"class":7651},"                duration",[3144,8488,7655],{"class":5663},[3144,8490,8412],{"class":7651},[3144,8492,3289],{"class":5663},[3144,8494,8451],{"class":7651},[3144,8496,3289],{"class":5663},[3144,8498,8499],{"class":7651},"Duration",[3144,8501,3289],{"class":5663},[3144,8503,8504],{"class":7651},"TotalMilliseconds\n",[3144,8506,8507],{"class":3142,"line":5218},[3144,8508,8509],{"class":5663},"            }),\n",[3144,8511,8512,8515,8517,8519,8521,8524,8526],{"class":3142,"line":5223},[3144,8513,8514],{"class":7651},"            totalDuration",[3144,8516,7655],{"class":5663},[3144,8518,8293],{"class":7651},[3144,8520,3289],{"class":5663},[3144,8522,8523],{"class":7651},"TotalDuration",[3144,8525,3289],{"class":5663},[3144,8527,8504],{"class":7651},[3144,8529,8530],{"class":3142,"line":5228},[3144,8531,8532],{"class":5663},"        });\n",[3144,8534,8535,8538,8541,8543,8545,8547,8550,8552,8555],{"class":3142,"line":5234},[3144,8536,8537],{"class":7647},"        await",[3144,8539,8540],{"class":7651}," context",[3144,8542,3289],{"class":5663},[3144,8544,8311],{"class":7651},[3144,8546,3289],{"class":5663},[3144,8548,8549],{"class":7663},"WriteAsync",[3144,8551,7667],{"class":5663},[3144,8553,8554],{"class":7651},"result",[3144,8556,7673],{"class":5663},[3144,8558,8559],{"class":3142,"line":5240},[3144,8560,3435],{"class":5663},[3144,8562,8563],{"class":3142,"line":5246},[3144,8564,8158],{"class":5663},[3144,8566,8567],{"class":3142,"line":5251},[3144,8568,3411],{"emptyLinePlaceholder":3410},[3144,8570,8571,8573,8575,8577],{"class":3142,"line":5256},[3144,8572,7736],{"class":7651},[3144,8574,3289],{"class":5663},[3144,8576,7761],{"class":7663},[3144,8578,7700],{"class":5663},[3144,8580,8581],{"class":3142,"line":5261},[3144,8582,3411],{"emptyLinePlaceholder":3410},[3144,8584,8585],{"class":3142,"line":5266},[3144,8586,8587],{"class":7200},"// Кастомна перевірка пам'яті\n",[3144,8589,8590,8593,8596,8599,8602],{"class":3142,"line":5272},[3144,8591,8592],{"class":7647},"public",[3144,8594,8595],{"class":7647}," class",[3144,8597,8598],{"class":7812}," MemoryHealthCheck",[3144,8600,8601],{"class":5663}," : ",[3144,8603,8604],{"class":7812},"IHealthCheck\n",[3144,8606,8607],{"class":3142,"line":5277},[3144,8608,8134],{"class":5663},[3144,8610,8611,8614,8617,8619,8622,8625,8628],{"class":3142,"line":5282},[3144,8612,8613],{"class":7647},"    public",[3144,8615,8616],{"class":7812}," Task",[3144,8618,8069],{"class":5663},[3144,8620,8621],{"class":7812},"HealthCheckResult",[3144,8623,8624],{"class":5663},"> ",[3144,8626,8627],{"class":7663},"CheckHealthAsync",[3144,8629,7890],{"class":5663},[3144,8631,8632,8635,8637],{"class":3142,"line":5287},[3144,8633,8634],{"class":7812},"        HealthCheckContext",[3144,8636,8540],{"class":7651},[3144,8638,7930],{"class":5663},[3144,8640,8641,8644,8647,8649,8652],{"class":3142,"line":5292},[3144,8642,8643],{"class":7812},"        CancellationToken",[3144,8645,8646],{"class":7651}," cancellationToken",[3144,8648,7655],{"class":5663},[3144,8650,8651],{"class":7647},"default",[3144,8653,8237],{"class":5663},[3144,8655,8656],{"class":3142,"line":5298},[3144,8657,8301],{"class":5663},[3144,8659,8660,8662,8665,8667,8670,8672,8675,8677,8680,8682,8684],{"class":3142,"line":5303},[3144,8661,8328],{"class":7647},[3144,8663,8664],{"class":7651}," allocated",[3144,8666,7655],{"class":5663},[3144,8668,8669],{"class":7651},"GC",[3144,8671,3289],{"class":5663},[3144,8673,8674],{"class":7663},"GetTotalMemory",[3144,8676,7667],{"class":5663},[3144,8678,8679],{"class":7651},"forceFullCollection",[3144,8681,5679],{"class":5663},[3144,8683,8150],{"class":7647},[3144,8685,7673],{"class":5663},[3144,8687,8688,8690,8693,8695,8698,8701,8703,8705,8707,8710],{"class":3142,"line":5308},[3144,8689,8328],{"class":7647},[3144,8691,8692],{"class":7651}," threshold",[3144,8694,7655],{"class":5663},[3144,8696,8697],{"class":5700},"1024L",[3144,8699,8700],{"class":5663}," * ",[3144,8702,8697],{"class":5700},[3144,8704,8700],{"class":5663},[3144,8706,8697],{"class":5700},[3144,8708,8709],{"class":5663},"; ",[3144,8711,8712],{"class":7200},"// 1 GB\n",[3144,8714,8715],{"class":3142,"line":5314},[3144,8716,8717],{"class":5663},"        \n",[3144,8719,8720,8722,8725,8727,8730,8733,8736],{"class":3142,"line":5320},[3144,8721,8328],{"class":7647},[3144,8723,8724],{"class":7651}," status",[3144,8726,7655],{"class":5663},[3144,8728,8729],{"class":7651},"allocated",[3144,8731,8732],{"class":5663}," \u003C ",[3144,8734,8735],{"class":7651},"threshold",[3144,8737,8738],{"class":5663}," \n",[3144,8740,8741,8744,8746,8748,8751],{"class":3142,"line":5325},[3144,8742,8743],{"class":5663},"            ? ",[3144,8745,7940],{"class":7651},[3144,8747,3289],{"class":5663},[3144,8749,8750],{"class":7651},"Healthy",[3144,8752,8738],{"class":5663},[3144,8754,8755,8758,8760,8762,8764],{"class":3142,"line":5330},[3144,8756,8757],{"class":5663},"            : ",[3144,8759,7940],{"class":7651},[3144,8761,3289],{"class":5663},[3144,8763,7945],{"class":7651},[3144,8765,7831],{"class":5663},[3144,8767,8768],{"class":3142,"line":5336},[3144,8769,8717],{"class":5663},[3144,8771,8772,8775,8777,8779,8782,8784,8786,8789],{"class":3142,"line":5341},[3144,8773,8774],{"class":7808},"        return",[3144,8776,8616],{"class":7651},[3144,8778,3289],{"class":5663},[3144,8780,8781],{"class":7663},"FromResult",[3144,8783,7667],{"class":5663},[3144,8785,7957],{"class":7647},[3144,8787,8788],{"class":7812}," HealthCheckResult",[3144,8790,7890],{"class":5663},[3144,8792,8793,8795],{"class":3142,"line":5346},[3144,8794,8371],{"class":7651},[3144,8796,7930],{"class":5663},[3144,8798,8799,8802,8804,8807,8811,8813,8816,8819,8821,8823,8826,8829],{"class":3142,"line":5352},[3144,8800,8801],{"class":7651},"            description",[3144,8803,5679],{"class":5663},[3144,8805,8806],{"class":7139},"$\"Allocated memory: ",[3144,8808,8810],{"class":8809},"sD7JJ","{",[3144,8812,8729],{"class":7651},[3144,8814,8815],{"class":5663}," /",[3144,8817,8818],{"class":5700}," 1024",[3144,8820,8815],{"class":5663},[3144,8822,8818],{"class":5700},[3144,8824,8825],{"class":8809},"}",[3144,8827,8828],{"class":7139}," MB\"",[3144,8830,8831],{"class":5663},"));\n",[3144,8833,8834],{"class":3142,"line":5357},[3144,8835,3435],{"class":5663},[3144,8837,8838],{"class":3142,"line":5362},[3144,8839,3441],{"class":5663},[3122,8841,8843],{"id":8842},"різниця-між-liveness-та-readiness-для-net","Різниця між Liveness та Readiness для .NET",[3330,8845,8846,8941],{},[3333,8847,8850,8860,8865,8873,8878,8886,8890,8936],{"icon":8848,"title":8849},"i-heroicons-heart","Liveness Probe (/health/live)",[3114,8851,8852,8855,8856,8859],{},[3118,8853,8854],{},"Мета:"," Перевірити, чи застосунок ",[3118,8857,8858],{},"живий"," (не deadlock, не crash).",[3114,8861,8862],{},[3118,8863,8864],{},"Що перевіряти:",[3903,8866,8867,8870],{},[3906,8868,8869],{},"Базову доступність процесу (просто повернути 200 OK)",[3906,8871,8872],{},"Критичні внутрішні компоненти (наприклад, чи не зависла черга повідомлень)",[3114,8874,8875],{},[3118,8876,8877],{},"Що НЕ перевіряти:",[3903,8879,8880,8883],{},[3906,8881,8882],{},"Підключення до БД (якщо БД недоступна, це не означає, що застосунок мертвий)",[3906,8884,8885],{},"Зовнішні API (їхня недоступність не означає deadlock)",[3114,8887,8888],{},[3118,8889,7176],{},[3379,8891,8893],{"className":7638,"code":8892,"language":7640,"meta":3384,"style":3384},"app.MapHealthChecks(\"/health/live\", new HealthCheckOptions\n{\n    Predicate = _ => false // Лише базова перевірка\n});\n",[3349,8894,8895,8913,8917,8932],{"__ignoreMap":3384},[3144,8896,8897,8899,8901,8903,8905,8907,8909,8911],{"class":3142,"line":3389},[3144,8898,7736],{"class":7651},[3144,8900,3289],{"class":5663},[3144,8902,7741],{"class":7663},[3144,8904,7667],{"class":5663},[3144,8906,8122],{"class":7139},[3144,8908,6241],{"class":5663},[3144,8910,7957],{"class":7647},[3144,8912,8129],{"class":7812},[3144,8914,8915],{"class":3142,"line":3395},[3144,8916,8134],{"class":5663},[3144,8918,8919,8921,8923,8925,8927,8929],{"class":3142,"line":3401},[3144,8920,8139],{"class":7651},[3144,8922,7655],{"class":5663},[3144,8924,8144],{"class":7651},[3144,8926,8147],{"class":5663},[3144,8928,8150],{"class":7647},[3144,8930,8931],{"class":7200}," // Лише базова перевірка\n",[3144,8933,8934],{"class":3142,"line":3407},[3144,8935,8158],{"class":5663},[3114,8937,8938,8940],{},[3118,8939,4925],{}," Завжди повертає 200 OK, якщо процес працює.",[3333,8942,8945,8953,8957,8968,8972,9045],{"icon":8943,"title":8944},"i-heroicons-check-circle","Readiness Probe (/health/ready)",[3114,8946,8947,8855,8949,8952],{},[3118,8948,8854],{},[3118,8950,8951],{},"готовий"," приймати трафік.",[3114,8954,8955],{},[3118,8956,8864],{},[3903,8958,8959,8962,8965],{},[3906,8960,8961],{},"Підключення до БД (якщо БД недоступна, застосунок не може обробляти запити)",[3906,8963,8964],{},"Зовнішні залежності (API, черги повідомлень)",[3906,8966,8967],{},"Завершення ініціалізації (кеші завантажені, конфігурація прочитана)",[3114,8969,8970],{},[3118,8971,7176],{},[3379,8973,8975],{"className":7638,"code":8974,"language":7640,"meta":3384,"style":3384},"app.MapHealthChecks(\"/health/ready\", new HealthCheckOptions\n{\n    Predicate = check => check.Tags.Contains(\"db\") || check.Tags.Contains(\"external\")\n});\n",[3349,8976,8977,8995,8999,9041],{"__ignoreMap":3384},[3144,8978,8979,8981,8983,8985,8987,8989,8991,8993],{"class":3142,"line":3389},[3144,8980,7736],{"class":7651},[3144,8982,3289],{"class":5663},[3144,8984,7741],{"class":7663},[3144,8986,7667],{"class":5663},[3144,8988,8180],{"class":7139},[3144,8990,6241],{"class":5663},[3144,8992,7957],{"class":7647},[3144,8994,8129],{"class":7812},[3144,8996,8997],{"class":3142,"line":3395},[3144,8998,8134],{"class":5663},[3144,9000,9001,9003,9005,9007,9009,9011,9013,9015,9017,9019,9021,9023,9025,9027,9029,9031,9033,9035,9037,9039],{"class":3142,"line":3401},[3144,9002,8139],{"class":7651},[3144,9004,7655],{"class":5663},[3144,9006,8199],{"class":7651},[3144,9008,8147],{"class":5663},[3144,9010,8199],{"class":7651},[3144,9012,3289],{"class":5663},[3144,9014,8208],{"class":7651},[3144,9016,3289],{"class":5663},[3144,9018,8213],{"class":7663},[3144,9020,7667],{"class":5663},[3144,9022,7963],{"class":7139},[3144,9024,8220],{"class":5663},[3144,9026,8199],{"class":7651},[3144,9028,3289],{"class":5663},[3144,9030,8208],{"class":7651},[3144,9032,3289],{"class":5663},[3144,9034,8213],{"class":7663},[3144,9036,7667],{"class":5663},[3144,9038,8048],{"class":7139},[3144,9040,8237],{"class":5663},[3144,9042,9043],{"class":3142,"line":3407},[3144,9044,8158],{"class":5663},[3114,9046,9047,9049],{},[3118,9048,4925],{}," Повертає 200 OK лише якщо БД доступна та зовнішні API працюють.",[3122,9051,9053],{"id":9052},"налаштування-kubernetes-probes-для-net","Налаштування Kubernetes probes для .NET",[3114,9055,9056],{},"Тепер налаштуємо Deployment YAML для використання цих endpoints:",[3379,9058,9060],{"className":5650,"code":9059,"language":5652,"meta":3384,"style":3384},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: todoapi\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: todoapi\n  template:\n    metadata:\n      labels:\n        app: todoapi\n    spec:\n      containers:\n        - name: todoapi\n          image: todoapi:2.0.0\n          ports:\n            - containerPort: 8080\n          \n          # Liveness probe — перевірка живості\n          livenessProbe:\n            httpGet:\n              path: /health/live\n              port: 8080\n            initialDelaySeconds: 30    # Час на старт застосунку\n            periodSeconds: 10           # Перевірка кожні 10 секунд\n            timeoutSeconds: 5           # Таймаут запиту\n            failureThreshold: 3         # 3 невдалі спроби → restart\n            successThreshold: 1         # 1 успішна спроба → healthy\n          \n          # Readiness probe — перевірка готовності\n          readinessProbe:\n            httpGet:\n              path: /health/ready\n              port: 8080\n            initialDelaySeconds: 10    # Менше за liveness (швидше виявити готовність)\n            periodSeconds: 5            # Частіше перевіряти\n            timeoutSeconds: 3           # Менший таймаут\n            failureThreshold: 3         # 3 невдалі спроби → not ready\n            successThreshold: 1         # 1 успішна спроба → ready\n          \n          # Startup probe — для застосунків з повільним стартом\n          startupProbe:\n            httpGet:\n              path: /health/live\n              port: 8080\n            initialDelaySeconds: 0\n            periodSeconds: 5\n            timeoutSeconds: 3\n            failureThreshold: 30        # 30 * 5s = 150s максимум на старт\n            successThreshold: 1\n          \n          resources:\n            requests:\n              memory: \"128Mi\"\n              cpu: \"100m\"\n            limits:\n              memory: \"256Mi\"\n              cpu: \"500m\"\n",[3349,9061,9062,9072,9082,9089,9099,9105,9115,9122,9129,9138,9145,9152,9159,9168,9175,9182,9194,9204,9211,9224,9229,9234,9241,9248,9258,9267,9280,9292,9305,9318,9330,9334,9339,9346,9352,9361,9369,9380,9391,9402,9413,9424,9428,9433,9440,9446,9454,9462,9470,9479,9487,9498,9506,9510,9517,9524,9534,9544,9551,9560],{"__ignoreMap":3384},[3144,9063,9064,9067,9069],{"class":3142,"line":3389},[3144,9065,9066],{"class":5659},"apiVersion",[3144,9068,5679],{"class":5663},[3144,9070,9071],{"class":5682},"apps/v1\n",[3144,9073,9074,9077,9079],{"class":3142,"line":3395},[3144,9075,9076],{"class":5659},"kind",[3144,9078,5679],{"class":5663},[3144,9080,9081],{"class":5682},"Deployment\n",[3144,9083,9084,9087],{"class":3142,"line":3401},[3144,9085,9086],{"class":5659},"metadata",[3144,9088,5664],{"class":5663},[3144,9090,9091,9094,9096],{"class":3142,"line":3407},[3144,9092,9093],{"class":5659},"  name",[3144,9095,5679],{"class":5663},[3144,9097,9098],{"class":5682},"todoapi\n",[3144,9100,9101,9103],{"class":3142,"line":3414},[3144,9102,5660],{"class":5659},[3144,9104,5664],{"class":5663},[3144,9106,9107,9110,9112],{"class":3142,"line":3420},[3144,9108,9109],{"class":5659},"  replicas",[3144,9111,5679],{"class":5663},[3144,9113,9114],{"class":5700},"3\n",[3144,9116,9117,9120],{"class":3142,"line":3426},[3144,9118,9119],{"class":5659},"  selector",[3144,9121,5664],{"class":5663},[3144,9123,9124,9127],{"class":3142,"line":3432},[3144,9125,9126],{"class":5659},"    matchLabels",[3144,9128,5664],{"class":5663},[3144,9130,9131,9134,9136],{"class":3142,"line":3438},[3144,9132,9133],{"class":5659},"      app",[3144,9135,5679],{"class":5663},[3144,9137,9098],{"class":5682},[3144,9139,9140,9143],{"class":3142,"line":3444},[3144,9141,9142],{"class":5659},"  template",[3144,9144,5664],{"class":5663},[3144,9146,9147,9150],{"class":3142,"line":3449},[3144,9148,9149],{"class":5659},"    metadata",[3144,9151,5664],{"class":5663},[3144,9153,9154,9157],{"class":3142,"line":3455},[3144,9155,9156],{"class":5659},"      labels",[3144,9158,5664],{"class":5663},[3144,9160,9161,9164,9166],{"class":3142,"line":3461},[3144,9162,9163],{"class":5659},"        app",[3144,9165,5679],{"class":5663},[3144,9167,9098],{"class":5682},[3144,9169,9170,9173],{"class":3142,"line":3467},[3144,9171,9172],{"class":5659},"    spec",[3144,9174,5664],{"class":5663},[3144,9176,9177,9180],{"class":3142,"line":3473},[3144,9178,9179],{"class":5659},"      containers",[3144,9181,5664],{"class":5663},[3144,9183,9184,9187,9190,9192],{"class":3142,"line":3478},[3144,9185,9186],{"class":5663},"        - ",[3144,9188,9189],{"class":5659},"name",[3144,9191,5679],{"class":5663},[3144,9193,9098],{"class":5682},[3144,9195,9196,9199,9201],{"class":3142,"line":3483},[3144,9197,9198],{"class":5659},"          image",[3144,9200,5679],{"class":5663},[3144,9202,9203],{"class":5682},"todoapi:2.0.0\n",[3144,9205,9206,9209],{"class":3142,"line":3489},[3144,9207,9208],{"class":5659},"          ports",[3144,9210,5664],{"class":5663},[3144,9212,9213,9216,9219,9221],{"class":3142,"line":3495},[3144,9214,9215],{"class":5663},"            - ",[3144,9217,9218],{"class":5659},"containerPort",[3144,9220,5679],{"class":5663},[3144,9222,9223],{"class":5700},"8080\n",[3144,9225,9226],{"class":3142,"line":3501},[3144,9227,9228],{"class":5663},"          \n",[3144,9230,9231],{"class":3142,"line":3506},[3144,9232,9233],{"class":7200},"          # Liveness probe — перевірка живості\n",[3144,9235,9236,9239],{"class":3142,"line":3512},[3144,9237,9238],{"class":5659},"          livenessProbe",[3144,9240,5664],{"class":5663},[3144,9242,9243,9246],{"class":3142,"line":3518},[3144,9244,9245],{"class":5659},"            httpGet",[3144,9247,5664],{"class":5663},[3144,9249,9250,9253,9255],{"class":3142,"line":3524},[3144,9251,9252],{"class":5659},"              path",[3144,9254,5679],{"class":5663},[3144,9256,9257],{"class":5682},"/health/live\n",[3144,9259,9260,9263,9265],{"class":3142,"line":3530},[3144,9261,9262],{"class":5659},"              port",[3144,9264,5679],{"class":5663},[3144,9266,9223],{"class":5700},[3144,9268,9269,9272,9274,9277],{"class":3142,"line":3535},[3144,9270,9271],{"class":5659},"            initialDelaySeconds",[3144,9273,5679],{"class":5663},[3144,9275,9276],{"class":5700},"30",[3144,9278,9279],{"class":7200},"    # Час на старт застосунку\n",[3144,9281,9282,9285,9287,9289],{"class":3142,"line":3705},[3144,9283,9284],{"class":5659},"            periodSeconds",[3144,9286,5679],{"class":5663},[3144,9288,6883],{"class":5700},[3144,9290,9291],{"class":7200},"           # Перевірка кожні 10 секунд\n",[3144,9293,9294,9297,9299,9302],{"class":3142,"line":3711},[3144,9295,9296],{"class":5659},"            timeoutSeconds",[3144,9298,5679],{"class":5663},[3144,9300,9301],{"class":5700},"5",[3144,9303,9304],{"class":7200},"           # Таймаут запиту\n",[3144,9306,9307,9310,9312,9315],{"class":3142,"line":3717},[3144,9308,9309],{"class":5659},"            failureThreshold",[3144,9311,5679],{"class":5663},[3144,9313,9314],{"class":5700},"3",[3144,9316,9317],{"class":7200},"         # 3 невдалі спроби → restart\n",[3144,9319,9320,9323,9325,9327],{"class":3142,"line":3722},[3144,9321,9322],{"class":5659},"            successThreshold",[3144,9324,5679],{"class":5663},[3144,9326,6240],{"class":5700},[3144,9328,9329],{"class":7200},"         # 1 успішна спроба → healthy\n",[3144,9331,9332],{"class":3142,"line":3727},[3144,9333,9228],{"class":5663},[3144,9335,9336],{"class":3142,"line":4084},[3144,9337,9338],{"class":7200},"          # Readiness probe — перевірка готовності\n",[3144,9340,9341,9344],{"class":3142,"line":4090},[3144,9342,9343],{"class":5659},"          readinessProbe",[3144,9345,5664],{"class":5663},[3144,9347,9348,9350],{"class":3142,"line":4096},[3144,9349,9245],{"class":5659},[3144,9351,5664],{"class":5663},[3144,9353,9354,9356,9358],{"class":3142,"line":4102},[3144,9355,9252],{"class":5659},[3144,9357,5679],{"class":5663},[3144,9359,9360],{"class":5682},"/health/ready\n",[3144,9362,9363,9365,9367],{"class":3142,"line":4107},[3144,9364,9262],{"class":5659},[3144,9366,5679],{"class":5663},[3144,9368,9223],{"class":5700},[3144,9370,9371,9373,9375,9377],{"class":3142,"line":4112},[3144,9372,9271],{"class":5659},[3144,9374,5679],{"class":5663},[3144,9376,6883],{"class":5700},[3144,9378,9379],{"class":7200},"    # Менше за liveness (швидше виявити готовність)\n",[3144,9381,9382,9384,9386,9388],{"class":3142,"line":4118},[3144,9383,9284],{"class":5659},[3144,9385,5679],{"class":5663},[3144,9387,9301],{"class":5700},[3144,9389,9390],{"class":7200},"            # Частіше перевіряти\n",[3144,9392,9393,9395,9397,9399],{"class":3142,"line":4124},[3144,9394,9296],{"class":5659},[3144,9396,5679],{"class":5663},[3144,9398,9314],{"class":5700},[3144,9400,9401],{"class":7200},"           # Менший таймаут\n",[3144,9403,9404,9406,9408,9410],{"class":3142,"line":4130},[3144,9405,9309],{"class":5659},[3144,9407,5679],{"class":5663},[3144,9409,9314],{"class":5700},[3144,9411,9412],{"class":7200},"         # 3 невдалі спроби → not ready\n",[3144,9414,9415,9417,9419,9421],{"class":3142,"line":4135},[3144,9416,9322],{"class":5659},[3144,9418,5679],{"class":5663},[3144,9420,6240],{"class":5700},[3144,9422,9423],{"class":7200},"         # 1 успішна спроба → ready\n",[3144,9425,9426],{"class":3142,"line":4140},[3144,9427,9228],{"class":5663},[3144,9429,9430],{"class":3142,"line":5169},[3144,9431,9432],{"class":7200},"          # Startup probe — для застосунків з повільним стартом\n",[3144,9434,9435,9438],{"class":3142,"line":5174},[3144,9436,9437],{"class":5659},"          startupProbe",[3144,9439,5664],{"class":5663},[3144,9441,9442,9444],{"class":3142,"line":5179},[3144,9443,9245],{"class":5659},[3144,9445,5664],{"class":5663},[3144,9447,9448,9450,9452],{"class":3142,"line":5185},[3144,9449,9252],{"class":5659},[3144,9451,5679],{"class":5663},[3144,9453,9257],{"class":5682},[3144,9455,9456,9458,9460],{"class":3142,"line":5190},[3144,9457,9262],{"class":5659},[3144,9459,5679],{"class":5663},[3144,9461,9223],{"class":5700},[3144,9463,9464,9466,9468],{"class":3142,"line":5196},[3144,9465,9271],{"class":5659},[3144,9467,5679],{"class":5663},[3144,9469,6631],{"class":5700},[3144,9471,9472,9474,9476],{"class":3142,"line":5202},[3144,9473,9284],{"class":5659},[3144,9475,5679],{"class":5663},[3144,9477,9478],{"class":5700},"5\n",[3144,9480,9481,9483,9485],{"class":3142,"line":5207},[3144,9482,9296],{"class":5659},[3144,9484,5679],{"class":5663},[3144,9486,9114],{"class":5700},[3144,9488,9489,9491,9493,9495],{"class":3142,"line":5212},[3144,9490,9309],{"class":5659},[3144,9492,5679],{"class":5663},[3144,9494,9276],{"class":5700},[3144,9496,9497],{"class":7200},"        # 30 * 5s = 150s максимум на старт\n",[3144,9499,9500,9502,9504],{"class":3142,"line":5218},[3144,9501,9322],{"class":5659},[3144,9503,5679],{"class":5663},[3144,9505,5701],{"class":5700},[3144,9507,9508],{"class":3142,"line":5223},[3144,9509,9228],{"class":5663},[3144,9511,9512,9515],{"class":3142,"line":5228},[3144,9513,9514],{"class":5659},"          resources",[3144,9516,5664],{"class":5663},[3144,9518,9519,9522],{"class":3142,"line":5234},[3144,9520,9521],{"class":5659},"            requests",[3144,9523,5664],{"class":5663},[3144,9525,9526,9529,9531],{"class":3142,"line":5240},[3144,9527,9528],{"class":5659},"              memory",[3144,9530,5679],{"class":5663},[3144,9532,9533],{"class":7139},"\"128Mi\"\n",[3144,9535,9536,9539,9541],{"class":3142,"line":5246},[3144,9537,9538],{"class":5659},"              cpu",[3144,9540,5679],{"class":5663},[3144,9542,9543],{"class":7139},"\"100m\"\n",[3144,9545,9546,9549],{"class":3142,"line":5251},[3144,9547,9548],{"class":5659},"            limits",[3144,9550,5664],{"class":5663},[3144,9552,9553,9555,9557],{"class":3142,"line":5256},[3144,9554,9528],{"class":5659},[3144,9556,5679],{"class":5663},[3144,9558,9559],{"class":7139},"\"256Mi\"\n",[3144,9561,9562,9564,9566],{"class":3142,"line":5261},[3144,9563,9538],{"class":5659},[3144,9565,5679],{"class":5663},[3144,9567,9568],{"class":7139},"\"500m\"\n",[3311,9570,9571,9576,9583,9588,9598,9603,9610,9614],{},[3114,9572,9573],{},[3118,9574,9575],{},"Startup Probe — що це?",[3114,9577,9578,9579,9582],{},"Startup probe — це спеціальна перевірка для застосунків з ",[3118,9580,9581],{},"повільним стартом",". Вона відключає liveness та readiness probes до тих пір, поки застосунок не стартує.",[3114,9584,9585],{},[3118,9586,9587],{},"Проблема без startup probe:",[3114,9589,9590,9591,9594,9595,3289],{},"Якщо застосунок стартує 60 секунд, а ",[3349,9592,9593],{},"livenessProbe.initialDelaySeconds: 30",", то liveness probe почне перевіряти застосунок через 30 секунд. Застосунок ще не готовий → liveness fails → Kubernetes вб'є контейнер → restart → знову 60 секунд старту → знову fails → ",[3118,9596,9597],{},"crash loop",[3114,9599,9600],{},[3118,9601,9602],{},"Рішення з startup probe:",[3114,9604,9605,9606,9609],{},"Startup probe перевіряє застосунок кожні 5 секунд, максимум 30 разів (150 секунд). Liveness та readiness probes ",[3118,9607,9608],{},"не працюють",", поки startup probe не пройде. Це дає застосунку достатньо часу на старт.",[3114,9611,9612],{},[3118,9613,5746],{},[3903,9615,9616,9619,9622],{},[3906,9617,9618],{},"Застосунки з повільним стартом (> 30 секунд)",[3906,9620,9621],{},"Застосунки, які завантажують багато даних при старті",[3906,9623,9624],{},"Legacy застосунки з довгою ініціалізацією",[3122,9626,9628],{"id":9627},"приклад-відповідей-health-checks","Приклад відповідей health checks",[3114,9630,9631],{},[3118,9632,9633,9634,9637],{},"Liveness endpoint (",[3349,9635,9636],{},"/health/live","):",[3135,9639,9641,9650],{"title":9640},"curl /health/live",[3139,9642,9644,3156,9647],{"className":9643},[3142],[3144,9645,3155],{"className":9646},[3147],[3118,9648,9649],{},"curl http://localhost:8080/health/live",[3139,9651,8750],{"className":9652},[3142],[3114,9654,9655],{},[3118,9656,9657,9658,9637],{},"Readiness endpoint (",[3349,9659,9660],{},"/health/ready",[3135,9662,9664,9673],{"title":9663},"curl /health/ready (успішно)",[3139,9665,9667,3156,9670],{"className":9666},[3142],[3144,9668,3155],{"className":9669},[3147],[3118,9671,9672],{},"curl http://localhost:8080/health/ready",[3139,9674,8750],{"className":9675},[3142],[3135,9677,9679,9687,9695],{"title":9678},"curl /health/ready (БД недоступна)",[3139,9680,9682,3156,9685],{"className":9681},[3142],[3144,9683,3155],{"className":9684},[3147],[3118,9686,9672],{},[3139,9688,9690],{"className":9689},[3142],[3144,9691,9694],{"className":9692},[9693],"text-rose-400","HTTP/1.1 503 Service Unavailable",[3139,9696,7945],{"className":9697},[3142],[3114,9699,9700],{},[3118,9701,9702,9703,9637],{},"Детальний endpoint (",[3349,9704,9705],{},"/health/detailed",[3135,9707,9709,9718,9721,9725,9729,9733,9737,9741,9745,9749,9753,9756,9760,9763,9767,9771,9774,9777,9781,9784,9788,9792,9796,9800,9804],{"title":9708},"curl /health/detailed",[3139,9710,9712,3156,9715],{"className":9711},[3142],[3144,9713,3155],{"className":9714},[3147],[3118,9716,9717],{},"curl http://localhost:8080/health/detailed",[3139,9719,8810],{"className":9720},[3142],[3139,9722,9724],{"className":9723},[3142],"  \"status\": \"Healthy\",",[3139,9726,9728],{"className":9727},[3142],"  \"checks\": [",[3139,9730,9732],{"className":9731},[3142],"    {",[3139,9734,9736],{"className":9735},[3142],"      \"name\": \"postgresql\",",[3139,9738,9740],{"className":9739},[3142],"      \"status\": \"Healthy\",",[3139,9742,9744],{"className":9743},[3142],"      \"description\": \"Connection successful\",",[3139,9746,9748],{"className":9747},[3142],"      \"duration\": 12.5",[3139,9750,9752],{"className":9751},[3142],"    },",[3139,9754,9732],{"className":9755},[3142],[3139,9757,9759],{"className":9758},[3142],"      \"name\": \"external-api\",",[3139,9761,9740],{"className":9762},[3142],[3139,9764,9766],{"className":9765},[3142],"      \"description\": null,",[3139,9768,9770],{"className":9769},[3142],"      \"duration\": 45.2",[3139,9772,9752],{"className":9773},[3142],[3139,9775,9732],{"className":9776},[3142],[3139,9778,9780],{"className":9779},[3142],"      \"name\": \"memory\",",[3139,9782,9740],{"className":9783},[3142],[3139,9785,9787],{"className":9786},[3142],"      \"description\": \"Allocated memory: 156 MB\",",[3139,9789,9791],{"className":9790},[3142],"      \"duration\": 0.8",[3139,9793,9795],{"className":9794},[3142],"    }",[3139,9797,9799],{"className":9798},[3142],"  ],",[3139,9801,9803],{"className":9802},[3142],"  \"totalDuration\": 58.5",[3139,9805,8825],{"className":9806},[3142],[3291,9808],{},[3109,9810,9812],{"id":9811},"resource-management-для-net-застосунків","Resource Management для .NET застосунків",[3114,9814,9815],{},"Правильне налаштування ресурсів критично важливе для стабільності .NET застосунків у Kubernetes.",[3122,9817,9819],{"id":9818},"розуміння-requests-та-limits","Розуміння requests та limits",[3376,9821,9822],{},[3379,9823,9825],{"className":3381,"code":9824,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nrectangle \"Вузол (Node)\" {\n    rectangle \"Доступні ресурси\" as total #e3f2fd {\n        [CPU: 4 cores]\n        [Memory: 8 GB]\n    }\n    \n    rectangle \"Pod 1\" as p1 #e8f5e9 {\n        rectangle \"requests\" as r1 #fff3e0 {\n            [CPU: 500m]\n            [Memory: 512Mi]\n        }\n        rectangle \"limits\" as l1 #ffebee {\n            [CPU: 1000m]\n            [Memory: 1Gi]\n        }\n        rectangle \"Реальне використання\" as u1 {\n            [CPU: 750m]\n            [Memory: 768Mi]\n        }\n    }\n    \n    rectangle \"Pod 2\" as p2 #e8f5e9 {\n        rectangle \"requests\" as r2 #fff3e0 {\n            [CPU: 250m]\n            [Memory: 256Mi]\n        }\n        rectangle \"limits\" as l2 #ffebee {\n            [CPU: 500m]\n            [Memory: 512Mi]\n        }\n        rectangle \"Реальне використання\" as u2 {\n            [CPU: 300m]\n            [Memory: 384Mi]\n        }\n    }\n}\n\nnote right of r1\n    Requests — гарантовані ресурси\n    Scheduler використовує для\n    вибору вузла\nend note\n\nnote right of l1\n    Limits — максимум\n    CPU: throttling\n    Memory: OOMKilled\nend note\n\nnote right of u1\n    Реальне використання\n    може бути між\n    requests та limits\nend note\n\n@enduml\n",[3349,9826,9827,9831,9835,9839,9843,9848,9853,9858,9863,9867,9871,9876,9881,9886,9891,9896,9901,9906,9911,9915,9920,9925,9930,9934,9938,9942,9947,9952,9957,9962,9966,9971,9975,9979,9983,9988,9993,9998,10002,10006,10010,10014,10019,10024,10029,10034,10038,10042,10047,10052,10057,10062,10066,10070,10075,10080,10085,10090,10094,10098],{"__ignoreMap":3384},[3144,9828,9829],{"class":3142,"line":3389},[3144,9830,3392],{},[3144,9832,9833],{"class":3142,"line":3395},[3144,9834,3398],{},[3144,9836,9837],{"class":3142,"line":3401},[3144,9838,3404],{},[3144,9840,9841],{"class":3142,"line":3407},[3144,9842,3411],{"emptyLinePlaceholder":3410},[3144,9844,9845],{"class":3142,"line":3414},[3144,9846,9847],{},"rectangle \"Вузол (Node)\" {\n",[3144,9849,9850],{"class":3142,"line":3420},[3144,9851,9852],{},"    rectangle \"Доступні ресурси\" as total #e3f2fd {\n",[3144,9854,9855],{"class":3142,"line":3426},[3144,9856,9857],{},"        [CPU: 4 cores]\n",[3144,9859,9860],{"class":3142,"line":3432},[3144,9861,9862],{},"        [Memory: 8 GB]\n",[3144,9864,9865],{"class":3142,"line":3438},[3144,9866,3435],{},[3144,9868,9869],{"class":3142,"line":3444},[3144,9870,3802],{},[3144,9872,9873],{"class":3142,"line":3449},[3144,9874,9875],{},"    rectangle \"Pod 1\" as p1 #e8f5e9 {\n",[3144,9877,9878],{"class":3142,"line":3455},[3144,9879,9880],{},"        rectangle \"requests\" as r1 #fff3e0 {\n",[3144,9882,9883],{"class":3142,"line":3461},[3144,9884,9885],{},"            [CPU: 500m]\n",[3144,9887,9888],{"class":3142,"line":3467},[3144,9889,9890],{},"            [Memory: 512Mi]\n",[3144,9892,9893],{"class":3142,"line":3473},[3144,9894,9895],{},"        }\n",[3144,9897,9898],{"class":3142,"line":3478},[3144,9899,9900],{},"        rectangle \"limits\" as l1 #ffebee {\n",[3144,9902,9903],{"class":3142,"line":3483},[3144,9904,9905],{},"            [CPU: 1000m]\n",[3144,9907,9908],{"class":3142,"line":3489},[3144,9909,9910],{},"            [Memory: 1Gi]\n",[3144,9912,9913],{"class":3142,"line":3495},[3144,9914,9895],{},[3144,9916,9917],{"class":3142,"line":3501},[3144,9918,9919],{},"        rectangle \"Реальне використання\" as u1 {\n",[3144,9921,9922],{"class":3142,"line":3506},[3144,9923,9924],{},"            [CPU: 750m]\n",[3144,9926,9927],{"class":3142,"line":3512},[3144,9928,9929],{},"            [Memory: 768Mi]\n",[3144,9931,9932],{"class":3142,"line":3518},[3144,9933,9895],{},[3144,9935,9936],{"class":3142,"line":3524},[3144,9937,3435],{},[3144,9939,9940],{"class":3142,"line":3530},[3144,9941,3802],{},[3144,9943,9944],{"class":3142,"line":3535},[3144,9945,9946],{},"    rectangle \"Pod 2\" as p2 #e8f5e9 {\n",[3144,9948,9949],{"class":3142,"line":3705},[3144,9950,9951],{},"        rectangle \"requests\" as r2 #fff3e0 {\n",[3144,9953,9954],{"class":3142,"line":3711},[3144,9955,9956],{},"            [CPU: 250m]\n",[3144,9958,9959],{"class":3142,"line":3717},[3144,9960,9961],{},"            [Memory: 256Mi]\n",[3144,9963,9964],{"class":3142,"line":3722},[3144,9965,9895],{},[3144,9967,9968],{"class":3142,"line":3727},[3144,9969,9970],{},"        rectangle \"limits\" as l2 #ffebee {\n",[3144,9972,9973],{"class":3142,"line":4084},[3144,9974,9885],{},[3144,9976,9977],{"class":3142,"line":4090},[3144,9978,9890],{},[3144,9980,9981],{"class":3142,"line":4096},[3144,9982,9895],{},[3144,9984,9985],{"class":3142,"line":4102},[3144,9986,9987],{},"        rectangle \"Реальне використання\" as u2 {\n",[3144,9989,9990],{"class":3142,"line":4107},[3144,9991,9992],{},"            [CPU: 300m]\n",[3144,9994,9995],{"class":3142,"line":4112},[3144,9996,9997],{},"            [Memory: 384Mi]\n",[3144,9999,10000],{"class":3142,"line":4118},[3144,10001,9895],{},[3144,10003,10004],{"class":3142,"line":4124},[3144,10005,3435],{},[3144,10007,10008],{"class":3142,"line":4130},[3144,10009,3441],{},[3144,10011,10012],{"class":3142,"line":4135},[3144,10013,3411],{"emptyLinePlaceholder":3410},[3144,10015,10016],{"class":3142,"line":4140},[3144,10017,10018],{},"note right of r1\n",[3144,10020,10021],{"class":3142,"line":5169},[3144,10022,10023],{},"    Requests — гарантовані ресурси\n",[3144,10025,10026],{"class":3142,"line":5174},[3144,10027,10028],{},"    Scheduler використовує для\n",[3144,10030,10031],{"class":3142,"line":5179},[3144,10032,10033],{},"    вибору вузла\n",[3144,10035,10036],{"class":3142,"line":5185},[3144,10037,3527],{},[3144,10039,10040],{"class":3142,"line":5190},[3144,10041,3411],{"emptyLinePlaceholder":3410},[3144,10043,10044],{"class":3142,"line":5196},[3144,10045,10046],{},"note right of l1\n",[3144,10048,10049],{"class":3142,"line":5202},[3144,10050,10051],{},"    Limits — максимум\n",[3144,10053,10054],{"class":3142,"line":5207},[3144,10055,10056],{},"    CPU: throttling\n",[3144,10058,10059],{"class":3142,"line":5212},[3144,10060,10061],{},"    Memory: OOMKilled\n",[3144,10063,10064],{"class":3142,"line":5218},[3144,10065,3527],{},[3144,10067,10068],{"class":3142,"line":5223},[3144,10069,3411],{"emptyLinePlaceholder":3410},[3144,10071,10072],{"class":3142,"line":5228},[3144,10073,10074],{},"note right of u1\n",[3144,10076,10077],{"class":3142,"line":5234},[3144,10078,10079],{},"    Реальне використання\n",[3144,10081,10082],{"class":3142,"line":5240},[3144,10083,10084],{},"    може бути між\n",[3144,10086,10087],{"class":3142,"line":5246},[3144,10088,10089],{},"    requests та limits\n",[3144,10091,10092],{"class":3142,"line":5251},[3144,10093,3527],{},[3144,10095,10096],{"class":3142,"line":5256},[3144,10097,3411],{"emptyLinePlaceholder":3410},[3144,10099,10100],{"class":3142,"line":5261},[3144,10101,3538],{},[3122,10103,10105],{"id":10104},"що-відбувається-при-перевищенні-limits","Що відбувається при перевищенні limits",[3330,10107,10108,10189],{},[3333,10109,10112,10125,10130,10141,10145,10177,10180],{"icon":10110,"title":10111},"i-heroicons-cpu-chip","CPU Throttling",[3114,10113,10114,10116,10117,10120,10121,10124],{},[3118,10115,7405],{}," Якщо Pod спробує використати більше CPU, ніж ",[3349,10118,10119],{},"limits.cpu",", Kubernetes ",[3118,10122,10123],{},"обмежує"," (throttle) його CPU.",[3114,10126,10127],{},[3118,10128,10129],{},"Симптоми:",[3903,10131,10132,10135,10138],{},[3906,10133,10134],{},"Застосунок працює повільніше",[3906,10136,10137],{},"Запити обробляються довше",[3906,10139,10140],{},"Таймаути у клієнтів",[3114,10142,10143],{},[3118,10144,7176],{},[3379,10146,10148],{"className":5650,"code":10147,"language":5652,"meta":3384,"style":3384},"resources:\n  limits:\n    cpu: \"500m\"  # 0.5 cores\n",[3349,10149,10150,10157,10164],{"__ignoreMap":3384},[3144,10151,10152,10155],{"class":3142,"line":3389},[3144,10153,10154],{"class":5659},"resources",[3144,10156,5664],{"class":5663},[3144,10158,10159,10162],{"class":3142,"line":3395},[3144,10160,10161],{"class":5659},"  limits",[3144,10163,5664],{"class":5663},[3144,10165,10166,10169,10171,10174],{"class":3142,"line":3401},[3144,10167,10168],{"class":5659},"    cpu",[3144,10170,5679],{"class":5663},[3144,10172,10173],{"class":7139},"\"500m\"",[3144,10175,10176],{"class":7200},"  # 0.5 cores\n",[3114,10178,10179],{},"Якщо застосунок спробує використати 1 core, Kubernetes обмежить його до 0.5 cores. Застосунок не впаде, але працюватиме повільніше.",[3114,10181,10182,3156,10185,10188],{},[3118,10183,10184],{},"Як виявити:",[3349,10186,10187],{},"kubectl top pods"," показує CPU usage близько до limits",[3333,10190,10193,10205,10209,10230,10234,10260,10263,10267],{"icon":10191,"title":10192},"i-heroicons-exclamation-triangle","OOMKilled (Out Of Memory)",[3114,10194,10195,10197,10198,10120,10201,10204],{},[3118,10196,7405],{}," Якщо Pod спробує використати більше пам'яті, ніж ",[3349,10199,10200],{},"limits.memory",[3118,10202,10203],{},"вб'є"," (kill) контейнер.",[3114,10206,10207],{},[3118,10208,10129],{},[3903,10210,10211,10214,10224],{},[3906,10212,10213],{},"Pod постійно перезапускається",[3906,10215,10216,10217,10220,10221],{},"Статус ",[3349,10218,10219],{},"OOMKilled"," у ",[3349,10222,10223],{},"kubectl describe pod",[3906,10225,10226,10229],{},[3349,10227,10228],{},"RESTARTS"," збільшується",[3114,10231,10232],{},[3118,10233,7176],{},[3379,10235,10237],{"className":5650,"code":10236,"language":5652,"meta":3384,"style":3384},"resources:\n  limits:\n    memory: \"256Mi\"\n",[3349,10238,10239,10245,10251],{"__ignoreMap":3384},[3144,10240,10241,10243],{"class":3142,"line":3389},[3144,10242,10154],{"class":5659},[3144,10244,5664],{"class":5663},[3144,10246,10247,10249],{"class":3142,"line":3395},[3144,10248,10161],{"class":5659},[3144,10250,5664],{"class":5663},[3144,10252,10253,10256,10258],{"class":3142,"line":3401},[3144,10254,10255],{"class":5659},"    memory",[3144,10257,5679],{"class":5663},[3144,10259,9559],{"class":7139},[3114,10261,10262],{},"Якщо застосунок спробує виділити 300 MB, Kubernetes вб'є контейнер з exit code 137.",[3114,10264,10265],{},[3118,10266,10184],{},[3379,10268,10272],{"className":10269,"code":10270,"language":10271,"meta":3384,"style":3384},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","kubectl describe pod \u003Cpod-name>\n# Last State:     Terminated\n#   Reason:       OOMKilled\n#   Exit Code:    137\n","bash",[3349,10273,10274,10294,10299,10304],{"__ignoreMap":3384},[3144,10275,10276,10279,10282,10285,10288,10291],{"class":3142,"line":3389},[3144,10277,10278],{"class":7663},"kubectl",[3144,10280,10281],{"class":7139}," describe",[3144,10283,10284],{"class":7139}," pod",[3144,10286,10287],{"class":5663}," \u003C",[3144,10289,10290],{"class":7139},"pod-nam",[3144,10292,10293],{"class":5663},"e>\n",[3144,10295,10296],{"class":3142,"line":3395},[3144,10297,10298],{"class":7200},"# Last State:     Terminated\n",[3144,10300,10301],{"class":3142,"line":3401},[3144,10302,10303],{"class":7200},"#   Reason:       OOMKilled\n",[3144,10305,10306],{"class":3142,"line":3407},[3144,10307,10308],{"class":7200},"#   Exit Code:    137\n",[3122,10310,10312],{"id":10311},"net-garbage-collection-та-kubernetes",".NET Garbage Collection та Kubernetes",[3114,10314,10315],{},".NET має особливості роботи з пам'яттю, які важливо враховувати у Kubernetes:",[3114,10317,10318,10320],{},[3118,10319,3209],{}," .NET GC не знає про Kubernetes memory limits. Він бачить всю пам'ять вузла (наприклад, 8 GB) та намагається використати до 75% від неї. Але Pod має limit 256 MB → OOMKilled.",[3114,10322,10323,10326],{},[3118,10324,10325],{},"Рішення:"," Налаштувати .NET GC для роботи у контейнері:",[3379,10328,10332],{"className":10329,"code":10330,"language":10331,"meta":3384,"style":3384},"language-dockerfile shiki shiki-themes light-plus dark-plus dark-plus","# У Dockerfile\nENV DOTNET_RUNNING_IN_CONTAINER=true\nENV DOTNET_GCHeapHardLimit=0x10000000  # 256 MB у hex (опціонально)\n","dockerfile",[3349,10333,10334,10339,10347],{"__ignoreMap":3384},[3144,10335,10336],{"class":3142,"line":3389},[3144,10337,10338],{"class":7200},"# У Dockerfile\n",[3144,10340,10341,10344],{"class":3142,"line":3395},[3144,10342,10343],{"class":7647},"ENV",[3144,10345,10346],{"class":5663}," DOTNET_RUNNING_IN_CONTAINER=true\n",[3144,10348,10349,10351],{"class":3142,"line":3401},[3144,10350,10343],{"class":7647},[3144,10352,10353],{"class":5663}," DOTNET_GCHeapHardLimit=0x10000000  # 256 MB у hex (опціонально)\n",[3114,10355,10356],{},"Або через Deployment YAML:",[3379,10358,10360],{"className":5650,"code":10359,"language":5652,"meta":3384,"style":3384},"spec:\n  containers:\n    - name: todoapi\n      image: todoapi:2.0.0\n      env:\n        - name: DOTNET_RUNNING_IN_CONTAINER\n          value: \"true\"\n        - name: DOTNET_GCHeapHardLimit\n          value: \"0x10000000\"  # 256 MB\n      resources:\n        limits:\n          memory: \"256Mi\"\n",[3349,10361,10362,10368,10375,10385,10394,10401,10412,10422,10433,10445,10452,10459],{"__ignoreMap":3384},[3144,10363,10364,10366],{"class":3142,"line":3389},[3144,10365,5660],{"class":5659},[3144,10367,5664],{"class":5663},[3144,10369,10370,10373],{"class":3142,"line":3395},[3144,10371,10372],{"class":5659},"  containers",[3144,10374,5664],{"class":5663},[3144,10376,10377,10379,10381,10383],{"class":3142,"line":3401},[3144,10378,7121],{"class":5663},[3144,10380,9189],{"class":5659},[3144,10382,5679],{"class":5663},[3144,10384,9098],{"class":5682},[3144,10386,10387,10390,10392],{"class":3142,"line":3407},[3144,10388,10389],{"class":5659},"      image",[3144,10391,5679],{"class":5663},[3144,10393,9203],{"class":5682},[3144,10395,10396,10399],{"class":3142,"line":3414},[3144,10397,10398],{"class":5659},"      env",[3144,10400,5664],{"class":5663},[3144,10402,10403,10405,10407,10409],{"class":3142,"line":3420},[3144,10404,9186],{"class":5663},[3144,10406,9189],{"class":5659},[3144,10408,5679],{"class":5663},[3144,10410,10411],{"class":5682},"DOTNET_RUNNING_IN_CONTAINER\n",[3144,10413,10414,10417,10419],{"class":3142,"line":3426},[3144,10415,10416],{"class":5659},"          value",[3144,10418,5679],{"class":5663},[3144,10420,10421],{"class":7139},"\"true\"\n",[3144,10423,10424,10426,10428,10430],{"class":3142,"line":3432},[3144,10425,9186],{"class":5663},[3144,10427,9189],{"class":5659},[3144,10429,5679],{"class":5663},[3144,10431,10432],{"class":5682},"DOTNET_GCHeapHardLimit\n",[3144,10434,10435,10437,10439,10442],{"class":3142,"line":3438},[3144,10436,10416],{"class":5659},[3144,10438,5679],{"class":5663},[3144,10440,10441],{"class":7139},"\"0x10000000\"",[3144,10443,10444],{"class":7200},"  # 256 MB\n",[3144,10446,10447,10450],{"class":3142,"line":3444},[3144,10448,10449],{"class":5659},"      resources",[3144,10451,5664],{"class":5663},[3144,10453,10454,10457],{"class":3142,"line":3449},[3144,10455,10456],{"class":5659},"        limits",[3144,10458,5664],{"class":5663},[3144,10460,10461,10464,10466],{"class":3142,"line":3455},[3144,10462,10463],{"class":5659},"          memory",[3144,10465,5679],{"class":5663},[3144,10467,9559],{"class":7139},[3114,10469,10470],{},[3118,10471,10472,10473,10476],{},"Що робить ",[3349,10474,10475],{},"DOTNET_RUNNING_IN_CONTAINER",":",[3903,10478,10479,10482,10485],{},[3906,10480,10481],{},".NET GC читає cgroup limits (Kubernetes memory limits)",[3906,10483,10484],{},"GC використовує максимум 75% від limits (наприклад, 192 MB з 256 MB)",[3906,10486,10487],{},"Це запобігає OOMKilled",[4932,10489,10490,10495,10528,10532],{},[3114,10491,10492],{},[3118,10493,10494],{},"Best practice для .NET у Kubernetes:",[3926,10496,10497,10506,10512,10518],{},[3906,10498,10499,10505],{},[3118,10500,10501,10502],{},"Завжди встановлюйте ",[3349,10503,10504],{},"DOTNET_RUNNING_IN_CONTAINER=true"," — це критично важливо",[3906,10507,10508,10511],{},[3118,10509,10510],{},"Встановлюйте memory limits"," — без них .NET може з'їсти всю пам'ять вузла",[3906,10513,10514,10517],{},[3118,10515,10516],{},"Requests = 50-70% від limits"," — залишає запас для GC",[3906,10519,10520,10523,10524,10527],{},[3118,10521,10522],{},"Моніторте GC"," — використовуйте ",[3349,10525,10526],{},"dotnet-counters"," або Application Insights",[3114,10529,10530],{},[3118,10531,7176],{},[3379,10533,10535],{"className":5650,"code":10534,"language":5652,"meta":3384,"style":3384},"resources:\n  requests:\n    memory: \"128Mi\"  # Мінімум для роботи\n    cpu: \"100m\"\n  limits:\n    memory: \"256Mi\"  # Максимум (GC використає ~192 MB)\n    cpu: \"500m\"\n",[3349,10536,10537,10543,10550,10562,10570,10576,10588],{"__ignoreMap":3384},[3144,10538,10539,10541],{"class":3142,"line":3389},[3144,10540,10154],{"class":5659},[3144,10542,5664],{"class":5663},[3144,10544,10545,10548],{"class":3142,"line":3395},[3144,10546,10547],{"class":5659},"  requests",[3144,10549,5664],{"class":5663},[3144,10551,10552,10554,10556,10559],{"class":3142,"line":3401},[3144,10553,10255],{"class":5659},[3144,10555,5679],{"class":5663},[3144,10557,10558],{"class":7139},"\"128Mi\"",[3144,10560,10561],{"class":7200},"  # Мінімум для роботи\n",[3144,10563,10564,10566,10568],{"class":3142,"line":3407},[3144,10565,10168],{"class":5659},[3144,10567,5679],{"class":5663},[3144,10569,9543],{"class":7139},[3144,10571,10572,10574],{"class":3142,"line":3414},[3144,10573,10161],{"class":5659},[3144,10575,5664],{"class":5663},[3144,10577,10578,10580,10582,10585],{"class":3142,"line":3420},[3144,10579,10255],{"class":5659},[3144,10581,5679],{"class":5663},[3144,10583,10584],{"class":7139},"\"256Mi\"",[3144,10586,10587],{"class":7200},"  # Максимум (GC використає ~192 MB)\n",[3144,10589,10590,10592,10594],{"class":3142,"line":3426},[3144,10591,10168],{"class":5659},[3144,10593,5679],{"class":5663},[3144,10595,9568],{"class":7139},[3122,10597,10599],{"id":10598},"підбір-оптимальних-ресурсів-для-net","Підбір оптимальних ресурсів для .NET",[3114,10601,10602],{},"Як визначити правильні значення requests та limits?",[3114,10604,10605],{},[3118,10606,10607],{},"Крок 1: Запустити без limits та виміряти",[3379,10609,10611],{"className":5650,"code":10610,"language":5652,"meta":3384,"style":3384},"resources:\n  requests:\n    memory: \"128Mi\"\n    cpu: \"100m\"\n  # Без limits — дозволити використовувати скільки потрібно\n",[3349,10612,10613,10619,10625,10633,10641],{"__ignoreMap":3384},[3144,10614,10615,10617],{"class":3142,"line":3389},[3144,10616,10154],{"class":5659},[3144,10618,5664],{"class":5663},[3144,10620,10621,10623],{"class":3142,"line":3395},[3144,10622,10547],{"class":5659},[3144,10624,5664],{"class":5663},[3144,10626,10627,10629,10631],{"class":3142,"line":3401},[3144,10628,10255],{"class":5659},[3144,10630,5679],{"class":5663},[3144,10632,9533],{"class":7139},[3144,10634,10635,10637,10639],{"class":3142,"line":3407},[3144,10636,10168],{"class":5659},[3144,10638,5679],{"class":5663},[3144,10640,9543],{"class":7139},[3144,10642,10643],{"class":3142,"line":3414},[3144,10644,10645],{"class":7200},"  # Без limits — дозволити використовувати скільки потрібно\n",[3114,10647,10648],{},[3118,10649,10650],{},"Крок 2: Згенерувати навантаження та виміряти споживання",[3135,10652,10654,10661,10670,10673,10680,10689,10693,10697,10701],{"title":10653},"Вимірювання ресурсів",[3139,10655,10657],{"className":10656},[3142],[3144,10658,10660],{"className":10659},[3147],"# Генерація навантаження",[3139,10662,10664,3156,10667],{"className":10663},[3142],[3144,10665,3155],{"className":10666},[3147],[3118,10668,10669],{},"wrk -t10 -c100 -d60s http://localhost:8080/todos",[3139,10671],{"className":10672},[3142],[3139,10674,10676],{"className":10675},[3142],[3144,10677,10679],{"className":10678},[3147],"# Моніторинг ресурсів (у іншому терміналі)",[3139,10681,10683,3156,10686],{"className":10682},[3142],[3144,10684,3155],{"className":10685},[3147],[3118,10687,10688],{},"kubectl top pods -l app=todoapi --watch",[3139,10690,10692],{"className":10691},[3142],"NAME                       CPU(cores)   MEMORY(bytes)",[3139,10694,10696],{"className":10695},[3142],"todoapi-xxx-yyy            450m         180Mi",[3139,10698,10700],{"className":10699},[3142],"todoapi-xxx-zzz            420m         175Mi",[3139,10702,10704],{"className":10703},[3142],"todoapi-xxx-www            480m         185Mi",[3114,10706,10707],{},[3118,10708,10709],{},"Крок 3: Встановити limits з запасом",[3114,10711,10712],{},"На основі вимірювань:",[3903,10714,10715,10725],{},[3906,10716,10717,10720,10721,10724],{},[3118,10718,10719],{},"Пікове CPU:"," 480m → встановити limit ",[3349,10722,10723],{},"600m"," (запас 25%)",[3906,10726,10727,10730,10731,10734],{},[3118,10728,10729],{},"Пікова пам'ять:"," 185 MB → встановити limit ",[3349,10732,10733],{},"256Mi"," (запас 38%)",[3379,10736,10738],{"className":5650,"code":10737,"language":5652,"meta":3384,"style":3384},"resources:\n  requests:\n    memory: \"128Mi\"  # Базове споживання\n    cpu: \"200m\"      # Середнє споживання\n  limits:\n    memory: \"256Mi\"  # Пік + запас\n    cpu: \"600m\"      # Пік + запас\n",[3349,10739,10740,10746,10752,10763,10775,10781,10792],{"__ignoreMap":3384},[3144,10741,10742,10744],{"class":3142,"line":3389},[3144,10743,10154],{"class":5659},[3144,10745,5664],{"class":5663},[3144,10747,10748,10750],{"class":3142,"line":3395},[3144,10749,10547],{"class":5659},[3144,10751,5664],{"class":5663},[3144,10753,10754,10756,10758,10760],{"class":3142,"line":3401},[3144,10755,10255],{"class":5659},[3144,10757,5679],{"class":5663},[3144,10759,10558],{"class":7139},[3144,10761,10762],{"class":7200},"  # Базове споживання\n",[3144,10764,10765,10767,10769,10772],{"class":3142,"line":3407},[3144,10766,10168],{"class":5659},[3144,10768,5679],{"class":5663},[3144,10770,10771],{"class":7139},"\"200m\"",[3144,10773,10774],{"class":7200},"      # Середнє споживання\n",[3144,10776,10777,10779],{"class":3142,"line":3414},[3144,10778,10161],{"class":5659},[3144,10780,5664],{"class":5663},[3144,10782,10783,10785,10787,10789],{"class":3142,"line":3420},[3144,10784,10255],{"class":5659},[3144,10786,5679],{"class":5663},[3144,10788,10584],{"class":7139},[3144,10790,10791],{"class":7200},"  # Пік + запас\n",[3144,10793,10794,10796,10798,10801],{"class":3142,"line":3426},[3144,10795,10168],{"class":5659},[3144,10797,5679],{"class":5663},[3144,10799,10800],{"class":7139},"\"600m\"",[3144,10802,10803],{"class":7200},"      # Пік + запас\n",[3114,10805,10806],{},[3118,10807,10808],{},"Типові проблеми та рішення:",[3330,10810,10811,10860],{},[3333,10812,10815,10820,10826,10831],{"icon":10813,"title":10814},"i-heroicons-exclamation-circle","Проблема: OOMKilled під час GC",[3114,10816,10817,10819],{},[3118,10818,10129],{}," Pod падає під час garbage collection",[3114,10821,10822,10825],{},[3118,10823,10824],{},"Причина:"," GC потребує додаткової пам'яті для роботи. Якщо limits занадто жорсткі, GC не може виділити пам'ять → OOMKilled.",[3114,10827,10828,10830],{},[3118,10829,10325],{}," Збільшити limits на 20-30% понад пікове споживання:",[3379,10832,10834],{"className":5650,"code":10833,"language":5652,"meta":3384,"style":3384},"resources:\n  limits:\n    memory: \"320Mi\"  # Було 256Mi\n",[3349,10835,10836,10842,10848],{"__ignoreMap":3384},[3144,10837,10838,10840],{"class":3142,"line":3389},[3144,10839,10154],{"class":5659},[3144,10841,5664],{"class":5663},[3144,10843,10844,10846],{"class":3142,"line":3395},[3144,10845,10161],{"class":5659},[3144,10847,5664],{"class":5663},[3144,10849,10850,10852,10854,10857],{"class":3142,"line":3401},[3144,10851,10255],{"class":5659},[3144,10853,5679],{"class":5663},[3144,10855,10856],{"class":7139},"\"320Mi\"",[3144,10858,10859],{"class":7200},"  # Було 256Mi\n",[3333,10861,10863,10868,10873,10878],{"icon":3335,"title":10862},"Проблема: Повільні запити під навантаженням",[3114,10864,10865,10867],{},[3118,10866,10129],{}," Запити обробляються повільно, таймаути",[3114,10869,10870,10872],{},[3118,10871,10824],{}," CPU throttling — застосунок досяг CPU limits",[3114,10874,10875,10877],{},[3118,10876,10325],{}," Збільшити CPU limits або додати більше реплік:",[3379,10879,10881],{"className":5650,"code":10880,"language":5652,"meta":3384,"style":3384},"resources:\n  limits:\n    cpu: \"1000m\"  # Було 500m\n# АБО\nspec:\n  replicas: 5  # Було 3\n",[3349,10882,10883,10889,10895,10907,10912,10918],{"__ignoreMap":3384},[3144,10884,10885,10887],{"class":3142,"line":3389},[3144,10886,10154],{"class":5659},[3144,10888,5664],{"class":5663},[3144,10890,10891,10893],{"class":3142,"line":3395},[3144,10892,10161],{"class":5659},[3144,10894,5664],{"class":5663},[3144,10896,10897,10899,10901,10904],{"class":3142,"line":3401},[3144,10898,10168],{"class":5659},[3144,10900,5679],{"class":5663},[3144,10902,10903],{"class":7139},"\"1000m\"",[3144,10905,10906],{"class":7200},"  # Було 500m\n",[3144,10908,10909],{"class":3142,"line":3407},[3144,10910,10911],{"class":7200},"# АБО\n",[3144,10913,10914,10916],{"class":3142,"line":3414},[3144,10915,5660],{"class":5659},[3144,10917,5664],{"class":5663},[3144,10919,10920,10922,10924,10926],{"class":3142,"line":3420},[3144,10921,9109],{"class":5659},[3144,10923,5679],{"class":5663},[3144,10925,9301],{"class":5700},[3144,10927,10928],{"class":7200},"  # Було 3\n",[3291,10930],{},[3109,10932,10934],{"id":10933},"rollback-та-історія-версій","Rollback та історія версій",[3114,10936,10937,10938,10941],{},"Одна з найважливіших можливостей Deployment — швидкий ",[3118,10939,10940],{},"rollback"," (повернення до попередньої версії).",[3122,10943,10945],{"id":10944},"як-kubernetes-зберігає-історію","Як Kubernetes зберігає історію",[3114,10947,10948,10949,10951,10952,10955],{},"Кожна зміна у ",[3349,10950,3738],{}," створює нову ",[3118,10953,10954],{},"ревізію"," (revision) Deployment. Kubernetes зберігає старі ReplicaSet для можливості rollback.",[3376,10957,10958],{},[3379,10959,10961],{"className":3381,"code":10960,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Deployment: todoapi\" #e3f2fd {\n    rectangle \"Revision History\" {\n        component \"ReplicaSet v1.0\\n(revision 1)\" as rs1 #e8f5e9 {\n            [replicas: 0]\n            [image: todoapi:1.0.0]\n        }\n        \n        component \"ReplicaSet v1.1\\n(revision 2)\" as rs2 #e8f5e9 {\n            [replicas: 0]\n            [image: todoapi:1.1.0]\n        }\n        \n        component \"ReplicaSet v2.0\\n(revision 3)\" as rs3 #fff3e0 {\n            [replicas: 3]\n            [image: todoapi:2.0.0]\n        }\n    }\n}\n\nnote right of rs1\n    Старі ReplicaSet\n    зберігаються з replicas=0\n    для швидкого rollback\nend note\n\nnote right of rs3\n    Поточна версія\n    (активна)\nend note\n\n@enduml\n",[3349,10962,10963,10967,10971,10975,10979,10983,10988,10993,10998,11003,11007,11011,11016,11020,11025,11029,11033,11038,11043,11048,11052,11056,11060,11064,11068,11073,11078,11083,11087,11091,11096,11101,11106,11110,11114],{"__ignoreMap":3384},[3144,10964,10965],{"class":3142,"line":3389},[3144,10966,3392],{},[3144,10968,10969],{"class":3142,"line":3395},[3144,10970,3398],{},[3144,10972,10973],{"class":3142,"line":3401},[3144,10974,3404],{},[3144,10976,10977],{"class":3142,"line":3407},[3144,10978,3411],{"emptyLinePlaceholder":3410},[3144,10980,10981],{"class":3142,"line":3414},[3144,10982,3417],{},[3144,10984,10985],{"class":3142,"line":3420},[3144,10986,10987],{},"    rectangle \"Revision History\" {\n",[3144,10989,10990],{"class":3142,"line":3426},[3144,10991,10992],{},"        component \"ReplicaSet v1.0\\n(revision 1)\" as rs1 #e8f5e9 {\n",[3144,10994,10995],{"class":3142,"line":3432},[3144,10996,10997],{},"            [replicas: 0]\n",[3144,10999,11000],{"class":3142,"line":3438},[3144,11001,11002],{},"            [image: todoapi:1.0.0]\n",[3144,11004,11005],{"class":3142,"line":3444},[3144,11006,9895],{},[3144,11008,11009],{"class":3142,"line":3449},[3144,11010,8717],{},[3144,11012,11013],{"class":3142,"line":3455},[3144,11014,11015],{},"        component \"ReplicaSet v1.1\\n(revision 2)\" as rs2 #e8f5e9 {\n",[3144,11017,11018],{"class":3142,"line":3461},[3144,11019,10997],{},[3144,11021,11022],{"class":3142,"line":3467},[3144,11023,11024],{},"            [image: todoapi:1.1.0]\n",[3144,11026,11027],{"class":3142,"line":3473},[3144,11028,9895],{},[3144,11030,11031],{"class":3142,"line":3478},[3144,11032,8717],{},[3144,11034,11035],{"class":3142,"line":3483},[3144,11036,11037],{},"        component \"ReplicaSet v2.0\\n(revision 3)\" as rs3 #fff3e0 {\n",[3144,11039,11040],{"class":3142,"line":3489},[3144,11041,11042],{},"            [replicas: 3]\n",[3144,11044,11045],{"class":3142,"line":3495},[3144,11046,11047],{},"            [image: todoapi:2.0.0]\n",[3144,11049,11050],{"class":3142,"line":3501},[3144,11051,9895],{},[3144,11053,11054],{"class":3142,"line":3506},[3144,11055,3435],{},[3144,11057,11058],{"class":3142,"line":3512},[3144,11059,3441],{},[3144,11061,11062],{"class":3142,"line":3518},[3144,11063,3411],{"emptyLinePlaceholder":3410},[3144,11065,11066],{"class":3142,"line":3524},[3144,11067,3509],{},[3144,11069,11070],{"class":3142,"line":3530},[3144,11071,11072],{},"    Старі ReplicaSet\n",[3144,11074,11075],{"class":3142,"line":3535},[3144,11076,11077],{},"    зберігаються з replicas=0\n",[3144,11079,11080],{"class":3142,"line":3705},[3144,11081,11082],{},"    для швидкого rollback\n",[3144,11084,11085],{"class":3142,"line":3711},[3144,11086,3527],{},[3144,11088,11089],{"class":3142,"line":3717},[3144,11090,3411],{"emptyLinePlaceholder":3410},[3144,11092,11093],{"class":3142,"line":3722},[3144,11094,11095],{},"note right of rs3\n",[3144,11097,11098],{"class":3142,"line":3727},[3144,11099,11100],{},"    Поточна версія\n",[3144,11102,11103],{"class":3142,"line":4084},[3144,11104,11105],{},"    (активна)\n",[3144,11107,11108],{"class":3142,"line":4090},[3144,11109,3527],{},[3144,11111,11112],{"class":3142,"line":4096},[3144,11113,3411],{"emptyLinePlaceholder":3410},[3144,11115,11116],{"class":3142,"line":4102},[3144,11117,3538],{},[3114,11119,11120],{},[3118,11121,11122],{},"Скільки ревізій зберігається?",[3114,11124,11125,11126,11129,11130,10476],{},"За замовчуванням Kubernetes зберігає ",[3118,11127,11128],{},"10 останніх ревізій",". Це контролюється параметром ",[3349,11131,11132],{},"revisionHistoryLimit",[3379,11134,11136],{"className":5650,"code":11135,"language":5652,"meta":3384,"style":3384},"spec:\n  revisionHistoryLimit: 10  # За замовчуванням\n",[3349,11137,11138,11144],{"__ignoreMap":3384},[3144,11139,11140,11142],{"class":3142,"line":3389},[3144,11141,5660],{"class":5659},[3144,11143,5664],{"class":5663},[3144,11145,11146,11149,11151,11153],{"class":3142,"line":3395},[3144,11147,11148],{"class":5659},"  revisionHistoryLimit",[3144,11150,5679],{"class":5663},[3144,11152,6883],{"class":5700},[3144,11154,11155],{"class":7200},"  # За замовчуванням\n",[3114,11157,11158,11159,11161],{},"Якщо встановити ",[3349,11160,6875],{}," — rollback буде неможливий (старі ReplicaSet видаляються одразу).",[3122,11163,11165],{"id":11164},"перегляд-історії-ревізій","Перегляд історії ревізій",[3114,11167,11168],{},"Переглянемо історію оновлень Deployment:",[3135,11170,11172,11181,11185,11189,11196,11202],{"title":11171},"kubectl rollout history",[3139,11173,11175,3156,11178],{"className":11174},[3142],[3144,11176,3155],{"className":11177},[3147],[3118,11179,11180],{},"kubectl rollout history deployment/todoapi",[3139,11182,11184],{"className":11183},[3142],"deployment.apps/todoapi",[3139,11186,11188],{"className":11187},[3142],"REVISION  CHANGE-CAUSE",[3139,11190,11192,11193],{"className":11191},[3142],"1         ",[11194,11195],"none",{},[3139,11197,11199,11200],{"className":11198},[3142],"2         ",[11194,11201],{},[3139,11203,11205],{"className":11204},[3142],"3         kubectl set image deployment/todoapi todoapi=todoapi:2.0.0",[3114,11207,11208],{},[3118,11209,11210],{},"Що означають колонки:",[3903,11212,11213,11219],{},[3906,11214,11215,11218],{},[3118,11216,11217],{},"REVISION"," — номер ревізії (збільшується з кожним оновленням)",[3906,11220,11221,11224],{},[3118,11222,11223],{},"CHANGE-CAUSE"," — причина зміни (якщо вказана через annotation)",[4932,11226,11227,11232,11235,11265,11268,11296,11299],{},[3114,11228,11229],{},[3118,11230,11231],{},"Як додати CHANGE-CAUSE:",[3114,11233,11234],{},"Щоб у історії було зрозуміло, що змінилось, додайте annotation при оновленні:",[3379,11236,11238],{"className":10269,"code":11237,"language":10271,"meta":3384,"style":3384},"kubectl set image deployment/todoapi todoapi=todoapi:2.0.0 \\\n  --record\n",[3349,11239,11240,11260],{"__ignoreMap":3384},[3144,11241,11242,11244,11247,11250,11253,11256],{"class":3142,"line":3389},[3144,11243,10278],{"class":7663},[3144,11245,11246],{"class":7139}," set",[3144,11248,11249],{"class":7139}," image",[3144,11251,11252],{"class":7139}," deployment/todoapi",[3144,11254,11255],{"class":7139}," todoapi=todoapi:2.0.0",[3144,11257,11259],{"class":11258},"sjcCO"," \\\n",[3144,11261,11262],{"class":3142,"line":3395},[3144,11263,11264],{"class":7647},"  --record\n",[3114,11266,11267],{},"Або через annotation у YAML:",[3379,11269,11271],{"className":5650,"code":11270,"language":5652,"meta":3384,"style":3384},"metadata:\n  annotations:\n    kubernetes.io/change-cause: \"Update to version 2.0.0 with bug fixes\"\n",[3349,11272,11273,11279,11286],{"__ignoreMap":3384},[3144,11274,11275,11277],{"class":3142,"line":3389},[3144,11276,9086],{"class":5659},[3144,11278,5664],{"class":5663},[3144,11280,11281,11284],{"class":3142,"line":3395},[3144,11282,11283],{"class":5659},"  annotations",[3144,11285,5664],{"class":5663},[3144,11287,11288,11291,11293],{"class":3142,"line":3401},[3144,11289,11290],{"class":5659},"    kubernetes.io/change-cause",[3144,11292,5679],{"class":5663},[3144,11294,11295],{"class":7139},"\"Update to version 2.0.0 with bug fixes\"\n",[3114,11297,11298],{},"Тепер у історії буде:",[3379,11300,11303],{"className":11301,"code":11302,"language":6266},[6264],"REVISION  CHANGE-CAUSE\n3         Update to version 2.0.0 with bug fixes\n",[3349,11304,11302],{"__ignoreMap":3384},[3122,11306,11308],{"id":11307},"детальна-інформація-про-ревізію","Детальна інформація про ревізію",[3114,11310,11311],{},"Переглянемо детальну інформацію про конкретну ревізію:",[3135,11313,11315,11324,11328,11332,11336,11340,11344,11348,11352,11356,11360,11364,11368,11372,11376,11380],{"title":11314},"kubectl rollout history (детально)",[3139,11316,11318,3156,11321],{"className":11317},[3142],[3144,11319,3155],{"className":11320},[3147],[3118,11322,11323],{},"kubectl rollout history deployment/todoapi --revision=3",[3139,11325,11327],{"className":11326},[3142],"deployment.apps/todoapi with revision #3",[3139,11329,11331],{"className":11330},[3142],"Pod Template:",[3139,11333,11335],{"className":11334},[3142],"  Labels:       app=todoapi",[3139,11337,11339],{"className":11338},[3142],"                version=2.0.0",[3139,11341,11343],{"className":11342},[3142],"  Annotations:  kubernetes.io/change-cause: kubectl set image deployment/todoapi todoapi=todoapi:2.0.0",[3139,11345,11347],{"className":11346},[3142],"  Containers:",[3139,11349,11351],{"className":11350},[3142],"   todoapi:",[3139,11353,11355],{"className":11354},[3142],"    Image:      todoapi:2.0.0",[3139,11357,11359],{"className":11358},[3142],"    Port:       8080/TCP",[3139,11361,11363],{"className":11362},[3142],"    Limits:",[3139,11365,11367],{"className":11366},[3142],"      cpu:      500m",[3139,11369,11371],{"className":11370},[3142],"      memory:   256Mi",[3139,11373,11375],{"className":11374},[3142],"    Requests:",[3139,11377,11379],{"className":11378},[3142],"      cpu:      100m",[3139,11381,11383],{"className":11382},[3142],"      memory:   128Mi",[3114,11385,11386],{},"Тут ви бачите повну конфігурацію Pod для цієї ревізії.",[3122,11388,11390],{"id":11389},"rollback-до-попередньої-версії","Rollback до попередньої версії",[3114,11392,11393],{},"Якщо нова версія має баг, можна швидко повернутись до попередньої:",[3135,11395,11397,11406],{"title":11396},"kubectl rollout undo",[3139,11398,11400,3156,11403],{"className":11399},[3142],[3144,11401,3155],{"className":11402},[3147],[3118,11404,11405],{},"kubectl rollout undo deployment/todoapi",[3139,11407,11409],{"className":11408},[3142],[3144,11410,11412],{"className":11411},[3203],"deployment.apps/todoapi rolled back",[3114,11414,7405],{},[3926,11416,11417,11420,11425,11430],{},[3906,11418,11419],{},"Kubernetes знаходить попередню ревізію (revision 2)",[3906,11421,3930,11422,11424],{},[3349,11423,3933],{}," старого ReplicaSet (revision 2) з 0 до 3",[3906,11426,3937,11427,11429],{},[3349,11428,3933],{}," поточного ReplicaSet (revision 3) з 3 до 0",[3906,11431,11432],{},"Виконує rolling update у зворотному напрямку",[3376,11434,11435],{},[3379,11436,11438],{"className":3381,"code":11437,"language":3383,"meta":3384,"style":3384},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nparticipant \"kubectl\" as kubectl\nparticipant \"Deployment\\nController\" as dc\nparticipant \"ReplicaSet v1.1\\n(revision 2)\" as rs2\nparticipant \"ReplicaSet v2.0\\n(revision 3)\" as rs3\n\nkubectl -> dc: kubectl rollout undo deployment/todoapi\n\nactivate dc\ndc -> dc: Знайти попередню ревізію (2)\ndc -> rs2: Збільшити replicas: 0 → 3\ndc -> rs3: Зменшити replicas: 3 → 0\ndeactivate dc\n\nnote right of dc\n    Rolling update\n    у зворотному напрямку:\n    v2.0 → v1.1\nend note\n\nrs2 -> rs2: Створити Pod з v1.1\nrs3 -> rs3: Видалити Pod з v2.0\n\nnote right of rs2\n    Образ v1.1 вже є\n    на вузлах (кешовано)\n    → швидкий rollback\n    (10-20 секунд)\nend note\n\n@enduml\n",[3349,11439,11440,11444,11448,11452,11456,11460,11464,11469,11474,11478,11483,11487,11491,11496,11501,11506,11510,11514,11518,11523,11528,11533,11537,11541,11546,11551,11555,11559,11564,11569,11574,11579,11583,11587],{"__ignoreMap":3384},[3144,11441,11442],{"class":3142,"line":3389},[3144,11443,3392],{},[3144,11445,11446],{"class":3142,"line":3395},[3144,11447,3398],{},[3144,11449,11450],{"class":3142,"line":3401},[3144,11451,3404],{},[3144,11453,11454],{"class":3142,"line":3407},[3144,11455,3411],{"emptyLinePlaceholder":3410},[3144,11457,11458],{"class":3142,"line":3414},[3144,11459,3605],{},[3144,11461,11462],{"class":3142,"line":3420},[3144,11463,3620],{},[3144,11465,11466],{"class":3142,"line":3426},[3144,11467,11468],{},"participant \"ReplicaSet v1.1\\n(revision 2)\" as rs2\n",[3144,11470,11471],{"class":3142,"line":3432},[3144,11472,11473],{},"participant \"ReplicaSet v2.0\\n(revision 3)\" as rs3\n",[3144,11475,11476],{"class":3142,"line":3438},[3144,11477,3411],{"emptyLinePlaceholder":3410},[3144,11479,11480],{"class":3142,"line":3444},[3144,11481,11482],{},"kubectl -> dc: kubectl rollout undo deployment/todoapi\n",[3144,11484,11485],{"class":3142,"line":3449},[3144,11486,3411],{"emptyLinePlaceholder":3410},[3144,11488,11489],{"class":3142,"line":3455},[3144,11490,3668],{},[3144,11492,11493],{"class":3142,"line":3461},[3144,11494,11495],{},"dc -> dc: Знайти попередню ревізію (2)\n",[3144,11497,11498],{"class":3142,"line":3467},[3144,11499,11500],{},"dc -> rs2: Збільшити replicas: 0 → 3\n",[3144,11502,11503],{"class":3142,"line":3473},[3144,11504,11505],{},"dc -> rs3: Зменшити replicas: 3 → 0\n",[3144,11507,11508],{"class":3142,"line":3478},[3144,11509,3683],{},[3144,11511,11512],{"class":3142,"line":3483},[3144,11513,3411],{"emptyLinePlaceholder":3410},[3144,11515,11516],{"class":3142,"line":3489},[3144,11517,3692],{},[3144,11519,11520],{"class":3142,"line":3495},[3144,11521,11522],{},"    Rolling update\n",[3144,11524,11525],{"class":3142,"line":3501},[3144,11526,11527],{},"    у зворотному напрямку:\n",[3144,11529,11530],{"class":3142,"line":3506},[3144,11531,11532],{},"    v2.0 → v1.1\n",[3144,11534,11535],{"class":3142,"line":3512},[3144,11536,3527],{},[3144,11538,11539],{"class":3142,"line":3518},[3144,11540,3411],{"emptyLinePlaceholder":3410},[3144,11542,11543],{"class":3142,"line":3524},[3144,11544,11545],{},"rs2 -> rs2: Створити Pod з v1.1\n",[3144,11547,11548],{"class":3142,"line":3530},[3144,11549,11550],{},"rs3 -> rs3: Видалити Pod з v2.0\n",[3144,11552,11553],{"class":3142,"line":3535},[3144,11554,3411],{"emptyLinePlaceholder":3410},[3144,11556,11557],{"class":3142,"line":3705},[3144,11558,3869],{},[3144,11560,11561],{"class":3142,"line":3711},[3144,11562,11563],{},"    Образ v1.1 вже є\n",[3144,11565,11566],{"class":3142,"line":3717},[3144,11567,11568],{},"    на вузлах (кешовано)\n",[3144,11570,11571],{"class":3142,"line":3722},[3144,11572,11573],{},"    → швидкий rollback\n",[3144,11575,11576],{"class":3142,"line":3727},[3144,11577,11578],{},"    (10-20 секунд)\n",[3144,11580,11581],{"class":3142,"line":4084},[3144,11582,3527],{},[3144,11584,11585],{"class":3142,"line":4090},[3144,11586,3411],{"emptyLinePlaceholder":3410},[3144,11588,11589],{"class":3142,"line":4096},[3144,11590,3538],{},[3114,11592,11593],{},[3118,11594,11595],{},"Швидкість rollback:",[3114,11597,11598,11599,11602],{},"Rollback зазвичай займає ",[3118,11600,11601],{},"10-20 секунд",", бо:",[3903,11604,11605,11608,11611],{},[3906,11606,11607],{},"Образ попередньої версії вже є на вузлах (кешовано)",[3906,11609,11610],{},"Не потрібно завантажувати образ з registry",[3906,11612,11613],{},"Kubernetes просто перемикає ReplicaSet",[3122,11615,11617],{"id":11616},"rollback-до-конкретної-ревізії","Rollback до конкретної ревізії",[3114,11619,11620],{},"Можна повернутись не лише до попередньої, а до будь-якої ревізії:",[3135,11622,11624,11633],{"title":11623},"kubectl rollout undo (конкретна ревізія)",[3139,11625,11627,3156,11630],{"className":11626},[3142],[3144,11628,3155],{"className":11629},[3147],[3118,11631,11632],{},"kubectl rollout undo deployment/todoapi --to-revision=1",[3139,11634,11636],{"className":11635},[3142],[3144,11637,11412],{"className":11638},[3203],[3114,11640,11641],{},"Це повертає Deployment до ревізії 1 (самої першої версії).",[3122,11643,11645],{"id":11644},"моніторинг-процесу-rollout","Моніторинг процесу rollout",[3114,11647,11648],{},"Під час оновлення або rollback можна стежити за прогресом:",[3135,11650,11652,11661,11665,11668,11672,11676,11680],{"title":11651},"kubectl rollout status",[3139,11653,11655,3156,11658],{"className":11654},[3142],[3144,11656,3155],{"className":11657},[3147],[3118,11659,11660],{},"kubectl rollout status deployment/todoapi",[3139,11662,11664],{"className":11663},[3142],"Waiting for deployment \"todoapi\" rollout to finish: 1 out of 3 new replicas have been updated...",[3139,11666,11664],{"className":11667},[3142],[3139,11669,11671],{"className":11670},[3142],"Waiting for deployment \"todoapi\" rollout to finish: 2 out of 3 new replicas have been updated...",[3139,11673,11675],{"className":11674},[3142],"Waiting for deployment \"todoapi\" rollout to finish: 2 old replicas are pending termination...",[3139,11677,11679],{"className":11678},[3142],"Waiting for deployment \"todoapi\" rollout to finish: 1 old replicas are pending termination...",[3139,11681,11683],{"className":11682},[3142],[3144,11684,11686],{"className":11685},[3203],"deployment \"todoapi\" successfully rolled out",[3114,11688,11689],{},"Ця команда блокується до завершення rollout. Корисно для CI/CD pipelines.",[3122,11691,11693],{"id":11692},"призупинення-та-відновлення-rollout","Призупинення та відновлення rollout",[3114,11695,11696],{},"Іноді потрібно призупинити оновлення (наприклад, для canary deployment):",[3135,11698,11700,11709],{"title":11699},"kubectl rollout pause",[3139,11701,11703,3156,11706],{"className":11702},[3142],[3144,11704,3155],{"className":11705},[3147],[3118,11707,11708],{},"kubectl rollout pause deployment/todoapi",[3139,11710,11712],{"className":11711},[3142],[3144,11713,11715],{"className":11714},[3166],"deployment.apps/todoapi paused",[3114,11717,11718],{},"Після паузи оновлення зупиняється. Можна внести кілька змін, а потім відновити:",[3135,11720,11722,11731],{"title":11721},"kubectl rollout resume",[3139,11723,11725,3156,11728],{"className":11724},[3142],[3144,11726,3155],{"className":11727},[3147],[3118,11729,11730],{},"kubectl rollout resume deployment/todoapi",[3139,11732,11734],{"className":11733},[3142],[3144,11735,11737],{"className":11736},[3203],"deployment.apps/todoapi resumed",[3114,11739,11740],{},[3118,11741,6509],{},[3114,11743,11744,11747],{},[3118,11745,11746],{},"Canary deployment:"," Оновити 1 Pod, перевірити метрики, і якщо все добре — продовжити оновлення решти Pod.",[3379,11749,11751],{"className":10269,"code":11750,"language":10271,"meta":3384,"style":3384},"# Призупинити оновлення\nkubectl rollout pause deployment/todoapi\n\n# Оновити образ (створюється лише 1 новий Pod)\nkubectl set image deployment/todoapi todoapi=todoapi:2.0.0\n\n# Почекати 5 хвилин, перевірити метрики\nsleep 300\n\n# Якщо все добре — продовжити\nkubectl rollout resume deployment/todoapi\n\n# Якщо є проблеми — rollback\nkubectl rollout undo deployment/todoapi\n",[3349,11752,11753,11758,11771,11775,11780,11793,11797,11802,11810,11814,11819,11830,11834,11839],{"__ignoreMap":3384},[3144,11754,11755],{"class":3142,"line":3389},[3144,11756,11757],{"class":7200},"# Призупинити оновлення\n",[3144,11759,11760,11762,11765,11768],{"class":3142,"line":3395},[3144,11761,10278],{"class":7663},[3144,11763,11764],{"class":7139}," rollout",[3144,11766,11767],{"class":7139}," pause",[3144,11769,11770],{"class":7139}," deployment/todoapi\n",[3144,11772,11773],{"class":3142,"line":3401},[3144,11774,3411],{"emptyLinePlaceholder":3410},[3144,11776,11777],{"class":3142,"line":3407},[3144,11778,11779],{"class":7200},"# Оновити образ (створюється лише 1 новий Pod)\n",[3144,11781,11782,11784,11786,11788,11790],{"class":3142,"line":3414},[3144,11783,10278],{"class":7663},[3144,11785,11246],{"class":7139},[3144,11787,11249],{"class":7139},[3144,11789,11252],{"class":7139},[3144,11791,11792],{"class":7139}," todoapi=todoapi:2.0.0\n",[3144,11794,11795],{"class":3142,"line":3420},[3144,11796,3411],{"emptyLinePlaceholder":3410},[3144,11798,11799],{"class":3142,"line":3426},[3144,11800,11801],{"class":7200},"# Почекати 5 хвилин, перевірити метрики\n",[3144,11803,11804,11807],{"class":3142,"line":3432},[3144,11805,11806],{"class":7663},"sleep",[3144,11808,11809],{"class":5700}," 300\n",[3144,11811,11812],{"class":3142,"line":3438},[3144,11813,3411],{"emptyLinePlaceholder":3410},[3144,11815,11816],{"class":3142,"line":3444},[3144,11817,11818],{"class":7200},"# Якщо все добре — продовжити\n",[3144,11820,11821,11823,11825,11828],{"class":3142,"line":3449},[3144,11822,10278],{"class":7663},[3144,11824,11764],{"class":7139},[3144,11826,11827],{"class":7139}," resume",[3144,11829,11770],{"class":7139},[3144,11831,11832],{"class":3142,"line":3455},[3144,11833,3411],{"emptyLinePlaceholder":3410},[3144,11835,11836],{"class":3142,"line":3461},[3144,11837,11838],{"class":7200},"# Якщо є проблеми — rollback\n",[3144,11840,11841,11843,11845,11848],{"class":3142,"line":3467},[3144,11842,10278],{"class":7663},[3144,11844,11764],{"class":7139},[3144,11846,11847],{"class":7139}," undo",[3144,11849,11770],{"class":7139},[3291,11851],{},[3109,11853,11855],{"id":11854},"практичний-приклад-оновлення-todoapi-з-v10-на-v20","Практичний приклад: оновлення TodoApi з v1.0 на v2.0",[3114,11857,11858],{},"Тепер створимо реальний приклад оновлення застосунку з новою функціональністю.",[3122,11860,11862],{"id":11861},"версія-10-базовий-crud","Версія 1.0: Базовий CRUD",[3114,11864,11865],{},"Це наш початковий TodoApi (з попередньої статті):",[3114,11867,11868],{},[3118,11869,11870],{},"Program.cs (v1.0):",[3379,11872,11874],{"className":7638,"code":11873,"language":7640,"meta":3384,"style":3384},"using System.Collections.Concurrent;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nvar todos = new ConcurrentDictionary\u003Cint, Todo>();\nvar nextId = 1;\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\napp.MapGet(\"/health\", () => Results.Ok(new { status = \"healthy\", version = \"1.0.0\" }));\n\napp.MapGet(\"/todos\", () => Results.Ok(new { todos = todos.Values, count = todos.Count }));\n\napp.MapGet(\"/todos/{id:int}\", (int id) =>\n    todos.TryGetValue(id, out var todo) ? Results.Ok(todo) : Results.NotFound());\n\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    todos[id] = todo;\n    return Results.Created($\"/todos/{id}\", todo);\n});\n\napp.MapPut(\"/todos/{id:int}\", (int id, UpdateTodoRequest request) =>\n{\n    if (!todos.TryGetValue(id, out var todo))\n        return Results.NotFound();\n    \n    todo.Title = request.Title ?? todo.Title;\n    todo.IsCompleted = request.IsCompleted ?? todo.IsCompleted;\n    return Results.Ok(todo);\n});\n\napp.MapDelete(\"/todos/{id:int}\", (int id) =>\n    todos.TryRemove(id, out var todo) ? Results.Ok(todo) : Results.NotFound());\n\napp.Run();\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",[3349,11875,11876,11895,11899,11919,11923,11938,11953,11957,11984,11997,12001,12017,12021,12043,12047,12059,12070,12074,12078,12129,12133,12188,12192,12215,12268,12272,12295,12299,12325,12338,12342,12353,12370,12381,12396,12401,12417,12450,12454,12458,12486,12490,12519,12531,12535,12563,12590,12606,12610,12614,12635,12678,12682,12692,12696,12703,12707,12730,12753,12773,12793,12797,12801,12817],{"__ignoreMap":3384},[3144,11877,11878,11880,11883,11885,11888,11890,11893],{"class":3142,"line":3389},[3144,11879,7809],{"class":7808},[3144,11881,11882],{"class":7812}," System",[3144,11884,3289],{"class":5663},[3144,11886,11887],{"class":7812},"Collections",[3144,11889,3289],{"class":5663},[3144,11891,11892],{"class":7812},"Concurrent",[3144,11894,7831],{"class":5663},[3144,11896,11897],{"class":3142,"line":3395},[3144,11898,3411],{"emptyLinePlaceholder":3410},[3144,11900,11901,11903,11905,11907,11909,11911,11913,11915,11917],{"class":3142,"line":3401},[3144,11902,7648],{"class":7647},[3144,11904,7652],{"class":7651},[3144,11906,7655],{"class":5663},[3144,11908,7658],{"class":7651},[3144,11910,3289],{"class":5663},[3144,11912,7664],{"class":7663},[3144,11914,7667],{"class":5663},[3144,11916,7670],{"class":7651},[3144,11918,7673],{"class":5663},[3144,11920,11921],{"class":3142,"line":3407},[3144,11922,3411],{"emptyLinePlaceholder":3410},[3144,11924,11925,11927,11929,11931,11933,11936],{"class":3142,"line":3414},[3144,11926,7687],{"class":7651},[3144,11928,3289],{"class":5663},[3144,11930,7692],{"class":7651},[3144,11932,3289],{"class":5663},[3144,11934,11935],{"class":7663},"AddEndpointsApiExplorer",[3144,11937,7700],{"class":5663},[3144,11939,11940,11942,11944,11946,11948,11951],{"class":3142,"line":3420},[3144,11941,7687],{"class":7651},[3144,11943,3289],{"class":5663},[3144,11945,7692],{"class":7651},[3144,11947,3289],{"class":5663},[3144,11949,11950],{"class":7663},"AddSwaggerGen",[3144,11952,7700],{"class":5663},[3144,11954,11955],{"class":3142,"line":3426},[3144,11956,3411],{"emptyLinePlaceholder":3410},[3144,11958,11959,11961,11964,11966,11968,11971,11973,11976,11978,11981],{"class":3142,"line":3432},[3144,11960,7648],{"class":7647},[3144,11962,11963],{"class":7651}," todos",[3144,11965,7655],{"class":5663},[3144,11967,7957],{"class":7647},[3144,11969,11970],{"class":7812}," ConcurrentDictionary",[3144,11972,8069],{"class":5663},[3144,11974,11975],{"class":7647},"int",[3144,11977,6241],{"class":5663},[3144,11979,11980],{"class":7812},"Todo",[3144,11982,11983],{"class":5663},">();\n",[3144,11985,11986,11988,11991,11993,11995],{"class":3142,"line":3438},[3144,11987,7648],{"class":7647},[3144,11989,11990],{"class":7651}," nextId",[3144,11992,7655],{"class":5663},[3144,11994,6240],{"class":5700},[3144,11996,7831],{"class":5663},[3144,11998,11999],{"class":3142,"line":3444},[3144,12000,3411],{"emptyLinePlaceholder":3410},[3144,12002,12003,12005,12007,12009,12011,12013,12015],{"class":3142,"line":3449},[3144,12004,7648],{"class":7647},[3144,12006,7711],{"class":7651},[3144,12008,7655],{"class":5663},[3144,12010,7687],{"class":7651},[3144,12012,3289],{"class":5663},[3144,12014,7720],{"class":7663},[3144,12016,7700],{"class":5663},[3144,12018,12019],{"class":3142,"line":3455},[3144,12020,3411],{"emptyLinePlaceholder":3410},[3144,12022,12023,12026,12028,12030,12032,12035,12037,12040],{"class":3142,"line":3461},[3144,12024,12025],{"class":7808},"if",[3144,12027,6248],{"class":5663},[3144,12029,7736],{"class":7651},[3144,12031,3289],{"class":5663},[3144,12033,12034],{"class":7651},"Environment",[3144,12036,3289],{"class":5663},[3144,12038,12039],{"class":7663},"IsDevelopment",[3144,12041,12042],{"class":5663},"())\n",[3144,12044,12045],{"class":3142,"line":3467},[3144,12046,8134],{"class":5663},[3144,12048,12049,12052,12054,12057],{"class":3142,"line":3473},[3144,12050,12051],{"class":7651},"    app",[3144,12053,3289],{"class":5663},[3144,12055,12056],{"class":7663},"UseSwagger",[3144,12058,7700],{"class":5663},[3144,12060,12061,12063,12065,12068],{"class":3142,"line":3478},[3144,12062,12051],{"class":7651},[3144,12064,3289],{"class":5663},[3144,12066,12067],{"class":7663},"UseSwaggerUI",[3144,12069,7700],{"class":5663},[3144,12071,12072],{"class":3142,"line":3483},[3144,12073,3441],{"class":5663},[3144,12075,12076],{"class":3142,"line":3489},[3144,12077,3411],{"emptyLinePlaceholder":3410},[3144,12079,12080,12082,12084,12087,12089,12091,12094,12097,12099,12102,12104,12106,12109,12111,12113,12116,12118,12121,12123,12126],{"class":3142,"line":3495},[3144,12081,7736],{"class":7651},[3144,12083,3289],{"class":5663},[3144,12085,12086],{"class":7663},"MapGet",[3144,12088,7667],{"class":5663},[3144,12090,7746],{"class":7139},[3144,12092,12093],{"class":5663},", () => ",[3144,12095,12096],{"class":7651},"Results",[3144,12098,3289],{"class":5663},[3144,12100,12101],{"class":7663},"Ok",[3144,12103,7667],{"class":5663},[3144,12105,7957],{"class":7647},[3144,12107,12108],{"class":5663}," { ",[3144,12110,7107],{"class":7651},[3144,12112,7655],{"class":5663},[3144,12114,12115],{"class":7139},"\"healthy\"",[3144,12117,6241],{"class":5663},[3144,12119,12120],{"class":7651},"version",[3144,12122,7655],{"class":5663},[3144,12124,12125],{"class":7139},"\"1.0.0\"",[3144,12127,12128],{"class":5663}," }));\n",[3144,12130,12131],{"class":3142,"line":3501},[3144,12132,3411],{"emptyLinePlaceholder":3410},[3144,12134,12135,12137,12139,12141,12143,12146,12148,12150,12152,12154,12156,12158,12160,12163,12165,12167,12169,12172,12174,12177,12179,12181,12183,12186],{"class":3142,"line":3506},[3144,12136,7736],{"class":7651},[3144,12138,3289],{"class":5663},[3144,12140,12086],{"class":7663},[3144,12142,7667],{"class":5663},[3144,12144,12145],{"class":7139},"\"/todos\"",[3144,12147,12093],{"class":5663},[3144,12149,12096],{"class":7651},[3144,12151,3289],{"class":5663},[3144,12153,12101],{"class":7663},[3144,12155,7667],{"class":5663},[3144,12157,7957],{"class":7647},[3144,12159,12108],{"class":5663},[3144,12161,12162],{"class":7651},"todos",[3144,12164,7655],{"class":5663},[3144,12166,12162],{"class":7651},[3144,12168,3289],{"class":5663},[3144,12170,12171],{"class":7651},"Values",[3144,12173,6241],{"class":5663},[3144,12175,12176],{"class":7651},"count",[3144,12178,7655],{"class":5663},[3144,12180,12162],{"class":7651},[3144,12182,3289],{"class":5663},[3144,12184,12185],{"class":7651},"Count",[3144,12187,12128],{"class":5663},[3144,12189,12190],{"class":3142,"line":3512},[3144,12191,3411],{"emptyLinePlaceholder":3410},[3144,12193,12194,12196,12198,12200,12202,12205,12208,12210,12213],{"class":3142,"line":3518},[3144,12195,7736],{"class":7651},[3144,12197,3289],{"class":5663},[3144,12199,12086],{"class":7663},[3144,12201,7667],{"class":5663},[3144,12203,12204],{"class":7139},"\"/todos/{id:int}\"",[3144,12206,12207],{"class":5663},", (",[3144,12209,11975],{"class":7647},[3144,12211,12212],{"class":7651}," id",[3144,12214,8296],{"class":5663},[3144,12216,12217,12220,12222,12225,12227,12230,12232,12235,12238,12241,12244,12246,12248,12250,12252,12255,12258,12260,12262,12265],{"class":3142,"line":3524},[3144,12218,12219],{"class":7651},"    todos",[3144,12221,3289],{"class":5663},[3144,12223,12224],{"class":7663},"TryGetValue",[3144,12226,7667],{"class":5663},[3144,12228,12229],{"class":7651},"id",[3144,12231,6241],{"class":5663},[3144,12233,12234],{"class":7647},"out",[3144,12236,12237],{"class":7647}," var",[3144,12239,12240],{"class":7651}," todo",[3144,12242,12243],{"class":5663},") ? ",[3144,12245,12096],{"class":7651},[3144,12247,3289],{"class":5663},[3144,12249,12101],{"class":7663},[3144,12251,7667],{"class":5663},[3144,12253,12254],{"class":7651},"todo",[3144,12256,12257],{"class":5663},") : ",[3144,12259,12096],{"class":7651},[3144,12261,3289],{"class":5663},[3144,12263,12264],{"class":7663},"NotFound",[3144,12266,12267],{"class":5663},"());\n",[3144,12269,12270],{"class":3142,"line":3530},[3144,12271,3411],{"emptyLinePlaceholder":3410},[3144,12273,12274,12276,12278,12281,12283,12285,12287,12290,12293],{"class":3142,"line":3535},[3144,12275,7736],{"class":7651},[3144,12277,3289],{"class":5663},[3144,12279,12280],{"class":7663},"MapPost",[3144,12282,7667],{"class":5663},[3144,12284,12145],{"class":7139},[3144,12286,12207],{"class":5663},[3144,12288,12289],{"class":7812},"CreateTodoRequest",[3144,12291,12292],{"class":7651}," request",[3144,12294,8296],{"class":5663},[3144,12296,12297],{"class":3142,"line":3705},[3144,12298,8134],{"class":5663},[3144,12300,12301,12304,12306,12308,12311,12313,12316,12318,12321,12323],{"class":3142,"line":3711},[3144,12302,12303],{"class":7647},"    var",[3144,12305,12212],{"class":7651},[3144,12307,7655],{"class":5663},[3144,12309,12310],{"class":7651},"Interlocked",[3144,12312,3289],{"class":5663},[3144,12314,12315],{"class":7663},"Increment",[3144,12317,7667],{"class":5663},[3144,12319,12320],{"class":7647},"ref",[3144,12322,11990],{"class":7651},[3144,12324,7673],{"class":5663},[3144,12326,12327,12329,12331,12333,12335],{"class":3142,"line":3717},[3144,12328,12303],{"class":7647},[3144,12330,12240],{"class":7651},[3144,12332,7655],{"class":5663},[3144,12334,7957],{"class":7647},[3144,12336,12337],{"class":7812}," Todo\n",[3144,12339,12340],{"class":3142,"line":3722},[3144,12341,8301],{"class":5663},[3144,12343,12344,12347,12349,12351],{"class":3142,"line":3727},[3144,12345,12346],{"class":7651},"        Id",[3144,12348,7655],{"class":5663},[3144,12350,12229],{"class":7651},[3144,12352,7930],{"class":5663},[3144,12354,12355,12358,12360,12363,12365,12368],{"class":3142,"line":4084},[3144,12356,12357],{"class":7651},"        Title",[3144,12359,7655],{"class":5663},[3144,12361,12362],{"class":7651},"request",[3144,12364,3289],{"class":5663},[3144,12366,12367],{"class":7651},"Title",[3144,12369,7930],{"class":5663},[3144,12371,12372,12375,12377,12379],{"class":3142,"line":4090},[3144,12373,12374],{"class":7651},"        IsCompleted",[3144,12376,7655],{"class":5663},[3144,12378,8150],{"class":7647},[3144,12380,7930],{"class":5663},[3144,12382,12383,12386,12388,12391,12393],{"class":3142,"line":4096},[3144,12384,12385],{"class":7651},"        CreatedAt",[3144,12387,7655],{"class":5663},[3144,12389,12390],{"class":7651},"DateTime",[3144,12392,3289],{"class":5663},[3144,12394,12395],{"class":7651},"UtcNow\n",[3144,12397,12398],{"class":3142,"line":4102},[3144,12399,12400],{"class":5663},"    };\n",[3144,12402,12403,12405,12408,12410,12413,12415],{"class":3142,"line":4107},[3144,12404,12219],{"class":7651},[3144,12406,12407],{"class":5663},"[",[3144,12409,12229],{"class":7651},[3144,12411,12412],{"class":5663},"] = ",[3144,12414,12254],{"class":7651},[3144,12416,7831],{"class":5663},[3144,12418,12419,12422,12425,12427,12430,12432,12435,12437,12439,12441,12444,12446,12448],{"class":3142,"line":4112},[3144,12420,12421],{"class":7808},"    return",[3144,12423,12424],{"class":7651}," Results",[3144,12426,3289],{"class":5663},[3144,12428,12429],{"class":7663},"Created",[3144,12431,7667],{"class":5663},[3144,12433,12434],{"class":7139},"$\"/todos/",[3144,12436,8810],{"class":8809},[3144,12438,12229],{"class":7651},[3144,12440,8825],{"class":8809},[3144,12442,12443],{"class":7139},"\"",[3144,12445,6241],{"class":5663},[3144,12447,12254],{"class":7651},[3144,12449,7673],{"class":5663},[3144,12451,12452],{"class":3142,"line":4118},[3144,12453,8158],{"class":5663},[3144,12455,12456],{"class":3142,"line":4124},[3144,12457,3411],{"emptyLinePlaceholder":3410},[3144,12459,12460,12462,12464,12467,12469,12471,12473,12475,12477,12479,12482,12484],{"class":3142,"line":4130},[3144,12461,7736],{"class":7651},[3144,12463,3289],{"class":5663},[3144,12465,12466],{"class":7663},"MapPut",[3144,12468,7667],{"class":5663},[3144,12470,12204],{"class":7139},[3144,12472,12207],{"class":5663},[3144,12474,11975],{"class":7647},[3144,12476,12212],{"class":7651},[3144,12478,6241],{"class":5663},[3144,12480,12481],{"class":7812},"UpdateTodoRequest",[3144,12483,12292],{"class":7651},[3144,12485,8296],{"class":5663},[3144,12487,12488],{"class":3142,"line":4135},[3144,12489,8134],{"class":5663},[3144,12491,12492,12495,12498,12500,12502,12504,12506,12508,12510,12512,12514,12516],{"class":3142,"line":4140},[3144,12493,12494],{"class":7808},"    if",[3144,12496,12497],{"class":5663}," (!",[3144,12499,12162],{"class":7651},[3144,12501,3289],{"class":5663},[3144,12503,12224],{"class":7663},[3144,12505,7667],{"class":5663},[3144,12507,12229],{"class":7651},[3144,12509,6241],{"class":5663},[3144,12511,12234],{"class":7647},[3144,12513,12237],{"class":7647},[3144,12515,12240],{"class":7651},[3144,12517,12518],{"class":5663},"))\n",[3144,12520,12521,12523,12525,12527,12529],{"class":3142,"line":5169},[3144,12522,8774],{"class":7808},[3144,12524,12424],{"class":7651},[3144,12526,3289],{"class":5663},[3144,12528,12264],{"class":7663},[3144,12530,7700],{"class":5663},[3144,12532,12533],{"class":3142,"line":5174},[3144,12534,3802],{"class":5663},[3144,12536,12537,12540,12542,12544,12546,12548,12550,12552,12555,12557,12559,12561],{"class":3142,"line":5179},[3144,12538,12539],{"class":7651},"    todo",[3144,12541,3289],{"class":5663},[3144,12543,12367],{"class":7651},[3144,12545,7655],{"class":5663},[3144,12547,12362],{"class":7651},[3144,12549,3289],{"class":5663},[3144,12551,12367],{"class":7651},[3144,12553,12554],{"class":5663}," ?? ",[3144,12556,12254],{"class":7651},[3144,12558,3289],{"class":5663},[3144,12560,12367],{"class":7651},[3144,12562,7831],{"class":5663},[3144,12564,12565,12567,12569,12572,12574,12576,12578,12580,12582,12584,12586,12588],{"class":3142,"line":5185},[3144,12566,12539],{"class":7651},[3144,12568,3289],{"class":5663},[3144,12570,12571],{"class":7651},"IsCompleted",[3144,12573,7655],{"class":5663},[3144,12575,12362],{"class":7651},[3144,12577,3289],{"class":5663},[3144,12579,12571],{"class":7651},[3144,12581,12554],{"class":5663},[3144,12583,12254],{"class":7651},[3144,12585,3289],{"class":5663},[3144,12587,12571],{"class":7651},[3144,12589,7831],{"class":5663},[3144,12591,12592,12594,12596,12598,12600,12602,12604],{"class":3142,"line":5190},[3144,12593,12421],{"class":7808},[3144,12595,12424],{"class":7651},[3144,12597,3289],{"class":5663},[3144,12599,12101],{"class":7663},[3144,12601,7667],{"class":5663},[3144,12603,12254],{"class":7651},[3144,12605,7673],{"class":5663},[3144,12607,12608],{"class":3142,"line":5196},[3144,12609,8158],{"class":5663},[3144,12611,12612],{"class":3142,"line":5202},[3144,12613,3411],{"emptyLinePlaceholder":3410},[3144,12615,12616,12618,12620,12623,12625,12627,12629,12631,12633],{"class":3142,"line":5207},[3144,12617,7736],{"class":7651},[3144,12619,3289],{"class":5663},[3144,12621,12622],{"class":7663},"MapDelete",[3144,12624,7667],{"class":5663},[3144,12626,12204],{"class":7139},[3144,12628,12207],{"class":5663},[3144,12630,11975],{"class":7647},[3144,12632,12212],{"class":7651},[3144,12634,8296],{"class":5663},[3144,12636,12637,12639,12641,12644,12646,12648,12650,12652,12654,12656,12658,12660,12662,12664,12666,12668,12670,12672,12674,12676],{"class":3142,"line":5212},[3144,12638,12219],{"class":7651},[3144,12640,3289],{"class":5663},[3144,12642,12643],{"class":7663},"TryRemove",[3144,12645,7667],{"class":5663},[3144,12647,12229],{"class":7651},[3144,12649,6241],{"class":5663},[3144,12651,12234],{"class":7647},[3144,12653,12237],{"class":7647},[3144,12655,12240],{"class":7651},[3144,12657,12243],{"class":5663},[3144,12659,12096],{"class":7651},[3144,12661,3289],{"class":5663},[3144,12663,12101],{"class":7663},[3144,12665,7667],{"class":5663},[3144,12667,12254],{"class":7651},[3144,12669,12257],{"class":5663},[3144,12671,12096],{"class":7651},[3144,12673,3289],{"class":5663},[3144,12675,12264],{"class":7663},[3144,12677,12267],{"class":5663},[3144,12679,12680],{"class":3142,"line":5218},[3144,12681,3411],{"emptyLinePlaceholder":3410},[3144,12683,12684,12686,12688,12690],{"class":3142,"line":5223},[3144,12685,7736],{"class":7651},[3144,12687,3289],{"class":5663},[3144,12689,7761],{"class":7663},[3144,12691,7700],{"class":5663},[3144,12693,12694],{"class":3142,"line":5228},[3144,12695,3411],{"emptyLinePlaceholder":3410},[3144,12697,12698,12701],{"class":3142,"line":5234},[3144,12699,12700],{"class":7647},"record",[3144,12702,12337],{"class":7812},[3144,12704,12705],{"class":3142,"line":5240},[3144,12706,8134],{"class":5663},[3144,12708,12709,12711,12714,12717,12719,12722,12724,12727],{"class":3142,"line":5246},[3144,12710,8613],{"class":7647},[3144,12712,12713],{"class":7647}," int",[3144,12715,12716],{"class":7651}," Id",[3144,12718,12108],{"class":5663},[3144,12720,12721],{"class":7647},"get",[3144,12723,8709],{"class":5663},[3144,12725,12726],{"class":7647},"set",[3144,12728,12729],{"class":5663},"; }\n",[3144,12731,12732,12734,12737,12740,12743,12745,12747,12749,12751],{"class":3142,"line":5251},[3144,12733,8613],{"class":7647},[3144,12735,12736],{"class":7647}," required",[3144,12738,12739],{"class":7647}," string",[3144,12741,12742],{"class":7651}," Title",[3144,12744,12108],{"class":5663},[3144,12746,12721],{"class":7647},[3144,12748,8709],{"class":5663},[3144,12750,12726],{"class":7647},[3144,12752,12729],{"class":5663},[3144,12754,12755,12757,12760,12763,12765,12767,12769,12771],{"class":3142,"line":5256},[3144,12756,8613],{"class":7647},[3144,12758,12759],{"class":7647}," bool",[3144,12761,12762],{"class":7651}," IsCompleted",[3144,12764,12108],{"class":5663},[3144,12766,12721],{"class":7647},[3144,12768,8709],{"class":5663},[3144,12770,12726],{"class":7647},[3144,12772,12729],{"class":5663},[3144,12774,12775,12777,12780,12783,12785,12787,12789,12791],{"class":3142,"line":5261},[3144,12776,8613],{"class":7647},[3144,12778,12779],{"class":7812}," DateTime",[3144,12781,12782],{"class":7651}," CreatedAt",[3144,12784,12108],{"class":5663},[3144,12786,12721],{"class":7647},[3144,12788,8709],{"class":5663},[3144,12790,12726],{"class":7647},[3144,12792,12729],{"class":5663},[3144,12794,12795],{"class":3142,"line":5266},[3144,12796,3441],{"class":5663},[3144,12798,12799],{"class":3142,"line":5272},[3144,12800,3411],{"emptyLinePlaceholder":3410},[3144,12802,12803,12805,12808,12810,12813,12815],{"class":3142,"line":5277},[3144,12804,12700],{"class":7647},[3144,12806,12807],{"class":7812}," CreateTodoRequest",[3144,12809,7667],{"class":5663},[3144,12811,12812],{"class":7647},"string",[3144,12814,12742],{"class":7651},[3144,12816,7673],{"class":5663},[3144,12818,12819,12821,12824,12826,12828,12831,12833,12835,12838,12840,12842],{"class":3142,"line":5282},[3144,12820,12700],{"class":7647},[3144,12822,12823],{"class":7812}," UpdateTodoRequest",[3144,12825,7667],{"class":5663},[3144,12827,12812],{"class":7647},[3144,12829,12830],{"class":5663},"? ",[3144,12832,12367],{"class":7651},[3144,12834,6241],{"class":5663},[3144,12836,12837],{"class":7647},"bool",[3144,12839,12830],{"class":5663},[3144,12841,12571],{"class":7651},[3144,12843,7673],{"class":5663},[3122,12845,12847],{"id":12846},"версія-20-додавання-статистики","Версія 2.0: Додавання статистики",[3114,12849,12850,12851,12854],{},"Тепер додамо новий endpoint ",[3349,12852,12853],{},"/stats"," для статистики:",[3114,12856,12857],{},[3118,12858,12859],{},"Program.cs (v2.0):",[3379,12861,12863],{"className":7638,"code":12862,"language":7640,"meta":3384,"style":3384},"using System.Collections.Concurrent;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEndpointsApiExplorer();\nbuilder.Services.AddSwaggerGen();\n\nvar todos = new ConcurrentDictionary\u003Cint, Todo>();\nvar nextId = 1;\n\nvar app = builder.Build();\n\nif (app.Environment.IsDevelopment())\n{\n    app.UseSwagger();\n    app.UseSwaggerUI();\n}\n\n// Оновлена версія у health check\napp.MapGet(\"/health\", () => Results.Ok(new { status = \"healthy\", version = \"2.0.0\" }));\n\napp.MapGet(\"/todos\", () => Results.Ok(new { todos = todos.Values, count = todos.Count }));\n\napp.MapGet(\"/todos/{id:int}\", (int id) =>\n    todos.TryGetValue(id, out var todo) ? Results.Ok(todo) : Results.NotFound());\n\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    todos[id] = todo;\n    return Results.Created($\"/todos/{id}\", todo);\n});\n\napp.MapPut(\"/todos/{id:int}\", (int id, UpdateTodoRequest request) =>\n{\n    if (!todos.TryGetValue(id, out var todo))\n        return Results.NotFound();\n    \n    todo.Title = request.Title ?? todo.Title;\n    todo.IsCompleted = request.IsCompleted ?? todo.IsCompleted;\n    return Results.Ok(todo);\n});\n\napp.MapDelete(\"/todos/{id:int}\", (int id) =>\n    todos.TryRemove(id, out var todo) ? Results.Ok(todo) : Results.NotFound());\n\n// НОВИЙ ENDPOINT: Статистика\napp.MapGet(\"/stats\", () =>\n{\n    var allTodos = todos.Values.ToList();\n    var completed = allTodos.Count(t => t.IsCompleted);\n    var pending = allTodos.Count - completed;\n    var oldestTodo = allTodos.MinBy(t => t.CreatedAt);\n    var newestTodo = allTodos.MaxBy(t => t.CreatedAt);\n    \n    return Results.Ok(new\n    {\n        total = allTodos.Count,\n        completed,\n        pending,\n        completionRate = allTodos.Count > 0 ? (double)completed / allTodos.Count * 100 : 0,\n        oldestTodo = oldestTodo?.CreatedAt,\n        newestTodo = newestTodo?.CreatedAt\n    });\n})\n.WithName(\"GetStatistics\")\n.WithOpenApi();\n\napp.Run();\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",[3349,12864,12865,12881,12885,12905,12909,12923,12937,12941,12963,12975,12979,12995,12999,13017,13021,13031,13041,13045,13049,13054,13097,13101,13151,13155,13175,13217,13221,13241,13245,13267,13279,13283,13293,13307,13317,13329,13333,13347,13375,13379,13383,13409,13413,13439,13451,13455,13481,13507,13523,13527,13531,13551,13593,13597,13602,13618,13622,13644,13675,13698,13729,13759,13763,13777,13781,13796,13803,13810,13858,13875,13890,13895,13900,13914,13923,13927,13937,13941,13947,13951,13969,13989,14007,14025,14029,14033,14047],{"__ignoreMap":3384},[3144,12866,12867,12869,12871,12873,12875,12877,12879],{"class":3142,"line":3389},[3144,12868,7809],{"class":7808},[3144,12870,11882],{"class":7812},[3144,12872,3289],{"class":5663},[3144,12874,11887],{"class":7812},[3144,12876,3289],{"class":5663},[3144,12878,11892],{"class":7812},[3144,12880,7831],{"class":5663},[3144,12882,12883],{"class":3142,"line":3395},[3144,12884,3411],{"emptyLinePlaceholder":3410},[3144,12886,12887,12889,12891,12893,12895,12897,12899,12901,12903],{"class":3142,"line":3401},[3144,12888,7648],{"class":7647},[3144,12890,7652],{"class":7651},[3144,12892,7655],{"class":5663},[3144,12894,7658],{"class":7651},[3144,12896,3289],{"class":5663},[3144,12898,7664],{"class":7663},[3144,12900,7667],{"class":5663},[3144,12902,7670],{"class":7651},[3144,12904,7673],{"class":5663},[3144,12906,12907],{"class":3142,"line":3407},[3144,12908,3411],{"emptyLinePlaceholder":3410},[3144,12910,12911,12913,12915,12917,12919,12921],{"class":3142,"line":3414},[3144,12912,7687],{"class":7651},[3144,12914,3289],{"class":5663},[3144,12916,7692],{"class":7651},[3144,12918,3289],{"class":5663},[3144,12920,11935],{"class":7663},[3144,12922,7700],{"class":5663},[3144,12924,12925,12927,12929,12931,12933,12935],{"class":3142,"line":3420},[3144,12926,7687],{"class":7651},[3144,12928,3289],{"class":5663},[3144,12930,7692],{"class":7651},[3144,12932,3289],{"class":5663},[3144,12934,11950],{"class":7663},[3144,12936,7700],{"class":5663},[3144,12938,12939],{"class":3142,"line":3426},[3144,12940,3411],{"emptyLinePlaceholder":3410},[3144,12942,12943,12945,12947,12949,12951,12953,12955,12957,12959,12961],{"class":3142,"line":3432},[3144,12944,7648],{"class":7647},[3144,12946,11963],{"class":7651},[3144,12948,7655],{"class":5663},[3144,12950,7957],{"class":7647},[3144,12952,11970],{"class":7812},[3144,12954,8069],{"class":5663},[3144,12956,11975],{"class":7647},[3144,12958,6241],{"class":5663},[3144,12960,11980],{"class":7812},[3144,12962,11983],{"class":5663},[3144,12964,12965,12967,12969,12971,12973],{"class":3142,"line":3438},[3144,12966,7648],{"class":7647},[3144,12968,11990],{"class":7651},[3144,12970,7655],{"class":5663},[3144,12972,6240],{"class":5700},[3144,12974,7831],{"class":5663},[3144,12976,12977],{"class":3142,"line":3444},[3144,12978,3411],{"emptyLinePlaceholder":3410},[3144,12980,12981,12983,12985,12987,12989,12991,12993],{"class":3142,"line":3449},[3144,12982,7648],{"class":7647},[3144,12984,7711],{"class":7651},[3144,12986,7655],{"class":5663},[3144,12988,7687],{"class":7651},[3144,12990,3289],{"class":5663},[3144,12992,7720],{"class":7663},[3144,12994,7700],{"class":5663},[3144,12996,12997],{"class":3142,"line":3455},[3144,12998,3411],{"emptyLinePlaceholder":3410},[3144,13000,13001,13003,13005,13007,13009,13011,13013,13015],{"class":3142,"line":3461},[3144,13002,12025],{"class":7808},[3144,13004,6248],{"class":5663},[3144,13006,7736],{"class":7651},[3144,13008,3289],{"class":5663},[3144,13010,12034],{"class":7651},[3144,13012,3289],{"class":5663},[3144,13014,12039],{"class":7663},[3144,13016,12042],{"class":5663},[3144,13018,13019],{"class":3142,"line":3467},[3144,13020,8134],{"class":5663},[3144,13022,13023,13025,13027,13029],{"class":3142,"line":3473},[3144,13024,12051],{"class":7651},[3144,13026,3289],{"class":5663},[3144,13028,12056],{"class":7663},[3144,13030,7700],{"class":5663},[3144,13032,13033,13035,13037,13039],{"class":3142,"line":3478},[3144,13034,12051],{"class":7651},[3144,13036,3289],{"class":5663},[3144,13038,12067],{"class":7663},[3144,13040,7700],{"class":5663},[3144,13042,13043],{"class":3142,"line":3483},[3144,13044,3441],{"class":5663},[3144,13046,13047],{"class":3142,"line":3489},[3144,13048,3411],{"emptyLinePlaceholder":3410},[3144,13050,13051],{"class":3142,"line":3495},[3144,13052,13053],{"class":7200},"// Оновлена версія у health check\n",[3144,13055,13056,13058,13060,13062,13064,13066,13068,13070,13072,13074,13076,13078,13080,13082,13084,13086,13088,13090,13092,13095],{"class":3142,"line":3501},[3144,13057,7736],{"class":7651},[3144,13059,3289],{"class":5663},[3144,13061,12086],{"class":7663},[3144,13063,7667],{"class":5663},[3144,13065,7746],{"class":7139},[3144,13067,12093],{"class":5663},[3144,13069,12096],{"class":7651},[3144,13071,3289],{"class":5663},[3144,13073,12101],{"class":7663},[3144,13075,7667],{"class":5663},[3144,13077,7957],{"class":7647},[3144,13079,12108],{"class":5663},[3144,13081,7107],{"class":7651},[3144,13083,7655],{"class":5663},[3144,13085,12115],{"class":7139},[3144,13087,6241],{"class":5663},[3144,13089,12120],{"class":7651},[3144,13091,7655],{"class":5663},[3144,13093,13094],{"class":7139},"\"2.0.0\"",[3144,13096,12128],{"class":5663},[3144,13098,13099],{"class":3142,"line":3506},[3144,13100,3411],{"emptyLinePlaceholder":3410},[3144,13102,13103,13105,13107,13109,13111,13113,13115,13117,13119,13121,13123,13125,13127,13129,13131,13133,13135,13137,13139,13141,13143,13145,13147,13149],{"class":3142,"line":3512},[3144,13104,7736],{"class":7651},[3144,13106,3289],{"class":5663},[3144,13108,12086],{"class":7663},[3144,13110,7667],{"class":5663},[3144,13112,12145],{"class":7139},[3144,13114,12093],{"class":5663},[3144,13116,12096],{"class":7651},[3144,13118,3289],{"class":5663},[3144,13120,12101],{"class":7663},[3144,13122,7667],{"class":5663},[3144,13124,7957],{"class":7647},[3144,13126,12108],{"class":5663},[3144,13128,12162],{"class":7651},[3144,13130,7655],{"class":5663},[3144,13132,12162],{"class":7651},[3144,13134,3289],{"class":5663},[3144,13136,12171],{"class":7651},[3144,13138,6241],{"class":5663},[3144,13140,12176],{"class":7651},[3144,13142,7655],{"class":5663},[3144,13144,12162],{"class":7651},[3144,13146,3289],{"class":5663},[3144,13148,12185],{"class":7651},[3144,13150,12128],{"class":5663},[3144,13152,13153],{"class":3142,"line":3518},[3144,13154,3411],{"emptyLinePlaceholder":3410},[3144,13156,13157,13159,13161,13163,13165,13167,13169,13171,13173],{"class":3142,"line":3524},[3144,13158,7736],{"class":7651},[3144,13160,3289],{"class":5663},[3144,13162,12086],{"class":7663},[3144,13164,7667],{"class":5663},[3144,13166,12204],{"class":7139},[3144,13168,12207],{"class":5663},[3144,13170,11975],{"class":7647},[3144,13172,12212],{"class":7651},[3144,13174,8296],{"class":5663},[3144,13176,13177,13179,13181,13183,13185,13187,13189,13191,13193,13195,13197,13199,13201,13203,13205,13207,13209,13211,13213,13215],{"class":3142,"line":3530},[3144,13178,12219],{"class":7651},[3144,13180,3289],{"class":5663},[3144,13182,12224],{"class":7663},[3144,13184,7667],{"class":5663},[3144,13186,12229],{"class":7651},[3144,13188,6241],{"class":5663},[3144,13190,12234],{"class":7647},[3144,13192,12237],{"class":7647},[3144,13194,12240],{"class":7651},[3144,13196,12243],{"class":5663},[3144,13198,12096],{"class":7651},[3144,13200,3289],{"class":5663},[3144,13202,12101],{"class":7663},[3144,13204,7667],{"class":5663},[3144,13206,12254],{"class":7651},[3144,13208,12257],{"class":5663},[3144,13210,12096],{"class":7651},[3144,13212,3289],{"class":5663},[3144,13214,12264],{"class":7663},[3144,13216,12267],{"class":5663},[3144,13218,13219],{"class":3142,"line":3535},[3144,13220,3411],{"emptyLinePlaceholder":3410},[3144,13222,13223,13225,13227,13229,13231,13233,13235,13237,13239],{"class":3142,"line":3705},[3144,13224,7736],{"class":7651},[3144,13226,3289],{"class":5663},[3144,13228,12280],{"class":7663},[3144,13230,7667],{"class":5663},[3144,13232,12145],{"class":7139},[3144,13234,12207],{"class":5663},[3144,13236,12289],{"class":7812},[3144,13238,12292],{"class":7651},[3144,13240,8296],{"class":5663},[3144,13242,13243],{"class":3142,"line":3711},[3144,13244,8134],{"class":5663},[3144,13246,13247,13249,13251,13253,13255,13257,13259,13261,13263,13265],{"class":3142,"line":3717},[3144,13248,12303],{"class":7647},[3144,13250,12212],{"class":7651},[3144,13252,7655],{"class":5663},[3144,13254,12310],{"class":7651},[3144,13256,3289],{"class":5663},[3144,13258,12315],{"class":7663},[3144,13260,7667],{"class":5663},[3144,13262,12320],{"class":7647},[3144,13264,11990],{"class":7651},[3144,13266,7673],{"class":5663},[3144,13268,13269,13271,13273,13275,13277],{"class":3142,"line":3722},[3144,13270,12303],{"class":7647},[3144,13272,12240],{"class":7651},[3144,13274,7655],{"class":5663},[3144,13276,7957],{"class":7647},[3144,13278,12337],{"class":7812},[3144,13280,13281],{"class":3142,"line":3727},[3144,13282,8301],{"class":5663},[3144,13284,13285,13287,13289,13291],{"class":3142,"line":4084},[3144,13286,12346],{"class":7651},[3144,13288,7655],{"class":5663},[3144,13290,12229],{"class":7651},[3144,13292,7930],{"class":5663},[3144,13294,13295,13297,13299,13301,13303,13305],{"class":3142,"line":4090},[3144,13296,12357],{"class":7651},[3144,13298,7655],{"class":5663},[3144,13300,12362],{"class":7651},[3144,13302,3289],{"class":5663},[3144,13304,12367],{"class":7651},[3144,13306,7930],{"class":5663},[3144,13308,13309,13311,13313,13315],{"class":3142,"line":4096},[3144,13310,12374],{"class":7651},[3144,13312,7655],{"class":5663},[3144,13314,8150],{"class":7647},[3144,13316,7930],{"class":5663},[3144,13318,13319,13321,13323,13325,13327],{"class":3142,"line":4102},[3144,13320,12385],{"class":7651},[3144,13322,7655],{"class":5663},[3144,13324,12390],{"class":7651},[3144,13326,3289],{"class":5663},[3144,13328,12395],{"class":7651},[3144,13330,13331],{"class":3142,"line":4107},[3144,13332,12400],{"class":5663},[3144,13334,13335,13337,13339,13341,13343,13345],{"class":3142,"line":4112},[3144,13336,12219],{"class":7651},[3144,13338,12407],{"class":5663},[3144,13340,12229],{"class":7651},[3144,13342,12412],{"class":5663},[3144,13344,12254],{"class":7651},[3144,13346,7831],{"class":5663},[3144,13348,13349,13351,13353,13355,13357,13359,13361,13363,13365,13367,13369,13371,13373],{"class":3142,"line":4118},[3144,13350,12421],{"class":7808},[3144,13352,12424],{"class":7651},[3144,13354,3289],{"class":5663},[3144,13356,12429],{"class":7663},[3144,13358,7667],{"class":5663},[3144,13360,12434],{"class":7139},[3144,13362,8810],{"class":8809},[3144,13364,12229],{"class":7651},[3144,13366,8825],{"class":8809},[3144,13368,12443],{"class":7139},[3144,13370,6241],{"class":5663},[3144,13372,12254],{"class":7651},[3144,13374,7673],{"class":5663},[3144,13376,13377],{"class":3142,"line":4124},[3144,13378,8158],{"class":5663},[3144,13380,13381],{"class":3142,"line":4130},[3144,13382,3411],{"emptyLinePlaceholder":3410},[3144,13384,13385,13387,13389,13391,13393,13395,13397,13399,13401,13403,13405,13407],{"class":3142,"line":4135},[3144,13386,7736],{"class":7651},[3144,13388,3289],{"class":5663},[3144,13390,12466],{"class":7663},[3144,13392,7667],{"class":5663},[3144,13394,12204],{"class":7139},[3144,13396,12207],{"class":5663},[3144,13398,11975],{"class":7647},[3144,13400,12212],{"class":7651},[3144,13402,6241],{"class":5663},[3144,13404,12481],{"class":7812},[3144,13406,12292],{"class":7651},[3144,13408,8296],{"class":5663},[3144,13410,13411],{"class":3142,"line":4140},[3144,13412,8134],{"class":5663},[3144,13414,13415,13417,13419,13421,13423,13425,13427,13429,13431,13433,13435,13437],{"class":3142,"line":5169},[3144,13416,12494],{"class":7808},[3144,13418,12497],{"class":5663},[3144,13420,12162],{"class":7651},[3144,13422,3289],{"class":5663},[3144,13424,12224],{"class":7663},[3144,13426,7667],{"class":5663},[3144,13428,12229],{"class":7651},[3144,13430,6241],{"class":5663},[3144,13432,12234],{"class":7647},[3144,13434,12237],{"class":7647},[3144,13436,12240],{"class":7651},[3144,13438,12518],{"class":5663},[3144,13440,13441,13443,13445,13447,13449],{"class":3142,"line":5174},[3144,13442,8774],{"class":7808},[3144,13444,12424],{"class":7651},[3144,13446,3289],{"class":5663},[3144,13448,12264],{"class":7663},[3144,13450,7700],{"class":5663},[3144,13452,13453],{"class":3142,"line":5179},[3144,13454,3802],{"class":5663},[3144,13456,13457,13459,13461,13463,13465,13467,13469,13471,13473,13475,13477,13479],{"class":3142,"line":5185},[3144,13458,12539],{"class":7651},[3144,13460,3289],{"class":5663},[3144,13462,12367],{"class":7651},[3144,13464,7655],{"class":5663},[3144,13466,12362],{"class":7651},[3144,13468,3289],{"class":5663},[3144,13470,12367],{"class":7651},[3144,13472,12554],{"class":5663},[3144,13474,12254],{"class":7651},[3144,13476,3289],{"class":5663},[3144,13478,12367],{"class":7651},[3144,13480,7831],{"class":5663},[3144,13482,13483,13485,13487,13489,13491,13493,13495,13497,13499,13501,13503,13505],{"class":3142,"line":5190},[3144,13484,12539],{"class":7651},[3144,13486,3289],{"class":5663},[3144,13488,12571],{"class":7651},[3144,13490,7655],{"class":5663},[3144,13492,12362],{"class":7651},[3144,13494,3289],{"class":5663},[3144,13496,12571],{"class":7651},[3144,13498,12554],{"class":5663},[3144,13500,12254],{"class":7651},[3144,13502,3289],{"class":5663},[3144,13504,12571],{"class":7651},[3144,13506,7831],{"class":5663},[3144,13508,13509,13511,13513,13515,13517,13519,13521],{"class":3142,"line":5196},[3144,13510,12421],{"class":7808},[3144,13512,12424],{"class":7651},[3144,13514,3289],{"class":5663},[3144,13516,12101],{"class":7663},[3144,13518,7667],{"class":5663},[3144,13520,12254],{"class":7651},[3144,13522,7673],{"class":5663},[3144,13524,13525],{"class":3142,"line":5202},[3144,13526,8158],{"class":5663},[3144,13528,13529],{"class":3142,"line":5207},[3144,13530,3411],{"emptyLinePlaceholder":3410},[3144,13532,13533,13535,13537,13539,13541,13543,13545,13547,13549],{"class":3142,"line":5212},[3144,13534,7736],{"class":7651},[3144,13536,3289],{"class":5663},[3144,13538,12622],{"class":7663},[3144,13540,7667],{"class":5663},[3144,13542,12204],{"class":7139},[3144,13544,12207],{"class":5663},[3144,13546,11975],{"class":7647},[3144,13548,12212],{"class":7651},[3144,13550,8296],{"class":5663},[3144,13552,13553,13555,13557,13559,13561,13563,13565,13567,13569,13571,13573,13575,13577,13579,13581,13583,13585,13587,13589,13591],{"class":3142,"line":5218},[3144,13554,12219],{"class":7651},[3144,13556,3289],{"class":5663},[3144,13558,12643],{"class":7663},[3144,13560,7667],{"class":5663},[3144,13562,12229],{"class":7651},[3144,13564,6241],{"class":5663},[3144,13566,12234],{"class":7647},[3144,13568,12237],{"class":7647},[3144,13570,12240],{"class":7651},[3144,13572,12243],{"class":5663},[3144,13574,12096],{"class":7651},[3144,13576,3289],{"class":5663},[3144,13578,12101],{"class":7663},[3144,13580,7667],{"class":5663},[3144,13582,12254],{"class":7651},[3144,13584,12257],{"class":5663},[3144,13586,12096],{"class":7651},[3144,13588,3289],{"class":5663},[3144,13590,12264],{"class":7663},[3144,13592,12267],{"class":5663},[3144,13594,13595],{"class":3142,"line":5223},[3144,13596,3411],{"emptyLinePlaceholder":3410},[3144,13598,13599],{"class":3142,"line":5228},[3144,13600,13601],{"class":7200},"// НОВИЙ ENDPOINT: Статистика\n",[3144,13603,13604,13606,13608,13610,13612,13615],{"class":3142,"line":5234},[3144,13605,7736],{"class":7651},[3144,13607,3289],{"class":5663},[3144,13609,12086],{"class":7663},[3144,13611,7667],{"class":5663},[3144,13613,13614],{"class":7139},"\"/stats\"",[3144,13616,13617],{"class":5663},", () =>\n",[3144,13619,13620],{"class":3142,"line":5240},[3144,13621,8134],{"class":5663},[3144,13623,13624,13626,13629,13631,13633,13635,13637,13639,13642],{"class":3142,"line":5246},[3144,13625,12303],{"class":7647},[3144,13627,13628],{"class":7651}," allTodos",[3144,13630,7655],{"class":5663},[3144,13632,12162],{"class":7651},[3144,13634,3289],{"class":5663},[3144,13636,12171],{"class":7651},[3144,13638,3289],{"class":5663},[3144,13640,13641],{"class":7663},"ToList",[3144,13643,7700],{"class":5663},[3144,13645,13646,13648,13651,13653,13656,13658,13660,13662,13665,13667,13669,13671,13673],{"class":3142,"line":5251},[3144,13647,12303],{"class":7647},[3144,13649,13650],{"class":7651}," completed",[3144,13652,7655],{"class":5663},[3144,13654,13655],{"class":7651},"allTodos",[3144,13657,3289],{"class":5663},[3144,13659,12185],{"class":7663},[3144,13661,7667],{"class":5663},[3144,13663,13664],{"class":7651},"t",[3144,13666,8147],{"class":5663},[3144,13668,13664],{"class":7651},[3144,13670,3289],{"class":5663},[3144,13672,12571],{"class":7651},[3144,13674,7673],{"class":5663},[3144,13676,13677,13679,13682,13684,13686,13688,13690,13693,13696],{"class":3142,"line":5256},[3144,13678,12303],{"class":7647},[3144,13680,13681],{"class":7651}," pending",[3144,13683,7655],{"class":5663},[3144,13685,13655],{"class":7651},[3144,13687,3289],{"class":5663},[3144,13689,12185],{"class":7651},[3144,13691,13692],{"class":5663}," - ",[3144,13694,13695],{"class":7651},"completed",[3144,13697,7831],{"class":5663},[3144,13699,13700,13702,13705,13707,13709,13711,13714,13716,13718,13720,13722,13724,13727],{"class":3142,"line":5261},[3144,13701,12303],{"class":7647},[3144,13703,13704],{"class":7651}," oldestTodo",[3144,13706,7655],{"class":5663},[3144,13708,13655],{"class":7651},[3144,13710,3289],{"class":5663},[3144,13712,13713],{"class":7663},"MinBy",[3144,13715,7667],{"class":5663},[3144,13717,13664],{"class":7651},[3144,13719,8147],{"class":5663},[3144,13721,13664],{"class":7651},[3144,13723,3289],{"class":5663},[3144,13725,13726],{"class":7651},"CreatedAt",[3144,13728,7673],{"class":5663},[3144,13730,13731,13733,13736,13738,13740,13742,13745,13747,13749,13751,13753,13755,13757],{"class":3142,"line":5266},[3144,13732,12303],{"class":7647},[3144,13734,13735],{"class":7651}," newestTodo",[3144,13737,7655],{"class":5663},[3144,13739,13655],{"class":7651},[3144,13741,3289],{"class":5663},[3144,13743,13744],{"class":7663},"MaxBy",[3144,13746,7667],{"class":5663},[3144,13748,13664],{"class":7651},[3144,13750,8147],{"class":5663},[3144,13752,13664],{"class":7651},[3144,13754,3289],{"class":5663},[3144,13756,13726],{"class":7651},[3144,13758,7673],{"class":5663},[3144,13760,13761],{"class":3142,"line":5272},[3144,13762,3802],{"class":5663},[3144,13764,13765,13767,13769,13771,13773,13775],{"class":3142,"line":5277},[3144,13766,12421],{"class":7808},[3144,13768,12424],{"class":7651},[3144,13770,3289],{"class":5663},[3144,13772,12101],{"class":7663},[3144,13774,7667],{"class":5663},[3144,13776,8361],{"class":7647},[3144,13778,13779],{"class":3142,"line":5282},[3144,13780,8301],{"class":5663},[3144,13782,13783,13786,13788,13790,13792,13794],{"class":3142,"line":5287},[3144,13784,13785],{"class":7651},"        total",[3144,13787,7655],{"class":5663},[3144,13789,13655],{"class":7651},[3144,13791,3289],{"class":5663},[3144,13793,12185],{"class":7651},[3144,13795,7930],{"class":5663},[3144,13797,13798,13801],{"class":3142,"line":5292},[3144,13799,13800],{"class":7651},"        completed",[3144,13802,7930],{"class":5663},[3144,13804,13805,13808],{"class":3142,"line":5298},[3144,13806,13807],{"class":7651},"        pending",[3144,13809,7930],{"class":5663},[3144,13811,13812,13815,13817,13819,13821,13823,13826,13828,13831,13834,13836,13838,13841,13843,13845,13847,13849,13852,13854,13856],{"class":3142,"line":5303},[3144,13813,13814],{"class":7651},"        completionRate",[3144,13816,7655],{"class":5663},[3144,13818,13655],{"class":7651},[3144,13820,3289],{"class":5663},[3144,13822,12185],{"class":7651},[3144,13824,13825],{"class":5663}," > ",[3144,13827,6875],{"class":5700},[3144,13829,13830],{"class":5663}," ? (",[3144,13832,13833],{"class":7647},"double",[3144,13835,5602],{"class":5663},[3144,13837,13695],{"class":7651},[3144,13839,13840],{"class":5663}," / ",[3144,13842,13655],{"class":7651},[3144,13844,3289],{"class":5663},[3144,13846,12185],{"class":7651},[3144,13848,8700],{"class":5663},[3144,13850,13851],{"class":5700},"100",[3144,13853,8601],{"class":5663},[3144,13855,6875],{"class":5700},[3144,13857,7930],{"class":5663},[3144,13859,13860,13863,13865,13868,13871,13873],{"class":3142,"line":5308},[3144,13861,13862],{"class":7651},"        oldestTodo",[3144,13864,7655],{"class":5663},[3144,13866,13867],{"class":7651},"oldestTodo",[3144,13869,13870],{"class":5663},"?.",[3144,13872,13726],{"class":7651},[3144,13874,7930],{"class":5663},[3144,13876,13877,13880,13882,13885,13887],{"class":3142,"line":5314},[3144,13878,13879],{"class":7651},"        newestTodo",[3144,13881,7655],{"class":5663},[3144,13883,13884],{"class":7651},"newestTodo",[3144,13886,13870],{"class":5663},[3144,13888,13889],{"class":7651},"CreatedAt\n",[3144,13891,13892],{"class":3142,"line":5320},[3144,13893,13894],{"class":5663},"    });\n",[3144,13896,13897],{"class":3142,"line":5325},[3144,13898,13899],{"class":5663},"})\n",[3144,13901,13902,13904,13907,13909,13912],{"class":3142,"line":5330},[3144,13903,3289],{"class":5663},[3144,13905,13906],{"class":7663},"WithName",[3144,13908,7667],{"class":5663},[3144,13910,13911],{"class":7139},"\"GetStatistics\"",[3144,13913,8237],{"class":5663},[3144,13915,13916,13918,13921],{"class":3142,"line":5336},[3144,13917,3289],{"class":5663},[3144,13919,13920],{"class":7663},"WithOpenApi",[3144,13922,7700],{"class":5663},[3144,13924,13925],{"class":3142,"line":5341},[3144,13926,3411],{"emptyLinePlaceholder":3410},[3144,13928,13929,13931,13933,13935],{"class":3142,"line":5346},[3144,13930,7736],{"class":7651},[3144,13932,3289],{"class":5663},[3144,13934,7761],{"class":7663},[3144,13936,7700],{"class":5663},[3144,13938,13939],{"class":3142,"line":5352},[3144,13940,3411],{"emptyLinePlaceholder":3410},[3144,13942,13943,13945],{"class":3142,"line":5357},[3144,13944,12700],{"class":7647},[3144,13946,12337],{"class":7812},[3144,13948,13949],{"class":3142,"line":5362},[3144,13950,8134],{"class":5663},[3144,13952,13953,13955,13957,13959,13961,13963,13965,13967],{"class":3142,"line":5368},[3144,13954,8613],{"class":7647},[3144,13956,12713],{"class":7647},[3144,13958,12716],{"class":7651},[3144,13960,12108],{"class":5663},[3144,13962,12721],{"class":7647},[3144,13964,8709],{"class":5663},[3144,13966,12726],{"class":7647},[3144,13968,12729],{"class":5663},[3144,13970,13971,13973,13975,13977,13979,13981,13983,13985,13987],{"class":3142,"line":5373},[3144,13972,8613],{"class":7647},[3144,13974,12736],{"class":7647},[3144,13976,12739],{"class":7647},[3144,13978,12742],{"class":7651},[3144,13980,12108],{"class":5663},[3144,13982,12721],{"class":7647},[3144,13984,8709],{"class":5663},[3144,13986,12726],{"class":7647},[3144,13988,12729],{"class":5663},[3144,13990,13991,13993,13995,13997,13999,14001,14003,14005],{"class":3142,"line":5378},[3144,13992,8613],{"class":7647},[3144,13994,12759],{"class":7647},[3144,13996,12762],{"class":7651},[3144,13998,12108],{"class":5663},[3144,14000,12721],{"class":7647},[3144,14002,8709],{"class":5663},[3144,14004,12726],{"class":7647},[3144,14006,12729],{"class":5663},[3144,14008,14009,14011,14013,14015,14017,14019,14021,14023],{"class":3142,"line":5384},[3144,14010,8613],{"class":7647},[3144,14012,12779],{"class":7812},[3144,14014,12782],{"class":7651},[3144,14016,12108],{"class":5663},[3144,14018,12721],{"class":7647},[3144,14020,8709],{"class":5663},[3144,14022,12726],{"class":7647},[3144,14024,12729],{"class":5663},[3144,14026,14027],{"class":3142,"line":5390},[3144,14028,3441],{"class":5663},[3144,14030,14031],{"class":3142,"line":5396},[3144,14032,3411],{"emptyLinePlaceholder":3410},[3144,14034,14035,14037,14039,14041,14043,14045],{"class":3142,"line":5401},[3144,14036,12700],{"class":7647},[3144,14038,12807],{"class":7812},[3144,14040,7667],{"class":5663},[3144,14042,12812],{"class":7647},[3144,14044,12742],{"class":7651},[3144,14046,7673],{"class":5663},[3144,14048,14049,14051,14053,14055,14057,14059,14061,14063,14065,14067,14069],{"class":3142,"line":5406},[3144,14050,12700],{"class":7647},[3144,14052,12823],{"class":7812},[3144,14054,7667],{"class":5663},[3144,14056,12812],{"class":7647},[3144,14058,12830],{"class":5663},[3144,14060,12367],{"class":7651},[3144,14062,6241],{"class":5663},[3144,14064,12837],{"class":7647},[3144,14066,12830],{"class":5663},[3144,14068,12571],{"class":7651},[3144,14070,7673],{"class":5663},[3114,14072,14073],{},[3118,14074,14075],{},"Зміни у версії 2.0:",[3926,14077,14078,14084],{},[3906,14079,14080,14081,14083],{},"Оновлено версію у ",[3349,14082,7769],{}," endpoint (1.0.0 → 2.0.0)",[3906,14085,14086,14087,14089],{},"Додано новий endpoint ",[3349,14088,12853],{}," з детальною статистикою",[3122,14091,14093],{"id":14092},"збірка-нової-версії","Збірка нової версії",[3114,14095,14096],{},"Зберемо образ версії 2.0:",[3135,14098,14100,14109,14118,14122,14129],{"title":14099},"Збірка v2.0",[3139,14101,14103,3156,14106],{"className":14102},[3142],[3144,14104,3155],{"className":14105},[3147],[3118,14107,14108],{},"eval $(minikube docker-env)",[3139,14110,14112,3156,14115],{"className":14111},[3142],[3144,14113,3155],{"className":14114},[3147],[3118,14116,14117],{},"docker build -t todoapi:2.0.0 .",[3139,14119,14121],{"className":14120},[3142],"[+] Building 42.1s (15/15) FINISHED",[3139,14123,14125],{"className":14124},[3142],[3144,14126,14128],{"className":14127},[3203]," => exporting to image",[3139,14130,14132],{"className":14131},[3142],[3144,14133,14135],{"className":14134},[3203]," => => naming to docker.io/library/todoapi:2.0.0",[3122,14137,14139],{"id":14138},"виконання-rolling-update","Виконання rolling update",[3114,14141,14142],{},"Тепер оновимо Deployment на нову версію:",[3135,14144,14146,14155],{"title":14145},"Rolling update на v2.0",[3139,14147,14149,3156,14152],{"className":14148},[3142],[3144,14150,3155],{"className":14151},[3147],[3118,14153,14154],{},"kubectl set image deployment/todoapi todoapi=todoapi:2.0.0 --record",[3139,14156,14158],{"className":14157},[3142],[3144,14159,3569],{"className":14160},[3203],[3114,14162,14163],{},"Стежимо за прогресом:",[3135,14165,14166,14174,14177,14180,14183,14186],{"title":11651},[3139,14167,14169,3156,14172],{"className":14168},[3142],[3144,14170,3155],{"className":14171},[3147],[3118,14173,11660],{},[3139,14175,11664],{"className":14176},[3142],[3139,14178,11664],{"className":14179},[3142],[3139,14181,11671],{"className":14182},[3142],[3139,14184,11679],{"className":14185},[3142],[3139,14187,14189],{"className":14188},[3142],[3144,14190,11686],{"className":14191},[3203],[3114,14193,14194],{},"Спостерігаємо за Pod у реальному часі:",[3135,14196,14198,14207,14211,14215,14219,14223,14227,14231,14235,14239,14243,14247,14251,14255,14259,14263,14267],{"title":14197},"kubectl get pods -w",[3139,14199,14201,3156,14204],{"className":14200},[3142],[3144,14202,3155],{"className":14203},[3147],[3118,14205,14206],{},"kubectl get pods -l app=todoapi -w",[3139,14208,14210],{"className":14209},[3142],"NAME                       READY   STATUS    RESTARTS   AGE",[3139,14212,14214],{"className":14213},[3142],"todoapi-abc123-xxx         1/1     Running   0          5m",[3139,14216,14218],{"className":14217},[3142],"todoapi-abc123-yyy         1/1     Running   0          5m",[3139,14220,14222],{"className":14221},[3142],"todoapi-abc123-zzz         1/1     Running   0          5m",[3139,14224,14226],{"className":14225},[3142],"todoapi-def456-aaa         0/1     Pending   0          0s",[3139,14228,14230],{"className":14229},[3142],"todoapi-def456-aaa         0/1     ContainerCreating   0          2s",[3139,14232,14234],{"className":14233},[3142],"todoapi-def456-aaa         1/1     Running             0          15s",[3139,14236,14238],{"className":14237},[3142],"todoapi-abc123-xxx         1/1     Terminating         0          5m15s",[3139,14240,14242],{"className":14241},[3142],"todoapi-def456-bbb         0/1     Pending             0          0s",[3139,14244,14246],{"className":14245},[3142],"todoapi-def456-bbb         0/1     ContainerCreating   0          1s",[3139,14248,14250],{"className":14249},[3142],"todoapi-def456-bbb         1/1     Running             0          12s",[3139,14252,14254],{"className":14253},[3142],"todoapi-abc123-yyy         1/1     Terminating         0          5m27s",[3139,14256,14258],{"className":14257},[3142],"todoapi-def456-ccc         0/1     Pending             0          0s",[3139,14260,14262],{"className":14261},[3142],"todoapi-def456-ccc         0/1     ContainerCreating   0          2s",[3139,14264,14266],{"className":14265},[3142],"todoapi-def456-ccc         1/1     Running             0          14s",[3139,14268,14270],{"className":14269},[3142],"todoapi-abc123-zzz         1/1     Terminating         0          5m41s",[3114,14272,14273],{},"Бачимо класичний rolling update: нові Pod створюються, старі видаляються по черзі.",[3122,14275,14277],{"id":14276},"тестування-нової-версії","Тестування нової версії",[3114,14279,14280],{},"Після завершення оновлення протестуємо новий endpoint:",[3135,14282,14284,14293,14296,14303,14312,14316,14319,14326,14335,14342,14345,14353,14359,14362,14369,14378,14384,14387,14394,14403,14406,14410,14414,14418,14422,14426,14430],{"title":14283},"Тестування v2.0",[3139,14285,14287,3156,14290],{"className":14286},[3142],[3144,14288,3155],{"className":14289},[3147],[3118,14291,14292],{},"kubectl port-forward deployment/todoapi 8080:8080 &",[3139,14294],{"className":14295},[3142],[3139,14297,14299],{"className":14298},[3142],[3144,14300,14302],{"className":14301},[3147],"# Перевірка версії",[3139,14304,14306,3156,14309],{"className":14305},[3142],[3144,14307,3155],{"className":14308},[3147],[3118,14310,14311],{},"curl http://localhost:8080/health",[3139,14313,14315],{"className":14314},[3142],"{\"status\":\"healthy\",\"version\":\"2.0.0\"}",[3139,14317],{"className":14318},[3142],[3139,14320,14322],{"className":14321},[3142],[3144,14323,14325],{"className":14324},[3147],"# Створення кількох todos",[3139,14327,14329,3156,14332],{"className":14328},[3142],[3144,14330,3155],{"className":14331},[3147],[3118,14333,14334],{},"curl -X POST http://localhost:8080/todos -H \"Content-Type: application/json\" \\",[3139,14336,14338,14339],{"className":14337},[3142],"  ",[3118,14340,14341],{},"-d '{\"title\":\"Вивчити Rolling Updates\"}'",[3139,14343],{"className":14344},[3142],[3139,14346,14348,3156,14351],{"className":14347},[3142],[3144,14349,3155],{"className":14350},[3147],[3118,14352,14334],{},[3139,14354,14338,14356],{"className":14355},[3142],[3118,14357,14358],{},"-d '{\"title\":\"Протестувати Rollback\"}'",[3139,14360],{"className":14361},[3142],[3139,14363,14365],{"className":14364},[3142],[3144,14366,14368],{"className":14367},[3147],"# Позначити перший як completed",[3139,14370,14372,3156,14375],{"className":14371},[3142],[3144,14373,3155],{"className":14374},[3147],[3118,14376,14377],{},"curl -X PUT http://localhost:8080/todos/1 -H \"Content-Type: application/json\" \\",[3139,14379,14338,14381],{"className":14380},[3142],[3118,14382,14383],{},"-d '{\"isCompleted\":true}'",[3139,14385],{"className":14386},[3142],[3139,14388,14390],{"className":14389},[3142],[3144,14391,14393],{"className":14392},[3147],"# НОВИЙ ENDPOINT: Статистика",[3139,14395,14397,3156,14400],{"className":14396},[3142],[3144,14398,3155],{"className":14399},[3147],[3118,14401,14402],{},"curl http://localhost:8080/stats",[3139,14404,8810],{"className":14405},[3142],[3139,14407,14409],{"className":14408},[3142],"  \"total\": 2,",[3139,14411,14413],{"className":14412},[3142],"  \"completed\": 1,",[3139,14415,14417],{"className":14416},[3142],"  \"pending\": 1,",[3139,14419,14421],{"className":14420},[3142],"  \"completionRate\": 50.0,",[3139,14423,14425],{"className":14424},[3142],"  \"oldestTodo\": \"2026-05-09T20:50:00.123Z\",",[3139,14427,14429],{"className":14428},[3142],"  \"newestTodo\": \"2026-05-09T20:50:05.456Z\"",[3139,14431,8825],{"className":14432},[3142],[3114,14434,14435,14436,14438],{},"Новий endpoint ",[3349,14437,12853],{}," працює! Оновлення успішне.",[3122,14440,14442],{"id":14441},"симуляція-проблеми-та-rollback","Симуляція проблеми та rollback",[3114,14444,14445,14446,14448],{},"Тепер уявімо, що версія 2.0 має критичний баг (наприклад, endpoint ",[3349,14447,12853],{}," падає під навантаженням). Потрібно швидко повернутись до версії 1.0.",[3135,14450,14452,14460],{"title":14451},"Rollback до v1.0",[3139,14453,14455,3156,14458],{"className":14454},[3142],[3144,14456,3155],{"className":14457},[3147],[3118,14459,11405],{},[3139,14461,14463],{"className":14462},[3142],[3144,14464,11412],{"className":14465},[3203],[3135,14467,14469,14477,14480,14483],{"title":14468},"Моніторинг rollback",[3139,14470,14472,3156,14475],{"className":14471},[3142],[3144,14473,3155],{"className":14474},[3147],[3118,14476,11660],{},[3139,14478,11664],{"className":14479},[3142],[3139,14481,11671],{"className":14482},[3142],[3139,14484,14486],{"className":14485},[3142],[3144,14487,11686],{"className":14488},[3203],[3114,14490,14491],{},"Перевіримо версію:",[3135,14493,14495,14503,14507,14510,14517,14525],{"title":14494},"Перевірка версії після rollback",[3139,14496,14498,3156,14501],{"className":14497},[3142],[3144,14499,3155],{"className":14500},[3147],[3118,14502,14311],{},[3139,14504,14506],{"className":14505},[3142],"{\"status\":\"healthy\",\"version\":\"1.0.0\"}",[3139,14508],{"className":14509},[3142],[3139,14511,14513],{"className":14512},[3142],[3144,14514,14516],{"className":14515},[3147],"# Endpoint /stats більше не існує",[3139,14518,14520,3156,14523],{"className":14519},[3142],[3144,14521,3155],{"className":14522},[3147],[3118,14524,14402],{},[3139,14526,14528],{"className":14527},[3142],[3144,14529,14531],{"className":14530},[9693],"HTTP/1.1 404 Not Found",[3114,14533,14534,14535,14538],{},"Rollback виконано за ",[3118,14536,14537],{},"15-20 секунд","! Застосунок повернувся до стабільної версії 1.0.",[3122,14540,14542],{"id":14541},"перегляд-історії-після-rollback","Перегляд історії після rollback",[3135,14544,14545,14553,14556,14559,14563],{"title":11171},[3139,14546,14548,3156,14551],{"className":14547},[3142],[3144,14549,3155],{"className":14550},[3147],[3118,14552,11180],{},[3139,14554,11184],{"className":14555},[3142],[3139,14557,11188],{"className":14558},[3142],[3139,14560,14562],{"className":14561},[3142],"2         kubectl set image deployment/todoapi todoapi=todoapi:2.0.0 --record=true",[3139,14564,14566,14567],{"className":14565},[3142],"3         ",[11194,14568],{},[3114,14570,14571],{},[3118,14572,14573],{},"Що сталося з ревізіями?",[3903,14575,14576,14586,14592],{},[3906,14577,14578,14581,14582,14585],{},[3118,14579,14580],{},"Ревізія 1"," (v1.0) зникла — вона стала ",[3118,14583,14584],{},"ревізією 3"," після rollback",[3906,14587,14588,14591],{},[3118,14589,14590],{},"Ревізія 2"," (v2.0) залишилась у історії",[3906,14593,14594,14597],{},[3118,14595,14596],{},"Ревізія 3"," — це знову v1.0 (результат rollback)",[3114,14599,14600],{},"Kubernetes не видаляє ревізії, а створює нову з тим самим шаблоном Pod.",[3291,14602],{},[3109,14604,14606],{"id":14605},"troubleshooting-типові-проблеми-та-їх-вирішення","Troubleshooting: типові проблеми та їх вирішення",[3114,14608,14609],{},"Тепер розглянемо найчастіші проблеми при rolling updates та як їх діагностувати.",[3122,14611,14613],{"id":14612},"проблема-1-оновлення-зависло-progressing-false","Проблема 1: Оновлення зависло (Progressing: False)",[3114,14615,14616],{},[3118,14617,10129],{},[3135,14619,14621,14629,14633],{"title":14620},"kubectl get deployments",[3139,14622,14624,3156,14627],{"className":14623},[3142],[3144,14625,3155],{"className":14626},[3147],[3118,14628,14620],{},[3139,14630,14632],{"className":14631},[3142],"NAME      READY   UP-TO-DATE   AVAILABLE   AGE",[3139,14634,14636],{"className":14635},[3142],"todoapi   2/3     1            2           10m",[3114,14638,14639,14640,14643],{},"Бачимо ",[3349,14641,14642],{},"2/3"," — лише 2 з 3 реплік готові. Оновлення не завершується.",[3114,14645,14646],{},[3118,14647,14648],{},"Діагностика:",[3135,14650,14652,14661,14665,14669,14673,14677,14681,14684,14688,14692,14696],{"title":14651},"kubectl describe deployment",[3139,14653,14655,3156,14658],{"className":14654},[3142],[3144,14656,3155],{"className":14657},[3147],[3118,14659,14660],{},"kubectl describe deployment todoapi",[3139,14662,14664],{"className":14663},[3142],"Conditions:",[3139,14666,14668],{"className":14667},[3142],"  Type           Status  Reason",[3139,14670,14672],{"className":14671},[3142],"  ----           ------  ------",[3139,14674,14676],{"className":14675},[3142],"  Available      True    MinimumReplicasAvailable",[3139,14678,14680],{"className":14679},[3142],"  Progressing    False   ProgressDeadlineExceeded",[3139,14682],{"className":14683},[3142],[3139,14685,14687],{"className":14686},[3142],"Events:",[3139,14689,14691],{"className":14690},[3142],"  Type     Reason        Age   Message",[3139,14693,14695],{"className":14694},[3142],"  ----     ------        ----  -------",[3139,14697,14699],{"className":14698},[3142],"  Warning  FailedCreate  5m    Error creating: pods \"todoapi-xxx\" is forbidden: exceeded quota",[3114,14701,14702,3156,14704,14706,14707,3289],{},[3118,14703,10824],{},[3349,14705,7058],{}," — оновлення не досягло прогресу за ",[3349,14708,4378],{},[3114,14710,14711],{},[3118,14712,14713],{},"Можливі причини:",[3330,14715,14716,14786,14839],{},[3333,14717,14720,14725,14750,14763,14767],{"icon":14718,"title":14719},"i-heroicons-x-circle","Новий Pod не проходить readiness probe",[3114,14721,14722],{},[3118,14723,14724],{},"Перевірка:",[3379,14726,14728],{"className":10269,"code":14727,"language":10271,"meta":3384,"style":3384},"kubectl get pods\n# NAME                       READY   STATUS    RESTARTS   AGE\n# todoapi-def456-aaa         0/1     Running   0          5m\n",[3349,14729,14730,14740,14745],{"__ignoreMap":3384},[3144,14731,14732,14734,14737],{"class":3142,"line":3389},[3144,14733,10278],{"class":7663},[3144,14735,14736],{"class":7139}," get",[3144,14738,14739],{"class":7139}," pods\n",[3144,14741,14742],{"class":3142,"line":3395},[3144,14743,14744],{"class":7200},"# NAME                       READY   STATUS    RESTARTS   AGE\n",[3144,14746,14747],{"class":3142,"line":3401},[3144,14748,14749],{"class":7200},"# todoapi-def456-aaa         0/1     Running   0          5m\n",[3114,14751,14752,14753,7167,14756,7655,14759,14762],{},"Pod у стані ",[3349,14754,14755],{},"Running",[3349,14757,14758],{},"READY",[3349,14760,14761],{},"0/1"," — readiness probe fails.",[3114,14764,14765],{},[3118,14766,10325],{},[3926,14768,14769,14775,14781],{},[3906,14770,14771,14772],{},"Переглянути логи: ",[3349,14773,14774],{},"kubectl logs todoapi-def456-aaa",[3906,14776,14777,14778],{},"Перевірити readiness probe endpoint вручну: ",[3349,14779,14780],{},"kubectl exec -it todoapi-def456-aaa -- curl localhost:8080/health/ready",[3906,14782,14783,14784],{},"Виправити проблему (наприклад, БД недоступна) або відкотити: ",[3349,14785,11405],{},[3333,14787,14790,14794,14820,14824],{"icon":14788,"title":14789},"i-heroicons-server","Недостатньо ресурсів на вузлах",[3114,14791,14792],{},[3118,14793,14724],{},[3379,14795,14797],{"className":10269,"code":14796,"language":10271,"meta":3384,"style":3384},"kubectl describe pod todoapi-def456-aaa\n# Events:\n#   Warning  FailedScheduling  5m  0/3 nodes are available: insufficient memory\n",[3349,14798,14799,14810,14815],{"__ignoreMap":3384},[3144,14800,14801,14803,14805,14807],{"class":3142,"line":3389},[3144,14802,10278],{"class":7663},[3144,14804,10281],{"class":7139},[3144,14806,10284],{"class":7139},[3144,14808,14809],{"class":7139}," todoapi-def456-aaa\n",[3144,14811,14812],{"class":3142,"line":3395},[3144,14813,14814],{"class":7200},"# Events:\n",[3144,14816,14817],{"class":3142,"line":3401},[3144,14818,14819],{"class":7200},"#   Warning  FailedScheduling  5m  0/3 nodes are available: insufficient memory\n",[3114,14821,14822],{},[3118,14823,10325],{},[3926,14825,14826,14833,14836],{},[3906,14827,14828,14829,14832],{},"Зменшити ",[3349,14830,14831],{},"resources.requests"," у Deployment",[3906,14834,14835],{},"Додати більше вузлів до кластера",[3906,14837,14838],{},"Видалити непотрібні Pod для звільнення ресурсів",[3333,14840,14843,14847,14871,14875],{"icon":14841,"title":14842},"i-heroicons-photo","Образ не може завантажитись",[3114,14844,14845],{},[3118,14846,14724],{},[3379,14848,14850],{"className":10269,"code":14849,"language":10271,"meta":3384,"style":3384},"kubectl describe pod todoapi-def456-aaa\n# Events:\n#   Warning  Failed  5m  Failed to pull image \"todoapi:2.0.0\": rpc error: code = Unknown desc = Error response from daemon: pull access denied\n",[3349,14851,14852,14862,14866],{"__ignoreMap":3384},[3144,14853,14854,14856,14858,14860],{"class":3142,"line":3389},[3144,14855,10278],{"class":7663},[3144,14857,10281],{"class":7139},[3144,14859,10284],{"class":7139},[3144,14861,14809],{"class":7139},[3144,14863,14864],{"class":3142,"line":3395},[3144,14865,14814],{"class":7200},[3144,14867,14868],{"class":3142,"line":3401},[3144,14869,14870],{"class":7200},"#   Warning  Failed  5m  Failed to pull image \"todoapi:2.0.0\": rpc error: code = Unknown desc = Error response from daemon: pull access denied\n",[3114,14872,14873],{},[3118,14874,10325],{},[3926,14876,14877,14883,14893],{},[3906,14878,14879,14880],{},"Перевірити, чи існує образ: ",[3349,14881,14882],{},"docker images | grep todoapi",[3906,14884,14885,14886,14889,14890,5602],{},"Перевірити ",[3349,14887,14888],{},"imagePullPolicy"," (для Minikube має бути ",[3349,14891,14892],{},"Never",[3906,14894,14895],{},"Перевірити credentials для private registry",[3122,14897,14899],{"id":14898},"проблема-2-pod-постійно-перезапускаються-crashloopbackoff","Проблема 2: Pod постійно перезапускаються (CrashLoopBackOff)",[3114,14901,14902],{},[3118,14903,10129],{},[3135,14905,14907,14915,14919],{"title":14906},"kubectl get pods",[3139,14908,14910,3156,14913],{"className":14909},[3142],[3144,14911,3155],{"className":14912},[3147],[3118,14914,14906],{},[3139,14916,14918],{"className":14917},[3142],"NAME                       READY   STATUS             RESTARTS   AGE",[3139,14920,14922],{"className":14921},[3142],"todoapi-def456-aaa         0/1     CrashLoopBackOff   5          3m",[3114,14924,14925],{},[3118,14926,14648],{},[3135,14928,14929,14938,14942,14946,14950,14954],{"title":10223},[3139,14930,14932,3156,14935],{"className":14931},[3142],[3144,14933,3155],{"className":14934},[3147],[3118,14936,14937],{},"kubectl describe pod todoapi-def456-aaa",[3139,14939,14941],{"className":14940},[3142],"Last State:     Terminated",[3139,14943,14945],{"className":14944},[3142],"  Reason:       Error",[3139,14947,14949],{"className":14948},[3142],"  Exit Code:    1",[3139,14951,14953],{"className":14952},[3142],"  Started:      Fri, 09 May 2026 20:50:00 +0000",[3139,14955,14957],{"className":14956},[3142],"  Finished:     Fri, 09 May 2026 20:50:05 +0000",[3114,14959,14960],{},[3118,14961,14962],{},"Перегляд логів:",[3135,14964,14966,14974],{"title":14965},"kubectl logs",[3139,14967,14969,3156,14972],{"className":14968},[3142],[3144,14970,3155],{"className":14971},[3147],[3118,14973,14774],{},[3139,14975,14977],{"className":14976},[3142],[3144,14978,14980],{"className":14979},[9693],"Unhandled exception. System.InvalidOperationException: Unable to resolve service for type 'MyService'",[3114,14982,14983],{},[3118,14984,14713],{},[3903,14986,14987,14990,14993,14996],{},[3906,14988,14989],{},"Помилка у коді (exception при старті)",[3906,14991,14992],{},"Відсутня залежність (DI не може resolve service)",[3906,14994,14995],{},"Неправильна конфігурація (змінні оточення, ConfigMap)",[3906,14997,14998],{},"OOMKilled (перевищено memory limits)",[3114,15000,15001],{},[3118,15002,10325],{},[3926,15004,15005,15008],{},[3906,15006,15007],{},"Виправити код та зібрати новий образ",[3906,15009,15010,15011],{},"Або виконати rollback: ",[3349,15012,11405],{},[3122,15014,15016],{"id":15015},"проблема-3-оновлення-занадто-повільне","Проблема 3: Оновлення занадто повільне",[3114,15018,15019,15021],{},[3118,15020,10129],{}," Rolling update займає 10+ хвилин для 10 реплік.",[3114,15023,15024,15026,15027,3352,15029,3289],{},[3118,15025,10824],{}," Обережні налаштування ",[3349,15028,3351],{},[3349,15030,3355],{},[3114,15032,15033],{},[3118,15034,15035],{},"Поточна конфігурація:",[3379,15037,15039],{"className":5650,"code":15038,"language":5652,"meta":3384,"style":3384},"strategy:\n  rollingUpdate:\n    maxSurge: 1\n    maxUnavailable: 0\n",[3349,15040,15041,15048,15055,15064],{"__ignoreMap":3384},[3144,15042,15043,15046],{"class":3142,"line":3389},[3144,15044,15045],{"class":5659},"strategy",[3144,15047,5664],{"class":5663},[3144,15049,15050,15053],{"class":3142,"line":3395},[3144,15051,15052],{"class":5659},"  rollingUpdate",[3144,15054,5664],{"class":5663},[3144,15056,15057,15060,15062],{"class":3142,"line":3401},[3144,15058,15059],{"class":5659},"    maxSurge",[3144,15061,5679],{"class":5663},[3144,15063,5701],{"class":5700},[3144,15065,15066,15069,15071],{"class":3142,"line":3407},[3144,15067,15068],{"class":5659},"    maxUnavailable",[3144,15070,5679],{"class":5663},[3144,15072,6631],{"class":5700},[3114,15074,15075],{},"Це означає: оновлювати по 1 Pod за раз, завжди тримати всі репліки доступними.",[3114,15077,15078,15080,15081,15083],{},[3118,15079,10325],{}," Збільшити ",[3349,15082,3351],{}," для швидшого оновлення:",[3379,15085,15087],{"className":5650,"code":15086,"language":5652,"meta":3384,"style":3384},"strategy:\n  rollingUpdate:\n    maxSurge: 50%      # Було: 1\n    maxUnavailable: 0\n",[3349,15088,15089,15095,15101,15112],{"__ignoreMap":3384},[3144,15090,15091,15093],{"class":3142,"line":3389},[3144,15092,15045],{"class":5659},[3144,15094,5664],{"class":5663},[3144,15096,15097,15099],{"class":3142,"line":3395},[3144,15098,15052],{"class":5659},[3144,15100,5664],{"class":5663},[3144,15102,15103,15105,15107,15109],{"class":3142,"line":3401},[3144,15104,15059],{"class":5659},[3144,15106,5679],{"class":5663},[3144,15108,6254],{"class":5682},[3144,15110,15111],{"class":7200},"      # Було: 1\n",[3144,15113,15114,15116,15118],{"class":3142,"line":3407},[3144,15115,15068],{"class":5659},[3144,15117,5679],{"class":5663},[3144,15119,6631],{"class":5700},[3114,15121,15122],{},"Тепер Kubernetes створить 50% нових Pod одразу (5 з 10), що прискорить оновлення.",[3122,15124,15126],{"id":15125},"проблема-4-downtime-під-час-оновлення","Проблема 4: Downtime під час оновлення",[3114,15128,15129,15131],{},[3118,15130,10129],{}," Користувачі отримують помилки 503 під час rolling update.",[3114,15133,15134,15136],{},[3118,15135,10824],{}," Pod видаляються до того, як нові стануть готовими.",[3114,15138,15139],{},[3118,15140,14648],{},[3114,15142,15143,15144,10476],{},"Перевірте ",[3349,15145,3355],{},[3379,15147,15149],{"className":5650,"code":15148,"language":5652,"meta":3384,"style":3384},"strategy:\n  rollingUpdate:\n    maxSurge: 0\n    maxUnavailable: 1\n",[3349,15150,15151,15157,15163,15171],{"__ignoreMap":3384},[3144,15152,15153,15155],{"class":3142,"line":3389},[3144,15154,15045],{"class":5659},[3144,15156,5664],{"class":5663},[3144,15158,15159,15161],{"class":3142,"line":3395},[3144,15160,15052],{"class":5659},[3144,15162,5664],{"class":5663},[3144,15164,15165,15167,15169],{"class":3142,"line":3401},[3144,15166,15059],{"class":5659},[3144,15168,5679],{"class":5663},[3144,15170,6631],{"class":5700},[3144,15172,15173,15175,15177],{"class":3142,"line":3407},[3144,15174,15068],{"class":5659},[3144,15176,5679],{"class":5663},[3144,15178,5701],{"class":5700},[3114,15180,15181,15182,15184],{},"Якщо ",[3349,15183,6439],{},", Kubernetes спочатку видаляє старий Pod, потім створює новий. Є момент, коли реплік менше, ніж потрібно.",[3114,15186,15187,15189,15190,3352,15192,10476],{},[3118,15188,10325],{}," Встановити ",[3349,15191,6407],{},[3349,15193,6226],{},[3379,15195,15196],{"className":5650,"code":15038,"language":5652,"meta":3384,"style":3384},[3349,15197,15198,15204,15210,15218],{"__ignoreMap":3384},[3144,15199,15200,15202],{"class":3142,"line":3389},[3144,15201,15045],{"class":5659},[3144,15203,5664],{"class":5663},[3144,15205,15206,15208],{"class":3142,"line":3395},[3144,15207,15052],{"class":5659},[3144,15209,5664],{"class":5663},[3144,15211,15212,15214,15216],{"class":3142,"line":3401},[3144,15213,15059],{"class":5659},[3144,15215,5679],{"class":5663},[3144,15217,5701],{"class":5700},[3144,15219,15220,15222,15224],{"class":3142,"line":3407},[3144,15221,15068],{"class":5659},[3144,15223,5679],{"class":5663},[3144,15225,6631],{"class":5700},[3114,15227,15228],{},"Тепер Kubernetes спочатку створює новий Pod, чекає його готовності, і лише потім видаляє старий. Завжди є повна кількість реплік.",[3122,15230,15232],{"id":15231},"проблема-5-старі-pod-не-видаляються","Проблема 5: Старі Pod не видаляються",[3114,15234,15235,15237,15238,3289],{},[3118,15236,10129],{}," Після оновлення залишаються старі Pod у стані ",[3349,15239,15240],{},"Terminating",[3135,15242,15243,15251,15255,15259],{"title":14906},[3139,15244,15246,3156,15249],{"className":15245},[3142],[3144,15247,3155],{"className":15248},[3147],[3118,15250,14906],{},[3139,15252,15254],{"className":15253},[3142],"NAME                       READY   STATUS        RESTARTS   AGE",[3139,15256,15258],{"className":15257},[3142],"todoapi-abc123-xxx         1/1     Terminating   0          10m",[3139,15260,15262],{"className":15261},[3142],"todoapi-def456-aaa         1/1     Running       0          2m",[3114,15264,15265,15267],{},[3118,15266,10824],{}," Pod не завершується gracefully (не обробляє SIGTERM).",[3114,15269,15270],{},[3118,15271,7405],{},[3926,15273,15274,15277,15280],{},[3906,15275,15276],{},"Kubernetes надсилає SIGTERM контейнеру",[3906,15278,15279],{},"Контейнер має 30 секунд (за замовчуванням) для graceful shutdown",[3906,15281,15282],{},"Якщо контейнер не завершується — Kubernetes надсилає SIGKILL (force kill)",[3114,15284,15285],{},[3118,15286,15287],{},"Рішення для .NET:",[3114,15289,15290],{},"Додати обробку graceful shutdown:",[3379,15292,15294],{"className":7638,"code":15293,"language":7640,"meta":3384,"style":3384},"var builder = WebApplication.CreateBuilder(args);\n\n// ... конфігурація ...\n\nvar app = builder.Build();\n\n// Налаштування graceful shutdown\nvar lifetime = app.Services.GetRequiredService\u003CIHostApplicationLifetime>();\n\nlifetime.ApplicationStopping.Register(() =>\n{\n    Console.WriteLine(\"Application is stopping. Finishing current requests...\");\n    // Тут можна закрити з'єднання з БД, flush кеші тощо\n});\n\napp.Run();\n",[3349,15295,15296,15316,15320,15325,15329,15345,15349,15354,15381,15385,15403,15407,15424,15429,15433,15437],{"__ignoreMap":3384},[3144,15297,15298,15300,15302,15304,15306,15308,15310,15312,15314],{"class":3142,"line":3389},[3144,15299,7648],{"class":7647},[3144,15301,7652],{"class":7651},[3144,15303,7655],{"class":5663},[3144,15305,7658],{"class":7651},[3144,15307,3289],{"class":5663},[3144,15309,7664],{"class":7663},[3144,15311,7667],{"class":5663},[3144,15313,7670],{"class":7651},[3144,15315,7673],{"class":5663},[3144,15317,15318],{"class":3142,"line":3395},[3144,15319,3411],{"emptyLinePlaceholder":3410},[3144,15321,15322],{"class":3142,"line":3401},[3144,15323,15324],{"class":7200},"// ... конфігурація ...\n",[3144,15326,15327],{"class":3142,"line":3407},[3144,15328,3411],{"emptyLinePlaceholder":3410},[3144,15330,15331,15333,15335,15337,15339,15341,15343],{"class":3142,"line":3414},[3144,15332,7648],{"class":7647},[3144,15334,7711],{"class":7651},[3144,15336,7655],{"class":5663},[3144,15338,7687],{"class":7651},[3144,15340,3289],{"class":5663},[3144,15342,7720],{"class":7663},[3144,15344,7700],{"class":5663},[3144,15346,15347],{"class":3142,"line":3420},[3144,15348,3411],{"emptyLinePlaceholder":3410},[3144,15350,15351],{"class":3142,"line":3426},[3144,15352,15353],{"class":7200},"// Налаштування graceful shutdown\n",[3144,15355,15356,15358,15361,15363,15365,15367,15369,15371,15374,15376,15379],{"class":3142,"line":3432},[3144,15357,7648],{"class":7647},[3144,15359,15360],{"class":7651}," lifetime",[3144,15362,7655],{"class":5663},[3144,15364,7736],{"class":7651},[3144,15366,3289],{"class":5663},[3144,15368,7692],{"class":7651},[3144,15370,3289],{"class":5663},[3144,15372,15373],{"class":7663},"GetRequiredService",[3144,15375,8069],{"class":5663},[3144,15377,15378],{"class":7812},"IHostApplicationLifetime",[3144,15380,11983],{"class":5663},[3144,15382,15383],{"class":3142,"line":3438},[3144,15384,3411],{"emptyLinePlaceholder":3410},[3144,15386,15387,15390,15392,15395,15397,15400],{"class":3142,"line":3444},[3144,15388,15389],{"class":7651},"lifetime",[3144,15391,3289],{"class":5663},[3144,15393,15394],{"class":7651},"ApplicationStopping",[3144,15396,3289],{"class":5663},[3144,15398,15399],{"class":7663},"Register",[3144,15401,15402],{"class":5663},"(() =>\n",[3144,15404,15405],{"class":3142,"line":3449},[3144,15406,8134],{"class":5663},[3144,15408,15409,15412,15414,15417,15419,15422],{"class":3142,"line":3455},[3144,15410,15411],{"class":7651},"    Console",[3144,15413,3289],{"class":5663},[3144,15415,15416],{"class":7663},"WriteLine",[3144,15418,7667],{"class":5663},[3144,15420,15421],{"class":7139},"\"Application is stopping. Finishing current requests...\"",[3144,15423,7673],{"class":5663},[3144,15425,15426],{"class":3142,"line":3461},[3144,15427,15428],{"class":7200},"    // Тут можна закрити з'єднання з БД, flush кеші тощо\n",[3144,15430,15431],{"class":3142,"line":3467},[3144,15432,8158],{"class":5663},[3144,15434,15435],{"class":3142,"line":3473},[3144,15436,3411],{"emptyLinePlaceholder":3410},[3144,15438,15439,15441,15443,15445],{"class":3142,"line":3478},[3144,15440,7736],{"class":7651},[3144,15442,3289],{"class":5663},[3144,15444,7761],{"class":7663},[3144,15446,7700],{"class":5663},[3114,15448,15449,15450,15453],{},"Також можна збільшити ",[3349,15451,15452],{},"terminationGracePeriodSeconds"," у Deployment:",[3379,15455,15457],{"className":5650,"code":15456,"language":5652,"meta":3384,"style":3384},"spec:\n  template:\n    spec:\n      terminationGracePeriodSeconds: 60  # За замовчуванням 30\n      containers:\n        - name: todoapi\n          image: todoapi:2.0.0\n",[3349,15458,15459,15465,15471,15477,15490,15496,15506],{"__ignoreMap":3384},[3144,15460,15461,15463],{"class":3142,"line":3389},[3144,15462,5660],{"class":5659},[3144,15464,5664],{"class":5663},[3144,15466,15467,15469],{"class":3142,"line":3395},[3144,15468,9142],{"class":5659},[3144,15470,5664],{"class":5663},[3144,15472,15473,15475],{"class":3142,"line":3401},[3144,15474,9172],{"class":5659},[3144,15476,5664],{"class":5663},[3144,15478,15479,15482,15484,15487],{"class":3142,"line":3407},[3144,15480,15481],{"class":5659},"      terminationGracePeriodSeconds",[3144,15483,5679],{"class":5663},[3144,15485,15486],{"class":5700},"60",[3144,15488,15489],{"class":7200},"  # За замовчуванням 30\n",[3144,15491,15492,15494],{"class":3142,"line":3414},[3144,15493,9179],{"class":5659},[3144,15495,5664],{"class":5663},[3144,15497,15498,15500,15502,15504],{"class":3142,"line":3420},[3144,15499,9186],{"class":5663},[3144,15501,9189],{"class":5659},[3144,15503,5679],{"class":5663},[3144,15505,9098],{"class":5682},[3144,15507,15508,15510,15512],{"class":3142,"line":3426},[3144,15509,9198],{"class":5659},[3144,15511,5679],{"class":5663},[3144,15513,9203],{"class":5682},[3122,15515,15517],{"id":15516},"корисні-команди-для-debugging","Корисні команди для debugging",[15519,15520,15521,15624,15745,15881],"code-group",{},[3379,15522,15525],{"className":10269,"code":15523,"filename":15524,"language":10271,"meta":3384,"style":3384},"# Статус Deployment\nkubectl get deployment todoapi\n\n# Детальна інформація\nkubectl describe deployment todoapi\n\n# Статус rollout\nkubectl rollout status deployment/todoapi\n\n# Історія ревізій\nkubectl rollout history deployment/todoapi\n\n# Детальна інформація про ревізію\nkubectl rollout history deployment/todoapi --revision=3\n","Перегляд стану",[3349,15526,15527,15532,15544,15548,15553,15563,15567,15572,15582,15586,15591,15602,15606,15611],{"__ignoreMap":3384},[3144,15528,15529],{"class":3142,"line":3389},[3144,15530,15531],{"class":7200},"# Статус Deployment\n",[3144,15533,15534,15536,15538,15541],{"class":3142,"line":3395},[3144,15535,10278],{"class":7663},[3144,15537,14736],{"class":7139},[3144,15539,15540],{"class":7139}," deployment",[3144,15542,15543],{"class":7139}," todoapi\n",[3144,15545,15546],{"class":3142,"line":3401},[3144,15547,3411],{"emptyLinePlaceholder":3410},[3144,15549,15550],{"class":3142,"line":3407},[3144,15551,15552],{"class":7200},"# Детальна інформація\n",[3144,15554,15555,15557,15559,15561],{"class":3142,"line":3414},[3144,15556,10278],{"class":7663},[3144,15558,10281],{"class":7139},[3144,15560,15540],{"class":7139},[3144,15562,15543],{"class":7139},[3144,15564,15565],{"class":3142,"line":3420},[3144,15566,3411],{"emptyLinePlaceholder":3410},[3144,15568,15569],{"class":3142,"line":3426},[3144,15570,15571],{"class":7200},"# Статус rollout\n",[3144,15573,15574,15576,15578,15580],{"class":3142,"line":3432},[3144,15575,10278],{"class":7663},[3144,15577,11764],{"class":7139},[3144,15579,8724],{"class":7139},[3144,15581,11770],{"class":7139},[3144,15583,15584],{"class":3142,"line":3438},[3144,15585,3411],{"emptyLinePlaceholder":3410},[3144,15587,15588],{"class":3142,"line":3444},[3144,15589,15590],{"class":7200},"# Історія ревізій\n",[3144,15592,15593,15595,15597,15600],{"class":3142,"line":3449},[3144,15594,10278],{"class":7663},[3144,15596,11764],{"class":7139},[3144,15598,15599],{"class":7139}," history",[3144,15601,11770],{"class":7139},[3144,15603,15604],{"class":3142,"line":3455},[3144,15605,3411],{"emptyLinePlaceholder":3410},[3144,15607,15608],{"class":3142,"line":3461},[3144,15609,15610],{"class":7200},"# Детальна інформація про ревізію\n",[3144,15612,15613,15615,15617,15619,15621],{"class":3142,"line":3467},[3144,15614,10278],{"class":7663},[3144,15616,11764],{"class":7139},[3144,15618,15599],{"class":7139},[3144,15620,11252],{"class":7139},[3144,15622,15623],{"class":7647}," --revision=3\n",[3379,15625,15628],{"className":10269,"code":15626,"filename":15627,"language":10271,"meta":3384,"style":3384},"# Список Pod\nkubectl get pods -l app=todoapi\n\n# Спостереження в реальному часі\nkubectl get pods -l app=todoapi -w\n\n# Детальна інформація про Pod\nkubectl describe pod \u003Cpod-name>\n\n# Логи Pod\nkubectl logs \u003Cpod-name>\n\n# Логи попереднього контейнера (якщо Pod перезапустився)\nkubectl logs \u003Cpod-name> --previous\n","Перегляд Pod",[3349,15629,15630,15635,15650,15654,15659,15675,15679,15684,15698,15702,15707,15720,15724,15729],{"__ignoreMap":3384},[3144,15631,15632],{"class":3142,"line":3389},[3144,15633,15634],{"class":7200},"# Список Pod\n",[3144,15636,15637,15639,15641,15644,15647],{"class":3142,"line":3395},[3144,15638,10278],{"class":7663},[3144,15640,14736],{"class":7139},[3144,15642,15643],{"class":7139}," pods",[3144,15645,15646],{"class":7647}," -l",[3144,15648,15649],{"class":7139}," app=todoapi\n",[3144,15651,15652],{"class":3142,"line":3401},[3144,15653,3411],{"emptyLinePlaceholder":3410},[3144,15655,15656],{"class":3142,"line":3407},[3144,15657,15658],{"class":7200},"# Спостереження в реальному часі\n",[3144,15660,15661,15663,15665,15667,15669,15672],{"class":3142,"line":3414},[3144,15662,10278],{"class":7663},[3144,15664,14736],{"class":7139},[3144,15666,15643],{"class":7139},[3144,15668,15646],{"class":7647},[3144,15670,15671],{"class":7139}," app=todoapi",[3144,15673,15674],{"class":7647}," -w\n",[3144,15676,15677],{"class":3142,"line":3420},[3144,15678,3411],{"emptyLinePlaceholder":3410},[3144,15680,15681],{"class":3142,"line":3426},[3144,15682,15683],{"class":7200},"# Детальна інформація про Pod\n",[3144,15685,15686,15688,15690,15692,15694,15696],{"class":3142,"line":3432},[3144,15687,10278],{"class":7663},[3144,15689,10281],{"class":7139},[3144,15691,10284],{"class":7139},[3144,15693,10287],{"class":5663},[3144,15695,10290],{"class":7139},[3144,15697,10293],{"class":5663},[3144,15699,15700],{"class":3142,"line":3438},[3144,15701,3411],{"emptyLinePlaceholder":3410},[3144,15703,15704],{"class":3142,"line":3444},[3144,15705,15706],{"class":7200},"# Логи Pod\n",[3144,15708,15709,15711,15714,15716,15718],{"class":3142,"line":3449},[3144,15710,10278],{"class":7663},[3144,15712,15713],{"class":7139}," logs",[3144,15715,10287],{"class":5663},[3144,15717,10290],{"class":7139},[3144,15719,10293],{"class":5663},[3144,15721,15722],{"class":3142,"line":3455},[3144,15723,3411],{"emptyLinePlaceholder":3410},[3144,15725,15726],{"class":3142,"line":3461},[3144,15727,15728],{"class":7200},"# Логи попереднього контейнера (якщо Pod перезапустився)\n",[3144,15730,15731,15733,15735,15737,15739,15742],{"class":3142,"line":3467},[3144,15732,10278],{"class":7663},[3144,15734,15713],{"class":7139},[3144,15736,10287],{"class":5663},[3144,15738,10290],{"class":7139},[3144,15740,15741],{"class":5663},"e> ",[3144,15743,15744],{"class":7647},"--previous\n",[3379,15746,15749],{"className":10269,"code":15747,"filename":15748,"language":10271,"meta":3384,"style":3384},"# Виконати команду всередині Pod\nkubectl exec -it \u003Cpod-name> -- /bin/bash\n\n# Перевірити health endpoint\nkubectl exec -it \u003Cpod-name> -- curl localhost:8080/health\n\n# Копіювати файли з Pod\nkubectl cp \u003Cpod-name>:/app/logs/app.log ./app.log\n\n# Події кластера\nkubectl get events --sort-by=.metadata.creationTimestamp\n\n# Моніторинг ресурсів\nkubectl top pods -l app=todoapi\n","Debugging",[3349,15750,15751,15756,15778,15782,15787,15809,15813,15818,15838,15842,15847,15859,15863,15868],{"__ignoreMap":3384},[3144,15752,15753],{"class":3142,"line":3389},[3144,15754,15755],{"class":7200},"# Виконати команду всередині Pod\n",[3144,15757,15758,15760,15763,15766,15768,15770,15772,15775],{"class":3142,"line":3395},[3144,15759,10278],{"class":7663},[3144,15761,15762],{"class":7139}," exec",[3144,15764,15765],{"class":7647}," -it",[3144,15767,10287],{"class":5663},[3144,15769,10290],{"class":7139},[3144,15771,15741],{"class":5663},[3144,15773,15774],{"class":7647},"--",[3144,15776,15777],{"class":7139}," /bin/bash\n",[3144,15779,15780],{"class":3142,"line":3401},[3144,15781,3411],{"emptyLinePlaceholder":3410},[3144,15783,15784],{"class":3142,"line":3407},[3144,15785,15786],{"class":7200},"# Перевірити health endpoint\n",[3144,15788,15789,15791,15793,15795,15797,15799,15801,15803,15806],{"class":3142,"line":3414},[3144,15790,10278],{"class":7663},[3144,15792,15762],{"class":7139},[3144,15794,15765],{"class":7647},[3144,15796,10287],{"class":5663},[3144,15798,10290],{"class":7139},[3144,15800,15741],{"class":5663},[3144,15802,15774],{"class":7647},[3144,15804,15805],{"class":7139}," curl",[3144,15807,15808],{"class":7139}," localhost:8080/health\n",[3144,15810,15811],{"class":3142,"line":3420},[3144,15812,3411],{"emptyLinePlaceholder":3410},[3144,15814,15815],{"class":3142,"line":3426},[3144,15816,15817],{"class":7200},"# Копіювати файли з Pod\n",[3144,15819,15820,15822,15825,15827,15829,15832,15835],{"class":3142,"line":3432},[3144,15821,10278],{"class":7663},[3144,15823,15824],{"class":7139}," cp",[3144,15826,10287],{"class":5663},[3144,15828,10290],{"class":7139},[3144,15830,15831],{"class":5663},"e>",[3144,15833,15834],{"class":7139},":/app/logs/app.log",[3144,15836,15837],{"class":7139}," ./app.log\n",[3144,15839,15840],{"class":3142,"line":3438},[3144,15841,3411],{"emptyLinePlaceholder":3410},[3144,15843,15844],{"class":3142,"line":3444},[3144,15845,15846],{"class":7200},"# Події кластера\n",[3144,15848,15849,15851,15853,15856],{"class":3142,"line":3449},[3144,15850,10278],{"class":7663},[3144,15852,14736],{"class":7139},[3144,15854,15855],{"class":7139}," events",[3144,15857,15858],{"class":7647}," --sort-by=.metadata.creationTimestamp\n",[3144,15860,15861],{"class":3142,"line":3455},[3144,15862,3411],{"emptyLinePlaceholder":3410},[3144,15864,15865],{"class":3142,"line":3461},[3144,15866,15867],{"class":7200},"# Моніторинг ресурсів\n",[3144,15869,15870,15872,15875,15877,15879],{"class":3142,"line":3467},[3144,15871,10278],{"class":7663},[3144,15873,15874],{"class":7139}," top",[3144,15876,15643],{"class":7139},[3144,15878,15646],{"class":7647},[3144,15880,15649],{"class":7139},[3379,15882,15885],{"className":10269,"code":15883,"filename":15884,"language":10271,"meta":3384,"style":3384},"# Rollback до попередньої версії\nkubectl rollout undo deployment/todoapi\n\n# Rollback до конкретної ревізії\nkubectl rollout undo deployment/todoapi --to-revision=2\n\n# Призупинити оновлення\nkubectl rollout pause deployment/todoapi\n\n# Відновити оновлення\nkubectl rollout resume deployment/todoapi\n","Rollback",[3349,15886,15887,15892,15902,15906,15911,15924,15928,15932,15942,15946,15951],{"__ignoreMap":3384},[3144,15888,15889],{"class":3142,"line":3389},[3144,15890,15891],{"class":7200},"# Rollback до попередньої версії\n",[3144,15893,15894,15896,15898,15900],{"class":3142,"line":3395},[3144,15895,10278],{"class":7663},[3144,15897,11764],{"class":7139},[3144,15899,11847],{"class":7139},[3144,15901,11770],{"class":7139},[3144,15903,15904],{"class":3142,"line":3401},[3144,15905,3411],{"emptyLinePlaceholder":3410},[3144,15907,15908],{"class":3142,"line":3407},[3144,15909,15910],{"class":7200},"# Rollback до конкретної ревізії\n",[3144,15912,15913,15915,15917,15919,15921],{"class":3142,"line":3414},[3144,15914,10278],{"class":7663},[3144,15916,11764],{"class":7139},[3144,15918,11847],{"class":7139},[3144,15920,11252],{"class":7139},[3144,15922,15923],{"class":7647}," --to-revision=2\n",[3144,15925,15926],{"class":3142,"line":3420},[3144,15927,3411],{"emptyLinePlaceholder":3410},[3144,15929,15930],{"class":3142,"line":3426},[3144,15931,11757],{"class":7200},[3144,15933,15934,15936,15938,15940],{"class":3142,"line":3432},[3144,15935,10278],{"class":7663},[3144,15937,11764],{"class":7139},[3144,15939,11767],{"class":7139},[3144,15941,11770],{"class":7139},[3144,15943,15944],{"class":3142,"line":3438},[3144,15945,3411],{"emptyLinePlaceholder":3410},[3144,15947,15948],{"class":3142,"line":3444},[3144,15949,15950],{"class":7200},"# Відновити оновлення\n",[3144,15952,15953,15955,15957,15959],{"class":3142,"line":3449},[3144,15954,10278],{"class":7663},[3144,15956,11764],{"class":7139},[3144,15958,11827],{"class":7139},[3144,15960,11770],{"class":7139},[3291,15962],{},[3109,15964,15966],{"id":15965},"практичні-завдання","Практичні завдання",[3114,15968,15969],{},"Тепер виконайте завдання для закріплення знань про rolling updates.",[3122,15971,15973],{"id":15972},"завдання-1-експерименти-з-maxsurge-та-maxunavailable","Завдання 1: Експерименти з maxSurge та maxUnavailable",[3114,15975,15976,15978],{},[3118,15977,8854],{}," Зрозуміти, як різні комбінації параметрів впливають на швидкість та безпеку оновлення.",[3114,15980,15981],{},[3118,15982,15983],{},"Завдання:",[3926,15985,15986,15989,16012,16026],{},[3906,15987,15988],{},"Створіть Deployment з 5 репліками nginx",[3906,15990,15991,15992],{},"Виконайте 3 оновлення з різними налаштуваннями:",[3903,15993,15994,16000,16006],{},[3906,15995,15996,15999],{},[3349,15997,15998],{},"maxSurge: 0, maxUnavailable: 1"," (повільне, економне)",[3906,16001,16002,16005],{},[3349,16003,16004],{},"maxSurge: 1, maxUnavailable: 0"," (безпечне, zero-downtime)",[3906,16007,16008,16011],{},[3349,16009,16010],{},"maxSurge: 100%, maxUnavailable: 0"," (швидке, ресурсомістке)",[3906,16013,16014,16015],{},"Для кожного оновлення виміряйте:",[3903,16016,16017,16020,16023],{},[3906,16018,16019],{},"Час оновлення (від початку до завершення)",[3906,16021,16022],{},"Максимальну кількість Pod під час оновлення",[3906,16024,16025],{},"Мінімальну кількість доступних Pod",[3906,16027,16028],{},"Порівняйте результати",[3114,16030,16031,16034],{},[3118,16032,16033],{},"Очікуваний результат:"," Ви побачите, як різні налаштування впливають на швидкість та ресурси.",[16036,16037,16039,16044,16225,16230,16423,16428],"collapsible",{"title":16038},"Показати рішення",[3114,16040,16041],{},[3118,16042,16043],{},"deployment.yaml:",[3379,16045,16047],{"className":5650,"code":16046,"language":5652,"meta":3384,"style":3384},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx-test\nspec:\n  replicas: 5\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxSurge: 0\n      maxUnavailable: 1\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.25\n          ports:\n            - containerPort: 80\n",[3349,16048,16049,16057,16065,16071,16080,16086,16094,16100,16108,16114,16122,16130,16136,16142,16151,16157,16163,16169,16177,16183,16189,16199,16208,16214],{"__ignoreMap":3384},[3144,16050,16051,16053,16055],{"class":3142,"line":3389},[3144,16052,9066],{"class":5659},[3144,16054,5679],{"class":5663},[3144,16056,9071],{"class":5682},[3144,16058,16059,16061,16063],{"class":3142,"line":3395},[3144,16060,9076],{"class":5659},[3144,16062,5679],{"class":5663},[3144,16064,9081],{"class":5682},[3144,16066,16067,16069],{"class":3142,"line":3401},[3144,16068,9086],{"class":5659},[3144,16070,5664],{"class":5663},[3144,16072,16073,16075,16077],{"class":3142,"line":3407},[3144,16074,9093],{"class":5659},[3144,16076,5679],{"class":5663},[3144,16078,16079],{"class":5682},"nginx-test\n",[3144,16081,16082,16084],{"class":3142,"line":3414},[3144,16083,5660],{"class":5659},[3144,16085,5664],{"class":5663},[3144,16087,16088,16090,16092],{"class":3142,"line":3420},[3144,16089,9109],{"class":5659},[3144,16091,5679],{"class":5663},[3144,16093,9478],{"class":5700},[3144,16095,16096,16098],{"class":3142,"line":3426},[3144,16097,5669],{"class":5659},[3144,16099,5664],{"class":5663},[3144,16101,16102,16104,16106],{"class":3142,"line":3432},[3144,16103,5676],{"class":5659},[3144,16105,5679],{"class":5663},[3144,16107,5683],{"class":5682},[3144,16109,16110,16112],{"class":3142,"line":3438},[3144,16111,5688],{"class":5659},[3144,16113,5664],{"class":5663},[3144,16115,16116,16118,16120],{"class":3142,"line":3444},[3144,16117,5695],{"class":5659},[3144,16119,5679],{"class":5663},[3144,16121,6631],{"class":5700},[3144,16123,16124,16126,16128],{"class":3142,"line":3449},[3144,16125,5706],{"class":5659},[3144,16127,5679],{"class":5663},[3144,16129,5701],{"class":5700},[3144,16131,16132,16134],{"class":3142,"line":3455},[3144,16133,9119],{"class":5659},[3144,16135,5664],{"class":5663},[3144,16137,16138,16140],{"class":3142,"line":3461},[3144,16139,9126],{"class":5659},[3144,16141,5664],{"class":5663},[3144,16143,16144,16146,16148],{"class":3142,"line":3467},[3144,16145,9133],{"class":5659},[3144,16147,5679],{"class":5663},[3144,16149,16150],{"class":5682},"nginx\n",[3144,16152,16153,16155],{"class":3142,"line":3473},[3144,16154,9142],{"class":5659},[3144,16156,5664],{"class":5663},[3144,16158,16159,16161],{"class":3142,"line":3478},[3144,16160,9149],{"class":5659},[3144,16162,5664],{"class":5663},[3144,16164,16165,16167],{"class":3142,"line":3483},[3144,16166,9156],{"class":5659},[3144,16168,5664],{"class":5663},[3144,16170,16171,16173,16175],{"class":3142,"line":3489},[3144,16172,9163],{"class":5659},[3144,16174,5679],{"class":5663},[3144,16176,16150],{"class":5682},[3144,16178,16179,16181],{"class":3142,"line":3495},[3144,16180,9172],{"class":5659},[3144,16182,5664],{"class":5663},[3144,16184,16185,16187],{"class":3142,"line":3501},[3144,16186,9179],{"class":5659},[3144,16188,5664],{"class":5663},[3144,16190,16191,16193,16195,16197],{"class":3142,"line":3506},[3144,16192,9186],{"class":5663},[3144,16194,9189],{"class":5659},[3144,16196,5679],{"class":5663},[3144,16198,16150],{"class":5682},[3144,16200,16201,16203,16205],{"class":3142,"line":3512},[3144,16202,9198],{"class":5659},[3144,16204,5679],{"class":5663},[3144,16206,16207],{"class":5682},"nginx:1.25\n",[3144,16209,16210,16212],{"class":3142,"line":3518},[3144,16211,9208],{"class":5659},[3144,16213,5664],{"class":5663},[3144,16215,16216,16218,16220,16222],{"class":3142,"line":3524},[3144,16217,9215],{"class":5663},[3144,16219,9218],{"class":5659},[3144,16221,5679],{"class":5663},[3144,16223,16224],{"class":5700},"80\n",[3114,16226,16227],{},[3118,16228,16229],{},"Команди:",[3379,16231,16233],{"className":10269,"code":16232,"language":10271,"meta":3384,"style":3384},"# Створення\nkubectl apply -f deployment.yaml\n\n# Тест 1: maxSurge=0, maxUnavailable=1\ntime kubectl set image deployment/nginx-test nginx=nginx:1.26\nkubectl get pods -l app=nginx -w  # Спостерігати\n\n# Тест 2: maxSurge=1, maxUnavailable=0\nkubectl patch deployment nginx-test -p '{\"spec\":{\"strategy\":{\"rollingUpdate\":{\"maxSurge\":1,\"maxUnavailable\":0}}}}'\ntime kubectl set image deployment/nginx-test nginx=nginx:1.27\nkubectl get pods -l app=nginx -w\n\n# Тест 3: maxSurge=100%, maxUnavailable=0\nkubectl patch deployment nginx-test -p '{\"spec\":{\"strategy\":{\"rollingUpdate\":{\"maxSurge\":\"100%\",\"maxUnavailable\":0}}}}'\ntime kubectl set image deployment/nginx-test nginx=nginx:1.25\nkubectl get pods -l app=nginx -w\n\n# Очищення\nkubectl delete deployment nginx-test\n",[3349,16234,16235,16240,16253,16257,16262,16279,16298,16302,16307,16325,16338,16352,16356,16361,16376,16388,16402,16406,16411],{"__ignoreMap":3384},[3144,16236,16237],{"class":3142,"line":3389},[3144,16238,16239],{"class":7200},"# Створення\n",[3144,16241,16242,16244,16247,16250],{"class":3142,"line":3395},[3144,16243,10278],{"class":7663},[3144,16245,16246],{"class":7139}," apply",[3144,16248,16249],{"class":7647}," -f",[3144,16251,16252],{"class":7139}," deployment.yaml\n",[3144,16254,16255],{"class":3142,"line":3401},[3144,16256,3411],{"emptyLinePlaceholder":3410},[3144,16258,16259],{"class":3142,"line":3407},[3144,16260,16261],{"class":7200},"# Тест 1: maxSurge=0, maxUnavailable=1\n",[3144,16263,16264,16267,16270,16273,16276],{"class":3142,"line":3414},[3144,16265,16266],{"class":7647},"time",[3144,16268,16269],{"class":5663}," kubectl set image deployment/nginx-test ",[3144,16271,16272],{"class":7651},"nginx",[3144,16274,16275],{"class":5663},"=",[3144,16277,16278],{"class":7139},"nginx:1.26\n",[3144,16280,16281,16283,16285,16287,16289,16292,16295],{"class":3142,"line":3420},[3144,16282,10278],{"class":7663},[3144,16284,14736],{"class":7139},[3144,16286,15643],{"class":7139},[3144,16288,15646],{"class":7647},[3144,16290,16291],{"class":7139}," app=nginx",[3144,16293,16294],{"class":7647}," -w",[3144,16296,16297],{"class":7200},"  # Спостерігати\n",[3144,16299,16300],{"class":3142,"line":3426},[3144,16301,3411],{"emptyLinePlaceholder":3410},[3144,16303,16304],{"class":3142,"line":3432},[3144,16305,16306],{"class":7200},"# Тест 2: maxSurge=1, maxUnavailable=0\n",[3144,16308,16309,16311,16314,16316,16319,16322],{"class":3142,"line":3438},[3144,16310,10278],{"class":7663},[3144,16312,16313],{"class":7139}," patch",[3144,16315,15540],{"class":7139},[3144,16317,16318],{"class":7139}," nginx-test",[3144,16320,16321],{"class":7647}," -p",[3144,16323,16324],{"class":7139}," '{\"spec\":{\"strategy\":{\"rollingUpdate\":{\"maxSurge\":1,\"maxUnavailable\":0}}}}'\n",[3144,16326,16327,16329,16331,16333,16335],{"class":3142,"line":3444},[3144,16328,16266],{"class":7647},[3144,16330,16269],{"class":5663},[3144,16332,16272],{"class":7651},[3144,16334,16275],{"class":5663},[3144,16336,16337],{"class":7139},"nginx:1.27\n",[3144,16339,16340,16342,16344,16346,16348,16350],{"class":3142,"line":3449},[3144,16341,10278],{"class":7663},[3144,16343,14736],{"class":7139},[3144,16345,15643],{"class":7139},[3144,16347,15646],{"class":7647},[3144,16349,16291],{"class":7139},[3144,16351,15674],{"class":7647},[3144,16353,16354],{"class":3142,"line":3455},[3144,16355,3411],{"emptyLinePlaceholder":3410},[3144,16357,16358],{"class":3142,"line":3461},[3144,16359,16360],{"class":7200},"# Тест 3: maxSurge=100%, maxUnavailable=0\n",[3144,16362,16363,16365,16367,16369,16371,16373],{"class":3142,"line":3467},[3144,16364,10278],{"class":7663},[3144,16366,16313],{"class":7139},[3144,16368,15540],{"class":7139},[3144,16370,16318],{"class":7139},[3144,16372,16321],{"class":7647},[3144,16374,16375],{"class":7139}," '{\"spec\":{\"strategy\":{\"rollingUpdate\":{\"maxSurge\":\"100%\",\"maxUnavailable\":0}}}}'\n",[3144,16377,16378,16380,16382,16384,16386],{"class":3142,"line":3473},[3144,16379,16266],{"class":7647},[3144,16381,16269],{"class":5663},[3144,16383,16272],{"class":7651},[3144,16385,16275],{"class":5663},[3144,16387,16207],{"class":7139},[3144,16389,16390,16392,16394,16396,16398,16400],{"class":3142,"line":3478},[3144,16391,10278],{"class":7663},[3144,16393,14736],{"class":7139},[3144,16395,15643],{"class":7139},[3144,16397,15646],{"class":7647},[3144,16399,16291],{"class":7139},[3144,16401,15674],{"class":7647},[3144,16403,16404],{"class":3142,"line":3483},[3144,16405,3411],{"emptyLinePlaceholder":3410},[3144,16407,16408],{"class":3142,"line":3489},[3144,16409,16410],{"class":7200},"# Очищення\n",[3144,16412,16413,16415,16418,16420],{"class":3142,"line":3495},[3144,16414,10278],{"class":7663},[3144,16416,16417],{"class":7139}," delete",[3144,16419,15540],{"class":7139},[3144,16421,16422],{"class":7139}," nginx-test\n",[3114,16424,16425],{},[3118,16426,16427],{},"Очікувані результати:",[6845,16429,16430,16446],{},[6848,16431,16432],{},[6851,16433,16434,16437,16440,16443],{},[6854,16435,16436],{},"Конфігурація",[6854,16438,16439],{},"Час оновлення",[6854,16441,16442],{},"Max Pod",[6854,16444,16445],{},"Min Available",[6868,16447,16448,16461,16474],{},[6851,16449,16450,16453,16456,16458],{},[6873,16451,16452],{},"maxSurge=0, maxUnavailable=1",[6873,16454,16455],{},"~60s",[6873,16457,9301],{},[6873,16459,16460],{},"4",[6851,16462,16463,16466,16469,16472],{},[6873,16464,16465],{},"maxSurge=1, maxUnavailable=0",[6873,16467,16468],{},"~75s",[6873,16470,16471],{},"6",[6873,16473,9301],{},[6851,16475,16476,16479,16482,16484],{},[6873,16477,16478],{},"maxSurge=100%, maxUnavailable=0",[6873,16480,16481],{},"~30s",[6873,16483,6883],{},[6873,16485,9301],{},[3291,16487],{},[3122,16489,16491],{"id":16490},"завдання-2-симуляція-невдалого-оновлення","Завдання 2: Симуляція невдалого оновлення",[3114,16493,16494,16496],{},[3118,16495,8854],{}," Навчитись діагностувати та виправляти проблеми при rolling update.",[3114,16498,16499],{},[3118,16500,15983],{},[3926,16502,16503,16509,16512,16517,16520],{},[3906,16504,16505,16506,5602],{},"Створіть Deployment з образом, який не існує (наприклад, ",[3349,16507,16508],{},"nginx:nonexistent",[3906,16510,16511],{},"Спостерігайте, як Kubernetes намагається оновити Pod",[3906,16513,16514,16515],{},"Діагностуйте проблему через ",[3349,16516,10223],{},[3906,16518,16519],{},"Виконайте rollback до робочої версії",[3906,16521,16522],{},"Перевірте, що застосунок знову працює",[3114,16524,16525,16527],{},[3118,16526,16033],{}," Ви навчитесь виявляти проблеми з образами та швидко відкочувати оновлення.",[16036,16529,16530,16534,16819,16823],{"title":16038},[3114,16531,16532],{},[3118,16533,16229],{},[3379,16535,16537],{"className":10269,"code":16536,"language":10271,"meta":3384,"style":3384},"# Створення робочого Deployment\nkubectl create deployment nginx-test --image=nginx:1.27 --replicas=3\n\n# Перевірка, що все працює\nkubectl get pods -l app=nginx-test\n# NAME                          READY   STATUS    RESTARTS   AGE\n# nginx-test-xxx-yyy            1/1     Running   0          10s\n# nginx-test-xxx-zzz            1/1     Running   0          10s\n# nginx-test-xxx-www            1/1     Running   0          10s\n\n# Спроба оновлення на неіснуючий образ\nkubectl set image deployment/nginx-test nginx=nginx:nonexistent --record\n\n# Спостереження за процесом (оновлення зависне)\nkubectl rollout status deployment/nginx-test\n# Waiting for deployment \"nginx-test\" rollout to finish: 1 out of 3 new replicas have been updated...\n\n# Перегляд Pod (новий Pod не може стартувати)\nkubectl get pods -l app=nginx-test\n# NAME                          READY   STATUS             RESTARTS   AGE\n# nginx-test-xxx-yyy            1/1     Running            0          2m\n# nginx-test-xxx-zzz            1/1     Running            0          2m\n# nginx-test-aaa-bbb            0/1     ImagePullBackOff   0          30s\n\n# Діагностика проблеми\nkubectl describe pod nginx-test-aaa-bbb\n# Events:\n#   Warning  Failed     30s   Failed to pull image \"nginx:nonexistent\": rpc error: code = NotFound desc = manifest for nginx:nonexistent not found\n\n# Rollback до попередньої версії\nkubectl rollout undo deployment/nginx-test\n\n# Перевірка, що rollback успішний\nkubectl rollout status deployment/nginx-test\n# deployment \"nginx-test\" successfully rolled out\n\nkubectl get pods -l app=nginx-test\n# NAME                          READY   STATUS    RESTARTS   AGE\n# nginx-test-xxx-yyy            1/1     Running   0          3m\n# nginx-test-xxx-zzz            1/1     Running   0          3m\n# nginx-test-xxx-www            1/1     Running   0          3m\n\n# Очищення\nkubectl delete deployment nginx-test\n",[3349,16538,16539,16544,16561,16565,16570,16583,16588,16593,16598,16603,16607,16612,16629,16633,16638,16649,16654,16658,16663,16675,16680,16685,16690,16695,16699,16704,16715,16719,16724,16728,16732,16742,16746,16751,16761,16766,16770,16782,16786,16791,16796,16801,16805,16809],{"__ignoreMap":3384},[3144,16540,16541],{"class":3142,"line":3389},[3144,16542,16543],{"class":7200},"# Створення робочого Deployment\n",[3144,16545,16546,16548,16551,16553,16555,16558],{"class":3142,"line":3395},[3144,16547,10278],{"class":7663},[3144,16549,16550],{"class":7139}," create",[3144,16552,15540],{"class":7139},[3144,16554,16318],{"class":7139},[3144,16556,16557],{"class":7647}," --image=nginx:1.27",[3144,16559,16560],{"class":7647}," --replicas=3\n",[3144,16562,16563],{"class":3142,"line":3401},[3144,16564,3411],{"emptyLinePlaceholder":3410},[3144,16566,16567],{"class":3142,"line":3407},[3144,16568,16569],{"class":7200},"# Перевірка, що все працює\n",[3144,16571,16572,16574,16576,16578,16580],{"class":3142,"line":3414},[3144,16573,10278],{"class":7663},[3144,16575,14736],{"class":7139},[3144,16577,15643],{"class":7139},[3144,16579,15646],{"class":7647},[3144,16581,16582],{"class":7139}," app=nginx-test\n",[3144,16584,16585],{"class":3142,"line":3420},[3144,16586,16587],{"class":7200},"# NAME                          READY   STATUS    RESTARTS   AGE\n",[3144,16589,16590],{"class":3142,"line":3426},[3144,16591,16592],{"class":7200},"# nginx-test-xxx-yyy            1/1     Running   0          10s\n",[3144,16594,16595],{"class":3142,"line":3432},[3144,16596,16597],{"class":7200},"# nginx-test-xxx-zzz            1/1     Running   0          10s\n",[3144,16599,16600],{"class":3142,"line":3438},[3144,16601,16602],{"class":7200},"# nginx-test-xxx-www            1/1     Running   0          10s\n",[3144,16604,16605],{"class":3142,"line":3444},[3144,16606,3411],{"emptyLinePlaceholder":3410},[3144,16608,16609],{"class":3142,"line":3449},[3144,16610,16611],{"class":7200},"# Спроба оновлення на неіснуючий образ\n",[3144,16613,16614,16616,16618,16620,16623,16626],{"class":3142,"line":3455},[3144,16615,10278],{"class":7663},[3144,16617,11246],{"class":7139},[3144,16619,11249],{"class":7139},[3144,16621,16622],{"class":7139}," deployment/nginx-test",[3144,16624,16625],{"class":7139}," nginx=nginx:nonexistent",[3144,16627,16628],{"class":7647}," --record\n",[3144,16630,16631],{"class":3142,"line":3461},[3144,16632,3411],{"emptyLinePlaceholder":3410},[3144,16634,16635],{"class":3142,"line":3467},[3144,16636,16637],{"class":7200},"# Спостереження за процесом (оновлення зависне)\n",[3144,16639,16640,16642,16644,16646],{"class":3142,"line":3473},[3144,16641,10278],{"class":7663},[3144,16643,11764],{"class":7139},[3144,16645,8724],{"class":7139},[3144,16647,16648],{"class":7139}," deployment/nginx-test\n",[3144,16650,16651],{"class":3142,"line":3478},[3144,16652,16653],{"class":7200},"# Waiting for deployment \"nginx-test\" rollout to finish: 1 out of 3 new replicas have been updated...\n",[3144,16655,16656],{"class":3142,"line":3483},[3144,16657,3411],{"emptyLinePlaceholder":3410},[3144,16659,16660],{"class":3142,"line":3489},[3144,16661,16662],{"class":7200},"# Перегляд Pod (новий Pod не може стартувати)\n",[3144,16664,16665,16667,16669,16671,16673],{"class":3142,"line":3495},[3144,16666,10278],{"class":7663},[3144,16668,14736],{"class":7139},[3144,16670,15643],{"class":7139},[3144,16672,15646],{"class":7647},[3144,16674,16582],{"class":7139},[3144,16676,16677],{"class":3142,"line":3501},[3144,16678,16679],{"class":7200},"# NAME                          READY   STATUS             RESTARTS   AGE\n",[3144,16681,16682],{"class":3142,"line":3506},[3144,16683,16684],{"class":7200},"# nginx-test-xxx-yyy            1/1     Running            0          2m\n",[3144,16686,16687],{"class":3142,"line":3512},[3144,16688,16689],{"class":7200},"# nginx-test-xxx-zzz            1/1     Running            0          2m\n",[3144,16691,16692],{"class":3142,"line":3518},[3144,16693,16694],{"class":7200},"# nginx-test-aaa-bbb            0/1     ImagePullBackOff   0          30s\n",[3144,16696,16697],{"class":3142,"line":3524},[3144,16698,3411],{"emptyLinePlaceholder":3410},[3144,16700,16701],{"class":3142,"line":3530},[3144,16702,16703],{"class":7200},"# Діагностика проблеми\n",[3144,16705,16706,16708,16710,16712],{"class":3142,"line":3535},[3144,16707,10278],{"class":7663},[3144,16709,10281],{"class":7139},[3144,16711,10284],{"class":7139},[3144,16713,16714],{"class":7139}," nginx-test-aaa-bbb\n",[3144,16716,16717],{"class":3142,"line":3705},[3144,16718,14814],{"class":7200},[3144,16720,16721],{"class":3142,"line":3711},[3144,16722,16723],{"class":7200},"#   Warning  Failed     30s   Failed to pull image \"nginx:nonexistent\": rpc error: code = NotFound desc = manifest for nginx:nonexistent not found\n",[3144,16725,16726],{"class":3142,"line":3717},[3144,16727,3411],{"emptyLinePlaceholder":3410},[3144,16729,16730],{"class":3142,"line":3722},[3144,16731,15891],{"class":7200},[3144,16733,16734,16736,16738,16740],{"class":3142,"line":3727},[3144,16735,10278],{"class":7663},[3144,16737,11764],{"class":7139},[3144,16739,11847],{"class":7139},[3144,16741,16648],{"class":7139},[3144,16743,16744],{"class":3142,"line":4084},[3144,16745,3411],{"emptyLinePlaceholder":3410},[3144,16747,16748],{"class":3142,"line":4090},[3144,16749,16750],{"class":7200},"# Перевірка, що rollback успішний\n",[3144,16752,16753,16755,16757,16759],{"class":3142,"line":4096},[3144,16754,10278],{"class":7663},[3144,16756,11764],{"class":7139},[3144,16758,8724],{"class":7139},[3144,16760,16648],{"class":7139},[3144,16762,16763],{"class":3142,"line":4102},[3144,16764,16765],{"class":7200},"# deployment \"nginx-test\" successfully rolled out\n",[3144,16767,16768],{"class":3142,"line":4107},[3144,16769,3411],{"emptyLinePlaceholder":3410},[3144,16771,16772,16774,16776,16778,16780],{"class":3142,"line":4112},[3144,16773,10278],{"class":7663},[3144,16775,14736],{"class":7139},[3144,16777,15643],{"class":7139},[3144,16779,15646],{"class":7647},[3144,16781,16582],{"class":7139},[3144,16783,16784],{"class":3142,"line":4118},[3144,16785,16587],{"class":7200},[3144,16787,16788],{"class":3142,"line":4124},[3144,16789,16790],{"class":7200},"# nginx-test-xxx-yyy            1/1     Running   0          3m\n",[3144,16792,16793],{"class":3142,"line":4130},[3144,16794,16795],{"class":7200},"# nginx-test-xxx-zzz            1/1     Running   0          3m\n",[3144,16797,16798],{"class":3142,"line":4135},[3144,16799,16800],{"class":7200},"# nginx-test-xxx-www            1/1     Running   0          3m\n",[3144,16802,16803],{"class":3142,"line":4140},[3144,16804,3411],{"emptyLinePlaceholder":3410},[3144,16806,16807],{"class":3142,"line":5169},[3144,16808,16410],{"class":7200},[3144,16810,16811,16813,16815,16817],{"class":3142,"line":5174},[3144,16812,10278],{"class":7663},[3144,16814,16417],{"class":7139},[3144,16816,15540],{"class":7139},[3144,16818,16422],{"class":7139},[3114,16820,16821],{},[3118,16822,5589],{},[3926,16824,16825,16831,16841,16844],{},[3906,16826,7418,16827,16830],{},[3118,16828,16829],{},"не відкочує автоматично"," — старі Pod продовжують працювати",[3906,16832,16833,16834,16837,16838],{},"Нові Pod застрягають у стані ",[3349,16835,16836],{},"ImagePullBackOff"," або ",[3349,16839,16840],{},"ErrImagePull",[3906,16842,16843],{},"Rollback виконується швидко (10-15 секунд), бо образ вже є на вузлах",[3906,16845,16846],{},"Сервіс залишається доступним весь час (старі Pod працюють)",[3291,16848],{},[3122,16850,16852],{"id":16851},"завдання-3-canary-deployment","Завдання 3: Canary Deployment",[3114,16854,16855,16857],{},[3118,16856,8854],{}," Навчитись виконувати canary deployment — оновлення з поступовою перевіркою.",[3114,16859,16860],{},[3118,16861,15983],{},[3926,16863,16864,16867,16870,16873,16876,16879],{},[3906,16865,16866],{},"Створіть Deployment з 10 репліками nginx:1.25",[3906,16868,16869],{},"Призупиніть rollout",[3906,16871,16872],{},"Оновіть образ на nginx:1.26 (створюється лише 1 новий Pod)",[3906,16874,16875],{},"Перевірте метрики нового Pod (логи, CPU, пам'ять)",[3906,16877,16878],{},"Якщо все добре — відновіть rollout для оновлення решти Pod",[3906,16880,16881],{},"Якщо є проблеми — виконайте rollback",[3114,16883,16884,16886],{},[3118,16885,16033],{}," Ви навчитесь безпечно оновлювати застосунки, перевіряючи нову версію на малій кількості Pod перед повним rollout.",[16036,16888,16889,16893,17221,17226,17265,17270],{"title":16038},[3114,16890,16891],{},[3118,16892,16229],{},[3379,16894,16896],{"className":10269,"code":16895,"language":10271,"meta":3384,"style":3384},"# Створення Deployment з 10 репліками\nkubectl create deployment nginx-canary --image=nginx:1.25 --replicas=10\n\n# Перевірка, що все працює\nkubectl get pods -l app=nginx-canary\n# 10 Pod у стані Running\n\n# Призупинити rollout\nkubectl rollout pause deployment/nginx-canary\n\n# Оновити образ (створюється лише 1 новий Pod через maxSurge)\nkubectl set image deployment/nginx-canary nginx=nginx:1.26 --record\n\n# Спостереження (лише 1 новий Pod створюється)\nkubectl get pods -l app=nginx-canary\n# NAME                            READY   STATUS    RESTARTS   AGE\n# nginx-canary-abc123-xxx         1/1     Running   0          2m  (старий)\n# nginx-canary-abc123-yyy         1/1     Running   0          2m  (старий)\n# ...\n# nginx-canary-def456-aaa         1/1     Running   0          10s (новий!)\n\n# Перевірка нового Pod\nNEW_POD=$(kubectl get pods -l app=nginx-canary -o jsonpath='{.items[0].metadata.name}')\n\n# Логи\nkubectl logs $NEW_POD\n\n# Ресурси\nkubectl top pod $NEW_POD\n\n# Тестування\nkubectl exec -it $NEW_POD -- curl localhost\n\n# Якщо все добре — продовжити rollout\nkubectl rollout resume deployment/nginx-canary\n\n# Спостереження за повним оновленням\nkubectl rollout status deployment/nginx-canary\n\n# Перевірка, що всі Pod оновлені\nkubectl get pods -l app=nginx-canary\n# Всі Pod мають новий hash (def456)\n\n# Очищення\nkubectl delete deployment nginx-canary\n",[3349,16897,16898,16903,16920,16924,16928,16941,16946,16950,16955,16966,16970,16975,16991,16995,17000,17012,17017,17022,17027,17032,17037,17041,17046,17073,17077,17082,17091,17095,17100,17110,17114,17119,17138,17142,17147,17157,17161,17166,17176,17180,17185,17197,17202,17206,17210],{"__ignoreMap":3384},[3144,16899,16900],{"class":3142,"line":3389},[3144,16901,16902],{"class":7200},"# Створення Deployment з 10 репліками\n",[3144,16904,16905,16907,16909,16911,16914,16917],{"class":3142,"line":3395},[3144,16906,10278],{"class":7663},[3144,16908,16550],{"class":7139},[3144,16910,15540],{"class":7139},[3144,16912,16913],{"class":7139}," nginx-canary",[3144,16915,16916],{"class":7647}," --image=nginx:1.25",[3144,16918,16919],{"class":7647}," --replicas=10\n",[3144,16921,16922],{"class":3142,"line":3401},[3144,16923,3411],{"emptyLinePlaceholder":3410},[3144,16925,16926],{"class":3142,"line":3407},[3144,16927,16569],{"class":7200},[3144,16929,16930,16932,16934,16936,16938],{"class":3142,"line":3414},[3144,16931,10278],{"class":7663},[3144,16933,14736],{"class":7139},[3144,16935,15643],{"class":7139},[3144,16937,15646],{"class":7647},[3144,16939,16940],{"class":7139}," app=nginx-canary\n",[3144,16942,16943],{"class":3142,"line":3420},[3144,16944,16945],{"class":7200},"# 10 Pod у стані Running\n",[3144,16947,16948],{"class":3142,"line":3426},[3144,16949,3411],{"emptyLinePlaceholder":3410},[3144,16951,16952],{"class":3142,"line":3432},[3144,16953,16954],{"class":7200},"# Призупинити rollout\n",[3144,16956,16957,16959,16961,16963],{"class":3142,"line":3438},[3144,16958,10278],{"class":7663},[3144,16960,11764],{"class":7139},[3144,16962,11767],{"class":7139},[3144,16964,16965],{"class":7139}," deployment/nginx-canary\n",[3144,16967,16968],{"class":3142,"line":3444},[3144,16969,3411],{"emptyLinePlaceholder":3410},[3144,16971,16972],{"class":3142,"line":3449},[3144,16973,16974],{"class":7200},"# Оновити образ (створюється лише 1 новий Pod через maxSurge)\n",[3144,16976,16977,16979,16981,16983,16986,16989],{"class":3142,"line":3455},[3144,16978,10278],{"class":7663},[3144,16980,11246],{"class":7139},[3144,16982,11249],{"class":7139},[3144,16984,16985],{"class":7139}," deployment/nginx-canary",[3144,16987,16988],{"class":7139}," nginx=nginx:1.26",[3144,16990,16628],{"class":7647},[3144,16992,16993],{"class":3142,"line":3461},[3144,16994,3411],{"emptyLinePlaceholder":3410},[3144,16996,16997],{"class":3142,"line":3467},[3144,16998,16999],{"class":7200},"# Спостереження (лише 1 новий Pod створюється)\n",[3144,17001,17002,17004,17006,17008,17010],{"class":3142,"line":3473},[3144,17003,10278],{"class":7663},[3144,17005,14736],{"class":7139},[3144,17007,15643],{"class":7139},[3144,17009,15646],{"class":7647},[3144,17011,16940],{"class":7139},[3144,17013,17014],{"class":3142,"line":3478},[3144,17015,17016],{"class":7200},"# NAME                            READY   STATUS    RESTARTS   AGE\n",[3144,17018,17019],{"class":3142,"line":3483},[3144,17020,17021],{"class":7200},"# nginx-canary-abc123-xxx         1/1     Running   0          2m  (старий)\n",[3144,17023,17024],{"class":3142,"line":3489},[3144,17025,17026],{"class":7200},"# nginx-canary-abc123-yyy         1/1     Running   0          2m  (старий)\n",[3144,17028,17029],{"class":3142,"line":3495},[3144,17030,17031],{"class":7200},"# ...\n",[3144,17033,17034],{"class":3142,"line":3501},[3144,17035,17036],{"class":7200},"# nginx-canary-def456-aaa         1/1     Running   0          10s (новий!)\n",[3144,17038,17039],{"class":3142,"line":3506},[3144,17040,3411],{"emptyLinePlaceholder":3410},[3144,17042,17043],{"class":3142,"line":3512},[3144,17044,17045],{"class":7200},"# Перевірка нового Pod\n",[3144,17047,17048,17051,17054,17056,17058,17060,17062,17065,17068,17071],{"class":3142,"line":3518},[3144,17049,17050],{"class":7651},"NEW_POD",[3144,17052,17053],{"class":5663},"=$(",[3144,17055,10278],{"class":7663},[3144,17057,14736],{"class":7139},[3144,17059,15643],{"class":7139},[3144,17061,15646],{"class":7647},[3144,17063,17064],{"class":7139}," app=nginx-canary",[3144,17066,17067],{"class":7647}," -o",[3144,17069,17070],{"class":7139}," jsonpath='{.items[0].metadata.name}'",[3144,17072,8237],{"class":5663},[3144,17074,17075],{"class":3142,"line":3524},[3144,17076,3411],{"emptyLinePlaceholder":3410},[3144,17078,17079],{"class":3142,"line":3530},[3144,17080,17081],{"class":7200},"# Логи\n",[3144,17083,17084,17086,17088],{"class":3142,"line":3535},[3144,17085,10278],{"class":7663},[3144,17087,15713],{"class":7139},[3144,17089,17090],{"class":7651}," $NEW_POD\n",[3144,17092,17093],{"class":3142,"line":3705},[3144,17094,3411],{"emptyLinePlaceholder":3410},[3144,17096,17097],{"class":3142,"line":3711},[3144,17098,17099],{"class":7200},"# Ресурси\n",[3144,17101,17102,17104,17106,17108],{"class":3142,"line":3717},[3144,17103,10278],{"class":7663},[3144,17105,15874],{"class":7139},[3144,17107,10284],{"class":7139},[3144,17109,17090],{"class":7651},[3144,17111,17112],{"class":3142,"line":3722},[3144,17113,3411],{"emptyLinePlaceholder":3410},[3144,17115,17116],{"class":3142,"line":3727},[3144,17117,17118],{"class":7200},"# Тестування\n",[3144,17120,17121,17123,17125,17127,17130,17133,17135],{"class":3142,"line":4084},[3144,17122,10278],{"class":7663},[3144,17124,15762],{"class":7139},[3144,17126,15765],{"class":7647},[3144,17128,17129],{"class":7651}," $NEW_POD",[3144,17131,17132],{"class":7647}," --",[3144,17134,15805],{"class":7139},[3144,17136,17137],{"class":7139}," localhost\n",[3144,17139,17140],{"class":3142,"line":4090},[3144,17141,3411],{"emptyLinePlaceholder":3410},[3144,17143,17144],{"class":3142,"line":4096},[3144,17145,17146],{"class":7200},"# Якщо все добре — продовжити rollout\n",[3144,17148,17149,17151,17153,17155],{"class":3142,"line":4102},[3144,17150,10278],{"class":7663},[3144,17152,11764],{"class":7139},[3144,17154,11827],{"class":7139},[3144,17156,16965],{"class":7139},[3144,17158,17159],{"class":3142,"line":4107},[3144,17160,3411],{"emptyLinePlaceholder":3410},[3144,17162,17163],{"class":3142,"line":4112},[3144,17164,17165],{"class":7200},"# Спостереження за повним оновленням\n",[3144,17167,17168,17170,17172,17174],{"class":3142,"line":4118},[3144,17169,10278],{"class":7663},[3144,17171,11764],{"class":7139},[3144,17173,8724],{"class":7139},[3144,17175,16965],{"class":7139},[3144,17177,17178],{"class":3142,"line":4124},[3144,17179,3411],{"emptyLinePlaceholder":3410},[3144,17181,17182],{"class":3142,"line":4130},[3144,17183,17184],{"class":7200},"# Перевірка, що всі Pod оновлені\n",[3144,17186,17187,17189,17191,17193,17195],{"class":3142,"line":4135},[3144,17188,10278],{"class":7663},[3144,17190,14736],{"class":7139},[3144,17192,15643],{"class":7139},[3144,17194,15646],{"class":7647},[3144,17196,16940],{"class":7139},[3144,17198,17199],{"class":3142,"line":4140},[3144,17200,17201],{"class":7200},"# Всі Pod мають новий hash (def456)\n",[3144,17203,17204],{"class":3142,"line":5169},[3144,17205,3411],{"emptyLinePlaceholder":3410},[3144,17207,17208],{"class":3142,"line":5174},[3144,17209,16410],{"class":7200},[3144,17211,17212,17214,17216,17218],{"class":3142,"line":5179},[3144,17213,10278],{"class":7663},[3144,17215,16417],{"class":7139},[3144,17217,15540],{"class":7139},[3144,17219,17220],{"class":7139}," nginx-canary\n",[3114,17222,17223],{},[3118,17224,17225],{},"Альтернатива: Rollback якщо є проблеми",[3379,17227,17229],{"className":10269,"code":17228,"language":10271,"meta":3384,"style":3384},"# Якщо новий Pod має проблеми\nkubectl rollout undo deployment/nginx-canary\n\n# Відновити rollout (щоб undo застосувався)\nkubectl rollout resume deployment/nginx-canary\n",[3349,17230,17231,17236,17246,17250,17255],{"__ignoreMap":3384},[3144,17232,17233],{"class":3142,"line":3389},[3144,17234,17235],{"class":7200},"# Якщо новий Pod має проблеми\n",[3144,17237,17238,17240,17242,17244],{"class":3142,"line":3395},[3144,17239,10278],{"class":7663},[3144,17241,11764],{"class":7139},[3144,17243,11847],{"class":7139},[3144,17245,16965],{"class":7139},[3144,17247,17248],{"class":3142,"line":3401},[3144,17249,3411],{"emptyLinePlaceholder":3410},[3144,17251,17252],{"class":3142,"line":3407},[3144,17253,17254],{"class":7200},"# Відновити rollout (щоб undo застосувався)\n",[3144,17256,17257,17259,17261,17263],{"class":3142,"line":3414},[3144,17258,10278],{"class":7663},[3144,17260,11764],{"class":7139},[3144,17262,11827],{"class":7139},[3144,17264,16965],{"class":7139},[3114,17266,17267],{},[3118,17268,17269],{},"Переваги canary deployment:",[3926,17271,17272,17275,17278,17281],{},[3906,17273,17274],{},"Ризик мінімальний — лише 1 Pod з новою версією",[3906,17276,17277],{},"Можна перевірити метрики, логи, поведінку під навантаженням",[3906,17279,17280],{},"Якщо є проблеми — вплив на 10% трафіку (1 з 10 Pod)",[3906,17282,17283],{},"Швидкий rollback — більшість Pod ще на старій версії",[3291,17285],{},[3122,17287,17289],{"id":17288},"завдання-4-blue-green-deployment","Завдання 4: Blue-Green Deployment",[3114,17291,17292,17294],{},[3118,17293,8854],{}," Навчитись виконувати blue-green deployment — миттєве перемикання між версіями.",[3114,17296,17297],{},[3118,17298,15983],{},[3926,17300,17301,17312,17317,17320,17325,17328],{},[3906,17302,17303,17304,17307,17308,17311],{},"Створіть два Deployment: ",[3349,17305,17306],{},"app-blue"," (v1.0) та ",[3349,17309,17310],{},"app-green"," (v2.0)",[3906,17313,17314,17315],{},"Створіть Service, який спрямовує трафік на ",[3349,17316,17306],{},[3906,17318,17319],{},"Перевірте, що трафік йде на v1.0",[3906,17321,17322,17323],{},"Змініть Service selector на ",[3349,17324,17310],{},[3906,17326,17327],{},"Перевірте, що трафік миттєво перемкнувся на v2.0",[3906,17329,17330,17331],{},"Якщо є проблеми — поверніть selector на ",[3349,17332,17306],{},[3114,17334,17335,17337],{},[3118,17336,16033],{}," Ви навчитесь виконувати миттєве перемикання між версіями без rolling update.",[16036,17339,17340,17345,17535,17540,17724,17729,17837,17841,18103,18108,18133,18137],{"title":16038},[3114,17341,17342],{},[3118,17343,17344],{},"app-blue-deployment.yaml:",[3379,17346,17348],{"className":5650,"code":17347,"language":5652,"meta":3384,"style":3384},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: app-blue\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: myapp\n      version: blue\n  template:\n    metadata:\n      labels:\n        app: myapp\n        version: blue\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.25\n          ports:\n            - containerPort: 80\n          env:\n            - name: VERSION\n              value: \"1.0.0\"\n",[3349,17349,17350,17358,17366,17372,17381,17387,17395,17401,17407,17416,17426,17432,17438,17444,17452,17461,17467,17473,17483,17491,17497,17507,17514,17525],{"__ignoreMap":3384},[3144,17351,17352,17354,17356],{"class":3142,"line":3389},[3144,17353,9066],{"class":5659},[3144,17355,5679],{"class":5663},[3144,17357,9071],{"class":5682},[3144,17359,17360,17362,17364],{"class":3142,"line":3395},[3144,17361,9076],{"class":5659},[3144,17363,5679],{"class":5663},[3144,17365,9081],{"class":5682},[3144,17367,17368,17370],{"class":3142,"line":3401},[3144,17369,9086],{"class":5659},[3144,17371,5664],{"class":5663},[3144,17373,17374,17376,17378],{"class":3142,"line":3407},[3144,17375,9093],{"class":5659},[3144,17377,5679],{"class":5663},[3144,17379,17380],{"class":5682},"app-blue\n",[3144,17382,17383,17385],{"class":3142,"line":3414},[3144,17384,5660],{"class":5659},[3144,17386,5664],{"class":5663},[3144,17388,17389,17391,17393],{"class":3142,"line":3420},[3144,17390,9109],{"class":5659},[3144,17392,5679],{"class":5663},[3144,17394,9114],{"class":5700},[3144,17396,17397,17399],{"class":3142,"line":3426},[3144,17398,9119],{"class":5659},[3144,17400,5664],{"class":5663},[3144,17402,17403,17405],{"class":3142,"line":3432},[3144,17404,9126],{"class":5659},[3144,17406,5664],{"class":5663},[3144,17408,17409,17411,17413],{"class":3142,"line":3438},[3144,17410,9133],{"class":5659},[3144,17412,5679],{"class":5663},[3144,17414,17415],{"class":5682},"myapp\n",[3144,17417,17418,17421,17423],{"class":3142,"line":3444},[3144,17419,17420],{"class":5659},"      version",[3144,17422,5679],{"class":5663},[3144,17424,17425],{"class":5682},"blue\n",[3144,17427,17428,17430],{"class":3142,"line":3449},[3144,17429,9142],{"class":5659},[3144,17431,5664],{"class":5663},[3144,17433,17434,17436],{"class":3142,"line":3455},[3144,17435,9149],{"class":5659},[3144,17437,5664],{"class":5663},[3144,17439,17440,17442],{"class":3142,"line":3461},[3144,17441,9156],{"class":5659},[3144,17443,5664],{"class":5663},[3144,17445,17446,17448,17450],{"class":3142,"line":3467},[3144,17447,9163],{"class":5659},[3144,17449,5679],{"class":5663},[3144,17451,17415],{"class":5682},[3144,17453,17454,17457,17459],{"class":3142,"line":3473},[3144,17455,17456],{"class":5659},"        version",[3144,17458,5679],{"class":5663},[3144,17460,17425],{"class":5682},[3144,17462,17463,17465],{"class":3142,"line":3478},[3144,17464,9172],{"class":5659},[3144,17466,5664],{"class":5663},[3144,17468,17469,17471],{"class":3142,"line":3483},[3144,17470,9179],{"class":5659},[3144,17472,5664],{"class":5663},[3144,17474,17475,17477,17479,17481],{"class":3142,"line":3489},[3144,17476,9186],{"class":5663},[3144,17478,9189],{"class":5659},[3144,17480,5679],{"class":5663},[3144,17482,16150],{"class":5682},[3144,17484,17485,17487,17489],{"class":3142,"line":3495},[3144,17486,9198],{"class":5659},[3144,17488,5679],{"class":5663},[3144,17490,16207],{"class":5682},[3144,17492,17493,17495],{"class":3142,"line":3501},[3144,17494,9208],{"class":5659},[3144,17496,5664],{"class":5663},[3144,17498,17499,17501,17503,17505],{"class":3142,"line":3506},[3144,17500,9215],{"class":5663},[3144,17502,9218],{"class":5659},[3144,17504,5679],{"class":5663},[3144,17506,16224],{"class":5700},[3144,17508,17509,17512],{"class":3142,"line":3512},[3144,17510,17511],{"class":5659},"          env",[3144,17513,5664],{"class":5663},[3144,17515,17516,17518,17520,17522],{"class":3142,"line":3518},[3144,17517,9215],{"class":5663},[3144,17519,9189],{"class":5659},[3144,17521,5679],{"class":5663},[3144,17523,17524],{"class":5682},"VERSION\n",[3144,17526,17527,17530,17532],{"class":3142,"line":3524},[3144,17528,17529],{"class":5659},"              value",[3144,17531,5679],{"class":5663},[3144,17533,17534],{"class":7139},"\"1.0.0\"\n",[3114,17536,17537],{},[3118,17538,17539],{},"app-green-deployment.yaml:",[3379,17541,17543],{"className":5650,"code":17542,"language":5652,"meta":3384,"style":3384},"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: app-green\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: myapp\n      version: green\n  template:\n    metadata:\n      labels:\n        app: myapp\n        version: green\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:1.26\n          ports:\n            - containerPort: 80\n          env:\n            - name: VERSION\n              value: \"2.0.0\"\n",[3349,17544,17545,17553,17561,17567,17576,17582,17590,17596,17602,17610,17619,17625,17631,17637,17645,17653,17659,17665,17675,17683,17689,17699,17705,17715],{"__ignoreMap":3384},[3144,17546,17547,17549,17551],{"class":3142,"line":3389},[3144,17548,9066],{"class":5659},[3144,17550,5679],{"class":5663},[3144,17552,9071],{"class":5682},[3144,17554,17555,17557,17559],{"class":3142,"line":3395},[3144,17556,9076],{"class":5659},[3144,17558,5679],{"class":5663},[3144,17560,9081],{"class":5682},[3144,17562,17563,17565],{"class":3142,"line":3401},[3144,17564,9086],{"class":5659},[3144,17566,5664],{"class":5663},[3144,17568,17569,17571,17573],{"class":3142,"line":3407},[3144,17570,9093],{"class":5659},[3144,17572,5679],{"class":5663},[3144,17574,17575],{"class":5682},"app-green\n",[3144,17577,17578,17580],{"class":3142,"line":3414},[3144,17579,5660],{"class":5659},[3144,17581,5664],{"class":5663},[3144,17583,17584,17586,17588],{"class":3142,"line":3420},[3144,17585,9109],{"class":5659},[3144,17587,5679],{"class":5663},[3144,17589,9114],{"class":5700},[3144,17591,17592,17594],{"class":3142,"line":3426},[3144,17593,9119],{"class":5659},[3144,17595,5664],{"class":5663},[3144,17597,17598,17600],{"class":3142,"line":3432},[3144,17599,9126],{"class":5659},[3144,17601,5664],{"class":5663},[3144,17603,17604,17606,17608],{"class":3142,"line":3438},[3144,17605,9133],{"class":5659},[3144,17607,5679],{"class":5663},[3144,17609,17415],{"class":5682},[3144,17611,17612,17614,17616],{"class":3142,"line":3444},[3144,17613,17420],{"class":5659},[3144,17615,5679],{"class":5663},[3144,17617,17618],{"class":5682},"green\n",[3144,17620,17621,17623],{"class":3142,"line":3449},[3144,17622,9142],{"class":5659},[3144,17624,5664],{"class":5663},[3144,17626,17627,17629],{"class":3142,"line":3455},[3144,17628,9149],{"class":5659},[3144,17630,5664],{"class":5663},[3144,17632,17633,17635],{"class":3142,"line":3461},[3144,17634,9156],{"class":5659},[3144,17636,5664],{"class":5663},[3144,17638,17639,17641,17643],{"class":3142,"line":3467},[3144,17640,9163],{"class":5659},[3144,17642,5679],{"class":5663},[3144,17644,17415],{"class":5682},[3144,17646,17647,17649,17651],{"class":3142,"line":3473},[3144,17648,17456],{"class":5659},[3144,17650,5679],{"class":5663},[3144,17652,17618],{"class":5682},[3144,17654,17655,17657],{"class":3142,"line":3478},[3144,17656,9172],{"class":5659},[3144,17658,5664],{"class":5663},[3144,17660,17661,17663],{"class":3142,"line":3483},[3144,17662,9179],{"class":5659},[3144,17664,5664],{"class":5663},[3144,17666,17667,17669,17671,17673],{"class":3142,"line":3489},[3144,17668,9186],{"class":5663},[3144,17670,9189],{"class":5659},[3144,17672,5679],{"class":5663},[3144,17674,16150],{"class":5682},[3144,17676,17677,17679,17681],{"class":3142,"line":3495},[3144,17678,9198],{"class":5659},[3144,17680,5679],{"class":5663},[3144,17682,16278],{"class":5682},[3144,17684,17685,17687],{"class":3142,"line":3501},[3144,17686,9208],{"class":5659},[3144,17688,5664],{"class":5663},[3144,17690,17691,17693,17695,17697],{"class":3142,"line":3506},[3144,17692,9215],{"class":5663},[3144,17694,9218],{"class":5659},[3144,17696,5679],{"class":5663},[3144,17698,16224],{"class":5700},[3144,17700,17701,17703],{"class":3142,"line":3512},[3144,17702,17511],{"class":5659},[3144,17704,5664],{"class":5663},[3144,17706,17707,17709,17711,17713],{"class":3142,"line":3518},[3144,17708,9215],{"class":5663},[3144,17710,9189],{"class":5659},[3144,17712,5679],{"class":5663},[3144,17714,17524],{"class":5682},[3144,17716,17717,17719,17721],{"class":3142,"line":3524},[3144,17718,17529],{"class":5659},[3144,17720,5679],{"class":5663},[3144,17722,17723],{"class":7139},"\"2.0.0\"\n",[3114,17725,17726],{},[3118,17727,17728],{},"service.yaml:",[3379,17730,17732],{"className":5650,"code":17731,"language":5652,"meta":3384,"style":3384},"apiVersion: v1\nkind: Service\nmetadata:\n  name: myapp-service\nspec:\n  selector:\n    app: myapp\n    version: blue  # Спочатку трафік на blue\n  ports:\n    - port: 80\n      targetPort: 80\n  type: ClusterIP\n",[3349,17733,17734,17743,17752,17758,17767,17773,17779,17787,17800,17807,17818,17827],{"__ignoreMap":3384},[3144,17735,17736,17738,17740],{"class":3142,"line":3389},[3144,17737,9066],{"class":5659},[3144,17739,5679],{"class":5663},[3144,17741,17742],{"class":5682},"v1\n",[3144,17744,17745,17747,17749],{"class":3142,"line":3395},[3144,17746,9076],{"class":5659},[3144,17748,5679],{"class":5663},[3144,17750,17751],{"class":5682},"Service\n",[3144,17753,17754,17756],{"class":3142,"line":3401},[3144,17755,9086],{"class":5659},[3144,17757,5664],{"class":5663},[3144,17759,17760,17762,17764],{"class":3142,"line":3407},[3144,17761,9093],{"class":5659},[3144,17763,5679],{"class":5663},[3144,17765,17766],{"class":5682},"myapp-service\n",[3144,17768,17769,17771],{"class":3142,"line":3414},[3144,17770,5660],{"class":5659},[3144,17772,5664],{"class":5663},[3144,17774,17775,17777],{"class":3142,"line":3420},[3144,17776,9119],{"class":5659},[3144,17778,5664],{"class":5663},[3144,17780,17781,17783,17785],{"class":3142,"line":3426},[3144,17782,12051],{"class":5659},[3144,17784,5679],{"class":5663},[3144,17786,17415],{"class":5682},[3144,17788,17789,17792,17794,17797],{"class":3142,"line":3432},[3144,17790,17791],{"class":5659},"    version",[3144,17793,5679],{"class":5663},[3144,17795,17796],{"class":5682},"blue",[3144,17798,17799],{"class":7200},"  # Спочатку трафік на blue\n",[3144,17801,17802,17805],{"class":3142,"line":3438},[3144,17803,17804],{"class":5659},"  ports",[3144,17806,5664],{"class":5663},[3144,17808,17809,17811,17814,17816],{"class":3142,"line":3444},[3144,17810,7121],{"class":5663},[3144,17812,17813],{"class":5659},"port",[3144,17815,5679],{"class":5663},[3144,17817,16224],{"class":5700},[3144,17819,17820,17823,17825],{"class":3142,"line":3449},[3144,17821,17822],{"class":5659},"      targetPort",[3144,17824,5679],{"class":5663},[3144,17826,16224],{"class":5700},[3144,17828,17829,17832,17834],{"class":3142,"line":3455},[3144,17830,17831],{"class":5659},"  type",[3144,17833,5679],{"class":5663},[3144,17835,17836],{"class":5682},"ClusterIP\n",[3114,17838,17839],{},[3118,17840,16229],{},[3379,17842,17844],{"className":10269,"code":17843,"language":10271,"meta":3384,"style":3384},"# Створення обох Deployment\nkubectl apply -f app-blue-deployment.yaml\nkubectl apply -f app-green-deployment.yaml\n\n# Створення Service (трафік на blue)\nkubectl apply -f service.yaml\n\n# Перевірка, що обидва Deployment працюють\nkubectl get deployments\n# NAME        READY   UP-TO-DATE   AVAILABLE   AGE\n# app-blue    3/3     3            3           30s\n# app-green   3/3     3            3           30s\n\n# Тестування (трафік йде на blue - v1.0)\nkubectl run test-pod --image=curlimages/curl --rm -it --restart=Never -- \\\n  curl http://myapp-service\n\n# Перемикання на green (v2.0)\nkubectl patch service myapp-service -p '{\"spec\":{\"selector\":{\"version\":\"green\"}}}'\n\n# Тестування (трафік миттєво перемкнувся на green - v2.0)\nkubectl run test-pod --image=curlimages/curl --rm -it --restart=Never -- \\\n  curl http://myapp-service\n\n# Якщо є проблеми — повернутись на blue\nkubectl patch service myapp-service -p '{\"spec\":{\"selector\":{\"version\":\"blue\"}}}'\n\n# Після успішного тестування — видалити blue\nkubectl delete deployment app-blue\n\n# Очищення\nkubectl delete deployment app-green\nkubectl delete service myapp-service\n",[3349,17845,17846,17851,17862,17873,17877,17882,17893,17897,17902,17911,17916,17921,17926,17930,17935,17960,17968,17972,17977,17994,17998,18003,18023,18029,18033,18038,18053,18057,18062,18073,18077,18081,18092],{"__ignoreMap":3384},[3144,17847,17848],{"class":3142,"line":3389},[3144,17849,17850],{"class":7200},"# Створення обох Deployment\n",[3144,17852,17853,17855,17857,17859],{"class":3142,"line":3395},[3144,17854,10278],{"class":7663},[3144,17856,16246],{"class":7139},[3144,17858,16249],{"class":7647},[3144,17860,17861],{"class":7139}," app-blue-deployment.yaml\n",[3144,17863,17864,17866,17868,17870],{"class":3142,"line":3401},[3144,17865,10278],{"class":7663},[3144,17867,16246],{"class":7139},[3144,17869,16249],{"class":7647},[3144,17871,17872],{"class":7139}," app-green-deployment.yaml\n",[3144,17874,17875],{"class":3142,"line":3407},[3144,17876,3411],{"emptyLinePlaceholder":3410},[3144,17878,17879],{"class":3142,"line":3414},[3144,17880,17881],{"class":7200},"# Створення Service (трафік на blue)\n",[3144,17883,17884,17886,17888,17890],{"class":3142,"line":3420},[3144,17885,10278],{"class":7663},[3144,17887,16246],{"class":7139},[3144,17889,16249],{"class":7647},[3144,17891,17892],{"class":7139}," service.yaml\n",[3144,17894,17895],{"class":3142,"line":3426},[3144,17896,3411],{"emptyLinePlaceholder":3410},[3144,17898,17899],{"class":3142,"line":3432},[3144,17900,17901],{"class":7200},"# Перевірка, що обидва Deployment працюють\n",[3144,17903,17904,17906,17908],{"class":3142,"line":3438},[3144,17905,10278],{"class":7663},[3144,17907,14736],{"class":7139},[3144,17909,17910],{"class":7139}," deployments\n",[3144,17912,17913],{"class":3142,"line":3444},[3144,17914,17915],{"class":7200},"# NAME        READY   UP-TO-DATE   AVAILABLE   AGE\n",[3144,17917,17918],{"class":3142,"line":3449},[3144,17919,17920],{"class":7200},"# app-blue    3/3     3            3           30s\n",[3144,17922,17923],{"class":3142,"line":3455},[3144,17924,17925],{"class":7200},"# app-green   3/3     3            3           30s\n",[3144,17927,17928],{"class":3142,"line":3461},[3144,17929,3411],{"emptyLinePlaceholder":3410},[3144,17931,17932],{"class":3142,"line":3467},[3144,17933,17934],{"class":7200},"# Тестування (трафік йде на blue - v1.0)\n",[3144,17936,17937,17939,17942,17945,17948,17951,17953,17956,17958],{"class":3142,"line":3473},[3144,17938,10278],{"class":7663},[3144,17940,17941],{"class":7139}," run",[3144,17943,17944],{"class":7139}," test-pod",[3144,17946,17947],{"class":7647}," --image=curlimages/curl",[3144,17949,17950],{"class":7647}," --rm",[3144,17952,15765],{"class":7647},[3144,17954,17955],{"class":7647}," --restart=Never",[3144,17957,17132],{"class":7647},[3144,17959,11259],{"class":11258},[3144,17961,17962,17965],{"class":3142,"line":3478},[3144,17963,17964],{"class":7139},"  curl",[3144,17966,17967],{"class":7139}," http://myapp-service\n",[3144,17969,17970],{"class":3142,"line":3483},[3144,17971,3411],{"emptyLinePlaceholder":3410},[3144,17973,17974],{"class":3142,"line":3489},[3144,17975,17976],{"class":7200},"# Перемикання на green (v2.0)\n",[3144,17978,17979,17981,17983,17986,17989,17991],{"class":3142,"line":3495},[3144,17980,10278],{"class":7663},[3144,17982,16313],{"class":7139},[3144,17984,17985],{"class":7139}," service",[3144,17987,17988],{"class":7139}," myapp-service",[3144,17990,16321],{"class":7647},[3144,17992,17993],{"class":7139}," '{\"spec\":{\"selector\":{\"version\":\"green\"}}}'\n",[3144,17995,17996],{"class":3142,"line":3501},[3144,17997,3411],{"emptyLinePlaceholder":3410},[3144,17999,18000],{"class":3142,"line":3506},[3144,18001,18002],{"class":7200},"# Тестування (трафік миттєво перемкнувся на green - v2.0)\n",[3144,18004,18005,18007,18009,18011,18013,18015,18017,18019,18021],{"class":3142,"line":3512},[3144,18006,10278],{"class":7663},[3144,18008,17941],{"class":7139},[3144,18010,17944],{"class":7139},[3144,18012,17947],{"class":7647},[3144,18014,17950],{"class":7647},[3144,18016,15765],{"class":7647},[3144,18018,17955],{"class":7647},[3144,18020,17132],{"class":7647},[3144,18022,11259],{"class":11258},[3144,18024,18025,18027],{"class":3142,"line":3518},[3144,18026,17964],{"class":7139},[3144,18028,17967],{"class":7139},[3144,18030,18031],{"class":3142,"line":3524},[3144,18032,3411],{"emptyLinePlaceholder":3410},[3144,18034,18035],{"class":3142,"line":3530},[3144,18036,18037],{"class":7200},"# Якщо є проблеми — повернутись на blue\n",[3144,18039,18040,18042,18044,18046,18048,18050],{"class":3142,"line":3535},[3144,18041,10278],{"class":7663},[3144,18043,16313],{"class":7139},[3144,18045,17985],{"class":7139},[3144,18047,17988],{"class":7139},[3144,18049,16321],{"class":7647},[3144,18051,18052],{"class":7139}," '{\"spec\":{\"selector\":{\"version\":\"blue\"}}}'\n",[3144,18054,18055],{"class":3142,"line":3705},[3144,18056,3411],{"emptyLinePlaceholder":3410},[3144,18058,18059],{"class":3142,"line":3711},[3144,18060,18061],{"class":7200},"# Після успішного тестування — видалити blue\n",[3144,18063,18064,18066,18068,18070],{"class":3142,"line":3717},[3144,18065,10278],{"class":7663},[3144,18067,16417],{"class":7139},[3144,18069,15540],{"class":7139},[3144,18071,18072],{"class":7139}," app-blue\n",[3144,18074,18075],{"class":3142,"line":3722},[3144,18076,3411],{"emptyLinePlaceholder":3410},[3144,18078,18079],{"class":3142,"line":3727},[3144,18080,16410],{"class":7200},[3144,18082,18083,18085,18087,18089],{"class":3142,"line":4084},[3144,18084,10278],{"class":7663},[3144,18086,16417],{"class":7139},[3144,18088,15540],{"class":7139},[3144,18090,18091],{"class":7139}," app-green\n",[3144,18093,18094,18096,18098,18100],{"class":3142,"line":4090},[3144,18095,10278],{"class":7663},[3144,18097,16417],{"class":7139},[3144,18099,17985],{"class":7139},[3144,18101,18102],{"class":7139}," myapp-service\n",[3114,18104,18105],{},[3118,18106,18107],{},"Переваги blue-green deployment:",[3926,18109,18110,18116,18121,18127],{},[3906,18111,18112,18115],{},[3118,18113,18114],{},"Миттєве перемикання"," — зміна selector займає \u003C 1 секунди",[3906,18117,18118,18120],{},[3118,18119,3336],{}," — обидві версії працюють, перемикання без простою",[3906,18122,18123,18126],{},[3118,18124,18125],{},"Швидкий rollback"," — просто змінити selector назад",[3906,18128,18129,18132],{},[3118,18130,18131],{},"Тестування у production"," — green працює, але не отримує трафік",[3114,18134,18135],{},[3118,18136,5730],{},[3926,18138,18139,18145],{},[3906,18140,18141,18144],{},[3118,18142,18143],{},"Подвійні ресурси"," — потрібно тримати обидві версії одночасно",[3906,18146,18147,18150],{},[3118,18148,18149],{},"Складніше для stateful застосунків"," — проблеми з БД, якщо схема змінилась",[3291,18152],{},[3109,18154,18156],{"id":18155},"резюме","Резюме",[3114,18158,18159,18160,18163],{},"У цій статті ми детально вивчили ",[3118,18161,18162],{},"Rolling Updates"," та управління життєвим циклом Deployment. Ось що ми розглянули:",[3330,18165,18166,18170,18175,18180,18185,18189,18193,18197,18201,18204,18209,18214],{},[3333,18167,18169],{"icon":10191,"title":18168},"Проблема оновлення без downtime","Чому наївний підхід (видалити всі Pod → створити нові) неприйнятний для production. Потрібен механізм поступового оновлення.",[3333,18171,18174],{"icon":18172,"title":18173},"i-heroicons-arrow-path","Що таке Rolling Update","Стратегія оновлення, при якій старі Pod поступово замінюються новими, завжди залишаючи мінімальну кількість працюючих реплік. Zero-downtime гарантовано.",[3333,18176,18179],{"icon":18177,"title":18178},"i-heroicons-eye","Покрокова візуалізація","Детальний розбір кожного кроку rolling update з PlantUML діаграмами: від ініціації до завершення. Розуміння внутрішньої роботи Kubernetes.",[3333,18181,18184],{"icon":18182,"title":18183},"i-heroicons-arrows-right-left","Стратегії оновлення","RollingUpdate (поступове, zero-downtime) vs Recreate (швидке, з downtime). Коли використовувати кожну стратегію.",[3333,18186,18188],{"icon":3345,"title":18187},"Параметри maxSurge та maxUnavailable","Детальний розбір з математичними розрахунками. Як різні комбінації впливають на швидкість, ресурси та безпеку оновлення.",[3333,18190,18192],{"icon":3335,"title":18191},"progressDeadlineSeconds та minReadySeconds","Додаткові параметри для контролю життєвого циклу. Як запобігти зависанню оновлення та перевірити стабільність нових Pod.",[3333,18194,18196],{"icon":8848,"title":18195},"Health Checks для .NET","Детальна реалізація liveness, readiness та startup probes для ASP.NET Core. Різниця між ними та правильні налаштування.",[3333,18198,18200],{"icon":10110,"title":18199},"Resource Management для .NET","Особливості .NET GC у Kubernetes. Як правильно налаштувати requests/limits, щоб уникнути OOMKilled та CPU throttling.",[3333,18202,18203],{"icon":3359,"title":10934},"Як Kubernetes зберігає історію ревізій. Швидкий rollback до попередньої версії або конкретної ревізії. Canary та blue-green deployments.",[3333,18205,18208],{"icon":18206,"title":18207},"i-heroicons-code-bracket","Практичний приклад: TodoApi v1.0 → v2.0","Реальне оновлення ASP.NET Core застосунку з додаванням нового endpoint. Повний цикл: збірка, rolling update, тестування, rollback.",[3333,18210,18213],{"icon":18211,"title":18212},"i-heroicons-wrench-screwdriver","Troubleshooting","Типові проблеми та їх вирішення: зависле оновлення, CrashLoopBackOff, повільний rollout, downtime, Pod не видаляються. Корисні команди для debugging.",[3333,18215,18217],{"icon":18216,"title":15966},"i-heroicons-academic-cap","4 завдання для закріплення знань: експерименти з параметрами, симуляція невдалого оновлення, canary deployment, blue-green deployment.",[3122,18219,18221],{"id":18220},"ключові-висновки","Ключові висновки",[3926,18223,18224,18230,18238,18244,18253,18262,18271],{},[3906,18225,18226,18229],{},[3118,18227,18228],{},"Rolling Update — стандарт для production"," — завжди використовуйте RollingUpdate стратегію для stateless застосунків. Recreate лише для особливих випадків.",[3906,18231,18232,18235,18236,3289],{},[3118,18233,18234],{},"maxSurge та maxUnavailable контролюють все"," — правильний підбір цих параметрів критично важливий. Для zero-downtime: ",[3349,18237,7003],{},[3906,18239,18240,18243],{},[3118,18241,18242],{},"Health checks обов'язкові"," — без readiness probe rolling update не працює правильно. Liveness probe запобігає deadlock. Startup probe для повільних застосунків.",[3906,18245,18246,18249,18250,18252],{},[3118,18247,18248],{},".NET потребує особливої уваги"," — обов'язково встановлюйте ",[3349,18251,10504],{},". GC має знати про memory limits.",[3906,18254,18255,18258,18259,18261],{},[3118,18256,18257],{},"Rollback має бути швидким"," — зберігайте достатню кількість ревізій (",[3349,18260,11132],{},"). Образи кешуються на вузлах для швидкого rollback.",[3906,18263,18264,18267,18268,18270],{},[3118,18265,18266],{},"Моніторинг критично важливий"," — стежте за ",[3349,18269,11651],{},", логами, метриками. Виявляйте проблеми на ранній стадії.",[3906,18272,18273,18276],{},[3118,18274,18275],{},"Canary та blue-green для критичних оновлень"," — для важливих застосунків використовуйте поступове розгортання з перевіркою на малій кількості Pod.",[3122,18278,18280],{"id":18279},"що-далі","Що далі?",[3114,18282,18283],{},"Ви вивчили основи Deployment та rolling updates. Наступні теми для поглибленого вивчення:",[3903,18285,18286,18292,18298,18304,18310,18316],{},[3906,18287,18288,18291],{},[3118,18289,18290],{},"Service та Ingress"," — як організувати мережевий доступ до Pod",[3906,18293,18294,18297],{},[3118,18295,18296],{},"ConfigMap та Secret"," — управління конфігурацією та секретами",[3906,18299,18300,18303],{},[3118,18301,18302],{},"StatefulSet"," — для stateful застосунків (бази даних)",[3906,18305,18306,18309],{},[3118,18307,18308],{},"HorizontalPodAutoscaler"," — автоматичне масштабування на основі метрик",[3906,18311,18312,18315],{},[3118,18313,18314],{},"Helm"," — пакетний менеджер для Kubernetes",[3906,18317,18318,18321],{},[3118,18319,18320],{},"GitOps"," — автоматизація розгортання через Git (ArgoCD, Flux)",[3291,18323],{},[3109,18325,18327],{"id":18326},"корисні-команди","Корисні команди",[3114,18329,18330],{},"Для швидкого доступу — всі команди для роботи з rolling updates:",[15519,18332,18333,18540,18658,18772],{},[3379,18334,18336],{"className":10269,"code":18335,"filename":3222,"language":10271,"meta":3384,"style":3384},"# Оновлення образу\nkubectl set image deployment/\u003Cname> \u003Ccontainer>=\u003Cimage>:\u003Ctag>\n\n# Оновлення з записом у історію\nkubectl set image deployment/\u003Cname> \u003Ccontainer>=\u003Cimage>:\u003Ctag> --record\n\n# Моніторинг процесу\nkubectl rollout status deployment/\u003Cname>\n\n# Спостереження за Pod у реальному часі\nkubectl get pods -l app=\u003Cname> -w\n\n# Призупинити оновлення\nkubectl rollout pause deployment/\u003Cname>\n\n# Відновити оновлення\nkubectl rollout resume deployment/\u003Cname>\n",[3349,18337,18338,18343,18387,18391,18396,18436,18440,18445,18461,18465,18470,18492,18496,18500,18516,18520,18524],{"__ignoreMap":3384},[3144,18339,18340],{"class":3142,"line":3389},[3144,18341,18342],{"class":7200},"# Оновлення образу\n",[3144,18344,18345,18347,18349,18351,18354,18356,18359,18362,18365,18368,18370,18372,18375,18377,18379,18381,18384],{"class":3142,"line":3395},[3144,18346,10278],{"class":7663},[3144,18348,11246],{"class":7139},[3144,18350,11249],{"class":7139},[3144,18352,18353],{"class":7139}," deployment/",[3144,18355,8069],{"class":5663},[3144,18357,18358],{"class":7139},"nam",[3144,18360,18361],{"class":5663},"e> \u003C",[3144,18363,18364],{"class":7139},"containe",[3144,18366,18367],{"class":5663},"r>",[3144,18369,16275],{"class":7139},[3144,18371,8069],{"class":5663},[3144,18373,18374],{"class":7139},"imag",[3144,18376,15831],{"class":5663},[3144,18378,10476],{"class":7139},[3144,18380,8069],{"class":5663},[3144,18382,18383],{"class":7139},"ta",[3144,18385,18386],{"class":5663},"g>\n",[3144,18388,18389],{"class":3142,"line":3401},[3144,18390,3411],{"emptyLinePlaceholder":3410},[3144,18392,18393],{"class":3142,"line":3407},[3144,18394,18395],{"class":7200},"# Оновлення з записом у історію\n",[3144,18397,18398,18400,18402,18404,18406,18408,18410,18412,18414,18416,18418,18420,18422,18424,18426,18428,18430,18433],{"class":3142,"line":3414},[3144,18399,10278],{"class":7663},[3144,18401,11246],{"class":7139},[3144,18403,11249],{"class":7139},[3144,18405,18353],{"class":7139},[3144,18407,8069],{"class":5663},[3144,18409,18358],{"class":7139},[3144,18411,18361],{"class":5663},[3144,18413,18364],{"class":7139},[3144,18415,18367],{"class":5663},[3144,18417,16275],{"class":7139},[3144,18419,8069],{"class":5663},[3144,18421,18374],{"class":7139},[3144,18423,15831],{"class":5663},[3144,18425,10476],{"class":7139},[3144,18427,8069],{"class":5663},[3144,18429,18383],{"class":7139},[3144,18431,18432],{"class":5663},"g> ",[3144,18434,18435],{"class":7647},"--record\n",[3144,18437,18438],{"class":3142,"line":3420},[3144,18439,3411],{"emptyLinePlaceholder":3410},[3144,18441,18442],{"class":3142,"line":3426},[3144,18443,18444],{"class":7200},"# Моніторинг процесу\n",[3144,18446,18447,18449,18451,18453,18455,18457,18459],{"class":3142,"line":3432},[3144,18448,10278],{"class":7663},[3144,18450,11764],{"class":7139},[3144,18452,8724],{"class":7139},[3144,18454,18353],{"class":7139},[3144,18456,8069],{"class":5663},[3144,18458,18358],{"class":7139},[3144,18460,10293],{"class":5663},[3144,18462,18463],{"class":3142,"line":3438},[3144,18464,3411],{"emptyLinePlaceholder":3410},[3144,18466,18467],{"class":3142,"line":3444},[3144,18468,18469],{"class":7200},"# Спостереження за Pod у реальному часі\n",[3144,18471,18472,18474,18476,18478,18480,18483,18485,18487,18489],{"class":3142,"line":3449},[3144,18473,10278],{"class":7663},[3144,18475,14736],{"class":7139},[3144,18477,15643],{"class":7139},[3144,18479,15646],{"class":7647},[3144,18481,18482],{"class":7139}," app=",[3144,18484,8069],{"class":5663},[3144,18486,18358],{"class":7139},[3144,18488,15741],{"class":5663},[3144,18490,18491],{"class":7647},"-w\n",[3144,18493,18494],{"class":3142,"line":3455},[3144,18495,3411],{"emptyLinePlaceholder":3410},[3144,18497,18498],{"class":3142,"line":3461},[3144,18499,11757],{"class":7200},[3144,18501,18502,18504,18506,18508,18510,18512,18514],{"class":3142,"line":3467},[3144,18503,10278],{"class":7663},[3144,18505,11764],{"class":7139},[3144,18507,11767],{"class":7139},[3144,18509,18353],{"class":7139},[3144,18511,8069],{"class":5663},[3144,18513,18358],{"class":7139},[3144,18515,10293],{"class":5663},[3144,18517,18518],{"class":3142,"line":3473},[3144,18519,3411],{"emptyLinePlaceholder":3410},[3144,18521,18522],{"class":3142,"line":3478},[3144,18523,15950],{"class":7200},[3144,18525,18526,18528,18530,18532,18534,18536,18538],{"class":3142,"line":3483},[3144,18527,10278],{"class":7663},[3144,18529,11764],{"class":7139},[3144,18531,11827],{"class":7139},[3144,18533,18353],{"class":7139},[3144,18535,8069],{"class":5663},[3144,18537,18358],{"class":7139},[3144,18539,10293],{"class":5663},[3379,18541,18543],{"className":10269,"code":18542,"filename":15884,"language":10271,"meta":3384,"style":3384},"# Rollback до попередньої версії\nkubectl rollout undo deployment/\u003Cname>\n\n# Rollback до конкретної ревізії\nkubectl rollout undo deployment/\u003Cname> --to-revision=\u003Cnumber>\n\n# Перегляд історії\nkubectl rollout history deployment/\u003Cname>\n\n# Детальна інформація про ревізію\nkubectl rollout history deployment/\u003Cname> --revision=\u003Cnumber>\n",[3349,18544,18545,18549,18565,18569,18573,18600,18604,18609,18625,18629,18633],{"__ignoreMap":3384},[3144,18546,18547],{"class":3142,"line":3389},[3144,18548,15891],{"class":7200},[3144,18550,18551,18553,18555,18557,18559,18561,18563],{"class":3142,"line":3395},[3144,18552,10278],{"class":7663},[3144,18554,11764],{"class":7139},[3144,18556,11847],{"class":7139},[3144,18558,18353],{"class":7139},[3144,18560,8069],{"class":5663},[3144,18562,18358],{"class":7139},[3144,18564,10293],{"class":5663},[3144,18566,18567],{"class":3142,"line":3401},[3144,18568,3411],{"emptyLinePlaceholder":3410},[3144,18570,18571],{"class":3142,"line":3407},[3144,18572,15910],{"class":7200},[3144,18574,18575,18577,18579,18581,18583,18585,18587,18589,18592,18594,18597],{"class":3142,"line":3414},[3144,18576,10278],{"class":7663},[3144,18578,11764],{"class":7139},[3144,18580,11847],{"class":7139},[3144,18582,18353],{"class":7139},[3144,18584,8069],{"class":5663},[3144,18586,18358],{"class":7139},[3144,18588,15741],{"class":5663},[3144,18590,18591],{"class":7647},"--to-revision=",[3144,18593,8069],{"class":5663},[3144,18595,18596],{"class":7647},"number",[3144,18598,18599],{"class":5663},">\n",[3144,18601,18602],{"class":3142,"line":3420},[3144,18603,3411],{"emptyLinePlaceholder":3410},[3144,18605,18606],{"class":3142,"line":3426},[3144,18607,18608],{"class":7200},"# Перегляд історії\n",[3144,18610,18611,18613,18615,18617,18619,18621,18623],{"class":3142,"line":3432},[3144,18612,10278],{"class":7663},[3144,18614,11764],{"class":7139},[3144,18616,15599],{"class":7139},[3144,18618,18353],{"class":7139},[3144,18620,8069],{"class":5663},[3144,18622,18358],{"class":7139},[3144,18624,10293],{"class":5663},[3144,18626,18627],{"class":3142,"line":3438},[3144,18628,3411],{"emptyLinePlaceholder":3410},[3144,18630,18631],{"class":3142,"line":3444},[3144,18632,15610],{"class":7200},[3144,18634,18635,18637,18639,18641,18643,18645,18647,18649,18652,18654,18656],{"class":3142,"line":3449},[3144,18636,10278],{"class":7663},[3144,18638,11764],{"class":7139},[3144,18640,15599],{"class":7139},[3144,18642,18353],{"class":7139},[3144,18644,8069],{"class":5663},[3144,18646,18358],{"class":7139},[3144,18648,15741],{"class":5663},[3144,18650,18651],{"class":7647},"--revision=",[3144,18653,8069],{"class":5663},[3144,18655,18596],{"class":7647},[3144,18657,18599],{"class":5663},[3379,18659,18662],{"className":10269,"code":18660,"filename":18661,"language":10271,"meta":3384,"style":3384},"# Зміна maxSurge та maxUnavailable\nkubectl patch deployment \u003Cname> -p '{\"spec\":{\"strategy\":{\"rollingUpdate\":{\"maxSurge\":1,\"maxUnavailable\":0}}}}'\n\n# Зміна progressDeadlineSeconds\nkubectl patch deployment \u003Cname> -p '{\"spec\":{\"progressDeadlineSeconds\":300}}'\n\n# Зміна minReadySeconds\nkubectl patch deployment \u003Cname> -p '{\"spec\":{\"minReadySeconds\":30}}'\n\n# Зміна revisionHistoryLimit\nkubectl patch deployment \u003Cname> -p '{\"spec\":{\"revisionHistoryLimit\":5}}'\n","Налаштування параметрів",[3349,18663,18664,18669,18688,18692,18697,18716,18720,18725,18744,18748,18753],{"__ignoreMap":3384},[3144,18665,18666],{"class":3142,"line":3389},[3144,18667,18668],{"class":7200},"# Зміна maxSurge та maxUnavailable\n",[3144,18670,18671,18673,18675,18677,18679,18681,18683,18686],{"class":3142,"line":3395},[3144,18672,10278],{"class":7663},[3144,18674,16313],{"class":7139},[3144,18676,15540],{"class":7139},[3144,18678,10287],{"class":5663},[3144,18680,18358],{"class":7139},[3144,18682,15741],{"class":5663},[3144,18684,18685],{"class":7647},"-p",[3144,18687,16324],{"class":7139},[3144,18689,18690],{"class":3142,"line":3401},[3144,18691,3411],{"emptyLinePlaceholder":3410},[3144,18693,18694],{"class":3142,"line":3407},[3144,18695,18696],{"class":7200},"# Зміна progressDeadlineSeconds\n",[3144,18698,18699,18701,18703,18705,18707,18709,18711,18713],{"class":3142,"line":3414},[3144,18700,10278],{"class":7663},[3144,18702,16313],{"class":7139},[3144,18704,15540],{"class":7139},[3144,18706,10287],{"class":5663},[3144,18708,18358],{"class":7139},[3144,18710,15741],{"class":5663},[3144,18712,18685],{"class":7647},[3144,18714,18715],{"class":7139}," '{\"spec\":{\"progressDeadlineSeconds\":300}}'\n",[3144,18717,18718],{"class":3142,"line":3420},[3144,18719,3411],{"emptyLinePlaceholder":3410},[3144,18721,18722],{"class":3142,"line":3426},[3144,18723,18724],{"class":7200},"# Зміна minReadySeconds\n",[3144,18726,18727,18729,18731,18733,18735,18737,18739,18741],{"class":3142,"line":3432},[3144,18728,10278],{"class":7663},[3144,18730,16313],{"class":7139},[3144,18732,15540],{"class":7139},[3144,18734,10287],{"class":5663},[3144,18736,18358],{"class":7139},[3144,18738,15741],{"class":5663},[3144,18740,18685],{"class":7647},[3144,18742,18743],{"class":7139}," '{\"spec\":{\"minReadySeconds\":30}}'\n",[3144,18745,18746],{"class":3142,"line":3438},[3144,18747,3411],{"emptyLinePlaceholder":3410},[3144,18749,18750],{"class":3142,"line":3444},[3144,18751,18752],{"class":7200},"# Зміна revisionHistoryLimit\n",[3144,18754,18755,18757,18759,18761,18763,18765,18767,18769],{"class":3142,"line":3449},[3144,18756,10278],{"class":7663},[3144,18758,16313],{"class":7139},[3144,18760,15540],{"class":7139},[3144,18762,10287],{"class":5663},[3144,18764,18358],{"class":7139},[3144,18766,15741],{"class":5663},[3144,18768,18685],{"class":7647},[3144,18770,18771],{"class":7139}," '{\"spec\":{\"revisionHistoryLimit\":5}}'\n",[3379,18773,18775],{"className":10269,"code":18774,"filename":15748,"language":10271,"meta":3384,"style":3384},"# Перегляд стану Deployment\nkubectl describe deployment \u003Cname>\n\n# Перегляд ReplicaSet\nkubectl get replicasets -l app=\u003Cname>\n\n# Детальна інформація про ReplicaSet\nkubectl describe replicaset \u003Creplicaset-name>\n\n# Логи всіх Pod\nkubectl logs -l app=\u003Cname> --tail=50\n\n# Логи попереднього контейнера\nkubectl logs \u003Cpod-name> --previous\n\n# Події\nkubectl get events --sort-by=.metadata.creationTimestamp --field-selector involvedObject.name=\u003Cname>\n",[3349,18776,18777,18782,18796,18800,18805,18824,18828,18833,18849,18853,18858,18877,18881,18886,18900,18904,18909],{"__ignoreMap":3384},[3144,18778,18779],{"class":3142,"line":3389},[3144,18780,18781],{"class":7200},"# Перегляд стану Deployment\n",[3144,18783,18784,18786,18788,18790,18792,18794],{"class":3142,"line":3395},[3144,18785,10278],{"class":7663},[3144,18787,10281],{"class":7139},[3144,18789,15540],{"class":7139},[3144,18791,10287],{"class":5663},[3144,18793,18358],{"class":7139},[3144,18795,10293],{"class":5663},[3144,18797,18798],{"class":3142,"line":3401},[3144,18799,3411],{"emptyLinePlaceholder":3410},[3144,18801,18802],{"class":3142,"line":3407},[3144,18803,18804],{"class":7200},"# Перегляд ReplicaSet\n",[3144,18806,18807,18809,18811,18814,18816,18818,18820,18822],{"class":3142,"line":3414},[3144,18808,10278],{"class":7663},[3144,18810,14736],{"class":7139},[3144,18812,18813],{"class":7139}," replicasets",[3144,18815,15646],{"class":7647},[3144,18817,18482],{"class":7139},[3144,18819,8069],{"class":5663},[3144,18821,18358],{"class":7139},[3144,18823,10293],{"class":5663},[3144,18825,18826],{"class":3142,"line":3420},[3144,18827,3411],{"emptyLinePlaceholder":3410},[3144,18829,18830],{"class":3142,"line":3426},[3144,18831,18832],{"class":7200},"# Детальна інформація про ReplicaSet\n",[3144,18834,18835,18837,18839,18842,18844,18847],{"class":3142,"line":3432},[3144,18836,10278],{"class":7663},[3144,18838,10281],{"class":7139},[3144,18840,18841],{"class":7139}," replicaset",[3144,18843,10287],{"class":5663},[3144,18845,18846],{"class":7139},"replicaset-nam",[3144,18848,10293],{"class":5663},[3144,18850,18851],{"class":3142,"line":3438},[3144,18852,3411],{"emptyLinePlaceholder":3410},[3144,18854,18855],{"class":3142,"line":3444},[3144,18856,18857],{"class":7200},"# Логи всіх Pod\n",[3144,18859,18860,18862,18864,18866,18868,18870,18872,18874],{"class":3142,"line":3449},[3144,18861,10278],{"class":7663},[3144,18863,15713],{"class":7139},[3144,18865,15646],{"class":7647},[3144,18867,18482],{"class":7139},[3144,18869,8069],{"class":5663},[3144,18871,18358],{"class":7139},[3144,18873,15741],{"class":5663},[3144,18875,18876],{"class":7647},"--tail=50\n",[3144,18878,18879],{"class":3142,"line":3455},[3144,18880,3411],{"emptyLinePlaceholder":3410},[3144,18882,18883],{"class":3142,"line":3461},[3144,18884,18885],{"class":7200},"# Логи попереднього контейнера\n",[3144,18887,18888,18890,18892,18894,18896,18898],{"class":3142,"line":3467},[3144,18889,10278],{"class":7663},[3144,18891,15713],{"class":7139},[3144,18893,10287],{"class":5663},[3144,18895,10290],{"class":7139},[3144,18897,15741],{"class":5663},[3144,18899,15744],{"class":7647},[3144,18901,18902],{"class":3142,"line":3473},[3144,18903,3411],{"emptyLinePlaceholder":3410},[3144,18905,18906],{"class":3142,"line":3478},[3144,18907,18908],{"class":7200},"# Події\n",[3144,18910,18911,18913,18915,18917,18920,18923,18926,18928,18930],{"class":3142,"line":3483},[3144,18912,10278],{"class":7663},[3144,18914,14736],{"class":7139},[3144,18916,15855],{"class":7139},[3144,18918,18919],{"class":7647}," --sort-by=.metadata.creationTimestamp",[3144,18921,18922],{"class":7647}," --field-selector",[3144,18924,18925],{"class":7139}," involvedObject.name=",[3144,18927,8069],{"class":5663},[3144,18929,18358],{"class":7139},[3144,18931,10293],{"class":5663},[3291,18933],{},[3109,18935,18937],{"id":18936},"додаткові-ресурси","Додаткові ресурси",[3330,18939,18940,18947,18952,18958,18963],{},[3333,18941,18946],{"icon":18942,"title":18943,"target":18944,"to":18945},"i-heroicons-book-open","Офіційна документація: Deployments","_blank","https://kubernetes.io/docs/concepts/workloads/controllers/deployment/","Повна документація про Deployment з усіма полями та прикладами.",[3333,18948,18951],{"icon":18172,"title":18949,"target":18944,"to":18950},"Performing a Rolling Update","https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/","Офіційний туторіал з rolling updates від Kubernetes.",[3333,18953,18957],{"icon":18954,"title":18955,"target":18944,"to":18956},"i-heroicons-chart-bar","Deployment Strategies","https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy","Детальний опис стратегій оновлення: RollingUpdate та Recreate.",[3333,18959,18962],{"icon":8848,"title":18960,"target":18944,"to":18961},"ASP.NET Core Health Checks","https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks","Офіційна документація Microsoft про health checks у ASP.NET Core.",[3333,18964,18968],{"icon":18965,"title":18966,"target":18944,"to":18967},"i-heroicons-cube",".NET in Containers","https://learn.microsoft.com/en-us/dotnet/core/docker/introduction","Best practices для запуску .NET застосунків у контейнерах.",[3291,18970],{},[3114,18972,18973,3156,18976],{},[3118,18974,18975],{},"Попередня стаття:",[18977,18978,2763],"a",{"href":2764},[18980,18981,18982],"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 .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}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 .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 .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}",{"title":3384,"searchDepth":3395,"depth":3395,"links":18984},[18985,18988,18991,19001,19002,19007,19013,19017,19024,19030,19039,19048,19056,19062,19066,19067],{"id":3111,"depth":3395,"text":3112,"children":18986},[18987],{"id":3124,"depth":3401,"text":3125},{"id":3295,"depth":3395,"text":3296,"children":18989},[18990],{"id":3327,"depth":3401,"text":3328},{"id":3366,"depth":3395,"text":3367,"children":18992},[18993,18994,18995,18996,18997,18998,18999,19000],{"id":3373,"depth":3401,"text":3374},{"id":3547,"depth":3401,"text":3548},{"id":3750,"depth":3401,"text":3751},{"id":3920,"depth":3401,"text":3921},{"id":4169,"depth":3401,"text":4170},{"id":4382,"depth":3401,"text":4383},{"id":4564,"depth":3401,"text":4565},{"id":4751,"depth":3401,"text":4752},{"id":4967,"depth":3395,"text":4968},{"id":5632,"depth":3395,"text":5633,"children":19003},[19004,19005,19006],{"id":5639,"depth":3401,"text":5640},{"id":5760,"depth":3401,"text":5761},{"id":5839,"depth":3401,"text":5840},{"id":6182,"depth":3395,"text":6183,"children":19008},[19009,19010,19011,19012],{"id":6189,"depth":3401,"text":3355},{"id":6411,"depth":3401,"text":3351},{"id":6598,"depth":3401,"text":6599},{"id":6832,"depth":3401,"text":6833},{"id":7018,"depth":3395,"text":7019,"children":19014},[19015,19016],{"id":7030,"depth":3401,"text":4378},{"id":7276,"depth":3401,"text":7277},{"id":7616,"depth":3395,"text":7617,"children":19018},[19019,19020,19021,19022,19023],{"id":7623,"depth":3401,"text":7624},{"id":7794,"depth":3401,"text":7795},{"id":8842,"depth":3401,"text":8843},{"id":9052,"depth":3401,"text":9053},{"id":9627,"depth":3401,"text":9628},{"id":9811,"depth":3395,"text":9812,"children":19025},[19026,19027,19028,19029],{"id":9818,"depth":3401,"text":9819},{"id":10104,"depth":3401,"text":10105},{"id":10311,"depth":3401,"text":10312},{"id":10598,"depth":3401,"text":10599},{"id":10933,"depth":3395,"text":10934,"children":19031},[19032,19033,19034,19035,19036,19037,19038],{"id":10944,"depth":3401,"text":10945},{"id":11164,"depth":3401,"text":11165},{"id":11307,"depth":3401,"text":11308},{"id":11389,"depth":3401,"text":11390},{"id":11616,"depth":3401,"text":11617},{"id":11644,"depth":3401,"text":11645},{"id":11692,"depth":3401,"text":11693},{"id":11854,"depth":3395,"text":11855,"children":19040},[19041,19042,19043,19044,19045,19046,19047],{"id":11861,"depth":3401,"text":11862},{"id":12846,"depth":3401,"text":12847},{"id":14092,"depth":3401,"text":14093},{"id":14138,"depth":3401,"text":14139},{"id":14276,"depth":3401,"text":14277},{"id":14441,"depth":3401,"text":14442},{"id":14541,"depth":3401,"text":14542},{"id":14605,"depth":3395,"text":14606,"children":19049},[19050,19051,19052,19053,19054,19055],{"id":14612,"depth":3401,"text":14613},{"id":14898,"depth":3401,"text":14899},{"id":15015,"depth":3401,"text":15016},{"id":15125,"depth":3401,"text":15126},{"id":15231,"depth":3401,"text":15232},{"id":15516,"depth":3401,"text":15517},{"id":15965,"depth":3395,"text":15966,"children":19057},[19058,19059,19060,19061],{"id":15972,"depth":3401,"text":15973},{"id":16490,"depth":3401,"text":16491},{"id":16851,"depth":3401,"text":16852},{"id":17288,"depth":3401,"text":17289},{"id":18155,"depth":3395,"text":18156,"children":19063},[19064,19065],{"id":18220,"depth":3401,"text":18221},{"id":18279,"depth":3401,"text":18280},{"id":18326,"depth":3395,"text":18327},{"id":18936,"depth":3395,"text":18937},"Оновлення застосунків без downtime — від теорії до практики з детальною візуалізацією, математичними розрахунками та реальними прикладами","md",null,{},{"title":2767,"description":19068},"XAqXLn4K7iRfd6JVzRGyuyV5ozLxYUaQjznYd7Vd6hI",[19075,19077],{"title":2763,"path":2764,"stem":2765,"description":19076,"children":-1},"Від ручного управління Pod до автоматизованої оркестрації — self-healing, масштабування та декларативні оновлення",{"title":2771,"path":2772,"stem":2773,"description":19078,"children":-1},"Від ефемерних IP-адрес Pod до стабільних Service endpoints — service discovery, балансування навантаження та мережева архітектура Kubernetes",1778489423889]