Skip to content

Commit

Permalink
feat: implement terraform modules and infrastructure example
Browse files Browse the repository at this point in the history
Signed-off-by: Gabor Boros <[email protected]>
  • Loading branch information
gabor-boros committed Dec 6, 2024
1 parent 26e9310 commit 5e54e34
Show file tree
Hide file tree
Showing 21 changed files with 1,050 additions and 124 deletions.
50 changes: 50 additions & 0 deletions infra-examples/digitalocean/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# DigitalOcean Infrastructure Example

This is an example implementation to create a production grade infrastructure for hosting Open edX instances on DigitalOcean, using the Terraform modules provided by Harmony.

## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_digitalocean"></a> [digitalocean](#requirement\_digitalocean) | >=2.45 |
| <a name="requirement_helm"></a> [helm](#requirement\_helm) | >=2.16 |
| <a name="requirement_kubectl"></a> [kubectl](#requirement\_kubectl) | >=1.17 |
| <a name="requirement_kubernetes"></a> [kubernetes](#requirement\_kubernetes) | >=2.34 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_digitalocean"></a> [digitalocean](#provider\_digitalocean) | 2.45.0 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_kubernetes_cluster"></a> [kubernetes\_cluster](#module\_kubernetes\_cluster) | ../../terraform/modules/digitalocean/doks | n/a |
| <a name="module_main_vpc"></a> [main\_vpc](#module\_main\_vpc) | ../../terraform/modules/digitalocean/vpc | n/a |
| <a name="module_mongodb_database"></a> [mongodb\_database](#module\_mongodb\_database) | ../../terraform/modules/digitalocean/database | n/a |
| <a name="module_mysql_database"></a> [mysql\_database](#module\_mysql\_database) | ../../terraform/modules/digitalocean/database | n/a |
| <a name="module_spaces"></a> [spaces](#module\_spaces) | ../../terraform/modules/digitalocean/spaces | n/a |

## Resources

| Name | Type |
|------|------|
| [digitalocean_database_db.forum_database](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/database_db) | resource |
| [digitalocean_project.project](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/project) | resource |
| [digitalocean_kubernetes_cluster.cluster](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/kubernetes_cluster) | data source |
| [digitalocean_kubernetes_versions.available_versions](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/kubernetes_versions) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_do_access_token"></a> [do\_access\_token](#input\_do\_access\_token) | DitialOcean access token. | `string` | n/a | yes |
| <a name="input_environment"></a> [environment](#input\_environment) | The DigitalOcean project environment. | `string` | n/a | yes |
| <a name="input_kubernetes_cluster_name"></a> [kubernetes\_cluster\_name](#input\_kubernetes\_cluster\_name) | Name of the DigitalOcean Kubernetes cluster to create. | `string` | n/a | yes |
| <a name="input_region"></a> [region](#input\_region) | DigitalOcean region to create the resources in. | `string` | n/a | yes |

## Outputs

No outputs.
55 changes: 0 additions & 55 deletions infra-examples/digitalocean/k8s-cluster/main.tf

This file was deleted.

160 changes: 91 additions & 69 deletions infra-examples/digitalocean/main.tf
Original file line number Diff line number Diff line change
@@ -1,94 +1,116 @@
# A cluster to test proof of concept on DigitalOcean
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = ">=2.23"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "2.15.0"
}
kubectl = {
source = "gavinbunney/kubectl"
version = "1.14.0"
}
helm = {
source = "hashicorp/helm"
version = "2.7.1"
}
}
locals {
instances = [
"my-instance-1",
"my-instance-2",
"my-instance-3",
]
}

# Configure the DigitalOcean Provider
provider "digitalocean" {
token = var.do_token
}
data "digitalocean_kubernetes_versions" "available_versions" {}

module "main_vpc" {
source = "../../terraform/modules/digitalocean/vpc"

variable "cluster_name" { type = string }
variable "do_token" {
type = string
sensitive = true
region = var.region
environment = var.environment
}

module "k8s_cluster" {
source = "./k8s-cluster"
module "kubernetes_cluster" {
source = "../../terraform/modules/digitalocean/doks"

region = var.region
environment = var.environment
vpc_id = module.main_vpc.vpc_id

cluster_name = var.cluster_name
# max_worker_node_count = var.max_worker_node_count
# min_worker_node_count = var.min_worker_node_count
# worker_node_size = var.worker_node_size
# region = var.do_region
# vpc_uuid = digitalocean_vpc.main_vpc.id
# vpc_ip_range = var.vpc_ip_range
cluster_name = var.kubernetes_cluster_name
kubernetes_version = data.digitalocean_kubernetes_versions.available_versions.latest_version
}

# Pre-declare data sources that we can use to get the cluster ID and auth info, once it's created
data "digitalocean_kubernetes_cluster" "cluster" {
name = var.cluster_name
# Set the depends_on so that the data source doesn't
# try to read from a cluster that doesn't exist, causing
# failures when trying to run a `tofu plan`.
depends_on = [module.k8s_cluster.cluster_id]
module "spaces" {
source = "../../terraform/modules/digitalocean/spaces"

region = var.region
environment = var.environment

bucket_prefix = "my-institute"
}

# Configure Kubernetes provider
provider "kubernetes" {
host = data.digitalocean_kubernetes_cluster.cluster.endpoint
token = data.digitalocean_kubernetes_cluster.cluster.kube_config[0].token
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate)
module "mysql_database" {
source = "../../terraform/modules/digitalocean/database"

region = var.region
environment = var.environment
access_token = var.do_access_token
vpc_id = module.main_vpc.vpc_id
kubernetes_cluster_name = var.kubernetes_cluster_name

database_engine = "mysql"
database_engine_version = 8
database_cluster_instances = 1
database_cluster_instance_size = "db-s-1vcpu-1gb"
database_maintenance_window_day = "sunday"
database_maintenance_window_time = "01:00:00"

# Database cluster firewalls cannot use VPC CIDR, therefore the access is
# limited to the k8s cluster
firewall_rules = [
{
type = "k8s"
value = module.kubernetes_cluster.cluster_id
},
]
}

# Configure Helm provider
provider "helm" {
kubernetes {
host = data.digitalocean_kubernetes_cluster.cluster.endpoint
token = data.digitalocean_kubernetes_cluster.cluster.kube_config[0].token
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate)
module "mongodb_database" {
source = "../../terraform/modules/digitalocean/database"

region = var.region
environment = var.environment
access_token = var.do_access_token
vpc_id = module.main_vpc.vpc_id
kubernetes_cluster_name = var.kubernetes_cluster_name

database_engine = "mongodb"
database_engine_version = 7
database_cluster_instances = 3
database_cluster_instance_size = "db-s-1vcpu-1gb"
database_maintenance_window_day = "sunday"
database_maintenance_window_time = "1:00"

database_users = {
for instance in toset(local.instances) :
instance => {
username = instance,
database = "${instance}-db"
}
}
}

provider "kubectl" {
host = data.digitalocean_kubernetes_cluster.cluster.endpoint
token = data.digitalocean_kubernetes_cluster.cluster.kube_config[0].token
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate)
load_config_file = false
# Database cluster firewalls cannot use VPC CIDR, therefore the access is
# limited to the k8s cluster
firewall_rules = [
{
type = "k8s"
value = module.kubernetes_cluster.cluster_id
},
]
}

resource "digitalocean_database_db" "forum_database" {
for_each = toset(local.instances)

# Declare the kubeconfig as an output - access it anytime with "tofu output -raw kubeconfig"
output "kubeconfig" {
value = module.k8s_cluster.kubeconfig.raw_config
sensitive = true
cluster_id = module.mongodb_database.cluster_id
name = "${each.key}-cs_comments_service"
}

resource "digitalocean_project" "project" {
name = var.cluster_name
description = "Testing the use of Helm to provision a cluster for multi-instance tutor deployment"
name = var.kubernetes_cluster_name
description = "Open edX deployment using Harmony"
purpose = "Web Application"
environment = "Production"

resources = [
module.k8s_cluster.cluster_urn,
module.kubernetes_cluster.cluster_urn,
module.spaces.bucket_urn,
module.mysql_database.cluster_urn,
module.mongodb_database.cluster_urn,
]
}
56 changes: 56 additions & 0 deletions infra-examples/digitalocean/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = ">=2.45"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">=2.34"
}
kubectl = {
source = "gavinbunney/kubectl"
version = ">=1.17"
}
helm = {
source = "hashicorp/helm"
version = ">=2.16"
}
}
}

# Pre-declare data sources that we can use to get the cluster ID and auth info,
# once it's created. Set the `depends_on` so that the data source doesn't try
# to read from a cluster that doesn't exist, causing failures when trying to
# run a `terraform plan`.
data "digitalocean_kubernetes_cluster" "cluster" {
name = module.kubernetes_cluster.cluster_name
depends_on = [module.kubernetes_cluster.cluster_id]
}

provider "digitalocean" {
token = var.do_access_token
spaces_access_id = "DO00M682HAUD3KXUQNL3"
spaces_secret_key = "fnoviZN11Y0NAoeAXxUrOU0liJyKcfP4yQboHJkJKY0"
}

provider "kubernetes" {
host = data.digitalocean_kubernetes_cluster.cluster.endpoint
token = data.digitalocean_kubernetes_cluster.cluster.kube_config[0].token
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate)
}

provider "helm" {
kubernetes {
host = data.digitalocean_kubernetes_cluster.cluster.endpoint
token = data.digitalocean_kubernetes_cluster.cluster.kube_config[0].token
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate)
}
}

provider "kubectl" {
host = data.digitalocean_kubernetes_cluster.cluster.endpoint
token = data.digitalocean_kubernetes_cluster.cluster.kube_config[0].token
cluster_ca_certificate = base64decode(data.digitalocean_kubernetes_cluster.cluster.kube_config[0].cluster_ca_certificate)
load_config_file = false
}
35 changes: 35 additions & 0 deletions infra-examples/digitalocean/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
variable "do_access_token" {
type = string
description = "DitialOcean access token."
sensitive = true
}

variable "kubernetes_cluster_name" {
type = string
description = "Name of the DigitalOcean Kubernetes cluster to create."
}

variable "region" {
type = string
description = "DigitalOcean region to create the resources in."
validation {
condition = contains([
"ams3",
"blr1",
"fra1",
"lon1",
"nyc3",
"sfo2",
"sfo3",
"sgp1",
"syd1",
"tor1",
], var.region)
error_message = "The DigitalOcean region must be in the acceptable region list."
}
}

variable "environment" {
type = string
description = "The DigitalOcean project environment."
}
Loading

0 comments on commit 5e54e34

Please sign in to comment.