<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Lali</title><generator>teletype.in</generator><description><![CDATA[Lali]]></description><image><url>https://img2.teletype.in/files/9e/1b/9e1bb239-36e7-4587-b05e-ce6b97a76bfa.png</url><title>Lali</title><link>https://teletype.in/@lalimi</link></image><link>https://teletype.in/@lalimi?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/lalimi?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/lalimi?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 23 Apr 2026 23:08:05 GMT</pubDate><lastBuildDate>Thu, 23 Apr 2026 23:08:05 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@lalimi/C1mzBjYP8NO</guid><link>https://teletype.in/@lalimi/C1mzBjYP8NO?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/C1mzBjYP8NO?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Як продавати цифрові продукти в Україні: повний гайд 2026</title><pubDate>Thu, 09 Apr 2026 19:00:48 GMT</pubDate><description><![CDATA[<img src="https://img1.teletype.in/files/8b/39/8b394ba5-40ea-477c-9236-80b0a069b480.png"></img>Цифровий продукт — це будь-який контент, який покупець отримує у вигляді файлу або доступу: електронна книга, відеокурс, Notion-шаблон, пресет для Lightroom, гайд у PDF, пензлі для Procreate.]]></description><content:encoded><![CDATA[
  <h2 id="bQ5q">Що таке цифровий продукт і чому це вигідно</h2>
  <p id="Anms">Цифровий продукт — це будь-який контент, який покупець отримує у вигляді файлу або доступу: електронна книга, відеокурс, Notion-шаблон, пресет для Lightroom, гайд у PDF, пензлі для Procreate.</p>
  <p id="P1YC">Головна перевага над фізичними товарами — <strong>нульова собівартість кожного наступного продажу</strong>. Ви створюєте продукт один раз, а продаєте його необмежену кількість разів. Немає склadu, доставки, залишків.</p>
  <p id="NbP5">У 2026 році ринок цифрових продуктів в Україні активно зростає. За даними глобальних досліджень, ринок e-learning щорічно збільшується на 15–20%. Українські автори — дизайнери, нутриціологи, SMM-спеціалісти, коучі — все частіше монетизують свою експертизу саме через цифровий контент.</p>
  <hr />
  <h2 id="a1Zf">Що можна продавати: конкретні приклади</h2>
  <p id="9TLe"><strong>Освіта та експертиза:</strong></p>
  <ul id="8rOY">
    <li id="wZrP">Відеокурси та вебінари</li>
    <li id="bsBl">PDF-гайди та чек-листи</li>
    <li id="9nca">Воркбуки та практичні завдання</li>
    <li id="eSvP">Закриті Telegram-канали з навчальними матеріалами</li>
  </ul>
  <p id="GVsX"><strong>Дизайн та творчість:</strong></p>
  <ul id="zlsY">
    <li id="5J49">Шаблони для Notion, Figma, Canva, Excel</li>
    <li id="oZ8I">Пресети для Lightroom та Capture One</li>
    <li id="jPYT">Пензлі та текстури для Procreate та Photoshop</li>
    <li id="4yEw">Стоковий цифровий арт та 3D-моделі</li>
  </ul>
  <p id="NkZm"><strong>Бізнес та автоматизація:</strong></p>
  <ul id="cOwb">
    <li id="kBwP">Готові скрипти для продажів</li>
    <li id="Bc0n">Таблиці фінансового обліку</li>
    <li id="QkLR">Медіаплани та контент-стратегії</li>
  </ul>
  <hr />
  <h2 id="Xofg">Де продавати цифрові продукти в Україні</h2>
  <p id="amhx">Це найважливіше питання. Розберемо варіанти.</p>
  <h3 id="e3RI">Варіант 1: &quot;На картку через Telegram&quot;</h3>
  <p id="11Pp">Найпоширеніший спосіб серед початківців. Покупець переказує гроші, автор вручну надсилає файл.</p>
  <p id="YMeJ"><strong>Проблеми:</strong></p>
  <ul id="JWvH">
    <li id="0Mms">Прийом оплат на особисту картку за товари є порушенням податкового законодавства при регулярному характері</li>
    <li id="sEVV">Немає автоматизації — кожен продаж вимагає вашого часу</li>
    <li id="AR3h">Неможливо масштабуватися</li>
    <li id="UQaD">Немає аналітики, статистики, повторних продажів</li>
  </ul>
  <h3 id="mubI">Варіант 2: Іноземні платформи (Gumroad, Patreon, Stanstore)</h3>
  <p id="LCmy">Здавалося б, зручне рішення. Але для українських авторів — суцільний головний біль.</p>
  <p id="ZGQe"><strong>Реальні проблеми:</strong></p>
  <ul id="EKQ7">
    <li id="cTvM">Gumroad вимагає Payoneer або PayPal для виводу — обидва погано або зовсім не працюють з українськими ФОП</li>
    <li id="PdkW">Подвійна конвертація: долар → гривня через посередника — це додаткові 2–4% втрат</li>
    <li id="MEWn">Блокування виплат без пояснень — масова проблема 2022–2024 років</li>
    <li id="L26v">Покупці в Україні часто не можуть оплатити через іноземний еквайринг українськими картками</li>
  </ul>
  <h3 id="kTWX">Варіант 3: Українська платформа BlackSea</h3>
  <p id="lmNq"><a href="https://blacksea.click" target="_blank">BlackSea</a> — перший спеціалізований маркетплейс цифрових продуктів в Україні.</p>
  <p id="rabi"><strong>Ключові переваги:</strong></p>
  <ul id="jyTS">
    <li id="Tlf6">Гроші надходять <strong>напряму на ваш рахунок ФОП</strong> без посередників</li>
    <li id="weLT">Покупці платять зручно: Monobank, PrivatBank, Apple Pay, Google Pay</li>
    <li id="4G88">Комісія <strong>10%</strong> від продажу — і більше нічого (жодних підписок, плати за зберігання файлів або вивід коштів)</li>
    <li id="FYhW">Платформа автоматично надсилає файл покупцеві одразу після оплати</li>
    <li id="5DOu">Підтримує як ФОП, так і фізичних осіб</li>
  </ul>
  <hr />
  <h2 id="NWew">Як легально продавати: питання ФОП та податків</h2>
  <p id="ijYX">Це питання зупиняє багатьох авторів. Насправді все простіше, ніж здається.</p>
  <h3 id="mdBj">Хто може продавати цифрові продукти?</h3>
  <p id="N6Mk"><strong>ФОП (фізична особа-підприємець)</strong> — найзручніший варіант. Для продажу цифрових продуктів підходить ФОП 2-ї або 3-ї групи спрощеної системи оподаткування. Ставка — 5% від доходу для 3-ї групи.</p>
  <p id="JWkw"><strong>Фізична особа</strong> — технічно можливо, але при регулярному доході потрібно сплачувати ПДФО (18%) та військовий збір (5%). Якщо ви серйозно плануєте монетизацію, реєстрація ФОП — правильний крок.</p>
  <h3 id="yZts">Як відбувається оподаткування через BlackSea?</h3>
  <p id="zKap">При роботі через платформу з агентською схемою платформа виступає агентом. Ваш оподатковуваний дохід — сума, що надходить на ваш рахунок після вирахування комісії. Платформа надає всі необхідні документи для звітності.</p>
  <hr />
  <h2 id="8Yxx">Покроковий план: як почати продавати</h2>
  <p id="Ilez"><strong>Крок 1. Визначте свій продукт</strong></p>
  <p id="N0MT">Запитайте себе: яке питання ваша аудиторія ставить найчастіше? Відповідь на це питання у зручному форматі (PDF, відео, шаблон) — це і є ваш перший цифровий продукт.</p>
  <p id="AzeZ">Не намагайтеся зробити &quot;ідеальний&quot; великий курс одразу. Почніть з малого: гайд на 10–15 сторінок або чек-лист вирішать конкретну проблему і коштуватимуть 150–500 грн.</p>
  <p id="2dRV"><strong>Крок 2. Встановіть ціну</strong></p>
  <p id="fo4M">Розповсюджена помилка — занижувати ціну. Цифровий продукт вирішує реальну проблему, і це коштує грошей. Орієнтири для українського ринку:</p>
  <ul id="dUeF">
    <li id="Qhzz">Чек-лист / короткий гайд: 100–300 грн</li>
    <li id="vXET">Детальний гайд / воркбук: 300–800 грн</li>
    <li id="oY6b">Шаблони (Notion, Figma): 200–600 грн</li>
    <li id="z00m">Міні-курс (3–5 відео): 500–1500 грн</li>
    <li id="7Gxs">Повноцінний курс: 1500–5000+ грн</li>
  </ul>
  <p id="YcaK"><strong>Крок 3. Зареєструйтеся на платформі</strong></p>
  <p id="nynd">Реєстрація на BlackSea займає кілька хвилин. Завантажте свій продукт, заповніть опис, встановіть ціну — і ваша сторінка продажу готова. Посилання можна одразу розміщувати в Instagram Bio, Telegram-каналі або YouTube.</p>
  <p id="V2Ro"><strong>Крок 4. Просування</strong></p>
  <p id="ioro">Для першого продажу не потрібна велика аудиторія. Достатньо 200–300 підписників у будь-якій соцмережі. Розкажіть про свій продукт у сторіс, напишіть пост-кейс, покажіть, яку проблему він вирішує — і перші продажі не змусять чекати.</p>
  <p id="NvOZ"><strong>Крок 5. Масштабування</strong></p>
  <p id="RrVV">Отримавши перші відгуки, вдосконалюйте продукт і додавайте нові. Авторам із BlackSea доступний каталог маркетплейсу — ваш продукт побачать покупці, які самостійно шукають контент у вашій ніші (з комісією 15%).</p>
  <hr />
  <h2 id="NrOt">Поширені питання (FAQ)</h2>
  <p id="ZNrU"><strong>Чи можна продавати без ФОП?</strong> Так, BlackSea працює з фізичними особами. Але якщо ви плануєте стабільний дохід, реєстрація ФОП 3-ї групи захистить від зайвих питань з боку податкової.</p>
  <p id="946z"><strong>Як покупець отримує товар?</strong> Автоматично на пошту одразу після підтвердження оплати. Ваша участь не потрібна.</p>
  <p id="pAgB"><strong>Що якщо мій продукт скопіюють і поширять безкоштовно?</strong> Повністю захиститися неможливо, але правильне ціноутворення і регулярне оновлення контенту роблять &quot;злив&quot; менш критичним. Нові покупці завжди купуватимуть актуальну версію.</p>
  <p id="Z9aQ"><strong>Чи є мінімальна сума для виводу коштів?</strong> На BlackSea виплати надходять безпосередньо на рахунок автора без значних затримок.</p>
  <p id="FBVK"><strong>Скільки часу займає налаштування?</strong> Реально — від 30 хвилин до кількох годин залежно від складності продукту. Платформа не вимагає технічних знань.</p>
  <hr />
  <h2 id="PiLQ">Висновок</h2>
  <p id="bWWH">Продавати цифрові продукти в Україні у 2026 році — реально, легально і без зайвих технічних складнощів. Головне — обрати правильний інструмент, який бере на себе всю операційну роботу: прийом платежів, зберігання файлів, видачу покупцям і аналітику.</p>
  <p id="aw0o">Якщо ви хочете почати без зайвого головного болю — <a href="https://blacksea.click" target="_blank">зареєструйтесь на BlackSea</a> і опублікуйте перший продукт сьогодні.</p>
  <hr />
  <p id="jvmi"><em>Стаття оновлена у квітні 2026 року.</em></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/-j4eLqUcDJj</guid><link>https://teletype.in/@lalimi/-j4eLqUcDJj?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/-j4eLqUcDJj?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Як заробляти онлайн в Україні у 2026: покроковий гайд для авторів</title><pubDate>Mon, 16 Mar 2026 12:55:35 GMT</pubDate><description><![CDATA[Ринок онлайн-заробітку в Україні більше не є нішевою темою. Щороку тисячі людей — дизайнери, коучи, педагоги, маркетологи, фотографи — шукають відповідь на одне просте запитання: як перетворити свої знання або навички на стабільний дохід в інтернеті? Ця стаття — не про швидке збагачення. Це про реальні моделі, які працюють саме в українських реаліях.]]></description><content:encoded><![CDATA[
  <p id="Z2tk">Ринок онлайн-заробітку в Україні більше не є нішевою темою. Щороку тисячі людей — дизайнери, коучи, педагоги, маркетологи, фотографи — шукають відповідь на одне просте запитання: як перетворити свої знання або навички на стабільний дохід в інтернеті? Ця стаття — не про швидке збагачення. Це про реальні моделі, які працюють саме в українських реаліях.</p>
  <h2 id="etjf"><strong>Що означає заробляти онлайн у 2026 році</strong></h2>
  <p id="uDfm">Онлайн-заробіток у 2026 — це не обов&#x27;язково фріланс і не обов&#x27;язково блогерство. Найстійкіша модель сьогодні — пасивний дохід через цифрові продукти: одного разу створюєш, продаєш нескінченно. Саме тому монетизація контенту та цифрові продукти стали головним трендом серед українських авторів.</p>
  <p id="qLKi">Є кілька базових моделей онлайн-заробітку, кожна з яких підходить різним людям залежно від досвіду та ресурсів.</p>
  <h3 id="EnCs"><strong>1. Онлайн-курси та навчальні матеріали</strong></h3>
  <p id="0okQ">Якщо ти знаєш щось краще за інших — це вже продукт. Онлайн-курс може бути записаним відео, серією PDF-уроків, або навіть текстовим гайдом. Головне — конкретний результат для покупця. Найчастіше питають: з чого почати? Відповідь: з однієї болючої проблеми твоєї аудиторії.</p>
  <h3 id="xTMn"><strong>2. Шаблони, пресети та цифрові файли</strong></h3>
  <p id="wogf">Canva-шаблони, Notion-бази, Lightroom-пресети, Figma-компоненти — все це продається. Причому без записів, без вебінарів і без великої аудиторії. Дизайнер один раз робить шаблон — і він може продаватись роками. Це і є пасивний дохід у найчистішому вигляді.</p>
  <h3 id="Fk7E"><strong>3. Консультації та менторство</strong></h3>
  <p id="ptV3">Якщо у тебе є практичний досвід — консультації є найшвидшим способом монетизувати його онлайн. Перші продажі зазвичай йдуть саме через цей формат, бо немає порогу входу: не потрібно нічого створювати наперед.</p>
  <h3 id="3q3c"><strong>4. Доступ до спільноти або закритого контенту</strong></h3>
  <p id="IaW8">Підписна модель набирає популярність і в Україні. Це може бути закритий Telegram-канал, курс з регулярним оновленням, або база знань для вузької ніші.</p>
  <h2 id="AnTT"><strong>Як створити онлайн продукт: 5 кроків для початківців</strong></h2>
  <p id="jxbP">Одна з найпоширеніших помилок — намагатись зробити ідеальний продукт перш ніж продати хоч один. Реальність: кращий перший продукт — це той, що ти вже можеш продати цього тижня.</p>
  <ul id="vune">
    <li id="KXMf">Визнач одну конкретну проблему своєї аудиторії — не тему, а проблему</li>
    <li id="G68h">Вибери найпростіший формат: PDF-гайд, шаблон або запис консультації</li>
    <li id="ALAP">Поставь ціну — навіть символічну. Безкоштовне не вчить продавати</li>
    <li id="2m7B">Вибери платформу для продажу з автодоставкою файлу покупцю</li>
    <li id="dERB">Отримай перший відгук і покращ продукт на основі реального досвіду</li>
  </ul>
  <h2 id="Dabw"><strong>Пасивний дохід в інтернеті: міф чи реальність для українців?</strong></h2>
  <p id="ZKlb">Слово «пасивний» часто вводить в оману. Пасивний дохід не означає «без роботи» — він означає «без постійної присутності». Ти вкладаєш зусилля один раз при створенні продукту, а потім система продажів працює без тебе.</p>
  <p id="iiY3">В Україні є кілька специфічних бар&#x27;єрів: складнощі з отриманням міжнародних платежів, питання ФОП-оподаткування, відсутність зручних локальних інструментів продажу. Саме тому для українських авторів критично важливо вибирати платформи, які вирішують ці проблеми нативно — з гривневими платежами, локальними платіжними системами та відповідністю українському законодавству.</p>
  <h2 id="KXMo"><strong>Монетизація контенту: коли аудиторія — не обов&#x27;язкова умова</strong></h2>
  <p id="acGw">Популярний міф: «спочатку набери 10 000 підписників, потім монетизуй». Практика показує інше. Автори з мікро-аудиторією у 500–1000 реальних читачів часто заробляють більше, ніж блогери з десятками тисяч підписників. Ключ — точне потрапляння продукту в потребу конкретної людини.</p>
  <p id="ec4m">Монетизація контенту в Україні у 2025 — це не реклама і не донати. Це цифрові продукти: курси, шаблони, гайди, консультації. І найзручніший спосіб їх продавати — через спеціалізований маркетплейс, де вже є інфраструктура для прийому платежів, доставки файлів та юридичного оформлення.</p>
  <p id="pioE"><a href="https://blacksea.click/" target="_blank">→ Почніть </a><strong>продавати свій перший цифровий продукт на BlackSea — безкоштовна реєстрація за 5 хвилин</strong></p>
  <p id="mBip"><strong>Висновок</strong></p>
  <p id="obSc">Заробляти онлайн в Україні реально — і у 2026 році для цього є більше інструментів, ніж будь-коли. Найстійкіша стратегія: одна аудиторія, одна проблема, один цифровий продукт, одна платформа для продажу. Починай з малого — і масштабуй те, що вже продається.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/dCHY23K2TsU</guid><link>https://teletype.in/@lalimi/dCHY23K2TsU?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/dCHY23K2TsU?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Как Kuse AI «взломал» Threads: разбор матричной стратегии и пошаговый план, как повторить это для своего стартапа</title><pubDate>Mon, 18 Aug 2025 06:25:32 GMT</pubDate><description><![CDATA[Представьте: прокручиваете ленту Threads и снова видите пост «10 мощных AI‑инструментов для работы». И снова среди ChatGPT, Canva и Notion — Kuse AI. День за днём, у разных авторов, на разных языках. Кажется, будто весь рынок уже решил: Kuse — must‑have.]]></description><content:encoded><![CDATA[
  <p id="ZSdD">За последние полгода в сети Threads регулярно появлялись посты, упоминающие сервис Kuse AI. Эти публикации часто создавались несколькими аккаунтами, и в них почти всегда присутствовала только Kuse. Чтобы понять, как такой “матричный” маркетинг помогает продвигать стартап, были проанализированы доступные посты (около 10 публикаций) из разных аккаунтов на Threads: @ai.tools.learn, @buildinhq, @ai.ezhacker, @ai.academy_, @kenchoi.kc и официального аккаунта @kusehq. С помощью браузерных инструментов была собрана информация о контенте, которая систематизирована ниже.</p>
  <p id="9tw6">Ссылки на источники указаны в тексте в виде цитат.</p>
  <figure id="nTP8" class="m_original">
    <img src="https://img2.teletype.in/files/50/c6/50c67dec-6e51-452f-a405-c8668bac8289.jpeg" width="528" />
  </figure>
  <p id="jMFn"></p>
  <h2 id="8ooY"></h2>
  <p id="yoS2"></p>
  <h2 id="8AX9">Почему один продукт упоминают все — и как это меняет сознание аудитории</h2>
  <p id="Auy6">Маркетинг в соцсетях — это игра повторений. Когда один и тот же бренд «случайно» встречается в каруселях полезных инструментов, кейсах и туториалах в разных лентах и от разных авторов, мозг делает вывод: «Если об этом говорят повсюду, значит, это стоит внимания».</p>
  <p id="cHCV">Kuse AI добился именно этого эффекта: узнаваемость через «естественную» многократность в независимых каналах. Не агрессивная реклама, а будто органическая рекомендация экспертов.</p>
  <p id="ak4y"></p>
  <p id="x8YE"></p>
  <p id="kB0J"></p>
  <h2 id="2">Боль аудитории: контент есть, но охваты и доверие не растут</h2>
  <ul id="kD99">
    <li id="c3ZR">Один бренд‑аккаунт тонет в шумах.</li>
    <li id="cCv8">Реклама дорожает, конверсия падает.</li>
    <li id="QxHz">«Пост от лица компании» воспринимается как продажа, а не как помощь.</li>
    <li id="TZTO">Алгоритм любит сохранения, репосты и дискуссии — но большинству постов нечем зацепить.</li>
  </ul>
  <p id="UU74">Вывод: нужно присутствовать шире и умнее, чем «одна страница — одна подача».</p>
  <p id="vpie"></p>
  <p id="HSKq"></p>
  <p id="3bVy"></p>
  <h2 id="3">Почему стандартные решения не срабатывают</h2>
  <ul id="siqs">
    <li id="ut1n">Закупить рекламу? Быстро проедает бюджет, выгорает аудитория, растёт цена клика.</li>
    <li id="TT70">Попросить блогеров? Одноразовые интеграции без системности не строят узнаваемость.</li>
    <li id="0x3F">Писать «полезный контент» на одном канале? Хорошо, но медленно и с ограниченным охватом.</li>
  </ul>
  <p id="pP9r">Нужен способ масштабировать органику без «пахоты в никуда» и без бесконечных согласований.</p>
  <p id="1SWH"></p>
  <p id="MnUm"></p>
  <p id="Njlw"></p>
  <h2 id="4">Инсайт: сеть профильных аккаунтов + три формата контента = эффект домино</h2>
  <p id="k8As">Из анализа постов на Threads за последние полгода (около 10 публикаций из разных аккаунтов, включая @ai.tools.learn, @buildinhq, @ai.ezhacker, @ai.academy_, @kenchoi.kc и @kusehq) заметны повторяющиеся паттерны:</p>
  <ul id="j1c9">
    <li id="fss4">Формат «топ‑N инструментов» с призывом «сохранить» — взрывает охваты: высокие save/share по умолчанию.</li>
    <li id="CZD4">Кейсы под конкретные боли сегментов (студенты, преподаватели): «5 минут — и готов DSE‑экзамен в PDF». Практичность → регистрация.</li>
    <li id="IszR">Официальный аккаунт докручивает позиционирование: «почему Kuse», визуальный интерфейс, контекст, мультимодальность.</li>
    <li id="3QJ7">Межъязыковая диверсификация: англ./китайский; одинаковая структура подачи — разная локализация.</li>
    <li id="iSvn">Взаимодействие между аккаунтами создаёт «эффект живого сообщества», а не рекламной сетки.</li>
  </ul>
  <p id="GXlt">Ключ: не один громкий канал, а согласованная сеть носителей смысла. Матричная подача — как музыкальная тема, которую подхватывают разные инструменты.</p>
  <figure id="1VG5" class="m_original">
    <img src="https://img1.teletype.in/files/4f/27/4f270329-36a3-46a9-94df-5de93bf91b2c.png" width="462" />
  </figure>
  <figure id="EGq3" class="m_column">
    <img src="https://img4.teletype.in/files/7b/ba/7bbac153-dc15-4469-8d3a-91219e09cdec.png" width="1028" />
  </figure>
  <figure id="fJLB" class="m_column">
    <img src="https://img2.teletype.in/files/54/ec/54ec0505-66e0-4406-bac2-fa50bc3de549.png" width="1024" />
  </figure>
  <figure id="mnuN" class="m_column">
    <img src="https://img1.teletype.in/files/82/c9/82c988d4-8086-4940-bdcf-8c8119a43230.png" width="1024" />
  </figure>
  <p id="Sp4K"></p>
  <p id="zxiI"></p>
  <h2 id="5">Конструктор матричной стратегии для вашего стартапа</h2>
  <p id="kuLM">Ниже — пошагово, что и как делать. Это работает и в Threads, и в Telegram, и в X/LinkedIn.</p>
  <h3 id="BbNw">Шаг 1. Разбейте аудиторию на кластеры</h3>
  <ul id="0rid">
    <li id="BfYX">Студенты и преподаватели</li>
    <li id="08tA">SMM/контент‑менеджеры</li>
    <li id="M8V3">Продакт‑менеджеры/операторы бизнес‑процессов</li>
    <li id="exI6">Фаундеры/малый бизнес</li>
  </ul>
  <p id="SQGS">Для каждого — свой язык, проблемы и формат доказательства.</p>
  <h3 id="euiY">Шаг 2. Заводим 3–6 профильных аккаунтов</h3>
  <ul id="xtTv">
    <li id="epLr">Тематические «дайджесты инструментов» (как бы независимые кураторы)</li>
    <li id="FT6M">Нишевые «практики» (кейсы «как решить X за 5 минут»)</li>
    <li id="MRtv">Официальный бренд‑аккаунт (стратегия, фичи, road‑map, milestone’ы)</li>
  </ul>
  <p id="OqXO">Важно: стиль дружелюбного эксперта, а не «рекламной листовки». Никакого спама.</p>
  <h3 id="Xtrk">Шаг 3. Три опорных формата контента</h3>
  <ol id="jYEF">
    <li id="YpsC">Топ‑N инструментов с призывом «Save/Share»</li>
    <li id="hdBH">Пошаговые туториалы/кейсы на боль сегмента</li>
    <li id="hOEq">Позиционирующие треды от бренда: «почему мы → чем отличаемся → где выигрываем»</li>
  </ol>
  <h3 id="cMg0">Шаг 4. Ритм и перекрёстная поддержка</h3>
  <ul id="QzWf">
    <li id="3Al8">3–4 поста в неделю по сети с разнотипными форматами.</li>
    <li id="m74P">Взаимные упоминания, ответы, продолжения («разбор вопросов из вирусного поста»).</li>
    <li id="0xWQ">Синхронизированные CTA (единая ссылка/линк‑хаб).</li>
  </ul>
  <h3 id="JV1X">Шаг 5. Измеряем только то, что влияет на рост</h3>
  <ul id="qt6u">
    <li id="uaKx">Save/Share Rate у «топов».</li>
    <li id="pMYr">CTR/Sign‑ups у кейсов.</li>
    <li id="BpWd">LTV/Retention по каналам (какие аккаунты приводят «долгоиграющих»).</li>
  </ul>
  <p id="B9Jh"></p>
  <p id="UQyY"></p>
  <p id="IYha"></p>
  <h2 id="6">Как производить такой контент быстро и качественно</h2>
  <p id="Tha7">Матричная стратегия упирается в один узкий горлышко: скорость и стабильность производства. Здесь вступают в игру AI‑инструменты, которые закрывают разные уровни контент‑воронки:</p>
  <ul id="XDhJ">
    <li id="8wJt">Идеи и структуру — генерируем промпт‑инжинирингом под GPT‑5.</li>
    <li id="XwiN">Визуал для каруселей и тредов — синтез по готовым стилям.</li>
    <li id="FvWL">Видео‑демо, кейсы и анонсы — скрипт → сцен‑план → рендер через видео‑AI.</li>
  </ul>
  <p id="m4iF">Ниже — готовая связка инструментов, которыми я лично пользуюсь и которые доступно собраны в продуктах «под ключ».</p>
  <p id="eeTr"></p>
  <p id="sa3D"></p>
  <p id="pT6v"></p>
  <h2 id="7">Инструменты, которые экономят месяцы и делают контент предсказуемо сильным</h2>
  <p id="C6H0">В этой системе каждый продукт — модуль большого «контент‑двигателя». Их можно использовать по отдельности, но максимальный эффект — в связке.</p>
  <ol id="dg0W">
    <li id="VomG">Гайд по промптам для GPT‑5: точные ответы без лишних итераций<br /> <a href="https://lalirun.gumroad.com/l/PromptOptimizationCookbook" target="_blank">https://lalirun.gumroad.com/l/PromptOptimizationCookbook</a></li>
  </ol>
  <p id="rjaY">Что решает:</p>
  <ul id="tgEK">
    <li id="firj">Боль «много воды и мало сути», «постоянно переспрашивать ИИ».</li>
    <li id="4wiD">Дает конструкцию промптов, которые возвращают сразу продаваемый текст: хук, доказательства, CTA, тред‑структуры.</li>
    <li id="QFYM">Специальные промпты для «топ‑N», «гайдов за 5 минут», «разборов кейсов», «ответов на возражения».</li>
  </ul>
  <p id="RuIG">Где применять:</p>
  <ul id="n76N">
    <li id="K3Mr">Threads‑треды, Telegram‑лента, email‑лиды, лендинги.</li>
    <li id="uO38">Подготовка сценариев для видео/вебинаров.</li>
  </ul>
  <ol id="cVio">
    <li id="sNMR">Veo 3: Visual Command Protocol (VCP) — революционная технология создания AI‑видео<br /><a href="https://lalirun.gumroad.com/l/CommandProtocol" target="_blank">https://lalirun.gumroad.com/l/CommandProtocol</a></li>
  </ol>
  <p id="tAQj">Что решает:</p>
  <ul id="CdFq">
    <li id="EEm6">Быстрое производство кинематографичных, «продающих» видео под треды/сторис/лендинги.</li>
    <li id="ELIX">Пошаговый протокол команд: от идеи → сториборда → сцен‑плана → камерных команд → финального рендера.</li>
    <li id="X6Wu">50+ шаблонов на любые ниши: демо‑продукта, кейс‑истории, туториалы, тизеры.</li>
  </ul>
  <p id="IZXh">Где применять:</p>
  <ul id="SjLp">
    <li id="wmjP">Видео‑кейсы «как за 5 минут решить задачу» (идеально для матричной стратегии).</li>
    <li id="dQbd">«Вирусные» анонсы функционала, сравнения «до/после», тизеры запуска.</li>
  </ul>
  <ol id="rJuU">
    <li id="RteP">AI‑Agent Guide — как построить собственных контент‑агентов<br /> <a href="https://lalirun.gumroad.com/l/ai-agent-guide" target="_blank">https://lalirun.gumroad.com/l/ai-agent-guide</a></li>
  </ol>
  <p id="vwAx">Что решает:</p>
  <ul id="ZEUj">
    <li id="lcn9">Автоматизацию: агент сам выбирает тему (по рандому или по слоту календаря), тянет актуальные факты, собирает структуру, подставляет UTM и публикует в нужные каналы.</li>
    <li id="Dn8b">Сценарии для разных форматов: «топ‑N», «гайд 5 шагов», «разбор боли», «кейсы с метриками».</li>
    <li id="9USK">Подключение к CMS/ноушен‑базе, контроль качества, шаблоны проверки «без воды».</li>
  </ul>
  <p id="RqSd">Где применять:</p>
  <ul id="g4rs">
    <li id="8zff">Масштабирование сети аккаунтов без роста штата.</li>
    <li id="Z0ut">Регулярный выпуск контента с единым тоном и CTA.</li>
  </ul>
  <p id="N5aN"></p>
  <p id="E4U9"></p>
  <p id="mYNB"></p>
  <h2 id="8------14">Практика: как это запустить за 14 дней</h2>
  <p id="x9Ti">Ниже — реальный план, которым можно руководствоваться.</p>
  <p id="dz3A">Дни 1–3</p>
  <ul id="RCn1">
    <li id="pFJU">Определяем 3–4 кластера аудитории и «болезненные задачи» (по 2 на кластер).</li>
    <li id="8kb1">Настраиваем шаблоны промптов из Prompt Optimization Cookbook под каждый кластер.</li>
    <li id="7rjx">Готовим 12–16 постов: 6 «топ‑N», 6 мини‑кейсов, 2 позиционирующих треда.</li>
  </ul>
  <p id="dbYS">Дни 4–6</p>
  <ul id="EiJd">
    <li id="mcvD">Поднимаем 3–5 профильных аккаунтов в Threads.</li>
    <li id="tS5b">Подключаем AI‑агентов: темы, источники, расписание, формат вывода.</li>
    <li id="w95g">Готовим визуальные карусели (единый стиль), делаем 2 коротких видео по VCP.</li>
  </ul>
  <p id="2rQm">Дни 7–10</p>
  <ul id="K5qC">
    <li id="l4bp">Публикуем по сетке:</li>
    <ul id="Rd7P">
      <li id="94CJ">Пн/Ср/Пт — «топ‑N» с CTA «Save/Share».</li>
      <li id="hjGr">Вт/Чт — туториалы/кейсы с CTA «Try now».</li>
      <li id="ZC4o">Сб — позиционирующий тред от бренда.</li>
    </ul>
    <li id="V6Nu">В тредах аккаунты «подхватывают» друг друга: ответы, дополнения, уточнения.</li>
    <li id="cpyz">Параллельно — Telegram‑репосты: «3 инсайта за неделю» с ссылками.</li>
  </ul>
  <p id="jSNp">Дни 11–14</p>
  <ul id="U6vF">
    <li id="uYE4">Аналитика: какие форматы дали больше Save/Share/CTR.</li>
    <li id="Z6Do">Ротация постов в новых языках (локализация).</li>
    <li id="Vf8l">Видео‑дубляж: тот же кейс — короткое видео по VCP с выдержкой 30–45 секунд.</li>
    <li id="yTct">Первый оффер: «только сегодня гайд/шаблоны со скидкой», «бонус — чек‑лист».</li>
  </ul>
  <p id="lr9o"></p>
  <p id="i241"></p>
  <p id="THyf"></p>
  <h2 id="9">Модель контента под матрицу: примеры, которые можно вставить в прод</h2>
  <p id="h5aI">Пример 1: Топ‑пост для сохранений<br /> Заголовок: 10 AI‑инструментов, которые реально экономят 10 часов в неделю<br /> Структура: 10 пунктов по 1–2 строки, призыв «Save и вернись к этому вечером».<br /> CTA: «Если нужны готовые промпты под эти инструменты — гайд по GPT‑5 в профиле».</p>
  <p id="zjBC">Пример 2: Туториал «5 минут — готово»<br /> «Как за 5 минут собрать экзамен по шаблону и выгрузить в PDF»<br /> Шаги: 1) приложить шаблон 2) команда X 3) проверка структуры 4) экспорт<br /> CTA: «Сохранить на потом» + ссылка «шаблон команд — в гайде VCP».</p>
  <p id="Jw66">Пример 3: Позиционирующий тред<br /> «Почему мы отказались от линейного чата и перешли к визуальному рабочему полю: меньше контекста — меньше шума — быстрее результат».<br /> CTA: «Полная методика и готовые сценарии — в AI‑Agent Guide».</p>
  <p id="q4X4"></p>
  <h2 id="10"></h2>
  <h2 id="k8vO">Риски и как их обойти</h2>
  <ul id="jk7p">
    <li id="s4dd">«Нас обвинят в сетке фейков». Выводите пользу на первое место: реальные кейсы, микро‑инструкции, ответы на вопросы.</li>
    <li id="zhNA">«Контент будет однообразным». Ротируйте форматы: списки, кейсы, сравнения, «миф/факт», short‑видео.</li>
    <li id="4cfd">«Сложно держать качество». Стандартизируйте промпты и чек‑листы проверки. Агентами — публикуйте черновики, финальный штрих — человеком.</li>
    <li id="5P9D">«Нет мощности для видео». VCP даёт сцен‑план и командный протокол — 80% времени экономится на пре‑продакшне.</li>
  </ul>
  <p id="aZfu"></p>
  <p id="oJGO"></p>
  <h2 id="11----3060">Что произойдёт через 30–60 дней при такой стратегии</h2>
  <ul id="yYAU">
    <li id="0gXs">Ваш бренд начинают «видеть везде» — и это ощущается органично.</li>
    <li id="xr9V">Сообщество отвечает не на один пост, а на тему — её продолжают другие аккаунты.</li>
    <li id="9k6s">Переходы на лендинг растут за счёт сохранений и рекомендаций.</li>
    <li id="Ebj2">Кейсы дают не лайки, а регистрацию/триал, потому что закрывают конкретную боль «здесь и сейчас».</li>
  </ul>
  <p id="rnjV"></p>
  <h2 id="12--cta"></h2>
  <h2 id="B0U2">Cоберите свою матрицу уже сегодня</h2>
  <p id="DIXl">Если хочется перестать «стрелять вслепую» и построить систему публикаций, которая работает на узнаваемость и продажи, начните с базового набора:</p>
  <p id="Tcc7">Products</p>
  <ol id="GhiN">
    <li id="bI8h">Гайд по промптам для GPT‑5: точные ответы без лишних итераций<br />  <a href="https://lalirun.gumroad.com/l/PromptOptimizationCookbook" target="_blank">https://lalirun.gumroad.com/l/PromptOptimizationCookbook</a></li>
    <li id="FpXi">Veo 3: Visual Command Protocol (VCP) — революционная технология создания AI‑видео<br />  <a href="https://lalirun.gumroad.com/l/CommandProtocol" target="_blank">https://lalirun.gumroad.com/l/CommandProtocol</a></li>
    <li id="QQgE">AI‑Agent Guide — как собрать и запустить контент‑агентов<br /> <a href="https://lalirun.gumroad.com/l/ai-agent-guide" target="_blank">https://lalirun.gumroad.com/l/ai-agent-guide</a></li>
  </ol>
  <p id="Etv6">Эта тройка закрывает весь цикл: текст → визуал/видео → автоматизация. Под ваш тон, ваши каналы и вашу цель.</p>
  <p id="hs48"></p>
  <p id="pO4P"></p>
  <h2 id="deJw">Финальная мысль</h2>
  <p id="diWq">Алгоритмы меняются. Привычки людей — тоже. Неподвижным остаётся одно: победа у тех, кто умеет приходить к аудитории с пользой — часто, разными голосами, в правильный момент.</p>
  <p id="ufvf">Kuse AI показал, что «матрица» побеждает «одиночный голос». Ваша задача — собрать свою и включить её на максимум.</p>
  <p id="0dKU">Готов обсудить вашу сферу и предложить первую матрицу контента под ключ. В каком сегменте вы работаете? Напишите в комментариях нишу и продукт — предложу 3 формата постов на ближайшую неделю.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/7lEaFsJiTTl</guid><link>https://teletype.in/@lalimi/7lEaFsJiTTl?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/7lEaFsJiTTl?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>CapCut слил — OpenCut спас. Как установить?</title><pubDate>Fri, 11 Jul 2025 14:34:36 GMT</pubDate><description><![CDATA[CapCut, конечно, красиво монтирует, но с 12 июня 2025 начал красиво и забирать себе права на твои видео, лицо и голос. Даже если это просто черновик. Даже если ты ничего не публиковал.]]></description><content:encoded><![CDATA[
  <hr />
  <p id="HAGJ">CapCut, конечно, красиво монтирует, но с 12 июня 2025 начал красиво и <strong>забирать себе права</strong> на твои видео, лицо и голос. Даже если это просто черновик. Даже если ты ничего не публиковал.</p>
  <p id="HPRk">Ну окей, подумали open-source ребята — и запилили <strong>OpenCut</strong>. Бесплатный аналог CapCut, где <strong>всё хранится только на твоём компе</strong>, без рекламы и “вечных лицензий”.</p>
  <p id="G2QG">🎬 Уже 8.4K звёзд на GitHub. Проект живёт, обновляется и всё больше становится похож на нормальную монтажку.</p>
  <hr />
  <p id="Xxxs"><strong>Хочешь попробовать? Вот как установить (максимально просто):</strong></p>
  <p id="eUb2">📍 <em>Для macOS (MacBook)</em></p>
  <ul id="ykxU">
    <li id="j1C0">Установи <strong>Git</strong>: </li>
  </ul>
  <p id="ePOP"><code>xcode-select --install </code></p>
  <ul id="qlsq">
    <li id="Mhc3">Скачай <strong>Docker Desktop</strong>:<br /> <a href="https://www.docker.com/products/docker-desktop" target="_blank">docker.com/products/docker-desktop</a></li>
    <li id="58RD">Установи bun:</li>
  </ul>
  <p id="t2mS"><code>curl -fsSL <a href="https://bun.sh/install" target="_blank">https://bun.sh/install</a> | bash</code></p>
  <p id="4tC5"><code>exec /bin/zsh</code><br /></p>
  <ul id="LxsB">
    <li id="sal1">Клонируй проект: </li>
  </ul>
  <p id="MIOX"><code>git clone https://github.com/OpenCut-app/OpenCut.git </code></p>
  <p id="eBBb"><code>cd OpenCut </code></p>
  <ul id="aEOV">
    <li id="ag61">Запусти сервисы: </li>
  </ul>
  <p id="NjNc"><code>docker-compose up -d </code></p>
  <ul id="kooq">
    <li id="EGdb">Подготовь настройки:</li>
  </ul>
  <p id="9HFP"> <code>cd apps/web </code></p>
  <p id="eF7s"><code>cp .env.example .env.local </code></p>
  <ul id="Quff">
    <li id="ceM3">Установи зависимости: </li>
  </ul>
  <p id="HqgY"><code>bun install </code></p>
  <ul id="A6DD">
    <li id="afX1">Подключи базу: </li>
  </ul>
  <p id="IETd"><code>bun run db:push:local </code></p>
  <ul id="u96o">
    <li id="zcIV">Запусти: </li>
  </ul>
  <p id="HC0K"><code>bun run dev </code></p>
  <p id="CJur">Открой в браузере: <strong><a href="http://localhost:3000/" target="_blank">http://localhost:3000</a></strong></p>
  <hr />
  <p id="yysI">👀 Если хочешь такую же инструкцию для <strong>Windows</strong> — пиши, скину отдельно.</p>
  <p id="AYcF">А ещё могу сделать PDF или короткое видео, если хочешь сохранить. Напомни, и соберу 💾</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/b3j7SW8WLt0</guid><link>https://teletype.in/@lalimi/b3j7SW8WLt0?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/b3j7SW8WLt0?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Вот инструкция для Windows — максимально просто и понятно 💻👇</title><pubDate>Fri, 11 Jul 2025 14:27:09 GMT</pubDate><description><![CDATA[Как установить OpenCut на Windows]]></description><content:encoded><![CDATA[
  <p id="o4d3"><strong>Как установить OpenCut на Windows</strong></p>
  <p id="tDUK">OpenCut — это open-source альтернатива CapCut, которая:<br /> ✅ не сохраняет твои видео в облако<br /> ✅ не присваивает права на твой контент<br /> ✅ работает локально — всё только у тебя на компе</p>
  <hr />
  <h3 id="yNTd">🔧 Что нужно заранее:</h3>
  <ol id="Kihu">
    <li id="oRoj"><strong>Git for Windows</strong><br /> ➤ Скачай: <a href="https://git-scm.com/downloads" target="_blank">https://git-scm.com/downloads</a><br /> Во время установки поставь галочку “Git Bash” — пригодится.</li>
    <li id="vPUh"><strong>Docker Desktop</strong><br /> ➤ Скачай: <a href="https://www.docker.com/products/docker-desktop" target="_blank">https://www.docker.com/products/docker-desktop</a><br /> Во время установки включи поддержку <strong>WSL2</strong> (если попросит) и <strong>запусти Docker</strong>.</li>
    <li id="16by"><strong>Bun (вместо npm)</strong><br /> ➤ Открой Git Bash (не PowerShell, не CMD) и вставь: </li>
  </ol>
  <p id="sqLc"><code>curl -fsSL https://bun.sh/install | bash exec bash </code></p>
  <hr />
  <h3 id="w8f3">📦 Установка OpenCut:</h3>
  <ul id="GgGw">
    <li id="eLwc">Клонируй проект: </li>
  </ul>
  <p id="c1fO"><code>git clone https://github.com/OpenCut-app/OpenCut.git </code></p>
  <p id="LHqW"><code>cd OpenCut </code></p>
  <ul id="4JHa">
    <li id="raQG">Запусти бэкенд: </li>
  </ul>
  <p id="f2aI"><code>docker-compose up -d </code></p>
  <ul id="pLkK">
    <li id="HBw7">Перейди в папку фронтенда: </li>
  </ul>
  <p id="li2M"><code>cd apps/web </code></p>
  <p id="4vlG"><code>cp .env.example .env.local </code></p>
  <ul id="lXrN">
    <li id="sa5J">Установи зависимости: </li>
  </ul>
  <p id="9eIz"><code>bun install </code></p>
  <ul id="4wVR">
    <li id="3SEY">Прокинь базу: </li>
  </ul>
  <p id="wfy3"><code>bun run db:push:local </code></p>
  <ul id="duuO">
    <li id="qcgy">Запусти фронтенд: </li>
  </ul>
  <p id="9Q5B"><code>bun run dev </code></p>
  <p id="72ih">Открывай: 👉 <a href="http://localhost:3000/" target="_blank">http://localhost:3000</a></p>
  <hr />

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/Sgixz_QtYlX</guid><link>https://teletype.in/@lalimi/Sgixz_QtYlX?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/Sgixz_QtYlX?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Модуль 11: DevSecOps — Безопасность в DevOps</title><pubDate>Wed, 11 Jun 2025 11:15:54 GMT</pubDate><description><![CDATA[Курс DevOps для новичков 2025]]></description><content:encoded><![CDATA[
  <p id="11-devsecops----devops"><em>Курс DevOps для новичков 2025</em></p>
  <hr />
  <h2 id="devsecops">🔒 Что такое DevSecOps?</h2>
  <p id="FOSs"><strong>DevSecOps</strong> — это эволюция DevOps, которая интегрирует <strong>безопасность на каждом этапе</strong> разработки и эксплуатации. Вместо того чтобы думать о безопасности в конце, DevSecOps делает её частью повседневного процесса разработки.</p>
  <p id="y5Et">По данным исследований 2025 года, более <strong>85% инцидентов безопасности</strong> связаны с неправильной конфигурацией компонентов и недостаточной защитой в CI/CD пайплайнах. DevSecOps решает эти проблемы через автоматизацию проверок безопасности.</p>
  <hr />
  <h2 id="shift-left-security">🎯 Принцип &quot;Shift Left Security&quot;</h2>
  <p id="lySw"><strong>Shift Left</strong> означает перенос проверок безопасности <strong>как можно раньше</strong> в цикле разработки:</p>
  <ul id="XMTU">
    <li id="MN4k">Анализ кода на этапе написания</li>
    <li id="krBP">Автоматические проверки при commit</li>
    <li id="snn9">Сканирование зависимостей при сборке</li>
    <li id="xqGl">Тестирование безопасности в CI/CD</li>
    <li id="hv1R">Мониторинг безопасности в продакшне</li>
  </ul>
  <h2 id="gY88">Преимущества Shift Left</h2>
  <p id="Xfh6"><strong>Снижение затрат</strong> — исправление уязвимостей на раннем этапе в 100 раз дешевле<br /> <strong>Быстрая обратная связь</strong> — разработчики получают уведомления о проблемах сразу<br /> <strong>Повышение качества</strong> — безопасность становится частью культуры разработки</p>
  <hr />
  <h2 id="llWQ">⚠️ Основные угрозы безопасности</h2>
  <h2 id="kgq6">OWASP Top 10 — критичные уязвимости 2025</h2>
  <ol id="OBVL">
    <li id="D1hn"><strong>Broken Access Control</strong> — нарушения контроля доступа</li>
    <li id="JsYf"><strong>Cryptographic Failures</strong> — криптографические сбои</li>
    <li id="joyE"><strong>Injection</strong> — SQL, NoSQL, OS injection атаки</li>
    <li id="ZpF2"><strong>Insecure Design</strong> — небезопасный дизайн приложения</li>
    <li id="Wj8Q"><strong>Security Misconfiguration</strong> — неправильная конфигурация</li>
    <li id="jvud"><strong>Vulnerable Components</strong> — уязвимые компоненты</li>
    <li id="uPyz"><strong>Authentication Failures</strong> — сбои аутентификации</li>
    <li id="Fbko"><strong>Software Integrity Failures</strong> — нарушения целостности ПО</li>
    <li id="1AAD"><strong>Logging Failures</strong> — недостаточное логирование</li>
    <li id="g0M4"><strong>Server-Side Request Forgery</strong> — SSRF атаки</li>
  </ol>
  <hr />
  <h2 id="cicd">🔐 Безопасность в CI/CD пайплайне</h2>
  <h2 id="i7IO">Многоуровневая защита пайплайна</h2>
  <pre id="H3p4"># .github/workflows/security.yml
name: Security Checks

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    # 1. Сканирование секретов в коде
    - name: Scan for secrets
      uses: trufflesecurity/trufflehog@main
      with:
        path: ./
        base: ${{ github.event.repository.default_branch }}
        head: HEAD

    # 2. Статический анализ кода (SAST)
    - name: Run CodeQL Analysis
      uses: github/codeql-action/init@v2
      with:
        languages: javascript, python

    - name: Autobuild
      uses: github/codeql-action/autobuild@v2

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2

    # 3. Сканирование зависимостей (SCA)
    - name: Run Snyk to check vulnerabilities
      uses: snyk/actions/node@master
      env:
        SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
      with:
        args: --severity-threshold=high

    # 4. Проверка Docker образа
    - name: Build Docker image
      run: docker build -t myapp:${{ github.sha }} .

    - name: Scan Docker image with Trivy
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: &#x27;myapp:${{ github.sha }}&#x27;
        format: &#x27;sarif&#x27;
        output: &#x27;trivy-results.sarif&#x27;

    - name: Upload Trivy results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: &#x27;trivy-results.sarif&#x27;

    # 5. Проверка Infrastructure as Code
    - name: Run Checkov on Terraform
      uses: bridgecrewio/checkov-action@master
      with:
        directory: terraform/
        framework: terraform
        output_format: sarif
        output_file_path: checkov-report.sarif

    - name: Upload Checkov results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: checkov-report.sarif
</pre>
  <hr />
  <h2 id="VzsT">🔑 Управление секретами</h2>
  <h2 id="sjFL">Никогда не храните секреты в коде!</h2>
  <p id="n16z"><strong>Проблемы хранения секретов в коде:</strong></p>
  <ul id="vEK1">
    <li id="lPLY">Утечка через публичные репозитории</li>
    <li id="e6gL">Доступ к секретам всех разработчиков</li>
    <li id="wbxh">Сложность ротации паролей</li>
    <li id="cmi2">Отсутствие аудита доступа</li>
  </ul>
  <h2 id="lLyE">HashiCorp Vault — централизованное управление</h2>
  <pre id="eK9v" data-lang="bash"># Установка Vault
wget https://releases.hashicorp.com/vault/1.15.0/vault_1.15.0_linux_amd64.zip
unzip vault_1.15.0_linux_amd64.zip
sudo mv vault /usr/local/bin/

# Запуск Vault в dev режиме (только для обучения!)
vault server -dev

# Сохранение секрета
vault kv put secret/myapp/db \
    password=&quot;super-secret-password&quot; \
    username=&quot;dbuser&quot;

# Получение секрета
vault kv get secret/myapp/db

# Получение только пароля
vault kv get -field=password secret/myapp/db
</pre>
  <h2 id="Rgfi">Интеграция Vault с приложениями</h2>
  <pre id="zJ2q" data-lang="python"># Python приложение с Vault
import hvac
import os

def get_db_credentials():
    # Подключение к Vault
    client = hvac.Client(
        url=os.getenv(&#x27;VAULT_URL&#x27;, &#x27;http://localhost:8200&#x27;),
        token=os.getenv(&#x27;VAULT_TOKEN&#x27;)
    )
    
    # Получение секретов
    response = client.secrets.kv.v2.read_secret_version(
        path=&#x27;myapp/db&#x27;
    )
    
    credentials = response[&#x27;data&#x27;][&#x27;data&#x27;]
    return credentials[&#x27;username&#x27;], credentials[&#x27;password&#x27;]

# Использование в приложении
username, password = get_db_credentials()
db_url = f&quot;postgresql://{username}:{password}@localhost:5432/myapp&quot;
</pre>
  <h2 id="wIMW">Kubernetes Secrets</h2>
  <pre id="wGQs"># k8s-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  database-password: &lt;base64-encoded-password&gt;
  api-key: &lt;base64-encoded-api-key&gt;
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:latest
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database-password
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: api-key
</pre>
  <hr />
  <h2 id="OcoA">🛡️ Сканирование контейнеров</h2>
  <h2 id="9eWv">Безопасный Dockerfile</h2>
  <pre id="fHQo"># Используем минимальный базовый образ
FROM node:18-alpine

# Обновляем пакеты для устранения уязвимостей
RUN apk update &amp;&amp; apk upgrade &amp;&amp; \
    apk add --no-cache dumb-init &amp;&amp; \
    rm -rf /var/cache/apk/*

# Создаем непривилегированного пользователя
RUN addgroup -g 1001 -S nodejs &amp;&amp; \
    adduser -S nextjs -u 1001

# Устанавливаем рабочую директорию
WORKDIR /app

# Копируем только файлы зависимостей для кэширования
COPY package*.json ./

# Устанавливаем только production зависимости
RUN npm ci --only=production &amp;&amp; \
    npm cache clean --force &amp;&amp; \
    npm audit fix

# Копируем исходный код
COPY --chown=nextjs:nodejs . .

# Удаляем ненужные файлы
RUN rm -rf .git .gitignore README.md

# Переключаемся на непривилегированного пользователя
USER nextjs

# Используем init процесс для правильной обработки сигналов
ENTRYPOINT [&quot;dumb-init&quot;, &quot;--&quot;]

# Указываем команду запуска
CMD [&quot;npm&quot;, &quot;start&quot;]

# Открываем только необходимый порт
EXPOSE 3000

# Добавляем health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# Добавляем метки для трекинга
LABEL org.opencontainers.image.source=&quot;https://github.com/user/repo&quot;
LABEL org.opencontainers.image.version=&quot;1.0.0&quot;
LABEL org.opencontainers.image.created=&quot;2025-06-11&quot;
</pre>
  <h2 id="a0Yt">Автоматическое сканирование с Trivy</h2>
  <pre id="JaVo" data-lang="bash"># Сканирование локального образа
trivy image myapp:latest

# Сканирование с выводом только критических уязвимостей
trivy image --severity HIGH,CRITICAL myapp:latest

# Сканирование с сохранением в файл
trivy image --format json --output results.json myapp:latest

# Сканирование файловой системы
trivy fs .

# Сканирование Kubernetes манифестов
trivy config k8s/

# Сканирование Terraform файлов
trivy config terraform/
</pre>
  <hr />
  <h2 id="PmVn">🛠️ Практические задания</h2>
  <h2 id="7UnE">Задание 11.1: Настройка security scanning в CI/CD</h2>
  <pre id="c8i0" data-lang="bash"># 1. Создание проекта с уязвимостями для демонстрации
mkdir devsecops-demo
cd devsecops-demo

# 2. Создание package.json с уязвимыми зависимостями
cat &gt; package.json &lt;&lt; &#x27;EOF&#x27;
{
  &quot;name&quot;: &quot;devsecops-demo&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;express&quot;: &quot;4.16.0&quot;,
    &quot;lodash&quot;: &quot;4.17.4&quot;,
    &quot;moment&quot;: &quot;2.19.0&quot;
  }
}
EOF

# 3. Создание простого приложения
cat &gt; app.js &lt;&lt; &#x27;EOF&#x27;
const express = require(&#x27;express&#x27;);
const _ = require(&#x27;lodash&#x27;);
const moment = require(&#x27;moment&#x27;);

const app = express();
app.use(express.json());

// Уязвимый endpoint (SQL injection)
app.get(&#x27;/user/:id&#x27;, (req, res) =&gt; {
  const query = &#x60;SELECT * FROM users WHERE id = ${req.params.id}&#x60;;
  // Это уязвимо к SQL injection!
  res.json({ query, message: &#x27;This is vulnerable!&#x27; });
});

// Секрет в коде (плохая практика)
const API_KEY = &quot;sk-1234567890abcdef&quot;;
const DB_PASSWORD = &quot;password123&quot;;

app.listen(3000, () =&gt; {
  console.log(&#x27;Server running on port 3000&#x27;);
});
EOF

# 4. Создание GitHub Actions workflow (используйте пример выше)
mkdir -p .github/workflows
# Скопируйте security.yml из примера выше
</pre>
  <h2 id="Gq2t">Задание 11.2: Установка и настройка Vault</h2>
  <pre id="rBve" data-lang="bash"># 1. Установка Vault
wget https://releases.hashicorp.com/vault/1.15.0/vault_1.15.0_linux_amd64.zip
unzip vault_1.15.0_linux_amd64.zip
sudo mv vault /usr/local/bin/

# 2. Создание конфигурации Vault
cat &gt; vault-config.hcl &lt;&lt; &#x27;EOF&#x27;
storage &quot;file&quot; {
  path = &quot;./vault-data&quot;
}

listener &quot;tcp&quot; {
  address = &quot;127.0.0.1:8200&quot;
  tls_disable = true
}

ui = true
EOF

# 3. Инициализация Vault
vault server -config=vault-config.hcl &amp;
export VAULT_ADDR=&#x27;http://127.0.0.1:8200&#x27;

# Инициализация (сохраните ключи!)
vault operator init

# Разблокировка Vault (используйте 3 ключа из вывода init)
vault operator unseal &lt;key1&gt;
vault operator unseal &lt;key2&gt;
vault operator unseal &lt;key3&gt;

# 4. Настройка секретов приложения
vault auth &lt;root-token&gt;

# Включение KV секретов
vault secrets enable -path=secret kv-v2

# Сохранение секретов
vault kv put secret/myapp/database \
    username=&quot;dbuser&quot; \
    password=&quot;secure-password-123&quot;

vault kv put secret/myapp/api \
    key=&quot;sk-secure-api-key-456&quot; \
    url=&quot;https://api.example.com&quot;
</pre>
  <h2 id="SCuz">Задание 11.3: Сканирование безопасности</h2>
  <pre id="hlw7" data-lang="bash"># 1. Установка инструментов сканирования
# Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# TruffleHog для поиска секретов
pip install truffleHog

# 2. Сканирование проекта
# Поиск секретов в Git истории
truffleHog --regex --entropy=False .

# Сканирование зависимостей
npm audit

# Сканирование Docker образа
docker build -t vulnerable-app .
trivy image vulnerable-app

# 3. Создание отчета безопасности
cat &gt; security-scan.sh &lt;&lt; &#x27;EOF&#x27;
#!/bin/bash

echo &quot;=== Security Scan Report ===&quot;
echo &quot;Date: $(date)&quot;
echo

echo &quot;=== Dependency Vulnerabilities ===&quot;
npm audit --json &gt; npm-audit.json
cat npm-audit.json | jq &#x27;.vulnerabilities | length&#x27;

echo &quot;=== Secret Scanning ===&quot;
truffleHog --regex --entropy=False . &gt; secrets-report.txt
cat secrets-report.txt

echo &quot;=== Container Scanning ===&quot;
trivy image --format json vulnerable-app &gt; container-scan.json
cat container-scan.json | jq &#x27;.Results[].Vulnerabilities | length&#x27;
EOF

chmod +x security-scan.sh
./security-scan.sh
</pre>
  <h2 id="UFBv">Задание 11.4: Network Security в Kubernetes</h2>
  <pre id="Z2B8"># network-policies.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 80
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 8080
  - to: []  # DNS разрешение
    ports:
    - protocol: UDP
      port: 53
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
</pre>
  <hr />
  <h2 id="8R54">🔍 Мониторинг безопасности</h2>
  <h2 id="C55e">Falco для обнаружения аномалий</h2>
  <pre id="b4OU"># falco-deployment.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: falco
  namespace: falco-system
spec:
  selector:
    matchLabels:
      app: falco
  template:
    metadata:
      labels:
        app: falco
    spec:
      serviceAccount: falco
      hostNetwork: true
      hostPID: true
      containers:
      - name: falco
        image: falcosecurity/falco:latest
        securityContext:
          privileged: true
        args:
          - /usr/bin/falco
          - --cri=/run/containerd/containerd.sock
          - --k8s-api=https://kubernetes.default:443
        volumeMounts:
        - name: dev
          mountPath: /host/dev
        - name: proc
          mountPath: /host/proc
        - name: boot
          mountPath: /host/boot
        - name: lib-modules
          mountPath: /host/lib/modules
        - name: usr
          mountPath: /host/usr
        - name: etc
          mountPath: /host/etc
      volumes:
      - name: dev
        hostPath:
          path: /dev
      - name: proc
        hostPath:
          path: /proc
      - name: boot
        hostPath:
          path: /boot
      - name: lib-modules
        hostPath:
          path: /lib/modules
      - name: usr
        hostPath:
          path: /usr
      - name: etc
        hostPath:
          path: /etc
</pre>
  <h2 id="2EYv">Пользовательские правила Falco</h2>
  <pre id="Unnr"># custom-rules.yaml
- rule: Suspicious Network Activity
  desc: Detect suspicious network connections
  condition: &gt;
    spawned_process and
    proc.name in (nc, ncat, netcat) and
    not proc.args contains &quot;-l&quot;
  output: &gt;
    Suspicious network activity detected
    (user=%user.name command=%proc.cmdline container=%container.name)
  priority: WARNING

- rule: Unauthorized File Access
  desc: Detect access to sensitive files
  condition: &gt;
    open_read and
    fd.name in (/etc/passwd, /etc/shadow, /etc/hosts) and
    not proc.name in (systemd, sshd)
  output: &gt;
    Unauthorized access to sensitive file
    (user=%user.name file=%fd.name proc=%proc.name container=%container.name)
  priority: CRITICAL
</pre>
  <hr />
  <h2 id="compliance">📊 Compliance и аудит</h2>
  <h2 id="790t">CIS Kubernetes Benchmark</h2>
  <pre id="xGND" data-lang="bash"># Установка kube-bench
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml

# Просмотр результатов
kubectl logs job.batch/kube-bench

# Сохранение отчета
kubectl logs job.batch/kube-bench &gt; cis-benchmark-report.txt

# Проверка соответствия с kube-score
kubectl score deployment.yaml
</pre>
  <h2 id="l7tP">Policy as Code с Open Policy Agent</h2>
  <pre id="JYEV"># security-policies.rego
package kubernetes.security

# Запрет запуска контейнеров от root
deny[msg] {
    input.kind == &quot;Pod&quot;
    input.spec.securityContext.runAsUser == 0
    msg := &quot;Containers must not run as root user&quot;
}

# Обязательное использование read-only root filesystem
deny[msg] {
    input.kind == &quot;Pod&quot;
    container := input.spec.containers[_]
    not container.securityContext.readOnlyRootFilesystem
    msg := sprintf(&quot;Container %s must have read-only root filesystem&quot;, [container.name])
}

# Запрет privileged контейнеров
deny[msg] {
    input.kind == &quot;Pod&quot;
    container := input.spec.containers[_]
    container.securityContext.privileged == true
    msg := sprintf(&quot;Container %s cannot run in privileged mode&quot;, [container.name])
}

# Обязательные resource limits
deny[msg] {
    input.kind == &quot;Pod&quot;
    container := input.spec.containers[_]
    not container.resources.limits.memory
    msg := sprintf(&quot;Container %s must have memory limits&quot;, [container.name])
}

# Запрет использования latest тегов
deny[msg] {
    input.kind == &quot;Pod&quot;
    container := input.spec.containers[_]
    endswith(container.image, &quot;:latest&quot;)
    msg := sprintf(&quot;Container %s must not use &#x27;latest&#x27; tag&quot;, [container.name])
}
</pre>
  <hr />
  <h2 id="Bzb7">📖 Полезные ресурсы</h2>
  <ul id="8d0B">
    <li id="xxOD"><strong><a href="https://owasp.org/www-project-devsecops-guideline/" target="_blank">OWASP DevSecOps Guideline</a></strong> — практики безопасности в DevOps</li>
    <li id="hkrC"><strong><a href="https://www.nist.gov/cyberframework" target="_blank">NIST Cybersecurity Framework</a></strong> — стандарты кибербезопасности</li>
    <li id="bFSz"><strong><a href="https://www.cisecurity.org/controls/" target="_blank">CIS Controls</a></strong> — критичные контроли безопасности</li>
    <li id="2A9J"><strong>YouTube:</strong> &quot;DevSecOps: безопасность в CI/CD&quot; — практические видеоуроки</li>
    <li id="k2er"><strong><a href="https://github.com/TaptuIT/awesome-devsecops" target="_blank">Awesome DevSecOps</a></strong> — коллекция инструментов и ресурсов</li>
  </ul>
  <hr />
  <h2 id="S4wc">✅ Чек-лист модуля</h2>
  <ul id="Qz4p">
    <li id="ycMp">Понимаю принципы DevSecOps и Shift Left Security</li>
    <li id="7Y7c">Настроил автоматическое сканирование безопасности в CI/CD</li>
    <li id="pkDZ">Интегрировал проверку секретов с TruffleHog</li>
    <li id="uI4d">Добавил сканирование зависимостей с Snyk</li>
    <li id="ucZr">Настроил сканирование Docker образов с Trivy</li>
    <li id="rked">Установил и настроил HashiCorp Vault</li>
    <li id="q7pv">Создал безопасные Dockerfile согласно best practices</li>
    <li id="8Odv">Настроил Network Policies в Kubernetes</li>
    <li id="yMpX">Изучил мониторинг безопасности с Falco</li>
    <li id="pyQc">Понимаю основы compliance и Policy as Code</li>
  </ul>
  <hr />
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="IzTn">🚀 Что дальше?</h2>
    <p id="4TUQ">После освоения DevSecOps переходите к <strong>Финальному проекту</strong>, где объедините все изученные навыки для создания полноценной DevOps инфраструктуры с интегрированной безопасностью.</p>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/dXRooxklNcr</guid><link>https://teletype.in/@lalimi/dXRooxklNcr?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/dXRooxklNcr?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Модуль 10: Облачные платформы для DevOps</title><pubDate>Wed, 11 Jun 2025 11:12:24 GMT</pubDate><description><![CDATA[Курс DevOps для новичков 2025]]></description><content:encoded><![CDATA[
  <p id="10----devops"><em>Курс DevOps для новичков 2025</em></p>
  <hr />
  <h2 id="twL4">☁️ Зачем изучать облачные платформы?</h2>
  <p id="w0LS"><strong>Облако</strong> — это не просто чужие компьютеры. Это глобальная инфраструктура с автоматическим масштабированием, managed сервисами и pay-as-you-use моделью. Современные компании мигрируют в облако для снижения затрат, повышения гибкости и ускорения разработки.</p>
  <p id="yKrS">По данным исследований 2025 года, более <strong>94% предприятий</strong> используют облачные сервисы, а <strong>83% рабочих нагрузок</strong> работают в облаке. Знание облачных платформ стало обязательным навыком для DevOps-инженеров.</p>
  <hr />
  <h2 id="UFHk">🏆 Большая тройка облачных провайдеров</h2>
  <h2 id="9cN9">Amazon Web Services (AWS)</h2>
  <p id="yYpD"><strong>Лидер рынка</strong> с самой широкой экосистемой сервисов (более 200 сервисов). Первопроходец в области облачных вычислений.</p>
  <h2 id="4Ube">Microsoft Azure</h2>
  <p id="Xaxe">Отличная <strong>интеграция с корпоративными продуктами</strong> Microsoft. Сильные позиции в hybrid cloud решениях.</p>
  <h2 id="vakG">Google Cloud Platform (GCP)</h2>
  <p id="IDOo">Сильные позиции в <strong>машинном обучении и анализе данных</strong>. Инновационные решения для контейнеров и Kubernetes.</p>
  <hr />
  <h2 id="ZlaH">🔧 Основные сервисы облачных платформ</h2>
  <h2 id="aTHJ">Вычислительные ресурсы</h2>
  <p id="YJyt"><strong>Виртуальные машины:</strong></p>
  <ul id="Pikh">
    <li id="R4lr"><strong>AWS EC2</strong> — Elastic Compute Cloud</li>
    <li id="jPZg"><strong>Azure Virtual Machines</strong> — виртуальные машины Azure</li>
    <li id="wEla"><strong>GCP Compute Engine</strong> — вычислительные экземпляры</li>
  </ul>
  <p id="KdZL"><strong>Serverless вычисления:</strong></p>
  <ul id="KBjz">
    <li id="Ag8x"><strong>AWS Lambda</strong> — выполнение кода без серверов</li>
    <li id="cBEd"><strong>Azure Functions</strong> — событийно-ориентированные функции</li>
    <li id="1JA0"><strong>Google Cloud Functions</strong> — легковесные функции</li>
  </ul>
  <p id="h4IC"><strong>Контейнерные сервисы:</strong></p>
  <ul id="u4JC">
    <li id="EgO0"><strong>AWS ECS/EKS</strong> — управляемые контейнеры и Kubernetes</li>
    <li id="YkAQ"><strong>Azure AKS</strong> — Azure Kubernetes Service</li>
    <li id="rSAM"><strong>GCP GKE</strong> — Google Kubernetes Engine</li>
  </ul>
  <h2 id="Jbsg">Хранение данных</h2>
  <p id="Gt8i"><strong>Объектное хранилище:</strong></p>
  <ul id="B1u6">
    <li id="47ng"><strong>AWS S3</strong> — Simple Storage Service</li>
    <li id="ThFQ"><strong>Azure Blob Storage</strong> — хранилище больших объектов</li>
    <li id="2YYa"><strong>GCP Cloud Storage</strong> — объектное хранилище Google</li>
  </ul>
  <p id="oNee"><strong>Базы данных:</strong></p>
  <ul id="kd3W">
    <li id="u28z"><strong>AWS RDS</strong> — реляционные базы данных</li>
    <li id="BasP"><strong>Azure SQL Database</strong> — управляемый SQL Server</li>
    <li id="pFG0"><strong>GCP Cloud SQL</strong> — управляемые БД</li>
  </ul>
  <h2 id="XX2X">Сетевые сервисы</h2>
  <p id="s1Eq"><strong>Виртуальные сети:</strong></p>
  <ul id="rQnc">
    <li id="GcuK"><strong>AWS VPC</strong> — Virtual Private Cloud</li>
    <li id="3JRv"><strong>Azure Virtual Network</strong> — виртуальные сети Azure</li>
    <li id="aaU7"><strong>GCP VPC</strong> — Virtual Private Cloud Google</li>
  </ul>
  <p id="agHp"><strong>CDN и доставка контента:</strong></p>
  <ul id="RsIB">
    <li id="RvV9"><strong>AWS CloudFront</strong> — глобальная сеть доставки контента</li>
    <li id="QjHJ"><strong>Azure CDN</strong> — сеть доставки контента Microsoft</li>
    <li id="h9sD"><strong>GCP Cloud CDN</strong> — CDN от Google</li>
  </ul>
  <hr />
  <h2 id="aws">🛠️ Практика с AWS</h2>
  <h2 id="6pq0">Настройка AWS CLI</h2>
  <pre id="TLe5" data-lang="bash"># Установка AWS CLI v2
curl &quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot; -o &quot;awscliv2.zip&quot;
unzip awscliv2.zip
sudo ./aws/install

# Проверка установки
aws --version

# Настройка credentials (нужен AWS аккаунт)
aws configure
# AWS Access Key ID: [ваш ключ]
# AWS Secret Access Key: [ваш секретный ключ]
# Default region name: us-west-2
# Default output format: json
</pre>
  <h2 id="8LhB">Создание EC2 инстанса</h2>
  <pre id="VSzK" data-lang="bash"># Создание ключевой пары
aws ec2 create-key-pair \
    --key-name devops-course-key \
    --query &#x27;KeyMaterial&#x27; \
    --output text &gt; devops-course-key.pem

chmod 400 devops-course-key.pem

# Создание security group
aws ec2 create-security-group \
    --group-name devops-sg \
    --description &quot;Security group for DevOps course&quot;

# Получение ID security group
SG_ID=$(aws ec2 describe-security-groups \
    --group-names devops-sg \
    --query &#x27;SecurityGroups[0].GroupId&#x27; \
    --output text)

# Разрешение SSH доступа
aws ec2 authorize-security-group-ingress \
    --group-id $SG_ID \
    --protocol tcp \
    --port 22 \
    --cidr 0.0.0.0/0

# Разрешение HTTP доступа
aws ec2 authorize-security-group-ingress \
    --group-id $SG_ID \
    --protocol tcp \
    --port 80 \
    --cidr 0.0.0.0/0

# Запуск EC2 инстанса
aws ec2 run-instances \
    --image-id ami-0c02fb55956c7d316 \
    --count 1 \
    --instance-type t2.micro \
    --key-name devops-course-key \
    --security-group-ids $SG_ID \
    --tag-specifications &#x27;ResourceType=instance,Tags=[{Key=Name,Value=DevOps-Course-Server}]&#x27;
</pre>
  <hr />
  <h2 id="h1Y0">🛠️ Практические задания</h2>
  <h2 id="mVMw">Задание 10.1: Развертывание веб-приложения в AWS</h2>
  <pre id="NwxO" data-lang="bash"># 1. Получение публичного IP созданного инстанса
INSTANCE_ID=$(aws ec2 describe-instances \
    --filters &quot;Name=tag:Name,Values=DevOps-Course-Server&quot; \
    --query &#x27;Reservations[0].Instances[0].InstanceId&#x27; \
    --output text)

PUBLIC_IP=$(aws ec2 describe-instances \
    --instance-ids $INSTANCE_ID \
    --query &#x27;Reservations[0].Instances[0].PublicIpAddress&#x27; \
    --output text)

echo &quot;Instance IP: $PUBLIC_IP&quot;

# 2. Подключение к серверу
ssh -i devops-course-key.pem ubuntu@$PUBLIC_IP

# 3. Установка Docker на сервере
sudo apt update
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ubuntu

# 4. Запуск простого веб-приложения
docker run -d --name web-app -p 80:80 nginx:alpine

# 5. Проверка работы
curl http://$PUBLIC_IP
</pre>
  <h2 id="0s33">Задание 10.2: Создание S3 bucket и загрузка файлов</h2>
  <pre id="grXm" data-lang="bash"># Создание уникального имени bucket
BUCKET_NAME=&quot;devops-course-$(date +%s)&quot;

# Создание S3 bucket
aws s3 mb s3://$BUCKET_NAME

# Создание тестового файла
echo &quot;Hello from DevOps course!&quot; &gt; test-file.txt

# Загрузка файла в S3
aws s3 cp test-file.txt s3://$BUCKET_NAME/

# Просмотр содержимого bucket
aws s3 ls s3://$BUCKET_NAME/

# Создание статического веб-сайта
cat &gt; index.html &lt;&lt; &#x27;EOF&#x27;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;DevOps Course - S3 Static Website&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome to DevOps Course!&lt;/h1&gt;
    &lt;p&gt;This website is hosted on AWS S3&lt;/p&gt;
    &lt;p&gt;Deployed at: &lt;span id=&quot;timestamp&quot;&gt;&lt;/span&gt;&lt;/p&gt;
    &lt;script&gt;
        document.getElementById(&#x27;timestamp&#x27;).textContent = new Date().toLocaleString();
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
EOF

# Загрузка веб-сайта
aws s3 cp index.html s3://$BUCKET_NAME/ --acl public-read

# Настройка статического хостинга
aws s3 website s3://$BUCKET_NAME/ \
    --index-document index.html \
    --error-document error.html

echo &quot;Website URL: http://$BUCKET_NAME.s3-website-us-west-2.amazonaws.com&quot;
</pre>
  <h2 id="5c6w">Задание 10.3: Serverless функция с AWS Lambda</h2>
  <pre id="8qdJ" data-lang="python"># lambda_function.py
import json
import boto3
from datetime import datetime

def lambda_handler(event, context):
    &quot;&quot;&quot;
    AWS Lambda функция для обработки HTTP запросов
    &quot;&quot;&quot;
    
    # Логирование входящего события
    print(f&quot;Received event: {json.dumps(event)}&quot;)
    
    # Получение параметров из запроса
    http_method = event.get(&#x27;httpMethod&#x27;, &#x27;GET&#x27;)
    path = event.get(&#x27;path&#x27;, &#x27;/&#x27;)
    query_params = event.get(&#x27;queryStringParameters&#x27;) or {}
    
    # Обработка различных путей
    if path == &#x27;/health&#x27;:
        return {
            &#x27;statusCode&#x27;: 200,
            &#x27;headers&#x27;: {
                &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,
                &#x27;Access-Control-Allow-Origin&#x27;: &#x27;*&#x27;
            },
            &#x27;body&#x27;: json.dumps({
                &#x27;status&#x27;: &#x27;healthy&#x27;,
                &#x27;timestamp&#x27;: datetime.utcnow().isoformat(),
                &#x27;version&#x27;: &#x27;1.0.0&#x27;
            })
        }
    
    elif path == &#x27;/info&#x27; and http_method == &#x27;GET&#x27;:
        return {
            &#x27;statusCode&#x27;: 200,
            &#x27;headers&#x27;: {
                &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,
                &#x27;Access-Control-Allow-Origin&#x27;: &#x27;*&#x27;
            },
            &#x27;body&#x27;: json.dumps({
                &#x27;message&#x27;: &#x27;DevOps Course Lambda Function&#x27;,
                &#x27;timestamp&#x27;: datetime.utcnow().isoformat(),
                &#x27;method&#x27;: http_method,
                &#x27;query_params&#x27;: query_params
            })
        }
    
    else:
        return {
            &#x27;statusCode&#x27;: 404,
            &#x27;headers&#x27;: {
                &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,
                &#x27;Access-Control-Allow-Origin&#x27;: &#x27;*&#x27;
            },
            &#x27;body&#x27;: json.dumps({
                &#x27;error&#x27;: &#x27;Not Found&#x27;,
                &#x27;path&#x27;: path,
                &#x27;method&#x27;: http_method
            })
        }
</pre>
  <pre id="Hkif" data-lang="bash"># Создание Lambda функции
zip lambda_function.zip lambda_function.py

# Создание роли для Lambda
aws iam create-role \
    --role-name lambda-execution-role \
    --assume-role-policy-document &#x27;{
        &quot;Version&quot;: &quot;2012-10-17&quot;,
        &quot;Statement&quot;: [
            {
                &quot;Effect&quot;: &quot;Allow&quot;,
                &quot;Principal&quot;: {
                    &quot;Service&quot;: &quot;lambda.amazonaws.com&quot;
                },
                &quot;Action&quot;: &quot;sts:AssumeRole&quot;
            }
        ]
    }&#x27;

# Присоединение политики
aws iam attach-role-policy \
    --role-name lambda-execution-role \
    --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# Создание Lambda функции
aws lambda create-function \
    --function-name devops-course-function \
    --runtime python3.9 \
    --role arn:aws:iam::ACCOUNT-ID:role/lambda-execution-role \
    --handler lambda_function.lambda_handler \
    --zip-file fileb://lambda_function.zip

# Тестирование функции
aws lambda invoke \
    --function-name devops-course-function \
    --payload &#x27;{&quot;httpMethod&quot;: &quot;GET&quot;, &quot;path&quot;: &quot;/health&quot;}&#x27; \
    response.json

cat response.json
</pre>
  <hr />
  <h2 id="terraform">🔧 Terraform для облачной инфраструктуры</h2>
  <h2 id="S7nS">AWS инфраструктура с Terraform</h2>
  <pre id="92pM"># main.tf
terraform {
  required_providers {
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;~&gt; 5.0&quot;
    }
  }
}

provider &quot;aws&quot; {
  region = var.aws_region
}

# VPC
resource &quot;aws_vpc&quot; &quot;main&quot; {
  cidr_block           = &quot;10.0.0.0/16&quot;
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = &quot;devops-course-vpc&quot;
  }
}

# Internet Gateway
resource &quot;aws_internet_gateway&quot; &quot;main&quot; {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = &quot;devops-course-igw&quot;
  }
}

# Public Subnet
resource &quot;aws_subnet&quot; &quot;public&quot; {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = &quot;10.0.1.0/24&quot;
  availability_zone       = &quot;${var.aws_region}a&quot;
  map_public_ip_on_launch = true

  tags = {
    Name = &quot;devops-course-public-subnet&quot;
  }
}

# Route Table
resource &quot;aws_route_table&quot; &quot;public&quot; {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = &quot;0.0.0.0/0&quot;
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = &quot;devops-course-public-rt&quot;
  }
}

resource &quot;aws_route_table_association&quot; &quot;public&quot; {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

# Security Group
resource &quot;aws_security_group&quot; &quot;web&quot; {
  name_prefix = &quot;devops-course-web-&quot;
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = &quot;-1&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  tags = {
    Name = &quot;devops-course-web-sg&quot;
  }
}

# Application Load Balancer
resource &quot;aws_lb&quot; &quot;main&quot; {
  name               = &quot;devops-course-alb&quot;
  internal           = false
  load_balancer_type = &quot;application&quot;
  security_groups    = [aws_security_group.web.id]
  subnets            = [aws_subnet.public.id, aws_subnet.public2.id]

  enable_deletion_protection = false

  tags = {
    Name = &quot;devops-course-alb&quot;
  }
}

# Auto Scaling Group
resource &quot;aws_launch_template&quot; &quot;web&quot; {
  name_prefix   = &quot;devops-course-&quot;
  image_id      = data.aws_ami.ubuntu.id
  instance_type = var.instance_type

  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = base64encode(templatefile(&quot;${path.module}/user_data.sh&quot;, {
    app_name = &quot;devops-course&quot;
  }))

  tag_specifications {
    resource_type = &quot;instance&quot;
    tags = {
      Name = &quot;devops-course-web-server&quot;
    }
  }
}

resource &quot;aws_autoscaling_group&quot; &quot;web&quot; {
  name                = &quot;devops-course-asg&quot;
  vpc_zone_identifier = [aws_subnet.public.id]
  target_group_arns   = [aws_lb_target_group.web.arn]
  health_check_type   = &quot;ELB&quot;
  
  min_size         = 1
  max_size         = 3
  desired_capacity = 2

  launch_template {
    id      = aws_launch_template.web.id
    version = &quot;$Latest&quot;
  }

  tag {
    key                 = &quot;Name&quot;
    value               = &quot;devops-course-asg-instance&quot;
    propagate_at_launch = true
  }
}
</pre>
  <hr />
  <h2 id="EXBS">💰 Управление затратами в облаке</h2>
  <h2 id="4gjr">AWS Cost Management</h2>
  <pre id="jCu6" data-lang="bash"># Просмотр текущих затрат
aws ce get-cost-and-usage \
    --time-period Start=2025-01-01,End=2025-01-31 \
    --granularity MONTHLY \
    --metrics BlendedCost

# Создание бюджета
cat &gt; budget.json &lt;&lt; &#x27;EOF&#x27;
{
  &quot;BudgetName&quot;: &quot;DevOps-Course-Budget&quot;,
  &quot;BudgetLimit&quot;: {
    &quot;Amount&quot;: &quot;50&quot;,
    &quot;Unit&quot;: &quot;USD&quot;
  },
  &quot;TimeUnit&quot;: &quot;MONTHLY&quot;,
  &quot;CostFilters&quot;: {
    &quot;TagKey&quot;: [&quot;Project&quot;],
    &quot;TagValue&quot;: [&quot;DevOps-Course&quot;]
  },
  &quot;BudgetType&quot;: &quot;COST&quot;
}
EOF

aws budgets create-budget \
    --account-id $(aws sts get-caller-identity --query Account --output text) \
    --budget file://budget.json
</pre>
  <h2 id="wmWE">Оптимизация затрат</h2>
  <p id="VUf3"><strong>Лучшие практики:</strong></p>
  <ul id="TWXq">
    <li id="Q6T5">Используйте <strong>Reserved Instances</strong> для долгосрочных нагрузок</li>
    <li id="ssFj">Настройте <strong>Auto Scaling</strong> для динамического масштабирования</li>
    <li id="chev">Регулярно проверяйте <strong>неиспользуемые ресурсы</strong></li>
    <li id="m2g0">Используйте <strong>Spot Instances</strong> для некритичных задач</li>
    <li id="PqUZ">Настройте <strong>lifecycle policies</strong> для S3</li>
  </ul>
  <hr />
  <h2 id="iURi">🔒 Безопасность в облаке</h2>
  <h2 id="5OzU">Основные принципы</h2>
  <p id="xtJW"><strong>Shared Responsibility Model</strong> — модель разделенной ответственности:</p>
  <ul id="Fd4i">
    <li id="Mk5D"><strong>Облачный провайдер</strong> отвечает за безопасность облака</li>
    <li id="YyTQ"><strong>Клиент</strong> отвечает за безопасность в облаке</li>
  </ul>
  <h2 id="tDaA">IAM (Identity and Access Management)</h2>
  <pre id="9ZCy">json
{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [
        &quot;s3:GetObject&quot;,
        &quot;s3:PutObject&quot;
      ],
      &quot;Resource&quot;: &quot;arn:aws:s3:::devops-course-bucket/*&quot;
    },
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [
        &quot;logs:CreateLogGroup&quot;,
        &quot;logs:CreateLogStream&quot;,
        &quot;logs:PutLogEvents&quot;
      ],
      &quot;Resource&quot;: &quot;arn:aws:logs:*:*:*&quot;
    }
  ]
}
</pre>
  <h2 id="dK23">Лучшие практики безопасности</h2>
  <ul id="TPj5">
    <li id="g8K5"><strong>Принцип минимальных привилегий</strong> — предоставляйте только необходимые права</li>
    <li id="xNno"><strong>Multi-Factor Authentication</strong> — включите 2FA для всех аккаунтов</li>
    <li id="cGwA"><strong>Шифрование данных</strong> — в покое и при передаче</li>
    <li id="RqSG"><strong>Регулярный аудит</strong> — проверяйте права доступа и логи</li>
    <li id="rZEh"><strong>Сетевая сегментация</strong> — используйте VPC и security groups</li>
  </ul>
  <hr />
  <h2 id="Ehjv">📖 Полезные ресурсы</h2>
  <ul id="OTf9">
    <li id="N781"><strong><a href="https://aws.amazon.com/free/" target="_blank">AWS Free Tier</a></strong> — бесплатные ресурсы для изучения AWS</li>
    <li id="eVWw"><strong><a href="https://azure.microsoft.com/free/" target="_blank">Azure Free Account</a></strong> — $200 кредитов для новых пользователей</li>
    <li id="0F1O"><strong><a href="https://cloud.google.com/free/" target="_blank">Google Cloud Free Tier</a></strong> — $300 кредитов и Always Free ресурсы</li>
    <li id="leL5"><strong>YouTube:</strong> &quot;AWS для DevOps инженеров&quot; — практические видеоуроки</li>
    <li id="cfuV"><strong><a href="https://docs.microsoft.com/azure/architecture/" target="_blank">Cloud Architecture Center</a></strong> — паттерны облачной архитектуры</li>
  </ul>
  <hr />
  <h2 id="ih9o">✅ Чек-лист модуля</h2>
  <ul id="IKdZ">
    <li id="gA9O">Понимаю основные концепции облачных вычислений</li>
    <li id="XePJ">Изучил основные сервисы AWS/Azure/GCP</li>
    <li id="7Tnx">Настроил AWS CLI и создал первые ресурсы</li>
    <li id="13Ib">Развернул веб-приложение в облаке</li>
    <li id="TWMf">Создал S3 bucket и настроил статический хостинг</li>
    <li id="p88m">Написал и развернул Lambda функцию</li>
    <li id="GEB4">Использовал Terraform для создания облачной инфраструктуры</li>
    <li id="Y7BP">Настроил мониторинг затрат и бюджеты</li>
    <li id="j5ai">Изучил основы безопасности в облаке</li>
    <li id="LEzO">Понимаю принципы выбора облачного провайдера</li>
  </ul>
  <hr />
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="ijrv">🚀 Что дальше?</h2>
    <p id="55AZ">После освоения облачных платформ переходите к <strong>Модулю 11: DevSecOps — Безопасность в DevOps</strong> для изучения интеграции безопасности в процессы разработки и эксплуатации.</p>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/IAFqUAdUrt5</guid><link>https://teletype.in/@lalimi/IAFqUAdUrt5?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/IAFqUAdUrt5?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Модуль 9: Мониторинг и наблюдаемость в DevOps</title><pubDate>Wed, 11 Jun 2025 11:08:41 GMT</pubDate><description><![CDATA[Курс DevOps для новичков 2025]]></description><content:encoded><![CDATA[
  <p id="9-----devops"><em>Курс DevOps для новичков 2025</em></p>
  <hr />
  <h2 id="5MTc">📊 Зачем нужен мониторинг?</h2>
  <p id="ub93"><strong>Мониторинг</strong> — это процесс непрерывного наблюдения за состоянием системы, сбора метрик и генерации предупреждений. Без мониторинга вы узнаете о проблемах только когда пользователи начнут жаловаться. Это как водить машину с закрытыми глазами — опасно и безответственно.</p>
  <p id="Y7mW">По данным исследований 2025 года, <strong>87% компаний</strong> с зрелыми практиками мониторинга обнаруживают проблемы до того, как они повлияют на пользователей<a href="https://habr.com/ru/companies/ru_mts/articles/908452/" target="_blank">1</a>. Prometheus и Grafana стали стандартом де-факто для мониторинга в DevOps — как нож и вилка, вместе они делают мониторинг инфраструктуры и приложений простым и наглядным<a href="https://habr.com/ru/companies/ru_mts/articles/908452/" target="_blank">1</a>.</p>
  <hr />
  <h2 id="Yp45">🎯 Четыре золотых сигнала мониторинга</h2>
  <p id="IyeH">Google SRE определила четыре ключевые метрики, которые должен отслеживать любой сервис:</p>
  <h2 id="e16A">1. Latency (Задержка)</h2>
  <p id="3yFz">Время ответа на запросы. Критично для пользовательского опыта.</p>
  <h2 id="Dr8n">2. Traffic (Трафик)</h2>
  <p id="Uhe0">Количество запросов в секунду. Показывает нагрузку на систему.</p>
  <h2 id="s9Lk">3. Errors (Ошибки)</h2>
  <p id="J7BX">Процент неуспешных запросов. Индикатор качества сервиса.</p>
  <h2 id="Lql4">4. Saturation (Насыщенность)</h2>
  <p id="pv9r">Использование ресурсов (CPU, память, диск). Предупреждает о приближении к лимитам.</p>
  <hr />
  <h2 id="prometheus">🔥 Prometheus — сердце мониторинга</h2>
  <p id="LY4M"><strong>Prometheus</strong> — open-source система мониторинга, которая собирает метрики, хранит их в виде временных рядов и предоставляет мощный язык запросов PromQL<a href="https://habr.com/ru/companies/ru_mts/articles/908452/" target="_blank">1</a>. Это движок мониторинга, который сам опрашивает сервисы, собирает данные и сохраняет их в своей базе<a href="https://habr.com/ru/companies/ru_mts/articles/908452/" target="_blank">1</a>.</p>
  <h2 id="HuuL">Архитектура Prometheus</h2>
  <p id="U1G0"><strong>Prometheus Server</strong> — основной компонент, который:</p>
  <ul id="RpLC">
    <li id="tUO8">Собирает метрики с целевых систем (pull-модель)</li>
    <li id="gHzp">Хранит данные в формате временных рядов</li>
    <li id="H00i">Предоставляет HTTP API для запросов</li>
    <li id="w9nc">Оценивает правила алертов</li>
  </ul>
  <p id="tn7t"><strong>Exporters</strong> — программы, которые собирают метрики:</p>
  <ul id="PcOT">
    <li id="A3wS"><strong>Node Exporter</strong> — метрики ОС Linux</li>
    <li id="oxrc"><strong>cAdvisor</strong> — метрики Docker контейнеров</li>
    <li id="zdLQ"><strong>MySQL Exporter</strong> — метрики базы данных</li>
    <li id="thpA"><strong>Blackbox Exporter</strong> — проверка доступности сервисов</li>
  </ul>
  <p id="7Wrn"><strong>Alertmanager</strong> — компонент для управления алертами и уведомлениями</p>
  <hr />
  <h2 id="KUHz">⚙️ Установка стека мониторинга</h2>
  <h2 id="FOwj">Docker Compose для полного стека</h2>
  <pre id="9Gmv"># docker-compose.yml
version: &#x27;3.8&#x27;

services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - &quot;9090:9090&quot;
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./prometheus/rules:/etc/prometheus/rules
      - prometheus_data:/prometheus
    command:
      - &#x27;--config.file=/etc/prometheus/prometheus.yml&#x27;
      - &#x27;--storage.tsdb.path=/prometheus&#x27;
      - &#x27;--web.console.libraries=/etc/prometheus/console_libraries&#x27;
      - &#x27;--web.console.templates=/etc/prometheus/consoles&#x27;
      - &#x27;--storage.tsdb.retention.time=200h&#x27;
      - &#x27;--web.enable-lifecycle&#x27;
      - &#x27;--web.enable-admin-api&#x27;
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - &quot;3000:3000&quot;
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - &quot;9100:9100&quot;
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - &#x27;--path.procfs=/host/proc&#x27;
      - &#x27;--path.rootfs=/rootfs&#x27;
      - &#x27;--path.sysfs=/host/sys&#x27;
      - &#x27;--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)&#x27;
    restart: unless-stopped

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    ports:
      - &quot;8080:8080&quot;
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    privileged: true
    restart: unless-stopped

  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    ports:
      - &quot;9093:9093&quot;
    volumes:
      - ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:
</pre>
  <h2 id="B1QD">Конфигурация Prometheus</h2>
  <pre id="nISY"># prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - &quot;rules/*.yml&quot;

scrape_configs:
  - job_name: &#x27;prometheus&#x27;
    static_configs:
      - targets: [&#x27;localhost:9090&#x27;]

  - job_name: &#x27;node-exporter&#x27;
    static_configs:
      - targets: [&#x27;node-exporter:9100&#x27;]

  - job_name: &#x27;cadvisor&#x27;
    static_configs:
      - targets: [&#x27;cadvisor:8080&#x27;]

  - job_name: &#x27;my-app&#x27;
    static_configs:
      - targets: [&#x27;my-app:3000&#x27;]
    metrics_path: &#x27;/metrics&#x27;
    scrape_interval: 5s

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093
</pre>
  <hr />
  <h2 id="promql">📈 Основы PromQL</h2>
  <p id="7Stq">PromQL — язык запросов Prometheus для анализа метрик:</p>
  <h2 id="bLGZ">Базовые запросы</h2>
  <pre id="P1ej"># Текущее значение метрики
up

# Фильтрация по лейблам
up{job=&quot;node-exporter&quot;}

# Арифметические операции
node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes

# Процент использования памяти
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100

# Загрузка CPU за последние 5 минут
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode=&quot;idle&quot;}[5m])) * 100)
</pre>
  <h2 id="aSOo">Функции времени</h2>
  <pre id="MR7R"># Количество запросов в секунду
rate(http_requests_total[5m])

# Увеличение за период
increase(http_requests_total[1h])

# 95-й процентиль времени ответа
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

# Среднее значение за период
avg_over_time(cpu_usage[10m])
</pre>
  <hr />
  <h2 id="6wCr">🛠️ Практические задания</h2>
  <h2 id="upek">Задание 9.1: Запуск стека мониторинга</h2>
  <pre id="oGp1" data-lang="bash"># 1. Создание структуры проекта
mkdir monitoring-stack
cd monitoring-stack
mkdir -p prometheus/rules grafana/provisioning alertmanager

# 2. Создание docker-compose.yml (используйте пример выше)

# 3. Запуск стека
docker-compose up -d

# 4. Проверка сервисов
curl http://localhost:9090/targets    # Prometheus targets
curl http://localhost:3000           # Grafana (admin/admin123)
curl http://localhost:9100/metrics   # Node Exporter metrics
</pre>
  <h2 id="osoa">Задание 9.2: Мониторинг приложения</h2>
  <pre id="BS8q" data-lang="javascript">// app.js - Node.js приложение с метриками
const express = require(&#x27;express&#x27;);
const promClient = require(&#x27;prom-client&#x27;);

const app = express();
const port = process.env.PORT || 3000;

// Создание реестра метрик
const register = new promClient.Registry();

// Счетчик запросов
const httpRequestsTotal = new promClient.Counter({
  name: &#x27;http_requests_total&#x27;,
  help: &#x27;Total number of HTTP requests&#x27;,
  labelNames: [&#x27;method&#x27;, &#x27;route&#x27;, &#x27;status_code&#x27;],
  registers: [register]
});

// Гистограмма времени ответа
const httpRequestDuration = new promClient.Histogram({
  name: &#x27;http_request_duration_seconds&#x27;,
  help: &#x27;Duration of HTTP requests in seconds&#x27;,
  labelNames: [&#x27;method&#x27;, &#x27;route&#x27;, &#x27;status_code&#x27;],
  buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10],
  registers: [register]
});

// Gauge для активных подключений
const activeConnections = new promClient.Gauge({
  name: &#x27;active_connections&#x27;,
  help: &#x27;Number of active connections&#x27;,
  registers: [register]
});

// Middleware для сбора метрик
app.use((req, res, next) =&gt; {
  const start = Date.now();
  
  res.on(&#x27;finish&#x27;, () =&gt; {
    const duration = (Date.now() - start) / 1000;
    const route = req.route?.path || req.path;
    
    httpRequestsTotal
      .labels(req.method, route, res.statusCode.toString())
      .inc();
    
    httpRequestDuration
      .labels(req.method, route, res.statusCode.toString())
      .observe(duration);
  });
  
  next();
});

// Эндпоинт для метрик
app.get(&#x27;/metrics&#x27;, async (req, res) =&gt; {
  res.set(&#x27;Content-Type&#x27;, register.contentType);
  res.end(await register.metrics());
});

// API эндпоинты
app.get(&#x27;/&#x27;, (req, res) =&gt; {
  res.json({ 
    message: &#x27;Hello Monitoring!&#x27;,
    timestamp: new Date().toISOString()
  });
});

app.get(&#x27;/health&#x27;, (req, res) =&gt; {
  res.json({ status: &#x27;healthy&#x27; });
});

app.get(&#x27;/api/users&#x27;, (req, res) =&gt; {
  // Симуляция времени обработки
  setTimeout(() =&gt; {
    res.json({ users: [&#x27;Alice&#x27;, &#x27;Bob&#x27;, &#x27;Charlie&#x27;] });
  }, Math.random() * 100);
});

app.listen(port, () =&gt; {
  console.log(&#x60;Server running on port ${port}&#x60;);
});
</pre>
  <h2 id="hBlw">Задание 9.3: Настройка алертов</h2>
  <pre id="b3v2"># prometheus/rules/alerts.yml
groups:
  - name: system.rules
    rules:
      - alert: InstanceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: &quot;Instance {{ $labels.instance }} down&quot;
          description: &quot;{{ $labels.instance }} has been down for more than 1 minute.&quot;

      - alert: HighCPUUsage
        expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode=&quot;idle&quot;}[5m])) * 100) &gt; 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: &quot;High CPU usage on {{ $labels.instance }}&quot;
          description: &quot;CPU usage is above 80% for more than 2 minutes.&quot;

      - alert: HighMemoryUsage
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 &gt; 90
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: &quot;High memory usage on {{ $labels.instance }}&quot;
          description: &quot;Memory usage is above 90% for more than 2 minutes.&quot;

      - alert: DiskSpaceLow
        expr: (node_filesystem_avail_bytes{mountpoint=&quot;/&quot;} / node_filesystem_size_bytes{mountpoint=&quot;/&quot;}) * 100 &lt; 10
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: &quot;Low disk space on {{ $labels.instance }}&quot;
          description: &quot;Disk space is below 10% on {{ $labels.instance }}.&quot;

      - alert: HighErrorRate
        expr: rate(http_requests_total{status_code=~&quot;5..&quot;}[5m]) / rate(http_requests_total[5m]) &gt; 0.1
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: &quot;High error rate detected&quot;
          description: &quot;Error rate is above 10% for more than 5 minutes.&quot;
</pre>
  <h2 id="caez">Конфигурация Alertmanager</h2>
  <pre id="46hm"># alertmanager/alertmanager.yml
global:
  smtp_smarthost: &#x27;localhost:587&#x27;
  smtp_from: &#x27;alerts@company.com&#x27;

route:
  group_by: [&#x27;alertname&#x27;]
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: &#x27;web.hook&#x27;

receivers:
- name: &#x27;web.hook&#x27;
  email_configs:
  - to: &#x27;admin@company.com&#x27;
    subject: &#x27;Alert: {{ .GroupLabels.alertname }}&#x27;
    body: |
      {{ range .Alerts }}
      Alert: {{ .Annotations.summary }}
      Description: {{ .Annotations.description }}
      {{ end }}
  
  slack_configs:
  - api_url: &#x27;YOUR_SLACK_WEBHOOK_URL&#x27;
    channel: &#x27;#alerts&#x27;
    title: &#x27;Alert: {{ .GroupLabels.alertname }}&#x27;
    text: |
      {{ range .Alerts }}
      {{ .Annotations.summary }}
      {{ .Annotations.description }}
      {{ end }}

inhibit_rules:
  - source_match:
      severity: &#x27;critical&#x27;
    target_match:
      severity: &#x27;warning&#x27;
    equal: [&#x27;alertname&#x27;, &#x27;dev&#x27;, &#x27;instance&#x27;]
</pre>
  <hr />
  <h2 id="grafana">📊 Grafana — визуализация данных</h2>
  <h2 id="NW4i">Создание дашборда</h2>
  <pre id="w7BL">json
# grafana/provisioning/dashboards/system-overview.json
{
  &quot;dashboard&quot;: {
    &quot;title&quot;: &quot;System Overview&quot;,
    &quot;tags&quot;: [&quot;system&quot;, &quot;monitoring&quot;],
    &quot;panels&quot;: [
      {
        &quot;title&quot;: &quot;CPU Usage&quot;,
        &quot;type&quot;: &quot;stat&quot;,
        &quot;targets&quot;: [
          {
            &quot;expr&quot;: &quot;100 - (avg(irate(node_cpu_seconds_total{mode=\&quot;idle\&quot;}[5m])) * 100)&quot;,
            &quot;legendFormat&quot;: &quot;CPU Usage %&quot;
          }
        ],
        &quot;fieldConfig&quot;: {
          &quot;defaults&quot;: {
            &quot;unit&quot;: &quot;percent&quot;,
            &quot;thresholds&quot;: {
              &quot;steps&quot;: [
                {&quot;color&quot;: &quot;green&quot;, &quot;value&quot;: null},
                {&quot;color&quot;: &quot;yellow&quot;, &quot;value&quot;: 70},
                {&quot;color&quot;: &quot;red&quot;, &quot;value&quot;: 90}
              ]
            }
          }
        }
      },
      {
        &quot;title&quot;: &quot;Memory Usage&quot;,
        &quot;type&quot;: &quot;timeseries&quot;,
        &quot;targets&quot;: [
          {
            &quot;expr&quot;: &quot;(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100&quot;,
            &quot;legendFormat&quot;: &quot;Memory Usage %&quot;
          }
        ]
      },
      {
        &quot;title&quot;: &quot;HTTP Requests Rate&quot;,
        &quot;type&quot;: &quot;timeseries&quot;,
        &quot;targets&quot;: [
          {
            &quot;expr&quot;: &quot;rate(http_requests_total[5m])&quot;,
            &quot;legendFormat&quot;: &quot;{{ method }} {{ route }}&quot;
          }
        ]
      }
    ]
  }
}
</pre>
  <hr />
  <h2 id="elk-stack">🔍 Логирование с ELK Stack</h2>
  <h2 id="RIoL">Docker Compose для ELK</h2>
  <pre id="7vrE"># elk-stack.yml
version: &#x27;3.8&#x27;
services:
  elasticsearch:
    image: elasticsearch:8.11.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - &quot;ES_JAVA_OPTS=-Xms512m -Xmx512m&quot;
    ports:
      - &quot;9200:9200&quot;
    volumes:
      - elasticsearch_data:/usr/share/elasticsearch/data

  kibana:
    image: kibana:8.11.0
    ports:
      - &quot;5601:5601&quot;
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch

  logstash:
    image: logstash:8.11.0
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
    ports:
      - &quot;5000:5000&quot;
    depends_on:
      - elasticsearch

volumes:
  elasticsearch_data:
</pre>
  <h2 id="ZZdB">Конфигурация Logstash</h2>
  <pre id="Caag" data-lang="ruby">ruby
# logstash/pipeline/logstash.conf
input {
  beats {
    port =&gt; 5044
  }
  
  tcp {
    port =&gt; 5000
    codec =&gt; json
  }
}

filter {
  if [fields][service] == &quot;nginx&quot; {
    grok {
      match =&gt; { &quot;message&quot; =&gt; &quot;%{NGINXACCESS}&quot; }
    }
    
    date {
      match =&gt; [ &quot;timestamp&quot;, &quot;dd/MMM/yyyy:HH:mm:ss Z&quot; ]
    }
  }
}

output {
  elasticsearch {
    hosts =&gt; [&quot;elasticsearch:9200&quot;]
    index =&gt; &quot;logs-%{+YYYY.MM.dd}&quot;
  }
  
  stdout {
    codec =&gt; rubydebug
  }
}
</pre>
  <hr />
  <h2 id="H6Vg">📖 Полезные ресурсы</h2>
  <ul id="NDXg">
    <li id="DeuX"><strong><a href="https://prometheus.io/docs/" target="_blank">Prometheus Documentation</a></strong> — официальная документация</li>
    <li id="gcbZ"><strong><a href="https://grafana.com/docs/" target="_blank">Grafana Documentation</a></strong> — руководство по Grafana</li>
    <li id="tj0w"><strong><a href="https://prometheus.io/docs/prometheus/latest/querying/basics/" target="_blank">PromQL Tutorial</a></strong> — изучение языка запросов<a href="https://habr.com/ru/companies/ru_mts/articles/908452/" target="_blank">1</a></li>
    <li id="KEnj"><strong>YouTube:</strong> &quot;Мониторинг в DevOps с Prometheus и Grafana&quot; — практический курс</li>
    <li id="eZUL"><strong><a href="https://sre.google/sre-book/monitoring-distributed-systems/" target="_blank">The Four Golden Signals</a></strong> — принципы мониторинга от Google SRE</li>
  </ul>
  <hr />
  <h2 id="QFNU">✅ Чек-лист модуля</h2>
  <ul id="R0uf">
    <li id="1aWK">Понимаю важность мониторинга в DevOps</li>
    <li id="Y17a">Знаю четыре золотых сигнала мониторинга</li>
    <li id="HO7y">Установил и настроил Prometheus</li>
    <li id="iiQY">Освоил основы PromQL для запросов метрик</li>
    <li id="MTVS">Настроил сбор метрик с Node Exporter и cAdvisor</li>
    <li id="sstM">Создал дашборды в Grafana</li>
    <li id="0QcB">Настроил алерты в Prometheus и Alertmanager</li>
    <li id="C0Px">Добавил метрики в собственное приложение</li>
    <li id="jwVc">Изучил основы логирования с ELK Stack</li>
    <li id="UQJP">Понимаю разницу между мониторингом и наблюдаемостью</li>
  </ul>
  <hr />
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="BtLg">🚀 Что дальше?</h2>
    <p id="WfEE">После освоения мониторинга переходите к <strong>Модулю 10: Облачные платформы</strong> для изучения современных облачных решений и их интеграции с системами мониторинга.</p>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/Yqk30YHfsou</guid><link>https://teletype.in/@lalimi/Yqk30YHfsou?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/Yqk30YHfsou?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Модуль 8: Infrastructure as Code (IaC) — Управление инфраструктурой через код</title><pubDate>Wed, 11 Jun 2025 11:04:04 GMT</pubDate><description><![CDATA[Курс DevOps для новичков 2025]]></description><content:encoded><![CDATA[
  <p id="8-infrastructure-as-code-iac"><em>Курс DevOps для новичков 2025</em></p>
  <hr />
  <h2 id="infrastructure-as-code">🏗️ Что такое Infrastructure as Code?</h2>
  <p id="lr1c"><strong>IaC</strong> — это практика управления инфраструктурой через код вместо ручной настройки. Вместо того чтобы настраивать сервера через веб-интерфейс или командную строку, вы описываете желаемое состояние инфраструктуры в текстовых файлах и применяете их автоматически.</p>
  <p id="X3nw">По данным исследований 2025 года, компании, использующие IaC, развертывают инфраструктуру в <strong>10 раз быстрее</strong> и имеют на <strong>50% меньше ошибок</strong> конфигурации<a href="https://habr.com/ru/companies/ru_mts/articles/908452/" target="_blank">1</a>.</p>
  <hr />
  <h2 id="iac">🎯 Преимущества IaC</h2>
  <h2 id="rb5w">Версионирование</h2>
  <p id="qVcw">Инфраструктура хранится в Git с полной историей изменений. Можно откатиться к любой версии и отследить, кто что изменил.</p>
  <h2 id="G22T">Воспроизводимость</h2>
  <p id="p7Dw">Одинаковые конфигурации в разных средах (dev, staging, production). Исключается проблема &quot;работает на моей машине&quot;.</p>
  <h2 id="eUr8">Автоматизация</h2>
  <p id="q6Bv">Быстрое создание и удаление инфраструктуры. Новая среда разворачивается за минуты, а не дни.</p>
  <h2 id="7rzD">Документация</h2>
  <p id="Bn9d">Код служит живой документацией архитектуры. Всегда актуальное описание инфраструктуры.</p>
  <h2 id="LN2X">Совместная работа</h2>
  <p id="PgGK">Команда может вместе работать над инфраструктурой через pull requests и code review.</p>
  <hr />
  <h2 id="terraform---iac">🔧 Terraform — лидер IaC</h2>
  <p id="Fn60"><strong>Terraform</strong> — инструмент от HashiCorp, который позволяет описывать инфраструктуру на декларативном языке HCL (HashiCorp Configuration Language). Он поддерживает сотни провайдеров: AWS, Azure, GCP, Kubernetes, Docker и многие другие.</p>
  <h2 id="qGfS">Основные концепции Terraform</h2>
  <p id="TWEd"><strong>Provider</strong> — плагин для взаимодействия с API сервиса (AWS, Azure, etc.)<br /> <strong>Resource</strong> — компонент инфраструктуры (сервер, база данных, сеть)<br /> <strong>Data Source</strong> — информация о существующих ресурсах<br /> <strong>Variable</strong> — входные параметры конфигурации<br /> <strong>Output</strong> — выходные значения конфигурации<br /> <strong>State</strong> — текущее состояние инфраструктуры</p>
  <hr />
  <h2 id="terraform">⚙️ Установка Terraform</h2>
  <pre id="C2Yk" data-lang="bash"># Загрузка и установка Terraform
wget https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_linux_amd64.zip
unzip terraform_1.7.0_linux_amd64.zip
sudo mv terraform /usr/local/bin/

# Проверка установки
terraform version

# Включение автодополнения
terraform -install-autocomplete
</pre>
  <hr />
  <h2 id="terraform">🛠️ Первый проект на Terraform</h2>
  <h2 id="Zgpb">Структура проекта</h2>
  <pre id="FLjX">terraform-project/
├── main.tf          # Основная конфигурация
├── variables.tf     # Переменные
├── outputs.tf       # Выходные значения
├── terraform.tfvars # Значения переменных
└── providers.tf     # Провайдеры
</pre>
  <h2 id="Tp4y">providers.tf</h2>
  <pre id="L4eF">terraform {
  required_version = &quot;&gt;= 1.0&quot;
  required_providers {
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;~&gt; 5.0&quot;
    }
  }
}

provider &quot;aws&quot; {
  region = var.aws_region
}
</pre>
  <h2 id="wcwj">variables.tf</h2>
  <pre id="k4kn">variable &quot;aws_region&quot; {
  description = &quot;AWS region&quot;
  type        = string
  default     = &quot;us-west-2&quot;
}

variable &quot;instance_type&quot; {
  description = &quot;EC2 instance type&quot;
  type        = string
  default     = &quot;t3.micro&quot;
}

variable &quot;environment&quot; {
  description = &quot;Environment name&quot;
  type        = string
  validation {
    condition     = contains([&quot;dev&quot;, &quot;staging&quot;, &quot;prod&quot;], var.environment)
    error_message = &quot;Environment must be dev, staging, or prod.&quot;
  }
}

variable &quot;project_name&quot; {
  description = &quot;Name of the project&quot;
  type        = string
  default     = &quot;devops-course&quot;
}
</pre>
  <h2 id="2Fiz">main.tf</h2>
  <pre id="5GAo"># Data source для получения AMI
data &quot;aws_ami&quot; &quot;ubuntu&quot; {
  most_recent = true
  owners      = [&quot;099720109477&quot;] # Canonical

  filter {
    name   = &quot;name&quot;
    values = [&quot;ubuntu/images/hvm-ssd/ubuntu-22.04-amd64-server-*&quot;]
  }

  filter {
    name   = &quot;virtualization-type&quot;
    values = [&quot;hvm&quot;]
  }
}

# Security Group
resource &quot;aws_security_group&quot; &quot;web_sg&quot; {
  name_prefix = &quot;${var.environment}-${var.project_name}-web-&quot;
  description = &quot;Security group for web servers&quot;

  ingress {
    description = &quot;HTTP&quot;
    from_port   = 80
    to_port     = 80
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  ingress {
    description = &quot;HTTPS&quot;
    from_port   = 443
    to_port     = 443
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  ingress {
    description = &quot;SSH&quot;
    from_port   = 22
    to_port     = 22
    protocol    = &quot;tcp&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = &quot;-1&quot;
    cidr_blocks = [&quot;0.0.0.0/0&quot;]
  }

  tags = {
    Name        = &quot;${var.environment}-${var.project_name}-web-sg&quot;
    Environment = var.environment
    Project     = var.project_name
  }
}

# EC2 Instance
resource &quot;aws_instance&quot; &quot;web_server&quot; {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  vpc_security_group_ids = [aws_security_group.web_sg.id]

  user_data = base64encode(templatefile(&quot;${path.module}/user_data.sh&quot;, {
    environment = var.environment
    project     = var.project_name
  }))

  tags = {
    Name        = &quot;${var.environment}-${var.project_name}-web-server&quot;
    Environment = var.environment
    Project     = var.project_name
  }
}

# Elastic IP
resource &quot;aws_eip&quot; &quot;web_eip&quot; {
  instance = aws_instance.web_server.id
  domain   = &quot;vpc&quot;

  tags = {
    Name        = &quot;${var.environment}-${var.project_name}-eip&quot;
    Environment = var.environment
  }
}
</pre>
  <h2 id="a6GF">outputs.tf</h2>
  <pre id="rBgb">output &quot;instance_public_ip&quot; {
  description = &quot;Public IP address of the EC2 instance&quot;
  value       = aws_eip.web_eip.public_ip
}

output &quot;instance_public_dns&quot; {
  description = &quot;Public DNS name of the EC2 instance&quot;
  value       = aws_instance.web_server.public_dns
}

output &quot;security_group_id&quot; {
  description = &quot;ID of the security group&quot;
  value       = aws_security_group.web_sg.id
}

output &quot;ssh_command&quot; {
  description = &quot;SSH command to connect to the instance&quot;
  value       = &quot;ssh -i ~/.ssh/your-key.pem ubuntu@${aws_eip.web_eip.public_ip}&quot;
}
</pre>
  <h2 id="mRER">terraform.tfvars</h2>
  <pre id="tHdO">aws_region    = &quot;us-west-2&quot;
instance_type = &quot;t3.micro&quot;
environment   = &quot;dev&quot;
project_name  = &quot;devops-learning&quot;
</pre>
  <h2 id="Nqhk">user_data.sh</h2>
  <pre id="vd0F" data-lang="bash">#!/bin/bash
apt update
apt install -y nginx docker.io

# Настройка nginx
systemctl start nginx
systemctl enable nginx

# Создание простой страницы
cat &gt; /var/www/html/index.html &lt;&lt; EOF
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;${project} - ${environment}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Welcome to ${project}!&lt;/h1&gt;
    &lt;p&gt;Environment: ${environment}&lt;/p&gt;
    &lt;p&gt;Server deployed with Terraform&lt;/p&gt;
    &lt;p&gt;Timestamp: $(date)&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
EOF

# Настройка Docker
systemctl start docker
systemctl enable docker
usermod -aG docker ubuntu
</pre>
  <hr />
  <h2 id="terraform">🚀 Основные команды Terraform</h2>
  <pre id="9Max" data-lang="bash"># Инициализация проекта (загрузка провайдеров)
terraform init

# Форматирование кода
terraform fmt

# Валидация конфигурации
terraform validate

# Планирование изменений (dry-run)
terraform plan

# Планирование с сохранением в файл
terraform plan -out=tfplan

# Применение изменений
terraform apply

# Применение сохраненного плана
terraform apply tfplan

# Просмотр текущего состояния
terraform show

# Список ресурсов в state
terraform state list

# Подробная информация о ресурсе
terraform state show aws_instance.web_server

# Уничтожение инфраструктуры
terraform destroy

# Импорт существующего ресурса
terraform import aws_instance.web_server i-1234567890abcdef0
</pre>
  <hr />
  <h2 id="8s2O">🛠️ Практические задания</h2>
  <h2 id="zdKB">Задание 8.1: Создание базовой инфраструктуры</h2>
  <pre id="l5fp" data-lang="bash"># 1. Создание проекта
mkdir terraform-aws-demo
cd terraform-aws-demo

# 2. Настройка AWS CLI (если еще не настроен)
aws configure

# 3. Создание файлов конфигурации
# (используйте примеры выше)

# 4. Инициализация и применение
terraform init
terraform plan
terraform apply

# 5. Проверка результата
curl http://$(terraform output -raw instance_public_ip)
</pre>
  <h2 id="1tWx">Задание 8.2: Модули Terraform</h2>
  <pre id="svsY"># modules/vpc/main.tf
resource &quot;aws_vpc&quot; &quot;main&quot; {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = var.vpc_name
  }
}

resource &quot;aws_internet_gateway&quot; &quot;main&quot; {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = &quot;${var.vpc_name}-igw&quot;
  }
}

resource &quot;aws_subnet&quot; &quot;public&quot; {
  count             = length(var.public_subnet_cidrs)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.public_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  map_public_ip_on_launch = true

  tags = {
    Name = &quot;${var.vpc_name}-public-${count.index + 1}&quot;
    Type = &quot;public&quot;
  }
}

resource &quot;aws_route_table&quot; &quot;public&quot; {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = &quot;0.0.0.0/0&quot;
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = &quot;${var.vpc_name}-public-rt&quot;
  }
}

resource &quot;aws_route_table_association&quot; &quot;public&quot; {
  count          = length(aws_subnet.public)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}
</pre>
  <pre id="2HH2"># modules/vpc/variables.tf
variable &quot;vpc_name&quot; {
  description = &quot;Name of the VPC&quot;
  type        = string
}

variable &quot;cidr_block&quot; {
  description = &quot;CIDR block for VPC&quot;
  type        = string
  default     = &quot;10.0.0.0/16&quot;
}

variable &quot;public_subnet_cidrs&quot; {
  description = &quot;CIDR blocks for public subnets&quot;
  type        = list(string)
  default     = [&quot;10.0.1.0/24&quot;, &quot;10.0.2.0/24&quot;]
}

variable &quot;availability_zones&quot; {
  description = &quot;Availability zones&quot;
  type        = list(string)
  default     = [&quot;us-west-2a&quot;, &quot;us-west-2b&quot;]
}
</pre>
  <pre id="mu67"># modules/vpc/outputs.tf
output &quot;vpc_id&quot; {
  description = &quot;ID of the VPC&quot;
  value       = aws_vpc.main.id
}

output &quot;public_subnet_ids&quot; {
  description = &quot;IDs of the public subnets&quot;
  value       = aws_subnet.public[*].id
}

output &quot;internet_gateway_id&quot; {
  description = &quot;ID of the Internet Gateway&quot;
  value       = aws_internet_gateway.main.id
}
</pre>
  <h2 id="wC66">Использование модуля</h2>
  <pre id="dJo4"># main.tf
module &quot;vpc&quot; {
  source = &quot;./modules/vpc&quot;

  vpc_name             = &quot;my-vpc&quot;
  cidr_block           = &quot;10.0.0.0/16&quot;
  public_subnet_cidrs  = [&quot;10.0.1.0/24&quot;, &quot;10.0.2.0/24&quot;]
  availability_zones   = [&quot;us-west-2a&quot;, &quot;us-west-2b&quot;]
}

resource &quot;aws_instance&quot; &quot;web&quot; {
  ami           = data.aws_ami.ubuntu.id
  instance_type = &quot;t3.micro&quot;
  subnet_id     = module.vpc.public_subnet_ids[0]

  tags = {
    Name = &quot;web-server-in-custom-vpc&quot;
  }
}
</pre>
  <hr />
  <h2 id="ansible">🔄 Ansible — управление конфигурациями</h2>
  <p id="71hr"><strong>Ansible</strong> — инструмент автоматизации для управления конфигурациями серверов. В отличие от Terraform, который создает инфраструктуру, Ansible настраивает программное обеспечение.</p>
  <h2 id="CJIK">Установка Ansible</h2>
  <pre id="vwGz" data-lang="bash"># Ubuntu/Debian
sudo apt update
sudo apt install ansible

# Проверка установки
ansible --version
</pre>
  <h2 id="95cU">Inventory файл</h2>
  <pre id="1zKH"># inventory/hosts.yml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
          ansible_host: 192.168.1.10
          ansible_user: ubuntu
        web2.example.com:
          ansible_host: 192.168.1.11
          ansible_user: ubuntu
    databases:
      hosts:
        db1.example.com:
          ansible_host: 192.168.1.20
          ansible_user: ubuntu
      vars:
        db_port: 5432
</pre>
  <h2 id="gJ8v">Playbook для настройки веб-сервера</h2>
  <pre id="4BEJ"># playbooks/webserver.yml
---
- name: Configure web servers
  hosts: webservers
  become: yes
  vars:
    nginx_port: 80
    app_name: &quot;devops-demo&quot;
    
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install required packages
      apt:
        name:
          - nginx
          - docker.io
          - git
          - curl
        state: present

    - name: Start and enable nginx
      systemd:
        name: nginx
        state: started
        enabled: yes

    - name: Start and enable docker
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: Add ubuntu user to docker group
      user:
        name: ubuntu
        groups: docker
        append: yes

    - name: Create application directory
      file:
        path: /opt/{{ app_name }}
        state: directory
        owner: ubuntu
        group: ubuntu
        mode: &#x27;0755&#x27;

    - name: Configure nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/sites-available/{{ app_name }}
      notify: restart nginx

    - name: Enable nginx site
      file:
        src: /etc/nginx/sites-available/{{ app_name }}
        dest: /etc/nginx/sites-enabled/{{ app_name }}
        state: link
      notify: restart nginx

    - name: Remove default nginx site
      file:
        path: /etc/nginx/sites-enabled/default
        state: absent
      notify: restart nginx

    - name: Open firewall for HTTP
      ufw:
        rule: allow
        port: &quot;{{ nginx_port }}&quot;

  handlers:
    - name: restart nginx
      systemd:
        name: nginx
        state: restarted
</pre>
  <h2 id="9Tir">Шаблон Nginx</h2>
  <pre id="98Jx"># templates/nginx.conf.j2
server {
    listen {{ nginx_port }};
    server_name {{ inventory_hostname }};

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /health {
        access_log off;
        return 200 &quot;healthy\n&quot;;
        add_header Content-Type text/plain;
    }
}
</pre>
  <h2 id="aPKp">Запуск Ansible</h2>
  <pre id="1kTv" data-lang="bash"># Проверка подключения
ansible all -i inventory/hosts.yml -m ping

# Запуск playbook
ansible-playbook -i inventory/hosts.yml playbooks/webserver.yml

# Запуск с ограничением на группу хостов
ansible-playbook -i inventory/hosts.yml playbooks/webserver.yml --limit webservers

# Dry-run (проверка без изменений)
ansible-playbook -i inventory/hosts.yml playbooks/webserver.yml --check
</pre>
  <hr />
  <h2 id="terraform--ansible">📊 Интеграция Terraform и Ansible</h2>
  <h2 id="AcRo">Terraform с Ansible provisioner</h2>
  <pre id="bWKM">resource &quot;aws_instance&quot; &quot;web_server&quot; {
  ami           = data.aws_ami.ubuntu.id
  instance_type = var.instance_type
  key_name      = aws_key_pair.deployer.key_name

  tags = {
    Name = &quot;web-server&quot;
  }

  provisioner &quot;remote-exec&quot; {
    inline = [
      &quot;sudo apt update&quot;,
      &quot;sudo apt install -y python3&quot;
    ]

    connection {
      type        = &quot;ssh&quot;
      user        = &quot;ubuntu&quot;
      private_key = file(&quot;~/.ssh/id_rsa&quot;)
      host        = self.public_ip
    }
  }

  provisioner &quot;local-exec&quot; {
    command = &quot;ansible-playbook -i &#x27;${self.public_ip},&#x27; --private-key ~/.ssh/id_rsa playbooks/webserver.yml&quot;
  }
}
</pre>
  <h2 id="l5PU">Генерация Ansible inventory из Terraform</h2>
  <pre id="Krt9"># outputs.tf
output &quot;ansible_inventory&quot; {
  value = templatefile(&quot;${path.module}/inventory.tpl&quot;, {
    webservers = aws_instance.web_servers[*].public_ip
    databases  = aws_instance.db_servers[*].private_ip
  })
}

resource &quot;local_file&quot; &quot;ansible_inventory&quot; {
  content  = templatefile(&quot;${path.module}/inventory.tpl&quot;, {
    webservers = aws_instance.web_servers[*].public_ip
    databases  = aws_instance.db_servers[*].private_ip
  })
  filename = &quot;${path.module}/generated_inventory.yml&quot;
}
</pre>
  <hr />
  <h2 id="aer6">📖 Полезные ресурсы</h2>
  <ul id="3qjL">
    <li id="g9j4"><strong><a href="https://developer.hashicorp.com/terraform/docs" target="_blank">Terraform Documentation</a></strong> — официальная документация</li>
    <li id="MzDi"><strong><a href="https://registry.terraform.io/" target="_blank">Terraform Registry</a></strong> — готовые модули и провайдеры</li>
    <li id="OVBb"><strong><a href="https://docs.ansible.com/ansible/latest/user_guide/" target="_blank">Ansible Documentation</a></strong> — руководство по Ansible</li>
    <li id="BOm2"><strong>YouTube:</strong> &quot;Terraform от А до Я&quot; — практический курс по IaC<a href="https://www.youtube.com/watch?v=2FsmJrorp9Q" target="_blank">8</a></li>
    <li id="Q3lM"><strong><a href="https://spacelift.io/blog/terraform-infrastructure-as-code" target="_blank">Infrastructure as Code Guide</a></strong> — подробное руководство</li>
  </ul>
  <hr />
  <h2 id="tyhv">✅ Чек-лист модуля</h2>
  <ul id="Y8LZ">
    <li id="hujl">Понимаю концепции Infrastructure as Code</li>
    <li id="saAQ">Установил и настроил Terraform</li>
    <li id="e0tF">Создал первую инфраструктуру с Terraform</li>
    <li id="Tz4U">Освоил основные команды Terraform</li>
    <li id="znfR">Работал с переменными и outputs</li>
    <li id="o3zW">Создал и использовал Terraform модули</li>
    <li id="ZcAx">Настроил удаленное хранение state</li>
    <li id="fwhQ">Установил и изучил Ansible</li>
    <li id="JG52">Создал Ansible playbook для настройки серверов</li>
    <li id="ujS9">Интегрировал Terraform с Ansible</li>
  </ul>
  <hr />
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="gOKv">🚀 Что дальше?</h2>
    <p id="wLTS">После освоения Infrastructure as Code переходите к <strong>Модулю 9: Мониторинг и наблюдаемость</strong> для изучения систем мониторинга инфраструктуры и приложений.</p>
  </section>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@lalimi/rMI44ETMvtL</guid><link>https://teletype.in/@lalimi/rMI44ETMvtL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi</link><comments>https://teletype.in/@lalimi/rMI44ETMvtL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=lalimi#comments</comments><dc:creator>lalimi</dc:creator><title>Модуль 7: CI/CD — Непрерывная интеграция и развертывание</title><pubDate>Wed, 11 Jun 2025 10:58:13 GMT</pubDate><description><![CDATA[Курс DevOps для новичков 2025]]></description><content:encoded><![CDATA[
  <p id="ocCh"><em>Курс DevOps для новичков 2025</em></p>
  <hr />
  <h2 id="cicd">🔄 Что такое CI/CD?</h2>
  <p id="JxDo"><strong>CI/CD</strong> — это набор практик, которые автоматизируют процесс доставки кода от разработчика до пользователя. Это позволяет выпускать изменения быстро, безопасно и с минимальным вмешательством человека.</p>
  <p id="nrk6"><strong>Continuous Integration (CI)</strong> — автоматическая сборка, тестирование и интеграция изменений в основную ветку кода</p>
  <p id="Cr9i"><strong>Continuous Delivery (CD)</strong> — автоматическая подготовка изменений к развертыванию</p>
  <p id="sAcU"><strong>Continuous Deployment (CD)</strong> — автоматическое развертывание изменений в продакшн</p>
  <p id="ZwwV">По данным DevOps Research and Assessment 2024, команды с зрелыми CI/CD практиками развертывают код в <strong>208 раз чаще</strong> и восстанавливаются после сбоев в <strong>106 раз быстрее</strong>.</p>
  <hr />
  <h2 id="cicd">🎯 Зачем нужен CI/CD?</h2>
  <h2 id="gAXd">Проблемы традиционной разработки</h2>
  <ul id="47li">
    <li id="QsRC">Долгие циклы релизов (месяцы)</li>
    <li id="2eCH">Высокий риск ошибок при развертывании</li>
    <li id="Ku9n">Сложности интеграции кода разных разработчиков</li>
    <li id="rVrt">Ручные процессы тестирования и развертывания</li>
    <li id="Fvf3">&quot;Integration Hell&quot; — проблемы при слиянии кода</li>
  </ul>
  <h2 id="pMBv">Преимущества CI/CD</h2>
  <ul id="aXD8">
    <li id="k4sp"><strong>Быстрая обратная связь</strong> — ошибки обнаруживаются в течение минут</li>
    <li id="4i3R"><strong>Снижение рисков</strong> — маленькие изменения легче откатить</li>
    <li id="xfsv"><strong>Повышение качества</strong> — автоматические тесты на каждом этапе</li>
    <li id="49i3"><strong>Ускорение доставки</strong> — от идеи до пользователя за часы, а не месяцы</li>
  </ul>
  <hr />
  <h2 id="cicd">🏗️ Архитектура CI/CD пайплайна</h2>
  <h2 id="6EGG">Типичный пайплайн включает этапы:</h2>
  <pre id="8PbI">1. Source        → Разработчик делает commit в Git
2. Build         → Автоматическая сборка приложения
3. Test          → Запуск автоматических тестов
4. Security Scan → Проверка на уязвимости
5. Package       → Создание артефактов (Docker образы)
6. Deploy Staging → Развертывание в тестовую среду
7. Integration Tests → Тестирование в реальной среде
8. Deploy Production → Развертывание в продакшн
9. Monitor       → Мониторинг работы приложения
</pre>
  <h2 id="HvZF">Стратегии развертывания</h2>
  <p id="Zywm"><strong>Blue-Green</strong> — две идентичные среды, переключение между ними<br /> <strong>Canary</strong> — постепенный перевод трафика на новую версию<br /> <strong>Rolling</strong> — поэтапная замена экземпляров приложения</p>
  <hr />
  <h2 id="github-actions---cicd">🚀 GitHub Actions — современный CI/CD</h2>
  <h2 id="vnsI">Основные концепции</h2>
  <p id="DZXh"><strong>Workflow</strong> — автоматизированный процесс, описанный в YAML файле<br /> <strong>Job</strong> — набор шагов, выполняющихся на одном runner<br /> <strong>Step</strong> — отдельная задача (команда или action)<br /> <strong>Runner</strong> — сервер, выполняющий workflows</p>
  <h2 id="4IuB">Базовый workflow для Node.js</h2>
  <pre id="85hR"># .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  NODE_VERSION: &#x27;18&#x27;
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [16.x, 18.x, 20.x]
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Setup Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: &#x27;npm&#x27;
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linter
      run: npm run lint
    
    - name: Run unit tests
      run: npm test -- --coverage
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage/lcov.info
    
    - name: Run security audit
      run: npm audit --audit-level moderate

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == &#x27;refs/heads/main&#x27;
    
    outputs:
      image-digest: ${{ steps.build.outputs.digest }}
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    
    - name: Login to Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=sha,prefix={{branch}}-
          type=raw,value=latest,enable={{is_default_branch}}
    
    - name: Build and push Docker image
      id: build
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  deploy-staging:
    needs: build-and-push
    runs-on: ubuntu-latest
    environment: staging
    
    steps:
    - name: Deploy to staging
      run: |
        echo &quot;Deploying to staging environment...&quot;
        echo &quot;Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}&quot;
        # Здесь команды для развертывания в staging

  integration-tests:
    needs: deploy-staging
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Run integration tests
      run: |
        echo &quot;Running integration tests against staging...&quot;
        # npm run test:integration

  deploy-production:
    needs: [build-and-push, integration-tests]
    runs-on: ubuntu-latest
    environment: production
    if: github.ref == &#x27;refs/heads/main&#x27;
    
    steps:
    - name: Deploy to production
      run: |
        echo &quot;Deploying to production...&quot;
        echo &quot;Image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}&quot;
        # kubectl set image deployment/myapp myapp=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
</pre>
  <hr />
  <h2 id="IsEo">🛠️ Практические задания</h2>
  <h2 id="vh9i">Задание 7.1: Создание простого CI пайплайна</h2>
  <pre id="WEmp" data-lang="bash"># 1. Создание Node.js приложения
mkdir ci-cd-demo
cd ci-cd-demo
npm init -y

# 2. Установка зависимостей
npm install express
npm install --save-dev jest supertest eslint nodemon

# 3. Настройка package.json
cat &gt; package.json &lt;&lt; &#x27;EOF&#x27;
{
  &quot;name&quot;: &quot;ci-cd-demo&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;Demo app for CI/CD learning&quot;,
  &quot;main&quot;: &quot;app.js&quot;,
  &quot;scripts&quot;: {
    &quot;start&quot;: &quot;node app.js&quot;,
    &quot;dev&quot;: &quot;nodemon app.js&quot;,
    &quot;test&quot;: &quot;jest&quot;,
    &quot;test:watch&quot;: &quot;jest --watch&quot;,
    &quot;lint&quot;: &quot;eslint .&quot;,
    &quot;lint:fix&quot;: &quot;eslint . --fix&quot;
  },
  &quot;dependencies&quot;: {
    &quot;express&quot;: &quot;^4.18.2&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;jest&quot;: &quot;^29.7.0&quot;,
    &quot;supertest&quot;: &quot;^6.3.3&quot;,
    &quot;eslint&quot;: &quot;^8.57.0&quot;,
    &quot;nodemon&quot;: &quot;^3.0.2&quot;
  }
}
EOF
</pre>
  <h2 id="Hc6J">Создание приложения</h2>
  <pre id="ic3B" data-lang="javascript">// app.js
const express = require(&#x27;express&#x27;);
const app = express();
const port = process.env.PORT || 3000;

app.use(express.json());

// Простая база данных в памяти
let users = [
  { id: 1, name: &#x27;Alice&#x27;, email: &#x27;alice@example.com&#x27; },
  { id: 2, name: &#x27;Bob&#x27;, email: &#x27;bob@example.com&#x27; }
];

app.get(&#x27;/&#x27;, (req, res) =&gt; {
  res.json({ 
    message: &#x27;Hello CI/CD!&#x27;, 
    version: &#x27;1.0.0&#x27;,
    timestamp: new Date().toISOString()
  });
});

app.get(&#x27;/health&#x27;, (req, res) =&gt; {
  res.json({ 
    status: &#x27;OK&#x27;, 
    uptime: process.uptime(),
    timestamp: new Date().toISOString()
  });
});

app.get(&#x27;/users&#x27;, (req, res) =&gt; {
  res.json(users);
});

app.post(&#x27;/users&#x27;, (req, res) =&gt; {
  const { name, email } = req.body;
  
  if (!name || !email) {
    return res.status(400).json({ error: &#x27;Name and email are required&#x27; });
  }
  
  const newUser = {
    id: users.length + 1,
    name,
    email
  };
  
  users.push(newUser);
  res.status(201).json(newUser);
});

if (require.main === module) {
  app.listen(port, () =&gt; {
    console.log(&#x60;Server running on port ${port}&#x60;);
  });
}

module.exports = app;
</pre>
  <h2 id="uOI7">Создание тестов</h2>
  <pre id="kjqL" data-lang="javascript">// app.test.js
const request = require(&#x27;supertest&#x27;);
const app = require(&#x27;./app&#x27;);

describe(&#x27;App&#x27;, () =&gt; {
  test(&#x27;GET / should return hello message&#x27;, async () =&gt; {
    const response = await request(app).get(&#x27;/&#x27;);
    expect(response.status).toBe(200);
    expect(response.body.message).toBe(&#x27;Hello CI/CD!&#x27;);
    expect(response.body.version).toBe(&#x27;1.0.0&#x27;);
  });

  test(&#x27;GET /health should return OK status&#x27;, async () =&gt; {
    const response = await request(app).get(&#x27;/health&#x27;);
    expect(response.status).toBe(200);
    expect(response.body.status).toBe(&#x27;OK&#x27;);
    expect(response.body.uptime).toBeGreaterThanOrEqual(0);
  });

  test(&#x27;GET /users should return users list&#x27;, async () =&gt; {
    const response = await request(app).get(&#x27;/users&#x27;);
    expect(response.status).toBe(200);
    expect(Array.isArray(response.body)).toBe(true);
    expect(response.body.length).toBeGreaterThan(0);
  });

  test(&#x27;POST /users should create new user&#x27;, async () =&gt; {
    const newUser = {
      name: &#x27;Charlie&#x27;,
      email: &#x27;charlie@example.com&#x27;
    };

    const response = await request(app)
      .post(&#x27;/users&#x27;)
      .send(newUser);

    expect(response.status).toBe(201);
    expect(response.body.name).toBe(newUser.name);
    expect(response.body.email).toBe(newUser.email);
    expect(response.body.id).toBeDefined();
  });

  test(&#x27;POST /users should return 400 for invalid data&#x27;, async () =&gt; {
    const response = await request(app)
      .post(&#x27;/users&#x27;)
      .send({ name: &#x27;Test&#x27; }); // missing email

    expect(response.status).toBe(400);
    expect(response.body.error).toBe(&#x27;Name and email are required&#x27;);
  });
});
</pre>
  <h2 id="CYHV">Задание 7.2: Dockerfile для приложения</h2>
  <pre id="NFqg"># Dockerfile
FROM node:18-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine AS runtime

# Создание пользователя
RUN addgroup -g 1001 -S nodejs &amp;&amp; \
    adduser -S nextjs -u 1001

WORKDIR /app

# Копирование зависимостей
COPY --from=builder /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .

# Переключение на непривилегированного пользователя
USER nextjs

EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node -e &quot;require(&#x27;http&#x27;).get(&#x27;http://localhost:3000/health&#x27;, (res) =&gt; { process.exit(res.statusCode === 200 ? 0 : 1) })&quot;

CMD [&quot;npm&quot;, &quot;start&quot;]
</pre>
  <h2 id="lL9A">Задание 7.3: GitHub Actions workflow</h2>
  <pre id="3YRP"># .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: &#x27;18&#x27;
        cache: &#x27;npm&#x27;
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linter
      run: npm run lint
    
    - name: Run tests
      run: npm test
    
    - name: Security audit
      run: npm audit --audit-level moderate

  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == &#x27;push&#x27;
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    
    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: |
          ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
</pre>
  <hr />
  <h2 id="jenkins---cicd">🔧 Jenkins — классический CI/CD</h2>
  <h2 id="FEXb">Установка Jenkins</h2>
  <pre id="8c2L" data-lang="bash"># Установка Java
sudo apt update
sudo apt install openjdk-11-jdk

# Добавление репозитория Jenkins
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c &#x27;echo deb http://pkg.jenkins.io/debian-stable binary/ &gt; /etc/apt/sources.list.d/jenkins.list&#x27;

# Установка Jenkins
sudo apt update
sudo apt install jenkins

# Запуск Jenkins
sudo systemctl start jenkins
sudo systemctl enable jenkins

# Получение первоначального пароля
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
</pre>
  <h2 id="VPf6">Jenkinsfile пример</h2>
  <pre id="SkMy" data-lang="groovy">groovy
pipeline {
    agent any
    
    environment {
        DOCKER_IMAGE = &quot;myapp&quot;
        DOCKER_TAG = &quot;${BUILD_NUMBER}&quot;
        DOCKER_REGISTRY = &quot;ghcr.io&quot;
    }
    
    stages {
        stage(&#x27;Checkout&#x27;) {
            steps {
                checkout scm
            }
        }
        
        stage(&#x27;Install Dependencies&#x27;) {
            steps {
                sh &#x27;npm install&#x27;
            }
        }
        
        stage(&#x27;Lint&#x27;) {
            steps {
                sh &#x27;npm run lint&#x27;
            }
        }
        
        stage(&#x27;Test&#x27;) {
            steps {
                sh &#x27;npm test&#x27;
            }
            post {
                always {
                    publishTestResults testResultsPattern: &#x27;test-results.xml&#x27;
                }
            }
        }
        
        stage(&#x27;Security Scan&#x27;) {
            steps {
                sh &#x27;npm audit&#x27;
            }
        }
        
        stage(&#x27;Build Docker Image&#x27;) {
            steps {
                script {
                    def image = docker.build(&quot;${DOCKER_IMAGE}:${DOCKER_TAG}&quot;)
                    docker.withRegistry(&quot;https://${DOCKER_REGISTRY}&quot;, &#x27;github-registry-credentials&#x27;) {
                        image.push()
                        image.push(&quot;latest&quot;)
                    }
                }
            }
        }
        
        stage(&#x27;Deploy to Staging&#x27;) {
            steps {
                sh &quot;&quot;&quot;
                    docker run -d --name staging-${BUILD_NUMBER} \
                    -p 3001:3000 \
                    ${DOCKER_IMAGE}:${DOCKER_TAG}
                &quot;&quot;&quot;
            }
        }
        
        stage(&#x27;Integration Tests&#x27;) {
            steps {
                sh &#x27;npm run test:integration&#x27;
            }
        }
        
        stage(&#x27;Deploy to Production&#x27;) {
            when {
                branch &#x27;main&#x27;
            }
            steps {
                script {
                    input message: &#x27;Deploy to production?&#x27;, ok: &#x27;Deploy&#x27;
                }
                sh &quot;&quot;&quot;
                    kubectl set image deployment/myapp-deployment \
                    myapp=${DOCKER_IMAGE}:${DOCKER_TAG}
                &quot;&quot;&quot;
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        success {
            slackSend channel: &#x27;#deployments&#x27;, 
                      message: &quot;✅ Pipeline succeeded for ${env.JOB_NAME} #${env.BUILD_NUMBER}&quot;
        }
        failure {
            slackSend channel: &#x27;#deployments&#x27;,
                      message: &quot;❌ Pipeline failed for ${env.JOB_NAME} #${env.BUILD_NUMBER}&quot;
        }
    }
}
</pre>
  <hr />
  <h2 id="6woJ">📊 Мониторинг пайплайнов</h2>
  <h2 id="jcyk">Метрики CI/CD</h2>
  <p id="On2j"><strong>Lead Time</strong> — время от коммита до развертывания<br /> <strong>Deployment Frequency</strong> — частота развертываний<br /> <strong>Mean Time to Recovery</strong> — время восстановления после сбоя<br /> <strong>Change Failure Rate</strong> — процент неудачных изменений</p>
  <h2 id="NHt8">Уведомления и алерты</h2>
  <pre id="QgBG"># Добавление в GitHub Actions
- name: Notify Slack on failure
  if: failure()
  uses: 8398a7/action-slack@v3
  with:
    status: failure
    webhook_url: ${{ secrets.SLACK_WEBHOOK }}
    message: |
      ❌ Build failed!
      Repository: ${{ github.repository }}
      Branch: ${{ github.ref }}
      Commit: ${{ github.sha }}
</pre>
  <hr />
  <h2 id="qMCo">📖 Полезные ресурсы</h2>
  <ul id="OJ0x">
    <li id="xxV1"><strong><a href="https://docs.github.com/en/actions" target="_blank">GitHub Actions Documentation</a></strong> — официальная документация</li>
    <li id="1owC"><strong><a href="https://www.jenkins.io/doc/book/" target="_blank">Jenkins Handbook</a></strong> — полное руководство по Jenkins</li>
    <li id="iD4J"><strong><a href="https://docs.gitlab.com/ee/ci/" target="_blank">GitLab CI/CD</a></strong> — альтернативная платформа CI/CD</li>
    <li id="oWxk"><strong>YouTube:</strong> &quot;CI/CD с нуля до продакшена&quot; — практический курс</li>
    <li id="WAe5"><strong><a href="https://itrevolution.com/the-devops-handbook/" target="_blank">The DevOps Handbook</a></strong> — книга о DevOps практиках</li>
  </ul>
  <hr />
  <h2 id="EtDq">✅ Чек-лист модуля</h2>
  <ul id="Qlbz">
    <li id="BRgI">Понимаю концепции CI/CD и их преимущества</li>
    <li id="qPVF">Создал простое приложение с тестами</li>
    <li id="4ZwK">Настроил GitHub Actions workflow</li>
    <li id="AoE8">Реализовал автоматическую сборку Docker образов</li>
    <li id="DYlP">Настроил автоматическое развертывание</li>
    <li id="Hb8O">Изучил стратегии развертывания (Blue-Green, Canary)</li>
    <li id="brN0">Добавил проверки безопасности в пайплайн</li>
    <li id="We4n">Настроил уведомления о статусе сборки</li>
    <li id="2sol">Понимаю метрики эффективности CI/CD</li>
    <li id="GZOG">Умею отлаживать проблемы в пайплайнах</li>
  </ul>
  <hr />
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <h2 id="5rAV">🚀 Что дальше?</h2>
    <p id="sdkN">После освоения CI/CD переходите к <strong>Модулю 8: Infrastructure as Code</strong> для изучения управления инфраструктурой через код и автоматизации развертывания сред.</p>
  </section>

]]></content:encoded></item></channel></rss>