Skip to content

Otus-DevOps-2023-09/stpzuev_infra

Repository files navigation

stpzuev_infra

Stepan Zuev Infrastructure repository for educational purposes

Homework 9, Ansible 2

Main Part

Clean ups

Закомментировал провиженининг в модулях.

Single Playbooks

Создаем единый плейбук для всех блоков нашего проекта. Разделяем его выполнение при помощи тегов.

DB part

Создаем новый плейбук ./ansible/reddit_app.yml

Добавляем переменную, используем template модуль.

Handlers

Используем хендлер после изменения конфигурации.

  tasks:
    - name: Change mongo config file
      ...
      notify: restart mongodb # trigger here
  handlers: 
  - name: restart mongodb # exec here
    ...

App part

Добавляем puma.service на хост приложения. Он будет работать с конфигом для бд.

Создаем шаблон конфигурации с данными о бд /home/ubuntu/db_config, добавляем адрес бд через переменную

Deploy part

Используем модули git и bundler. Разворачиваем наше приложение из репозитория.

Проверяем наш плейбук, ограничиваем и при помощи

ansible-playbook reddit_app.yml --limit app --tags deploy-tag 

Multiple Playbook

Разбиваем плейбук на логические части. Вызываем нужные части так же при помощи тегов.

Splited Playbook

Разбиваем наш плейбук на отдельные файлы: app.yml, db.yml, deploy.yml

Копируем логические блоки из единого плейбука в эти файлы. Теперь мы можем исполнять нужные части без тегов.

Что бы исполнить все части одновременно, создадим новый плейбук.

Файл ansible/site.yml

---
- import_playbook: db.yml
- import_playbook: app.yml
- import_playbook: deploy.yml

Проверяем работу плейбука

ansible-playbook site.yml

Homework 8, Ansible 1

Main Part

  • Установил Ansible.
  • Создал первые конфиг файлы
    • ansible.cfg - параметры по-умолчанию
    • inventory - описание группы хостов и параметры доступа к ним
    • inventory.yaml - то же самое только в yaml формате
  • Создал первый плейбук clone.yaml
  • Проверил работу

Для использования инвентори отличного от указанного по-умолчанию необходимо использовать опцию "-i"

$ ansible all -m ping -i inventory.yml

В моей конфигурации хост БД создается без внешнего адреса, пришлось настроить подключение через jump сервер.

db:
  hosts:
    10.128.0.13:
  vars:
    ansible_ssh_common_args:
      '-J ubuntu@158.160.120.253'

Плейбук проверяет состояние системы и применяет команды только если они приведут к изменениям конфигурации.

Bonus part

Создал inventory.json для своих хостов. Проверил, работает.

Создал скрипт inventory.sh который генерирует список хостов в JSON формате. Конечно с поддержкой опций --list и --host

yc compute instance get --name reddit-app-stage --format=json | jq -r '.network_interfaces[0].primary_v4_address.one_to_one_nat.address'

Дальше пробуем исполнить ansible с нашим динамическим inventory

$ ansible -i inventory.sh all -m ping

Меняем в нашем конфиг файле значение inventory на наш скрипт

inventory = ./inventory.sh

Проверяем

$ ansible all -m ping
158.160.48.94 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
10.128.0.28 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Не забыть добавить исполняемость в гите

$ git update-index --chmod=+x inventory.sh

Homework 7, Terraform 2

Main Part

Clean project before

Packer new images

Bake!

packer build -var-file="variables.json" app.json
packer build -var-file="variables.json" db.json

Done!

Split main.tf

        /  -  app.tf
main.tf
        \  -  db.tf
        \  -  vpc.tf

New variables in variables.tf

New file vpc.tf and edit main.tf

Edit outputs.tf

Boot images

Terraform yandex images Doc

VPC

Quota for networks = 2 by default in Yandex. Can be upgraded in WebUI.

Apply everything. Done.

!!! Question !!! Можно ли автоматически получать id образов своих сборок по source_family?

Modules

Copy paste

terraform get
terraform apply

SSH test. Done

Stage & Prod

Copy. Paste. Edit both main.tf

terraform init
terraform apply
terraform destroy
terraform fmt

Bonus Part

backend.tf

Create bucket YC Doc Bucket

yc storage bucket create --help

yc storage bucket create `
--name "stpzuev-app-backend" `
--default-storage-class cold

Плохой вариант. Лучше создать бакет в терраформ. Создадим в корне проекта bucket.tf

...
resource "yandex_storage_bucket" "terraform-storage" {
  bucket        = var.bucket_name
  access_key    = var.access_key
  secret_key    = var.secret_key
  force_destroy = "true"
}

Create config YC Terraform State Storage

Terraform backend bucket

Create backend.tf at /stage

terraform init
>> The parameter "endpoint" is deprecated. Use parameter "endpoints.s3" instead.

Здесь было потрачено очень много времени на поиск решения в глючном Terraform 1.6.3. Пришлось откатиться до 1.5.7 и "О ЧУДО!" все заработало по инструкции.

Создаем бакет. Создаем статический ключ через веб. Копируем значения.

$Env:ACCESS_KEY="key value"
$Env:SECRET_KEY="key value"
terraform init -backend-config="access_key=$env:ACCESS_KEY" -backend-config="secret_key=$env:SECRET_KEY"

Удаляем terraform.tfstate делаем apply. Копируем папку в новое место делаем init + apply. Все раеботает.

Bonus part 2

Копируем deploy.sh в модуль app. Добавляем provision с сервисом puma в main.tf. Но нам нужно передать адрес инстанса DB в передаваемый сервис.

Переделаем puma.service в темплейт puma.tpl Terraform Template

Добавляем переменную db_ip_address и передаем ее в модуль app. Запускаем puma.service.

Запускаем проект. Все работает.


Homework 6, Terraform 1

OAuth Token

Main Part

Preps

New branch terraform-1

Create main.tf at /terraform, with auth information. Better to use service account.

yc config list

Yandex Terraform install

$Env:YC_TOKEN=$(yc iam create-token)
$Env:YC_CLOUD_ID=$(yc config get cloud-id)
$Env:YC_FOLDER_ID=$(yc config get folder-id)

terraform providers lock -net-mirror=https://terraform-mirror.yandexcloud.net -platform=linux_amd64 -platform=darwin_arm64 -platform=windows_amd64 yandex-cloud/yandex

Trying to init

terraform init
terraform plan
terraform apply
terraform show
terraform destroy

Done!

Adding metadata with ssh key to "app" resource.

terraform apply
ssh ubuntu@<ip.yandex.app>

Done!

Output variables

Make file otputs.tf at /terraform

terraform refresh
...
Outputs:
external_ip_address_app = "51.250.69.230"

terraform output
external_ip_address_app = "51.250.69.230"

terraform output external_ip_address_app
"51.250.69.230"

Provisioners

provisioner "file" {
  source = "files/puma.service"
  destination = "/tmp/puma.service"
}

puma.service deploy.sh

Remote connection

terraform taint yandex_compute_instance.app
terraform plan
terraform apply -auto-approve
terraform destroy -auto-approve

На этом основная часть сделана. Переменные определены и применены.

Bonus part

Load balancer

YC Network Load Balancer DOCs

YC Load Balancer Instruction

Terraform Yandex LB Docs

Terraform Yandex LB Target Group

Duplicate reddit-app. Test. Ok!

Adding count variable

Create lb.tf

resource "yandex_lb_network_load_balancer" "foo" {
  name = "my-network-load-balancer"

  listener {
    name = "my-listener"
    port = 9292
    external_address_spec {
      ip_version = "ipv4"
    }
  }

  attached_target_group {
    target_group_id = "${yandex_lb_target_group.my-target-group.id}"

    healthcheck {
      name = "http"
      http_options {
        port = 9292
        path = "/"
      }
    }
  }
}

Start. Test. Destroy.

Небольшие проблемы с provisioning. Добавил в deploy.sh ожидание завершения apt

a=1; while [ -n "$(pgrep apt-get)" ]; do echo $a; sleep 1s; a=$(expr $a + 1); done

Homework 5, Packer

Main Part

Packer Yandex plugin integration

Install packer

Download link https://hashicorp-releases.yandexcloud.net/packer/1.9.4/

wget packer.zip
unzip packer.zip
sudo mv packer /usr/local/bin
packer -v

YC Service Account

yc config list
yc iam service-account create --name $SVC_ACCT --folder-id $FOLDER_ID
yc iam service-account get $SVC_ACCT
yc resource-manager folder add-access-binding --id $FOLDER_ID \
--role editor \
--service-account-id $ACCT_ID
yc iam key create --service-account-id $ACCT_ID --output <вставьте свой путь>/key.json

Packer Template

Builder .json template ubuntu.json

Template with provisioning ubuntu1.json

Packer plugin for YandexCloud packer for yandex

packer init .\config.pkr.hcl
packer validate .\ubuntu16.json

Error for network. Edit ubuntu16.json in builders add

"subnet_id": "e9bda6c3k22fog******",
"zone": "ru-central1-a",
"use_ipv4_nat": "true"

Building image

packer build .\ubuntu16.json
yc compute images list

Creating new VM with our image via Web

Done!

Deploy application reddit

ssh -i ~/.ssh/appuser appuser@<публичный IP машины>
sudo apt-get update
sudo apt-get install -y git
git clone -b monolith https://github.com/express42/reddit.git
cd reddit && bundle install
puma -d

Done!

Exercise

Packer Variables Usage

Variables in .json

{
    "variable": "value"
}

Implementation

{
    "parameter": "{{user `variable`}}"
}

Testing

packer build -var-file=variables.json ubuntu.json

Starting by CLI

yc compute instance create `
--name reddit-app `
--zone ru-central1-a `
--hostname reddit-app `
--memory=2 `
--platform=standard-v3 `
--cores=2 `
--core-fraction 50 `
--create-boot-disk image-folder-id=<user-folder-id>,image-family=reddit-base `
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 `
--ssh-key C:\Users\****\.ssh\appuser.pub

Success!

Bonus part

Bake

immutable.json мы можем задеплоить приложение, но оно должно стартовать при запуске. Текущий скрипт содержит puma -d и эта часть не сохранится при "запекании". Нужно сделать скрипт, который будет автоматически запускаться как сервис.

systemd service статья как сделать из нашего скрипта сервис.

Создаем puma.service по инструкции. Теперь надо добавить его в сборку и активировать сервис в образе.

Добавляем в immutable.json:

"provisioners": [
    {
            "type": "file",
            "source": "./files/puma.service",
            "destination": "/tmp/puma.service"
    },
    {
      "type": "shell",
      "inline": [
        "sudo mv /tmp/puma.service /etc/systemd/system/puma.service",
        ...
        "cd /monolith/reddit && bundle install",
        "sudo systemctl daemon-reload",
        "sudo systemctl start puma",
        "sudo systemctl enable puma"
      ]
    }

Что бы использовать образы из reddit-base, packer должен знать где их искать. Добавляем в json "source_image_folder_id" со значением нашего folder_id packer yandex builder

"builders": [
    {
      "type": "yandex",
      ...
      "source_image_folder_id": "{{ user `folder_id`}}",
      "source_image_family": "reddit-base",

Собираем образ с установленным приложением

packer build -var-file=".\variables.json" .\immutable.json

Запускаем ВМ на образе reddit-full

yc compute instance create `
 --name reddit-app `
 --zone ru-central1-a `
 --hostname reddit-app `
 --platform=standard-v3 --cores=2 --memory=2 --core-fraction 50 `
 --create-boot-disk source-image-folder-id=b1gk6gb6l49ucpjkrmtr,image-family=reddit-full `
 --network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 `
 --ssh-key ...

Ждем когда поднимется инстанс. Вуа-ля! Приложение работает из коробки.

Automate

Упаковываем все в скрипт create-reddit-vm.sh

Homework 4, Test Application Deploy and Run:

Main Part

Variables for Tests

testapp_IP = 158.160.117.127
testapp_port = 9292

Git checkout

git checkout -b cloud-testapp
git mv cloud-bastion.ovpn ./VPN/
git mv setupvpn.sh ./VPN/

Yandex Cloud CLI

Необходимо использовать версию образа указанную в проекте ubuntu-1604-lts

yc compute instance create `
--name reddit-app `
--zone ru-central1-a `
--hostname reddit-app `
--memory=2 `
--platform=standard-v3 `
--cores=2 `
--core-fraction 50 `
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1604-lts,size=8 `
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 `
--ssh-key C:\Users\stpzu\.ssh\appuser.pub

Проверяем подключение к хосту через ssh yc-user@

ENV Install

sudo apt update
sudo apt install -y ruby-full ruby-bundler build-essential
sudo apt install mongodb
sudo systemctl enable mongodb
sudo systemctl start mongodb
sudo systemctl status mongodb

Application Deploy

sudo apt install -y git
git clone -b monolith https://github.com/express42/reddit.git
cd reddit && bundle install
puma -d
ps aux | grep puma

Common Bash Scripts

Собираем все что набили выше в скрипты и добавляем в гите атрибуты исполняемости

git update-index --chmod=+x install_ruby.sh
git update-index --chmod=+x install_mongodb.sh
git update-index --chmod=+x deploy.sh

Bonus Part

Bash Script for deploy

Собираем все в один скрипт startup.yaml, внутри будет и скрипт и пользовательские данные

Теперь запускаем ВМ которая сразу запускается с нашим приложением через CLI

yc compute instance create `
--name reddit-app2 `
--zone ru-central1-a `
--hostname reddit-app2 `
--memory=2 `
--platform=standard-v3 `
--cores=2 `
--core-fraction 50 `
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1604-lts,size=8 `
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 `
--metadata-from-file user-data=startup.yaml

Homework 3, SSH Proxy Jump

Основное задание

Данные для подключения к YC

bastion_IP = 158.160.41.35
someinternalhost_IP = 10.128.0.21

Первый способ правильный, использовать опцию "-J"

ssh -J appuser@158.160.41.35 appuser@10.128.0.21

Второй способ с костылями

ssh -A -t appuser@158.160.41.35 ssh appuser@10.128.0.21

Запускаем удаленно ssh подключение, "-A" использует ssh агента, "-t" запускает команду на псевдо-терминале.

Бонусное задание

SSH Config Alias

Добавляем настройки для бастиона и локальных машин в ~/.ssh/config

Host bastion
  user appuser
  IdentityFile ~/.ssh/appuser
  hostname 158.160.41.35

Host someinternalhost
  IdentityFile ~/.ssh/appuser
  hostname 10.128.0.21
  user appuser
  proxyjump gateway

Подключаемся к бастиону

ssh bastion

Подключаемся к локальной машине

ssh someinternalhost

Pritunl Bastion Lets Encrypt

В веб интерфейсе Pritunl нажимаем Settings, выбираем Lets Encrypt Domain и прописываем "<наш ip-адрес>.nip.io" Сохраняем. Перезагружем страницу и все работает. https://158.160.41.35.nip.io

How to make README readable

About

stpzuev Infra repository

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •