Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Added automated provisioning of CosmosDB and App Insights for OPEA applications - Infosys #657

Merged
merged 5 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cloud-service-provider/azure/aks/terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ terraform init

By default, 1-node cluster is created which is suitable for running the OPEA application. See `variables.tf` and `opea-<application-name>.tfvars` if you want to tune the cluster properties, e.g., number of nodes, instance types or disk size.

## Cosmos DB

By default Cosmos DB will not be provisioned. If you want Cosmos DB as part of your resource provisioning, update `is_cosmosdb_required` property present in `opea-<application-name>.tfvars` to `true`.

## Persistent Volume Claim

OPEA needs a volume where to store the model. For that we need to create Kubernetes Persistent Volume Claim (PVC). OPEA requires `ReadWriteMany` option since multiple pods needs access to the storage and they can be on different nodes. On AKS, only Azure File Service supports `ReadWriteMany`. Thus, each OPEA application below uses the file `aks-azfs-csi-pvc.yaml` to create PVC in its namespace.
Expand Down
133 changes: 133 additions & 0 deletions cloud-service-provider/azure/aks/terraform/azure_main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,79 @@ module "vnet" {
depends_on = [azurerm_resource_group.main]
}

# Cosmos DB
resource "azurerm_cosmosdb_account" "main" {
count = var.is_cosmosdb_required ? 1 : 0
name = "${var.cluster_name}-cosmosdb"
location = var.cosmosdb_account_location
resource_group_name = azurerm_resource_group.main.name
offer_type = "Standard"
kind = "GlobalDocumentDB"
geo_location {
location = var.cosmosdb_account_location
failover_priority = 0
}
consistency_policy {
consistency_level = "BoundedStaleness"
max_interval_in_seconds = 300
max_staleness_prefix = 100000
}
capabilities {
name = "EnableNoSQLFullTextSearch"
}
capabilities {
name = "EnableNoSQLVectorSearch"
}
depends_on = [
azurerm_resource_group.main
]
}

resource "azurerm_cosmosdb_sql_database" "main" {
count = var.is_cosmosdb_required ? 1 : 0
name = "${var.cluster_name}-sqldb"
resource_group_name = azurerm_resource_group.main.name
account_name = azurerm_cosmosdb_account.main[count.index].name
throughput = var.throughput
depends_on = [
azurerm_cosmosdb_account.main
]
}

resource "azurerm_cosmosdb_sql_container" "main" {
count = var.is_cosmosdb_required ? 1 : 0
name = "${var.cluster_name}-sql-container"
resource_group_name = azurerm_resource_group.main.name
account_name = azurerm_cosmosdb_account.main[count.index].name
database_name = azurerm_cosmosdb_sql_database.main[count.index].name
partition_key_paths = ["/definition/id"]
partition_key_version = 1
throughput = var.throughput

indexing_policy {
indexing_mode = "consistent"

included_path {
path = "/*"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not /* cover everything? Is there some ordering effect here .. from most general to specific?

}

included_path {
path = "/included/?"
}

excluded_path {
path = "/excluded/?"
}
}

unique_key {
paths = ["/definition/idlong", "/definition/idshort"]
}
depends_on = [
azurerm_cosmosdb_sql_database.main
]
}

# AKS Cluster
resource "azurerm_kubernetes_cluster" "main" {
name = var.cluster_name
Expand Down Expand Up @@ -109,5 +182,65 @@ resource "null_resource" "kubectl" {
depends_on = [azurerm_kubernetes_cluster.main]
}

# Application Insights
resource "azurerm_log_analytics_workspace" "main" {
name = "workspace-${var.cluster_name}"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
retention_in_days = 30
}

resource "azurerm_application_insights" "t_appinsights" {
name = "${var.cluster_name}-appinsights"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
workspace_id = azurerm_log_analytics_workspace.main.id
application_type = "web"
}

# Data source for Azure subscription information
data "azurerm_client_config" "current" {}

# Cosmos db Primary connection string into key vault
resource "azurerm_key_vault_secret" "cosdb_primary" {
count = var.is_cosmosdb_required ? 1 : 0
name = "AzCosmosDBConnectionStringPrimary"
value = tostring("AccountEndpoint=${azurerm_cosmosdb_account.main[count.index].endpoint};AccountKey=${azurerm_cosmosdb_account.main[count.index].primary_key};")
key_vault_id = azurerm_key_vault.main.id
}

# Cosmos db Secondary connection string into key vault
resource "azurerm_key_vault_secret" "cosdb_secondary" {
count = var.is_cosmosdb_required ? 1 : 0
name = "AzCosmosDBConnectionStringSecondary"
value = tostring("AccountEndpoint=${azurerm_cosmosdb_account.main[count.index].endpoint};AccountKey=${azurerm_cosmosdb_account.main[count.index].secondary_key};")
key_vault_id = azurerm_key_vault.main.id
}

# Kubernetes cluster end point into key vault
resource "azurerm_key_vault_secret" "kube_cluster_endpoint" {
name = "KubeClusterEndPoint"
value = tostring("${azurerm_kubernetes_cluster.main.kube_config.0.host}")
key_vault_id = azurerm_key_vault.main.id
}

# App Insights Instrumentation Key
resource "azurerm_key_vault_secret" "app_insights_instrumentation_key" {
name = "AppInsightsInstrumentationKey"
value = tostring("${azurerm_application_insights.t_appinsights.instrumentation_key}")
key_vault_id = azurerm_key_vault.main.id
}

# App Insights app id
resource "azurerm_key_vault_secret" "app_insights_app_id" {
name = "AppInsightsAppId"
value = tostring("${azurerm_application_insights.t_appinsights.app_id}")
key_vault_id = azurerm_key_vault.main.id
}

# App Insights Connection String
resource "azurerm_key_vault_secret" "app_insights_connection_string" {
name = "AppInsightsConnectionString"
value = tostring("${azurerm_application_insights.t_appinsights.connection_string}")
key_vault_id = azurerm_key_vault.main.id
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_pool_type = "Spot" # cheaper
os_disk_size_gb = 50
location = "eastus"
kubernetes_version = "1.30"
is_cosmosdb_required = false
18 changes: 17 additions & 1 deletion cloud-service-provider/azure/aks/terraform/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
output "cluster_endpoint" {
description = "Endpoint for AKS control plane"
#sensitive = false
sensitive = true
value = azurerm_kubernetes_cluster.main.kube_config.0.host
}
Expand All @@ -19,3 +18,20 @@ output "cluster_name" {
description = "Kubernetes Cluster Name"
value = azurerm_kubernetes_cluster.main.name
}

output "instrumentation_key" {
description = "App Insights Instrumentation Key"
value = azurerm_application_insights.t_appinsights.instrumentation_key
sensitive = true
}

output "app_id" {
description = "App Insights App Id"
value = azurerm_application_insights.t_appinsights.app_id
}

output "app_insights_connection_string" {
description = "App Insights Connection String"
value = azurerm_application_insights.t_appinsights.connection_string
sensitive = true
}
26 changes: 26 additions & 0 deletions cloud-service-provider/azure/aks/terraform/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ variable "location" {
default = "eastus"
}

variable "cosmosdb_account_location" {
description = "Cosmos DB account Location"
type = string
default = "westus"
}

variable "cluster_name" {
description = "AKS cluster name"
type = string
Expand Down Expand Up @@ -81,3 +87,23 @@ variable "instance_types" {
type = list(string)
default = ["Standard_D32d_v5"]
}

variable "throughput" {
type = number
default = 400
description = "Cosmos db database throughput"
validation {
condition = var.throughput >= 400 && var.throughput <= 1000000
error_message = "Cosmos db manual throughput should be equal to or greater than 400 and less than or equal to 1000000."
}
validation {
condition = var.throughput % 100 == 0
error_message = "Cosmos db throughput should be in increments of 100."
}
}

variable "is_cosmosdb_required" {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add default (false) so use is not asked every time?

Copy link
Contributor Author

@kkrishTa kkrishTa Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added and added one more parameter in tfvars also defaulted to false. Mentioned in README that users can update this if they want Cosmos DB as part of their provisioning.

type = bool
description = "Is Cosmos DB required for your deployment? [true/false]"
default = false
}
Loading