From 99e544087b01b21d31133ccc366056131b824e64 Mon Sep 17 00:00:00 2001 From: Siva Guruvareddiar <1725781+sguruvar@users.noreply.github.com> Date: Mon, 29 Apr 2024 13:08:42 -0500 Subject: [PATCH] feat: Apache Superset feature support (#447) Co-authored-by: Vara Bonthu Co-authored-by: avasu80 --- analytics/terraform/superset-on-eks/README.md | 52 ++ analytics/terraform/superset-on-eks/addons.tf | 160 ++++ .../terraform/superset-on-eks/cleanup.sh | 50 ++ .../helm-values/superset-values.yaml | 95 ++ .../terraform/superset-on-eks/install.sh | 34 + analytics/terraform/superset-on-eks/main.tf | 153 ++++ .../terraform/superset-on-eks/outputs.tf | 11 + .../terraform/superset-on-eks/providers.tf | 36 + .../terraform/superset-on-eks/values.yaml | 831 ++++++++++++++++++ .../terraform/superset-on-eks/variables.tf | 32 + .../terraform/superset-on-eks/versions.tf | 29 + analytics/terraform/superset-on-eks/vpc.tf | 51 ++ .../data-analytics/img/superset1.png | Bin 0 -> 52076 bytes .../data-analytics/img/superset2.png | Bin 0 -> 81800 bytes .../data-analytics/superset-on-eks.md | 102 +++ 15 files changed, 1636 insertions(+) create mode 100644 analytics/terraform/superset-on-eks/README.md create mode 100755 analytics/terraform/superset-on-eks/addons.tf create mode 100755 analytics/terraform/superset-on-eks/cleanup.sh create mode 100644 analytics/terraform/superset-on-eks/helm-values/superset-values.yaml create mode 100755 analytics/terraform/superset-on-eks/install.sh create mode 100755 analytics/terraform/superset-on-eks/main.tf create mode 100755 analytics/terraform/superset-on-eks/outputs.tf create mode 100755 analytics/terraform/superset-on-eks/providers.tf create mode 100644 analytics/terraform/superset-on-eks/values.yaml create mode 100755 analytics/terraform/superset-on-eks/variables.tf create mode 100644 analytics/terraform/superset-on-eks/versions.tf create mode 100755 analytics/terraform/superset-on-eks/vpc.tf create mode 100644 website/docs/blueprints/data-analytics/img/superset1.png create mode 100644 website/docs/blueprints/data-analytics/img/superset2.png create mode 100644 website/docs/blueprints/data-analytics/superset-on-eks.md diff --git a/analytics/terraform/superset-on-eks/README.md b/analytics/terraform/superset-on-eks/README.md new file mode 100644 index 000000000..89efc0bcd --- /dev/null +++ b/analytics/terraform/superset-on-eks/README.md @@ -0,0 +1,52 @@ +## Requirements + + For security reasons, ALB is deployed as internal one and it can be changed to internet-facing during the deployment, if needed. +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.36.0 | +| [helm](#provider\_helm) | 2.12.1 | +| [kubernetes](#provider\_kubernetes) | 2.25.2 | +| [null](#provider\_null) | 3.2.2 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [ebs\_csi\_driver\_irsa](#module\_ebs\_csi\_driver\_irsa) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | ~> 5.20 | +| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 19.15 | +| [eks\_blueprints\_addons](#module\_eks\_blueprints\_addons) | aws-ia/eks-blueprints-addons/aws | ~> 1.2 | +| [lb\_role](#module\_lb\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | 5.37.1 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | + +## Resources + +| Name | Type | +|------|------| +| [helm_release.alb_controller](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [helm_release.superset](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource | +| [kubernetes_ingress_class_v1.aws_alb](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/ingress_class_v1) | resource | +| [kubernetes_ingress_v1.superset](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/ingress_v1) | resource | +| [kubernetes_namespace.superset](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | +| [kubernetes_service_account.service_account](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account) | resource | +| [null_resource.add_superset_repo](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [null_resource.helm_update_repos](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [eks\_cluster\_version](#input\_eks\_cluster\_version) | EKS Cluster version | `string` | `"1.28"` | no | +| [name](#input\_name) | Name of the VPC and EKS Cluster | `string` | `"superset-on-eks"` | no | +| [region](#input\_region) | Region | `string` | `"us-east-1"` | no | +| [secondary\_cidr\_blocks](#input\_secondary\_cidr\_blocks) | Secondary CIDR blocks to be attached to VPC | `list(string)` |
[
"100.64.0.0/16"
]
| no | +| [vpc\_cidr](#input\_vpc\_cidr) | VPC CIDR. This should be a valid private (RFC 1918) CIDR range | `string` | `"10.1.0.0/21"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [configure\_kubectl](#output\_configure\_kubectl) | Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig | +| [superset\_url](#output\_superset\_url) | Configure kubectl: Once the kubeconfig is configured as above, use the below command to get the Superset URL | diff --git a/analytics/terraform/superset-on-eks/addons.tf b/analytics/terraform/superset-on-eks/addons.tf new file mode 100755 index 000000000..c6aecd758 --- /dev/null +++ b/analytics/terraform/superset-on-eks/addons.tf @@ -0,0 +1,160 @@ +#--------------------------------------------------------------- +# GP3 Encrypted Storage Class +#--------------------------------------------------------------- +resource "kubernetes_annotations" "disable_gp2" { + annotations = { + "storageclass.kubernetes.io/is-default-class" : "false" + } + api_version = "storage.k8s.io/v1" + kind = "StorageClass" + metadata { + name = "gp2" + } + force = true + + depends_on = [module.eks.eks_cluster_id] +} + +resource "kubernetes_storage_class" "default_gp3" { + metadata { + name = "gp3" + annotations = { + "storageclass.kubernetes.io/is-default-class" : "true" + } + } + + storage_provisioner = "ebs.csi.aws.com" + reclaim_policy = "Delete" + allow_volume_expansion = true + volume_binding_mode = "WaitForFirstConsumer" + parameters = { + fsType = "ext4" + encrypted = true + type = "gp3" + } + + depends_on = [kubernetes_annotations.disable_gp2] +} + +#--------------------------------------------------------------- +# IRSA for EBS CSI Driver +#--------------------------------------------------------------- +module "ebs_csi_driver_irsa" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + version = "~> 5.20" + role_name_prefix = format("%s-%s", local.name, "ebs-csi-driver-") + attach_ebs_csi_policy = true + oidc_providers = { + main = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] + } + } + tags = local.tags +} + +module "eks_blueprints_addons" { + source = "aws-ia/eks-blueprints-addons/aws" + version = "~> 1.2" + + cluster_name = module.eks.cluster_name + cluster_endpoint = module.eks.cluster_endpoint + cluster_version = module.eks.cluster_version + oidc_provider_arn = module.eks.oidc_provider_arn + + #--------------------------------------- + # Amazon EKS Managed Add-ons + #--------------------------------------- + eks_addons = { + aws-ebs-csi-driver = { + service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn + } + coredns = { + preserve = true + } + vpc-cni = { + preserve = true + } + kube-proxy = { + preserve = true + } + } + + #--------------------------------------- + # AWS Load Balancer Controller Add-on + #--------------------------------------- + enable_aws_load_balancer_controller = true + # turn off the mutating webhook for services because we are using + # service.beta.kubernetes.io/aws-load-balancer-type: external + aws_load_balancer_controller = { + set = [{ + name = "enableServiceMutatorWebhook" + value = "false" + }] + } + + tags = local.tags +} + +module "eks_data_addons" { + source = "aws-ia/eks-data-addons/aws" + version = "~> 1.31.5" # ensure to update this to the latest/desired version + + oidc_provider_arn = module.eks.oidc_provider_arn + + #--------------------------------------- + # AWS Apache Superset Add-on + #--------------------------------------- + enable_superset = true + superset_helm_config = { + values = [templatefile("${path.module}/helm-values/superset-values.yaml", {})] + } + depends_on = [module.eks_blueprints_addons] + +} + +#------------------------------------------------------------ +# Create AWS Application Load balancer with Ingres +#------------------------------------------------------------ +resource "kubernetes_ingress_class_v1" "aws_alb" { + metadata { + name = "aws-alb" + } + + spec { + controller = "ingress.k8s.aws/alb" + } + + depends_on = [module.eks.cluster_id] +} + +resource "kubernetes_ingress_v1" "superset" { + metadata { + name = "superset-ingress3" + namespace = "superset" + annotations = { + "alb.ingress.kubernetes.io/scheme" = "internet-facing" + "alb.ingress.kubernetes.io/target-type" = "ip" + } + } + spec { + ingress_class_name = "aws-alb" + rule { + http { + path { + path = "/*" + backend { + service { + name = "superset" + port { + number = 8088 + } + } + } + } + } + } + } + + depends_on = [module.eks_blueprints_addons, module.eks_data_addons] +} diff --git a/analytics/terraform/superset-on-eks/cleanup.sh b/analytics/terraform/superset-on-eks/cleanup.sh new file mode 100755 index 000000000..b020546f3 --- /dev/null +++ b/analytics/terraform/superset-on-eks/cleanup.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -o errexit +set -o pipefail + +targets=( + "module.eks_blueprints_addons" + "module.eks" + "module.vpc" +) + +#------------------------------------------- +# Helpful to delete the stuck in "Terminating" namespaces +# Rerun the cleanup.sh script to detect and delete the stuck resources +#------------------------------------------- +terminating_namespaces=$(kubectl get namespaces --field-selector status.phase=Terminating -o json | jq -r '.items[].metadata.name') + +# If there are no terminating namespaces, exit the script +if [[ -z $terminating_namespaces ]]; then + echo "No terminating namespaces found" +fi + +for ns in $terminating_namespaces; do + echo "Terminating namespace: $ns" + kubectl get namespace $ns -o json | sed 's/"kubernetes"//' | kubectl replace --raw "/api/v1/namespaces/$ns/finalize" -f - +done + +#------------------------------------------- +# Terraform destroy per module target +#------------------------------------------- +for target in "${targets[@]}" +do + destroy_output=$(terraform destroy -target="$target" -auto-approve | tee /dev/tty) + if [[ ${PIPESTATUS[0]} -eq 0 && $destroy_output == *"Destroy complete!"* ]]; then + echo "SUCCESS: Terraform destroy of $target completed successfully" + else + echo "FAILED: Terraform destroy of $target failed" + exit 1 + fi +done + +#------------------------------------------- +# Terraform destroy full +#------------------------------------------- +destroy_output=$(terraform destroy -target="$target" -auto-approve | tee /dev/tty) +if [[ ${PIPESTATUS[0]} -eq 0 && $destroy_output == *"Destroy complete!"* ]]; then + echo "SUCCESS: Terraform destroy of all targets completed successfully" +else + echo "FAILED: Terraform destroy of all targets failed" + exit 1 +fi diff --git a/analytics/terraform/superset-on-eks/helm-values/superset-values.yaml b/analytics/terraform/superset-on-eks/helm-values/superset-values.yaml new file mode 100644 index 000000000..40a5d4955 --- /dev/null +++ b/analytics/terraform/superset-on-eks/helm-values/superset-values.yaml @@ -0,0 +1,95 @@ +# Superset node configuration +supersetNode: + replicaCount: 1 + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + + resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 200m + memory: 256Mi + +# Superset Celery worker configuration +supersetWorker: + replicaCount: 1 + autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + command: + - "/bin/sh" + - "-c" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app worker" + # -- If true, forces deployment to reload on each upgrade + forceReload: false + # -- Init container + # @default -- a container waiting for postgres and redis + initContainers: + - name: wait-for-postgres-redis + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: "{{ .Values.initImage.pullPolicy }}" + envFrom: + - secretRef: + name: "{{ tpl .Values.envFromSecret . }}" + command: + - /bin/sh + - -c + - dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s + + resources: + limits: + cpu: 200m + memory: 512Mi + requests: + cpu: 200m + memory: 400Mi + + persistence: + enabled: true + + +postgresql: + ## Set to false if bringing your own PostgreSQL. + enabled: true + loadExamples: true + primary: + persistence: + ## Enable PostgreSQL persistence using Persistent Volume Claims. + enabled: true + storageClass: gp3 + +configOverrides: + secret: | + SECRET_KEY = '5WPuGEgPfGTrk9MCVLFkzNk0fO4hyfsykSrM03fHn1m8d3yQQd4yjyvf' + + + +redis: + + master: + ## + ## Image configuration + # image: + ## + ## docker registry secret names (list) + # pullSecrets: nil + ## + persistence: + ## + ## Use a PVC to persist data. + enabled: true + ## + ## Persistent class + # storageClass: classname + ## + ## Access mode: + accessModes: + - ReadWriteOnce +runAsUser: 1000 \ No newline at end of file diff --git a/analytics/terraform/superset-on-eks/install.sh b/analytics/terraform/superset-on-eks/install.sh new file mode 100755 index 000000000..0e7b7166d --- /dev/null +++ b/analytics/terraform/superset-on-eks/install.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +echo "Initializing ..." +terraform init || echo "\"terraform init\" failed" + +# List of Terraform modules to apply in sequence +targets=( + "module.vpc" + "module.eks" + "module.eks_blueprints_addons" +) + +# Apply modules in sequence +for target in "${targets[@]}" +do + echo "Applying module $target..." + apply_output=$(terraform apply -target="$target" -auto-approve 2>&1 | tee /dev/tty) + if [[ ${PIPESTATUS[0]} -eq 0 && $apply_output == *"Apply complete"* ]]; then + echo "SUCCESS: Terraform apply of $target completed successfully" + else + echo "FAILED: Terraform apply of $target failed" + exit 1 + fi +done + +# Final apply to catch any remaining resources +echo "Applying remaining resources..." +apply_output=$(terraform apply -auto-approve 2>&1 | tee /dev/tty) +if [[ ${PIPESTATUS[0]} -eq 0 && $apply_output == *"Apply complete"* ]]; then + echo "SUCCESS: Terraform apply of all modules completed successfully" +else + echo "FAILED: Terraform apply of all modules failed" + exit 1 +fi diff --git a/analytics/terraform/superset-on-eks/main.tf b/analytics/terraform/superset-on-eks/main.tf new file mode 100755 index 000000000..fa15b3eb5 --- /dev/null +++ b/analytics/terraform/superset-on-eks/main.tf @@ -0,0 +1,153 @@ +data "aws_availability_zones" "available" {} + +locals { + name = var.name + region = var.region + azs = slice(data.aws_availability_zones.available.names, 0, 2) + tags = { + Blueprint = local.name + GithubRepo = "github.com/awslabs/data-on-eks" + } +} + +#--------------------------------------------------------------- +# EKS Cluster +#--------------------------------------------------------------- +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 19.15" + + cluster_name = local.name + cluster_version = var.eks_cluster_version + + #WARNING: Avoid using this option (cluster_endpoint_public_access = true) in preprod or prod accounts. This feature is designed for sandbox accounts, simplifying cluster deployment and testing. + cluster_endpoint_public_access = true # if true, Your cluster API server is accessible from the internet. You can, optionally, limit the CIDR blocks that can access the public endpoint. + + vpc_id = module.vpc.vpc_id + # Filtering only Secondary CIDR private subnets starting with "100.". Subnet IDs where the EKS Control Plane ENIs will be created + subnet_ids = compact([for subnet_id, cidr_block in zipmap(module.vpc.private_subnets, module.vpc.private_subnets_cidr_blocks) : substr(cidr_block, 0, 4) == "100." ? subnet_id : null]) + + + #manage_aws_auth_configmap = true + + #--------------------------------------- + # Note: This can further restricted to specific required for each Add-on and your application + #--------------------------------------- + # Extend cluster security group rules + cluster_security_group_additional_rules = { + ingress_nodes_ephemeral_ports_tcp = { + description = "Nodes on ephemeral ports" + protocol = "tcp" + from_port = 1025 + to_port = 65535 + type = "ingress" + source_node_security_group = true + } + } + + # Extend node-to-node security group rules + node_security_group_additional_rules = { + ingress_self_all = { + description = "Node to node all ports/protocols" + protocol = "-1" + from_port = 0 + to_port = 0 + type = "ingress" + self = true + } + + # Allows Control Plane Nodes to talk to Worker nodes on all ports. Added this to simplify the example and further avoid issues with Add-ons communication with Control plane. + # This can be restricted further to specific port based on the requirement for each Add-on e.g., coreDNS 53, metrics-server 4443, spark-operator 8080, karpenter 8443 etc. + # Update this according to your security requirements if needed + ingress_cluster_to_node_all_traffic = { + description = "Cluster API to Nodegroup all traffic" + protocol = "-1" + from_port = 0 + to_port = 0 + type = "ingress" + source_cluster_security_group = true + } + } + # cluster_addons = { + # aws-ebs-csi-driver = { + # service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn + # most_recent = true + # resolve_conflicts_on_update = "PRESERVE" + # } + # } + + eks_managed_node_group_defaults = { + iam_role_additional_policies = { + # Not required, but used in the example to access the nodes to inspect mounted volumes + AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + } + } + + eks_managed_node_groups = { + # It's recommended to have a Managed Node group for hosting critical add-ons + # You can leverage nodeSelector and Taints/tolerations to distribute workloads across Managed Node group or Karpenter nodes. + core_node_group = { + name = "core-node-group" + description = "EKS Core node group for hosting critical add-ons" + # Filtering only Secondary CIDR private subnets starting with "100.". Subnet IDs where the nodes/node groups will be provisioned + subnet_ids = compact([for subnet_id, cidr_block in zipmap(module.vpc.private_subnets, module.vpc.private_subnets_cidr_blocks) : substr(cidr_block, 0, 4) == "100." ? subnet_id : null]) + + min_size = 2 + max_size = 6 + desired_size = 2 + + instance_types = ["m5.xlarge"] + + ebs_optimized = true + block_device_mappings = { + xvda = { + device_name = "/dev/xvda" + ebs = { + volume_size = 100 + volume_type = "gp3" + } + } + } + + labels = { + WorkerType = "ON_DEMAND" + NodeGroupType = "core" + } + + tags = merge(local.tags, { + Name = "core-node-grp" }) + }, + + superset_node_group = { + name = "superset-node-group" + description = "Apache Superset node group" + # Filtering only Secondary CIDR private subnets starting with "100.". Subnet IDs where the nodes/node groups will be provisioned + subnet_ids = compact([for subnet_id, cidr_block in zipmap(module.vpc.private_subnets, module.vpc.private_subnets_cidr_blocks) : substr(cidr_block, 0, 4) == "100." ? subnet_id : null]) + + min_size = 4 + max_size = 8 + desired_size = 4 + + instance_types = ["m5.xlarge"] + + ebs_optimized = true + block_device_mappings = { + xvda = { + device_name = "/dev/xvda" + ebs = { + volume_size = 100 + volume_type = "gp3" + } + } + } + + labels = { + WorkerType = "ON_DEMAND" + NodeGroupType = "superset" + } + + tags = merge(local.tags, { + Name = "superset-node-grp" }) + } + } +} diff --git a/analytics/terraform/superset-on-eks/outputs.tf b/analytics/terraform/superset-on-eks/outputs.tf new file mode 100755 index 000000000..c3a5d2a38 --- /dev/null +++ b/analytics/terraform/superset-on-eks/outputs.tf @@ -0,0 +1,11 @@ +output "configure_kubectl" { + description = "Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig" + value = "aws eks --region ${var.region} update-kubeconfig --name ${var.name}" +} + +output "superset_url" { + description = "Configure kubectl: Once the kubeconfig is configured as above, use the below command to get the Superset URL" + value = < https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html +provider "aws" { + alias = "ecr" + region = "us-east-1" +} diff --git a/analytics/terraform/superset-on-eks/values.yaml b/analytics/terraform/superset-on-eks/values.yaml new file mode 100644 index 000000000..f3017bbcc --- /dev/null +++ b/analytics/terraform/superset-on-eks/values.yaml @@ -0,0 +1,831 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default values for superset. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# A README is automatically generated from this file to document it, using helm-docs (see https://github.com/norwoodj/helm-docs) +# To update it, install helm-docs and run helm-docs from the root of this chart + +# -- Provide a name to override the name of the chart +nameOverride: ~ +# -- Provide a name to override the full names of resources +fullnameOverride: ~ + +# -- User ID directive. This user must have enough permissions to run the bootstrap script +# Running containers as root is not recommended in production. Change this to another UID - e.g. 1000 to be more secure +runAsUser: 0 + +# -- Specify service account name to be used +serviceAccountName: ~ +serviceAccount: + # -- Create custom service account for Superset. If create: true and serviceAccountName is not provided, `superset.fullname` will be used. + create: false + annotations: {} + +# -- Install additional packages and do any other bootstrap configuration in this script +# For production clusters it's recommended to build own image with this step done in CI +# @default -- see `values.yaml` +bootstrapScript: | + #!/bin/bash + if [ ! -f ~/bootstrap ]; then echo "Running Superset with uid {{ .Values.runAsUser }}" > ~/bootstrap; fi + +# -- The name of the secret which we will use to generate a superset_config.py file +# Note: this secret must have the key superset_config.py in it and can include other files as well +configFromSecret: '{{ template "superset.fullname" . }}-config' + +# -- The name of the secret which we will use to populate env vars in deployed pods +# This can be useful for secret keys, etc. +envFromSecret: '{{ template "superset.fullname" . }}-env' +# -- This can be a list of templated strings +envFromSecrets: [] + +# -- Extra environment variables that will be passed into pods +extraEnv: {} + # Different gunicorn settings, refer to the gunicorn documentation + # https://docs.gunicorn.org/en/stable/settings.html# + # These variables are used as Flags at the gunicorn startup + # https://github.com/apache/superset/blob/master/docker/run-server.sh#L22 + # Extend timeout to allow long running queries. + # GUNICORN_TIMEOUT: 300 + # Increase the gunicorn worker amount, can improve performance drastically + # See: https://docs.gunicorn.org/en/stable/design.html#how-many-workers + # SERVER_WORKER_AMOUNT: 4 + # WORKER_MAX_REQUESTS: 0 + # WORKER_MAX_REQUESTS_JITTER: 0 + # SERVER_THREADS_AMOUNT: 20 + # GUNICORN_KEEPALIVE: 2 + # SERVER_LIMIT_REQUEST_LINE: 0 + # SERVER_LIMIT_REQUEST_FIELD_SIZE: 0 + + # OAUTH_HOME_DOMAIN: .. + # # If a whitelist is not set, any address that can use your OAuth2 endpoint will be able to login. + # # this includes any random Gmail address if your OAuth2 Web App is set to External. + # OAUTH_WHITELIST_REGEX: ... + +# -- Extra environment variables in RAW format that will be passed into pods +extraEnvRaw: [] + # Load DB password from other secret (e.g. for zalando operator) + # - name: DB_PASS + # valueFrom: + # secretKeyRef: + # name: superset.superset-postgres.credentials.postgresql.acid.zalan.do + # key: password + +# -- Extra environment variables to pass as secrets +extraSecretEnv: {} + # MAPBOX_API_KEY: ... + # # Google API Keys: https://console.cloud.google.com/apis/credentials + # GOOGLE_KEY: ... + # GOOGLE_SECRET: ... + # # Generate your own secret key for encryption. Use openssl rand -base64 42 to generate a good key + # SUPERSET_SECRET_KEY: 'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET' + +# -- Extra files to mount on `/app/pythonpath` +extraConfigs: {} + # import_datasources.yaml: | + # databases: + # - allow_file_upload: true + # allow_ctas: true + # allow_cvas: true + # database_name: example-db + # extra: "{\r\n \"metadata_params\": {},\r\n \"engine_params\": {},\r\n \"\ + # metadata_cache_timeout\": {},\r\n \"schemas_allowed_for_file_upload\": []\r\n\ + # }" + # sqlalchemy_uri: example://example-db.local + # tables: [] + +# -- Extra files to mount on `/app/pythonpath` as secrets +extraSecrets: {} + +extraVolumes: [] + # - name: customConfig + # configMap: + # name: '{{ template "superset.fullname" . }}-custom-config' + # - name: additionalSecret + # secret: + # secretName: my-secret + # defaultMode: 0600 + +extraVolumeMounts: [] + # - name: customConfig + # mountPath: /mnt/config + # readOnly: true + # - name: additionalSecret: + # mountPath: /mnt/secret + +# -- A dictionary of overrides to append at the end of superset_config.py - the name does not matter +# WARNING: the order is not guaranteed +# Files can be passed as helm --set-file configOverrides.my-override=my-file.py +configOverrides: + secret: | + SECRET_KEY = '5WPuGEgPfGTrk9MCVLFkzNk0fO4hyfsykSrM03fHn1m8d3yQQd4yjyvf' + + # extend_timeout: | + # # Extend timeout to allow long running queries. + # SUPERSET_WEBSERVER_TIMEOUT = ... + # enable_oauth: | + # from flask_appbuilder.security.manager import (AUTH_DB, AUTH_OAUTH) + # AUTH_TYPE = AUTH_OAUTH + # OAUTH_PROVIDERS = [ + # { + # "name": "google", + # "whitelist": [ os.getenv("OAUTH_WHITELIST_REGEX", "") ], + # "icon": "fa-google", + # "token_key": "access_token", + # "remote_app": { + # "client_id": os.environ.get("GOOGLE_KEY"), + # "client_secret": os.environ.get("GOOGLE_SECRET"), + # "api_base_url": "https://www.googleapis.com/oauth2/v2/", + # "client_kwargs": {"scope": "email profile"}, + # "request_token_url": None, + # "access_token_url": "https://accounts.google.com/o/oauth2/token", + # "authorize_url": "https://accounts.google.com/o/oauth2/auth", + # "authorize_params": {"hd": os.getenv("OAUTH_HOME_DOMAIN", "")} + # } + # } + # ] + # # Map Authlib roles to superset roles + # AUTH_ROLE_ADMIN = 'Admin' + # AUTH_ROLE_PUBLIC = 'Public' + # # Will allow user self registration, allowing to create Flask users from Authorized User + # AUTH_USER_REGISTRATION = True + # # The default user self registration role + # AUTH_USER_REGISTRATION_ROLE = "Admin" + #secret: | + # SECRET_KEY = '5WPuGEgPfGTrk9MCVLFkzNk0fO4hyfsykSrM03fHn1m8d3yQQd4yjyvf' + +# -- Same as above but the values are files +configOverridesFiles: {} + # extend_timeout: extend_timeout.py + # enable_oauth: enable_oauth.py + +configMountPath: "/app/pythonpath" + +extraConfigMountPath: "/app/configs" + +image: + repository: apachesuperset.docker.scarf.sh/apache/superset + tag: ~ + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +initImage: + repository: apache/superset + tag: dockerize + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 8088 + annotations: {} + # cloud.google.com/load-balancer-type: "Internal" + loadBalancerIP: ~ + nodePort: + # -- (int) + http: nil + +ingress: + enabled: false + ingressClassName: ~ + annotations: {} + # kubernetes.io/tls-acme: "true" + ## Extend timeout to allow long running queries. + # nginx.ingress.kubernetes.io/proxy-connect-timeout: "300" + # nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + # nginx.ingress.kubernetes.io/proxy-send-timeout: "300" + path: / + pathType: ImplementationSpecific + hosts: + - chart-example.local + tls: [] + extraHostsRaw: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # The limits below will apply to all Superset components. To set individual resource limitations refer to the pod specific values below. + # The pod specific values will overwrite anything that is set here. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# -- Custom hostAliases for all superset pods +## https://kubernetes.io/docs/tasks/network/customize-hosts-file-for-pods/ +hostAliases: [] +# - hostnames: +# - nodns.my.lan +# ip: 18.27.36.45 + +# Superset node configuration +supersetNode: + replicaCount: 1 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + + # -- Startup command + # @default -- See `values.yaml` + command: + - "/bin/sh" + - "-c" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; /usr/bin/run-server.sh" + connections: + # -- Change in case of bringing your own redis and then also set redis.enabled:false + redis_host: '{{ .Release.Name }}-redis-headless' + redis_port: "6379" + redis_user: "" + # redis_password: superset + redis_cache_db: "1" + redis_celery_db: "0" + # Or SSL port is usually 6380 + # Update following for using Redis with SSL + redis_ssl: + enabled: false + ssl_cert_reqs: CERT_NONE + db_host: '{{ .Release.Name }}-postgresql' + db_port: "5432" + db_user: superset + db_pass: superset + db_name: superset + env: {} + # -- If true, forces deployment to reload on each upgrade + forceReload: false + # -- Init containers + # @default -- a container waiting for postgres + initContainers: + - name: wait-for-postgres + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: "{{ .Values.initImage.pullPolicy }}" + envFrom: + - secretRef: + name: "{{ tpl .Values.envFromSecret . }}" + command: + - /bin/sh + - -c + - dockerize -wait "tcp://$DB_HOST:$DB_PORT" -timeout 120s + + # -- Launch additional containers into supersetNode pod + extraContainers: [] + # -- Annotations to be added to supersetNode deployment + deploymentAnnotations: {} + # -- Labels to be added to supersetNode deployment + deploymentLabels: {} + # -- Affinity to be added to supersetNode deployment + affinity: {} + # -- TopologySpreadConstrains to be added to supersetNode deployments + topologySpreadConstraints: [] + # -- Annotations to be added to supersetNode pods + podAnnotations: {} + # -- Labels to be added to supersetNode pods + podLabels: {} + startupProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + timeoutSeconds: 1 + failureThreshold: 60 + periodSeconds: 5 + successThreshold: 1 + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + timeoutSeconds: 1 + failureThreshold: 3 + periodSeconds: 15 + successThreshold: 1 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 15 + timeoutSeconds: 1 + failureThreshold: 3 + periodSeconds: 15 + successThreshold: 1 + # -- Resource settings for the supersetNode pods - these settings overwrite might existing values from the global resources object defined above. + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + podSecurityContext: {} + containerSecurityContext: {} + strategy: {} + # type: RollingUpdate + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + +# Superset Celery worker configuration +supersetWorker: + replicaCount: 1 + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + + # -- Worker startup command + # @default -- a `celery worker` command + command: + - "/bin/sh" + - "-c" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app worker" + # -- If true, forces deployment to reload on each upgrade + forceReload: false + # -- Init container + # @default -- a container waiting for postgres and redis + initContainers: + - name: wait-for-postgres-redis + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: "{{ .Values.initImage.pullPolicy }}" + envFrom: + - secretRef: + name: "{{ tpl .Values.envFromSecret . }}" + command: + - /bin/sh + - -c + - dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s + # -- Launch additional containers into supersetWorker pod + extraContainers: [] + # -- Annotations to be added to supersetWorker deployment + deploymentAnnotations: {} + # -- Labels to be added to supersetWorker deployment + deploymentLabels: {} + # -- Affinity to be added to supersetWorker deployment + affinity: {} + # -- TopologySpreadConstrains to be added to supersetWorker deployments + topologySpreadConstraints: [] + # -- Annotations to be added to supersetWorker pods + podAnnotations: {} + # -- Labels to be added to supersetWorker pods + podLabels: {} + # -- Resource settings for the supersetWorker pods - these settings overwrite might existing values from the global resources object defined above. + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + podSecurityContext: {} + containerSecurityContext: {} + strategy: {} + # type: RollingUpdate + # rollingUpdate: + # maxSurge: 25% + # maxUnavailable: 25% + livenessProbe: + exec: + # -- Liveness probe command + # @default -- a `celery inspect ping` command + command: + - sh + - -c + - celery -A superset.tasks.celery_app:app inspect ping -d celery@$HOSTNAME + initialDelaySeconds: 120 + timeoutSeconds: 60 + failureThreshold: 3 + periodSeconds: 60 + successThreshold: 1 + # -- No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic) + startupProbe: {} + # -- No startup/readiness probes by default since we don't really care about its startup time (it doesn't serve traffic) + readinessProbe: {} + +# Superset beat configuration (to trigger scheduled jobs like reports) +supersetCeleryBeat: + # -- This is only required if you intend to use alerts and reports + enabled: false + # -- Command + # @default -- a `celery beat` command + command: + - "/bin/sh" + - "-c" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; celery --app=superset.tasks.celery_app:app beat --pidfile /tmp/celerybeat.pid --schedule /tmp/celerybeat-schedule" + # -- If true, forces deployment to reload on each upgrade + forceReload: false + # -- List of init containers + # @default -- a container waiting for postgres + initContainers: + - name: wait-for-postgres-redis + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: "{{ .Values.initImage.pullPolicy }}" + envFrom: + - secretRef: + name: "{{ tpl .Values.envFromSecret . }}" + command: + - /bin/sh + - -c + - dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s + # -- Launch additional containers into supersetCeleryBeat pods + extraContainers: [] + # -- Annotations to be added to supersetCeleryBeat deployment + deploymentAnnotations: {} + # -- Affinity to be added to supersetCeleryBeat deployment + affinity: {} + # -- TopologySpreadConstrains to be added to supersetCeleryBeat deployments + topologySpreadConstraints: [] + # -- Annotations to be added to supersetCeleryBeat pods + podAnnotations: {} + # -- Labels to be added to supersetCeleryBeat pods + podLabels: {} + # -- Resource settings for the CeleryBeat pods - these settings overwrite might existing values from the global resources object defined above. + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + podSecurityContext: {} + containerSecurityContext: {} + +supersetCeleryFlower: + # -- Enables a Celery flower deployment (management UI to monitor celery jobs) + # WARNING: on superset 1.x, this requires a Superset image that has `flower<1.0.0` installed (which is NOT the case of the default images) + # flower>=1.0.0 requires Celery 5+ which Superset 1.5 does not support + enabled: false + replicaCount: 1 + # -- Command + # @default -- a `celery flower` command + command: + - "/bin/sh" + - "-c" + - "celery --app=superset.tasks.celery_app:app flower" + service: + type: ClusterIP + annotations: {} + loadBalancerIP: ~ + port: 5555 + nodePort: + # -- (int) + http: nil + startupProbe: + httpGet: + path: /api/workers + port: flower + initialDelaySeconds: 5 + timeoutSeconds: 1 + failureThreshold: 60 + periodSeconds: 5 + successThreshold: 1 + livenessProbe: + httpGet: + path: /api/workers + port: flower + initialDelaySeconds: 5 + timeoutSeconds: 1 + failureThreshold: 3 + periodSeconds: 5 + successThreshold: 1 + readinessProbe: + httpGet: + path: /api/workers + port: flower + initialDelaySeconds: 5 + timeoutSeconds: 1 + failureThreshold: 3 + periodSeconds: 5 + successThreshold: 1 + # -- List of init containers + # @default -- a container waiting for postgres and redis + initContainers: + - name: wait-for-postgres-redis + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: "{{ .Values.initImage.pullPolicy }}" + envFrom: + - secretRef: + name: "{{ tpl .Values.envFromSecret . }}" + command: + - /bin/sh + - -c + - dockerize -wait "tcp://$DB_HOST:$DB_PORT" -wait "tcp://$REDIS_HOST:$REDIS_PORT" -timeout 120s + # -- Launch additional containers into supersetCeleryFlower pods + extraContainers: [] + # -- Annotations to be added to supersetCeleryFlower deployment + deploymentAnnotations: {} + # -- Affinity to be added to supersetCeleryFlower deployment + affinity: {} + # -- TopologySpreadConstrains to be added to supersetCeleryFlower deployments + topologySpreadConstraints: [] + # -- Annotations to be added to supersetCeleryFlower pods + podAnnotations: {} + # -- Labels to be added to supersetCeleryFlower pods + podLabels: {} + # -- Resource settings for the CeleryBeat pods - these settings overwrite might existing values from the global resources object defined above. + resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + podSecurityContext: {} + containerSecurityContext: {} + +supersetWebsockets: + # -- This is only required if you intend to use `GLOBAL_ASYNC_QUERIES` in `ws` mode + # see https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries + enabled: false + replicaCount: 1 + ingress: + path: /ws + pathType: Prefix + image: + # -- There is no official image (yet), this one is community-supported + repository: oneacrefund/superset-websocket + tag: latest + pullPolicy: IfNotPresent + # -- The config.json to pass to the server, see https://github.com/apache/superset/tree/master/superset-websocket + # Note that the configuration can also read from environment variables (which will have priority), see https://github.com/apache/superset/blob/master/superset-websocket/src/config.ts for a list of supported variables + # @default -- see `values.yaml` + config: + { + "port": 8080, + "logLevel": "debug", + "logToFile": false, + "logFilename": "app.log", + "statsd": { "host": "127.0.0.1", "port": 8125, "globalTags": [] }, + "redis": + { + "port": 6379, + "host": "127.0.0.1", + "password": "", + "db": 0, + "ssl": false, + }, + "redisStreamPrefix": "async-events-", + "jwtSecret": "CHANGE-ME", + "jwtCookieName": "async-token", + } + service: + type: ClusterIP + annotations: {} + loadBalancerIP: ~ + port: 8080 + nodePort: + # -- (int) + http: nil + command: [] + resources: {} + # -- Launch additional containers into supersetWebsockets pods + extraContainers: [] + deploymentAnnotations: {} + # -- Affinity to be added to supersetWebsockets deployment + affinity: {} + # -- TopologySpreadConstrains to be added to supersetWebsockets deployments + topologySpreadConstraints: [] + podAnnotations: {} + podLabels: {} + strategy: {} + podSecurityContext: {} + containerSecurityContext: {} + startupProbe: + httpGet: + path: /health + port: ws + initialDelaySeconds: 5 + timeoutSeconds: 1 + failureThreshold: 60 + periodSeconds: 5 + successThreshold: 1 + livenessProbe: + httpGet: + path: /health + port: ws + initialDelaySeconds: 5 + timeoutSeconds: 1 + failureThreshold: 3 + periodSeconds: 5 + successThreshold: 1 + readinessProbe: + httpGet: + path: /health + port: ws + initialDelaySeconds: 5 + timeoutSeconds: 1 + failureThreshold: 3 + periodSeconds: 5 + successThreshold: 1 + +init: + # Configure resources + # Warning: fab command consumes a lot of ram and can + # cause the process to be killed due to OOM if it exceeds limit + # Make sure you are giving a strong password for the admin user creation( else make sure you are changing after setup) + # Also change the admin email to your own custom email. + resources: {} + # limits: + # cpu: + # memory: + # requests: + # cpu: + # memory: + # -- Command + # @default -- a `superset_init.sh` command + command: + - "/bin/sh" + - "-c" + - ". {{ .Values.configMountPath }}/superset_bootstrap.sh; . {{ .Values.configMountPath }}/superset_init.sh" + enabled: true + jobAnnotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": "before-hook-creation" + loadExamples: false + createAdmin: true + adminUser: + username: admin + firstname: Superset + lastname: Admin + email: admin@superset.com + password: admin + # -- List of initContainers + # @default -- a container waiting for postgres + initContainers: + - name: wait-for-postgres + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: "{{ .Values.initImage.pullPolicy }}" + envFrom: + - secretRef: + name: "{{ tpl .Values.envFromSecret . }}" + command: + - /bin/sh + - -c + - dockerize -wait "tcp://$DB_HOST:$DB_PORT" -timeout 120s + # -- A Superset init script + # @default -- a script to create admin user and initialize roles + initscript: |- + #!/bin/sh + set -eu + echo "Upgrading DB schema..." + superset db upgrade + echo "Initializing roles..." + superset init + {{ if .Values.init.createAdmin }} + echo "Creating admin user..." + superset fab create-admin \ + --username {{ .Values.init.adminUser.username }} \ + --firstname {{ .Values.init.adminUser.firstname }} \ + --lastname {{ .Values.init.adminUser.lastname }} \ + --email {{ .Values.init.adminUser.email }} \ + --password {{ .Values.init.adminUser.password }} \ + || true + {{- end }} + {{ if .Values.init.loadExamples }} + echo "Loading examples..." + superset load_examples + {{- end }} + if [ -f "{{ .Values.extraConfigMountPath }}/import_datasources.yaml" ]; then + echo "Importing database connections.... " + superset import_datasources -p {{ .Values.extraConfigMountPath }}/import_datasources.yaml + fi + # -- Launch additional containers into init job pod + extraContainers: [] + ## Annotations to be added to init job pods + podAnnotations: {} + podSecurityContext: {} + containerSecurityContext: {} + ## Tolerations to be added to init job pods + tolerations: [] + ## Affinity to be added to init job pods + affinity: {} + # -- TopologySpreadConstrains to be added to init job + topologySpreadConstraints: [] + +# -- Configuration values for the postgresql dependency. +# ref: https://github.com/bitnami/charts/tree/main/bitnami/postgresql +# @default -- see `values.yaml` +postgresql: + ## + ## Use the PostgreSQL chart dependency. + ## Set to false if bringing your own PostgreSQL. + enabled: true + + ## Authentication parameters + auth: + ## The name of an existing secret that contains the postgres password. + existingSecret: + ## PostgreSQL name for a custom user to create + username: superset + ## PostgreSQL password for the custom user to create. Ignored if `auth.existingSecret` with key `password` is provided + password: superset + ## PostgreSQL name for a custom database to create + database: superset + + image: + tag: "14.6.0-debian-11-r13" + + ## PostgreSQL Primary parameters + primary: + ## + ## Persistent Volume Storage configuration. + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes + persistence: + ## + ## Enable PostgreSQL persistence using Persistent Volume Claims. + enabled: true + ## + ## Persistent class + # storageClass: classname + ## + ## Access modes: + accessModes: + - ReadWriteOnce + ## PostgreSQL port + service: + ports: + postgresql: "5432" + +# -- Configuration values for the Redis dependency. +# ref: https://github.com/bitnami/charts/blob/master/bitnami/redis +# More documentation can be found here: https://artifacthub.io/packages/helm/bitnami/redis +# @default -- see `values.yaml` +redis: + ## + ## Use the redis chart dependency. + ## + ## If you are bringing your own redis, you can set the host in supersetNode.connections.redis_host + ## + ## Set to false if bringing your own redis. + enabled: true + ## + ## Set architecture to standalone/replication + architecture: standalone + ## + ## Auth configuration: + ## + auth: + ## Enable password authentication + enabled: false + ## The name of an existing secret that contains the redis password. + existingSecret: "" + ## Name of the key containing the secret. + existingSecretKey: "" + ## Redis password + password: superset + ## + ## Master configuration + ## + master: + ## + ## Image configuration + # image: + ## + ## docker registry secret names (list) + # pullSecrets: nil + ## + persistence: + ## + ## Use a PVC to persist data. + enabled: false + ## + ## Persistent class + # storageClass: classname + ## + ## Access mode: + accessModes: + - ReadWriteOnce + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# -- TopologySpreadConstrains to be added to all deployments +topologySpreadConstraints: [] diff --git a/analytics/terraform/superset-on-eks/variables.tf b/analytics/terraform/superset-on-eks/variables.tf new file mode 100755 index 000000000..49366ba68 --- /dev/null +++ b/analytics/terraform/superset-on-eks/variables.tf @@ -0,0 +1,32 @@ +variable "name" { + description = "Name of the VPC and EKS Cluster" + default = "superset-on-eks" + type = string +} + +variable "region" { + description = "Region" + type = string + default = "us-east-1" +} + +variable "eks_cluster_version" { + description = "EKS Cluster version" + default = "1.29" + type = string +} + +# VPC with 2046 IPs (10.1.0.0/21) and 2 AZs +variable "vpc_cidr" { + description = "VPC CIDR. This should be a valid private (RFC 1918) CIDR range" + default = "10.1.0.0/21" + type = string +} + +# RFC6598 range 100.64.0.0/10 +# Note you can only /16 range to VPC. You can add multiples of /16 if required +variable "secondary_cidr_blocks" { + description = "Secondary CIDR blocks to be attached to VPC" + default = ["100.64.0.0/16"] + type = list(string) +} diff --git a/analytics/terraform/superset-on-eks/versions.tf b/analytics/terraform/superset-on-eks/versions.tf new file mode 100644 index 000000000..591d6754b --- /dev/null +++ b/analytics/terraform/superset-on-eks/versions.tf @@ -0,0 +1,29 @@ +terraform { + required_version = ">= 1.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.72" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = ">= 2.10" + } + helm = { + source = "hashicorp/helm" + version = ">= 2.4.1" + } + random = { + source = "hashicorp/random" + version = "3.3.2" + } + } + + # ## Used for end-to-end testing on project; update to suit your needs + # backend "s3" { + # bucket = "doeks-github-actions-e2e-test-state" + # region = "us-west-2" + # key = "e2e/superset/terraform.tfstate" + # } +} diff --git a/analytics/terraform/superset-on-eks/vpc.tf b/analytics/terraform/superset-on-eks/vpc.tf new file mode 100755 index 000000000..e9d80ee29 --- /dev/null +++ b/analytics/terraform/superset-on-eks/vpc.tf @@ -0,0 +1,51 @@ +locals { + # Routable Private subnets only for Private NAT Gateway -> Transit Gateway -> Second VPC for overlapping CIDRs + # e.g., var.vpc_cidr = "10.1.0.0/21" => output: ["10.1.0.0/24", "10.1.1.0/24"] => 256-2 = 254 usable IPs per subnet/AZ + private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 3, k)] + # Routable Public subnets with NAT Gateway and Internet Gateway + # e.g., var.vpc_cidr = "10.1.0.0/21" => output: ["10.1.2.0/26", "10.1.2.64/26"] => 64-2 = 62 usable IPs per subnet/AZ + public_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 5, k + 8)] + # RFC6598 range 100.64.0.0/16 for EKS Data Plane for two subnets(32768 IPs per Subnet) across two AZs for EKS Control Plane ENI + Nodes + Pods + # e.g., var.secondary_cidr_blocks = "100.64.0.0/16" => output: ["100.64.0.0/17", "100.64.128.0/17"] => 32768-2 = 32766 usable IPs per subnet/AZ + secondary_ip_range_private_subnets = [for k, v in local.azs : cidrsubnet(element(var.secondary_cidr_blocks, 0), 1, k)] +} + +#--------------------------------------------------------------- +# VPC +#--------------------------------------------------------------- +# WARNING: This VPC module includes the creation of an Internet Gateway and NAT Gateway, which simplifies cluster deployment and testing, primarily intended for sandbox accounts. +# IMPORTANT: For preprod and prod use cases, it is crucial to consult with your security team and AWS architects to design a private infrastructure solution that aligns with your security requirements + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.0" + + name = local.name + cidr = var.vpc_cidr + azs = local.azs + + # Secondary CIDR block attached to VPC for EKS Control Plane ENI + Nodes + Pods + secondary_cidr_blocks = var.secondary_cidr_blocks + + # 1/ EKS Data Plane secondary CIDR blocks for two subnets across two AZs for EKS Control Plane ENI + Nodes + Pods + # 2/ Two private Subnets with RFC1918 private IPv4 address range for Private NAT + NLB + Airflow + EC2 Jumphost etc. + private_subnets = concat(local.private_subnets, local.secondary_ip_range_private_subnets) + + # ------------------------------ + # Optional Public Subnets for NAT and IGW for PoC/Dev/Test environments + # Public Subnets can be disabled while deploying to Production and use Private NAT + TGW + public_subnets = local.public_subnets + enable_nat_gateway = true + single_nat_gateway = true + #------------------------------- + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = 1 + } + + tags = local.tags +} diff --git a/website/docs/blueprints/data-analytics/img/superset1.png b/website/docs/blueprints/data-analytics/img/superset1.png new file mode 100644 index 0000000000000000000000000000000000000000..dcfd7e76a37b138c0899a21ddd389cb9af44d5a8 GIT binary patch literal 52076 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 b0DjvM`vrQsQ__XEfWHeG7tiJYZhPzBj&L~8 literal 0 HcmV?d00001 diff --git a/website/docs/blueprints/data-analytics/img/superset2.png b/website/docs/blueprints/data-analytics/img/superset2.png new file mode 100644 index 0000000000000000000000000000000000000000..ac9f22ca0f3a4e4cb746df7fae5ca26fa50b3140 GIT binary patch literal 81800 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 mP^gJLR;t2}^s=k<= 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 K&B~9v$NdNMTI5Xt literal 0 HcmV?d00001 diff --git a/website/docs/blueprints/data-analytics/superset-on-eks.md b/website/docs/blueprints/data-analytics/superset-on-eks.md new file mode 100644 index 000000000..880badd4e --- /dev/null +++ b/website/docs/blueprints/data-analytics/superset-on-eks.md @@ -0,0 +1,102 @@ +--- +sidebar_position: 5 +sidebar_label: Superset on EKS +--- +# Superset on EKS + +## Introduction +[Apache Superset](https://superset.apache.org/) is a popular open source data exploration and visualization platform. Superset provides a rich set of data visualizations and easy ad-hoc query and analysis functionalities for data scientists, analysts, and business users. + +This [blueprint](https://github.com/awslabs/data-on-eks/tree/main/analytics/terraform/superset-on-eks) deploys Superset on an EKS cluster using Postgres as the backend database and Amazon Elastic Block Store (Amazon EBS) for persistent storage. + +## Superset on AWS + +On AWS, Superset can run on an EKS cluster. By using EKS, you can leverage Kubernetes for deployment, scaling, and management of Superset services. Other AWS services like VPC, IAM, and EBS provide the networking, security, and storage capabilities. + +Key AWS services used: + +- Amazon EKS as the managed Kubernetes cluster to run Superset pods and services. +- Amazon EBS to provide a scalable block store for Superset persistent storage. +- Amazon ECR to store Docker container images for Superset and dependencies + +## Deploying the Solution + +The blueprint performs the following to deploy Superset on EKS: + +- Create a new VPC with public and private subnets +- Provision an EKS cluster control plane and managed worker nodes +- Create an Amazon EBS file system and access point +- Build Docker images and push to Amazon ECR +- Install Superset and services on EKS via Helm chart +- Expose Superset UI through a load balancer + + Ingress is enabled and AWS LoadBalancer Controller will provision an ALB to expose the Superset frontend UI. + +:::info +You may customize the blueprint by changing values in `variables.tf`, to deploy to a different region (default to `us-west-1` ), use different cluster name, number of subnets / AZs, or disable addons like fluentbit +::: + + +### Prerequisites + +Ensure that you have installed the following tools on your machine. + +1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) +2. [kubectl](https://Kubernetes.io/docs/tasks/tools/) +3. [terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli) +4. [Helm](https://helm.sh) + +### Deploy + +Clone the repository + +```bash +git clone https://github.com/awslabs/data-on-eks.git +``` + +Navigate into one of the example directories and run `install.sh` script + +```bash +cd data-on-eks/analytics/terraform/superset-on-eks +chmod +x install.sh +./install.sh +``` +or simply +```bash +terraform init +terraform apply --auto-approve +``` + + +### Verify Deployment + +After the deployment completes, we can access the Superset UI . For demo purpose, this blueprint creates the Ingress object for the Superset FrontEnd UI with public LoadBalancer. + + +You may find the URL to the Superset frontend from the output superset_url, or by running kubectl command below: + +```sh +kubectl get ingress -n superset + +# OUTPUT should looks like below +NAME CLASS HOSTS ADDRESS PORTS AGE +superset-ingress aws-alb * k8s-superset-***.***.elb.amazonaws.com 80 125m +``` + +Copy the ADDRESS field from the output, then open browser and enter the URL as `http://
/`. Enter `admin` as both user name and password when prompted. We can view the Superset UI like below. + +![img.png](img/superset1.png) +![img.png](img/superset2.png) + +## Cleanup + +To clean up your environment, run the `cleanup.sh` script. + +```bash +chmod +x cleanup.sh +./cleanup.sh +``` +otherwise +```bash +terraform destroy --auto-approve +```