chore: add terraform configs

This commit is contained in:
Christy Jacob
2024-08-08 01:37:18 +04:00
parent 45c98bb9ea
commit 4b571c469d
14 changed files with 386 additions and 1 deletions

5
.gitignore vendored
View File

@@ -15,4 +15,7 @@ package-lock.json
.vscode
# Sentry Config File
.sentryclirc
.history
.history
terraform/**/.t*
terraform/**/.env
terraform/**/**/*.tfstate*

2
terraform/.env.example Normal file
View File

@@ -0,0 +1,2 @@
export TF_VAR_DO_TOKEN=
export TF_VAR_PRIVATE_KEY=

View File

@@ -0,0 +1,16 @@
module "droplets" {
source = "../../modules/digitalocean"
private_key = "${var.PRIVATE_KEY}"
project_name = "hmp"
region = "fra1"
environment = "prd"
base_image = "docker-20-04"
worker_size = "s-2vcpu-2gb-amd"
worker_count = 6
subnet_range = "10.117.0.0/20"
manager_size = "s-2vcpu-2gb-amd"
manager_count = 2
digitalocean_project_name = "Production - Homepage"
}

View File

@@ -0,0 +1,18 @@
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
cloud {
organization = "appwrite"
workspaces {
name = "production-homepage"
}
}
}
provider "digitalocean" {
token = var.DO_TOKEN
}

View File

@@ -0,0 +1,7 @@
variable "DO_TOKEN" {
description = "DigitalOcean API token"
}
variable "PRIVATE_KEY" {
description = "Contents of your local SSH private key file"
default = "$(cat ~/.ssh/id_rsa)"
}

View File

@@ -0,0 +1,16 @@
module "droplets" {
source = "../../modules/digitalocean"
private_key = "${var.PRIVATE_KEY}"
project_name = "hmp"
region = "fra1"
environment = "stg"
base_image = "docker-20-04"
subnet_range = "10.116.0.0/20"
worker_size = "s-1vcpu-2gb"
worker_count = 4
manager_size = "s-1vcpu-2gb"
manager_count = 2
digitalocean_project_name = "Staging - Homepage"
}

View File

@@ -0,0 +1,18 @@
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
cloud {
organization = "appwrite"
workspaces {
name = "staging-homepage"
}
}
}
provider "digitalocean" {
token = var.DO_TOKEN
}

View File

@@ -0,0 +1,7 @@
variable "DO_TOKEN" {
description = "DigitalOcean API token"
}
variable "PRIVATE_KEY" {
description = "Contents of your local SSH private key file"
default = "$(cat ~/.ssh/id_rsa)"
}

View File

@@ -0,0 +1,153 @@
locals {
mount_nfs = "/letsencrypt"
setup_firewall = [
"ufw allow 2377/tcp",
"ufw allow 7946/tcp",
"ufw allow 7946/udp",
"ufw allow 4789/udp",
"ufw reload",
"systemctl restart docker"
]
setup_nfs = [
"ufw allow 2049",
"ufw reload",
"apt install -y nfs-common",
"mkdir -p ${local.mount_nfs}",
"echo '${digitalocean_droplet.nfs.ipv4_address_private}:${local.mount_nfs} ${local.mount_nfs} nfs proto=tcp,port=2049,nfsvers=4,sync,noexec,rw 0 0' >> /etc/fstab",
"mount -a",
]
}
resource "digitalocean_project" "homepage" {
name = var.digitalocean_project_name
description = "Appwrite Homepage"
purpose = "Web Application"
environment = "Development"
resources = flatten([
digitalocean_droplet.leader.urn,
digitalocean_droplet.manager[*].urn,
digitalocean_droplet.worker[*].urn,
digitalocean_droplet.nfs.urn
])
}
# Tags
resource "digitalocean_tag" "worker" {
name = "${var.environment}-worker"
}
resource "digitalocean_tag" "manager" {
name = "${var.environment}-manager"
}
resource "digitalocean_droplet" "leader" {
image = var.base_image
name = "${var.project_name}-${var.region}-${var.environment}-leader-0"
region = var.region
size = var.manager_size
tags = [digitalocean_tag.manager.id]
ssh_keys = [
data.digitalocean_ssh_key.Christy.id
]
vpc_uuid = digitalocean_vpc.subnet.id
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = var.private_key
timeout = "2m"
}
provisioner "remote-exec" {
inline = concat(local.setup_firewall, local.setup_nfs, [
"docker swarm init --advertise-addr ${self.ipv4_address_private}"
])
}
}
resource "digitalocean_droplet" "manager" {
count = var.manager_count
image = var.base_image
name = "${var.project_name}-${var.region}-${var.environment}-manager-${count.index}"
region = var.region
size = var.manager_size
tags = [digitalocean_tag.manager.id]
vpc_uuid = digitalocean_vpc.subnet.id
ssh_keys = [
data.digitalocean_ssh_key.Christy.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = var.private_key
timeout = "2m"
}
provisioner "remote-exec" {
inline = concat(local.setup_firewall, local.setup_nfs, [
"docker swarm join --token ${data.external.swarm_join_token.result.manager} ${digitalocean_droplet.leader.ipv4_address_private}:2377"
])
}
}
resource "digitalocean_droplet" "worker" {
count = var.worker_count
image = var.base_image
name = "${var.project_name}-${var.region}-${var.environment}-worker-${count.index}"
region = var.region
size = var.worker_size
tags = [digitalocean_tag.worker.id]
vpc_uuid = digitalocean_vpc.subnet.id
ssh_keys = [
data.digitalocean_ssh_key.Christy.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = var.private_key
timeout = "2m"
}
provisioner "remote-exec" {
inline = concat(local.setup_firewall, [
"docker swarm join --token ${data.external.swarm_join_token.result.worker} ${digitalocean_droplet.leader.ipv4_address_private}:2377"
])
}
}
resource "digitalocean_droplet" "nfs" {
image = var.base_image
name = "${var.project_name}-${var.region}-${var.environment}-nfs-0"
region = var.region
size = var.worker_size
vpc_uuid = digitalocean_vpc.subnet.id
ssh_keys = [
data.digitalocean_ssh_key.Christy.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = var.private_key
timeout = "2m"
}
provisioner "remote-exec" {
inline = [
"ufw allow 2049",
"ufw reload",
"sudo apt update",
"sudo apt install -y nfs-kernel-server",
"mkdir -p ${local.mount_nfs}",
"echo '${local.mount_nfs} ${var.subnet_range}(rw,sync,no_root_squash,no_subtree_check)' >> /etc/exports",
"exportfs -arvf",
"systemctl restart nfs-kernel-server",
]
}
}

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# Exit if any of the intermediate steps fail
set -e
# Extract input variables
eval "$(jq -r '@sh "HOST=\(.host)"')"
# Get worker join token
WORKER=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$HOST docker swarm join-token worker -q)
MANAGER=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$HOST docker swarm join-token manager -q)
# Pass back a JSON object
jq -n --arg worker $WORKER --arg manager $MANAGER '{"worker":$worker,"manager":$manager}'

View File

@@ -0,0 +1,75 @@
# VPC
resource "digitalocean_vpc" "subnet" {
name = "${var.environment}-subnet"
region = var.region
ip_range = var.subnet_range
}
# Firewall Rules
resource "digitalocean_firewall" "web" {
name = "${var.environment}-web"
tags = [digitalocean_tag.worker.id, digitalocean_tag.manager.id]
# HTTP/HTTPS
inbound_rule {
protocol = "tcp"
port_range = "80"
source_addresses = ["0.0.0.0/0", "::/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "443"
source_addresses = ["0.0.0.0/0", "::/0"]
}
# Outbound communication
outbound_rule {
protocol = "tcp"
port_range = "all"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "udp"
port_range = "all"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "icmp"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
}
resource "digitalocean_firewall" "vpc_communication" {
name = "${var.environment}-vpc-communication"
droplet_ids = [ digitalocean_droplet.nfs.id ]
tags = [digitalocean_tag.worker.id, digitalocean_tag.manager.id]
# Internal communication
inbound_rule {
protocol = "tcp"
port_range = "all"
source_addresses = [var.subnet_range]
}
inbound_rule {
protocol = "udp"
port_range = "all"
source_addresses = [var.subnet_range]
}
}
resource "digitalocean_firewall" "ssh" {
name = "${var.environment}-ssh"
droplet_ids = [ digitalocean_droplet.nfs.id ]
tags = [digitalocean_tag.worker.id, digitalocean_tag.manager.id]
# SSH
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = ["0.0.0.0/0", "::/0"]
}
}

View File

@@ -0,0 +1,4 @@
output "leader_public_ip" {
value = digitalocean_droplet.leader.ipv4_address
description = "The public IP address of the leader node"
}

View File

@@ -0,0 +1,8 @@
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}

View File

@@ -0,0 +1,44 @@
variable "private_key" {
description = "The path to the private key used to SSH into the droplets"
}
variable "project_name" {
description = "Name for the current infrastructure project"
}
variable "region" {
description = "The region to deploy the infrastructure to. See https://docs.digitalocean.com/products/platform/availability-matrix/#available-datacenters"
}
variable "environment" {
description = "Name of the current environment"
}
variable "base_image" {
description = "Base Image to use for all droplets"
}
variable "subnet_range" {
description = "Subnet range for the VPC"
}
variable "worker_size" {
description = "Size of the NFS node. See https://slugs.do-api.dev/"
}
variable "worker_count" {
description = "Count of worker nodes required"
}
variable "manager_size" {
description = "Size of the manager node. See https://slugs.do-api.dev/"
}
variable "manager_count" {
description = "Count of API nodes required"
}
variable "digitalocean_project_name" {
description = "Name of the DigitalOcean Project"
}
data "digitalocean_ssh_key" "Christy" {
name = "Christy"
}
data "external" "swarm_join_token" {
program = ["${path.module}/get-join-token.sh"]
query = {
host = "${digitalocean_droplet.leader.ipv4_address}"
}
}