DevOps для сайта: выкладка, откат и мониторинг без авралов | Hated.Ru
Размер шрифта
Цвет фона и шрифта
Изображения
Озвучивание текста
Обычная версия сайта
HATED.RU разработка и SEO продвижение сайтов, контекстная реклама в РФ
Разработка сайтов, контекстная реклама и привлечение трафика
SEO продвижение
+7 985 220-54-74
+7 985 220-54-74 mts.svg
+7 495 220-54-74 mts.svg
E-mail
info@hated.ru
Адрес
г. Тула, Московское шоссе , д.2в, офис № 3
Рязань, 2-й Школьный переулок, 1
Москва, МКАД 60й километр 4а
Режим работы
Ежедневно: с 9:00 до 20:00
Услуги
  • Разработка сайтов на CMS "1с Битрикс"
    • Информационный сайт на CMS 1с Битрикс
    • Лендинг на CMS 1с Битрикс
    • Доработка и модернизация сайта на 1С-Битрикс
    • Интернет-магазин на CMS 1с Битрикс
    • Корпоративный сайт на 1С‑Битрикс под ключ
    • Разработка маркетплейсов и агрегаторов на 1С-Битрикс
    • Разработка промо-сайта на 1С-Битрикс | Создание промо-сайтов под ключ
    • Сайт-визитка на CMS 1С Битрикс
    • Техническая поддержка сайта на 1С-Битрикс
  • Контекстная реклама
    • Настройка контекстной рекламы «Уверенный старт»
    • Настройка контекстной рекламы Яндекс Директ «Быстрый старт»
    • Сопровождение контекстной рекламы "+"
    • Сопровождение и ведение контекстной рекламы
    • Email-маркетинг и Email-Рассылка
    • Аудит Яндекс.Директ | Проверка контекстной рекламы
    • Медийная реклама
    • Мобильная реклама
    • Продвижение в Яндекс ПромоСтраницах
  • SEO продвижение сайтов
    • SEO‑аудит сайта: комплексный анализ для эффективного продвижения
    • Первичная SEO‑оптимизация сайта — залог эффективного продвижения!
    • SEO‑продвижение интернет‑магазина: рост продаж через поисковый трафик
    • SEO для «1С‑Битрикс»: выведем сайт в топ поиска за 3–6 месяцев
    • SEO-продвижение сайта в Google
    • Комплексное GEO-продвижение сайта в AI
    • Продвижение сайтов в ТОП Яндекс
    • Продвижение сайтов по трафику в России | Рост целевого SEO‑трафика
  • Дизайн "Брендинг"
    • Графический дизайн
    • Дизайн рекламных кампаний
    • Разработка брендбука — заказать создание брендбука для компании
    • Разработка логотипа — логотип, который работает на узнаваемость и продажи
    • Создание презентаций и каталогов на заказ | Дизайн, тексты, вёрстка
  • Разработка сайтов на CMS "Wordpress"
    • Доработка и модернизация сайтов на Wordpress
    • Разработка Landing page на WordPress
    • Разработка интернет-магазина на Wordpress
    • Разработка сайта-портфолио на Wordpress
    • Сайт-визитка на WordPress
  • Разработка сайтов на конструкторе "Tilda"
    • Одностраничный сайт на тильде
    • Доработка сайта на Tilda
    • Интернет-магазин на Тильде
    • Корпоративный сайт на Тильде
    • Промо-сайт на Тильде
    • Сайт-каталог на Тильде
    • Сайт-портфолио на Тильде
    • Создание сайта-визитки на Тильде
    • Сопровождение и техническая поддержка сайтов на Тильде
  • Создание быстрого сайта без CMS
    • Разработка базового лендинга без CMS в
  • DevOps
    • Внедрение CI/CD — автоматизация разработки под ключ
    • Настройка Яндекс Клауд и VK Cloud
    • Подбор и настройка инфраструктуры
  • SMM
    • Комплексное ведение социальных сетей под ключ
    • Контент-план для социальных сетей на месяц
    • Реклама у блогеров под ключ: подбор, ТЗ и сопровождение
    • Создание контента для социальных сетей
  • UI/UX разработка \ дизайн
    • Backend разработка на заказ
    • Frontend разработка сайтов и приложений
    • Разработка мобильных приложений для iOS и Android
  • Разработка под Telegram
    • Разработка Telegram Mini Apps
    • Разработка чат-ботов для Telegram
  • Таргетированная реклама
    • Авитолог — продвижение на Авито
    • Таргетированная реклама в Telegram \ Телеграмм ADS
    • Таргетированная реклама в VK (ВКонтакте)
    • Таргетированная реклама в Одноклассниках под ключ
Наши работы
  • Разработка сайтов на Drupal
  • Брендинг и айдентика
  • Портфолио: сайты на 1С‑Битрикс
  • Портфолио: сайты на CMS Tilda
  • Разработка сайта на "Чистом коде"
Магазин
  • Битрикс 24
  • Готовые шаблоны для cms 1c Bitrix
Компания
  • О компании
  • Лицензии
  • Реквизиты
  • Сотрудники
  • Отзывы
Статьи
Контакты
  • Отзывы
  • Лицензии
  • Новости
  • Вопрос-ответ
  • ...
    ...
    +7 985 220-54-74
    +7 985 220-54-74 mts.svg
    +7 495 220-54-74 mts.svg
    E-mail
    info@hated.ru
    Адрес
    г. Тула, Московское шоссе , д.2в, офис № 3
    Рязань, 2-й Школьный переулок, 1
    Москва, МКАД 60й километр 4а
    Режим работы
    Ежедневно: с 9:00 до 20:00
    HATED.RU разработка и SEO продвижение сайтов, контекстная реклама в РФ
    Разработка сайтов, контекстная реклама и привлечение трафика
    SEO продвижение
    Услуги
    Наши работы
    Магазин
    Компания
    Статьи
    Контакты
      HATED.RU разработка и SEO продвижение сайтов, контекстная реклама в РФ
      Услуги
      Наши работы
      Магазин
      Компания
      Статьи
      Контакты
        +7 985 220-54-74 mts.svg
        +7 495 220-54-74 mts.svg
        E-mail
        info@hated.ru
        Адрес
        г. Тула, Московское шоссе , д.2в, офис № 3
        Рязань, 2-й Школьный переулок, 1
        Москва, МКАД 60й километр 4а
        Режим работы
        Ежедневно: с 9:00 до 20:00
        HATED.RU разработка и SEO продвижение сайтов, контекстная реклама в РФ
        Телефоны
        +7 985 220-54-74 mts.svg
        +7 495 220-54-74 mts.svg
        E-mail
        info@hated.ru
        Адрес
        г. Тула, Московское шоссе , д.2в, офис № 3
        Рязань, 2-й Школьный переулок, 1
        Москва, МКАД 60й километр 4а
        Режим работы
        Ежедневно: с 9:00 до 20:00
        HATED.RU разработка и SEO продвижение сайтов, контекстная реклама в РФ
        • Услуги
          • Услуги
          • Разработка сайтов на CMS "1с Битрикс"
            • Разработка сайтов на CMS "1с Битрикс"
            • Информационный сайт на CMS 1с Битрикс
            • Лендинг на CMS 1с Битрикс
            • Доработка и модернизация сайта на 1С-Битрикс
            • Интернет-магазин на CMS 1с Битрикс
            • Корпоративный сайт на 1С‑Битрикс под ключ
            • Разработка маркетплейсов и агрегаторов на 1С-Битрикс
            • Разработка промо-сайта на 1С-Битрикс | Создание промо-сайтов под ключ
            • Сайт-визитка на CMS 1С Битрикс
            • Техническая поддержка сайта на 1С-Битрикс
          • Контекстная реклама
            • Контекстная реклама
            • Настройка контекстной рекламы «Уверенный старт»
            • Настройка контекстной рекламы Яндекс Директ «Быстрый старт»
            • Сопровождение контекстной рекламы "+"
            • Сопровождение и ведение контекстной рекламы
            • Email-маркетинг и Email-Рассылка
            • Аудит Яндекс.Директ | Проверка контекстной рекламы
            • Медийная реклама
            • Мобильная реклама
            • Продвижение в Яндекс ПромоСтраницах
          • SEO продвижение сайтов
            • SEO продвижение сайтов
            • SEO‑аудит сайта: комплексный анализ для эффективного продвижения
            • Первичная SEO‑оптимизация сайта — залог эффективного продвижения!
            • SEO‑продвижение интернет‑магазина: рост продаж через поисковый трафик
            • SEO для «1С‑Битрикс»: выведем сайт в топ поиска за 3–6 месяцев
            • SEO-продвижение сайта в Google
            • Комплексное GEO-продвижение сайта в AI
            • Продвижение сайтов в ТОП Яндекс
            • Продвижение сайтов по трафику в России | Рост целевого SEO‑трафика
          • Дизайн "Брендинг"
            • Дизайн "Брендинг"
            • Графический дизайн
            • Дизайн рекламных кампаний
            • Разработка брендбука — заказать создание брендбука для компании
            • Разработка логотипа — логотип, который работает на узнаваемость и продажи
            • Создание презентаций и каталогов на заказ | Дизайн, тексты, вёрстка
          • Разработка сайтов на CMS "Wordpress"
            • Разработка сайтов на CMS "Wordpress"
            • Доработка и модернизация сайтов на Wordpress
            • Разработка Landing page на WordPress
            • Разработка интернет-магазина на Wordpress
            • Разработка сайта-портфолио на Wordpress
            • Сайт-визитка на WordPress
          • Разработка сайтов на конструкторе "Tilda"
            • Разработка сайтов на конструкторе "Tilda"
            • Одностраничный сайт на тильде
            • Доработка сайта на Tilda
            • Интернет-магазин на Тильде
            • Корпоративный сайт на Тильде
            • Промо-сайт на Тильде
            • Сайт-каталог на Тильде
            • Сайт-портфолио на Тильде
            • Создание сайта-визитки на Тильде
            • Сопровождение и техническая поддержка сайтов на Тильде
          • Создание быстрого сайта без CMS
            • Создание быстрого сайта без CMS
            • Разработка базового лендинга без CMS в
          • DevOps
            • DevOps
            • Внедрение CI/CD — автоматизация разработки под ключ
            • Настройка Яндекс Клауд и VK Cloud
            • Подбор и настройка инфраструктуры
          • SMM
            • SMM
            • Комплексное ведение социальных сетей под ключ
            • Контент-план для социальных сетей на месяц
            • Реклама у блогеров под ключ: подбор, ТЗ и сопровождение
            • Создание контента для социальных сетей
          • UI/UX разработка \ дизайн
            • UI/UX разработка \ дизайн
            • Backend разработка на заказ
            • Frontend разработка сайтов и приложений
            • Разработка мобильных приложений для iOS и Android
          • Разработка под Telegram
            • Разработка под Telegram
            • Разработка Telegram Mini Apps
            • Разработка чат-ботов для Telegram
          • Таргетированная реклама
            • Таргетированная реклама
            • Авитолог — продвижение на Авито
            • Таргетированная реклама в Telegram \ Телеграмм ADS
            • Таргетированная реклама в VK (ВКонтакте)
            • Таргетированная реклама в Одноклассниках под ключ
        • Наши работы
          • Наши работы
          • Разработка сайтов на Drupal
          • Брендинг и айдентика
          • Портфолио: сайты на 1С‑Битрикс
          • Портфолио: сайты на CMS Tilda
          • Разработка сайта на "Чистом коде"
        • Магазин
          • Магазин
          • Битрикс 24
          • Готовые шаблоны для cms 1c Bitrix
        • Компания
          • Компания
          • О компании
          • Лицензии
          • Реквизиты
          • Сотрудники
          • Отзывы
        • Статьи
        • Контакты
        • +7 985 220-54-74
          • Телефоны
          • +7 985 220-54-74
            mts.svg
          • +7 495 220-54-74
            mts.svg
        • г. Тула, Московское шоссе , д.2в, офис № 3
          Рязань, 2-й Школьный переулок, 1
          Москва, МКАД 60й километр 4а
        • info@hated.ru
        • Ежедневно: с 9:00 до 20:00

        DevOps для владельца сайта: шесть ступеней от Git до мониторинга, готовый код и чек-листы

        Создание сайтов и SEO продвижение сайтов любой тематики в и по всей России. Индивидуальная разработка сайтов и комплексный подход к SEO продвижению.
        —
        Статьи о рекламе в интернете, разработке сайтов и их продвижению
        —
        Статьи на тему: Веб-дизайн, разработка сайтов
        Блог о 1С Битрикс
        Статьи о SEO продвижении сайтов
        Статьи о контекстной рекламе - Блог Hated.ru
        Полезное
        —DevOps для владельца сайта: шесть ступеней от Git до мониторинга, готовый код и чек-листы
        Нужен ли сайту Docker и Kubernetes: таблица решения | Hated.Ru
        Статьи о разработки сайтов
        16 октября 2026
        С чего начать DevOps, если сайт на Битриксе, WordPress или Python?
         Дени Булатов собрал шесть ступеней с проверкой для каждой, рабочие конфигурации, скрипт для метрик DORA и три шаблона: ТЗ для подрядчика, регламент отката и разбор сбоев. Разделы читаются по отдельности, а чек-листы пригодятся сразу.
        DevOps для сайтов
        DevOps для сайта: как выкладывать обновления спокойно и откатываться за секунды

        Шесть ступеней от Git до мониторинга, рабочий код, замеры выкладки на 4000 файлов и чек-листы. Для владельцев сайтов, которым нужно понимать, что просить у подрядчика, и для разработчиков, которым нужны готовые файлы.

        Обновлено 17 октября 2026 Чтение около 55 минут 27 блоков кода, проверены Замеры на 4000 файлов
        1. КодGit
        2. ПроверкиCI
        3. Сборкаобраз или архив
        4. Релизвыкладка и откат
        5. Наблюдениеметрики и алерты
        Д

        Дени БулатовРазработчик Hated.Ru: frontend на React, backend на Python, PostgreSQL

        Пятница, вечер. Файлы нового релиза заливают на сервер по FTP, и пока идёт копирование, часть страниц открывается без стилей. В это время кто-то из клиентов оформляет заказ. Через час выясняется, что форма заявки в новой версии не отправляется, и старые файлы уже перезаписаны.

        DevOps убирает такие вечера. Это набор практик, при которых код, проверки, выкладка и наблюдение за сайтом работают как один процесс. Изменение проходит автоматические проверки, выкладывается одной командой и при проблеме возвращается к прошлой версии за секунды.

        В статье шесть ступеней. Каждую можно внедрять отдельно и оценивать по результату. Конфигурации прошли линтеры и проверку по схемам, скрипт выкладки запущен, замеры сделаны на тестовом стенде. Рядом с каждым примером я указал, что именно проверялось.

        Владельцу сайта в конце пригодятся шаблон технического задания и регламент отката. С ними разговор с подрядчиком идёт по делу.

        0с

        Сайт недоступен при замене каталога на 4000 файлов

        0с

        Часть запросов получает смесь старых и новых файлов при копировании поверх

        0

        Ошибок при переключении ссылки на готовый релиз

        0

        Метрик DORA, по которым видно, стало ли лучше

        Коротко

        Что важно запомнить до чтения

        • DevOps объединяет разработку и эксплуатацию в один процесс с общими целями. Начинать нужно с порядка в Git и автоматических проверок. Kubernetes подождёт.
        • Три первые ступени, Git, автопроверки и выкладка с откатом, закрывают большую часть проблем сайта на 1С-Битрикс, WordPress или Python.
        • Способ выкладки меняет то, что видят посетители. В замере переключение ссылки не дало ни одной ошибки. Замена каталога держала сайт недоступным больше секунды.
        • Контейнеры, Terraform и Kubernetes нужны не каждому. В статье есть таблица, по которой можно решить, пора ли.
        • Результат измеряют пятью метриками DORA. Скрипт для их подсчёта лежит в разделе про мониторинг.
        • Terraform и Docker Hub в России требуют зеркал. Рабочие настройки и даты ограничений приведены с источниками.

        Содержание

        1. Что такое DevOps и зачем он сайту
        2. Релиз без процесса и с процессом
        3. Шесть ступеней внедрения
        4. Ступень 1: Git и порядок в репозитории
        5. Ступень 2: автоматические проверки (CI)
        6. Ступень 3: выкладка без простоя и откат
        7. Ступень 4: окружения и контейнеры
        8. Ступень 5: инфраструктура кодом
        9. Kubernetes: когда он нужен сайту
        10. Ступень 6: мониторинг и оповещения
        11. Безопасность внутри процесса
        12. Команда и договорённости
        13. Что внедрять вашему типу сайта
        14. План на 90 дней и пакеты Hated.Ru
        15. Чек-листы: до, после и при приёмке
        16. Шаблоны: ТЗ, регламент отката, разбор сбоя
        17. Вопросы и ответы
        18. Что проверено в статье
        19. Источники

        01Основы

        Что такое DevOps и зачем он сайту

        Слово собрано из двух частей: development, разработка, и operations, эксплуатация. Разработчики пишут код и хотят выпускать изменения быстро. Те, кто запускает сайт на сервере, отвечают за стабильность и хотят менять как можно меньше. Пока эти задачи ведут разные люди с разными показателями успеха, каждый релиз превращается в переговоры. DevOps сводит их в один процесс с общими целями.

        Термин появился в 2009 году. На конференции O'Reilly Velocity Джон Оллспоу и Пол Хаммонд рассказали, как Flickr выкладывает больше десяти релизов в день. Тогда же бельгийский специалист Патрик Дебуа организовал в Генте первую конференцию DevOpsDays. С тех пор подход прошёл путь от идеи энтузиастов до обычной практики разработки.

        1

        Общая ответственность

        За работающий сайт отвечают и тот, кто писал код, и тот, кто его запускает. Граница «мы сделали, дальше не наше» исчезает.

        2

        Автоматизация

        Сборка, проверки, выкладка и настройка серверов выполняются скриптами. Человек принимает решения, а рутину делает конвейер.

        3

        Обратная связь по метрикам

        Скорость выкладок, число сбоев и время восстановления измеряются. По цифрам видно, стало ли лучше.

        Какие проблемы сайта решает DevOps

        Что происходитЧто делает DevOpsСтупень
        Файлы заливают вручную по FTPВыкладка запускается из репозитория одной командой1 и 3
        Правки вносят на боевом сервере, потом никто не помнит, что менялосьВесь код в Git, у каждого изменения есть автор и дата1
        Ошибки находят клиентыАвтоматические проверки срабатывают до выкладки2
        Во время обновления страницы отдаются с ошибками или без стилейНовый релиз ставится рядом, ссылка переключается целиком3
        «У меня работает, на сервере нет»Разработка, тест и бой собираются из одних и тех же файлов4
        Сервер настроен руками, пересоздать его страшноНастройки лежат в репозитории и применяются скриптом5
        О падении сайта узнают от клиентовОповещения приходят в Telegram за минуты6

        Кто такой DevOps-инженер

        Это специалист, который отвечает за путь кода от репозитория до работающего сайта. Он настраивает конвейер выкладки, окружения и мониторинг, следит за безопасностью процесса и помогает команде не повторять одни и те же ручные операции. Ему нужны Linux, Git, скрипты на Bash или Python, понимание сетей, контейнеров и облачных платформ.

        Не менее важна работа с людьми. С разработчиками он говорит о коде и тестах, с владельцем сайта о рисках, сроках и деньгах. В небольшой студии эти обязанности часто делят между разработчиком и системным администратором, и это нормально: ступени из статьи можно внедрять по очереди без отдельной штатной единицы.

        02Сравнение

        Как выглядит релиз без процесса и с процессом

        Проще всего увидеть разницу на одном и том же изменении. Допустим, на сайте нужно поменять форму заявки. Ниже два сценария для одного и того же сайта: слева привычная пятница, справа спокойный вторник с конвейером.

        1. Правки на своём компьютереВерсия «финал_2» лежит в папке и в чате. Какая из них актуальная, знает автор.
        2. Копирование на серверФайлы заливают по FTP или через панель хостинга прямо поверх живого сайта.
        3. Смесь версийПока идёт копирование, часть посетителей получает новую страницу со старыми скриптами или наоборот.
        4. Проверка глазамиОткрыли главную, вроде работает. Форму заявки никто не отправлял.
        5. Сигнал от менеджераЧерез час выясняется, что заявки не приходят. Каждая минута стоит обращений.
        6. Откатываться нечемСтарые файлы перезаписаны. Прошлую версию ищут в переписке или на чужом компьютере.
        7. Нет историиЧто именно изменилось в этом релизе, знает тот, кто его заливал.
        1. Ветка и проверка коллегойИзменение живёт в отдельной ветке Git. Коллега смотрит его в pull request и оставляет замечания.
        2. Автоматические проверкиПосле отправки запускаются линтер и тесты. Красный статус не даёт слить изменение в основную ветку.
        3. Тестовая средаКонвейер собирает релиз и ставит его на стенд, который повторяет боевой сайт.
        4. ПриёмкаЗаказчик или менеджер отправляет тестовую заявку на стенде и подтверждает изменение.
        5. Выкладка одной командойНовый релиз ставится рядом со старым, затем ссылка на активную версию переключается целиком.
        6. НаблюдениеМониторинг следит за ошибками и скоростью ответа в первые минуты после выкладки.
        7. Откат и историяЕсли заявки перестали приходить, одна команда возвращает прошлый релиз. Git хранит, кто и что менял.

        Самопроверка: пора ли вашему сайту на DevOps

        Отметьте пункты, которые узнаёте в своей работе. Результат подскажет, с какой ступени начать. Отметки сохраняются в вашем браузере, на сервер ничего не уходит.

        Признаки, что процесс пора менять

        Отмечайте то, что есть у вас сегодня.

        0 из 10
        Порядок уже есть

        Базовая гигиена на месте. Проверьте ступени 4-6: окружения, инфраструктуру кодом и мониторинг. Чаще всего именно там остаются слабые места.

        Начните с первых трёх ступеней

        Git, автопроверки и выкладка с откатом закроют большую часть ваших рисков. Каждая ступень даёт результат сама по себе, их можно внедрять по очереди.

        Нужен план на ближайший месяц

        Начните со ступени 1 и сразу поставьте мониторинг доступности из ступени 6: так вы узнаете о падении раньше клиентов. Дальше идут автопроверки и выкладка с откатом. Чек-лист и шаблон технического задания в конце статьи помогут обсудить план с подрядчиком.

        03Карта

        Шесть ступеней от Git до мониторинга

        DevOps внедряют по очереди. Каждая ступень даёт результат сама по себе, и сверху её можно надстраивать следующей. Под каждой ступенью указано, как проверить, что она закрыта. Если проверка не проходит, переходить дальше рано.

        1. 1

          Git и порядок в репозитории

          Весь код, настройки и миграции лежат в Git. Прямые правки на сервере прекращаются. У каждого изменения есть автор, дата и причина.

          Проверка: на сервере нет файлов, которых нет в репозитории
          Разобрать ступень
        2. 2

          Автоматические проверки (CI)

          Каждое изменение проходит линтер, тесты и сборку на отдельной машине. Красный статус блокирует слияние в основную ветку.

          Проверка: специально сломайте тест, слияние должно заблокироваться
          Разобрать ступень
        3. 3

          Выкладка с откатом (CD)

          Релиз ставится в отдельный каталог, ссылка на активную версию переключается целиком, прошлые версии лежат рядом.

          Проверка: откат на прошлый релиз занимает одну команду и меньше минуты
          Разобрать ступень
        4. 4

          Окружения и контейнеры

          Разработка, тест и бой собираются из одних файлов. Новый разработчик поднимает проект одной командой, а различия окружений видны в конфигурации.

          Проверка: чистая машина запускает проект командой docker compose up
          Разобрать ступень
        5. 5

          Инфраструктура кодом

          Сервер, сеть и настройки описаны файлами. Terraform создаёт ресурсы в облаке, Ansible настраивает систему внутри них.

          Проверка: тестовую среду можно удалить и поднять заново одним запуском
          Разобрать ступень
        6. 6

          Мониторинг и оповещения

          Недоступность, рост ошибок и медленные ответы приходят в Telegram раньше, чем звонит клиент. Состояние сайта видно на одном экране.

          Проверка: остановите сервис на тестовой среде, оповещение придёт через 2-3 минуты
          Разобрать ступень
        Где Kubernetes

        Его нет в списке ступеней намеренно. Kubernetes подходит проектам с несколькими сервисами и переменной нагрузкой, а лендингу и корпоративному сайту он добавит работы больше, чем пользы. Критерии выбора и готовый манифест собраны в отдельном разделе.

        04Ступень 1

        Git и порядок в репозитории

        Git хранит историю каждого изменения и позволяет вернуться к любому состоянию проекта. Для сайта правило одно: исходный код живёт в репозитории, а сервер получает файлы оттуда. Файлы на сервере руками не правят, даже если очень надо срочно.

        Договорённости для команды

        1. Ветка main равна боевому сайту. В неё не пишут напрямую. Работа идёт в коротких ветках: одна задача, одна ветка, закрывается за несколько дней.
        2. Слияние только через pull request. Коллега смотрит изменение, автоматические проверки зелёные, и только после этого кнопка слияния становится доступной. В настройках репозитория это включается защитой ветки.
        3. Коммит описывает причину. «Исправил форму заявки: поле телефона не пропускало номера с плюсом» читается через год. «Правки» не читается уже на следующий день.
        4. Секреты не попадают в репозиторий. Пароли, ключи API и файлы окружения лежат в хранилище секретов. Если пароль всё же попал в историю, его считают скомпрометированным и меняют. Удаление коммита пароль не возвращает.
        5. Изменения базы данных идут миграциями. Инфоблоки, свойства и формы руками в админке не заводят: на разных средах получится разный результат.

        Что смотрит ревьюер

        Содержание

        • Код делает то, что описано в задаче.
        • Нет очевидных ошибок и забытых отладочных строк.
        • Крайние случаи обработаны: пустое поле, длинный текст, чужой регион.

        Читаемость и безопасность

        • Названия понятны без пояснений автора.
        • Пароли и адреса боевых серверов в код не вшиты.
        • Ввод пользователя экранируется перед выводом и перед запросом в базу.

        Хороший комментарий объясняет причину и предлагает вариант. «Здесь запрос в цикле: на странице с 200 товарами это 200 обращений к базе, можно выбрать все товары одним запросом» полезнее, чем «плохо». Отметить удачное решение тоже стоит: это задаёт тон работы команды.

        Что не хранить в репозитории

        Для проекта на 1С-Битрикс разумно держать в репозитории только свой код, обычно каталог /local/. Ядро ставится из дистрибутива, загрузки лежат на сервере, а в настройках веб-сервера у каждой площадки свои значения. Список ниже собран по рекомендациям Habr (статья 2021 года о правильной настройке площадки Битрикс) и дополнен защитой файлов окружения. Под свой проект его стоит перечитать.

        .gitignoreТекст
        # Ядро 1С-Битрикс ставится из дистрибутива, в репозитории его нет
        /bitrix/
        # Загрузки посетителей и менеджеров живут на сервере и в бэкапе
        /upload/
        # Зависимости Composer
        /local/vendor/
        # Настройки веб-сервера и поисковые файлы на каждой площадке свои
        /.htaccess
        /web.config
        /robots.txt
        /*.xml
        # Мусор редакторов и логи
        *.swp
        *.log
        # Секреты и локальные настройки
        .env
        .env.*
        !.env.example
        

        Список я проверил в тестовом репозитории: создал файлы по этим путям и спросил Git командой git check-ignore, что он о них думает. Ядро Битрикс, каталог upload, зависимости Composer, robots.txt, sitemap.xml, .htaccess, логи и .env игнорируются. Шаблоны из local/templates, файл local/php_interface.php и пример .env.example остаются под контролем Git.

        Для проекта на Python и React набор другой.

        .gitignoreТекст
        # Python
        .venv/
        __pycache__/
        *.pyc
        # Node и сборка фронтенда
        node_modules/
        dist/
        # Секреты и локальные настройки
        .env
        .env.*
        !.env.example
        # Мусор редакторов и логи
        .idea/
        .vscode/
        *.log
        

        Та же проверка: виртуальное окружение, node_modules, каталог сборки dist, кэш Python, .env и логи игнорируются, исходники и .env.example остаются.

        Миграции для 1С-Битрикс

        Изменения структуры базы, например новый инфоблок или свойство, переносят между тестовой и боевой площадкой миграциями. В экосистеме Битрикс для этого используют модуль Sprint Migration из Маркетплейса. Для Python-проектов то же делают Alembic или миграции Django. Файлы миграций лежат в репозитории рядом с кодом.

        Как понять, что ступень закрыта
        • Любой член команды может ответить, кто, когда и зачем изменил любую строку кода.
        • Сравнение каталога на сервере с релизом из репозитория не находит отличий: ручных правок нет.
        • Прямое слияние в main запрещено настройками репозитория.
        • Секретов в репозитории нет. Проверьте историю поиском по словам password, secret, token.

        05Ступень 2

        Автоматические проверки: CI

        CI, непрерывная интеграция, означает две вещи. Разработчики отправляют изменения в общий репозиторий часто, почти каждый день. А сервер при каждой отправке сам запускает проверки и сборку. Чем чаще интеграция, тем меньше конфликтов при слиянии и тем раньше видна поломка: если тест краснеет через пять минут после коммита, автор ещё помнит, что менял.

        CI, непрерывная доставка и непрерывное развёртывание

        CI

        Непрерывная интеграция

        Изменения регулярно попадают в общий репозиторий и автоматически проверяются: линтер, тесты, сборка.

        Delivery

        Непрерывная доставка

        После проверок релиз всегда готов к выкладке. Саму выкладку запускает человек одним нажатием.

        Deployment

        Непрерывное развёртывание

        Выкладка на боевой сайт идёт сама, если все проверки зелёные. Подходит командам с сильными тестами и быстрым откатом.

        Для сайтов с заказами и заявками я рекомендую начинать с непрерывной доставки. Тесты покрывают не всё, и последнее слово перед боем лучше оставить человеку, который видит ситуацию целиком: идёт ли сейчас акция, не пик ли нагрузки.

        Что проверяет конвейер сайта

        • Линтер и форматирование. Находит опечатки в именах переменных, забытые отладочные строки, нарушения стиля.
        • Юнит-тесты логики. Расчёт цены и скидки, проверка полей формы, фильтры каталога. Именно здесь ошибки стоят дороже всего.
        • Сборка. Для React это npm run build, для PHP на Composer установка зависимостей без dev-пакетов. Если сборка падает, до сервера релиз не доходит.
        • Проверка зависимостей. npm audit и pip-audit показывают библиотеки с известными уязвимостями.
        • Проверка после выкладки на тест. Главная отвечает кодом 200, форма заявки отправляется, API возвращает данные. Такой тест называют дымовым.

        Тесты производительности и интерфейса добавляют вторым этапом, когда базовый конвейер работает стабильно.

        Пример для GitHub Actions

        Конфигурация проверяет Python-проект: ставит зависимости, запускает линтер и тесты, а из основной ветки ещё и собирает образ с тегом по хешу коммита.

        .github/workflows/ci.ymlYAML
        name: ci
        
        on:
          push:
            branches: [main]
          pull_request:
        
        permissions:
          contents: read
        
        concurrency:
          group: ci-${{ github.ref }}
          cancel-in-progress: true
        
        jobs:
          test:
            runs-on: ubuntu-latest
            timeout-minutes: 15
            steps:
              - uses: actions/checkout@v6
              - name: Окружение
                run: |
                  python3 -m venv .venv
                  .venv/bin/pip install -r requirements.txt -r requirements-dev.txt
              - name: Линтер
                run: .venv/bin/ruff check .
              - name: Тесты
                run: .venv/bin/pytest -q
        
          image:
            needs: test
            if: github.ref == 'refs/heads/main'
            runs-on: ubuntu-latest
            timeout-minutes: 20
            env:
              IMAGE: registry.example.ru/site/app
            steps:
              - uses: actions/checkout@v6
              - name: Сборка образа с тегом по коммиту
                run: docker build -t "$IMAGE:${GITHUB_SHA::12}" .
              - name: Вход в реестр и публикация
                env:
                  REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
                  REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
                run: |
                  echo "$REGISTRY_TOKEN" | docker login registry.example.ru -u "$REGISTRY_USER" --password-stdin
                  docker push "$IMAGE:${GITHUB_SHA::12}"
        
        • permissions: contents: read даёт токену конвейера только чтение кода. Больше ему не нужно.
        • concurrency отменяет предыдущий запуск на той же ветке, когда приходит новый коммит.
        • timeout-minutes прерывает зависшую сборку, чтобы она не съедала лимит минут.
        • Пароль реестра берётся из secrets и передаётся в docker login через --password-stdin, чтобы он не оказался в списке аргументов процесса.
        • Тег образа состоит из первых двенадцати символов хеша коммита. По тегу видно, какой код внутри, а тег latest такой информации не несёт.
        Версии действий

        В примере стоит actions/checkout@v6: так читать проще. В боевом проекте GitHub советует закреплять сторонние действия полным хешем коммита вместо плавающего тега и оставлять номер версии комментарием. Тег может быть перенесён, хеш нет. Ещё одна причина обновиться: по уведомлению GitHub, действия на Node.js 20 устаревают: принудительный запуск на Node.js 24 планировался с 2 июня 2026 года, а удаление Node.js 20 с раннеров на 16 сентября 2026 года. Версии вроде checkout@v4 стоит обновить заранее.

        Пример для GitLab CI

        Три стадии: тесты, сборка образа, выкладка. На тестовую среду релиз уходит сам, на боевую по кнопке.

        .gitlab-ci.ymlYAML
        stages: [test, build, deploy]
        
        default:
          image: python:3.12-slim
          interruptible: true
        
        variables:
          PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
        
        cache:
          key:
            files: [requirements.txt]
          paths: [.cache/pip]
        
        test:
          stage: test
          script:
            - pip install -r requirements.txt -r requirements-dev.txt
            - ruff check .
            - pytest -q
          rules:
            - if: $CI_PIPELINE_SOURCE == "merge_request_event"
            - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
        
        build:
          stage: build
          image: docker:27
          services: [docker:27-dind]
          script:
            - echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
            - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
            - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
          rules:
            - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
        
        deploy_staging:
          stage: deploy
          environment: staging
          script:
            - ./deploy.sh staging "$CI_COMMIT_SHORT_SHA"
          rules:
            - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
        
        deploy_production:
          stage: deploy
          environment: production
          script:
            - ./deploy.sh production "$CI_COMMIT_SHORT_SHA"
          rules:
            - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
              when: manual
        
        • rules запускает тесты на merge request и на основной ветке, а образ собирает только на основной.
        • when: manual оставляет выкладку на боевой сайт человеку: это и есть непрерывная доставка.
        • Ключ кэша привязан к файлу requirements.txt: кэш пересоздаётся, когда меняются зависимости.
        • interruptible: true разрешает отменять устаревшие запуски, если автоотмена включена в настройках проекта.
        • Сервис docker:27-dind запускает Docker внутри задачи и требует раннера с привилегиями. Для общих раннеров это стоит уточнить заранее.
        Что проверено

        Оба файла прошли yamllint и проверку по официальным схемам vendor.github-workflows и vendor.gitlab-ci в утилите check-jsonschema. Запускать их на живом GitHub или GitLab я не мог: нужен репозиторий с раннерами и реестр. Перед боевым использованием файл стоит прогнать в вашем проекте.

        Какой инструмент выбрать

        ИнструментГде живёт конфигурацияГде выполняетсяКогда подходит
        GitHub ActionsФайлы в .github/workflowsРаннеры GitHub или ваши собственныеКод уже на GitHub, нужен быстрый старт
        GitLab CI/CDФайл .gitlab-ci.ymlРаннеры GitLab, облачного или развёрнутого у васХочется держать код и сборку в своём контуре, на своих серверах
        JenkinsЗадания и конвейеры в самом JenkinsВаш сервер, расширения через плагиныСложные нестандартные сценарии и полный контроль над сервером сборки

        Инструмент выбирают по тому, где живёт код. Если репозиторий на GitLab, конвейер проще держать там же. Отдельный Jenkins ради двух сайтов обычно избыточен: его самого нужно обновлять, защищать и мониторить.

        Мой ориентир по скорости

        Когда конвейер идёт дольше 10-15 минут, его перестают ждать и начинают обходить. Тогда проверки делят на быстрые, которые идут на каждый коммит, и долгие, которые запускаются раз в сутки или перед релизом.

        Как понять, что ступень закрыта

        • Слить изменение с красным статусом невозможно.
        • Время от коммита до результата проверок известно и укладывается в 10-15 минут.
        • Секреты лежат в хранилище конвейера. В файлах репозитория их нет.
        • Образ или архив релиза собирает конвейер на отдельной машине.

        06Ступень 3

        Выкладка без простоя и откат за секунды

        Вся ступень держится на одной идее: активный релиз это ссылка. Новая версия распаковывается рядом со старой, проверяется, и только потом ссылка переключается на неё. Старые релизы остаются на диске, поэтому откат превращается в такое же переключение ссылки в обратную сторону.

        Схема каталогов

        Структура каталогов на сервереТекст
        /var/www/site/
        ├── current -> releases/20261017-141502    # ссылка на активный релиз
        ├── releases/
        │   ├── 20261017-141502/                   # активный
        │   ├── 20261016-093011/                   # предыдущий, для отката
        │   └── 20261015-170544/
        └── shared/                                # живёт между релизами
            ├── upload/                            # загрузки посетителей
            └── .env                               # настройки и секреты сервера
        

        Конвейер распаковывает архив в новую папку releases/дата-время, подключает к ней общие каталоги из shared и в самом конце меняет ссылку current. Веб-сервер смотрит в current, поэтому видит либо старую версию целиком, либо новую целиком. Промежуточного состояния нет.

        Попробуйте переключение сами

        Это модель схемы. Кнопка «Выложить» создаёт новый релиз рядом и переносит ссылку на него, кнопка «Откатить» возвращает ссылку на ближайший релиз старше активного. Хранятся пять последних релизов, как в скрипте ниже.

        nginxсмотрит в current Сайт отдаёт релиз ...
        current

        Скрипт выкладки и отката

        Скрипт работает на обычном Linux-сервере без дополнительных программ. Он принимает архив с готовым релизом, ставит его рядом с предыдущими, подключает общие каталоги и атомарно переключает ссылку.

        deploy.shBash
        #!/usr/bin/env bash
        # Выкладка релиза по схеме releases/ + симлинк current. Откат за секунду.
        set -euo pipefail
        
        APP_DIR="${APP_DIR:-/var/www/site}"
        KEEP="${KEEP:-5}"
        
        usage() {
          echo "Использование: $0 deploy <архив.tar.gz> | rollback | list" >&2
          exit 2
        }
        
        deploy() {
          local archive="$1"
          local release
          release="$APP_DIR/releases/$(date +%Y%m%d-%H%M%S)"
        
          mkdir -p "$release" "$APP_DIR/shared"
          tar -xzf "$archive" -C "$release"
        
          # Общие каталоги живут вне релиза: загрузки и конфиги не теряются при выкладке
          ln -sfn "$APP_DIR/shared/upload" "$release/upload"
          ln -sfn "$APP_DIR/shared/.env" "$release/.env"
        
          # Атомарное переключение: новый симлинк создаётся рядом и переименовывается
          ln -sfn "$release" "$APP_DIR/current.new"
          mv -Tf "$APP_DIR/current.new" "$APP_DIR/current"
        
          # Хранить только последние KEEP релизов, текущий не трогаем
          find "$APP_DIR/releases" -mindepth 1 -maxdepth 1 -type d | sort -r \
            | tail -n +"$((KEEP + 1))" | awk -v cur="$release" '$0 != cur' | xargs -r rm -rf
          echo "Выложен релиз: $release"
        }
        
        rollback() {
          local current previous
          current="$(readlink -f "$APP_DIR/current")"
          # Имена релизов это даты, поэтому сортировка по имени идёт по времени.
          # Берём ближайший релиз старше текущего: повторный откат идёт ещё глубже.
          previous="$(find "$APP_DIR/releases" -mindepth 1 -maxdepth 1 -type d | sort -r \
            | awk -v cur="$current" '$0 < cur { print; exit }')"
          if [ -z "$previous" ]; then
            echo "Предыдущего релиза нет" >&2
            exit 1
          fi
          ln -sfn "$previous" "$APP_DIR/current.new"
          mv -Tf "$APP_DIR/current.new" "$APP_DIR/current"
          echo "Откат на: $previous"
        }
        
        case "${1:-}" in
          deploy)   [ $# -eq 2 ] || usage; deploy "$2" ;;
          rollback) rollback ;;
          list)     find "$APP_DIR/releases" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort -r ;;
          *)        usage ;;
        esac
        
        • set -euo pipefail останавливает скрипт на первой ошибке. Выкладка, которая молча дошла до середины, хуже остановки с понятным сообщением.
        • Ссылка создаётся под именем current.new и затем переименовывается в current командой mv -Tf. Переименование выполняется одним системным вызовом, поэтому посетитель не застаёт момент, когда ссылки нет.
        • Имена релизов состоят из даты и времени. Сортировка по имени совпадает с порядком по времени, и откат находит ближайший релиз старше текущего. Повторный откат уходит ещё глубже и к сломанной версии не возвращается.
        • Очистка оставляет последние KEEP релизов и не трогает активный.

        Вот реальный прогон: две выкладки, откат и список релизов. Скрипт запущен в песочнице.

        bash: deploy.sh
        ./deploy.sh deploy site-v1.tar.gzВыложен релиз: /var/www/site/releases/20261017-170945exit=0./deploy.sh deploy site-v2.tar.gzВыложен релиз: /var/www/site/releases/20261017-170946readlink /var/www/site/current/var/www/site/releases/20261017-170946./deploy.sh rollbackОткат на: /var/www/site/releases/20261017-170945readlink /var/www/site/current/var/www/site/releases/20261017-170945./deploy.sh list20261017-17094620261017-170945
        Что показала проверка скрипта

        Первая версия скрипта падала с ненулевым кодом, пока релизов было меньше, чем значение KEEP: фильтр очистки ничего не находил, и конвейер считал выкладку неудачной, хотя ссылка уже переключилась. Нашёл на тесте, исправил и прогнал восемь выкладок подряд: все коды нулевые, на диске ровно пять релизов. Поэтому скрипты выкладки стоит проверять сразу в двух состояниях: на пустом сервере и после нескольких релизов.

        Замер: чем отличаются способы выкладки

        Чтобы не спорить о том, «как лучше», я сравнил четыре способа на одинаковом релизе. В нём 4002 файла: страница, скрипт и 4000 файлов по 2,4 КБ. Пока идёт выкладка, отдельный процесс отправляет около тысячи запросов в секунду. Каждый запрос читает страницу, скрипт и случайный файл из assets и проверяет, что все три из одного релиза. Ошибкой считаются три случая: файла нет, файл пустой (его как раз копируют) и версии не совпали. Цифры ниже это медианы трёх прогонов в пересчёте на одну выкладку.

        СпособОкно с ошибками на выкладкуЧто видит посетитель
        Удалить каталог и скопировать новый1,24 сОколо 98% запросов за это время получают ошибку: файлов нет
        Скопировать новые файлы поверх живого сайта0,5 сОколо половины запросов получают смесь старых и новых файлов, а в среднем 0,65 запроса на выкладку попадает на пустой файл
        Переключить ссылку командой ln -sfn0 сОшибок нет. Переключение занимает около 2 мс
        Ссылка и явное переименование mv -T0 сОшибок нет. Переключение занимает около 5 мс
        Удалить и скопировать1,24 с
        Копировать поверх сайта0,5 с
        Ссылка ln -sfn0 с
        Ссылка и mv -T0 с

        Код четырёх способов совпадает с тем, что делают на практике: заменить папку, залить файлы поверх, подменить ссылку.

        run.sh (фрагмент)Bash
        # Четыре способа выкладки, которые сравнивались в замере.
        # $1 = корень сайта, $2 = имя релиза (A или B), в каталоге rel/ лежат готовые релизы.
        
        # 1. Удалить каталог и скопировать новый (так делают при замене папки по FTP)
        d_rm_cp()     { rm -rf "$1/current"; cp -r "rel/$2" "$1/current"; }
        
        # 2. Скопировать новые файлы поверх живого сайта
        d_overwrite() { cp -r "rel/$2/." "$1/current/"; }
        
        # 3. Переключить ссылку на готовый релиз
        d_ln_sfn()    { ln -sfn "rel/$2" "$1/current"; }
        
        # 4. То же с явным переименованием: ссылка создаётся рядом и подменяется одним rename
        d_atomic()    { ln -sfn "rel/$2" "$1/current.new"; mv -Tf "$1/current.new" "$1/current"; }
        
        Условия замера

        Тестовый стенд: 2 ядра Intel Xeon 2,8 ГГц, Linux, файловая система ext4, Python 3.11, coreutils 9.4. Три прогона по 20 выкладок для способов с копированием и по 600 для переключения ссылки. Полный скрипт замера приведён под спойлером ниже, его можно запустить у себя. Результат измеряет только слой файлов. Веб-сервера, PHP, базы данных и кэша браузера в замере нет, поэтому на реальном сайте картина будет сложнее.

        Полный скрипт замера: run.sh и reader.py

        Запуск: FILES=4000 SLOW=20 FAST=600 ./run.sh. Нужны Bash и Python 3, рядом должен лежать reader.py. Скрипт создаёт временный каталог, сам его удаляет и печатает по строке на способ.

        run.shBash
        #!/usr/bin/env bash
        # Сравнение способов выкладки: что видит посетитель во время обновления.
        set -euo pipefail
        cd "$(dirname "$0")"
        FILES=${FILES:-4000}
        SLOW=${SLOW:-20}
        FAST=${FAST:-300}
        BASE=$(mktemp -d)
        trap 'rm -rf "$BASE"' EXIT
        
        make_release() {  # $1 = каталог релиза, $2 = номер версии
          mkdir -p "$1/public/assets"
          echo "version-$2" > "$1/public/index.html"
          echo "version-$2" > "$1/public/app.js"
          python3 - "$1/public/assets" "$FILES" "$2" <<'PY'
        import sys, os
        d, n, v = sys.argv[1], int(sys.argv[2]), sys.argv[3]
        blob = ("v" + v + " ") * 600
        for i in range(n):
            with open(os.path.join(d, f"f{i}.txt"), "w") as f:
                f.write(blob)
        PY
        }
        
        # релизы A и B, которые выкладываем по очереди
        make_release "$BASE/rel/A" A
        make_release "$BASE/rel/B" B
        
        run_method() {  # $1 = название, $2 = функция выкладки, $3 = число выкладок
          local name=$1 fn=$2 DEPLOYS=$3
          local root="$BASE/$name"; mkdir -p "$root"
          # стартовое состояние
          case $name in
            rm_cp|overwrite) cp -r "$BASE/rel/A" "$root/current" ;;
            *) mkdir -p "$root/releases"; cp -r "$BASE/rel/A" "$root/releases/A"; ln -sfn "$root/releases/A" "$root/current" ;;
          esac
          local stop="$root/STOP"; rm -f "$stop"
          FILES=$FILES python3 reader.py "$root" "$stop" "$root/result.json" &
          local rp=$!
          sleep 0.3
          local t0 t1
          t0=$(date +%s.%N)
          for ((i=0; i<DEPLOYS; i++)); do
            if (( i % 2 == 0 )); then rel=B; else rel=A; fi
            $fn "$root" "$rel"
          done
          t1=$(date +%s.%N)
          touch "$stop"; wait "$rp"
          python3 - "$name" "$root/result.json" "$t0" "$t1" "$DEPLOYS" <<'PY'
        import json, sys
        name, p, t0, t1, d = sys.argv[1], sys.argv[2], float(sys.argv[3]), float(sys.argv[4]), int(sys.argv[5])
        r = json.load(open(p))
        bad = r['missing'] + r['empty'] + r['mixed']
        per_req_ms = 1000 * r['elapsed'] / r['reads']
        print(f"{name:10s} выкладок={d:>4d} запросов={r['reads']:>7d} missing={r['missing']:>6d} empty={r['empty']:>4d} mixed={r['mixed']:>4d} "
              f"| на выкладку: ошибок {bad/d:7.1f}, окно {bad*per_req_ms/d:7.1f} мс | выкладка {1000*(t1-t0)/d:6.1f} мс")
        PY
        }
        
        d_rm_cp()     { rm -rf "$1/current"; cp -r "$BASE/rel/$2" "$1/current"; }
        d_overwrite() { cp -r "$BASE/rel/$2/." "$1/current/"; }
        d_ln_sfn()    { ln -sfn "$BASE/rel/$2" "$1/current"; }
        d_atomic()    { ln -sfn "$BASE/rel/$2" "$1/current.new"; mv -Tf "$1/current.new" "$1/current"; }
        
        echo "файлов в релизе: $((FILES+2)), поток около 1000 запросов в секунду"
        run_method rm_cp d_rm_cp "$SLOW"
        run_method overwrite d_overwrite "$SLOW"
        run_method ln_sfn d_ln_sfn "$FAST"
        run_method atomic d_atomic "$FAST"
        
        reader.pyPython
        import os, sys, json, time, random
        root, stop, out = sys.argv[1], sys.argv[2], sys.argv[3]
        idx = os.path.join(root, 'current/public/index.html')
        js = os.path.join(root, 'current/public/app.js')
        FILES = int(os.environ.get('FILES', '4000'))
        INTERVAL = 0.001  # один запрос в миллисекунду: около 1000 запросов в секунду
        
        def one():
            """Один визит: страница, затем скрипт. Возвращает ok, missing, empty или mixed."""
            try:
                with open(idx, 'rb') as f:
                    a = f.read()
            except OSError:
                return 'missing'
            if not a:
                return 'empty'
            try:
                with open(js, 'rb') as f:
                    b = f.read()
            except OSError:
                return 'missing'
            if not b:
                return 'empty'
            # страница и случайный файл из assets должны быть из одного релиза
            try:
                with open(os.path.join(root, 'current/public/assets/f%d.txt' % random.randrange(FILES)), 'rb') as f:
                    c = f.read()
            except OSError:
                return 'missing'
            if not c:
                return 'empty'
            v = a.strip()[-1:]
            return 'ok' if (b.strip()[-1:] == v and c[1:2] == v) else 'mixed'
        
        cnt = {'ok': 0, 'missing': 0, 'empty': 0, 'mixed': 0}
        t_start = time.perf_counter()
        while not os.path.exists(stop):
            t = time.perf_counter()
            cnt[one()] += 1
            left = INTERVAL - (time.perf_counter() - t)
            if left > 0:
                time.sleep(left)
        elapsed = time.perf_counter() - t_start
        reads = sum(cnt.values())
        json.dump(dict(reads=reads, elapsed=elapsed, **{k: v for k, v in cnt.items() if k != 'ok'}), open(out, 'w'))
        
        Что замер не доказывает

        В нескольких прогонах от 0 до 21 запроса на 600 переключений получили страницу из одного релиза и файл из другого: запрос попал ровно между двумя чтениями. На реальном сайте это страница, открытая за миг до переключения, которая запрашивает скрипт уже из нового релиза. Защита описана ниже, в ловушке про кэш и ресурсы. Поэтому слова «ноль простоя» без оговорок я не использую: на уровне файлов окно исчезает, а в целом выкладка зависит ещё от базы и кэшей.

        Про время копирования. В замере 4000 файлов дали около 1,2 с при замене каталога. Окно у способов с копированием растёт вместе с числом файлов в релизе, а у переключения ссылки не зависит от него. Распаковка нового релиза при этом идёт рядом с живым сайтом и посетителей не затрагивает.

        Четыре ловушки, о которых вспоминают после первого сбоя

        Ловушка 1

        OPcache держит старый код

        На PHP-сайте после переключения ссылки может продолжать работать прежний код. OPcache привязан к пути файла, а путь current остался тем же, поэтому смену релиза он не замечает.

        Решений два. Перезагрузить PHP-FPM или сбросить кэш сразу после переключения. Или отдавать PHP реальный путь релиза через переменную $realpath_root в настройках nginx. Во втором случае стоит проверить, не меняются ли значения в $_SERVER.

        Ловушка 2

        База данных не переключается ссылкой

        Файлы меняются за миллисекунды. Схема базы так быстро не меняется. Миграции делают в два шага: сначала добавляют колонки и таблицы, которые не мешают старому коду, потом выкладывают новый код, а лишнее удаляют только в следующем релизе.

        Так откат кода не упирается в схему. Перед миграцией делают бэкап и проверяют, что он восстанавливается. Откат кода базу назад не возвращает.

        Ловушка 3

        Страница и ресурсы из разных релизов

        Посетитель загрузил страницу до переключения и запросил скрипт после. Если в новом релизе у файла другое имя, получится ошибка 404.

        Защита простая. Называйте ресурсы с хешем, например app.4f9c2a.js, и складывайте их в общий каталог shared/assets, не удаляя старые сразу. Тогда страница любого релиза найдёт свой файл.

        Ловушка 4

        Общие каталоги и права

        Загрузки и файл окружения живут в shared и подключаются к каждому релизу ссылками. Если забыть подключить upload, новый релиз стартует без картинок.

        Скрипт подключает их сам, а проверка после выкладки должна открыть страницу с картинкой. Файлы релиза принадлежат пользователю, от имени которого работает веб-сервер, иначе первая же запись в кэш упадёт с ошибкой доступа.

        nginx: реальный путь релиза для PHPNginx
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT   $realpath_root;

        Строки приведены по описанию в разборе OPcache и симлинк-выкладок (ссылка в источниках). Перед применением проверьте конфигурацию командой nginx -t на своём сервере: у меня nginx в песочнице нет.

        Стратегии выкладки: от одного сервера до нескольких

        СтратегияКак работаетКогда нужна
        Переключение ссылкиРелиз ставится рядом, ссылка меняется целикомОдин сервер, обычный сайт или интернет-магазин
        RollingСерверы обновляются по одному, пока остальные принимают запросыНесколько серверов за балансировщиком
        Blue-GreenДве полные среды. Новая версия проверяется без нагрузки, затем весь трафик переключается, старая среда остаётся в резервеНужно проверить релиз на реальных данных до переключения и иметь мгновенный откат
        CanaryНовая версия сначала уходит на небольшую часть серверов, например на 1-2%, потом расширяетсяБольшой трафик и рискованные изменения

        Переключение ссылки это упрощённый Blue-Green для одного сервера: два каталога, одна ссылка. В услуге по внедрению CI/CD у Hated.Ru откаты и выкладка на боевой сайт входят в расширенный пакет, а Blue-Green и Canary в пакет Enterprise. Подробнее в разделе про дорожную карту.

        Как понять, что ступень закрыта

        • Откат выполняется одной командой, и вы его уже делали на тестовой среде.
        • На сервере лежат последние пять релизов.
        • Перед каждой миграцией базы делается бэкап, и восстановление из него проверено.
        • Вы знаете, что делает ваш PHP-кэш после переключения ссылки.

        07Ступень 4

        Окружения и контейнеры: одинаково у разработчика, на тесте и в бою

        У сайта минимум три места жизни: компьютер разработчика, тестовый сервер и боевой. Если там стоят разные версии PHP, Python или PostgreSQL, ошибки всплывают только в бою. Фраза «у меня работает» родом отсюда.

        Контейнер собирает окружение из файла. Версия языка, пакеты и настройки описаны в Dockerfile, а несколько сервисов, например приложение и база, поднимаются вместе через Docker Compose. Один набор файлов и одна команда запуска.

        Нужны ли контейнеры вашему сайту

        Сайту на Python, Node.js или Go контейнеры почти всегда полезны. С 1С-Битрикс картина другая. Часто сайт живёт на готовой виртуальной машине или хостинге с настроенным стеком, и контейнеры добавляют слой, который кто-то должен обслуживать. Рабочее правило такое: если проект одинаково собирается и запускается у всей команды, контейнеры можно отложить. Если он «собирается у одного разработчика», контейнеры закрывают проблему быстрее всего.

        Четыре понятия, с которых начинается разговор

        ПонятиеЧто этоПример
        DockerfileТекстовая инструкция сборки образаВзять Python 3.12, поставить зависимости, скопировать код
        ОбразРезультат сборки: приложение и всё, что ему нужно, в одном неизменяемом пакетеregistry.example.ru/site/app:3f9c2a1b7d40
        КонтейнерЗапущенный экземпляр образа. Из одного образа запускают сколько нужно копийТри копии сайта за балансировщиком
        Compose-файлОписание нескольких контейнеров, сетей и томов для одной команды запускаПриложение и PostgreSQL на ноутбуке разработчика

        Dockerfile для Python-сайта

        DockerfileDockerfile
        FROM python:3.12-slim AS builder
        WORKDIR /build
        COPY requirements.txt .
        RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
        
        FROM python:3.12-slim
        RUN useradd --create-home --uid 10001 app
        WORKDIR /app
        COPY --from=builder /install /usr/local
        COPY --chown=app:app . .
        USER 10001
        EXPOSE 8000
        HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
          CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"]
        CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
        
        • Два этапа сборки. На первом ставятся зависимости, на втором в образ попадает только результат. Образ получается меньше, в нём нет кэша pip.
        • Зависимости копируются раньше кода. Пока requirements.txt не менялся, Docker берёт этот слой из кэша, и сборка проходит за секунды.
        • Пользователь без прав администратора. Приложение запускается под номером 10001. Если в нём найдут уязвимость, права внутри контейнера у атакующего будут ограничены.
        • Проверка здоровья. Каждые 30 секунд Docker открывает адрес /health. Три ответа подряд без успеха, и контейнер помечается нездоровым.
        Что проверено

        Dockerfile прошёл линтер hadolint. Первая версия получила замечания по пользователю и по форме команды проверки здоровья, я их исправил. Образ я не собирал: в песочнице, где готовилась статья, нет демона Docker. Запустите docker build у себя и посмотрите размер образа.

        Compose для приложения и базы

        compose.yamlYAML
        services:
          app:
            build: .
            ports:
              - "8000:8000"
            environment:
              DATABASE_URL: postgresql://app:${DB_PASSWORD}@db:5432/app
            depends_on:
              db:
                condition: service_healthy
            restart: unless-stopped
          db:
            image: postgres:17
            environment:
              POSTGRES_USER: app
              POSTGRES_PASSWORD: ${DB_PASSWORD}
              POSTGRES_DB: app
            volumes:
              - pgdata:/var/lib/postgresql/data
            healthcheck:
              test: ["CMD-SHELL", "pg_isready -U app -d app"]
              interval: 5s
              timeout: 3s
              retries: 10
        volumes:
          pgdata:
        

        Приложение стартует после того, как база отвечает на pg_isready: за это отвечает depends_on с условием service_healthy. Пароль приходит из переменной DB_PASSWORD, которая лежит в файле .env рядом с compose-файлом. В репозиторий этот файл не попадает, в .gitignore он уже есть. Том pgdata хранит данные базы вне контейнера, поэтому пересоздание контейнера их не стирает.

        Файл разобран командой docker compose config без ошибок. Сами контейнеры я не поднимал по той же причине, что и образ.

        Команды Docker ComposeBash
        docker compose config        # проверить файл и подстановку переменных
        docker compose up -d --build # собрать образ и поднять приложение с базой
        docker compose ps            # состояние контейнеров и healthcheck
        docker compose logs -f app   # журнал приложения
        docker compose down          # остановить и удалить контейнеры, том с данными остаётся
        
        Docker Hub в России
        1. 30 мая 2024Docker Hub стал недоступен для пользователей из России. Docker сослался на то, что компания американская и обязана соблюдать правила экспортного контроля США. Об этом написал Security Lab в тот же день.
        2. 3 июня 2024В GitVerse появилось бета-зеркало Docker Hub для российских пользователей, инструкция по подключению лежит по адресу dh-mirror.gitverse.ru.

        Если сервер или конвейер тянут базовые образы напрямую с Docker Hub, сборка может упасть в самый неподходящий момент. Что с этим делают на практике:

        • Подключают зеркало в настройках демона Docker, параметр registry-mirrors в файле daemon.json.
        • Заводят свой реестр: GitLab Container Registry или реестр вашего облака. Базовые образы копируют в него заранее, а в Dockerfile пишут свой адрес.
        • Закрепляют версии образов, например python:3.12-slim и postgres:17. Тег latest завтра может оказаться другой версией.

        Условия доступа менялись и могут измениться снова, поэтому я привожу даты событий. Проверьте доступность со своего сервера до того, как от неё зависит релиз.

        Четыре ошибки с контейнерами

        Ошибка 1

        Тег latest в боевом образе

        Сегодня собралась одна версия базового образа, через месяц другая, и пересборка прошлого релиза даст уже другой результат. Тегом образа делайте короткий хеш коммита, как в примере для GitHub Actions выше.

        Ошибка 2

        Секреты внутри образа

        В образе остаётся всё, что в него скопировали, включая файл .env. Добавьте его в .dockerignore, а пароли передавайте при запуске через переменные окружения или хранилище секретов.

        Ошибка 3

        Данные базы без тома

        Контейнер можно удалить и создать заново в любой момент. Всё, что лежало внутри него без тома, исчезает вместе с ним. Базу, загрузки и сессии выносите в тома или во внешнее хранилище.

        Ошибка 4

        Всё в одном контейнере

        Приложение, база и веб-сервер в одном образе заставляют перезапускать всё сразу и мешают масштабировать нужную часть. Один процесс на контейнер, связь между ними описывает Compose.

        Как понять, что ступень закрыта

        • Чистая машина запускает проект командой docker compose up, и никто не вспоминает, какие пакеты поставить.
        • Версии базовых образов закреплены, а тега latest в боевых файлах нет.
        • Секретов в образе нет, это видно по .dockerignore и по содержимому слоёв.
        • Базовые образы доступны с сервера, где идёт сборка: через зеркало или свой реестр.

        08Ступень 5

        Инфраструктура кодом: сервер, который можно пересоздать

        Сервер, настроенный вручную, знает один человек. Какие пакеты стоят, какие строки поправлены в конфигах, где лежат ключи. Когда сервер падает, второй такой же собирают по памяти и по истории команд в терминале.

        Инфраструктура кодом описывает сервер, сеть и настройки файлами. Файлы лежат в Git рядом с кодом сайта: у каждой правки есть автор и причина, а новую среду создаёт запуск команды.

        Две задачи и два инструмента

        ИнструментЧто делаетПример задачи
        TerraformСоздаёт ресурсы в облаке и помнит, что создал: сети, виртуальные машины, диски, DNSПоднять сервер 2 ядра и 4 ГБ в Яндекс Облаке
        AnsibleНастраивает систему внутри машины по SSH, агент на сервер ставить не нужноУстановить nginx, включить файрвол, положить конфиг сайта

        Связка работает по очереди. Terraform создаёт машину и выдаёт её адрес, Ansible подключается к этому адресу и приводит систему в нужное состояние.

        Terraform: виртуальная машина в Яндекс Облаке

        main.tfTerraform
        terraform {
          required_providers {
            yandex = {
              source = "yandex-cloud/yandex"
              # Версию закрепите после первого terraform init: она запишется в .terraform.lock.hcl
            }
          }
          required_version = ">= 1.6"
        }
        
        provider "yandex" {
          zone      = "ru-central1-a"
          folder_id = var.folder_id
        }
        
        variable "folder_id" {
          type        = string
          description = "Идентификатор каталога в Yandex Cloud"
        }
        
        variable "ssh_public_key" {
          type        = string
          description = "Публичный ключ для доступа к серверу"
        }
        
        data "yandex_compute_image" "ubuntu" {
          family = "ubuntu-2404-lts"
        }
        
        resource "yandex_vpc_network" "main" {
          name = "site-network"
        }
        
        resource "yandex_vpc_subnet" "main" {
          name           = "site-subnet-a"
          zone           = "ru-central1-a"
          network_id     = yandex_vpc_network.main.id
          v4_cidr_blocks = ["10.10.0.0/24"]
        }
        
        resource "yandex_compute_instance" "web" {
          name        = "site-web-1"
          platform_id = "standard-v3"
        
          resources {
            cores         = 2
            memory        = 4
            core_fraction = 50
          }
        
          boot_disk {
            initialize_params {
              image_id = data.yandex_compute_image.ubuntu.id
              size     = 30
            }
          }
        
          network_interface {
            subnet_id = yandex_vpc_subnet.main.id
            nat       = true
          }
        
          metadata = {
            ssh-keys = "deploy:${var.ssh_public_key}"
          }
        }
        
        output "web_ip" {
          value = yandex_compute_instance.web.network_interface[0].nat_ip_address
        }
        

        В файле три группы ресурсов: сеть с подсетью, виртуальная машина с двумя ядрами, 4 ГБ памяти и диском 30 ГБ, и вывод её внешнего адреса. Образ системы берётся по семейству ubuntu-2404-lts, то есть при создании подставится актуальная сборка. Идентификатор каталога и публичный ключ передаются переменными: их кладут в файл terraform.tfvars вне Git или в переменные окружения с префиксом TF_VAR_.

        Версию провайдера закрепляют после первого terraform init. Она записывается в файл .terraform.lock.hcl, и этот файл коммитят в репозиторий.

        Команды TerraformBash
        terraform init                 # скачать провайдер, записать версии в .terraform.lock.hcl
        terraform fmt -check           # единый стиль файлов
        terraform validate             # проверка синтаксиса и ссылок между ресурсами
        terraform plan -out=tfplan     # что будет создано, изменено и удалено
        terraform apply tfplan         # применить именно этот план
        

        Правило одно: apply всегда выполняется по сохранённому плану, а план читает второй человек. Так видно, что будет создано, изменено и удалено, до того как это произойдёт.

        Состояние Terraform

        Terraform хранит соответствие между вашими файлами и реальными ресурсами в файле состояния. Его нельзя терять и править руками. Для команды состояние держат в общем хранилище с блокировкой: пока один человек выполняет apply, второй запустить его не сможет. В руководстве Яндекс Облака состояние лежит в Object Storage, а блокировку обеспечивает база YDB. Руководство предупреждает, что начиная с Terraform 1.11 блокировка через Document API объявлена устаревшей, поэтому сверяйте его с версией, которую используете. Ссылка в источниках.

        Что проверено

        Файл разобран парсером HCL без ошибок. Команды terraform init и plan я не запускал: им нужен доступ к облаку и к зеркалу провайдера. Перед первым запуском сверьте имена ресурсов и допустимые значения параметров (platform_id, zone, core_fraction) с документацией провайдера.

        Лицензия и доступность в России

        1. 10 августа 2023HashiCorp объявила о смене лицензии Terraform с MPL 2.0 на Business Source License. Последней версией под MPL стала 1.5.7 от 7 сентября 2023 года, версия 1.6.0 вышла 4 октября 2023 года уже под BUSL 1.1.
        2. 20 сентября 2023Linux Foundation приняла в свой состав OpenTofu, форк Terraform 1.5.7 под лицензией MPL 2.0. Первый стабильный релиз, OpenTofu 1.6.0, вышел 10 января 2024 года.
        3. 28 августа 2024OpenTofu закрыл доступ к своему реестру с российских адресов и убрал из него провайдеры российских облаков: Yandex Cloud, Cloud.ru и Rustack. Причиной назвали соблюдение санкций.

        HashiCorp тоже закрыла российским адресам доступ к своему реестру. Типичное проявление: terraform init падает с ошибкой 403 Forbidden при обращении к registry.terraform.io. Для Яндекс Облака есть зеркало, которое подключается файлом ~/.terraformrc (в Windows это terraform.rc в каталоге %APPDATA%):

        ~/.terraformrcTerraform
        provider_installation {
          network_mirror {
            url     = "https://terraform-mirror.yandexcloud.net/"
            include = ["registry.terraform.io/*/*"]
          }
          direct {
            exclude = ["registry.terraform.io/*/*"]
          }
        }
        

        После этого выполняют terraform init -upgrade. Содержимое файла совпадает с рекомендацией из документации Яндекс Облака на день написания статьи. Яндекс предупреждает, что зеркало может быть недоступно, поэтому закрепляйте версии и храните нужные провайдеры у себя на случай сбоя.

        Я не юрист, поэтому условия лицензии BUSL для вашего случая стоит обсудить с юристом. OpenTofu остаётся вариантом для тех, кому нужна открытая лицензия, но его реестр для российских провайдеров закрыт, и для российских облаков проще работать с Terraform и зеркалом.

        Ansible: настройка сервера

        Ansible описывает желаемое состояние сервера. Повторный запуск ничего не ломает: если nginx уже установлен и конфиг не менялся, задача отмечается как выполненная без изменений. Пример настраивает веб-сервер: ставит пакеты, кладёт конфигурацию сайта, включает файрвол.

        inventory.iniINI
        [web]
        site-web-1 ansible_host=203.0.113.10 ansible_user=deploy
        
        site.ymlYAML
        - name: Настройка веб-сервера
          hosts: web
          become: true
          roles:
            - web
        
        roles/web/tasks/main.ymlYAML
        - name: Пакеты
          ansible.builtin.apt:
            name: [nginx, ufw, fail2ban]
            state: present
            update_cache: true
            cache_valid_time: 3600
        
        - name: Конфиг сайта
          ansible.builtin.template:
            src: site.conf.j2
            dest: /etc/nginx/sites-available/site.conf
            mode: "0644"
          notify: Перезагрузить nginx
        
        - name: Включить сайт
          ansible.builtin.file:
            src: /etc/nginx/sites-available/site.conf
            dest: /etc/nginx/sites-enabled/site.conf
            state: link
          notify: Перезагрузить nginx
        
        - name: Разрешить SSH, HTTP и HTTPS
          community.general.ufw:
            rule: allow
            port: "{{ item }}"
            proto: tcp
          loop: ["22", "80", "443"]
        
        - name: Включить файрвол
          community.general.ufw:
            state: enabled
            policy: deny
        

        Задача «Конфиг сайта» и задача «Включить сайт» при изменении сообщают обработчику, что nginx нужно перезагрузить. Обработчик срабатывает один раз в конце прогона.

        roles/web/handlers/main.ymlYAML
        - name: Перезагрузить nginx
          ansible.builtin.service:
            name: nginx
            state: reloaded
        
        roles/web/templates/site.conf.j2Nginx
        server {
            listen 80;
            server_name {{ site_domain }};
            root /var/www/site/current/public;
            index index.php index.html;
        }
        

        Значение site_domain подставляется из переменных роли. Пароли и ключи хранят в зашифрованном файле Ansible Vault.

        Команды AnsibleBash
        ansible-playbook -i inventory.ini site.yml --syntax-check   # синтаксис
        ansible-playbook -i inventory.ini site.yml --check --diff     # пробный прогон: что изменилось бы
        ansible-playbook -i inventory.ini site.yml                    # настроить сервер
        

        Пробный прогон с флагами --check --diff показывает, какие файлы изменились бы и как. Его стоит делать перед каждым запуском на боевом сервере.

        Что проверено

        Плейбук прошёл проверку синтаксиса в ansible-core 2.19 с коллекцией community.general. На реальном сервере я его не запускал: для этого нужна машина с доступом по SSH.

        Как понять, что ступень закрыта

        • Тестовую среду можно удалить и поднять заново одним запуском, вручную ничего не настраивается.
        • Файлы Terraform и Ansible лежат в Git, а состояние хранится в общем хранилище с блокировкой.
        • Пробный прогон Ansible на боевом сервере показывает ноль изменений: сервер совпадает с описанием.
        • Секреты лежат в хранилище или в зашифрованном виде, в репозитории открытых паролей нет.

        09Оркестрация

        Kubernetes: когда он нужен сайту

        Kubernetes управляет контейнерами на нескольких серверах: запускает нужное число копий, заменяет упавшие, раскатывает новую версию по очереди и добавляет копии при росте нагрузки. Система выросла из внутреннего проекта Google, а в 2014 году Google открыл её исходный код.

        Возможности большие, и цена у них такая же. Кластер нужно обновлять, следить за ним и разбираться в его сетях. Поэтому сначала вопрос, а нужен ли он вам.

        Нужен ли Kubernetes: таблица для решения

        СитуацияВывод
        Лендинг или корпоративный сайт на Tilda, WordPress, БитриксеНе нужен. Хватит выкладки по ссылке и мониторинга
        Интернет-магазин на одном сервере с предсказуемой нагрузкойНе нужен. Docker Compose и один хороший сервер покрывают задачу
        Несколько сервисов, у каждого своя нагрузка и свой цикл релизовСтоит рассмотреть. Оцените, кто будет обслуживать кластер
        Нагрузка скачет в несколько раз в течение суток или сезонаПодходит: автомасштабирование как раз для этого
        Несколько команд выкладывают независимо друг от другаПодходит: у каждой команды свои пространства имён и права

        Если большинство строк вашего сайта в верхней части таблицы, пропустите этот раздел и вернитесь к нему, когда проект вырастет.

        Словарь Kubernetes для владельца сайта

        1

        Под (Pod)

        Минимальная единица запуска: один или несколько контейнеров, которые работают вместе и делят сеть и хранилище.

        2

        Deployment

        Описывает, сколько копий приложения должно работать и как их обновлять. Он же хранит историю ревизий для отката.

        3

        Service

        Постоянный адрес для набора подов. Поды приходят и уходят, адрес остаётся.

        4

        Ingress

        Правила входящего трафика: какой домен и путь ведёт на какой сервис.

        5

        HPA

        Автомасштабирование по нагрузке: добавляет поды, когда процессор загружен, и убирает, когда нагрузка спадает.

        6

        Узел (Node)

        Виртуальная или физическая машина, на которой запускаются поды. Управляющие узлы руководят кластером, рабочие несут нагрузку.

        Манифест для сайта: три копии, обновление без простоя, автомасштабирование

        k8s/app.yamlYAML
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: site
          labels:
            app: site
        spec:
          replicas: 3
          revisionHistoryLimit: 5
          strategy:
            type: RollingUpdate
            rollingUpdate:
              maxUnavailable: 0
              maxSurge: 1
          selector:
            matchLabels:
              app: site
          template:
            metadata:
              labels:
                app: site
            spec:
              containers:
                - name: site
                  image: registry.example.ru/site/app:3f9c2a1b7d40
                  ports:
                    - containerPort: 8000
                  envFrom:
                    - secretRef:
                        name: site-env
                  resources:
                    requests:
                      cpu: 100m
                      memory: 128Mi
                    limits:
                      memory: 256Mi
                  readinessProbe:
                    httpGet:
                      path: /health
                      port: 8000
                    initialDelaySeconds: 5
                    periodSeconds: 10
                  livenessProbe:
                    httpGet:
                      path: /health
                      port: 8000
                    initialDelaySeconds: 15
                    periodSeconds: 20
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: site
        spec:
          selector:
            app: site
          ports:
            - port: 80
              targetPort: 8000
        ---
        apiVersion: autoscaling/v2
        kind: HorizontalPodAutoscaler
        metadata:
          name: site
        spec:
          scaleTargetRef:
            apiVersion: apps/v1
            kind: Deployment
            name: site
          minReplicas: 3
          maxReplicas: 8
          metrics:
            - type: Resource
              resource:
                name: cpu
                target:
                  type: Utilization
                  averageUtilization: 70
        
        • Три копии. replicas: 3 держит три пода. Если один упадёт, Kubernetes создаст новый.
        • Обновление по очереди. Параметры maxUnavailable: 0 и maxSurge: 1 означают: сначала запускается один новый под, и только когда он готов, старый останавливается. Число рабочих копий в процессе обновления не падает ниже трёх.
        • Проверки готовности и жизни. Readiness-проба решает, можно ли отправлять трафик на под. Liveness-проба перезапускает под, который завис. Обе опрашивают адрес /health.
        • Ресурсы. Запрос процессора и памяти нужен планировщику, чтобы разместить под, а лимит памяти защищает соседей от утечки.
        • Секреты. Переменные окружения берутся из секрета site-env. Сам секрет создают отдельной командой, в Git он не лежит.
        • Автомасштабирование. HPA держит от 3 до 8 подов и добавляет копии, когда средняя загрузка процессора превышает 70% от запрошенного: в манифесте запрошено 100m, то есть десятая часть ядра на под.
        Команды kubectlBash
        kubectl apply -f k8s/app.yaml                 # применить манифест
        kubectl rollout status deployment/site         # дождаться, пока новые поды станут готовыми
        kubectl get pods -l app=site                   # посмотреть поды приложения
        kubectl rollout history deployment/site        # какие ревизии хранятся
        kubectl rollout undo deployment/site           # вернуть предыдущую ревизию
        

        Команда kubectl rollout undo возвращает предыдущую ревизию Deployment. Параметр revisionHistoryLimit: 5 в манифесте хранит пять последних, как схема с пятью релизами из раздела про выкладку.

        Что проверено

        Манифест прошёл проверку по схемам Kubernetes 1.37 (инструмент kubernetes-validate). В кластере я его не запускал. Адрес образа registry.example.ru учебный, подставьте адрес своего реестра.

        Сколько живёт версия Kubernetes

        Кластер нельзя поставить один раз и забыть. Минорные версии выходят примерно три раза в год, и каждая получает исправления около года. По данным kubernetes.io на 17 октября 2026 года поддерживаются четыре минорные версии:

        ВерсияПоследний патчОкончание поддержки
        1.371.37.1 от 15 сентября 202628 октября 2027
        1.361.36.5 от 15 сентября 202628 июня 2027
        1.351.35.9 от 15 сентября 202628 февраля 2027
        1.341.34.12 от 15 сентября 202627 октября 2026

        Версия 1.34 перестанет получать исправления безопасности через десять дней после даты, на которую написана статья. Кластер на ней придётся обновлять в ближайшие недели. Для небольшого проекта это постоянная статья расходов: время инженера на обновления раз в несколько месяцев.

        Практический вариант

        Если нагрузка действительно требует Kubernetes, а своих инженеров нет, берут управляемый кластер у облачного провайдера. Управляющие узлы обслуживает провайдер, а вам остаются манифесты, мониторинг и выбор момента для обновления версии. Настройка управляемого кластера входит в услуги Hated.Ru по облаку, стоимость указана на странице услуги.

        Как понять, что ступень закрыта, если Kubernetes вам нужен

        • Обновление приложения не роняет число рабочих копий ниже заданного, это видно по kubectl rollout status.
        • Откат на предыдущую ревизию вы уже делали на тестовом кластере.
        • У каждого пода есть пробы готовности и жизни, а также запросы и лимиты ресурсов.
        • В команде есть человек, который знает дату окончания поддержки текущей версии кластера и план обновления.

        10Ступень 6

        Мониторинг и оповещения: узнать о проблеме раньше клиента

        Выкладка и откат закрывают половину задачи. Вторая половина состоит в том, чтобы вовремя заметить, что с сайтом что-то не так. Если о падении узнают из звонка клиента, мониторинга у вас нет, даже если на сервере стоит десяток панелей.

        Четыре сигнала, с которых начинают

        В книге Google о надёжности сервисов перечислены четыре «золотых сигнала». Для сайта они читаются так:

        1

        Задержка

        За сколько секунд отвечает страница. Успешные и ошибочные ответы считают отдельно.

        2

        Трафик

        Сколько запросов приходит в секунду. Резкое падение так же важно, как рост.

        3

        Ошибки

        Доля ответов 5xx и упавших запросов к базе. Скачок означает дефект или сбой железа.

        4

        Насыщение

        Сколько ресурсов занято: процессор, память, диск. Высокое значение говорит, что сервер подходит к пределу.

        Что проверять на небольшом сайте

        УровеньЧто смотримКогда сработает
        СнаружиОткрывается ли главная и страница заявки с внешней точкиСайт не отвечает 2 минуты
        ПриложениеДоля ошибок 5xx, время ответаОшибок больше 5% за 5 минут или медленные ответы 10 минут
        СерверПроцессор, память, дискДиск закончится меньше чем за сутки
        ЗаявкиПриходят ли письма и записи в CRM с формыЗа рабочий день нет ни одной заявки

        Последняя строка выходит за рамки техники. Форма может отвечать кодом 200 и при этом не доставлять заявки. Для сайта, который живёт на заявках, это главная проверка, и её придумывают отдельно под ваш проект.

        Prometheus, Alertmanager и Grafana

        Стандартная связка для небольших проектов состоит из трёх частей. Prometheus сам опрашивает источники метрик по расписанию и хранит их как временные ряды. Alertmanager принимает от него сработавшие правила и рассылает уведомления, в том числе в Telegram, почту и Slack. Grafana рисует графики и дашборды.

        Prometheus не ждёт, пока приложение пришлёт данные: он сам обходит адреса, которые отдают метрики. Такие адреса называются экспортёрами. Для сервера используют node exporter, для внешней проверки сайта blackbox exporter, для приложения добавляют свою библиотеку метрик.

        Актуальная ветка Prometheus 3. Версия 3.0 вышла 14 ноября 2024 года и стала первым мажорным релизом за семь лет: в ней появился новый интерфейс, поддержка UTF-8 в названиях метрик и приём метрик по протоколу OpenTelemetry. Если ставите систему сейчас, берёте актуальную версию.

        Правила оповещений для сайта

        alerts.ymlYAML
        groups:
          - name: site
            rules:
              - alert: SiteDown
                expr: probe_success{job="blackbox-site"} == 0
                for: 2m
                labels:
                  severity: critical
                annotations:
                  summary: "Сайт {{ $labels.instance }} не отвечает"
              - alert: HighErrorRate
                expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.05
                for: 5m
                labels:
                  severity: warning
                annotations:
                  summary: "Доля ответов 5xx выше 5% за 5 минут"
              - alert: SlowResponses
                expr: histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m]))) > 1.5
                for: 10m
                labels:
                  severity: warning
                annotations:
                  summary: "95-й перцентиль времени ответа выше 1,5 секунды"
              - alert: DiskFillingUp
                expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 24 * 3600) < 0
                for: 30m
                labels:
                  severity: warning
                annotations:
                  summary: "Диск на {{ $labels.instance }} закончится меньше чем за сутки"
        
        • SiteDown. Внешняя проверка не проходит две минуты подряд. Параметр for защищает от ложных тревог при секундном сбое сети.
        • HighErrorRate. Доля ответов 5xx выше 5% в течение пяти минут. Это ранний признак сломанного релиза и повод проверить, не пора ли откатываться.
        • SlowResponses. 95-й перцентиль времени ответа выше 1,5 секунды десять минут. Он показывает, как отвечает медленная пятая часть запросов, и среднее значение этого не скрывает.
        • DiskFillingUp. Функция predict_linear смотрит на динамику за шесть часов и предупреждает, если диск заполнится меньше чем за сутки. Забитый диск остаётся частой причиной падения сайтов, и предупредить о нём можно заранее.

        Уведомления идут в Telegram-чат команды. Критичные срабатывания стоит отделить от предупреждений: SiteDown будит дежурного, а SlowResponses ждёт утра.

        Что проверено

        Все четыре выражения разобраны парсером PromQL без ошибок. Работу правил на реальном Prometheus с реальным трафиком я не проверял: названия метрик http_requests_total и http_request_duration_seconds_bucket зависят от библиотеки метрик вашего приложения, их нужно сверить.

        Логи: что нужно маленькому сайту

        Метрики показывают, что что-то сломалось. Логи отвечают, что именно. Для небольшого сайта достаточно писать журналы nginx и приложения в файлы с ротацией и уметь быстро их открыть. Когда серверов становится несколько, логи собирают в одном месте.

        Классический вариант называется ELK: Elasticsearch хранит и ищет, Logstash обрабатывает, Kibana показывает. Он требует заметного объёма памяти, поэтому для небольшого проекта часто выбирают более лёгкие решения вроде Grafana Loki. По лицензии у Elastic в 2024 году произошла смена курса: 29 августа 2024 года компания объявила, что Elasticsearch и Kibana снова открытые: к прежним лицензиям добавляется вариант AGPL.

        Метрики DORA: как понять, что стало лучше

        Установили конвейер, стало ли лучше? Ответ даёт исследовательская программа DORA. Первые четыре метрики она описала в 2014 году, а в отчёте 2024 года добавила пятую. Метрики делятся на две группы.

        Скорость

        Что показывает пропускную способность

        • Время изменения. Сколько проходит от коммита в систему контроля версий до работы в бою.
        • Частота выкладок. Сколько выкладок за период или какой промежуток между ними.
        • Время восстановления. За какое время исправляют выкладку, которая потребовала немедленного вмешательства.
        Нестабильность

        Что показывает, как проходят выкладки

        • Доля неудачных выкладок. Какая часть выкладок потребовала немедленного вмешательства.
        • Доля внеплановых выкладок. Какая часть выкладок случилась внепланово из-за инцидента в бою. Это пятая метрика, добавленная в 2024 году.

        Считать их можно без дорогих платформ: достаточно журнала выкладок. Для каждой выкладки записывают время коммита, время выкладки, признак неудачи, время восстановления и признак внеплановой выкладки. Скрипт на Python ниже считает все пять метрик из такого файла и использует только стандартную библиотеку.

        deployments.csvТекст
        commit_at,deployed_at,failed,recovered_at,unplanned
        2026-09-14T10:05,2026-09-14T15:30,0,,0
        2026-09-16T11:20,2026-09-16T16:00,0,,0
        2026-09-18T09:40,2026-09-18T14:10,1,2026-09-18T14:35,0
        2026-09-18T14:40,2026-09-18T15:00,0,,1
        2026-09-22T10:15,2026-09-22T15:45,0,,0
        2026-09-23T12:00,2026-09-23T16:20,0,,0
        2026-09-25T09:30,2026-09-25T13:50,0,,0
        2026-09-29T10:45,2026-09-29T15:10,0,,0
        2026-10-01T11:10,2026-10-01T16:40,0,,0
        2026-10-06T10:00,2026-10-06T15:20,0,,0
        2026-10-08T09:50,2026-10-08T14:30,0,,0
        2026-10-09T12:30,2026-10-09T17:05,0,,0
        
        dora.pyPython
        """Пять метрик DORA из журнала выкладок. Только стандартная библиотека Python 3.9+."""
        import csv
        import sys
        from datetime import datetime
        from statistics import median
        
        
        def ts(value):
            return datetime.fromisoformat(value) if value else None
        
        
        def hm(minutes):
            return f"{int(minutes // 60)} ч {int(minutes % 60):02d} мин"
        
        
        def pct(part, whole):
            return f"{100 * part / whole:.1f}".replace(".", ",") + f"% ({part} из {whole})"
        
        
        rows = []
        with open(sys.argv[1], newline="", encoding="utf-8") as f:
            for r in csv.DictReader(f):
                rows.append({
                    "commit": ts(r["commit_at"]),
                    "deploy": ts(r["deployed_at"]),
                    "failed": r["failed"] == "1",
                    "recovered": ts(r["recovered_at"]),
                    "unplanned": r["unplanned"] == "1",
                })
        
        first = min(r["deploy"] for r in rows)
        last = max(r["deploy"] for r in rows)
        weeks = max((last - first).days / 7, 1)
        lead = [(r["deploy"] - r["commit"]).total_seconds() / 60 for r in rows]
        recovery = [(r["recovered"] - r["deploy"]).total_seconds() / 60
                    for r in rows if r["failed"] and r["recovered"]]
        failed = sum(r["failed"] for r in rows)
        unplanned = sum(r["unplanned"] for r in rows)
        
        print(f"Период: {first:%d.%m.%Y} - {last:%d.%m.%Y}")
        print(f"Частота выкладок: {len(rows) / weeks:.1f}".replace(".", ",") + f" в неделю ({len(rows)} за период)")
        print(f"Время от коммита до боя, медиана: {hm(median(lead))}")
        print(f"Доля неудачных выкладок: {pct(failed, len(rows))}")
        print(f"Время восстановления, медиана: {hm(median(recovery)) if recovery else 'не было сбоев'}")
        print(f"Доля внеплановых выкладок: {pct(unplanned, len(rows))}")
        
        bash: dora.py
        python3 dora.py deployments.csvПериод: 14.09.2026 - 09.10.2026Частота выкладок: 3,4 в неделю (12 за период)Время от коммита до боя, медиана: 4 ч 37 минДоля неудачных выкладок: 8,3% (1 из 12)Время восстановления, медиана: 0 ч 25 минДоля внеплановых выкладок: 8,3% (1 из 12)

        Данные в файле учебные: двенадцать выкладок за неполные четыре недели, я составил их для демонстрации. На своих данных цифры получатся другими. Смотрите на динамику: если время от коммита до боя снижается, а доля неудачных выкладок не растёт, процесс улучшается.

        Пороги для «хорошо» и «плохо» я не привожу: они меняются от года к году и зависят от отрасли. Сравнивайте себя с собой за прошлый квартал.

        Как понять, что ступень закрыта

        • Остановите сервис на тестовой среде: оповещение в Telegram придёт через 2-3 минуты.
        • Состояние сайта видно на одном экране: доступность, ошибки, время ответа и свободное место.
        • Есть проверка доставки заявок, которая смотрит дальше кода ответа.
        • Журнал выкладок ведётся, и раз в месяц вы смотрите пять метрик DORA.

        11Безопасность

        Безопасность внутри процесса: что проверять на каждом шаге

        Автоматизация ускоряет выкладку и так же быстро выкладывает ошибки, в том числе опасные. Поэтому проверки безопасности встраивают в тот же конвейер: так их не забывают, и стоят они копейки по сравнению с утечкой базы клиентов. Для этого подхода есть название DevSecOps.

        Чтобы не утонуть в списках, ниже шесть мест, где у небольшого сайта обычно и случаются проблемы.

        1

        Секреты

        Пароли и ключи лежат в хранилище секретов конвейера или в зашифрованном файле. В репозитории их нет, а в журнале сборки значения скрыты: в GitHub это происходит само, в GitLab переменную нужно отметить как masked. Если пароль попал в Git, его считают утёкшим и меняют.

        2

        Права конвейера

        Токену конвейера выдают минимум прав. В примере для GitHub Actions это строка permissions: contents: read: сборка читает код и больше ничего не может.

        3

        Сторонние действия

        Готовые шаги из каталога GitHub Actions выполняются с доступом к вашему репозиторию. Рекомендация GitHub: закреплять их по полному хешу коммита и читать исходный код. В примере в статье тег для читаемости, а в рабочем проекте хеш подставьте сами.

        4

        Зависимости

        Библиотеки сайта проверяются сканером уязвимостей при каждой сборке. Известная дыра в старой версии пакета закрывается обновлением, пока на неё не пришли.

        5

        Доступы людей

        Доступ к серверу и панели управления у каждого свой, по ключам. Уволившегося сотрудника отключают в тот же день, и для этого есть список мест, где у него были права.

        6

        Резервные копии

        Копия, из которой ни разу не восстанавливались, работает только на бумаге. Раз в квартал восстановите сайт из резервной копии на тестовой среде и запишите, сколько это заняло.

        Внешние данные в конвейере

        Название ветки, заголовок pull request и тексты комментариев приходят от людей и могут содержать любые символы. Если подставить их в команду оболочки как есть, получится дыра: чужой текст выполнится на вашей сборочной машине. Данные из таких источников передают через входные параметры шага или через переменные окружения, а в строку команды не склеивают.

        Секрет попал в Git

        Удаление файла следующим коммитом ничего не меняет: пароль остаётся в истории. Порядок действий такой: сначала смените пароль или ключ у сервиса, потом уберите его из кода, и только после этого чистите историю, если без этого не обойтись. В обратном порядке вы теряете время, пока утёкший ключ ещё работает.

        Как понять, что безопасность встроена в процесс

        • Конвейер получает минимум прав и берёт пароли из хранилища секретов.
        • Сканер зависимостей запускается автоматически, и красный результат блокирует слияние.
        • Восстановление из резервной копии проверено в этом квартале, время записано.
        • Список людей с доступом к серверу актуален, а ключи у каждого свои.

        12Люди

        Команда и договорённости: кто за что отвечает

        Инструменты работают, когда за ними стоят понятные правила. В небольшом проекте все роли часто совмещает один-два человека, но список обязанностей всё равно стоит выписать. Тогда ясно, кто принимает решение о релизе и кто идёт разбираться, если сайт упал.

        РольЗа что отвечаетЧто должна уметь показать
        Заказчик или владелец продуктаПриоритеты, приёмка релиза, решение «выкладываем»Список изменений в ближайшем релизе и критерии готовности
        РазработчикКод, автотесты, ревью чужих измененийPull request с зелёными проверками
        DevOps-инженерКонвейер, окружения, мониторинг, откатОткат на прошлый релиз за минуту и работающие оповещения
        ТестировщикСценарии проверки, заявки и оплата перед релизомОтметку о том, какие сценарии пройдены на тесте

        Договорённости, которые дешевле всего внедрить

        Расписание

        Релизы по вторникам и средам

        В пятницу вечером сбой стоит дороже всего: команда уходит, а клиенты остаются. Если конвейер надёжный, релиз можно сделать в любой день. Пока он не такой, выбирайте начало недели и рабочие часы.

        Размер

        Маленькие релизы

        Один релиз с одним изменением проще проверить и откатить, чем «большое обновление на месяц». Когда что-то ломается, виновата короткая и понятная правка.

        Ответственный

        Один человек на релиз

        Он нажимает кнопку, смотрит на графики первые 15 минут и решает, откатываться ли. Его имя пишут в канале команды до выкладки.

        Разбор

        Постмортем без поиска виноватых

        После каждого сбоя команда пишет короткий разбор: что случилось, почему не заметили раньше, что меняем. Цель разбора: найти слабое место процесса. Шаблон в конце статьи.

        Доска для релизов

        Достаточно простой доски в формате Kanban с колонками «В работе», «На ревью», «Проверка на тесте», «Готово к релизу», «В бою». Число задач в работе ограничивают: когда колонка заполнена, начинать новое нельзя, пока не закончено старое. Так застрявшие изменения видны сразу и не копятся в ветках.

        Первые две недели после внедрения конвейера закладывайте время на разбор странных случаев. Тесты окажутся то слишком строгими, то слишком мягкими, а скрипт отката найдёт ещё один путь, который стоило проверить. Это обычное начало, и оно проходит, когда конвейером пользуются каждую неделю.

        13Выбор

        Что внедрять вашему типу сайта

        Нужны ли все шесть ступеней и в каком порядке их проходить, зависит от проекта. Таблица подскажет, с чего начать и что отложить.

        Тип сайтаС чего начатьЧто добавить позжеЧто чаще всего лишнее
        Лендинг или сайт на TildaМониторинг доступности и заявок, учёт домена и DNS, копия содержимогоПроверка скорости загрузки после каждой правкиКонтейнеры, Terraform, Kubernetes: выкладывать нечего, конструктор делает это сам
        Корпоративный сайт на WordPressGit для своей темы и плагинов, тестовая копия, бэкапы и проверка восстановленияCI с проверкой стиля и выкладка по ссылкеKubernetes
        Сайт или магазин на 1С-БитриксGit для каталога /local/, миграции базы, выкладка по ссылке, мониторинг заявок и заказовCI с автотестами оплаты и заказа, тестовая среда, как у боевойKubernetes при одном сервере
        Веб-приложение на Python или Node.jsВсе ступени с первой по четвёртую, мониторинг ошибок и времени ответаTerraform и Ansible, несколько окруженийKubernetes, пока нагрузка помещается на один сервер
        Маркетплейс или высоконагруженный сервисВсе шесть ступеней, метрики DORA, постмортемыBlue-Green и Canary, автомасштабированиеРучные операции на боевых серверах, их нужно убирать первыми

        Если сомневаетесь, к какой строке относится ваш проект, начните с общих двух шагов. Первый шаг: проверить, что код лежит в Git и что вы умеете откатиться. Второй шаг: настроить оповещение о недоступности сайта. Они занимают несколько дней и дают результат на любом типе проекта.

        14План

        План внедрения на 90 дней и как это делает Hated.Ru

        Все шесть ступеней разом никто не внедряет. Реалистичный план растягивается на квартал, и каждый этап заканчивается проверкой.

        СрокЧто делаемРезультат, который можно проверить
        Недели 1-2Код в Git, правила веток, оповещение о недоступности сайтаНа сервере нет файлов, которых нет в репозитории. Остановка сайта на тесте приводит сообщение в Telegram
        Недели 3-4Автоматические проверки: линтер, тесты, сборкаНамеренно сломанный тест блокирует слияние
        Недели 5-6Выкладка по ссылке и откат одной командойОткат выполнен на тестовой среде меньше чем за минуту
        Недели 7-10Контейнеры и одинаковые окруженияЧистая машина запускает проект командой docker compose up
        Недели 11-13Terraform, Ansible, журнал выкладок и метрики DORAТестовая среда пересоздаётся одним запуском, пять метрик посчитаны за месяц

        Пять ошибок внедрения

        • Начинать с Kubernetes. Сначала порядок в Git и откат, оркестрация потом и только если нужна.
        • Автоматизировать хаос. Если выкладка без процесса работает через раз, скрипт будет через раз выкладывать неправильно.
        • Писать тесты на всё сразу. Начните с трёх сценариев: открывается главная, отправляется заявка, проходит заказ.
        • Доверять откату, который ни разу не запускали. Проверенный откат отличается от задуманного на бумаге.
        • Считать внедрение законченным. Конвейеру нужен владелец, который следит за зависимостями и ключами.

        Какие работы есть в Hated.Ru

        Если разобраться самим некогда, внедрение CI/CD мы делаем под ключ. Цены и состав пакетов ниже взяты со страницы услуги на 17 октября 2026 года, окончательную стоимость мы называем после разбора проекта.

        Базовый

        от 40 000 ₽

        Срок: от 1 недели. Простой конвейер для небольшого проекта: настройка сборки, автоматические тесты, деплой на тестовую среду, документация процессов и обучение команды.

        Расширенный

        от 70 000 ₽

        Срок: от 2 недель. Всё из базового пакета, плюс деплой на продакшн, стратегии откатов, контейнеризация Docker, мониторинг и алерты, уведомления в Telegram и Slack.

        Enterprise

        от 120 000 ₽

        Срок: от 3 недель. Всё из расширенного пакета, плюс мультисредовый деплой, Blue-Green и Canary, инфраструктура как код.

        Отдельные задачи и доработки считаем по часам, от 3 500 ₽ в час. В первый месяц после внедрения мы на связи для правок, это указано на странице услуги.

        Смежная услугаСтоимостьСрок
        Аудит инфраструктуры с расчётом и рекомендациямиот 30 000 ₽от 5 дней
        Настройка мониторинга и алертовот 25 000 ₽от 3 дней
        Резервное копирование и регламент восстановленияот 30 000 ₽от 3 дней
        Сопровождение инфраструктурыот 25 000 ₽ в месяцпостоянно

        Подробности и состав каждого пакета описаны на страницах внедрения CI/CD, подбора и настройки инфраструктуры и настройки Яндекс Облака и VK Cloud.

        15Чек-листы

        Три чек-листа, которые стоит распечатать

        Первый нужен перед выкладкой, второй сразу после неё, третий при приёмке работ у подрядчика. Отметки сохраняются в вашем браузере, на сервер ничего не отправляется. Кнопка «Сбросить» очищает список перед следующим релизом.

        Чек-лист перед релизом

        Пройдите список за 10 минут до выкладки. Пункт без галочки означает, что выкладку стоит отложить.

        0 из 8

        Первые 15 минут после релиза

        0 из 6

        Приёмка работ у подрядчика

        Отмечайте то, что вы видели своими глазами.

        0 из 8

        16Шаблоны

        Готовые шаблоны: ТЗ подрядчику, регламент отката, разбор сбоя

        В квадратных скобках стоят места, которые нужно заменить своими данными. Кнопка «Копировать» переносит текст целиком.

        Техническое задание на внедрение CI/CD

        Показывает подрядчику, что вы знаете, что покупаете, и даёт вам список для приёмки. Все критерии проверяются руками, поэтому спорных моментов при сдаче почти нет.

        Техническое задание на внедрение CI/CDШаблон
        Техническое задание на внедрение CI/CD
        
        1. Проект
        Название и адрес сайта: [название, адрес]
        Платформа: [1С-Битрикс, WordPress, Python, другое] версии [укажите]
        Где размещён: [хостинг, VPS, облако], доступ у: [кого]
        
        2. Как выкладываем сегодня
        [Кто, как часто, чем: FTP, панель, скрипт]. Последний сбой при выкладке: [дата и что случилось].
        
        3. Что нужно получить
        - Код в репозитории [GitHub, GitLab, другое] с правилами веток и обязательным ревью
        - Автоматические проверки на каждое изменение: [линтер, тесты, сборка]
        - Выкладка на тестовую среду и на боевой сайт одной командой
        - Откат на прошлый релиз меньше чем за минуту, [N] последних релизов на сервере
        - Оповещения в [Telegram, Slack, почту] о недоступности, росте ошибок и медленных ответах
        
        4. Окружения
        Тест и бой [отличаются, совпадают] по версиям [PHP, Python, БД]. Тестовая среда должна быть доступна по адресу [адрес].
        
        5. Безопасность и доступы
        Секреты хранятся в хранилище конвейера. Аккаунты на нашу компанию. Права минимальные. Список людей с доступом: [список].
        
        6. Документация и обучение
        Инструкция по выкладке и откату, схема окружений, список секретов без значений. Обучение [N] сотрудников с выкладкой тестового изменения.
        
        7. Критерии приёмки
        - Намеренно сломанный тест блокирует слияние
        - Откат показан вживую на тестовой среде
        - Тестовая остановка сайта приводит оповещение за [2-3] минуты
        - Проверено восстановление из резервной копии
        
        8. Сроки и бюджет
        Срок: [дата]. Бюджет: [сумма или вилка]. Поддержка после запуска: [период].
        
        9. Что просим в ответе подрядчика
        План работ по этапам, список используемых инструментов, состав команды, стоимость отдельно по этапам, что в стоимость не входит.

        Регламент отката

        Короткая страница, которую открывает ответственный за релиз, когда что-то идёт не так. Пороги подставьте под свой сайт. Если порог уже превышен, обсуждать нечего: откат делается сразу, а причину ищут после.

        Регламент откатаШаблон
        Регламент отката сайта [название]
        
        Когда откатываемся без обсуждений
        - Сайт недоступен дольше [2] минут
        - Доля ответов 5xx выше [5]% дольше [5] минут после выкладки
        - Форма заявки или оплата не работает
        - Ответственный за релиз не может за [10] минут назвать причину и срок исправления
        
        Кто решает
        Ответственный за релиз: [имя, телефон]. Заместитель: [имя, телефон]. Решение принимает один человек, согласования не требуются.
        
        Порядок действий
        1. Написать в рабочий чат: «Откатываю релиз [номер], причина: [кратко]».
        2. Выполнить на сервере: ./deploy.sh rollback
        3. Проверить главную, форму заявки и корзину. Открыть графики ошибок и времени ответа.
        4. Если миграция базы была в релизе: сверить, совместим ли старый код со схемой. Восстановление базы из бэкапа выполняется только по решению [роль] и только после остановки записи.
        5. Написать в чат: «Откат выполнен в [время], сайт работает на релизе [номер]».
        
        После отката
        - Завести разбор сбоя по шаблону в течение [1] рабочего дня
        - Исправление выкладывается как новый релиз, возврат на неисправный релиз запрещён
        - Дату и причину отката внести в журнал выкладок
        
        Контакты
        Сервер: [адрес, доступ]. Хостинг или облако: [поддержка]. Подрядчик: [телефон].

        Разбор сбоя

        Пишется в течение рабочего дня после инцидента, пока детали свежи. Объём хватит на страницу.

        Разбор сбоя (постмортем)Шаблон
        Разбор сбоя [дата]
        
        Краткое резюме
        [Одно-два предложения: что произошло и как долго]
        
        Влияние
        Сколько минут сайт был недоступен или работал с ошибками: [N]. Сколько заявок или заказов потеряно по оценке: [N]. Кого затронуло: [все, часть посетителей, один раздел].
        
        Хронология (время по часам сервера)
        [чч:мм] выкладка релиза [номер]
        [чч:мм] первый сигнал: [оповещение, звонок клиента, график]
        [чч:мм] решение откатить
        [чч:мм] сайт восстановлен
        
        Причины
        Что сработало как триггер: [изменение, сбой, нагрузка].
        Почему оно дошло до боя: [какая проверка отсутствовала или не сработала].
        
        Что сработало хорошо
        [откат, оповещение, инструкция]
        
        Что не сработало
        [нет теста на сценарий, оповещение пришло поздно, нет доступа]
        
        Действия
        № | Что сделать | Ответственный | Срок | Статус
        1 | [действие] | [имя] | [дата] | [открыто]
        
        Правило разбора: ищем слабое место процесса. Имя автора правки в тексте не обязательно, если оно ничего не добавляет к причине.

        17FAQ

        Частые вопросы про DevOps для сайта

        Что такое DevOps простыми словами?

        Это способ работать, при котором разработка и эксплуатация сайта выстроены в один процесс. Код хранится в Git, проверяется автоматически, выкладывается одной командой и откатывается за секунды, а за состоянием сайта следят метрики и оповещения. Результат для владельца: обновления выходят без ночных авралов, а о сбое команда узнаёт раньше клиентов.

        С чего начать, если сайт на 1С-Битрикс?

        С двух шагов. Первый: положить в Git собственный код, обычно каталог /local/, и закрыть ядро и каталог upload в .gitignore. Второй: поставить оповещение о недоступности сайта и формы заявки. Дальше добавляют миграции базы, чтобы инфоблоки и свойства одинаково появлялись на тесте и в бою, затем выкладку по ссылке и автопроверки.

        Сколько стоит внедрение CI/CD?

        На странице услуги Hated.Ru на 17 октября 2026 года указаны три пакета. Базовый от 40 000 ₽ и от 1 недели: сборка, автотесты, деплой на тестовую среду. Расширенный от 70 000 ₽ и от 2 недель: добавляется деплой на продакшн, стратегии откатов, Docker, мониторинг и алерты. Enterprise от 120 000 ₽ и от 3 недель: мультисредовый деплой, Blue-Green и Canary, инфраструктура как код. Отдельные задачи по часам, от 3 500 ₽. Итоговую цену мы называем после разбора проекта.

        Сколько времени занимает внедрение?

        Подрядчик с готовыми наработками делает базовый конвейер от одной недели, расширенный от двух, полный от трёх. Своими силами за квартал проходят все шесть ступеней в темпе, который показан в плане на 90 дней. Самая долгая часть чаще всего касается людей: нужно договориться о правилах веток и ревью.

        В чём разница между CI, непрерывной доставкой и непрерывным развёртыванием?

        CI это автоматическая сборка и проверка каждого изменения. Непрерывная доставка добавляет к этому готовый к выкладке релиз, который выкладывают по решению человека. При непрерывном развёртывании выкладка на боевой сайт происходит сама, когда проверки пройдены. Для большинства сайтов подходит доставка: кнопка остаётся у ответственного за релиз.

        Нужен ли Docker сайту на Битриксе или WordPress?

        Часто нет. Если у всей команды проект собирается одинаково на виртуальной машине или хостинге, контейнеры добавляют обслуживание без заметной пользы. Они нужны, когда окружения расходятся, новому разработчику сложно поднять проект или у приложения несколько сервисов.

        Нужен ли Kubernetes интернет-магазину?

        Магазину на одном сервере с предсказуемой нагрузкой Kubernetes обычно не нужен: хватает Docker Compose и хорошего сервера с резервной копией. Его стоит рассматривать, когда сервисов несколько, нагрузка сильно скачет или несколько команд выкладывают независимо. Таблица для решения есть в разделе про Kubernetes.

        Можно ли выкладывать обновления без простоя?

        На уровне файлов да: в замере на 4000 файлов переключение ссылки не дало ни одной ошибки. Замена каталога держала сайт недоступным около 1,2 секунды. Полный ноль зависит ещё от базы данных, кэша PHP и браузера посетителя, поэтому слова «ноль простоя» без оговорок я не использую. Для строгих требований есть Blue-Green и Canary.

        Как откатиться, если в релизе была миграция базы?

        Откат кода базу назад не возвращает. Поэтому миграции делают в два шага: сначала добавляют колонки и таблицы, которые не мешают старому коду, потом выкладывают новый код, а лишнее удаляют в следующем релизе. Перед миграцией делают бэкап и проверяют, что он восстанавливается.

        Работают ли Terraform и Docker Hub в России?

        С ограничениями. С 30 мая 2024 года Docker Hub недоступен для пользователей из России, поэтому нужны зеркало или свой реестр. HashiCorp закрыла российским адресам доступ к реестру провайдеров, для Яндекс Облака есть зеркало. Реестр OpenTofu с 28 августа 2024 года закрыт для российских адресов и без провайдеров российских облаков. Условия могут измениться, проверяйте их до релиза.

        Как понять, что процесс стал лучше?

        По пяти метрикам DORA: время от коммита до боя, частота выкладок, время восстановления, доля неудачных выкладок и доля внеплановых выкладок. Скрипт для подсчёта есть в разделе про мониторинг. Сравнивайте текущий месяц с прошлым.

        Чем DevOps-инженер отличается от системного администратора?

        Системный администратор обслуживает серверы и сеть. DevOps-инженер отвечает за путь кода от коммита до боя: конвейер, окружения, откат и мониторинг, и описывает инфраструктуру кодом. На небольшом проекте эти роли нередко совмещает один человек.

        Кто отвечает за конвейер после внедрения?

        Нужен владелец: человек, который обновляет зависимости, меняет ключи, смотрит на красные сборки и знает, где лежит регламент отката. Это может быть сотрудник вашей команды после обучения или подрядчик на сопровождении. На странице инфраструктуры Hated.Ru сопровождение указано от 25 000 ₽ в месяц.

        18Прозрачность

        Что проверено в этой статье, а что нет

        Все примеры кода запускались или проверялись инструментами в тестовой среде. Ниже честный список: где проверка была настоящей, а где она остановилась на разборе синтаксиса.

        МатериалЧем провереноЧего не делалось
        Конвейеры GitHub Actions и GitLab CIПроверка по схемам файловЗапуск на реальных сервисах
        Скрипт выкладки и откатаShellCheck, запуск в песочнице: восемь выкладок, откат, список релизовЗапуск на боевом сервере с nginx
        Замер способов выкладкиТри прогона на 4000 файлов, скрипты приведены в статьеПроверка с PHP, базой данных и кэшем браузера
        Dockerfile и ComposeHadolint и команда docker compose configСборка образа и запуск контейнеров
        Манифест KubernetesПроверка по схемам Kubernetes 1.37Применение в кластере
        TerraformРазбор парсером HCLinit, plan и создание ресурсов в облаке
        AnsibleПроверка синтаксиса в ansible-core 2.19Запуск на реальной машине
        Правила PrometheusРазбор выражений парсером PromQLРабота с реальными метриками и трафиком
        Файлы .gitignoreКоманда git check-ignore в тестовом репозиторииПроверка на вашем проекте
        Скрипт DORAЗапуск на учебных данныхПроверка на журнале выкладок реального проекта
        Факты, даты, версииСверены с источниками ниже 17 октября 2026 годаСведения могут устареть: проверяйте их перед решением

        Цены Hated.Ru приведены со страниц услуг на ту же дату. Если вы заметите расхождение с актуальной ценой на сайте, ориентируйтесь на сайт.

        19Источники

        Источники

        Страницы открыты и сверены 17 октября 2026 года.

        1. DORA: метрики эффективности поставки ПО, определения пяти метрик и их группировка.
        2. DORA: история метрик, 2014 год для первых четырёх и 2024 год для пятой.
        3. HPE Developer: начало DevOps, доклад Flickr на Velocity 2009 и конференция в Генте.
        4. Kubernetes: релизы и сроки поддержки, таблица версий на 17 октября 2026.
        5. Prometheus 3.0, анонс от 14 ноября 2024.
        6. Cisco DevNet: четыре золотых сигнала, задержка, трафик, ошибки, насыщение.
        7. Alertmanager и Telegram: руководство по подключению.
        8. Яндекс Облако: ошибка 403 при установке провайдеров Terraform.
        9. Яндекс Облако: начало работы с Terraform, файл .terraformrc с зеркалом.
        10. Яндекс Облако: блокировки состояния Terraform.
        11. Spacelift: смена лицензии Terraform, даты BUSL, OpenTofu 1.6.
        12. Хабр: OpenTofu закрыл реестр для России, новость от 28 августа 2024.
        13. Security Lab: Docker Hub заблокировал российских пользователей, 30 мая 2024.
        14. Хабр: зеркало Docker Hub в GitVerse, 3 июня 2024.
        15. Elastic: Elasticsearch снова открытый, 29 августа 2024.
        16. Хабр: настройка проекта на 1С-Битрикс в Git, практика команды, 2021 год.
        17. OPcache и выкладки через символические ссылки, описание проблемы и двух способов её решить.
        18. Защита GitHub Actions: минимальные права токена, закрепление действий по хешу, секреты.
        19. Страницы услуг Hated.Ru: внедрение CI/CD, инфраструктура, Яндекс Облако и VK Cloud.
        Д

        Дени Булатов

        Разработчик Hated.Ru: frontend на React, backend на Python, PostgreSQL

        Работает в студии, которая делает сайты на 1С-Битрикс, WordPress, Tilda и на чистом коде и предлагает клиентам внедрение CI/CD и настройку инфраструктуры. Примеры и скрипты из статьи приведены целиком, их можно повторить у себя.

        Страница автора на hated.ru

        Hated.Ru · DevOps

        Покажем, какие ступени нужны вашему сайту

        Пришлите адрес сайта и расскажите, как вы выкладываете обновления сейчас. Скажем, с какой ступени начать, сколько это займёт и сколько будет стоить, до начала работ. Работаем по всей России, офисы в Москве, Туле и Рязани.

        Внедрение CI/CDот 40 000 ₽, от 1 недели Аудит инфраструктурыот 30 000 ₽, от 5 дней Настройка Яндекс Облака и VK Cloudот 30 000 ₽, от 3 дней
        +7 985 220-54-74 +7 495 220-54-74 Deni@hated.ru

        Тула, Московское шоссе, 2в, офис 3. Рязань, 2-й Школьный переулок, 1. Москва, МКАД, 60-й километр, 4а.

        Дополнительно

        Освоение 1С-Битрикс: Разбираемся в сложнох подготовки платформы к работе

        Система управления сайтом 1С-Битрикс считается одним из лидирующих решений для создания и поддержки интернет-проектов различной сложности. Однако начинающим пользователям может показаться непростой задачей подготовить платформу к полноценной работе. Рассмотрим ключевые шаги, которые помогут справиться с этой задачей.

        Первый и, пожалуй, самый важный этап – выбор и заказ хостинга. Для стабильной работы 1С-Битрикс требуется внимательно подойти к требованиям системы к серверу. Помимо стандартных параметров, таких как PHP и MySQL, стоит учитывать поддержку технологий, таких как memcached и поддержка отправки почты через SMTP.

        • Комментарии
        Загрузка комментариев...
        деплой сайта выкладка сайта без простоя откат релиза как задеплоить сайт автоматический деплой сайта деплой через symlink
        Назад к списку
        • Блог о 1С Битрикс 3
        • Полезное 6
        • Статьи о SEO продвижении сайтов 17
        • Статьи о контекстной рекламе - Блог Hated.ru 7
        • Статьи о разработки сайтов 13
        1с битрикс 1С‑Битрикс ai agent n8n claude code в vs code claude code для начинающих CMS 2026 css design engineer digital marketing services digital стратегия 2026 digital стратегия для оффлайн бизнеса Drupal E-E-A-T для сайта E-E-A-T что это EEAT EEAT SEO generative engine optimization GEO geo seo 2026 GEO продвижение geo продвижение GEO-оптимизация Яндекс gigachat api бесплатно Hated.ru hated.ru headless CMS html JavaScript Joomla js metrika n8n google calendar n8n openrouter n8n telegram бот n8n бесплатно n8n ии агент OpenCart pandas read_excel sape покупка ссылок Schema.org SEO SEO 2026 seo vs geo SEO аудит seo без накрутки пф SEO для AI 2026 SEO продвижение seo продвижение SEO продвижение Битрикс цена SEO продвижение интернет-магазина seo продвижение сайта seo услуги SEO услуги Битрикс seo-оптимизация Tilda ugc видео цена ugc креатор цена ugc под ключ ui аудит сайта ux ui аудит ux аудит сайта перед редизайном vk клипы для бизнеса WordPress xlwings python excel Yandex автозаполнение excel python автоматизация автоматизация поддержки нейросетью автоматический деплой сайта автостратегии яндекс директ Алиса AI аудит перед контекстной рекламой аудит сайта перед запуском рекламы аудит сайта перед редизайном безопасная покупка ссылок белое seo продвижение бесплатные ии агенты бесплатные ии платформы бесплатный ии агент для бизнеса библиотека openpyxl Бизнес бот с проверкой подписки бренд и seo брендовые запросы брендовый трафик бриф для ugc креатора веб-дизайн Веб-разработка видимость в ответах нейросетей визуал для ozon влияние искусственного интеллекта на бизнес внедрение ии в бизнес внедрение нейросети вордстат яндекс статистика воронка подписки max выбор CMS выкладка сайта без простоя где публиковать короткие видео генерация отчетов python генерация фото товара гео продвижение сайта геолокация деплой сайта деплой через symlink дизайн карточек wildberries дизайн-инженер дизайнер и нейросети дизайнер с ии доверие сайта Google доноры sape дорожная карта digital стратегии е е а т заказать SEO продвижение Битрикс заказать ugc видео запросы яндекс вордстат ии в бизнес-процессах ИИ-помощник Яндекс Карт ИКС инструкция claude code интеграция нейросети в crm интернет-маркетинг инфографика для маркетплейсов нейросеть информационная безопасность искусственный интеллект искусственный интеллект в маркетинге искусственный интеллект в рекламе ИТ-инфраструктура как автоматизировать excel python как внедрить E-E-A-T как задеплоить сайт как запустить рекламу как не попасть под минусинск как нейросеть выбирает компании как покупать ссылки в sape как пользоваться вордстатом как сделать сайт с claude code как улучшить дизайн сайта карточки товара нейросетью клипы без лица Лайфхаки Начинающим Обработка данных Персональные данные Продвижение сайта РКН Яндекс ИСК Яндекс нейро продвижение Яндекс Тег-менеджер Яндекс.Браузер бизнес ЯндексGPT оптимизация
        Проекты
        Разработка сайтов на Drupal
        Брендинг и айдентика
        Портфолио: сайты на 1С‑Битрикс
        Портфолио: сайты на CMS Tilda
        Разработка сайта на "Чистом коде"
        Фото компании
        Битрикс 24
        Готовые шаблоны для cms 1c Bitrix
        Услуги
        Разработка сайтов на CMS "1с Битрикс"
        Контекстная реклама
        SEO продвижение сайтов
        Дизайн "Брендинг"
        Разработка сайтов на CMS "Wordpress"
        Разработка сайтов на конструкторе "Tilda"
        Создание быстрого сайта без CMS
        DevOps
        SMM
        UI/UX разработка \ дизайн
        Разработка под Telegram
        Таргетированная реклама
        Статьи
        Новости
        О компании
        Сертификаты
        Реквизиты
        +7 985 220-54-74
        +7 985 220-54-74 mts.svg
        +7 495 220-54-74 mts.svg
        E-mail
        info@hated.ru
        Адрес
        г. Тула, Московское шоссе , д.2в, офис № 3
        Рязань, 2-й Школьный переулок, 1
        Москва, МКАД 60й километр 4а
        Режим работы
        Ежедневно: с 9:00 до 20:00
        info@hated.ru
        г. Тула, Московское шоссе , д.2в, офис № 3
        Рязань, 2-й Школьный переулок, 1
        Москва, МКАД 60й километр 4а
        © 2026 Разработка сайтов, контекстаная реклама,веб-дизайн hated
        ООО "ХАТЕД"
        ИНН/КПП 7100009120 / 710001001
        ОГРН: 1217100008960

        Политика конфиденциальности
        Версия для слабовидящих
        Карта сайта
        Главная Услуги Контакты Проекты Акции
        Регион сайта: Россия