yandex-cloud / terraform-provider-yandex

Terraform Yandex provider
https://www.terraform.io/docs/providers/yandex/
Mozilla Public License 2.0
208 stars 114 forks source link

Работа с yandex_mdb_postgresql_cluster, вопросы и предложения по работе с ресурсом провайдера #79

Open asterix201 opened 4 years ago

asterix201 commented 4 years ago

Приветствую!

Мы сейчас активно стараемся вести управление инфраструктурой в Я.Облаке с помощью терраформа и переносить разнообразные костыли и велосипеды, которые придумывали ранее для появляющихся ресурсов в провайдере.

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

Возможно это только наша специфика использования и она не особо востребована, однако я решил поделиться. Вдруг и нам поможет :)

Общая суть в том, что у нас есть набор кластеров для проектов и в каждом кластере набор баз для этого проекта. Т.е.: проект1 - кластер1 - [база-приложения1, база-приложения2, ..., база-приложенияn] проект2 - кластер2 - [база-приложения21, база-приложения22, ..., база-приложения2n] и т.д.

Доступы к базам разграничены - на каждую базу свой пользователь.

Все по очень простой причине - много небольших микросервисов и даже минимальный кластер один сервис не загружает, чтобы для каждого приложения создавать отдельный кластер баз.

С чем я столкнулся и что хотелось бы починить, поменять(если возможно) или понять как предполагается использовать (может нам в подходе надо что-то изменить).

  1. При импорте существующего кластера провайдер предлагает пересоздать всех пользователей и почти все базы. По этому поводу даже тикет в саппорте есть. При тестах выяснился любопытный ньюанс (про почти все базы) - базы без установленных расширений пересоздавать провайдер не предлагает. Если есть расширения, и даже если они указаны в описании, все равно настаивает на пересоздании.

  2. Описывать кучу повторяющихся блоков довольно неудобно. К тому же когда неизвестно сколько их может быть - это про user/database/permission/extension блоки. Тут очень хочется использовать конструкцию dynamic блоков из терраформа. И вынести создание базы в отдельный модуль, например, с доп. ресурсами необходимыми. Однако я столкнулся с проблемой, когда описываю ресурс используе dynamic блоки, то провайдер предлагает каждый раз пересоздавать все ресурсы - и базы и пользователей (все, что разворачивается из dynamic). Что крайне странно и непонятно. При любом изменении или не изменении ресурса.

  3. Оставили идею использования dynamic когда-нибудь в будущем и описали всех пользователей и базы в одном большом ресурсе. И тут все равно возникла проблема - при изменении расширений базы в блоке database -> extension провайдер хочет пересоздать базу. Т.е. удалить и создать новую, хотя эта операция не столь радикальна и расширения ставятся/убираются из консоли/командной строки/базы без таких методов.

  4. Не хватает большей информации о создаваемых ресурсах (IP адреса хостов кластера или возможности их получить из какой-либо даты). Непосредственно IP адреса нужны например для конфигурирования провайдера postgres. По хосту он не работает. Вообще с postres провайдером есть свои особенности из-за того, что он проверяет все подключения еще на этапе plan. И все пользователи и доступы должны быть созданы до его использования. В свою очередь постгрес провайдер нужен для очень многих вещей - от выдачи прав в базе для пользователя (например на чтение), до изменения конфигурации базы/пользователей которые есть в веб-консоли/апи, но которые нельзя указать через яндекс терраформ провайдер.

  5. Про пользователей. Вопрос в целом описан в #78 и вызван способом нашего использования баз и доступов. При наличии большого количества небольших баз и пользователей дефолтные значения того же connection limit не подходят. Однако указать их сейчас нет возможности и какие-то базовые параметры для пользователей тоже указать нет возможности (хотя для баз локаль хотя бы есть :) ) Можно делать многоходовку - создавая пользователя провайдером Я.Облака, а потом провайдером postgres конфигурировать, если бы не несколько ньюансов. Например, если пользователей уже очень много с лимитами по 10-15 коннектов и, к примеру, осталось 20-30 свободных коннектов в базе - пользователя уже не получится автоматически сиздать. Надо будет либо менять пользователей, уменьшая им пул подключений (что чревато), а потом создавать пользователя и все обратно откатывать. Либо создавать пользователя руками а потом.... а потом его невозможно импортировать в терраформе.

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

  7. Еще про пользователей и их пароли. Очень странно выглядит работа с пользователями, если им менять пароль (или импортировать базу и потом прогнать plan) - провайдер предлагает каждый раз пересоздавать пользователя. При любом изменении. Что тоже не очень хорошо и, возможно, было бы все равно (это как с секретами волта, там терраформ ничего о состоянии секретов не знает и каждый раз их пересоздает), если бы не вероятность потерать все настройки для пользователя из-за удаления и пересоздания. А это означает потери как настроек со стороны managed баз, так и внутри, которые делались postgres провайдером.

Часть из описанного выше - технические ошибки, которые (я надеюсь) поправят. Другая часть - не ошибки, а относятся к концепции управления пользователями, базами и кластером у вас в облаке. И если с ошибками можно только ждать исправления, то с концепциями я кощунственно предлагаю пересмотреть и всех поделить :)

У вас даже в API (по документации ) ресурсы/объекты кластер БД, база данных и пользователь разделены и могут использоваться достаточно независимо. Я, когда городил прошлым летом свой велосипед для создания баз при помощи терраформа, использовал эту идею как основную - отдельно описывается кластер, отдельно пользователь, отдельно база и все их нужные мне параметры.

Так может быть имеет смысл разделить их и у вас в провайдере?

Сделать их отдельными ресурсами postgres_cluster, postgres_user, postgres_database. Тогда (возможно) будет проще добавить тот функционал коотрый сейчас есть в api/веб-консоли/командной строке отдельно для каждого из этих ресурсов, их отдельно можно настраивать. Их так же по отдельности или все вместе можно вынести будет в модуль(и) (если захочется все одним блоком описать).

asalimonov commented 4 years ago

Привет @asterix201,

Проблемы из п1, п3, п5, п7 похоже имеют одну причину - баг в сравнении состояний, из-за этого Terraform и пытается пересоздать сущности. Добавили в бэклог следуюшего спринта.

п4: IP адрес не планируем добавлять в output так как:

Использования провайдера PostgreSQL (не Managed PostgreSQL) в случае многохостовых кластеров осложняется тем, что в output нет информации о том какой из хостов сейчас мастер, чтобы там через SQL что-то изменить. Кроме того интерфейс PostgreSQL и API Облака могут иметь совершенно разные сетевые доступы, что может запутать пользователей.

п6. Для того, чтобы не хранить в коде пароль можно воспользоватьcя random провайдером, пример как это сделать для нескольких пользователей можно посмотреть тут.

Настройки пользователей мы конечно собираемся добавить, но пока не можем точно назвать сроки. На счет разделения на ресурсы есть определенные сомнения, так как это увеличит итоговую сложность кода и провайдера, и модулей пользователей, где основной случай 1-3 базы и столько же пользователей.

asterix201 commented 4 years ago

@asalimonov, спасибо за ответ!

Про IP адрес - я согласен, что его отдавать это совсем не лучшая идея, мы подобные запросы про IP адреса сервисов у себя пресекаем, чтобы пользовались именами. Однако я, пока, не могу придумать еще решения (коме как руками такие запросы делать).

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

Чтобы это автоматизировать можно использовать провайдер PostgreSQL. И вот тут начинаются танцы с бубном:

С последним была довольно интересная идея использовать DNS Provider, чтобы им получать IP мастера и дальше уже применять настройки. Но не получилось - CI/CD живут в отдельной от других сред сети (да и разные среды выполнения в разных сетях).

Может быть есть возможность в API облака, или сам сервис добавить возможность выдачи дополнительных прав пользователю (помимо CONNECT)?

С примером - спасибо большое! Я пробовал через count делать и получалось "не очень". Я бы в примеры предложил добавить - хорошо получается :) Там сейчас при нескольких пользователях один и тот же пароль получаться будет, если генерировать random_passwordом.

asterix201 commented 4 years ago

Есть еще вопрос.

Возможно ли провайдером отдавать cname? Я знаю, что он составляется по описанию в документации как c-.rw.mdb.yandexcloud.net , но может возможно уже полный cname получать про создании ресурса?

asalimonov commented 4 years ago

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

Мы в курсе этой проблемы, пока думаем как сделать более удобное решение. Генерализованный вариант решения, в т.ч. вне контекста данного случая - прокидывать внутрь периметра задачи для некоего воркера, который имеет доступ к внутренним ресурсам.

С последним была довольно интересная идея использовать DNS Provider, чтобы им получать IP мастера и дальше уже применять настройки. Но не получилось - CI/CD живут в отдельной от других сред сети (да и разные среды выполнения в разных сетях).

Да, это ровно таже самая проблема.

Я бы в примеры предложил добавить - хорошо получается :) Там сейчас при нескольких пользователях один и тот же пароль получаться будет, если генерировать random_passwordом.

Ага, обновлю как-нибудь на днях.

Возможно ли провайдером отдавать cname? Я знаю, что он составляется по описанию в документации как c-.rw.mdb.yandexcloud.net , но может возможно уже полный cname получать про создании ресурса?

На мой взгляд это избыточно. Конкатенация строк поддерживается в HCL, то есть можно самостоятельно сделать output с таким результатом.

pcheliniy commented 4 years ago

Добавлю к стартовым пунктам про пересоздание. Тоже самое случается и при изменении блоков host. Если я правильно пониманию механику, то проблема в том, что в state набор хостов это просто массив объектов без каких либо "уникальных" идентификаторов по которым их можно сравнить с планом. В результате когда мы к примеру удаляем 1 из 3 хостов, то у нас просто 2 элемента нового плана сравниваются с 3мя элементами старого стейта, без учёта какого либо идентификатора(да и непонятно как их сравнивать то, если в subnet + zone будет несколько инстансов) И получается такая картинка

~ host { assign_public_ip = false fqdn = "rc1a-6hgfqeq80tozgzd.mdb.yandexcloud.net" ~ subnet_id = "e9bb0l6rolpms6g2m4sl" -> "e2l3rsvsc3q9l4dr4j8v" ~ zone = "ru-central1-a" -> "ru-central1-b" } ~ host { ~ assign_public_ip = false -> true fqdn = "rc1b-hyx7v9tbym3xefq.mdb.yandexcloud.net" ~ subnet_id = "e2l3rsvsc3q9l4dr4j8v" -> "b0cd4m938uvgqa2iqfg0" ~ zone = "ru-central1-b" -> "ru-central1-c" }

  • host {
  • assign_public_ip = true -> null
  • fqdn = "rc1c-7kmirtiks6d3fu2.mdb.yandexcloud.net" -> null
  • subnet_id = "b0cd4m938uvgqa2iqfg0" -> null
  • zone = "ru-central1-c" -> null }

Будем рады если вы сможете найти решение или предложить какой то workaround.

pcheliniy commented 3 years ago

any news?

superbotan commented 3 years ago

Приветствую!

Есть хорошие новости. Добавили большой объём изменений (https://github.com/yandex-cloud/terraform-provider-yandex/blob/master/CHANGELOG.md)

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

superbotan commented 3 years ago

п5 для пользователя теперь можно выставлять настройки ограничивающие количество подключений

Для пользователя теперь доступны следующие настройки:

The settings block supports: Full description https://cloud.yandex.com/docs/managed-postgresql/grpc/user_service#UserSettings

pcheliniy commented 3 years ago

@superbotan Я протестировал последнюю версию С хостами, при попытке удалить непоследний хост из списка, получаем следующего рода diff

      ~ host {
          ~ subnet_id        = "e2l4u9j29cal2vo8i854" -> "b0cclpfber2u5dni60um"
          ~ zone             = "ru-central1-b" -> "ru-central1-c"
            # (4 unchanged attributes hidden)
        }
      - host {
          - assign_public_ip = false -> null
          - fqdn             = "rc1c-gnl9r2xuxqt262cg.mdb.yandexcloud.net" -> null
          - priority         = 0 -> null
          - role             = "REPLICA" -> null
          - subnet_id        = "b0cclpfber2u5dni60um" -> null
          - zone             = "ru-central1-c" -> null
        }

Тоесть ситуация особо не поменялась, НО на самом деле хост никуда не перемещается, а всё остаётся как и должно было, без лишних пересозданий.

Таже самая история с отображением стейтов для db/users. Только есть небольшое отличие. Для db также отображается что объекты будут перемещены, но всё остаётся на своих местах.

А вот пользователи (кажется) начинают переименовываться, в соответствии с тем, как это указано в стейте. В логе операций в веб интерфейсе мы видим записи вида Modify user in PostgreSQL cluster К сожалению нет возможности на данный момент приводит ли это к каким то проблемам доступности,если сервис в этот момент работает, но полагаю что может быть.

superbotan commented 3 years ago

@pcheliniy, Добрый день! по первому пункту из первого сообщения: При импорте существующего кластера провайдер предлагает пересоздать всех пользователей и почти все базы. По этому поводу даже тикет в саппорте есть. При тестах выяснился любопытный ньюанс (про почти все базы) - базы без установленных расширений пересоздавать провайдер не предлагает. Если есть расширения, и даже если они указаны в описании, все равно настаивает на пересоздании.

Тут фактически 3 проблемы: 1) Порядок ресурсов приходящих из облака не совпадает с порядком описанным в tf файле. Так как в провайдере используются списки, а терраформ сравнивает для списков в том числе порядок, возникает отображение изменения. Оно исправляется либо применением, либо изменением порядка в tf файле. Применение изменения для БД и для Хостов согласно коду и моим экспериментам, не приводит к внесению изменений в облако. В состояние terraform.tfstate же будет записан верный порядок, не вызывающий diff в terraform. 2) Пароль пользователей. Пароли - секретная информация и она не возвращается из облака. По этому получив состояние из облака состояние будет с не заполненными паролями. Исправление: или применить изменение и тогда будет внесено изменение в облако (переопределится пароль на указанный в TF файле) или в файле terraform.tfstate, который отвечает за состояние, прописать пароли, например так: "password": "ezIH+bqYFbU!Yk\u0026o",. 3) Сравнение изменений у пользователя. Наличие обновления у пользователя проверяется на основании того, видит ли terraform эти изменения или нет. Эту ситуацию мы с коллегами обсудим и решим, что с этим делать.

suslovsergey commented 3 years ago

@superbotan А может стоит вынести описание БД и юзеров просто в отдельные ресурсы? и проблема по сутиуйдет (я про сортировку юзеров и бд). потому как, если есть скажем 20 юзеров и надо добавить юзера чье имя "по середине" - ну странно видеть дифф более чем добавление юзера. можно например оставить в ресурсе yandex_mdb_postgresql_cluster (если остальные mdb так же сортируют юзеров, то и в них соотвественно) - типо главная БД и главный юзер, а остальное отдельными ресурсами.

suslovsergey commented 3 years ago

Вообщем за 3 часа я написал, то о чем говорил выше. Есть отдельно ресурс самой MDB, и есть ресурсы отдельно DB и ресурс User. имхо стало в разы удобнее, нежели чем держать все в одном ресурсе (особенно когда мы говорим когда более 10-20 баз и более 50 юзеров).

superbotan commented 3 years ago

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

suslovsergey commented 3 years ago

@superbotan к сожалению, это выполнено в виде отдельного провайдера, да и гоха не мой язык программирования (поэтому с пуллреквестом это точно не ко мне:)). Я в течении дня выложу на гитхаб и закину сюда ссылку.

suslovsergey commented 3 years ago

@superbotan - https://github.com/webbankir/terraform-provider-wbyandex

asterix201 commented 3 years ago

@suslovsergey , круто! Прям про, про что изначально спрашивал :)

@superbotan С учетом все расширяющихся свойств у пользователя и параметров баз - что теперь можно довольно много дополнительных параметров как у пользователя, так и у баз добавлять. И, как видно, использование в подобном виде кластеров это вполне востребованное решение, то может рассмотрите разделение?

С доп. параметрами для пользователей и баз (и то небольшими) в "монолитном" виде конфиг разрастается очень сильно.

Ranger-X commented 3 years ago

Еще предложение: добавьте плз в документацию провайдера ссылку на конфиг https://cloud.yandex.com/en-ru/docs/managed-postgresql/grpc/cluster_service#ConfigSpec1 и/или в самой документации к атрибутам кроме их типа вписывать еще и юнит (например, _autovacuumnaptime integer => _autovacuumnaptime integer (milliseconds).

PS. По поводу того, что провайдер "меняет" пользователей/базы данных: есть вот такой манифест:

variable "postgres_databases" {
  type = list(object({
    db_name = string
    db_pass = string
    db_extensions = list(string)
    lc_type = string
    lc_collate = string
  }))
}

...
  dynamic "database" {
    for_each = var.postgres_databases
    content {
      name       = database.value.db_name
      owner      = database.value.db_name
      lc_type    = database.value.lc_type
      lc_collate = database.value.lc_collate

      dynamic "extension" {
        for_each = database.value.db_extensions
        content {
          name = extension.value
        }
      }
    }
  }

  dynamic "user" {
    for_each = var.postgres_databases
    content {
      name     = user.value.db_name
      password = user.value.db_pass
      login    = true

      permission {
        database_name = user.value.db_name
      }
    }
  }

  dynamic "host" {
    for_each = var.host_networks

    content {
      zone = host.value.zone
      subnet_id = host.value.id
      assign_public_ip = true
    }
  }

  lifecycle {
    # выключаем случайное удаление базы
    prevent_destroy = true
    # а также не "обращаем внимания" на изменения в юзерах и базах, т.к. их лучше создавать вручную, через веб-консоль
    ignore_changes = [user, database]
  }

Т.е. как и указано в комменте манифеста, в качестве workaround проблемы с динамическими пользователями/базами используется секция _lifecycle/ignorechanges, а базы и пользователи создаются через веб-интерфейс. Благо, что базы редко добавляются/меняются и их всего пара десятков пока :-) Может кому-то пригодится такой способ :-)

virtualshuric commented 3 years ago

@superbotan Есть ли новости по управлению пользователями/базами через отдельные ресурсы (у меня ровно такая же беда с MySQL-ресурсом сейчас)? Кстати, действительно ли сломается обратная совместимость, если просто добавить новые типы ресурсов, но оставить основной ресурс (yandex_mdb_***) без изменений? Пусть в нем остается возможность задать пользователей и БД через блоки, как сейчас - это вполне удобно для первоначального создания базы. Список узлов кластера - это да, проблема. В теории, решается через labeled-блоки, но это сломает совместимость.

k0t3n commented 3 years ago

@apilikov проблема c изменением позиции хостов всё ещё воспроизводится и изрядно мешает. Any ETA?

apilikov commented 3 years ago

Не могли бы выделить эту проблему в отдельный тикет? @k0t3n

k0t3n commented 3 years ago

@apilikov https://github.com/yandex-cloud/terraform-provider-yandex/issues/204

Denchick commented 2 years ago

Привет! Мы таки вытащили database и user в отдельные ресурсы для PG и MySQL https://github.com/yandex-cloud/terraform-provider-yandex/blob/master/CHANGELOG.md

@suslovsergey @virtualshuric

suslovsergey commented 2 years ago

@Denchick Огонь!