работать с Terraform в yandex облаке

Thank you for reading this post, don't forget to subscribe!

Регистрация на хостинге

Для рабо­ты с хостинг-про­вай­де­ром нам зара­нее пона­до­бят­ся авто­ри­за­ци­он­ные дан­ные и иден­ти­фи­ка­то­ры ресур­сов. Их мы будем про­пи­сы­вать в сце­на­ри­ях terraform.

Нам нуж­но заре­ги­стри­ро­вать­ся на сер­ви­се Yandex.Cloud (в нашем при­ме­ре, мы будем рабо­тать, имен­но, с ним). После это­го, нам нуж­но получить:

1. OAuth-токен. Исполь­зу­ет­ся в про­це­ду­ре аутен­ти­фи­ка­ции для полу­че­ния IAM-токе­на и нужен нам для про­хож­де­ния авто­ри­за­ции при под­клю­че­нии terraform. Выда­ет­ся на 1 год. Полу­чить мож­но, отпра­вив запрос со стра­ни­цы доку­мен­та­ции Яндекс.

2. Иден­ти­фи­ка­тор обла­ка. После реги­стра­ции на хостин­ге мы захо­дим в кон­троль-панель. Мы долж­ны уви­деть наши ресур­сы, в част­но­сти, облако:

А спра­ва от него будет идентификатор.

3. Иден­ти­фи­ка­тор ката­ло­га. На той же стра­ни­це кон­троль пане­ли, ниже обла­ка мы уви­дим каталог:

Так­же, спра­ва мы можем ско­пи­ро­вать его идентификатор.

После полу­че­ния нуж­ных дан­ных про­сто сохра­ня­ем их в отдель­ный файл. После уста­нов­ки terraform они нам понадобятся.

Установка Terraform

Terraform явля­ет­ся кли­ен­том и необ­хо­ди­мо выпол­нить уста­нов­ку на ком­пью­тер, с кото­ро­го пла­ни­ру­ет­ся управ­ле­ние инфра­струк­ту­рой. Акту­аль­ная инструк­ция по раз­вер­ты­ва­нию пред­став­ле­на на офи­ци­аль­ном сай­те. На теку­щий момент кли­ент может быть уста­нов­лен из репо­зи­то­рия, с исполь­зо­ва­ни­ем бинар­ни­ка, собран из исход­ни­ков, а так­же с помо­щью choco на Windows или brew на Mac OS. В нашем при­ме­ре будут рас­смот­ре­ны уста­нов­ка на Ubuntu и Rocky Linux из репо­зи­то­рия, а так­же загруз­ка бинарника.

Ubuntu (Debian)

Обнов­ля­ем спи­сок пакетов:

apt update

Уста­но­вим допол­ни­тель­ные пакеты:

apt install gnupg software-properties-common curl

* где:

  • gnupg — про­грам­ма для шиф­ров­ки и дешиф­ров­ки циф­ро­вых под­пи­сей. Нуж­на для рабо­ты с репозиториями.
  • software-properties-common — ути­ли­та для рабо­ты с репозиториями.
  • curl — отправ­ка GET, POST и дру­гих запросов.

Уста­но­вим в систе­му ключ для репозитория:

curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -

Добав­ля­ем репозиторий:

apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"

Обнов­ля­ем спи­сок паке­тов, что­бы загру­зить спис­ки с ново­го репозитория:

apt update

Мож­но уста­нав­ли­вать terraform:

apt install terraform

Rocky Linux

Уста­нав­ли­ва­ем ути­ли­ту для рабо­ты с репозиториями:

yum install yum-utils

Добав­ля­ем репозиторий:

yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

Уста­нав­ли­ва­ем terraform:

yum install terraform

Скачиваем бинарник

Суть дан­но­го мето­да — ска­чать бинар­ный файл и пере­не­сти его в ката­лог /usr/local/bin.

Таким же обра­зом уста­нав­ли­ва­ем terraform на Windows.

Для нача­ла нам пона­до­бят­ся ути­ли­ты unzip и wget. Уста­нав­ли­ва­ют­ся они по-раз­но­му, в зави­си­мо­сти от дис­три­бу­ти­ва Linux.

а) Ubuntu / Debian:

apt install unzip wget

б) Rocky Linux:

yum install unzip wget

Пере­хо­дим к загруз­ке бинар­но­го фай­ла. Посмот­реть ссыл­ку на него мож­но на стра­ни­це офи­ци­аль­но­го сай­та:

Вос­поль­зу­ем­ся ссыл­кой, что­бы ска­чать архив:

wget https://releases.hashicorp.com/terraform/1.1.7/terraform_1.1.7_linux_amd64.zip

* в нашем при­ме­ре будет загру­же­на вер­сия 1.1.7.

Рас­па­ку­ем архив командой:

unzip terraform_*_linux_amd64.zip

Пере­не­сем рас­па­ко­ван­ный бинар­ник в каталог:

mv terraform /usr/local/bin/

После установки

Убе­дим­ся, что terraform рабо­та­ет. Для это­го вво­дим команду:

terraform -version

Мы долж­ны полу­чить что-то на подобие:

Terraform v1.1.7
on linux_amd64

Так­же реко­мен­ду­ет­ся уста­но­вить автоподстановки:

terraform -install-autocomplete

Это поз­во­лит нам завер­шать коман­ды terraform с помо­щью кла­ви­ши Tab.

Теперь созда­дим ката­лог, в кото­ром будем рабо­тать со сце­на­ри­я­ми для тераформа:

mkdir -p /opt/terraform/yandex

* в моем при­ме­ре я решил рабо­тать в ката­ло­ге /opt/terraform/yandex.

Перей­дем в создан­ный каталог:

cd /opt/terraform/yandex

Мы гото­вы при­сту­пить непо­сред­ствен­но к рабо­те с terraform.

Установка провайдера

Что­бы terraform мог кор­рект­но вза­и­мо­дей­ство­вать с инфра­струк­ту­рой про­вай­де­ра, необ­хо­ди­мо исполь­зо­вать набор инструк­ций, кото­рые будет исполь­зо­вать наша ути­ли­та. В тер­ми­но­ло­гии terraform это назы­ва­ет­ся провайдер.

На сай­те Hashicorp мож­но най­ти все под­дер­жи­ва­е­мые про­вай­де­ры. Как гово­ри­лось выше, мы будем рабо­тать с Yandex.Cloud. Рас­смот­рим подроб­нее уста­нов­ку про­вай­де­ра для рабо­ты с ним.

В нашем рабо­чем ката­ло­ге созда­ем пер­вый файл:

vi main.tf

* где:

  • terraform required_version — вер­сия кли­ен­та terraform, для кото­ро­го дол­жен запус­кать­ся сце­на­рий. Пара­метр не обя­за­тель­ный, но явля­ет­ся пра­ви­лом хоро­ше­го тона и может помочь избе­жать неко­то­рых оши­бок, кото­рые воз­ник­нут из-за несов­ме­сти­мо­сти при рабо­те в команде.
  • required_providers version — вер­сия про­вай­де­ра. Мы можем ука­зать, как в дан­ном при­ме­ре, необ­хо­ди­мость исполь­зо­вать кон­крет­ную вер­сию, а мож­но с помо­щью зна­ка >= или <= ука­зать не выше или не ниже определенной.
  • token — OAuth-токен для про­хож­де­ния авто­ри­за­ции. Был нами полу­чен после реги­стра­ции на хостинге
  • cloud_id — иден­ти­фи­ка­тор для обла­ка, в кото­ром будут созда­вать­ся ресур­сы. Так­же полу­чен был нами заранее.
  • folder_id — иден­ти­фи­ка­тор ката­ло­га, кото­рый так­же был полу­чен в нача­ле инструкции.
  • zone — ресур­сы, кото­рые хра­нят­ся на мощ­но­стях Яндекс раз­де­ле­ны по зонам. Каж­дая зона — это опре­де­лен­ная гео­гра­фи­че­ская лока­ция цен­тра обра­бот­ки дан­ных. Спи­сок доступ­ных зон мож­но посмот­реть на стра­ни­це Зоны доступ­но­сти.

Теперь выпол­ним команду:

terraform init

Систе­ма загру­зит нуж­ные фай­лы и уста­но­вит провайдер:

Мы гото­вы дви­гать­ся дальше.

Работа с ресурсами

Мы рас­смот­рим неболь­шие при­ме­ры по созда­нию, редак­ти­ро­ва­нию и уда­ле­нию ресур­сов. В боль­шей сте­пе­ни мы будем рабо­тать с вир­ту­аль­ны­ми маши­на­ми. Боль­шую часть инфор­ма­ции по напи­са­нию сце­на­ри­ев мож­но най­ти на офи­ци­аль­ном сай­те хостин­га или само­го terraform.

Создание ресурсов

В нашем рабо­чем ката­ло­ге созда­дим новый файл:

vi infrastructure1.tf

Напи­шем мини­маль­но необ­хо­ди­мый сце­на­рий для созда­ния вир­ту­аль­ной машины:

* где:

  • data — поз­во­ля­ет запра­ши­вать дан­ные. В дан­ном при­ме­ре мы обра­ща­ем­ся к ресур­су yandex_compute_image с целью поис­ка иден­ти­фи­ка­то­ра обра­за, с кото­ро­го долж­на загру­зить­ся наша машина. 
    • yandex_compute_image — ресурс обра­за. Его назва­ние опре­де­ле­но провайдером.
    • ubuntu_image — про­из­воль­ное назва­ние ресур­са. Его мы опре­де­ли­ли сами и будем исполь­зо­вать в нашем сценарии.
    • ubuntu-2004-lts — в нашем при­ме­ре мы запра­ши­ва­ем дан­ные ресур­са, кото­рый ищем по family с назва­ни­ем ubuntu-2004-lts. Дан­ное назва­ние мож­но посмот­реть в кон­троль пане­ли хостин­га — нуж­но выбрать образ и клик­нуть по нему. Откро­ет­ся стра­ни­ца с допол­ни­тель­ной инфор­ма­ци­ей. В дан­ном слу­чае ubuntu-2004-lts соот­вет­ству­ем Ubuntu 20.04 LTS.
  • resource — поз­во­ля­ет созда­вать раз­лич­ные ресурсы. 
    • yandex_compute_instance — ресурс вир­ту­аль­ной маши­ны. Его назва­ние опре­де­ле­но провайдером.
    • vm-test1 — наше назва­ние ресур­са для вир­ту­аль­ной машины.
    • yandex_vpc_network — ресурс сети, опре­де­лен­ный провайдером.
    • network_terraform — наше назва­ние для ресур­са сети.
    • yandex_vpc_subnet — ресурс под­се­ти, опре­де­лен­ный провайдером.
    • subnet_terraform — наше назва­ние для ресур­са подсети.
    • metadata — обра­ти­те осо­бое вни­ма­ние на пере­да­чу мете­дан­ных. В дан­ном при­ме­ре мы пере­да­ем содер­жи­мое фай­ла, а сам файл рас­смот­рим ниже.

** в нашем при­ме­ре будет созда­на вир­ту­аль­ная маши­на с назва­ни­ем test1, 2 CPU, 2 Gb RAM в сети 192.168.15.0/24 на базе Ubuntu 20.04 LTS.
*** обра­ти­те вни­ма­ние, что мы ука­за­ли иден­ти­фи­ка­тор той под­се­ти для вир­ту­аль­ной маши­ны, кото­рую созда­ли так­же с помо­щью наше­го сце­на­рия terraform.

Созда­ем файл с метеданными:

meta.yml

* в дан­ном фай­ле мы опи­шем поль­зо­ва­те­ля, под кото­рым мы смо­жем под­клю­чить­ся к наше­му сер­ве­ру по SSH:

  • #cloud-config — как выяс­ни­лось, этот ком­мен­та­рий обязательный.
  • name — имя поль­зо­ва­те­ля, кото­рый будет создан на вир­ту­аль­ной машине.
  • groups — в какую груп­пу доба­вить пользователя.
  • shell — обо­лоч­ка shell по умолчанию.
  • sudo — пра­ви­ло повы­ше­ния привилений.
  • ssh-authorized-keys — спи­сок клю­чей, кото­рые будут добав­ле­ны в authorized_keys.

Попро­бу­ем испы­тать наш сце­на­рий. Сна­ча­ла вводим:

terraform plan

Если мы не ошиб­лись, ути­ли­та пока­жет, что terraform сде­ла­ет в нашей облач­ной инфра­струк­ту­ре. Очень важ­но вни­ма­тель­но про­смат­ри­вать изменения.

После того, как мы убе­дим­ся в кор­рект­но­сти дей­ствий, при­ме­ня­ем настройку:

terraform apply

Мы еще раз уви­дим, что будет выпол­не­но, а так­же полу­чим запрос на под­твер­жде­ние дей­ствий — вво­дим yes:

Enter a value: yes

Terraform выпол­нит необ­хо­ди­мые дей­ствия. Мы можем перей­ти в кон­троль-панель хостин­га и убе­дить­ся, что наша вир­ту­аль­ная маши­на создалась.

Редактирование данных

Если необ­хо­ди­мо вне­сти изме­не­ния в нашу инфра­струк­ту­ру, то доста­точ­но про­сто открыть наш файл со сценарием:

vi infrastructure1.tf

Вне­сти нуж­ные изме­не­ния, например:

* в нашем кон­крет­ном слу­чае, мы уве­ли­чи­ли коли­че­ство про­цес­со­ров и объ­ем памя­ти для создан­ной вир­ту­аль­ной машины.

Одна­ко, неко­то­рые изме­не­ния тре­бу­ют оста­нов­ки вир­ту­аль­ной маши­ны. В этом слу­чае мы полу­чим ошиб­ку при попыт­ке при­ме­нить новые настрой­ки с текстом:

Error: Changing the `secondary_disk`, `resources`, `platform_id`, `network_acceleration_type` or `network_interfaces` on an instance requires stopping it. To acknowledge this action, please set allow_stopping_for_update = true in your config file.

Мы долж­ны явно раз­ре­шить для кон­крет­ных ресур­сов выпол­не­ние оста­нов­ки их рабо­ты для вне­се­ния изме­не­ний. В нашем фай­ле для кон­крет­но­го ресур­са добавим:

* для нашей маши­ны test1 мы ука­за­ли опцию allow_stopping_for_update, кото­рая гово­рит о том, что рабо­ту ресур­са мож­но оста­нав­ли­вать для вне­се­ния изменений.

Стро­им план:

terraform plan

Мы долж­ны уви­деть, что будет изме­не­но в нашей инфра­струк­ту­ре. В моем случае:

При­ме­ня­ем настройки:

terraform apply

Про­ве­ря­ем через кон­троль-панель, что изме­не­ния всту­пи­ли в силу.

Удаление ресурсов

Для уда­ле­ния ресур­са мож­но исполь­зо­вать раз­ные приемы.

1. Напри­мер, мож­но ука­зать count = 0:

2. Мож­но заком­мен­ти­ро­вать или уда­лить стро­ки ресур­са из фай­ла tf.

3. Если мы хотим оста­вить содер­жи­мое фай­лов нетро­ну­тым, но при этом уда­лить все ресур­сы, вводим:

terraform destroy

Ути­ли­та прой­дет по всем фай­лам tf в дирек­то­рии, най­дет ресур­сы и выпол­нит их удаление.

Файл state

Очень важ­но уметь рабо­тать с фай­лом состо­я­ния terraform, а так­же понять, что это. Дан­ный файл появ­ля­ет­ся после пер­во­го при­ме­не­ния сце­на­рия в том же рабо­чем каталоге:

ls

terraform.tfstate

В нем содер­жат­ся все изме­не­ния, кото­рые про­ис­хо­ди­ли с при­ме­не­ни­ем terraform. Без него послед­ний не будет знать, что уже было сде­ла­но — на прак­ти­ке это при­ве­дет к дуб­ли­ро­ва­нию инфра­струк­ту­ры, то есть, если мы созда­ли сер­вер, поте­ря­ли файл состо­я­ния и сно­ва запу­сти­ли terraform, он создаст еще один сер­вер. Для боль­ших инфра­струк­тур с боль­шой исто­ри­ей все намно­го критичнее.

И так, файл состо­я­ния важен с точ­ки зре­ния пони­ма­ния инфра­струк­ту­ры самим terraform. Так­же у него есть еще одна функ­ция — бло­ки­ров­ка парал­лель­ных выпол­не­ний. Это важ­но для рабо­ты в коман­дах, где одно­вре­мен­ные дей­ствия могут при­ве­сти к неожи­дан­ным послед­стви­ям. Что­бы это­го не про­изо­шло, terraform созда­ет бло­ки­ров­ку, кото­рая запре­ща­ет выпол­не­ния, если уже идет про­цесс при­ме­не­ния плана.

Из выше­ска­зан­но­го дела­ем выводы:

  • Фай­лы состо­я­ния нуж­но бэкапить.
  • Они долж­ны нахо­дить­ся в надеж­ном месте.
  • У всех инже­не­ров, кото­рые рабо­та­ют с инфра­струк­ту­рой дол­жен быть доступ к фай­лу состо­я­ния. Они не долж­ны его копи­ро­вать на свои ком­пью­те­ры и исполь­зо­вать индивидуально.

Рас­смот­рим воз­мож­ность хра­не­ния дан­но­го фай­ла состо­я­ния на облач­ном хра­ни­ли­ще Яндекс.

Сна­ча­ла мы долж­ны создать хра­ни­ли­ще. Это мож­но сде­лать через веб-интер­фейс, но мы это сде­ла­ем в terraform.

Но сна­ча­ла мы сде­ла­ем файл:

vi variables.tf

* где yandex_folder_id — назва­ние для нашей пере­мен­ной; <иден­ти­фи­ка­тор ката­ло­га> — тот иден­ти­фи­ка­тор, кото­рый мы ука­за­ли в фай­ле main под аргу­мен­том folder_id.

Теперь откро­ем файл main:

vi main.tf

И отре­дак­ти­ру­ем зна­че­ние folder_id на:

* в дан­ном при­ме­ре мы теперь зада­ем folder_id не явно, а через создан­ную переменную.

Теперь созда­дим файл:

vi yandex_storage_bucket.tf

* рас­смот­рим файл немно­го подробнее:

  • yandex_iam_service_account — созда­ние ресур­са для учет­ной запи­си. В нашем при­ме­ре она будет иметь назва­ние sa-testи вхо­дить в систем­ный ката­лог yandex_folder_id (пере­мен­ная, кото­рую мы созда­ли ранее).
  • yandex_resourcemanager_folder_iam_member — созда­ние роли. Она будет иметь доступ для редак­ти­ро­ва­ния хра­ни­лищ. В состав роли вой­дет создан­ная запись sa (с име­нем sa-test).
  • yandex_iam_service_account_static_access_key — созда­ем ключ досту­па для нашей сер­вис­ной учет­ной записи.
  • yandex_storage_bucket — созда­ем хра­ни­ли­ще с назва­ни­ем tf-state-bucket-test.

Созда­ем еще один файл:

vi outputs.tf

* это дела­ет­ся для полу­че­ния зна­че­ний аргу­мен­тов access_key и secret_key и сохра­не­ния дан­ных зна­че­ний в фай­ле состо­я­ния. Если access_key мож­но посмот­реть в пане­ли Яндек­са, то secret_key мы уви­деть не можем.

Стро­им план и при­ме­ня­ем настройки:

terraform plan

terraform apply

Захо­дим в кон­троль-панель Яндек­са и видим, что у нас созда­лось хра­ни­ли­ще. Его мы будем исполь­зо­вать для хра­не­ния фай­лов состо­я­ний terraform.

Откры­ва­ем наш файл main:

vi main.tf

Добав­ля­ем:

* в раз­дел terraform мы доба­ви­ли инструк­цию для хра­не­ния фай­ла state. В нашем при­ме­ре он будет раз­ме­щен на баке­те tf-state-bucket-test по пути terraform/infrastructure1/terraform.tfstate.
** осо­бое вни­ма­ние обра­ти­те на access_key и secret_key. В раз­де­ле terraform мы не можем ука­зать пере­мен­ные, как это дела­ли при созда­нии хра­ни­ли­ща. Сами зна­че­ния мож­но най­ти в нашем локаль­ном фай­ле state.

Исполь­зу­ем команду:,

terraform init

Систе­ма сно­ва ини­ци­а­ли­зи­ру­ет состо­я­ние теку­ще­го ката­ло­га и создаст файл state на уда­лен­ном хра­ни­ли­ще. Что­бы убе­дить­ся, мож­но зай­ти в кон­троль панель, най­ти наше хра­ни­ли­ще, а в нем — файл terraform/infrastructure1/terraform.tfstate.

Реальный пример

Мы раз­вер­нем 2 веб-сер­ве­ра и поме­стим их за Network load balancer.

Для это­го мы созда­дим новый рабо­чий каталог:

mkdir -p /opt/terraform/yandex-network-load balancer

И перей­дем в него:

cd /opt/terraform/yandex-network-load balancer

Под­ра­зу­ме­ва­ет­ся, что все инфра­струк­ту­ра созда­ет­ся с нуля, поэто­му нуж­но либо уда­лить преды­ду­щие нара­бот­ки, либо создать новый сер­вис­ный акка­унт для storage.

Мы созда­дим 5 файлов:

  1. main.tf — в нем мы опи­шем инструк­ции для init, а имен­но тре­бо­ва­ния к вер­сии кли­ен­та terraform, провайдера.
  2. web-servers.tf — сце­на­рий для созда­ния 2-х веб-серверов.
  3. network-load-balancer.tf — созда­ние Network Load Balancer.
  4. variables.tf — опи­са­ние переменных.
  5. outputs.tf — вывод зна­че­ний после отра­бот­ки terraform на экран и в файл состояния.

1. Созда­ем файл main.tf:

vi main.tf

* это стан­дарт­ный вари­ант фай­ла main.tf, кото­рый мы раз­би­ра­ли в начале.

2. web-servers.tf:

vi web-servers.tf

* в дан­ном при­ме­ре мы созда­дим 2 вир­ту­аль­ные маши­ны — одну с обра­зом lamp (Apache), вто­рую на lemp (NGINX).

3. network-load-balancer.tf:

vi network-load-balancer.tf

* в дан­ном сце­на­рии мы:

  • созда­ем target_group из 2-х наших веб-серверов.
  • созда­ем балан­си­ров­щик lb-test.
  • при­вя­зы­ва­ем к балан­си­ров­щи­ку target_group.

4. variables.tf:

vi variables.tf

* созда­дим пере­мен­ную с folder_id, в кото­ром мы долж­ны рабо­тать. Выше мы уже дела­ли такую переменную.

5. outputs.tf:

vi outputs.tf

* на самом деле, это не обя­за­тель­но — инфор­ма­цию о создан­ном балан­си­ров­щи­ке мож­но посмот­реть в кон­троль пане­ли, но для при­ме­ра мы реши­ли это рассмотреть.

Гото­во. После при­ме­не­ния дан­но­го сце­на­рия мы полу­чим то, что хоте­ли — две вир­ту­аль­ные маши­ны + балан­си­ров­щик сети, кото­рый будет рас­пре­де­лять запро­сы меж­ду дан­ны­ми серверами.