OpenStack: как быстро создать множество серверов


Инструментарий для работы с облачной инфраструктурой сегодня огромен: Terraform, Pulumi, Ansible, всевозможные SDK, прямое взаимодействие с REST API. У каждого варианта своя ниша — Terraform хорош, когда инфраструктуру нужно описать декларативно и хранить в репозитории, Ansible берёт на себя конфигурацию уже запущенных машин. Однако бывают ситуации проще: нужно развернуть полсотни одинаковых серверов под нагрузочный тест и удалить их через пару часов. Тащить для этого тяжеловесный фреймворк — лишние усилия.

Между тем существует куда более лёгкий способ, о котором почему-то вспоминают в последнюю очередь: консольный клиент OpenStack плюс стандартные утилиты командной оболочки. Ни state-файлов, ни YAML-манифестов — терминал и одна строчка, которая запускает весь процесс.

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

openstack server create \
  --image "Ubuntu 22.04 LTS 64-bit" \
  --network my_network \
  --flavor SL1.1-2048-16 \
  --max 100 \
  my_server

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

Подготовка окружения

Первым делом организуем рабочее место. Консольный клиент OpenStack — обычный Python-пакет, который устанавливается через pip и взаимодействует с облаком по HTTP. Запускать его можно на локальной рабочей станции или на любой машине, у которой есть доступ к API-эндпоинту облака.

Установка клиента. Рекомендуется создать отдельное виртуальное окружение Python, чтобы зависимости клиента не пересекались с системными пакетами:

python3 -m venv ~/openstack-env
source ~/openstack-env/bin/activate
pip install python-openstackclient

Когда установка завершится, в терминале появится команда openstack.

Создание сервисного пользователя. Для обращения к API необходим отдельный сервисный аккаунт с правами администратора проекта. Заводится он через веб-панель облачного провайдера в разделе контроля доступа. Такой аккаунт предназначен строго для программного взаимодействия — входить через него в графический интерфейс не нужно.

Скачивание RC-файла. Это небольшой shell-скрипт, содержащий переменные окружения для авторизации клиента: URL Keystone, название проекта, имя пользователя, регион. Как правило, его можно загрузить из панели управления провайдера. Содержимое файла выглядит приблизительно так:

export OS_AUTH_URL=https://cloud.example.com:5000/v3
export OS_PROJECT_NAME="my_project"
export OS_USERNAME="service_user"
export OS_REGION_NAME="ru-1"
export OS_IDENTITY_API_VERSION=3
echo "Please enter your OpenStack Password for project $OS_PROJECT_NAME as user $OS_USERNAME: "
read -sr OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT

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

Загружаем переменные в текущую сессию:

source rc.sh

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

openstack token issue

Ожидаемый результат:

+------------+----------------------------------+
| Field      | Value                            |
+------------+----------------------------------+
| expires    | 2025-06-15T12:00:00+0000         |
| id         | gAAAAABmTk...                    |
| project_id | a1b2c3d4e5f6                     |
| user_id    | f6e5d4c3b2a1                     |
+------------+----------------------------------+

Появилась таблица — значит, связь с облаком установлена и можно переходить к следующему этапу. Если терминал выдаёт HTTP 401 Unauthorized — перепроверьте учётные данные в RC-файле.

Знакомство с ключевыми сущностями

Для запуска виртуальной машины в OpenStack требуется четыре объекта: Image, Flavor, Network и Keypair. Рассмотрим назначение каждого.

Image — готовый образ операционной системы, на основе которого формируется диск ВМ. Облачный провайдер, как правило, предлагает набор публичных образов: Ubuntu, CentOS, Debian и ряд других дистрибутивов.

Flavor — шаблон ресурсов виртуальной машины: число vCPU, объём RAM, размер диска. По сути, это аналог «размера» или «типа инстанса» в других облаках. Ключевой нюанс: когда значение поля Disk в Flavor-е равно нулю, подразумевается подключение сетевого тома (Volume), создаваемого отдельной командой. При ненулевом значении Disk применяется локальный диск, и Volume не нужен.

Network — изолированная виртуальная сеть, к которой подключается сервер в момент создания. Без указания сети запустить ВМ невозможно.

Keypair — публичный SSH-ключ, который инжектируется в экземпляр при развёртывании. Благодаря ему на сервер можно зайти по ключу, минуя парольную аутентификацию.

Архитектурно OpenStack разделён на независимые сервисы, каждый из которых закрывает свою зону ответственности. При повседневной работе с CLI-клиентом задумываться об этом не обязательно — клиент сам определяет, к какому сервису направить запрос. Тем не менее общее представление о модулях полезно: Keystone занимается аутентификацией и ведёт каталог эндпоинтов, Nova (Compute) управляет жизненным циклом виртуальных машин, Neutron (Network) отвечает за виртуальные сети, Glance (Images) хранит образы ОС, а Cinder (Volume) управляет сетевыми дисками.

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

openstack image list

Фрагмент вывода:

+--------------------------------------+------------------------------------------+--------+
| ID                                   | Name                                     | Status |
+--------------------------------------+------------------------------------------+--------+
| 41383415-4c61-46e5-8cc4-ead5a6f78cf8 | CentOS 7 64-bit                          | active |
| a1b081d7-9eff-44b0-8d3f-85fabf2c88a6 | CentOS 9 Stream 64-bit                   | active |
| 7a827898-058b-4faf-bf8a-0d2311bef7c3 | Ubuntu 20.04 LTS 64-bit                  | active |
| 1fd341e8-82c2-4421-8a37-912cd5ee3ae3 | Ubuntu 22.04 LTS 64-bit                  | active |
+--------------------------------------+------------------------------------------+--------+

У каждого образа есть UUID и текстовое название. В командах допускается использовать оба варианта идентификации.

Смотрим доступные Flavor-ы:

openstack flavor list

Фрагмент вывода:

+------+--------------------+-------+------+-----------+-------+-----------+
| ID   | Name               |   RAM | Disk | Ephemeral | VCPUs | Is Public |
+------+--------------------+-------+------+-----------+-------+-----------+
| 1312 | SL1.1-2048-16      |  2048 |   16 |         0 |     1 | True      |
| 1313 | SL1.2-4096-32      |  4096 |   32 |         0 |     2 | True      |
| 1314 | SL1.2-8192-64      |  8192 |   64 |         0 |     2 | True      |
| 1012 | SL1.1-2048         |  2048 |    0 |         0 |     1 | True      |
| 1013 | SL1.2-4096         |  4096 |    0 |         0 |     2 | True      |
+------+--------------------+-------+------+-----------+-------+-----------+

Столбец Disk здесь принципиален: у SL1.1-2048-16 значение 16 ГБ — это Flavor с локальным хранилищем. У SL1.1-2048 стоит 0 — под такой Flavor понадобится отдельно созданный Volume. Выбор между ними определяет порядок действий при развёртывании.

Создание сети и SSH-ключа

До запуска серверов нужно организовать сетевую среду и зарегистрировать SSH-ключ.

Заводим приватную сеть:

openstack network create my_network

Внутри неё определяем подсеть:

openstack subnet create \
  --subnet-range 192.168.0.0/24 \
  --network my_network \
  my_subnet

Маска /24 обеспечивает 254 адреса — с запасом хватает для ста серверов. Если в перспективе экземпляров будет больше, имеет смысл сразу взять более широкий диапазон, например /16.

Обратите внимание: пока мы не создаём ни роутера, ни маршрута наружу. Машины останутся без интернет-доступа — это нормально, сетевую связность с внешним миром мы настроим чуть позже через bastion-хост.

Регистрируем SSH-ключ. Без него войти на сервер получится лишь по паролю, что неудобно и менее безопасно. Передаём публичную часть ключа в OpenStack:

openstack keypair create --public-key ~/.ssh/id_rsa.pub my_keypair

Если ключевой пары ещё нет, сгенерируйте её: ssh-keygen -t ed25519. Путь ~/.ssh/id_rsa.pub — расположение по умолчанию для RSA-ключей. Для ed25519 файл будет лежать по пути ~/.ssh/id_ed25519.pub.

Убедимся, что ключ зарегистрирован:

openstack keypair list
+------------+-------------------------------------------------+------+
| Name       | Fingerprint                                     | Type |
+------------+-------------------------------------------------+------+
| my_keypair | ab:cd:ef:12:34:56:78:90:ab:cd:ef:12:34:56:78:90 | ssh  |
+------------+-------------------------------------------------+------+

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

Массовое создание серверов с локальным диском

Ради этого момента мы и проделали всю подготовку. Запускаем сразу сто серверов одной командой:

openstack server create \
  --image "Ubuntu 22.04 LTS 64-bit" \
  --network my_network \
  --flavor SL1.1-2048-16 \
  --key-name my_keypair \
  --max 100 \
  my_server

Пройдёмся по аргументам. Флаг --image указывает, из какого образа создать диск. Флаг --flavor задаёт конфигурацию ресурсов (1 vCPU, 2 ГБ RAM, 16 ГБ локального диска). Флаг --network определяет сеть для подключения. Флаг --key-name добавляет SSH-ключ. Наконец, --max 100 — ключевой параметр, который указывает Nova сформировать не один экземпляр, а сразу сотню в рамках единственного обращения к API.

Что происходит на уровне API. Nova получает запрос и отдаёт его планировщику (scheduler). Тот размещает экземпляры по вычислительным узлам, учитывая загрузку и доступные ресурсы каждого гипервизора. Серверы получают автоматические суффиксы — my_server-1, my_server-2 и так далее до my_server-100, а также индивидуальные IP-адреса из нашей подсети.

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

openstack server list

Фрагмент вывода:

+--------------------------------------+--------------+--------+--------------------------+-------------------------+---------------+
| ID                                   | Name         | Status | Networks                 | Image                   | Flavor        |
+--------------------------------------+--------------+--------+--------------------------+-------------------------+---------------+
| 031080fa-3aa0-4848-8551-fd1e6f6ec65f | my_server-94 | ACTIVE | my_network=192.168.0.58  | Ubuntu 22.04 LTS 64-bit | SL1.1-2048-16 |
| 03864c9b-6b9b-4bea-8679-93367ca52250 | my_server-87 | ACTIVE | my_network=192.168.0.24  | Ubuntu 22.04 LTS 64-bit | SL1.1-2048-16 |
| 0efb6875-c481-4ec0-bc80-3392d84c89d6 | my_server-82 | ACTIVE | my_network=192.168.0.110 | Ubuntu 22.04 LTS 64-bit | SL1.1-2048-16 |
| 1619eaf7-c7ab-4db0-a875-38c39acabdac | my_server-76 | ACTIVE | my_network=192.168.0.52  | Ubuntu 22.04 LTS 64-bit | SL1.1-2048-16 |
+--------------------------------------+--------------+--------+--------------------------+-------------------------+---------------+

Считаем серверы в рабочем состоянии:

openstack server list -f value | grep -c ACTIVE
100

Все сто экземпляров перешли в статус ACTIVE. Единственная строка в терминале — и готово, без единого скрипта или конфигурационного файла.

Настройка сети и доступ через bastion

Серверы запущены, но попасть на них пока нельзя: вся сотня живёт в изолированной приватной сети. Стандартное решение — выделить один экземпляр под роль bastion-хоста, назначить ему внешний IP и заходить на остальные машины уже через него.

Формируем виртуальный роутер и подключаем к нему нашу подсеть:

openstack router create my_router
openstack router add subnet my_router my_subnet

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

openstack network list --external -f value -c Name
external-network

Назначаем внешний шлюз:

openstack router set --external-gateway external-network my_router

С этого момента все экземпляры в подсети имеют исходящий доступ в интернет через NAT. Однако инициировать подключение снаружи к ним по-прежнему нельзя — для этого понадобится плавающий (floating) IP-адрес.

Выделяем floating IP:

openstack floating ip create external-network

Команда вернёт присвоенный адрес. Увидеть все выделенные адреса можно так:

openstack floating ip list

Предположим, нам достался адрес 31.129.42.122. Привязываем его к первому серверу — он станет нашим bastion-хостом:

openstack server add floating ip my_server-1 31.129.42.122

Теперь my_server-1 доступен извне. Чтобы попасть на произвольную машину, достаточно узнать её приватный адрес. Допустим, нас интересует my_server-99:

openstack server show my_server-99 -f value -c addresses
{'my_network': ['192.168.0.111']}

Подключаемся транзитом через bastion — для этого в SSH предусмотрен механизм ProxyJump (флаг -J):

ssh -J root@31.129.42.122 root@192.168.0.111
root@my-server-99:~#

Мы на месте. Схема рабочая: единственный публичный адрес, один bastion — и с него открыт путь ко всей приватной инфраструктуре.

Массовое создание серверов с сетевым диском

Ранее мы использовали Flavor с ненулевым полем Disk — локальное хранилище создаётся вместе с ВМ автоматически. В реальных проектах нередко применяют сетевые диски (Volumes): они существуют независимо от гипервизора, поддерживают моментальные снимки и сохраняются при пересоздании экземпляра.

Процесс выглядит иначе: сперва формируется Volume на основе образа ОС, а затем он передаётся команде создания ВМ. Принципиальный момент — Cinder API не предоставляет аналога параметра --max для пакетного создания томов. Именно тут вступает в дело автоматизация средствами shell.

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

openstack server list -f value -c ID | xargs -I {} -P 10 bash -c 'openstack server delete {}'

Как это устроено: openstack server list -f value -c ID возвращает только идентификаторы серверов — по одному на строку, без оформления таблицы. Утилита xargs -I {} вставляет каждый ID на место плейсхолдера {}. Параметр -P 10 запускает до десяти параллельных процессов, что кратно ускоряет операцию по сравнению с последовательным выполнением.

Переходим к созданию ста сетевых дисков. Генерируем числовую последовательность утилитой seq и передаём её в xargs:

seq 1 100 | xargs -I {} -P 10 bash -c \
  'openstack volume create --size 10 --image "Ubuntu 22.04 LTS 64-bit" my_volume_{}'

Каждый том получает ёмкость 10 ГБ и создаётся на базе образа Ubuntu. Результатом станут объекты my_volume_1, my_volume_2, ..., my_volume_100. Десять параллельных потоков (-P 10) делают процесс существенно быстрее последовательного цикла.

Определяем собственный Flavor с нулевым диском — под сетевое хранилище:

openstack flavor create --private --disk 0 --vcpus 1 --ram 1024 my_flavor_net_hdd

Параметр --private ограничивает видимость этого Flavor-а только нашим проектом.

Финальный шаг — запуск ста серверов, каждый с привязанным Volume:

seq 1 100 | xargs -I {} -P 10 bash -c \
  'openstack server create \
    --network my_network \
    --key-name my_keypair \
    --flavor my_flavor_net_hdd \
    --volume my_volume_{} \
    my_server_{}'

Обратите внимание на замену: вместо --image используется --volume. Сервер не создаёт диск из образа самостоятельно, а получает заранее подготовленный том.

Ждём перехода всех экземпляров в рабочее состояние:

openstack server list -f value | grep -c ACTIVE
100

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

Полезные приёмы shell-автоматизации

Выше мы уже задействовали xargs и seq, но возможности shell-автоматизации этим не ограничиваются. Консольный клиент OpenStack поддерживает несколько форматов вывода, удобных для машинной обработки. Ниже — подборка приёмов, полезных в ежедневной работе.

Управление форматом вывода. Флаг -f value отключает табличную разметку и выдаёт «голые» значения, разделённые пробелом. Флаг -c COLUMN ограничивает набор колонок. Вместе они позволяют вытянуть ровно ту информацию, которая нужна:

# Только имена серверов
openstack server list -f value -c Name

# Только ID и статус
openstack server list -f value -c ID -c Status

Когда задача сложнее, на помощь приходит вывод в JSON и утилита jq:

# Получить IP-адреса всех серверов
openstack server list -f json | jq -r '.[].Networks'

# Найти серверы в статусе ERROR
openstack server list -f json | jq -r '.[] | select(.Status=="ERROR") | .Name'

Параллельное выполнение операций. Конструкция xargs -I {} -P N работает с любыми командами OpenStack — достаточно подставить нужную:

# Перезагрузить все серверы с именем my_server-*
openstack server list -f value -c ID -c Name | grep my_server | awk '{print $1}' \
  | xargs -I {} -P 5 bash -c 'openstack server reboot {}'

# Удалить все floating IP
openstack floating ip list -f value -c ID \
  | xargs -I {} -P 5 bash -c 'openstack floating ip delete {}'

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

watch -n2 'openstack server list -f value | grep -c ACTIVE'

Утилита watch перезапускает указанную команду раз в 2 секунды, показывая актуальный счётчик активных экземпляров.

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

# Серверы
openstack server list -f value -c ID | xargs -I {} -P 10 bash -c 'openstack server delete --wait {}'

# Volumes
openstack volume list -f value -c ID | xargs -I {} -P 10 bash -c 'openstack volume delete {}'

# Floating IP
openstack floating ip list -f value -c ID | xargs -I {} bash -c 'openstack floating ip delete {}'

# Роутер (сначала отвязать подсеть и шлюз)
openstack router remove subnet my_router my_subnet
openstack router unset --external-gateway my_router
openstack router delete my_router

# Сеть
openstack subnet delete my_subnet
openstack network delete my_network

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

Итоги и шпаргалка

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

В какой момент разумно перейти на полноценные IaC-инструменты? Когда инфраструктура превращается из временной в постоянную, когда нужна история изменений и код-ревью, когда настройка серверов выходит за пределы «создать и подключиться по SSH». В таких случаях стоит обратиться к Terraform или Ansible. А до тех пор — терминал и shell справятся.

Справочник по командам, использованным в статье:

# Авторизация и проверка
source rc.sh
openstack token issue

# Образы и Flavor-ы
openstack image list
openstack flavor list
openstack flavor create

# SSH-ключи
openstack keypair create
openstack keypair delete
openstack keypair list
openstack keypair show

# Серверы
openstack server create
openstack server delete
openstack server list
openstack server show
openstack server reboot
openstack server add floating ip
openstack server remove floating ip

# Сетевые диски
openstack volume create
openstack volume delete
openstack volume list
openstack volume show

# Сеть
openstack network create
openstack network delete
openstack network list
openstack network show
openstack network list --external

# Подсети
openstack subnet create
openstack subnet delete
openstack subnet list
openstack subnet show

# Роутеры
openstack router create
openstack router delete
openstack router list
openstack router show
openstack router add subnet
openstack router remove subnet
openstack router set --external-gateway
openstack router unset --external-gateway

# Floating IP
openstack floating ip create
openstack floating ip list
openstack floating ip delete

# Полезные флаги
-f value                    # вывод без таблицы
-f json                     # JSON-формат
-c COLUMN                   # выбор колонки
--max N                     # массовое создание серверов
--wait                      # ждать завершения операции

# Shell-утилиты
seq 1 N                     # последовательность чисел
xargs -I {} -P N bash -c '...' # параллельное выполнение
watch -n2 'команда'          # мониторинг в реальном времени
jq                           # обработка JSON

Полезный материал?
0
0
Автор: Олег
опубликовано: 16.03.2026
Читайте нас: 
Последние статьи
Вверх!