Пример использования Terraform, его основные команды, работа с бекенда и модулями.
Достаточно кратко, но со ссылками на документацию.
Устанавливаем на Arch Linux:
[simterm]
$ sudo pacman -S terraform
[/simterm]
Для авторизации используем созданный AWS профиль setevoy-root.
Содержание
main.tf
Создаём файл main.tf
, в котором указываем использование AWS provider, регион, и имя AWS профиля:
provider "aws" { region = "${var.aws-region}" profile = "setevoy-root" }
Тут регион мы выносим в переменные, которые будут задаваться через файл variables.tf
:
variable "aws-region" { default = "eu-west-1" description = "Default Amazon region" }
Далее – в main.tf
добавим создание Terraform ресурса – AWS EC2:
provider "aws" { region = "${var.aws-region}" profile = "setevoy-root" } resource "aws_instance" "tf-example-ec2" { ami = "ami-34414d4d" instance_type = "t2.nano" key_name = "${var.aws-key-name}" associate_public_ip_address = "true" tags { "Name" = "tf-example-ec2" "Env" = "${var.aws-cluster-name}" } }
И в variables.tf
– добавляем переменную с именем ключа:
variable "aws-region" { default = "eu-west-1" description = "Default Amazon region" } variable "aws-key-name" { default = "tf-example-ec2-key" description = "EC2 acces key pair" }
Документация по переменным в Terraform – тут>>>.
Первый запуск
Т.к. мы используем aws provider – Terraform должен сначала загрузить его плагин.
Для этого – используем init
:
[simterm]
$ terraform init Initializing provider plugins... - Checking for available provider plugins on https://releases.hashicorp.com... - Downloading plugin for provider "aws" (1.24.0)... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.aws: version = "~> 1.24" Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
[/simterm]
Файл aws плагина по умолчанию размещается в текущем каталоге, где создаётся папка .terraform
(путь можно переопределить с помощью -plugin-dir=
):
[simterm]
$ ls -la .terraform/plugins/linux_amd64/ total 74232 drwxr-xr-x 2 setevoy setevoy 4096 Jun 25 17:19 . drwxr-xr-x 3 setevoy setevoy 4096 Jun 25 17:19 .. -rwxr-xr-x 1 setevoy setevoy 79 Jun 25 17:19 lock.json -rwxr-xr-x 1 setevoy setevoy 75997088 Jun 25 17:19 terraform-provider-aws_v1.24.0_x4
[/simterm]
init
выполняет так же настройку бекендов для хранения state-файлов, но о них позже.
tarraform plan
Теперь можно запустить plan
, что бы увидеть – какие действия будут выполнены Terraform-ом:
[simterm]
$ 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: + aws_instance.tf-example-ec2 id: <computed> ami: "ami-34414d4d" associate_public_ip_address: "true" availability_zone: <computed> ebs_block_device.#: <computed> ephemeral_block_device.#: <computed> get_password_data: "false" instance_state: <computed> instance_type: "t2.nano" ipv6_address_count: <computed> ipv6_addresses.#: <computed> key_name: "tf-example-ec2-key" network_interface.#: <computed> network_interface_id: <computed> password_data: <computed> placement_group: <computed> primary_network_interface_id: <computed> private_dns: <computed> private_ip: <computed> public_dns: <computed> public_ip: <computed> root_block_device.#: <computed> security_groups.#: <computed> source_dest_check: "true" subnet_id: <computed> tags.%: "1" tags.Name: "tf-example-ec2" tenancy: <computed> volume_tags.%: <computed> vpc_security_group_ids.#: <computed> Plan: 1 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.
[/simterm]
В его выводе видим, что будет создан один ресурс (символ +
перед aws_instance.tf-example-ec2
).
“-
” значит удаление, а “~
” – модификацию существующего ресурса.
terraform apply
Что бы выполнить непосредственно создание ресурсов, описанных в main.tf
– вызываем apply
:
[simterm]
$ terraform apply 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: + aws_instance.tf-example-ec2 id: <computed> ami: "ami-34414d4d" associate_public_ip_address: "true" availability_zone: <computed> ebs_block_device.#: <computed> ephemeral_block_device.#: <computed> get_password_data: "false" instance_state: <computed> instance_type: "t2.nano" ipv6_address_count: <computed> ipv6_addresses.#: <computed> key_name: "tf-example-ec2-key" network_interface.#: <computed> network_interface_id: <computed> password_data: <computed> placement_group: <computed> primary_network_interface_id: <computed> private_dns: <computed> private_ip: <computed> public_dns: <computed> public_ip: <computed> root_block_device.#: <computed> security_groups.#: <computed> source_dest_check: "true" subnet_id: <computed> tags.%: "1" tags.Name: "tf-example-ec2" tenancy: <computed> volume_tags.%: <computed> vpc_security_group_ids.#: <computed> Plan: 1 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes aws_instance.tf-example-ec2: Creating... ami: "" => "ami-34414d4d" associate_public_ip_address: "" => "true" availability_zone: "" => "<computed>" ebs_block_device.#: "" => "<computed>" ephemeral_block_device.#: "" => "<computed>" get_password_data: "" => "false" instance_state: "" => "<computed>" instance_type: "" => "t2.nano" ipv6_address_count: "" => "<computed>" ipv6_addresses.#: "" => "<computed>" key_name: "" => "tf-example-ec2-key" network_interface.#: "" => "<computed>" network_interface_id: "" => "<computed>" password_data: "" => "<computed>" placement_group: "" => "<computed>" primary_network_interface_id: "" => "<computed>" private_dns: "" => "<computed>" private_ip: "" => "<computed>" public_dns: "" => "<computed>" public_ip: "" => "<computed>" root_block_device.#: "" => "<computed>" security_groups.#: "" => "<computed>" source_dest_check: "" => "true" subnet_id: "" => "<computed>" tags.%: "" => "1" tags.Name: "" => "tf-example-ec2" tenancy: "" => "<computed>" volume_tags.%: "" => "<computed>" vpc_security_group_ids.#: "" => "<computed>" aws_instance.tf-example-ec2: Still creating... (10s elapsed) aws_instance.tf-example-ec2: Still creating... (20s elapsed) aws_instance.tf-example-ec2: Still creating... (30s elapsed) aws_instance.tf-example-ec2: Still creating... (40s elapsed) aws_instance.tf-example-ec2: Creation complete after 47s (ID: i-062174155cee10e51) Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
[/simterm]
Проверяем, используя ID, выданный Terraform-ом:
[simterm]
$ aws ec2 describe-instances --instance-ids i-062174155cee10e51 { "Reservations": [ { "Groups": [], "Instances": [ { "AmiLaunchIndex": 0, "ImageId": "ami-34414d4d", "InstanceId": "i-062174155cee10e51", "InstanceType": "t2.nano", "KeyName": "tf-example-ec2-key", "LaunchTime": "2018-06-25T14:36:09.000Z", "Monitoring": { "State": "disabled" }, ...
terraform destroy
Для удаления созданных ресурсов – используем destroy
, который удалит все ресурсы, описанные в main.tf
:
[simterm]
$ terraform destroy aws_instance.tf-example-ec2: Refreshing state... (ID: i-062174155cee10e51) An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: - destroy Terraform will perform the following actions: - aws_instance.tf-example-ec2 Plan: 0 to add, 0 to change, 1 to destroy. Do you really want to destroy? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes aws_instance.tf-example-ec2: Destroying... (ID: i-062174155cee10e51) aws_instance.tf-example-ec2: Still destroying... (ID: i-062174155cee10e51, 10s elapsed) aws_instance.tf-example-ec2: Still destroying... (ID: i-062174155cee10e51, 20s elapsed) aws_instance.tf-example-ec2: Still destroying... (ID: i-062174155cee10e51, 30s elapsed) aws_instance.tf-example-ec2: Destruction complete after 32s Destroy complete! Resources: 1 destroyed.
[/simterm]
State-files
Terraform хранит информацию об инфрастуктуре в т.н. “state-файлах”.
По умолчанию это файл terraform.tfstate
в каталоге проекта.
Например, если проверить файл сейчас – в нём будет ноль ресурсов, т.к. мы только что выполнили destroy
:
[simterm]
$ cat terraform.tfstate { "version": 3, "terraform_version": "0.11.7", "serial": 3, "lineage": "a83f41ba-0a1e-ec21-cc4c-312940dfb53f", "modules": [ { "path": [ "root" ], "outputs": {}, "resources": {}, "depends_on": [] } ] }
[/simterm]
Перед выполнением apply
– TF создаст бекап текущего state-файла (если он есть) в файл terraform.tfstate.backup
– сейчас тут описано состояние проекта до выполнения destroy
:
[simterm]
$ cat terraform.tfstate.backup { "version": 3, "terraform_version": "0.11.7", "serial": 3, "lineage": "a83f41ba-0a1e-ec21-cc4c-312940dfb53f", "modules": [ { "path": [ "root" ], "outputs": {}, "resources": { "aws_instance.tf-example-ec2": { "type": "aws_instance", "depends_on": [], "primary": { "id": "i-062174155cee10e51", ...
[/simterm]
Имя файла, в котором будет храниться состояние инфрастуктуры, можно задать с помощью -state
, а имя файла бекапа – с помощью -backup
.
Проверяем – создаём ЕС2 заново, заодно используем опцию -auto-approve
, что бы не вводить yes каждый раз:
[simterm]
$ terraform apply -state /tmp/tf-example-ec2.tfstate -backup /tmp/tf-example-ec2.tfstate.backup -auto-approve
[/simterm]
Проверяем файл состояния:
[simterm]
$ cat /tmp/tf-example-ec2.tfstate { "version": 3, "terraform_version": "0.11.7", "serial": 4, "lineage": "a83f41ba-0a1e-ec21-cc4c-312940dfb53f", "modules": [ { "path": [ "root" ], "outputs": {}, "resources": { "aws_instance.tf-example-ec2": { "type": "aws_instance", "depends_on": [], "primary": { "id": "i-01b63239677ef31e8", ...
[/simterm]
Аналогично при destroy
необходимо указать этот же файл состояния – /tmp/tf-example-ec2.tfstate
:
[simterm]
$ terraform destroy -state /tmp/tf-example-ec2.tfstate -backup /tmp/tf-example-ec2.tfstate.backup -auto-approve aws_instance.tf-example-ec2: Refreshing state... (ID: i-0c6779781dabd81d8) aws_instance.tf-example-ec2: Destroying... (ID: i-0c6779781dabd81d8) ... aws_instance.tf-example-ec2: Destruction complete after 31s Destroy complete! Resources: 1 destroyed.
[/simterm]
terraform state
Для работы с файлами состояния – у Terrafrom имеется команда state
.
Например – просмотреть список ресурсов в state-файле можно с помощью list
:
[simterm]
$ terraform state list -state=/tmp/tf-example-ec2.tfstate aws_instance.tf-example-ec2
[/simterm]
Backends
По умолчанию – Terraform создаёт файлы локально, но так же их можно хранить в удалённых хранилищах.
Наиболее используемый вариант (по крайней мере из тех, что мне встречались) – это AWS S3 корзина.
Обновляем наш main.tf
, и описываем корзину, в которой будем хранить стейт-файлы – backend "s3"
:
provider "aws" { region = "${var.aws-region}" profile = "setevoy-root" } resource "aws_instance" "tf-example-ec2" { ami = "ami-34414d4d" instance_type = "t2.nano" key_name = "${var.aws-key-name}" associate_public_ip_address = "true" tags { "Name" = "tf-example-ec2" } } terraform { backend "s3" { profile = "setevoy-root" bucket = "tf-example-states" key = "tf-example/terraform.tfstate" region = "eu-west-1" } }
Создаём саму корзину:
[simterm]
$ aws s3api create-bucket --bucket tf-example-states --region eu-west-1 --create-bucket-configuration LocationConstraint=eu-west-1 { "Location": "http://tf-example-states.s3.amazonaws.com/" }
[/simterm]
Т.к. мы добавили бекенд s3 – выполняем инициализацию Terrafrom-проекта ещё раз:
[simterm]
$ terraform init Initializing the backend... Do you want to copy existing state to the new backend? Pre-existing state was found while migrating the previous "local" backend to the newly configured "s3" backend. No existing state was found in the newly configured "s3" backend. Do you want to copy this state to the new "s3" backend? Enter "yes" to copy and "no" to start with an empty state. Enter a value: yes Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes. Initializing provider plugins... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.aws: version = "~> 1.24" Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
[/simterm]
Terraform скопировал текущий state-файл в указанную корзину:
[simterm]
$ aws s3 ls s3://tf-example-states/tf-example/ 2018-06-25 18:40:11 317 terraform.tfstate
[/simterm]
Теперь состояние проекта будет записываться в этот файл.
Модули Terraform
О чём ещё стоит упомянуть – это модули в Terraform, которые позволяют разделять ресурсы и облегчают управление ими.
Например, файл main.tf
являет собой root-модуль проекта, в котором мы описываем ресурсы.
Аналогично – можно создать каталог, например ec2
, и уже в нём описать ресурс ЕС2 и добавить ему свои переменные.
Создаём каталог:
[simterm]
$ mkdir ec2
[/simterm]
Создаём файл, например ec2.tf
, и к нему заодно файл переменных – variables.tf
:
[simterm]
$ touch ec2/{ec2.tf,variables.tf}
[/simterm]
В файле ec2/ec2.tf
описываем создание ресурса ЕС2:
resource "aws_instance" "tf-example-ec2" { ami = "${var.aws-ec2-ami-id}" instance_type = "${var.aws-ec2-type}" key_name = "${var.aws-key-name}" associate_public_ip_address = "true" tags { "Name" = "tf-example-ec2" } }
В переменных – задаём сами переменные:
variable "aws-ec2-ami-id" { default = "ami-34414d4d" } variable "aws-ec2-type" { default = "t2.nano" } variable "aws-key-name" { default = "tf-example-ec2-key" }
В файле main.tf
описываем используем созданного модуля ec2
:
provider "aws" { region = "${var.aws-region}" profile = "setevoy-root" } module "ec2" { source = "ec2" } /* resource "aws_instance" "tf-example-ec2" { ami = "ami-34414d4d" instance_type = "t2.nano" key_name = "${var.aws-key-name}" associate_public_ip_address = "true" tags { "Name" = "tf-example-ec2" } } */ terraform { backend "s3" { profile = "setevoy-root" bucket = "tf-example-states" key = "tf-example/terraform.tfstate" region = "eu-west-1" } }
Т.к. добавили новый модуль – то сначала выполняем init
:
[simterm]
$ terraform init Initializing modules... - module.ec2 Getting source "ec2" Initializing the backend... Initializing provider plugins... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.aws: version = "~> 1.24" Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
[/simterm]
Теперь можно сделать plan
:
[simterm]
$ 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: + module.ec2.aws_instance.tf-example-ec2 id: <computed> ami: "ami-34414d4d" ...
[/simterm]
И выполнить apply
:
[simterm]
$ terraform apply -auto-approve module.ec2.aws_instance.tf-example-ec2: Creating... ami: "" => "ami-34414d4d" associate_public_ip_address: "" => "true" availability_zone: "" => "<computed>" ebs_block_device.#: "" => "<computed>" ephemeral_block_device.#: "" => "<computed>" get_password_data: "" => "false" instance_state: "" => "<computed>" instance_type: "" => "t2.nano" ipv6_address_count: "" => "<computed>" ipv6_addresses.#: "" => "<computed>" key_name: "" => "tf-example-ec2-key" network_interface.#: "" => "<computed>" network_interface_id: "" => "<computed>" password_data: "" => "<computed>" placement_group: "" => "<computed>" primary_network_interface_id: "" => "<computed>" private_dns: "" => "<computed>" private_ip: "" => "<computed>" public_dns: "" => "<computed>" public_ip: "" => "<computed>" root_block_device.#: "" => "<computed>" security_groups.#: "" => "<computed>" source_dest_check: "" => "true" subnet_id: "" => "<computed>" tags.%: "" => "1" tags.Name: "" => "tf-example-ec2" tenancy: "" => "<computed>" volume_tags.%: "" => "<computed>" vpc_security_group_ids.#: "" => "<computed>" module.ec2.aws_instance.tf-example-ec2: Still creating... (10s elapsed) module.ec2.aws_instance.tf-example-ec2: Creation complete after 15s (ID: i-08d703ddd4f252382) Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
[/simterm]
В целом – на этом пока всё.
Писать о Terraform можно (и, наверное, буду) много, но у него отличная документация.