Экспресс-курс · No. 20

Две идеи изменили то, как софт попадает с ноутбука разработчика к реальным пользователям. Первая — автоматизировать весь путь от кода до прода, чтобы выкатка была скучной, а не ужасающей: это CI/CD. Вторая — упаковать приложение со всем, что ему нужно, чтобы оно работало одинаково везде: это контейнеры. Вместе они превращают «работает у меня на машине» из отговорки в план.

Только суть · Один образ на идею · Выучи слова

§ 01

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

Ручные деплои медленны и страшны

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

Старый способ выкатывать — человек, выполняющий шаги руками: скопировать файлы на сервер, выполнить команды, скрестить пальцы. Это медленно, чревато ошибками и так рискованно, что команды деплоят редко, — что делает каждый релиз огромным, что делает его ещё рискованнее. Деплой (deploy) — это просто «сделай новую версию живой», но руками он становится самым стрессовым моментом недели. Вся область CI/CD существует, чтобы сделать этот момент скучным.

«Работает у меня на машине» — классический сбой

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

Код, что отлично работает на ноутбуке разработчика, часто ломается в проде, потому что две среды различаются — разные версии, настройки, библиотеки, операционная система. «Работает у меня на машине» (it works on my machine) — это панчлайн для целого класса багов, вызванных этими зазорами. Глубже лежит то, что ноутбук и сервер — не одно и то же место, так что «работало там, где я собрал» не гарантирует ничего о том, где оно запущено.

Выкатка должна быть скучной

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

Цель всего в этом курсе — сделать выкатку скучной: настолько автоматизированной, проверенной и повторяемой, чтобы ты выпускал маленькие изменения много раз в день без драмы. Контринтуитивно, деплоить чащебезопаснее: каждый релиз крошечный, так что если что-то ломается, причина очевидна, а починка мала. Редкие, гигантские, ручные релизы — вот опасные. «Скучный» — высший комплимент, что деплой может заслужить.

Ручные деплои медленны, рискованны и редки — что делает их рискованнее. Цель обратна: выкатка настолько автоматическая и малая, что она скучна, делается много раз в день.

§ 02

Первая половина CI/CD — про то, чтобы ловить проблемы в тот миг, как они появляются, автоматически собирая и тестируя каждое изменение, как оно прилетает, — чтобы общая кодовая база всегда работала.

Каждое изменение собирается и тестируется автоматически

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

Непрерывная интеграция (continuous integration, CI) значит, что каждый раз, как кто-то предлагает изменение, автоматическая система собирает проект и гоняет по нему тесты — за минуты, не тронутая человеческими руками. Если изменение ломает сборку или валит тест, ты узнаёшь сразу, пока оно мало и свежо в голове. CI — это всегда-включённый страж, что проверяет каждый вклад, прежде чем он сможет натворить бед.

Лови поломку, пока она мала

Заметить один криво положенный кирпич легко починить; обнаружить его лишь после постройки трёх этажей сверху — катастрофа. Рано — дёшево; поздно — дорого.

Ценность CI — в тайминге. Баг, пойманный в миг внесения, тривиально починить — ты точно знаешь, что изменилось. Тот же баг, обнаруженный недели спустя, погребённый под всем, что построено сверху, может занять дни на распутывание. Тестируя каждое изменение сразу, CI держит проблемы крошечными и прослеживаемыми. Он сдвигает обнаружение поломки с «много позже, смешано со всем» на «прямо сейчас, изолированно».

Держи главную ветку всегда рабочей

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

Глубже цель CI — держать главную ветку (main branch), общую доверенную версию, всегда в рабочем состоянии. Изменение присоединяется к main, лишь если собирается и проходит тесты, так что main никогда не становится сломанным месивом, что блокирует всю команду. Это и делает возможной частую, уверенную выкатку: когда main всегда работает, можно выпускать из него в любой миг, не распутывая сперва чью-то сломанную недоделанную работу.

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

§ 03

Если CI следит, что каждое изменение хорошо, CD автоматизирует доставку этого хорошего изменения пользователям — превращая релиз из ручного ритуала в кнопку или вовсе без кнопки.

Автоматизируй релиз, а не только тестирование

Заводская линия, что не останавливается на проверке качества, а несёт одобренный продукт до самого погрузочного дока и в грузовик, — никто не таскает коробки руками.

Непрерывная доставка (continuous delivery, CD) расширяет CI: после того как изменение проходит все автоматические проверки, система также автоматически готовит его к релизу и может выкатить в прод. Ручные, чреватые ошибками шаги деплоя становятся кодом, что выполняется одинаково каждый раз. CI доказывает, что изменение хорошо; CD доставляет это хорошее изменение пользователям без человека, аккуратно печатающего команды в сервер в полночь.

Доставка против развёртывания

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

Есть тонкое разделение. Continuous delivery (непрерывная доставка) значит, что каждое хорошее изменение автоматически делается готовым к деплою, но человек жмёт кнопку, чтобы выпустить. Continuous deployment (непрерывное развёртывание) идёт дальше: каждое прошедшее изменение выкатывается в прод автоматически, вообще без кнопки. Оба — «CD»; разница в том, одобряет ли человек финальный шаг. Команды выбирают по тому, насколько доверяют своим автоматическим проверкам.

Артефакт — это то, что ты отгружаешь

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

Пайплайн производит артефакт (artifact) — единый, собранный, версионированный пакет твоего приложения, готовый запускаться. Ты собираешь его раз, и ровно тот же артефакт движется через тестирование, staging и прод, так что то, что ты тестировал, — в точности то, что ты отгружаешь. Это ключ к надёжности: если пересобирать на каждом этапе, могли бы вкрасться тонкие различия. Один артефакт, собранный раз, развёрнутый везде, убирает целый класс сюрпризов «но в тестах же прошло».

Непрерывная доставка автоматизирует релиз каждого хорошего изменения как единого собранного артефакта — жмёт ли человек финальную кнопку (delivery) или оно отгружается само (deployment).

§ 04

CI и CD приводит в действие пайплайн: автоматическая сборочная линия, через которую изменение едет от коммита до прода, останавливаясь, если что-то идёт не так по пути.

Пайплайн — это автоматическая сборочная линия

Линия автозавода: шасси движется через сварку, покраску и проверку по порядку, каждая станция делает своё дело, прежде чем передать дальше, — ни один шаг не пропущен, ничего не таскают руками.

Пайплайн (pipeline) — это последовательность автоматических этапов, через которые проходит изменение на пути в прод, — обычно build (сборка), потом test (тесты), потом deploy (деплой). Коммит входит в один конец и, если всё хорошо, живой релиз выходит из другого, без человеческих шагов между. Пайплайн — это конкретная машина, что делает CI/CD реальным: вот где «автоматизировать весь путь» реально живёт, как заданные, повторяемые этапы.

Падай быстро: останови линию на проблеме

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

Хороший пайплайн построен, чтобы падать быстро (fail fast): если сборка ломается или тест валится на любом этапе, пайплайн останавливается, и изменение не идёт дальше. Сломанное изменение никогда не достигает прода, потому что линия встаёт в миг, как его поймали. Это свойство безопасности всей конструкции — каждое изменение должно пройти через каждый шлюз, и один красный свет холодно останавливает релиз. Ничто бракованное не проскользнёт к пользователям.

Зелёный — езжай, красный — стоп

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

Пайплайны говорят на языке зелёного (прошёл) и красного (упал). Зелёный пайплайн значит, что каждая проверка прошла и изменению безопасно идти дальше; красный значит, что-то сломалось и должно быть починено, прежде чем что-то двинется. Этот простой сигнал заменяет тревожное ручное суждение однозначным шлюзом. Правило команды становится простым: никогда не строй на красном, никогда не отгружай на красном — держи пайплайн зелёным, и выкатка остаётся безопасной по умолчанию.

Пайплайн — это автоматическая сборочная линия от коммита до прода: build, test, deploy, — что падает быстро и встаёт на красном, чтобы ни одно сломанное изменение не проскользнуло.

§ 05

Другая половина истории решает «работает у меня» напрямую: упаковать приложение со всем, что ему нужно, в один блок, что работает одинаково везде. Этот блок — контейнер.

Контейнер пакует приложение со всем, что ему нужно

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

Контейнер (container) связывает твоё приложение вместе со всем, что ему нужно для работы, — кодом, библиотеками, настройками, нужными версиями — в один самодостаточный блок. Поскольку он несёт свою среду, он работает одинаково, куда бы ты его ни поставил: твой ноутбук, тестовый сервер, прод. Контейнер не зависит от того, что установлено на хосте, так что зазоры, что вызывают «работает у меня», просто закрываются.

Грузовой контейнер, что дал имя

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

Имя — это и метафора. До стандартных грузовых контейнеров груз грузили кусок за неуклюжим куском; стандартная коробка дала любому крану и любому кораблю обращаться с любым грузом одинаково. Программные контейнеры делают то же для приложений: стандартный блок, что любая совместимая система может запустить, не заботясь, что внутри. Docker — инструмент, что сделал это мейнстримом. Стандартизировать коробку — вот что заставляет всю логистическую цепочку — твой пайплайн — работать гладко.

Образ против контейнера: рецепт и блюдо

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

Два слова путают. Образ (image) — это упакованный, замороженный чертёж — собранный артефакт, содержащий твоё приложение и его среду, лежащий в хранилище. Контейнер — это работающий экземпляр, запущенный из этого образа. Один образ может запустить много одинаковых контейнеров, как один рецепт делает много одинаковых блюд. Ты собираешь образ раз в своём пайплайне, потом запускаешь из него сколько угодно контейнеров — и каждый одинаков.

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

§ 06

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

Оркестрация гоняет много контейнеров через много машин

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

Как только у тебя много контейнеров, размазанных по флоту серверов, что-то должно решать, где каждый работает, перезапускать те, что умерли, и балансировать нагрузку. Это оркестрация (orchestration), и доминирующий инструмент — Kubernetes. Ты говоришь ему желаемое состояние — «гоняй пять копий этого сервиса» — и он непрерывно работает, чтобы реальность совпала, размещая контейнеры, наблюдая за ними и исправляя дрейф. Это дирижёр, координирующий весь оркестр контейнеров.

Самовосстановление и масштабирование встроены

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

Оркестрация декларативна (declarative): ты описываешь цель, а не шаги. Скажи «всегда держи пять здоровых копий работающими», и если одна падает, система запускает замену автоматически — самовосстановление (self-healing). Если трафик всплёскивает, она может добавить копий и убрать их, когда спрос падает, — автомасштабирование (auto-scaling). Ты перестаёшь вручную нянчить серверы и вместо этого объявляешь, что хочешь, давая оркестратору держать систему там. Этот сдвиг от «делать» к «объявлять» — суть всего.

Накатывающие обновления выкатывают без простоя

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

Оркестрация даёт обновлять без простоя через накатывающее обновление (rolling update): она заменяет старые контейнеры новыми по несколько за раз, проверяя, что каждый здоров, прежде чем продолжить, так что сервис остаётся живым на протяжении. Если новая версия ведёт себя плохо, она может остановиться и откатиться (roll back) к старой автоматически. Так современные сервисы выкатывают обновления непрерывно без окна обслуживания — замена случается вживую, постепенно и обратимо.

Оркестрация (Kubernetes) гоняет много контейнеров через много машин: ты объявляешь желаемое состояние, а она самовосстанавливается, автомасштабируется и накатывает обновления без простоя.

§ 07

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

Всегда имей путь назад: откат

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

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

Снизь радиус взрыва: canary и blue-green

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

Два паттерна сжимают риск релиза. Деплой canary («канарейка») шлёт новую версию сперва малому срезу пользователей; если их метрики остаются здоровыми, ты выкатываешь её всем, а если нет — забираешь назад, задев почти никого. Blue-green держит две продакшен-среды и переключает трафик со старой (blue) на новую (green) разом, с мгновенным переключением назад при нужде. Оба превращают релиз из ставки «всё или ничего» в управляемый, обратимый шаг.

Опиши свою инфраструктуру как код

Чертёж, по которому можно точно отстроить весь дом заново, против дома, построенного по памяти, что никто не воспроизведёт, если он сгорит.

Та же дисциплина распространяется на сами серверы. Инфраструктура как код (infrastructure as code, IaC) значит определять свои серверы, сети и конфигурацию в файлах, а не кликая по консоли руками. Теперь вся твоя среда версионирована, ревьюируема и воспроизводима — её можно отстроить идентично, а изменения идут через тот же пайплайн, что и код. Это закрывает последний зазор, где «кто-то сменил настройку руками и забыл» мог бы отменить всю строгость в остальном.

Прежде чем полагаться на процесс деплоя
  • Автоматизирован ли он от края до края — от коммита до прода через пайплайн, а не руками? - Собирает и тестирует ли CI каждое изменение, держа main всегда рабочим? - То, что ты отгружаешь, — единый артефакт или контейнер, идентичный от теста до прода? - Можешь ли ты откатиться мгновенно к последней хорошей версии? - Выходят ли рискованные релизы постепенно — canary или blue-green, а не всё разом? - Сама ли инфраструктура — код — версионированная и воспроизводимая?
Слова, которыми ты теперь владеешь
  • deploy / release — сделать новую версию живой для пользователей. - CI (continuous integration) — авто-сборка и тест каждого изменения. - CD (continuous delivery / deployment) — автоматизировать релиз; кнопка или без кнопки. - pipeline / build / artifact / fail fast — автоматическая линия, её выход и её безопасность. - container / image / Docker — приложение со средой, чертёж, инструмент. - orchestration / Kubernetes / self-healing / rolling update — гонять контейнеры в масштабе, безопасно. - rollback / canary / blue-green / infrastructure as code — паттерны безопасного деплоя.
Признаки, что ты отгружаешь хорошо
  • Выкатка скучна — малая, автоматическая, делается часто, а не редкое страшное событие. - CI зелёный до того, как что-то мёржится, и main всегда работает. - Ты отгружаешь тот же артефакт/контейнер, что тестировал, до самого прода. - Плохой релиз — это быстрый откат, а не кризис. - Рискованные изменения выходят через canary или blue-green, а твоя инфра — это код.

Безопасная выкатка — это обратимая выкатка: автоматизируй пайплайн, отгружай один протестированный артефакт, держи откат в одно нажатие, выпускай рискованное постепенно и описывай инфраструктуру как код.

Конец экспресс-курса · 7 глав · выучи слова

Дальше — практика: возьми маленький проект, напиши Dockerfile, чтобы он работал в контейнере, и настрой пайплайн, что собирает и тестирует его на каждом изменении. Потом задеплой его, сломай и откатись. Вся философия щёлкает в тот миг, как релиз перестаёт ощущаться страшным, потому что ты знаешь, что линия его поймала, а ты можешь его отменить. Но держи одну мысль выше прочих: выкатка должна быть скучной. Автоматизируй путь, чтобы каждое изменение собиралось, тестировалось и деплоилось одинаково, и упакуй приложение, чтобы оно работало одинаково везде, — и «работает у меня на машине» наконец станет «работает везде».