
Оглавление
Ссылки
Некоторые ссылки требует VPN
Сегодня мы познакомимся с понятиями data source и output; посмотрим, как применяются изменения в уже существующей инфраструктуре.
Data source
В прошлый раз для поднятия сервера нам приходилось часть параметром искать с помощью cli (или консоли), а конкретно subnet_id и id образа ОС. Что бы не делать это каждый раз и что бы иметь актуальную информацию (например последнею версию ОС) существует data source. Эта сущность дает возможность получить информацию от провайдера, которая ни как не связана с текущей инфраструктурой (той что описана в нашем файле)
Синтаксис у всех провайдеров одинаковый, такой же как и для ресурса:
data "<НАЗВАНИЕ>" "<ИМЯЮ>" { <ПАРАМЕТР> = <ЗНАЧЕНИЕ> <ДРУГОЙ_ПАРАМЕТР> = { <ПАРАМЕТР> = <ЗНАЧЕНИЕ> } }
Представьте что ресурс и дата это одно и тоже, но ресурс инициируем мы сами, а дата это то, что уже существует.
Yandex
Что бы получить id для Ubuntu 22.04, необходимо указать параметр family — ubuntu-2204-lts; название подсети для получения ее id — default-ru-central1-a
yandex/main.tf
... data "yandex_compute_image" "last_ubuntu" { family = "ubuntu-2204-lts" # ОС (Ubuntu, 22.04 LTS) } data "yandex_vpc_subnet" "default_a" { name = "default-ru-central1-a" # одна из дефолтных подсетей } ...
aws
С aws чуть посложнее: для получения id последней версии ubuntu необходимо указать id владельца образа и его название (это мы получали через cli в прошлой части):
-
owner_id — 099720109477
-
name — ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server*
Звездочка заменяет номер версии, который мы не указываем, потому что нам нужна всегда последняя версия: за это отвечает параметр most_recent = true
Для получения id подсети, укажем желаемую зону (пусть будет как в Яндексе — “а”)
Звездочка заменяет номер версии, который мы не указываем, потому что нам нужна всегда последняя версия: за это отвечает параметр most_recent = true
Для получения id подсети, укажем желаемую зону (пусть будет как в Яндексе — “а”)
aws/main.tf
... data "aws_ami" "last_ubuntu" { most_recent = true # только новейшая версия owners = ["099720109477"] # id владельца образа filter { name = "name" # название фильтра - по какому параметру искать values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server*"] # что искать } } data "aws_subnet" "default_a" { filter { name = "availability-zone" values = ["eu-north-1a"] } } ...
Outputs и подстановка значений
Теперь необходимо использовать эти полученные данные в место хардкода. Но, помимо подстановки их в параметры ресурсов, мы выведем их в терминал с помощью output.
Выводы (назовем из так) используются не только для вывода информации на экран, но пока остановимся на этом. Давайте добавим в вывод id образа ОС и id подсети, так же добавим ip адрес поднятого сервера.
Что бы получить нужные значения из объектов, нужно просто обратится к ним по имени через точку.
Важно: terraform читает все файлы с расширением .tf в директории как одно целое — можно все описывать в одном файле или в нескольких, как вам угодно (по этой же причине не важен порядок указания сущностей в самом файле).
touch outputs.tf
Yandex
yandex/outputs.tf
output "default_instance_public_ip" { value = yandex_compute_instance.default.network_interface[0].nat_ip_address } output "subnet_id" { value = data.yandex_vpc_subnet.default_a.subnet_id } output "last_ubuntu" { value = data.yandex_compute_image.last_ubuntu.id }
yandex/main.tf
terraform { required_providers { yandex = { source = "yandex-cloud/yandex" } } } provider "yandex" { token = "..." # OAuth-токен яндекса cloud_id = "b1gos1rh49bip4rnmrmg" folder_id = "b1gjju43i1pr11i5c4ic" zone = "ru-central1-a" } data "yandex_compute_image" "last_ubuntu" { family = "ubuntu-2204-lts" # ОС (Ubuntu, 22.04 LTS) } data "yandex_vpc_subnet" "default_a" { name = "default-ru-central1-a" # одна из дефолтных подсетей } # ресурс "yandex_compute_instance" т.е. сервер # Terraform будет знаеть его по имени "yandex_compute_instance.default" resource "yandex_compute_instance" "default" { name = "test-instance" platform_id = "standard-v1" # тип процессора (Intel Broadwell) resources { core_fraction = 5 # Гарантированная доля vCPU cores = 2 # vCPU memory = 1 # RAM } boot_disk { initialize_params { image_id = data.yandex_compute_image.last_ubuntu.id } } network_interface { subnet_id = data.yandex_vpc_subnet.default_a.subnet_id nat = true # автоматически установить динамический ip } }
aws
aws/outputs.tf
output "default_instance_public_ip" { value = aws_instance.default.public_ip } output "subnet_id" { value = data.aws_subnet.default_a.id } output "last_ubuntu" { value = data.aws_ami.last_ubuntu.id }
aws/main.tf
provider "aws" { access_key = "AK..." secret_key = "2X..." region = "eu-north-1" } data "aws_ami" "last_ubuntu" { most_recent = true owners = ["099720109477"] filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server*"] } } data "aws_subnet" "default_a" { filter { name = "availability-zone" values = ["eu-north-1a"] } } #ресурс "aws_instance" т.е. сервер #terraform будет знаеть его по имени "aws_instance.default" resource "aws_instance" "default" { ami = data.aws_ami.last_ubuntu.id # ОС (Ubuntu, 22.04 LTS) instance_type = "t3.micro" # тип процессора и ресурс машины (CPU и RAM) subnet_id = data.aws_subnet.default_a.id # одна из дефолтных подсетей associate_public_ip_address = true # автоматически установить динамический ip tags = { Name = "test-instance" } }
Применение и вывод данных в терминал
После terraform apply мы увидем примерно следующее:
Outputs: # yandex default_instance_public_ip = "51.250.95.35" last_ubuntu = "fd8v0s6adqu3ui3rsuap" subnet_id = "e9bdgo95ucmut6r7pioq" # aws default_instance_public_ip = "16.16.64.183" last_ubuntu = "ami-0a2b8744b4fe77f92" subnet_id = "subnet-82b67deb"
Если мы заглянем в вывод команды plan (тоже самое выводит apply перед применением), то увидим следующее:
Changes to Outputs: # yandex + default_instance_public_ip = (known after apply) + last_ubuntu = "fd8v0s6adqu3ui3rsuap" + subnet_id = "e9bdgo95ucmut6r7pioq" # aws + default_instance_public_ip = (known after apply) + last_ubuntu = "ami-0a2b8744b4fe77f92" + subnet_id = "subnet-82b67deb"
Возвращаясь к началу данной статьи: data source это то, что уже существует, поэтому, еще до применения, нам известны эти данные: last_ubuntu и subnet_id; в отличии от default_instance_public_ip, который мы получаем от поднятого сервера, а значит узнаем мы эти данные только после apply.
Изменения в инфраструктуре
До этого мы создавали сущности с нуля. Теперь попробуем изменить уже существующие. Для примера, добавим к нашим серверам параметр с ssh ключом, и посмотрим, что предлагает terraform.
Что бы не вставлять публичный ключ в файл (слишком длинный и можем измениться), лучше используем file() для подстановки все содержимого любого файла в строку.
Yandex
В Яндекс для добавления ssh ключа нужна metadata. Также надо указать пользователя, к которому ключ даст доступ: пользователь ubuntu создается по умолчанию.
yandex/main.tf
... resource "yandex_compute_instance" "default" { name = "test-instance" ... metadata = { ssh-keys = "ubuntu:${file("~/.ssh/id_rsa.pub")}" } }
aws
В aws все немного сложнее — тут ключ это отдельная сущность, а значит надо добавить еще один ресурс: aws_key_pair и его указать в качестве значения параметра для aws_instance.
aws/main.tf
... resource "aws_instance" "default" { ... key_name = aws_key_pair.test_key.key_name } resource "aws_key_pair" "test_key" { key_name = "test-key" public_key = "${file("~/.ssh/id_rsa.pub")}" }
Применение изменений
terraform plan покажет нам план действий terrafrom:
Yandex
Поведения яндекса немного не очевидное. Дело в том, что создав виртуальную машину без ssh ключа (то, что было у нас изначально), и добавив его потом, terraform не предлагает пересоздать машину. ~ update in-place означает, что ресурс будет изменен без пересоздания.
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: ~ update in-place Terraform will perform the following actions: # yandex_compute_instance.default will be updated in-place ~ resource "yandex_compute_instance" "default" { id = "fhm3kp62eahete5kofke" ~ metadata = { + "ssh-keys" = <<-EOT ...
Однако, после добавления ключа, подключение по нему будет недоступно. Необходимо пересоздать машину, что бы ssh не требовал пароль, а только ключ. Для этого добавим параметр replace в команду: apply -replace=yandex_compute_instance.default. Мы явно указываем, что необходимо пересоздать. Terraform это покажет — мы получим новый адрес, что видно в Changes to Outputs.
... # yandex_compute_instance.default will be replaced, as requested -/+ resource "yandex_compute_instance" "default" { ... Changes to Outputs: ~ default_instance_public_ip = "51.250.95.35" -> (known after apply) ...
После подтверждения и пересоздания сервера, мы можем к нему подключиться по ssh.
yc compute instance list +----------------------+---------------+---------------+---------+--------------+-------------+ | ID | NAME | ZONE ID | STATUS | EXTERNAL IP | INTERNAL IP | +----------------------+---------------+---------------+---------+--------------+-------------+ | fhmpng88a49dihen141a | test-instance | ru-central1-a | RUNNING | 62.84.117.26 | 10.128.0.17 | +----------------------+---------------+---------------+---------+--------------+-------------+
aws
В aws все работает как и ожидается — terraform сам предлагает пересоздать сервер. Также он указывает: что именно заставляет его так поступать. В данном случае это key_name — # forces replacement значит, что именно этот параметр причина пересоздания.
Terraform will perform the following actions: # aws_instance.default must be replaced -/+ resource "aws_instance" "default" { ... + key_name = "test-key" # forces replacement ... # aws_key_pair.test_key will be created + resource "aws_key_pair" "test_key" { ... Changes to Outputs: ~ default_instance_public_ip = "16.16.64.183" -> (known after apply) ...
К сожалению, мы не можем просто подключится к новому серверу на aws — нам необходимо настроить security group (сделаем это в одной из следующих статей). Но сервер работает и новый ключ создан.
aws ec2 describe-instances \ --region eu-north-1 \ --query "Reservations[*].Instances[*].{Instance:InstanceId,KeyName:KeyName,Address:PublicIpAddress,Name:Tags[?Key=='Name']|[0].Value}" \ --output table ---------------------------------------------------------------------------- | DescribeInstances | +--------------+-----------------------+-----------------+-----------------+ | Address | Instance | KeyName | Name | +--------------+-----------------------+-----------------+-----------------+ | 13.49.23.154| i-075afc37d4775ccb0 | test-key | test-instance | +--------------+-----------------------+-----------------+-----------------+ aws ec2 describe-key-pairs \ --region eu-north-1 \ --query="KeyPairs[*].{KeyName:KeyName,CreateTime:CreateTime}" \ --output table ------------------------------------------------- | DescribeKeyPairs | +----------------------------+------------------+ | CreateTime | KeyName | +----------------------------+------------------+ | 2022-08-28T11:07:42+00:00 | test-key | +----------------------------+------------------+
Уничтожим все c terraform destroy
Заключение
Data source незаменимый инструмент для построения сложной инфраструктуры. Outputs на данном этапе кажутся несколько бесполезными, но в дальнейшем мы познакомимся с другим применением данной возможности.
Любые вопросы пишите на почту v.valentinvolkov@gmail.com. Буду рад помочь!
ссылка на оригинал статьи https://habr.com/ru/post/685520/
Добавить комментарий