Курица или яйцо: раскалываем IaC

Что появилось раньше — курица или яйцо? Довольно странное начало для статьи про Infrastructure-as-Code, не так ли?

Что такое яйцо?

Чаще всего Infrastructure-as-Code (IaC) — декларативный способ представления инфраструктуры. В нем мы описываем состояние, которое хотим получить, начиная от железной части, заканчивая конфигурацией ПО. Поэтому IaC используется для:

  1. Resource Provision. Это VMs, S3, VPC и т.д. Основные инструменты для работы: Terraform и CloudFormation.
  2. Software Configuration. Основные инструменты: Ansible, Chef и т.д.

Любой код лежит в git-репозиториях. И рано или поздно тимлид решит, что надо бы навести порядок в них. И будет рефакторить он. И создаст некоторую структуру. И увидит он, что это есть хорошо.

Также хорошо, что уже существует GitLab и GitHub-провайдер для Terraform (и это Software Configuration). С их помощью можно управлять всем проектом: членами команды, CI/CD, git-flow и т.д.

Откуда взялось яйцо?

Вот мы и постепенно подходим к главному вопросу.

Прежде всего надо начинать с репозитория, который описывает структуру других репозиториев, в том числе себя. И конечно же, в рамках GitOps нужно добавить CI, чтобы автоматически изменения исполнялись.

  1. Как его хранить в Git?
  2. Как прикрутить CI?
  3. Если Gitlab мы тоже разворачиваем с помощью IaC, да еще и в Kubernetes?
  4. И GitLab Runner тоже в Kubernetes?
  5. А Kubernetes в облачном провайдере?
Что появилось раньше: GitLab, на который я загружу свой код, или код, описывающий то, какой GitLab мне нужен?

Курица с яйцами

Попробуем приготовить блюдо, используя в качестве облачного провайдера Managed Kubernetes Selectel.

TL;DR

А можно, чтобы сразу и в одну команду?

$ export MY_SELECTEL_TOKEN=<token>
$ curl https://gitlab.com/chicken-or-egg/mks/make/-/snippets/2002106/raw | bash

Ингредиенты:

  • Аккаунт от my.selectel.ru;
  • Токен от аккаунта;
  • Навыки Kubernetes;
  • Навыки Helm;
  • Навыки Terraform;
  • Helm chart GitLab;
  • Helm chart GitLab Runner.

Рецепт:

  1. Получить MY_SELECTEL_TOKEN из панели my.selectel.ru.
  2. Создать кластер Kubernetes, передав в него токен от аккаунта.
  3. Получить KUBECONFIG от созданного кластера.
  4. Установить GitLab в Kubernetes.
  5. Получить GitLab-token от созданного GitLab для пользователя root.
  6. Создать структуру проектов в GitLab, используя GitLab-token.
  7. Запушить имеющийся код в GitLab.
  8. ???
  9. Profit!

Шаг 1. Токен можно получить в разделе Ключи API.

Шаг 2. Подготавливаем наш Terraform для «запекания» кластера из 2 нод. Если вы уверены в том, что у вас хватит на все ресурсов, то можно включить автоквоты:

provider "selectel" {
 token = var.my_selectel_token
}

variable "my_selectel_token" {}
variable "username" {}
variable "region" {}


resource "selectel_vpc_project_v2" "my-k8s" {
 name = "my-k8s-cluster"
 theme = {
   color = "269926"
 }
 quotas {
   resource_name = "compute_cores"
   resource_quotas {
     region = var.region
     zone = "${var.region}a"
     value = 16
   }
 }
 quotas {
   resource_name = "network_floatingips"
   resource_quotas {
     region = var.region
     value = 1
   }
 }
 quotas {
   resource_name = "load_balancers"
   resource_quotas {
     region = var.region
     value = 1
   }
 }
 quotas {
   resource_name = "compute_ram"
   resource_quotas {
     region = var.region
     zone = "${var.region}a"
     value = 32768
   }
 }
 quotas {
   resource_name = "volume_gigabytes_fast"
   resource_quotas {
     region = var.region
     zone = "${var.region}a"
     # (20 * 2) + 50 + (8 * 3 + 10)
     value = 130
   }
 }
}

resource "selectel_mks_cluster_v1" "k8s-cluster" {
 name         = "k8s-cluster"
 project_id   = selectel_vpc_project_v2.my-k8s.id
 region       = var.region
 kube_version = "1.17.9"
}

resource "selectel_mks_nodegroup_v1" "nodegroup_1" {
 cluster_id        = selectel_mks_cluster_v1.k8s-cluster.id
 project_id        = selectel_mks_cluster_v1.k8s-cluster.project_id
 region            = selectel_mks_cluster_v1.k8s-cluster.region
 availability_zone = "${var.region}a"
 nodes_count       = 2
 cpus              = 8
 ram_mb            = 16384
 volume_gb         = 15
 volume_type       = "fast.${var.region}a"
 labels            = {
   "project": "my",
 }
}

Добавляем пользователя в проект:

resource "random_password" "my-k8s-user-pass" {
 length = 16
 special = true
 override_special = "_%@"
}

resource "selectel_vpc_user_v2" "my-k8s-user" {
 password = random_password.my-k8s-user-pass.result
 name = var.username
 enabled  = true
}

resource "selectel_vpc_keypair_v2" "my-k8s-user-ssh" {
 public_key = file("~/.ssh/id_rsa.pub")
 user_id    = selectel_vpc_user_v2.my-k8s-user.id
 name = var.username
}

resource "selectel_vpc_role_v2" "my-k8s-role" {
 project_id = selectel_vpc_project_v2.my-k8s.id
 user_id    = selectel_vpc_user_v2.my-k8s-user.id
}

Выходные данные:

output "project_id" {
 value = selectel_vpc_project_v2.my-k8s.id
}

output "k8s_id" {
 value = selectel_mks_cluster_v1.k8s-cluster.id
}

output "user_name" {
 value = selectel_vpc_user_v2.my-k8s-user.name
}

output "user_pass" {
 value = selectel_vpc_user_v2.my-k8s-user.password
}

Запускаем:

$ env \
TF_VAR_region=ru-3 \
TF_VAR_username=diamon \
TF_VAR_my_selectel_token=<token> \
terraform plan -out planfile

$ terraform apply -input=false -auto-approve planfile

Шаг 3. Получаем кубконфиг.

Чтобы программно скачать KUBECONFIG, нужно получить токен от OpenStack:

openstack token issue -c id -f value > token

И уже с этим токеном сделать запрос в Managed Kubernetes Selectel API. k8s_id выдает terraform:

curl -XGET -H "x-auth-token: $(cat token)" "https://ru-3.mks.selcloud.ru/v1/clusters/$(cat k8s_id)/kubeconfig" -o kubeConfig.yaml

Кубконфиг также можно получить через панель.

Шаг 4. После того, как кластер запекся и у нас есть к нему доступ, можно добавить сверху yaml по вкусу.

Я предпочитаю добавлять:

  • namespace,
  • storage class,
  • pod security policy и прочее.

Storage Class для Selectel можно брать из официального репозитория.

Так как изначально я выбрал кластер в зоне ru-3a, то и Storage Class мне нужен из этой зоны.

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
 name: fast.ru-3a
 annotations:
   storageclass.kubernetes.io/is-default-class: "true"
provisioner: cinder.csi.openstack.org
parameters:
 type: fast.ru-3a
 availability: ru-3a
allowVolumeExpansion: true

Шаг 5. Ставим балансировщик нагрузки.

Будем использовать стандартный для многих nginx-ingress. Инструкций по его установке уже предостаточно, так что не будем на этом задерживаться.

$ helm repo add nginx-stable https://helm.nginx.com/stable
$ helm upgrade nginx-ingress nginx-stable/nginx-ingress -n ingress --install -f ../internal/K8S-cluster/ingress/values.yml

Ждем, когда он получит внешний IP примерно 3-4 минуты:

Получили внешний IP:

Шаг 6. Устанавливаем GitLab.

$ helm repo add gitlab https://charts.gitlab.io
$ helm upgrade gitlab gitlab/gitlab -n gitlab  --install -f gitlab/values.yml --set "global.hosts.domain=gitlab.$EXTERNAL_IP.nip.io"

Снова ждем, когда все поды поднимутся.

kubectl get po -n gitlab
NAME                                      	READY   STATUS  	RESTARTS   AGE
gitlab-gitaly-0                           	0/1 	Pending 	0      	0s
gitlab-gitlab-exporter-88f6cc8c4-fl52d    	0/1 	Pending 	0      	0s
gitlab-gitlab-runner-6b6867c5cf-hd9dp     	0/1 	Pending 	0      	0s
gitlab-gitlab-shell-55cb6ccdb-h5g8x       	0/1 	Init:0/2	0      	0s
gitlab-migrations.1-2cg6n                 	0/1 	Pending 	0      	0s
gitlab-minio-6dd7d96ddb-zd9j6             	0/1 	Pending 	0      	0s
gitlab-minio-create-buckets.1-bncdp       	0/1 	Pending 	0      	0s
gitlab-postgresql-0                       	0/2 	Pending 	0      	0s
gitlab-prometheus-server-6cfb57f575-v8k6j 	0/2 	Pending 	0      	0s
gitlab-redis-master-0                     	0/2 	Pending 	0      	0s
gitlab-registry-6bd77b4b8c-pb9v9          	0/1 	Pending 	0      	0s
gitlab-registry-6bd77b4b8c-zgb6r          	0/1 	Init:0/2	0      	0s
gitlab-shared-secrets.1-pc7-5jgq4         	0/1 	Completed   0      	20s
gitlab-sidekiq-all-in-1-v1-54dbcf7f5f-qbq67   0/1 	Pending 	0      	0s
gitlab-task-runner-6fd6857db7-9x567       	0/1 	Pending 	0      	0s
gitlab-webservice-d9d4fcff8-hp8wl         	0/2 	Pending 	0      	0s
Waiting gitlab
./wait_gitlab.sh ../internal/gitlab/gitlab/.pods
waiting for pod...
waiting for pod...
waiting for pod...

Поды поднялись:

Шаг 7. Получаем GitLab-token.

Сначала узнаем пароль для входа:

kubectl get secret -n gitlab gitlab-gitlab-initial-root-password -o jsonpath='{.data.password}' | base64 --decode 

Теперь авторизуемся и получим токен:

python3 get_gitlab_token.py root $GITLAB_PASSWORD http://gitlab.gitlab.$EXTERNAL_IP.nip.io

Шаг 8. Приводим Git-репозитории к правильной иерархии с помощью Gitlab Provider.

cd ../internal/gitlab/hierarchy && terraform apply -input=false -auto-approve planfile

К сожалению, в terraform GitLab provider есть плавающий баг. Тогда придется удалить конфликтующие проекты руками, чтобы tf.state починился. Затем перезапустите команду $ make all

Шаг 9. Переносим локальные репозитории на сервер.

$ make push

[master (root-commit) b61d977] 😍 Initial commit
 3 files changed, 46 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 values.yml
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 770 bytes | 770.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)

Готово:

Заключение

Мы добились того, что с нашей локальной машины можем декларативно управлять всем. Теперь хочется перенести все эти задачи в CI и только кнопочки нажимать. Для этого нужно передать наши локальные состояния (Terraform state) в CI. О том, как это сделать, в следующей части.

Что еще почитать по теме

Кирилл Филипенко 14 сентября 2022

Увеличиваем FPS в аниме с помощью нейросети и GPU Tesla T4

Рассказываем про технологию интерполяции и ее практическое применение с помощью облачных серверов с GPU.
Кирилл Филипенко 14 сентября 2022
Ульяна Малышева 25 августа 2022

CDN против DDoS-атак: в каких случаях это действительно работает

Рассказываем про неочевидное преимущество CDN — услуга повышает безопасность инфраструктуры за счет защиты от DDoS-атак.
Ульяна Малышева 25 августа 2022
T-Rex 24 августа 2022

IT-инфраструктура организации: понятие, типы и функции

Рассказываем об IT-инфраструктуре предприятия и ее компонентах для малого, среднего и крупного бизнеса.
T-Rex 24 августа 2022

Новое в блоге

Михаил Фомин 24 июня 2022

Docker Swarm VS Kubernetes — как бизнес выбирает оркестраторы

Рассказываем, для каких задач бизнесу больше подойдет Docker Swarm, а когда следует выбрать Kubernetes.
Михаил Фомин 24 июня 2022
Ульяна Малышева 30 сентября 2022

«Нулевой» локальный диск. Как мы запустили облако только с сетевыми дисками и приручили Ceph

Чем хороши сетевые диски и почему именно Ceph, рассказал директор по развитию ядра облачной платформы Иван Романько.
Ульяна Малышева 30 сентября 2022
Валентин Тимофеев 30 сентября 2022

Как проходит онбординг сотрудников ИТО? Что нужно, чтобы выйти на смену в дата-центр

Рассказываем, как обучаем новых сотрудников, какие задачи и испытания проходят инженеры прежде, чем выйти на свою первую смену.
Валентин Тимофеев 30 сентября 2022
T-Rex 28 сентября 2022

Книги по SQL: что почитать новичкам и специалистам

Собрали 6 книг, которые помогут на старте изучения SQL и при углублении в тему.
T-Rex 28 сентября 2022