cloud-bastion
Знакомство с облачной инфраструктурой
Запуск VM в Yandex Cloud, управление правилами фаервола, настройка SSH подключения, настройка SSH подключения через Bastion Host, настройка VPN сервера и VPN-подключения.
Цель: В данном дз студент создаст виртуальные машины в Yandex.Cloud. Настроит bastion host и ssh. В данном задании тренируются навыки: создания виртуальных машин, настройки bastion host, ssh
Все действия описаны в методическом указании.
Критерии оценки: 0 б. - задание не выполнено 1 б. - задание выполнено 2 б. - выполнены все дополнительные задания
-
Создаем инстансы VM bastion и someinternalhost через веб-морду Yandex.Cloud
-
Генерим пару ключей
ssh-keygen -t rsa -f ~/.ssh/appuser -C appuser -P ""
- Проверяем подключение по полученному внешнему адресу
ssh -i ~/.ssh/appuser [email protected]
The authenticity of host '84.252.136.193 (84.252.136.193)' can't be established.
ECDSA key fingerprint is SHA256:mbIKmLTZYygwUXSfTBJ8E017RPU9kNESKHYfdoDWbXY.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '84.252.136.193' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-42-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
dpp@h470m ~/otus-devops/Deron-D_infra $ ssh -i ~/.ssh/appuser [email protected]
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-42-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Sun Jun 20 13:12:26 2021 from 82.194.224.170
appuser@bastion:~$ ssh 10.129.0.18
The authenticity of host '10.129.0.18 (10.129.0.18)' can't be established.
ECDSA key fingerprint is SHA256:WsrgIfB+b7qerWOk1tNLqeyGmKoBfKdWkdVJKVzo6u8.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.0.18' (ECDSA) to the list of known hosts.
[email protected]: Permission denied (publickey).
- Используем Bastion host для прямого подключения к инстансам внутренней сети:
- Настроим SSH Forwarding на нашей локальной машине:
~/otus-devops/Deron-D_infra $ eval $(ssh-agent -s)
Agent pid 1739595
- Добавим приватный ключ в ssh агент авторизации:
~/otus-devops/Deron-D_infra $ ssh-add ~/.ssh/appuser
Identity added: /home/dpp/.ssh/appuser (appuser)
- Проверяем прямое подключение:
ssh -i ~/.ssh/appuser -A [email protected]
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-42-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Sun Jun 20 13:22:54 2021 from 82.194.224.170
appuser@bastion:~$ ssh 10.129.0.18
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-42-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
appuser@someinternalhost:~$ uname -n
someinternalhost
appuser@someinternalhost:~$ uname -a
Linux someinternalhost 5.4.0-42-generic #46-Ubuntu SMP Fri Jul 10 00:24:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
appuser@someinternalhost:~$ ip a show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether d0:0d:e1:f3:67:f3 brd ff:ff:ff:ff:ff:ff
inet 10.129.0.18/24 brd 10.129.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::d20d:e1ff:fef3:67f3/64 scope link
valid_lft forever preferred_lft forever
- Проверим отсутствие каких-либо приватных ключей на bastion машине:
appuser@bastion:~$ ls -la ~/.ssh/
total 16
drwx------ 2 appuser appuser 4096 Jun 20 13:19 .
drwxr-xr-x 4 appuser appuser 4096 Jun 20 13:12 ..
-rw------- 1 appuser appuser 561 Jun 20 13:11 authorized_keys
-rw-r--r-- 1 appuser appuser 222 Jun 20 13:19 known_hosts
- Самостоятельное задание. Исследовать способ подключения к someinternalhost в одну команду из вашего рабочего устройства:
Добавим в ~/.ssh/config содержимое:
dpp@h470m ~/otus-devops/Deron-D_infra $ cat ~/.ssh/config
Host 84.252.136.193
User appuser
IdentityFile /home/dpp/.ssh/appuser
Host 10.129.0.18
User appuser
ProxyCommand ssh -W %h:%p 84.252.136.193
IdentityFile /home/dpp/.ssh/appuser
Проверяем работоспособность найденного решения:
~/otus-devops/Deron-D_infra $ ssh 10.129.0.18
The authenticity of host '10.129.0.18 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:WsrgIfB+b7qerWOk1tNLqeyGmKoBfKdWkdVJKVzo6u8.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.0.18' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-42-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sun Jun 20 13:24:16 2021 from 10.129.0.30
- Дополнительное задание:
На локальной машине правим /etc/hosts
sudo bash -c 'echo "10.129.0.18 someinternalhost" >> /etc/hosts'
Добавим в ~/.ssh/config содержимое:
Host someinternalhost
User appuser
ProxyCommand ssh -W %h:%p 84.252.136.193
IdentityFile /home/dpp/.ssh/appuser
Проверяем:
dpp@h470m ~/otus-devops/Deron-D_infra $ ssh someinternalhost
The authenticity of host 'someinternalhost (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:WsrgIfB+b7qerWOk1tNLqeyGmKoBfKdWkdVJKVzo6u8.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'someinternalhost' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-42-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sun Jun 20 14:03:20 2021 from 10.129.0.30
- Создаем VPN-сервер для серверов Yandex.Cloud:
Создан скрипт установки VPN-сервера (setupvpn.sh)[./setupvpn.sh]
cloud-testapp
Деплой тестового приложения
Практика управления ресурсами yandex cloud через yc.
Цель: В данном дз произведет ручной деплой тестового приложения. Напишет bash скрипт для автоматизации задач настройки VM и деплоя приложения. В данном задании тренируются навыки: деплоя приложения на сервер, написания bash скриптов.
Ручной деплой тестового приложения. Написание bash скриптов для автоматизации задач настройки VM и деплоя приложения. Все действия описаны в методическом указании.
Критерии оценки: 0 б. - задание не выполнено 1 б. - задание выполнено 2 б. - выполнены все дополнительные задания
- Установлен YC CLI:
curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash
- Создан новый инстанс reddit-app create_instance.sh:
yc compute instance create \
--name reddit-app \
--hostname reddit-app \
--memory=4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1604-lts,size=10GB \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--metadata serial-port-enable=1 \
--ssh-key ~/.ssh/appuser.pub
- Установлен Ruby install_ruby.sh:
sudo apt update
sudo apt install -y ruby-full ruby-bundler build-essential
- Проверен Ruby и Bundler:
$ ruby -v
ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]
$ bundler -v
Bundler version 1.11.2
- Установлен и запущен MongoDB install_mongodb.sh:
wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo systemctl start mongod
sudo systemctl enable mongod
- Выполнен деплой приложения deploy.sh:
sudo apt-get install -y git
git clone -b monolith https://github.com/express42/reddit.git
cd reddit && bundle install
- Дополнительное задание:
Для создания инстанса с уже развернутым приложением достаточно запустить:
yc compute instance create \
--name reddit-app \
--hostname reddit-app \
--memory=4 \
--create-boot-disk image-folder-id=standard-images,image-family=ubuntu-1604-lts,size=10GB \
--network-interface subnet-name=default-ru-central1-a,nat-ip-version=ipv4 \
--metadata-from-file user-data=metadata.yaml \
--metadata serial-port-enable=1
Содержимое metadata.yaml:
#cloud-config
users:
- default
- name: yc-user
groups: sudo
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh-authorized-keys:
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUSGRF2QvKndsn1hbFv93CgS3/AiwCoETwjHL6Wzkyape+sW5EXKT/MXjCTlBVfqPtKWvY2pqXpEY7oJAOmJJrBvwnuod2SzEEoFncK1YOLXJOhzeXkT1+1cgo27jJYb4TQTWjawCYv48kJnPNwSL/jNLGQSdosfH3POQVWkB3xCjoLZ7/kMqZQbFEvol5BI5T0HM7uKtPJdWUPD0X1Jpu5MgFV6ZmSWWVrGY25nTehs0nTy4AkAv5mp8VJQtzpKu+fennhQdeb+8aGEaZkFNUOGFAf9ph0G4Lq/gks491Un7cL1/HvcRgPvDdqS+ZRKaPopqK/f978VkpzovlZNJWERZyTrzbgkme6x88zv+rWUu3DiWhldGNuBdghA2kOGhSpSX80gLlj8yE3IP8pdveOq10OztLVpy+8j7tSegOdU9QnBNZ/wqgSVa9kWCU/fui4ASDAA4IAWtthUkaqmDdSPM8mPv8KYueR75LOPKMCCclAOz8S8LK1kFRwcJVEs8= appuser"
runcmd:
- wget https://raw.githubusercontent.com/Otus-DevOps-2021-05/Deron-D_infra/cloud-testapp/bootstrap.sh
- bash bootstrap.sh
Содержимое bootstrap.sh:
#!/bin/bash
wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates ruby-full ruby-bundler build-essential mongodb-org git
sudo systemctl --now enable mongod
git clone -b monolith https://github.com/express42/reddit.git
cd reddit && bundle install
puma -d
packer-base
Сборка образов VM при помощи Packer
Подготовка базового образа VM при помощи Packer.
Цель: В данном дз студент произведет сборку готового образа с уже установленным приложением при помощи Packer. Задеплоит приложение в Compute Engine при помощи ранее подготовленного образа. В данном задании тренируются навыки: работы с Packer, работы с GCP Compute Engine.
Все действия описаны в методическом указании.
Критерии оценки: 0 б. - задание не выполнено 1 б. - задание выполнено 2 б. - выполнены все дополнительные задания
- Установлен Packer:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install packer
2.1 Создан сервисный аккаунт:
SVC_ACCT="svcuser"
FOLDER_ID="b1gu87e4thvariradsue"
yc iam service-account create --name $SVC_ACCT --folder-id $FOLDER_ID
2.2 Делегированы правы сервисному аккаунту для Packer:
$ ACCT_ID=$(yc iam service-account get $SVC_ACCT | \
grep ^id | \
awk '{print $2}')
$ yc resource-manager folder add-access-binding --id $FOLDER_ID \
--role editor \
--service-account-id $ACCT_ID
2.3 Создан service account key file
Deron-D_infra git:(packer-base) ✗ yc iam key create --service-account-id $ACCT_ID --output ~/.yc_keys/key.json
id: aje6jvgee8cm640mh2b0
service_account_id: ajeeg8qoctaevkcq8jmv
created_at: "2021-06-28T13:08:50.312786870Z"
key_algorithm: RSA_2048
Deron-D_infra git:(packer-base) ✗ ll ~/.yc_keys
total 4.0K
-rw-------. 1 dpp dpp 2.4K Jun 28 16:08 key.json
-
Создан файла-шаблона Packer ubuntu16.json
-
Созданы скрипты для provisioners install_ruby.sh;install_mongodb.sh
-
Выполнена проверка на ошибки
packer validate ./ubuntu16.json
- Произведен запуск сборки образа
packer build ./ubuntu16.json
-
Создана ВМ с использованием созданного образа
-
Выполнено "дожаривание" ВМ для запуска приложения:
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
-
Выполнено параметризирование шаблона с применением variables.json.example
-
Построение bake-образа
*
- Создан immutable.json
- Создан systemd unit puma.service
- Запущена сборка
packer build -var-file=./variables.json immutable.json
- Проверка созданных образов:
packer git:(packer-base) yc compute image list
+----------------------+------------------------+-------------+----------------------+--------+
| ID | NAME | FAMILY | PRODUCT IDS | STATUS |
+----------------------+------------------------+-------------+----------------------+--------+
| fd821hvkilmtrb7tbi2n | reddit-base-1624888205 | reddit-base | f2el9g14ih63bjul3ed3 | READY |
| fd8t49b4simvfj6crpta | reddit-full-1624909929 | reddit-full | f2el9g14ih63bjul3ed3 | READY |
+----------------------+------------------------+-------------+----------------------+--------+
- Автоматизация создания ВМ
*
- Создан create-reddit-vm.sh
terraform-1
Знакомство с terraform
Декларативное описание в виде кода инфраструктуры YC, требуемой для запуска тестового приложения, при помощи Terraform.
Цель: В данном дз студент опишет всю инфраструктуру в Yandex Cloud при помощи Terraform. В данном задании тренируются навыки: создания и описания инфраструктуры при помощи Terraform. Принципы и подходы IaC.
Все действия описаны в методическом указании.
Критерии оценки: 0 б. - задание не выполнено 1 б. - задание выполнено 2 б. - выполнены все дополнительные задания
- Установлен terraform 0.12.8 с помощью terraform-switcher
curl -L https://raw.githubusercontent.com/warrensbox/terraform-switcher/release/install.sh | sudo bash
➜ Deron-D_infra git:(terraform-1) ✗ tfswitch
Use the arrow keys to navigate: ↓ ↑ → ←
? Select Terraform version:
▸ 0.12.8 *recent
terraform git:(terraform-1) ✗ terraform -v
Terraform v0.12.8
- В корне репозитория создали файл .gitignore с содержимым:
*.tfstate
*.tfstate.*.backup
*.tfstate.backup
*.tfvars
.terraform/
- Узнаем свои параметры токена, идентификатора облака и каталога:
yc config list
➜ Deron-D_infra git:(terraform-1) ✗ yc config list
token: <OAuth или статический ключ сервисного аккаунта>
cloud-id: <идентификатор облака>
folder-id: <идентификатор каталога>
compute-default-zone: ru-central1-a
- Создадим сервисный аккаунт для работы terraform:
FOLDER_ID=$(yc config list | grep folder-id | awk '{print $2}')
SRV_ACC=trfuser
yc iam service-account create --name $SRV_ACC --folder-id $FOLDER_ID
SRV_ACC_ID=$(yc iam service-account get $SRV_ACC | grep ^id | awk '{print $2}')
yc resource-manager folder add-access-binding --id $FOLDER_ID --role editor --service-account-id $SRV_ACC_ID
yc iam key create --service-account-id $SRV_ACC_ID --output ~/.yc_keys/key.json
- Cмотрим информацию о имени, семействе и id пользовательских образов своего каталога с помощью команды yc compute image list:
Deron-D_infra git:(terraform-1) yc compute image list
+----------------------+------------------------+-------------+----------------------+--------+
| ID | NAME | FAMILY | PRODUCT IDS | STATUS |
+----------------------+------------------------+-------------+----------------------+--------+
| fd821hvkilmtrb7tbi2n | reddit-base-1624888205 | reddit-base | f2el9g14ih63bjul3ed3 | READY |
| fd8t49b4simvfj6crpta | reddit-full-1624909929 | reddit-full | f2el9g14ih63bjul3ed3 | READY |
+----------------------+------------------------+-------------+----------------------+--------+
- Cмотрим информацию о имени и id сети своего каталога с помощью команды yc vpc network list:
Deron-D_infra git:(terraform-1) yc vpc network list
+----------------------+--------+
| ID | NAME |
+----------------------+--------+
| enpv6gbrqnhhbp41jurh | my-net |
+----------------------+--------+
- Правим main.tf до состояния:
terraform {
required_version = "0.12.8"
}
provider "yandex" {
version= "0.35"
service_account_key_file = pathexpand("~/.yc_keys/key.json")
folder_id = "b1gu87e4thvariradsue"
zone = "ru-central1-a"
}
resource "yandex_compute_instance" "app" {
name = "reddit-app"
resources {
cores = 2
memory = 2
}
boot_disk {
initialize_params {
# Указать id образа созданного в предыдущем домашнем задании
image_id = "fd821hvkilmtrb7tbi2n"
}
}
network_interface {
# Указан id подсети default-ru-central1-a
subnet_id = "e9b7qomc4stvbnr6ejde"
nat = true
}
}
- Для того чтобы загрузить провайдер и начать его использовать выполняем следующую команду в директории terraform:
terraform init
- Планируем изменения:
terraform plan
...
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
- Создаем VM согласно описанию:
➜ terraform git:(terraform-1) terraform apply -auto-approve
yandex_compute_instance.app: Creating...
yandex_compute_instance.app: Still creating... [10s elapsed]
yandex_compute_instance.app: Still creating... [20s elapsed]
yandex_compute_instance.app: Still creating... [30s elapsed]
yandex_compute_instance.app: Still creating... [40s elapsed]
yandex_compute_instance.app: Creation complete after 46s [id=fhm2sg90ppv3l27lhudf]
- Смотрим внешний IP адрес созданного инстанса,
terraform git:(terraform-1) ✗ terraform show | grep nat_ip_address
nat_ip_address = "84.201.158.45"
- Пробуем подключиться по SSH:
terraform git:(terraform-1) ✗ ssh [email protected]
[email protected]'s password:
- Нужно определить SSH публичный ключ пользователя ubuntu в метаданных нашего инстанса добавив в main.tf:
metadata = {
ssh-keys = "ubuntu:${file("~/.ssh/appuser.pub")}"
}
- Проверяем:
➜ terraform git:(terraform-1) ✗ ssh [email protected] -i ~/.ssh/appuser -o StrictHostKeyChecking=no
Warning: Permanently added '178.154.201.37' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 16.04.7 LTS (GNU/Linux 4.4.0-142-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
- Создадим файл outputs.tf для управления выходными переменными с содержимым:
output "external_ip_address_app" {
value = yandex_compute_instance.app.network_interface.0.nat_ip_address
}
- Проверяем работоспособность outputs.tf:
➜ terraform git:(terraform-1) ✗ terraform refresh
yandex_compute_instance.app: Refreshing state... [id=fhm5o0gooknpnp6v5nmk]
Outputs:
external_ip_address_app = 84.201.157.46
➜ terraform git:(terraform-1) ✗ terraform output
external_ip_address_app = 84.201.157.46
- Добавляем provisioners в main.tf:
provisioner "file" {
source = "files/puma.service"
destination = "/tmp/puma.service"
}
provisioner "remote-exec" {
script = "files/deploy.sh"
}
-
Создадим файл юнита для провижионинга puma.service
-
Добавляем секцию для определения паметров подключения привиженеров:
connection {
type = "ssh"
host = yandex_compute_instance.app.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
# путь до приватного ключа
private_key = file("~/.ssh/appuser")
}
- Проверяем работу провижинеров. Говорим terraform'y пересоздать ресурс VM при следующем применении изменений:
➜ terraform git:(terraform-1) ✗ terraform taint yandex_compute_instance.app
Resource instance yandex_compute_instance.app has been marked as tainted.
- Планируем и применяем изменения:
terraform plan
terraform apply --auto-approve
- Проверяем результат изменений и работу приложения:
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Outputs:
external_ip_address_app = 178.154.206.153
- Параметризируем конфигурационные файлы с помощью входных переменных:
- Создадим для этих целей еще один конфигурационный файл variables.tf
- Определим соответствующие параметры ресурсов main.tf через переменные:
provider "yandex" {
service_account_key_file = var.service_account_key_file
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = var.zone
}
boot_disk {
initialize_params {
# Указать id образа созданного в предыдущем домашем задании
image_id = var.image_id
}
}
network_interface {
# Указан id подсети default-ru-central1-a
subnet_id = var.subnet_id
nat = true
}
metadata = {
ssh-keys = "ubuntu:${file(var.public_key_path)}"
}
-
Определим переменные используя специальный файл terraform.tfvars
-
Форматирование и финальная проверка:
terraform fmt
terraform destroy
terraform plan
terraform apply --auto-approve
Проверка сервиса по адресу: http://178.154.206.153:9292/
- Создадим файл lb.tf со следующим содержимым:
resource "yandex_lb_target_group" "reddit_lb_target_group" {
name = "reddit-app-lb-group"
region_id = var.region_id
target {
subnet_id = var.subnet_id
address = yandex_compute_instance.app.network_interface.0.ip_address
}
}
resource "yandex_lb_network_load_balancer" "load_balancer" {
name = "reddit-app-lb"
listener {
name = "reddit-app-listener"
port = 80
target_port = 9292
external_address_spec {
ip_version = "ipv4"
}
}
attached_target_group {
target_group_id = "${yandex_lb_target_group.reddit_lb_target_group.id}"
healthcheck {
name = "http"
http_options {
port = 9292
path = "/"
}
}
}
}
- Добавляем в outputs.tf переменные адреса балансировщика и проверяем работоспособность решения:
output "loadbalancer_ip_address" {
value = yandex_lb_network_load_balancer.load_balancer.listener.*.external_address_spec[0].*.address
}
- Добавляем в код еще один terraform ресурс для нового инстанса приложения (reddit-app2):
- main.tf
resource "yandex_compute_instance" "app2" {
name = "reddit-app2"
resources {
cores = 2
memory = 2
}
...
connection {
type = "ssh"
host = yandex_compute_instance.app2.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
# путь до приватного ключа
private_key = file("~/.ssh/appuser")
}
- lb.tf
target {
address = yandex_compute_instance.app2.network_interface.0.ip_address
subnet_id = var.subnet_id
}
- outputs.tf
output "external_ip_address_app2" {
value = yandex_compute_instance.app2.network_interface.0.nat_ip_address
}
- Избыточный код
- На инстансах нет единого бэкэнда в части БД (mongodb)
- Подход с заданием количества инстансов через параметр ресурса count:
- Добавим в variables.tf
variable count_of_instances {
description = "Count of instances"
default = 1
}
- В main.tf удалим код для reddit-app2 и добавим:
resource "yandex_compute_instance" "app" {
name = "reddit-app-${count.index}"
count = var.count_of_instances
resources {
cores = 2
memory = 2
}
...
connection {
type = "ssh"
host = self.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
# путь до приватного ключа
private_key = file("~/.ssh/appuser")
}
- В lb.tf внесем изменения для динамического определения target:
dynamic "target" {
for_each = yandex_compute_instance.app.*.network_interface.0.ip_address
content {
subnet_id = var.subnet_id
address = target.value
}
}
- Создать внутренний сетевой балансировщик
- yandex_lb_network_load_balancer
- yandex_lb_target_group
- dynamic Blocks
- HashiCorp Terraform 0.12 Preview: For and For-Each
➜ terraform git:(terraform-1) ✗ yc load-balancer network-load-balancer list
+----------------------+---------------+-------------+----------+----------------+------------------------+--------+
| ID | NAME | REGION ID | TYPE | LISTENER COUNT | ATTACHED TARGET GROUPS | STATUS |
+----------------------+---------------+-------------+----------+----------------+------------------------+--------+
| b7rul6kjivb12lgkrfud | reddit-app-lb | ru-central1 | EXTERNAL | 1 | enp46hq1qjve9id5v9oo | ACTIVE |
+----------------------+---------------+-------------+----------+----------------+------------------------+--------+
➜ terraform git:(terraform-1) ✗ yc load-balancer target-group list
+----------------------+---------------------+---------------------+-------------+--------------+
| ID | NAME | CREATED | REGION ID | TARGET COUNT |
+----------------------+---------------------+---------------------+-------------+--------------+
| enp46hq1qjve9id5v9oo | reddit-app-lb-group | 2021-07-24 12:42:52 | ru-central1 | 2 |
+----------------------+---------------------+---------------------+-------------+--------------+
Лекция №9: Принципы организации инфраструктурного кода и работа над инфраструктурой в команде на примере Terraform
terraform-2
Создание Terraform модулей для управления компонентами инфраструктуры.
Создание Terraform модулей для управления компонентами инфраструктуры.
Цель: В данном дз студент продолжит работать с Terraform. Опишет и произведет настройку нескольких окружений при помощи Terraform. Настроит remote backend. В данном задании тренируются навыки: работы с Terraform, использования внешних хранилищ состояния инфраструктуры.
Описание и настройка инфраструктуры нескольких окружений. Работа с Terraform remote backend.
Критерии оценки: 0 б. - задание не выполнено 1 б. - задание выполнено 2 б. выполнены все дополнительные задания
- Создаем новую ветку в инфраструктурном репозитории и подчищаем результаты заданий со ⭐:
git checkout -b terraform-2
git mv terraform/lb.tf terraform/files/
- Зададим IP для инстанса с приложением в виде внешнего ресурса, добавив в
main.tf
:
resource "yandex_vpc_network" "app-network" {
name = "reddit-app-network"
}
resource "yandex_vpc_subnet" "app-subnet" {
name = "reddit-app-subnet"
zone = "ru-central1-a"
network_id = "${yandex_vpc_network.app-network.id}"
v4_cidr_blocks = ["192.168.10.0/24"]
}
- также добавим в 'main.tf' ссылку на внешний ресурс:
network_interface {
subnet_id = yandex_vpc_subnet.app-subnet.id
nat = true
}
- Применим изменения
terraform destroy
terraform apply
yandex_vpc_network.app-network: Creating...
yandex_vpc_network.app-network: Creation complete after 5s
yandex_vpc_subnet.app-subnet: Creating...
yandex_vpc_subnet.app-subnet: Creation complete after 5s
yandex_compute_instance.app: Creating...
Видим, что ресурс VM начал создаваться только после завершения создания yandex_vpc_subnet в результате неявной зависимости этих ресурсов.
- Создание раздельных образов для инстансов app и db с помощью Packer:
В директории packer, где содержатся ваши шаблоны для билда VM, создадим два новых шаблона db.json и app.json.
В качестве базового шаблона используем уже имеющийся шаблон ubuntu16.json, корректирую только соответствующие секции с наименованиями образов и секциями провизионеров.
- Создадим две VM
Разобьем конфиг main.tf
на несколько конфигов
Создадим файл app.tf
, куда вынесем конфигурацию для VM с приложением:
resource "yandex_compute_instance" "app" {
name = "reddit-app"
labels = {
tags = "reddit-app"
}
resources {
cores = 1
memory = 2
}
boot_disk {
initialize_params {
image_id = var.app_disk_image
}
}
network_interface {
subnet_id = yandex_vpc_subnet.app-subnet.id
nat = true
}
metadata = {
ssh-keys = "ubuntu:${file(var.public_key_path)}"
}
}
И создадим файл db.tf
, куда вынесем конфигурацию для VM с приложением:
resource "yandex_compute_instance" "db" {
name = "reddit-db"
labels = {
tags = "reddit-db"
}
resources {
cores = 1
memory = 2
}
boot_disk {
initialize_params {
image_id = var.db_disk_image
}
}
network_interface {
subnet_id = yandex_vpc_subnet.app-subnet.id
nat = true
}
metadata = {
ssh-keys = "ubuntu:${file(var.public_key_path)}"
}
}
Не забудем объявить соответствующие переменные для образов приложения и базы данных в variables.tf
:
variable app_disk_image {
description = "Disk image for reddit app"
default = "reddit-app-base"
variable db_disk_image {
description = "Disk image for reddit db"
default = "reddit-db-base"
}
}
Создадим файл vpc.tf
, в который вынесем конфигурацию сети и подсети, которое применимо для всех инстансов нашей сети.
resource "yandex_vpc_network" "app-network" {
name = "app-network"
}
resource "yandex_vpc_subnet" "app-subnet" {
name = "app-subnet"
zone = "ru-central1-a"
network_id = "${yandex_vpc_network.app-network.id}"
v4_cidr_blocks = ["192.168.10.0/24"]
}
В итоге, в файле main.tf
должно остаться только определение провайдера:
provider "yandex" {
version = 0.35
service_account_key_file = var.service_account_key_file
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = var.zone
}
Не забудем добавить nat адреса инстансов в outputs.tf
переменные:
output "external_ip_address_app" {
value = yandex_compute_instance.app.network_interface.0.nat_ip_address
}
output "external_ip_address_db" {
value = yandex_compute_instance.db.network_interface.0.nat_ip_address
}
Планируем и применяем изменения одной командой:
terraform apply --auto-approve
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
external_ip_address_app = 178.154.220.171
external_ip_address_db = 178.154.200.211
Проверим доступность по SSH:
➜ terraform git:(terraform-2) ✗ ssh [email protected] -i ~/.ssh/appuser
➜ terraform git:(terraform-2) ✗ ssh [email protected] -i ~/.ssh/appuser
Удалим созданные ресурсы, используя terraform destroy
terraform destroy --auto-approve
- Создание модулей
Внутри директории terraform создадим директорию modules, в которой мы будем определять модули.
Внутри директории modules создадим директорию db со следующими файлами:
main.tf
resource "yandex_compute_instance" "db" {
name = "reddit-db"
labels = {
tags = "reddit-db"
}
resources {
cores = 2
memory = 2
}
boot_disk {
initialize_params {
# Указать id образа созданного в предыдущем домашем задании
image_id = var.db_disk_image
}
}
network_interface {
subnet_id = var.subnet_id
nat = true
}
metadata = {
ssh-keys = "ubuntu:${file(var.public_key_path)}"
}
connection {
type = "ssh"
host = self.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
# путь до приватного ключа
private_key = file(var.private_key_path)
}
scheduling_policy {
preemptible = true
}
variables.tf
variable public_key_path {
description = "Path to the public key used for ssh access"
}
variable db_disk_image {
description = "Disk image for reddit db"
default = "reddit-db-base"
}
variable subnet_id {
description = "Subnets for modules"
}
variable private_key_path {
description = "path to private key"
}
outputs.tf
output "external_ip_address_db" {
value = yandex_compute_instance.db.network_interface.0.nat_ip_address
}
Создадим по аналогии для модуля приложения директорию modules\app
с содержимым
main.tf
resource "yandex_compute_instance" "app" {
name = "reddit-app"
labels = {
tags = "reddit-app"
}
resources {
cores = 2
memory = 2
}
boot_disk {
initialize_params {
image_id = var.app_disk_image
}
}
network_interface {
subnet_id = var.subnet_id
nat = true
}
metadata = {
ssh-keys = "ubuntu:${file(var.public_key_path)}"
}
connection {
type = "ssh"
host = self.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
# путь до приватного ключа
private_key = file(var.private_key_path)
}
scheduling_policy {
preemptible = true
}
provisioner "file" {
source = "files/puma.service"
destination = "/tmp/puma.service"
}
provisioner "remote-exec" {
script = "files/deploy.sh"
}
}
variables.tf
variable public_key_path {
description = "Path to the public key used for ssh access"
}
variable app_disk_image {
description = "Disk image for reddit app"
default = "reddit-app-base"
}
variable subnet_id {
description = "Subnets for modules"
}
variable private_key_path {
description = "path to private key"
}
outputs.tf
output "external_ip_address_app" {
value = yandex_compute_instance.app.network_interface.0.nat_ip_address
}
В файл main.tf
, где у нас определен провайдер вставим секции вызова созданных нами модулей
provider "yandex" {
version = "0.35"
service_account_key_file = var.service_account_key_file
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = var.zone
}
data "yandex_compute_image" "app_image" {
name = var.app_disk_image
}
data "yandex_compute_image" "db_image" {
name = var.db_disk_image
}
module "app" {
source = "./modules/app"
public_key_path = var.public_key_path
private_key_path = var.private_key_path
app_disk_image = "${data.yandex_compute_image.app_image.id}"
subnet_id = var.subnet_id
}
module "db" {
source = "./modules/db"
public_key_path = var.public_key_path
private_key_path = var.private_key_path
db_disk_image = "${data.yandex_compute_image.db_image.id}"
subnet_id = var.subnet_id
}
Для использования модулей нужно сначала их загрузить из указанного источника source
:
terraform get
Из папки terraform удаляем уже ненужные файлы app.tf, db.tf, vpc.tf и изменяем outputs.tf
:
output "external_ip_address_app" {
value = module.app.external_ip_address_app
}
output "external_ip_address_db" {
value = module.db.external_ip_address_db
}
Планируем изменения:
terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
Plan: 2 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
После применения конфигурации с помощью terraform apply в соответствии с нашей конфигурацией проверяем SSH доступ ко обоим инстансам Проверим доступность по SSH:
➜ terraform git:(terraform-2) ✗ ssh [email protected] -i ~/.ssh/appuser
➜ terraform git:(terraform-2) ✗ ssh [email protected] -i ~/.ssh/appuser
- Переиспользование модулей В директории terrafrom создадим две директории: stage и prod. Скопируем файлы main.tf, variables.tf, outputs.tf, terraform.tfvars, key.json из директории terraform в каждую из созданных директорий.
Поменяем пути к модулям в main.tf на ../modules/xxx вместо ./modules/xxx в папках stage и prod.
Проверим правильность настроек инфраструктуры каждого окружения:
cd stage
terraform init
terraform plan
terraform apply --auto-approve
terraform destroy --auto-approve
cd ../prod
terraform init
terraform plan
terraform apply --auto-approve
terraform destroy --auto-approve
Отформатируем конфигурационные файлы, используя команду terraform fmt
- Настройка хранения стейт файла в удаленном бекенде. Задание со ⭐
➜ Deron-D_infra git:(terraform-2) ✗ yc iam service-account list
+----------------------+---------+
| ID | NAME |
+----------------------+---------+
| aje0qs1bcqu8rckr53aq | svcuser |
| aje6upad8qvh1nri7dld | appuser |
| ajee7g8ig96qqk45gufm | trfuser |
+----------------------+---------+
➜ Deron-D_infra git:(terraform-2) ✗ yc iam access-key create --service-account-name trfuser
access_key:
id: ajehu3smo5o7v4pkc3rm
service_account_id: ajee7g8ig96qqk45gufm
created_at: "2021-08-11T19:29:47.457399290Z"
key_id: access-key
secret: secret-key
Соответственно заносим полученные данные в variables.tf
и terraform.tvars
Планируем и вносим изменения в инфраструктуру:
➜ terraform git:(terraform-2) ✗ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# yandex_storage_bucket.otus-storage-bucket will be created
+ resource "yandex_storage_bucket" "otus-storage-bucket" {
+ access_key = "access-key"
+ acl = "private"
+ bucket = "otus-bucket"
+ bucket_domain_name = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ secret_key = (sensitive value)
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
➜ terraform git:(terraform-2) ✗ terraform apply -auto-approve
yandex_storage_bucket.otus-storage-bucket: Creating...
yandex_storage_bucket.otus-storage-bucket: Creation complete after 0s [id=otus-bucket]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
В окружениях stage и prod создаем backend.tf:
terraform {
backend "s3" {
endpoint = "storage.yandexcloud.net"
bucket = "otus-bucket"
region = "ru-central1-a"
key = "terraform.tfstate"
# access_key = var.access_key
# secret_key = var.secret_key
access_key = "access-key"
secret_key = "secret-key"
skip_region_validation = true
skip_credentials_validation = true
}
}
Проверяем сохранение state VM's для каждого из окружений в bucket 'otus-bucket'
➜ prod git:(terraform-2) ✗ cd ../stage
➜ stage git:(terraform-2) ✗ pwd
/home/dpp/otus-devops/Deron-D_infra/terraform/stage
➜ stage git:(terraform-2) ✗ terraform apply -auto-approve
data.yandex_compute_image.app_image: Refreshing state...
data.yandex_compute_image.db_image: Refreshing state...
module.app.yandex_compute_instance.app: Creating...
module.db.yandex_compute_instance.db: Creating...
module.db.yandex_compute_instance.db: Still creating... [10s elapsed]
module.app.yandex_compute_instance.app: Still creating... [10s elapsed]
module.app.yandex_compute_instance.app: Still creating... [20s elapsed]
module.db.yandex_compute_instance.db: Still creating... [20s elapsed]
module.db.yandex_compute_instance.db: Still creating... [30s elapsed]
module.app.yandex_compute_instance.app: Still creating... [30s elapsed]
module.app.yandex_compute_instance.app: Creation complete after 38s [id=fhmlrq77m98nick4v2g9]
module.db.yandex_compute_instance.db: Still creating... [40s elapsed]
module.db.yandex_compute_instance.db: Creation complete after 40s [id=fhmbi51mj3cg0lnpsa1v]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
external_ip_address_app = 130.193.50.99
external_ip_address_db = 84.201.132.24
➜ stage git:(terraform-2) ✗ cd ../prod
➜ prod git:(terraform-2) ✗ terraform apply -auto-approve
data.yandex_compute_image.db_image: Refreshing state...
data.yandex_compute_image.app_image: Refreshing state...
module.app.yandex_compute_instance.app: Refreshing state... [id=fhmlrq77m98nick4v2g9]
module.db.yandex_compute_instance.db: Refreshing state... [id=fhmbi51mj3cg0lnpsa1v]
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
external_ip_address_app = 130.193.50.99
external_ip_address_db = 84.201.132.24
- Настройка provisioner. Задание со ⭐⭐
Добавим переменную на включения/отключения provisioner в variables.tf
окружений stage|prod:
variable enable_provision {
description = "Enable provision"
default = true
}
Добавляем в 'main.tf' для модулей app и db соответственно следующий код перед секцией connection:
resource "null_resource" "app" {
count = var.enable_provision ? 1 : 0
triggers = {
cluster_instance_ids = yandex_compute_instance.app.id
}
...
resource "null_resource" "db" {
count = var.enable_provision ? 1 : 0
triggers = {
cluster_instance_ids = yandex_compute_instance.db.id
}
Добавляем передачу значений переменной enable_provision в секции вызова модуля 'main.tf'
module "db|app" {
...
enable_provision = var.enable_provision
...