В першій частині розібрались з основами AWS OpenSearch Service взагалі, і з типами інстансів для Data Nodes – AWS: знайомство з OpenSearch Service в ролі vector store.
В другій – з доступами, AWS: створення OpenSearch Service cluster та налаштування аутентифікації і авторизації.
Тепер напишемо Terraform code для створення кластера, юзерів та індексів.
Створювати кластер будемо в VPC, для аутентифікації використаємо internal user database.
А в VPC не можна… Бо – suprize! – AWS Bedrock вимагає OpenSeach Managed кластер саме Public, а не в VPC.
The OpenSearch Managed Cluster you provided is not supported because it is VPC protected. Your cluster must be behind a public network.
Писав в сапорт, сказали, що:
However, there is an ongoing product feature request (PFR) to have Bedrock KnowledgeBases support provisioned Open Search clusters in VPC.
І пропонують використати Amazon OpenSearch Serverless, з якого ми власне і тікаємо, бо ціни дурні.
Друга проблема, яка виявилась, коли я почав писати ресурси bedrockagent_knowledge_base
– це те, що він не підтримує storage_configuration
з type == OPENSEARCH_MANAGED
, тільки Serverless.
Але Pull Request на це вже є, колись, може, замержать.
Отже, будемо робити OpenSearch Managed Service кластер, кластер буде один, з трьома індексами – Dev/Staging/Prod.
В кластері буде три маленькі дата-ноди, а в кожному індексі – 1 primary shard та 1 репліка, бо проект маленький, даних в нашому Production індексі на AWS OpenSearch Serverless, з якого ми хочемо переїхати на AWS OpenSearch Service – зараз всього 2 GiB, і навряд чи в майбутньому буде дуже багато.
Було б добре кластер зробити у власному Terraform модулі аби простіше створювати якісь тестові оточення, як в мене це зроблено для AWS EKS – але поки не дуже є на це час, тому робимо просто tf-файлами з окремим prod.tfvars
для змінних.
Може, потім напишу окремо по переносу у власний модуль, бо це дійсно зручно.
І в наступній частині – поговоримо про моніторинг, бо наш Production вже разок падав 🙂
Зміст
Структура Terraform файлів
Початкова схема файлів і директорій проекту така:
$ tree . . ├── README.md └── terraform ├── Makefile ├── backend.tf ├── data.tf ├── envs │ └── prod │ └── prod.tfvars ├── locals.tf ├── outputs.tf ├── providers.tf ├── variables.tf └── versions.tf
В providers.tf
– налаштування провайдерів, тут поки тільки AWS, і через нього задаємо дефолтні теги:
provider "aws" { region = var.aws_region default_tags { tags = { component = var.component created-by = "terraform" environment = var.environment } } }
В data.tf
збираємо дані AWS Account ID, Availability Zones, VPC та приватні subnets, в яких будемо створювати кластер в яких колись потім будемо створювати кластер:
data "aws_caller_identity" "current" {} data "aws_availability_zones" "available" { state = "available" } data "aws_vpc" "eks_vpc" { id = var.vpc_id } data "aws_subnets" "private" { filter { name = "vpc-id" values = [var.vpc_id] } tags = { subnet-type = "private" } }
Файл variables.tf
з нашими дефолтними змінними, потім будемо додавати нові:
variable "aws_region" { type = string } variable "project_name" { description = "A project name to be used in resources" type = string } variable "component" { description = "A team using this project (backend, web, ios, data, devops)" type = string } variable "environment" { description = "Dev/Prod, will be used in AWS resources Name tag, and resources names" type = string } variable "vpc_id" { type = string description = "A VPC ID to be used to create OpenSearch cluster and its Nodes" }
Значення змінних передаємо через окремий prod.tfvars
, потім, при потребі, можна буде створити нове оточення через файл типу envs/test/test.tfvars
:
aws_region = "us-east-1" project_name = "atlas-kb" component = "backend" environment = "prod" vpc_id = "vpc-0fbaffe234c0d81ea" dns_zone = "prod.example.co"
В Makefile
– спрощуємо собі локальне життя:
############ ### PROD ### ############ init-prod: terraform init -reconfigure -backend-config="key=prod/atlas-knowledge-base-prod.tfstate" plan-prod: terraform plan -var-file=envs/prod/prod.tfvars apply-prod: terraform apply -var-file=envs/prod/prod.tfvars #destroy-prod: # terraform destroy -var-file=envs/prod/prod.tfvars
Які файли будуть далі?
У нас тут ще буде AWS Bedrock, якому треба буде налаштувати доступ – аде це зробимо через його IAM Role, і про Bedrock тут писати не буду – бо і тема окрема, і в Terraform поки що нема підтримки OPENSEARCH_MANAGED
, тому ми зробили його руками, а потім виконаємо terraform import.
Індекси, юзерів для нашого Backend API та Bedrock IAM Role mappings будемо робити в internal database самого OpenSearch через Terraform OpenSearch Provider аби не морочитись з доступами до дашборди.
Планування проекту
Кластер можемо зробити просто з ресурсу aws_opensearch_domain
.
А можна взяти готові модулі, наприклад opensearch від @Anton Babenko.
Давайте візьмемо модуль Антона, бо я багато де його модулі використовую, в принципі все працює чудово.
Створення кластера
Приклади – terraform-aws-opensearch/tree/master/examples.
До variables.tf
додаємо змінну з параметрами кластеру:
... variable "cluser_options" { description = "A map of options to configure the OpenSearch cluster" type = object({ instance_type = string instance_count = number volume_size = number volume_type = string engine_version = string auto_software_update_enabled = bool }) }
І значення в prod.tfvars
:
... cluser_options = { instance_type = "t3.small.search" instance_count = 3 volume_size = 50 volume_type = "gp3" engine_version = "OpenSearch_2.19" auto_software_update_enabled = true }
Інстанси t3.small.search
– самі мінімальні, нам цього поки що вистачить, хоча для t3
є обмеження – наприклад не підтримується Auto-tune.
Ну і взагалі t3
не для Production use case. Див. також Operational best practices for Amazon OpenSearch Service, Current generation instance types і Amazon OpenSearch Service quotas.
Версію тут я задавав 2.9, але буквально на днях додали 3.1 – див. Supported versions of Elasticsearch and OpenSearch.
Беремо три ноди, аби кластер міг вибрати cluster manager node, якщо одна нода впаде, див. Dedicated master node distribution, Learning OpenSearch from scratch, part 2: Digging deeper і Enhance stability with dedicated cluster manager nodes using Amazon OpenSearch Service.
Зміст locals.tf
:
locals { # 'atlas-kb-prod' env_name = "${var.project_name}-${var.environment}" }
Більша частина locals
буде саме тут, але деякі, які зовсім вже “локальні” до якогось коду – будуть у файлах з кодом ресурсів.
Додаємо файл opensearcth_users.tf
– поки тут тільки рутовий юзер, пароль зберігаємо в AWS Parameter Store (замість AWS Secrets Manager – “так історично склалося”):
############ ### ROOT ### ############ # generate root password # waiting for write-only: https://github.com/hashicorp/terraform-provider-aws/pull/43621 # then will update it with the ephemeral type resource "random_password" "os_master_password" { length = 16 special = true } # store the root password in AWS Parameter Store resource "aws_ssm_parameter" "os_master_password" { name = "/${var.environment}/${local.env_name}-root-password" description = "OpenSearch cluster master password" type = "SecureString" value = random_password.os_master_password.result overwrite = true tier = "Standard" lifecycle { ignore_changes = [value] # to prevent diff every time password is regenerated } } data "aws_ssm_parameter" "os_master_password" { name = "/${var.environment}/${local.env_name}-root-password" with_decryption = true depends_on = [aws_ssm_parameter.os_master_password] }
Пишемо файл opensearch_cluster.tf
.
Я тут залишив конфіг для VPC, і на майбутнє, і просто для прикладу, хоча перенести вже створений кластер у VPC не можна буде – доведеться створювати новий, див. Limitations в документації Launching your Amazon OpenSearch Service domains within a VPC:
module "opensearch" { source = "terraform-aws-modules/opensearch/aws" version = "~> 2.0.0" # enable Fine-grained access control # by using the internal user database, we'll simply access to the Dashboards # for backend API Kubernetes Pods, will use Kubernetes Secrets with username:password from AWS Parameter Store advanced_security_options = { enabled = true anonymous_auth_enabled = false internal_user_database_enabled = true master_user_options = { master_user_name = "os_root" master_user_password = data.aws_ssm_parameter.os_master_password.value } } # can't be used with t3 instances auto_tune_options = { desired_state = "DISABLED" } # have three data nodes - t3.small.search nodes in two AZs # will use 3 indexes - dev/stage/prod with 1 shard and 1 replica each cluster_config = { instance_count = var.cluser_options.instance_count dedicated_master_enabled = false instance_type = var.cluser_options.instance_type # put both data-nodes in different AZs zone_awareness_config = { availability_zone_count = 2 } zone_awareness_enabled = true } # the cluster's name # 'atlas-kb-prod' domain_name = "${local.env_name}-cluster" # 50 GiB for each Data Node ebs_options = { ebs_enabled = true volume_type = var.cluser_options.volume_type volume_size = var.cluser_options.volume_size } encrypt_at_rest = { enabled = true } # latest for today: # https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html#choosing-version engine_version = var.cluser_options.engine_version # enable CloudWatch logs for Index and Search slow logs # TODO: collect to VictoriaLogs or Loki, and create metrics and alerts log_publishing_options = [ { log_type = "INDEX_SLOW_LOGS" }, { log_type = "SEARCH_SLOW_LOGS" }, ] ip_address_type = "ipv4" node_to_node_encryption = { enabled = true } # allow minor version updates automatically # will be performed during off-peak windows software_update_options = { auto_software_update_enabled = var.cluser_options.auto_software_update_enabled } # DO NOT use 'atlas-vpc-ops' VPC and its private subnets # > "The OpenSearch Managed Cluster you provided is not supported because it is VPC protected. Your cluster must be behind a public network." # vpc_options = { # subnet_ids = data.aws_subnets.private.ids # } # # VPC endpoint to access from Kubernetes Pods # vpc_endpoints = { # one = { # subnet_ids = data.aws_subnets.private.ids # } # } # Security Group rules to allow access from the VPC only # security_group_rules = { # ingress_443 = { # type = "ingress" # description = "HTTPS access from VPC" # from_port = 443 # to_port = 443 # ip_protocol = "tcp" # cidr_ipv4 = data.aws_vpc.ops_vpc.cidr_block # } # } # Access policy # necessary to allow access for AWS user to the Dashboards access_policy_statements = [ { effect = "Allow" principals = [{ type = "*" identifiers = ["*"] }] actions = ["es:*"] } ] # 'atlas-kb-ops-os-cluster' tags = { Name = "${var.project_name}-${var.environment}-os-cluster" } }
В принципі, тут все в коментах описано, але кратко:
- включаємо fine-grained access control і локальну базу юзерів
- три дата-ноди, кожна з 50 гіг дисків, в різних Availability Zones
- включаємо логи в CloudWatch
кластер робимо в приватних сабнетах- в Domain Access Policy дозволяємо доступ для всіх
- ну – поки так… Security Groups ми використати не можемо, бо не в VPC, а створити IP-Based policy – як? ми ж не знаємо CIDR Bedrock
- в принципі, тут в
principals.identifiers
можна додати ліміт на наших IAM Users + Bedrock AIM Role, бо вона буде одна
Запускаємо створення кластера і йдемо пити чай.
Налаштування Custom endpoint
Після створення кластеру перевіряємо доступ до дашборди, якщо все ОК – то додаємо Custom endpoint.
Note: з Custom endpoint свої приколи: в Terraform OpenSearch Provider треба використовувати саме Custom endpoint URL, але в AWS Bedrock Knowledge Base – дефолтний URL кластеру
Для цього нам треба зробити сертифікат в AWS Certificate Manager і додати новий запис в Route53.
Я тут очікував можливу проблему куриця і яйця, бо налаштування Custom Endpoint залежать від AWS ACM і запису в AWS Route53, а запис в AWS Route53 буде залежати від кластеру – бо використовує його ендпоінт.
Але ні, якщо робити новий кластер з налаштуваннями, які описав нижче – все нормально створюється: спочатку сертифікат в AWS ACM, потім кластер з Custom Endpoint, потім запис в Route53 з CNAME на cluster default URL.
Додаємо нову local
– os_custom_domain_name
:
locals { # 'atlas-kb-prod' env_name = "${var.project_name}-${var.environment}" # 'opensearch.prod.example.co' os_custom_domain_name = "opensearch.${var.dns_zone}" }
Додаємо отримання даних про Route53 зону до data.tf
:
... data "aws_route53_zone" "zone" { name = var.dns_zone }
Додаємо створення сертифіката і запис у Route53 до opensearch_cluster.tf
:
# TLS for the Custom Domain module "prod_opensearch_acm" { source = "terraform-aws-modules/acm/aws" version = "~> 6.0" # 'opensearch.example.co' domain_name = local.os_custom_domain_name zone_id = data.aws_route53_zone.zone.zone_id validation_method = "DNS" wait_for_validation = true tags = { Name = local.os_custom_domain_name } } resource "aws_route53_record" "opensearch_domain_endpoint" { zone_id = data.aws_route53_zone.zone.zone_id name = local.os_custom_domain_name type = "CNAME" ttl = 300 records = [module.opensearch.domain_endpoint] } ...
І в module "opensearch"
додаємо налаштування custom ендпоінту:
... domain_endpoint_options = { custom_endpoint_certificate_arn = module.prod_opensearch_acm.acm_certificate_arn custom_endpoint_enabled = true custom_endpoint = local.os_custom_domain_name tls_security_policy = "Policy-Min-TLS-1-2-2019-07" } ...
Виконуємо terrform init
та terrform apply
, перевіряємо налаштування:
І перевіряємо доступ до дашборд.
Terraform Outputs
Додамо трохи аутуптів.
Поки просто для себе, потім, можливо, будемо використовувати в імпортах інших проектів, див. Terraform: terraform_remote_state – отримання outputs інших state-файлів:
output "vpc_id" { value = var.vpc_id } output "cluster_arn" { value = module.opensearch.domain_arn } output "opensearch_domain_endpoint_cluster" { value = "https://${module.opensearch.domain_endpoint}" } output "opensearch_domain_endpoint_custom" { value = "https://${local.os_custom_domain_name}" } output "opensearch_root_username" { value = "os_root" } output "opensearch_root_user_password_secret_name" { value = "/${var.environment}/${local.env_name}-root-password" }
Створення OpenSearch Users
Власне, що нам залишилось – це користувачі і індекси.
Юзерів у нас буде два типи:
- звичайні юзери з OpenSearch internal database – для нашого Backend API в Kubernetes (насправді, потім ми все ж перейшли на IAM Roles, які мапляться в поди Backend через EKS Pod Identities)
- і юзери (IAM Role) для Bedrock – там буде три Knowledge Bases, кожна зі своєю IAM Role, для якої треба буде додати OpenSearch Role і зробити mapping на IAM-ролі
Почнемо зі звичайних юзерів.
Додаємо провайдера, в мене це у файлі versions.tf
:
terraform { required_version = "~> 1.6" required_providers { aws = { source = "hashicorp/aws" version = "~> 6.0" } opensearch = { source = "opensearch-project/opensearch" version = "~> 2.3" } } }
В файлі providers.tf
описуємо доступ до кластеру:
... provider "opensearch" { url = "https://${local.os_custom_domain_name}" username = "os_root" password = data.aws_ssm_parameter.os_master_password.value healthcheck = false }
Error: elastic: Error 403 (Forbidden)
Тут важливий момент з url
в конфігурації провайдеру, писав про це вище, тепер – як воно виглядає.
Спершу в provider.url
задав як outputs
модуля, тобто module.opensearch.domain_endpoint
.
І через це ловив 403, коли намагався створити юзерів:
... opensearch_user.os_kraken_dev_user: Creating... opensearch_role.os_kraken_dev_role: Creating... ╷ │ Error: elastic: Error 403 (Forbidden) │ │ with opensearch_user.os_kraken_dev_user, │ on opensearch_users.tf line 23, in resource "opensearch_user" "os_kraken_dev_user": │ 23: resource "opensearch_user" "os_kraken_dev_user" { │ ╵ ╷ │ Error: elastic: Error 403 (Forbidden) │ │ with opensearch_role.os_kraken_dev_role, │ on opensearch_users.tf line 30, in resource "opensearch_role" "os_kraken_dev_role": │ 30: resource "opensearch_role" "os_kraken_dev_role" {
Власне, задаємо URL саме у вигляді FQDN, який робили для Custom Endpoint, щось типу "url = https://opensearch.exmaple.com"
– і з ним все працює.
Створення Internal юзерів
Тепер самі юзери.
Їх буде три – dev, staging, prod, кожен з доступом до відповідного індексу.
Тут використаємо opensearch_user
.
Якщо кластер всеж створений в VPC – то потрібен підключений VPN, аби провайдер зміг підключитись до кластеру.
До variables.tf
додаємо list()
зі списком оточень:
... variable "app_environments" { type = list(string) description = "The Application's environments, to be used to created Dev/Staging/Prod DynamoDB tables, etc" }
І значення в prod.tfvars
:
... app_environments = [ "dev", "staging", "prod" ]
Internal database users
Спершу я планував просто використовувати локальних юзерів, і в цей пост записав такий варіант – нехай буде. Далі покажу, як все ж зробили потім – з IAM Users та IAM Roles.
У файлі opensearch_users.tf
додаємо в циклах три паролі, трьох юзерів, і три ролі, на які мапимо юзерів – кожна роль з доступом до власного індексу:
... ############## ### KRAKEN ### ############## resource "random_password" "os_kraken_password" { for_each = toset(var.app_environments) length = 16 special = true } # store the root password in AWS Parameter Store resource "aws_ssm_parameter" "os_kraken_password" { for_each = toset(var.app_environments) name = "/${var.environment}/${local.env_name}-kraken-${each.key}-password" description = "OpenSearch cluster Backend Dev password" type = "SecureString" value = random_password.os_kraken_password[each.key].result overwrite = true tier = "Standard" lifecycle { ignore_changes = [value] # to prevent diff every time password is regenerated } } # Create a user resource "opensearch_user" "os_kraken_user" { for_each = toset(var.app_environments) username = "os_kraken_${each.key}" password = random_password.os_kraken_password[each.key].result description = "Backend EKS ${each.key} user" depends_on = [module.opensearch] } # And a full user, role and role mapping example: resource "opensearch_role" "os_kraken_role" { for_each = toset(var.app_environments) role_name = "os_kraken_${each.key}_role" description = "Backend EKS ${each.key} role" cluster_permissions = [ "indices:data/read/msearch", "indices:data/write/bulk*", "indices:data/read/mget*" ] index_permissions { index_patterns = ["kraken-kb-index-${each.key}"] allowed_actions = ["*"] } depends_on = [module.opensearch] }
В cluster_permissions
додаємо дозволи, які потрібні і для index level, і для cluster level, бо Bedrock без них не працював, див. Cluster wide index permissions.
Деплоїмо, перевіряємо в Dashboards:
Додавання IAM Users
Тут ідея така сама, просто замість звичайних юзерів з логіном:паролем для аутентифікації використовується IAM та його Users && Roles.
Про роль для Bedrock далі, а зараз додамо мапінг юзерів.
Що нам треба – це взяти список наших Backend team юзерів, дати їм IAM Policy з доступом до OpenSearch, а потім в OpnSearch internal users database додати мапінг на локальну роль.
Локальну роль поки можна взяти all_access
, хоча краще потім все ж написати власну. Див. Predefined roles та About the master user.
Додаємо нову змінну в variables.tf:
... variable "backend_team_users_arns" { type = list(string) }
Її значення в prod.tfvars
:
... backend_team_users_arns = [ "arn:aws:iam::492***148:user/arseny", "arn:aws:iam::492***148:user/misha", "arn:aws:iam::492***148:user/oleksii", "arn:aws:iam::492***148:user/vladimir", "os_root" ]
Тут довелося костиляти з юзером os_root
, бо інакше його випилює з ролі.
Тому таки краще зробити нормальні ролі – але для MVP міжна і так.
І додаємо мапінг цих IAM Users до ролі all_access
:
... #################### ### BACKEND TEAM ### #################### resource "opensearch_roles_mapping" "all_access_mapping" { role_name = "all_access" users = var.backend_team_users_arns }
Деплоїмо, перевіряємо роль all_access
:
Note: ChatGPT вперто казав додавати IAM Users в Backend Roles, але ні, і це явно вказано в документації – додавати треба в Users, див. Additional master users.
І всім IAM Users треба додати IAM-політику з доступом.
Знов-таки для MVP можна просто взяти голову policy AmazonOpenSearchServiceFullAccess
, яка підключена до IAM Group:
Створення AWS Bedrock IAM Roles та OpenSearch Role mappings
Bedrock у нас вже є, треба просто створити нові IAM Roles і замапити їх до OpenSeach Roles.
Додаємо файл iam.tf
– описуємо IAM Role та IAM Policy (Identity-based Policy для доступу до OpenSearch), тут також в циклі по кожному з var.app_environmetns
:
##################################### ### MAIN ROLE FOR KNOWLEDGE BASE ### ##################################### # grants permissions for AWS Bedrock to interact with other AWS services resource "aws_iam_role" "knowledge_base_role" { for_each = toset(var.app_environments) name = "${var.project_name}-role-${each.key}-managed" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "bedrock.amazonaws.com" } Condition = { StringEquals = { "aws:SourceAccount" = data.aws_caller_identity.current.account_id } ArnLike = { # restricts the role to be assumed only by Bedrock knowledge base in the specified region "aws:SourceArn" = "arn:aws:bedrock:${var.aws_region}:${data.aws_caller_identity.current.account_id}:knowledge-base/*" } } } ] }) } # IAM policy for Knowledge Base to access OpenSearch Managed resource "aws_iam_policy" "knowledge_base_opensearch_policy" { for_each = toset(var.app_environments) name = "${var.project_name}-kb-opensearch-policy-${each.key}-managed" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "es:*", ] Resource = [ module.opensearch.domain_arn, "${module.opensearch.domain_arn}/*" ] } ] }) } resource "aws_iam_role_policy_attachment" "knowledge_base_opensearch" { for_each = toset(var.app_environments) role = aws_iam_role.knowledge_base_role[each.key].name policy_arn = aws_iam_policy.knowledge_base_opensearch_policy[each.key].arn }
Далі в opensearch_users.tf
створимо:
opensearch_role
: зcluster_permissions
таindex_permissions
на кожен індексlocals
з усіма IAM Roles, які створили вище- і
opensearch_roles_mapping
для кожноїopensearch_role.os_bedrock_roles
, які черезbackend_roles
додаємо до кожноїopensearch_role
Виглядає якось так:
... ################# #### BEDROCK #### ################# resource "opensearch_role" "os_bedrock_roles" { for_each = toset(var.app_environments) role_name = "os_bedrock_${each.key}_role" description = "Backend Bedrock KB ${each.key} role" cluster_permissions = [ "indices:data/read/msearch", "indices:data/write/bulk*", "indices:data/read/mget*" ] index_permissions { index_patterns = ["kraken-kb-index-${each.key}"] allowed_actions = ["*"] } depends_on = [module.opensearch] } # 'aws_iam_role' is defined in iam.tf locals { knowledge_base_role_arns = { for env, role in aws_iam_role.knowledge_base_role : env => role.arn } } resource "opensearch_roles_mapping" "os_bedrock_role_mappings" { for_each = toset(var.app_environments) role_name = opensearch_role.os_bedrock_roles[each.key].role_name backend_roles = [ local.knowledge_base_role_arns[each.key] ] depends_on = [module.opensearch] }
Власне, саме тут зіткнулись з помилками доступу Bedrock, через які довелось додавати cluster_permissions
:
The knowledge base storage configuration provided is invalid… Request failed: [security_exception] no permissions for [indices:data/read/msearch] and User [name=arn:aws:iam::492***148:role/kraken-kb-role-dev, backend_roles=[arn:aws:iam::492***148:role/kraken-kb-role-dev], requestedTenant=null]
Деплоїмо, перевіряємо:
Створення OpenSearch індексів
Провайдер вже є, ресурс беремо opensearch_index
.
В locals
записуємо шаблон індексу – я його просто взяв у девелоперів зі старого конфігу:
locals { # 'atlas-kb-prod' env_name = "${var.project_name}-${var.environment}" # 'opensearch.prod.example.co' os_custom_domain_name = "opensearch.${var.dns_zone}" # index mappings os_index_mappings = <<-EOF { "dynamic_templates": [ { "strings": { "match_mapping_type": "string", "mapping": { "fields": { "keyword": { "ignore_above": 8192, "type": "keyword" } }, "type": "text" } } } ], "properties": { "bedrock-knowledge-base-default-vector": { "type": "knn_vector", "dimension": 1024, "method": { "name": "hnsw", "engine": "faiss", "parameters": { "m": 16, "ef_construction": 512 }, "space_type": "l2" } }, "AMAZON_BEDROCK_METADATA": { "type": "text", "index": false }, "AMAZON_BEDROCK_TEXT_CHUNK": { "type": "text", "index": true } } } EOF }
Створюємо файл opensearch_indexes.tf
. І додаємо сам індекси – тут я все ж вирішив без циклу, прямо створити окремі Dev/Staging/Prod:
# Dev Index resource "opensearch_index" "kb_vector_index_dev" { name = "kraken-kb-index-dev" # enable approximate nearest neighbor search by setting index_knn to true index_knn = true index_knn_algo_param_ef_search = "512" number_of_shards = "1" number_of_replicas = "1" mappings = local.os_index_mappings # When new documents are ingested into the Knowledge Base, # OpenSearch automatically creates field mappings for new metadata fields under # AMAZON_BEDROCK_METADATA. Since these fields are created outside of TF resource definitions, # TF detects them as configuration drift and attempts to recreate the index to match its # known state. # # This lifecycle rule prevents unnecessary index recreation by ignoring mapping changes # that occur after initial deployment. lifecycle { ignore_changes = [mappings] } } ...
Деплоїмо і перевіряємо:
Власне, на цьому і все.
Bedrock вже підключили, все працює.
Але трохи погемороїтись довелось.
І впевнений, що не останній раз 🙂