August 23, 2022

HAQQ. Kubernetes guide

Hi !

Сегодня мы рассмотрим установку и настройку валидатора для проекта haqq.

Это будет не типичный гайд с набором однострочников, копипастом которых вы сможете развернуть валидатора.
Однако из данного гайда вы сможете немного попрактиковаться в разворачивании проекта в kubernetes.
Cтоит понимать что наш гайд не будет затрагивать множества тонкостей и предполагает наличия базовых знаний по контейнерам (docker)
За время инсталляции, нам придётся пройтись по следующим основным шагам

  1. Развертывание kubernetes кластера в облачном провайдер при помощи terraform.
  2. Развертывание приложения.
  3. Запуск валидатора
  4. Удаление кластера

И хотя существенная часть шагов будет специфична для проекта, адаптировать полученный опыт вы сможете и для множества других ваших проектов.

Подготовка

Мы будем разворачивать наш кластер в европейском провайдере scaleway
Для начала нам потребуется скачать и установить terraform
Сделать это вы можете практически на любой операционной системе.
Здесь можно найти официальный урок, как это делается
https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started

Мы в качестве примера будем использовать ручную установку для macos/linux.

  1. Переходим на страницу загрузки https://www.terraform.io/downloads
  2. Выбираем операционную систему и архитектуру, и скачиваем версию.
  3. Копируем скачаный файл в директорию /usr/local/bin

    mv ~/Downloads/terraform /usr/local/bin
  4. Добавляем права на запуск

    chmod 755 /usr/local/bin/terraform
  5. Проверяем что наш скачанный бинарник запускается, и показывает версию

    terraform version

Развертывание kubernetes кластера

В качестве оператора k8s мы возьмём облачного провайдера scaleway
Он отличается довольно демократичными ценами, среди тех операторов которые предоставляют услугу k8s как сервис.

Для начала создадим директорию в которой будет храниться вся наша конфигурация.

mkdir haqq-tf
cd haqq-tf

Далее, мы всегда будем работать внутри этой директории

Для начала нам нужно подключить модуль terraform который позволяет нам управлять облаком scaleway.
Модуль этот является официальным и поддерживается самим scaleway и найти документацию по нему можно здесь
https://registry.terraform.io/providers/scaleway/scaleway

1. Получаем конфигурационный файл нашего оператора
Для начала нам нужно зарегистрироваться, и подтвердить свою почту, надеюсь у вас не возникнет с этим проблем.
Следующем шагом, нам необходимо будет сгенерировать ключи доступа для terraform.
Делается это в профиле, в разделе credentials

Здесь нажимаем "generate api key", вводим название, и из появившегося окна с ключами, cохраняем access_key и secret_key.


Очень важно их сохранить, потому что посмотреть их повторно будет нельзя.
Так же нельзя допустить утечку этих ключей, иначе злоумышленники, смогут за ваш счёт создать кучу серверов от вашего имени и оператор может выставить вам некислый счёт.
Также отсюда следует сохранить project_id он нам потребуется в дальнейшем.

2. Создаем файл variables.tf, где пропишем наши только что полученные ключи.

variable "project_id" {
  default = "87432-d326-0474-91b8-d52a6789ccc5"
}
variable "access_key" {
  default = "SCW7141234562F0W60"
}
variable "secret_key" {
  default = "0dde9e8d-8e5a-4b5e-9499-da4c17bd5fc6"
}

3. Создаём файл с описанием модулей, назовём его providers.tf

terraform {
  required_providers {
    scaleway = {
      source = "scaleway/scaleway"
      version = "2.2.8"
    }
  }
}

provider "scaleway" {
  project_id = var.project_id
  access_key = var.access_key
  secret_key = var.secret_key
  zone   = "fr-par-1"
  region = "fr-par"
}

Мы делаем регионом по умолчанию Францию, вы можете выбрать любой другой регион, со списком можно ознакомиться здесь https://registry.terraform.io/providers/scaleway/scaleway/latest/docs/guides/regions_and_zones

4. Переходим к описанию нашего кластера.
Для начала нам потребуется описать сам кластер кубернетеса.
Создадим файл с его описанием kubernetes.tf

resource "scaleway_k8s_cluster" "haqq" {
  name    = "haqq"
  version = "1.24.3"
  cni     = "calico"
}

Далее опишем ноды, которые будут в нашем кластере в файле kubernetes-nodes.tf

resource "scaleway_k8s_pool" "haqq-nodes1" {
  cluster_id  = scaleway_k8s_cluster.haqq.id
  name        = "nodes1"
  node_type   = "DEV1-L"
  size        = 1
  autoscaling = false
  autohealing = true
  min_size    = 1
  max_size    = 1
}

Здесь я обращу внимание на параметры.
node_type - это тип серверов которые мы добавляем в кластер.
Scaleway предоставляет множество различных узлов, и вы их можете комбинировать в своём кластере.
Актуальные названия инстансов можно посмотреть здесь https://www.scaleway.com/en/docs/faq/instances/
size - количество серверов которые мы создаем в кластере
min_size / max_size - в нашем случае равны size поскольку мы не используем автоматическое масштабирование кластера.

5. Мы готовы начинать !
Попробуем развернуть наш кластер.
Для этого, нам нужно произвести инициализацию конфигурации

terrafrom init

Далее попробуем сделать plan, то есть посмотрим, какие изменения попытается сделать наша созданная конфигурация.

terrafrom plan

Если план проходит без проблем, попробуем развернуть наш кластер

terraform apply

После этого вас попросят ввести yes подтвердив создание кластера.

После этого вы должны увидеть примерно следующий вывод в консоли

scaleway_k8s_cluster.haqq: Creating...
scaleway_k8s_cluster.haqq: Creation complete after 6s [id=fr-par/505b2785-9d2f-4249-90d1-216190f1ef41]
scaleway_k8s_pool.haqq-pool-1: Creating...
scaleway_k8s_pool.haqq-pool-1: Still creating... [10s elapsed]
scaleway_k8s_pool.haqq-pool-1: Still creating... [20s elapsed]
.....
.....
scaleway_k8s_pool.haqq-pool-1: Still creating... [5m41s elapsed]
scaleway_k8s_pool.haqq-pool-1: Still creating... [5m51s elapsed]
scaleway_k8s_pool.haqq-pool-1: Creation complete after 5m59s [id=fr-par/730c89aa-99bb-494b-85e1-b2f6f22fc671]

В веб интерфейсе scaleway в разделе Project Dasboard должна появится информация о только что созданных кластере и инстансе.

Развертывание приложения

Теперь мы можем приступить к развертыванию самого приложения.

Но перед тем как мы начнём писать конфигурацию, нам необходимо сконфигурировать terraform для того чтобы он умел работать с kubernetes

Для этого мы возьмём официальный модуль для работы с kubernetes, документацию по нему вы можете найти здесь https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment

Чтобы подключить данный модуль, нам необходимо в файле providers.tf в секции requred_providers добавить нового провайдера

kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "2.13.0"
}

По аналогии, как мы это делали с провайдером scaleway

Далее, нам необходимо сконфигурировать этого провайдера. Сделать мы это можем также по аналогии с тем как конфигурировали scaleway, добавив в том же самом файле providers.tf конфигурацию вида

provider "kubernetes" {
  host                   = scaleway_k8s_cluster.haqq.kubeconfig.host
  token                  = scaleway_k8s_cluster.haqq.kubeconfig.token
  cluster_ca_certificate = scaleway_k8s_cluster.haqq.kubeconfig.cluster_ca_certificate
}

Мы не будем вдаваться в подробности описания конфигурации, вы всегда можете почитать документацию модуля, по тому что все эти параметры означают.

Теперь вернемся к нашим файлам конфигурации приложения

Нам понадобится описать ряд компонентов kubernetes которые и будут отвечать за наше приложение.

У нас это будут

  1. StatefulSet- собственно сам docker контейнер с конфигурацией нашего сервиса и томом, в котором будут хранится наши данные.
  2. ConfigMap - объект хранящий скрипт инициализации для первичного запуска.

Для начала напишем скрипт инициализации сети, и положим его в файл k8s_init.sh

#!/bin/bash

NETWORK=haqq_53211-1
MONIKER=beething

if [ -f "/root/.haqqd/haqq-k8s-init" ]; then
    echo already init
else
    haqqd config chain-id $NETWORK
    haqqd init $MONIKER -o --chain-id $NETWORK
    touch /root/.haqqd/haqq-k8s-init
fi

if [ -f "/root/.haqqd/haqq-k8s-genesis" ]; then
    echo already downloaded
else
    wget https://storage.googleapis.com/haqq-testedge-snapshots/genesis.json -O /root/.haqqd/config/genesis.json
    touch /root/.haqqd/haqq-k8s-genesis
fi

if [ -f "/root/.haqqd/haqq-k8s-state-sync" ]; then
    echo state-sync already configured
else
    wget https://raw.githubusercontent.com/haqq-network/testnets/main/TestEdge/state_sync.sh -O state_sync.sh
    bash state_sync.sh
    touch /root/.haqqd/haqq-k8s-state-sync
fi

Далее, создадим объект configMap который будет содержать наш скрипт.

Для простоты создадим файл haqq.tf и всю конфигурацию компонентов kubernetes будем описывать в нём

resource "kubernetes_config_map" "haqq-init" {
  metadata {
    name = "haqq-init"
  }

  data = {
    "haqq_init.sh" = "${file("${path.module}/k8s_init.sh")}"
  }

}

Далее опишем наш StatefulSet

resource "kubernetes_stateful_set" "haqq" {
  metadata {
    name   = "haqq-node"
    labels = { test = "haqq-node" }
  }

  spec {
    replicas     = 1
    service_name = "haqq"
    selector { match_labels = { test = "haqq-node" } }

    template {
      metadata { labels = { test = "haqq-node" } }

      spec {

        volume {
          name = "haqq-init"
          config_map {
            name = "haqq-init"
          }
        }

        init_container {
          name    = "init"
          image   = "alhaqq/haqq:v1.0.3"
          command = ["/bin/bash", "-c", "bash /root/haqq_init.sh"]

          volume_mount {
            name       = "haqq-init"
            mount_path = "/root/haqq_init.sh"
            sub_path   = "haqq_init.sh"
            read_only  = true
          }

          volume_mount {
            name       = "haqq-data"
            mount_path = "/root/.haqqd"
          }
        }

        container {
          image   = "alhaqq/haqq:v1.0.3"
          name    = "haqq-node"
          command = ["haqqd", "start", "--x-crisis-skip-assert-invariants"]

          resources {
            limits   = { cpu = "1", memory = "4G" }
            requests = { cpu = "1", memory = "4G" }
          }

          volume_mount {
            name       = "haqq-data"
            mount_path = "/root/.haqqd"
          }

        }
      }
    }

    volume_claim_template {
      metadata {
        name = "haqq-data"
      }

      spec {
        access_modes = ["ReadWriteOnce"]

        resources {
          requests = {
            storage = "10G"
          }
        }
      }
    }

  }
}

На этом моменте, мы уже можем запускать нашу ноду !

Поскольку мы добавили новые модули, делаем повторно terraform initи terraform apply

После того как все применилось нам необходимо убедиться, что все запустилось нормально. Для этого нам понадобиться утилита для доступа к нашему кластеру - kubectl

Как установить kubectl для вашей операционной системы вы можете найти в официальной документации Install Tools | Kubernetes

Мы же снова рассмотрим пример инсталяции для macOS.

Всё по аналогии с terraform потому не буду описывать что делает каждая команда

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
chmod +x ./kubectl 
sudo mv ./kubectl /usr/local/bin/kubectl
sudo chown root: /usr/local/bin/kubectl
kubectl version --client

Далее, нам нужно получить конфигурацию для доступа в кластер, сделать это мы можем опять же при помощи terraform.

Модуль scaleway который помогал нам создать кластер, предоставляет нам функционал, позволяющий получить его.

Воспользуемся им и опишем следующий ресурс

resource "local_file" "haqq-kubeconfig" {
  content  = scaleway_k8s_cluster.haqq.kubeconfig.0.config_file
  filename = "haqq.kubeconfig"
}

Далее чтобы им воспользоваться, необходим прописать в переменную окружения KUBECONFIG путь до нашего конфига.

export KUBECONFIG=haqq.kubeconfig

После чего мы можем выполнить

kubect get pods

И увидим примерно следующую картинку

NAME          READY   STATUS    RESTARTS   AGE
haqq-node-0   1/1     Running   0          23m

Значение 1/1 означает что наш узел запущен и работает

Его логи можно посмотреть командой

kubectl logs -f haqq-node-0

Вот и всё ! Мы запустили ноду, теперь можно приступать к созданию валидатора !

Запуск валидатора

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

Мы просто зайдём в контейнер нашей ноды и зарегистрируем валидатора.

Для начала проверим, что нода синхронизировалась.

Сделаем это зайдя внутрь ноды при помощи kubectl

Имя контейнера можно взять из вывода, который мы делали в конце предыдущего этапа. В нашем случае это haqq-node-0

kubectl exec -it haqq-node-0 bash

Оказавшись внутри контейнера делаем

haqqd status | jq ."SyncInfo".catching_up

Если значение false значит нода успешно синхронизирована.

Перед активацией, забираем необходимые токены с крана на свой кошелек, и добавляем это кошелек на ноде из нашей seed фразы

haqqd keys add <key_name> --recover

Далее можем регистрировать валидатора !

haqqd tx staking create-validator \
  --amount=1000000000000aISLM \
  --pubkey=$(haqqd tendermint show-validator) \
  --moniker=<your_moniker_name> \
  --chain-id=<chain_id> \
  --commission-rate="0.10" \
  --commission-max-rate="0.20" \
  --commission-max-change-rate="0.01" \
  --min-self-delegation="1000000" \
  --gas="auto" \
  --gas-prices="0.025aISLM" \
  --from=<key_name> \
  --node https://rpc.tm.testedge.haqq.network:443

Далее подтверждаем транзакцию и готово !

Далее как и в шаге выше, можете проверить что в логах всё хорошо

kubectl logs -f haqq-node-0

Удаление кластера

Для того чтобы удалить весь кластер, со всеми виртуалками, данными и т д, достаточно ввести всего лишь одну команду

terraform destroy