Источник: alt-linuxmetod.ru
Все последующие действия выполняются из-под пользователя altlinux (не root)
- Создаём директорию /home/altlinux/Projects/Project_01:
mkdir -p /home/altlinux/Projects/Project_01
- Для скрипта deploy_project_01.sh будет использоваться Terraform
- Создадим директорию terraform в ранее созданной директории для Project_01:
mkdir /home/altlinux/Projects/Project_01/terraform
- Перейдём в директорию /home/altlinux/Projects/:
cd /home/altlinux/Projects/
- Создадим файл с именем cloudinit.conf (имя произвольное) и поместим в него следующее содержимое:
export OS_AUTH_URL=https://cyberinfra.ssa2026.region:5000/v3
export OS_IDENTITY_API_VERSION=3
export OS_AUTH_TYPE=password
export OS_PROJECT_DOMAIN_NAME=Competence_SiSA
export OS_USER_DOMAIN_NAME=Competence_SiSA
export OS_PROJECT_NAME=Project0
export OS_USERNAME=User0
export OS_PASSWORD=P@ssw0rd
- Применяем переменные окружения указанные в файле
source cloudinit.conf
- Проверяем возможность взаимодействовать чере openstack-cli с облаком:например, выведем список инстансов командой openstack —insecure server list
- или сетей командой openstack —insecure network list
- Для корректной работы с Terraform, необходимо создадать файл конфигурации зеркала
- Файл должен иметь имя .terraformrc ибыть расположен в домашнем каталоге пользователя
- Файл ~/.terraformrc должен содержать в себе следующее:
provider_installation {
network_mirror {
url = "https://terraform-mirror.mcs.mail.ru"
include = ["registry.terraform.io/*/*"]
}
direct {
exclude = ["registry.terraform.io/*/*"]
}
}
- Перейдём в директорию Project_01/terraform/:
cd Project_01/terraform/
- Создадим файл provider.tf и опишем параметры для подключения к провайдеру openstack для работы с облаком:
в данном конкретном примере подразумевается что у участника:Внешний доступ к панели самообслуживания: https://cyberinfra.ssa2026.region:8800 Домен: Competence_SiSA
Проект: Project0
Учётная запись: User0
Пароль от уч.записи: P@ssw0rd
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "2.1.0"
}
}
}
provider "openstack" {
auth_url = "https://cyberinfra.ssa2026.region:5000/v3"
tenant_name = "Project0"
user_name = "User0"
password = "P@ssw0rd"
insecure = true
}
- Инициализируем текущий каталог для работы с terraform и провайдером openstack:
terraform init
- Результат успешной инициализации Terraform провайдера
- Чтобы с инстанса Cloud-ADM был доступ по SSH (для Ansible) до всех создаваемых инстансов (средствами Terraform), будет использоваться файл cloud-init.yml
- Создадим ключевую пару:
ssh-keygen -t rsa
- Поместим содержимое публичного ключа (id_rsa.pub) в файл cloud-init.yml:
cat ~/.ssh/id_rsa.pub > cloud-init.yml
- Откроем файл cloud-init.yml на редактирование и добавим в самое начало (до содержимого ключа ssh):
#cloud-config
chpasswd:
expire: false
users:
- {name: altlinux, password: P@ssw0rd, type: text}
- {name: root, password: toor, type: text}
ssh_pwauth: false
users:
- name: altlinux
sudo: ALL=(ALL) NOPASSWD:ALL
groups: wheel
shell: /bin/bash
ssh_authorized_keys:
- <СОДЕРЖИМОЕ_ПУБЛИЧНОГО_КЛЮЧА_SSH>
- Создадим файл vm-game.tf и опишем конфигурацию для создаваемых инстансов game01, game02 и game03:
для минимального написания строк кода (время на выполнения задания не резиновое, хардкод приветствуется, данный модуль не проверяет применение лучших практик для автоматизации):используем цикл по счётчику count;
избавляемся от параметризации с использованием переменных (хардкодим все значения в данный файл).
значение для параметра flavor_id можно узнать используя команду openstack —insecure flavor list скопировав ID Типа ВМ с 1024 RAM и 1 VCPUs;
значение для параметра uuid можно узнать используя команду openstack —insecure image list скопировав ID образа alt-p11-cloud-x86_64.qcow2.
resource "openstack_compute_instance_v2" "game" {
count = "3"
name = "game0${count.index + 1}"
flavor_id = "03bf1b85-2f5f-4ada-a07b-8b994b6dcb57"
user_data = file("cloud-init.yml")
block_device {
uuid = "827e08fa-fd3c-41cd-92ca-845bb5018478"
source_type = "image"
volume_size = "10"
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.port_vm_game[count.index].id
}
}
- Создадим файл vm-haproxy01.tf и опишем конфигурацию для создаваемого инстанса haproxy01:значение для параметра flavor_id можно узнать используя команду openstack —insecure flavor list скопировав ID Типа ВМ с 1024 RAM и 1 VCPUs;
значение для параметра uuid можно узнать используя команду openstack —insecure image list скопировав ID образа alt-p11-cloud-x86_64.qcow2.
resource "openstack_compute_instance_v2" "haproxy" {
name = "haproxy01"
flavor_id = "03bf1b85-2f5f-4ada-a07b-8b994b6dcb57"
user_data = file("cloud-init.yml")
block_device {
uuid = "827e08fa-fd3c-41cd-92ca-845bb5018478"
source_type = "image"
volume_size = "10"
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.haproxy.id
}
}
- Создадим файл network.tf и опишем конфигурацию сети в соответствие с топологией для Project_01:значение для параметра network_id можно узнать используя команду openstack —insecure network list скопировав ID виртуальной сети с именем cloud-net;
значение для параметра subnet_id можно узнать используя команду openstack —insecure subnet list скопировав ID виртуальной подсети 192.168.1.0/24;
resource "openstack_networking_port_v2" "port_vm_game" {
count = "3"
name = "port_vm_game0${count.index + 1}"
network_id = "61845892-f9cc-4fde-962c-34b59425a74d"
admin_state_up = true
fixed_ip {
subnet_id = "13592ca4-8782-410b-9bcc-90810ccab6fe"
ip_address = "192.168.1.10${count.index + 1}"
}
}
resource "openstack_networking_port_v2" "haproxy" {
name = "port_vm_haproxy"
network_id = "61845892-f9cc-4fde-962c-34b59425a74d"
admin_state_up = true
fixed_ip {
subnet_id = "13592ca4-8782-410b-9bcc-90810ccab6fe"
ip_address = "192.168.1.100"
}
}
- Создадим файл floatingip.tf и опишем конфигурацию Плавающего IP-адреса в соответствие с топологией для Project_01:
resource "openstack_networking_floatingip_v2" "floatingip_haproxy" {
pool = "public"
}
resource "openstack_networking_floatingip_associate_v2" "association_haproxy" {
port_id = openstack_networking_port_v2.haproxy.id
floating_ip = openstack_networking_floatingip_v2.floatingip_haproxy.address
}
- Выполняем проверку синтаксиса и структуры файлов конфигурации Terraform:
terraform validate
- Результат Success
- Перейдём в директорию /home/altlinux/Projects/Project_01:
cd /home/altlinux/Projects/Project_01
- Создадим файл deploy_project_01.sh и укажем инструкции необходимые для запуска Terraform:
#!/bin/bash
cd /home/$USER/Projects
source cloudinit.conf
cd /home/$USER/Projects/Project_01/terraform
terraform init
terraform apply -auto-approve
sleep 60
echo "done"
- Выдать права на исполнение для файла deploy_project_01.sh:
chmod +x deploy_project_01.sh
- Запустить скрипт на исполнение:
./deploy_project_01.sh
- Результат: Apply complete
- Проверить автоматически созданные инстансы в облаке
- Проверить Плавающий IP-адрес для инстанса haproxy01
- Поскольку IP-адресация задаётся статическая, то для удобства работы редактируем конфигурационный файл /etc/hosts:
- Создадим директорию ansible в ранее созданной директории для Project_01:
mkdir /home/altlinux/Projects/Project_01/ansible
- Перейдём в директорию /home/altlinux/Projects/Project_01/ansible:
cd /home/altlinux/Projects/Project_01/ansible
- Создадим файл конфигурации ansible.cfg поместим в него следующее содержимое:
[defaults]
inventory = ./inventory.yml
host_key_checking = False
callback_enabled = profile_tasks
callback_whitelist = profile_tasks
- Создадим файл inventory.yml и опишем файл инвентаря для Ansible:
описав 2 группы хостов с именами proxys и games:группа proxys содержит в себе инстанс haproxy01;
группа games содержит в себе инстансы game01, game02 и game03;
all:
children:
proxys:
hosts:
haproxy01:
games:
hosts:
game01:
game02:
game03:
- Создадим директорию group_vars где будем создавать файлы с переменными для групп хостов:
mkdir group_vars
- Создадим файл ‘all.yml‘ в директории group_vars:
---
ansible_python_interpreter: /usr/bin/python3
ansible_ssh_user: altlinux
ansible_ssh_private_key_file: ~/.ssh/id_rsa
- Проверяем возможность ansible подключиться к инстансам описанным в инвентарном файле:
ansible -m ping all
- Результат Success
- Скачиваем любым способом файлы https://disk.yandex.ru/d/uhpN6U6UYRK_Nw (у приложения есть README от программиста-разработчика)
- Распаковываем архив в директорию /home/altlinux/Projects/Project_01/2048-game/:
unzip ~/Project_01.zip -d /home/altlinux/Projects/Project_01/2048-game/
- В директории /home/altlinux/Projects/Project_01/ansible создаём файл Dockerfile:образ приложения нужно сделать легковесным через мультистейджинг
FROM node:16-alpine AS builder
WORKDIR /2048-game
COPY package*.json ./
RUN npm install --include=dev
COPY . .
RUN npm run build
EXPOSE 8080
FROM nginx:stable-alpine3.19
COPY --from=builder /2048-game/dist /usr/share/nginx/html
- В директории /home/altlinux/Projects/Project_01/ansible создаём файл games_playbook.yml:
таким образом, на группе хостов games (на инстансах: game01, game02 и game03) будет настроено:Установлены пакеты: docker-engine и docker-buildx;
Запущена и добавлена в автозагрузку служба docker;
Скопированы все файлы с Cloud-ADM на удалённые хосты, необходимые для приложения;
Скопирован с Cloud-ADM на удалённые хосты файл Dockerfile;
На удалённых хостах выполнена локальная сборка образа для приложения;
На удалённых хостав выполнен запуск контейнеров с веб-приложением.
---
- hosts: games
become: true
tasks:
- name: Install docker
community.general.apt_rpm:
name:
- docker-engine
- docker-buildx
state: present
update_cache: true
- name: Started and enabled docker
ansible.builtin.systemd:
name: docker
state: started
enabled: true
- name: Copying the project files
ansible.builtin.copy:
src: ../2048-game/
dest: "/home/{{ ansible_ssh_user }}/2048-game/"
- name: Copying the Dockerfile
ansible.builtin.copy:
src: ./Dockerfile
dest: "/home/{{ ansible_ssh_user }}/2048-game/"
- name: Build docker image
community.docker.docker_image_build:
name: "2048-game"
tag: latest
path: "/home/{{ ansible_ssh_user }}/2048-game/"
dockerfile: Dockerfile
- name: Start docker container
community.docker.docker_container:
name: "2048-game"
image: "2048-game"
ports: "80:80"
state: started
restart: true
- На текущем этап можно выполнить тестовый запуск созданного playbook-а games_playbook.yml:
ansible-playbook games_playbook.yml
- Результат успешно
- Таким образом при обращении в браузере с Cloud-ADM по описанным ниже url должно открываться веб-приложение:
- game01.dev.au.team
- game02.dev.au.team
- game03.dev.au.team
- В директории /home/altlinux/Projects/Project_01/ansible создаём директорию files:
mkdir files
- В директории /home/altlinux/Projects/Project_01/ansible/files создаём конфигурационный файл haproxy.cfg
в данном файле стандартное содержимое haproxy.cfg за исключением блока frontend и backend для реализации необходимого функционаладля удобства можно найти конфигаруционный файл в сети Интернет;
или же выполнить установку пакета haproxy и забрать его из директории /etc/haproxy/haproxy.cfg (т.к. инстанс haproxy01 в любом случае будет удалён).
global
log /dev/log daemon
chroot /var/lib/haproxy
pidfile /run/haproxy.pid
maxconn 4000
user _haproxy
group _haproxy
daemon
stats socket /var/lib/haproxy/stats
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
frontend http_front
bind *:80
bind *:443 ssl crt /var/lib/ssl/game.pem
default_backend http_back
backend http_back
balance roundrobin
server game01 192.168.1.101:80 check
server game02 192.168.1.102:80 check
server game03 192.168.1.103:80 check
listen stats
bind :80
bind *:443 ssl crt /var/lib/ssl/game.pem
stats enable
stats uri /
stats refresh 5s
stats realm Haproxy\ Stats
- Средствами утилиты openssl разворачиваем свой Удостоверяющий Центр сертификатов:
openssl req -x509 -sha256 -days 3653 -newkey rsa:2048 -keyout ca.key -out ca.crt
- Результат:
- Добавляем корневой сертификат в хранилище на Cloud-ADM:
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust
- Перемещаем файлы сертификатов в директорию files:
mv ca.* files/
- Генерируем ключи, запросы и сертификаты для веб:Ключ:
openssl genrsa -out files/game.key 2048
- Запрос:
openssl req -key files/game.key -new -out files/game.csr
- Результат:
- Файл с расширениями для сертификата:
cat <<EOF > files/game.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
subjectAltName=@alt_names
[alt_names]
DNS.1=game.au.team
IP.1=192.168.1.100
EOF
- Выпускаем сертификат:
openssl x509 -req -CA files/ca.crt -CAkey files/ca.key -in files/game.csr -out files/game.crt -days 365 -CAcreateserial -extfile files/game.ext
- Создаём цепочку для haproxy:
cat files/game.key files/game.crt > files/game.pem
- В директории /home/altlinux/Projects/Project_01/ansible создаём файл proxys_playbook.yml:
таким образом, на группе хостов games (на инстансах: game01, game02 и game03) будет настроено:Установлен пакет haproxy;
Скопирован с Cloud-ADM на удалённый хост файл haproxy.cfg;
Скопирован с CloudpADM на удалённый хост файл game.pem;
Включена и добавлена в автозагрузку служба haproxy.
---
- hosts: proxys
become: true
tasks:
- name: Install HAProxy
community.general.apt_rpm:
name: haproxy
state: present
update_cache: true
- name: Copy file 'haproxy.cfg'
ansible.builtin.copy:
src: files/haproxy.cfg
dest: /etc/haproxy/haproxy.cfg
notify:
- Restarted HAProxy
- name: Copy certificate for HAProxy
ansible.builtin.copy:
src: files/game.pem
dest: /var/lib/ssl/game.pem
notify:
- Restarted HAProxy
- name: Started and enabled HAProxy
ansible.builtin.systemd:
name: haproxy
state: restarted
enabled: true
handlers:
- name: Restarted HAProxy
ansible.builtin.systemd:
name: haproxy
state: restarted
- На текущем этап можно выполнить тестовый запуск созданного playbook-а proxys_playbook.yml:
ansible-playbook proxys_playbook.yml
- Результат успех
- Таким образом при обращении в браузере с Cloud-ADM по описанным ниже url должно открываться веб-приложение:https://game.au.team (в файле /etc/hosts должна быть добавлена запись):
- haproxy01.dev.au.team /haproxy?stats
- Смотрим плавающий IP-адрес и заходим по нему
- Перейдём в директорию /home/altlinux/Projects/Project_01:
cd /home/altlinux/Projects/Project_01
- Создадим файл configure_project_01.sh и укажем инструкции необходимые для запуска Ansible:
#!/bin/bash
export PATH=/home/altlinux/.local/bin:$PATH
cd /home/$USER/Projects/Project_01/ansible
sleep 10
ansible-playbook games_playbook.yml
sleep 5
ansible-playbook proxys_playbook.yml
- Выдать права на исполнение для файла configure_project_01.sh:
chmod +x configure_project_01.sh
- Запустить скрипт на исполнение:
./configure_project_01.sh
- Создадим файл destroy_project_01.sh и укажем инструкции необходимые для запуска Ansible:
#!/bin/bash
cd /home/$USER/Projects
source cloudinit.conf
cd /home/$USER/Projects/Project_01/terraform
terraform destroy -auto-approve
rm -f ~/.ssh/known_hosts
echo "done"
- Выдать права на исполнение для файла destroy_project_01.sh:
chmod +x destroy_project_01.sh
- Запустить скрипт на исполнение:
./destroy_project_01.sh
- Результат Destroy complete
- Таким образом, структура для Project01 получилась следующая: tree