You’re going to need a bigger boat.
Волею судеб в 2022м году среднему российскому сисадмину пришлось прикинуть всякое к носу и решить, как жить без таких привычных AWS, GCP и, прости хосспади, Azure. Выяснилось, что вопреки верещанию некоторых "интеллектуалов", клауды в РФ таки есть и более того, можно походить по рынку и выбрать. В качестве примера такого клауда возьмём SberCloud, а точнее тот его кусок, который зовётся advanced и попробуем натравить на него Terraform.
Сразу оговорюсь - это не обзор функционала и не сравнение с другими игроками, хоть и жаль немного, что пропадает столько хороших шуток хотя бы на тему диффок доки того же Elastic Cloud Server в SberCloud и HuaweiCloud. Будем практичны - есть задача X а вот для неё решение Y (нам же ехать, а не шашечки).
Задача - поднять минимальный энв с managed Kubernetes и не сильно задолбаться. Под минимальным энвом будем
понимать вот такую историю:
Решение - читаем дальше.
Когда знакомишься с очередным клауд-провайдером, после первых 20и минут, проведённых в веб-консоли у эникея здорового человека возникает закономерный вопрос - а как бы устроить IaC по заветам дяди Боба? Видно, что парни из сберклауда тоже озаботились этим вопросом - поиск по гитхабу даёт нам репозиторий с соответствующим терраформ провайдером. Беглый взгляд говорит, что репа вполне себе живая, есть собранные релизные бинари, какой-то движ в ишуях и пуллреквестах, чейнджлог - в общем, всё по-взрослому. Лично мне не терпелось, поэтому первым делом я пошёл в examples. А там... мама дорогая, практически всё, что может пригодиться для старта - там есть. Но дьявол, как всегда, в деталях.
Начинаем с конфигурации аутентификации терраформ клиента, делаем по инструкции отсюда. Но не всё так просто, в случае, когда у нас есть креды только для IAM юзера (а именно так было в моём случае), то нужно посетать ещё пару переменных окружения, а именно:
$ export SBC_ENTERPRISE_PROJECT_ID="YOUR_ENTERPRISE_UUID"
$ export SBC_PROJECT_NAME="YOUR_ENTERPRISE_PROJECT_NAME"
При этом, в SBC_PROJECT_NAME нужно писать имя не в том виде, в котором его видно в веб-консоли. Формат
должен быть следующий: ${SBC_REGION_NAME}_${PROJECT_NAME_FROM_WEBCONSOLE}
. Например ru-moscow-1_my-awesome-project
.
Если кто-то знает, где эта инфа лежит в документации - готов угостить пивом, потому что я это доставал из исходников
провайдера.
В остальном, можно смело брать примеры из репозитория и компилировать из них своё решение, HCL код там хороший и показать его потом будет не стыдно.
Нельзя не помянуть добрым словом менеджмент Hashicorp'a, ударившийся в модную в этом сезоне
cancel culture. Для админов из РФ это означает следующее: вот ты набросал
HCL кодец, собираешься приступить к отладке, пишешь в терминале terraform init
и получаешь в руки известно что:
╷
│ Error: Failed to query available provider packages
│
│ Could not retrieve the list of available versions for provider hashicorp/aws: could not connect to registry.terraform.io:
│ Failed to request discovery document: 403 Forbidden
╵
Хорошая новость в том, что в нашем распоряжении есть несколько лазеек, позволяющих обойти такие ограничения. Первая и самая очевидная - использовать VPN с адресом где-нибудь за пределами подсанкционных стран.
Вторая - это завести свой собственный registry сервер, который будет отдавать бинари провайдеров. Есть открытая (пока ещё) спецификация Provider Registry Protocol, никакого rocket science, при желании, минимальная рабочая версия реализуется за пару выходных (но это будет темой отдельного поста). Из готовых же opensource реализаций гуглится только terralist.
Третья лазейка - это заставить терраформ использовать в качестве registry какую-нибудь локальную директорию, в которую подложить заранее собранные\скачанные бинари провайдеров. В моём случае мне понадобится два провайдера:
Сначала подготовим terraform:
Создаём конфиг для терраформа с путями до директории с бинарями провайдеров:
provider_installation {
filesystem_mirror {
path = "/home/user/tfproviders"
}
}
disable_checkpoint = true
Создаём директории, где терраформ будет их искать:
$ mkdir -p ~/tfproviders/registry.terraform.io
Скаченные бинари по ссылкам выше раскладываем в следующие места (кто на MacOS - повнимательней с названием платформы и путями $HOME, кто на Windows - мне вас жаль):
/home/user/tfproviders/registry.terraform.io/hashicorp/local/2.2.2/linux_amd64/terraform-provider-local_v2.2.2_x5
/home/user/tfproviders/registry.terraform.io/sbercloud-terraform/sbercloud/1.10.0/linux_amd64/terraform-provider-sbercloud_v1.10.0
Вот теперь terraform init
корректно сделает всё, что нужно и можно будет двигаться дальше.
Все переменные, использующиеся в HCL коде, определим потом отдельно.
Начнём с создания VPC, подсетей и секурити рулов, потом бастион и, наконец, сам кластер k8s.
resource "sbercloud_vpc" "vpc" {
name = var.vpc_name
cidr = var.vpc_cidr
tags = var.tags
}
resource "sbercloud_vpc_subnet" "subnet" {
name = var.subnet_name
cidr = var.subnet_cidr
gateway_ip = var.subnet_gateway_ip
primary_dns = var.subnet_dns1
secondary_dns = var.subnet_dns2
vpc_id = sbercloud_vpc.vpc.id
tags = var.tags
}
locals {
default_rules = {
http-rule = {
description = "Allow HTTP from anywhere",
protocol = "tcp",
port = 80,
source = "0.0.0.0/0"
},
https-rule = {
description = "Allow HTTPS from anywhere",
protocol = "tcp",
port = 443,
source = "0.0.0.0/0"
},
ssh-rule = {
description = "Allow SSH from anywhere",
protocol = "tcp",
port = 22,
source = "0.0.0.0/0"
}
}
}
resource "sbercloud_networking_secgroup" "sg_default" {
name = var.default_security_group_name
description = "Default security group"
}
resource "sbercloud_networking_secgroup_rule" "default_ingress_icmp" {
direction = "ingress"
ethertype = "IPv4"
description = "Allow ICMP from anywhere"
protocol = "icmp"
remote_ip_prefix = "0.0.0.0/0"
security_group_id = sbercloud_networking_secgroup.sg_default.id
}
resource "sbercloud_networking_secgroup_rule" "default_ingress" {
for_each = local.default_rules
direction = "ingress"
ethertype = "IPv4"
description = each.value.description
protocol = each.value.protocol
port_range_min = each.value.port
port_range_max = each.value.port
remote_ip_prefix = each.value.source
security_group_id = sbercloud_networking_secgroup.sg_default.id
}
resource "sbercloud_compute_instance" "bastion" {
name = var.hostname
image_id = var.ecs_image_id
flavor_id = var.ecs_flavor
security_groups = [var.default_security_group_name]
availability_zone = var.vpc_az
key_pair = var.keypair_name
system_disk_type = var.ecs_system_disk_type
system_disk_size = var.ecs_system_disk_size
# как и в любом уважающем себя клауде, инстансы в SberCloud умеют в cloud-init
user_data = file("${path.module}/setup.sh")
tags = var.tags
network {
uuid = var.subnet_id
}
}
resource "sbercloud_vpc_eip" "bastion_eip" {
tags = var.tags
publicip {
type = "5_bgp"
}
bandwidth {
name = "elb-bastion-bandwidth"
size = var.bandwidth_size
share_type = "PER"
charge_mode = var.bandwidth_charge_mode
}
}
resource "sbercloud_compute_eip_associate" "associated_01" {
public_ip = sbercloud_vpc_eip.bastion_eip.address
instance_id = sbercloud_compute_instance.bastion.id
}
resource "sbercloud_cce_cluster" "default" {
name = var.cce_cluster_name
flavor_id = var.cce_flavor
container_network_type = "overlay_l2"
container_network_cidr = var.subnet_cidr
multi_az = false
vpc_id = var.vpc_id
subnet_id = var.subnet_id
}
resource "sbercloud_cce_node_pool" "default" {
cluster_id = sbercloud_cce_cluster.default.id
name = var.cce_nodepool_name
flavor_id = var.cce_node_flavor
availability_zone = var.vpc_az
key_pair = var.keypair_name
scall_enable = true
min_node_count = 1
initial_node_count = 1
max_node_count = var.max_node_count
scale_down_cooldown_time = 100
priority = 1
type = "vm"
os = "CentOS 7.6"
tags = var.tags
root_volume {
size = 50
volumetype = "SAS"
}
data_volumes {
size = 100
volumetype = "SAS"
}
}
resource "local_file" "kubeconfig" {
depends_on = [sbercloud_cce_cluster.default]
filename = "./kubeconfig"
# А вот тут нам и пригодился провайдер local - он нам сгенерит конфиг для kubectl
content = sbercloud_cce_cluster.default.kube_config_raw
}
На данном этапе мы подготовили примерный код, который можно аккуратно разложить по директориями и запустить наконец
terraform apply
, после чего можно заходить по ssh на бастион и запускать всякое в кластере k8s (не забыв забрать
сгенерённый kubeconfig). Готовый к использованию код можно найти тут.
Подробнее про варианты конфигурации ресурсов в SberCloud можно почитать в
документации.