Требуемые условия завершения
Задание:
- a) Требования к виртуальным машинам:
- 1. Основные характеристики:
- i. Операционная система: Альт p10 StarterKit/
Альт Сервер p10-cloud - ii. Количество vCPU: 1.
- iii. Объём оперативной памяти: 1024 МБ.
- iv. Объём диска: 10 ГБ/
30 ГБ v. Тип диска: HDD.
- i. Операционная система: Альт p10 StarterKit/
- 1. Основные характеристики:
- b) Подготовьте сценарий автоматизации развёртывания облачной инфраструктуры:
- 1. Создание виртуальных машин и сетей:
- i. Виртуальные машины и сети должны быть созданы строго в соответствии с предложенной топологией (см. Топология ниже).
- ii. Имена виртуальных машин, сетей, подсетей и маршрутизаторов должны соответствовать именованиям, указанным в Топологии.
- iii. Обеспечьте правильное подключение виртуальных машин к соответствующим сетям в рамках заданной топологии.
- 1. Создание виртуальных машин и сетей:
- 2. Безопасность и доступ:
- i. Разрешите трафик по протоколу ICMP для всех виртуальных машин для диагностики сетевых подключений.
- ii. Назначьте IP-адреса всем машинам. Сохраните внешние IP-адреса всех машин в файле /home/altlinux/white.ip на машине ControlVM.
- iii. Настройте аутентификацию на основе открытых ключей для SSH.
- iv. В случае предоставления внешнего доступа к виртуальным машинам, разрешите его только по протоколу SSH (публичный ключ, пароль отключён) и только с соответствующих IP-адресов.
- 3. Балансировка нагрузки:
- i. Создайте балансировщик нагрузки и распределите трафик между серверами Web1 и Web2 (см. Топология).
- ii. Ограничьте внешний доступ к балансировщику только протоколами HTTP и HTTPS. Все остальные порты должны быть закрыты.
- iii. Балансировка нагрузки должна использовать алгоритм round robin.
- iv. При обращении на внешний адрес балансировщика нагрузки должен выводиться ответ от приложения, работающего на внутренних серверах Web1 и Web2.
- 4. Настройка подключения:
- i. Настройте машину WebAdm так, чтобы она могла подключаться по SSH с использованием пользователя altlinux и пароля «P@ssw0rd» к серверам Web1 и Web2 с помощью VPN туннеля.
- ii. Убедитесь, что машина ControlVM может подключаться к машине WebAdm используя ключевую пару пользователя altlinux по SSH через её глобальный IP-адрес.
Вариант реализации:
ControlVM
Все файлы создаются в контексте каталога /home/altlinux/bin, если не сказано иное
- Создаём файл network.tf и описываем последовательно сетевую часть для развёртывания данной инфраструктуры с поэтапным запуском и наблюдением созданных ресурсов:
vim network.tf
-
- Помещаем следующее содержимое:
- Создадим виртуальную сеть с именем INTERNET в соответствие с топологией;
- В созданной виртуальной сети создадим одноимённую подсеть, т.к. все сетевые параметры задаются на подсеть в рамках сети (с произвольными параметрами);
- В данном случае подсеть INTERNETимеет IP-адрес 192.168.200.0/24, DHCP-пул адресов для раздачи из данной подсети с 192.168.200.100 по 192.168.200.200, в качестве шлюза по умолчанию в данной подсети будет использоваться IP-адрес 192.168.200.1, в качестве DNS IP-адрес 77.88.8.8;
- Также данную подсеть INTERNET необходимо добавить в ранее созданный виртуальный маршрутизатор с именем cloud (чтобы не создавать новый), который необходим для работы ControlVM и не должен быть удалён по завершению работы, для того чтобы для всех подключённых инстансов к данной подсети был доступ в сеть Интернет, т.к. на данном виртуальном маршрутизаторе ранее был выставлен чек-бокс для SNAT, и именно через него ControlVM и все остальные будут иметь доступ в сеть Интернет;
- Помещаем следующее содержимое:
# Обращаемся к источнику данных, чтобы узнать ID существующего виртуального маршрутизатора
data "openstack_networking_router_v2" "router" {
name = "cloud"
}
# Создаём сеть с именем "INTERNET" в соответствие с топологией
resource "openstack_networking_network_v2" "network" {
name = "INTERNET"
admin_state_up = "true"
}
# Создаём подсеть с именем "INTERNET" в ранее созданной сети с именем "INTERNET"
resource "openstack_networking_subnet_v2" "subnet" {
name = "INTERNET"
network_id = openstack_networking_network_v2.network.id
cidr = "192.168.200.0/24"
ip_version = 4
gateway_ip = "192.168.200.1"
dns_nameservers = [ "77.88.8.8" ]
enable_dhcp = true
allocation_pool {
start = "192.168.200.100"
end = "192.168.200.200"
}
}
# Добавляем в существующий маршрутизатор созданную подсеть с именем "INTERNET", чтобы в дальнейшем ВМ подключённые к данной подсети имели доступ в Интернет
resource "openstack_networking_router_interface_v2" "router_interface" {
router_id = data.openstack_networking_router_v2.router.id
subnet_id = openstack_networking_subnet_v2.subnet.id
}
- Выполняем проверку синтаксиса и структуры файлов конфигурации Terraform
terraform validate
-
- Результат:
- Проверяем конфигурацию перед развёртыванием, смотрим план развёртывания ресурсов:
terraform plan
-
- Результат:
- По плану будет создано (добавлено) 3 ресурса: Сеть INTERNET, Подсеть INTERNET и добавление в виртуальный маршрутизатор cloud подсети INTERNET:
- Результат:
- Запускаем развёртывание данных ресурсов:
terraform apply
-
- Подтверждаем развёртывание введя yes:
-
- Результат:
- Проверяем наличие созданных ресурсов средствами openstack-cli:
- Сеть с подсетью:
- Проверяем наличие созданных ресурсов средствами веб-интерфейса:
- Сеть с подсетью:
-
- Подсеть добавленная в существующий маршрутизатор (для доступа в сеть Интернет из данной подсети):
- В файл network.tf добавляем следующий код:
- Создаём порты в сети INTERNET в одноимённой подсети для каждого инстанса и балансировщика нагрузки
- Обращаясь уже к ранее созданным ресурсам, а именно к идентификаторам id ресурсов с именемами network и subnet
# Создадим порт для инстанса WebADM
resource "openstack_networking_port_v2" "port_webadm" {
name = "webadm"
network_id = openstack_networking_network_v2.network.id
fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.20"
}
}
# Создадим порт для инстанса WEB1
resource "openstack_networking_port_v2" "port_web1" {
name = "web1"
network_id = openstack_networking_network_v2.network.id
fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.21"
}
}
# Создадим порт для инстанса WEB2
resource "openstack_networking_port_v2" "port_web2" {
name = "web2"
network_id = openstack_networking_network_v2.network.id
fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.22"
}
}
# Создадим порт для балансировщика нагрузки Load Balancer
resource "openstack_networking_port_v2" "port_loadbalancer" {
name = "Load Balancer"
network_id = openstack_networking_network_v2.network.id
fixed_ip {
subnet_id = openstack_networking_subnet_v2.subnet.id
ip_address = "192.168.200.23"
}
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов:
terraform apply
-
- Подтверждаем развёртывание введя yes:
-
- Результат:
- Проверяем наличие созданных ресурсов средствами openstack-cli:
- в веб-интерфейсе нет возможности посмотреть созданные порты;
- В файл network.tf добавляем следующий код:
- Создаём плавающие IP-адреса ("публичные") для каждого инстанса и балансировщика нагрузки;
- Плавающие IP-адреса должны браться из "Публичной" сети указав её имя, в данном случае public;
# Создадим плавающий IP для инстанса WebADM
resource "openstack_networking_floatingip_v2" "floatingip_webadm" {
pool = "public"
}
# Создадим плавающий IP для инстанса WEB1
resource "openstack_networking_floatingip_v2" "floatingip_web1" {
pool = "public"
}
# Создадим плавающий IP для инстанса WEB2
resource "openstack_networking_floatingip_v2" "floatingip_web2" {
pool = "public"
}
# Создадим плавающий IP для балансировщика нагрузки Load Balancer
resource "openstack_networking_floatingip_v2" "floatingip_loadbalancer" {
pool = "public"
}
-
- Имя "публичной" сети можно посмотреть через openstack-cli:
- Та сеть, которую ранее не создавали в ручную для работы ControlVM (cloud) и средствами Terraform в соответствие с топологией (INTERNET);
- Имя "публичной" сети можно посмотреть через openstack-cli:
-
- Имя "публичной" сети можно посмотреть через веб-интерфейс:
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие созданных ресурсов средствами openstack-cli:
- Плавающие IP-адреса (4 шт.):
- Проверяем наличие созданных ресурсов средствами веб-интерфейса:
- Плавающие IP-адреса (4 шт.):
- В файл network.tf добавляем следующий код:
- Создавая ассоциацию ранее созданного плавающего IP-адреса с ещё ранее созданным портов;
- Для каждого инстанса и балансировщика нагрузки;
# Создадим для WebADM ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_webadm" {
port_id = openstack_networking_port_v2.port_webadm.id
floating_ip = openstack_networking_floatingip_v2.floatingip_webadm.address
}
# Создадим для WEB1 ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_web1" {
port_id = openstack_networking_port_v2.port_web1.id
floating_ip = openstack_networking_floatingip_v2.floatingip_web1.address
}
# Создадим для WEB2 ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_web2" {
port_id = openstack_networking_port_v2.port_web2.id
floating_ip = openstack_networking_floatingip_v2.floatingip_web2.address
}
# Создадим для Load Balancer ассоциацию плавающего IP и порт (публичного и приватного IP адресов)
resource "openstack_networking_floatingip_associate_v2" "association_loadbalancer" {
port_id = openstack_networking_port_v2.port_loadbalancer.id
floating_ip = openstack_networking_floatingip_v2.floatingip_loadbalancer.address
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем в веб-интерфейсе статус плавающих IP-адресов сменился с Неактивен на Запущена:
- Также появились IP-адреса ВМ (но сами ВМ (инстансы) ещё не созданы) из подсети INTERNET (192.168.200.0/24 созданной ранее);
- Создаём файл security.tf и описываем последовательно часть касающуюся сетевой безопасности (с точки зрения требования задания) для развёртывания данной инфраструктуры с поэтапным запуском и наблюдением созданных ресурсов:
vim network.tf
-
- Помещаем следующее содержимое:
- Создаём две группы безопасности для будущего контроля ICMP и SSH
- Помещаем следующее содержимое:
# Создаём группу безопасности для ICMP
resource "openstack_networking_secgroup_v2" "secgroup_1" {
name = "ICMP"
description = "ICMP"
}
# Создаём группу безопасности для SSH
resource "openstack_networking_secgroup_v2" "secgroup_2" {
name = "SSH"
description = "SSH"
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие созданных ресурсов средствами openstack-cli:
- Группы безапасности:
- Проверяем наличие созданных ресурсов средствами веб-интерфейса:
- Группы безапасности:
- В файл security.tf добавляем следующий код:
- Создавая правило, в ранее созданную группу безопасности обращаясь по идентификатору id созданного ресурса secgroup_1 для фильтрации по протоколу icmp с любых IP-адресов для входящего трафика;
- Создавая правило, в ранее созданную группу безопасности обращаясь по идентификатору id созданного ресурса secgroup_2 для фильтрации по протоколу tcp на порт 22 (SSH) с любых IP-адресов для входящего трафика;
- Ограничивая тем самом доступ к будущим инстансам по ICMP и SSH из внешних сетей;
# Добавляем правило в группу безопасности для ICMP
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_icmp" {
direction = "ingress"
ethertype = "IPv4"
protocol = "icmp"
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_1.id
}
# Добавляем правило в группу безопасности для SSH
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_ssh" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_2.id
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие созданных ресурсов средствами openstack-cli:
- Правила в группах безопасности:
- Проверяем наличие созданных ресурсов средствами веб-интерфейса:
- Правила в группах безопасности:
- В файл security.tf добавляем следующий код:
- Поскольку в дальнейшем по заданию на инстансах WEB1 и WEB2 должно быть развёрнуто веб-приложение, а значит они должны принимать входящий трафик на порты 80/tcp и 443/tcp (HTTP и HTTPS)
- Поскольку в дальнейшем по заданию необходимо реализовать туннельное соединение между инстансами WebADM и WEB1, WEB2, а значит WebADM должен принимать входящий трафик на порт реализуемого VPN-соединения, например WireGuard (51820/udp);
- Таким образом необходимо создать ещё 1 или 2 группы безопасности с соответствующими правилами;
- В данном примере создаются две группы безопасности с именами WEB и VPN и три правила: два для группы WEB и одно для группы VPN;
# Создаём группу безопасность для WEB-трафика (HTTP/HTTPS)
resource "openstack_networking_secgroup_v2" "secgroup_3" {
name = "WEB"
description = "WEB for HTTP/HTTPS"
}
# Добавляем первое правило в группу безопасности для WEB (HTTP)
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_http" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_3.id
}
# Добавляем второе правило в группу безопасности для WEB (HTTPS)
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_https" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_3.id
}
# Создаём группу безопасность для VPN (WireGuard)
resource "openstack_networking_secgroup_v2" "secgroup_4" {
name = "VPN"
description = "VPN (Wireguard)"
}
# Добавляем правило в группу безопасности для VPN (WireGuard)
resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_vpn" {
direction = "ingress"
ethertype = "IPv4"
protocol = "udp"
port_range_min = 51820
port_range_max = 51820
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.secgroup_4.id
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие созданных ресурсов средствами openstack-cli:
- Правила в группах безопасности:
- Проверяем наличие созданных ресурсов средствами веб-интерфейса:
- Правила в группах безопасности:
- В файл network.tf добавляем следующий код:
- Назначая ранее созданные группы безопасности на ранее созданные порты для каждого инстанса, обращаясь по идентификаторам id созданных ранее ресурсов;
- На порт для инстанса WebADM назначаем группы безопасности ICMP, SSH (явное требование задания) и VPN, т.к. в дальнейшем данный инстанс будет выступать в роле VPN-сервера и должен принимать соединения на порт указанный в правиле для данной группы безопасности;
- На порт для инстансов WEB1 и WEB2 назначаем группы безопасности ICMP, SSH (явное требование задания) и WEB, т.к. в дальнейшем на данных инстансах будет развёрнуто веб-приложение и должны приниматься соединения на порты указанные в правилах для данной группы безопасности;
# Назначим группы (ICMP, SSH, VPN) безопасности на порт для инстанса WebADM
resource "openstack_networking_port_secgroup_associate_v2" "security_group_associate_webadm" {
port_id = openstack_networking_port_v2.port_webadm.id
enforce = true
security_group_ids = [
openstack_networking_secgroup_v2.secgroup_1.id,
openstack_networking_secgroup_v2.secgroup_2.id,
openstack_networking_secgroup_v2.secgroup_4.id
]
}
# Назначим группы (ICMP, SSH, WEB) безопасности на порт для инстанса WEB1
resource "openstack_networking_port_secgroup_associate_v2" "security_group_associate_web1" {
port_id = openstack_networking_port_v2.port_web1.id
enforce = true
security_group_ids = [
openstack_networking_secgroup_v2.secgroup_1.id,
openstack_networking_secgroup_v2.secgroup_2.id,
openstack_networking_secgroup_v2.secgroup_3.id
]
}
# Назначим группы (ICMP, SSH, WEB) безопасности на порт для инстанса WEB2
resource "openstack_networking_port_secgroup_associate_v2" "security_group_associate_web2" {
port_id = openstack_networking_port_v2.port_web2.id
enforce = true
security_group_ids = [
openstack_networking_secgroup_v2.secgroup_1.id,
openstack_networking_secgroup_v2.secgroup_2.id,
openstack_networking_secgroup_v2.secgroup_3.id
]
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Создаём файл instance.tf и описываем последовательно часть для развёртывания необходимых инстансов (виртуальных машин) с поэтапным запуском и наблюдением созданных ресурсов:
vim instance.tf
-
- Помещаем следующее содержимое:
- Т.к. все ресурсы и сети должны быть созданы в соответствие с топологией по условиям задания, а на топологии ControlVM также должна быть подключена к сети INTERNET;
- То добавляем к инстансу ControlVM ещё один сетевой интерфейс из подсети INTERNET и назначаем фиксированный IP-адрес;
- Необходимо указать идентификатор id виртуальной машины ControlVM
- Помещаем следующее содержимое:
# Подключаем в ControlVM интерфейс из подсети "INTERNET" в соответствие с топологией
resource "openstack_compute_interface_attach_v2" "controlvm" {
instance_id = "96306ddf-a488-4e35-8a8f-25696821efe5"
network_id = openstack_networking_network_v2.network.id
fixed_ip = "192.168.200.10"
depends_on = [ openstack_networking_subnet_v2.subnet ]
}
-
- instance_id (идентификатор) можно получить средствами openstack-cli:
-
- instance_id (идентификатор) можно получить средствами веб-интерфейса:
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
-
- Проверяем наличие второго сетевого интерфейса из подсети INTERNET средствами openstack-cli:
-
- Проверяем наличие второго сетевого интерфейса из подсети INTERNET средствами веб-интерфейса:
- В файл instance.tf добавляем следующий код:
- Описывая конфигурацию инстансов WebADM, WEB1 и WEB2;
- См. ниже информацию о переменных var.* и файле cloud-init.yml;
# Создаём инстанс с именем "WebADM"
resource "openstack_compute_instance_v2" "webadm" {
name = "WebADM"
flavor_id = var.flavor_id
key_pair = var.key_pair
user_data = file("cloud-init.yml")
block_device {
uuid = var.image_id
source_type = "image"
volume_size = 10
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.port_webadm.id
}
}
# Создаём инстанс с именами "WEB1"
resource "openstack_compute_instance_v2" "web1" {
name = "WEB1"
flavor_id = var.flavor_id
key_pair = var.key_pair
user_data = file("cloud-init.yml")
block_device {
uuid = var.image_id
source_type = "image"
volume_size = 10
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.port_web1.id
}
}
# Создаём инстанс с именами "WEB2"
resource "openstack_compute_instance_v2" "web2" {
name = "WEB2"
flavor_id = var.flavor_id
key_pair = var.key_pair
user_data = file("cloud-init.yml")
block_device {
uuid = var.image_id
source_type = "image"
volume_size = 10
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.port_web2.id
}
}
- В файл variables.tf добавляем следующий код:
- Определяя значение переменных указывая идентификаторы id для соответствующих (существующих) ресурсов;
- См. ниже где брать идентификаторы;
- Учитывая основные требования задания для создаваемых инстансов:
# ID для образа "alt-p10-cloud-x86_64.qcow2" (Starterkit)
variable "image_id" {
type = string
default = "92e78753-4f88-40eb-a10a-5a7fb9bfc106"
}
# ID для шаблона 1 vCPU 1 RAM (openstack --insecure flavor list)
variable "flavor_id" {
type = string
default = "1f64883c-1cdd-45e4-ac8a-82ab57a12fdf"
}
# Имя ssh-ключа
variable "key_pair" {
type = string
default = "cloud"
}
- Для переменной image_id значение можно получить:
- Средствами openstack-cli:
-
- Средствами веб-интерфейса:
- Для переменной flavor_id значение можно получить:
- Средствами openstack-cli:
-
- Средствами веб-интерфейса нет возможности;
- Для переменной key_pair значение можно получить:
- Средствами openstack-cli:
-
- Средствами веб-интерфейса:
- Средствами Terraform передаём именно тот ключ, на основе которого осуществляется доступ с рабочего места до ControlVM
- Чтобы при необходимости можно было подключиться по SSH к каждому инстансу по "публичному" (Плавающему) IP-адресу, что также является требованиями задания;
- Создаём файл cloud-init.yml:
vim cloud-init.yml
-
- Помещаем следующее содержимое:
- Задавая пароль по требованиям задания P@ssw0rd для каждого создаваемого инстанса;
- Также передаём значение публичной части SSH-ключа (генерация ssh-ключей см. ниже), чтобы в дальнейшем был доступ с ControlVM до каждого инстанса по SSH;
- И именно по ключам, т.к. по условиям задания подключение по SSH на основе открытых ключей;
- Доступ по SSH с ControlVM до каждого инстанса необходим для дальнейшей работы Ansible;
- Помещаем следующее содержимое:
#cloud-config
chpasswd:
expire: false
users:
- {name: altlinux, password: P@ssw0rd, type: text}
ssh_pwauth: false
users:
- name: altlinux
sudo: ALL=(ALL) NOPASSWD:ALL
groups: wheel
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFzYMn859ToNndaRRTlXoDnBDgME7rTPNvPlh9hmo4QsvmMdF3lKgQTR6p53cP1zUgxfstkKCfvMlAfNCE3iJWGxaY8B8tM4fO6jC8eMKe93cIK74lFk1cZnrHHUJSvM4dsD+hsoZ06VPOWSoUtipi9B7AgOF0EsL63GTx0EeajJCOerQUlA6qJM785NfcRNS23vSHhlRyV84xrfBhP/0w2jiHWy+jNCVtpO/M7lAQ5EtcVegSZupzDoJ6K/4GpgjLoP4eUfr8EVNMLGZ5AJlJmlFL5hTsOwa/42CN+TwFSC4G6PFXtWLdZJebPJ2nlcKpydmwSDEeXTy4vPAZWXBD altlinux@controlvm
- Генерируем на ControlVM ключевую пару и смотрим содержимое публичного (открытого) ключа, которое необходимо указать в файле cloud-init.yml:
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие созданных инстансов средствами openstack-cli:
- Проверяем наличие созданных инстансов средствами веб-интерфейса:
- Также проверяем что:
- Каждый инстанс подключён к сети INTERNET с неоходимыми группами безопасности:
-
- Каждый Плавающий IP-адрес назначем на инстансы (за исключением 1-го для балансировки нагрузки):
- Создаём файл loadbalancer.tf и описываем последовательно часть для развёртывания балансировщика нагрузки с поэтапным запуском и наблюдением созданных ресурсов:
vim loadbalancer.tf
-
- Помещаем следующее содержимое:
- Создадим балансировщик нагрузки, который будет подключён в соответствие с топологией к подсети INTERNET;
- Помещаем следующее содержимое:
# Создаём балансировщик нагрузки с именем "Load Balancer"
resource "openstack_lb_loadbalancer_v2" "loadbalancer" {
name = "Load Balancer"
vip_subnet_id = openstack_networking_subnet_v2.subnet.id
depends_on = [
openstack_compute_instance_v2.web1,
openstack_compute_instance_v2.web2
]
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие созданного балансировщика нагрузки средствами openstack-cli:
- Проверяем наличие созданного балансировщика нагрузки средствами веб-интерфейса:
- В файл loadbalancer.tf добавляем следующий код:
- Определяя правила в ранее созданном балансировщике нагрузки для HTTP и HTTPS трафика;
# Создаём правило в балансировщике нагрузки для HTTP
resource "openstack_lb_listener_v2" "listener_http" {
name = "HTTP"
protocol = "TCP"
protocol_port = "80"
loadbalancer_id = openstack_lb_loadbalancer_v2.loadbalancer.id
}
# Создаём правило в балансировщике нагрузки для HTTPS
resource "openstack_lb_listener_v2" "listener_https" {
name = "HTTPS"
protocol = "TCP"
protocol_port = "443"
loadbalancer_id = openstack_lb_loadbalancer_v2.loadbalancer.id
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- В файл loadbalancer.tf добавляем следующий код:
- Создавая целевые группы для HTTP и HTTPS указывая соответствующий протокол и алгоритм балансировки в соответствие с заданием round robin;
# Создаём целевую группу для HTTP с указанием алгоритма балансировки "ROUND ROBIN"
resource "openstack_lb_pool_v2" "pool_http" {
name = "HTTP"
protocol = "HTTP"
lb_method = "ROUND_ROBIN"
listener_id = openstack_lb_listener_v2.listener_http.id
}
# Создаём целевую группу для HTTPS с указанием алгоритма балансировки "ROUND ROBIN"
resource "openstack_lb_pool_v2" "pool_https" {
name = "HTTPS"
protocol = "HTTPS"
lb_method = "ROUND_ROBIN"
listener_id = openstack_lb_listener_v2.listener_https.id
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем созданные целевые группы средствами openstack-cli:
- Проверяем созданные целевые группы средствами веб-интерфейса:
- В файл loadbalancer.tf добавляем следующий код:
- Добавляя инстансы WEB1 и WEB2 в ранее созданные целевые группы для HTTP и HTTPS;
# Добавляем инстанс WEB1 в целевую группу HTTP
resource "openstack_lb_member_v2" "member_web1_http" {
name = "WEB1"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_http.id
address = "192.168.200.21"
protocol_port = "80"
}
# Добавляем инстанс WEB2 в целевую группу HTTP
resource "openstack_lb_member_v2" "member_web2_http" {
name = "WEB2"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_http.id
address = "192.168.200.22"
protocol_port = "80"
}
# Добавляем инстанс WEB1 в целевую группу HTTPS
resource "openstack_lb_member_v2" "member_web1_https" {
name = "WEB1"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_https.id
address = "192.168.200.21"
protocol_port = "80"
}
# Добавляем инстанс WEB2 в целевую группу HTTPS
resource "openstack_lb_member_v2" "member_web2_https" {
name = "WEB2"
subnet_id = openstack_networking_subnet_v2.subnet.id
pool_id = openstack_lb_pool_v2.pool_https.id
address = "192.168.200.22"
protocol_port = "80"
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие инстансов в целевых группах средствами openstack-cli:
- Проверяем наличие инстансов в целевых группах средствами веб-интерфейса:
- В файл loadbalancer.tf добавляем следующий код:
- Добавляя проверку доступности инстансов в каждой целевой группе;
# Создаём проверку доступности инстансов в целевой группе HTTP
resource "openstack_lb_monitor_v2" "monitor_http" {
name = "monitor HTTP"
pool_id = openstack_lb_pool_v2.pool_http.id
type = "PING"
delay = "10"
timeout = "4"
max_retries = "5"
}
# Создаём проверку доступности инстансов в целевой группе HTTPS
resource "openstack_lb_monitor_v2" "monitor_https" {
name = "monitor HTTPS"
pool_id = openstack_lb_pool_v2.pool_https.id
type = "PING"
delay = "10"
timeout = "4"
max_retries = "5"
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем доступность мониторинга инстансов в целевых группах средствами openstack-cli:
- Проверяем доступность мониторинга инстансов в целевых группах средствами веб-интерфейса:
- Создаём файл output.tf:
vim output.tf
-
- Помещаем следующее содержимое:
- Для печати на экран "внешний (публичные)" плавающие IP-адреса всех инстансов;
- Понадобится в дальнейшем для сохранения вывода в файл по требованиям задания;
- Помещаем следующее содержимое:
output "WebADM" {
value = openstack_networking_floatingip_v2.floatingip_webadm.address
depends_on = [ openstack_compute_instance_v2.webadm ]
}
output "WEB1" {
value = openstack_networking_floatingip_v2.floatingip_web1.address
depends_on = [ openstack_compute_instance_v2.web1 ]
}
output "WEB2" {
value = openstack_networking_floatingip_v2.floatingip_web2.address
depends_on = [ openstack_compute_instance_v2.web2 ]
}
output "LoadBalancer" {
value = openstack_networking_floatingip_v2.floatingip_loadbalancer.address
}
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- На текущий момент вся инфраструктура развёрнута в соответствие с требованиями задания, за исключением настройки VPN-туннеля и SSH по паролю в рамках туннельного соединения
- Реализуем недостающий функционал средствами связки Terraform и Ansible
- Создаём файл templates.tf:
vim templates.tf
-
- Помещаем следующее содержимое:
- Поскольку нельзя предугадать какие IP-адреса будут выданы инстансам в роли "внешних" (Плавающих) реализуем возможность генерации автоматического инвентаря для Ansible средствами Terraform;
- Т.к. в конечном варианте ожидается запуск единого скрипта который развёрнёт инфраструктуру а затем её настроит;
- Помещаем следующее содержимое:
data "template_file" "inventory" {
template = file("/home/altlinux/bin/_templates/inventory.tpl")
vars = {
user = "altlinux"
webadm = join("", [openstack_compute_instance_v2.webadm.name, " ansible_host=", openstack_networking_floatingip_v2.floatingip_webadm.address])
web1 = join("", [openstack_compute_instance_v2.web1.name, " ansible_host=", openstack_networking_floatingip_v2.floatingip_web1.address])
web2 = join("", [openstack_compute_instance_v2.web2.name, " ansible_host=", openstack_networking_floatingip_v2.floatingip_web2.address])
}
}
resource "local_file" "save_inventory" {
content = data.template_file.inventory.rendered
filename = "/home/altlinux/bin/ansible/inventory"
}
- Создаём директорию "_templates" где будет хранить шаблоны:
mkdir _templates
- Создаём сам файл шаблона для автоматической генерации инвентаря для Ansible:
vim _templates/inventory.tpl
-
- Помещаем в него следующее содержимое:
- В результате Terraform должен будет генерировать инвентарь для Ansible по пути /home/altlinux/bin/ansible/inventory описанный в ini-формате;
- Помещаем в него следующее содержимое:
${webadm}
${web1}
${web2}
[all:vars]
ansible_ssh_user = ${user}
ansible_ssh_extra_args = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
ansible_python_interpreter = /usr/bin/python3
- Создаём директорию для Ansible:
mkdir ansible
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие файла /home/altlinux/bin/ansible/inventory и его содержимое:
- Устанавливаем ansible:
sudo apt-get update && sudo apt-get install -y ansible
- Проверяем корректность сгенерированного инвентарного файла:
ansible -i ansible/inventory -m ping all
-
- Результат:
- Ansible успешно может подключиться с ControlVM до всех инстансов для дальнейшей их настройки;
- Результат:
- В файл templates.tf добавляем следующий код:
- Добавляя возможность Terraform автоматически генерировать конфигурационные файлы для WireGuard;
- Поскольку нет информации о "внешнем" (Плавающим) IP-адресе который будет назначем WebADM при новом развёртывании проекта;
- Он необходим для конфигурационного файла WireGuard для параметра Endpoint;
data "template_file" "web1_wg0" {
template = file("/home/altlinux/bin/_templates/web1_wg0.tpl")
vars = {
floatingip_webadm = join("",[openstack_networking_floatingip_v2.floatingip_webadm.address])
}
}
resource "local_file" "save_web1_wg0" {
content = data.template_file.web1_wg0.rendered
filename = "/home/altlinux/bin/ansible/wireguard/web1_wg0.conf"
}
data "template_file" "web2_wg0" {
template = file("/home/altlinux/bin/_templates/web2_wg0.tpl")
vars = {
floatingip_webadm = join("",[openstack_networking_floatingip_v2.floatingip_webadm.address])
}
}
resource "local_file" "save_web2_wg0" {
content = data.template_file.web2_wg0.rendered
filename = "/home/altlinux/bin/ansible/wireguard/web2_wg0.conf"
}
- Создаём сам файл шаблона конфигурационного файла туннельного соединения для инстанса WEB1:
vim _templates/web1_wg0.tpl
-
- Помещаем в него следующее содержимое:
[Interface]
Address = 10.20.30.2/29
PrivateKey = +OoP8y4QuTZL7P5Esr7YV7p/GqleqSTE5Mf6R1E6y38=
# WebAdm
[Peer]
PublicKey = Pn1bzrpZoBob/VeAvWDQfazwrZh18WarKSKzid2K1wc=
AllowedIPs = 10.20.30.0/29
PersistentKeepalive = 10
Endpoint = ${floatingip_webadm}:51820
- Создаём сам файл шаблона конфигурационного файла туннельного соединения для инстанса WEB2:
vim _templates/web2_wg0.tpl
-
- Помещаем в него следующее содержимое:
[Interface]
Address = 10.20.30.3/29
PrivateKey = UEN3Lbpm+NyXsw+XMEZPKHAaMocFRP0KfDZvIiu+BUk=
# WebAdm
[Peer]
PublicKey = Pn1bzrpZoBob/VeAvWDQfazwrZh18WarKSKzid2K1wc=
AllowedIPs = 10.20.30.0/29
PersistentKeepalive = 10
Endpoint = ${floatingip_webadm}:51820
- Создаём директорию для куда Terraform сохранит сгенерированные конфигурационные файлы:
mkdir ansible/wireguard
- Проверяем конфигурацию и план terraform validate и terraform plan и запускаем развёртывание ресурсов terraform apply:
- Результат:
- Проверяем наличие конфигурационных файлов для туннельных соединений и их содержимое:
- Должен корректно подставляться параметр Endpoint:
- Создаём конфигурационный файл туннельного соединения для инстанса WebADM:
vim ansible/wireguard/webadm_wg0.conf
-
- Помещаем в него следующее содержимое:
- Т.к. с точки зрения WireGuard инстанс WebADM будет выступать сервером, то данный файл может бьть статическим и не требуем какой-либо динамически получаемой информации;
- Помещаем в него следующее содержимое:
[Interface]
Address = 10.20.30.1/29
ListenPort = 51820
PrivateKey = wHpaZtEfAYGxVXhwzUa8OS1EbBXqrjDW8E1Z73pzTUI=
# WEB1
[Peer]
PublicKey = dZuyzPZVpJ4gyvMpJRCOI3PBGEIYe1x1MZuYyYHjfT0=
AllowedIPs = 10.20.30.2/32
# WEB2
[Peer]
PublicKey = ZSTT8CxQsxJ1KU9bO5PoSG6iLFBb8hQtIc4Kyox4AnU=
AllowedIPs = 10.20.30.3/32
- Создаём playbook который будет реализовывать настройку VPN-соединения средствами WireGuard между WebADM и WEB1, WEB2:
vim ansible/wireguard_playbook.yml
-
- Помещаем в него следующее содержимое:
---
- name: Install packages
hosts: all
become: true
tasks:
- name: Install Wireguard
community.general.apt_rpm:
name:
- wireguard-tools
- wireguard-tools-wg-quick
state: latest
update_cache: true
- name: Settings WebAdm Wireguard server
hosts: WebADM
become: true
tasks:
- name: Create directory '/etc/wireguard'
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: '0755'
- name: Copy file 'wg0.conf'
ansible.builtin.copy:
src: wireguard/webadm_wg0.conf
dest: /etc/wireguard/wg0.conf
- name: Started and enabled wg0
ansible.builtin.systemd:
name: wg-quick@wg0
state: started
enabled: true
- name: Settings WEB1 Wireguard client
hosts: WEB1
become: true
tasks:
- name: Create directory '/etc/wireguard'
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: '0755'
- name: Copy file 'wg0.conf'
ansible.builtin.copy:
src: wireguard/web1_wg0.conf
dest: /etc/wireguard/wg0.conf
- name: Started and enabled wg0
ansible.builtin.systemd:
name: wg-quick@wg0
state: started
enabled: true
- name: Settings WEB2 Wireguard client
hosts: WEB2
become: true
tasks:
- name: Create directory '/etc/wireguard'
ansible.builtin.file:
path: /etc/wireguard
state: directory
mode: '0755'
- name: Copy file 'wg0.conf'
ansible.builtin.copy:
src: wireguard/web2_wg0.conf
dest: /etc/wireguard/wg0.conf
- name: Started and enabled wg0
ansible.builtin.systemd:
name: wg-quick@wg0
state: started
enabled: true
- Устанавливаем коллекцию необходимую для работы модуля community.general.apt_rpm:
ansible-galaxy collection install community.general
-
- Результат:
- Запускаем playbook-сценарий для настройки VPN-туннеля:
ansible-playbook -i ansible/inventory ansible/wireguard_playbook.yml
-
- Результат:
- Проверяем наличие VPN-соединения:
- Подключаемся по SSH с ControlVM на WebADM по Плавающему IP-адресу;
- Смотрим туннельные соединения;
- Проверяем связность;
- Создаём playbook который будет реализовывать:
- WebADM так, чтобы она могла подключаться по SSH с использованием пользователя altlinux и пароля «P@ssw0rd» к инстансам WEB1 и WEB2 с помощью VPN туннеля
vim ansible/ssh_playbook.yml
-
- Помещаем в него следующее содержимое:
---
- hosts: WEB1 WEB2
become: true
tasks:
- name: Setting 'sshd_config' file
ansible.builtin.lineinfile:
line: "{{ item }}"
path: /etc/openssh/sshd_config
state: present
with_items:
- "PasswordAuthentication no"
- "Match address 10.20.30.0/29"
- " PasswordAuthentication yes"
- name: Restarted sshd
ansible.builtin.systemd:
name: sshd
state: restarted
- Запускаем playbook-сценарий для настройки SSH по VPN-туннелю:
ansible-playbook -i ansible/inventory ansible/ssh_playbook.yml
-
- Результат:
- Проверяем:
- Подключение с WebADM по туннельным IP-адресам на основе пароля:
- Также проверяем что подключение по SSH из внешних сете (не по туннелю) доступно только по ключу:
- Таким образом, на текущий момент получаем следующую структуру файлов и каталогов в рамках каталога /home/altlinux/bin на ControlVM:
Последнее изменение: среда, 15 января 2025, 16:18