Выберите продукт

Быстрый bootstrap VDS: cloud-init, user-data и автоматическая подготовка окружения

Как развернуть VDS за 10 минут и получить готовое окружение без ручных команд. Разбираем cloud-init: структура user-data, пользователи и SSH, пакеты, Nginx, firewall, systemd, перезагрузка и отладка. Даём рабочие примеры и разбор типичных ошибок.
Быстрый bootstrap VDS: cloud-init, user-data и автоматическая подготовка окружения

Быстрый, повторяемый и предсказуемый старт новой VDS — это не «приятно иметь», а базовая гигиена для администраторов и разработчиков. Если каждый сервер вы настраиваете руками, вы платите временем и рисками: от пропущенных пакетов до несовпадающих конфигураций. Решение — облачный провижининг на этапе первого запуска. В этой статье разбираем, как использовать cloud-init и user-data, чтобы за один бут получить: пользователей и SSH ключи, обновления и пакеты, базовую безопасность, Nginx и даже собственные systemd-сервисы.

Зачем cloud-init для VDS

cloud-init — это стандарт де-факто для начальной инициализации образов Linux в облаках и виртуалках. Его задача — один раз на первом старте собрать данные из источника (datasource), выполнить модули (создать пользователей, добавить SSH ключи, установить пакеты, применить write_files, запустить команды) и зафиксировать, что инстанс готов к работе.

Преимущества для VDS:

  • повторяемость: один YAML — десятки идентичных инстансов;
  • скорость: «всё готово» сразу после запуска;
  • прозрачность: провижининг документирован и версионируется;
  • безопасность: первичный доступ через SSH ключи, без открытых паролей;
  • минимум ручных шагов — меньше шансов на ошибку.

Как это работает: datasources и этапы

На первом старте cloud-init ищет источник метаданных. Для VDS обычно используется NoCloud или совместимые провайдерские источники. В простом варианте NoCloud — это пара файлов user-data и meta-data, доступных системе при старте. meta-data задаёт, например, instance-id и имя хоста, а user-data — сценарий провижининга.

Этапы работы (упрощённо):

  • Local: ранние действия до сети;
  • Network: сетевые настройки и доступ к datasources;
  • Config: применение user-data, создание пользователей, пакеты;
  • Final: отложенные команды, сервисы, сообщения.

Важно: cloud-init стремится быть идемпотентным для своих модулей и запускается один раз. Повторный «стартовый» прогон выполняют осознанно командой очистки состояния.

Пример YAML user-data для cloud-init на экране терминала

Анатомия user-data

Форматов несколько, но чаще всего используют #cloud-config (YAML). Он декларативен и охватывает 80% типовых задач: пользователи, SSH ключи, пакеты, файлы, команды, перезагрузки. Также возможны сценарии shell (text/x-shellscript) и многочастные MIME-пакеты, если нужно комбинировать.

Минимальный рабочий пример

#cloud-config
hostname: app-1
manage_etc_hosts: true
users:
  - name: deploy
    gecos: Deploy User
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAA...your_public_key... user@laptop
ssh_pwauth: false
disable_root: true
timezone: UTC
package_update: true
package_upgrade: true
packages:
  - htop
  - curl
  - fail2ban
  - nginx
  - ufw
runcmd:
  - ufw default deny incoming
  - ufw default allow outgoing
  - ufw allow OpenSSH
  - ufw allow 80
  - ufw allow 443
  - ufw --force enable
final_message: "cloud-init complete on $INSTANCE_ID"

После первого бутстрапа у вас будет пользователь deploy с ключом, доступ по паролю выключен, обновления применены, базовые пакеты установлены, файрвол настроен, Nginx готов к старту. Для большинства образов Ubuntu/Debian это уже «почти прод» начало.

Пакеты и обновления: что лучше — packages или runcmd

cloud-init предоставляет директивы package_update и package_upgrade, которые вызывают менеджер пакетов корректно и вовремя. Для установки конкретных пакетов используйте packages. Это предпочтительнее, чем запускать apt-get вручную в runcmd: меньше гонок за блокировки и проблем с порядком модулей.

Если требуется перезагрузка после крупных обновлений, используйте power_state:

power_state:
  mode: reboot
  message: "Rebooting after upgrades"
  timeout: 30
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

SSH ключи и пользователи без сюрпризов

Правильный минимум — бессессионный root и логин только по ключам. Это делается настройками ssh_pwauth: false и disable_root: true плюс создание одного-двух пользователей с ssh_authorized_keys. Для нескольких ключей достаточно перечислить их списком.

Если вам нужно добавить ключи позже, можно использовать write_files, но безопаснее хранить ключи в системе контроля версий как часть user-data и обновлять инстансы через пересоздание (Immutable подход) или конфигурационный менеджер. Изменение авторизации вручную на первом старте — источник дрейфа конфигурации.

Файлы и конфиги через write_files

write_files позволяет положить любой конфиг, задать права и владельца. Это удобно для единичных шаблонов или unit-файлов systemd.

write_files:
  - path: /etc/nginx/sites-available/default
    permissions: '0644'
    owner: root:root
    content: |
      server {
        listen 80 default_server;
        server_name _;
        root /var/www/html;
        index index.html;
        location /health {
          return 200 'ok\n';
        }
      }

Если конфигураций много или они зависят от окружения, подумайте о многочастном MIME, где часть будет cloud-config, а часть — text/x-shellscript для генерации файлов средствами шаблонизации, доступными в вашем окружении.

Команды: bootcmd, runcmd, инициализация сервисов

bootcmd запускается очень рано (при каждом старте), лучше не использовать его для прикладных задач. runcmd — «подходящее место» для одноразовых команд по итогам установки пакетов и файлов. Для управления сервисами (enable, restart) также подойдёт runcmd:

runcmd:
  - systemctl enable nginx
  - systemctl restart nginx

Для сложной логики используйте systemd units, положив их через write_files и активировав в runcmd — так поведение будет детерминированным и наблюдаемым средствами journalctl.

Пример: веб-окружение за один бут

Ниже — объединённый пример для быстрого веб-узла с Nginx, deploy-пользователем, UFW и автообновлениями безопасности.

#cloud-config
hostname: web-1
manage_etc_hosts: true
users:
  - name: deploy
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAA...your_public_key... user@laptop
ssh_pwauth: false
disable_root: true
timezone: Europe/Moscow
package_update: true
package_upgrade: true
packages:
  - nginx
  - ufw
  - fail2ban
  - unattended-upgrades
write_files:
  - path: /etc/apt/apt.conf.d/51unattended-upgrades-fast
    permissions: '0644'
    content: |
      Unattended-Upgrade::Automatic-Reboot "true";
      Unattended-Upgrade::Automatic-Reboot-Time "03:30";
  - path: /etc/nginx/sites-available/default
    permissions: '0644'
    content: |
      server {
        listen 80 default_server;
        server_name _;
        root /var/www/html;
        index index.html;
        add_header X-Frame-Options SAMEORIGIN always;
        add_header X-Content-Type-Options nosniff always;
        add_header Referrer-Policy no-referrer-when-downgrade always;
        location / {
          try_files $uri $uri/ =404;
        }
      }
runcmd:
  - mkdir -p /var/www/html
  - chown -R www-data:www-data /var/www/html
  - bash -lc "echo 'Hello from cloud-init' > /var/www/html/index.html"
  - systemctl enable nginx
  - systemctl restart nginx
  - ufw default deny incoming
  - ufw default allow outgoing
  - ufw allow OpenSSH
  - ufw allow 80
  - ufw allow 443
  - ufw --force enable
final_message: "web-1 is ready"

Когда будете выводить сайт в интернет, сразу подключайте SSL-сертификаты и указывайте домены через регистрацию доменов. Для корректного редиректа на HTTPS и защиты HSTS пригодится чек-лист «301, HSTS и SSL» — см. материал перенос на HTTPS с 301 и HSTS.

Docker за минуту через cloud-init

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

#cloud-config
packages:
  - docker.io
  - docker-compose-plugin
runcmd:
  - systemctl enable docker
  - systemctl start docker
  - usermod -aG docker deploy

Далее можно положить файл docker-compose.yml через write_files и запустить docker compose up -d в runcmd. Такой подход избавляет от ручной установки и ускоряет повторные деплои.

Установка Docker на VDS через cloud-init в терминале

Сеть: когда нужен network-config

На большинстве VDS сеть уже настроена провайдером. Но если вы используете NoCloud с собственным seed, можно передать network-config для Netplan (версия 2). Пример для DHCP:

network:
  version: 2
  ethernets:
    ens3:
      dhcp4: true

Для статики — задайте адрес, шлюз и DNS. Убедитесь, что имена интерфейсов совпадают с реальными в образе.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Если предпочитаете управлять сервером через панель, посмотрите сравнение популярных решений: панели для VDS в 2025.

MIME multi-part: комбинируем конфиг и скрипты

Когда одного #cloud-config мало (например, нужен короткий shell-скрипт и несколько файлов), используйте многочастный MIME. Пример структуры:

MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="//"

--//
Content-Type: text/cloud-config; charset="us-ascii"

#cloud-config
package_update: true
packages:
  - nginx

--//
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
set -euo pipefail
echo "post-install hook" > /root/post_install.log

--//
Content-Type: text/cloud-config; charset="us-ascii"

#cloud-config
write_files:
  - path: /etc/motd
    content: |
      Provisioned by cloud-init
--//--

cloud-init сам распознает части по заголовкам Content-Type и выполнит их в корректном порядке.

Отладка и диагностика

Если что-то пошло не так, у cloud-init есть понятные точки входа:

  • логи: /var/log/cloud-init.log и /var/log/cloud-init-output.log;
  • сводка: cloud-init status и cloud-init analyze show;
  • повторный запуск: cloud-init clean и затем рестарт;
  • запрос данных: cloud-init query --all.
cloud-init --version
cloud-init status --wait
cloud-init analyze show
journalctl -u cloud-init -xe

Для проверки синтаксиса конфигурации в новых версиях доступна команда валидации схемы. Также помогает запуск cloud-init single для отдельного модуля, если нужно локально воспроизвести шаги.

Идемпотентность и порядок действий

Часть модулей запускается «один раз», часть — на каждом старте. Для прикладного провижининга ориентируйтесь на модуль runcmd и создание неизменяемых файлов. Если в runcmd есть операции, которые вы не хотите повторять при ручном перезапуске, оборачивайте их в проверки существования маркерных файлов или системных состояний.

runcmd:
  - test -f /opt/.init_done || (do-something && touch /opt/.init_done)

Так вы избежите «двоения» ресурсов и неожиданных перезапусков сервисов.

Типичные ошибки и как их избежать

  • Неверные отступы YAML. Проверяйте конфиг линтерами или валидацией cloud-init.
  • Смешивание packages и ручных apt-get в runcmd. Отдайте приоритет декларативным директивам.
  • Блокировки APT из-за параллельных задач. Не запускайте пост-обновления в runcmd до завершения package_*.
  • Отключили пароль и root, но не добавили SSH ключи. Проверяйте доступность ключей перед стартом.
  • Неверное имя сетевого интерфейса в network-config. Сверяйтесь с ip link.
  • Перегруженный runcmd. Для сложных сценариев лучше положить и активировать systemd unit.

Шаблоны для команд и стейджинга

Хорошая практика — хранить базовый user-data как шаблон и подставлять переменные (имя хоста, список ключей, роли) внешним генератором в вашем CI/CD. Для стейджинга и продакшена меняйте ровно то, что нужно: имя хоста, доменные имена в Nginx, списки авторизованных ключей, набор пакетов.

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

Практический чек-лист

  • Определите минимальный набор: пользователь, SSH ключи, часовой пояс, обновления, base-пакеты.
  • Добавьте безопасность: UFW или nftables, fail2ban, отключение пароля и root-логина.
  • Приложение: Nginx, PHP-FPM или Docker — в packages + runcmd.
  • Конфиги: write_files для site- и unit-файлов.
  • Проверка: валидация YAML и просмотр логов cloud-init.
  • Идемпотентность: маркеры или systemd-юниты для повторных прогонов.

Вместо финала

cloud-init — простой инструмент, который снимает 80% рутины в первые пять минут жизни VDS. С одним user-data вы закладываете фундамент безопасности, наблюдаемости и повторяемости. Дальше остаётся только наращивать логику: логгеры, агенты мониторинга, ротация ключей, деплой приложений — всё это можно добавить в тот же сценарий. Начните с малого, но сразу делайте это автоматизированно: так ваши серверы перестанут «рождаться вручную» и начнут воспроизводиться так же легко, как сборки в CI.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: nftables sets с timeout и interval для динамических списков IP OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: nftables sets с timeout и interval для динамических списков IP

Показываю, как в Debian и Ubuntu применять nftables sets для больших списков IP, временных банов и диапазонов адресов. Разберём ti ...
Debian и Ubuntu: как работает cloud-init на first boot, где искать datasource, cache и почему не применяется user-data OpenAI Статья написана AI (GPT 5)

Debian и Ubuntu: как работает cloud-init на first boot, где искать datasource, cache и почему не применяется user-data

Разбираем, почему в Debian и Ubuntu cloud-init не применяет user-data, как определяется datasource, что хранится в /var/lib/cloud ...
Debian/Ubuntu: как исправить x509: certificate signed by unknown authority в Docker, containerd и kubelet OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить x509: certificate signed by unknown authority в Docker, containerd и kubelet

Если Docker, containerd или kubelet на Debian/Ubuntu не могут скачать образ из registry и отвечают x509: certificate signed by unk ...