vitaliygut Infra repository
bastion_IP = 178.154.227.202 someinternalhost_IP = 10.130.0.14
Cпособ подключения к someinternalhost в одну команду
ssh -t -i ~/.ssh/key -A appuser@public_ip_bastion 'ssh ip_someinternalhost'
Подключение из консоли при помощи команды вида ssh someinternalhost:
Host * ForwardAgent yes
Host bastion
IdentityFile ~/.ssh/key
HostName ip_bastion
User appuser
Host someinternalhost
IdentityFile ~/.ssh/key
HostName ip_someinternalhost
User appuser
ProxyJump bastion
После запуска vpn сервера в логах были ошибки связанные с неудачной попыткой добыить правила в iptables, дополнително установил iptable и рестрарт службы pritunl
SSL В настройках сервера указал домен pritunl.178.154.227.202.sslip.io
testapp_IP = 130.193.37.11 testapp_port = 9292
Используйте созданные ранее скрипты для создания startup script, который будет запускаться при создании инстанса:
yc compute instance create
--name reddit-app2
--hostname reddit-app2
--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
Основное задание:
- Использование input vars, описываем переменные в variables.tf
...
variable cloud_id {
description = "Cloud"
}
variable folder_id {
description = "Folder"
}
variable zone {
description = "Zone"
default = "ru-central1-a"
...
- Определяем переменные в terraform.tfvars
cloud_id = "qaz123"
folder_id = "qaz456"
image_id = "qaz789"
- Указываем переменные в main.tf
folder_id = var.folder_id
Дополнительное задание:
- Создаем файл lb.tf и определяем целевую группу
resource "yandex_lb_target_group" "app" {
name = "target-group-reddit-app"
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" "lb" {
name = "reddit-load-balancer"
listener {
name = "http-listener"
port = 9292
external_address_spec {
ip_version = "ipv4"
}
}
attached_target_group {
target_group_id = "${yandex_lb_target_group.app.id"
healthcheck {
name = "http"
http_options {
port = 9292
path = "/"
...
- Добавление вывода в output для балнсировщика
output "external_lb_ip_address_app" {
value = yandex_lb_network_load_balancer.lb.listener.*.external_address_spec[0].*.address
}
- Добавление в балансировщик второй созданный сервер
target {
subnet_id = var.subnet_id
address = "${yandex_compute_instance.app.0.network_interface.0.ip_address}"
}
- Добавление вывода в output для второго сервера
output "external_ip_address_app" {
value = yandex_compute_instance.app.0.network_interface.0.nat_ip_address
}
output "internal_ip_address_app" {
value = yandex_compute_instance.app.0.network_interface.0.ip_address
- Удаляем инстанс и добавляем новый параметр ресурса count
variable count_app {
description = "count instances"
default = 1
- Создадим сеть с помощью yandex_vpc_network и yandex_vpc_subnet, добавляем в 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"]
}
- Делаем ссылку на на атрибут ресурса в наших VM
network_interface {
subnet_id = yandex_vpc_subnet.app-subnet.id
nat = true
}
- Создаем 2 новых шаблона для packer app.json и db.json и редактируем параметры
....
"image_name": "reddit-app-base-{{timestamp}}"
"image_family": "reddit-app-base"
....
-
Запускаем билд образов
-
Разбиваем конфиг main.tf на несколько конфигов, cоздаем файл app.tf db.tf и добавляем новые переменные
variable app_disk_image {
description = "disk image for reddit app"
default = "reddit-app-base"
}
variable db_disk_image {
description = "disk image for mongodb"
default = "reddit-db-base"
оставляем в 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
}
вносим изменения 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 plan; terraform apply --auto-approve
- Переход к работе с модульной структурой. Создаем папку modules внутри terraform и в каждом из модулей app и db создаем main.tf, outputs.ft и variables.tf
outputs.tf
output "external_ip_address_app" {
value = yandex_compute_instance.app.network_interface.0.nat_ip_address
}
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"
}
app/main.tf
resource "yandex_compute_instance" "app" {
name = "reddit-app"
labels = {
tags = "reddit-app"
}
resources {
core_fraction = 5
cores = 2
memory = 2
}
boot_disk {
initialize_params {
image_id = var.app_disk_image
}
}
....
-
удаляем из файлы app.tf, db.tf и vpc.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
}
module "app" {
source = "./modules/app"
public_key_path = var.public_key_path
app_disk_image = var.app_disk_image
subnet_id = var.subnet_id
}
module "db" {
source = "./modules/db"
public_key_path = var.public_key_path
db_disk_image = var.db_disk_image
subnet_id = var.subnet_id
}
изменяем 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 get
проверяем работу
terraform plan; terraform apply --auto-approve
- Переиспользование модулей. Создаем среды prod и stage
копируем в эти каталоги наши основные рабочией файлы
main.tf outputs.tf variables.tf terraform.tfvars
- Форматируем файлы
terraform fmt
- Проверяем работу
terraform init; terraform apply --auto-approve
Задание с ⭐
- Использование внешнего backend. Яндекс использует хранилища формата S3. Создаем бакет и получаем необходимые access_key и secret_key
в папке terraform создадим файл backet.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
}
resource "yandex_storage_bucket" "tf-bucket" {
bucket = var.bucket_name
}
создаем bucket
terraform apply --auto-approve
- Создаем в каждой из сред файл backend.tf для указания бекэнда
terraform {
backend "s3" {
endpoint = "storage.yandexcloud.net"
bucket = "infra-tf"
region = "ru-central1"
key = "stage/terraform.tfstate"
access_key = "123"
secret_key = "123"
skip_region_validation = true
skip_credentials_validation = true
}
}
- Проверка работы, переходим в директорию stage или prod
terraform init; terraform apply --auto-approve
Задание с ⭐⭐
- Добавим необходимые provisioner в модули для деплоя и работы приложения.
Создаем файл конфига puma.service для нашего приложения с указанием адрес для подключения к базе
[Unit]
Description=Puma HTTP Server
After=network.target
[Service]
Type=simple
User=ubuntu
Environment="DATABASE_URL=${ip_host_db}"
WorkingDirectory=/home/ubuntu/reddit
ExecStart=/bin/bash -lc 'puma'
Restart=always
[Install]
WantedBy=multi-user.target
- Теперь в main.tf добавив провижионеры.
connection {
type = "ssh"
host = self.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
private_key = file(var.private_key)
}
provisioner "file" {
content = templatefile("${path.module}/files/puma.service", { ip_host_db = var.ip_host_db })
destination = "/tmp/puma.service"
}
provisioner "remote-exec" {
script = "${path.module}/files/deploy.sh"
}
- Мы используем переменную var.ip_host_db ее также нужно определить в файле variables.tf
variable ip_host_db {
description = "Database IP address"
}
- Чтобы получить значение переменной мы добавляем в main.tf
module "app" {
source = "../modules/app"
public_key_path = var.public_key_path
app_disk_image = var.app_disk_image
subnet_id = var.subnet_id
ip_host_db = module.db.internal_ip_address_db
private_key = var.private_key
}
и получаем прописываем вывод значения в db/outputs.tf
output "internal_ip_address_db" {
value = yandex_compute_instance.db.network_interface.0.ip_address
}
- Создаем в db/files/ mongod.conf для прослушивания порта на нужном интерфейсе
...
# network interfaces
net:
bindIp: ${ip_host_db}
port: 27017
...
- Указав интерфейс для работы, теперь создадим скрипт который будет подкидывать наш конфиг deploy.sh на хост
#!/bin/bash
sudo mv -f /tmp/mongod.conf /etc/mongod.conf
sudo systemctl restart mongod
- Осталось добавить провижионеры в наш файл в db main.tf для модуля DB
connection {
type = "ssh"
host = self.network_interface.0.nat_ip_address
user = "ubuntu"
agent = false
private_key = file(var.private_key)
}
provisioner "file" {
content = templatefile("${path.module}/files/mongod.conf", { ip_host_db = yandex_compute_instance.db.network_interface.0.ip_address})
destination = "/tmp/mongod.conf"
}
provisioner "remote-exec" {
script = "${path.module}/files/deploy.sh"
}
- Проверяем работу
terraform destroy --auto-approve; terraform apply --auto-aprove
- Устанавлием Ansible brew install ansible
- Создаем каталог ansible и файл inventory, проверяем ...
ansible appserver -i ./inventory -m ping
...
appserver | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
- Создаем файл inventory.yml
---
app:
hosts:
appserver:
ansible_host:
84.201.131.179
db:
hosts:
dbserver:
ansible_host:
84.201.128.39
- Дабавляем clone.yml
---
- name: Clone
hosts: app
tasks:
- name: Clone repo
git:
repo: https://github.com/express42/reddit.git
dest: /home/ubuntu/reddit
- После запуска плейбука на сервере нет измененного состояния, так при создании окружения тераформом мы уже скачали этот репозиторий
appserver : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- Выполняем команду
ansible app -m command -a 'rm -rf ~/reddit'
- папка с приложением удалена, повторный запуск плейбука clone.yml приведет к загрузки приложения из репозитария.
Задание с ⭐
- Дабвил outputs.tf
### The Ansible inventory file
resource "local_file" "AnsibleInventory" {
content = templatefile("inventory.tmpl",
{
app-ext-ip = module.app.external_ip_address_app,
db-ext-ip = module.db.external_ip_address_db,
db-int-ip = module.db.internal_ip_address_db
}
)
filename = "../../ansible/inventory.ini"
}
и шаблон для формирования файла inventory.tmpl
[db]
dbserver ansible_host=${db-ext-ip}
[app]
appserver ansible_host=${app-ext-ip} db_host=${db-int-ip}
- Переменную db_host берем из inventory файла
vars:
db_host: "{{db_host}}"
Задание с ⭐
- Настройка использование динамического инвентори для окружений stage и prod, меняем пути в terraform/stage/outputs.tf
### The Ansible inventory file
resource "local_file" "AnsibleInventory" {
content = templatefile("inventory.tmpl",
{
app-ext-ip = module.app.external_ip_address_app,
db-ext-ip = module.db.external_ip_address_db,
db-int-ip = module.db.internal_ip_address_db
}
)
filename = "../../ansible/environments/stage/inventory.ini"
}
для terraform/prod/outputs.tf
### The Ansible inventory file
resource "local_file" "AnsibleInventory" {
content = templatefile("inventory.tmpl",
{
app-ext-ip = module.app.external_ip_address_app,
db-ext-ip = module.db.external_ip_address_db,
db-int-ip = module.db.internal_ip_address_db
}
)
filename = "../../ansible/environments/prod/inventory.ini"
}
Задание с ⭐⭐
- Настройка TravisCI, в файле .travis.yml добовляем новую секцию для установки нужных пакетов
install:
- sudo pip install ansible==2.9.17
- sudo pip install ansible-lint
- wget https://releases.hashicorp.com/terraform/0.12.5/terraform_0.12.5_linux_amd64.zip
- wget https://releases.hashicorp.com/packer/1.6.6/packer_1.6.6_linux_amd64.zip
- sudo unzip -o terraform_0.12.5_linux_amd64.zip -d /usr/local/bin/
- sudo unzip -o packer_1.6.6_linux_amd64.zip -d /usr/local/bin/
- Для контроля состояния в файле .travis.yml добовляем секцию script
script:
- packer validate -var-file=packer/variables.json.example packer/app.json
- packer validate -var-file=packer/variables.json.example packer/db.json
- packer validate -var-file=packer/variables.json.example packer/immutable.json
- packer validate -var-file=packer/variables.json.example packer/ubuntu16.json
- terraform init terraform/stage
- terraform validate terraform/stage
- terraform init terraform/prod
- terraform validate terraform/prod
- tflint terraform/stage
- tflint terraform/prod
- ansible-lint ansible/playbooks/*.yml