[{"data":1,"prerenderedAt":19663},["ShallowReactive",2],{"navigation_docs":3,"-aws-alb-asg":3338,"-aws-alb-asg-surround":19658},[4,1707,1904,2358,2539,2608,2815,2937,2987,3044,3078,3204,3281,3334],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.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},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.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 до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.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 та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.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},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.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},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.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","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.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: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.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","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"План навчання: Курс C++ — Продовження (Статті 29–60+)","\u002Fcpp\u002Fcurriculum-plan","02.cpp\u002Fcurriculum-plan",{"title":1905,"icon":1906,"path":1907,"stem":1908,"children":1909,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[1910,1936,1990,2012,2316,2354],{"title":1911,"icon":1912,"path":1913,"stem":1914,"children":1915,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[1916,1920,1924,1928,1932],{"title":1917,"path":1918,"stem":1919},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":1921,"path":1922,"stem":1923},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":1925,"path":1926,"stem":1927},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":1929,"path":1930,"stem":1931},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":1933,"path":1934,"stem":1935},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":1937,"icon":1938,"path":1939,"stem":1940,"children":1941,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[1942,1946,1950,1954,1958,1962,1966,1970,1974,1978,1982,1986],{"title":1943,"path":1944,"stem":1945},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":1947,"path":1948,"stem":1949},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":1951,"path":1952,"stem":1953},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":1955,"path":1956,"stem":1957},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":1959,"path":1960,"stem":1961},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":1963,"path":1964,"stem":1965},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":1967,"path":1968,"stem":1969},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":1971,"path":1972,"stem":1973},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":1975,"path":1976,"stem":1977},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":1979,"path":1980,"stem":1981},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":1983,"path":1984,"stem":1985},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":1987,"path":1988,"stem":1989},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":1991,"icon":1992,"path":1993,"stem":1994,"children":1995,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[1996,2000,2004,2008],{"title":1997,"path":1998,"stem":1999},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2001,"path":2002,"stem":2003},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2005,"path":2006,"stem":2007},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2009,"path":2010,"stem":2011},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2013,"icon":2014,"path":2015,"stem":2016,"children":2017},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2018,2019,2023,2027,2031,2035,2098,2133,2285],{"title":2013,"path":2015,"stem":2016},{"title":2020,"path":2021,"stem":2022},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2024,"path":2025,"stem":2026},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2028,"path":2029,"stem":2030},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2032,"path":2033,"stem":2034},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2036,"icon":132,"path":2037,"stem":2038,"children":2039},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2040,2042,2046,2050,2054,2058,2062,2066,2070,2074,2078,2082,2086,2090,2094],{"title":2041,"path":2037,"stem":2038},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2043,"path":2044,"stem":2045},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2047,"path":2048,"stem":2049},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2051,"path":2052,"stem":2053},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2055,"path":2056,"stem":2057},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2059,"path":2060,"stem":2061},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2063,"path":2064,"stem":2065},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2067,"path":2068,"stem":2069},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2071,"path":2072,"stem":2073},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2075,"path":2076,"stem":2077},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2079,"path":2080,"stem":2081},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2083,"path":2084,"stem":2085},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2087,"path":2088,"stem":2089},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2091,"path":2092,"stem":2093},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2095,"path":2096,"stem":2097},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2099,"icon":2014,"path":2100,"stem":2101,"children":2102},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2103,2105,2109,2113,2117,2121,2125,2129],{"title":2104,"path":2100,"stem":2101},"React Router: Навігаційна система сучасного вебу",{"title":2106,"path":2107,"stem":2108},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2110,"path":2111,"stem":2112},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2114,"path":2115,"stem":2116},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2118,"path":2119,"stem":2120},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2122,"path":2123,"stem":2124},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2126,"path":2127,"stem":2128},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2130,"path":2131,"stem":2132},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2134,"icon":132,"path":2135,"stem":2136,"children":2137},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2138,2140,2156,2185,2194,2215,2231,2260],{"title":2139,"path":2135,"stem":2136},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2141,"stem":2142,"children":2143,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2144,2148,2152],{"title":2145,"path":2146,"stem":2147},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2149,"path":2150,"stem":2151},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2153,"path":2154,"stem":2155},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2157,"icon":132,"path":2158,"stem":2159,"children":2160,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2161,2165,2169,2173,2177,2181],{"title":2162,"path":2163,"stem":2164},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2166,"path":2167,"stem":2168},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2170,"path":2171,"stem":2172},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2174,"path":2175,"stem":2176},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2178,"path":2179,"stem":2180},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2182,"path":2183,"stem":2184},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2186,"icon":132,"path":2187,"stem":2188,"children":2189,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2190],{"title":2191,"path":2192,"stem":2193},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2195,"icon":132,"path":2196,"stem":2197,"children":2198,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2199,2203,2207,2211],{"title":2200,"path":2201,"stem":2202},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2204,"path":2205,"stem":2206},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2208,"path":2209,"stem":2210},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2212,"path":2213,"stem":2214},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2216,"icon":92,"path":2217,"stem":2218,"children":2219,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2220,2224,2228],{"title":2221,"path":2222,"stem":2223},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2225,"path":2226,"stem":2227},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2075,"path":2229,"stem":2230},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2232,"icon":132,"path":2233,"stem":2234,"children":2235,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2236,2240,2244,2248,2252,2256],{"title":2237,"path":2238,"stem":2239},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2241,"path":2242,"stem":2243},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2245,"path":2246,"stem":2247},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2249,"path":2250,"stem":2251},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2253,"path":2254,"stem":2255},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2257,"path":2258,"stem":2259},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2261,"icon":132,"path":2262,"stem":2263,"children":2264,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2265,2269,2273,2277,2281],{"title":2266,"path":2267,"stem":2268},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2270,"path":2271,"stem":2272},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2274,"path":2275,"stem":2276},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2278,"path":2279,"stem":2280},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2282,"path":2283,"stem":2284},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2286,"icon":132,"path":2287,"stem":2288,"children":2289},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2290,2292,2296,2300,2304,2308,2312],{"title":2291,"path":2287,"stem":2288},"UI Бібліотеки в React",{"title":2293,"path":2294,"stem":2295},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2297,"path":2298,"stem":2299},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2301,"path":2302,"stem":2303},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2305,"path":2306,"stem":2307},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2309,"path":2310,"stem":2311},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2313,"path":2314,"stem":2315},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2317,"icon":2318,"path":2319,"stem":2320,"children":2321,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2322,2326,2330,2334,2338,2342,2346,2350],{"title":2323,"path":2324,"stem":2325},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2327,"path":2328,"stem":2329},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2331,"path":2332,"stem":2333},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2335,"path":2336,"stem":2337},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2339,"path":2340,"stem":2341},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2343,"path":2344,"stem":2345},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2347,"path":2348,"stem":2349},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2351,"path":2352,"stem":2353},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2355,"path":2356,"stem":2357},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2359,"icon":2360,"path":2361,"stem":2362,"children":2363,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2364,2367,2370,2374,2378,2382,2386],{"title":162,"path":2365,"stem":2366},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2368,"stem":2369},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2371,"path":2372,"stem":2373},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2375,"path":2376,"stem":2377},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2379,"path":2380,"stem":2381},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2383,"path":2384,"stem":2385},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2387,"path":2388,"stem":2389,"children":2390,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2391,2395,2399,2403,2407,2411,2415,2419,2423,2427,2431,2435,2439,2443,2447,2451,2455,2459,2463,2467,2471,2475,2479,2483,2487,2491,2495,2499,2503,2507,2511,2515,2519,2523,2527,2531,2535],{"title":2392,"path":2393,"stem":2394},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2396,"path":2397,"stem":2398},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2400,"path":2401,"stem":2402},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2404,"path":2405,"stem":2406},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2408,"path":2409,"stem":2410},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2412,"path":2413,"stem":2414},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2416,"path":2417,"stem":2418},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2420,"path":2421,"stem":2422},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2424,"path":2425,"stem":2426},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2428,"path":2429,"stem":2430},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2432,"path":2433,"stem":2434},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2436,"path":2437,"stem":2438},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2440,"path":2441,"stem":2442},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2444,"path":2445,"stem":2446},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2448,"path":2449,"stem":2450},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2452,"path":2453,"stem":2454},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2456,"path":2457,"stem":2458},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2460,"path":2461,"stem":2462},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2464,"path":2465,"stem":2466},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2468,"path":2469,"stem":2470},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2472,"path":2473,"stem":2474},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2476,"path":2477,"stem":2478},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2480,"path":2481,"stem":2482},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2484,"path":2485,"stem":2486},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2488,"path":2489,"stem":2490},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2492,"path":2493,"stem":2494},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2496,"path":2497,"stem":2498},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2500,"path":2501,"stem":2502},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2504,"path":2505,"stem":2506},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2508,"path":2509,"stem":2510},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2512,"path":2513,"stem":2514},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2516,"path":2517,"stem":2518},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2520,"path":2521,"stem":2522},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2524,"path":2525,"stem":2526},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2528,"path":2529,"stem":2530},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2532,"path":2533,"stem":2534},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2536,"path":2537,"stem":2538},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2540,"icon":2541,"path":2542,"stem":2543,"children":2544,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2545,2549,2552,2556,2560,2564,2568,2572,2576,2580,2584,2588,2592,2596,2600,2604],{"title":2546,"path":2547,"stem":2548},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2550,"stem":2551},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2553,"path":2554,"stem":2555},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2557,"path":2558,"stem":2559},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2561,"path":2562,"stem":2563},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2565,"path":2566,"stem":2567},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2569,"path":2570,"stem":2571},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2573,"path":2574,"stem":2575},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2577,"path":2578,"stem":2579},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2581,"path":2582,"stem":2583},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2585,"path":2586,"stem":2587},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2589,"path":2590,"stem":2591},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2593,"path":2594,"stem":2595},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2597,"path":2598,"stem":2599},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2601,"path":2602,"stem":2603},"📦 Повний посібник з модулів, пакетів та віртуальних середовищ у Python","\u002Fpython\u002Flesson_9","05.python\u002Flesson_9",{"title":2605,"path":2606,"stem":2607},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2609,"icon":2610,"path":2611,"stem":2612,"children":2613,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2614,2644,2667,2704,2733,2751,2785,2797,2806],{"title":2615,"icon":2616,"path":2617,"stem":2618,"children":2619,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2620,2624,2628,2632,2636,2640],{"title":2621,"path":2622,"stem":2623},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2625,"path":2626,"stem":2627},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2629,"path":2630,"stem":2631},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2633,"path":2634,"stem":2635},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2637,"path":2638,"stem":2639},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2641,"path":2642,"stem":2643},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2645,"icon":2610,"path":2646,"stem":2647,"children":2648,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2649,2653,2659,2663],{"title":2650,"path":2651,"stem":2652},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2654,"path":2655,"stem":2656,"children":2657},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2658],{"title":2654,"path":2655,"stem":2656},{"title":2660,"path":2661,"stem":2662},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2664,"path":2665,"stem":2666},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2668,"icon":2610,"path":2669,"stem":2670,"children":2671,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2672,2676,2680,2684,2688,2692,2696,2700],{"title":2673,"path":2674,"stem":2675},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2677,"path":2678,"stem":2679},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2681,"path":2682,"stem":2683},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2685,"path":2686,"stem":2687},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2689,"path":2690,"stem":2691},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2693,"path":2694,"stem":2695},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2697,"path":2698,"stem":2699},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2701,"path":2702,"stem":2703},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2705,"icon":2610,"path":2706,"stem":2707,"children":2708,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2709,2713,2717,2721,2725,2729],{"title":2710,"path":2711,"stem":2712},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2714,"path":2715,"stem":2716},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2718,"path":2719,"stem":2720},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2722,"path":2723,"stem":2724},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2726,"path":2727,"stem":2728},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2730,"path":2731,"stem":2732},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2734,"icon":2735,"path":2736,"stem":2737,"children":2738,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2739,2743,2747],{"title":2740,"path":2741,"stem":2742},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2744,"path":2745,"stem":2746},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2748,"path":2749,"stem":2750},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2752,"icon":2753,"path":2754,"stem":2755,"children":2756,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2757,2761,2765,2769,2773,2777,2781],{"title":2758,"path":2759,"stem":2760},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2762,"path":2763,"stem":2764},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2766,"path":2767,"stem":2768},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2770,"path":2771,"stem":2772},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2774,"path":2775,"stem":2776},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2778,"path":2779,"stem":2780},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2782,"path":2783,"stem":2784},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2782,"icon":793,"path":2786,"stem":2787,"children":2788,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[2789,2793],{"title":2790,"path":2791,"stem":2792},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":2794,"path":2795,"stem":2796},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":2798,"icon":2799,"path":2800,"stem":2801,"children":2802,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[2803],{"title":2798,"path":2804,"stem":2805},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":2807,"icon":2808,"path":2809,"stem":2810,"children":2811,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[2812],{"title":2807,"path":2813,"stem":2814},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":2816,"icon":2817,"path":2818,"stem":2819,"children":2820,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[2821,2897],{"title":2822,"icon":2823,"path":2824,"stem":2825,"children":2826},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[2827,2829,2833,2837,2841,2845,2849,2853,2857,2861,2865,2869,2873,2877,2881,2885,2889,2893],{"title":2828,"path":2824,"stem":2825},"Docker: від нуля до production",{"title":2830,"path":2831,"stem":2832},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":2834,"path":2835,"stem":2836},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":2838,"path":2839,"stem":2840},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":2842,"path":2843,"stem":2844},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":2846,"path":2847,"stem":2848},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":2850,"path":2851,"stem":2852},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":2854,"path":2855,"stem":2856},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":2858,"path":2859,"stem":2860},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":2862,"path":2863,"stem":2864},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":2866,"path":2867,"stem":2868},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":2870,"path":2871,"stem":2872},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":2874,"path":2875,"stem":2876},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":2878,"path":2879,"stem":2880},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":2882,"path":2883,"stem":2884},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":2886,"path":2887,"stem":2888},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":2890,"path":2891,"stem":2892},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":2894,"path":2895,"stem":2896},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":2898,"icon":2899,"path":2900,"stem":2901,"children":2902},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[2903,2905,2909,2913,2917,2921,2925,2929,2933],{"title":2904,"path":2900,"stem":2901},"Kubernetes: від розробки до production",{"title":2906,"path":2907,"stem":2908},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":2910,"path":2911,"stem":2912},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":2914,"path":2915,"stem":2916},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":2918,"path":2919,"stem":2920},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":2922,"path":2923,"stem":2924},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":2926,"path":2927,"stem":2928},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":2930,"path":2931,"stem":2932},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":2934,"path":2935,"stem":2936},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":2938,"icon":2939,"path":2940,"stem":2941,"children":2942,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[2943,2947,2951,2955,2959,2963,2967,2971,2975,2979,2983],{"title":2944,"path":2945,"stem":2946},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":2948,"path":2949,"stem":2950},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":2952,"path":2953,"stem":2954},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":2956,"path":2957,"stem":2958},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":2960,"path":2961,"stem":2962},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":2964,"path":2965,"stem":2966},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":2968,"path":2969,"stem":2970},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":2972,"path":2973,"stem":2974},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":2976,"path":2977,"stem":2978},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":2980,"path":2981,"stem":2982},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":2984,"path":2985,"stem":2986},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":2988,"icon":943,"path":2989,"stem":2990,"children":2991,"page":59},"DDD","\u002Fddd","10.ddd",[2992,2996,3000,3004,3008,3012,3016,3020,3024,3028,3032,3036,3040],{"title":2993,"path":2994,"stem":2995},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":2997,"path":2998,"stem":2999},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3001,"path":3002,"stem":3003},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3005,"path":3006,"stem":3007},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3009,"path":3010,"stem":3011},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3013,"path":3014,"stem":3015},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3017,"path":3018,"stem":3019},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3021,"path":3022,"stem":3023},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3025,"path":3026,"stem":3027},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3029,"path":3030,"stem":3031},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3033,"path":3034,"stem":3035},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3037,"path":3038,"stem":3039},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3041,"path":3042,"stem":3043},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3045,"icon":3046,"path":3047,"stem":3048,"children":3049,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3050,3054,3058,3062,3066,3070,3074],{"title":3051,"path":3052,"stem":3053},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3055,"path":3056,"stem":3057},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3059,"path":3060,"stem":3061},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3063,"path":3064,"stem":3065},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3067,"path":3068,"stem":3069},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3071,"path":3072,"stem":3073},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3075,"path":3076,"stem":3077},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3079,"icon":3080,"path":3081,"stem":3082,"children":3083,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3084,3088,3092,3096,3100,3104,3108,3112,3116,3120,3124,3128,3132,3136,3140,3144,3148,3152,3156,3160,3164,3168,3172,3176,3180,3184,3188,3192,3196,3200],{"title":3085,"path":3086,"stem":3087},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3089,"path":3090,"stem":3091},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3093,"path":3094,"stem":3095},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3097,"path":3098,"stem":3099},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3101,"path":3102,"stem":3103},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3105,"path":3106,"stem":3107},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3109,"path":3110,"stem":3111},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3113,"path":3114,"stem":3115},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3117,"path":3118,"stem":3119},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3121,"path":3122,"stem":3123},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3125,"path":3126,"stem":3127},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3129,"path":3130,"stem":3131},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3133,"path":3134,"stem":3135},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3137,"path":3138,"stem":3139},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3141,"path":3142,"stem":3143},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3145,"path":3146,"stem":3147},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3149,"path":3150,"stem":3151},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3153,"path":3154,"stem":3155},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3157,"path":3158,"stem":3159},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3161,"path":3162,"stem":3163},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3165,"path":3166,"stem":3167},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3169,"path":3170,"stem":3171},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3173,"path":3174,"stem":3175},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3177,"path":3178,"stem":3179},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3181,"path":3182,"stem":3183},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3185,"path":3186,"stem":3187},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3189,"path":3190,"stem":3191},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3193,"path":3194,"stem":3195},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3197,"path":3198,"stem":3199},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3201,"path":3202,"stem":3203},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3205,"path":3206,"stem":3207,"children":3208,"page":59},"AWS","\u002Faws","13.aws",[3209,3213,3217,3221,3225,3229,3233,3237,3241,3245,3249,3253,3257,3261,3265,3269,3273,3277],{"title":3210,"path":3211,"stem":3212},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3214,"path":3215,"stem":3216},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3218,"path":3219,"stem":3220},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3222,"path":3223,"stem":3224},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3226,"path":3227,"stem":3228},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3230,"path":3231,"stem":3232},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3234,"path":3235,"stem":3236},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3238,"path":3239,"stem":3240},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3242,"path":3243,"stem":3244},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3246,"path":3247,"stem":3248},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3250,"path":3251,"stem":3252},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3254,"path":3255,"stem":3256},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3258,"path":3259,"stem":3260},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3262,"path":3263,"stem":3264},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3266,"path":3267,"stem":3268},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3270,"path":3271,"stem":3272},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3274,"path":3275,"stem":3276},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3278,"path":3279,"stem":3280},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3282,"path":3283,"stem":3284,"children":3285,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[3286,3290,3294,3298,3302,3306,3310,3314,3318,3322,3326,3330],{"title":3287,"path":3288,"stem":3289},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":3291,"path":3292,"stem":3293},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":3295,"path":3296,"stem":3297},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":3299,"path":3300,"stem":3301},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":3303,"path":3304,"stem":3305},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":3307,"path":3308,"stem":3309},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":3311,"path":3312,"stem":3313},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":3315,"path":3316,"stem":3317},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":3319,"path":3320,"stem":3321},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":3323,"path":3324,"stem":3325},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":3327,"path":3328,"stem":3329},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":3331,"path":3332,"stem":3333},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":3335,"path":3336,"stem":3337},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":3339,"title":3242,"body":3340,"description":19652,"extension":19653,"links":19654,"meta":19655,"navigation":3424,"path":3243,"seo":19656,"stem":3244,"__hash__":19657},"docs\u002F13.aws\u002F05.alb-asg.md",{"type":3341,"value":3342,"toc":19585},"minimark",[3343,3347,3352,3356,3363,3369,3375,3386,3625,3628,3632,3638,3714,3807,3809,3813,3823,3828,3831,4008,4012,4018,4062,4066,4072,4077,4131,4136,4154,4158,4164,4169,4184,4186,4190,4204,4211,4366,4372,4374,4378,4382,4385,4390,4396,4402,4407,4421,4558,4572,4732,4738,4822,4824,4828,4838,4843,4849,5023,5029,5121,5126,5143,5148,5201,5285,5290,5335,5382,5384,5388,5394,5400,5406,5625,5630,5637,6083,6208,6227,6229,6233,6377,6382,6496,6500,6506,6654,6659,6779,6828,6838,6840,6844,6851,6861,7869,7881,7883,7887,7893,7897,7900,8093,8121,8125,8131,8285,8295,8297,8301,8304,8454,8458,8461,8652,8722,8727,8841,8886,8890,8897,9348,9390,9395,9559,9602,9719,9723,9726,10009,10066,10068,10072,10076,10082,10243,10248,10632,10719,10723,10733,10741,10744,10749,11025,11027,11031,11035,11046,11170,11286,11290,11300,11382,11393,11397,11622,11624,11628,11631,11635,11649,12566,12568,12572,12575,12671,13282,13285,13289,13292,13356,13416,13420,13423,13513,13564,13568,13571,13796,13869,13873,13876,13914,13972,13974,13977,13979,13983,13986,14226,14271,14273,14277,14281,14295,14305,14478,14482,14485,14566,14570,14586,14616,14630,14760,14764,14774,14780,14784,14806,15026,15031,15052,15061,15067,15083,15092,15096,15105,15136,15149,15153,15724,15834,15921,15934,15936,15940,15943,16128,16707,16767,16769,16773,17043,17066,17068,17072,17601,17641,17643,17647,17650,17814,17816,17820,17825,18235,18255,18257,18261,18595,18645,18647,18651,18654,18704,18707,18774,18814,18816,18820,18823,18894,18897,18956,19000,19010,19012,19016,19023,19026,19373,19375,19379,19501,19503,19507,19511,19517,19526,19530,19552,19571,19575,19581],[3344,3345,3242],"h1",{"id":3346},"elastic-load-balancing-та-auto-scaling",[3348,3349,3351],"h2",{"id":3350},"проблема-одного-сервера-мотивація-до-масштабування","Проблема одного сервера: мотивація до масштабування",[3353,3354,3355],"p",{},"Розглянемо фундаментальну проблему, з якою стикається кожна команда розробників при переході від прототипу до production-системи. Уявіть, що ваш .NET API розміщений на єдиному EC2 instance — це типова відправна точка для більшості проектів. Архітектура проста, і на початковому етапі вона цілком задовольняє вимоги. Однак із зростанням продукту виникають три класи критичних проблем, кожна з яких потенційно призводить до повної недоступності сервісу.",[3353,3357,3358,3362],{},[3359,3360,3361],"strong",{},"Перший клас: пікові навантаження."," Система функціонує стабільно при нормальному трафіку, але в момент публічного анонсу, маркетингової акції або вірусного поширення кількість одночасних користувачів зростає на порядок. Єдиний сервер вичерпує ресурси процесора та оперативної пам'яті, час відповіді зростає з мілісекунд до десятків секунд, черга з'єднань переповнюється, і сервер переходить у стан відмови.",[3353,3364,3365,3368],{},[3359,3366,3367],{},"Другий клас: апаратні та програмні збої."," Жоден фізичний або віртуальний сервер не має стовідсоткової гарантії безперебійної роботи. Відмова дискового масиву, пошкодження операційної системи, мережевий збій або баг у коді — кожна з цих подій при архітектурі єдиного сервера означає повну недоступність сервісу до ручного втручання інженера, що може тривати від хвилин до годин.",[3353,3370,3371,3374],{},[3359,3372,3373],{},"Третій клас: деплой без простою."," Оновлення застосунку на єдиному сервері неминуче передбачає зупинку процесу, встановлення нової версії та повторний запуск. Протягом цього часу сервіс недоступний — це неприйнятно для систем із вимогами до uptime 99.9% і вище.",[3353,3376,3377,3378,3381,3382,3385],{},"Всі три проблеми вирішуються архітектурним підходом, що базується на двох взаємодоповнюючих механізмах: ",[3359,3379,3380],{},"Load Balancer"," рівномірно розподіляє вхідний трафік між кількома серверами, а ",[3359,3383,3384],{},"Auto Scaling"," автоматично регулює кількість серверів відповідно до поточного навантаження.",[3387,3388,3389],"plant-uml",{},[3390,3391,3396],"pre",{"className":3392,"code":3393,"language":3394,"meta":3395,"style":3395},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Користувач A\" as UA\nactor \"Користувач B\" as UB\nactor \"Користувач C\" as UC\n\nrectangle \"Application Load Balancer\\n(Layer 7 — HTTP\u002FHTTPS)\" as ALB #fef3c7 {\n    note right\n      Приймає ВЕСЬ вхідний трафік\n      Перевіряє health кожного сервера\n      Рівномірно розподіляє запити\n      SSL termination\n    end note\n}\n\npackage \"Auto Scaling Group (min:2, max:10)\" #d1fae5 {\n    node \"EC2 #1\\n.NET API\\n(Running)\" as EC1 #bbf7d0\n    node \"EC2 #2\\n.NET API\\n(Running)\" as EC2 #bbf7d0\n    node \"EC2 #3\\n.NET API\\n(Auto-launched)\" as EC3 #fef9c3 {\n        note right : Запускається автоматично\\nколи CPU > 70%\n    }\n}\n\nUA --> ALB\nUB --> ALB\nUC --> ALB\nALB --> EC1 : Round Robin\nALB --> EC2 : Round Robin\nALB --> EC3 : Round Robin\n\nnote bottom of ALB\n  Якщо EC2 #2 \"падає\" — ALB\n  автоматично перестає\n  направляти трафік на нього\nend note\n@enduml\n","plantuml","",[3397,3398,3399,3407,3413,3419,3426,3432,3438,3444,3449,3455,3461,3467,3473,3479,3485,3491,3497,3502,3508,3514,3520,3526,3532,3538,3543,3548,3554,3560,3566,3572,3578,3584,3589,3595,3601,3607,3613,3619],"code",{"__ignoreMap":3395},[3400,3401,3404],"span",{"class":3402,"line":3403},"line",1,[3400,3405,3406],{},"@startuml\n",[3400,3408,3410],{"class":3402,"line":3409},2,[3400,3411,3412],{},"skinparam style plain\n",[3400,3414,3416],{"class":3402,"line":3415},3,[3400,3417,3418],{},"skinparam backgroundColor #ffffff\n",[3400,3420,3422],{"class":3402,"line":3421},4,[3400,3423,3425],{"emptyLinePlaceholder":3424},true,"\n",[3400,3427,3429],{"class":3402,"line":3428},5,[3400,3430,3431],{},"actor \"Користувач A\" as UA\n",[3400,3433,3435],{"class":3402,"line":3434},6,[3400,3436,3437],{},"actor \"Користувач B\" as UB\n",[3400,3439,3441],{"class":3402,"line":3440},7,[3400,3442,3443],{},"actor \"Користувач C\" as UC\n",[3400,3445,3447],{"class":3402,"line":3446},8,[3400,3448,3425],{"emptyLinePlaceholder":3424},[3400,3450,3452],{"class":3402,"line":3451},9,[3400,3453,3454],{},"rectangle \"Application Load Balancer\\n(Layer 7 — HTTP\u002FHTTPS)\" as ALB #fef3c7 {\n",[3400,3456,3458],{"class":3402,"line":3457},10,[3400,3459,3460],{},"    note right\n",[3400,3462,3464],{"class":3402,"line":3463},11,[3400,3465,3466],{},"      Приймає ВЕСЬ вхідний трафік\n",[3400,3468,3470],{"class":3402,"line":3469},12,[3400,3471,3472],{},"      Перевіряє health кожного сервера\n",[3400,3474,3476],{"class":3402,"line":3475},13,[3400,3477,3478],{},"      Рівномірно розподіляє запити\n",[3400,3480,3482],{"class":3402,"line":3481},14,[3400,3483,3484],{},"      SSL termination\n",[3400,3486,3488],{"class":3402,"line":3487},15,[3400,3489,3490],{},"    end note\n",[3400,3492,3494],{"class":3402,"line":3493},16,[3400,3495,3496],{},"}\n",[3400,3498,3500],{"class":3402,"line":3499},17,[3400,3501,3425],{"emptyLinePlaceholder":3424},[3400,3503,3505],{"class":3402,"line":3504},18,[3400,3506,3507],{},"package \"Auto Scaling Group (min:2, max:10)\" #d1fae5 {\n",[3400,3509,3511],{"class":3402,"line":3510},19,[3400,3512,3513],{},"    node \"EC2 #1\\n.NET API\\n(Running)\" as EC1 #bbf7d0\n",[3400,3515,3517],{"class":3402,"line":3516},20,[3400,3518,3519],{},"    node \"EC2 #2\\n.NET API\\n(Running)\" as EC2 #bbf7d0\n",[3400,3521,3523],{"class":3402,"line":3522},21,[3400,3524,3525],{},"    node \"EC2 #3\\n.NET API\\n(Auto-launched)\" as EC3 #fef9c3 {\n",[3400,3527,3529],{"class":3402,"line":3528},22,[3400,3530,3531],{},"        note right : Запускається автоматично\\nколи CPU > 70%\n",[3400,3533,3535],{"class":3402,"line":3534},23,[3400,3536,3537],{},"    }\n",[3400,3539,3541],{"class":3402,"line":3540},24,[3400,3542,3496],{},[3400,3544,3546],{"class":3402,"line":3545},25,[3400,3547,3425],{"emptyLinePlaceholder":3424},[3400,3549,3551],{"class":3402,"line":3550},26,[3400,3552,3553],{},"UA --> ALB\n",[3400,3555,3557],{"class":3402,"line":3556},27,[3400,3558,3559],{},"UB --> ALB\n",[3400,3561,3563],{"class":3402,"line":3562},28,[3400,3564,3565],{},"UC --> ALB\n",[3400,3567,3569],{"class":3402,"line":3568},29,[3400,3570,3571],{},"ALB --> EC1 : Round Robin\n",[3400,3573,3575],{"class":3402,"line":3574},30,[3400,3576,3577],{},"ALB --> EC2 : Round Robin\n",[3400,3579,3581],{"class":3402,"line":3580},31,[3400,3582,3583],{},"ALB --> EC3 : Round Robin\n",[3400,3585,3587],{"class":3402,"line":3586},32,[3400,3588,3425],{"emptyLinePlaceholder":3424},[3400,3590,3592],{"class":3402,"line":3591},33,[3400,3593,3594],{},"note bottom of ALB\n",[3400,3596,3598],{"class":3402,"line":3597},34,[3400,3599,3600],{},"  Якщо EC2 #2 \"падає\" — ALB\n",[3400,3602,3604],{"class":3402,"line":3603},35,[3400,3605,3606],{},"  автоматично перестає\n",[3400,3608,3610],{"class":3402,"line":3609},36,[3400,3611,3612],{},"  направляти трафік на нього\n",[3400,3614,3616],{"class":3402,"line":3615},37,[3400,3617,3618],{},"end note\n",[3400,3620,3622],{"class":3402,"line":3621},38,[3400,3623,3624],{},"@enduml\n",[3626,3627],"hr",{},[3348,3629,3631],{"id":3630},"aws-elastic-load-balancing-огляд-сімейства-сервісів","AWS Elastic Load Balancing: огляд сімейства сервісів",[3353,3633,3634,3637],{},[3359,3635,3636],{},"AWS Elastic Load Balancing (ELB)"," — це керований сервіс AWS, що автоматично розподіляє вхідний трафік між кількома цільовими ресурсами: EC2 instances, контейнерами, IP-адресами або Lambda-функціями. AWS надає три типи балансувальників навантаження, кожен з яких оптимізований для конкретних сценаріїв використання.",[3639,3640,3641,3668,3691],"card-group",{},[3642,3643,3646,3652],"card",{"icon":3644,"title":3645},"i-heroicons-globe-alt","Application Load Balancer",[3353,3647,3648,3651],{},[3359,3649,3650],{},"Рівень OSI:"," 7 (Application)",[3653,3654,3655,3659,3662,3665],"ul",{},[3656,3657,3658],"li",{},"HTTP\u002FHTTPS, HTTP\u002F2, gRPC, WebSocket",[3656,3660,3661],{},"Маршрутизація за URL, заголовками, cookies",[3656,3663,3664],{},"SSL\u002FTLS termination",[3656,3666,3667],{},"Ідеальний для .NET Web API та мікросервісів",[3642,3669,3672,3677],{"icon":3670,"title":3671},"i-heroicons-bolt","Network Load Balancer",[3353,3673,3674,3676],{},[3359,3675,3650],{}," 4 (Transport)",[3653,3678,3679,3682,3685,3688],{},[3656,3680,3681],{},"TCP, UDP, TLS",[3656,3683,3684],{},"Мільйони запитів\u002Fсек, latency \u003C 1ms",[3656,3686,3687],{},"Статична IP-адреса балансувальника",[3656,3689,3690],{},"Gaming, IoT, фінансові системи",[3642,3692,3695,3700],{"icon":3693,"title":3694},"i-heroicons-shield-check","Gateway Load Balancer",[3353,3696,3697,3699],{},[3359,3698,3650],{}," 3 (Network)",[3653,3701,3702,3705,3708,3711],{},[3656,3703,3704],{},"Прозора вставка в мережевий шлях",[3656,3706,3707],{},"Firewall, IDS\u002FIPS, deep packet inspection",[3656,3709,3710],{},"Для мережевих virtual appliances",[3656,3712,3713],{},"Рідко потрібен .NET розробникам",[3387,3715,3716],{},[3390,3717,3719],{"className":3392,"code":3718,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"OSI Model\" {\n    rectangle \"L7 Application\\nHTTP\u002FHTTPS\u002FgRPC\" as L7 #fef3c7\n    rectangle \"L4 Transport\\nTCP\u002FUDP\u002FTLS\" as L4 #dbeafe\n    rectangle \"L3 Network\\nIP Packets\" as L3 #fce7f3\n}\n\nrectangle \"Application Load Balancer\" as ALB #fef3c7\nrectangle \"Network Load Balancer\" as NLB #dbeafe\nrectangle \"Gateway Load Balancer\" as GWLB #fce7f3\n\nL7 --> ALB : \"«розуміє» HTTP-запити\\nURL, заголовки, cookies\"\nL4 --> NLB : \"бачить лише TCP\u002FUDP пакети\\nнадзвичайно швидкий\"\nL3 --> GWLB : \"прозора вставка\\nу мережевий шлях\"\n\n@enduml\n",[3397,3720,3721,3725,3729,3733,3737,3742,3747,3752,3757,3761,3765,3770,3775,3780,3784,3789,3794,3799,3803],{"__ignoreMap":3395},[3400,3722,3723],{"class":3402,"line":3403},[3400,3724,3406],{},[3400,3726,3727],{"class":3402,"line":3409},[3400,3728,3412],{},[3400,3730,3731],{"class":3402,"line":3415},[3400,3732,3418],{},[3400,3734,3735],{"class":3402,"line":3421},[3400,3736,3425],{"emptyLinePlaceholder":3424},[3400,3738,3739],{"class":3402,"line":3428},[3400,3740,3741],{},"package \"OSI Model\" {\n",[3400,3743,3744],{"class":3402,"line":3434},[3400,3745,3746],{},"    rectangle \"L7 Application\\nHTTP\u002FHTTPS\u002FgRPC\" as L7 #fef3c7\n",[3400,3748,3749],{"class":3402,"line":3440},[3400,3750,3751],{},"    rectangle \"L4 Transport\\nTCP\u002FUDP\u002FTLS\" as L4 #dbeafe\n",[3400,3753,3754],{"class":3402,"line":3446},[3400,3755,3756],{},"    rectangle \"L3 Network\\nIP Packets\" as L3 #fce7f3\n",[3400,3758,3759],{"class":3402,"line":3451},[3400,3760,3496],{},[3400,3762,3763],{"class":3402,"line":3457},[3400,3764,3425],{"emptyLinePlaceholder":3424},[3400,3766,3767],{"class":3402,"line":3463},[3400,3768,3769],{},"rectangle \"Application Load Balancer\" as ALB #fef3c7\n",[3400,3771,3772],{"class":3402,"line":3469},[3400,3773,3774],{},"rectangle \"Network Load Balancer\" as NLB #dbeafe\n",[3400,3776,3777],{"class":3402,"line":3475},[3400,3778,3779],{},"rectangle \"Gateway Load Balancer\" as GWLB #fce7f3\n",[3400,3781,3782],{"class":3402,"line":3481},[3400,3783,3425],{"emptyLinePlaceholder":3424},[3400,3785,3786],{"class":3402,"line":3487},[3400,3787,3788],{},"L7 --> ALB : \"«розуміє» HTTP-запити\\nURL, заголовки, cookies\"\n",[3400,3790,3791],{"class":3402,"line":3493},[3400,3792,3793],{},"L4 --> NLB : \"бачить лише TCP\u002FUDP пакети\\nнадзвичайно швидкий\"\n",[3400,3795,3796],{"class":3402,"line":3499},[3400,3797,3798],{},"L3 --> GWLB : \"прозора вставка\\nу мережевий шлях\"\n",[3400,3800,3801],{"class":3402,"line":3504},[3400,3802,3425],{"emptyLinePlaceholder":3424},[3400,3804,3805],{"class":3402,"line":3510},[3400,3806,3624],{},[3626,3808],{},[3348,3810,3812],{"id":3811},"application-load-balancer-alb-детальний-розгляд","Application Load Balancer (ALB) — детальний розгляд",[3353,3814,3815,3818,3819,3822],{},[3359,3816,3817],{},"Application Load Balancer (ALB)"," є найбільш функціонально насиченим балансувальником навантаження у сімействі AWS ELB та є стандартним вибором для .NET Web API та мікросервісних архітектур. Його ключова відмінність від NLB полягає у тому, що ALB функціонує на ",[3359,3820,3821],{},"сьомому рівні моделі OSI"," — рівні застосунків. Це означає, що ALB не просто передає мережеві пакети, а повністю розуміє семантику протоколу HTTP: він аналізує URI запиту, HTTP-заголовки, query parameters, тіло запиту та cookie-файли. На основі цього аналізу ALB приймає інтелектуальні рішення про маршрутизацію.",[3824,3825,3827],"h3",{"id":3826},"архітектура-alb-listener-rule-target-group","Архітектура ALB: Listener → Rule → Target Group",[3353,3829,3830],{},"Внутрішня архітектура ALB будується на трирівневій ієрархії компонентів:",[3387,3832,3833],{},[3390,3834,3836],{"className":3392,"code":3835,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nrectangle \"Internet\" as I #e2e8f0\n\nrectangle \"ALB\\n(DNS: myapp.elb.amazonaws.com)\" as ALB #fef3c7\n\nrectangle \"Listener :80\\n(HTTP)\" as L80 #e0e7ff\nrectangle \"Listener :443\\n(HTTPS)\" as L443 #dbeafe\n\ntogether {\n    rectangle \"Rule 1\\nIF path = \u002Fapi\u002F*\\nTHEN forward → api-tg\" as R1 #f0fdf4\n    rectangle \"Rule 2\\nIF path = \u002Fhealth\\nTHEN return 200 OK\" as R2 #f0fdf4\n    rectangle \"Rule 3 (default)\\nIF true\\nTHEN forward → web-tg\" as R3 #f0fdf4\n}\n\npackage \"Target Group: api-tg\\nprotocol=HTTP port=5000\" as APITG #d1fae5 {\n    node \"EC2 #1\\nHealthy ✓\" as A1 #bbf7d0\n    node \"EC2 #2\\nHealthy ✓\" as A2 #bbf7d0\n}\n\npackage \"Target Group: web-tg\\nprotocol=HTTP port=3000\" as WEBTG #fce7f3 {\n    node \"EC2 #3\\nHealthy ✓\" as W1 #fbcfe8\n}\n\nI --> ALB\nALB -down-> L80 : \"HTTP:80\"\nALB -down-> L443 : \"HTTPS:443\"\nL80 -down-> R1\nL80 -down-> R2\nL80 -down-> R3\nL443 -down-> R1 : \"(Same rules)\"\nR1 -right-> APITG\nR3 -right-> WEBTG\n\n@enduml\n",[3397,3837,3838,3842,3846,3850,3854,3859,3863,3868,3872,3877,3882,3886,3891,3896,3901,3906,3910,3914,3919,3924,3929,3933,3937,3942,3947,3951,3955,3960,3965,3970,3975,3980,3985,3990,3995,4000,4004],{"__ignoreMap":3395},[3400,3839,3840],{"class":3402,"line":3403},[3400,3841,3406],{},[3400,3843,3844],{"class":3402,"line":3409},[3400,3845,3412],{},[3400,3847,3848],{"class":3402,"line":3415},[3400,3849,3418],{},[3400,3851,3852],{"class":3402,"line":3421},[3400,3853,3425],{"emptyLinePlaceholder":3424},[3400,3855,3856],{"class":3402,"line":3428},[3400,3857,3858],{},"rectangle \"Internet\" as I #e2e8f0\n",[3400,3860,3861],{"class":3402,"line":3434},[3400,3862,3425],{"emptyLinePlaceholder":3424},[3400,3864,3865],{"class":3402,"line":3440},[3400,3866,3867],{},"rectangle \"ALB\\n(DNS: myapp.elb.amazonaws.com)\" as ALB #fef3c7\n",[3400,3869,3870],{"class":3402,"line":3446},[3400,3871,3425],{"emptyLinePlaceholder":3424},[3400,3873,3874],{"class":3402,"line":3451},[3400,3875,3876],{},"rectangle \"Listener :80\\n(HTTP)\" as L80 #e0e7ff\n",[3400,3878,3879],{"class":3402,"line":3457},[3400,3880,3881],{},"rectangle \"Listener :443\\n(HTTPS)\" as L443 #dbeafe\n",[3400,3883,3884],{"class":3402,"line":3463},[3400,3885,3425],{"emptyLinePlaceholder":3424},[3400,3887,3888],{"class":3402,"line":3469},[3400,3889,3890],{},"together {\n",[3400,3892,3893],{"class":3402,"line":3475},[3400,3894,3895],{},"    rectangle \"Rule 1\\nIF path = \u002Fapi\u002F*\\nTHEN forward → api-tg\" as R1 #f0fdf4\n",[3400,3897,3898],{"class":3402,"line":3481},[3400,3899,3900],{},"    rectangle \"Rule 2\\nIF path = \u002Fhealth\\nTHEN return 200 OK\" as R2 #f0fdf4\n",[3400,3902,3903],{"class":3402,"line":3487},[3400,3904,3905],{},"    rectangle \"Rule 3 (default)\\nIF true\\nTHEN forward → web-tg\" as R3 #f0fdf4\n",[3400,3907,3908],{"class":3402,"line":3493},[3400,3909,3496],{},[3400,3911,3912],{"class":3402,"line":3499},[3400,3913,3425],{"emptyLinePlaceholder":3424},[3400,3915,3916],{"class":3402,"line":3504},[3400,3917,3918],{},"package \"Target Group: api-tg\\nprotocol=HTTP port=5000\" as APITG #d1fae5 {\n",[3400,3920,3921],{"class":3402,"line":3510},[3400,3922,3923],{},"    node \"EC2 #1\\nHealthy ✓\" as A1 #bbf7d0\n",[3400,3925,3926],{"class":3402,"line":3516},[3400,3927,3928],{},"    node \"EC2 #2\\nHealthy ✓\" as A2 #bbf7d0\n",[3400,3930,3931],{"class":3402,"line":3522},[3400,3932,3496],{},[3400,3934,3935],{"class":3402,"line":3528},[3400,3936,3425],{"emptyLinePlaceholder":3424},[3400,3938,3939],{"class":3402,"line":3534},[3400,3940,3941],{},"package \"Target Group: web-tg\\nprotocol=HTTP port=3000\" as WEBTG #fce7f3 {\n",[3400,3943,3944],{"class":3402,"line":3540},[3400,3945,3946],{},"    node \"EC2 #3\\nHealthy ✓\" as W1 #fbcfe8\n",[3400,3948,3949],{"class":3402,"line":3545},[3400,3950,3496],{},[3400,3952,3953],{"class":3402,"line":3550},[3400,3954,3425],{"emptyLinePlaceholder":3424},[3400,3956,3957],{"class":3402,"line":3556},[3400,3958,3959],{},"I --> ALB\n",[3400,3961,3962],{"class":3402,"line":3562},[3400,3963,3964],{},"ALB -down-> L80 : \"HTTP:80\"\n",[3400,3966,3967],{"class":3402,"line":3568},[3400,3968,3969],{},"ALB -down-> L443 : \"HTTPS:443\"\n",[3400,3971,3972],{"class":3402,"line":3574},[3400,3973,3974],{},"L80 -down-> R1\n",[3400,3976,3977],{"class":3402,"line":3580},[3400,3978,3979],{},"L80 -down-> R2\n",[3400,3981,3982],{"class":3402,"line":3586},[3400,3983,3984],{},"L80 -down-> R3\n",[3400,3986,3987],{"class":3402,"line":3591},[3400,3988,3989],{},"L443 -down-> R1 : \"(Same rules)\"\n",[3400,3991,3992],{"class":3402,"line":3597},[3400,3993,3994],{},"R1 -right-> APITG\n",[3400,3996,3997],{"class":3402,"line":3603},[3400,3998,3999],{},"R3 -right-> WEBTG\n",[3400,4001,4002],{"class":3402,"line":3609},[3400,4003,3425],{"emptyLinePlaceholder":3424},[3400,4005,4006],{"class":3402,"line":3615},[3400,4007,3624],{},[3824,4009,4011],{"id":4010},"listener-точка-входу-трафіку","Listener — точка входу трафіку",[3353,4013,4014,4017],{},[3359,4015,4016],{},"Listener"," — це процес, що виконується на ALB і очікує вхідні з'єднання на визначеному порті та протоколі. Кожен ALB може мати кілька Listener'ів одночасно. Типова production-конфігурація включає два Listener'и: HTTP на порту 80 (для редиректу на HTTPS) та HTTPS на порту 443 (для обробки реального трафіку).",[4019,4020,4021,4036,4049],"field-group",{},[4022,4023,4027,4028,4031,4032,4035],"field",{"name":4024,"type":4025,"required":4026},"Protocol","enum","true","Протокол Listener'а. Допустимі значення: ",[3397,4029,4030],{},"HTTP",", ",[3397,4033,4034],{},"HTTPS",". HTTPS Listener вимагає прив'язки SSL\u002FTLS сертифіката від AWS Certificate Manager або власного сертифіката.",[4022,4037,4040,4041,4044,4045,4048],{"name":4038,"type":4039,"required":4026},"Port","integer","Порт, на якому Listener очікує вхідні з'єднання. Стандартні значення: ",[3397,4042,4043],{},"80"," для HTTP та ",[3397,4046,4047],{},"443"," для HTTPS. Допустимий діапазон: 1–65535.",[4022,4050,4053,4054,4057,4058,4061],{"name":4051,"type":4052,"required":4026},"DefaultActions","array","Дія, що виконується за замовчуванням, якщо жодне правило (Rule) не збіглось. Типово: ",[3397,4055,4056],{},"Forward"," до Target Group або ",[3397,4059,4060],{},"Redirect"," на HTTPS.",[3824,4063,4065],{"id":4064},"listener-rules-логіка-маршрутизації","Listener Rules — логіка маршрутизації",[3353,4067,4068,4071],{},[3359,4069,4070],{},"Listener Rules"," — це набір умовних правил, що визначають, як саме ALB обробляє кожен вхідний запит. Кожне правило має пріоритет (priority), одну або кілька умов (conditions) та одну дію (action). ALB перевіряє правила у порядку зростання пріоритету і виконує першу дію, умова якої співпала.",[3353,4073,4074],{},[3359,4075,4076],{},"Типи умов (conditions):",[3653,4078,4079,4092,4101,4107,4119,4125],{},[3656,4080,4081,4084,4085,4031,4088,4091],{},[3397,4082,4083],{},"path-pattern"," — URI path (",[3397,4086,4087],{},"\u002Fapi\u002F*",[3397,4089,4090],{},"\u002Fstatic\u002F*",")",[3656,4093,4094,4097,4098,4091],{},[3397,4095,4096],{},"host-header"," — доменне ім'я (",[3397,4099,4100],{},"api.example.com",[3656,4102,4103,4106],{},[3397,4104,4105],{},"http-header"," — значення HTTP-заголовка",[3656,4108,4109,4112,4113,4031,4116,4091],{},[3397,4110,4111],{},"http-request-method"," — HTTP-метод (",[3397,4114,4115],{},"GET",[3397,4117,4118],{},"POST",[3656,4120,4121,4124],{},[3397,4122,4123],{},"query-string"," — параметр query string",[3656,4126,4127,4130],{},[3397,4128,4129],{},"source-ip"," — IP-адреса або CIDR клієнта",[3353,4132,4133],{},[3359,4134,4135],{},"Типи дій (actions):",[3653,4137,4138,4143,4148],{},[3656,4139,4140,4142],{},[3397,4141,4056],{}," — передати запит до Target Group",[3656,4144,4145,4147],{},[3397,4146,4060],{}," — HTTP redirect (301\u002F302) на інший URL",[3656,4149,4150,4153],{},[3397,4151,4152],{},"Fixed Response"," — повернути статичну відповідь (код + body) без залучення серверів",[3824,4155,4157],{"id":4156},"target-group-пул-серверів","Target Group — пул серверів",[3353,4159,4160,4163],{},[3359,4161,4162],{},"Target Group"," — це логічна група серверів (targets), між якими ALB розподіляє запити, що відповідають умовам правила. Target Group є центральною концепцією архітектури ALB: вона відокремлює логіку маршрутизації від конфігурації конкретних серверів.",[3353,4165,4166],{},[3359,4167,4168],{},"Типи targets у Target Group:",[4019,4170,4171,4176,4180],{},[4022,4172,4175],{"name":4173,"type":4174},"instance","тип target","EC2 instance за його Instance ID. ALB автоматично визначає приватну IP-адресу instance та надсилає трафік напряму. Найпоширеніший тип для ASG.",[4022,4177,4179],{"name":4178,"type":4174},"ip","Довільна IP-адреса (у межах VPC або через VPN\u002FDirect Connect). Використовується для ECS Fargate задач, on-premises серверів або будь-якого іншого ресурсу з IP.",[4022,4181,4183],{"name":4182,"type":4174},"lambda","AWS Lambda function. ALB перетворює HTTP-запит у подію Lambda та очікує HTTP-відповідь. Дозволяє реалізувати serverless API за звичайним URL.",[3626,4185],{},[3348,4187,4189],{"id":4188},"network-load-balancer-nlb-layer-4","Network Load Balancer (NLB) — Layer 4",[3353,4191,4192,4195,4196,4199,4200,4203],{},[3359,4193,4194],{},"Network Load Balancer (NLB)"," функціонує на ",[3359,4197,4198],{},"четвертому рівні моделі OSI"," — транспортному рівні. На відміну від ALB, NLB не розуміє семантики HTTP: він оперує лише TCP-з'єднаннями та UDP-дейтаграмами. Ця принципова відмінність визначає його сценарії використання: NLB здатен обробляти ",[3359,4201,4202],{},"мільйони запитів на секунду"," із затримкою (latency) порядку мілісекунд або навіть мікросекунд.",[3353,4205,4206,4207,4210],{},"Ключова технічна особливість NLB — збереження IP-адреси клієнта (source IP preservation) без додаткового налаштування. ALB виступає проксі і замінює IP клієнта своєю адресою, передаючи оригінальний IP лише через HTTP-заголовок ",[3397,4208,4209],{},"X-Forwarded-For",". NLB же прозоро передає TCP-з'єднання, і сервер бачить справжній IP клієнта.",[3387,4212,4213],{},[3390,4214,4216],{"className":3392,"code":4215,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Client\\n(IP: 203.0.113.5)\" as C\n\nrectangle \"ALB\\n(Layer 7 Proxy)\" as ALB #fef3c7 {\n    note right\n      Перериває TCP-з'єднання.\n      Встановлює НОВЕ з'єднання\n      до сервера від свого IP.\n      Додає X-Forwarded-For: 203.0.113.5\n    end note\n}\n\nrectangle \"NLB\\n(Layer 4 Pass-through)\" as NLB #dbeafe {\n    note right\n      Прозоро передає TCP-з'єднання.\n      Сервер бачить справжній\n      IP клієнта: 203.0.113.5\n    end note\n}\n\nnode \".NET API\\nServer\" as SRV1 #bbf7d0\nnode \".NET API\\nServer\" as SRV2 #bbf7d0\n\nC --> ALB : \"TCP з'єднання\"\nALB --> SRV1 : \"Нове TCP з'єднання\\nвід IP ALB\"\n\nC --> NLB : \"TCP з'єднання\"\nNLB --> SRV2 : \"Те саме TCP з'єднання\\n від IP клієнта\"\n\n@enduml\n",[3397,4217,4218,4222,4226,4230,4234,4239,4243,4248,4252,4257,4262,4267,4272,4276,4280,4284,4289,4293,4298,4303,4308,4312,4316,4320,4325,4330,4334,4339,4344,4348,4353,4358,4362],{"__ignoreMap":3395},[3400,4219,4220],{"class":3402,"line":3403},[3400,4221,3406],{},[3400,4223,4224],{"class":3402,"line":3409},[3400,4225,3412],{},[3400,4227,4228],{"class":3402,"line":3415},[3400,4229,3418],{},[3400,4231,4232],{"class":3402,"line":3421},[3400,4233,3425],{"emptyLinePlaceholder":3424},[3400,4235,4236],{"class":3402,"line":3428},[3400,4237,4238],{},"actor \"Client\\n(IP: 203.0.113.5)\" as C\n",[3400,4240,4241],{"class":3402,"line":3434},[3400,4242,3425],{"emptyLinePlaceholder":3424},[3400,4244,4245],{"class":3402,"line":3440},[3400,4246,4247],{},"rectangle \"ALB\\n(Layer 7 Proxy)\" as ALB #fef3c7 {\n",[3400,4249,4250],{"class":3402,"line":3446},[3400,4251,3460],{},[3400,4253,4254],{"class":3402,"line":3451},[3400,4255,4256],{},"      Перериває TCP-з'єднання.\n",[3400,4258,4259],{"class":3402,"line":3457},[3400,4260,4261],{},"      Встановлює НОВЕ з'єднання\n",[3400,4263,4264],{"class":3402,"line":3463},[3400,4265,4266],{},"      до сервера від свого IP.\n",[3400,4268,4269],{"class":3402,"line":3469},[3400,4270,4271],{},"      Додає X-Forwarded-For: 203.0.113.5\n",[3400,4273,4274],{"class":3402,"line":3475},[3400,4275,3490],{},[3400,4277,4278],{"class":3402,"line":3481},[3400,4279,3496],{},[3400,4281,4282],{"class":3402,"line":3487},[3400,4283,3425],{"emptyLinePlaceholder":3424},[3400,4285,4286],{"class":3402,"line":3493},[3400,4287,4288],{},"rectangle \"NLB\\n(Layer 4 Pass-through)\" as NLB #dbeafe {\n",[3400,4290,4291],{"class":3402,"line":3499},[3400,4292,3460],{},[3400,4294,4295],{"class":3402,"line":3504},[3400,4296,4297],{},"      Прозоро передає TCP-з'єднання.\n",[3400,4299,4300],{"class":3402,"line":3510},[3400,4301,4302],{},"      Сервер бачить справжній\n",[3400,4304,4305],{"class":3402,"line":3516},[3400,4306,4307],{},"      IP клієнта: 203.0.113.5\n",[3400,4309,4310],{"class":3402,"line":3522},[3400,4311,3490],{},[3400,4313,4314],{"class":3402,"line":3528},[3400,4315,3496],{},[3400,4317,4318],{"class":3402,"line":3534},[3400,4319,3425],{"emptyLinePlaceholder":3424},[3400,4321,4322],{"class":3402,"line":3540},[3400,4323,4324],{},"node \".NET API\\nServer\" as SRV1 #bbf7d0\n",[3400,4326,4327],{"class":3402,"line":3545},[3400,4328,4329],{},"node \".NET API\\nServer\" as SRV2 #bbf7d0\n",[3400,4331,4332],{"class":3402,"line":3550},[3400,4333,3425],{"emptyLinePlaceholder":3424},[3400,4335,4336],{"class":3402,"line":3556},[3400,4337,4338],{},"C --> ALB : \"TCP з'єднання\"\n",[3400,4340,4341],{"class":3402,"line":3562},[3400,4342,4343],{},"ALB --> SRV1 : \"Нове TCP з'єднання\\nвід IP ALB\"\n",[3400,4345,4346],{"class":3402,"line":3568},[3400,4347,3425],{"emptyLinePlaceholder":3424},[3400,4349,4350],{"class":3402,"line":3574},[3400,4351,4352],{},"C --> NLB : \"TCP з'єднання\"\n",[3400,4354,4355],{"class":3402,"line":3580},[3400,4356,4357],{},"NLB --> SRV2 : \"Те саме TCP з'єднання\\n від IP клієнта\"\n",[3400,4359,4360],{"class":3402,"line":3586},[3400,4361,3425],{"emptyLinePlaceholder":3424},[3400,4363,4364],{"class":3402,"line":3591},[3400,4365,3624],{},[3353,4367,4368,4371],{},[3359,4369,4370],{},"Для більшості .NET Web API — ALB є правильним вибором."," NLB варто розглядати лише при специфічних вимогах до продуктивності або протоколів.",[3626,4373],{},[3348,4375,4377],{"id":4376},"target-groups-та-health-checks","Target Groups та Health Checks",[3824,4379,4381],{"id":4380},"алгоритми-балансування-навантаження","Алгоритми балансування навантаження",[3353,4383,4384],{},"Target Group підтримує три алгоритми розподілу запитів між targets. Вибір алгоритму суттєво впливає на рівномірність навантаження залежно від характеристик вхідного трафіку: однорідності запитів, часу їх обробки та відносної потужності серверів.",[4386,4387,4389],"h4",{"id":4388},"weighted-round-robin-зважений-циклічний-розподіл","Weighted Round Robin — зважений циклічний розподіл",[3353,4391,4392,4395],{},[3359,4393,4394],{},"Weighted Round Robin (WRR)"," є алгоритмом за замовчуванням для ALB Target Groups. Класичний Round Robin розподіляє запити по черзі між targets у фіксованому циклі, не беручи до уваги жодних метрик поточного стану. Зважений варіант (Weighted) розширює базовий алгоритм: кожному target може бути призначена числова вага, і target з більшою вагою отримує пропорційно більше запитів у межах одного циклу.",[3353,4397,4398,4401],{},[3359,4399,4400],{},"Принцип роботи."," ALB підтримує внутрішній покажчик (pointer), що вказує на наступний target у послідовності. При надходженні кожного нового з'єднання ALB спрямовує його на поточний target і переміщує покажчик на наступний. По досягненні кінця списку покажчик повертається на початок. Цей підхід є детермінованим: за умови стабільного переліку targets порядок розподілу завжди передбачуваний.",[3353,4403,4404],{},[3359,4405,4406],{},"Коли WRR є оптимальним вибором:",[3653,4408,4409,4412,4418],{},[3656,4410,4411],{},"Всі запити до вашого API приблизно однакові за часом обробки та споживанням ресурсів (наприклад, простий CRUD API)",[3656,4413,4414,4415,4091],{},"Всі targets мають однаковий тип EC2 instance (наприклад, всі ",[3397,4416,4417],{},"t3.medium",[3656,4419,4420],{},"Немає довготривалих з'єднань або стрімінгу",[3387,4422,4423],{},[3390,4424,4426],{"className":3392,"code":4425,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"Weighted Round Robin: розподіл 9 послідовних запитів\"\n\nparticipant \"ALB\\n(pointer → Target 1)\" as ALB #fef3c7\nparticipant \"Target 1\\n(.NET API)\" as T1 #bbf7d0\nparticipant \"Target 2\\n(.NET API)\" as T2 #bbf7d0\nparticipant \"Target 3\\n(.NET API)\" as T3 #bbf7d0\n\nnote over ALB : Вага всіх targets = 1\\n(рівний розподіл)\n\nALB -> T1 : Запит #1\\n(pointer -> T2)\nALB -> T2 : Запит #2\\n(pointer -> T3)\nALB -> T3 : Запит #3\\n(pointer -> T1)\nALB -> T1 : Запит #4\\n(pointer -> T2)\nALB -> T2 : Запит #5\\n(pointer -> T3)\nALB -> T3 : Запит #6\\n(pointer -> T1)\nALB -> T1 : Запит #7\nALB -> T2 : Запит #8\nALB -> T3 : Запит #9\n\nnote over T1 : Отримав: #1, #4, #7\\n(3 запити \u002F 33%)\nnote over T2 : Отримав: #2, #5, #8\\n(3 запити \u002F 33%)\nnote over T3 : Отримав: #3, #6, #9\\n(3 запити \u002F 33%)\n\n@enduml\n",[3397,4427,4428,4432,4436,4440,4444,4449,4453,4458,4463,4468,4473,4477,4482,4486,4491,4496,4501,4506,4511,4516,4521,4526,4531,4535,4540,4545,4550,4554],{"__ignoreMap":3395},[3400,4429,4430],{"class":3402,"line":3403},[3400,4431,3406],{},[3400,4433,4434],{"class":3402,"line":3409},[3400,4435,3412],{},[3400,4437,4438],{"class":3402,"line":3415},[3400,4439,3418],{},[3400,4441,4442],{"class":3402,"line":3421},[3400,4443,3425],{"emptyLinePlaceholder":3424},[3400,4445,4446],{"class":3402,"line":3428},[3400,4447,4448],{},"title \"Weighted Round Robin: розподіл 9 послідовних запитів\"\n",[3400,4450,4451],{"class":3402,"line":3434},[3400,4452,3425],{"emptyLinePlaceholder":3424},[3400,4454,4455],{"class":3402,"line":3440},[3400,4456,4457],{},"participant \"ALB\\n(pointer → Target 1)\" as ALB #fef3c7\n",[3400,4459,4460],{"class":3402,"line":3446},[3400,4461,4462],{},"participant \"Target 1\\n(.NET API)\" as T1 #bbf7d0\n",[3400,4464,4465],{"class":3402,"line":3451},[3400,4466,4467],{},"participant \"Target 2\\n(.NET API)\" as T2 #bbf7d0\n",[3400,4469,4470],{"class":3402,"line":3457},[3400,4471,4472],{},"participant \"Target 3\\n(.NET API)\" as T3 #bbf7d0\n",[3400,4474,4475],{"class":3402,"line":3463},[3400,4476,3425],{"emptyLinePlaceholder":3424},[3400,4478,4479],{"class":3402,"line":3469},[3400,4480,4481],{},"note over ALB : Вага всіх targets = 1\\n(рівний розподіл)\n",[3400,4483,4484],{"class":3402,"line":3475},[3400,4485,3425],{"emptyLinePlaceholder":3424},[3400,4487,4488],{"class":3402,"line":3481},[3400,4489,4490],{},"ALB -> T1 : Запит #1\\n(pointer -> T2)\n",[3400,4492,4493],{"class":3402,"line":3487},[3400,4494,4495],{},"ALB -> T2 : Запит #2\\n(pointer -> T3)\n",[3400,4497,4498],{"class":3402,"line":3493},[3400,4499,4500],{},"ALB -> T3 : Запит #3\\n(pointer -> T1)\n",[3400,4502,4503],{"class":3402,"line":3499},[3400,4504,4505],{},"ALB -> T1 : Запит #4\\n(pointer -> T2)\n",[3400,4507,4508],{"class":3402,"line":3504},[3400,4509,4510],{},"ALB -> T2 : Запит #5\\n(pointer -> T3)\n",[3400,4512,4513],{"class":3402,"line":3510},[3400,4514,4515],{},"ALB -> T3 : Запит #6\\n(pointer -> T1)\n",[3400,4517,4518],{"class":3402,"line":3516},[3400,4519,4520],{},"ALB -> T1 : Запит #7\n",[3400,4522,4523],{"class":3402,"line":3522},[3400,4524,4525],{},"ALB -> T2 : Запит #8\n",[3400,4527,4528],{"class":3402,"line":3528},[3400,4529,4530],{},"ALB -> T3 : Запит #9\n",[3400,4532,4533],{"class":3402,"line":3534},[3400,4534,3425],{"emptyLinePlaceholder":3424},[3400,4536,4537],{"class":3402,"line":3540},[3400,4538,4539],{},"note over T1 : Отримав: #1, #4, #7\\n(3 запити \u002F 33%)\n",[3400,4541,4542],{"class":3402,"line":3545},[3400,4543,4544],{},"note over T2 : Отримав: #2, #5, #8\\n(3 запити \u002F 33%)\n",[3400,4546,4547],{"class":3402,"line":3550},[3400,4548,4549],{},"note over T3 : Отримав: #3, #6, #9\\n(3 запити \u002F 33%)\n",[3400,4551,4552],{"class":3402,"line":3556},[3400,4553,3425],{"emptyLinePlaceholder":3424},[3400,4555,4556],{"class":3402,"line":3562},[3400,4557,3624],{},[3353,4559,4560,4563,4564,4567,4568,4571],{},[3359,4561,4562],{},"Проблема WRR при неоднорідних запитах."," Уявіть API, де ",[3397,4565,4566],{},"GET \u002Fusers"," обробляється за 5ms, а ",[3397,4569,4570],{},"POST \u002Freports\u002Fgenerate"," — за 30 секунд. Round Robin розподіляє запити по черзі без урахування їхньої складності:",[3387,4573,4574],{},[3390,4575,4577],{"className":3392,"code":4576,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"Проблема WRR при неоднорідних запитах\"\n\nparticipant \"ALB\" as ALB #fef3c7\nparticipant \"Target 1\" as T1 #fca5a5\nparticipant \"Target 2\" as T2 #bbf7d0\nparticipant \"Target 3\" as T3 #bbf7d0\n\nALB -> T1 : POST \u002Freports\u002Fgenerate\\n(30 секунд!)\nALB -> T2 : GET \u002Fusers\\n(5ms)\nALB -> T3 : GET \u002Fusers\\n(5ms)\nALB -> T1 : GET \u002Fproducts \u003C- WRR каже: черга T1!\\n(Target 1 досi зайнятий)\nT2 --> ALB : done (5ms)\nT3 --> ALB : done (5ms)\n\nnote over T1 #fee2e2\n  Target 1 оброблює важкий\n  запит 30 секунд. Усi наступнi\n  запити на нього вишиковуються\n  в чергу. CPU: 100%!\nend note\n\nnote over T2, T3 #f0fdf4\n  Target 2 та 3 вiльнi:\n  вони вже завершили своi\n  запити, але WRR продовжує\n  направляти на T1.\nend note\n\n@enduml\n",[3397,4578,4579,4583,4587,4591,4595,4600,4604,4609,4614,4619,4624,4628,4633,4638,4643,4648,4653,4658,4662,4667,4672,4677,4682,4687,4691,4695,4700,4705,4710,4715,4720,4724,4728],{"__ignoreMap":3395},[3400,4580,4581],{"class":3402,"line":3403},[3400,4582,3406],{},[3400,4584,4585],{"class":3402,"line":3409},[3400,4586,3412],{},[3400,4588,4589],{"class":3402,"line":3415},[3400,4590,3418],{},[3400,4592,4593],{"class":3402,"line":3421},[3400,4594,3425],{"emptyLinePlaceholder":3424},[3400,4596,4597],{"class":3402,"line":3428},[3400,4598,4599],{},"title \"Проблема WRR при неоднорідних запитах\"\n",[3400,4601,4602],{"class":3402,"line":3434},[3400,4603,3425],{"emptyLinePlaceholder":3424},[3400,4605,4606],{"class":3402,"line":3440},[3400,4607,4608],{},"participant \"ALB\" as ALB #fef3c7\n",[3400,4610,4611],{"class":3402,"line":3446},[3400,4612,4613],{},"participant \"Target 1\" as T1 #fca5a5\n",[3400,4615,4616],{"class":3402,"line":3451},[3400,4617,4618],{},"participant \"Target 2\" as T2 #bbf7d0\n",[3400,4620,4621],{"class":3402,"line":3457},[3400,4622,4623],{},"participant \"Target 3\" as T3 #bbf7d0\n",[3400,4625,4626],{"class":3402,"line":3463},[3400,4627,3425],{"emptyLinePlaceholder":3424},[3400,4629,4630],{"class":3402,"line":3469},[3400,4631,4632],{},"ALB -> T1 : POST \u002Freports\u002Fgenerate\\n(30 секунд!)\n",[3400,4634,4635],{"class":3402,"line":3475},[3400,4636,4637],{},"ALB -> T2 : GET \u002Fusers\\n(5ms)\n",[3400,4639,4640],{"class":3402,"line":3481},[3400,4641,4642],{},"ALB -> T3 : GET \u002Fusers\\n(5ms)\n",[3400,4644,4645],{"class":3402,"line":3487},[3400,4646,4647],{},"ALB -> T1 : GET \u002Fproducts \u003C- WRR каже: черга T1!\\n(Target 1 досi зайнятий)\n",[3400,4649,4650],{"class":3402,"line":3493},[3400,4651,4652],{},"T2 --> ALB : done (5ms)\n",[3400,4654,4655],{"class":3402,"line":3499},[3400,4656,4657],{},"T3 --> ALB : done (5ms)\n",[3400,4659,4660],{"class":3402,"line":3504},[3400,4661,3425],{"emptyLinePlaceholder":3424},[3400,4663,4664],{"class":3402,"line":3510},[3400,4665,4666],{},"note over T1 #fee2e2\n",[3400,4668,4669],{"class":3402,"line":3516},[3400,4670,4671],{},"  Target 1 оброблює важкий\n",[3400,4673,4674],{"class":3402,"line":3522},[3400,4675,4676],{},"  запит 30 секунд. Усi наступнi\n",[3400,4678,4679],{"class":3402,"line":3528},[3400,4680,4681],{},"  запити на нього вишиковуються\n",[3400,4683,4684],{"class":3402,"line":3534},[3400,4685,4686],{},"  в чергу. CPU: 100%!\n",[3400,4688,4689],{"class":3402,"line":3540},[3400,4690,3618],{},[3400,4692,4693],{"class":3402,"line":3545},[3400,4694,3425],{"emptyLinePlaceholder":3424},[3400,4696,4697],{"class":3402,"line":3550},[3400,4698,4699],{},"note over T2, T3 #f0fdf4\n",[3400,4701,4702],{"class":3402,"line":3556},[3400,4703,4704],{},"  Target 2 та 3 вiльнi:\n",[3400,4706,4707],{"class":3402,"line":3562},[3400,4708,4709],{},"  вони вже завершили своi\n",[3400,4711,4712],{"class":3402,"line":3568},[3400,4713,4714],{},"  запити, але WRR продовжує\n",[3400,4716,4717],{"class":3402,"line":3574},[3400,4718,4719],{},"  направляти на T1.\n",[3400,4721,4722],{"class":3402,"line":3580},[3400,4723,3618],{},[3400,4725,4726],{"class":3402,"line":3586},[3400,4727,3425],{"emptyLinePlaceholder":3424},[3400,4729,4730],{"class":3402,"line":3591},[3400,4731,3624],{},[3353,4733,4734,4737],{},[3359,4735,4736],{},"Налаштування ваги у Weighted Round Robin."," За замовчуванням всі targets мають однакову вагу. Вагу можна змінити для нерівномірного розподілу:",[3390,4739,4743],{"className":4740,"code":4741,"language":4742,"meta":3395,"style":3395},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# t3.large має вдвічі більше CPU — встановити вагу 2:1\naws elbv2 register-targets \\\n    --target-group-arn \"$TG_ARN\" \\\n    --targets \\\n        Id=i-0small123,Weight=1 \\\n        Id=i-0large456,Weight=2 \\\n    --region eu-central-1\n","bash",[3397,4744,4745,4751,4768,4786,4793,4804,4814],{"__ignoreMap":3395},[3400,4746,4747],{"class":3402,"line":3403},[3400,4748,4750],{"class":4749},"spJ8K","# t3.large має вдвічі більше CPU — встановити вагу 2:1\n",[3400,4752,4753,4757,4761,4764],{"class":3402,"line":3409},[3400,4754,4756],{"class":4755},"s8Opu","aws",[3400,4758,4760],{"class":4759},"sbdoH"," elbv2",[3400,4762,4763],{"class":4759}," register-targets",[3400,4765,4767],{"class":4766},"sjcCO"," \\\n",[3400,4769,4770,4774,4777,4781,4784],{"class":3402,"line":3415},[3400,4771,4773],{"class":4772},"su1O8","    --target-group-arn",[3400,4775,4776],{"class":4759}," \"",[3400,4778,4780],{"class":4779},"siwwj","$TG_ARN",[3400,4782,4783],{"class":4759},"\"",[3400,4785,4767],{"class":4766},[3400,4787,4788,4791],{"class":3402,"line":3421},[3400,4789,4790],{"class":4772},"    --targets",[3400,4792,4767],{"class":4766},[3400,4794,4795,4798,4802],{"class":3402,"line":3428},[3400,4796,4797],{"class":4759},"        Id=i-0small123,Weight=",[3400,4799,4801],{"class":4800},"sJj4R","1",[3400,4803,4767],{"class":4766},[3400,4805,4806,4809,4812],{"class":3402,"line":3434},[3400,4807,4808],{"class":4759},"        Id=i-0large456,Weight=",[3400,4810,4811],{"class":4800},"2",[3400,4813,4767],{"class":4766},[3400,4815,4816,4819],{"class":3402,"line":3440},[3400,4817,4818],{"class":4772},"    --region",[3400,4820,4821],{"class":4759}," eu-central-1\n",[3626,4823],{},[4386,4825,4827],{"id":4826},"least-outstanding-requests-мінімум-незавершених-зєднань","Least Outstanding Requests — мінімум незавершених з'єднань",[3353,4829,4830,4833,4834,4837],{},[3359,4831,4832],{},"Least Outstanding Requests (LOR)"," — алгоритм, що вирішує фундаментальний недолік Round Robin при неоднорідному навантаженні. Замість механічної черги ALB аналізує ",[3359,4835,4836],{},"поточний стан"," кожного target і направляє новий запит туди, де кількість активних (незавершених) з'єднань є найменшою у даний момент.",[3353,4839,4840,4842],{},[3359,4841,4400],{}," ALB підтримує лічильник активних запитів для кожного target. Коли надходить новий запит, ALB перебирає всі healthy targets, знаходить той з мінімальним лічильником і направляє запит туди, одночасно збільшуючи лічильник на 1. Коли target завершує обробку та повертає відповідь — лічильник зменшується на 1.",[3353,4844,4845,4848],{},[3359,4846,4847],{},"Детальний приклад."," Система з трьома targets і змішаним трафіком (швидкі та повільні запити):",[3387,4850,4851],{},[3390,4852,4854],{"className":3392,"code":4853,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"Least Outstanding Requests: адаптивний розподiл\"\n\nparticipant \"ALB\\n(лiчильники: T1=0 T2=0 T3=0)\" as ALB #fef3c7\nparticipant \"Target 1\" as T1 #bbf7d0\nparticipant \"Target 2\" as T2 #bbf7d0\nparticipant \"Target 3\" as T3 #bbf7d0\n\nALB -> T1 : [1] POST \u002Freports (30s)\\nT1=1, T2=0, T3=0\nALB -> T2 : [2] GET \u002Fusers (5ms)\\nMIN=0 -> T2\\nT1=1, T2=1, T3=0\nALB -> T3 : [3] GET \u002Forders (5ms)\\nMIN=0 -> T3\\nT1=1, T2=1, T3=1\nT2 --> ALB : done -> T2=0\nT3 --> ALB : done -> T3=0\n\nnote over ALB : Стан: T1=1, T2=0, T3=0\n\nALB -> T2 : [4] GET \u002Fproducts\\nMIN=0 -> T2 (уникаємо T1!)\nALB -> T3 : [5] GET \u002Fcatalog\\nMIN=0 -> T3 (уникаємо T1!)\nALB -> T2 : [6] GET \u002Fsearch\nALB -> T3 : [7] GET \u002Fusers\n\nnote over T1 #fee2e2\n  Target 1 зайнятий 30s.\n  LOR автоматично\n  обходить його!\nend note\n\nnote over T2, T3 #f0fdf4\n  T2 та T3 рiвномiрно\n  отримують легкi запити.\n  Система збалансована!\nend note\n\n@enduml\n",[3397,4855,4856,4860,4864,4868,4872,4877,4881,4886,4891,4895,4899,4903,4908,4913,4918,4923,4928,4932,4937,4941,4946,4951,4956,4961,4965,4969,4974,4979,4984,4988,4992,4996,5001,5006,5011,5015,5019],{"__ignoreMap":3395},[3400,4857,4858],{"class":3402,"line":3403},[3400,4859,3406],{},[3400,4861,4862],{"class":3402,"line":3409},[3400,4863,3412],{},[3400,4865,4866],{"class":3402,"line":3415},[3400,4867,3418],{},[3400,4869,4870],{"class":3402,"line":3421},[3400,4871,3425],{"emptyLinePlaceholder":3424},[3400,4873,4874],{"class":3402,"line":3428},[3400,4875,4876],{},"title \"Least Outstanding Requests: адаптивний розподiл\"\n",[3400,4878,4879],{"class":3402,"line":3434},[3400,4880,3425],{"emptyLinePlaceholder":3424},[3400,4882,4883],{"class":3402,"line":3440},[3400,4884,4885],{},"participant \"ALB\\n(лiчильники: T1=0 T2=0 T3=0)\" as ALB #fef3c7\n",[3400,4887,4888],{"class":3402,"line":3446},[3400,4889,4890],{},"participant \"Target 1\" as T1 #bbf7d0\n",[3400,4892,4893],{"class":3402,"line":3451},[3400,4894,4618],{},[3400,4896,4897],{"class":3402,"line":3457},[3400,4898,4623],{},[3400,4900,4901],{"class":3402,"line":3463},[3400,4902,3425],{"emptyLinePlaceholder":3424},[3400,4904,4905],{"class":3402,"line":3469},[3400,4906,4907],{},"ALB -> T1 : [1] POST \u002Freports (30s)\\nT1=1, T2=0, T3=0\n",[3400,4909,4910],{"class":3402,"line":3475},[3400,4911,4912],{},"ALB -> T2 : [2] GET \u002Fusers (5ms)\\nMIN=0 -> T2\\nT1=1, T2=1, T3=0\n",[3400,4914,4915],{"class":3402,"line":3481},[3400,4916,4917],{},"ALB -> T3 : [3] GET \u002Forders (5ms)\\nMIN=0 -> T3\\nT1=1, T2=1, T3=1\n",[3400,4919,4920],{"class":3402,"line":3487},[3400,4921,4922],{},"T2 --> ALB : done -> T2=0\n",[3400,4924,4925],{"class":3402,"line":3493},[3400,4926,4927],{},"T3 --> ALB : done -> T3=0\n",[3400,4929,4930],{"class":3402,"line":3499},[3400,4931,3425],{"emptyLinePlaceholder":3424},[3400,4933,4934],{"class":3402,"line":3504},[3400,4935,4936],{},"note over ALB : Стан: T1=1, T2=0, T3=0\n",[3400,4938,4939],{"class":3402,"line":3510},[3400,4940,3425],{"emptyLinePlaceholder":3424},[3400,4942,4943],{"class":3402,"line":3516},[3400,4944,4945],{},"ALB -> T2 : [4] GET \u002Fproducts\\nMIN=0 -> T2 (уникаємо T1!)\n",[3400,4947,4948],{"class":3402,"line":3522},[3400,4949,4950],{},"ALB -> T3 : [5] GET \u002Fcatalog\\nMIN=0 -> T3 (уникаємо T1!)\n",[3400,4952,4953],{"class":3402,"line":3528},[3400,4954,4955],{},"ALB -> T2 : [6] GET \u002Fsearch\n",[3400,4957,4958],{"class":3402,"line":3534},[3400,4959,4960],{},"ALB -> T3 : [7] GET \u002Fusers\n",[3400,4962,4963],{"class":3402,"line":3540},[3400,4964,3425],{"emptyLinePlaceholder":3424},[3400,4966,4967],{"class":3402,"line":3545},[3400,4968,4666],{},[3400,4970,4971],{"class":3402,"line":3550},[3400,4972,4973],{},"  Target 1 зайнятий 30s.\n",[3400,4975,4976],{"class":3402,"line":3556},[3400,4977,4978],{},"  LOR автоматично\n",[3400,4980,4981],{"class":3402,"line":3562},[3400,4982,4983],{},"  обходить його!\n",[3400,4985,4986],{"class":3402,"line":3568},[3400,4987,3618],{},[3400,4989,4990],{"class":3402,"line":3574},[3400,4991,3425],{"emptyLinePlaceholder":3424},[3400,4993,4994],{"class":3402,"line":3580},[3400,4995,4699],{},[3400,4997,4998],{"class":3402,"line":3586},[3400,4999,5000],{},"  T2 та T3 рiвномiрно\n",[3400,5002,5003],{"class":3402,"line":3591},[3400,5004,5005],{},"  отримують легкi запити.\n",[3400,5007,5008],{"class":3402,"line":3597},[3400,5009,5010],{},"  Система збалансована!\n",[3400,5012,5013],{"class":3402,"line":3603},[3400,5014,3618],{},[3400,5016,5017],{"class":3402,"line":3609},[3400,5018,3425],{"emptyLinePlaceholder":3424},[3400,5020,5021],{"class":3402,"line":3615},[3400,5022,3624],{},[3353,5024,5025,5028],{},[3359,5026,5027],{},"Кількісне порівняння WRR vs LOR"," при 100 запитах: 1 важкий (30s) + 99 легких (5ms):",[3387,5030,5031],{},[3390,5032,5034],{"className":3392,"code":5033,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"WRR vs LOR: порiвняння розподiлу при неоднорiдному трафiку (100 запитiв)\"\n\nrectangle \"Weighted Round Robin\" as WRR {\n    rectangle \"Target 1\\n34 запити\\n(1 важкий x 30s + 33 легких)\\nCPU: ПЕРЕВАНТАЖЕНИЙ\" as W1 #fca5a5\n    rectangle \"Target 2\\n33 запити\\n(33 легких x 5ms)\\nCPU: ~30%\" as W2 #bbf7d0\n    rectangle \"Target 3\\n33 запити\\n(33 легких x 5ms)\\nCPU: ~30%\" as W3 #bbf7d0\n}\n\nrectangle \"Least Outstanding Requests\" as LOR {\n    rectangle \"Target 1\\n1 запит\\n(важкий x 30s)\\nCPU: 100% протягом 30s\" as L1 #fde68a\n    rectangle \"Target 2\\n~50 запитiв\\n(легкi x 5ms)\\nCPU: рiвномiрний\" as L2 #bbf7d0\n    rectangle \"Target 3\\n~49 запитiв\\n(легкi x 5ms)\\nCPU: рiвномiрний\" as L3 #bbf7d0\n}\n\n@enduml\n",[3397,5035,5036,5040,5044,5048,5052,5057,5061,5066,5071,5076,5081,5085,5089,5094,5099,5104,5109,5113,5117],{"__ignoreMap":3395},[3400,5037,5038],{"class":3402,"line":3403},[3400,5039,3406],{},[3400,5041,5042],{"class":3402,"line":3409},[3400,5043,3412],{},[3400,5045,5046],{"class":3402,"line":3415},[3400,5047,3418],{},[3400,5049,5050],{"class":3402,"line":3421},[3400,5051,3425],{"emptyLinePlaceholder":3424},[3400,5053,5054],{"class":3402,"line":3428},[3400,5055,5056],{},"title \"WRR vs LOR: порiвняння розподiлу при неоднорiдному трафiку (100 запитiв)\"\n",[3400,5058,5059],{"class":3402,"line":3434},[3400,5060,3425],{"emptyLinePlaceholder":3424},[3400,5062,5063],{"class":3402,"line":3440},[3400,5064,5065],{},"rectangle \"Weighted Round Robin\" as WRR {\n",[3400,5067,5068],{"class":3402,"line":3446},[3400,5069,5070],{},"    rectangle \"Target 1\\n34 запити\\n(1 важкий x 30s + 33 легких)\\nCPU: ПЕРЕВАНТАЖЕНИЙ\" as W1 #fca5a5\n",[3400,5072,5073],{"class":3402,"line":3451},[3400,5074,5075],{},"    rectangle \"Target 2\\n33 запити\\n(33 легких x 5ms)\\nCPU: ~30%\" as W2 #bbf7d0\n",[3400,5077,5078],{"class":3402,"line":3457},[3400,5079,5080],{},"    rectangle \"Target 3\\n33 запити\\n(33 легких x 5ms)\\nCPU: ~30%\" as W3 #bbf7d0\n",[3400,5082,5083],{"class":3402,"line":3463},[3400,5084,3496],{},[3400,5086,5087],{"class":3402,"line":3469},[3400,5088,3425],{"emptyLinePlaceholder":3424},[3400,5090,5091],{"class":3402,"line":3475},[3400,5092,5093],{},"rectangle \"Least Outstanding Requests\" as LOR {\n",[3400,5095,5096],{"class":3402,"line":3481},[3400,5097,5098],{},"    rectangle \"Target 1\\n1 запит\\n(важкий x 30s)\\nCPU: 100% протягом 30s\" as L1 #fde68a\n",[3400,5100,5101],{"class":3402,"line":3487},[3400,5102,5103],{},"    rectangle \"Target 2\\n~50 запитiв\\n(легкi x 5ms)\\nCPU: рiвномiрний\" as L2 #bbf7d0\n",[3400,5105,5106],{"class":3402,"line":3493},[3400,5107,5108],{},"    rectangle \"Target 3\\n~49 запитiв\\n(легкi x 5ms)\\nCPU: рiвномiрний\" as L3 #bbf7d0\n",[3400,5110,5111],{"class":3402,"line":3499},[3400,5112,3496],{},[3400,5114,5115],{"class":3402,"line":3504},[3400,5116,3425],{"emptyLinePlaceholder":3424},[3400,5118,5119],{"class":3402,"line":3510},[3400,5120,3624],{},[3353,5122,5123],{},[3359,5124,5125],{},"Коли LOR є оптимальним вибором:",[3653,5127,5128,5134,5137,5140],{},[3656,5129,5130,5131,5133],{},"API з різнорідними ендпоінтами: прості ",[3397,5132,4115],{}," запити поруч із важкими операціями генерації звітів, обробки файлів або ML-inference",[3656,5135,5136],{},"Наявність стрімінгових з'єднань або WebSocket (одне з'єднання тривалістю хвилини)",[3656,5138,5139],{},"Targets мають різну потужність (різні типи EC2 instances)",[3656,5141,5142],{},"Будь-який сценарій, де час обробки запитів суттєво варіюється",[3353,5144,5145],{},[3359,5146,5147],{},"Налаштування LOR через AWS CLI:",[3390,5149,5151],{"className":4740,"code":5150,"language":4742,"meta":3395,"style":3395},"# Змінити алгоритм Target Group на Least Outstanding Requests\naws elbv2 modify-target-group-attributes \\\n    --target-group-arn \"$TG_ARN\" \\\n    --attributes \\\n        Key=load_balancing.algorithm.type,Value=least_outstanding_requests \\\n    --region eu-central-1\n",[3397,5152,5153,5158,5169,5181,5188,5195],{"__ignoreMap":3395},[3400,5154,5155],{"class":3402,"line":3403},[3400,5156,5157],{"class":4749},"# Змінити алгоритм Target Group на Least Outstanding Requests\n",[3400,5159,5160,5162,5164,5167],{"class":3402,"line":3409},[3400,5161,4756],{"class":4755},[3400,5163,4760],{"class":4759},[3400,5165,5166],{"class":4759}," modify-target-group-attributes",[3400,5168,4767],{"class":4766},[3400,5170,5171,5173,5175,5177,5179],{"class":3402,"line":3415},[3400,5172,4773],{"class":4772},[3400,5174,4776],{"class":4759},[3400,5176,4780],{"class":4779},[3400,5178,4783],{"class":4759},[3400,5180,4767],{"class":4766},[3400,5182,5183,5186],{"class":3402,"line":3421},[3400,5184,5185],{"class":4772},"    --attributes",[3400,5187,4767],{"class":4766},[3400,5189,5190,5193],{"class":3402,"line":3428},[3400,5191,5192],{"class":4759},"        Key=load_balancing.algorithm.type,Value=least_outstanding_requests",[3400,5194,4767],{"class":4766},[3400,5196,5197,5199],{"class":3402,"line":3434},[3400,5198,4818],{"class":4772},[3400,5200,4821],{"class":4759},[5202,5203,5205,5218,5222,5226,5229,5233,5243,5247,5262,5273,5277,5281],"terminal-preview",{"title":5204},"Зміна алгоритму на LOR",[5206,5207,5209,5214,5215],"div",{"className":5208},[3402],[3400,5210,5213],{"className":5211},[5212],"opacity-40","$"," ",[3359,5216,5217],{},"aws elbv2 modify-target-group-attributes \\",[5206,5219,5221],{"className":5220},[3402],"    --target-group-arn \"$TG_ARN\" \\",[5206,5223,5225],{"className":5224},[3402],"    --attributes Key=load_balancing.algorithm.type,Value=least_outstanding_requests",[5206,5227],{"className":5228},[3402],[5206,5230,5232],{"className":5231},[3402],"{",[5206,5234,5236,5237,5242],{"className":5235},[3402],"    ",[3400,5238,5241],{"className":5239},[5240],"text-blue-400","\"Attributes\"",": [",[5206,5244,5246],{"className":5245},[3402],"        {",[5206,5248,5250,5251,5255,5256,5261],{"className":5249},[3402],"            ",[3400,5252,5254],{"className":5253},[5240],"\"Key\"",": ",[3400,5257,5260],{"className":5258},[5259],"text-green-400","\"load_balancing.algorithm.type\"",",",[5206,5263,5250,5265,5255,5269],{"className":5264},[3402],[3400,5266,5268],{"className":5267},[5240],"\"Value\"",[3400,5270,5272],{"className":5271},[5259],"\"least_outstanding_requests\"",[5206,5274,5276],{"className":5275},[3402],"        }",[5206,5278,5280],{"className":5279},[3402],"    ]",[5206,5282,5284],{"className":5283},[3402],"}",[3353,5286,5287],{},[3359,5288,5289],{},"Перевірка поточного алгоритму Target Group:",[3390,5291,5293],{"className":4740,"code":5292,"language":4742,"meta":3395,"style":3395},"aws elbv2 describe-target-group-attributes \\\n    --target-group-arn \"$TG_ARN\" \\\n    --region eu-central-1 \\\n    --query \"Attributes[?Key=='load_balancing.algorithm.type']\"\n",[3397,5294,5295,5306,5318,5327],{"__ignoreMap":3395},[3400,5296,5297,5299,5301,5304],{"class":3402,"line":3403},[3400,5298,4756],{"class":4755},[3400,5300,4760],{"class":4759},[3400,5302,5303],{"class":4759}," describe-target-group-attributes",[3400,5305,4767],{"class":4766},[3400,5307,5308,5310,5312,5314,5316],{"class":3402,"line":3409},[3400,5309,4773],{"class":4772},[3400,5311,4776],{"class":4759},[3400,5313,4780],{"class":4779},[3400,5315,4783],{"class":4759},[3400,5317,4767],{"class":4766},[3400,5319,5320,5322,5325],{"class":3402,"line":3415},[3400,5321,4818],{"class":4772},[3400,5323,5324],{"class":4759}," eu-central-1",[3400,5326,4767],{"class":4766},[3400,5328,5329,5332],{"class":3402,"line":3421},[3400,5330,5331],{"class":4772},"    --query",[3400,5333,5334],{"class":4759}," \"Attributes[?Key=='load_balancing.algorithm.type']\"\n",[5202,5336,5338,5347,5351,5355,5365,5374,5378],{"title":5337},"Перевірка активного алгоритму",[5206,5339,5341,5214,5344],{"className":5340},[3402],[3400,5342,5213],{"className":5343},[5212],[3359,5345,5346],{},"aws elbv2 describe-target-group-attributes --target-group-arn \"$TG_ARN\" ...",[5206,5348,5350],{"className":5349},[3402],"[",[5206,5352,5354],{"className":5353},[3402],"    {",[5206,5356,5358,5359,5255,5362,5261],{"className":5357},[3402],"        ",[3400,5360,5254],{"className":5361},[5240],[3400,5363,5260],{"className":5364},[5259],[5206,5366,5358,5368,5255,5371],{"className":5367},[3402],[3400,5369,5268],{"className":5370},[5240],[3400,5372,5272],{"className":5373},[5259],[5206,5375,5377],{"className":5376},[3402],"    }",[5206,5379,5381],{"className":5380},[3402],"]",[3626,5383],{},[4386,5385,5387],{"id":5386},"weighted-random-зважений-випадковий-розподіл","Weighted Random — зважений випадковий розподіл",[3353,5389,5390,5393],{},[3359,5391,5392],{},"Weighted Random"," — алгоритм, що поєднує випадковість із можливістю задати пропорції розподілу трафіку між targets. На відміну від Round Robin, послідовність розподілу не є фіксованою: ALB обирає target псевдовипадково, але з урахуванням ваги — target з вищою вагою має пропорційно вищу ймовірність бути обраним.",[3353,5395,5396,5399],{},[3359,5397,5398],{},"Математичний принцип."," Якщо Target 1 має вагу 70, Target 2 — вагу 20, Target 3 — вагу 10, то ймовірність обрання: P(T1) = 70\u002F100 = 70%, P(T2) = 20\u002F100 = 20%, P(T3) = 10\u002F100 = 10%.",[3353,5401,5402,5405],{},[3359,5403,5404],{},"Ключова сфера застосування: Canary Deployment."," Weighted Random є стандартним інструментом для поступового розгортання нових версій застосунку. Замість одночасного переключення всього трафіку на нову версію, частка трафіку збільшується поступово, поки команда спостерігає за метриками помилок та латентністю:",[3387,5407,5408],{},[3390,5409,5411],{"className":3392,"code":5410,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"Canary Deployment через Weighted Random: три фази\"\n\nrectangle \"ALB Listener Rule\" as ALB #fef3c7\n\nrectangle \"Фаза 1: Canary 5%\" as P1 #fff3e0 {\n    rectangle \"api-v1-tg\\nВага: 95\\n(поточна версiя)\" as V1_1 #bbf7d0\n    rectangle \"api-v2-tg\\nВага: 5\\n(нова версiя)\" as V2_1 #fde68a\n}\n\nrectangle \"Фаза 2: 50\u002F50\" as P2 #fff3e0 {\n    rectangle \"api-v1-tg\\nВага: 50\" as V1_2 #fde68a\n    rectangle \"api-v2-tg\\nВага: 50\" as V2_2 #fde68a\n}\n\nrectangle \"Фаза 3: Повна мiграцiя\" as P3 #f0fdf4 {\n    rectangle \"api-v2-tg\\nВага: 100\\n(v1 виведено)\" as V2_3 #bbf7d0\n}\n\nALB --> P1\nP1 -[hidden]down-> P2\nP2 -[hidden]down-> P3\n\nnote right of P1\n  5% реальних користувачiв\n  тестують нову версiю.\n  Монiторимо: error rate,\n  latency, business metrics.\nend note\n\nnote right of P2\n  Метрики гарнi -> 50%.\n  Вiдкочування: вага v1=100,\n  вага v2=0 (миттєво!).\nend note\n\nnote right of P3\n  Нова версiя стабiльна.\n  Виводимо v1 з обiгу.\nend note\n\n@enduml\n",[3397,5412,5413,5417,5421,5425,5429,5434,5438,5443,5447,5452,5457,5462,5466,5470,5475,5480,5485,5489,5493,5498,5503,5507,5511,5516,5521,5526,5530,5535,5540,5545,5550,5555,5559,5563,5568,5573,5578,5583,5587,5592,5598,5604,5610,5615,5620],{"__ignoreMap":3395},[3400,5414,5415],{"class":3402,"line":3403},[3400,5416,3406],{},[3400,5418,5419],{"class":3402,"line":3409},[3400,5420,3412],{},[3400,5422,5423],{"class":3402,"line":3415},[3400,5424,3418],{},[3400,5426,5427],{"class":3402,"line":3421},[3400,5428,3425],{"emptyLinePlaceholder":3424},[3400,5430,5431],{"class":3402,"line":3428},[3400,5432,5433],{},"title \"Canary Deployment через Weighted Random: три фази\"\n",[3400,5435,5436],{"class":3402,"line":3434},[3400,5437,3425],{"emptyLinePlaceholder":3424},[3400,5439,5440],{"class":3402,"line":3440},[3400,5441,5442],{},"rectangle \"ALB Listener Rule\" as ALB #fef3c7\n",[3400,5444,5445],{"class":3402,"line":3446},[3400,5446,3425],{"emptyLinePlaceholder":3424},[3400,5448,5449],{"class":3402,"line":3451},[3400,5450,5451],{},"rectangle \"Фаза 1: Canary 5%\" as P1 #fff3e0 {\n",[3400,5453,5454],{"class":3402,"line":3457},[3400,5455,5456],{},"    rectangle \"api-v1-tg\\nВага: 95\\n(поточна версiя)\" as V1_1 #bbf7d0\n",[3400,5458,5459],{"class":3402,"line":3463},[3400,5460,5461],{},"    rectangle \"api-v2-tg\\nВага: 5\\n(нова версiя)\" as V2_1 #fde68a\n",[3400,5463,5464],{"class":3402,"line":3469},[3400,5465,3496],{},[3400,5467,5468],{"class":3402,"line":3475},[3400,5469,3425],{"emptyLinePlaceholder":3424},[3400,5471,5472],{"class":3402,"line":3481},[3400,5473,5474],{},"rectangle \"Фаза 2: 50\u002F50\" as P2 #fff3e0 {\n",[3400,5476,5477],{"class":3402,"line":3487},[3400,5478,5479],{},"    rectangle \"api-v1-tg\\nВага: 50\" as V1_2 #fde68a\n",[3400,5481,5482],{"class":3402,"line":3493},[3400,5483,5484],{},"    rectangle \"api-v2-tg\\nВага: 50\" as V2_2 #fde68a\n",[3400,5486,5487],{"class":3402,"line":3499},[3400,5488,3496],{},[3400,5490,5491],{"class":3402,"line":3504},[3400,5492,3425],{"emptyLinePlaceholder":3424},[3400,5494,5495],{"class":3402,"line":3510},[3400,5496,5497],{},"rectangle \"Фаза 3: Повна мiграцiя\" as P3 #f0fdf4 {\n",[3400,5499,5500],{"class":3402,"line":3516},[3400,5501,5502],{},"    rectangle \"api-v2-tg\\nВага: 100\\n(v1 виведено)\" as V2_3 #bbf7d0\n",[3400,5504,5505],{"class":3402,"line":3522},[3400,5506,3496],{},[3400,5508,5509],{"class":3402,"line":3528},[3400,5510,3425],{"emptyLinePlaceholder":3424},[3400,5512,5513],{"class":3402,"line":3534},[3400,5514,5515],{},"ALB --> P1\n",[3400,5517,5518],{"class":3402,"line":3540},[3400,5519,5520],{},"P1 -[hidden]down-> P2\n",[3400,5522,5523],{"class":3402,"line":3545},[3400,5524,5525],{},"P2 -[hidden]down-> P3\n",[3400,5527,5528],{"class":3402,"line":3550},[3400,5529,3425],{"emptyLinePlaceholder":3424},[3400,5531,5532],{"class":3402,"line":3556},[3400,5533,5534],{},"note right of P1\n",[3400,5536,5537],{"class":3402,"line":3562},[3400,5538,5539],{},"  5% реальних користувачiв\n",[3400,5541,5542],{"class":3402,"line":3568},[3400,5543,5544],{},"  тестують нову версiю.\n",[3400,5546,5547],{"class":3402,"line":3574},[3400,5548,5549],{},"  Монiторимо: error rate,\n",[3400,5551,5552],{"class":3402,"line":3580},[3400,5553,5554],{},"  latency, business metrics.\n",[3400,5556,5557],{"class":3402,"line":3586},[3400,5558,3618],{},[3400,5560,5561],{"class":3402,"line":3591},[3400,5562,3425],{"emptyLinePlaceholder":3424},[3400,5564,5565],{"class":3402,"line":3597},[3400,5566,5567],{},"note right of P2\n",[3400,5569,5570],{"class":3402,"line":3603},[3400,5571,5572],{},"  Метрики гарнi -> 50%.\n",[3400,5574,5575],{"class":3402,"line":3609},[3400,5576,5577],{},"  Вiдкочування: вага v1=100,\n",[3400,5579,5580],{"class":3402,"line":3615},[3400,5581,5582],{},"  вага v2=0 (миттєво!).\n",[3400,5584,5585],{"class":3402,"line":3621},[3400,5586,3618],{},[3400,5588,5590],{"class":3402,"line":5589},39,[3400,5591,3425],{"emptyLinePlaceholder":3424},[3400,5593,5595],{"class":3402,"line":5594},40,[3400,5596,5597],{},"note right of P3\n",[3400,5599,5601],{"class":3402,"line":5600},41,[3400,5602,5603],{},"  Нова версiя стабiльна.\n",[3400,5605,5607],{"class":3402,"line":5606},42,[3400,5608,5609],{},"  Виводимо v1 з обiгу.\n",[3400,5611,5613],{"class":3402,"line":5612},43,[3400,5614,3618],{},[3400,5616,5618],{"class":3402,"line":5617},44,[3400,5619,3425],{"emptyLinePlaceholder":3424},[3400,5621,5623],{"class":3402,"line":5622},45,[3400,5624,3624],{},[3353,5626,5627],{},[3359,5628,5629],{},"Реалізація Canary Deployment через ALB Weighted Target Groups:",[3353,5631,5632,5633,5636],{},"AWS реалізує Weighted Random через ",[3359,5634,5635],{},"ваги на рівні Listener Rule"," — правило може розподіляти трафік між кількома Target Groups з визначеними пропорціями:",[3390,5638,5640],{"className":4740,"code":5639,"language":4742,"meta":3395,"style":3395},"LISTENER_ARN=\"arn:aws:elasticloadbalancing:eu-central-1:123456789012:listener\u002Fapp\u002F...\"\nTG_V1_ARN=\"arn:aws:elasticloadbalancing:...:targetgroup\u002Fapi-v1-tg\u002F...\"\nTG_V2_ARN=\"arn:aws:elasticloadbalancing:...:targetgroup\u002Fapi-v2-tg\u002F...\"\n\n# Фаза 1: 5% трафіку на нову версію\naws elbv2 modify-listener \\\n    --listener-arn \"$LISTENER_ARN\" \\\n    --default-actions \"[\n        {\n            \\\"Type\\\": \\\"forward\\\",\n            \\\"ForwardConfig\\\": {\n                \\\"TargetGroups\\\": [\n                    {\\\"TargetGroupArn\\\": \\\"$TG_V1_ARN\\\", \\\"Weight\\\": 95},\n                    {\\\"TargetGroupArn\\\": \\\"$TG_V2_ARN\\\", \\\"Weight\\\": 5}\n                ]\n            }\n        }\n    ]\" \\\n    --region eu-central-1\n\n# Фаза 2: 50\u002F50 (перевіряємо рівну продуктивність)\naws elbv2 modify-listener \\\n    --listener-arn \"$LISTENER_ARN\" \\\n    --default-actions \"[\n        {\n            \\\"Type\\\": \\\"forward\\\",\n            \\\"ForwardConfig\\\": {\n                \\\"TargetGroups\\\": [\n                    {\\\"TargetGroupArn\\\": \\\"$TG_V1_ARN\\\", \\\"Weight\\\": 50},\n                    {\\\"TargetGroupArn\\\": \\\"$TG_V2_ARN\\\", \\\"Weight\\\": 50}\n                ]\n            }\n        }\n    ]\" \\\n    --region eu-central-1\n\n# Фаза 3: повна міграція на v2\naws elbv2 modify-listener \\\n    --listener-arn \"$LISTENER_ARN\" \\\n    --default-actions \"Type=forward,TargetGroupArn=$TG_V2_ARN\" \\\n    --region eu-central-1\n\necho \"Migration complete: 100% traffic -> v2\"\n",[3397,5641,5642,5654,5664,5674,5678,5683,5694,5708,5716,5721,5744,5756,5769,5802,5832,5837,5842,5847,5854,5860,5864,5869,5879,5891,5897,5901,5919,5929,5939,5968,5997,6001,6005,6009,6015,6021,6025,6030,6040,6052,6065,6071,6075],{"__ignoreMap":3395},[3400,5643,5644,5647,5651],{"class":3402,"line":3403},[3400,5645,5646],{"class":4779},"LISTENER_ARN",[3400,5648,5650],{"class":5649},"sHH4Y","=",[3400,5652,5653],{"class":4759},"\"arn:aws:elasticloadbalancing:eu-central-1:123456789012:listener\u002Fapp\u002F...\"\n",[3400,5655,5656,5659,5661],{"class":3402,"line":3409},[3400,5657,5658],{"class":4779},"TG_V1_ARN",[3400,5660,5650],{"class":5649},[3400,5662,5663],{"class":4759},"\"arn:aws:elasticloadbalancing:...:targetgroup\u002Fapi-v1-tg\u002F...\"\n",[3400,5665,5666,5669,5671],{"class":3402,"line":3415},[3400,5667,5668],{"class":4779},"TG_V2_ARN",[3400,5670,5650],{"class":5649},[3400,5672,5673],{"class":4759},"\"arn:aws:elasticloadbalancing:...:targetgroup\u002Fapi-v2-tg\u002F...\"\n",[3400,5675,5676],{"class":3402,"line":3421},[3400,5677,3425],{"emptyLinePlaceholder":3424},[3400,5679,5680],{"class":3402,"line":3428},[3400,5681,5682],{"class":4749},"# Фаза 1: 5% трафіку на нову версію\n",[3400,5684,5685,5687,5689,5692],{"class":3402,"line":3434},[3400,5686,4756],{"class":4755},[3400,5688,4760],{"class":4759},[3400,5690,5691],{"class":4759}," modify-listener",[3400,5693,4767],{"class":4766},[3400,5695,5696,5699,5701,5704,5706],{"class":3402,"line":3440},[3400,5697,5698],{"class":4772},"    --listener-arn",[3400,5700,4776],{"class":4759},[3400,5702,5703],{"class":4779},"$LISTENER_ARN",[3400,5705,4783],{"class":4759},[3400,5707,4767],{"class":4766},[3400,5709,5710,5713],{"class":3402,"line":3446},[3400,5711,5712],{"class":4772},"    --default-actions",[3400,5714,5715],{"class":4759}," \"[\n",[3400,5717,5718],{"class":3402,"line":3451},[3400,5719,5720],{"class":4759},"        {\n",[3400,5722,5723,5726,5729,5732,5734,5736,5739,5741],{"class":3402,"line":3457},[3400,5724,5725],{"class":4766},"            \\\"",[3400,5727,5728],{"class":4759},"Type",[3400,5730,5731],{"class":4766},"\\\"",[3400,5733,5255],{"class":4759},[3400,5735,5731],{"class":4766},[3400,5737,5738],{"class":4759},"forward",[3400,5740,5731],{"class":4766},[3400,5742,5743],{"class":4759},",\n",[3400,5745,5746,5748,5751,5753],{"class":3402,"line":3463},[3400,5747,5725],{"class":4766},[3400,5749,5750],{"class":4759},"ForwardConfig",[3400,5752,5731],{"class":4766},[3400,5754,5755],{"class":4759},": {\n",[3400,5757,5758,5761,5764,5766],{"class":3402,"line":3469},[3400,5759,5760],{"class":4766},"                \\\"",[3400,5762,5763],{"class":4759},"TargetGroups",[3400,5765,5731],{"class":4766},[3400,5767,5768],{"class":4759},": [\n",[3400,5770,5771,5774,5776,5779,5781,5783,5785,5788,5790,5792,5794,5797,5799],{"class":3402,"line":3475},[3400,5772,5773],{"class":4759},"                    {",[3400,5775,5731],{"class":4766},[3400,5777,5778],{"class":4759},"TargetGroupArn",[3400,5780,5731],{"class":4766},[3400,5782,5255],{"class":4759},[3400,5784,5731],{"class":4766},[3400,5786,5787],{"class":4779},"$TG_V1_ARN",[3400,5789,5731],{"class":4766},[3400,5791,4031],{"class":4759},[3400,5793,5731],{"class":4766},[3400,5795,5796],{"class":4759},"Weight",[3400,5798,5731],{"class":4766},[3400,5800,5801],{"class":4759},": 95},\n",[3400,5803,5804,5806,5808,5810,5812,5814,5816,5819,5821,5823,5825,5827,5829],{"class":3402,"line":3481},[3400,5805,5773],{"class":4759},[3400,5807,5731],{"class":4766},[3400,5809,5778],{"class":4759},[3400,5811,5731],{"class":4766},[3400,5813,5255],{"class":4759},[3400,5815,5731],{"class":4766},[3400,5817,5818],{"class":4779},"$TG_V2_ARN",[3400,5820,5731],{"class":4766},[3400,5822,4031],{"class":4759},[3400,5824,5731],{"class":4766},[3400,5826,5796],{"class":4759},[3400,5828,5731],{"class":4766},[3400,5830,5831],{"class":4759},": 5}\n",[3400,5833,5834],{"class":3402,"line":3487},[3400,5835,5836],{"class":4759},"                ]\n",[3400,5838,5839],{"class":3402,"line":3493},[3400,5840,5841],{"class":4759},"            }\n",[3400,5843,5844],{"class":3402,"line":3499},[3400,5845,5846],{"class":4759},"        }\n",[3400,5848,5849,5852],{"class":3402,"line":3504},[3400,5850,5851],{"class":4759},"    ]\"",[3400,5853,4767],{"class":4766},[3400,5855,5856,5858],{"class":3402,"line":3510},[3400,5857,4818],{"class":4772},[3400,5859,4821],{"class":4759},[3400,5861,5862],{"class":3402,"line":3516},[3400,5863,3425],{"emptyLinePlaceholder":3424},[3400,5865,5866],{"class":3402,"line":3522},[3400,5867,5868],{"class":4749},"# Фаза 2: 50\u002F50 (перевіряємо рівну продуктивність)\n",[3400,5870,5871,5873,5875,5877],{"class":3402,"line":3528},[3400,5872,4756],{"class":4755},[3400,5874,4760],{"class":4759},[3400,5876,5691],{"class":4759},[3400,5878,4767],{"class":4766},[3400,5880,5881,5883,5885,5887,5889],{"class":3402,"line":3534},[3400,5882,5698],{"class":4772},[3400,5884,4776],{"class":4759},[3400,5886,5703],{"class":4779},[3400,5888,4783],{"class":4759},[3400,5890,4767],{"class":4766},[3400,5892,5893,5895],{"class":3402,"line":3540},[3400,5894,5712],{"class":4772},[3400,5896,5715],{"class":4759},[3400,5898,5899],{"class":3402,"line":3545},[3400,5900,5720],{"class":4759},[3400,5902,5903,5905,5907,5909,5911,5913,5915,5917],{"class":3402,"line":3550},[3400,5904,5725],{"class":4766},[3400,5906,5728],{"class":4759},[3400,5908,5731],{"class":4766},[3400,5910,5255],{"class":4759},[3400,5912,5731],{"class":4766},[3400,5914,5738],{"class":4759},[3400,5916,5731],{"class":4766},[3400,5918,5743],{"class":4759},[3400,5920,5921,5923,5925,5927],{"class":3402,"line":3556},[3400,5922,5725],{"class":4766},[3400,5924,5750],{"class":4759},[3400,5926,5731],{"class":4766},[3400,5928,5755],{"class":4759},[3400,5930,5931,5933,5935,5937],{"class":3402,"line":3562},[3400,5932,5760],{"class":4766},[3400,5934,5763],{"class":4759},[3400,5936,5731],{"class":4766},[3400,5938,5768],{"class":4759},[3400,5940,5941,5943,5945,5947,5949,5951,5953,5955,5957,5959,5961,5963,5965],{"class":3402,"line":3568},[3400,5942,5773],{"class":4759},[3400,5944,5731],{"class":4766},[3400,5946,5778],{"class":4759},[3400,5948,5731],{"class":4766},[3400,5950,5255],{"class":4759},[3400,5952,5731],{"class":4766},[3400,5954,5787],{"class":4779},[3400,5956,5731],{"class":4766},[3400,5958,4031],{"class":4759},[3400,5960,5731],{"class":4766},[3400,5962,5796],{"class":4759},[3400,5964,5731],{"class":4766},[3400,5966,5967],{"class":4759},": 50},\n",[3400,5969,5970,5972,5974,5976,5978,5980,5982,5984,5986,5988,5990,5992,5994],{"class":3402,"line":3574},[3400,5971,5773],{"class":4759},[3400,5973,5731],{"class":4766},[3400,5975,5778],{"class":4759},[3400,5977,5731],{"class":4766},[3400,5979,5255],{"class":4759},[3400,5981,5731],{"class":4766},[3400,5983,5818],{"class":4779},[3400,5985,5731],{"class":4766},[3400,5987,4031],{"class":4759},[3400,5989,5731],{"class":4766},[3400,5991,5796],{"class":4759},[3400,5993,5731],{"class":4766},[3400,5995,5996],{"class":4759},": 50}\n",[3400,5998,5999],{"class":3402,"line":3580},[3400,6000,5836],{"class":4759},[3400,6002,6003],{"class":3402,"line":3586},[3400,6004,5841],{"class":4759},[3400,6006,6007],{"class":3402,"line":3591},[3400,6008,5846],{"class":4759},[3400,6010,6011,6013],{"class":3402,"line":3597},[3400,6012,5851],{"class":4759},[3400,6014,4767],{"class":4766},[3400,6016,6017,6019],{"class":3402,"line":3603},[3400,6018,4818],{"class":4772},[3400,6020,4821],{"class":4759},[3400,6022,6023],{"class":3402,"line":3609},[3400,6024,3425],{"emptyLinePlaceholder":3424},[3400,6026,6027],{"class":3402,"line":3615},[3400,6028,6029],{"class":4749},"# Фаза 3: повна міграція на v2\n",[3400,6031,6032,6034,6036,6038],{"class":3402,"line":3621},[3400,6033,4756],{"class":4755},[3400,6035,4760],{"class":4759},[3400,6037,5691],{"class":4759},[3400,6039,4767],{"class":4766},[3400,6041,6042,6044,6046,6048,6050],{"class":3402,"line":5589},[3400,6043,5698],{"class":4772},[3400,6045,4776],{"class":4759},[3400,6047,5703],{"class":4779},[3400,6049,4783],{"class":4759},[3400,6051,4767],{"class":4766},[3400,6053,6054,6056,6059,6061,6063],{"class":3402,"line":5594},[3400,6055,5712],{"class":4772},[3400,6057,6058],{"class":4759}," \"Type=forward,TargetGroupArn=",[3400,6060,5818],{"class":4779},[3400,6062,4783],{"class":4759},[3400,6064,4767],{"class":4766},[3400,6066,6067,6069],{"class":3402,"line":5600},[3400,6068,4818],{"class":4772},[3400,6070,4821],{"class":4759},[3400,6072,6073],{"class":3402,"line":5606},[3400,6074,3425],{"emptyLinePlaceholder":3424},[3400,6076,6077,6080],{"class":3402,"line":5612},[3400,6078,6079],{"class":4755},"echo",[3400,6081,6082],{"class":4759}," \"Migration complete: 100% traffic -> v2\"\n",[5202,6084,6086,6095,6098,6106,6113,6124,6132,6140,6162,6181,6185,6189,6193,6197,6200],{"title":6085},"Canary Deployment: Фаза 1 → 5%",[5206,6087,6089,5214,6092],{"className":6088},[3402],[3400,6090,5213],{"className":6091},[5212],[3359,6093,6094],{},"aws elbv2 modify-listener --listener-arn \"$LISTENER_ARN\" --default-actions '[...]'",[5206,6096,5232],{"className":6097},[3402],[5206,6099,5236,6101,6105],{"className":6100},[3402],[3400,6102,6104],{"className":6103},[5240],"\"Listeners\"",": [{",[5206,6107,5358,6109,6105],{"className":6108},[3402],[3400,6110,6112],{"className":6111},[5240],"\"DefaultActions\"",[5206,6114,5250,6116,5255,6120,5261],{"className":6115},[3402],[3400,6117,6119],{"className":6118},[5240],"\"Type\"",[3400,6121,6123],{"className":6122},[5259],"\"forward\"",[5206,6125,5250,6127,6131],{"className":6126},[3402],[3400,6128,6130],{"className":6129},[5240],"\"ForwardConfig\"",": {",[5206,6133,6135,6136,5242],{"className":6134},[3402],"                ",[3400,6137,6139],{"className":6138},[5240],"\"TargetGroups\"",[5206,6141,6143,6144,5255,6148,4031,6153,5255,6157,6161],{"className":6142},[3402],"                    { ",[3400,6145,6147],{"className":6146},[5240],"\"Weight\"",[3400,6149,6152],{"className":6150},[6151],"text-yellow-400","95",[3400,6154,6156],{"className":6155},[5240],"\"TargetGroupArn\"",[3400,6158,6160],{"className":6159},[5259],"\"...api-v1-tg...\""," },",[5206,6163,6143,6165,5255,6168,6172,6173,5255,6176,6180],{"className":6164},[3402],[3400,6166,6147],{"className":6167},[5240],[3400,6169,6171],{"className":6170},[6151],"5",",  ",[3400,6174,6156],{"className":6175},[5240],[3400,6177,6179],{"className":6178},[5259],"\"...api-v2-tg...\""," }",[5206,6182,6184],{"className":6183},[3402],"                ]",[5206,6186,6188],{"className":6187},[3402],"            }",[5206,6190,6192],{"className":6191},[3402],"        }]",[5206,6194,6196],{"className":6195},[3402],"    }]",[5206,6198,5284],{"className":6199},[3402],[5206,6201,6203],{"className":6202},[3402],[3400,6204,6207],{"className":6205},[5259,6206],"opacity-60","← 5% реального трафіку тепер іде на нову версію",[6209,6210,6211,6214,6215,6218,6219,6222,6223,6226],"tip",{},[3359,6212,6213],{},"Weighted Random vs Round Robin при рівних вагах."," Якщо всі targets мають однакову вагу, Weighted Random та Round Robin дають ідентичний результат у довгостроковій перспективі. Різниця: Round Robin гарантує ",[3359,6216,6217],{},"точний"," розподіл (",[3397,6220,6221],{},"N\u002F3"," запитів кожному з трьох targets), тоді як Weighted Random дає ",[3359,6224,6225],{},"статистично очікуваний"," розподіл — рівний у середньому, але з можливими коливаннями в короткій перспективі.",[3626,6228],{},[4386,6230,6232],{"id":6231},"порівняльна-таблиця-алгоритмів","Порівняльна таблиця алгоритмів",[6234,6235,6236,6254],"table",{},[6237,6238,6239],"thead",{},[6240,6241,6242,6246,6249,6252],"tr",{},[6243,6244,6245],"th",{},"Характеристика",[6243,6247,6248],{},"Weighted Round Robin",[6243,6250,6251],{},"Least Outstanding Requests",[6243,6253,5392],{},[6255,6256,6257,6273,6288,6303,6316,6330,6345,6361],"tbody",{},[6240,6258,6259,6265,6268,6271],{},[6260,6261,6262],"td",{},[3359,6263,6264],{},"За замовчуванням",[6260,6266,6267],{},"Так",[6260,6269,6270],{},"Ні",[6260,6272,6270],{},[6240,6274,6275,6280,6283,6286],{},[6260,6276,6277],{},[3359,6278,6279],{},"Однорідні запити",[6260,6281,6282],{},"Оптимально",[6260,6284,6285],{},"Добре",[6260,6287,6285],{},[6240,6289,6290,6295,6298,6300],{},[6260,6291,6292],{},[3359,6293,6294],{},"Неоднорідні запити",[6260,6296,6297],{},"Погано",[6260,6299,6282],{},[6260,6301,6302],{},"Задовільно",[6240,6304,6305,6310,6312,6314],{},[6260,6306,6307],{},[3359,6308,6309],{},"WebSocket \u002F streaming",[6260,6311,6297],{},[6260,6313,6282],{},[6260,6315,6302],{},[6240,6317,6318,6323,6326,6328],{},[6260,6319,6320],{},[3359,6321,6322],{},"Canary deployment",[6260,6324,6325],{},"Не підходить",[6260,6327,6325],{},[6260,6329,6282],{},[6240,6331,6332,6337,6340,6343],{},[6260,6333,6334],{},[3359,6335,6336],{},"Різна потужність targets",[6260,6338,6339],{},"Через ваги",[6260,6341,6342],{},"Автоматично",[6260,6344,6339],{},[6240,6346,6347,6352,6355,6358],{},[6260,6348,6349],{},[3359,6350,6351],{},"Передбачуваність",[6260,6353,6354],{},"Детермінована",[6260,6356,6357],{},"Адаптивна",[6260,6359,6360],{},"Стохастична",[6240,6362,6363,6368,6371,6374],{},[6260,6364,6365],{},[3359,6366,6367],{},"Рекомендовано для .NET API",[6260,6369,6370],{},"Простий CRUD API",[6260,6372,6373],{},"API з важкими операціями",[6260,6375,6376],{},"A\u002FB тест, canary",[3353,6378,6379],{},[3359,6380,6381],{},"Дерево рішень для вибору алгоритму:",[3387,6383,6384],{},[3390,6385,6387],{"className":3392,"code":6386,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nstart\n\n:Потрiбен canary deployment\\nабо A\u002FB тестування?;\n\nif (Так) then\n    :Weighted Random\\n(ваги мiж Target Groups\\nна рiвнi Listener Rule);\n    stop\nendif\n\n:Запити однорiднi за\\nчасом обробки?;\n\nif (Так — всi запити\\nблизько N ms) then\n    :Weighted Round Robin\\n(алгоритм за замовчуванням,\\nнiчого не мiняти);\n    stop\nelse (Нi — є важкi запити,\\nWebSocket, streaming)\n    :Least Outstanding Requests\\n(автоматично уникає\\nперевантажених targets);\n    stop\nendif\n\n@enduml\n",[3397,6388,6389,6393,6397,6401,6405,6410,6414,6419,6423,6428,6433,6438,6443,6447,6452,6456,6461,6466,6470,6475,6480,6484,6488,6492],{"__ignoreMap":3395},[3400,6390,6391],{"class":3402,"line":3403},[3400,6392,3406],{},[3400,6394,6395],{"class":3402,"line":3409},[3400,6396,3412],{},[3400,6398,6399],{"class":3402,"line":3415},[3400,6400,3418],{},[3400,6402,6403],{"class":3402,"line":3421},[3400,6404,3425],{"emptyLinePlaceholder":3424},[3400,6406,6407],{"class":3402,"line":3428},[3400,6408,6409],{},"start\n",[3400,6411,6412],{"class":3402,"line":3434},[3400,6413,3425],{"emptyLinePlaceholder":3424},[3400,6415,6416],{"class":3402,"line":3440},[3400,6417,6418],{},":Потрiбен canary deployment\\nабо A\u002FB тестування?;\n",[3400,6420,6421],{"class":3402,"line":3446},[3400,6422,3425],{"emptyLinePlaceholder":3424},[3400,6424,6425],{"class":3402,"line":3451},[3400,6426,6427],{},"if (Так) then\n",[3400,6429,6430],{"class":3402,"line":3457},[3400,6431,6432],{},"    :Weighted Random\\n(ваги мiж Target Groups\\nна рiвнi Listener Rule);\n",[3400,6434,6435],{"class":3402,"line":3463},[3400,6436,6437],{},"    stop\n",[3400,6439,6440],{"class":3402,"line":3469},[3400,6441,6442],{},"endif\n",[3400,6444,6445],{"class":3402,"line":3475},[3400,6446,3425],{"emptyLinePlaceholder":3424},[3400,6448,6449],{"class":3402,"line":3481},[3400,6450,6451],{},":Запити однорiднi за\\nчасом обробки?;\n",[3400,6453,6454],{"class":3402,"line":3487},[3400,6455,3425],{"emptyLinePlaceholder":3424},[3400,6457,6458],{"class":3402,"line":3493},[3400,6459,6460],{},"if (Так — всi запити\\nблизько N ms) then\n",[3400,6462,6463],{"class":3402,"line":3499},[3400,6464,6465],{},"    :Weighted Round Robin\\n(алгоритм за замовчуванням,\\nнiчого не мiняти);\n",[3400,6467,6468],{"class":3402,"line":3504},[3400,6469,6437],{},[3400,6471,6472],{"class":3402,"line":3510},[3400,6473,6474],{},"else (Нi — є важкi запити,\\nWebSocket, streaming)\n",[3400,6476,6477],{"class":3402,"line":3516},[3400,6478,6479],{},"    :Least Outstanding Requests\\n(автоматично уникає\\nперевантажених targets);\n",[3400,6481,6482],{"class":3402,"line":3522},[3400,6483,6437],{},[3400,6485,6486],{"class":3402,"line":3528},[3400,6487,6442],{},[3400,6489,6490],{"class":3402,"line":3534},[3400,6491,3425],{"emptyLinePlaceholder":3424},[3400,6493,6494],{"class":3402,"line":3540},[3400,6495,3624],{},[3824,6497,6499],{"id":6498},"health-check-механізм-виявлення-відмов","Health Check: механізм виявлення відмов",[3353,6501,6502,6505],{},[3359,6503,6504],{},"Health Check"," — це механізм, за допомогою якого ALB постійно моніторить стан кожного target у Target Group. ALB регулярно надсилає тестові HTTP-запити на спеціальний ендпоінт кожного сервера і аналізує відповідь. На основі результатів цих перевірок ALB ухвалює рішення: включати чи виключати target із ротації.",[3387,6507,6508],{},[3390,6509,6511],{"className":3392,"code":6510,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nparticipant \"ALB\\nHealth Checker\" as HC\nparticipant \"Target (EC2)\\n\u002Fhealth endpoint\" as TG\n\nloop \"Кожні HealthCheckIntervalSeconds (30s)\"\n    HC -> TG : \"GET \u002Fhealth HTTP\u002F1.1\\nHost: internal-ip:5000\"\n    alt \"Відповідь 200 OK за \u003C 5s\"\n        TG --> HC : \"200 OK\\n{\\\"status\\\":\\\"Healthy\\\"}\"\n        HC -> HC : \"successCount++\"\n        note right of HC\n            Якщо successCount >=\n            HealthyThresholdCount (2)\n            → target стає Healthy\n        end note\n    else \"Таймаут або помилка\"\n        TG --> HC : \"(немає відповіді \u002F 500)\"\n        HC -> HC : \"failureCount++\"\n        note right of HC\n            Якщо failureCount >=\n            UnhealthyThresholdCount (3)\n            → target стає Unhealthy\n            → ALB виключає з ротації\n        end note\n    end\nend\n\n@enduml\n",[3397,6512,6513,6517,6521,6525,6529,6534,6539,6543,6548,6553,6558,6563,6568,6573,6578,6583,6588,6593,6598,6603,6608,6612,6617,6622,6627,6632,6636,6641,6646,6650],{"__ignoreMap":3395},[3400,6514,6515],{"class":3402,"line":3403},[3400,6516,3406],{},[3400,6518,6519],{"class":3402,"line":3409},[3400,6520,3412],{},[3400,6522,6523],{"class":3402,"line":3415},[3400,6524,3418],{},[3400,6526,6527],{"class":3402,"line":3421},[3400,6528,3425],{"emptyLinePlaceholder":3424},[3400,6530,6531],{"class":3402,"line":3428},[3400,6532,6533],{},"participant \"ALB\\nHealth Checker\" as HC\n",[3400,6535,6536],{"class":3402,"line":3434},[3400,6537,6538],{},"participant \"Target (EC2)\\n\u002Fhealth endpoint\" as TG\n",[3400,6540,6541],{"class":3402,"line":3440},[3400,6542,3425],{"emptyLinePlaceholder":3424},[3400,6544,6545],{"class":3402,"line":3446},[3400,6546,6547],{},"loop \"Кожні HealthCheckIntervalSeconds (30s)\"\n",[3400,6549,6550],{"class":3402,"line":3451},[3400,6551,6552],{},"    HC -> TG : \"GET \u002Fhealth HTTP\u002F1.1\\nHost: internal-ip:5000\"\n",[3400,6554,6555],{"class":3402,"line":3457},[3400,6556,6557],{},"    alt \"Відповідь 200 OK за \u003C 5s\"\n",[3400,6559,6560],{"class":3402,"line":3463},[3400,6561,6562],{},"        TG --> HC : \"200 OK\\n{\\\"status\\\":\\\"Healthy\\\"}\"\n",[3400,6564,6565],{"class":3402,"line":3469},[3400,6566,6567],{},"        HC -> HC : \"successCount++\"\n",[3400,6569,6570],{"class":3402,"line":3475},[3400,6571,6572],{},"        note right of HC\n",[3400,6574,6575],{"class":3402,"line":3481},[3400,6576,6577],{},"            Якщо successCount >=\n",[3400,6579,6580],{"class":3402,"line":3487},[3400,6581,6582],{},"            HealthyThresholdCount (2)\n",[3400,6584,6585],{"class":3402,"line":3493},[3400,6586,6587],{},"            → target стає Healthy\n",[3400,6589,6590],{"class":3402,"line":3499},[3400,6591,6592],{},"        end note\n",[3400,6594,6595],{"class":3402,"line":3504},[3400,6596,6597],{},"    else \"Таймаут або помилка\"\n",[3400,6599,6600],{"class":3402,"line":3510},[3400,6601,6602],{},"        TG --> HC : \"(немає відповіді \u002F 500)\"\n",[3400,6604,6605],{"class":3402,"line":3516},[3400,6606,6607],{},"        HC -> HC : \"failureCount++\"\n",[3400,6609,6610],{"class":3402,"line":3522},[3400,6611,6572],{},[3400,6613,6614],{"class":3402,"line":3528},[3400,6615,6616],{},"            Якщо failureCount >=\n",[3400,6618,6619],{"class":3402,"line":3534},[3400,6620,6621],{},"            UnhealthyThresholdCount (3)\n",[3400,6623,6624],{"class":3402,"line":3540},[3400,6625,6626],{},"            → target стає Unhealthy\n",[3400,6628,6629],{"class":3402,"line":3545},[3400,6630,6631],{},"            → ALB виключає з ротації\n",[3400,6633,6634],{"class":3402,"line":3550},[3400,6635,6592],{},[3400,6637,6638],{"class":3402,"line":3556},[3400,6639,6640],{},"    end\n",[3400,6642,6643],{"class":3402,"line":3562},[3400,6644,6645],{},"end\n",[3400,6647,6648],{"class":3402,"line":3568},[3400,6649,3425],{"emptyLinePlaceholder":3424},[3400,6651,6652],{"class":3402,"line":3574},[3400,6653,3624],{},[3353,6655,6656],{},[3359,6657,6658],{},"Конфігурація Health Check у консолі AWS або через CLI:",[3390,6660,6664],{"className":6661,"code":6662,"language":6663,"meta":3395,"style":3395},"language-json shiki shiki-themes light-plus dark-plus dark-plus","{\n    \"Protocol\": \"HTTP\",\n    \"Path\": \"\u002Fhealth\",\n    \"Port\": \"traffic-port\",\n    \"HealthyThresholdCount\": 2,\n    \"UnhealthyThresholdCount\": 3,\n    \"HealthCheckIntervalSeconds\": 15,\n    \"HealthCheckTimeoutSeconds\": 5,\n    \"Matcher\": {\n        \"HttpCode\": \"200\"\n    }\n}\n","json",[3397,6665,6666,6671,6684,6696,6708,6719,6731,6743,6754,6761,6771,6775],{"__ignoreMap":3395},[3400,6667,6668],{"class":3402,"line":3403},[3400,6669,6670],{"class":5649},"{\n",[3400,6672,6673,6677,6679,6682],{"class":3402,"line":3409},[3400,6674,6676],{"class":6675},"sLwNe","    \"Protocol\"",[3400,6678,5255],{"class":5649},[3400,6680,6681],{"class":4759},"\"HTTP\"",[3400,6683,5743],{"class":5649},[3400,6685,6686,6689,6691,6694],{"class":3402,"line":3415},[3400,6687,6688],{"class":6675},"    \"Path\"",[3400,6690,5255],{"class":5649},[3400,6692,6693],{"class":4759},"\"\u002Fhealth\"",[3400,6695,5743],{"class":5649},[3400,6697,6698,6701,6703,6706],{"class":3402,"line":3421},[3400,6699,6700],{"class":6675},"    \"Port\"",[3400,6702,5255],{"class":5649},[3400,6704,6705],{"class":4759},"\"traffic-port\"",[3400,6707,5743],{"class":5649},[3400,6709,6710,6713,6715,6717],{"class":3402,"line":3428},[3400,6711,6712],{"class":6675},"    \"HealthyThresholdCount\"",[3400,6714,5255],{"class":5649},[3400,6716,4811],{"class":4800},[3400,6718,5743],{"class":5649},[3400,6720,6721,6724,6726,6729],{"class":3402,"line":3434},[3400,6722,6723],{"class":6675},"    \"UnhealthyThresholdCount\"",[3400,6725,5255],{"class":5649},[3400,6727,6728],{"class":4800},"3",[3400,6730,5743],{"class":5649},[3400,6732,6733,6736,6738,6741],{"class":3402,"line":3440},[3400,6734,6735],{"class":6675},"    \"HealthCheckIntervalSeconds\"",[3400,6737,5255],{"class":5649},[3400,6739,6740],{"class":4800},"15",[3400,6742,5743],{"class":5649},[3400,6744,6745,6748,6750,6752],{"class":3402,"line":3446},[3400,6746,6747],{"class":6675},"    \"HealthCheckTimeoutSeconds\"",[3400,6749,5255],{"class":5649},[3400,6751,6171],{"class":4800},[3400,6753,5743],{"class":5649},[3400,6755,6756,6759],{"class":3402,"line":3451},[3400,6757,6758],{"class":6675},"    \"Matcher\"",[3400,6760,5755],{"class":5649},[3400,6762,6763,6766,6768],{"class":3402,"line":3457},[3400,6764,6765],{"class":6675},"        \"HttpCode\"",[3400,6767,5255],{"class":5649},[3400,6769,6770],{"class":4759},"\"200\"\n",[3400,6772,6773],{"class":3402,"line":3463},[3400,6774,3537],{"class":5649},[3400,6776,6777],{"class":3402,"line":3469},[3400,6778,3496],{"class":5649},[4019,6780,6781,6790,6798,6810,6817,6821],{},[4022,6782,6784,6785,4031,6787,6789],{"name":4024,"type":6783,"required":4026},"string","Протокол для health check запитів. Допустимі значення: ",[3397,6786,4030],{},[3397,6788,4034],{},". Має відповідати протоколу, на якому працює застосунок.",[4022,6791,6793,6794,6797],{"name":6792,"type":6783,"required":4026},"Path","URI-шлях, на який ALB надсилає GET-запити. За угодою: ",[3397,6795,6796],{},"\u002Fhealth",". Цей ендпоінт повинен повертати HTTP 200 лише тоді, коли застосунок та всі його залежності справно функціонують.",[4022,6799,6801,6802,6805,6806,6809],{"name":6800,"type":4039},"HealthyThresholdCount","Кількість послідовних успішних перевірок, після яких target переходить зі стану ",[3397,6803,6804],{},"unhealthy"," у стан ",[3397,6807,6808],{},"healthy",". Значення 2 означає, що після двох поспіль успішних відповідей ALB відновить маршрутизацію трафіку на цей target.",[4022,6811,6813,6814,6816],{"name":6812,"type":4039},"UnhealthyThresholdCount","Кількість послідовних невдалих перевірок, після яких target переходить у стан ",[3397,6815,6804],{}," і виключається з ротації. При значенні 3 та інтервалі 15 секунд — від першої невдалої відповіді до виключення мине 45 секунд.",[4022,6818,6820],{"name":6819,"type":4039},"HealthCheckIntervalSeconds","Інтервал між перевірками в секундах. Допустимий діапазон: 5–300 секунд. Менший інтервал забезпечує швидше виявлення відмов, але генерує більше трафіку.",[4022,6822,6824,6825,6827],{"name":6823,"type":4039},"HealthCheckTimeoutSeconds","Максимальний час очікування відповіді на health check запит. Якщо відповідь не надійшла протягом цього часу — перевірка вважається невдалою. Має бути меншим за ",[3397,6826,6819],{},".",[6829,6830,6831,6834,6835,6837],"caution",{},[3359,6832,6833],{},"Найпоширеніша помилка:"," реалізація ",[3397,6836,6796],{}," endpoint, що завжди повертає HTTP 200 незалежно від реального стану застосунку. Це призводить до ситуації, коли ALB вважає сервер здоровим, хоча він втратив з'єднання з базою даних або іншими критичними залежностями. ALB продовжує направляти реальний трафік на цей сервер, і користувачі отримують помилки 500.",[3626,6839],{},[3348,6841,6843],{"id":6842},"health-check-у-net-правильна-реалізація","Health Check у .NET — правильна реалізація",[3353,6845,6846,6847,6850],{},"ASP.NET Core надає вбудовану систему Health Checks через NuGet пакет ",[3397,6848,6849],{},"Microsoft.Extensions.Diagnostics.HealthChecks",". Ця система дозволяє декларативно описати перелік перевірок, що мають виконуватись, та агрегувати їхні результати в єдину відповідь.",[3353,6852,6853,6854,6856,6857,6860],{},"Правильний підхід полягає у тому, що ",[3397,6855,6796],{}," endpoint має перевіряти ",[3359,6858,6859],{},"реальну функціональну готовність"," застосунку до обробки запитів — не лише факт того, що процес запущений.",[3390,6862,6867],{"className":6863,"code":6864,"filename":6865,"language":6866,"meta":3395,"style":3395},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","using Microsoft.AspNetCore.Diagnostics.HealthChecks;\nusing Microsoft.Extensions.Diagnostics.HealthChecks;\nusing HealthChecks.UI.Client;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F Реєстрація Health Check сервісів\nbuilder.Services.AddHealthChecks()\n    \u002F\u002F Перевірка підключення до SQL Server\n    .AddSqlServer(\n        connectionString: builder.Configuration.GetConnectionString(\"DefaultConnection\")!,\n        name: \"sqlserver\",\n        failureStatus: HealthStatus.Unhealthy,\n        tags: [\"db\", \"sql\", \"infrastructure\"])\n\n    \u002F\u002F Перевірка Redis (якщо використовується для кешування або сесій)\n    .AddRedis(\n        redisConnectionString: builder.Configuration[\"Redis:ConnectionString\"]!,\n        name: \"redis\",\n        failureStatus: HealthStatus.Degraded,\n        tags: [\"cache\", \"infrastructure\"])\n\n    \u002F\u002F Кастомна перевірка: чи доступний зовнішній API\n    .AddCheck(\"external-api\", async () =>\n    {\n        \u002F\u002F Перевіряємо доступність залежного сервісу\n        using var http = new HttpClient();\n        try\n        {\n            var response = await http.GetAsync(\"https:\u002F\u002Fapi.payment-provider.com\u002Fping\");\n            return response.IsSuccessStatusCode\n                ? HealthCheckResult.Healthy(\"Payment API is reachable\")\n                : HealthCheckResult.Degraded(\"Payment API returned non-2xx status\");\n        }\n        catch\n        {\n            return HealthCheckResult.Unhealthy(\"Payment API is unreachable\");\n        }\n    }, tags: [\"external\"])\n\n    \u002F\u002F Базова самоперевірка: чи процес взагалі живий\n    .AddCheck(\"self\", () => HealthCheckResult.Healthy(\"Application process is running\"));\n\nvar app = builder.Build();\n\n\u002F\u002F Ендпоінт для ALB Health Check — мінімальна відповідь (200 або 503)\n\u002F\u002F ALB не потребує деталей, лише HTTP статус код\napp.MapHealthChecks(\"\u002Fhealth\", new HealthCheckOptions\n{\n    \u002F\u002F Включити ВСІ перевірки (без фільтрації за тегами)\n    Predicate = _ => true,\n\n    \u002F\u002F Кастомний writer: повертаємо мінімальний JSON\n    ResponseWriter = async (context, report) =>\n    {\n        context.Response.ContentType = \"application\u002Fjson\";\n        var result = System.Text.Json.JsonSerializer.Serialize(new\n        {\n            status = report.Status.ToString(),\n            timestamp = DateTime.UtcNow,\n            \u002F\u002F Короткий перелік перевірок без чутливих деталей\n            checks = report.Entries.Select(e => new\n            {\n                name = e.Key,\n                status = e.Value.Status.ToString()\n            })\n        });\n        await context.Response.WriteAsync(result);\n    }\n});\n\n\u002F\u002F Детальний ендпоінт для внутрішнього моніторингу\n\u002F\u002F НІКОЛИ не відкривайте цей ендпоінт публічно через ALB!\n\u002F\u002F Додайте окреме правило ALB: \u002Fhealth\u002Fdetail → Fixed Response 403\napp.MapHealthChecks(\"\u002Fhealth\u002Fdetail\", new HealthCheckOptions\n{\n    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse\n}).RequireAuthorization(); \u002F\u002F додаткова авторизація\n\napp.Run();\n","Program.cs","csharp",[3397,6868,6869,6897,6918,6937,6941,6969,6973,6978,6996,7001,7012,7039,7051,7068,7091,7095,7100,7109,7130,7141,7156,7171,7175,7180,7200,7205,7210,7232,7237,7241,7268,7280,7301,7319,7323,7328,7332,7350,7354,7369,7373,7378,7406,7410,7428,7432,7438,7444,7466,7471,7477,7495,7500,7506,7530,7535,7558,7597,7602,7625,7643,7649,7678,7684,7701,7726,7732,7738,7763,7768,7774,7779,7785,7791,7797,7817,7822,7837,7852,7857],{"__ignoreMap":3395},[3400,6870,6871,6875,6879,6881,6884,6886,6889,6891,6894],{"class":3402,"line":3403},[3400,6872,6874],{"class":6873},"s8xlr","using",[3400,6876,6878],{"class":6877},"sN1BT"," Microsoft",[3400,6880,6827],{"class":5649},[3400,6882,6883],{"class":6877},"AspNetCore",[3400,6885,6827],{"class":5649},[3400,6887,6888],{"class":6877},"Diagnostics",[3400,6890,6827],{"class":5649},[3400,6892,6893],{"class":6877},"HealthChecks",[3400,6895,6896],{"class":5649},";\n",[3400,6898,6899,6901,6903,6905,6908,6910,6912,6914,6916],{"class":3402,"line":3409},[3400,6900,6874],{"class":6873},[3400,6902,6878],{"class":6877},[3400,6904,6827],{"class":5649},[3400,6906,6907],{"class":6877},"Extensions",[3400,6909,6827],{"class":5649},[3400,6911,6888],{"class":6877},[3400,6913,6827],{"class":5649},[3400,6915,6893],{"class":6877},[3400,6917,6896],{"class":5649},[3400,6919,6920,6922,6925,6927,6930,6932,6935],{"class":3402,"line":3415},[3400,6921,6874],{"class":6873},[3400,6923,6924],{"class":6877}," HealthChecks",[3400,6926,6827],{"class":5649},[3400,6928,6929],{"class":6877},"UI",[3400,6931,6827],{"class":5649},[3400,6933,6934],{"class":6877},"Client",[3400,6936,6896],{"class":5649},[3400,6938,6939],{"class":3402,"line":3421},[3400,6940,3425],{"emptyLinePlaceholder":3424},[3400,6942,6943,6946,6949,6952,6955,6957,6960,6963,6966],{"class":3402,"line":3428},[3400,6944,6945],{"class":4772},"var",[3400,6947,6948],{"class":4779}," builder",[3400,6950,6951],{"class":5649}," = ",[3400,6953,6954],{"class":4779},"WebApplication",[3400,6956,6827],{"class":5649},[3400,6958,6959],{"class":4755},"CreateBuilder",[3400,6961,6962],{"class":5649},"(",[3400,6964,6965],{"class":4779},"args",[3400,6967,6968],{"class":5649},");\n",[3400,6970,6971],{"class":3402,"line":3434},[3400,6972,3425],{"emptyLinePlaceholder":3424},[3400,6974,6975],{"class":3402,"line":3440},[3400,6976,6977],{"class":4749},"\u002F\u002F Реєстрація Health Check сервісів\n",[3400,6979,6980,6983,6985,6988,6990,6993],{"class":3402,"line":3446},[3400,6981,6982],{"class":4779},"builder",[3400,6984,6827],{"class":5649},[3400,6986,6987],{"class":4779},"Services",[3400,6989,6827],{"class":5649},[3400,6991,6992],{"class":4755},"AddHealthChecks",[3400,6994,6995],{"class":5649},"()\n",[3400,6997,6998],{"class":3402,"line":3451},[3400,6999,7000],{"class":4749},"    \u002F\u002F Перевірка підключення до SQL Server\n",[3400,7002,7003,7006,7009],{"class":3402,"line":3457},[3400,7004,7005],{"class":5649},"    .",[3400,7007,7008],{"class":4755},"AddSqlServer",[3400,7010,7011],{"class":5649},"(\n",[3400,7013,7014,7017,7019,7021,7023,7026,7028,7031,7033,7036],{"class":3402,"line":3463},[3400,7015,7016],{"class":4779},"        connectionString",[3400,7018,5255],{"class":5649},[3400,7020,6982],{"class":4779},[3400,7022,6827],{"class":5649},[3400,7024,7025],{"class":4779},"Configuration",[3400,7027,6827],{"class":5649},[3400,7029,7030],{"class":4755},"GetConnectionString",[3400,7032,6962],{"class":5649},[3400,7034,7035],{"class":4759},"\"DefaultConnection\"",[3400,7037,7038],{"class":5649},")!,\n",[3400,7040,7041,7044,7046,7049],{"class":3402,"line":3469},[3400,7042,7043],{"class":4779},"        name",[3400,7045,5255],{"class":5649},[3400,7047,7048],{"class":4759},"\"sqlserver\"",[3400,7050,5743],{"class":5649},[3400,7052,7053,7056,7058,7061,7063,7066],{"class":3402,"line":3475},[3400,7054,7055],{"class":4779},"        failureStatus",[3400,7057,5255],{"class":5649},[3400,7059,7060],{"class":4779},"HealthStatus",[3400,7062,6827],{"class":5649},[3400,7064,7065],{"class":4779},"Unhealthy",[3400,7067,5743],{"class":5649},[3400,7069,7070,7073,7075,7078,7080,7083,7085,7088],{"class":3402,"line":3481},[3400,7071,7072],{"class":4779},"        tags",[3400,7074,5242],{"class":5649},[3400,7076,7077],{"class":4759},"\"db\"",[3400,7079,4031],{"class":5649},[3400,7081,7082],{"class":4759},"\"sql\"",[3400,7084,4031],{"class":5649},[3400,7086,7087],{"class":4759},"\"infrastructure\"",[3400,7089,7090],{"class":5649},"])\n",[3400,7092,7093],{"class":3402,"line":3487},[3400,7094,3425],{"emptyLinePlaceholder":3424},[3400,7096,7097],{"class":3402,"line":3493},[3400,7098,7099],{"class":4749},"    \u002F\u002F Перевірка Redis (якщо використовується для кешування або сесій)\n",[3400,7101,7102,7104,7107],{"class":3402,"line":3499},[3400,7103,7005],{"class":5649},[3400,7105,7106],{"class":4755},"AddRedis",[3400,7108,7011],{"class":5649},[3400,7110,7111,7114,7116,7118,7120,7122,7124,7127],{"class":3402,"line":3504},[3400,7112,7113],{"class":4779},"        redisConnectionString",[3400,7115,5255],{"class":5649},[3400,7117,6982],{"class":4779},[3400,7119,6827],{"class":5649},[3400,7121,7025],{"class":4779},[3400,7123,5350],{"class":5649},[3400,7125,7126],{"class":4759},"\"Redis:ConnectionString\"",[3400,7128,7129],{"class":5649},"]!,\n",[3400,7131,7132,7134,7136,7139],{"class":3402,"line":3510},[3400,7133,7043],{"class":4779},[3400,7135,5255],{"class":5649},[3400,7137,7138],{"class":4759},"\"redis\"",[3400,7140,5743],{"class":5649},[3400,7142,7143,7145,7147,7149,7151,7154],{"class":3402,"line":3516},[3400,7144,7055],{"class":4779},[3400,7146,5255],{"class":5649},[3400,7148,7060],{"class":4779},[3400,7150,6827],{"class":5649},[3400,7152,7153],{"class":4779},"Degraded",[3400,7155,5743],{"class":5649},[3400,7157,7158,7160,7162,7165,7167,7169],{"class":3402,"line":3522},[3400,7159,7072],{"class":4779},[3400,7161,5242],{"class":5649},[3400,7163,7164],{"class":4759},"\"cache\"",[3400,7166,4031],{"class":5649},[3400,7168,7087],{"class":4759},[3400,7170,7090],{"class":5649},[3400,7172,7173],{"class":3402,"line":3528},[3400,7174,3425],{"emptyLinePlaceholder":3424},[3400,7176,7177],{"class":3402,"line":3534},[3400,7178,7179],{"class":4749},"    \u002F\u002F Кастомна перевірка: чи доступний зовнішній API\n",[3400,7181,7182,7184,7187,7189,7192,7194,7197],{"class":3402,"line":3540},[3400,7183,7005],{"class":5649},[3400,7185,7186],{"class":4755},"AddCheck",[3400,7188,6962],{"class":5649},[3400,7190,7191],{"class":4759},"\"external-api\"",[3400,7193,4031],{"class":5649},[3400,7195,7196],{"class":4772},"async",[3400,7198,7199],{"class":5649}," () =>\n",[3400,7201,7202],{"class":3402,"line":3545},[3400,7203,7204],{"class":5649},"    {\n",[3400,7206,7207],{"class":3402,"line":3550},[3400,7208,7209],{"class":4749},"        \u002F\u002F Перевіряємо доступність залежного сервісу\n",[3400,7211,7212,7215,7218,7221,7223,7226,7229],{"class":3402,"line":3556},[3400,7213,7214],{"class":6873},"        using",[3400,7216,7217],{"class":4772}," var",[3400,7219,7220],{"class":4779}," http",[3400,7222,6951],{"class":5649},[3400,7224,7225],{"class":4772},"new",[3400,7227,7228],{"class":6877}," HttpClient",[3400,7230,7231],{"class":5649},"();\n",[3400,7233,7234],{"class":3402,"line":3562},[3400,7235,7236],{"class":6873},"        try\n",[3400,7238,7239],{"class":3402,"line":3568},[3400,7240,5720],{"class":5649},[3400,7242,7243,7246,7249,7251,7254,7256,7258,7261,7263,7266],{"class":3402,"line":3574},[3400,7244,7245],{"class":4772},"            var",[3400,7247,7248],{"class":4779}," response",[3400,7250,6951],{"class":5649},[3400,7252,7253],{"class":4772},"await",[3400,7255,7220],{"class":4779},[3400,7257,6827],{"class":5649},[3400,7259,7260],{"class":4755},"GetAsync",[3400,7262,6962],{"class":5649},[3400,7264,7265],{"class":4759},"\"https:\u002F\u002Fapi.payment-provider.com\u002Fping\"",[3400,7267,6968],{"class":5649},[3400,7269,7270,7273,7275,7277],{"class":3402,"line":3580},[3400,7271,7272],{"class":6873},"            return",[3400,7274,7248],{"class":4779},[3400,7276,6827],{"class":5649},[3400,7278,7279],{"class":4779},"IsSuccessStatusCode\n",[3400,7281,7282,7285,7288,7290,7293,7295,7298],{"class":3402,"line":3586},[3400,7283,7284],{"class":5649},"                ? ",[3400,7286,7287],{"class":4779},"HealthCheckResult",[3400,7289,6827],{"class":5649},[3400,7291,7292],{"class":4755},"Healthy",[3400,7294,6962],{"class":5649},[3400,7296,7297],{"class":4759},"\"Payment API is reachable\"",[3400,7299,7300],{"class":5649},")\n",[3400,7302,7303,7306,7308,7310,7312,7314,7317],{"class":3402,"line":3591},[3400,7304,7305],{"class":5649},"                : ",[3400,7307,7287],{"class":4779},[3400,7309,6827],{"class":5649},[3400,7311,7153],{"class":4755},[3400,7313,6962],{"class":5649},[3400,7315,7316],{"class":4759},"\"Payment API returned non-2xx status\"",[3400,7318,6968],{"class":5649},[3400,7320,7321],{"class":3402,"line":3597},[3400,7322,5846],{"class":5649},[3400,7324,7325],{"class":3402,"line":3603},[3400,7326,7327],{"class":6873},"        catch\n",[3400,7329,7330],{"class":3402,"line":3609},[3400,7331,5720],{"class":5649},[3400,7333,7334,7336,7339,7341,7343,7345,7348],{"class":3402,"line":3615},[3400,7335,7272],{"class":6873},[3400,7337,7338],{"class":4779}," HealthCheckResult",[3400,7340,6827],{"class":5649},[3400,7342,7065],{"class":4755},[3400,7344,6962],{"class":5649},[3400,7346,7347],{"class":4759},"\"Payment API is unreachable\"",[3400,7349,6968],{"class":5649},[3400,7351,7352],{"class":3402,"line":3621},[3400,7353,5846],{"class":5649},[3400,7355,7356,7359,7362,7364,7367],{"class":3402,"line":5589},[3400,7357,7358],{"class":5649},"    }, ",[3400,7360,7361],{"class":4779},"tags",[3400,7363,5242],{"class":5649},[3400,7365,7366],{"class":4759},"\"external\"",[3400,7368,7090],{"class":5649},[3400,7370,7371],{"class":3402,"line":5594},[3400,7372,3425],{"emptyLinePlaceholder":3424},[3400,7374,7375],{"class":3402,"line":5600},[3400,7376,7377],{"class":4749},"    \u002F\u002F Базова самоперевірка: чи процес взагалі живий\n",[3400,7379,7380,7382,7384,7386,7389,7392,7394,7396,7398,7400,7403],{"class":3402,"line":5606},[3400,7381,7005],{"class":5649},[3400,7383,7186],{"class":4755},[3400,7385,6962],{"class":5649},[3400,7387,7388],{"class":4759},"\"self\"",[3400,7390,7391],{"class":5649},", () => ",[3400,7393,7287],{"class":4779},[3400,7395,6827],{"class":5649},[3400,7397,7292],{"class":4755},[3400,7399,6962],{"class":5649},[3400,7401,7402],{"class":4759},"\"Application process is running\"",[3400,7404,7405],{"class":5649},"));\n",[3400,7407,7408],{"class":3402,"line":5612},[3400,7409,3425],{"emptyLinePlaceholder":3424},[3400,7411,7412,7414,7417,7419,7421,7423,7426],{"class":3402,"line":5617},[3400,7413,6945],{"class":4772},[3400,7415,7416],{"class":4779}," app",[3400,7418,6951],{"class":5649},[3400,7420,6982],{"class":4779},[3400,7422,6827],{"class":5649},[3400,7424,7425],{"class":4755},"Build",[3400,7427,7231],{"class":5649},[3400,7429,7430],{"class":3402,"line":5622},[3400,7431,3425],{"emptyLinePlaceholder":3424},[3400,7433,7435],{"class":3402,"line":7434},46,[3400,7436,7437],{"class":4749},"\u002F\u002F Ендпоінт для ALB Health Check — мінімальна відповідь (200 або 503)\n",[3400,7439,7441],{"class":3402,"line":7440},47,[3400,7442,7443],{"class":4749},"\u002F\u002F ALB не потребує деталей, лише HTTP статус код\n",[3400,7445,7447,7450,7452,7455,7457,7459,7461,7463],{"class":3402,"line":7446},48,[3400,7448,7449],{"class":4779},"app",[3400,7451,6827],{"class":5649},[3400,7453,7454],{"class":4755},"MapHealthChecks",[3400,7456,6962],{"class":5649},[3400,7458,6693],{"class":4759},[3400,7460,4031],{"class":5649},[3400,7462,7225],{"class":4772},[3400,7464,7465],{"class":6877}," HealthCheckOptions\n",[3400,7467,7469],{"class":3402,"line":7468},49,[3400,7470,6670],{"class":5649},[3400,7472,7474],{"class":3402,"line":7473},50,[3400,7475,7476],{"class":4749},"    \u002F\u002F Включити ВСІ перевірки (без фільтрації за тегами)\n",[3400,7478,7480,7483,7485,7488,7491,7493],{"class":3402,"line":7479},51,[3400,7481,7482],{"class":4779},"    Predicate",[3400,7484,6951],{"class":5649},[3400,7486,7487],{"class":4779},"_",[3400,7489,7490],{"class":5649}," => ",[3400,7492,4026],{"class":4772},[3400,7494,5743],{"class":5649},[3400,7496,7498],{"class":3402,"line":7497},52,[3400,7499,3425],{"emptyLinePlaceholder":3424},[3400,7501,7503],{"class":3402,"line":7502},53,[3400,7504,7505],{"class":4749},"    \u002F\u002F Кастомний writer: повертаємо мінімальний JSON\n",[3400,7507,7509,7512,7514,7516,7519,7522,7524,7527],{"class":3402,"line":7508},54,[3400,7510,7511],{"class":4779},"    ResponseWriter",[3400,7513,6951],{"class":5649},[3400,7515,7196],{"class":4772},[3400,7517,7518],{"class":5649}," (",[3400,7520,7521],{"class":4779},"context",[3400,7523,4031],{"class":5649},[3400,7525,7526],{"class":4779},"report",[3400,7528,7529],{"class":5649},") =>\n",[3400,7531,7533],{"class":3402,"line":7532},55,[3400,7534,7204],{"class":5649},[3400,7536,7538,7541,7543,7546,7548,7551,7553,7556],{"class":3402,"line":7537},56,[3400,7539,7540],{"class":4779},"        context",[3400,7542,6827],{"class":5649},[3400,7544,7545],{"class":4779},"Response",[3400,7547,6827],{"class":5649},[3400,7549,7550],{"class":4779},"ContentType",[3400,7552,6951],{"class":5649},[3400,7554,7555],{"class":4759},"\"application\u002Fjson\"",[3400,7557,6896],{"class":5649},[3400,7559,7561,7564,7567,7569,7572,7574,7577,7579,7582,7584,7587,7589,7592,7594],{"class":3402,"line":7560},57,[3400,7562,7563],{"class":4772},"        var",[3400,7565,7566],{"class":4779}," result",[3400,7568,6951],{"class":5649},[3400,7570,7571],{"class":4779},"System",[3400,7573,6827],{"class":5649},[3400,7575,7576],{"class":4779},"Text",[3400,7578,6827],{"class":5649},[3400,7580,7581],{"class":4779},"Json",[3400,7583,6827],{"class":5649},[3400,7585,7586],{"class":4779},"JsonSerializer",[3400,7588,6827],{"class":5649},[3400,7590,7591],{"class":4755},"Serialize",[3400,7593,6962],{"class":5649},[3400,7595,7596],{"class":4772},"new\n",[3400,7598,7600],{"class":3402,"line":7599},58,[3400,7601,5720],{"class":5649},[3400,7603,7605,7608,7610,7612,7614,7617,7619,7622],{"class":3402,"line":7604},59,[3400,7606,7607],{"class":4779},"            status",[3400,7609,6951],{"class":5649},[3400,7611,7526],{"class":4779},[3400,7613,6827],{"class":5649},[3400,7615,7616],{"class":4779},"Status",[3400,7618,6827],{"class":5649},[3400,7620,7621],{"class":4755},"ToString",[3400,7623,7624],{"class":5649},"(),\n",[3400,7626,7628,7631,7633,7636,7638,7641],{"class":3402,"line":7627},60,[3400,7629,7630],{"class":4779},"            timestamp",[3400,7632,6951],{"class":5649},[3400,7634,7635],{"class":4779},"DateTime",[3400,7637,6827],{"class":5649},[3400,7639,7640],{"class":4779},"UtcNow",[3400,7642,5743],{"class":5649},[3400,7644,7646],{"class":3402,"line":7645},61,[3400,7647,7648],{"class":4749},"            \u002F\u002F Короткий перелік перевірок без чутливих деталей\n",[3400,7650,7652,7655,7657,7659,7661,7664,7666,7669,7671,7674,7676],{"class":3402,"line":7651},62,[3400,7653,7654],{"class":4779},"            checks",[3400,7656,6951],{"class":5649},[3400,7658,7526],{"class":4779},[3400,7660,6827],{"class":5649},[3400,7662,7663],{"class":4779},"Entries",[3400,7665,6827],{"class":5649},[3400,7667,7668],{"class":4755},"Select",[3400,7670,6962],{"class":5649},[3400,7672,7673],{"class":4779},"e",[3400,7675,7490],{"class":5649},[3400,7677,7596],{"class":4772},[3400,7679,7681],{"class":3402,"line":7680},63,[3400,7682,7683],{"class":5649},"            {\n",[3400,7685,7687,7690,7692,7694,7696,7699],{"class":3402,"line":7686},64,[3400,7688,7689],{"class":4779},"                name",[3400,7691,6951],{"class":5649},[3400,7693,7673],{"class":4779},[3400,7695,6827],{"class":5649},[3400,7697,7698],{"class":4779},"Key",[3400,7700,5743],{"class":5649},[3400,7702,7704,7707,7709,7711,7713,7716,7718,7720,7722,7724],{"class":3402,"line":7703},65,[3400,7705,7706],{"class":4779},"                status",[3400,7708,6951],{"class":5649},[3400,7710,7673],{"class":4779},[3400,7712,6827],{"class":5649},[3400,7714,7715],{"class":4779},"Value",[3400,7717,6827],{"class":5649},[3400,7719,7616],{"class":4779},[3400,7721,6827],{"class":5649},[3400,7723,7621],{"class":4755},[3400,7725,6995],{"class":5649},[3400,7727,7729],{"class":3402,"line":7728},66,[3400,7730,7731],{"class":5649},"            })\n",[3400,7733,7735],{"class":3402,"line":7734},67,[3400,7736,7737],{"class":5649},"        });\n",[3400,7739,7741,7744,7747,7749,7751,7753,7756,7758,7761],{"class":3402,"line":7740},68,[3400,7742,7743],{"class":4772},"        await",[3400,7745,7746],{"class":4779}," context",[3400,7748,6827],{"class":5649},[3400,7750,7545],{"class":4779},[3400,7752,6827],{"class":5649},[3400,7754,7755],{"class":4755},"WriteAsync",[3400,7757,6962],{"class":5649},[3400,7759,7760],{"class":4779},"result",[3400,7762,6968],{"class":5649},[3400,7764,7766],{"class":3402,"line":7765},69,[3400,7767,3537],{"class":5649},[3400,7769,7771],{"class":3402,"line":7770},70,[3400,7772,7773],{"class":5649},"});\n",[3400,7775,7777],{"class":3402,"line":7776},71,[3400,7778,3425],{"emptyLinePlaceholder":3424},[3400,7780,7782],{"class":3402,"line":7781},72,[3400,7783,7784],{"class":4749},"\u002F\u002F Детальний ендпоінт для внутрішнього моніторингу\n",[3400,7786,7788],{"class":3402,"line":7787},73,[3400,7789,7790],{"class":4749},"\u002F\u002F НІКОЛИ не відкривайте цей ендпоінт публічно через ALB!\n",[3400,7792,7794],{"class":3402,"line":7793},74,[3400,7795,7796],{"class":4749},"\u002F\u002F Додайте окреме правило ALB: \u002Fhealth\u002Fdetail → Fixed Response 403\n",[3400,7798,7800,7802,7804,7806,7808,7811,7813,7815],{"class":3402,"line":7799},75,[3400,7801,7449],{"class":4779},[3400,7803,6827],{"class":5649},[3400,7805,7454],{"class":4755},[3400,7807,6962],{"class":5649},[3400,7809,7810],{"class":4759},"\"\u002Fhealth\u002Fdetail\"",[3400,7812,4031],{"class":5649},[3400,7814,7225],{"class":4772},[3400,7816,7465],{"class":6877},[3400,7818,7820],{"class":3402,"line":7819},76,[3400,7821,6670],{"class":5649},[3400,7823,7825,7827,7829,7832,7834],{"class":3402,"line":7824},77,[3400,7826,7511],{"class":4779},[3400,7828,6951],{"class":5649},[3400,7830,7831],{"class":4779},"UIResponseWriter",[3400,7833,6827],{"class":5649},[3400,7835,7836],{"class":4779},"WriteHealthCheckUIResponse\n",[3400,7838,7840,7843,7846,7849],{"class":3402,"line":7839},78,[3400,7841,7842],{"class":5649},"}).",[3400,7844,7845],{"class":4755},"RequireAuthorization",[3400,7847,7848],{"class":5649},"(); ",[3400,7850,7851],{"class":4749},"\u002F\u002F додаткова авторизація\n",[3400,7853,7855],{"class":3402,"line":7854},79,[3400,7856,3425],{"emptyLinePlaceholder":3424},[3400,7858,7860,7862,7864,7867],{"class":3402,"line":7859},80,[3400,7861,7449],{"class":4779},[3400,7863,6827],{"class":5649},[3400,7865,7866],{"class":4755},"Run",[3400,7868,7231],{"class":5649},[6209,7870,7871,5214,7874,7876,7877,7880],{},[3359,7872,7873],{},"Розподіл відповідальності між ендпоінтами:",[3397,7875,6796],{}," є спрощеним ендпоінтом виключно для ALB — він має бути максимально швидким та легким. ",[3397,7878,7879],{},"\u002Fhealth\u002Fdetail"," надає повну діагностичну інформацію для команди DevOps та систем моніторингу (Grafana, Datadog), але має бути захищений авторизацією або доступний лише у внутрішній мережі.",[3626,7882],{},[3348,7884,7886],{"id":7885},"auto-scaling-groups-asg-концепція-та-архітектура","Auto Scaling Groups (ASG): концепція та архітектура",[3353,7888,7889,7892],{},[3359,7890,7891],{},"Auto Scaling Group (ASG)"," — це сервіс AWS, що управляє lifecycle-ом колекції EC2 instances як єдиного цілого. ASG безперервно порівнює поточний стан кластера instances з бажаним станом і виконує необхідні коригуючі дії: запускає нові instances або завершує існуючі.",[3824,7894,7896],{"id":7895},"три-фундаментальні-параметри-asg","Три фундаментальні параметри ASG",[3353,7898,7899],{},"Вся логіка роботи ASG будується навколо трьох числових параметрів, що визначають границі масштабування:",[3387,7901,7902],{},[3390,7903,7905],{"className":3392,"code":7904,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nskinparam rectangle {\n    BackgroundColor #f0fdf4\n    BorderColor #16a34a\n}\n\nrectangle \"Auto Scaling Group: dotnet-api-asg\" as ASG {\n\n    rectangle \"Maximum Capacity = 10\\n(жорстка верхня межа — захист від надмірних витрат)\" as MAX #fef3c7\n\n    rectangle \"Desired Capacity = 4\\n(поточна цільова кількість — змінюється Scaling Policies)\" as DES #dbeafe\n\n    rectangle \"Minimum Capacity = 2\\n(жорстка нижня межа — гарантія High Availability)\" as MIN #dcfce7\n\n    MAX -[hidden]down-> DES\n    DES -[hidden]down-> MIN\n}\n\nnote right of MAX\n    ASG ніколи не запустить\n    більше ніж max instances,\n    навіть якщо навантаження\n    продовжує зростати\nend note\n\nnote right of DES\n    ASG постійно прагне\n    підтримувати саме\n    цю кількість healthy instances\nend note\n\nnote right of MIN\n    ASG ніколи не зменшить\n    кількість instances нижче min,\n    навіть якщо навантаження = 0\nend note\n\n@enduml\n",[3397,7906,7907,7911,7915,7919,7923,7928,7933,7938,7942,7946,7951,7955,7960,7964,7969,7973,7978,7982,7987,7992,7996,8000,8005,8010,8015,8020,8025,8029,8033,8038,8043,8048,8053,8057,8061,8066,8071,8076,8081,8085,8089],{"__ignoreMap":3395},[3400,7908,7909],{"class":3402,"line":3403},[3400,7910,3406],{},[3400,7912,7913],{"class":3402,"line":3409},[3400,7914,3412],{},[3400,7916,7917],{"class":3402,"line":3415},[3400,7918,3418],{},[3400,7920,7921],{"class":3402,"line":3421},[3400,7922,3425],{"emptyLinePlaceholder":3424},[3400,7924,7925],{"class":3402,"line":3428},[3400,7926,7927],{},"skinparam rectangle {\n",[3400,7929,7930],{"class":3402,"line":3434},[3400,7931,7932],{},"    BackgroundColor #f0fdf4\n",[3400,7934,7935],{"class":3402,"line":3440},[3400,7936,7937],{},"    BorderColor #16a34a\n",[3400,7939,7940],{"class":3402,"line":3446},[3400,7941,3496],{},[3400,7943,7944],{"class":3402,"line":3451},[3400,7945,3425],{"emptyLinePlaceholder":3424},[3400,7947,7948],{"class":3402,"line":3457},[3400,7949,7950],{},"rectangle \"Auto Scaling Group: dotnet-api-asg\" as ASG {\n",[3400,7952,7953],{"class":3402,"line":3463},[3400,7954,3425],{"emptyLinePlaceholder":3424},[3400,7956,7957],{"class":3402,"line":3469},[3400,7958,7959],{},"    rectangle \"Maximum Capacity = 10\\n(жорстка верхня межа — захист від надмірних витрат)\" as MAX #fef3c7\n",[3400,7961,7962],{"class":3402,"line":3475},[3400,7963,3425],{"emptyLinePlaceholder":3424},[3400,7965,7966],{"class":3402,"line":3481},[3400,7967,7968],{},"    rectangle \"Desired Capacity = 4\\n(поточна цільова кількість — змінюється Scaling Policies)\" as DES #dbeafe\n",[3400,7970,7971],{"class":3402,"line":3487},[3400,7972,3425],{"emptyLinePlaceholder":3424},[3400,7974,7975],{"class":3402,"line":3493},[3400,7976,7977],{},"    rectangle \"Minimum Capacity = 2\\n(жорстка нижня межа — гарантія High Availability)\" as MIN #dcfce7\n",[3400,7979,7980],{"class":3402,"line":3499},[3400,7981,3425],{"emptyLinePlaceholder":3424},[3400,7983,7984],{"class":3402,"line":3504},[3400,7985,7986],{},"    MAX -[hidden]down-> DES\n",[3400,7988,7989],{"class":3402,"line":3510},[3400,7990,7991],{},"    DES -[hidden]down-> MIN\n",[3400,7993,7994],{"class":3402,"line":3516},[3400,7995,3496],{},[3400,7997,7998],{"class":3402,"line":3522},[3400,7999,3425],{"emptyLinePlaceholder":3424},[3400,8001,8002],{"class":3402,"line":3528},[3400,8003,8004],{},"note right of MAX\n",[3400,8006,8007],{"class":3402,"line":3534},[3400,8008,8009],{},"    ASG ніколи не запустить\n",[3400,8011,8012],{"class":3402,"line":3540},[3400,8013,8014],{},"    більше ніж max instances,\n",[3400,8016,8017],{"class":3402,"line":3545},[3400,8018,8019],{},"    навіть якщо навантаження\n",[3400,8021,8022],{"class":3402,"line":3550},[3400,8023,8024],{},"    продовжує зростати\n",[3400,8026,8027],{"class":3402,"line":3556},[3400,8028,3618],{},[3400,8030,8031],{"class":3402,"line":3562},[3400,8032,3425],{"emptyLinePlaceholder":3424},[3400,8034,8035],{"class":3402,"line":3568},[3400,8036,8037],{},"note right of DES\n",[3400,8039,8040],{"class":3402,"line":3574},[3400,8041,8042],{},"    ASG постійно прагне\n",[3400,8044,8045],{"class":3402,"line":3580},[3400,8046,8047],{},"    підтримувати саме\n",[3400,8049,8050],{"class":3402,"line":3586},[3400,8051,8052],{},"    цю кількість healthy instances\n",[3400,8054,8055],{"class":3402,"line":3591},[3400,8056,3618],{},[3400,8058,8059],{"class":3402,"line":3597},[3400,8060,3425],{"emptyLinePlaceholder":3424},[3400,8062,8063],{"class":3402,"line":3603},[3400,8064,8065],{},"note right of MIN\n",[3400,8067,8068],{"class":3402,"line":3609},[3400,8069,8070],{},"    ASG ніколи не зменшить\n",[3400,8072,8073],{"class":3402,"line":3615},[3400,8074,8075],{},"    кількість instances нижче min,\n",[3400,8077,8078],{"class":3402,"line":3621},[3400,8079,8080],{},"    навіть якщо навантаження = 0\n",[3400,8082,8083],{"class":3402,"line":5589},[3400,8084,3618],{},[3400,8086,8087],{"class":3402,"line":5594},[3400,8088,3425],{"emptyLinePlaceholder":3424},[3400,8090,8091],{"class":3402,"line":5600},[3400,8092,3624],{},[4019,8094,8095,8106,8110,8117],{},[4022,8096,8098,8099,8101,8102,8105],{"name":8097,"type":4039,"required":4026},"MinSize","Мінімальна кількість instances, що завжди залишаються запущеними, незалежно від рівня навантаження. Значення ",[3397,8100,4811],{}," є стандартом для High Availability: якщо один instance відмовить, другий продовжує обробляти запити. Значення ",[3397,8103,8104],{},"0"," допустиме для batch-обробки або розробки, але неприйнятне для production API.",[4022,8107,8109],{"name":8108,"type":4039,"required":4026},"MaxSize","Верхня межа кількості instances. Захищає від неконтрольованого зростання витрат у разі аномальної поведінки (DDoS-атака, нескінченний цикл, помилка в Scaling Policy). Завжди встановлюйте це значення усвідомлено, розуміючи максимально допустимий рахунок AWS.",[4022,8111,8113,8114,8116],{"name":8112,"type":4039,"required":4026},"DesiredCapacity","Поточна цільова кількість instances. ASG безперервно контролює, щоб кількість healthy instances дорівнювала цьому значенню. Scaling Policies змінюють ",[3397,8115,8112],{},", а ASG відповідно запускає або завершує instances.",[4022,8118,8120],{"name":8119,"type":4039},"HealthCheckGracePeriod","Час у секундах після запуску нового instance, протягом якого ASG ігнорує результати health check. Необхідний для того, щоб .NET застосунок встиг запуститися, підключитися до бази даних та бути готовим до обробки запитів до першої перевірки. Рекомендоване значення: 120–300 секунд.",[3824,8122,8124],{"id":8123},"launch-templates-специфікація-нового-instance","Launch Templates — специфікація нового instance",[3353,8126,8127,8130],{},[3359,8128,8129],{},"Launch Template"," — це версіонований документ-специфікація, що точно описує конфігурацію EC2 instance, який ASG створить при горизонтальному масштабуванні. Коли ASG вирішує додати новий instance (Scale Out), він звертається до Launch Template і створює EC2 instance відповідно до зазначених параметрів.",[3387,8132,8133],{},[3390,8134,8136],{"className":3392,"code":8135,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Launch Template: dotnet-api-lt\" as LT #fef9c3 {\n    artifact \"AMI ID\\n(Custom AMI з .NET 10\\nта вашим API)\" as AMI\n    artifact \"Instance Type\\n(t3.micro)\" as IT\n    artifact \"Key Pair\\n(ec2-lab-key.pem)\" as KP\n    artifact \"Security Group\\n(ec2-asg-sg)\" as SG\n    artifact \"User Data Script\\n(запуск systemd сервісу)\" as UD\n    artifact \"IAM Instance Profile\\n(дозволи для EC2)\" as IAM\n}\n\nrectangle \"Auto Scaling Group\" as ASG #d1fae5\n\nASG --> LT : \"Потрібен новий instance!\\n(DesiredCapacity зросла)\"\nLT --> AMI : читає\nLT --> IT : читає\nLT --> UD : читає\n\nnode \"Новий EC2 Instance\\n(точна копія специфікації)\" as EC #bbf7d0\n\nLT --> EC : \"Створює instance\\nза цим шаблоном\"\n\nnote bottom of EC\n    Кожен новий instance\n    запускається з ідентичною\n    конфігурацією автоматично,\n    без ручного втручання\nend note\n\n@enduml\n",[3397,8137,8138,8142,8146,8150,8154,8159,8164,8169,8174,8179,8184,8189,8193,8197,8202,8206,8211,8216,8221,8226,8230,8235,8239,8244,8248,8253,8258,8263,8268,8273,8277,8281],{"__ignoreMap":3395},[3400,8139,8140],{"class":3402,"line":3403},[3400,8141,3406],{},[3400,8143,8144],{"class":3402,"line":3409},[3400,8145,3412],{},[3400,8147,8148],{"class":3402,"line":3415},[3400,8149,3418],{},[3400,8151,8152],{"class":3402,"line":3421},[3400,8153,3425],{"emptyLinePlaceholder":3424},[3400,8155,8156],{"class":3402,"line":3428},[3400,8157,8158],{},"package \"Launch Template: dotnet-api-lt\" as LT #fef9c3 {\n",[3400,8160,8161],{"class":3402,"line":3434},[3400,8162,8163],{},"    artifact \"AMI ID\\n(Custom AMI з .NET 10\\nта вашим API)\" as AMI\n",[3400,8165,8166],{"class":3402,"line":3440},[3400,8167,8168],{},"    artifact \"Instance Type\\n(t3.micro)\" as IT\n",[3400,8170,8171],{"class":3402,"line":3446},[3400,8172,8173],{},"    artifact \"Key Pair\\n(ec2-lab-key.pem)\" as KP\n",[3400,8175,8176],{"class":3402,"line":3451},[3400,8177,8178],{},"    artifact \"Security Group\\n(ec2-asg-sg)\" as SG\n",[3400,8180,8181],{"class":3402,"line":3457},[3400,8182,8183],{},"    artifact \"User Data Script\\n(запуск systemd сервісу)\" as UD\n",[3400,8185,8186],{"class":3402,"line":3463},[3400,8187,8188],{},"    artifact \"IAM Instance Profile\\n(дозволи для EC2)\" as IAM\n",[3400,8190,8191],{"class":3402,"line":3469},[3400,8192,3496],{},[3400,8194,8195],{"class":3402,"line":3475},[3400,8196,3425],{"emptyLinePlaceholder":3424},[3400,8198,8199],{"class":3402,"line":3481},[3400,8200,8201],{},"rectangle \"Auto Scaling Group\" as ASG #d1fae5\n",[3400,8203,8204],{"class":3402,"line":3487},[3400,8205,3425],{"emptyLinePlaceholder":3424},[3400,8207,8208],{"class":3402,"line":3493},[3400,8209,8210],{},"ASG --> LT : \"Потрібен новий instance!\\n(DesiredCapacity зросла)\"\n",[3400,8212,8213],{"class":3402,"line":3499},[3400,8214,8215],{},"LT --> AMI : читає\n",[3400,8217,8218],{"class":3402,"line":3504},[3400,8219,8220],{},"LT --> IT : читає\n",[3400,8222,8223],{"class":3402,"line":3510},[3400,8224,8225],{},"LT --> UD : читає\n",[3400,8227,8228],{"class":3402,"line":3516},[3400,8229,3425],{"emptyLinePlaceholder":3424},[3400,8231,8232],{"class":3402,"line":3522},[3400,8233,8234],{},"node \"Новий EC2 Instance\\n(точна копія специфікації)\" as EC #bbf7d0\n",[3400,8236,8237],{"class":3402,"line":3528},[3400,8238,3425],{"emptyLinePlaceholder":3424},[3400,8240,8241],{"class":3402,"line":3534},[3400,8242,8243],{},"LT --> EC : \"Створює instance\\nза цим шаблоном\"\n",[3400,8245,8246],{"class":3402,"line":3540},[3400,8247,3425],{"emptyLinePlaceholder":3424},[3400,8249,8250],{"class":3402,"line":3545},[3400,8251,8252],{},"note bottom of EC\n",[3400,8254,8255],{"class":3402,"line":3550},[3400,8256,8257],{},"    Кожен новий instance\n",[3400,8259,8260],{"class":3402,"line":3556},[3400,8261,8262],{},"    запускається з ідентичною\n",[3400,8264,8265],{"class":3402,"line":3562},[3400,8266,8267],{},"    конфігурацією автоматично,\n",[3400,8269,8270],{"class":3402,"line":3568},[3400,8271,8272],{},"    без ручного втручання\n",[3400,8274,8275],{"class":3402,"line":3574},[3400,8276,3618],{},[3400,8278,8279],{"class":3402,"line":3580},[3400,8280,3425],{"emptyLinePlaceholder":3424},[3400,8282,8283],{"class":3402,"line":3586},[3400,8284,3624],{},[8286,8287,8288,8291,8292],"note",{},[3359,8289,8290],{},"Launch Templates vs Launch Configurations."," До 2017 року для налаштування ASG використовувались Launch Configurations. Вони застаріли (deprecated) і не підтримують ряд сучасних функцій. Launch Templates є їх повноцінною заміною: вони підтримують версіонування (v1, v2, v3), змішані стратегії використання Spot та On-Demand instances в одному ASG, та всі нові можливості EC2. ",[3359,8293,8294],{},"Завжди використовуйте Launch Templates.",[3626,8296],{},[3348,8298,8300],{"id":8299},"scaling-policies-стратегії-автоматичного-масштабування","Scaling Policies — стратегії автоматичного масштабування",[3353,8302,8303],{},"AWS Auto Scaling підтримує три принципово різні стратегії масштабування, кожна з яких оптимальна для певного класу навантажень.",[3387,8305,8306],{},[3390,8307,8309],{"className":3392,"code":8308,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Scaling Policies\" {\n\n    rectangle \"Target Tracking\\nПідтримуй метрику на цільовому рівні\\n(найпростіший, рекомендований)\" as TT #d1fae5\n\n    rectangle \"Step Scaling\\nДодавай різну кількість instances\\nзалежно від відхилення\" as SS #dbeafe\n\n    rectangle \"Scheduled Scaling\\nМасштабуй за розкладом\\n(передбачуване навантаження)\" as SCHED #fef3c7\n\n}\n\nnote right of TT\n    \"Підтримуй CPU = 70%\"\n    ASG сам розраховує\n    скільки instances потрібно\nend note\n\nnote right of SS\n    CPU 70-80%: +1 instance\n    CPU 80-90%: +2 instances\n    CPU > 90%: +4 instances\nend note\n\nnote right of SCHED\n    Пн-Пт 08:00 UTC: desired=5\n    Пн-Пт 20:00 UTC: desired=2\nend note\n\n@enduml\n",[3397,8310,8311,8315,8319,8323,8327,8332,8336,8341,8345,8350,8354,8359,8363,8367,8371,8376,8381,8386,8391,8395,8399,8404,8409,8414,8419,8423,8427,8432,8437,8442,8446,8450],{"__ignoreMap":3395},[3400,8312,8313],{"class":3402,"line":3403},[3400,8314,3406],{},[3400,8316,8317],{"class":3402,"line":3409},[3400,8318,3412],{},[3400,8320,8321],{"class":3402,"line":3415},[3400,8322,3418],{},[3400,8324,8325],{"class":3402,"line":3421},[3400,8326,3425],{"emptyLinePlaceholder":3424},[3400,8328,8329],{"class":3402,"line":3428},[3400,8330,8331],{},"package \"Scaling Policies\" {\n",[3400,8333,8334],{"class":3402,"line":3434},[3400,8335,3425],{"emptyLinePlaceholder":3424},[3400,8337,8338],{"class":3402,"line":3440},[3400,8339,8340],{},"    rectangle \"Target Tracking\\nПідтримуй метрику на цільовому рівні\\n(найпростіший, рекомендований)\" as TT #d1fae5\n",[3400,8342,8343],{"class":3402,"line":3446},[3400,8344,3425],{"emptyLinePlaceholder":3424},[3400,8346,8347],{"class":3402,"line":3451},[3400,8348,8349],{},"    rectangle \"Step Scaling\\nДодавай різну кількість instances\\nзалежно від відхилення\" as SS #dbeafe\n",[3400,8351,8352],{"class":3402,"line":3457},[3400,8353,3425],{"emptyLinePlaceholder":3424},[3400,8355,8356],{"class":3402,"line":3463},[3400,8357,8358],{},"    rectangle \"Scheduled Scaling\\nМасштабуй за розкладом\\n(передбачуване навантаження)\" as SCHED #fef3c7\n",[3400,8360,8361],{"class":3402,"line":3469},[3400,8362,3425],{"emptyLinePlaceholder":3424},[3400,8364,8365],{"class":3402,"line":3475},[3400,8366,3496],{},[3400,8368,8369],{"class":3402,"line":3481},[3400,8370,3425],{"emptyLinePlaceholder":3424},[3400,8372,8373],{"class":3402,"line":3487},[3400,8374,8375],{},"note right of TT\n",[3400,8377,8378],{"class":3402,"line":3493},[3400,8379,8380],{},"    \"Підтримуй CPU = 70%\"\n",[3400,8382,8383],{"class":3402,"line":3499},[3400,8384,8385],{},"    ASG сам розраховує\n",[3400,8387,8388],{"class":3402,"line":3504},[3400,8389,8390],{},"    скільки instances потрібно\n",[3400,8392,8393],{"class":3402,"line":3510},[3400,8394,3618],{},[3400,8396,8397],{"class":3402,"line":3516},[3400,8398,3425],{"emptyLinePlaceholder":3424},[3400,8400,8401],{"class":3402,"line":3522},[3400,8402,8403],{},"note right of SS\n",[3400,8405,8406],{"class":3402,"line":3528},[3400,8407,8408],{},"    CPU 70-80%: +1 instance\n",[3400,8410,8411],{"class":3402,"line":3534},[3400,8412,8413],{},"    CPU 80-90%: +2 instances\n",[3400,8415,8416],{"class":3402,"line":3540},[3400,8417,8418],{},"    CPU > 90%: +4 instances\n",[3400,8420,8421],{"class":3402,"line":3545},[3400,8422,3618],{},[3400,8424,8425],{"class":3402,"line":3550},[3400,8426,3425],{"emptyLinePlaceholder":3424},[3400,8428,8429],{"class":3402,"line":3556},[3400,8430,8431],{},"note right of SCHED\n",[3400,8433,8434],{"class":3402,"line":3562},[3400,8435,8436],{},"    Пн-Пт 08:00 UTC: desired=5\n",[3400,8438,8439],{"class":3402,"line":3568},[3400,8440,8441],{},"    Пн-Пт 20:00 UTC: desired=2\n",[3400,8443,8444],{"class":3402,"line":3574},[3400,8445,3618],{},[3400,8447,8448],{"class":3402,"line":3580},[3400,8449,3425],{"emptyLinePlaceholder":3424},[3400,8451,8452],{"class":3402,"line":3586},[3400,8453,3624],{},[3824,8455,8457],{"id":8456},"target-tracking-scaling-policy","Target Tracking Scaling Policy",[3353,8459,8460],{},"Найпростіший та найрекомендованіший підхід. Ви декларуєте одне просте правило: «підтримуй метрику X на рівні Y». ASG автоматично розраховує необхідну кількість instances для досягнення цього показника та додає або видаляє instances відповідно.",[8462,8463,8464,8546],"tabs",{},[8465,8466,8468],"tabs-item",{"label":8467},"AWS Console",[8469,8470,8471,8485,8490,8496,8504,8510,8518,8527,8541],"ol",{},[3656,8472,8473,8474,8477,8478,8481,8482],{},"EC2 → ",[3359,8475,8476],{},"Auto Scaling Groups"," → оберіть ",[3397,8479,8480],{},"dotnet-api-asg"," → вкладка ",[3359,8483,8484],{},"Automatic scaling",[3656,8486,8487],{},[3359,8488,8489],{},"Create dynamic scaling policy",[3656,8491,8492,8495],{},[3359,8493,8494],{},"Policy type:"," Target tracking scaling",[3656,8497,8498,5214,8501],{},[3359,8499,8500],{},"Scaling policy name:",[3397,8502,8503],{},"cpu-target-tracking",[3656,8505,8506,8509],{},[3359,8507,8508],{},"Metric type:"," Average CPU utilization",[3656,8511,8512,5214,8515],{},[3359,8513,8514],{},"Target value:",[3397,8516,8517],{},"70",[3656,8519,8520,5214,8523,8526],{},[3359,8521,8522],{},"Instance warmup:",[3397,8524,8525],{},"60"," seconds",[3656,8528,8529,8533,8534,8537,8538],{},[8530,8531,8532],"em",{},"(Необов'язково)"," Розгорніть ",[3359,8535,8536],{},"Additional settings"," → Scale-in cooldown: ",[3397,8539,8540],{},"300",[3656,8542,8543],{},[3359,8544,8545],{},"Create",[8465,8547,8549],{"label":8548},"AWS CLI",[3390,8550,8552],{"className":4740,"code":8551,"language":4742,"meta":3395,"style":3395},"aws autoscaling put-scaling-policy \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --policy-name cpu-target-tracking \\\n    --policy-type TargetTrackingScaling \\\n    --target-tracking-configuration '{\n        \"PredefinedMetricSpecification\": {\n            \"PredefinedMetricType\": \"ASGAverageCPUUtilization\"\n        },\n        \"TargetValue\": 70.0,\n        \"ScaleOutCooldown\": 60,\n        \"ScaleInCooldown\": 300,\n        \"DisableScaleIn\": false\n    }' \\\n    --region eu-central-1\n",[3397,8553,8554,8566,8576,8586,8596,8604,8609,8614,8619,8624,8629,8634,8639,8646],{"__ignoreMap":3395},[3400,8555,8556,8558,8561,8564],{"class":3402,"line":3403},[3400,8557,4756],{"class":4755},[3400,8559,8560],{"class":4759}," autoscaling",[3400,8562,8563],{"class":4759}," put-scaling-policy",[3400,8565,4767],{"class":4766},[3400,8567,8568,8571,8574],{"class":3402,"line":3409},[3400,8569,8570],{"class":4772},"    --auto-scaling-group-name",[3400,8572,8573],{"class":4759}," dotnet-api-asg",[3400,8575,4767],{"class":4766},[3400,8577,8578,8581,8584],{"class":3402,"line":3415},[3400,8579,8580],{"class":4772},"    --policy-name",[3400,8582,8583],{"class":4759}," cpu-target-tracking",[3400,8585,4767],{"class":4766},[3400,8587,8588,8591,8594],{"class":3402,"line":3421},[3400,8589,8590],{"class":4772},"    --policy-type",[3400,8592,8593],{"class":4759}," TargetTrackingScaling",[3400,8595,4767],{"class":4766},[3400,8597,8598,8601],{"class":3402,"line":3428},[3400,8599,8600],{"class":4772},"    --target-tracking-configuration",[3400,8602,8603],{"class":4759}," '{\n",[3400,8605,8606],{"class":3402,"line":3434},[3400,8607,8608],{"class":4759},"        \"PredefinedMetricSpecification\": {\n",[3400,8610,8611],{"class":3402,"line":3440},[3400,8612,8613],{"class":4759},"            \"PredefinedMetricType\": \"ASGAverageCPUUtilization\"\n",[3400,8615,8616],{"class":3402,"line":3446},[3400,8617,8618],{"class":4759},"        },\n",[3400,8620,8621],{"class":3402,"line":3451},[3400,8622,8623],{"class":4759},"        \"TargetValue\": 70.0,\n",[3400,8625,8626],{"class":3402,"line":3457},[3400,8627,8628],{"class":4759},"        \"ScaleOutCooldown\": 60,\n",[3400,8630,8631],{"class":3402,"line":3463},[3400,8632,8633],{"class":4759},"        \"ScaleInCooldown\": 300,\n",[3400,8635,8636],{"class":3402,"line":3469},[3400,8637,8638],{"class":4759},"        \"DisableScaleIn\": false\n",[3400,8640,8641,8644],{"class":3402,"line":3475},[3400,8642,8643],{"class":4759},"    }'",[3400,8645,4767],{"class":4766},[3400,8647,8648,8650],{"class":3402,"line":3481},[3400,8649,4818],{"class":4772},[3400,8651,4821],{"class":4759},[5202,8653,8655,8664,8667,8679,8686,8698,8708,8712,8715],{"title":8654},"put-scaling-policy → Target Tracking",[5206,8656,8658,5214,8661],{"className":8657},[3402],[3400,8659,5213],{"className":8660},[5212],[3359,8662,8663],{},"aws autoscaling put-scaling-policy --policy-name cpu-target-tracking ...",[5206,8665,5232],{"className":8666},[3402],[5206,8668,8670,8671,5255,8675,5261],{"className":8669},[3402],"  ",[3400,8672,8674],{"className":8673},[5240],"\"PolicyARN\"",[3400,8676,8678],{"className":8677},[5259],"\"arn:aws:autoscaling:eu-central-1:123456789012:scalingPolicy:...\"",[5206,8680,8670,8682,5242],{"className":8681},[3402],[3400,8683,8685],{"className":8684},[5240],"\"Alarms\"",[5206,8687,8689,8690,5255,8694,6161],{"className":8688},[3402],"    { ",[3400,8691,8693],{"className":8692},[5240],"\"AlarmName\"",[3400,8695,8697],{"className":8696},[5259],"\"TargetTracking-dotnet-api-asg-AlarmHigh-...\"",[5206,8699,8689,8701,5255,8704,6180],{"className":8700},[3402],[3400,8702,8693],{"className":8703},[5240],[3400,8705,8707],{"className":8706},[5259],"\"TargetTracking-dotnet-api-asg-AlarmLow-...\"",[5206,8709,8711],{"className":8710},[3402],"  ]",[5206,8713,5284],{"className":8714},[3402],[5206,8716,8718],{"className":8717},[3402],[3400,8719,8721],{"className":8720},[6151,6206],"← ASG автоматично створив два CloudWatch Alarms: для Scale Out та Scale In",[3353,8723,8724],{},[3359,8725,8726],{},"Конфігурація параметрів:",[3390,8728,8731],{"className":6661,"code":8729,"filename":8730,"language":6663,"meta":3395,"style":3395},"{\n    \"PolicyName\": \"cpu-target-tracking\",\n    \"PolicyType\": \"TargetTrackingScaling\",\n    \"TargetTrackingConfiguration\": {\n        \"PredefinedMetricSpecification\": {\n            \"PredefinedMetricType\": \"ASGAverageCPUUtilization\"\n        },\n        \"TargetValue\": 70.0,\n        \"ScaleOutCooldown\": 60,\n        \"ScaleInCooldown\": 300,\n        \"DisableScaleIn\": false\n    }\n}\n","Повна конфігурація Target Tracking Policy",[3397,8732,8733,8737,8749,8761,8768,8775,8785,8789,8801,8812,8823,8833,8837],{"__ignoreMap":3395},[3400,8734,8735],{"class":3402,"line":3403},[3400,8736,6670],{"class":5649},[3400,8738,8739,8742,8744,8747],{"class":3402,"line":3409},[3400,8740,8741],{"class":6675},"    \"PolicyName\"",[3400,8743,5255],{"class":5649},[3400,8745,8746],{"class":4759},"\"cpu-target-tracking\"",[3400,8748,5743],{"class":5649},[3400,8750,8751,8754,8756,8759],{"class":3402,"line":3415},[3400,8752,8753],{"class":6675},"    \"PolicyType\"",[3400,8755,5255],{"class":5649},[3400,8757,8758],{"class":4759},"\"TargetTrackingScaling\"",[3400,8760,5743],{"class":5649},[3400,8762,8763,8766],{"class":3402,"line":3421},[3400,8764,8765],{"class":6675},"    \"TargetTrackingConfiguration\"",[3400,8767,5755],{"class":5649},[3400,8769,8770,8773],{"class":3402,"line":3428},[3400,8771,8772],{"class":6675},"        \"PredefinedMetricSpecification\"",[3400,8774,5755],{"class":5649},[3400,8776,8777,8780,8782],{"class":3402,"line":3434},[3400,8778,8779],{"class":6675},"            \"PredefinedMetricType\"",[3400,8781,5255],{"class":5649},[3400,8783,8784],{"class":4759},"\"ASGAverageCPUUtilization\"\n",[3400,8786,8787],{"class":3402,"line":3440},[3400,8788,8618],{"class":5649},[3400,8790,8791,8794,8796,8799],{"class":3402,"line":3446},[3400,8792,8793],{"class":6675},"        \"TargetValue\"",[3400,8795,5255],{"class":5649},[3400,8797,8798],{"class":4800},"70.0",[3400,8800,5743],{"class":5649},[3400,8802,8803,8806,8808,8810],{"class":3402,"line":3451},[3400,8804,8805],{"class":6675},"        \"ScaleOutCooldown\"",[3400,8807,5255],{"class":5649},[3400,8809,8525],{"class":4800},[3400,8811,5743],{"class":5649},[3400,8813,8814,8817,8819,8821],{"class":3402,"line":3457},[3400,8815,8816],{"class":6675},"        \"ScaleInCooldown\"",[3400,8818,5255],{"class":5649},[3400,8820,8540],{"class":4800},[3400,8822,5743],{"class":5649},[3400,8824,8825,8828,8830],{"class":3402,"line":3463},[3400,8826,8827],{"class":6675},"        \"DisableScaleIn\"",[3400,8829,5255],{"class":5649},[3400,8831,8832],{"class":4772},"false\n",[3400,8834,8835],{"class":3402,"line":3469},[3400,8836,3537],{"class":5649},[3400,8838,8839],{"class":3402,"line":3475},[3400,8840,3496],{"class":5649},[4019,8842,8843,8873,8878,8882],{},[4022,8844,8846,8849],{"name":8845,"type":4025,"required":4026},"PredefinedMetricType",[3353,8847,8848],{},"Метрика, яку ASG буде підтримувати на цільовому рівні. Доступні значення:",[3653,8850,8851,8857,8867],{},[3656,8852,8853,8856],{},[3397,8854,8855],{},"ASGAverageCPUUtilization"," — середнє завантаження CPU по всіх instances ASG",[3656,8858,8859,8862,8863,8866],{},[3397,8860,8861],{},"ASGAverageNetworkIn"," \u002F ",[3397,8864,8865],{},"ASGAverageNetworkOut"," — середній мережевий трафік (байт\u002Fсек)",[3656,8868,8869,8872],{},[3397,8870,8871],{},"ALBRequestCountPerTarget"," — середня кількість запитів ALB на один instance",[4022,8874,8877],{"name":8875,"type":8876,"required":4026},"TargetValue","number","Цільове значення метрики. ASG прагне підтримувати метрику саме на цьому рівні. Значення 70.0 для CPU означає: «тримай середнє CPU по ASG на рівні 70%». Рекомендований діапазон для CPU: 60–75%.",[4022,8879,8881],{"name":8880,"type":4039},"ScaleOutCooldown","Час очікування (секунди) між додаванням instances. Під час cooldown ASG не запускатиме нові instances навіть якщо метрика перевищує ціль. Мале значення (60с) дозволяє швидко реагувати на зростання навантаження.",[4022,8883,8885],{"name":8884,"type":4039},"ScaleInCooldown","Час очікування (секунди) між видаленням instances. Рекомендується встановлювати значно більшим за ScaleOutCooldown (300–600с), щоб уникнути передчасного видалення instances під час тимчасового спаду навантаження між піками.",[3824,8887,8889],{"id":8888},"step-scaling-policy","Step Scaling Policy",[3353,8891,8892,8893,8896],{},"Step Scaling дозволяє визначити ",[3359,8894,8895],{},"нелінійну"," залежність між відхиленням метрики від порогу та кількістю instances, що додаються або видаляються. Це корисно, коли потрібна більш агресивна реакція при екстремальних відхиленнях.",[8462,8898,8899,9034],{},[8465,8900,8901,8906,8956,8961],{"label":8467},[3353,8902,8903],{},[3359,8904,8905],{},"Крок 1: Спочатку створіть CloudWatch Alarm для CPU:",[8469,8907,8908,8921,8931,8942],{},[3656,8909,8910,8911,8914,8915,8914,8918],{},"CloudWatch → ",[3359,8912,8913],{},"Alarms"," → ",[3359,8916,8917],{},"Create alarm",[3359,8919,8920],{},"Select metric",[3656,8922,8923,8924,8914,8926,8914,8929],{},"EC2 → By Auto Scaling Group → ",[3397,8925,8480],{},[3397,8927,8928],{},"CPUUtilization",[3359,8930,8920],{},[3656,8932,8933,8936,8937,8914,8939],{},[3359,8934,8935],{},"Conditions:"," Threshold type: Static → Greater than: ",[3397,8938,8517],{},[3359,8940,8941],{},"Next",[3656,8943,8944,8947,8948,8950,8951,8914,8954],{},[3359,8945,8946],{},"Actions:"," Remove default action → ",[3359,8949,8941],{}," → Name: ",[3397,8952,8953],{},"cpu-above-70",[3359,8955,8917],{},[3353,8957,8958],{},[3359,8959,8960],{},"Крок 2: Додайте Step Scaling Policy до ASG:",[8469,8962,8963,8971,8975,8980,8987,8995,9025],{},[3656,8964,8473,8965,8914,8967,8481,8969],{},[3359,8966,8476],{},[3397,8968,8480],{},[3359,8970,8484],{},[3656,8972,8973],{},[3359,8974,8489],{},[3656,8976,8977,8979],{},[3359,8978,8494],{}," Step scaling",[3656,8981,8982,5214,8984],{},[3359,8983,8500],{},[3397,8985,8986],{},"cpu-step-scaling",[3656,8988,8989,8992,8993],{},[3359,8990,8991],{},"CloudWatch alarm:"," оберіть ",[3397,8994,8953],{},[3656,8996,8997,9000],{},[3359,8998,8999],{},"Take the action:",[3653,9001,9002,9011,9019],{},[3656,9003,9004,9005,9007,9008],{},"Add 1 capacity unit when ",[3397,9006,8104],{}," ≤ alarm value \u003C ",[3397,9009,9010],{},"10",[3656,9012,9013,9014,9007,9016],{},"Add 2 capacity units when ",[3397,9015,9010],{},[3397,9017,9018],{},"20",[3656,9020,9021,9022,9024],{},"Add 4 capacity units when ",[3397,9023,9018],{}," ≤ alarm value",[3656,9026,9027,5214,9029,9031,9032],{},[3359,9028,8522],{},[3397,9030,8525],{}," seconds → ",[3359,9033,8545],{},[8465,9035,9036],{"label":8548},[3390,9037,9039],{"className":4740,"code":9038,"language":4742,"meta":3395,"style":3395},"# Крок 1: Створити CloudWatch Alarm для CPU > 70%\naws cloudwatch put-metric-alarm \\\n    --alarm-name \"cpu-above-70\" \\\n    --alarm-description \"CPU above 70% — trigger Step Scaling\" \\\n    --metric-name CPUUtilization \\\n    --namespace AWS\u002FEC2 \\\n    --statistic Average \\\n    --period 60 \\\n    --threshold 70 \\\n    --comparison-operator GreaterThanThreshold \\\n    --dimensions Name=AutoScalingGroupName,Value=dotnet-api-asg \\\n    --evaluation-periods 2 \\\n    --region eu-central-1\n\n# Крок 2: Створити Step Scaling Policy\nSTEP_POLICY_ARN=$(aws autoscaling put-scaling-policy \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --policy-name cpu-step-scaling \\\n    --policy-type StepScaling \\\n    --adjustment-type ChangeInCapacity \\\n    --step-adjustments \\\n        \"MetricIntervalLowerBound=0,MetricIntervalUpperBound=10,ScalingAdjustment=1\" \\\n        \"MetricIntervalLowerBound=10,MetricIntervalUpperBound=20,ScalingAdjustment=2\" \\\n        \"MetricIntervalLowerBound=20,ScalingAdjustment=4\" \\\n    --estimated-instance-warmup 60 \\\n    --region eu-central-1 \\\n    --query PolicyARN --output text)\n\necho \"Step Scaling Policy ARN: $STEP_POLICY_ARN\"\n\n# Крок 3: Прив'язати Alarm до Policy\naws cloudwatch put-metric-alarm \\\n    --alarm-name \"cpu-above-70\" \\\n    --alarm-actions \"$STEP_POLICY_ARN\" \\\n    --region eu-central-1\n",[3397,9040,9041,9046,9058,9068,9078,9088,9098,9108,9118,9128,9138,9148,9158,9164,9168,9173,9189,9197,9206,9215,9225,9232,9239,9246,9253,9262,9270,9285,9289,9302,9306,9311,9321,9329,9342],{"__ignoreMap":3395},[3400,9042,9043],{"class":3402,"line":3403},[3400,9044,9045],{"class":4749},"# Крок 1: Створити CloudWatch Alarm для CPU > 70%\n",[3400,9047,9048,9050,9053,9056],{"class":3402,"line":3409},[3400,9049,4756],{"class":4755},[3400,9051,9052],{"class":4759}," cloudwatch",[3400,9054,9055],{"class":4759}," put-metric-alarm",[3400,9057,4767],{"class":4766},[3400,9059,9060,9063,9066],{"class":3402,"line":3415},[3400,9061,9062],{"class":4772},"    --alarm-name",[3400,9064,9065],{"class":4759}," \"cpu-above-70\"",[3400,9067,4767],{"class":4766},[3400,9069,9070,9073,9076],{"class":3402,"line":3421},[3400,9071,9072],{"class":4772},"    --alarm-description",[3400,9074,9075],{"class":4759}," \"CPU above 70% — trigger Step Scaling\"",[3400,9077,4767],{"class":4766},[3400,9079,9080,9083,9086],{"class":3402,"line":3428},[3400,9081,9082],{"class":4772},"    --metric-name",[3400,9084,9085],{"class":4759}," CPUUtilization",[3400,9087,4767],{"class":4766},[3400,9089,9090,9093,9096],{"class":3402,"line":3434},[3400,9091,9092],{"class":4772},"    --namespace",[3400,9094,9095],{"class":4759}," AWS\u002FEC2",[3400,9097,4767],{"class":4766},[3400,9099,9100,9103,9106],{"class":3402,"line":3440},[3400,9101,9102],{"class":4772},"    --statistic",[3400,9104,9105],{"class":4759}," Average",[3400,9107,4767],{"class":4766},[3400,9109,9110,9113,9116],{"class":3402,"line":3446},[3400,9111,9112],{"class":4772},"    --period",[3400,9114,9115],{"class":4800}," 60",[3400,9117,4767],{"class":4766},[3400,9119,9120,9123,9126],{"class":3402,"line":3451},[3400,9121,9122],{"class":4772},"    --threshold",[3400,9124,9125],{"class":4800}," 70",[3400,9127,4767],{"class":4766},[3400,9129,9130,9133,9136],{"class":3402,"line":3457},[3400,9131,9132],{"class":4772},"    --comparison-operator",[3400,9134,9135],{"class":4759}," GreaterThanThreshold",[3400,9137,4767],{"class":4766},[3400,9139,9140,9143,9146],{"class":3402,"line":3463},[3400,9141,9142],{"class":4772},"    --dimensions",[3400,9144,9145],{"class":4759}," Name=AutoScalingGroupName,Value=dotnet-api-asg",[3400,9147,4767],{"class":4766},[3400,9149,9150,9153,9156],{"class":3402,"line":3469},[3400,9151,9152],{"class":4772},"    --evaluation-periods",[3400,9154,9155],{"class":4800}," 2",[3400,9157,4767],{"class":4766},[3400,9159,9160,9162],{"class":3402,"line":3475},[3400,9161,4818],{"class":4772},[3400,9163,4821],{"class":4759},[3400,9165,9166],{"class":3402,"line":3481},[3400,9167,3425],{"emptyLinePlaceholder":3424},[3400,9169,9170],{"class":3402,"line":3487},[3400,9171,9172],{"class":4749},"# Крок 2: Створити Step Scaling Policy\n",[3400,9174,9175,9178,9181,9183,9185,9187],{"class":3402,"line":3493},[3400,9176,9177],{"class":4779},"STEP_POLICY_ARN",[3400,9179,9180],{"class":5649},"=$(",[3400,9182,4756],{"class":4755},[3400,9184,8560],{"class":4759},[3400,9186,8563],{"class":4759},[3400,9188,4767],{"class":4766},[3400,9190,9191,9193,9195],{"class":3402,"line":3499},[3400,9192,8570],{"class":4772},[3400,9194,8573],{"class":4759},[3400,9196,4767],{"class":4766},[3400,9198,9199,9201,9204],{"class":3402,"line":3504},[3400,9200,8580],{"class":4772},[3400,9202,9203],{"class":4759}," cpu-step-scaling",[3400,9205,4767],{"class":4766},[3400,9207,9208,9210,9213],{"class":3402,"line":3510},[3400,9209,8590],{"class":4772},[3400,9211,9212],{"class":4759}," StepScaling",[3400,9214,4767],{"class":4766},[3400,9216,9217,9220,9223],{"class":3402,"line":3516},[3400,9218,9219],{"class":4772},"    --adjustment-type",[3400,9221,9222],{"class":4759}," ChangeInCapacity",[3400,9224,4767],{"class":4766},[3400,9226,9227,9230],{"class":3402,"line":3522},[3400,9228,9229],{"class":4772},"    --step-adjustments",[3400,9231,4767],{"class":4766},[3400,9233,9234,9237],{"class":3402,"line":3528},[3400,9235,9236],{"class":4759},"        \"MetricIntervalLowerBound=0,MetricIntervalUpperBound=10,ScalingAdjustment=1\"",[3400,9238,4767],{"class":4766},[3400,9240,9241,9244],{"class":3402,"line":3534},[3400,9242,9243],{"class":4759},"        \"MetricIntervalLowerBound=10,MetricIntervalUpperBound=20,ScalingAdjustment=2\"",[3400,9245,4767],{"class":4766},[3400,9247,9248,9251],{"class":3402,"line":3540},[3400,9249,9250],{"class":4759},"        \"MetricIntervalLowerBound=20,ScalingAdjustment=4\"",[3400,9252,4767],{"class":4766},[3400,9254,9255,9258,9260],{"class":3402,"line":3545},[3400,9256,9257],{"class":4772},"    --estimated-instance-warmup",[3400,9259,9115],{"class":4800},[3400,9261,4767],{"class":4766},[3400,9263,9264,9266,9268],{"class":3402,"line":3550},[3400,9265,4818],{"class":4772},[3400,9267,5324],{"class":4759},[3400,9269,4767],{"class":4766},[3400,9271,9272,9274,9277,9280,9283],{"class":3402,"line":3556},[3400,9273,5331],{"class":4772},[3400,9275,9276],{"class":4759}," PolicyARN",[3400,9278,9279],{"class":4772}," --output",[3400,9281,9282],{"class":4759}," text",[3400,9284,7300],{"class":5649},[3400,9286,9287],{"class":3402,"line":3562},[3400,9288,3425],{"emptyLinePlaceholder":3424},[3400,9290,9291,9293,9296,9299],{"class":3402,"line":3568},[3400,9292,6079],{"class":4755},[3400,9294,9295],{"class":4759}," \"Step Scaling Policy ARN: ",[3400,9297,9298],{"class":4779},"$STEP_POLICY_ARN",[3400,9300,9301],{"class":4759},"\"\n",[3400,9303,9304],{"class":3402,"line":3574},[3400,9305,3425],{"emptyLinePlaceholder":3424},[3400,9307,9308],{"class":3402,"line":3580},[3400,9309,9310],{"class":4749},"# Крок 3: Прив'язати Alarm до Policy\n",[3400,9312,9313,9315,9317,9319],{"class":3402,"line":3586},[3400,9314,4756],{"class":4755},[3400,9316,9052],{"class":4759},[3400,9318,9055],{"class":4759},[3400,9320,4767],{"class":4766},[3400,9322,9323,9325,9327],{"class":3402,"line":3591},[3400,9324,9062],{"class":4772},[3400,9326,9065],{"class":4759},[3400,9328,4767],{"class":4766},[3400,9330,9331,9334,9336,9338,9340],{"class":3402,"line":3597},[3400,9332,9333],{"class":4772},"    --alarm-actions",[3400,9335,4776],{"class":4759},[3400,9337,9298],{"class":4779},[3400,9339,4783],{"class":4759},[3400,9341,4767],{"class":4766},[3400,9343,9344,9346],{"class":3402,"line":3603},[3400,9345,4818],{"class":4772},[3400,9347,4821],{"class":4759},[5202,9349,9351,9360,9364,9371,9374,9383],{"title":9350},"Step Scaling Policy → створення",[5206,9352,9354,5214,9357],{"className":9353},[3402],[3400,9355,5213],{"className":9356},[5212],[3359,9358,9359],{},"aws autoscaling put-scaling-policy --policy-name cpu-step-scaling ...",[5206,9361,9363],{"className":9362},[3402],"Step Scaling Policy ARN:",[5206,9365,9367],{"className":9366},[3402],[3400,9368,9370],{"className":9369},[5259],"arn:aws:autoscaling:eu-central-1:123456789012:scalingPolicy:abc123:autoScalingGroupName\u002Fdotnet-api-asg:policyName\u002Fcpu-step-scaling",[5206,9372],{"className":9373},[3402],[5206,9375,9377,5214,9380],{"className":9376},[3402],[3400,9378,5213],{"className":9379},[5212],[3359,9381,9382],{},"aws cloudwatch put-metric-alarm --alarm-name cpu-above-70 --alarm-actions ...",[5206,9384,9386],{"className":9385},[3402],[3400,9387,9389],{"className":9388},[5259],"(no output = success — Alarm прив'язаний до Policy)",[3353,9391,9392],{},[3359,9393,9394],{},"Конфігурація кроків масштабування:",[3390,9396,9399],{"className":6661,"code":9397,"filename":9398,"language":6663,"meta":3395,"style":3395},"{\n    \"PolicyName\": \"cpu-step-scaling\",\n    \"PolicyType\": \"StepScaling\",\n    \"AdjustmentType\": \"ChangeInCapacity\",\n    \"StepAdjustments\": [\n        {\n            \"MetricIntervalLowerBound\": 0,\n            \"MetricIntervalUpperBound\": 10,\n            \"ScalingAdjustment\": 1\n        },\n        {\n            \"MetricIntervalLowerBound\": 10,\n            \"MetricIntervalUpperBound\": 20,\n            \"ScalingAdjustment\": 2\n        },\n        {\n            \"MetricIntervalLowerBound\": 20,\n            \"ScalingAdjustment\": 4\n        }\n    ]\n}\n","Конфігурація Step Scaling Policy",[3397,9400,9401,9405,9416,9427,9439,9446,9450,9461,9472,9482,9486,9490,9500,9510,9519,9523,9527,9537,9546,9550,9555],{"__ignoreMap":3395},[3400,9402,9403],{"class":3402,"line":3403},[3400,9404,6670],{"class":5649},[3400,9406,9407,9409,9411,9414],{"class":3402,"line":3409},[3400,9408,8741],{"class":6675},[3400,9410,5255],{"class":5649},[3400,9412,9413],{"class":4759},"\"cpu-step-scaling\"",[3400,9415,5743],{"class":5649},[3400,9417,9418,9420,9422,9425],{"class":3402,"line":3415},[3400,9419,8753],{"class":6675},[3400,9421,5255],{"class":5649},[3400,9423,9424],{"class":4759},"\"StepScaling\"",[3400,9426,5743],{"class":5649},[3400,9428,9429,9432,9434,9437],{"class":3402,"line":3421},[3400,9430,9431],{"class":6675},"    \"AdjustmentType\"",[3400,9433,5255],{"class":5649},[3400,9435,9436],{"class":4759},"\"ChangeInCapacity\"",[3400,9438,5743],{"class":5649},[3400,9440,9441,9444],{"class":3402,"line":3428},[3400,9442,9443],{"class":6675},"    \"StepAdjustments\"",[3400,9445,5768],{"class":5649},[3400,9447,9448],{"class":3402,"line":3434},[3400,9449,5720],{"class":5649},[3400,9451,9452,9455,9457,9459],{"class":3402,"line":3440},[3400,9453,9454],{"class":6675},"            \"MetricIntervalLowerBound\"",[3400,9456,5255],{"class":5649},[3400,9458,8104],{"class":4800},[3400,9460,5743],{"class":5649},[3400,9462,9463,9466,9468,9470],{"class":3402,"line":3446},[3400,9464,9465],{"class":6675},"            \"MetricIntervalUpperBound\"",[3400,9467,5255],{"class":5649},[3400,9469,9010],{"class":4800},[3400,9471,5743],{"class":5649},[3400,9473,9474,9477,9479],{"class":3402,"line":3451},[3400,9475,9476],{"class":6675},"            \"ScalingAdjustment\"",[3400,9478,5255],{"class":5649},[3400,9480,9481],{"class":4800},"1\n",[3400,9483,9484],{"class":3402,"line":3457},[3400,9485,8618],{"class":5649},[3400,9487,9488],{"class":3402,"line":3463},[3400,9489,5720],{"class":5649},[3400,9491,9492,9494,9496,9498],{"class":3402,"line":3469},[3400,9493,9454],{"class":6675},[3400,9495,5255],{"class":5649},[3400,9497,9010],{"class":4800},[3400,9499,5743],{"class":5649},[3400,9501,9502,9504,9506,9508],{"class":3402,"line":3475},[3400,9503,9465],{"class":6675},[3400,9505,5255],{"class":5649},[3400,9507,9018],{"class":4800},[3400,9509,5743],{"class":5649},[3400,9511,9512,9514,9516],{"class":3402,"line":3481},[3400,9513,9476],{"class":6675},[3400,9515,5255],{"class":5649},[3400,9517,9518],{"class":4800},"2\n",[3400,9520,9521],{"class":3402,"line":3487},[3400,9522,8618],{"class":5649},[3400,9524,9525],{"class":3402,"line":3493},[3400,9526,5720],{"class":5649},[3400,9528,9529,9531,9533,9535],{"class":3402,"line":3499},[3400,9530,9454],{"class":6675},[3400,9532,5255],{"class":5649},[3400,9534,9018],{"class":4800},[3400,9536,5743],{"class":5649},[3400,9538,9539,9541,9543],{"class":3402,"line":3504},[3400,9540,9476],{"class":6675},[3400,9542,5255],{"class":5649},[3400,9544,9545],{"class":4800},"4\n",[3400,9547,9548],{"class":3402,"line":3510},[3400,9549,5846],{"class":5649},[3400,9551,9552],{"class":3402,"line":3516},[3400,9553,9554],{"class":5649},"    ]\n",[3400,9556,9557],{"class":3402,"line":3522},[3400,9558,3496],{"class":5649},[4019,9560,9561,9591,9599],{},[4022,9562,9564,9571],{"name":9563,"type":4025,"required":4026},"AdjustmentType",[3353,9565,9566,9567,9570],{},"Спосіб інтерпретації значення ",[3397,9568,9569],{},"ScalingAdjustment",":",[3653,9572,9573,9579,9585],{},[3656,9574,9575,9578],{},[3397,9576,9577],{},"ChangeInCapacity"," — змінити DesiredCapacity на вказане число (додати або видалити N instances)",[3656,9580,9581,9584],{},[3397,9582,9583],{},"ExactCapacity"," — встановити DesiredCapacity рівно до вказаного числа",[3656,9586,9587,9590],{},[3397,9588,9589],{},"PercentChangeInCapacity"," — змінити DesiredCapacity на вказаний відсоток від поточного значення",[4022,9592,9594,9595,9598],{"name":9593,"type":8876},"MetricIntervalLowerBound \u002F UpperBound","Визначають діапазон відхилення метрики від порогового значення (threshold). Наприклад, якщо threshold = 70% CPU, то ",[3397,9596,9597],{},"LowerBound: 0, UpperBound: 10"," означає діапазон 70–80% CPU.",[4022,9600,9601],{"name":9569,"type":4039,"required":4026},"Кількість instances для додавання (позитивне значення) або видалення (від'ємне значення) при спрацюванні цього кроку.",[3387,9603,9604],{},[3390,9605,9607],{"className":3392,"code":9606,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"Step Scaling: поведінка при CPU threshold = 70%\"\n\nrectangle \"CPU 70-80%\\n(перевищення на 0-10%)\" as S1 #d1fae5\nrectangle \"CPU 80-90%\\n(перевищення на 10-20%)\" as S2 #fef3c7\nrectangle \"CPU > 90%\\n(перевищення > 20%)\" as S3 #fee2e2\n\nrectangle \"+1 instance\\n(помірна реакція)\" as R1 #bbf7d0\nrectangle \"+2 instances\\n(активна реакція)\" as R2 #fde68a\nrectangle \"+4 instances\\n(агресивна реакція)\" as R3 #fca5a5\n\nS1 --> R1\nS2 --> R2\nS3 --> R3\n\nnote bottom\n    При поверненні CPU нижче 70%\n    ASG починає видаляти instances\n    (Scale In з cooldown 300с)\nend note\n@enduml\n",[3397,9608,9609,9613,9617,9621,9625,9630,9634,9639,9644,9649,9653,9658,9663,9668,9672,9677,9682,9687,9691,9696,9701,9706,9711,9715],{"__ignoreMap":3395},[3400,9610,9611],{"class":3402,"line":3403},[3400,9612,3406],{},[3400,9614,9615],{"class":3402,"line":3409},[3400,9616,3412],{},[3400,9618,9619],{"class":3402,"line":3415},[3400,9620,3418],{},[3400,9622,9623],{"class":3402,"line":3421},[3400,9624,3425],{"emptyLinePlaceholder":3424},[3400,9626,9627],{"class":3402,"line":3428},[3400,9628,9629],{},"title \"Step Scaling: поведінка при CPU threshold = 70%\"\n",[3400,9631,9632],{"class":3402,"line":3434},[3400,9633,3425],{"emptyLinePlaceholder":3424},[3400,9635,9636],{"class":3402,"line":3440},[3400,9637,9638],{},"rectangle \"CPU 70-80%\\n(перевищення на 0-10%)\" as S1 #d1fae5\n",[3400,9640,9641],{"class":3402,"line":3446},[3400,9642,9643],{},"rectangle \"CPU 80-90%\\n(перевищення на 10-20%)\" as S2 #fef3c7\n",[3400,9645,9646],{"class":3402,"line":3451},[3400,9647,9648],{},"rectangle \"CPU > 90%\\n(перевищення > 20%)\" as S3 #fee2e2\n",[3400,9650,9651],{"class":3402,"line":3457},[3400,9652,3425],{"emptyLinePlaceholder":3424},[3400,9654,9655],{"class":3402,"line":3463},[3400,9656,9657],{},"rectangle \"+1 instance\\n(помірна реакція)\" as R1 #bbf7d0\n",[3400,9659,9660],{"class":3402,"line":3469},[3400,9661,9662],{},"rectangle \"+2 instances\\n(активна реакція)\" as R2 #fde68a\n",[3400,9664,9665],{"class":3402,"line":3475},[3400,9666,9667],{},"rectangle \"+4 instances\\n(агресивна реакція)\" as R3 #fca5a5\n",[3400,9669,9670],{"class":3402,"line":3481},[3400,9671,3425],{"emptyLinePlaceholder":3424},[3400,9673,9674],{"class":3402,"line":3487},[3400,9675,9676],{},"S1 --> R1\n",[3400,9678,9679],{"class":3402,"line":3493},[3400,9680,9681],{},"S2 --> R2\n",[3400,9683,9684],{"class":3402,"line":3499},[3400,9685,9686],{},"S3 --> R3\n",[3400,9688,9689],{"class":3402,"line":3504},[3400,9690,3425],{"emptyLinePlaceholder":3424},[3400,9692,9693],{"class":3402,"line":3510},[3400,9694,9695],{},"note bottom\n",[3400,9697,9698],{"class":3402,"line":3516},[3400,9699,9700],{},"    При поверненні CPU нижче 70%\n",[3400,9702,9703],{"class":3402,"line":3522},[3400,9704,9705],{},"    ASG починає видаляти instances\n",[3400,9707,9708],{"class":3402,"line":3528},[3400,9709,9710],{},"    (Scale In з cooldown 300с)\n",[3400,9712,9713],{"class":3402,"line":3534},[3400,9714,3618],{},[3400,9716,9717],{"class":3402,"line":3540},[3400,9718,3624],{},[3824,9720,9722],{"id":9721},"scheduled-scaling","Scheduled Scaling",[3353,9724,9725],{},"Масштабування за розкладом підходить для систем з передбачуваними та циклічними піками навантаження: робочі дні vs вихідні, ранок vs ніч, початок місяця vs середина місяця.",[8462,9727,9728,9813],{},[8465,9729,9730,9806],{"label":8467},[8469,9731,9732,9740,9749,9757,9775,9787],{},[3656,9733,8473,9734,8914,9736,8481,9738],{},[3359,9735,8476],{},[3397,9737,8480],{},[3359,9739,8484],{},[3656,9741,9742,9743,8914,9746],{},"Розгорніть ",[3359,9744,9745],{},"Scheduled actions",[3359,9747,9748],{},"Create scheduled action",[3656,9750,9751,5214,9754],{},[3359,9752,9753],{},"Name:",[3397,9755,9756],{},"scale-up-morning",[3656,9758,9759,5214,9762,4031,9764,5214,9767,4031,9769,5214,9772],{},[3359,9760,9761],{},"Desired capacity:",[3397,9763,6171],{},[3359,9765,9766],{},"Min:",[3397,9768,6171],{},[3359,9770,9771],{},"Max:",[8530,9773,9774],{},"(залишити порожнім)",[3656,9776,9777,9780,9781,5214,9784],{},[3359,9778,9779],{},"Recurrence:"," Cron: ",[3397,9782,9783],{},"0 8 * * MON-FRI",[8530,9785,9786],{},"(UTC)",[3656,9788,9789,9791,9792,9795,9796,4031,9799,9802,9803],{},[3359,9790,8545],{}," → повторіть для ",[3397,9793,9794],{},"scale-down-evening"," з ",[3397,9797,9798],{},"desired=2",[3397,9800,9801],{},"min=2",", cron ",[3397,9804,9805],{},"0 20 * * MON-FRI",[3353,9807,9808,9809,9812],{},"Всі часи в Scheduled Scaling задаються у ",[3359,9810,9811],{},"UTC",". Для України (UTC+2\u002FUTC+3) враховуйте різницю: 08:00 UTC = 10:00\u002F11:00 за київським часом.",[8465,9814,9815],{"label":8548},[3390,9816,9818],{"className":4740,"code":9817,"language":4742,"meta":3395,"style":3395},"# Кожен робочий день о 08:00 UTC — збільшити до 5 instances\naws autoscaling put-scheduled-update-group-action \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --scheduled-action-name \"scale-up-morning\" \\\n    --recurrence \"0 8 * * MON-FRI\" \\\n    --min-size 5 \\\n    --desired-capacity 5 \\\n    --region eu-central-1\n\n# О 20:00 UTC — повернутись до мінімуму\naws autoscaling put-scheduled-update-group-action \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --scheduled-action-name \"scale-down-evening\" \\\n    --recurrence \"0 20 * * MON-FRI\" \\\n    --min-size 2 \\\n    --desired-capacity 2 \\\n    --region eu-central-1\n\n# Перевірити створені scheduled actions\naws autoscaling describe-scheduled-actions \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --region eu-central-1 \\\n    --query \"ScheduledUpdateGroupActions[*].{Name:ScheduledActionName,Recurrence:Recurrence,Desired:DesiredCapacity}\" \\\n    --output table\n",[3397,9819,9820,9825,9836,9844,9854,9864,9874,9883,9889,9893,9898,9908,9916,9925,9934,9942,9950,9956,9960,9965,9976,9984,9992,10001],{"__ignoreMap":3395},[3400,9821,9822],{"class":3402,"line":3403},[3400,9823,9824],{"class":4749},"# Кожен робочий день о 08:00 UTC — збільшити до 5 instances\n",[3400,9826,9827,9829,9831,9834],{"class":3402,"line":3409},[3400,9828,4756],{"class":4755},[3400,9830,8560],{"class":4759},[3400,9832,9833],{"class":4759}," put-scheduled-update-group-action",[3400,9835,4767],{"class":4766},[3400,9837,9838,9840,9842],{"class":3402,"line":3415},[3400,9839,8570],{"class":4772},[3400,9841,8573],{"class":4759},[3400,9843,4767],{"class":4766},[3400,9845,9846,9849,9852],{"class":3402,"line":3421},[3400,9847,9848],{"class":4772},"    --scheduled-action-name",[3400,9850,9851],{"class":4759}," \"scale-up-morning\"",[3400,9853,4767],{"class":4766},[3400,9855,9856,9859,9862],{"class":3402,"line":3428},[3400,9857,9858],{"class":4772},"    --recurrence",[3400,9860,9861],{"class":4759}," \"0 8 * * MON-FRI\"",[3400,9863,4767],{"class":4766},[3400,9865,9866,9869,9872],{"class":3402,"line":3434},[3400,9867,9868],{"class":4772},"    --min-size",[3400,9870,9871],{"class":4800}," 5",[3400,9873,4767],{"class":4766},[3400,9875,9876,9879,9881],{"class":3402,"line":3440},[3400,9877,9878],{"class":4772},"    --desired-capacity",[3400,9880,9871],{"class":4800},[3400,9882,4767],{"class":4766},[3400,9884,9885,9887],{"class":3402,"line":3446},[3400,9886,4818],{"class":4772},[3400,9888,4821],{"class":4759},[3400,9890,9891],{"class":3402,"line":3451},[3400,9892,3425],{"emptyLinePlaceholder":3424},[3400,9894,9895],{"class":3402,"line":3457},[3400,9896,9897],{"class":4749},"# О 20:00 UTC — повернутись до мінімуму\n",[3400,9899,9900,9902,9904,9906],{"class":3402,"line":3463},[3400,9901,4756],{"class":4755},[3400,9903,8560],{"class":4759},[3400,9905,9833],{"class":4759},[3400,9907,4767],{"class":4766},[3400,9909,9910,9912,9914],{"class":3402,"line":3469},[3400,9911,8570],{"class":4772},[3400,9913,8573],{"class":4759},[3400,9915,4767],{"class":4766},[3400,9917,9918,9920,9923],{"class":3402,"line":3475},[3400,9919,9848],{"class":4772},[3400,9921,9922],{"class":4759}," \"scale-down-evening\"",[3400,9924,4767],{"class":4766},[3400,9926,9927,9929,9932],{"class":3402,"line":3481},[3400,9928,9858],{"class":4772},[3400,9930,9931],{"class":4759}," \"0 20 * * MON-FRI\"",[3400,9933,4767],{"class":4766},[3400,9935,9936,9938,9940],{"class":3402,"line":3487},[3400,9937,9868],{"class":4772},[3400,9939,9155],{"class":4800},[3400,9941,4767],{"class":4766},[3400,9943,9944,9946,9948],{"class":3402,"line":3493},[3400,9945,9878],{"class":4772},[3400,9947,9155],{"class":4800},[3400,9949,4767],{"class":4766},[3400,9951,9952,9954],{"class":3402,"line":3499},[3400,9953,4818],{"class":4772},[3400,9955,4821],{"class":4759},[3400,9957,9958],{"class":3402,"line":3504},[3400,9959,3425],{"emptyLinePlaceholder":3424},[3400,9961,9962],{"class":3402,"line":3510},[3400,9963,9964],{"class":4749},"# Перевірити створені scheduled actions\n",[3400,9966,9967,9969,9971,9974],{"class":3402,"line":3516},[3400,9968,4756],{"class":4755},[3400,9970,8560],{"class":4759},[3400,9972,9973],{"class":4759}," describe-scheduled-actions",[3400,9975,4767],{"class":4766},[3400,9977,9978,9980,9982],{"class":3402,"line":3522},[3400,9979,8570],{"class":4772},[3400,9981,8573],{"class":4759},[3400,9983,4767],{"class":4766},[3400,9985,9986,9988,9990],{"class":3402,"line":3528},[3400,9987,4818],{"class":4772},[3400,9989,5324],{"class":4759},[3400,9991,4767],{"class":4766},[3400,9993,9994,9996,9999],{"class":3402,"line":3534},[3400,9995,5331],{"class":4772},[3400,9997,9998],{"class":4759}," \"ScheduledUpdateGroupActions[*].{Name:ScheduledActionName,Recurrence:Recurrence,Desired:DesiredCapacity}\"",[3400,10000,4767],{"class":4766},[3400,10002,10003,10006],{"class":3402,"line":3540},[3400,10004,10005],{"class":4772},"    --output",[3400,10007,10008],{"class":4759}," table\n",[5202,10010,10012,10021,10025,10029,10033,10037,10040,10052,10063],{"title":10011},"Scheduled Scaling → перевірка",[5206,10013,10015,5214,10018],{"className":10014},[3402],[3400,10016,5213],{"className":10017},[5212],[3359,10019,10020],{},"aws autoscaling describe-scheduled-actions --auto-scaling-group-name dotnet-api-asg ...",[5206,10022,10024],{"className":10023},[3402],"-----------------------------------------------------",[5206,10026,10028],{"className":10027},[3402],"|         DescribeScheduledActions                  |",[5206,10030,10032],{"className":10031},[3402],"+--------------------+----------------+-----------+",[5206,10034,10036],{"className":10035},[3402],"| Name               | Recurrence     | Desired   |",[5206,10038,10032],{"className":10039},[3402],[5206,10041,10043,10044,10047,10048,10051],{"className":10042},[3402],"| scale-up-morning   | ",[3400,10045,9783],{"className":10046},[5259]," | ",[3400,10049,6171],{"className":10050},[6151],"         |",[5206,10053,10055,10056,10059,10060,10051],{"className":10054},[3402],"| scale-down-evening | ",[3400,10057,9805],{"className":10058},[5240],"| ",[3400,10061,4811],{"className":10062},[6151],[5206,10064,10032],{"className":10065},[3402],[3626,10067],{},[3348,10069,10071],{"id":10070},"alb-listener-rules-маршрутизація-запитів","ALB Listener Rules: маршрутизація запитів",[3824,10073,10075],{"id":10074},"path-based-routing","Path-based Routing",[3353,10077,10078,10081],{},[3359,10079,10080],{},"Path-based routing"," дозволяє направляти запити на різні Target Groups залежно від URI-шляху. Це фундаментальна можливість для реалізації мікросервісної архітектури за єдиним ALB.",[3387,10083,10084],{},[3390,10085,10087],{"className":3392,"code":10086,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Client\" as C\n\nrectangle \"ALB\\napi.example.com:443\" as ALB #fef3c7\n\npackage \"UserService Target Group\\n(EC2 з UserService .NET API)\" as UTG #d1fae5 {\n    node \"EC2 #1\" as U1 #bbf7d0\n    node \"EC2 #2\" as U2 #bbf7d0\n}\n\npackage \"OrderService Target Group\\n(EC2 з OrderService .NET API)\" as OTG #dbeafe {\n    node \"EC2 #3\" as O1 #bfdbfe\n    node \"EC2 #4\" as O2 #bfdbfe\n}\n\nrectangle \"Fixed Response\\n200 OK (без сервера)\" as FR #f0fdf4\n\nC --> ALB\n\nALB --> UTG : \"IF path = \u002Fapi\u002Fusers\u002F*\"\nALB --> OTG : \"IF path = \u002Fapi\u002Forders\u002F*\"\nALB --> FR : \"IF path = \u002Fhealth\"\n\nnote right of FR\n    Для health check ALB\n    не потрібен сервер —\n    Fixed Response повертає\n    200 миттєво\nend note\n\n@enduml\n",[3397,10088,10089,10093,10097,10101,10105,10110,10114,10119,10123,10128,10133,10138,10142,10146,10151,10156,10161,10165,10169,10174,10178,10183,10187,10192,10197,10202,10206,10211,10216,10221,10226,10231,10235,10239],{"__ignoreMap":3395},[3400,10090,10091],{"class":3402,"line":3403},[3400,10092,3406],{},[3400,10094,10095],{"class":3402,"line":3409},[3400,10096,3412],{},[3400,10098,10099],{"class":3402,"line":3415},[3400,10100,3418],{},[3400,10102,10103],{"class":3402,"line":3421},[3400,10104,3425],{"emptyLinePlaceholder":3424},[3400,10106,10107],{"class":3402,"line":3428},[3400,10108,10109],{},"actor \"Client\" as C\n",[3400,10111,10112],{"class":3402,"line":3434},[3400,10113,3425],{"emptyLinePlaceholder":3424},[3400,10115,10116],{"class":3402,"line":3440},[3400,10117,10118],{},"rectangle \"ALB\\napi.example.com:443\" as ALB #fef3c7\n",[3400,10120,10121],{"class":3402,"line":3446},[3400,10122,3425],{"emptyLinePlaceholder":3424},[3400,10124,10125],{"class":3402,"line":3451},[3400,10126,10127],{},"package \"UserService Target Group\\n(EC2 з UserService .NET API)\" as UTG #d1fae5 {\n",[3400,10129,10130],{"class":3402,"line":3457},[3400,10131,10132],{},"    node \"EC2 #1\" as U1 #bbf7d0\n",[3400,10134,10135],{"class":3402,"line":3463},[3400,10136,10137],{},"    node \"EC2 #2\" as U2 #bbf7d0\n",[3400,10139,10140],{"class":3402,"line":3469},[3400,10141,3496],{},[3400,10143,10144],{"class":3402,"line":3475},[3400,10145,3425],{"emptyLinePlaceholder":3424},[3400,10147,10148],{"class":3402,"line":3481},[3400,10149,10150],{},"package \"OrderService Target Group\\n(EC2 з OrderService .NET API)\" as OTG #dbeafe {\n",[3400,10152,10153],{"class":3402,"line":3487},[3400,10154,10155],{},"    node \"EC2 #3\" as O1 #bfdbfe\n",[3400,10157,10158],{"class":3402,"line":3493},[3400,10159,10160],{},"    node \"EC2 #4\" as O2 #bfdbfe\n",[3400,10162,10163],{"class":3402,"line":3499},[3400,10164,3496],{},[3400,10166,10167],{"class":3402,"line":3504},[3400,10168,3425],{"emptyLinePlaceholder":3424},[3400,10170,10171],{"class":3402,"line":3510},[3400,10172,10173],{},"rectangle \"Fixed Response\\n200 OK (без сервера)\" as FR #f0fdf4\n",[3400,10175,10176],{"class":3402,"line":3516},[3400,10177,3425],{"emptyLinePlaceholder":3424},[3400,10179,10180],{"class":3402,"line":3522},[3400,10181,10182],{},"C --> ALB\n",[3400,10184,10185],{"class":3402,"line":3528},[3400,10186,3425],{"emptyLinePlaceholder":3424},[3400,10188,10189],{"class":3402,"line":3534},[3400,10190,10191],{},"ALB --> UTG : \"IF path = \u002Fapi\u002Fusers\u002F*\"\n",[3400,10193,10194],{"class":3402,"line":3540},[3400,10195,10196],{},"ALB --> OTG : \"IF path = \u002Fapi\u002Forders\u002F*\"\n",[3400,10198,10199],{"class":3402,"line":3545},[3400,10200,10201],{},"ALB --> FR : \"IF path = \u002Fhealth\"\n",[3400,10203,10204],{"class":3402,"line":3550},[3400,10205,3425],{"emptyLinePlaceholder":3424},[3400,10207,10208],{"class":3402,"line":3556},[3400,10209,10210],{},"note right of FR\n",[3400,10212,10213],{"class":3402,"line":3562},[3400,10214,10215],{},"    Для health check ALB\n",[3400,10217,10218],{"class":3402,"line":3568},[3400,10219,10220],{},"    не потрібен сервер —\n",[3400,10222,10223],{"class":3402,"line":3574},[3400,10224,10225],{},"    Fixed Response повертає\n",[3400,10227,10228],{"class":3402,"line":3580},[3400,10229,10230],{},"    200 миттєво\n",[3400,10232,10233],{"class":3402,"line":3586},[3400,10234,3618],{},[3400,10236,10237],{"class":3402,"line":3591},[3400,10238,3425],{"emptyLinePlaceholder":3424},[3400,10240,10241],{"class":3402,"line":3597},[3400,10242,3624],{},[3353,10244,10245],{},[3359,10246,10247],{},"Налаштування Path-based routing:",[8462,10249,10250,10335],{},[8465,10251,10252],{"label":8467},[8469,10253,10254,10265,10277,10284,10297,10310,10319,10328],{},[3656,10255,8473,10256,8914,10259,8481,10262],{},[3359,10257,10258],{},"Load Balancers",[3397,10260,10261],{},"dotnet-api-alb",[3359,10263,10264],{},"Listeners",[3656,10266,10267,10268,8914,10271,8914,10274],{},"Оберіть ",[3359,10269,10270],{},"HTTPS:443",[3359,10272,10273],{},"Manage rules",[3359,10275,10276],{},"Add rule",[3656,10278,10279,5214,10281],{},[3359,10280,9753],{},[3397,10282,10283],{},"users-service-rule",[3656,10285,10286,8914,10289,8914,10291,8914,10294],{},[3359,10287,10288],{},"Add condition",[3359,10290,6792],{},[3397,10292,10293],{},"\u002Fapi\u002Fusers\u002F*",[3359,10295,10296],{},"Confirm",[3656,10298,10299,8914,10302,8477,10305,8914,10308],{},[3359,10300,10301],{},"Add action",[3359,10303,10304],{},"Forward to target groups",[3397,10306,10307],{},"user-service-tg",[3359,10309,10296],{},[3656,10311,10312,5214,10315,8914,10317],{},[3359,10313,10314],{},"Priority:",[3397,10316,9010],{},[3359,10318,8545],{},[3656,10320,10321,10322,10325,10326],{},"Повторіть для OrderService: path ",[3397,10323,10324],{},"\u002Fapi\u002Forders\u002F*",", priority ",[3397,10327,9018],{},[3656,10329,10330,10331,10334],{},"Переконайтесь, що ",[3397,10332,10333],{},"default rule"," (пріоритет найнижчий) залишається як fallback",[8465,10336,10337],{"label":8548},[3390,10338,10340],{"className":4740,"code":10339,"language":4742,"meta":3395,"style":3395},"# ARN HTTPS Listener\nHTTPS_LISTENER_ARN=$(aws elbv2 describe-listeners \\\n    --load-balancer-arn \"$ALB_ARN\" \\\n    --query \"Listeners[?Port==\\`443\\`].ListenerArn\" \\\n    --output text --region \"$REGION\")\n\n# Правило: \u002Fapi\u002Fusers\u002F* → user-service-tg\naws elbv2 create-rule \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --priority 10 \\\n    --conditions '[{\"Field\":\"path-pattern\",\"Values\":[\"\u002Fapi\u002Fusers\u002F*\"]}]' \\\n    --actions '[{\"Type\":\"forward\",\"TargetGroupArn\":\"'\"$USER_TG_ARN\"'\"}]' \\\n    --region \"$REGION\"\n\n# Правило: \u002Fapi\u002Forders\u002F* → order-service-tg\naws elbv2 create-rule \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --priority 20 \\\n    --conditions '[{\"Field\":\"path-pattern\",\"Values\":[\"\u002Fapi\u002Forders\u002F*\"]}]' \\\n    --actions '[{\"Type\":\"forward\",\"TargetGroupArn\":\"'\"$ORDER_TG_ARN\"'\"}]' \\\n    --region \"$REGION\"\n\n# Правило: \u002Fhealth → Fixed Response 200\naws elbv2 create-rule \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --priority 5 \\\n    --conditions '[{\"Field\":\"path-pattern\",\"Values\":[\"\u002Fhealth\"]}]' \\\n    --actions '[{\"Type\":\"fixed-response\",\"FixedResponseConfig\":{\"StatusCode\":\"200\",\"ContentType\":\"text\u002Fplain\",\"MessageBody\":\"OK\"}}]' \\\n    --region \"$REGION\"\n",[3397,10341,10342,10347,10363,10377,10396,10414,10418,10423,10434,10447,10457,10467,10483,10493,10497,10502,10512,10524,10533,10542,10555,10565,10569,10574,10584,10596,10604,10613,10622],{"__ignoreMap":3395},[3400,10343,10344],{"class":3402,"line":3403},[3400,10345,10346],{"class":4749},"# ARN HTTPS Listener\n",[3400,10348,10349,10352,10354,10356,10358,10361],{"class":3402,"line":3409},[3400,10350,10351],{"class":4779},"HTTPS_LISTENER_ARN",[3400,10353,9180],{"class":5649},[3400,10355,4756],{"class":4755},[3400,10357,4760],{"class":4759},[3400,10359,10360],{"class":4759}," describe-listeners",[3400,10362,4767],{"class":4766},[3400,10364,10365,10368,10370,10373,10375],{"class":3402,"line":3415},[3400,10366,10367],{"class":4772},"    --load-balancer-arn",[3400,10369,4776],{"class":4759},[3400,10371,10372],{"class":4779},"$ALB_ARN",[3400,10374,4783],{"class":4759},[3400,10376,4767],{"class":4766},[3400,10378,10379,10381,10384,10387,10389,10391,10394],{"class":3402,"line":3421},[3400,10380,5331],{"class":4772},[3400,10382,10383],{"class":4759}," \"Listeners[?Port==",[3400,10385,10386],{"class":4766},"\\`",[3400,10388,4047],{"class":4759},[3400,10390,10386],{"class":4766},[3400,10392,10393],{"class":4759},"].ListenerArn\"",[3400,10395,4767],{"class":4766},[3400,10397,10398,10400,10402,10405,10407,10410,10412],{"class":3402,"line":3428},[3400,10399,10005],{"class":4772},[3400,10401,9282],{"class":4759},[3400,10403,10404],{"class":4772}," --region",[3400,10406,4776],{"class":4759},[3400,10408,10409],{"class":4779},"$REGION",[3400,10411,4783],{"class":4759},[3400,10413,7300],{"class":5649},[3400,10415,10416],{"class":3402,"line":3434},[3400,10417,3425],{"emptyLinePlaceholder":3424},[3400,10419,10420],{"class":3402,"line":3440},[3400,10421,10422],{"class":4749},"# Правило: \u002Fapi\u002Fusers\u002F* → user-service-tg\n",[3400,10424,10425,10427,10429,10432],{"class":3402,"line":3446},[3400,10426,4756],{"class":4755},[3400,10428,4760],{"class":4759},[3400,10430,10431],{"class":4759}," create-rule",[3400,10433,4767],{"class":4766},[3400,10435,10436,10438,10440,10443,10445],{"class":3402,"line":3451},[3400,10437,5698],{"class":4772},[3400,10439,4776],{"class":4759},[3400,10441,10442],{"class":4779},"$HTTPS_LISTENER_ARN",[3400,10444,4783],{"class":4759},[3400,10446,4767],{"class":4766},[3400,10448,10449,10452,10455],{"class":3402,"line":3457},[3400,10450,10451],{"class":4772},"    --priority",[3400,10453,10454],{"class":4800}," 10",[3400,10456,4767],{"class":4766},[3400,10458,10459,10462,10465],{"class":3402,"line":3463},[3400,10460,10461],{"class":4772},"    --conditions",[3400,10463,10464],{"class":4759}," '[{\"Field\":\"path-pattern\",\"Values\":[\"\u002Fapi\u002Fusers\u002F*\"]}]'",[3400,10466,4767],{"class":4766},[3400,10468,10469,10472,10475,10478,10481],{"class":3402,"line":3469},[3400,10470,10471],{"class":4772},"    --actions",[3400,10473,10474],{"class":4759}," '[{\"Type\":\"forward\",\"TargetGroupArn\":\"'\"",[3400,10476,10477],{"class":4779},"$USER_TG_ARN",[3400,10479,10480],{"class":4759},"\"'\"}]'",[3400,10482,4767],{"class":4766},[3400,10484,10485,10487,10489,10491],{"class":3402,"line":3475},[3400,10486,4818],{"class":4772},[3400,10488,4776],{"class":4759},[3400,10490,10409],{"class":4779},[3400,10492,9301],{"class":4759},[3400,10494,10495],{"class":3402,"line":3481},[3400,10496,3425],{"emptyLinePlaceholder":3424},[3400,10498,10499],{"class":3402,"line":3487},[3400,10500,10501],{"class":4749},"# Правило: \u002Fapi\u002Forders\u002F* → order-service-tg\n",[3400,10503,10504,10506,10508,10510],{"class":3402,"line":3493},[3400,10505,4756],{"class":4755},[3400,10507,4760],{"class":4759},[3400,10509,10431],{"class":4759},[3400,10511,4767],{"class":4766},[3400,10513,10514,10516,10518,10520,10522],{"class":3402,"line":3499},[3400,10515,5698],{"class":4772},[3400,10517,4776],{"class":4759},[3400,10519,10442],{"class":4779},[3400,10521,4783],{"class":4759},[3400,10523,4767],{"class":4766},[3400,10525,10526,10528,10531],{"class":3402,"line":3504},[3400,10527,10451],{"class":4772},[3400,10529,10530],{"class":4800}," 20",[3400,10532,4767],{"class":4766},[3400,10534,10535,10537,10540],{"class":3402,"line":3510},[3400,10536,10461],{"class":4772},[3400,10538,10539],{"class":4759}," '[{\"Field\":\"path-pattern\",\"Values\":[\"\u002Fapi\u002Forders\u002F*\"]}]'",[3400,10541,4767],{"class":4766},[3400,10543,10544,10546,10548,10551,10553],{"class":3402,"line":3516},[3400,10545,10471],{"class":4772},[3400,10547,10474],{"class":4759},[3400,10549,10550],{"class":4779},"$ORDER_TG_ARN",[3400,10552,10480],{"class":4759},[3400,10554,4767],{"class":4766},[3400,10556,10557,10559,10561,10563],{"class":3402,"line":3522},[3400,10558,4818],{"class":4772},[3400,10560,4776],{"class":4759},[3400,10562,10409],{"class":4779},[3400,10564,9301],{"class":4759},[3400,10566,10567],{"class":3402,"line":3528},[3400,10568,3425],{"emptyLinePlaceholder":3424},[3400,10570,10571],{"class":3402,"line":3534},[3400,10572,10573],{"class":4749},"# Правило: \u002Fhealth → Fixed Response 200\n",[3400,10575,10576,10578,10580,10582],{"class":3402,"line":3540},[3400,10577,4756],{"class":4755},[3400,10579,4760],{"class":4759},[3400,10581,10431],{"class":4759},[3400,10583,4767],{"class":4766},[3400,10585,10586,10588,10590,10592,10594],{"class":3402,"line":3545},[3400,10587,5698],{"class":4772},[3400,10589,4776],{"class":4759},[3400,10591,10442],{"class":4779},[3400,10593,4783],{"class":4759},[3400,10595,4767],{"class":4766},[3400,10597,10598,10600,10602],{"class":3402,"line":3550},[3400,10599,10451],{"class":4772},[3400,10601,9871],{"class":4800},[3400,10603,4767],{"class":4766},[3400,10605,10606,10608,10611],{"class":3402,"line":3556},[3400,10607,10461],{"class":4772},[3400,10609,10610],{"class":4759}," '[{\"Field\":\"path-pattern\",\"Values\":[\"\u002Fhealth\"]}]'",[3400,10612,4767],{"class":4766},[3400,10614,10615,10617,10620],{"class":3402,"line":3562},[3400,10616,10471],{"class":4772},[3400,10618,10619],{"class":4759}," '[{\"Type\":\"fixed-response\",\"FixedResponseConfig\":{\"StatusCode\":\"200\",\"ContentType\":\"text\u002Fplain\",\"MessageBody\":\"OK\"}}]'",[3400,10621,4767],{"class":4766},[3400,10623,10624,10626,10628,10630],{"class":3402,"line":3568},[3400,10625,4818],{"class":4772},[3400,10627,4776],{"class":4759},[3400,10629,10409],{"class":4779},[3400,10631,9301],{"class":4759},[5202,10633,10635,10644,10647,10654,10665,10676,10700,10712,10716],{"title":10634},"create-rule → Path-based routing",[5206,10636,10638,5214,10641],{"className":10637},[3402],[3400,10639,5213],{"className":10640},[5212],[3359,10642,10643],{},"aws elbv2 create-rule --priority 10 --conditions '[{\"Field\":\"path-pattern\"...}]' ...",[5206,10645,5232],{"className":10646},[3402],[5206,10648,8670,10650,6105],{"className":10649},[3402],[3400,10651,10653],{"className":10652},[5240],"\"Rules\"",[5206,10655,5236,10657,5255,10661,5261],{"className":10656},[3402],[3400,10658,10660],{"className":10659},[5240],"\"RuleArn\"",[3400,10662,10664],{"className":10663},[5259],"\"arn:aws:elasticloadbalancing:eu-central-1:...:rule\u002Fapp\u002F...\"",[5206,10666,5236,10668,5255,10672,5261],{"className":10667},[3402],[3400,10669,10671],{"className":10670},[5240],"\"Priority\"",[3400,10673,10675],{"className":10674},[6151],"\"10\"",[5206,10677,5236,10679,6105,10683,5255,10687,4031,10691,5242,10695,10699],{"className":10678},[3402],[3400,10680,10682],{"className":10681},[5240],"\"Conditions\"",[3400,10684,10686],{"className":10685},[5240],"\"Field\"",[3400,10688,10690],{"className":10689},[5259],"\"path-pattern\"",[3400,10692,10694],{"className":10693},[5240],"\"Values\"",[3400,10696,10698],{"className":10697},[5259],"\"\u002Fapi\u002Fusers\u002F*\"","]}],",[5206,10701,5236,10703,5255,10707],{"className":10702},[3402],[3400,10704,10706],{"className":10705},[5240],"\"IsDefault\"",[3400,10708,10711],{"className":10709},[10710],"text-red-400","false",[5206,10713,10715],{"className":10714},[3402],"  }]",[5206,10717,5284],{"className":10718},[3402],[3824,10720,10722],{"id":10721},"host-based-routing","Host-based Routing",[3353,10724,10725,10728,10729,10732],{},[3359,10726,10727],{},"Host-based routing"," маршрутизує запити на основі значення HTTP-заголовка ",[3397,10730,10731],{},"Host",", що дозволяє обслуговувати кілька доменних імен за єдиним ALB:",[3390,10734,10739],{"className":10735,"code":10737,"language":10738},[10736],"language-text","ALB (один балансувальник, одна IP-адреса)\n├── api.example.com    → API Target Group      (EC2 з .NET Web API)\n├── admin.example.com  → Admin Target Group    (EC2 з Admin Dashboard)\n└── static.example.com → Fixed Response 301   → redirect to CloudFront\n","text",[3397,10740,10737],{"__ignoreMap":3395},[3353,10742,10743],{},"Це суттєво знижує витрати: замість окремого ALB для кожного сервісу — один ALB для всіх.",[3353,10745,10746],{},[3359,10747,10748],{},"Налаштування Host-based routing:",[8462,10750,10751,10812],{},[8465,10752,10753],{"label":8467},[8469,10754,10755,10767,10774,10785,10801],{},[3656,10756,8473,10757,8914,10759,8481,10761,8914,10763,8914,10765],{},[3359,10758,10258],{},[3397,10760,10261],{},[3359,10762,10264],{},[3359,10764,10270],{},[3359,10766,10273],{},[3656,10768,10769,8950,10771],{},[3359,10770,10276],{},[3397,10772,10773],{},"api-host-rule",[3656,10775,10776,8914,10778,8914,10781,8914,10783],{},[3359,10777,10288],{},[3359,10779,10780],{},"Host header",[3397,10782,4100],{},[3359,10784,10296],{},[3656,10786,10787,8914,10789,8914,10791,8914,10794,10796,10797,8914,10799],{},[3359,10788,10301],{},[3359,10790,4056],{},[3397,10792,10793],{},"api-tg",[3359,10795,10296],{}," → Priority: ",[3397,10798,9010],{},[3359,10800,8545],{},[3656,10802,10803,10804,8914,10807,10325,10810],{},"Повторіть для ",[3397,10805,10806],{},"admin.example.com",[3397,10808,10809],{},"admin-tg",[3397,10811,9018],{},[8465,10813,10814],{"label":8548},[3390,10815,10817],{"className":4740,"code":10816,"language":4742,"meta":3395,"style":3395},"# Маршрутизація за host-header: api.example.com → api-tg\naws elbv2 create-rule \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --priority 10 \\\n    --conditions '[{\"Field\":\"host-header\",\"Values\":[\"api.example.com\"]}]' \\\n    --actions '[{\"Type\":\"forward\",\"TargetGroupArn\":\"'\"$API_TG_ARN\"'\"}]' \\\n    --region \"$REGION\"\n\n# admin.example.com → admin-tg\naws elbv2 create-rule \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --priority 20 \\\n    --conditions '[{\"Field\":\"host-header\",\"Values\":[\"admin.example.com\"]}]' \\\n    --actions '[{\"Type\":\"forward\",\"TargetGroupArn\":\"'\"$ADMIN_TG_ARN\"'\"}]' \\\n    --region \"$REGION\"\n\n# static.example.com → redirect to CloudFront\naws elbv2 create-rule \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --priority 30 \\\n    --conditions '[{\"Field\":\"host-header\",\"Values\":[\"static.example.com\"]}]' \\\n    --actions '[{\"Type\":\"redirect\",\"RedirectConfig\":{\"Host\":\"d1234abcd.cloudfront.net\",\"Protocol\":\"HTTPS\",\"StatusCode\":\"HTTP_301\"}}]' \\\n    --region \"$REGION\"\n",[3397,10818,10819,10824,10834,10846,10854,10863,10876,10886,10890,10895,10905,10917,10925,10934,10947,10957,10961,10966,10976,10988,10997,11006,11015],{"__ignoreMap":3395},[3400,10820,10821],{"class":3402,"line":3403},[3400,10822,10823],{"class":4749},"# Маршрутизація за host-header: api.example.com → api-tg\n",[3400,10825,10826,10828,10830,10832],{"class":3402,"line":3409},[3400,10827,4756],{"class":4755},[3400,10829,4760],{"class":4759},[3400,10831,10431],{"class":4759},[3400,10833,4767],{"class":4766},[3400,10835,10836,10838,10840,10842,10844],{"class":3402,"line":3415},[3400,10837,5698],{"class":4772},[3400,10839,4776],{"class":4759},[3400,10841,10442],{"class":4779},[3400,10843,4783],{"class":4759},[3400,10845,4767],{"class":4766},[3400,10847,10848,10850,10852],{"class":3402,"line":3421},[3400,10849,10451],{"class":4772},[3400,10851,10454],{"class":4800},[3400,10853,4767],{"class":4766},[3400,10855,10856,10858,10861],{"class":3402,"line":3428},[3400,10857,10461],{"class":4772},[3400,10859,10860],{"class":4759}," '[{\"Field\":\"host-header\",\"Values\":[\"api.example.com\"]}]'",[3400,10862,4767],{"class":4766},[3400,10864,10865,10867,10869,10872,10874],{"class":3402,"line":3434},[3400,10866,10471],{"class":4772},[3400,10868,10474],{"class":4759},[3400,10870,10871],{"class":4779},"$API_TG_ARN",[3400,10873,10480],{"class":4759},[3400,10875,4767],{"class":4766},[3400,10877,10878,10880,10882,10884],{"class":3402,"line":3440},[3400,10879,4818],{"class":4772},[3400,10881,4776],{"class":4759},[3400,10883,10409],{"class":4779},[3400,10885,9301],{"class":4759},[3400,10887,10888],{"class":3402,"line":3446},[3400,10889,3425],{"emptyLinePlaceholder":3424},[3400,10891,10892],{"class":3402,"line":3451},[3400,10893,10894],{"class":4749},"# admin.example.com → admin-tg\n",[3400,10896,10897,10899,10901,10903],{"class":3402,"line":3457},[3400,10898,4756],{"class":4755},[3400,10900,4760],{"class":4759},[3400,10902,10431],{"class":4759},[3400,10904,4767],{"class":4766},[3400,10906,10907,10909,10911,10913,10915],{"class":3402,"line":3463},[3400,10908,5698],{"class":4772},[3400,10910,4776],{"class":4759},[3400,10912,10442],{"class":4779},[3400,10914,4783],{"class":4759},[3400,10916,4767],{"class":4766},[3400,10918,10919,10921,10923],{"class":3402,"line":3469},[3400,10920,10451],{"class":4772},[3400,10922,10530],{"class":4800},[3400,10924,4767],{"class":4766},[3400,10926,10927,10929,10932],{"class":3402,"line":3475},[3400,10928,10461],{"class":4772},[3400,10930,10931],{"class":4759}," '[{\"Field\":\"host-header\",\"Values\":[\"admin.example.com\"]}]'",[3400,10933,4767],{"class":4766},[3400,10935,10936,10938,10940,10943,10945],{"class":3402,"line":3481},[3400,10937,10471],{"class":4772},[3400,10939,10474],{"class":4759},[3400,10941,10942],{"class":4779},"$ADMIN_TG_ARN",[3400,10944,10480],{"class":4759},[3400,10946,4767],{"class":4766},[3400,10948,10949,10951,10953,10955],{"class":3402,"line":3487},[3400,10950,4818],{"class":4772},[3400,10952,4776],{"class":4759},[3400,10954,10409],{"class":4779},[3400,10956,9301],{"class":4759},[3400,10958,10959],{"class":3402,"line":3493},[3400,10960,3425],{"emptyLinePlaceholder":3424},[3400,10962,10963],{"class":3402,"line":3499},[3400,10964,10965],{"class":4749},"# static.example.com → redirect to CloudFront\n",[3400,10967,10968,10970,10972,10974],{"class":3402,"line":3504},[3400,10969,4756],{"class":4755},[3400,10971,4760],{"class":4759},[3400,10973,10431],{"class":4759},[3400,10975,4767],{"class":4766},[3400,10977,10978,10980,10982,10984,10986],{"class":3402,"line":3510},[3400,10979,5698],{"class":4772},[3400,10981,4776],{"class":4759},[3400,10983,10442],{"class":4779},[3400,10985,4783],{"class":4759},[3400,10987,4767],{"class":4766},[3400,10989,10990,10992,10995],{"class":3402,"line":3516},[3400,10991,10451],{"class":4772},[3400,10993,10994],{"class":4800}," 30",[3400,10996,4767],{"class":4766},[3400,10998,10999,11001,11004],{"class":3402,"line":3522},[3400,11000,10461],{"class":4772},[3400,11002,11003],{"class":4759}," '[{\"Field\":\"host-header\",\"Values\":[\"static.example.com\"]}]'",[3400,11005,4767],{"class":4766},[3400,11007,11008,11010,11013],{"class":3402,"line":3528},[3400,11009,10471],{"class":4772},[3400,11011,11012],{"class":4759}," '[{\"Type\":\"redirect\",\"RedirectConfig\":{\"Host\":\"d1234abcd.cloudfront.net\",\"Protocol\":\"HTTPS\",\"StatusCode\":\"HTTP_301\"}}]'",[3400,11014,4767],{"class":4766},[3400,11016,11017,11019,11021,11023],{"class":3402,"line":3534},[3400,11018,4818],{"class":4772},[3400,11020,4776],{"class":4759},[3400,11022,10409],{"class":4779},[3400,11024,9301],{"class":4759},[3626,11026],{},[3348,11028,11030],{"id":11029},"sticky-sessions-для-aspnet-сесій","Sticky Sessions для ASP.NET сесій",[3824,11032,11034],{"id":11033},"проблема-стану-у-розподіленій-системі","Проблема стану у розподіленій системі",[3353,11036,11037,11038,11041,11042,11045],{},"При використанні ASP.NET Core з ",[3397,11039,11040],{},"AddSession()"," без розподіленого сховища (у конфігурації за замовчуванням), дані сесії зберігаються ",[3359,11043,11044],{},"в оперативній пам'яті конкретного EC2 instance",". При балансуванні навантаження між кількома instances виникає критична проблема: якщо користувач після авторизації потрапляє на Instance 1, де зберігається його сесія, а наступний запит ALB направляє на Instance 2 — Instance 2 не знає про існування цієї сесії, і користувач виглядає як неавторизований.",[3387,11047,11048],{},[3390,11049,11051],{"className":3392,"code":11050,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"User\\n(authenticated)\" as U\n\nrectangle \"ALB\" as ALB #fef3c7\n\npackage \"ASG\" {\n    node \"EC2 #1\\nSession: {userId: 42}\\n← сесія тут\" as EC1 #bbf7d0\n    node \"EC2 #2\\nNo session for this user\\n← сесія відсутня\" as EC2 #fca5a5\n}\n\nU --> ALB : \"Request 1\\n(login → session created)\"\nALB --> EC1 : \"→ EC2 #1\\n✓ Session created\"\n\nU --> ALB : \"Request 2\\n(view profile)\"\nALB --> EC2 : \"→ EC2 #2 (Round Robin!)\\n✗ Session not found!\"\n\nnote bottom of EC2\n    ПРОБЛЕМА: без sticky sessions\n    ALB може направити запит\n    на instance без сесії\nend note\n\n@enduml\n",[3397,11052,11053,11057,11061,11065,11069,11074,11078,11083,11087,11092,11097,11102,11106,11110,11115,11120,11124,11129,11134,11138,11143,11148,11153,11158,11162,11166],{"__ignoreMap":3395},[3400,11054,11055],{"class":3402,"line":3403},[3400,11056,3406],{},[3400,11058,11059],{"class":3402,"line":3409},[3400,11060,3412],{},[3400,11062,11063],{"class":3402,"line":3415},[3400,11064,3418],{},[3400,11066,11067],{"class":3402,"line":3421},[3400,11068,3425],{"emptyLinePlaceholder":3424},[3400,11070,11071],{"class":3402,"line":3428},[3400,11072,11073],{},"actor \"User\\n(authenticated)\" as U\n",[3400,11075,11076],{"class":3402,"line":3434},[3400,11077,3425],{"emptyLinePlaceholder":3424},[3400,11079,11080],{"class":3402,"line":3440},[3400,11081,11082],{},"rectangle \"ALB\" as ALB #fef3c7\n",[3400,11084,11085],{"class":3402,"line":3446},[3400,11086,3425],{"emptyLinePlaceholder":3424},[3400,11088,11089],{"class":3402,"line":3451},[3400,11090,11091],{},"package \"ASG\" {\n",[3400,11093,11094],{"class":3402,"line":3457},[3400,11095,11096],{},"    node \"EC2 #1\\nSession: {userId: 42}\\n← сесія тут\" as EC1 #bbf7d0\n",[3400,11098,11099],{"class":3402,"line":3463},[3400,11100,11101],{},"    node \"EC2 #2\\nNo session for this user\\n← сесія відсутня\" as EC2 #fca5a5\n",[3400,11103,11104],{"class":3402,"line":3469},[3400,11105,3496],{},[3400,11107,11108],{"class":3402,"line":3475},[3400,11109,3425],{"emptyLinePlaceholder":3424},[3400,11111,11112],{"class":3402,"line":3481},[3400,11113,11114],{},"U --> ALB : \"Request 1\\n(login → session created)\"\n",[3400,11116,11117],{"class":3402,"line":3487},[3400,11118,11119],{},"ALB --> EC1 : \"→ EC2 #1\\n✓ Session created\"\n",[3400,11121,11122],{"class":3402,"line":3493},[3400,11123,3425],{"emptyLinePlaceholder":3424},[3400,11125,11126],{"class":3402,"line":3499},[3400,11127,11128],{},"U --> ALB : \"Request 2\\n(view profile)\"\n",[3400,11130,11131],{"class":3402,"line":3504},[3400,11132,11133],{},"ALB --> EC2 : \"→ EC2 #2 (Round Robin!)\\n✗ Session not found!\"\n",[3400,11135,11136],{"class":3402,"line":3510},[3400,11137,3425],{"emptyLinePlaceholder":3424},[3400,11139,11140],{"class":3402,"line":3516},[3400,11141,11142],{},"note bottom of EC2\n",[3400,11144,11145],{"class":3402,"line":3522},[3400,11146,11147],{},"    ПРОБЛЕМА: без sticky sessions\n",[3400,11149,11150],{"class":3402,"line":3528},[3400,11151,11152],{},"    ALB може направити запит\n",[3400,11154,11155],{"class":3402,"line":3534},[3400,11156,11157],{},"    на instance без сесії\n",[3400,11159,11160],{"class":3402,"line":3540},[3400,11161,3618],{},[3400,11163,11164],{"class":3402,"line":3545},[3400,11165,3425],{"emptyLinePlaceholder":3424},[3400,11167,11168],{"class":3402,"line":3550},[3400,11169,3624],{},[3387,11171,11172],{},[3390,11173,11175],{"className":3392,"code":11174,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"User\\n(authenticated)\" as U\n\nrectangle \"ALB\\n(Sticky Sessions увімкнено\\nAWSALB cookie)\" as ALB #fef3c7\n\npackage \"ASG\" {\n    node \"EC2 #1\\nSession: {userId: 42}\" as EC1 #bbf7d0\n    node \"EC2 #2\" as EC2 #e5e7eb\n}\n\nU --> ALB : \"Request 1\\n(login)\"\nALB --> EC1 : \"→ EC2 #1\\nSet-Cookie: AWSALB=abc123; Path=\u002F; HttpOnly\"\n\nU --> ALB : \"Request 2\\nCookie: AWSALB=abc123\"\nALB --> EC1 : \"→ EC2 #1 (завжди!)\\nALB читає cookie та\\nнаправляє на той самий instance\"\n\nnote bottom\n    РІШЕННЯ (тимчасове): sticky sessions.\n    ПРАВИЛЬНЕ рішення: розподілений кеш (Redis)\nend note\n\n@enduml\n",[3397,11176,11177,11181,11185,11189,11193,11197,11201,11206,11210,11214,11219,11224,11228,11232,11237,11242,11246,11251,11256,11260,11264,11269,11274,11278,11282],{"__ignoreMap":3395},[3400,11178,11179],{"class":3402,"line":3403},[3400,11180,3406],{},[3400,11182,11183],{"class":3402,"line":3409},[3400,11184,3412],{},[3400,11186,11187],{"class":3402,"line":3415},[3400,11188,3418],{},[3400,11190,11191],{"class":3402,"line":3421},[3400,11192,3425],{"emptyLinePlaceholder":3424},[3400,11194,11195],{"class":3402,"line":3428},[3400,11196,11073],{},[3400,11198,11199],{"class":3402,"line":3434},[3400,11200,3425],{"emptyLinePlaceholder":3424},[3400,11202,11203],{"class":3402,"line":3440},[3400,11204,11205],{},"rectangle \"ALB\\n(Sticky Sessions увімкнено\\nAWSALB cookie)\" as ALB #fef3c7\n",[3400,11207,11208],{"class":3402,"line":3446},[3400,11209,3425],{"emptyLinePlaceholder":3424},[3400,11211,11212],{"class":3402,"line":3451},[3400,11213,11091],{},[3400,11215,11216],{"class":3402,"line":3457},[3400,11217,11218],{},"    node \"EC2 #1\\nSession: {userId: 42}\" as EC1 #bbf7d0\n",[3400,11220,11221],{"class":3402,"line":3463},[3400,11222,11223],{},"    node \"EC2 #2\" as EC2 #e5e7eb\n",[3400,11225,11226],{"class":3402,"line":3469},[3400,11227,3496],{},[3400,11229,11230],{"class":3402,"line":3475},[3400,11231,3425],{"emptyLinePlaceholder":3424},[3400,11233,11234],{"class":3402,"line":3481},[3400,11235,11236],{},"U --> ALB : \"Request 1\\n(login)\"\n",[3400,11238,11239],{"class":3402,"line":3487},[3400,11240,11241],{},"ALB --> EC1 : \"→ EC2 #1\\nSet-Cookie: AWSALB=abc123; Path=\u002F; HttpOnly\"\n",[3400,11243,11244],{"class":3402,"line":3493},[3400,11245,3425],{"emptyLinePlaceholder":3424},[3400,11247,11248],{"class":3402,"line":3499},[3400,11249,11250],{},"U --> ALB : \"Request 2\\nCookie: AWSALB=abc123\"\n",[3400,11252,11253],{"class":3402,"line":3504},[3400,11254,11255],{},"ALB --> EC1 : \"→ EC2 #1 (завжди!)\\nALB читає cookie та\\nнаправляє на той самий instance\"\n",[3400,11257,11258],{"class":3402,"line":3510},[3400,11259,3425],{"emptyLinePlaceholder":3424},[3400,11261,11262],{"class":3402,"line":3516},[3400,11263,9695],{},[3400,11265,11266],{"class":3402,"line":3522},[3400,11267,11268],{},"    РІШЕННЯ (тимчасове): sticky sessions.\n",[3400,11270,11271],{"class":3402,"line":3528},[3400,11272,11273],{},"    ПРАВИЛЬНЕ рішення: розподілений кеш (Redis)\n",[3400,11275,11276],{"class":3402,"line":3534},[3400,11277,3618],{},[3400,11279,11280],{"class":3402,"line":3540},[3400,11281,3425],{"emptyLinePlaceholder":3424},[3400,11283,11284],{"class":3402,"line":3545},[3400,11285,3624],{},[3824,11287,11289],{"id":11288},"sticky-sessions-через-alb-cookie","Sticky Sessions через ALB Cookie",[3353,11291,11292,11295,11296,11299],{},[3359,11293,11294],{},"Sticky Sessions (Session Affinity)"," — механізм, за якого ALB встановлює спеціальний cookie (",[3397,11297,11298],{},"AWSALB",") при першому запиті користувача та у всіх наступних запитах направляє його до того самого instance, що обробляв перший запит.",[8462,11301,11302,11318],{},[8465,11303,11304,11305,8914,11308,8914,11311,11314,11315],{"label":8467},"Target Groups → оберіть TG → вкладка ",[3359,11306,11307],{},"Attributes",[3359,11309,11310],{},"Edit",[3359,11312,11313],{},"Stickiness"," → Enable → Type: Load balancer generated cookie → Duration: 1 day → ",[3359,11316,11317],{},"Save changes",[8465,11319,11320],{"label":8548},[3390,11321,11323],{"className":4740,"code":11322,"language":4742,"meta":3395,"style":3395},"aws elbv2 modify-target-group-attributes \\\n    --target-group-arn arn:aws:elasticloadbalancing:eu-central-1:123456789012:targetgroup\u002Fmy-api-tg\u002Fxxx \\\n    --attributes \\\n        Key=stickiness.enabled,Value=true \\\n        Key=stickiness.type,Value=lb_cookie \\\n        Key=stickiness.lb_cookie.duration_seconds,Value=86400 \\\n    --region eu-central-1\n",[3397,11324,11325,11335,11344,11350,11359,11366,11376],{"__ignoreMap":3395},[3400,11326,11327,11329,11331,11333],{"class":3402,"line":3403},[3400,11328,4756],{"class":4755},[3400,11330,4760],{"class":4759},[3400,11332,5166],{"class":4759},[3400,11334,4767],{"class":4766},[3400,11336,11337,11339,11342],{"class":3402,"line":3409},[3400,11338,4773],{"class":4772},[3400,11340,11341],{"class":4759}," arn:aws:elasticloadbalancing:eu-central-1:123456789012:targetgroup\u002Fmy-api-tg\u002Fxxx",[3400,11343,4767],{"class":4766},[3400,11345,11346,11348],{"class":3402,"line":3415},[3400,11347,5185],{"class":4772},[3400,11349,4767],{"class":4766},[3400,11351,11352,11355,11357],{"class":3402,"line":3421},[3400,11353,11354],{"class":4759},"        Key=stickiness.enabled,Value=",[3400,11356,4026],{"class":4772},[3400,11358,4767],{"class":4766},[3400,11360,11361,11364],{"class":3402,"line":3428},[3400,11362,11363],{"class":4759},"        Key=stickiness.type,Value=lb_cookie",[3400,11365,4767],{"class":4766},[3400,11367,11368,11371,11374],{"class":3402,"line":3434},[3400,11369,11370],{"class":4759},"        Key=stickiness.lb_cookie.duration_seconds,Value=",[3400,11372,11373],{"class":4800},"86400",[3400,11375,4767],{"class":4766},[3400,11377,11378,11380],{"class":3402,"line":3440},[3400,11379,4818],{"class":4772},[3400,11381,4821],{"class":4759},[11383,11384,11385,11388,11389,11392],"warning",{},[3359,11386,11387],{},"Sticky sessions — це технічний борг, а не вирішення проблеми."," Sticky sessions знижують ефективність балансування: якщо один instance обслуговує «важких» користувачів з довгими сесіями, він стає перевантаженим, тоді як інші instances простоюють. Єдине правильне рішення — перейти на ",[3359,11390,11391],{},"розподілений кеш"," для зберігання стану сесії.",[3824,11394,11396],{"id":11395},"правильна-реалізація-розподілені-сесії-через-redis","Правильна реалізація: розподілені сесії через Redis",[3390,11398,11400],{"className":6863,"code":11399,"filename":6865,"language":6866,"meta":3395,"style":3395},"\u002F\u002F Правильний підхід для production: сесії у Redis\n\u002F\u002F Будь-який instance може обробити запит будь-якого користувача\nbuilder.Services.AddStackExchangeRedisCache(options =>\n{\n    options.Configuration = builder.Configuration[\"Redis:ConnectionString\"];\n    options.InstanceName = \"MyApp:Sessions:\";\n});\n\nbuilder.Services.AddSession(options =>\n{\n    options.IdleTimeout = TimeSpan.FromMinutes(30);\n    options.Cookie.HttpOnly = true;\n    options.Cookie.IsEssential = true;\n    options.Cookie.SameSite = SameSiteMode.Strict;\n});\n\n\u002F\u002F Middleware\napp.UseSession();\n",[3397,11401,11402,11407,11412,11433,11437,11461,11477,11481,11485,11504,11508,11534,11554,11573,11598,11602,11606,11611],{"__ignoreMap":3395},[3400,11403,11404],{"class":3402,"line":3403},[3400,11405,11406],{"class":4749},"\u002F\u002F Правильний підхід для production: сесії у Redis\n",[3400,11408,11409],{"class":3402,"line":3409},[3400,11410,11411],{"class":4749},"\u002F\u002F Будь-який instance може обробити запит будь-якого користувача\n",[3400,11413,11414,11416,11418,11420,11422,11425,11427,11430],{"class":3402,"line":3415},[3400,11415,6982],{"class":4779},[3400,11417,6827],{"class":5649},[3400,11419,6987],{"class":4779},[3400,11421,6827],{"class":5649},[3400,11423,11424],{"class":4755},"AddStackExchangeRedisCache",[3400,11426,6962],{"class":5649},[3400,11428,11429],{"class":4779},"options",[3400,11431,11432],{"class":5649}," =>\n",[3400,11434,11435],{"class":3402,"line":3421},[3400,11436,6670],{"class":5649},[3400,11438,11439,11442,11444,11446,11448,11450,11452,11454,11456,11458],{"class":3402,"line":3428},[3400,11440,11441],{"class":4779},"    options",[3400,11443,6827],{"class":5649},[3400,11445,7025],{"class":4779},[3400,11447,6951],{"class":5649},[3400,11449,6982],{"class":4779},[3400,11451,6827],{"class":5649},[3400,11453,7025],{"class":4779},[3400,11455,5350],{"class":5649},[3400,11457,7126],{"class":4759},[3400,11459,11460],{"class":5649},"];\n",[3400,11462,11463,11465,11467,11470,11472,11475],{"class":3402,"line":3434},[3400,11464,11441],{"class":4779},[3400,11466,6827],{"class":5649},[3400,11468,11469],{"class":4779},"InstanceName",[3400,11471,6951],{"class":5649},[3400,11473,11474],{"class":4759},"\"MyApp:Sessions:\"",[3400,11476,6896],{"class":5649},[3400,11478,11479],{"class":3402,"line":3440},[3400,11480,7773],{"class":5649},[3400,11482,11483],{"class":3402,"line":3446},[3400,11484,3425],{"emptyLinePlaceholder":3424},[3400,11486,11487,11489,11491,11493,11495,11498,11500,11502],{"class":3402,"line":3451},[3400,11488,6982],{"class":4779},[3400,11490,6827],{"class":5649},[3400,11492,6987],{"class":4779},[3400,11494,6827],{"class":5649},[3400,11496,11497],{"class":4755},"AddSession",[3400,11499,6962],{"class":5649},[3400,11501,11429],{"class":4779},[3400,11503,11432],{"class":5649},[3400,11505,11506],{"class":3402,"line":3457},[3400,11507,6670],{"class":5649},[3400,11509,11510,11512,11514,11517,11519,11522,11524,11527,11529,11532],{"class":3402,"line":3463},[3400,11511,11441],{"class":4779},[3400,11513,6827],{"class":5649},[3400,11515,11516],{"class":4779},"IdleTimeout",[3400,11518,6951],{"class":5649},[3400,11520,11521],{"class":4779},"TimeSpan",[3400,11523,6827],{"class":5649},[3400,11525,11526],{"class":4755},"FromMinutes",[3400,11528,6962],{"class":5649},[3400,11530,11531],{"class":4800},"30",[3400,11533,6968],{"class":5649},[3400,11535,11536,11538,11540,11543,11545,11548,11550,11552],{"class":3402,"line":3469},[3400,11537,11441],{"class":4779},[3400,11539,6827],{"class":5649},[3400,11541,11542],{"class":4779},"Cookie",[3400,11544,6827],{"class":5649},[3400,11546,11547],{"class":4779},"HttpOnly",[3400,11549,6951],{"class":5649},[3400,11551,4026],{"class":4772},[3400,11553,6896],{"class":5649},[3400,11555,11556,11558,11560,11562,11564,11567,11569,11571],{"class":3402,"line":3475},[3400,11557,11441],{"class":4779},[3400,11559,6827],{"class":5649},[3400,11561,11542],{"class":4779},[3400,11563,6827],{"class":5649},[3400,11565,11566],{"class":4779},"IsEssential",[3400,11568,6951],{"class":5649},[3400,11570,4026],{"class":4772},[3400,11572,6896],{"class":5649},[3400,11574,11575,11577,11579,11581,11583,11586,11588,11591,11593,11596],{"class":3402,"line":3481},[3400,11576,11441],{"class":4779},[3400,11578,6827],{"class":5649},[3400,11580,11542],{"class":4779},[3400,11582,6827],{"class":5649},[3400,11584,11585],{"class":4779},"SameSite",[3400,11587,6951],{"class":5649},[3400,11589,11590],{"class":4779},"SameSiteMode",[3400,11592,6827],{"class":5649},[3400,11594,11595],{"class":4779},"Strict",[3400,11597,6896],{"class":5649},[3400,11599,11600],{"class":3402,"line":3487},[3400,11601,7773],{"class":5649},[3400,11603,11604],{"class":3402,"line":3493},[3400,11605,3425],{"emptyLinePlaceholder":3424},[3400,11607,11608],{"class":3402,"line":3499},[3400,11609,11610],{"class":4749},"\u002F\u002F Middleware\n",[3400,11612,11613,11615,11617,11620],{"class":3402,"line":3504},[3400,11614,7449],{"class":4779},[3400,11616,6827],{"class":5649},[3400,11618,11619],{"class":4755},"UseSession",[3400,11621,7231],{"class":5649},[3626,11623],{},[3348,11625,11627],{"id":11626},"практичний-приклад-alb-asg-https-від-а-до-я","Практичний приклад: ALB + ASG + HTTPS від А до Я",[3353,11629,11630],{},"У цьому розділі ми побудуємо повноцінну production-готову інфраструктуру покроково: C# проєкт → Базовий EC2 інстанс → Custom AMI з .NET API → SSL-сертифікат → Security Groups → Target Group → ALB → Launch Template → Auto Scaling Group.",[3824,11632,11634],{"id":11633},"крок-1-створення-c-проєкту-для-тестування","Крок 1: Створення C# проєкту для тестування",[3353,11636,11637,11638,11641,11642,11644,11645,11648],{},"Для демонстрації роботи Application Load Balancer нам потрібен вебзастосунок, який буде повертати інформацію про сервер, що обробляє запит. Ми створимо простий API на ASP.NET Core (.NET 10), який виводить ім'я хоста (інстансу), HTTP-заголовки (включаючи ",[3397,11639,11640],{},"X-Forwarded-*"," від балансера), та має ",[3397,11643,6796],{}," endpoint для Target Group, а також ",[3397,11646,11647],{},"\u002Fstress"," для симуляції навантаження на процесор.",[11650,11651,11652,11774],"code-tree",{},[3390,11653,11658],{"className":11654,"code":11655,"filename":11656,"language":11657,"meta":3395,"style":3395},"language-xml shiki shiki-themes light-plus dark-plus dark-plus","\u003CProject Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  \u003CPropertyGroup>\n    \u003CTargetFramework>net10.0\u003C\u002FTargetFramework>\n    \u003CNullable>enable\u003C\u002FNullable>\n    \u003CImplicitUsings>enable\u003C\u002FImplicitUsings>\n  \u003C\u002FPropertyGroup>\n\n\u003C\u002FProject>\n","ec2lab-api.csproj","xml",[3397,11659,11660,11683,11687,11697,11718,11736,11753,11762,11766],{"__ignoreMap":3395},[3400,11661,11662,11666,11670,11674,11676,11680],{"class":3402,"line":3403},[3400,11663,11665],{"class":11664},"s0P7L","\u003C",[3400,11667,11669],{"class":11668},"sKtos","Project",[3400,11671,11673],{"class":11672},"sa4r_"," Sdk",[3400,11675,5650],{"class":5649},[3400,11677,11679],{"class":11678},"su9tN","\"Microsoft.NET.Sdk.Web\"",[3400,11681,11682],{"class":11664},">\n",[3400,11684,11685],{"class":3402,"line":3409},[3400,11686,3425],{"emptyLinePlaceholder":3424},[3400,11688,11689,11692,11695],{"class":3402,"line":3415},[3400,11690,11691],{"class":11664},"  \u003C",[3400,11693,11694],{"class":11668},"PropertyGroup",[3400,11696,11682],{"class":11664},[3400,11698,11699,11702,11705,11708,11711,11714,11716],{"class":3402,"line":3421},[3400,11700,11701],{"class":11664},"    \u003C",[3400,11703,11704],{"class":11668},"TargetFramework",[3400,11706,11707],{"class":11664},">",[3400,11709,11710],{"class":5649},"net10.0",[3400,11712,11713],{"class":11664},"\u003C\u002F",[3400,11715,11704],{"class":11668},[3400,11717,11682],{"class":11664},[3400,11719,11720,11722,11725,11727,11730,11732,11734],{"class":3402,"line":3428},[3400,11721,11701],{"class":11664},[3400,11723,11724],{"class":11668},"Nullable",[3400,11726,11707],{"class":11664},[3400,11728,11729],{"class":5649},"enable",[3400,11731,11713],{"class":11664},[3400,11733,11724],{"class":11668},[3400,11735,11682],{"class":11664},[3400,11737,11738,11740,11743,11745,11747,11749,11751],{"class":3402,"line":3434},[3400,11739,11701],{"class":11664},[3400,11741,11742],{"class":11668},"ImplicitUsings",[3400,11744,11707],{"class":11664},[3400,11746,11729],{"class":5649},[3400,11748,11713],{"class":11664},[3400,11750,11742],{"class":11668},[3400,11752,11682],{"class":11664},[3400,11754,11755,11758,11760],{"class":3402,"line":3440},[3400,11756,11757],{"class":11664},"  \u003C\u002F",[3400,11759,11694],{"class":11668},[3400,11761,11682],{"class":11664},[3400,11763,11764],{"class":3402,"line":3446},[3400,11765,3425],{"emptyLinePlaceholder":3424},[3400,11767,11768,11770,11772],{"class":3402,"line":3451},[3400,11769,11713],{"class":11664},[3400,11771,11669],{"class":11668},[3400,11773,11682],{"class":11664},[3390,11775,11777],{"className":6863,"code":11776,"filename":6865,"language":6866,"meta":3395,"style":3395},"using System.Diagnostics;\nusing System.Net;\n\nvar builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\n\u002F\u002F Лічильник запитів для цього інстансу\nlong requestCount = 0;\n\napp.MapGet(\"\u002F\", (HttpContext context) =>\n{\n    Interlocked.Increment(ref requestCount);\n\n    var hostname = Dns.GetHostName();\n    var ips = Dns.GetHostAddresses(hostname)\n        .Where(ip => ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)\n        .Select(ip => ip.ToString());\n    \n    \u002F\u002F Отримуємо специфічні заголовки від Load Balancer\n    var headers = context.Request.Headers\n        .Where(h => h.Key.StartsWith(\"X-Forwarded-\") || h.Key == \"Host\" || h.Key == \"User-Agent\")\n        .ToDictionary(h => h.Key, h => h.Value.ToString());\n\n    return Results.Ok(new\n    {\n        Message = \"Hello from load-balanced EC2 instance!\",\n        ServerName = hostname,\n        LocalIPs = ips,\n        RequestCount = requestCount,\n        DotnetVersion = Environment.Version.ToString(),\n        Headers = headers,\n        Time = DateTime.UtcNow\n    });\n});\n\napp.MapGet(\"\u002Fhealth\", () => Results.Ok(new { Status = \"Healthy\", Server = Dns.GetHostName() }));\n\napp.MapGet(\"\u002Fstress\", (int? seconds) =>\n{\n    var sec = seconds ?? 30;\n    \n    \u002F\u002F Запускаємо фонову задачу для навантаження CPU\n    _ = Task.Run(() =>\n    {\n        var watch = Stopwatch.StartNew();\n        while (watch.ElapsedMilliseconds \u003C sec * 1000)\n        {\n            \u002F\u002F Симуляція обчислень\n        }\n    });\n\n    return Results.Ok(new { Message = $\"CPU stress started for {sec} seconds\", Server = Dns.GetHostName() });\n});\n\napp.Run(\"http:\u002F\u002F0.0.0.0:5000\");\n",[3397,11778,11779,11792,11805,11809,11829,11845,11849,11854,11868,11872,11896,11900,11919,11923,11943,11966,12012,12033,12038,12043,12064,12123,12160,12164,12181,12185,12197,12208,12220,12232,12253,12265,12279,12284,12288,12292,12343,12347,12373,12377,12395,12399,12404,12421,12425,12444,12473,12477,12482,12486,12490,12494,12543,12547,12551],{"__ignoreMap":3395},[3400,11780,11781,11783,11786,11788,11790],{"class":3402,"line":3403},[3400,11782,6874],{"class":6873},[3400,11784,11785],{"class":6877}," System",[3400,11787,6827],{"class":5649},[3400,11789,6888],{"class":6877},[3400,11791,6896],{"class":5649},[3400,11793,11794,11796,11798,11800,11803],{"class":3402,"line":3409},[3400,11795,6874],{"class":6873},[3400,11797,11785],{"class":6877},[3400,11799,6827],{"class":5649},[3400,11801,11802],{"class":6877},"Net",[3400,11804,6896],{"class":5649},[3400,11806,11807],{"class":3402,"line":3415},[3400,11808,3425],{"emptyLinePlaceholder":3424},[3400,11810,11811,11813,11815,11817,11819,11821,11823,11825,11827],{"class":3402,"line":3421},[3400,11812,6945],{"class":4772},[3400,11814,6948],{"class":4779},[3400,11816,6951],{"class":5649},[3400,11818,6954],{"class":4779},[3400,11820,6827],{"class":5649},[3400,11822,6959],{"class":4755},[3400,11824,6962],{"class":5649},[3400,11826,6965],{"class":4779},[3400,11828,6968],{"class":5649},[3400,11830,11831,11833,11835,11837,11839,11841,11843],{"class":3402,"line":3428},[3400,11832,6945],{"class":4772},[3400,11834,7416],{"class":4779},[3400,11836,6951],{"class":5649},[3400,11838,6982],{"class":4779},[3400,11840,6827],{"class":5649},[3400,11842,7425],{"class":4755},[3400,11844,7231],{"class":5649},[3400,11846,11847],{"class":3402,"line":3434},[3400,11848,3425],{"emptyLinePlaceholder":3424},[3400,11850,11851],{"class":3402,"line":3440},[3400,11852,11853],{"class":4749},"\u002F\u002F Лічильник запитів для цього інстансу\n",[3400,11855,11856,11859,11862,11864,11866],{"class":3402,"line":3446},[3400,11857,11858],{"class":4772},"long",[3400,11860,11861],{"class":4779}," requestCount",[3400,11863,6951],{"class":5649},[3400,11865,8104],{"class":4800},[3400,11867,6896],{"class":5649},[3400,11869,11870],{"class":3402,"line":3451},[3400,11871,3425],{"emptyLinePlaceholder":3424},[3400,11873,11874,11876,11878,11881,11883,11886,11889,11892,11894],{"class":3402,"line":3457},[3400,11875,7449],{"class":4779},[3400,11877,6827],{"class":5649},[3400,11879,11880],{"class":4755},"MapGet",[3400,11882,6962],{"class":5649},[3400,11884,11885],{"class":4759},"\"\u002F\"",[3400,11887,11888],{"class":5649},", (",[3400,11890,11891],{"class":6877},"HttpContext",[3400,11893,7746],{"class":4779},[3400,11895,7529],{"class":5649},[3400,11897,11898],{"class":3402,"line":3463},[3400,11899,6670],{"class":5649},[3400,11901,11902,11905,11907,11910,11912,11915,11917],{"class":3402,"line":3469},[3400,11903,11904],{"class":4779},"    Interlocked",[3400,11906,6827],{"class":5649},[3400,11908,11909],{"class":4755},"Increment",[3400,11911,6962],{"class":5649},[3400,11913,11914],{"class":4772},"ref",[3400,11916,11861],{"class":4779},[3400,11918,6968],{"class":5649},[3400,11920,11921],{"class":3402,"line":3475},[3400,11922,3425],{"emptyLinePlaceholder":3424},[3400,11924,11925,11928,11931,11933,11936,11938,11941],{"class":3402,"line":3481},[3400,11926,11927],{"class":4772},"    var",[3400,11929,11930],{"class":4779}," hostname",[3400,11932,6951],{"class":5649},[3400,11934,11935],{"class":4779},"Dns",[3400,11937,6827],{"class":5649},[3400,11939,11940],{"class":4755},"GetHostName",[3400,11942,7231],{"class":5649},[3400,11944,11945,11947,11950,11952,11954,11956,11959,11961,11964],{"class":3402,"line":3487},[3400,11946,11927],{"class":4772},[3400,11948,11949],{"class":4779}," ips",[3400,11951,6951],{"class":5649},[3400,11953,11935],{"class":4779},[3400,11955,6827],{"class":5649},[3400,11957,11958],{"class":4755},"GetHostAddresses",[3400,11960,6962],{"class":5649},[3400,11962,11963],{"class":4779},"hostname",[3400,11965,7300],{"class":5649},[3400,11967,11968,11971,11974,11976,11978,11980,11982,11984,11987,11990,11992,11994,11996,11998,12001,12003,12005,12007,12010],{"class":3402,"line":3493},[3400,11969,11970],{"class":5649},"        .",[3400,11972,11973],{"class":4755},"Where",[3400,11975,6962],{"class":5649},[3400,11977,4178],{"class":4779},[3400,11979,7490],{"class":5649},[3400,11981,4178],{"class":4779},[3400,11983,6827],{"class":5649},[3400,11985,11986],{"class":4779},"AddressFamily",[3400,11988,11989],{"class":5649}," == ",[3400,11991,7571],{"class":4779},[3400,11993,6827],{"class":5649},[3400,11995,11802],{"class":4779},[3400,11997,6827],{"class":5649},[3400,11999,12000],{"class":4779},"Sockets",[3400,12002,6827],{"class":5649},[3400,12004,11986],{"class":4779},[3400,12006,6827],{"class":5649},[3400,12008,12009],{"class":4779},"InterNetwork",[3400,12011,7300],{"class":5649},[3400,12013,12014,12016,12018,12020,12022,12024,12026,12028,12030],{"class":3402,"line":3499},[3400,12015,11970],{"class":5649},[3400,12017,7668],{"class":4755},[3400,12019,6962],{"class":5649},[3400,12021,4178],{"class":4779},[3400,12023,7490],{"class":5649},[3400,12025,4178],{"class":4779},[3400,12027,6827],{"class":5649},[3400,12029,7621],{"class":4755},[3400,12031,12032],{"class":5649},"());\n",[3400,12034,12035],{"class":3402,"line":3504},[3400,12036,12037],{"class":5649},"    \n",[3400,12039,12040],{"class":3402,"line":3510},[3400,12041,12042],{"class":4749},"    \u002F\u002F Отримуємо специфічні заголовки від Load Balancer\n",[3400,12044,12045,12047,12050,12052,12054,12056,12059,12061],{"class":3402,"line":3516},[3400,12046,11927],{"class":4772},[3400,12048,12049],{"class":4779}," headers",[3400,12051,6951],{"class":5649},[3400,12053,7521],{"class":4779},[3400,12055,6827],{"class":5649},[3400,12057,12058],{"class":4779},"Request",[3400,12060,6827],{"class":5649},[3400,12062,12063],{"class":4779},"Headers\n",[3400,12065,12066,12068,12070,12072,12075,12077,12079,12081,12083,12085,12088,12090,12093,12096,12098,12100,12102,12104,12107,12110,12112,12114,12116,12118,12121],{"class":3402,"line":3522},[3400,12067,11970],{"class":5649},[3400,12069,11973],{"class":4755},[3400,12071,6962],{"class":5649},[3400,12073,12074],{"class":4779},"h",[3400,12076,7490],{"class":5649},[3400,12078,12074],{"class":4779},[3400,12080,6827],{"class":5649},[3400,12082,7698],{"class":4779},[3400,12084,6827],{"class":5649},[3400,12086,12087],{"class":4755},"StartsWith",[3400,12089,6962],{"class":5649},[3400,12091,12092],{"class":4759},"\"X-Forwarded-\"",[3400,12094,12095],{"class":5649},") || ",[3400,12097,12074],{"class":4779},[3400,12099,6827],{"class":5649},[3400,12101,7698],{"class":4779},[3400,12103,11989],{"class":5649},[3400,12105,12106],{"class":4759},"\"Host\"",[3400,12108,12109],{"class":5649}," || ",[3400,12111,12074],{"class":4779},[3400,12113,6827],{"class":5649},[3400,12115,7698],{"class":4779},[3400,12117,11989],{"class":5649},[3400,12119,12120],{"class":4759},"\"User-Agent\"",[3400,12122,7300],{"class":5649},[3400,12124,12125,12127,12130,12132,12134,12136,12138,12140,12142,12144,12146,12148,12150,12152,12154,12156,12158],{"class":3402,"line":3528},[3400,12126,11970],{"class":5649},[3400,12128,12129],{"class":4755},"ToDictionary",[3400,12131,6962],{"class":5649},[3400,12133,12074],{"class":4779},[3400,12135,7490],{"class":5649},[3400,12137,12074],{"class":4779},[3400,12139,6827],{"class":5649},[3400,12141,7698],{"class":4779},[3400,12143,4031],{"class":5649},[3400,12145,12074],{"class":4779},[3400,12147,7490],{"class":5649},[3400,12149,12074],{"class":4779},[3400,12151,6827],{"class":5649},[3400,12153,7715],{"class":4779},[3400,12155,6827],{"class":5649},[3400,12157,7621],{"class":4755},[3400,12159,12032],{"class":5649},[3400,12161,12162],{"class":3402,"line":3534},[3400,12163,3425],{"emptyLinePlaceholder":3424},[3400,12165,12166,12169,12172,12174,12177,12179],{"class":3402,"line":3540},[3400,12167,12168],{"class":6873},"    return",[3400,12170,12171],{"class":4779}," Results",[3400,12173,6827],{"class":5649},[3400,12175,12176],{"class":4755},"Ok",[3400,12178,6962],{"class":5649},[3400,12180,7596],{"class":4772},[3400,12182,12183],{"class":3402,"line":3545},[3400,12184,7204],{"class":5649},[3400,12186,12187,12190,12192,12195],{"class":3402,"line":3550},[3400,12188,12189],{"class":4779},"        Message",[3400,12191,6951],{"class":5649},[3400,12193,12194],{"class":4759},"\"Hello from load-balanced EC2 instance!\"",[3400,12196,5743],{"class":5649},[3400,12198,12199,12202,12204,12206],{"class":3402,"line":3556},[3400,12200,12201],{"class":4779},"        ServerName",[3400,12203,6951],{"class":5649},[3400,12205,11963],{"class":4779},[3400,12207,5743],{"class":5649},[3400,12209,12210,12213,12215,12218],{"class":3402,"line":3562},[3400,12211,12212],{"class":4779},"        LocalIPs",[3400,12214,6951],{"class":5649},[3400,12216,12217],{"class":4779},"ips",[3400,12219,5743],{"class":5649},[3400,12221,12222,12225,12227,12230],{"class":3402,"line":3568},[3400,12223,12224],{"class":4779},"        RequestCount",[3400,12226,6951],{"class":5649},[3400,12228,12229],{"class":4779},"requestCount",[3400,12231,5743],{"class":5649},[3400,12233,12234,12237,12239,12242,12244,12247,12249,12251],{"class":3402,"line":3574},[3400,12235,12236],{"class":4779},"        DotnetVersion",[3400,12238,6951],{"class":5649},[3400,12240,12241],{"class":4779},"Environment",[3400,12243,6827],{"class":5649},[3400,12245,12246],{"class":4779},"Version",[3400,12248,6827],{"class":5649},[3400,12250,7621],{"class":4755},[3400,12252,7624],{"class":5649},[3400,12254,12255,12258,12260,12263],{"class":3402,"line":3580},[3400,12256,12257],{"class":4779},"        Headers",[3400,12259,6951],{"class":5649},[3400,12261,12262],{"class":4779},"headers",[3400,12264,5743],{"class":5649},[3400,12266,12267,12270,12272,12274,12276],{"class":3402,"line":3586},[3400,12268,12269],{"class":4779},"        Time",[3400,12271,6951],{"class":5649},[3400,12273,7635],{"class":4779},[3400,12275,6827],{"class":5649},[3400,12277,12278],{"class":4779},"UtcNow\n",[3400,12280,12281],{"class":3402,"line":3591},[3400,12282,12283],{"class":5649},"    });\n",[3400,12285,12286],{"class":3402,"line":3597},[3400,12287,7773],{"class":5649},[3400,12289,12290],{"class":3402,"line":3603},[3400,12291,3425],{"emptyLinePlaceholder":3424},[3400,12293,12294,12296,12298,12300,12302,12304,12306,12309,12311,12313,12315,12317,12320,12322,12324,12327,12329,12332,12334,12336,12338,12340],{"class":3402,"line":3609},[3400,12295,7449],{"class":4779},[3400,12297,6827],{"class":5649},[3400,12299,11880],{"class":4755},[3400,12301,6962],{"class":5649},[3400,12303,6693],{"class":4759},[3400,12305,7391],{"class":5649},[3400,12307,12308],{"class":4779},"Results",[3400,12310,6827],{"class":5649},[3400,12312,12176],{"class":4755},[3400,12314,6962],{"class":5649},[3400,12316,7225],{"class":4772},[3400,12318,12319],{"class":5649}," { ",[3400,12321,7616],{"class":4779},[3400,12323,6951],{"class":5649},[3400,12325,12326],{"class":4759},"\"Healthy\"",[3400,12328,4031],{"class":5649},[3400,12330,12331],{"class":4779},"Server",[3400,12333,6951],{"class":5649},[3400,12335,11935],{"class":4779},[3400,12337,6827],{"class":5649},[3400,12339,11940],{"class":4755},[3400,12341,12342],{"class":5649},"() }));\n",[3400,12344,12345],{"class":3402,"line":3615},[3400,12346,3425],{"emptyLinePlaceholder":3424},[3400,12348,12349,12351,12353,12355,12357,12360,12362,12365,12368,12371],{"class":3402,"line":3621},[3400,12350,7449],{"class":4779},[3400,12352,6827],{"class":5649},[3400,12354,11880],{"class":4755},[3400,12356,6962],{"class":5649},[3400,12358,12359],{"class":4759},"\"\u002Fstress\"",[3400,12361,11888],{"class":5649},[3400,12363,12364],{"class":4772},"int",[3400,12366,12367],{"class":5649},"? ",[3400,12369,12370],{"class":4779},"seconds",[3400,12372,7529],{"class":5649},[3400,12374,12375],{"class":3402,"line":5589},[3400,12376,6670],{"class":5649},[3400,12378,12379,12381,12384,12386,12388,12391,12393],{"class":3402,"line":5594},[3400,12380,11927],{"class":4772},[3400,12382,12383],{"class":4779}," sec",[3400,12385,6951],{"class":5649},[3400,12387,12370],{"class":4779},[3400,12389,12390],{"class":5649}," ?? ",[3400,12392,11531],{"class":4800},[3400,12394,6896],{"class":5649},[3400,12396,12397],{"class":3402,"line":5600},[3400,12398,12037],{"class":5649},[3400,12400,12401],{"class":3402,"line":5606},[3400,12402,12403],{"class":4749},"    \u002F\u002F Запускаємо фонову задачу для навантаження CPU\n",[3400,12405,12406,12409,12411,12414,12416,12418],{"class":3402,"line":5612},[3400,12407,12408],{"class":4779},"    _",[3400,12410,6951],{"class":5649},[3400,12412,12413],{"class":4779},"Task",[3400,12415,6827],{"class":5649},[3400,12417,7866],{"class":4755},[3400,12419,12420],{"class":5649},"(() =>\n",[3400,12422,12423],{"class":3402,"line":5617},[3400,12424,7204],{"class":5649},[3400,12426,12427,12429,12432,12434,12437,12439,12442],{"class":3402,"line":5622},[3400,12428,7563],{"class":4772},[3400,12430,12431],{"class":4779}," watch",[3400,12433,6951],{"class":5649},[3400,12435,12436],{"class":4779},"Stopwatch",[3400,12438,6827],{"class":5649},[3400,12440,12441],{"class":4755},"StartNew",[3400,12443,7231],{"class":5649},[3400,12445,12446,12449,12451,12454,12456,12459,12462,12465,12468,12471],{"class":3402,"line":7434},[3400,12447,12448],{"class":6873},"        while",[3400,12450,7518],{"class":5649},[3400,12452,12453],{"class":4779},"watch",[3400,12455,6827],{"class":5649},[3400,12457,12458],{"class":4779},"ElapsedMilliseconds",[3400,12460,12461],{"class":5649}," \u003C ",[3400,12463,12464],{"class":4779},"sec",[3400,12466,12467],{"class":5649}," * ",[3400,12469,12470],{"class":4800},"1000",[3400,12472,7300],{"class":5649},[3400,12474,12475],{"class":3402,"line":7440},[3400,12476,5720],{"class":5649},[3400,12478,12479],{"class":3402,"line":7446},[3400,12480,12481],{"class":4749},"            \u002F\u002F Симуляція обчислень\n",[3400,12483,12484],{"class":3402,"line":7468},[3400,12485,5846],{"class":5649},[3400,12487,12488],{"class":3402,"line":7473},[3400,12489,12283],{"class":5649},[3400,12491,12492],{"class":3402,"line":7479},[3400,12493,3425],{"emptyLinePlaceholder":3424},[3400,12495,12496,12498,12500,12502,12504,12506,12508,12510,12513,12515,12518,12521,12523,12525,12528,12530,12532,12534,12536,12538,12540],{"class":3402,"line":7497},[3400,12497,12168],{"class":6873},[3400,12499,12171],{"class":4779},[3400,12501,6827],{"class":5649},[3400,12503,12176],{"class":4755},[3400,12505,6962],{"class":5649},[3400,12507,7225],{"class":4772},[3400,12509,12319],{"class":5649},[3400,12511,12512],{"class":4779},"Message",[3400,12514,6951],{"class":5649},[3400,12516,12517],{"class":4759},"$\"CPU stress started for ",[3400,12519,5232],{"class":12520},"sD7JJ",[3400,12522,12464],{"class":4779},[3400,12524,5284],{"class":12520},[3400,12526,12527],{"class":4759}," seconds\"",[3400,12529,4031],{"class":5649},[3400,12531,12331],{"class":4779},[3400,12533,6951],{"class":5649},[3400,12535,11935],{"class":4779},[3400,12537,6827],{"class":5649},[3400,12539,11940],{"class":4755},[3400,12541,12542],{"class":5649},"() });\n",[3400,12544,12545],{"class":3402,"line":7502},[3400,12546,7773],{"class":5649},[3400,12548,12549],{"class":3402,"line":7508},[3400,12550,3425],{"emptyLinePlaceholder":3424},[3400,12552,12553,12555,12557,12559,12561,12564],{"class":3402,"line":7532},[3400,12554,7449],{"class":4779},[3400,12556,6827],{"class":5649},[3400,12558,7866],{"class":4755},[3400,12560,6962],{"class":5649},[3400,12562,12563],{"class":4759},"\"http:\u002F\u002F0.0.0.0:5000\"",[3400,12565,6968],{"class":5649},[3626,12567],{},[3824,12569,12571],{"id":12570},"крок-2-створення-та-налаштування-базового-ec2-інстансу","Крок 2: Створення та налаштування базового EC2 інстансу",[3353,12573,12574],{},"Тепер нам потрібно створити базовий EC2 інстанс, встановити на нього .NET 10, розгорнути наш C# застосунок та налаштувати його запуск як фонову службу (systemd service). З цього налаштованого інстансу ми згодом зробимо Custom AMI.",[12576,12577,12578,12582,12585],"steps",{},[3824,12579,12581],{"id":12580},"_1-створення-ec2-інстансу","1. Створення EC2 інстансу",[3353,12583,12584],{},"Створіть віртуальну машину (Instance) в AWS, яка буде нашою базою для AMI.",[8462,12586,12587],{},[8465,12588,12589],{"label":8467},[8469,12590,12591,12603,12609,12616,12623,12630,12665],{},[3656,12592,12593,12594,8914,12597,8914,12600,6827],{},"Перейдіть до ",[3359,12595,12596],{},"EC2 Dashboard",[3359,12598,12599],{},"Instances",[3359,12601,12602],{},"Launch instances",[3656,12604,12605,12606,6827],{},"Вкажіть ім'я: ",[3397,12607,12608],{},"dotnet-api-base",[3656,12610,12611,12612,12615],{},"Оберіть AMI: ",[3359,12613,12614],{},"Ubuntu Server 24.04 LTS"," (64-bit x86).",[3656,12617,12618,12619,12622],{},"Instance type: ",[3359,12620,12621],{},"t3.micro"," (входить до Free Tier).",[3656,12624,12625,12626,12629],{},"Key pair: оберіть вашу наявну пару ",[3397,12627,12628],{},"ec2-lab-key"," або створіть нову.",[3656,12631,12632,12633],{},"Security Groups (Налаштування мережі):\n",[3653,12634,12635,12641],{},[3656,12636,12637,12638,6827],{},"Створіть нову Security Group ",[3397,12639,12640],{},"dotnet-api-base-sg",[3656,12642,12643,12644],{},"Додайте Inbound правила:\n",[3653,12645,12646,12656],{},[3656,12647,12648,12651,12652,12655],{},[3359,12649,12650],{},"SSH (порт 22)"," → джерело: ",[8530,12653,12654],{},"My IP"," (для безпечного підключення).",[3656,12657,12658,12651,12661,12664],{},[3359,12659,12660],{},"Custom TCP (порт 5000)",[8530,12662,12663],{},"Anywhere IPv4 (0.0.0.0\u002F0)"," (тимчасово для тестування з інтернету).",[3656,12666,12667,12668,6827],{},"Натисніть ",[3359,12669,12670],{},"Launch instance",[8465,12672,12673],{"label":8548},[3390,12674,12676],{"className":4740,"code":12675,"language":4742,"meta":3395,"style":3395},"REGION=\"eu-central-1\"\nKEY_NAME=\"ec2-lab-key\"\n\n# 1. Отримання default VPC ID\nVPC_ID=$(aws ec2 describe-vpcs \\\n    --filters \"Name=isDefault,Values=true\" \\\n    --query \"Vpcs[0].VpcId\" --output text --region \"$REGION\")\n\n# 2. Створення тимчасової Security Group для налаштування\nBASE_SG=$(aws ec2 create-security-group \\\n    --group-name dotnet-api-base-sg \\\n    --description \"Temporary SG for EC2 setup\" \\\n    --vpc-id \"$VPC_ID\" \\\n    --region \"$REGION\" \\\n    --query GroupId --output text)\n\n# Дозволити SSH\nMY_IP=$(curl -s https:\u002F\u002Fcheckip.amazonaws.com)\naws ec2 authorize-security-group-ingress \\\n    --group-id \"$BASE_SG\" \\\n    --protocol tcp --port 22 \\\n    --cidr \"${MY_IP}\u002F32\" --region \"$REGION\"\n\n# Дозволити порт 5000 для тестування\naws ec2 authorize-security-group-ingress \\\n    --group-id \"$BASE_SG\" \\\n    --protocol tcp --port 5000 \\\n    --cidr 0.0.0.0\u002F0 --region \"$REGION\"\n\n# 3. Запуск інстансу Ubuntu 24.04 LTS (знайдіть актуальний AMI ID для вашого регіону)\nAMI_ID=$(aws ec2 describe-images \\\n    --owners amazon \\\n    --filters \"Name=name,Values=ubuntu\u002Fimages\u002Fhvm-ssd-gp3\u002Fubuntu-noble-24.04-amd64-server-*\" \\\n    --query \"sort_by(Images, &CreationDate)[-1].ImageId\" \\\n    --output text --region \"$REGION\")\n\nINSTANCE_ID=$(aws ec2 run-instances \\\n    --image-id \"$AMI_ID\" \\\n    --instance-type t3.micro \\\n    --key-name \"$KEY_NAME\" \\\n    --security-group-ids \"$BASE_SG\" \\\n    --tag-specifications \"ResourceType=instance,Tags=[{Key=Name,Value=dotnet-api-base}]\" \\\n    --query \"Instances[0].InstanceId\" --output text --region \"$REGION\")\n\necho \"Instance ID: $INSTANCE_ID\"\n\n# Очікування запуску та отримання Public IP\naws ec2 wait instance-running --instance-ids \"$INSTANCE_ID\" --region \"$REGION\"\nPUBLIC_IP=$(aws ec2 describe-instances \\\n    --instance-ids \"$INSTANCE_ID\" \\\n    --query \"Reservations[0].Instances[0].PublicIpAddress\" \\\n    --output text --region \"$REGION\")\n\necho \"Public IP: $PUBLIC_IP\"\n",[3397,12677,12678,12688,12698,12702,12707,12724,12734,12755,12759,12764,12780,12790,12800,12814,12826,12839,12843,12848,12866,12877,12891,12907,12928,12932,12937,12947,12959,12972,12987,12991,12996,13012,13022,13031,13040,13056,13060,13076,13090,13100,13114,13127,13137,13158,13162,13174,13178,13183,13212,13228,13241,13250,13266,13270],{"__ignoreMap":3395},[3400,12679,12680,12683,12685],{"class":3402,"line":3403},[3400,12681,12682],{"class":4779},"REGION",[3400,12684,5650],{"class":5649},[3400,12686,12687],{"class":4759},"\"eu-central-1\"\n",[3400,12689,12690,12693,12695],{"class":3402,"line":3409},[3400,12691,12692],{"class":4779},"KEY_NAME",[3400,12694,5650],{"class":5649},[3400,12696,12697],{"class":4759},"\"ec2-lab-key\"\n",[3400,12699,12700],{"class":3402,"line":3415},[3400,12701,3425],{"emptyLinePlaceholder":3424},[3400,12703,12704],{"class":3402,"line":3421},[3400,12705,12706],{"class":4749},"# 1. Отримання default VPC ID\n",[3400,12708,12709,12712,12714,12716,12719,12722],{"class":3402,"line":3428},[3400,12710,12711],{"class":4779},"VPC_ID",[3400,12713,9180],{"class":5649},[3400,12715,4756],{"class":4755},[3400,12717,12718],{"class":4759}," ec2",[3400,12720,12721],{"class":4759}," describe-vpcs",[3400,12723,4767],{"class":4766},[3400,12725,12726,12729,12732],{"class":3402,"line":3434},[3400,12727,12728],{"class":4772},"    --filters",[3400,12730,12731],{"class":4759}," \"Name=isDefault,Values=true\"",[3400,12733,4767],{"class":4766},[3400,12735,12736,12738,12741,12743,12745,12747,12749,12751,12753],{"class":3402,"line":3440},[3400,12737,5331],{"class":4772},[3400,12739,12740],{"class":4759}," \"Vpcs[0].VpcId\"",[3400,12742,9279],{"class":4772},[3400,12744,9282],{"class":4759},[3400,12746,10404],{"class":4772},[3400,12748,4776],{"class":4759},[3400,12750,10409],{"class":4779},[3400,12752,4783],{"class":4759},[3400,12754,7300],{"class":5649},[3400,12756,12757],{"class":3402,"line":3446},[3400,12758,3425],{"emptyLinePlaceholder":3424},[3400,12760,12761],{"class":3402,"line":3451},[3400,12762,12763],{"class":4749},"# 2. Створення тимчасової Security Group для налаштування\n",[3400,12765,12766,12769,12771,12773,12775,12778],{"class":3402,"line":3457},[3400,12767,12768],{"class":4779},"BASE_SG",[3400,12770,9180],{"class":5649},[3400,12772,4756],{"class":4755},[3400,12774,12718],{"class":4759},[3400,12776,12777],{"class":4759}," create-security-group",[3400,12779,4767],{"class":4766},[3400,12781,12782,12785,12788],{"class":3402,"line":3463},[3400,12783,12784],{"class":4772},"    --group-name",[3400,12786,12787],{"class":4759}," dotnet-api-base-sg",[3400,12789,4767],{"class":4766},[3400,12791,12792,12795,12798],{"class":3402,"line":3469},[3400,12793,12794],{"class":4772},"    --description",[3400,12796,12797],{"class":4759}," \"Temporary SG for EC2 setup\"",[3400,12799,4767],{"class":4766},[3400,12801,12802,12805,12807,12810,12812],{"class":3402,"line":3475},[3400,12803,12804],{"class":4772},"    --vpc-id",[3400,12806,4776],{"class":4759},[3400,12808,12809],{"class":4779},"$VPC_ID",[3400,12811,4783],{"class":4759},[3400,12813,4767],{"class":4766},[3400,12815,12816,12818,12820,12822,12824],{"class":3402,"line":3481},[3400,12817,4818],{"class":4772},[3400,12819,4776],{"class":4759},[3400,12821,10409],{"class":4779},[3400,12823,4783],{"class":4759},[3400,12825,4767],{"class":4766},[3400,12827,12828,12830,12833,12835,12837],{"class":3402,"line":3487},[3400,12829,5331],{"class":4772},[3400,12831,12832],{"class":4759}," GroupId",[3400,12834,9279],{"class":4772},[3400,12836,9282],{"class":4759},[3400,12838,7300],{"class":5649},[3400,12840,12841],{"class":3402,"line":3493},[3400,12842,3425],{"emptyLinePlaceholder":3424},[3400,12844,12845],{"class":3402,"line":3499},[3400,12846,12847],{"class":4749},"# Дозволити SSH\n",[3400,12849,12850,12853,12855,12858,12861,12864],{"class":3402,"line":3504},[3400,12851,12852],{"class":4779},"MY_IP",[3400,12854,9180],{"class":5649},[3400,12856,12857],{"class":4755},"curl",[3400,12859,12860],{"class":4772}," -s",[3400,12862,12863],{"class":4759}," https:\u002F\u002Fcheckip.amazonaws.com",[3400,12865,7300],{"class":5649},[3400,12867,12868,12870,12872,12875],{"class":3402,"line":3510},[3400,12869,4756],{"class":4755},[3400,12871,12718],{"class":4759},[3400,12873,12874],{"class":4759}," authorize-security-group-ingress",[3400,12876,4767],{"class":4766},[3400,12878,12879,12882,12884,12887,12889],{"class":3402,"line":3516},[3400,12880,12881],{"class":4772},"    --group-id",[3400,12883,4776],{"class":4759},[3400,12885,12886],{"class":4779},"$BASE_SG",[3400,12888,4783],{"class":4759},[3400,12890,4767],{"class":4766},[3400,12892,12893,12896,12899,12902,12905],{"class":3402,"line":3522},[3400,12894,12895],{"class":4772},"    --protocol",[3400,12897,12898],{"class":4759}," tcp",[3400,12900,12901],{"class":4772}," --port",[3400,12903,12904],{"class":4800}," 22",[3400,12906,4767],{"class":4766},[3400,12908,12909,12912,12915,12917,12920,12922,12924,12926],{"class":3402,"line":3528},[3400,12910,12911],{"class":4772},"    --cidr",[3400,12913,12914],{"class":4759}," \"${",[3400,12916,12852],{"class":4779},[3400,12918,12919],{"class":4759},"}\u002F32\"",[3400,12921,10404],{"class":4772},[3400,12923,4776],{"class":4759},[3400,12925,10409],{"class":4779},[3400,12927,9301],{"class":4759},[3400,12929,12930],{"class":3402,"line":3534},[3400,12931,3425],{"emptyLinePlaceholder":3424},[3400,12933,12934],{"class":3402,"line":3540},[3400,12935,12936],{"class":4749},"# Дозволити порт 5000 для тестування\n",[3400,12938,12939,12941,12943,12945],{"class":3402,"line":3545},[3400,12940,4756],{"class":4755},[3400,12942,12718],{"class":4759},[3400,12944,12874],{"class":4759},[3400,12946,4767],{"class":4766},[3400,12948,12949,12951,12953,12955,12957],{"class":3402,"line":3550},[3400,12950,12881],{"class":4772},[3400,12952,4776],{"class":4759},[3400,12954,12886],{"class":4779},[3400,12956,4783],{"class":4759},[3400,12958,4767],{"class":4766},[3400,12960,12961,12963,12965,12967,12970],{"class":3402,"line":3556},[3400,12962,12895],{"class":4772},[3400,12964,12898],{"class":4759},[3400,12966,12901],{"class":4772},[3400,12968,12969],{"class":4800}," 5000",[3400,12971,4767],{"class":4766},[3400,12973,12974,12976,12979,12981,12983,12985],{"class":3402,"line":3562},[3400,12975,12911],{"class":4772},[3400,12977,12978],{"class":4759}," 0.0.0.0\u002F0",[3400,12980,10404],{"class":4772},[3400,12982,4776],{"class":4759},[3400,12984,10409],{"class":4779},[3400,12986,9301],{"class":4759},[3400,12988,12989],{"class":3402,"line":3568},[3400,12990,3425],{"emptyLinePlaceholder":3424},[3400,12992,12993],{"class":3402,"line":3574},[3400,12994,12995],{"class":4749},"# 3. Запуск інстансу Ubuntu 24.04 LTS (знайдіть актуальний AMI ID для вашого регіону)\n",[3400,12997,12998,13001,13003,13005,13007,13010],{"class":3402,"line":3580},[3400,12999,13000],{"class":4779},"AMI_ID",[3400,13002,9180],{"class":5649},[3400,13004,4756],{"class":4755},[3400,13006,12718],{"class":4759},[3400,13008,13009],{"class":4759}," describe-images",[3400,13011,4767],{"class":4766},[3400,13013,13014,13017,13020],{"class":3402,"line":3586},[3400,13015,13016],{"class":4772},"    --owners",[3400,13018,13019],{"class":4759}," amazon",[3400,13021,4767],{"class":4766},[3400,13023,13024,13026,13029],{"class":3402,"line":3591},[3400,13025,12728],{"class":4772},[3400,13027,13028],{"class":4759}," \"Name=name,Values=ubuntu\u002Fimages\u002Fhvm-ssd-gp3\u002Fubuntu-noble-24.04-amd64-server-*\"",[3400,13030,4767],{"class":4766},[3400,13032,13033,13035,13038],{"class":3402,"line":3597},[3400,13034,5331],{"class":4772},[3400,13036,13037],{"class":4759}," \"sort_by(Images, &CreationDate)[-1].ImageId\"",[3400,13039,4767],{"class":4766},[3400,13041,13042,13044,13046,13048,13050,13052,13054],{"class":3402,"line":3603},[3400,13043,10005],{"class":4772},[3400,13045,9282],{"class":4759},[3400,13047,10404],{"class":4772},[3400,13049,4776],{"class":4759},[3400,13051,10409],{"class":4779},[3400,13053,4783],{"class":4759},[3400,13055,7300],{"class":5649},[3400,13057,13058],{"class":3402,"line":3609},[3400,13059,3425],{"emptyLinePlaceholder":3424},[3400,13061,13062,13065,13067,13069,13071,13074],{"class":3402,"line":3615},[3400,13063,13064],{"class":4779},"INSTANCE_ID",[3400,13066,9180],{"class":5649},[3400,13068,4756],{"class":4755},[3400,13070,12718],{"class":4759},[3400,13072,13073],{"class":4759}," run-instances",[3400,13075,4767],{"class":4766},[3400,13077,13078,13081,13083,13086,13088],{"class":3402,"line":3621},[3400,13079,13080],{"class":4772},"    --image-id",[3400,13082,4776],{"class":4759},[3400,13084,13085],{"class":4779},"$AMI_ID",[3400,13087,4783],{"class":4759},[3400,13089,4767],{"class":4766},[3400,13091,13092,13095,13098],{"class":3402,"line":5589},[3400,13093,13094],{"class":4772},"    --instance-type",[3400,13096,13097],{"class":4759}," t3.micro",[3400,13099,4767],{"class":4766},[3400,13101,13102,13105,13107,13110,13112],{"class":3402,"line":5594},[3400,13103,13104],{"class":4772},"    --key-name",[3400,13106,4776],{"class":4759},[3400,13108,13109],{"class":4779},"$KEY_NAME",[3400,13111,4783],{"class":4759},[3400,13113,4767],{"class":4766},[3400,13115,13116,13119,13121,13123,13125],{"class":3402,"line":5600},[3400,13117,13118],{"class":4772},"    --security-group-ids",[3400,13120,4776],{"class":4759},[3400,13122,12886],{"class":4779},[3400,13124,4783],{"class":4759},[3400,13126,4767],{"class":4766},[3400,13128,13129,13132,13135],{"class":3402,"line":5606},[3400,13130,13131],{"class":4772},"    --tag-specifications",[3400,13133,13134],{"class":4759}," \"ResourceType=instance,Tags=[{Key=Name,Value=dotnet-api-base}]\"",[3400,13136,4767],{"class":4766},[3400,13138,13139,13141,13144,13146,13148,13150,13152,13154,13156],{"class":3402,"line":5612},[3400,13140,5331],{"class":4772},[3400,13142,13143],{"class":4759}," \"Instances[0].InstanceId\"",[3400,13145,9279],{"class":4772},[3400,13147,9282],{"class":4759},[3400,13149,10404],{"class":4772},[3400,13151,4776],{"class":4759},[3400,13153,10409],{"class":4779},[3400,13155,4783],{"class":4759},[3400,13157,7300],{"class":5649},[3400,13159,13160],{"class":3402,"line":5617},[3400,13161,3425],{"emptyLinePlaceholder":3424},[3400,13163,13164,13166,13169,13172],{"class":3402,"line":5622},[3400,13165,6079],{"class":4755},[3400,13167,13168],{"class":4759}," \"Instance ID: ",[3400,13170,13171],{"class":4779},"$INSTANCE_ID",[3400,13173,9301],{"class":4759},[3400,13175,13176],{"class":3402,"line":7434},[3400,13177,3425],{"emptyLinePlaceholder":3424},[3400,13179,13180],{"class":3402,"line":7440},[3400,13181,13182],{"class":4749},"# Очікування запуску та отримання Public IP\n",[3400,13184,13185,13187,13189,13192,13195,13198,13200,13202,13204,13206,13208,13210],{"class":3402,"line":7446},[3400,13186,4756],{"class":4755},[3400,13188,12718],{"class":4759},[3400,13190,13191],{"class":4759}," wait",[3400,13193,13194],{"class":4759}," instance-running",[3400,13196,13197],{"class":4772}," --instance-ids",[3400,13199,4776],{"class":4759},[3400,13201,13171],{"class":4779},[3400,13203,4783],{"class":4759},[3400,13205,10404],{"class":4772},[3400,13207,4776],{"class":4759},[3400,13209,10409],{"class":4779},[3400,13211,9301],{"class":4759},[3400,13213,13214,13217,13219,13221,13223,13226],{"class":3402,"line":7468},[3400,13215,13216],{"class":4779},"PUBLIC_IP",[3400,13218,9180],{"class":5649},[3400,13220,4756],{"class":4755},[3400,13222,12718],{"class":4759},[3400,13224,13225],{"class":4759}," describe-instances",[3400,13227,4767],{"class":4766},[3400,13229,13230,13233,13235,13237,13239],{"class":3402,"line":7473},[3400,13231,13232],{"class":4772},"    --instance-ids",[3400,13234,4776],{"class":4759},[3400,13236,13171],{"class":4779},[3400,13238,4783],{"class":4759},[3400,13240,4767],{"class":4766},[3400,13242,13243,13245,13248],{"class":3402,"line":7479},[3400,13244,5331],{"class":4772},[3400,13246,13247],{"class":4759}," \"Reservations[0].Instances[0].PublicIpAddress\"",[3400,13249,4767],{"class":4766},[3400,13251,13252,13254,13256,13258,13260,13262,13264],{"class":3402,"line":7497},[3400,13253,10005],{"class":4772},[3400,13255,9282],{"class":4759},[3400,13257,10404],{"class":4772},[3400,13259,4776],{"class":4759},[3400,13261,10409],{"class":4779},[3400,13263,4783],{"class":4759},[3400,13265,7300],{"class":5649},[3400,13267,13268],{"class":3402,"line":7502},[3400,13269,3425],{"emptyLinePlaceholder":3424},[3400,13271,13272,13274,13277,13280],{"class":3402,"line":7508},[3400,13273,6079],{"class":4755},[3400,13275,13276],{"class":4759}," \"Public IP: ",[3400,13278,13279],{"class":4779},"$PUBLIC_IP",[3400,13281,9301],{"class":4759},[3353,13283,13284],{},"::",[3824,13286,13288],{"id":13287},"_2-встановлення-aspnet-core-runtime","2. Встановлення ASP.NET Core Runtime",[3353,13290,13291],{},"Підключіться до інстансу через SSH та встановіть середовище виконання .NET 10.",[8462,13293,13294],{},[8465,13295,13297],{"label":13296},"Встановлення .NET",[3390,13298,13300],{"className":4740,"code":13299,"language":4742,"meta":3395,"style":3395},"# Підключення до інстансу (замініть EC2_PUBLIC_IP)\nssh -i ~\u002F.ssh\u002Fec2-lab-key.pem ubuntu@EC2_PUBLIC_IP\n\n# Оновлення пакетів та встановлення ASP.NET Core Runtime 10\nsudo apt-get update\nsudo apt-get install -y aspnetcore-runtime-10.0\n",[3397,13301,13302,13307,13321,13325,13330,13341],{"__ignoreMap":3395},[3400,13303,13304],{"class":3402,"line":3403},[3400,13305,13306],{"class":4749},"# Підключення до інстансу (замініть EC2_PUBLIC_IP)\n",[3400,13308,13309,13312,13315,13318],{"class":3402,"line":3409},[3400,13310,13311],{"class":4755},"ssh",[3400,13313,13314],{"class":4772}," -i",[3400,13316,13317],{"class":4759}," ~\u002F.ssh\u002Fec2-lab-key.pem",[3400,13319,13320],{"class":4759}," ubuntu@EC2_PUBLIC_IP\n",[3400,13322,13323],{"class":3402,"line":3415},[3400,13324,3425],{"emptyLinePlaceholder":3424},[3400,13326,13327],{"class":3402,"line":3421},[3400,13328,13329],{"class":4749},"# Оновлення пакетів та встановлення ASP.NET Core Runtime 10\n",[3400,13331,13332,13335,13338],{"class":3402,"line":3428},[3400,13333,13334],{"class":4755},"sudo",[3400,13336,13337],{"class":4759}," apt-get",[3400,13339,13340],{"class":4759}," update\n",[3400,13342,13343,13345,13347,13350,13353],{"class":3402,"line":3434},[3400,13344,13334],{"class":4755},[3400,13346,13337],{"class":4759},[3400,13348,13349],{"class":4759}," install",[3400,13351,13352],{"class":4772}," -y",[3400,13354,13355],{"class":4759}," aspnetcore-runtime-10.0\n",[5202,13357,13359,13368,13372,13375,13385,13389,13396,13399,13408,13412],{"title":13358},"Встановлення .NET Runtime на EC2",[5206,13360,13362,5214,13365],{"className":13361},[3402],[3400,13363,5213],{"className":13364},[5212],[3359,13366,13367],{},"ssh -i ~\u002F.ssh\u002Fec2-lab-key.pem ubuntu@54.93.120.45",[5206,13369,13371],{"className":13370},[3402],"Welcome to Ubuntu 24.04 LTS...",[5206,13373],{"className":13374},[3402],[5206,13376,13378,5214,13382],{"className":13377},[3402],[3400,13379,13381],{"className":13380},[5212],"ubuntu@ip-172-31-10-25:~$",[3359,13383,13384],{},"sudo apt-get update && sudo apt-get install -y aspnetcore-runtime-10.0",[5206,13386,13388],{"className":13387},[3402],"... (installing packages) ...",[5206,13390,13392],{"className":13391},[3402],[3400,13393,13395],{"className":13394},[5259],"Setting up aspnetcore-runtime-10.0 ... done.",[5206,13397],{"className":13398},[3402],[5206,13400,13402,5214,13405],{"className":13401},[3402],[3400,13403,13381],{"className":13404},[5212],[3359,13406,13407],{},"dotnet --list-runtimes",[5206,13409,13411],{"className":13410},[3402],"Microsoft.AspNetCore.App 10.0.0 [\u002Fusr\u002Fshare\u002Fdotnet\u002Fshared\u002FMicrosoft.AspNetCore.App]",[5206,13413,13415],{"className":13414},[3402],"Microsoft.NETCore.App 10.0.0 [\u002Fusr\u002Fshare\u002Fdotnet\u002Fshared\u002FMicrosoft.NETCore.App]",[3824,13417,13419],{"id":13418},"_3-публікація-та-розгортання-проєкту","3. Публікація та розгортання проєкту",[3353,13421,13422],{},"Зберіть ваш C# проєкт локально та скопіюйте скомпільовані файли на EC2.",[8462,13424,13425],{},[8465,13426,13428],{"label":13427},"Локальний термінал",[3390,13429,13431],{"className":4740,"code":13430,"language":4742,"meta":3395,"style":3395},"# 1. Локально у папці проєкту публікуємо під Linux x64\ndotnet publish -c Release -r linux-x64 --self-contained false -o .\u002Fpublish\n\n# 2. Переносимо файли на EC2 (створюємо папку app)\nssh -i ~\u002F.ssh\u002Fec2-lab-key.pem ubuntu@EC2_PUBLIC_IP \"mkdir -p ~\u002Fapp\"\nscp -i ~\u002F.ssh\u002Fec2-lab-key.pem -r .\u002Fpublish\u002F* ubuntu@EC2_PUBLIC_IP:~\u002Fapp\u002F\n",[3397,13432,13433,13438,13470,13474,13479,13493],{"__ignoreMap":3395},[3400,13434,13435],{"class":3402,"line":3403},[3400,13436,13437],{"class":4749},"# 1. Локально у папці проєкту публікуємо під Linux x64\n",[3400,13439,13440,13443,13446,13449,13452,13455,13458,13461,13464,13467],{"class":3402,"line":3409},[3400,13441,13442],{"class":4755},"dotnet",[3400,13444,13445],{"class":4759}," publish",[3400,13447,13448],{"class":4772}," -c",[3400,13450,13451],{"class":4759}," Release",[3400,13453,13454],{"class":4772}," -r",[3400,13456,13457],{"class":4759}," linux-x64",[3400,13459,13460],{"class":4772}," --self-contained",[3400,13462,13463],{"class":4772}," false",[3400,13465,13466],{"class":4772}," -o",[3400,13468,13469],{"class":4759}," .\u002Fpublish\n",[3400,13471,13472],{"class":3402,"line":3415},[3400,13473,3425],{"emptyLinePlaceholder":3424},[3400,13475,13476],{"class":3402,"line":3421},[3400,13477,13478],{"class":4749},"# 2. Переносимо файли на EC2 (створюємо папку app)\n",[3400,13480,13481,13483,13485,13487,13490],{"class":3402,"line":3428},[3400,13482,13311],{"class":4755},[3400,13484,13314],{"class":4772},[3400,13486,13317],{"class":4759},[3400,13488,13489],{"class":4759}," ubuntu@EC2_PUBLIC_IP",[3400,13491,13492],{"class":4759}," \"mkdir -p ~\u002Fapp\"\n",[3400,13494,13495,13498,13500,13502,13504,13507,13510],{"class":3402,"line":3434},[3400,13496,13497],{"class":4755},"scp",[3400,13499,13314],{"class":4772},[3400,13501,13317],{"class":4759},[3400,13503,13454],{"class":4772},[3400,13505,13506],{"class":4759}," .\u002Fpublish\u002F",[3400,13508,13509],{"class":4772},"*",[3400,13511,13512],{"class":4759}," ubuntu@EC2_PUBLIC_IP:~\u002Fapp\u002F\n",[5202,13514,13516,13525,13529,13533,13540,13543,13552,13556,13560],{"title":13515},"Локальна збірка та копіювання через SCP",[5206,13517,13519,5214,13522],{"className":13518},[3402],[3400,13520,5213],{"className":13521},[5212],[3359,13523,13524],{},"dotnet publish -c Release -r linux-x64 --self-contained false -o .\u002Fpublish",[5206,13526,13528],{"className":13527},[3402],"MSBuild version 17.12.0 for .NET",[5206,13530,13532],{"className":13531},[3402],"  ec2lab-api -> \u002Fpath\u002Fto\u002Fbin\u002FRelease\u002Fnet10.0\u002Flinux-x64\u002Fec2lab-api.dll",[5206,13534,13536],{"className":13535},[3402],[3400,13537,13539],{"className":13538},[5259],"SUCCESS: Publish completed in 2.4s.",[5206,13541],{"className":13542},[3402],[5206,13544,13546,5214,13549],{"className":13545},[3402],[3400,13547,5213],{"className":13548},[5212],[3359,13550,13551],{},"scp -i ~\u002F.ssh\u002Fec2-lab-key.pem -r .\u002Fpublish\u002F* ubuntu@54.93.120.45:~\u002Fapp\u002F",[5206,13553,13555],{"className":13554},[3402],"ec2lab-api.dll                                100%  120KB   1.2MB\u002Fs   00:00",[5206,13557,13559],{"className":13558},[3402],"ec2lab-api.runtimeconfig.json                 100%  340B    3.4KB\u002Fs   00:00",[5206,13561,13563],{"className":13562},[3402],"appsettings.json                              100%  150B    1.5KB\u002Fs   00:00",[3824,13565,13567],{"id":13566},"_4-налаштування-systemd-служби","4. Налаштування Systemd служби",[3353,13569,13570],{},"Для того, щоб застосунок автоматично запускався при старті сервера та працював у фоні, ми створимо службу systemd.",[8462,13572,13573],{},[8465,13574,13576,13596,13599,13733],{"label":13575},"Термінал EC2",[3390,13577,13579],{"className":4740,"code":13578,"language":4742,"meta":3395,"style":3395},"# 1. Створення файлу конфігурації служби\nsudo nano \u002Fetc\u002Fsystemd\u002Fsystem\u002Fec2lab-api.service\n",[3397,13580,13581,13586],{"__ignoreMap":3395},[3400,13582,13583],{"class":3402,"line":3403},[3400,13584,13585],{"class":4749},"# 1. Створення файлу конфігурації служби\n",[3400,13587,13588,13590,13593],{"class":3402,"line":3409},[3400,13589,13334],{"class":4755},[3400,13591,13592],{"class":4759}," nano",[3400,13594,13595],{"class":4759}," \u002Fetc\u002Fsystemd\u002Fsystem\u002Fec2lab-api.service\n",[3353,13597,13598],{},"Вставте наступну конфігурацію:",[3390,13600,13604],{"className":13601,"code":13602,"language":13603,"meta":3395,"style":3395},"language-ini shiki shiki-themes light-plus dark-plus dark-plus","[Unit]\nDescription=EC2 Lab .NET API Service\nAfter=network.target\n\n[Service]\nWorkingDirectory=\u002Fhome\u002Fubuntu\u002Fapp\nExecStart=\u002Fusr\u002Fbin\u002Fdotnet \u002Fhome\u002Fubuntu\u002Fapp\u002Fec2lab-api.dll\nRestart=always\nRestartSec=10\nKillSignal=SIGINT\nSyslogIdentifier=ec2lab-api\nUser=ubuntu\nEnvironment=ASPNETCORE_ENVIRONMENT=Production\nEnvironment=DOTNET_PRINT_TELEMETRY_MESSAGE=false\n\n[Install]\nWantedBy=multi-user.target\n","ini",[3397,13605,13606,13611,13619,13627,13631,13636,13644,13652,13660,13668,13676,13684,13692,13704,13716,13720,13725],{"__ignoreMap":3395},[3400,13607,13608],{"class":3402,"line":3403},[3400,13609,13610],{"class":5649},"[Unit]\n",[3400,13612,13613,13616],{"class":3402,"line":3409},[3400,13614,13615],{"class":4772},"Description",[3400,13617,13618],{"class":5649},"=EC2 Lab .NET API Service\n",[3400,13620,13621,13624],{"class":3402,"line":3415},[3400,13622,13623],{"class":4772},"After",[3400,13625,13626],{"class":5649},"=network.target\n",[3400,13628,13629],{"class":3402,"line":3421},[3400,13630,3425],{"emptyLinePlaceholder":3424},[3400,13632,13633],{"class":3402,"line":3428},[3400,13634,13635],{"class":5649},"[Service]\n",[3400,13637,13638,13641],{"class":3402,"line":3434},[3400,13639,13640],{"class":4772},"WorkingDirectory",[3400,13642,13643],{"class":5649},"=\u002Fhome\u002Fubuntu\u002Fapp\n",[3400,13645,13646,13649],{"class":3402,"line":3440},[3400,13647,13648],{"class":4772},"ExecStart",[3400,13650,13651],{"class":5649},"=\u002Fusr\u002Fbin\u002Fdotnet \u002Fhome\u002Fubuntu\u002Fapp\u002Fec2lab-api.dll\n",[3400,13653,13654,13657],{"class":3402,"line":3446},[3400,13655,13656],{"class":4772},"Restart",[3400,13658,13659],{"class":5649},"=always\n",[3400,13661,13662,13665],{"class":3402,"line":3451},[3400,13663,13664],{"class":4772},"RestartSec",[3400,13666,13667],{"class":5649},"=10\n",[3400,13669,13670,13673],{"class":3402,"line":3457},[3400,13671,13672],{"class":4772},"KillSignal",[3400,13674,13675],{"class":5649},"=SIGINT\n",[3400,13677,13678,13681],{"class":3402,"line":3463},[3400,13679,13680],{"class":4772},"SyslogIdentifier",[3400,13682,13683],{"class":5649},"=ec2lab-api\n",[3400,13685,13686,13689],{"class":3402,"line":3469},[3400,13687,13688],{"class":4772},"User",[3400,13690,13691],{"class":5649},"=ubuntu\n",[3400,13693,13694,13696,13698,13701],{"class":3402,"line":3475},[3400,13695,12241],{"class":4772},[3400,13697,5650],{"class":5649},[3400,13699,13700],{"class":4772},"ASPNETCORE_ENVIRONMENT",[3400,13702,13703],{"class":5649},"=Production\n",[3400,13705,13706,13708,13710,13713],{"class":3402,"line":3481},[3400,13707,12241],{"class":4772},[3400,13709,5650],{"class":5649},[3400,13711,13712],{"class":4772},"DOTNET_PRINT_TELEMETRY_MESSAGE",[3400,13714,13715],{"class":5649},"=false\n",[3400,13717,13718],{"class":3402,"line":3487},[3400,13719,3425],{"emptyLinePlaceholder":3424},[3400,13721,13722],{"class":3402,"line":3493},[3400,13723,13724],{"class":5649},"[Install]\n",[3400,13726,13727,13730],{"class":3402,"line":3499},[3400,13728,13729],{"class":4772},"WantedBy",[3400,13731,13732],{"class":5649},"=multi-user.target\n",[3390,13734,13736],{"className":4740,"code":13735,"language":4742,"meta":3395,"style":3395},"# 2. Перезавантаження демона та запуск служби\nsudo systemctl daemon-reload\nsudo systemctl enable ec2lab-api\nsudo systemctl start ec2lab-api\n\n# 3. Перевірка статусу служби\nsudo systemctl status ec2lab-api\n",[3397,13737,13738,13743,13753,13765,13776,13780,13785],{"__ignoreMap":3395},[3400,13739,13740],{"class":3402,"line":3403},[3400,13741,13742],{"class":4749},"# 2. Перезавантаження демона та запуск служби\n",[3400,13744,13745,13747,13750],{"class":3402,"line":3409},[3400,13746,13334],{"class":4755},[3400,13748,13749],{"class":4759}," systemctl",[3400,13751,13752],{"class":4759}," daemon-reload\n",[3400,13754,13755,13757,13759,13762],{"class":3402,"line":3415},[3400,13756,13334],{"class":4755},[3400,13758,13749],{"class":4759},[3400,13760,13761],{"class":4759}," enable",[3400,13763,13764],{"class":4759}," ec2lab-api\n",[3400,13766,13767,13769,13771,13774],{"class":3402,"line":3421},[3400,13768,13334],{"class":4755},[3400,13770,13749],{"class":4759},[3400,13772,13773],{"class":4759}," start",[3400,13775,13764],{"class":4759},[3400,13777,13778],{"class":3402,"line":3428},[3400,13779,3425],{"emptyLinePlaceholder":3424},[3400,13781,13782],{"class":3402,"line":3434},[3400,13783,13784],{"class":4749},"# 3. Перевірка статусу служби\n",[3400,13786,13787,13789,13791,13794],{"class":3402,"line":3440},[3400,13788,13334],{"class":4755},[3400,13790,13749],{"class":4759},[3400,13792,13793],{"class":4759}," status",[3400,13795,13764],{"class":4759},[5202,13797,13799,13808,13812,13815,13824,13827,13836,13840,13844,13853,13857,13861,13865],{"title":13798},"Активація systemd сервісу",[5206,13800,13802,5214,13805],{"className":13801},[3402],[3400,13803,13381],{"className":13804},[5212],[3359,13806,13807],{},"sudo systemctl enable ec2lab-api",[5206,13809,13811],{"className":13810},[3402],"Created symlink \u002Fetc\u002Fsystemd\u002Fsystem\u002Fmulti-user.target.wants\u002Fec2lab-api.service → \u002Fetc\u002Fsystemd\u002Fsystem\u002Fec2lab-api.service.",[5206,13813],{"className":13814},[3402],[5206,13816,13818,5214,13821],{"className":13817},[3402],[3400,13819,13381],{"className":13820},[5212],[3359,13822,13823],{},"sudo systemctl start ec2lab-api",[5206,13825],{"className":13826},[3402],[5206,13828,13830,5214,13833],{"className":13829},[3402],[3400,13831,13381],{"className":13832},[5212],[3359,13834,13835],{},"sudo systemctl status ec2lab-api",[5206,13837,13839],{"className":13838},[3402],"● ec2lab-api.service - EC2 Lab .NET API Service",[5206,13841,13843],{"className":13842},[3402],"     Loaded: loaded (\u002Fetc\u002Fsystemd\u002Fsystem\u002Fec2lab-api.service; enabled; vendor preset: enabled)",[5206,13845,13847,13848,13852],{"className":13846},[3402],"     Active: ",[3400,13849,13851],{"className":13850},[5259],"active (running)"," since Mon 2026-05-25 15:00:00 UTC; 5s ago",[5206,13854,13856],{"className":13855},[3402],"   Main PID: 12345 (dotnet)",[5206,13858,13860],{"className":13859},[3402],"      Tasks: 12 (limit: 1141)",[5206,13862,13864],{"className":13863},[3402],"     CGroup: \u002Fsystem.slice\u002Fec2lab-api.service",[5206,13866,13868],{"className":13867},[3402],"             └─12345 \u002Fusr\u002Fbin\u002Fdotnet \u002Fhome\u002Fubuntu\u002Fapp\u002Fec2lab-api.dll",[3824,13870,13872],{"id":13871},"_5-тестування-роботи-застосунку","5. Тестування роботи застосунку",[3353,13874,13875],{},"Перевірте, чи працює ваш застосунок локально на EC2, а потім з вашого локального комп'ютера.",[8462,13877,13878],{},[8465,13879,13881],{"label":13880},"Тестування API",[3390,13882,13884],{"className":4740,"code":13883,"language":4742,"meta":3395,"style":3395},"#  Локально на EC2 інстансі:\ncurl http:\u002F\u002Flocalhost:5000\u002Fhealth\n\n#  З вашого власного (робочого) комп'ютера (замініть EC2_PUBLIC_IP):\ncurl http:\u002F\u002FEC2_PUBLIC_IP:5000\u002F\n",[3397,13885,13886,13891,13898,13902,13907],{"__ignoreMap":3395},[3400,13887,13888],{"class":3402,"line":3403},[3400,13889,13890],{"class":4749},"#  Локально на EC2 інстансі:\n",[3400,13892,13893,13895],{"class":3402,"line":3409},[3400,13894,12857],{"class":4755},[3400,13896,13897],{"class":4759}," http:\u002F\u002Flocalhost:5000\u002Fhealth\n",[3400,13899,13900],{"class":3402,"line":3415},[3400,13901,3425],{"emptyLinePlaceholder":3424},[3400,13903,13904],{"class":3402,"line":3421},[3400,13905,13906],{"class":4749},"#  З вашого власного (робочого) комп'ютера (замініть EC2_PUBLIC_IP):\n",[3400,13908,13909,13911],{"class":3402,"line":3428},[3400,13910,12857],{"class":4755},[3400,13912,13913],{"class":4759}," http:\u002F\u002FEC2_PUBLIC_IP:5000\u002F\n",[5202,13915,13917,13926,13929,13933,13937,13941,13945,13949,13953,13957,13961,13965,13969],{"title":13916},"Перевірка відповіді API",[5206,13918,13920,5214,13923],{"className":13919},[3402],[3400,13921,5213],{"className":13922},[5212],[3359,13924,13925],{},"curl http:\u002F\u002F54.93.120.45:5000\u002F",[5206,13927,5232],{"className":13928},[3402],[5206,13930,13932],{"className":13931},[3402],"  \"message\": \"Hello from load-balanced EC2 instance!\",",[5206,13934,13936],{"className":13935},[3402],"  \"serverName\": \"ip-172-31-10-25\",",[5206,13938,13940],{"className":13939},[3402],"  \"localIPs\": [\"172.31.10.25\"],",[5206,13942,13944],{"className":13943},[3402],"  \"requestCount\": 1,",[5206,13946,13948],{"className":13947},[3402],"  \"dotnetVersion\": \"10.0.0\",",[5206,13950,13952],{"className":13951},[3402],"  \"headers\": {",[5206,13954,13956],{"className":13955},[3402],"    \"Host\": \"54.93.120.45:5000\",",[5206,13958,13960],{"className":13959},[3402],"    \"User-Agent\": \"curl\u002F8.4.0\"",[5206,13962,13964],{"className":13963},[3402],"  },",[5206,13966,13968],{"className":13967},[3402],"  \"time\": \"2026-05-25T12:05:00Z\"",[5206,13970,5284],{"className":13971},[3402],[3353,13973,13284],{},[3353,13975,13976],{},"Базовий інстанс повністю готовий. Тепер можна переходити до створення AMI образу на його основі.",[3626,13978],{},[3824,13980,13982],{"id":13981},"крок-3-підготовка-custom-ami-з-net-10","Крок 3: Підготовка Custom AMI з .NET 10",[3353,13984,13985],{},"Перед початком нам потрібен Custom AMI — образ EC2 instance з вже встановленим .NET 10 та розгорнутим застосунком. ASG буде створювати нові instances саме з цього AMI.",[8462,13987,13988,14032],{},[8465,13989,13990],{"label":8467},[8469,13991,13992,13997,14008,14014,14022],{},[3656,13993,8473,13994,13996],{},[3359,13995,12599],{}," → оберіть ваш налаштований instance",[3656,13998,13999,8914,14002,8914,14005],{},[3359,14000,14001],{},"Actions",[3359,14003,14004],{},"Image and templates",[3359,14006,14007],{},"Create image",[3656,14009,14010,14011],{},"Image name: ",[3397,14012,14013],{},"dotnet-api-ready-v1",[3656,14015,14016,14018,14019],{},[3359,14017,14007],{}," → зачекайте 5–10 хвилин поки AMI стане ",[3397,14020,14021],{},"available",[3656,14023,14024,14025,14028,14029,4091],{},"Запишіть AMI ID: EC2 → ",[3359,14026,14027],{},"AMIs"," → скопіюйте ID (формат: ",[3397,14030,14031],{},"ami-0a1b2c3d4e5f67890",[8465,14033,14034],{"label":8548},[3390,14035,14037],{"className":4740,"code":14036,"language":4742,"meta":3395,"style":3395},"# ЗАМІНІТЬ на ваш Instance ID\nINSTANCE_ID=\"i-1234567890abcdef0\"\nREGION=\"eu-central-1\"\n\n# Створення AMI з запущеного instance\nCUSTOM_AMI=$(aws ec2 create-image \\\n    --instance-id \"$INSTANCE_ID\" \\\n    --name \"dotnet-api-ready-v1\" \\\n    --description \".NET 10 API — ready for ASG deployment\" \\\n    --no-reboot \\\n    --region \"$REGION\" \\\n    --query ImageId --output text)\n\necho \"Custom AMI ID: $CUSTOM_AMI\"\n\n# Очікуємо завершення створення AMI\naws ec2 wait image-available \\\n    --image-ids \"$CUSTOM_AMI\" \\\n    --region \"$REGION\"\n\necho \"AMI is ready: $CUSTOM_AMI\"\n",[3397,14038,14039,14044,14053,14061,14065,14070,14086,14099,14109,14118,14125,14137,14150,14154,14166,14170,14175,14188,14201,14211,14215],{"__ignoreMap":3395},[3400,14040,14041],{"class":3402,"line":3403},[3400,14042,14043],{"class":4749},"# ЗАМІНІТЬ на ваш Instance ID\n",[3400,14045,14046,14048,14050],{"class":3402,"line":3409},[3400,14047,13064],{"class":4779},[3400,14049,5650],{"class":5649},[3400,14051,14052],{"class":4759},"\"i-1234567890abcdef0\"\n",[3400,14054,14055,14057,14059],{"class":3402,"line":3415},[3400,14056,12682],{"class":4779},[3400,14058,5650],{"class":5649},[3400,14060,12687],{"class":4759},[3400,14062,14063],{"class":3402,"line":3421},[3400,14064,3425],{"emptyLinePlaceholder":3424},[3400,14066,14067],{"class":3402,"line":3428},[3400,14068,14069],{"class":4749},"# Створення AMI з запущеного instance\n",[3400,14071,14072,14075,14077,14079,14081,14084],{"class":3402,"line":3434},[3400,14073,14074],{"class":4779},"CUSTOM_AMI",[3400,14076,9180],{"class":5649},[3400,14078,4756],{"class":4755},[3400,14080,12718],{"class":4759},[3400,14082,14083],{"class":4759}," create-image",[3400,14085,4767],{"class":4766},[3400,14087,14088,14091,14093,14095,14097],{"class":3402,"line":3440},[3400,14089,14090],{"class":4772},"    --instance-id",[3400,14092,4776],{"class":4759},[3400,14094,13171],{"class":4779},[3400,14096,4783],{"class":4759},[3400,14098,4767],{"class":4766},[3400,14100,14101,14104,14107],{"class":3402,"line":3446},[3400,14102,14103],{"class":4772},"    --name",[3400,14105,14106],{"class":4759}," \"dotnet-api-ready-v1\"",[3400,14108,4767],{"class":4766},[3400,14110,14111,14113,14116],{"class":3402,"line":3451},[3400,14112,12794],{"class":4772},[3400,14114,14115],{"class":4759}," \".NET 10 API — ready for ASG deployment\"",[3400,14117,4767],{"class":4766},[3400,14119,14120,14123],{"class":3402,"line":3457},[3400,14121,14122],{"class":4772},"    --no-reboot",[3400,14124,4767],{"class":4766},[3400,14126,14127,14129,14131,14133,14135],{"class":3402,"line":3463},[3400,14128,4818],{"class":4772},[3400,14130,4776],{"class":4759},[3400,14132,10409],{"class":4779},[3400,14134,4783],{"class":4759},[3400,14136,4767],{"class":4766},[3400,14138,14139,14141,14144,14146,14148],{"class":3402,"line":3469},[3400,14140,5331],{"class":4772},[3400,14142,14143],{"class":4759}," ImageId",[3400,14145,9279],{"class":4772},[3400,14147,9282],{"class":4759},[3400,14149,7300],{"class":5649},[3400,14151,14152],{"class":3402,"line":3475},[3400,14153,3425],{"emptyLinePlaceholder":3424},[3400,14155,14156,14158,14161,14164],{"class":3402,"line":3481},[3400,14157,6079],{"class":4755},[3400,14159,14160],{"class":4759}," \"Custom AMI ID: ",[3400,14162,14163],{"class":4779},"$CUSTOM_AMI",[3400,14165,9301],{"class":4759},[3400,14167,14168],{"class":3402,"line":3487},[3400,14169,3425],{"emptyLinePlaceholder":3424},[3400,14171,14172],{"class":3402,"line":3493},[3400,14173,14174],{"class":4749},"# Очікуємо завершення створення AMI\n",[3400,14176,14177,14179,14181,14183,14186],{"class":3402,"line":3499},[3400,14178,4756],{"class":4755},[3400,14180,12718],{"class":4759},[3400,14182,13191],{"class":4759},[3400,14184,14185],{"class":4759}," image-available",[3400,14187,4767],{"class":4766},[3400,14189,14190,14193,14195,14197,14199],{"class":3402,"line":3504},[3400,14191,14192],{"class":4772},"    --image-ids",[3400,14194,4776],{"class":4759},[3400,14196,14163],{"class":4779},[3400,14198,4783],{"class":4759},[3400,14200,4767],{"class":4766},[3400,14202,14203,14205,14207,14209],{"class":3402,"line":3510},[3400,14204,4818],{"class":4772},[3400,14206,4776],{"class":4759},[3400,14208,10409],{"class":4779},[3400,14210,9301],{"class":4759},[3400,14212,14213],{"class":3402,"line":3516},[3400,14214,3425],{"emptyLinePlaceholder":3424},[3400,14216,14217,14219,14222,14224],{"class":3402,"line":3522},[3400,14218,6079],{"class":4755},[3400,14220,14221],{"class":4759}," \"AMI is ready: ",[3400,14223,14163],{"class":4779},[3400,14225,9301],{"class":4759},[5202,14227,14229,14238,14245,14248,14257,14264],{"title":14228},"aws ec2 create-image",[5206,14230,14232,5214,14235],{"className":14231},[3402],[3400,14233,5213],{"className":14234},[5212],[3359,14236,14237],{},"CUSTOM_AMI=$(aws ec2 create-image ...)",[5206,14239,14241],{"className":14240},[3402],[3400,14242,14244],{"className":14243},[5259],"Custom AMI ID: ami-0a1b2c3d4e5f67890",[5206,14246],{"className":14247},[3402],[5206,14249,14251,5214,14254],{"className":14250},[3402],[3400,14252,5213],{"className":14253},[5212],[3359,14255,14256],{},"aws ec2 wait image-available --image-ids $CUSTOM_AMI ...",[5206,14258,14260],{"className":14259},[3402],[3400,14261,14263],{"className":14262},[5240],"(waiting ~5-10 minutes...)",[5206,14265,14267],{"className":14266},[3402],[3400,14268,14270],{"className":14269},[5259],"AMI is ready: ami-0a1b2c3d4e5f67890",[3626,14272],{},[3824,14274,14276],{"id":14275},"крок-4-ssltls-сертифікат-через-aws-certificate-manager-acm","Крок 4: SSL\u002FTLS сертифікат через AWS Certificate Manager (ACM)",[3348,14278,14280],{"id":14279},"aws-certificate-manager-acm-повний-огляд","AWS Certificate Manager (ACM) — повний огляд",[3353,14282,14283,14286,14287,14290,14291,14294],{},[3359,14284,14285],{},"AWS Certificate Manager (ACM)"," — це повністю керований сервіс AWS, що забезпечує весь lifecycle SSL\u002FTLS сертифікатів: запит, валідацію права власності на домен, зберігання, прив'язку до AWS-сервісів та автоматичне поновлення до закінчення терміну дії. Ключова перевага ACM перед традиційними центрами сертифікації (Let's Encrypt, Comodo, DigiCert) — ",[3359,14288,14289],{},"повна автоматизація"," та ",[3359,14292,14293],{},"безкоштовність"," для сертифікатів, що використовуються з AWS-сервісами.",[3353,14296,14297,14300,14301,14304],{},[3359,14298,14299],{},"Принцип роботи ACM."," Традиційний підхід до SSL-сертифікатів вимагає: генерації приватного ключа, створення CSR (Certificate Signing Request), верифікації у центрі сертифікації, встановлення сертифіката на сервер, моніторингу дати закінчення та ручного поновлення. ACM автоматизує ",[3359,14302,14303],{},"всі"," ці кроки: AWS генерує та зберігає приватний ключ у захищеному апаратному модулі (HSM), проводить валідацію, встановлює сертифікат у ALB\u002FCloudFront та поновлює його автоматично за 60 днів до закінчення.",[3387,14306,14307],{},[3390,14308,14310],{"className":3392,"code":14309,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"ACM Certificate Lifecycle\"\n\nparticipant \"Розробник\" as DEV\nparticipant \"ACM\" as ACM #fef3c7\nparticipant \"DNS-провайдер\" as DNS #dbeafe\nparticipant \"ALB \u002F CloudFront\" as ALB #d1fae5\nparticipant \"Браузер клієнта\" as BR\n\nDEV -> ACM : Request certificate\\n(domain: api.example.com)\nACM -> DEV : CNAME record для DNS валідації\\n(_abc123.api.example.com → _def456.acm-validations.aws.)\nDEV -> DNS : Додати CNAME запис\nDNS -> ACM : (ACM перевіряє CNAME ~5-30 хв)\nACM -> DEV : Status: Issued ✓\n\nDEV -> ALB : Прив'язати сертифікат\\n(до HTTPS Listener)\n\nloop \"Кожні 60 днів до закінчення (автоматично)\"\n    ACM -> DNS : Перевірка CNAME запису\n    alt \"CNAME ще існує\"\n        ACM -> ACM : Поновлення сертифіката\n        ACM -> ALB : Оновлення без простою\n    else \"CNAME видалено\"\n        ACM -> DEV : Попередження: поновлення неможливе\n    end\nend\n\nnote over BR\n    Браузер бачить\n    замок 🔒 та HTTPS\nend note\n\n@enduml\n",[3397,14311,14312,14316,14320,14324,14328,14333,14337,14342,14347,14352,14357,14362,14366,14371,14376,14381,14386,14391,14395,14400,14404,14409,14414,14419,14424,14429,14434,14439,14443,14447,14451,14456,14461,14466,14470,14474],{"__ignoreMap":3395},[3400,14313,14314],{"class":3402,"line":3403},[3400,14315,3406],{},[3400,14317,14318],{"class":3402,"line":3409},[3400,14319,3412],{},[3400,14321,14322],{"class":3402,"line":3415},[3400,14323,3418],{},[3400,14325,14326],{"class":3402,"line":3421},[3400,14327,3425],{"emptyLinePlaceholder":3424},[3400,14329,14330],{"class":3402,"line":3428},[3400,14331,14332],{},"title \"ACM Certificate Lifecycle\"\n",[3400,14334,14335],{"class":3402,"line":3434},[3400,14336,3425],{"emptyLinePlaceholder":3424},[3400,14338,14339],{"class":3402,"line":3440},[3400,14340,14341],{},"participant \"Розробник\" as DEV\n",[3400,14343,14344],{"class":3402,"line":3446},[3400,14345,14346],{},"participant \"ACM\" as ACM #fef3c7\n",[3400,14348,14349],{"class":3402,"line":3451},[3400,14350,14351],{},"participant \"DNS-провайдер\" as DNS #dbeafe\n",[3400,14353,14354],{"class":3402,"line":3457},[3400,14355,14356],{},"participant \"ALB \u002F CloudFront\" as ALB #d1fae5\n",[3400,14358,14359],{"class":3402,"line":3463},[3400,14360,14361],{},"participant \"Браузер клієнта\" as BR\n",[3400,14363,14364],{"class":3402,"line":3469},[3400,14365,3425],{"emptyLinePlaceholder":3424},[3400,14367,14368],{"class":3402,"line":3475},[3400,14369,14370],{},"DEV -> ACM : Request certificate\\n(domain: api.example.com)\n",[3400,14372,14373],{"class":3402,"line":3481},[3400,14374,14375],{},"ACM -> DEV : CNAME record для DNS валідації\\n(_abc123.api.example.com → _def456.acm-validations.aws.)\n",[3400,14377,14378],{"class":3402,"line":3487},[3400,14379,14380],{},"DEV -> DNS : Додати CNAME запис\n",[3400,14382,14383],{"class":3402,"line":3493},[3400,14384,14385],{},"DNS -> ACM : (ACM перевіряє CNAME ~5-30 хв)\n",[3400,14387,14388],{"class":3402,"line":3499},[3400,14389,14390],{},"ACM -> DEV : Status: Issued ✓\n",[3400,14392,14393],{"class":3402,"line":3504},[3400,14394,3425],{"emptyLinePlaceholder":3424},[3400,14396,14397],{"class":3402,"line":3510},[3400,14398,14399],{},"DEV -> ALB : Прив'язати сертифікат\\n(до HTTPS Listener)\n",[3400,14401,14402],{"class":3402,"line":3516},[3400,14403,3425],{"emptyLinePlaceholder":3424},[3400,14405,14406],{"class":3402,"line":3522},[3400,14407,14408],{},"loop \"Кожні 60 днів до закінчення (автоматично)\"\n",[3400,14410,14411],{"class":3402,"line":3528},[3400,14412,14413],{},"    ACM -> DNS : Перевірка CNAME запису\n",[3400,14415,14416],{"class":3402,"line":3534},[3400,14417,14418],{},"    alt \"CNAME ще існує\"\n",[3400,14420,14421],{"class":3402,"line":3540},[3400,14422,14423],{},"        ACM -> ACM : Поновлення сертифіката\n",[3400,14425,14426],{"class":3402,"line":3545},[3400,14427,14428],{},"        ACM -> ALB : Оновлення без простою\n",[3400,14430,14431],{"class":3402,"line":3550},[3400,14432,14433],{},"    else \"CNAME видалено\"\n",[3400,14435,14436],{"class":3402,"line":3556},[3400,14437,14438],{},"        ACM -> DEV : Попередження: поновлення неможливе\n",[3400,14440,14441],{"class":3402,"line":3562},[3400,14442,6640],{},[3400,14444,14445],{"class":3402,"line":3568},[3400,14446,6645],{},[3400,14448,14449],{"class":3402,"line":3574},[3400,14450,3425],{"emptyLinePlaceholder":3424},[3400,14452,14453],{"class":3402,"line":3580},[3400,14454,14455],{},"note over BR\n",[3400,14457,14458],{"class":3402,"line":3586},[3400,14459,14460],{},"    Браузер бачить\n",[3400,14462,14463],{"class":3402,"line":3591},[3400,14464,14465],{},"    замок 🔒 та HTTPS\n",[3400,14467,14468],{"class":3402,"line":3597},[3400,14469,3618],{},[3400,14471,14472],{"class":3402,"line":3603},[3400,14473,3425],{"emptyLinePlaceholder":3424},[3400,14475,14476],{"class":3402,"line":3609},[3400,14477,3624],{},[3824,14479,14481],{"id":14480},"типи-валідації-домену","Типи валідації домену",[3353,14483,14484],{},"ACM підтримує два методи валідації права власності на домен:",[3639,14486,14487,14521],{},[3642,14488,14490,14496,14501,14515],{"icon":3644,"title":14489},"DNS Validation (рекомендований)",[3353,14491,14492,14495],{},[3359,14493,14494],{},"Принцип:"," ACM надає CNAME-запис, який необхідно додати до DNS зони домену. ACM перевіряє наявність цього запису.",[3353,14497,14498],{},[3359,14499,14500],{},"Переваги:",[3653,14502,14503,14506,14509,14512],{},[3656,14504,14505],{},"Автоматичне поновлення (CNAME залишається назавжди)",[3656,14507,14508],{},"Не потребує доступу до електронної пошти",[3656,14510,14511],{},"Підходить для wildcard сертифікатів",[3656,14513,14514],{},"Один CNAME-запис для всіх сертифікатів одного домену",[3353,14516,14517,14520],{},[3359,14518,14519],{},"Недоліки:"," Потребує доступу до DNS панелі",[3642,14522,14525,14546,14551,14555],{"icon":14523,"title":14524},"i-heroicons-envelope","Email Validation",[3353,14526,14527,14529,14530,4031,14533,4031,14536,4031,14539,4031,14542,14545],{},[3359,14528,14494],{}," ACM надсилає листа на стандартні адміністративні адреси: ",[3397,14531,14532],{},"admin@",[3397,14534,14535],{},"webmaster@",[3397,14537,14538],{},"hostmaster@",[3397,14540,14541],{},"administrator@",[3397,14543,14544],{},"postmaster@"," вашого домену.",[3353,14547,14548,14550],{},[3359,14549,14500],{}," Швидко, не потребує DNS доступу",[3353,14552,14553],{},[3359,14554,14519],{},[3653,14556,14557,14560,14563],{},[3656,14558,14559],{},"Поновлення не є повністю автоматичним",[3656,14561,14562],{},"Потребує активних поштових скриньок",[3656,14564,14565],{},"Не підходить для wildcard",[3824,14567,14569],{"id":14568},"типи-сертифікатів","Типи сертифікатів",[3353,14571,14572,14575,14576,14578,14579,14582,14583,14585],{},[3359,14573,14574],{},"Single-domain сертифікат"," покриває лише один конкретний домен: ",[3397,14577,4100],{},". Запит на ",[3397,14580,14581],{},"www.example.com"," або ",[3397,14584,10806],{}," не буде захищений цим сертифікатом.",[3353,14587,14588,7518,14591,14594,14595,4031,14597,4031,14599,14602,14603,14606,14607,14610,14611,14290,14613,14615],{},[3359,14589,14590],{},"Wildcard сертифікат",[3397,14592,14593],{},"*.example.com",") покриває всі субдомени першого рівня: ",[3397,14596,4100],{},[3397,14598,10806],{},[3397,14600,14601],{},"static.example.com",". Важливо: wildcard не покриває кореневий домен ",[3397,14604,14605],{},"example.com"," та вкладені субдомени ",[3397,14608,14609],{},"api.v2.example.com",". Для повного покриття необхідно запросити обидва: ",[3397,14612,14605],{},[3397,14614,14593],{}," в одному сертифікаті (Subject Alternative Names).",[3353,14617,14618,14621,14622,4031,14624,4031,14626,14629],{},[3359,14619,14620],{},"Multi-domain сертифікат (SAN)"," — один сертифікат покриває до 10 різних доменів: ",[3397,14623,14605],{},[3397,14625,4100],{},[3397,14627,14628],{},"anothersite.com",". Корисний при консолідації кількох доменів.",[3387,14631,14632],{},[3390,14633,14635],{"className":3392,"code":14634,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \"Типи ACM сертифікатів: покриття доменів\"\n\nrectangle \"Single-domain\\n(api.example.com)\" as SD #d1fae5 {\n    rectangle \"api.example.com ✓\" as S1 #bbf7d0\n    rectangle \"www.example.com ✗\" as S2 #fca5a5\n    rectangle \"admin.example.com ✗\" as S3 #fca5a5\n}\n\nrectangle \"Wildcard\\n(*.example.com)\" as WC #dbeafe {\n    rectangle \"api.example.com ✓\" as W1 #bbf7d0\n    rectangle \"admin.example.com ✓\" as W2 #bbf7d0\n    rectangle \"example.com ✗\" as W3 #fca5a5\n    rectangle \"api.v2.example.com ✗\" as W4 #fca5a5\n}\n\nrectangle \"Multi-domain SAN\\n(example.com + *.example.com)\" as MD #fff3e0 {\n    rectangle \"example.com ✓\" as M1 #bbf7d0\n    rectangle \"api.example.com ✓\" as M2 #bbf7d0\n    rectangle \"admin.example.com ✓\" as M3 #bbf7d0\n    rectangle \"anothersite.com ✓\" as M4 #bbf7d0\n}\n\n@enduml\n",[3397,14636,14637,14641,14645,14649,14653,14658,14662,14667,14672,14677,14682,14686,14690,14695,14700,14705,14710,14715,14719,14723,14728,14733,14738,14743,14748,14752,14756],{"__ignoreMap":3395},[3400,14638,14639],{"class":3402,"line":3403},[3400,14640,3406],{},[3400,14642,14643],{"class":3402,"line":3409},[3400,14644,3412],{},[3400,14646,14647],{"class":3402,"line":3415},[3400,14648,3418],{},[3400,14650,14651],{"class":3402,"line":3421},[3400,14652,3425],{"emptyLinePlaceholder":3424},[3400,14654,14655],{"class":3402,"line":3428},[3400,14656,14657],{},"title \"Типи ACM сертифікатів: покриття доменів\"\n",[3400,14659,14660],{"class":3402,"line":3434},[3400,14661,3425],{"emptyLinePlaceholder":3424},[3400,14663,14664],{"class":3402,"line":3440},[3400,14665,14666],{},"rectangle \"Single-domain\\n(api.example.com)\" as SD #d1fae5 {\n",[3400,14668,14669],{"class":3402,"line":3446},[3400,14670,14671],{},"    rectangle \"api.example.com ✓\" as S1 #bbf7d0\n",[3400,14673,14674],{"class":3402,"line":3451},[3400,14675,14676],{},"    rectangle \"www.example.com ✗\" as S2 #fca5a5\n",[3400,14678,14679],{"class":3402,"line":3457},[3400,14680,14681],{},"    rectangle \"admin.example.com ✗\" as S3 #fca5a5\n",[3400,14683,14684],{"class":3402,"line":3463},[3400,14685,3496],{},[3400,14687,14688],{"class":3402,"line":3469},[3400,14689,3425],{"emptyLinePlaceholder":3424},[3400,14691,14692],{"class":3402,"line":3475},[3400,14693,14694],{},"rectangle \"Wildcard\\n(*.example.com)\" as WC #dbeafe {\n",[3400,14696,14697],{"class":3402,"line":3481},[3400,14698,14699],{},"    rectangle \"api.example.com ✓\" as W1 #bbf7d0\n",[3400,14701,14702],{"class":3402,"line":3487},[3400,14703,14704],{},"    rectangle \"admin.example.com ✓\" as W2 #bbf7d0\n",[3400,14706,14707],{"class":3402,"line":3493},[3400,14708,14709],{},"    rectangle \"example.com ✗\" as W3 #fca5a5\n",[3400,14711,14712],{"class":3402,"line":3499},[3400,14713,14714],{},"    rectangle \"api.v2.example.com ✗\" as W4 #fca5a5\n",[3400,14716,14717],{"class":3402,"line":3504},[3400,14718,3496],{},[3400,14720,14721],{"class":3402,"line":3510},[3400,14722,3425],{"emptyLinePlaceholder":3424},[3400,14724,14725],{"class":3402,"line":3516},[3400,14726,14727],{},"rectangle \"Multi-domain SAN\\n(example.com + *.example.com)\" as MD #fff3e0 {\n",[3400,14729,14730],{"class":3402,"line":3522},[3400,14731,14732],{},"    rectangle \"example.com ✓\" as M1 #bbf7d0\n",[3400,14734,14735],{"class":3402,"line":3528},[3400,14736,14737],{},"    rectangle \"api.example.com ✓\" as M2 #bbf7d0\n",[3400,14739,14740],{"class":3402,"line":3534},[3400,14741,14742],{},"    rectangle \"admin.example.com ✓\" as M3 #bbf7d0\n",[3400,14744,14745],{"class":3402,"line":3540},[3400,14746,14747],{},"    rectangle \"anothersite.com ✓\" as M4 #bbf7d0\n",[3400,14749,14750],{"class":3402,"line":3545},[3400,14751,3496],{},[3400,14753,14754],{"class":3402,"line":3550},[3400,14755,3425],{"emptyLinePlaceholder":3424},[3400,14757,14758],{"class":3402,"line":3556},[3400,14759,3624],{},[3824,14761,14763],{"id":14762},"обмеження-acm-безкоштовність-та-ppua-домени","Обмеження ACM: безкоштовність та .pp.ua домени",[3353,14765,14766,14769,14770,14773],{},[3359,14767,14768],{},"ACM сертифікати безкоштовні лише при використанні з AWS-сервісами."," Конкретно: ALB, CloudFront, API Gateway, Elastic Beanstalk, App Runner. Ви ",[3359,14771,14772],{},"не можете"," завантажити приватний ключ ACM-сертифіката та встановити його на власний сервер поза AWS — це архітектурне обмеження безпеки: приватний ключ ніколи не покидає HSM AWS.",[6829,14775,14776,14779],{},[3359,14777,14778],{},"ACM не є заміною Let's Encrypt для зовнішніх серверів."," Якщо ваш застосунок розміщений на власному VPS або хостингу поза AWS — ACM вам не допоможе. Використовуйте Let's Encrypt (Certbot) або платні сертифікати від Comodo\u002FDigiCert.",[4386,14781,14783],{"id":14782},"чи-буде-ppua-домен-безкоштовним-з-acm","Чи буде .pp.ua домен безкоштовним з ACM?",[3353,14785,14786,14793,14794,4031,14797,4031,14800,4031,14802,14805],{},[3359,14787,14788,14789,14792],{},"Коротка відповідь: ACM-сертифікат для домену ",[3397,14790,14791],{},".pp.ua"," буде безкоштовним."," Сам сертифікат ACM завжди безкоштовний незалежно від доменного розширення — ",[3397,14795,14796],{},".com",[3397,14798,14799],{},".ua",[3397,14801,14791],{},[3397,14803,14804],{},".io"," тощо. Однак повна відповідь складніша:",[3387,14807,14808],{},[3390,14809,14811],{"className":3392,"code":14810,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\ntitle \".pp.ua домен + ACM: аналіз витрат\"\n\n' Оголошення елементів\nrectangle \"Реєстрація домену .pp.ua\" as REG #fef3c7\nrectangle \"ACM сертифікат\" as ACM #d1fae5\nrectangle \"ALB (Application Load Balancer)\" as ALB #fef3c7\n\n' Прив'язка нотаток до відповідних елементів\nnote right of REG\n    myapp.pp.ua\n    Вартість реєстрації:\n    БЕЗКОШТОВНО (безстроково)\n    від Hosting Ukraine та\n    деяких інших реєстраторів\nend note\n\nnote right of ACM\n    SSL\u002FTLS для myapp.pp.ua\n    Вартість: БЕЗКОШТОВНО\n    (при використанні з ALB)\n    Автоматичне поновлення: ТАК\nend note\n\nnote right of ALB\n    ПЛАТНИЙ ресурс:\n    ~$16-22\u002Fмісяць\n    (+ $0.008 за LCU-hour)\nend note\n\n' Зв'язки між елементами\nREG -down-> ACM : DNS validation CNAME\nACM -down-> ALB : Прив'язка до HTTPS Listener\n\n' Фінальний підсумок знизу\nnote bottom of ALB\n    Загальна вартість для .pp.ua:\n    Домен: $0 (безкоштовно)\n    ACM: $0 (безкоштовно)\n    ALB: ~$16-22\u002Fмісяць ← основна витрата\nend note\n\n@enduml\n",[3397,14812,14813,14817,14821,14825,14829,14834,14838,14843,14848,14853,14858,14862,14867,14872,14877,14882,14887,14892,14897,14901,14905,14910,14915,14920,14925,14930,14934,14938,14943,14948,14953,14958,14962,14966,14971,14976,14981,14985,14990,14994,14999,15004,15009,15014,15018,15022],{"__ignoreMap":3395},[3400,14814,14815],{"class":3402,"line":3403},[3400,14816,3406],{},[3400,14818,14819],{"class":3402,"line":3409},[3400,14820,3412],{},[3400,14822,14823],{"class":3402,"line":3415},[3400,14824,3418],{},[3400,14826,14827],{"class":3402,"line":3421},[3400,14828,3425],{"emptyLinePlaceholder":3424},[3400,14830,14831],{"class":3402,"line":3428},[3400,14832,14833],{},"title \".pp.ua домен + ACM: аналіз витрат\"\n",[3400,14835,14836],{"class":3402,"line":3434},[3400,14837,3425],{"emptyLinePlaceholder":3424},[3400,14839,14840],{"class":3402,"line":3440},[3400,14841,14842],{},"' Оголошення елементів\n",[3400,14844,14845],{"class":3402,"line":3446},[3400,14846,14847],{},"rectangle \"Реєстрація домену .pp.ua\" as REG #fef3c7\n",[3400,14849,14850],{"class":3402,"line":3451},[3400,14851,14852],{},"rectangle \"ACM сертифікат\" as ACM #d1fae5\n",[3400,14854,14855],{"class":3402,"line":3457},[3400,14856,14857],{},"rectangle \"ALB (Application Load Balancer)\" as ALB #fef3c7\n",[3400,14859,14860],{"class":3402,"line":3463},[3400,14861,3425],{"emptyLinePlaceholder":3424},[3400,14863,14864],{"class":3402,"line":3469},[3400,14865,14866],{},"' Прив'язка нотаток до відповідних елементів\n",[3400,14868,14869],{"class":3402,"line":3475},[3400,14870,14871],{},"note right of REG\n",[3400,14873,14874],{"class":3402,"line":3481},[3400,14875,14876],{},"    myapp.pp.ua\n",[3400,14878,14879],{"class":3402,"line":3487},[3400,14880,14881],{},"    Вартість реєстрації:\n",[3400,14883,14884],{"class":3402,"line":3493},[3400,14885,14886],{},"    БЕЗКОШТОВНО (безстроково)\n",[3400,14888,14889],{"class":3402,"line":3499},[3400,14890,14891],{},"    від Hosting Ukraine та\n",[3400,14893,14894],{"class":3402,"line":3504},[3400,14895,14896],{},"    деяких інших реєстраторів\n",[3400,14898,14899],{"class":3402,"line":3510},[3400,14900,3618],{},[3400,14902,14903],{"class":3402,"line":3516},[3400,14904,3425],{"emptyLinePlaceholder":3424},[3400,14906,14907],{"class":3402,"line":3522},[3400,14908,14909],{},"note right of ACM\n",[3400,14911,14912],{"class":3402,"line":3528},[3400,14913,14914],{},"    SSL\u002FTLS для myapp.pp.ua\n",[3400,14916,14917],{"class":3402,"line":3534},[3400,14918,14919],{},"    Вартість: БЕЗКОШТОВНО\n",[3400,14921,14922],{"class":3402,"line":3540},[3400,14923,14924],{},"    (при використанні з ALB)\n",[3400,14926,14927],{"class":3402,"line":3545},[3400,14928,14929],{},"    Автоматичне поновлення: ТАК\n",[3400,14931,14932],{"class":3402,"line":3550},[3400,14933,3618],{},[3400,14935,14936],{"class":3402,"line":3556},[3400,14937,3425],{"emptyLinePlaceholder":3424},[3400,14939,14940],{"class":3402,"line":3562},[3400,14941,14942],{},"note right of ALB\n",[3400,14944,14945],{"class":3402,"line":3568},[3400,14946,14947],{},"    ПЛАТНИЙ ресурс:\n",[3400,14949,14950],{"class":3402,"line":3574},[3400,14951,14952],{},"    ~$16-22\u002Fмісяць\n",[3400,14954,14955],{"class":3402,"line":3580},[3400,14956,14957],{},"    (+ $0.008 за LCU-hour)\n",[3400,14959,14960],{"class":3402,"line":3586},[3400,14961,3618],{},[3400,14963,14964],{"class":3402,"line":3591},[3400,14965,3425],{"emptyLinePlaceholder":3424},[3400,14967,14968],{"class":3402,"line":3597},[3400,14969,14970],{},"' Зв'язки між елементами\n",[3400,14972,14973],{"class":3402,"line":3603},[3400,14974,14975],{},"REG -down-> ACM : DNS validation CNAME\n",[3400,14977,14978],{"class":3402,"line":3609},[3400,14979,14980],{},"ACM -down-> ALB : Прив'язка до HTTPS Listener\n",[3400,14982,14983],{"class":3402,"line":3615},[3400,14984,3425],{"emptyLinePlaceholder":3424},[3400,14986,14987],{"class":3402,"line":3621},[3400,14988,14989],{},"' Фінальний підсумок знизу\n",[3400,14991,14992],{"class":3402,"line":5589},[3400,14993,3594],{},[3400,14995,14996],{"class":3402,"line":5594},[3400,14997,14998],{},"    Загальна вартість для .pp.ua:\n",[3400,15000,15001],{"class":3402,"line":5600},[3400,15002,15003],{},"    Домен: $0 (безкоштовно)\n",[3400,15005,15006],{"class":3402,"line":5606},[3400,15007,15008],{},"    ACM: $0 (безкоштовно)\n",[3400,15010,15011],{"class":3402,"line":5612},[3400,15012,15013],{},"    ALB: ~$16-22\u002Fмісяць ← основна витрата\n",[3400,15015,15016],{"class":3402,"line":5617},[3400,15017,3618],{},[3400,15019,15020],{"class":3402,"line":5622},[3400,15021,3425],{"emptyLinePlaceholder":3424},[3400,15023,15024],{"class":3402,"line":7434},[3400,15025,3624],{},[3353,15027,15028],{},[3359,15029,15030],{},"Деталі щодо .pp.ua:",[3353,15032,15033,15036,15037,15039,15040,7518,15042,15048,15049,15051],{},[3359,15034,15035],{},"1. Реєстрація домену .pp.ua."," Домени у зоні ",[3397,15038,14791],{}," є безкоштовними для фізичних осіб в Україні — їх надає HOSTMASTER (адміністратор .ua) безкоштовно через уповноважених реєстраторів (наприклад, nic.ua). Це означає, що на відміну від ",[3397,15041,14796],{},[15043,15044,15045,15046,7518],"del",{},"$12-15\u002Fрік) або ",[3397,15047,14799],{},"$10-20\u002Fрік) — ",[3397,15050,14791],{}," домен не потребує щорічних платежів за реєстрацію.",[3353,15053,15054,15057,15058,15060],{},[3359,15055,15056],{},"2. ACM сертифікат для .pp.ua."," ACM підтримує будь-який домен, включаючи ",[3397,15059,14791],{},". Обмежень за TLD не існує. Єдина умова — ви повинні мати можливість додати CNAME-запис до DNS зони цього домену для підтвердження (DNS validation). Більшість DNS-панелей для .pp.ua підтримують CNAME-записи, тому проблем не виникне.",[3353,15062,15063,15066],{},[3359,15064,15065],{},"3. Підводні камені .pp.ua для production."," Хоча технічно все працює, для production-середовища слід врахувати:",[3653,15068,15069,15074,15077],{},[3656,15070,15071,15073],{},[3397,15072,14791],{}," домени сприймаються як «персональні» та можуть знижувати довіру корпоративних користувачів",[3656,15075,15076],{},"Деякі корпоративні фаєрволи можуть блокувати нестандартні TLD",[3656,15078,15079,15080,15082],{},"Для академічних та навчальних проектів ",[3397,15081,14791],{}," — цілком прийнятний вибір",[6209,15084,15085,15088,15089,15091],{},[3359,15086,15087],{},"Рекомендація для студентів:"," Домен ",[3397,15090,14791],{}," є ідеальним вибором для навчальних проектів та лабораторних робіт — безкоштовна реєстрація + безкоштовний ACM сертифікат. Єдина реальна витрата — ALB (~$16-22\u002Fмісяць) або EC2 instances. Після завершення лабораторної роботи — видаліть ALB, і витрат не буде.",[3824,15093,15095],{"id":15094},"регіональні-обмеження-acm","Регіональні обмеження ACM",[3353,15097,15098,15101,15102,6827],{},[3359,15099,15100],{},"Критично важлива деталь:"," ACM сертифікат дійсний лише в тому регіоні AWS, де він був виданий, — ",[3359,15103,15104],{},"за одним виключенням",[3653,15106,15107,15123],{},[3656,15108,15109,15112,15113,15116,15117,15120,15121,6827],{},[3359,15110,15111],{},"ALB, ASG, API Gateway:"," сертифікат ACM має бути виданий у ",[3359,15114,15115],{},"тому самому регіоні",", що і ресурс. Для ALB у ",[3397,15118,15119],{},"eu-central-1"," — сертифікат потрібен у ",[3397,15122,15119],{},[3656,15124,15125,15128,15129,15135],{},[3359,15126,15127],{},"CloudFront:"," є глобальним сервісом і ",[3359,15130,15131,15132],{},"вимагає сертифікат виключно у регіоні ",[3397,15133,15134],{},"us-east-1"," (N. Virginia), незалежно від того, де розгорнуто ваш застосунок.",[11383,15137,15138,15139,15142,15143,15145,15146,15148],{},"Якщо ви плануєте використовувати CloudFront — запросіть ",[3359,15140,15141],{},"два"," сертифікати ACM: один у ",[3397,15144,15119],{}," (для ALB), інший у ",[3397,15147,15134],{}," (для CloudFront).",[3824,15150,15152],{"id":15151},"отримання-acm-сертифіката-покрокова-інструкція","Отримання ACM сертифіката: покрокова інструкція",[8462,15154,15155,15292],{},[8465,15156,15157,15162,15184,15211,15242,15256,15270],{"label":8467},[3353,15158,15159],{},[3359,15160,15161],{},"Крок 1: Відкрити ACM у потрібному регіоні",[8469,15163,15164,15174],{},[3656,15165,15166,15167,15170,15171],{},"AWS Console → пошук ",[3359,15168,15169],{},"Certificate Manager"," → переконайтесь, що в правому верхньому куті обрано ",[3359,15172,15173],{},"EU (Frankfurt) eu-central-1",[3656,15175,15176,8914,15179,8914,15182],{},[3359,15177,15178],{},"Request a certificate",[3359,15180,15181],{},"Request a public certificate",[3359,15183,8941],{},[3353,15185,15186,15189,15190,5214,15193,15196,15197,8914,15200,5214,15203,15206,15207,15210],{},[3359,15187,15188],{},"Крок 2: Вказати домен(и)"," 3. ",[3359,15191,15192],{},"Fully qualified domain name:",[3397,15194,15195],{},"api.yoursite.com"," 4. ",[3359,15198,15199],{},"Add another name to this certificate",[3397,15201,15202],{},"*.yoursite.com",[8530,15204,15205],{},"(wildcard — покриє всі субдомени)"," 5. Якщо хочете покрити й кореневий домен — додайте ",[3397,15208,15209],{},"yoursite.com"," окремим рядком",[3353,15212,15213,15216,15217,15220,15221,5214,15224,15227,15228,15231,15232,15235,15236,15238,15239],{},[3359,15214,15215],{},"Крок 3: Вибрати метод валідації"," 6. ",[3359,15218,15219],{},"Validation method:"," ✅ ",[3359,15222,15223],{},"DNS validation",[8530,15225,15226],{},"(рекомендовано — підтримує автопоновлення)"," 7. ",[3359,15229,15230],{},"Key algorithm:"," RSA 2048 ",[8530,15233,15234],{},"(стандарт; ECDSA P-256 — швидший, але менша сумісність зі старими клієнтами)"," 8. ",[3359,15237,12058],{}," → натисніть ",[3359,15240,15241],{},"View certificate",[3353,15243,15244,15247,15248,15251,15252,15255],{},[3359,15245,15246],{},"Крок 4: Додати CNAME до DNS"," 9. Сторінка сертифіката покаже статус ",[3397,15249,15250],{},"Pending validation"," та секцію ",[3359,15253,15254],{},"Domains"," з CNAME-записом:",[3653,15257,15258,15264],{},[3656,15259,15260,15261],{},"Name: ",[3397,15262,15263],{},"_abc123def456.api.yoursite.com",[3656,15265,15266,15267],{},"Value: ",[3397,15268,15269],{},"_xyz789.acm-validations.aws.",[8469,15271,15272,15275,15285],{"start":3457},[3656,15273,15274],{},"Додайте цей CNAME у вашому DNS-провайдері (Route 53, Cloudflare, NIC.UA тощо)",[3656,15276,15277,15280,15281,15284],{},[8530,15278,15279],{},"(Якщо використовуєте Route 53)"," Натисніть ",[3359,15282,15283],{},"Create records in Route 53"," — ACM автоматично додасть CNAME",[3656,15286,15287,15288,15291],{},"Статус зміниться на ",[3359,15289,15290],{},"Issued"," протягом 5–30 хвилин",[8465,15293,15294,15571],{"label":8548},[3390,15295,15297],{"className":4740,"code":15296,"language":4742,"meta":3395,"style":3395},"REGION=\"eu-central-1\"\n# ЗАМІНІТЬ yoursite.com на ваш реальний домен\nDOMAIN=\"yoursite.com\"\n\n# Запит сертифіката з wildcard та кореневим доменом\nCERT_ARN=$(aws acm request-certificate \\\n    --domain-name \"${DOMAIN}\" \\\n    --subject-alternative-names \"*.${DOMAIN}\" \"api.${DOMAIN}\" \\\n    --validation-method DNS \\\n    --key-algorithm RSA_2048 \\\n    --region \"$REGION\" \\\n    --query CertificateArn --output text)\n\necho \"Certificate ARN: $CERT_ARN\"\n\n# Отримати CNAME-записи для DNS-валідації (може з'явитись через ~30 сек)\nsleep 30\naws acm describe-certificate \\\n    --certificate-arn \"$CERT_ARN\" \\\n    --region \"$REGION\" \\\n    --query \"Certificate.DomainValidationOptions[*].{Domain:DomainName,Name:ResourceRecord.Name,Value:ResourceRecord.Value}\" \\\n    --output table\n\n# Перевірка статусу сертифіката (запускати після додавання CNAME)\naws acm describe-certificate \\\n    --certificate-arn \"$CERT_ARN\" \\\n    --region \"$REGION\" \\\n    --query \"Certificate.{Status:Status,NotAfter:NotAfter,Domains:DomainValidationOptions[*].ValidationStatus}\" \\\n    --output json\n",[3397,15298,15299,15307,15312,15322,15326,15331,15348,15362,15383,15393,15403,15415,15428,15432,15444,15448,15453,15461,15472,15485,15497,15506,15512,15516,15521,15531,15543,15555,15564],{"__ignoreMap":3395},[3400,15300,15301,15303,15305],{"class":3402,"line":3403},[3400,15302,12682],{"class":4779},[3400,15304,5650],{"class":5649},[3400,15306,12687],{"class":4759},[3400,15308,15309],{"class":3402,"line":3409},[3400,15310,15311],{"class":4749},"# ЗАМІНІТЬ yoursite.com на ваш реальний домен\n",[3400,15313,15314,15317,15319],{"class":3402,"line":3415},[3400,15315,15316],{"class":4779},"DOMAIN",[3400,15318,5650],{"class":5649},[3400,15320,15321],{"class":4759},"\"yoursite.com\"\n",[3400,15323,15324],{"class":3402,"line":3421},[3400,15325,3425],{"emptyLinePlaceholder":3424},[3400,15327,15328],{"class":3402,"line":3428},[3400,15329,15330],{"class":4749},"# Запит сертифіката з wildcard та кореневим доменом\n",[3400,15332,15333,15336,15338,15340,15343,15346],{"class":3402,"line":3434},[3400,15334,15335],{"class":4779},"CERT_ARN",[3400,15337,9180],{"class":5649},[3400,15339,4756],{"class":4755},[3400,15341,15342],{"class":4759}," acm",[3400,15344,15345],{"class":4759}," request-certificate",[3400,15347,4767],{"class":4766},[3400,15349,15350,15353,15355,15357,15360],{"class":3402,"line":3440},[3400,15351,15352],{"class":4772},"    --domain-name",[3400,15354,12914],{"class":4759},[3400,15356,15316],{"class":4779},[3400,15358,15359],{"class":4759},"}\"",[3400,15361,4767],{"class":4766},[3400,15363,15364,15367,15370,15372,15374,15377,15379,15381],{"class":3402,"line":3446},[3400,15365,15366],{"class":4772},"    --subject-alternative-names",[3400,15368,15369],{"class":4759}," \"*.${",[3400,15371,15316],{"class":4779},[3400,15373,15359],{"class":4759},[3400,15375,15376],{"class":4759}," \"api.${",[3400,15378,15316],{"class":4779},[3400,15380,15359],{"class":4759},[3400,15382,4767],{"class":4766},[3400,15384,15385,15388,15391],{"class":3402,"line":3451},[3400,15386,15387],{"class":4772},"    --validation-method",[3400,15389,15390],{"class":4759}," DNS",[3400,15392,4767],{"class":4766},[3400,15394,15395,15398,15401],{"class":3402,"line":3457},[3400,15396,15397],{"class":4772},"    --key-algorithm",[3400,15399,15400],{"class":4759}," RSA_2048",[3400,15402,4767],{"class":4766},[3400,15404,15405,15407,15409,15411,15413],{"class":3402,"line":3463},[3400,15406,4818],{"class":4772},[3400,15408,4776],{"class":4759},[3400,15410,10409],{"class":4779},[3400,15412,4783],{"class":4759},[3400,15414,4767],{"class":4766},[3400,15416,15417,15419,15422,15424,15426],{"class":3402,"line":3469},[3400,15418,5331],{"class":4772},[3400,15420,15421],{"class":4759}," CertificateArn",[3400,15423,9279],{"class":4772},[3400,15425,9282],{"class":4759},[3400,15427,7300],{"class":5649},[3400,15429,15430],{"class":3402,"line":3475},[3400,15431,3425],{"emptyLinePlaceholder":3424},[3400,15433,15434,15436,15439,15442],{"class":3402,"line":3481},[3400,15435,6079],{"class":4755},[3400,15437,15438],{"class":4759}," \"Certificate ARN: ",[3400,15440,15441],{"class":4779},"$CERT_ARN",[3400,15443,9301],{"class":4759},[3400,15445,15446],{"class":3402,"line":3487},[3400,15447,3425],{"emptyLinePlaceholder":3424},[3400,15449,15450],{"class":3402,"line":3493},[3400,15451,15452],{"class":4749},"# Отримати CNAME-записи для DNS-валідації (може з'явитись через ~30 сек)\n",[3400,15454,15455,15458],{"class":3402,"line":3499},[3400,15456,15457],{"class":4755},"sleep",[3400,15459,15460],{"class":4800}," 30\n",[3400,15462,15463,15465,15467,15470],{"class":3402,"line":3504},[3400,15464,4756],{"class":4755},[3400,15466,15342],{"class":4759},[3400,15468,15469],{"class":4759}," describe-certificate",[3400,15471,4767],{"class":4766},[3400,15473,15474,15477,15479,15481,15483],{"class":3402,"line":3510},[3400,15475,15476],{"class":4772},"    --certificate-arn",[3400,15478,4776],{"class":4759},[3400,15480,15441],{"class":4779},[3400,15482,4783],{"class":4759},[3400,15484,4767],{"class":4766},[3400,15486,15487,15489,15491,15493,15495],{"class":3402,"line":3516},[3400,15488,4818],{"class":4772},[3400,15490,4776],{"class":4759},[3400,15492,10409],{"class":4779},[3400,15494,4783],{"class":4759},[3400,15496,4767],{"class":4766},[3400,15498,15499,15501,15504],{"class":3402,"line":3522},[3400,15500,5331],{"class":4772},[3400,15502,15503],{"class":4759}," \"Certificate.DomainValidationOptions[*].{Domain:DomainName,Name:ResourceRecord.Name,Value:ResourceRecord.Value}\"",[3400,15505,4767],{"class":4766},[3400,15507,15508,15510],{"class":3402,"line":3528},[3400,15509,10005],{"class":4772},[3400,15511,10008],{"class":4759},[3400,15513,15514],{"class":3402,"line":3534},[3400,15515,3425],{"emptyLinePlaceholder":3424},[3400,15517,15518],{"class":3402,"line":3540},[3400,15519,15520],{"class":4749},"# Перевірка статусу сертифіката (запускати після додавання CNAME)\n",[3400,15522,15523,15525,15527,15529],{"class":3402,"line":3545},[3400,15524,4756],{"class":4755},[3400,15526,15342],{"class":4759},[3400,15528,15469],{"class":4759},[3400,15530,4767],{"class":4766},[3400,15532,15533,15535,15537,15539,15541],{"class":3402,"line":3550},[3400,15534,15476],{"class":4772},[3400,15536,4776],{"class":4759},[3400,15538,15441],{"class":4779},[3400,15540,4783],{"class":4759},[3400,15542,4767],{"class":4766},[3400,15544,15545,15547,15549,15551,15553],{"class":3402,"line":3556},[3400,15546,4818],{"class":4772},[3400,15548,4776],{"class":4759},[3400,15550,10409],{"class":4779},[3400,15552,4783],{"class":4759},[3400,15554,4767],{"class":4766},[3400,15556,15557,15559,15562],{"class":3402,"line":3562},[3400,15558,5331],{"class":4772},[3400,15560,15561],{"class":4759}," \"Certificate.{Status:Status,NotAfter:NotAfter,Domains:DomainValidationOptions[*].ValidationStatus}\"",[3400,15563,4767],{"class":4766},[3400,15565,15566,15568],{"class":3402,"line":3568},[3400,15567,10005],{"class":4772},[3400,15569,15570],{"class":4759}," json\n",[3390,15572,15574],{"className":4740,"code":15573,"language":4742,"meta":3395,"style":3395},"# Прив'язати сертифікат до HTTPS Listener ALB\naws elbv2 add-listener-certificates \\\n    --listener-arn \"$HTTPS_LISTENER_ARN\" \\\n    --certificates CertificateArn=\"$CERT_ARN\" \\\n    --region \"$REGION\"\n\n# Або при створенні Listener одразу з сертифікатом:\naws elbv2 create-listener \\\n    --load-balancer-arn \"$ALB_ARN\" \\\n    --protocol HTTPS \\\n    --port 443 \\\n    --certificates CertificateArn=\"$CERT_ARN\" \\\n    --ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \\\n    --default-actions Type=forward,TargetGroupArn=\"$TG_ARN\" \\\n    --region \"$REGION\"\n",[3397,15575,15576,15581,15592,15604,15618,15628,15632,15637,15648,15660,15669,15679,15691,15701,15714],{"__ignoreMap":3395},[3400,15577,15578],{"class":3402,"line":3403},[3400,15579,15580],{"class":4749},"# Прив'язати сертифікат до HTTPS Listener ALB\n",[3400,15582,15583,15585,15587,15590],{"class":3402,"line":3409},[3400,15584,4756],{"class":4755},[3400,15586,4760],{"class":4759},[3400,15588,15589],{"class":4759}," add-listener-certificates",[3400,15591,4767],{"class":4766},[3400,15593,15594,15596,15598,15600,15602],{"class":3402,"line":3415},[3400,15595,5698],{"class":4772},[3400,15597,4776],{"class":4759},[3400,15599,10442],{"class":4779},[3400,15601,4783],{"class":4759},[3400,15603,4767],{"class":4766},[3400,15605,15606,15609,15612,15614,15616],{"class":3402,"line":3421},[3400,15607,15608],{"class":4772},"    --certificates",[3400,15610,15611],{"class":4759}," CertificateArn=\"",[3400,15613,15441],{"class":4779},[3400,15615,4783],{"class":4759},[3400,15617,4767],{"class":4766},[3400,15619,15620,15622,15624,15626],{"class":3402,"line":3428},[3400,15621,4818],{"class":4772},[3400,15623,4776],{"class":4759},[3400,15625,10409],{"class":4779},[3400,15627,9301],{"class":4759},[3400,15629,15630],{"class":3402,"line":3434},[3400,15631,3425],{"emptyLinePlaceholder":3424},[3400,15633,15634],{"class":3402,"line":3440},[3400,15635,15636],{"class":4749},"# Або при створенні Listener одразу з сертифікатом:\n",[3400,15638,15639,15641,15643,15646],{"class":3402,"line":3446},[3400,15640,4756],{"class":4755},[3400,15642,4760],{"class":4759},[3400,15644,15645],{"class":4759}," create-listener",[3400,15647,4767],{"class":4766},[3400,15649,15650,15652,15654,15656,15658],{"class":3402,"line":3451},[3400,15651,10367],{"class":4772},[3400,15653,4776],{"class":4759},[3400,15655,10372],{"class":4779},[3400,15657,4783],{"class":4759},[3400,15659,4767],{"class":4766},[3400,15661,15662,15664,15667],{"class":3402,"line":3457},[3400,15663,12895],{"class":4772},[3400,15665,15666],{"class":4759}," HTTPS",[3400,15668,4767],{"class":4766},[3400,15670,15671,15674,15677],{"class":3402,"line":3463},[3400,15672,15673],{"class":4772},"    --port",[3400,15675,15676],{"class":4800}," 443",[3400,15678,4767],{"class":4766},[3400,15680,15681,15683,15685,15687,15689],{"class":3402,"line":3469},[3400,15682,15608],{"class":4772},[3400,15684,15611],{"class":4759},[3400,15686,15441],{"class":4779},[3400,15688,4783],{"class":4759},[3400,15690,4767],{"class":4766},[3400,15692,15693,15696,15699],{"class":3402,"line":3475},[3400,15694,15695],{"class":4772},"    --ssl-policy",[3400,15697,15698],{"class":4759}," ELBSecurityPolicy-TLS13-1-2-2021-06",[3400,15700,4767],{"class":4766},[3400,15702,15703,15705,15708,15710,15712],{"class":3402,"line":3481},[3400,15704,5712],{"class":4772},[3400,15706,15707],{"class":4759}," Type=forward,TargetGroupArn=\"",[3400,15709,4780],{"class":4779},[3400,15711,4783],{"class":4759},[3400,15713,4767],{"class":4766},[3400,15715,15716,15718,15720,15722],{"class":3402,"line":3487},[3400,15717,4818],{"class":4772},[3400,15719,4776],{"class":4759},[3400,15721,10409],{"class":4779},[3400,15723,9301],{"class":4759},[5202,15725,15727,15736,15744,15747,15756,15760,15764,15768,15772,15775,15788,15798,15801,15808,15811,15818,15827],{"title":15726},"ACM → запит сертифіката та DNS валідація",[5206,15728,15730,5214,15733],{"className":15729},[3402],[3400,15731,5213],{"className":15732},[5212],[3359,15734,15735],{},"CERT_ARN=$(aws acm request-certificate --domain-name yoursite.com ...)",[5206,15737,15739,15740],{"className":15738},[3402],"Certificate ARN: ",[3400,15741,15743],{"className":15742},[5259],"arn:aws:acm:eu-central-1:123456789012:certificate\u002Fa1b2c3d4-1234-5678-abcd-ef1234567890",[5206,15745],{"className":15746},[3402],[5206,15748,15750,5214,15753],{"className":15749},[3402],[3400,15751,5213],{"className":15752},[5212],[3359,15754,15755],{},"aws acm describe-certificate ... --query \"...DomainValidationOptions...\" --output table",[5206,15757,15759],{"className":15758},[3402],"-------------------------------------------------------------------------------------------",[5206,15761,15763],{"className":15762},[3402],"|                          DescribeCertificate                                            |",[5206,15765,15767],{"className":15766},[3402],"+------------------+--------------------------------------+-------------------------------+",[5206,15769,15771],{"className":15770},[3402],"| Domain           | Name                                 | Value                         |",[5206,15773,15767],{"className":15774},[3402],[5206,15776,15778,15779,15783,15784,15787],{"className":15777},[3402],"| yoursite.com     | ",[3400,15780,15782],{"className":15781},[5240],"_abc123.yoursite.com.","            | ",[3400,15785,15269],{"className":15786},[5259],"  |",[5206,15789,15791,15792,15783,15795,15787],{"className":15790},[3402],"| *.yoursite.com   | ",[3400,15793,15782],{"className":15794},[5240],[3400,15796,15269],{"className":15797},[5259],[5206,15799,15767],{"className":15800},[3402],[5206,15802,15804],{"className":15803},[3402],[3400,15805,15807],{"className":15806},[6206,6151],"← Обидва домени share один CNAME (якщо вони на одному base domain)",[5206,15809],{"className":15810},[3402],[5206,15812,15814],{"className":15813},[3402],[3400,15815,15817],{"className":15816},[5212],"# Після додавання CNAME до DNS:",[5206,15819,15821,5214,15824],{"className":15820},[3402],[3400,15822,5213],{"className":15823},[5212],[3359,15825,15826],{},"aws acm describe-certificate ... --query \"Certificate.Status\"",[5206,15828,15830],{"className":15829},[3402],[3400,15831,15833],{"className":15832},[5259],"\"Issued\"",[5202,15835,15837,15846,15849,15855,15866,15876,15887,15898,15915,15918],{"title":15836},"Прив'язка сертифіката до ALB HTTPS Listener",[5206,15838,15840,5214,15843],{"className":15839},[3402],[3400,15841,5213],{"className":15842},[5212],[3359,15844,15845],{},"aws elbv2 create-listener --protocol HTTPS --port 443 --certificates CertificateArn=... ...",[5206,15847,5232],{"className":15848},[3402],[5206,15850,8670,15852,6105],{"className":15851},[3402],[3400,15853,6104],{"className":15854},[5240],[5206,15856,5236,15858,5255,15862,5261],{"className":15857},[3402],[3400,15859,15861],{"className":15860},[5240],"\"ListenerArn\"",[3400,15863,15865],{"className":15864},[5259],"\"arn:aws:elasticloadbalancing:eu-central-1:...:listener\u002Fapp\u002F...\u002F...\"",[5206,15867,5236,15869,5255,15873,5261],{"className":15868},[3402],[3400,15870,15872],{"className":15871},[5240],"\"Port\"",[3400,15874,4047],{"className":15875},[6151],[5206,15877,5236,15879,5255,15883,5261],{"className":15878},[3402],[3400,15880,15882],{"className":15881},[5240],"\"Protocol\"",[3400,15884,15886],{"className":15885},[5259],"\"HTTPS\"",[5206,15888,5236,15890,5255,15894,5261],{"className":15889},[3402],[3400,15891,15893],{"className":15892},[5240],"\"SslPolicy\"",[3400,15895,15897],{"className":15896},[5259],"\"ELBSecurityPolicy-TLS13-1-2-2021-06\"",[5206,15899,5236,15901,15905,15906,5255,15910,15914],{"className":15900},[3402],[3400,15902,15904],{"className":15903},[5240],"\"Certificates\"",": [{ ",[3400,15907,15909],{"className":15908},[5240],"\"CertificateArn\"",[3400,15911,15913],{"className":15912},[5259],"\"arn:aws:acm:...\""," }]",[5206,15916,10715],{"className":15917},[3402],[5206,15919,5284],{"className":15920},[3402],[8286,15922,15923,15924,15926,15927,15929,15930,15933],{},"Для отримання сертифіката ACM потрібен власний домен. Сам сертифікат є безкоштовним, але реєстрація домену коштує від $12\u002Fрік для ",[3397,15925,14796],{},". Домени ",[3397,15928,14791],{}," є ",[3359,15931,15932],{},"безкоштовними",". Якщо домену немає — пропустіть цей крок і використовуйте HTTP для лабораторної роботи.",[3626,15935],{},[3824,15937,15939],{"id":15938},"крок-5-створення-security-groups","Крок 5: Створення Security Groups",[3353,15941,15942],{},"Правильне налаштування Security Groups є критичним для безпеки інфраструктури. Загальний принцип: трафік з інтернету надходить лише до ALB, а EC2 instances приймають трафік виключно від ALB, але не безпосередньо з інтернету.",[3387,15944,15945],{},[3390,15946,15948],{"className":3392,"code":15947,"language":3394,"meta":3395,"style":3395},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Internet\\n0.0.0.0\u002F0\" as I\n\nrectangle \"alb-sg\\n(Security Group)\" as ALBSG #fef3c7 {\n    note right\n      Inbound:\n      TCP :80 from 0.0.0.0\u002F0\n      TCP :443 from 0.0.0.0\u002F0\n      Outbound: All traffic\n    end note\n}\n\nrectangle \"ec2-asg-sg\\n(Security Group)\" as EC2SG #d1fae5 {\n    note right\n      Inbound:\n      TCP :5000 from alb-sg (SG reference!)\n      TCP :22 from YOUR_IP\u002F32\n      Outbound: All traffic\n    end note\n}\n\npackage \"EC2 Instances (ASG)\" as EC2 #bbf7d0 {\n    node \".NET API :5000\" as API\n}\n\nI --> ALBSG : \"HTTP\u002FHTTPS\"\nALBSG --> EC2SG : \"Лише порт 5000\\nджерело = SG ID (не IP!)\"\nEC2SG --> API\n\nnote bottom\n    Security Group reference замість CIDR:\n    Якщо IP ALB зміниться — правило\n    залишається дійсним автоматично.\n    Це best practice AWS.\nend note\n\n@enduml\n",[3397,15949,15950,15954,15958,15962,15966,15971,15975,15980,15984,15989,15994,15999,16004,16008,16012,16016,16021,16025,16029,16034,16039,16043,16047,16051,16055,16060,16065,16069,16073,16078,16083,16088,16092,16096,16101,16106,16111,16116,16120,16124],{"__ignoreMap":3395},[3400,15951,15952],{"class":3402,"line":3403},[3400,15953,3406],{},[3400,15955,15956],{"class":3402,"line":3409},[3400,15957,3412],{},[3400,15959,15960],{"class":3402,"line":3415},[3400,15961,3418],{},[3400,15963,15964],{"class":3402,"line":3421},[3400,15965,3425],{"emptyLinePlaceholder":3424},[3400,15967,15968],{"class":3402,"line":3428},[3400,15969,15970],{},"actor \"Internet\\n0.0.0.0\u002F0\" as I\n",[3400,15972,15973],{"class":3402,"line":3434},[3400,15974,3425],{"emptyLinePlaceholder":3424},[3400,15976,15977],{"class":3402,"line":3440},[3400,15978,15979],{},"rectangle \"alb-sg\\n(Security Group)\" as ALBSG #fef3c7 {\n",[3400,15981,15982],{"class":3402,"line":3446},[3400,15983,3460],{},[3400,15985,15986],{"class":3402,"line":3451},[3400,15987,15988],{},"      Inbound:\n",[3400,15990,15991],{"class":3402,"line":3457},[3400,15992,15993],{},"      TCP :80 from 0.0.0.0\u002F0\n",[3400,15995,15996],{"class":3402,"line":3463},[3400,15997,15998],{},"      TCP :443 from 0.0.0.0\u002F0\n",[3400,16000,16001],{"class":3402,"line":3469},[3400,16002,16003],{},"      Outbound: All traffic\n",[3400,16005,16006],{"class":3402,"line":3475},[3400,16007,3490],{},[3400,16009,16010],{"class":3402,"line":3481},[3400,16011,3496],{},[3400,16013,16014],{"class":3402,"line":3487},[3400,16015,3425],{"emptyLinePlaceholder":3424},[3400,16017,16018],{"class":3402,"line":3493},[3400,16019,16020],{},"rectangle \"ec2-asg-sg\\n(Security Group)\" as EC2SG #d1fae5 {\n",[3400,16022,16023],{"class":3402,"line":3499},[3400,16024,3460],{},[3400,16026,16027],{"class":3402,"line":3504},[3400,16028,15988],{},[3400,16030,16031],{"class":3402,"line":3510},[3400,16032,16033],{},"      TCP :5000 from alb-sg (SG reference!)\n",[3400,16035,16036],{"class":3402,"line":3516},[3400,16037,16038],{},"      TCP :22 from YOUR_IP\u002F32\n",[3400,16040,16041],{"class":3402,"line":3522},[3400,16042,16003],{},[3400,16044,16045],{"class":3402,"line":3528},[3400,16046,3490],{},[3400,16048,16049],{"class":3402,"line":3534},[3400,16050,3496],{},[3400,16052,16053],{"class":3402,"line":3540},[3400,16054,3425],{"emptyLinePlaceholder":3424},[3400,16056,16057],{"class":3402,"line":3545},[3400,16058,16059],{},"package \"EC2 Instances (ASG)\" as EC2 #bbf7d0 {\n",[3400,16061,16062],{"class":3402,"line":3550},[3400,16063,16064],{},"    node \".NET API :5000\" as API\n",[3400,16066,16067],{"class":3402,"line":3556},[3400,16068,3496],{},[3400,16070,16071],{"class":3402,"line":3562},[3400,16072,3425],{"emptyLinePlaceholder":3424},[3400,16074,16075],{"class":3402,"line":3568},[3400,16076,16077],{},"I --> ALBSG : \"HTTP\u002FHTTPS\"\n",[3400,16079,16080],{"class":3402,"line":3574},[3400,16081,16082],{},"ALBSG --> EC2SG : \"Лише порт 5000\\nджерело = SG ID (не IP!)\"\n",[3400,16084,16085],{"class":3402,"line":3580},[3400,16086,16087],{},"EC2SG --> API\n",[3400,16089,16090],{"class":3402,"line":3586},[3400,16091,3425],{"emptyLinePlaceholder":3424},[3400,16093,16094],{"class":3402,"line":3591},[3400,16095,9695],{},[3400,16097,16098],{"class":3402,"line":3597},[3400,16099,16100],{},"    Security Group reference замість CIDR:\n",[3400,16102,16103],{"class":3402,"line":3603},[3400,16104,16105],{},"    Якщо IP ALB зміниться — правило\n",[3400,16107,16108],{"class":3402,"line":3609},[3400,16109,16110],{},"    залишається дійсним автоматично.\n",[3400,16112,16113],{"class":3402,"line":3615},[3400,16114,16115],{},"    Це best practice AWS.\n",[3400,16117,16118],{"class":3402,"line":3621},[3400,16119,3618],{},[3400,16121,16122],{"class":3402,"line":5589},[3400,16123,3425],{"emptyLinePlaceholder":3424},[3400,16125,16126],{"class":3402,"line":5594},[3400,16127,3624],{},[8462,16129,16130,16209],{},[8465,16131,16132,16137,16174,16179],{"label":8467},[3353,16133,16134],{},[3359,16135,16136],{},"Security Group для ALB:",[8469,16138,16139,16148,16154,16170],{},[3656,16140,16141,16142,8914,16145],{},"VPC → ",[3359,16143,16144],{},"Security groups",[3359,16146,16147],{},"Create security group",[3656,16149,15260,16150,16153],{},[3397,16151,16152],{},"alb-sg",", VPC: default",[3656,16155,16156,16157],{},"Inbound rules:\n",[3653,16158,16159,16165],{},[3656,16160,16161,16162,4091],{},"HTTP (80) → Anywhere IPv4 (",[3397,16163,16164],{},"0.0.0.0\u002F0",[3656,16166,16167,16168,4091],{},"HTTPS (443) → Anywhere IPv4 (",[3397,16169,16164],{},[3656,16171,16172],{},[3359,16173,8545],{},[3353,16175,16176],{},[3359,16177,16178],{},"Security Group для EC2:",[8469,16180,16181,16188,16205],{},[3656,16182,16183,8950,16185],{},[3359,16184,16147],{},[3397,16186,16187],{},"ec2-asg-sg",[3656,16189,16156,16190],{},[3653,16191,16192,16202],{},[3656,16193,16194,16195,5214,16199],{},"Custom TCP, Port 5000, Source: ",[3359,16196,16197],{},[3397,16198,16152],{},[8530,16200,16201],{},"(вкажіть SG ID, не CIDR!)",[3656,16203,16204],{},"SSH (22) → My IP",[3656,16206,16207],{},[3359,16208,8545],{},[8465,16210,16211],{"label":8548},[3390,16212,16214],{"className":4740,"code":16213,"language":4742,"meta":3395,"style":3395},"REGION=\"eu-central-1\"\nVPC_ID=$(aws ec2 describe-vpcs \\\n    --filters \"Name=isDefault,Values=true\" \\\n    --query \"Vpcs[0].VpcId\" --output text --region \"$REGION\")\n\necho \"VPC ID: $VPC_ID\"\n\n# Security Group для ALB\nALB_SG=$(aws ec2 create-security-group \\\n    --group-name alb-sg \\\n    --description \"ALB Security Group — internet-facing\" \\\n    --vpc-id \"$VPC_ID\" \\\n    --region \"$REGION\" \\\n    --query GroupId --output text)\n\naws ec2 authorize-security-group-ingress \\\n    --group-id \"$ALB_SG\" \\\n    --protocol tcp --port 80 \\\n    --cidr 0.0.0.0\u002F0 --region \"$REGION\"\n\naws ec2 authorize-security-group-ingress \\\n    --group-id \"$ALB_SG\" \\\n    --protocol tcp --port 443 \\\n    --cidr 0.0.0.0\u002F0 --region \"$REGION\"\n\n# Security Group для EC2 — джерело трафіку: ALB SG (не CIDR!)\nEC2_SG=$(aws ec2 create-security-group \\\n    --group-name ec2-asg-sg \\\n    --description \"EC2 ASG instances — traffic only from ALB\" \\\n    --vpc-id \"$VPC_ID\" \\\n    --region \"$REGION\" \\\n    --query GroupId --output text)\n\n# Трафік виключно від ALB Security Group\naws ec2 authorize-security-group-ingress \\\n    --group-id \"$EC2_SG\" \\\n    --protocol tcp --port 5000 \\\n    --source-group \"$ALB_SG\" --region \"$REGION\"\n\n# SSH лише з вашого IP\nMY_IP=$(curl -s https:\u002F\u002Fcheckip.amazonaws.com)\naws ec2 authorize-security-group-ingress \\\n    --group-id \"$EC2_SG\" \\\n    --protocol tcp --port 22 \\\n    --cidr \"${MY_IP}\u002F32\" --region \"$REGION\"\n\necho \"ALB SG: $ALB_SG\"\necho \"EC2 SG: $EC2_SG\"\n",[3397,16215,16216,16224,16238,16246,16266,16270,16281,16285,16290,16305,16314,16323,16335,16347,16359,16363,16373,16386,16399,16413,16417,16427,16439,16451,16465,16469,16474,16489,16498,16507,16519,16531,16543,16547,16552,16562,16575,16587,16606,16610,16615,16629,16639,16651,16663,16681,16685,16696],{"__ignoreMap":3395},[3400,16217,16218,16220,16222],{"class":3402,"line":3403},[3400,16219,12682],{"class":4779},[3400,16221,5650],{"class":5649},[3400,16223,12687],{"class":4759},[3400,16225,16226,16228,16230,16232,16234,16236],{"class":3402,"line":3409},[3400,16227,12711],{"class":4779},[3400,16229,9180],{"class":5649},[3400,16231,4756],{"class":4755},[3400,16233,12718],{"class":4759},[3400,16235,12721],{"class":4759},[3400,16237,4767],{"class":4766},[3400,16239,16240,16242,16244],{"class":3402,"line":3415},[3400,16241,12728],{"class":4772},[3400,16243,12731],{"class":4759},[3400,16245,4767],{"class":4766},[3400,16247,16248,16250,16252,16254,16256,16258,16260,16262,16264],{"class":3402,"line":3421},[3400,16249,5331],{"class":4772},[3400,16251,12740],{"class":4759},[3400,16253,9279],{"class":4772},[3400,16255,9282],{"class":4759},[3400,16257,10404],{"class":4772},[3400,16259,4776],{"class":4759},[3400,16261,10409],{"class":4779},[3400,16263,4783],{"class":4759},[3400,16265,7300],{"class":5649},[3400,16267,16268],{"class":3402,"line":3428},[3400,16269,3425],{"emptyLinePlaceholder":3424},[3400,16271,16272,16274,16277,16279],{"class":3402,"line":3434},[3400,16273,6079],{"class":4755},[3400,16275,16276],{"class":4759}," \"VPC ID: ",[3400,16278,12809],{"class":4779},[3400,16280,9301],{"class":4759},[3400,16282,16283],{"class":3402,"line":3440},[3400,16284,3425],{"emptyLinePlaceholder":3424},[3400,16286,16287],{"class":3402,"line":3446},[3400,16288,16289],{"class":4749},"# Security Group для ALB\n",[3400,16291,16292,16295,16297,16299,16301,16303],{"class":3402,"line":3451},[3400,16293,16294],{"class":4779},"ALB_SG",[3400,16296,9180],{"class":5649},[3400,16298,4756],{"class":4755},[3400,16300,12718],{"class":4759},[3400,16302,12777],{"class":4759},[3400,16304,4767],{"class":4766},[3400,16306,16307,16309,16312],{"class":3402,"line":3457},[3400,16308,12784],{"class":4772},[3400,16310,16311],{"class":4759}," alb-sg",[3400,16313,4767],{"class":4766},[3400,16315,16316,16318,16321],{"class":3402,"line":3463},[3400,16317,12794],{"class":4772},[3400,16319,16320],{"class":4759}," \"ALB Security Group — internet-facing\"",[3400,16322,4767],{"class":4766},[3400,16324,16325,16327,16329,16331,16333],{"class":3402,"line":3469},[3400,16326,12804],{"class":4772},[3400,16328,4776],{"class":4759},[3400,16330,12809],{"class":4779},[3400,16332,4783],{"class":4759},[3400,16334,4767],{"class":4766},[3400,16336,16337,16339,16341,16343,16345],{"class":3402,"line":3475},[3400,16338,4818],{"class":4772},[3400,16340,4776],{"class":4759},[3400,16342,10409],{"class":4779},[3400,16344,4783],{"class":4759},[3400,16346,4767],{"class":4766},[3400,16348,16349,16351,16353,16355,16357],{"class":3402,"line":3481},[3400,16350,5331],{"class":4772},[3400,16352,12832],{"class":4759},[3400,16354,9279],{"class":4772},[3400,16356,9282],{"class":4759},[3400,16358,7300],{"class":5649},[3400,16360,16361],{"class":3402,"line":3487},[3400,16362,3425],{"emptyLinePlaceholder":3424},[3400,16364,16365,16367,16369,16371],{"class":3402,"line":3493},[3400,16366,4756],{"class":4755},[3400,16368,12718],{"class":4759},[3400,16370,12874],{"class":4759},[3400,16372,4767],{"class":4766},[3400,16374,16375,16377,16379,16382,16384],{"class":3402,"line":3499},[3400,16376,12881],{"class":4772},[3400,16378,4776],{"class":4759},[3400,16380,16381],{"class":4779},"$ALB_SG",[3400,16383,4783],{"class":4759},[3400,16385,4767],{"class":4766},[3400,16387,16388,16390,16392,16394,16397],{"class":3402,"line":3504},[3400,16389,12895],{"class":4772},[3400,16391,12898],{"class":4759},[3400,16393,12901],{"class":4772},[3400,16395,16396],{"class":4800}," 80",[3400,16398,4767],{"class":4766},[3400,16400,16401,16403,16405,16407,16409,16411],{"class":3402,"line":3510},[3400,16402,12911],{"class":4772},[3400,16404,12978],{"class":4759},[3400,16406,10404],{"class":4772},[3400,16408,4776],{"class":4759},[3400,16410,10409],{"class":4779},[3400,16412,9301],{"class":4759},[3400,16414,16415],{"class":3402,"line":3516},[3400,16416,3425],{"emptyLinePlaceholder":3424},[3400,16418,16419,16421,16423,16425],{"class":3402,"line":3522},[3400,16420,4756],{"class":4755},[3400,16422,12718],{"class":4759},[3400,16424,12874],{"class":4759},[3400,16426,4767],{"class":4766},[3400,16428,16429,16431,16433,16435,16437],{"class":3402,"line":3528},[3400,16430,12881],{"class":4772},[3400,16432,4776],{"class":4759},[3400,16434,16381],{"class":4779},[3400,16436,4783],{"class":4759},[3400,16438,4767],{"class":4766},[3400,16440,16441,16443,16445,16447,16449],{"class":3402,"line":3534},[3400,16442,12895],{"class":4772},[3400,16444,12898],{"class":4759},[3400,16446,12901],{"class":4772},[3400,16448,15676],{"class":4800},[3400,16450,4767],{"class":4766},[3400,16452,16453,16455,16457,16459,16461,16463],{"class":3402,"line":3540},[3400,16454,12911],{"class":4772},[3400,16456,12978],{"class":4759},[3400,16458,10404],{"class":4772},[3400,16460,4776],{"class":4759},[3400,16462,10409],{"class":4779},[3400,16464,9301],{"class":4759},[3400,16466,16467],{"class":3402,"line":3545},[3400,16468,3425],{"emptyLinePlaceholder":3424},[3400,16470,16471],{"class":3402,"line":3550},[3400,16472,16473],{"class":4749},"# Security Group для EC2 — джерело трафіку: ALB SG (не CIDR!)\n",[3400,16475,16476,16479,16481,16483,16485,16487],{"class":3402,"line":3556},[3400,16477,16478],{"class":4779},"EC2_SG",[3400,16480,9180],{"class":5649},[3400,16482,4756],{"class":4755},[3400,16484,12718],{"class":4759},[3400,16486,12777],{"class":4759},[3400,16488,4767],{"class":4766},[3400,16490,16491,16493,16496],{"class":3402,"line":3562},[3400,16492,12784],{"class":4772},[3400,16494,16495],{"class":4759}," ec2-asg-sg",[3400,16497,4767],{"class":4766},[3400,16499,16500,16502,16505],{"class":3402,"line":3568},[3400,16501,12794],{"class":4772},[3400,16503,16504],{"class":4759}," \"EC2 ASG instances — traffic only from ALB\"",[3400,16506,4767],{"class":4766},[3400,16508,16509,16511,16513,16515,16517],{"class":3402,"line":3574},[3400,16510,12804],{"class":4772},[3400,16512,4776],{"class":4759},[3400,16514,12809],{"class":4779},[3400,16516,4783],{"class":4759},[3400,16518,4767],{"class":4766},[3400,16520,16521,16523,16525,16527,16529],{"class":3402,"line":3580},[3400,16522,4818],{"class":4772},[3400,16524,4776],{"class":4759},[3400,16526,10409],{"class":4779},[3400,16528,4783],{"class":4759},[3400,16530,4767],{"class":4766},[3400,16532,16533,16535,16537,16539,16541],{"class":3402,"line":3586},[3400,16534,5331],{"class":4772},[3400,16536,12832],{"class":4759},[3400,16538,9279],{"class":4772},[3400,16540,9282],{"class":4759},[3400,16542,7300],{"class":5649},[3400,16544,16545],{"class":3402,"line":3591},[3400,16546,3425],{"emptyLinePlaceholder":3424},[3400,16548,16549],{"class":3402,"line":3597},[3400,16550,16551],{"class":4749},"# Трафік виключно від ALB Security Group\n",[3400,16553,16554,16556,16558,16560],{"class":3402,"line":3603},[3400,16555,4756],{"class":4755},[3400,16557,12718],{"class":4759},[3400,16559,12874],{"class":4759},[3400,16561,4767],{"class":4766},[3400,16563,16564,16566,16568,16571,16573],{"class":3402,"line":3609},[3400,16565,12881],{"class":4772},[3400,16567,4776],{"class":4759},[3400,16569,16570],{"class":4779},"$EC2_SG",[3400,16572,4783],{"class":4759},[3400,16574,4767],{"class":4766},[3400,16576,16577,16579,16581,16583,16585],{"class":3402,"line":3615},[3400,16578,12895],{"class":4772},[3400,16580,12898],{"class":4759},[3400,16582,12901],{"class":4772},[3400,16584,12969],{"class":4800},[3400,16586,4767],{"class":4766},[3400,16588,16589,16592,16594,16596,16598,16600,16602,16604],{"class":3402,"line":3621},[3400,16590,16591],{"class":4772},"    --source-group",[3400,16593,4776],{"class":4759},[3400,16595,16381],{"class":4779},[3400,16597,4783],{"class":4759},[3400,16599,10404],{"class":4772},[3400,16601,4776],{"class":4759},[3400,16603,10409],{"class":4779},[3400,16605,9301],{"class":4759},[3400,16607,16608],{"class":3402,"line":5589},[3400,16609,3425],{"emptyLinePlaceholder":3424},[3400,16611,16612],{"class":3402,"line":5594},[3400,16613,16614],{"class":4749},"# SSH лише з вашого IP\n",[3400,16616,16617,16619,16621,16623,16625,16627],{"class":3402,"line":5600},[3400,16618,12852],{"class":4779},[3400,16620,9180],{"class":5649},[3400,16622,12857],{"class":4755},[3400,16624,12860],{"class":4772},[3400,16626,12863],{"class":4759},[3400,16628,7300],{"class":5649},[3400,16630,16631,16633,16635,16637],{"class":3402,"line":5606},[3400,16632,4756],{"class":4755},[3400,16634,12718],{"class":4759},[3400,16636,12874],{"class":4759},[3400,16638,4767],{"class":4766},[3400,16640,16641,16643,16645,16647,16649],{"class":3402,"line":5612},[3400,16642,12881],{"class":4772},[3400,16644,4776],{"class":4759},[3400,16646,16570],{"class":4779},[3400,16648,4783],{"class":4759},[3400,16650,4767],{"class":4766},[3400,16652,16653,16655,16657,16659,16661],{"class":3402,"line":5617},[3400,16654,12895],{"class":4772},[3400,16656,12898],{"class":4759},[3400,16658,12901],{"class":4772},[3400,16660,12904],{"class":4800},[3400,16662,4767],{"class":4766},[3400,16664,16665,16667,16669,16671,16673,16675,16677,16679],{"class":3402,"line":5622},[3400,16666,12911],{"class":4772},[3400,16668,12914],{"class":4759},[3400,16670,12852],{"class":4779},[3400,16672,12919],{"class":4759},[3400,16674,10404],{"class":4772},[3400,16676,4776],{"class":4759},[3400,16678,10409],{"class":4779},[3400,16680,9301],{"class":4759},[3400,16682,16683],{"class":3402,"line":7434},[3400,16684,3425],{"emptyLinePlaceholder":3424},[3400,16686,16687,16689,16692,16694],{"class":3402,"line":7440},[3400,16688,6079],{"class":4755},[3400,16690,16691],{"class":4759}," \"ALB SG: ",[3400,16693,16381],{"class":4779},[3400,16695,9301],{"class":4759},[3400,16697,16698,16700,16703,16705],{"class":3402,"line":7446},[3400,16699,6079],{"class":4755},[3400,16701,16702],{"class":4759}," \"EC2 SG: ",[3400,16704,16570],{"class":4779},[3400,16706,9301],{"class":4759},[5202,16708,16710,16719,16727,16730,16739,16747,16750,16759],{"title":16709},"Security Groups setup",[5206,16711,16713,5214,16716],{"className":16712},[3402],[3400,16714,5213],{"className":16715},[5212],[3359,16717,16718],{},"VPC_ID=$(aws ec2 describe-vpcs ...)",[5206,16720,16722,16723],{"className":16721},[3402],"VPC ID: ",[3400,16724,16726],{"className":16725},[5240],"vpc-0a1b2c3d4e5f67890",[5206,16728],{"className":16729},[3402],[5206,16731,16733,5214,16736],{"className":16732},[3402],[3400,16734,5213],{"className":16735},[5212],[3359,16737,16738],{},"ALB_SG=$(aws ec2 create-security-group --group-name alb-sg ...)",[5206,16740,16742,16743],{"className":16741},[3402],"ALB SG: ",[3400,16744,16746],{"className":16745},[5259],"sg-0abc123def456789a",[5206,16748],{"className":16749},[3402],[5206,16751,16753,5214,16756],{"className":16752},[3402],[3400,16754,5213],{"className":16755},[5212],[3359,16757,16758],{},"EC2_SG=$(aws ec2 create-security-group --group-name ec2-asg-sg ...)",[5206,16760,16762,16763],{"className":16761},[3402],"EC2 SG: ",[3400,16764,16766],{"className":16765},[5259],"sg-0def456abc789012b",[3626,16768],{},[3824,16770,16772],{"id":16771},"крок-6-створення-target-group","Крок 6: Створення Target Group",[8462,16774,16775,16867],{},[8465,16776,16777],{"label":8467},[8469,16778,16779,16787,16793,16801,16810,16816,16860,16864],{},[3656,16780,8473,16781,8914,16784],{},[3359,16782,16783],{},"Target Groups",[3359,16785,16786],{},"Create target group",[3656,16788,16789,16792],{},[3359,16790,16791],{},"Target type:"," Instances",[3656,16794,16795,5214,16798],{},[3359,16796,16797],{},"Target group name:",[3397,16799,16800],{},"dotnet-api-tg",[3656,16802,16803,16806,16807,12969],{},[3359,16804,16805],{},"Protocol:"," HTTP, ",[3359,16808,16809],{},"Port:",[3656,16811,16812,16815],{},[3359,16813,16814],{},"VPC:"," default",[3656,16817,16818,16821],{},[3359,16819,16820],{},"Health checks:",[3653,16822,16823,16826,16831,16834,16839,16844,16849,16854],{},[3656,16824,16825],{},"Protocol: HTTP",[3656,16827,16828,16829],{},"Path: ",[3397,16830,6796],{},[3656,16832,16833],{},"Port: Traffic port (5000)",[3656,16835,16836,16837],{},"Healthy threshold: ",[3359,16838,4811],{},[3656,16840,16841,16842],{},"Unhealthy threshold: ",[3359,16843,6728],{},[3656,16845,16846,16847,8526],{},"Timeout: ",[3359,16848,6171],{},[3656,16850,16851,16852,8526],{},"Interval: ",[3359,16853,6740],{},[3656,16855,16856,16857],{},"Success codes: ",[3359,16858,16859],{},"200",[3656,16861,16862],{},[3359,16863,16786],{},[3656,16865,16866],{},"Запишіть ARN Target Group",[8465,16868,16869],{"label":8548},[3390,16870,16872],{"className":4740,"code":16871,"language":4742,"meta":3395,"style":3395},"TG_ARN=$(aws elbv2 create-target-group \\\n    --name dotnet-api-tg \\\n    --protocol HTTP \\\n    --port 5000 \\\n    --vpc-id \"$VPC_ID\" \\\n    --target-type instance \\\n    --health-check-protocol HTTP \\\n    --health-check-path \u002Fhealth \\\n    --health-check-interval-seconds 15 \\\n    --health-check-timeout-seconds 5 \\\n    --healthy-threshold-count 2 \\\n    --unhealthy-threshold-count 3 \\\n    --matcher HttpCode=200 \\\n    --region eu-central-1 \\\n    --query \"TargetGroups[0].TargetGroupArn\" --output text)\n\necho \"Target Group ARN: $TG_ARN\"\n",[3397,16873,16874,16890,16899,16908,16916,16928,16938,16947,16957,16967,16976,16985,16995,17007,17015,17028,17032],{"__ignoreMap":3395},[3400,16875,16876,16879,16881,16883,16885,16888],{"class":3402,"line":3403},[3400,16877,16878],{"class":4779},"TG_ARN",[3400,16880,9180],{"class":5649},[3400,16882,4756],{"class":4755},[3400,16884,4760],{"class":4759},[3400,16886,16887],{"class":4759}," create-target-group",[3400,16889,4767],{"class":4766},[3400,16891,16892,16894,16897],{"class":3402,"line":3409},[3400,16893,14103],{"class":4772},[3400,16895,16896],{"class":4759}," dotnet-api-tg",[3400,16898,4767],{"class":4766},[3400,16900,16901,16903,16906],{"class":3402,"line":3415},[3400,16902,12895],{"class":4772},[3400,16904,16905],{"class":4759}," HTTP",[3400,16907,4767],{"class":4766},[3400,16909,16910,16912,16914],{"class":3402,"line":3421},[3400,16911,15673],{"class":4772},[3400,16913,12969],{"class":4800},[3400,16915,4767],{"class":4766},[3400,16917,16918,16920,16922,16924,16926],{"class":3402,"line":3428},[3400,16919,12804],{"class":4772},[3400,16921,4776],{"class":4759},[3400,16923,12809],{"class":4779},[3400,16925,4783],{"class":4759},[3400,16927,4767],{"class":4766},[3400,16929,16930,16933,16936],{"class":3402,"line":3434},[3400,16931,16932],{"class":4772},"    --target-type",[3400,16934,16935],{"class":4759}," instance",[3400,16937,4767],{"class":4766},[3400,16939,16940,16943,16945],{"class":3402,"line":3440},[3400,16941,16942],{"class":4772},"    --health-check-protocol",[3400,16944,16905],{"class":4759},[3400,16946,4767],{"class":4766},[3400,16948,16949,16952,16955],{"class":3402,"line":3446},[3400,16950,16951],{"class":4772},"    --health-check-path",[3400,16953,16954],{"class":4759}," \u002Fhealth",[3400,16956,4767],{"class":4766},[3400,16958,16959,16962,16965],{"class":3402,"line":3451},[3400,16960,16961],{"class":4772},"    --health-check-interval-seconds",[3400,16963,16964],{"class":4800}," 15",[3400,16966,4767],{"class":4766},[3400,16968,16969,16972,16974],{"class":3402,"line":3457},[3400,16970,16971],{"class":4772},"    --health-check-timeout-seconds",[3400,16973,9871],{"class":4800},[3400,16975,4767],{"class":4766},[3400,16977,16978,16981,16983],{"class":3402,"line":3463},[3400,16979,16980],{"class":4772},"    --healthy-threshold-count",[3400,16982,9155],{"class":4800},[3400,16984,4767],{"class":4766},[3400,16986,16987,16990,16993],{"class":3402,"line":3469},[3400,16988,16989],{"class":4772},"    --unhealthy-threshold-count",[3400,16991,16992],{"class":4800}," 3",[3400,16994,4767],{"class":4766},[3400,16996,16997,17000,17003,17005],{"class":3402,"line":3475},[3400,16998,16999],{"class":4772},"    --matcher",[3400,17001,17002],{"class":4759}," HttpCode=",[3400,17004,16859],{"class":4800},[3400,17006,4767],{"class":4766},[3400,17008,17009,17011,17013],{"class":3402,"line":3481},[3400,17010,4818],{"class":4772},[3400,17012,5324],{"class":4759},[3400,17014,4767],{"class":4766},[3400,17016,17017,17019,17022,17024,17026],{"class":3402,"line":3487},[3400,17018,5331],{"class":4772},[3400,17020,17021],{"class":4759}," \"TargetGroups[0].TargetGroupArn\"",[3400,17023,9279],{"class":4772},[3400,17025,9282],{"class":4759},[3400,17027,7300],{"class":5649},[3400,17029,17030],{"class":3402,"line":3493},[3400,17031,3425],{"emptyLinePlaceholder":3424},[3400,17033,17034,17036,17039,17041],{"class":3402,"line":3499},[3400,17035,6079],{"class":4755},[3400,17037,17038],{"class":4759}," \"Target Group ARN: ",[3400,17040,4780],{"class":4779},[3400,17042,9301],{"class":4759},[5202,17044,17046,17055,17059],{"title":17045},"aws elbv2 create-target-group",[5206,17047,17049,5214,17052],{"className":17048},[3402],[3400,17050,5213],{"className":17051},[5212],[3359,17053,17054],{},"TG_ARN=$(aws elbv2 create-target-group --name dotnet-api-tg ...)",[5206,17056,17058],{"className":17057},[3402],"Target Group ARN:",[5206,17060,17062],{"className":17061},[3402],[3400,17063,17065],{"className":17064},[5259],"arn:aws:elasticloadbalancing:eu-central-1:123456789012:targetgroup\u002Fdotnet-api-tg\u002Fabc123def456",[3626,17067],{},[3824,17069,17071],{"id":17070},"крок-7-створення-application-load-balancer","Крок 7: Створення Application Load Balancer",[8462,17073,17074,17166],{},[8465,17075,17076],{"label":8467},[8469,17077,17078,17085,17091,17111,17127,17134,17155,17159],{},[3656,17079,8473,17080,8914,17082],{},[3359,17081,10258],{},[3359,17083,17084],{},"Create load balancer",[3656,17086,10267,17087,8914,17089],{},[3359,17088,3645],{},[3359,17090,8545],{},[3656,17092,17093,17096],{},[3359,17094,17095],{},"Basic configuration:",[3653,17097,17098,17102,17108],{},[3656,17099,15260,17100],{},[3397,17101,10261],{},[3656,17103,17104,17105],{},"Scheme: ",[3359,17106,17107],{},"Internet-facing",[3656,17109,17110],{},"IP address type: IPv4",[3656,17112,17113,17116],{},[3359,17114,17115],{},"Network mapping:",[3653,17117,17118,17121],{},[3656,17119,17120],{},"VPC: default",[3656,17122,17123,17124],{},"Mappings: ✅ оберіть ",[3359,17125,17126],{},"мінімум 2 Availability Zones",[3656,17128,17129,8992,17132],{},[3359,17130,17131],{},"Security groups:",[3397,17133,16152],{},[3656,17135,17136,17139],{},[3359,17137,17138],{},"Listeners:",[3653,17140,17141,17146],{},[3656,17142,17143,17144],{},"HTTP:80 → Forward to: ",[3397,17145,16800],{},[3656,17147,17148,17151,17152,17154],{},[3359,17149,17150],{},"Add listener"," → HTTPS:443 → Forward to: ",[3397,17153,16800],{}," → SSL cert: ваш ACM сертифікат",[3656,17156,17157],{},[3359,17158,17084],{},[3656,17160,17161,17162,17165],{},"Запишіть ",[3359,17163,17164],{},"DNS name"," ALB",[8465,17167,17168],{"label":8548},[3390,17169,17171],{"className":4740,"code":17170,"language":4742,"meta":3395,"style":3395},"REGION=\"eu-central-1\"\n\n# Отримуємо підмережі у різних AZ (мінімум 2 AZ обов'язково)\nSUBNETS=$(aws ec2 describe-subnets \\\n    --filters \"Name=defaultForAz,Values=true\" \\\n    --query \"Subnets[*].SubnetId\" \\\n    --output text --region \"$REGION\")\n\nSUBNET_1=$(echo \"$SUBNETS\" | awk '{print $1}')\nSUBNET_2=$(echo \"$SUBNETS\" | awk '{print $2}')\n\n# Створення ALB\nALB_ARN=$(aws elbv2 create-load-balancer \\\n    --name dotnet-api-alb \\\n    --subnets \"$SUBNET_1\" \"$SUBNET_2\" \\\n    --security-groups \"$ALB_SG\" \\\n    --scheme internet-facing \\\n    --type application \\\n    --region \"$REGION\" \\\n    --query \"LoadBalancers[0].LoadBalancerArn\" --output text)\n\necho \"ALB ARN: $ALB_ARN\"\n\n# DNS name ALB\nALB_DNS=$(aws elbv2 describe-load-balancers \\\n    --load-balancer-arns \"$ALB_ARN\" \\\n    --query \"LoadBalancers[0].DNSName\" \\\n    --output text --region \"$REGION\")\necho \"ALB DNS: $ALB_DNS\"\n\n# HTTP Listener\naws elbv2 create-listener \\\n    --load-balancer-arn \"$ALB_ARN\" \\\n    --protocol HTTP --port 80 \\\n    --default-actions Type=forward,TargetGroupArn=\"$TG_ARN\" \\\n    --region \"$REGION\"\n\n# HTTPS Listener (потребує ACM сертифіката)\n# aws elbv2 create-listener \\\n#     --load-balancer-arn \"$ALB_ARN\" \\\n#     --protocol HTTPS --port 443 \\\n#     --certificates CertificateArn=\"$CERT_ARN\" \\\n#     --default-actions Type=forward,TargetGroupArn=\"$TG_ARN\" \\\n#     --region \"$REGION\"\n",[3397,17172,17173,17181,17185,17190,17206,17215,17224,17240,17244,17270,17294,17298,17303,17319,17328,17349,17362,17372,17382,17394,17407,17411,17422,17426,17431,17447,17460,17469,17485,17497,17501,17506,17516,17528,17540,17552,17562,17566,17571,17576,17581,17586,17591,17596],{"__ignoreMap":3395},[3400,17174,17175,17177,17179],{"class":3402,"line":3403},[3400,17176,12682],{"class":4779},[3400,17178,5650],{"class":5649},[3400,17180,12687],{"class":4759},[3400,17182,17183],{"class":3402,"line":3409},[3400,17184,3425],{"emptyLinePlaceholder":3424},[3400,17186,17187],{"class":3402,"line":3415},[3400,17188,17189],{"class":4749},"# Отримуємо підмережі у різних AZ (мінімум 2 AZ обов'язково)\n",[3400,17191,17192,17195,17197,17199,17201,17204],{"class":3402,"line":3421},[3400,17193,17194],{"class":4779},"SUBNETS",[3400,17196,9180],{"class":5649},[3400,17198,4756],{"class":4755},[3400,17200,12718],{"class":4759},[3400,17202,17203],{"class":4759}," describe-subnets",[3400,17205,4767],{"class":4766},[3400,17207,17208,17210,17213],{"class":3402,"line":3428},[3400,17209,12728],{"class":4772},[3400,17211,17212],{"class":4759}," \"Name=defaultForAz,Values=true\"",[3400,17214,4767],{"class":4766},[3400,17216,17217,17219,17222],{"class":3402,"line":3434},[3400,17218,5331],{"class":4772},[3400,17220,17221],{"class":4759}," \"Subnets[*].SubnetId\"",[3400,17223,4767],{"class":4766},[3400,17225,17226,17228,17230,17232,17234,17236,17238],{"class":3402,"line":3440},[3400,17227,10005],{"class":4772},[3400,17229,9282],{"class":4759},[3400,17231,10404],{"class":4772},[3400,17233,4776],{"class":4759},[3400,17235,10409],{"class":4779},[3400,17237,4783],{"class":4759},[3400,17239,7300],{"class":5649},[3400,17241,17242],{"class":3402,"line":3446},[3400,17243,3425],{"emptyLinePlaceholder":3424},[3400,17245,17246,17249,17251,17253,17255,17258,17260,17262,17265,17268],{"class":3402,"line":3451},[3400,17247,17248],{"class":4779},"SUBNET_1",[3400,17250,9180],{"class":5649},[3400,17252,6079],{"class":4755},[3400,17254,4776],{"class":4759},[3400,17256,17257],{"class":4779},"$SUBNETS",[3400,17259,4783],{"class":4759},[3400,17261,10047],{"class":5649},[3400,17263,17264],{"class":4755},"awk",[3400,17266,17267],{"class":4759}," '{print $1}'",[3400,17269,7300],{"class":5649},[3400,17271,17272,17275,17277,17279,17281,17283,17285,17287,17289,17292],{"class":3402,"line":3457},[3400,17273,17274],{"class":4779},"SUBNET_2",[3400,17276,9180],{"class":5649},[3400,17278,6079],{"class":4755},[3400,17280,4776],{"class":4759},[3400,17282,17257],{"class":4779},[3400,17284,4783],{"class":4759},[3400,17286,10047],{"class":5649},[3400,17288,17264],{"class":4755},[3400,17290,17291],{"class":4759}," '{print $2}'",[3400,17293,7300],{"class":5649},[3400,17295,17296],{"class":3402,"line":3463},[3400,17297,3425],{"emptyLinePlaceholder":3424},[3400,17299,17300],{"class":3402,"line":3469},[3400,17301,17302],{"class":4749},"# Створення ALB\n",[3400,17304,17305,17308,17310,17312,17314,17317],{"class":3402,"line":3475},[3400,17306,17307],{"class":4779},"ALB_ARN",[3400,17309,9180],{"class":5649},[3400,17311,4756],{"class":4755},[3400,17313,4760],{"class":4759},[3400,17315,17316],{"class":4759}," create-load-balancer",[3400,17318,4767],{"class":4766},[3400,17320,17321,17323,17326],{"class":3402,"line":3481},[3400,17322,14103],{"class":4772},[3400,17324,17325],{"class":4759}," dotnet-api-alb",[3400,17327,4767],{"class":4766},[3400,17329,17330,17333,17335,17338,17340,17342,17345,17347],{"class":3402,"line":3487},[3400,17331,17332],{"class":4772},"    --subnets",[3400,17334,4776],{"class":4759},[3400,17336,17337],{"class":4779},"$SUBNET_1",[3400,17339,4783],{"class":4759},[3400,17341,4776],{"class":4759},[3400,17343,17344],{"class":4779},"$SUBNET_2",[3400,17346,4783],{"class":4759},[3400,17348,4767],{"class":4766},[3400,17350,17351,17354,17356,17358,17360],{"class":3402,"line":3493},[3400,17352,17353],{"class":4772},"    --security-groups",[3400,17355,4776],{"class":4759},[3400,17357,16381],{"class":4779},[3400,17359,4783],{"class":4759},[3400,17361,4767],{"class":4766},[3400,17363,17364,17367,17370],{"class":3402,"line":3499},[3400,17365,17366],{"class":4772},"    --scheme",[3400,17368,17369],{"class":4759}," internet-facing",[3400,17371,4767],{"class":4766},[3400,17373,17374,17377,17380],{"class":3402,"line":3504},[3400,17375,17376],{"class":4772},"    --type",[3400,17378,17379],{"class":4759}," application",[3400,17381,4767],{"class":4766},[3400,17383,17384,17386,17388,17390,17392],{"class":3402,"line":3510},[3400,17385,4818],{"class":4772},[3400,17387,4776],{"class":4759},[3400,17389,10409],{"class":4779},[3400,17391,4783],{"class":4759},[3400,17393,4767],{"class":4766},[3400,17395,17396,17398,17401,17403,17405],{"class":3402,"line":3516},[3400,17397,5331],{"class":4772},[3400,17399,17400],{"class":4759}," \"LoadBalancers[0].LoadBalancerArn\"",[3400,17402,9279],{"class":4772},[3400,17404,9282],{"class":4759},[3400,17406,7300],{"class":5649},[3400,17408,17409],{"class":3402,"line":3522},[3400,17410,3425],{"emptyLinePlaceholder":3424},[3400,17412,17413,17415,17418,17420],{"class":3402,"line":3528},[3400,17414,6079],{"class":4755},[3400,17416,17417],{"class":4759}," \"ALB ARN: ",[3400,17419,10372],{"class":4779},[3400,17421,9301],{"class":4759},[3400,17423,17424],{"class":3402,"line":3534},[3400,17425,3425],{"emptyLinePlaceholder":3424},[3400,17427,17428],{"class":3402,"line":3540},[3400,17429,17430],{"class":4749},"# DNS name ALB\n",[3400,17432,17433,17436,17438,17440,17442,17445],{"class":3402,"line":3545},[3400,17434,17435],{"class":4779},"ALB_DNS",[3400,17437,9180],{"class":5649},[3400,17439,4756],{"class":4755},[3400,17441,4760],{"class":4759},[3400,17443,17444],{"class":4759}," describe-load-balancers",[3400,17446,4767],{"class":4766},[3400,17448,17449,17452,17454,17456,17458],{"class":3402,"line":3550},[3400,17450,17451],{"class":4772},"    --load-balancer-arns",[3400,17453,4776],{"class":4759},[3400,17455,10372],{"class":4779},[3400,17457,4783],{"class":4759},[3400,17459,4767],{"class":4766},[3400,17461,17462,17464,17467],{"class":3402,"line":3556},[3400,17463,5331],{"class":4772},[3400,17465,17466],{"class":4759}," \"LoadBalancers[0].DNSName\"",[3400,17468,4767],{"class":4766},[3400,17470,17471,17473,17475,17477,17479,17481,17483],{"class":3402,"line":3562},[3400,17472,10005],{"class":4772},[3400,17474,9282],{"class":4759},[3400,17476,10404],{"class":4772},[3400,17478,4776],{"class":4759},[3400,17480,10409],{"class":4779},[3400,17482,4783],{"class":4759},[3400,17484,7300],{"class":5649},[3400,17486,17487,17489,17492,17495],{"class":3402,"line":3568},[3400,17488,6079],{"class":4755},[3400,17490,17491],{"class":4759}," \"ALB DNS: ",[3400,17493,17494],{"class":4779},"$ALB_DNS",[3400,17496,9301],{"class":4759},[3400,17498,17499],{"class":3402,"line":3574},[3400,17500,3425],{"emptyLinePlaceholder":3424},[3400,17502,17503],{"class":3402,"line":3580},[3400,17504,17505],{"class":4749},"# HTTP Listener\n",[3400,17507,17508,17510,17512,17514],{"class":3402,"line":3586},[3400,17509,4756],{"class":4755},[3400,17511,4760],{"class":4759},[3400,17513,15645],{"class":4759},[3400,17515,4767],{"class":4766},[3400,17517,17518,17520,17522,17524,17526],{"class":3402,"line":3591},[3400,17519,10367],{"class":4772},[3400,17521,4776],{"class":4759},[3400,17523,10372],{"class":4779},[3400,17525,4783],{"class":4759},[3400,17527,4767],{"class":4766},[3400,17529,17530,17532,17534,17536,17538],{"class":3402,"line":3597},[3400,17531,12895],{"class":4772},[3400,17533,16905],{"class":4759},[3400,17535,12901],{"class":4772},[3400,17537,16396],{"class":4800},[3400,17539,4767],{"class":4766},[3400,17541,17542,17544,17546,17548,17550],{"class":3402,"line":3603},[3400,17543,5712],{"class":4772},[3400,17545,15707],{"class":4759},[3400,17547,4780],{"class":4779},[3400,17549,4783],{"class":4759},[3400,17551,4767],{"class":4766},[3400,17553,17554,17556,17558,17560],{"class":3402,"line":3609},[3400,17555,4818],{"class":4772},[3400,17557,4776],{"class":4759},[3400,17559,10409],{"class":4779},[3400,17561,9301],{"class":4759},[3400,17563,17564],{"class":3402,"line":3615},[3400,17565,3425],{"emptyLinePlaceholder":3424},[3400,17567,17568],{"class":3402,"line":3621},[3400,17569,17570],{"class":4749},"# HTTPS Listener (потребує ACM сертифіката)\n",[3400,17572,17573],{"class":3402,"line":5589},[3400,17574,17575],{"class":4749},"# aws elbv2 create-listener \\\n",[3400,17577,17578],{"class":3402,"line":5594},[3400,17579,17580],{"class":4749},"#     --load-balancer-arn \"$ALB_ARN\" \\\n",[3400,17582,17583],{"class":3402,"line":5600},[3400,17584,17585],{"class":4749},"#     --protocol HTTPS --port 443 \\\n",[3400,17587,17588],{"class":3402,"line":5606},[3400,17589,17590],{"class":4749},"#     --certificates CertificateArn=\"$CERT_ARN\" \\\n",[3400,17592,17593],{"class":3402,"line":5612},[3400,17594,17595],{"class":4749},"#     --default-actions Type=forward,TargetGroupArn=\"$TG_ARN\" \\\n",[3400,17597,17598],{"class":3402,"line":5617},[3400,17599,17600],{"class":4749},"#     --region \"$REGION\"\n",[5202,17602,17604,17613,17621,17624,17633],{"title":17603},"aws elbv2 create-load-balancer",[5206,17605,17607,5214,17610],{"className":17606},[3402],[3400,17608,5213],{"className":17609},[5212],[3359,17611,17612],{},"ALB_ARN=$(aws elbv2 create-load-balancer --name dotnet-api-alb ...)",[5206,17614,17616,17617],{"className":17615},[3402],"ALB ARN: ",[3400,17618,17620],{"className":17619},[5259],"arn:aws:elasticloadbalancing:eu-central-1:...",[5206,17622],{"className":17623},[3402],[5206,17625,17627,5214,17630],{"className":17626},[3402],[3400,17628,5213],{"className":17629},[5212],[3359,17631,17632],{},"echo \"ALB DNS: $ALB_DNS\"",[5206,17634,17636,17637],{"className":17635},[3402],"ALB DNS: ",[3400,17638,17640],{"className":17639},[5240],"dotnet-api-alb-123456789.eu-central-1.elb.amazonaws.com",[3626,17642],{},[3824,17644,17646],{"id":17645},"крок-8-http-https-redirect","Крок 8: HTTP → HTTPS Redirect",[3353,17648,17649],{},"Для production-середовища обов'язково налаштуйте автоматичний редирект з HTTP на HTTPS. Без цього частина користувачів буде використовувати незахищений HTTP-протокол навіть за наявності HTTPS.",[8462,17651,17652,17688],{},[8465,17653,17654],{"label":8467},[8469,17655,17656,17663,17671,17684],{},[3656,17657,17658,17659,8481,17661],{},"EC2 → Load Balancers → ",[3397,17660,10261],{},[3359,17662,10264],{},[3656,17664,10267,17665,8914,17668],{},[3359,17666,17667],{},"HTTP:80",[3359,17669,17670],{},"Edit listener",[3656,17672,17673,17676,17677,17679],{},[3359,17674,17675],{},"Default actions:"," видаліть Forward → ",[3359,17678,4060],{},[3653,17680,17681],{},[3656,17682,17683],{},"Protocol: HTTPS, Port: 443, Status code: 301 (Moved Permanently)",[3656,17685,17686],{},[3359,17687,11317],{},[8465,17689,17690],{"label":8548},[3390,17691,17693],{"className":4740,"code":17692,"language":4742,"meta":3395,"style":3395},"# Знайдемо ARN HTTP Listener\nHTTP_LISTENER_ARN=$(aws elbv2 describe-listeners \\\n    --load-balancer-arn \"$ALB_ARN\" \\\n    --query \"Listeners[?Port==\\`80\\`].ListenerArn\" \\\n    --output text --region \"$REGION\")\n\n# Змінюємо дію HTTP Listener на 301 redirect до HTTPS\naws elbv2 modify-listener \\\n    --listener-arn \"$HTTP_LISTENER_ARN\" \\\n    --default-actions \\\n        \"Type=redirect,RedirectConfig={Protocol=HTTPS,Port=443,StatusCode=HTTP_301}\" \\\n    --region \"$REGION\"\n",[3397,17694,17695,17700,17715,17727,17743,17759,17763,17768,17778,17791,17797,17804],{"__ignoreMap":3395},[3400,17696,17697],{"class":3402,"line":3403},[3400,17698,17699],{"class":4749},"# Знайдемо ARN HTTP Listener\n",[3400,17701,17702,17705,17707,17709,17711,17713],{"class":3402,"line":3409},[3400,17703,17704],{"class":4779},"HTTP_LISTENER_ARN",[3400,17706,9180],{"class":5649},[3400,17708,4756],{"class":4755},[3400,17710,4760],{"class":4759},[3400,17712,10360],{"class":4759},[3400,17714,4767],{"class":4766},[3400,17716,17717,17719,17721,17723,17725],{"class":3402,"line":3415},[3400,17718,10367],{"class":4772},[3400,17720,4776],{"class":4759},[3400,17722,10372],{"class":4779},[3400,17724,4783],{"class":4759},[3400,17726,4767],{"class":4766},[3400,17728,17729,17731,17733,17735,17737,17739,17741],{"class":3402,"line":3421},[3400,17730,5331],{"class":4772},[3400,17732,10383],{"class":4759},[3400,17734,10386],{"class":4766},[3400,17736,4043],{"class":4759},[3400,17738,10386],{"class":4766},[3400,17740,10393],{"class":4759},[3400,17742,4767],{"class":4766},[3400,17744,17745,17747,17749,17751,17753,17755,17757],{"class":3402,"line":3428},[3400,17746,10005],{"class":4772},[3400,17748,9282],{"class":4759},[3400,17750,10404],{"class":4772},[3400,17752,4776],{"class":4759},[3400,17754,10409],{"class":4779},[3400,17756,4783],{"class":4759},[3400,17758,7300],{"class":5649},[3400,17760,17761],{"class":3402,"line":3434},[3400,17762,3425],{"emptyLinePlaceholder":3424},[3400,17764,17765],{"class":3402,"line":3440},[3400,17766,17767],{"class":4749},"# Змінюємо дію HTTP Listener на 301 redirect до HTTPS\n",[3400,17769,17770,17772,17774,17776],{"class":3402,"line":3446},[3400,17771,4756],{"class":4755},[3400,17773,4760],{"class":4759},[3400,17775,5691],{"class":4759},[3400,17777,4767],{"class":4766},[3400,17779,17780,17782,17784,17787,17789],{"class":3402,"line":3451},[3400,17781,5698],{"class":4772},[3400,17783,4776],{"class":4759},[3400,17785,17786],{"class":4779},"$HTTP_LISTENER_ARN",[3400,17788,4783],{"class":4759},[3400,17790,4767],{"class":4766},[3400,17792,17793,17795],{"class":3402,"line":3457},[3400,17794,5712],{"class":4772},[3400,17796,4767],{"class":4766},[3400,17798,17799,17802],{"class":3402,"line":3463},[3400,17800,17801],{"class":4759},"        \"Type=redirect,RedirectConfig={Protocol=HTTPS,Port=443,StatusCode=HTTP_301}\"",[3400,17803,4767],{"class":4766},[3400,17805,17806,17808,17810,17812],{"class":3402,"line":3469},[3400,17807,4818],{"class":4772},[3400,17809,4776],{"class":4759},[3400,17811,10409],{"class":4779},[3400,17813,9301],{"class":4759},[3626,17815],{},[3824,17817,17819],{"id":17818},"крок-9-launch-template-для-asg","Крок 9: Launch Template для ASG",[3353,17821,17822,17824],{},[3359,17823,8129],{}," є специфікацією, за якою ASG автоматично створює нові EC2 instances при масштабуванні. User Data скрипт у Launch Template запускається при першому старті кожного нового instance та забезпечує автоматичний запуск .NET API.",[8462,17826,17827,17963],{},[8465,17828,17829,17888,17957],{"label":8467},[8469,17830,17831,17839,17847,17855,17863,17870,17877,17883],{},[3656,17832,8473,17833,8914,17836],{},[3359,17834,17835],{},"Launch Templates",[3359,17837,17838],{},"Create launch template",[3656,17840,17841,5214,17844],{},[3359,17842,17843],{},"Launch template name:",[3397,17845,17846],{},"dotnet-api-lt",[3656,17848,17849,5214,17852],{},[3359,17850,17851],{},"Template version description:",[3397,17853,17854],{},"v1 - .NET 10 API",[3656,17856,17857,17860,17861],{},[3359,17858,17859],{},"AMI:"," My AMIs → оберіть ",[3397,17862,14013],{},[3656,17864,17865,5214,17868],{},[3359,17866,17867],{},"Instance type:",[3397,17869,12621],{},[3656,17871,17872,5214,17875],{},[3359,17873,17874],{},"Key pair:",[3397,17876,12628],{},[3656,17878,17879,5214,17881],{},[3359,17880,17131],{},[3397,17882,16187],{},[3656,17884,17885],{},[3359,17886,17887],{},"Advanced details → User data:",[3390,17889,17891],{"className":4740,"code":17890,"language":4742,"meta":3395,"style":3395},"#!\u002Fbin\u002Fbash\n# Цей скрипт запускається ONE TIME при першому старті нового EC2 instance\n# systemd сервіс вже налаштований у Custom AMI\n\nsystemctl start ec2lab-api\nsystemctl enable ec2lab-api\n\n# Логуємо успішний запуск\necho \"$(date): .NET API service started\" >> \u002Fvar\u002Flog\u002Fuserdata.log\n",[3397,17892,17893,17898,17903,17908,17912,17921,17929,17933,17938],{"__ignoreMap":3395},[3400,17894,17895],{"class":3402,"line":3403},[3400,17896,17897],{"class":4749},"#!\u002Fbin\u002Fbash\n",[3400,17899,17900],{"class":3402,"line":3409},[3400,17901,17902],{"class":4749},"# Цей скрипт запускається ONE TIME при першому старті нового EC2 instance\n",[3400,17904,17905],{"class":3402,"line":3415},[3400,17906,17907],{"class":4749},"# systemd сервіс вже налаштований у Custom AMI\n",[3400,17909,17910],{"class":3402,"line":3421},[3400,17911,3425],{"emptyLinePlaceholder":3424},[3400,17913,17914,17917,17919],{"class":3402,"line":3428},[3400,17915,17916],{"class":4755},"systemctl",[3400,17918,13773],{"class":4759},[3400,17920,13764],{"class":4759},[3400,17922,17923,17925,17927],{"class":3402,"line":3434},[3400,17924,17916],{"class":4755},[3400,17926,13761],{"class":4759},[3400,17928,13764],{"class":4759},[3400,17930,17931],{"class":3402,"line":3440},[3400,17932,3425],{"emptyLinePlaceholder":3424},[3400,17934,17935],{"class":3402,"line":3446},[3400,17936,17937],{"class":4749},"# Логуємо успішний запуск\n",[3400,17939,17940,17942,17945,17948,17951,17954],{"class":3402,"line":3451},[3400,17941,6079],{"class":4755},[3400,17943,17944],{"class":4759}," \"$(",[3400,17946,17947],{"class":4755},"date",[3400,17949,17950],{"class":4759},"): .NET API service started\"",[3400,17952,17953],{"class":5649}," >> ",[3400,17955,17956],{"class":4759},"\u002Fvar\u002Flog\u002Fuserdata.log\n",[8469,17958,17959],{"start":3451},[3656,17960,17961],{},[3359,17962,17838],{},[8465,17964,17965],{"label":8548},[3390,17966,17968],{"className":4740,"code":17967,"language":4742,"meta":3395,"style":3395},"# ЗАМІНІТЬ ami-xxx на ваш реальний Custom AMI ID\nCUSTOM_AMI=\"ami-0a1b2c3d4e5f67890\"\n\n# User Data скрипт (base64-encoded)\nUSER_DATA=$(cat \u003C\u003C'USERDATA' | base64\n#!\u002Fbin\u002Fbash\nsystemctl start ec2lab-api\nsystemctl enable ec2lab-api\necho \"$(date): .NET API service started\" >> \u002Fvar\u002Flog\u002Fuserdata.log\nUSERDATA\n)\n\nLT_ID=$(aws ec2 create-launch-template \\\n    --launch-template-name dotnet-api-lt \\\n    --version-description \"v1 .NET 10 API\" \\\n    --launch-template-data \"{\n        \\\"ImageId\\\": \\\"$CUSTOM_AMI\\\",\n        \\\"InstanceType\\\": \\\"t3.micro\\\",\n        \\\"KeyName\\\": \\\"ec2-lab-key\\\",\n        \\\"SecurityGroupIds\\\": [\\\"$EC2_SG\\\"],\n        \\\"UserData\\\": \\\"$USER_DATA\\\"\n    }\" \\\n    --region \"$REGION\" \\\n    --query \"LaunchTemplate.LaunchTemplateId\" --output text)\n\necho \"Launch Template ID: $LT_ID\"\n",[3397,17969,17970,17975,17984,17988,17993,18014,18018,18023,18028,18033,18038,18042,18046,18062,18072,18082,18090,18110,18129,18148,18168,18187,18194,18206,18219,18223],{"__ignoreMap":3395},[3400,17971,17972],{"class":3402,"line":3403},[3400,17973,17974],{"class":4749},"# ЗАМІНІТЬ ami-xxx на ваш реальний Custom AMI ID\n",[3400,17976,17977,17979,17981],{"class":3402,"line":3409},[3400,17978,14074],{"class":4779},[3400,17980,5650],{"class":5649},[3400,17982,17983],{"class":4759},"\"ami-0a1b2c3d4e5f67890\"\n",[3400,17985,17986],{"class":3402,"line":3415},[3400,17987,3425],{"emptyLinePlaceholder":3424},[3400,17989,17990],{"class":3402,"line":3421},[3400,17991,17992],{"class":4749},"# User Data скрипт (base64-encoded)\n",[3400,17994,17995,17998,18000,18003,18006,18009,18011],{"class":3402,"line":3428},[3400,17996,17997],{"class":4779},"USER_DATA",[3400,17999,9180],{"class":5649},[3400,18001,18002],{"class":4755},"cat",[3400,18004,18005],{"class":5649}," \u003C\u003C",[3400,18007,18008],{"class":5649},"'USERDATA'",[3400,18010,10047],{"class":5649},[3400,18012,18013],{"class":4755},"base64\n",[3400,18015,18016],{"class":3402,"line":3434},[3400,18017,17897],{"class":4759},[3400,18019,18020],{"class":3402,"line":3440},[3400,18021,18022],{"class":4759},"systemctl start ec2lab-api\n",[3400,18024,18025],{"class":3402,"line":3446},[3400,18026,18027],{"class":4759},"systemctl enable ec2lab-api\n",[3400,18029,18030],{"class":3402,"line":3451},[3400,18031,18032],{"class":4759},"echo \"$(date): .NET API service started\" >> \u002Fvar\u002Flog\u002Fuserdata.log\n",[3400,18034,18035],{"class":3402,"line":3457},[3400,18036,18037],{"class":5649},"USERDATA\n",[3400,18039,18040],{"class":3402,"line":3463},[3400,18041,7300],{"class":5649},[3400,18043,18044],{"class":3402,"line":3469},[3400,18045,3425],{"emptyLinePlaceholder":3424},[3400,18047,18048,18051,18053,18055,18057,18060],{"class":3402,"line":3475},[3400,18049,18050],{"class":4779},"LT_ID",[3400,18052,9180],{"class":5649},[3400,18054,4756],{"class":4755},[3400,18056,12718],{"class":4759},[3400,18058,18059],{"class":4759}," create-launch-template",[3400,18061,4767],{"class":4766},[3400,18063,18064,18067,18070],{"class":3402,"line":3481},[3400,18065,18066],{"class":4772},"    --launch-template-name",[3400,18068,18069],{"class":4759}," dotnet-api-lt",[3400,18071,4767],{"class":4766},[3400,18073,18074,18077,18080],{"class":3402,"line":3487},[3400,18075,18076],{"class":4772},"    --version-description",[3400,18078,18079],{"class":4759}," \"v1 .NET 10 API\"",[3400,18081,4767],{"class":4766},[3400,18083,18084,18087],{"class":3402,"line":3493},[3400,18085,18086],{"class":4772},"    --launch-template-data",[3400,18088,18089],{"class":4759}," \"{\n",[3400,18091,18092,18095,18098,18100,18102,18104,18106,18108],{"class":3402,"line":3499},[3400,18093,18094],{"class":4766},"        \\\"",[3400,18096,18097],{"class":4759},"ImageId",[3400,18099,5731],{"class":4766},[3400,18101,5255],{"class":4759},[3400,18103,5731],{"class":4766},[3400,18105,14163],{"class":4779},[3400,18107,5731],{"class":4766},[3400,18109,5743],{"class":4759},[3400,18111,18112,18114,18117,18119,18121,18123,18125,18127],{"class":3402,"line":3504},[3400,18113,18094],{"class":4766},[3400,18115,18116],{"class":4759},"InstanceType",[3400,18118,5731],{"class":4766},[3400,18120,5255],{"class":4759},[3400,18122,5731],{"class":4766},[3400,18124,12621],{"class":4759},[3400,18126,5731],{"class":4766},[3400,18128,5743],{"class":4759},[3400,18130,18131,18133,18136,18138,18140,18142,18144,18146],{"class":3402,"line":3510},[3400,18132,18094],{"class":4766},[3400,18134,18135],{"class":4759},"KeyName",[3400,18137,5731],{"class":4766},[3400,18139,5255],{"class":4759},[3400,18141,5731],{"class":4766},[3400,18143,12628],{"class":4759},[3400,18145,5731],{"class":4766},[3400,18147,5743],{"class":4759},[3400,18149,18150,18152,18155,18157,18159,18161,18163,18165],{"class":3402,"line":3516},[3400,18151,18094],{"class":4766},[3400,18153,18154],{"class":4759},"SecurityGroupIds",[3400,18156,5731],{"class":4766},[3400,18158,5242],{"class":4759},[3400,18160,5731],{"class":4766},[3400,18162,16570],{"class":4779},[3400,18164,5731],{"class":4766},[3400,18166,18167],{"class":4759},"],\n",[3400,18169,18170,18172,18175,18177,18179,18181,18184],{"class":3402,"line":3522},[3400,18171,18094],{"class":4766},[3400,18173,18174],{"class":4759},"UserData",[3400,18176,5731],{"class":4766},[3400,18178,5255],{"class":4759},[3400,18180,5731],{"class":4766},[3400,18182,18183],{"class":4779},"$USER_DATA",[3400,18185,18186],{"class":4766},"\\\"\n",[3400,18188,18189,18192],{"class":3402,"line":3528},[3400,18190,18191],{"class":4759},"    }\"",[3400,18193,4767],{"class":4766},[3400,18195,18196,18198,18200,18202,18204],{"class":3402,"line":3534},[3400,18197,4818],{"class":4772},[3400,18199,4776],{"class":4759},[3400,18201,10409],{"class":4779},[3400,18203,4783],{"class":4759},[3400,18205,4767],{"class":4766},[3400,18207,18208,18210,18213,18215,18217],{"class":3402,"line":3540},[3400,18209,5331],{"class":4772},[3400,18211,18212],{"class":4759}," \"LaunchTemplate.LaunchTemplateId\"",[3400,18214,9279],{"class":4772},[3400,18216,9282],{"class":4759},[3400,18218,7300],{"class":5649},[3400,18220,18221],{"class":3402,"line":3545},[3400,18222,3425],{"emptyLinePlaceholder":3424},[3400,18224,18225,18227,18230,18233],{"class":3402,"line":3550},[3400,18226,6079],{"class":4755},[3400,18228,18229],{"class":4759}," \"Launch Template ID: ",[3400,18231,18232],{"class":4779},"$LT_ID",[3400,18234,9301],{"class":4759},[5202,18236,18238,18247],{"title":18237},"aws ec2 create-launch-template",[5206,18239,18241,5214,18244],{"className":18240},[3402],[3400,18242,5213],{"className":18243},[5212],[3359,18245,18246],{},"LT_ID=$(aws ec2 create-launch-template --launch-template-name dotnet-api-lt ...)",[5206,18248,18250,18251],{"className":18249},[3402],"Launch Template ID: ",[3400,18252,18254],{"className":18253},[5259],"lt-0a1b2c3d4e5f67890",[3626,18256],{},[3824,18258,18260],{"id":18259},"крок-10-створення-auto-scaling-group","Крок 10: Створення Auto Scaling Group",[8462,18262,18263,18374],{},[8465,18264,18265],{"label":8467},[8469,18266,18267,18274,18280,18289,18303,18334,18352],{},[3656,18268,8473,18269,8914,18271],{},[3359,18270,8476],{},[3359,18272,18273],{},"Create Auto Scaling group",[3656,18275,18276,5214,18278],{},[3359,18277,9753],{},[3397,18279,8480],{},[3656,18281,18282,5214,18285,8914,18287],{},[3359,18283,18284],{},"Launch template:",[3397,18286,17846],{},[3359,18288,8941],{},[3656,18290,18291,18294],{},[3359,18292,18293],{},"Network:",[3653,18295,18296,18298],{},[3656,18297,17120],{},[3656,18299,18300,18301],{},"Availability Zones: ті самі 2 AZ, що і для ALB → ",[3359,18302,8941],{},[3656,18304,18305,18308],{},[3359,18306,18307],{},"Load balancing:",[3653,18309,18310,18316,18321,18326],{},[3656,18311,18312,18313],{},"✅ ",[3359,18314,18315],{},"Attach to an existing load balancer",[3656,18317,18318,18319],{},"Target groups: ",[3397,18320,16800],{},[3656,18322,18312,18323],{},[3359,18324,18325],{},"Turn on Elastic Load Balancing health checks",[3656,18327,18328,18329,9031,18332],{},"Health check grace period: ",[3359,18330,18331],{},"120",[3359,18333,8941],{},[3656,18335,18336,18339],{},[3359,18337,18338],{},"Group size:",[3653,18340,18341],{},[3656,18342,18343,18344,18346,18347,18349,18350],{},"Desired: ",[3359,18345,4811],{},", Minimum: ",[3359,18348,4811],{},", Maximum: ",[3359,18351,6171],{},[3656,18353,18354,18357],{},[3359,18355,18356],{},"Scaling policies:",[3653,18358,18359,18362,18365,18370],{},[3656,18360,18361],{},"✅ Target tracking scaling policy",[3656,18363,18364],{},"Metric: Average CPU utilization",[3656,18366,18367,18368],{},"Target value: ",[3359,18369,8517],{},[3656,18371,18372],{},[3359,18373,18273],{},[8465,18375,18376],{"label":8548},[3390,18377,18379],{"className":4740,"code":18378,"language":4742,"meta":3395,"style":3395},"# Створення Auto Scaling Group\naws autoscaling create-auto-scaling-group \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --launch-template \"LaunchTemplateId=$LT_ID,Version=1\" \\\n    --min-size 2 \\\n    --max-size 5 \\\n    --desired-capacity 2 \\\n    --vpc-zone-identifier \"$SUBNET_1,$SUBNET_2\" \\\n    --target-group-arns \"$TG_ARN\" \\\n    --health-check-type ELB \\\n    --health-check-grace-period 120 \\\n    --region \"$REGION\"\n\n# Додаємо Target Tracking Scaling Policy\naws autoscaling put-scaling-policy \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --policy-name cpu-target-tracking \\\n    --policy-type TargetTrackingScaling \\\n    --target-tracking-configuration '{\n        \"PredefinedMetricSpecification\": {\n            \"PredefinedMetricType\": \"ASGAverageCPUUtilization\"\n        },\n        \"TargetValue\": 70.0,\n        \"ScaleOutCooldown\": 60,\n        \"ScaleInCooldown\": 300\n    }' \\\n    --region \"$REGION\"\n",[3397,18380,18381,18386,18397,18405,18420,18428,18437,18445,18462,18475,18485,18495,18505,18509,18514,18524,18532,18540,18548,18554,18558,18562,18566,18570,18574,18579,18585],{"__ignoreMap":3395},[3400,18382,18383],{"class":3402,"line":3403},[3400,18384,18385],{"class":4749},"# Створення Auto Scaling Group\n",[3400,18387,18388,18390,18392,18395],{"class":3402,"line":3409},[3400,18389,4756],{"class":4755},[3400,18391,8560],{"class":4759},[3400,18393,18394],{"class":4759}," create-auto-scaling-group",[3400,18396,4767],{"class":4766},[3400,18398,18399,18401,18403],{"class":3402,"line":3415},[3400,18400,8570],{"class":4772},[3400,18402,8573],{"class":4759},[3400,18404,4767],{"class":4766},[3400,18406,18407,18410,18413,18415,18418],{"class":3402,"line":3421},[3400,18408,18409],{"class":4772},"    --launch-template",[3400,18411,18412],{"class":4759}," \"LaunchTemplateId=",[3400,18414,18232],{"class":4779},[3400,18416,18417],{"class":4759},",Version=1\"",[3400,18419,4767],{"class":4766},[3400,18421,18422,18424,18426],{"class":3402,"line":3428},[3400,18423,9868],{"class":4772},[3400,18425,9155],{"class":4800},[3400,18427,4767],{"class":4766},[3400,18429,18430,18433,18435],{"class":3402,"line":3434},[3400,18431,18432],{"class":4772},"    --max-size",[3400,18434,9871],{"class":4800},[3400,18436,4767],{"class":4766},[3400,18438,18439,18441,18443],{"class":3402,"line":3440},[3400,18440,9878],{"class":4772},[3400,18442,9155],{"class":4800},[3400,18444,4767],{"class":4766},[3400,18446,18447,18450,18452,18454,18456,18458,18460],{"class":3402,"line":3446},[3400,18448,18449],{"class":4772},"    --vpc-zone-identifier",[3400,18451,4776],{"class":4759},[3400,18453,17337],{"class":4779},[3400,18455,5261],{"class":4759},[3400,18457,17344],{"class":4779},[3400,18459,4783],{"class":4759},[3400,18461,4767],{"class":4766},[3400,18463,18464,18467,18469,18471,18473],{"class":3402,"line":3451},[3400,18465,18466],{"class":4772},"    --target-group-arns",[3400,18468,4776],{"class":4759},[3400,18470,4780],{"class":4779},[3400,18472,4783],{"class":4759},[3400,18474,4767],{"class":4766},[3400,18476,18477,18480,18483],{"class":3402,"line":3457},[3400,18478,18479],{"class":4772},"    --health-check-type",[3400,18481,18482],{"class":4759}," ELB",[3400,18484,4767],{"class":4766},[3400,18486,18487,18490,18493],{"class":3402,"line":3463},[3400,18488,18489],{"class":4772},"    --health-check-grace-period",[3400,18491,18492],{"class":4800}," 120",[3400,18494,4767],{"class":4766},[3400,18496,18497,18499,18501,18503],{"class":3402,"line":3469},[3400,18498,4818],{"class":4772},[3400,18500,4776],{"class":4759},[3400,18502,10409],{"class":4779},[3400,18504,9301],{"class":4759},[3400,18506,18507],{"class":3402,"line":3475},[3400,18508,3425],{"emptyLinePlaceholder":3424},[3400,18510,18511],{"class":3402,"line":3481},[3400,18512,18513],{"class":4749},"# Додаємо Target Tracking Scaling Policy\n",[3400,18515,18516,18518,18520,18522],{"class":3402,"line":3487},[3400,18517,4756],{"class":4755},[3400,18519,8560],{"class":4759},[3400,18521,8563],{"class":4759},[3400,18523,4767],{"class":4766},[3400,18525,18526,18528,18530],{"class":3402,"line":3493},[3400,18527,8570],{"class":4772},[3400,18529,8573],{"class":4759},[3400,18531,4767],{"class":4766},[3400,18533,18534,18536,18538],{"class":3402,"line":3499},[3400,18535,8580],{"class":4772},[3400,18537,8583],{"class":4759},[3400,18539,4767],{"class":4766},[3400,18541,18542,18544,18546],{"class":3402,"line":3504},[3400,18543,8590],{"class":4772},[3400,18545,8593],{"class":4759},[3400,18547,4767],{"class":4766},[3400,18549,18550,18552],{"class":3402,"line":3510},[3400,18551,8600],{"class":4772},[3400,18553,8603],{"class":4759},[3400,18555,18556],{"class":3402,"line":3516},[3400,18557,8608],{"class":4759},[3400,18559,18560],{"class":3402,"line":3522},[3400,18561,8613],{"class":4759},[3400,18563,18564],{"class":3402,"line":3528},[3400,18565,8618],{"class":4759},[3400,18567,18568],{"class":3402,"line":3534},[3400,18569,8623],{"class":4759},[3400,18571,18572],{"class":3402,"line":3540},[3400,18573,8628],{"class":4759},[3400,18575,18576],{"class":3402,"line":3545},[3400,18577,18578],{"class":4759},"        \"ScaleInCooldown\": 300\n",[3400,18580,18581,18583],{"class":3402,"line":3550},[3400,18582,8643],{"class":4759},[3400,18584,4767],{"class":4766},[3400,18586,18587,18589,18591,18593],{"class":3402,"line":3556},[3400,18588,4818],{"class":4772},[3400,18590,4776],{"class":4759},[3400,18592,10409],{"class":4779},[3400,18594,9301],{"class":4759},[5202,18596,18598,18607,18614,18617,18625,18628,18635,18642],{"title":18597},"aws autoscaling create-auto-scaling-group",[5206,18599,18601,5214,18604],{"className":18600},[3402],[3400,18602,5213],{"className":18603},[5212],[3359,18605,18606],{},"aws autoscaling create-auto-scaling-group --auto-scaling-group-name dotnet-api-asg ...",[5206,18608,18610],{"className":18609},[3402],[3400,18611,18613],{"className":18612},[5259],"(no output = success)",[5206,18615],{"className":18616},[3402],[5206,18618,18620,5214,18623],{"className":18619},[3402],[3400,18621,5213],{"className":18622},[5212],[3359,18624,8663],{},[5206,18626,5232],{"className":18627},[3402],[5206,18629,8670,18631,18634],{"className":18630},[3402],[3400,18632,8674],{"className":18633},[5240],": \"arn:aws:autoscaling:eu-central-1:123456789012:scalingPolicy:...\",",[5206,18636,8670,18638,18641],{"className":18637},[3402],[3400,18639,8685],{"className":18640},[5240],": [...]",[5206,18643,5284],{"className":18644},[3402],[3626,18646],{},[3824,18648,18650],{"id":18649},"крок-11-перевірка-роботи","Крок 11: Перевірка роботи",[3353,18652,18653],{},"Після створення ASG зачекайте ~3 хвилини, поки instances запустяться та пройдуть health check.",[5202,18655,18657,18666,18675,18678,18686,18693,18696],{"title":18656},"Перевірка балансування через ALB DNS",[5206,18658,18660,5214,18663],{"className":18659},[3402],[3400,18661,5213],{"className":18662},[5212],[3359,18664,18665],{},"curl http:\u002F\u002Fdotnet-api-alb-123456789.eu-central-1.elb.amazonaws.com\u002F",[5206,18667,18669,18670,18674],{"className":18668},[3402],"{\"message\":\"Hello from EC2!\",\"server\":\"",[3400,18671,18673],{"className":18672},[5240],"ip-172-31-10-25","\",\"dotnetVersion\":\"8.0.0\"}",[5206,18676],{"className":18677},[3402],[5206,18679,18681,5214,18684],{"className":18680},[3402],[3400,18682,5213],{"className":18683},[5212],[3359,18685,18665],{},[5206,18687,18669,18689,18674],{"className":18688},[3402],[3400,18690,18692],{"className":18691},[5259],"ip-172-31-22-43",[5206,18694],{"className":18695},[3402],[5206,18697,18699],{"className":18698},[3402],[3400,18700,18703],{"className":18701},[5212,18702],"text-sm","← поле \"server\" відрізняється між запитами — Round Robin балансування працює!",[3353,18705,18706],{},"Перевірка стану targets у Target Group:",[8462,18708,18709,18721],{},[8465,18710,8473,18711,8914,18713,8481,18715,18718,18719,6827],{"label":8467},[3359,18712,16783],{},[3397,18714,16800],{},[3359,18716,18717],{},"Targets",". Обидва instances мають бути у стані ",[3397,18720,6808],{},[8465,18722,18723],{"label":8548},[3390,18724,18726],{"className":4740,"code":18725,"language":4742,"meta":3395,"style":3395},"aws elbv2 describe-target-health \\\n    --target-group-arn \"$TG_ARN\" \\\n    --region eu-central-1 \\\n    --query \"TargetHealthDescriptions[*].{ID:Target.Id,Port:Target.Port,Health:TargetHealth.State}\" \\\n    --output table\n",[3397,18727,18728,18739,18751,18759,18768],{"__ignoreMap":3395},[3400,18729,18730,18732,18734,18737],{"class":3402,"line":3403},[3400,18731,4756],{"class":4755},[3400,18733,4760],{"class":4759},[3400,18735,18736],{"class":4759}," describe-target-health",[3400,18738,4767],{"class":4766},[3400,18740,18741,18743,18745,18747,18749],{"class":3402,"line":3409},[3400,18742,4773],{"class":4772},[3400,18744,4776],{"class":4759},[3400,18746,4780],{"class":4779},[3400,18748,4783],{"class":4759},[3400,18750,4767],{"class":4766},[3400,18752,18753,18755,18757],{"class":3402,"line":3415},[3400,18754,4818],{"class":4772},[3400,18756,5324],{"class":4759},[3400,18758,4767],{"class":4766},[3400,18760,18761,18763,18766],{"class":3402,"line":3421},[3400,18762,5331],{"class":4772},[3400,18764,18765],{"class":4759}," \"TargetHealthDescriptions[*].{ID:Target.Id,Port:Target.Port,Health:TargetHealth.State}\"",[3400,18767,4767],{"class":4766},[3400,18769,18770,18772],{"class":3402,"line":3428},[3400,18771,10005],{"class":4772},[3400,18773,10008],{"class":4759},[5202,18775,18777,18781,18785,18789,18793,18796,18804,18811],{"title":18776},"Target Health status",[5206,18778,18780],{"className":18779},[3402],"---------------------------------------------",[5206,18782,18784],{"className":18783},[3402],"|        DescribeTargetHealth               |",[5206,18786,18788],{"className":18787},[3402],"+-------------------+------+---------------+",[5206,18790,18792],{"className":18791},[3402],"| ID                | Port | Health        |",[5206,18794,18788],{"className":18795},[3402],[5206,18797,18799,18800,18803],{"className":18798},[3402],"| i-0a1b2c3d4e5f678 | 5000 | ",[3400,18801,6808],{"className":18802},[5259],"       |",[5206,18805,18807,18808,18803],{"className":18806},[3402],"| i-0b2c3d4e5f6789a | 5000 | ",[3400,18809,6808],{"className":18810},[5259],[5206,18812,18788],{"className":18813},[3402],[3626,18815],{},[3824,18817,18819],{"id":18818},"крок-12-тестування-автоматичного-масштабування","Крок 12: Тестування автоматичного масштабування",[3353,18821,18822],{},"Симулюємо штучне навантаження на один із EC2 instances, щоб спостерігати автоматичний Scale Out:",[3390,18824,18826],{"className":4740,"code":18825,"language":4742,"meta":3395,"style":3395},"# Підключіться до одного з EC2 через SSH\nssh -i ~\u002F.ssh\u002Fec2-lab-key.pem ubuntu@INSTANCE_IP\n\n# Встановіть утиліту stress\nsudo apt-get install -y stress\n\n# Навантажте CPU на 5 хвилин (2 ядра)\nstress --cpu 2 --timeout 300 &\n",[3397,18827,18828,18833,18844,18848,18853,18866,18870,18875],{"__ignoreMap":3395},[3400,18829,18830],{"class":3402,"line":3403},[3400,18831,18832],{"class":4749},"# Підключіться до одного з EC2 через SSH\n",[3400,18834,18835,18837,18839,18841],{"class":3402,"line":3409},[3400,18836,13311],{"class":4755},[3400,18838,13314],{"class":4772},[3400,18840,13317],{"class":4759},[3400,18842,18843],{"class":4759}," ubuntu@INSTANCE_IP\n",[3400,18845,18846],{"class":3402,"line":3415},[3400,18847,3425],{"emptyLinePlaceholder":3424},[3400,18849,18850],{"class":3402,"line":3421},[3400,18851,18852],{"class":4749},"# Встановіть утиліту stress\n",[3400,18854,18855,18857,18859,18861,18863],{"class":3402,"line":3428},[3400,18856,13334],{"class":4755},[3400,18858,13337],{"class":4759},[3400,18860,13349],{"class":4759},[3400,18862,13352],{"class":4772},[3400,18864,18865],{"class":4759}," stress\n",[3400,18867,18868],{"class":3402,"line":3434},[3400,18869,3425],{"emptyLinePlaceholder":3424},[3400,18871,18872],{"class":3402,"line":3440},[3400,18873,18874],{"class":4749},"# Навантажте CPU на 5 хвилин (2 ядра)\n",[3400,18876,18877,18880,18883,18885,18888,18891],{"class":3402,"line":3446},[3400,18878,18879],{"class":4755},"stress",[3400,18881,18882],{"class":4772}," --cpu",[3400,18884,9155],{"class":4800},[3400,18886,18887],{"class":4772}," --timeout",[3400,18889,18890],{"class":4800}," 300",[3400,18892,18893],{"class":5649}," &\n",[3353,18895,18896],{},"Спостерігайте за поведінкою ASG:",[8462,18898,18899,18912],{},[8465,18900,8473,18901,8914,18903,8481,18905,18908,18909,6827],{"label":8467},[3359,18902,8476],{},[3397,18904,8480],{},[3359,18906,18907],{},"Activity"," → через 1–3 хвилини після зростання CPU з'явиться подія ",[3397,18910,18911],{},"Launching a new EC2 instance",[8465,18913,18914],{"label":8548},[3390,18915,18917],{"className":4740,"code":18916,"language":4742,"meta":3395,"style":3395},"# Моніторинг поточної кількості instances (оновлюйте кожні 30 сек)\nwatch -n 30 'aws autoscaling describe-auto-scaling-groups \\\n    --auto-scaling-group-names dotnet-api-asg \\\n    --query \"AutoScalingGroups[0].{Min:MinSize,Desired:DesiredCapacity,Max:MaxSize,Running:Instances[?LifecycleState==\\`InService\\`]|length(@)}\" \\\n    --output table \\\n    --region eu-central-1'\n",[3397,18918,18919,18924,18936,18941,18946,18951],{"__ignoreMap":3395},[3400,18920,18921],{"class":3402,"line":3403},[3400,18922,18923],{"class":4749},"# Моніторинг поточної кількості instances (оновлюйте кожні 30 сек)\n",[3400,18925,18926,18928,18931,18933],{"class":3402,"line":3409},[3400,18927,12453],{"class":4755},[3400,18929,18930],{"class":4772}," -n",[3400,18932,10994],{"class":4800},[3400,18934,18935],{"class":4759}," 'aws autoscaling describe-auto-scaling-groups \\\n",[3400,18937,18938],{"class":3402,"line":3415},[3400,18939,18940],{"class":4759},"    --auto-scaling-group-names dotnet-api-asg \\\n",[3400,18942,18943],{"class":3402,"line":3421},[3400,18944,18945],{"class":4759},"    --query \"AutoScalingGroups[0].{Min:MinSize,Desired:DesiredCapacity,Max:MaxSize,Running:Instances[?LifecycleState==\\`InService\\`]|length(@)}\" \\\n",[3400,18947,18948],{"class":3402,"line":3428},[3400,18949,18950],{"class":4759},"    --output table \\\n",[3400,18952,18953],{"class":3402,"line":3434},[3400,18954,18955],{"class":4759},"    --region eu-central-1'\n",[5202,18957,18959,18963,18967,18971,18975,18979,18990,18993],{"title":18958},"ASG масштабування в реальному часі",[5206,18960,18962],{"className":18961},[3402],"----------------------------",[5206,18964,18966],{"className":18965},[3402],"| DescribeAutoScalingGroups |",[5206,18968,18970],{"className":18969},[3402],"+------+---------+-----+---+",[5206,18972,18974],{"className":18973},[3402],"| Min  | Desired | Max | Running |",[5206,18976,18978],{"className":18977},[3402],"+------+---------+-----+---------+",[5206,18980,18982,18983,18986,18987,18803],{"className":18981},[3402],"| 2    | ",[3400,18984,6728],{"className":18985},[6151],"       | 5   | ",[3400,18988,6728],{"className":18989},[5259],[5206,18991,18978],{"className":18992},[3402],[5206,18994,18996],{"className":18995},[3402],[3400,18997,18999],{"className":18998},[6151],"← ASG збільшив Desired з 2 до 3 (Scale Out спрацював!)",[6209,19001,19002,19005,19006,19009],{},[3359,19003,19004],{},"Спостережуваність:"," Scale Out (додавання instances) спрацьовує швидко — через 1–3 хвилини після зростання CPU. Scale In (видалення instances) навмисно відбувається повільніше — через 5+ хвилин після нормалізації CPU, оскільки ",[3397,19007,19008],{},"ScaleInCooldown = 300с",". Це захищає від ситуації, коли ASG поспіхом видаляє instance під час тимчасового спаду між піками.",[3626,19011],{},[3824,19013,19015],{"id":19014},"крок-13-обовязково-очищення-ресурсів","Крок 13: ОБОВ'ЯЗКОВО — Очищення ресурсів",[6829,19017,19018,19019,19022],{},"ALB з двома EC2 t3.micro instances коштує приблизно: ALB ~$0.04\u002Fгод + EC2 ×2 ",[15043,19020,19021],{},"$0.046\u002Fгод = **","$62\u002Fмісяць**. Завжди видаляйте ресурси після завершення лабораторної роботи!",[3353,19024,19025],{},"Порядок видалення критично важливий через залежності між ресурсами. Видалення в неправильному порядку призведе до помилок.",[8462,19027,19028,19114],{},[8465,19029,19030],{"label":8467},[8469,19031,19032,19046,19052,19063,19074,19086,19098,19104],{},[3656,19033,19034,8914,19037,8914,19039,19042,19043],{},[3359,19035,19036],{},"EC2 → Auto Scaling Groups",[3397,19038,8480],{},[3359,19040,19041],{},"Delete"," → підтвердіть ",[8530,19044,19045],{},"(ASG автоматично завершить EC2 instances)",[3656,19047,19048,19049],{},"Зачекайте ~2 хвилини поки всі instances перейдуть у стан ",[3397,19050,19051],{},"terminated",[3656,19053,19054,8914,19057,8914,19059,8914,19061],{},[3359,19055,19056],{},"EC2 → Load Balancers",[3397,19058,10261],{},[3359,19060,14001],{},[3359,19062,19041],{},[3656,19064,19065,8914,19068,8914,19070,8914,19072],{},[3359,19066,19067],{},"EC2 → Target Groups",[3397,19069,16800],{},[3359,19071,14001],{},[3359,19073,19041],{},[3656,19075,19076,8914,19079,8914,19081,8914,19083],{},[3359,19077,19078],{},"EC2 → Launch Templates",[3397,19080,17846],{},[3359,19082,14001],{},[3359,19084,19085],{},"Delete template",[3656,19087,19088,8914,19091,8914,19093,8914,19095],{},[3359,19089,19090],{},"EC2 → AMIs",[3397,19092,14013],{},[3359,19094,14001],{},[3359,19096,19097],{},"Deregister AMI",[3656,19099,19100,19103],{},[3359,19101,19102],{},"EC2 → Snapshots"," → видаліть snapshot від вашого AMI",[3656,19105,19106,19109,19110,14290,19112],{},[3359,19107,19108],{},"VPC → Security Groups"," → видаліть ",[3397,19111,16152],{},[3397,19113,16187],{},[8465,19115,19116],{"label":8548},[3390,19117,19119],{"className":4740,"code":19118,"language":4742,"meta":3395,"style":3395},"REGION=\"eu-central-1\"\n\n# 1. Видалити ASG — автоматично завершить всі instances\naws autoscaling delete-auto-scaling-group \\\n    --auto-scaling-group-name dotnet-api-asg \\\n    --force-delete --region \"$REGION\"\n\necho \"Waiting for instances to terminate (~2 min)...\"\nsleep 120\n\n# 2. Видалити ALB\naws elbv2 delete-load-balancer \\\n    --load-balancer-arn \"$ALB_ARN\" --region \"$REGION\"\n\n# 3. Видалити Target Group\naws elbv2 delete-target-group \\\n    --target-group-arn \"$TG_ARN\" --region \"$REGION\"\n\n# 4. Видалити Launch Template\naws ec2 delete-launch-template \\\n    --launch-template-id \"$LT_ID\" --region \"$REGION\"\n\n# 5. Видалити Security Groups\naws ec2 delete-security-group --group-id \"$EC2_SG\" --region \"$REGION\"\naws ec2 delete-security-group --group-id \"$ALB_SG\" --region \"$REGION\"\n\necho \"Cleanup complete!\"\n",[3397,19120,19121,19129,19133,19138,19149,19157,19170,19174,19181,19188,19192,19197,19208,19226,19230,19235,19246,19264,19268,19273,19284,19303,19307,19312,19338,19362,19366],{"__ignoreMap":3395},[3400,19122,19123,19125,19127],{"class":3402,"line":3403},[3400,19124,12682],{"class":4779},[3400,19126,5650],{"class":5649},[3400,19128,12687],{"class":4759},[3400,19130,19131],{"class":3402,"line":3409},[3400,19132,3425],{"emptyLinePlaceholder":3424},[3400,19134,19135],{"class":3402,"line":3415},[3400,19136,19137],{"class":4749},"# 1. Видалити ASG — автоматично завершить всі instances\n",[3400,19139,19140,19142,19144,19147],{"class":3402,"line":3421},[3400,19141,4756],{"class":4755},[3400,19143,8560],{"class":4759},[3400,19145,19146],{"class":4759}," delete-auto-scaling-group",[3400,19148,4767],{"class":4766},[3400,19150,19151,19153,19155],{"class":3402,"line":3428},[3400,19152,8570],{"class":4772},[3400,19154,8573],{"class":4759},[3400,19156,4767],{"class":4766},[3400,19158,19159,19162,19164,19166,19168],{"class":3402,"line":3434},[3400,19160,19161],{"class":4772},"    --force-delete",[3400,19163,10404],{"class":4772},[3400,19165,4776],{"class":4759},[3400,19167,10409],{"class":4779},[3400,19169,9301],{"class":4759},[3400,19171,19172],{"class":3402,"line":3440},[3400,19173,3425],{"emptyLinePlaceholder":3424},[3400,19175,19176,19178],{"class":3402,"line":3446},[3400,19177,6079],{"class":4755},[3400,19179,19180],{"class":4759}," \"Waiting for instances to terminate (~2 min)...\"\n",[3400,19182,19183,19185],{"class":3402,"line":3451},[3400,19184,15457],{"class":4755},[3400,19186,19187],{"class":4800}," 120\n",[3400,19189,19190],{"class":3402,"line":3457},[3400,19191,3425],{"emptyLinePlaceholder":3424},[3400,19193,19194],{"class":3402,"line":3463},[3400,19195,19196],{"class":4749},"# 2. Видалити ALB\n",[3400,19198,19199,19201,19203,19206],{"class":3402,"line":3469},[3400,19200,4756],{"class":4755},[3400,19202,4760],{"class":4759},[3400,19204,19205],{"class":4759}," delete-load-balancer",[3400,19207,4767],{"class":4766},[3400,19209,19210,19212,19214,19216,19218,19220,19222,19224],{"class":3402,"line":3475},[3400,19211,10367],{"class":4772},[3400,19213,4776],{"class":4759},[3400,19215,10372],{"class":4779},[3400,19217,4783],{"class":4759},[3400,19219,10404],{"class":4772},[3400,19221,4776],{"class":4759},[3400,19223,10409],{"class":4779},[3400,19225,9301],{"class":4759},[3400,19227,19228],{"class":3402,"line":3481},[3400,19229,3425],{"emptyLinePlaceholder":3424},[3400,19231,19232],{"class":3402,"line":3487},[3400,19233,19234],{"class":4749},"# 3. Видалити Target Group\n",[3400,19236,19237,19239,19241,19244],{"class":3402,"line":3493},[3400,19238,4756],{"class":4755},[3400,19240,4760],{"class":4759},[3400,19242,19243],{"class":4759}," delete-target-group",[3400,19245,4767],{"class":4766},[3400,19247,19248,19250,19252,19254,19256,19258,19260,19262],{"class":3402,"line":3499},[3400,19249,4773],{"class":4772},[3400,19251,4776],{"class":4759},[3400,19253,4780],{"class":4779},[3400,19255,4783],{"class":4759},[3400,19257,10404],{"class":4772},[3400,19259,4776],{"class":4759},[3400,19261,10409],{"class":4779},[3400,19263,9301],{"class":4759},[3400,19265,19266],{"class":3402,"line":3504},[3400,19267,3425],{"emptyLinePlaceholder":3424},[3400,19269,19270],{"class":3402,"line":3510},[3400,19271,19272],{"class":4749},"# 4. Видалити Launch Template\n",[3400,19274,19275,19277,19279,19282],{"class":3402,"line":3516},[3400,19276,4756],{"class":4755},[3400,19278,12718],{"class":4759},[3400,19280,19281],{"class":4759}," delete-launch-template",[3400,19283,4767],{"class":4766},[3400,19285,19286,19289,19291,19293,19295,19297,19299,19301],{"class":3402,"line":3522},[3400,19287,19288],{"class":4772},"    --launch-template-id",[3400,19290,4776],{"class":4759},[3400,19292,18232],{"class":4779},[3400,19294,4783],{"class":4759},[3400,19296,10404],{"class":4772},[3400,19298,4776],{"class":4759},[3400,19300,10409],{"class":4779},[3400,19302,9301],{"class":4759},[3400,19304,19305],{"class":3402,"line":3528},[3400,19306,3425],{"emptyLinePlaceholder":3424},[3400,19308,19309],{"class":3402,"line":3534},[3400,19310,19311],{"class":4749},"# 5. Видалити Security Groups\n",[3400,19313,19314,19316,19318,19321,19324,19326,19328,19330,19332,19334,19336],{"class":3402,"line":3540},[3400,19315,4756],{"class":4755},[3400,19317,12718],{"class":4759},[3400,19319,19320],{"class":4759}," delete-security-group",[3400,19322,19323],{"class":4772}," --group-id",[3400,19325,4776],{"class":4759},[3400,19327,16570],{"class":4779},[3400,19329,4783],{"class":4759},[3400,19331,10404],{"class":4772},[3400,19333,4776],{"class":4759},[3400,19335,10409],{"class":4779},[3400,19337,9301],{"class":4759},[3400,19339,19340,19342,19344,19346,19348,19350,19352,19354,19356,19358,19360],{"class":3402,"line":3545},[3400,19341,4756],{"class":4755},[3400,19343,12718],{"class":4759},[3400,19345,19320],{"class":4759},[3400,19347,19323],{"class":4772},[3400,19349,4776],{"class":4759},[3400,19351,16381],{"class":4779},[3400,19353,4783],{"class":4759},[3400,19355,10404],{"class":4772},[3400,19357,4776],{"class":4759},[3400,19359,10409],{"class":4779},[3400,19361,9301],{"class":4759},[3400,19363,19364],{"class":3402,"line":3550},[3400,19365,3425],{"emptyLinePlaceholder":3424},[3400,19367,19368,19370],{"class":3402,"line":3556},[3400,19369,6079],{"class":4755},[3400,19371,19372],{"class":4759}," \"Cleanup complete!\"\n",[3626,19374],{},[3348,19376,19378],{"id":19377},"підсумок","Підсумок",[3639,19380,19381,19410,19439,19473],{},[3642,19382,19385],{"icon":19383,"title":19384},"i-heroicons-arrows-right-left","Elastic Load Balancing",[3653,19386,19387,19393,19399,19404],{},[3656,19388,19389,19392],{},[3359,19390,19391],{},"ALB"," — Layer 7 (HTTP\u002FHTTPS): URL-маршрутизація, SSL termination, WebSocket, gRPC",[3656,19394,19395,19398],{},[3359,19396,19397],{},"NLB"," — Layer 4 (TCP\u002FUDP): ultra-low latency, статична IP",[3656,19400,19401,19403],{},[3359,19402,4162],{}," — пул серверів з health check; ALB виключає unhealthy targets автоматично",[3656,19405,19406,19409],{},[3359,19407,19408],{},"Path\u002FHost routing"," — один ALB для всіх мікросервісів",[3642,19411,19414],{"icon":19412,"title":19413},"i-heroicons-arrow-trending-up","Auto Scaling Group",[3653,19415,19416,19422,19427,19433],{},[3656,19417,19418,19421],{},[3359,19419,19420],{},"min\u002Fdesired\u002Fmax"," — три параметри що визначають границі масштабування",[3656,19423,19424,19426],{},[3359,19425,8129],{}," — специфікація нового instance (AMI + type + SG + user data)",[3656,19428,19429,19432],{},[3359,19430,19431],{},"Target Tracking"," — «підтримуй CPU 70%», найпростіший підхід",[3656,19434,19435,19438],{},[3359,19436,19437],{},"ScaleInCooldown > ScaleOutCooldown"," — захист від передчасного масштабування вниз",[3642,19440,19442],{"icon":3693,"title":19441},"Health Checks та Безпека",[3653,19443,19444,19455,19461,19467],{},[3656,19445,19446,19451,19452],{},[3359,19447,19448,19450],{},[3397,19449,6796],{}," перевіряє реальні залежності"," (БД, Redis) — не лише ",[3397,19453,19454],{},"return 200",[3656,19456,19457,19460],{},[3359,19458,19459],{},"Security Group reference"," замість CIDR для EC2 → ALB",[3656,19462,19463,19466],{},[3359,19464,19465],{},"Sticky sessions"," — технічний борг; правильне рішення — Redis",[3656,19468,19469,19472],{},[3359,19470,19471],{},"HTTP→HTTPS redirect"," обов'язковий для production",[3642,19474,19477],{"icon":19475,"title":19476},"i-heroicons-lock-closed","SSL\u002FTLS та ACM",[3653,19478,19479,19485,19490,19496],{},[3656,19480,19481,19484],{},[3359,19482,19483],{},"ACM"," — безкоштовні SSL-сертифікати для AWS-сервісів",[3656,19486,19487,19489],{},[3359,19488,15223],{}," — рекомендований метод підтвердження",[3656,19491,19492,19495],{},[3359,19493,19494],{},"SSL termination"," відбувається на ALB — EC2 отримує незашифрований HTTP",[3656,19497,19498,19500],{},[3359,19499,19483],{}," автоматично поновлює сертифікати до закінчення терміну дії",[3626,19502],{},[3348,19504,19506],{"id":19505},"практичні-завдання","Практичні завдання",[3824,19508,19510],{"id":19509},"рівень-1-концептуальне-розуміння","Рівень 1 — Концептуальне розуміння",[3353,19512,19513,19516],{},[3359,19514,19515],{},"Завдання 1."," Поясніть різницю між ALB та NLB з точки зору моделі OSI. Що означає «Layer 7» та «Layer 4»? Наведіть конкретний приклад сценарію, де кожен тип є оптимальним вибором.",[3353,19518,19519,19522,19523,19525],{},[3359,19520,19521],{},"Завдання 2."," Що відбудеться, якщо ",[3397,19524,6796],{}," endpoint завжди повертає HTTP 200, але застосунок насправді не може підключитись до бази даних? Як ALB визначає стан сервера? Як правильно реалізувати health check?",[3824,19527,19529],{"id":19528},"рівень-2-практична-реалізація","Рівень 2 — Практична реалізація",[3353,19531,19532,19535,19536,19539,19540,14290,19542,19545,19546,19548,19549,19551],{},[3359,19533,19534],{},"Завдання 3."," Налаштуйте ALB з path-based routing для двох .NET сервісів: ",[3397,19537,19538],{},"UserService"," (порт 5001) за шляхом ",[3397,19541,10293],{},[3397,19543,19544],{},"OrderService"," (порт 5002) за ",[3397,19547,10324],{},". Протестуйте routing через ",[3397,19550,12857],{},". Поясніть, чому це зручніше ніж два окремих ALB.",[3353,19553,19554,19557,19558,19561,19562,19565,19566,19568,19569,6827],{},[3359,19555,19556],{},"Завдання 4."," Налаштуйте ASG із Scheduled Scaling: вранці (08:00 UTC, пн-пт) ",[3397,19559,19560],{},"desired = 3",", ввечері (20:00 UTC) ",[3397,19563,19564],{},"desired = 1",". Поясніть, чому ",[3397,19567,8884],{}," зазвичай встановлюють більшим за ",[3397,19570,8880],{},[3824,19572,19574],{"id":19573},"рівень-3-системне-проектування","Рівень 3 — Системне проектування",[3353,19576,19577,19580],{},[3359,19578,19579],{},"Завдання 5."," Спроектуйте інфраструктуру для .NET API з наступними вимогами: HTTPS з автоматичним редиректом, автомасштабування від 2 до 10 instances, RDS PostgreSQL у приватній підмережі, Redis для розподіленого кешування сесій, health check перевіряє одночасно БД і Redis. Security Groups мають дозволяти EC2→RDS та EC2→Redis трафік виключно між собою. Намалюйте архітектурну схему у PlantUML та обґрунтуйте кожне рішення.",[19582,19583,19584],"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 .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}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 .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .s0P7L, html code.shiki .s0P7L{--shiki-light:#800000;--shiki-default:#808080;--shiki-dark:#808080}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sa4r_, html code.shiki .sa4r_{--shiki-light:#E50000;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}",{"title":3395,"searchDepth":3409,"depth":3409,"links":19586},[19587,19588,19589,19595,19596,19600,19601,19605,19610,19614,19619,19630,19646,19647],{"id":3350,"depth":3409,"text":3351},{"id":3630,"depth":3409,"text":3631},{"id":3811,"depth":3409,"text":3812,"children":19590},[19591,19592,19593,19594],{"id":3826,"depth":3415,"text":3827},{"id":4010,"depth":3415,"text":4011},{"id":4064,"depth":3415,"text":4065},{"id":4156,"depth":3415,"text":4157},{"id":4188,"depth":3409,"text":4189},{"id":4376,"depth":3409,"text":4377,"children":19597},[19598,19599],{"id":4380,"depth":3415,"text":4381},{"id":6498,"depth":3415,"text":6499},{"id":6842,"depth":3409,"text":6843},{"id":7885,"depth":3409,"text":7886,"children":19602},[19603,19604],{"id":7895,"depth":3415,"text":7896},{"id":8123,"depth":3415,"text":8124},{"id":8299,"depth":3409,"text":8300,"children":19606},[19607,19608,19609],{"id":8456,"depth":3415,"text":8457},{"id":8888,"depth":3415,"text":8889},{"id":9721,"depth":3415,"text":9722},{"id":10070,"depth":3409,"text":10071,"children":19611},[19612,19613],{"id":10074,"depth":3415,"text":10075},{"id":10721,"depth":3415,"text":10722},{"id":11029,"depth":3409,"text":11030,"children":19615},[19616,19617,19618],{"id":11033,"depth":3415,"text":11034},{"id":11288,"depth":3415,"text":11289},{"id":11395,"depth":3415,"text":11396},{"id":11626,"depth":3409,"text":11627,"children":19620},[19621,19622,19623,19624,19625,19626,19627,19628,19629],{"id":11633,"depth":3415,"text":11634},{"id":12570,"depth":3415,"text":12571},{"id":12580,"depth":3415,"text":12581},{"id":13287,"depth":3415,"text":13288},{"id":13418,"depth":3415,"text":13419},{"id":13566,"depth":3415,"text":13567},{"id":13871,"depth":3415,"text":13872},{"id":13981,"depth":3415,"text":13982},{"id":14275,"depth":3415,"text":14276},{"id":14279,"depth":3409,"text":14280,"children":19631},[19632,19633,19634,19635,19636,19637,19638,19639,19640,19641,19642,19643,19644,19645],{"id":14480,"depth":3415,"text":14481},{"id":14568,"depth":3415,"text":14569},{"id":14762,"depth":3415,"text":14763},{"id":15094,"depth":3415,"text":15095},{"id":15151,"depth":3415,"text":15152},{"id":15938,"depth":3415,"text":15939},{"id":16771,"depth":3415,"text":16772},{"id":17070,"depth":3415,"text":17071},{"id":17645,"depth":3415,"text":17646},{"id":17818,"depth":3415,"text":17819},{"id":18259,"depth":3415,"text":18260},{"id":18649,"depth":3415,"text":18650},{"id":18818,"depth":3415,"text":18819},{"id":19014,"depth":3415,"text":19015},{"id":19377,"depth":3409,"text":19378},{"id":19505,"depth":3409,"text":19506,"children":19648},[19649,19650,19651],{"id":19509,"depth":3415,"text":19510},{"id":19528,"depth":3415,"text":19529},{"id":19573,"depth":3415,"text":19574},"Глибоке дослідження Application Load Balancer, Network Load Balancer, Target Groups та Auto Scaling Groups для .NET застосунків — від фундаментальних принципів балансування навантаження до повного налаштування HTTPS, health checks та стратегій автоматичного масштабування.","md",null,{},{"title":3242,"description":19652},"hHxtQsBIFvzct5sHBBLLqQvKB2bRoev-h83p1Rnaq3s",[19659,19661],{"title":3238,"path":3239,"stem":3240,"description":19660,"children":-1},"Повний довідник AWS CLI команд для Amazon EC2 — instances, AMI, Key Pairs, Security Groups, Elastic IP, EBS, snapshots, IAM Instance Profile, tags. Усі важливі команди з прикладами та очікуваним виводом.",{"title":3246,"path":3247,"stem":3248,"description":19662,"children":-1},"Повний посібник з Amazon S3 для .NET і React розробників. Buckets, Storage Classes, Versioning, Lifecycle Policies, безпека, статичний хостинг, CORS, Presigned URLs, SDK for .NET та медіа-стрімінг HLS\u002FDASH.",1782371304588]