Cloud-init давно стал стандартным механизмом первичной инициализации виртуальных машин. На практике он отвечает за то, что сервер при первом запуске получает hostname, пользователей, SSH-ключи, сетевые параметры, пакеты, файлы и команды инициализации. Но как только что-то идет не так, начинается знакомый сценарий: инстанс поднялся, доступ по SSH есть, а user-data будто проигнорирован.
В Debian и Ubuntu почти все такие проблемы упираются в четыре вещи: выбранный datasource, кэш cloud-init, семантика first boot и порядок выполнения модулей. Если понимать, как cloud-init принимает решение «это новый инстанс» или «я уже все делал», диагностика становится заметно проще.
Отдельно важно помнить: cloud-init — это не просто «скрипт на старте». Это набор этапов и модулей, которые исполняются в разное время загрузки. Поэтому ответ на вопрос «почему не создался пользователь, хотя команда есть в user-data» часто лежит не в YAML, а в том, что модуль уже был помечен как выполненный.
Если вы готовите шаблоны VM для CI, staging или production, эти детали особенно важны. Один неправильно очищенный образ — и новые виртуальные машины унаследуют старый instance-id, старый кэш или неожиданный datasource.
Как cloud-init мыслит: этапы запуска и логика first boot
На Debian и Ubuntu обычно работают сервисы cloud-init-local.service, cloud-init.service, cloud-config.service и cloud-final.service. Они стартуют последовательно и решают разные задачи: определение datasource, чтение metadata и user-data, применение сетевых параметров и поздний запуск модулей вроде установки пакетов и runcmd.
Термин first boot для cloud-init означает не просто «первый запуск после включения VM». Это первый запуск конкретного инстанса с точки зрения его идентичности. Обычно она определяется через instance-id, который приходит из datasource. Если cloud-init считает, что этот инстанс ему уже знаком, часть модулей повторно не выполняется.
Cloud-init запоминает не факт загрузки ОС, а состояние конкретного инстанса. Поэтому клон диска без очистки состояния часто ведет себя как уже инициализированная машина.
Из этого следует простое правило: если вы делаете шаблон из уже запущенной VM, перед публикацией нужно корректно очистить состояние cloud-init. Иначе новый сервер может не восприниматься как новый инстанс.
Что такое datasource и почему из-за него user-data не применяется
Datasource — это источник, из которого cloud-init получает metadata, user-data, vendor-data и instance-id. В зависимости от платформы это может быть NoCloud, ConfigDrive, OpenStack, EC2-совместимый источник или metadata-служба гипервизора.
Когда администратор говорит «cloud-init не применил user-data», очень часто реальная причина в другом: cloud-init не нашел ожидаемый datasource или выбрал не тот. Например, вы рассчитывали на NoCloud seed, а система определила другой источник или ушла в DataSourceNone.
Проверять datasource нужно в первую очередь:
cloud-init status --long
cloud-id
journalctl -u cloud-init -u cloud-config -u cloud-final -b
grep -R "DataSource" /var/log/cloud-init.log /var/log/cloud-init-output.log
Команда cloud-id часто сразу показывает, какой источник определился. Если ответ none, cloud-init либо не нашел datasource, либо инициализация сломалась слишком рано.
Дополнительно полезно посмотреть, что cloud-init видит прямо сейчас:
cloud-init query ds
cloud-init query userdata
cloud-init schema --system
На актуальных версиях Debian и Ubuntu этого обычно достаточно, чтобы быстро понять: проблема в получении данных или уже в их применении.

Типичные симптомы проблем с datasource
Не применяются SSH-ключи, хотя сама система загружается штатно.
Hostname остается дефолтным, хотя задан в metadata.
runcmdиwrite_filesне срабатывают совсем.В логах видны таймауты при поиске metadata endpoint.
Cloud-init определяет
DataSourceNoneили неожиданно выбирает другой источник.
Если платформа известна заранее, иногда полезно ограничить список допустимых datasource. Но делать это нужно аккуратно: слишком жесткая настройка удобна для одного облака и мешает переносимости образа в другом.
Где cloud-init хранит состояние: каталог /var/lib/cloud
Главная точка расследования — каталог /var/lib/cloud. Здесь хранится runtime-состояние cloud-init: сведения об инстансе, кэш metadata, результаты работы модулей и маркеры того, что уже было выполнено.
Когда cloud-init «упорно не делает то, что должен», почти всегда стоит заглянуть именно сюда:
find /var/lib/cloud -maxdepth 3 -type f | sort
ls -la /var/lib/cloud
ls -la /var/lib/cloud/instance
ls -la /var/lib/cloud/instances
Обычно интерес представляют такие зоны:
/var/lib/cloud/instance— ссылка на текущий инстанс./var/lib/cloud/instances/...— данные, привязанные к конкретномуinstance-id./var/lib/cloud/seed/— seed-данные для некоторых datasource, например NoCloud./var/lib/cloud/data/— общая служебная информация и маркеры загрузок./var/lib/cloud/sem/и семафоры внутри instance-каталога — признаки выполнения отдельных модулей.
Именно эти файлы объясняют, почему повторный запуск не приводит к повторному применению конфигурации. Cloud-init считает, что нужный модуль уже завершался ранее, и не выполняет его второй раз без явной очистки состояния или смены идентичности инстанса.
Если вы регулярно собираете шаблоны, советую держать под рукой и материал про подготовку golden image с cloud-init: именно на этапе упаковки образа обычно закладываются будущие проблемы с first boot.
Почему first boot не считается первым: частые причины
Самый частый сценарий — создание собственного шаблона. Вы поднимаете VM, устанавливаете пакеты, настраиваете систему, делаете snapshot и ожидаете, что каждая новая машина пройдет полноценную первичную инициализацию. Но если cloud-init не очищен, новый инстанс получает старое состояние.
Вторая типичная причина — платформа или гипервизор отдает один и тот же instance-id при пересоздании. Тогда cloud-init честно считает, что это прежний инстанс, и не запускает одноразовые шаги повторно.
Третья причина — ручной дебаг с обычными перезагрузками. Администратор меняет user-data, делает reboot и ждет результата. Но cloud-init не обязан перечитывать и полностью переисполнять конфигурацию после каждого старта.
Наконец, есть банальные ошибки в самом user-data: отсутствует заголовок #cloud-config, сломан YAML, используются табы вместо пробелов, либо команда выполняется на стадии, где еще нет сети, DNS или доступного пакетного менеджера.
Что из user-data выполняется один раз, а что нет
Это важный источник путаницы. Не весь user-data трактуется одинаково. Некоторые модули cloud-init рассчитаны на одноразовое применение на инстанс, некоторые имеют другую семантику, а shell-скрипты в user-data зависят от типа содержимого и фазы выполнения.
Например, runcmd обычно выполняется на финальном этапе и не предназначен как механизм «запускать на каждом boot». Если нужна логика на каждую загрузку, лучше создавать systemd unit через cloud-init, а не пытаться использовать cloud-init как замену постоянным системным механизмам.
Пошаговый debug cloud-init в Debian и Ubuntu
Когда нужно быстро понять, почему конфигурация не применилась, я рекомендую идти коротким маршрутом: статус, datasource, логи, кэш, и только потом очистка и повторный запуск. Это экономит время и не уничтожает полезные следы слишком рано.
1. Проверяем статус сервисов
systemctl status cloud-init-local.service cloud-init.service cloud-config.service cloud-final.service
cloud-init status --long
Статус done не означает, что все применилось успешно. Он говорит только о том, что жизненный цикл cloud-init завершен.
2. Смотрим логи
tail -n 200 /var/log/cloud-init.log
tail -n 200 /var/log/cloud-init-output.log
journalctl -b | grep cloud-init
/var/log/cloud-init.log показывает внутреннюю логику модулей, работу с datasource и семафорами. /var/log/cloud-init-output.log особенно полезен для диагностики ошибок в командах из runcmd.
3. Проверяем реально выбранный datasource
cloud-id
cloud-init query ds
grep -n "DataSource" /var/log/cloud-init.log
Если datasource не тот, дальнейший разбор YAML мало поможет. Сначала исправляем источник metadata.
4. Проверяем, что cloud-init увидел как user-data
cloud-init query userdata
cloud-init query instance_id
cloud-init query --all
Это помогает быстро разделить две проблемы: данные вообще не прочитаны или они прочитаны, но не применены.
5. Изучаем маркеры в /var/lib/cloud
find /var/lib/cloud -type f | sort | less
find /var/lib/cloud -type f | grep -E "sem|instance-id|result|status"
Если нужные семафоры уже есть, становится понятно, почему повторная перезагрузка ничего не меняет.
6. Проверяем синтаксис cloud-config
cloud-init schema --config-file /root/user-data.yaml
Эта проверка отлично ловит ошибки в YAML, особенно в блоках write_files, users, apt и многострочных командах.
Если вы разворачиваете машины в облаке, где нужен полный контроль над окружением, удобнее тестировать такие сценарии на отдельном VDS, а не на боевом шаблоне.

Когда нужен cloud-init clean и что он реально делает
Команда cloud-init clean — это не волшебная кнопка починки, а инструмент очистки локального состояния. Она удаляет кэш, служебные маркеры и подготавливает систему к повторному восприятию инстанса или новому циклу инициализации.
Базовый вариант:
cloud-init clean
reboot
Полезно знать и расширенные варианты, если они доступны в вашей версии пакета:
cloud-init clean --logs
cloud-init clean --machine-id
cloud-init clean --seed
Практический смысл опций такой:
--logsочищает логи, но для дебага использовать его лучше только после сохранения нужной информации.--machine-idполезен при подготовке шаблонов, чтобы не тащить идентичность системы дальше.--seedзатрагивает seed-данные и нужен не во всех сценариях.
После clean обычно выполняют перезагрузку. Но если datasource по-прежнему недоступен или отдает тот же instance-id, очистка лишь маскирует настоящую причину.
Если запускать
cloud-init cleanпосле каждой неудачной попытки, можно быстро потерять ключевые следы для диагностики. Сначала соберите логи и статус, потом очищайте состояние.
Безопасный повторный прогон cloud-init при тестировании
Во время отладки часто хочется быстро проверить измененный user-data без пересоздания VM. Это возможно, но важно не путать повторный прогон модулей с реальным сценарием первого старта. Некоторые модули неидемпотентны: могут повторно создать файлы, поменять конфиг или попытаться переустановить пакеты уже в другом состоянии системы.
Для аккуратного теста лучше использовать временную VM или snapshot. Базовый порядок такой:
Соберите текущие логи и статус.
Сохраните копию исходного
user-data.Выполните
cloud-init clean.Перезагрузите систему.
Сразу после загрузки проверьте статус и логи.
Для лабораторной отладки отдельных стадий существуют и ручные команды:
cloud-init init
cloud-init modules --mode=config
cloud-init modules --mode=final
Но это инструмент для понимания механики, а не полная замена реального first boot. Если нужен еще один прикладной сценарий, посмотрите разбор отладки user-data на VDS.
Практические причины, почему user-data не применяется
Неверный формат user-data
Для cloud-config нужен заголовок #cloud-config. Отступы YAML критичны. Табуляции недопустимы. Особенно часто ошибки встречаются в многострочных блоках и вложенных структурах.
Datasource недоступен или выбран неверно
Metadata endpoint не отвечает, NoCloud seed не найден, ConfigDrive не смонтирован или cloud-init выбирает неожиданный источник.
Кэш содержит старое состояние
Клон VM или шаблон был снят после уже выполненного cloud-init. Нужные модули помечены как завершенные, поэтому повторно не запускаются.
Команды выполняются не на той стадии
Некоторые задачи требуют полностью готовой сети, DNS или свободного пакетного менеджера. Если логика помещена в неподходящую фазу, результат будет нестабильным.
Ошибка внутри самой команды
Cloud-init мог дойти до runcmd, но конкретная shell-команда завершилась с ошибкой. Тогда внешне кажется, что user-data не применился, хотя проблема в правах, путях, окружении или доступности сети.
Как готовить шаблон Debian или Ubuntu правильно
Если вы собираете собственные образы для виртуализации, самое важное — выпускать их в нейтральном состоянии. Иначе отладка новых VM быстро превращается в лотерею.
Перед snapshot или публикацией шаблона обычно стоит:
проверить, что cloud-init установлен и включен;
убедиться, что datasource для целевой платформы поддерживается;
очистить состояние cloud-init;
при необходимости корректно очистить
machine-id;не оставлять временные ключи, токены и отладочные файлы.
Если вы разворачиваете такие образы на облачной инфраструктуре, заранее проверьте, как именно платформа передает metadata и меняется ли instance-id между пересозданиями. Именно в этом нюансе чаще всего скрывается причина странного поведения first boot.
Минимальный runbook для аварийной диагностики
Когда времени мало, можно идти по короткому сценарию:
cloud-init status --long
cloud-id
journalctl -u cloud-init -u cloud-config -u cloud-final -b
tail -n 100 /var/log/cloud-init.log
tail -n 100 /var/log/cloud-init-output.log
cloud-init query --all
find /var/lib/cloud -maxdepth 3 -type f | sort
Если стало ясно, что причина в старом состоянии, а не в datasource и не в синтаксисе, тогда:
cloud-init clean
reboot
После перезагрузки повторяем те же проверки и сравниваем результат. Такой подход почти всегда быстрее, чем сразу лезть в ручное редактирование внутренних файлов cloud-init.
Итоги
В Debian и Ubuntu cloud-init чаще всего «ломается» не сам по себе, а из-за непонимания его модели состояния. Сначала он должен найти правильный datasource, затем распознать инстанс как новый, а потом применить user-data в нужных стадиях. Все, что мешает хотя бы одному из этих шагов, выглядит как «cloud-init ничего не сделал».
Поэтому рабочая стратегия всегда одна: проверить datasource, посмотреть логи, изучить /var/lib/cloud, понять, был ли это действительно первый запуск, и только потом использовать cloud-init clean. Для шаблонов и golden image это особенно критично, потому что ошибка легко тиражируется на весь парк VM.
Если держать в голове эту модель, отладка cloud-init перестает быть магией и превращается в обычную инженерную задачу с понятным порядком действий.


