Terraform за 15 дней (AWS/Yandex cloud). День 3: data source и outputs

от автора

Оглавление

  1. День 1: Введение

  2. День 2: Поднимаем сервера

  3. День 3: Data source и outputs

Ссылки

Некоторые ссылки требует VPN

Справочник команд CLI aws

Справочник команд CLI yc

Terraform provider aws

Terraform provider Yandex

Сегодня мы познакомимся с понятиями 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *