На днях было решено что поднимаем наш проект в Alibaba Cloud. Около 100 однотипных виртуалок, из них 10 — это ядро.
Руками это делать нереально. Все виртуалки должны быть размазаны по 15-и различным регионам (Азия, Америка, Европа). Должны быть какие-никакие правила для фаерволла. Быть прокинуты ключи. Было решено использовать Terraform.
Подготовка
Terraform инструмент прекрасный, но из-за ограничений с их стороны для RU сегмента, немного мешает.
Сначала надо установить Terraform. У нас много ВМ в разных регионах, никаких проблем что бы что-то скачать.
1 |
curl -O https://releases.hashicorp.com/terraform/1.2.6/terraform_1.2.6_linux_amd64.zip |
И закинуть по SCP.
1 |
scp root@XX.XX.XX.X:/root/terraform_1.2.6_linux_amd64.zip . |
Распаковываем с помощью unzip и закидываем куда вам удобно.
1 2 |
unzip terraform_1.2.6_linux_amd64.zip mv terraform /usr/local/bin |
Теперь нужно подготовить провайдер, которым будем управлять. Для Alibaba Cloud есть провайдер aliyun/alicloud. Вот с его установкой уже могут возникнуть проблемы.
Что-ж, скачаем на другой машине архив с этим provider и перекинем через SCP и установим вручную.
1 |
curl -O https://releases.hashicorp.com/terraform-provider-alicloud/1.180.0/terraform-provider-alicloud_1.180.0_linux_amd64.zip |
Скачаем версию и для коллег с MacOS.
1 |
curl -O https://releases.hashicorp.com/terraform-provider-alicloud/1.180.0/terraform-provider-alicloud_1.180.0_darwin_amd64.zip |
Перекинем себе помощью SCP.
1 |
scp -r root@XX.XX.XX.XX:/root/terraform-provider-alicloud_1.120.0_*_amd64.zip . |
Подготовим каталог, в котором будем все хранить и потом запушим в GitLab (или кто чем пользуется).
1 |
mkdir -p terraform/mainnet |
Для локальной установки плагина, он должен лежать в правильном каталоге и соблюдать правильную последовательность этих каталогов.
Создадим.
1 2 3 |
cd terraform mkdir -p .terraform/plugins/registry.terraform.io/aliyun/alicloud/1.180.0/linux_amd64 mkdir -p .terraform/plugins/registry.terraform.io/aliyun/alicloud/1.180.0/darwin_amd64 |
И распакуем архивы.
1 2 |
unzip ../terraform-provider-alicloud_1.180.0_linux_amd64.zip -d .terraform/plugins/registry.terraform.io/aliyun/alicloud/1.180.0/linux_amd64/ unzip ../terraform-provider-alicloud_1.180.0_darwin_amd64.zip -d .terraform/plugins/registry.terraform.io/aliyun/alicloud/1.180.0/darwin_amd64/ |
Должно получиться вот так
Теперь можно создавать первую десятку ВМ!
Развертывание
Сразу отступление. Есть пример от Alibaba Cloud, о том, как развернуть ресурсы в нескольких регионах одним манифестом.
НО! При попытке провернуть тоже самое, задав провайдеру alias, ловил ошибку как на скриншоте ниже
Не знает, говорит, ни о каких опциях, кроме тех что вывел. Я попробовал перебрать множество версий провайдера, но там везде такая же ошибка.
Придется делать для каждой ВМ отдельный каталог и управлять отдельно каждой виртуалкой, что не очень удобно, но с другой стороны — надежнее. Ядро из 10 ВМ — самое важное.
Создаю каталог под ВМ — mgd00
Создаю манифест main.tf. Заполняю данными для провайдера.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
terraform { required_providers { alicloud = { source = "aliyun/alicloud" version = "1.180.0" } } } provider "alicloud" { access_key = var.access_key secret_key = var.secret_key region = "ap-south-1" } |
Первая ВМ будет в регионе India (Mumbai).
Коды регионов и зон можно найти тут: https://www.alibabacloud.com/help/en/elastic-container-instance/latest/regions-and-zones
Создаю variables.tf. Добавляю переменные с access_key и secret_key.
1 2 3 4 5 6 7 8 9 10 11 |
variable "access_key" { type = string default = "access_key_here" description = "Access key" } variable "secret_key" { type = string default = "secret_key_here" description = "Secret key" } |
Где взять ключи? В RAM Account (Alibaba Cloud рекомендуется завести отдельного пользователя под именем Terraform) в профиле есть пункт AccesKey Management.
Там можно создать ключи.
При создании будет сказано, что их нужно сохранить в надежном месте и никуда не выкладывать. Повторно значения ключей посмотреть не получится.
Теперь проинициализируем провайдера, с указанием путей до плагинов, из каталога mgd00.
1 |
terraform init -plugin-dir "../.terraform/plugins" |
Теперь создаем resource.
Что нам нужно:
- VPC (Virtual Private Cloud).
- vSwitch.
- Security Group.
- Правила для Security Group (22, ICMP, специфичный порт).
- Открытый ключ, что бы подключаться к ВМ.
- Монтирование ключа к ВМ.
- Внешний IP.
- Сам инстанс (ВМ).
Начнем с сети. VPC и vSwitch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
resource "alicloud_vpc" "vpc_mumbai_mainnet" { provider = alicloud vpc_name = "vpc_mumbai_mainnet" cidr_block = "172.16.100.0/28" } data "alicloud_zones" "mumbai" { provider = alicloud } resource "alicloud_vswitch" "vsw_mumbai_mainnet" { provider = alicloud vswitch_name = "vsw_mumbai_mainnet" vpc_id = alicloud_vpc.vpc_mumbai_mainnet.id cidr_block = "172.16.100.0/28" zone_id = data.alicloud_zones.mumbai.zones.0.id } |
Alibaba Cloud не дает создать сетевой интерфейс с внешним IP и только. Придется делать локальную сеть. Мне бы хватило 1 IP, но взял сеть чуть больше, с запасом. Ядро из 10 ВМ являются ведущими нодами, они будут раздавать с себя информацию в интернет.
Теперь Security Groups и правила к ней.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
resource "alicloud_security_group" "sg_mumbai_mainnet" { provider = alicloud name = "sg_mumbai_mainnet" vpc_id = alicloud_vpc.vpc_mumbai_mainnet.id } resource "alicloud_security_group_rule" "allow_ssh_22_mumbai_mainnet" { provider = alicloud type = "ingress" ip_protocol = "tcp" nic_type = "intranet" policy = "accept" port_range = "22/22" priority = 1 security_group_id = alicloud_security_group.sg_mumbai_mainnet.id cidr_ip = "0.0.0.0/0" } resource "alicloud_security_group_rule" "allow_icmp_mumbai_mainnet" { provider = alicloud type = "ingress" ip_protocol = "icmp" nic_type = "intranet" policy = "accept" port_range = "-1/-1" priority = 1 security_group_id = alicloud_security_group.sg_mumbai_mainnet.id cidr_ip = "0.0.0.0/0" } |
По умолчанию, весь исходящий трафик открыт, никаких доп. правил не нужно.
Теперь нужно решить вопрос с ключами. Оказалось, что нельзя взять пачку ключей и привязать к ВМ. 1 ключ — 1 ВМ. Спасибо, Alibaba!
Добавим ключ и привязку. Переменную с ключом можно будет увидеть дальше.
1 2 3 4 5 6 7 8 9 |
resource "alicloud_key_pair" "public_key" { key_pair_name = "admin" public_key = var.public_key } resource "alicloud_key_pair_attachment" "attachment" { key_pair_name = alicloud_key_pair.public_key.id instance_ids = alicloud_instance.mgd00.*.id } |
Теперь сам инстанс (ВМ) и дополним переменные к нему.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
resource "alicloud_instance" "mgd00" { provider = alicloud instance_name = "mgd00" host_name = "mgd00" image_id = var.image_id instance_type = var.instance_type_a security_groups = alicloud_security_group.sg_mumbai_mainnet.*.id instance_charge_type = var.instance_charge_type period = var.period system_disk_category = var.system_disk_category system_disk_size = var.system_disk_size vswitch_id = alicloud_vswitch.vsw_mumbai_mainnet.id internet_charge_type = var.internet_charge_type internet_max_bandwidth_out = var.internet_max_bandwidth_out_type_a tags = { Project = "Project" Role = "Core" } } |
Переменные в variables.tf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
variable "image_id" { type = string default = "centos_7_9_x64_20G_alibase_20220629.vhd" description = "Default image for ECS. Centos 7.9 x64." } variable "instance_type_a" { type = string default = "ecs.xn4.small" description = "1 CPU, 1 GB - Default instance type for ECS." } variable "instance_type_b" { type = string default = "ecs.t6-c2m1.large" description = "2 CPU, 1 GB - Default instance type for ECS." } variable "instance_type_c" { type = string default = "ecs.n1.tiny" description = "1 CPU, 1 GB - Default instance type for ECS." } variable "system_disk_category" { type = string default = "cloud_ssd" description = "Default system disk type for ECS." } variable "system_disk_size" { type = string default = "20" description = "Default system disk size for ECS" } variable "instance_charge_type" { type = string default = "PrePaid" # PostPaid description = "Payment type" } variable "period" { type = string default = 1 description = "Month count" } variable "internet_charge_type" { type = string default = "PayByTraffic" description = "Internet type" } variable "renewal_status" { type = string default = "AutoRenewal" description = "Auto Renewal. Not enable by default" } variable "internet_max_bandwidth_out_type_a" { type = string default = 100 description = "100 Mbit" } variable "internet_max_bandwidth_out_type_b" { type = string default = 80 description = "80 Mbit" } variable "public_key" { type = string default = "ssh-rsa AA.................SjiJ1BERxkJH6MpK+nRmis=" description = "public key" } |
Важные пояснения по всем опциям.
- image_id — я не нашел список всех ID образов, придется смотреть в веб-морде Alibaba Cloud.
- instance_type — Как можно заметить, в переменных у меня есть type_a, type_b, type_c. Все потому, что мне нужна самая дешевая ВМ, но не во всех регионах есть одинаковый тип инстансов. Для Dubai и London пришлось использовать другие.
- instance_charge_type — Оплата. Для тестов, рекомендую ставить PostPaid (Pay-as-you-go, плати потихоньку). Для прода уже поставил PrePaid, оплата сразу и так выходит чуть дешевле.
- period — Требуется только при типе оплаты PrePaid, нужно указать число (месяцы, по умолчанию), на сколько покупается инстанс.
- system_disk_category — Тип диска. Почитать о типах можно тут: https://www.alibabacloud.com/help/en/elastic-compute-service/latest/disk-categories
- system_disk_size — Размер диска. Минимум 20. Его и выбрал.
- internet_charge_type — Оплата внешнего канала. C объявлением данной опции автоматом получаем внешний IP. PayByTraffic — за 1 GB трафика отдаем 0.5-1 Юань. PayByBandwidth — за 1 Мбит безлимитного интернета отдаем 30 Юаней (!!!).
- internet_max_bandwidth_out — Ширина внешнего канала. Их у меня опять 2 типа, т.к некоторые инстансы не поддерживают 100 Мбит. Можно догадаться, что будет если выбрать оплату PayByBandwidth и указать ширину канала как 100 Мбит.
Для удобства, еще сделаем output.tf, что бы сразу видеть внешний IP.
1 2 3 |
output "public_ip" { value = alicloud_instance.mgd00.public_ip } |
Все готово. Попробуем запустить. Хотя лучше, сначала сделайте terraform plan.
1 |
terraform apply |
Все внимательно проверяем и печатаем yes.
Скриншота не осталось, но все должно пройти гладко.
Теперь осталось все продублировать еще 9 раз, сменить регион и развернуть ВМки.
Получилось что то по итогу такое
Дополнение. Я в каждом манифесте менял название региона в имени ресурса. Хотя можно было сделать одинаковое имя для всех ресурсов и просто копипастить. Я так делать не стал из-за того что работаю не один. Другой человек может не заметить в каком регионе он что-то делает, и совершить ошибку, и она может быть фатальной. Лучше в манифесте будет мельтешить название региона почаще.
Нужно запушить всё это добро в репозиторий. Рекомендация создать .gitignore и заполнить следующим образом.
1 2 3 |
.terraform.lock.hcl mgd*/.terraform/ variables.tf |
В корне репозитория оставить variables.tf.example без ключей. Передать ключи коллегам можно по более надежным каналам.
В следующей статье будем разворачивать следующие 90 ВМ, уже в более правильном виде, в одном манифесте будет по 10 ВМ, в одном регионе.