Skip to main content

Case 1

# Все последующие действия выполняются из-под пользователя altlinux (не root)
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 (имя произвольное) и поместим в него следующее содержимое:
# указав переменные окружения для работы OpenStack CLI с API облака
# в данном конкретном примере подразумевается что у участника:
# Внешний доступ к панели самообслуживания: https://172.17.60.13:8800
# Домен: Competence_SiSA (Если неизвестна, то можно посмотреть в адресной строке на экране логина)
# Проект: Project2
# Учётная запись: User2
# Пароль от уч.записи: P@ssw0rd
export OS_AUTH_URL=https://172.17.60.13: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=Project2
export OS_USERNAME=User2
export OS_PASSWORD=P@ssw0rd

# Применяем переменные окружения указанные в файле
source cloudinit.conf
# Тест (должно вывести сервер Cloud-ADM):
openstack --insecure server list
# Тест созданных сетей:
openstack --insecure network list
# Если есть ошибки, то исправить конфиг выше
# Например url будет не по https://cyberinfra.ssa2026.region:5000/v3, а по ip адресу

# Для корректной работы с Terraform, необходимо создадать файл конфигурации зеркала
# Файл должен иметь имя .terraformrc ибыть расположен в домашнем каталоге пользователя
# Файл ~/.terraformrc должен содержать в себе следующее:
nano  ~/.terraformrc
# Заполнить:
provider_installation {
    network_mirror {
        url = "https://terraform-mirror.mcs.mail.ru"
        include = ["registry.terraform.io/*/*"]
    }
    direct {
        exclude = ["registry.terraform.io/*/*"]
    }
}

# Перейти в директорию проекта/terraform
cd Project_01/terraform/
# СОздать файл:
provider.tf
# Заполнить:
terraform {
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "2.1.0"
    }
  }
}

provider "openstack" {
  auth_url    = "https://172.17.2.11:5000/v3"
  tenant_name = "Project2"
  user_name   = "User2"
  password    = "P@ssw0rd"
  insecure    = true
}
# Инициализируем текущий каталог для работы с terraform и провайдером openstack:
terraform init
# Должно вывести: Terraform has been succsessfully initialized!
# Чтобы с инстанса 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):
nano cloud-init.yml
# Содержимое файла (обратите внимание, строка #cloud-config бязательна):

#cloud-config
users:
  - name: altlinux
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: sudo
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDN/HflhQqq+He0Quo8c2edmgawCvq1gP2V+vfcLNIdL6u1VQskeKGKZBvPlAIZAvbvA49aP6BK1s3Eej+rbKxOmcUfLl4q9CDXSwy5zpcKw/nhxBG712D9LJr3EMDLHUbT76nRaThQUegRVN5SZeHs0s00Kk3PbLTwikt1UmPmQ1gjcYCIrCTIrda+7Rjnw6n5jzYVM0GdcDi8bxb43nXnif2VaNaYEyjhwei2VZwRs9VeU3lnVSK5cCi8TasqLTfEgEuEyHjRxzC/vDHkkGtd+06f5lOS57f/cuvhqxK1rmOoaL8duT+agxw0miu8QBTMpKMwyw68OWxudiDE14aCwgPFEgx70ui50hzVvDenv8XiksmVgoh5kqf/XaVm77ULb9HxtamCfpT+gjq8vwwLuJLYmxXq07KbutBm0aMA7n2dAiSjpd5Y392Wq/ySJIzf+KB/wzBpzFMNDrt5xWyPPJm5pIfRAdmHKDk0aho3ls9BycF0zc+CxrEImHwahuc= altlinux@cloud-a
chpasswd:
  list: |
    altlinux:P@ssw0rd
    root:toor
  expire: false
  ssh_pwauth: false


# Создадим файл vm-game.tf и опишем конфигурацию для создаваемых инстансов game01, game02 и game03:
# Значение для параметра 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 = "6b979c3e-fa96-4e95-a04c-7a44f46c8cfa"
  user_data = file("cloud-init.yml")

  block_device {
    uuid                  = "3903b898-594d-4c62-b4d5-ec413e6b106f"
    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       = "6b979c3e-fa96-4e95-a04c-7a44f46c8cfa"
  user_data       = file("cloud-init.yml")

  block_device {
    uuid                  = "3903b898-594d-4c62-b4d5-ec413e6b106f"
    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     = "50c56eef-a65c-4f74-867b-9ebf90c62071"
    admin_state_up = true

    fixed_ip {
        subnet_id   = "427eb4e8-4e42-4c1b-8ad4-0db8812d6a1a"
        ip_address  = "192.168.10.10${count.index + 1}"
    }
}

resource "openstack_networking_port_v2" "haproxy" {
    name           = "port_vm_haproxy"
    network_id     = "50c56eef-a65c-4f74-867b-9ebf90c62071"
    admin_state_up = true

    fixed_ip {
        subnet_id   = "427eb4e8-4e42-4c1b-8ad4-0db8812d6a1a"
        ip_address  = "192.168.10.100"
    }
}

# Создадим файл floatingip.tf и опишем конфигурацию Плавающего IP-адреса в соответствие с топологией для Project_01 (убедитесь что имя пула вы выбрали по имени пула с внешней сетью, например "public" или "PUB" как в моем случае):
resource "openstack_networking_floatingip_v2" "floatingip_haproxy" {
  pool = "PUB"
}

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
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"
# Выдать права
chmod +x deploy_project_01.sh
# Запустить
./deploy_project_01.sh
# В идеале будут созданы 3 виртуальные машины и haproxy присвоен плавающий ip
# Отредактируйте файл /etc/hosts
127.0.0.1	localhost.localdomain localhost
::1	localhost6.localdomain localhost6
192.168.10.100	haproxy.dev.au.team	    haproxy01
192.168.10.101	game01.dev.au.team	    game01
192.168.10.102  game02.dev.au.team      game02
192.168.10.103  game03.dev.au.team      game03
192.168.10.100	game.au.team		    game

# Создадим директорию ansible в ранее созданной директории для Project_01
mkdir /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
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
# Скачиваем любым способом файлы <ссылка на яндекс диск>
# Распаковываем архив в директорию /home/altlinux/Projects/Project_01/2048-game/
# Пример использования утилиты unzip
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
---
- 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
# В браузере открываем по очереди game01.dev.au.team, game02.dev.au.team
# В директории /home/altlinux/Projects/Project_01/ansible создаём директорию files
# В директории /home/altlinux/Projects/Project_01/ansible/files создаём конфигурационный файл haproxy.cfg
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 https_front
    bind *:443 ssl crt /var/lib/ssl/game.pem
    mode http
    default_backend https_back

backend https_back
    balance roundrobin
    server game01 192.168.10.101:80 check
    server game02 192.168.10.102:80 check
    server game03 192.168.10.103:80 check

listen stats
bind :80
stats enable
stats uri /haproxyhaproxy01
stats refresh 5s
stats realm Haproxy\ Stats

# Средствами утилиты openssl разворачиваем свой Удостоверяющий Центр сертификатов
openssl req -x509 -sha256 -days 3653 -newkey rsa:2048 -keyout ca.key -out ca.crt
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust
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.10.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

cat files/game.key files/game.crt > files/game.pem

# В директории /home/altlinux/Projects/Project_01/ansible создаём файл proxys_playbook.yml
---
- 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

# При открытии https://game.au.team должна открываться страница без ошибок безопсности
# haproxy01.dev.au.team страница haproxy
cd /home/altlinux/Projects/Project_01
# Создадим файл configure_project_01.sh
#!/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
# Делаем исполняемым
chmod +x configure_project_01.sh
./configure_project_01.sh

# Создадим файл destroy_project_01.sh
#!/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"

# Делаем исполняемым
chmod +x destroy_project_01.sh

./destroy_project_01.sh