mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-06 04:22:07 +00:00
chore: add terraform configs
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -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
2
terraform/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
export TF_VAR_DO_TOKEN=
|
||||
export TF_VAR_PRIVATE_KEY=
|
||||
16
terraform/environments/production/main.tf
Normal file
16
terraform/environments/production/main.tf
Normal 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"
|
||||
}
|
||||
18
terraform/environments/production/provider.tf
Normal file
18
terraform/environments/production/provider.tf
Normal 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
|
||||
}
|
||||
7
terraform/environments/production/variables.tf
Normal file
7
terraform/environments/production/variables.tf
Normal 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)"
|
||||
}
|
||||
16
terraform/environments/staging/main.tf
Normal file
16
terraform/environments/staging/main.tf
Normal 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"
|
||||
}
|
||||
18
terraform/environments/staging/provider.tf
Normal file
18
terraform/environments/staging/provider.tf
Normal 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
|
||||
}
|
||||
7
terraform/environments/staging/variables.tf
Normal file
7
terraform/environments/staging/variables.tf
Normal 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)"
|
||||
}
|
||||
153
terraform/modules/digitalocean/droplets.tf
Normal file
153
terraform/modules/digitalocean/droplets.tf
Normal 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",
|
||||
]
|
||||
}
|
||||
}
|
||||
14
terraform/modules/digitalocean/get-join-token.sh
Executable file
14
terraform/modules/digitalocean/get-join-token.sh
Executable 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}'
|
||||
75
terraform/modules/digitalocean/networking.tf
Normal file
75
terraform/modules/digitalocean/networking.tf
Normal 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"]
|
||||
}
|
||||
}
|
||||
4
terraform/modules/digitalocean/outputs.tf
Normal file
4
terraform/modules/digitalocean/outputs.tf
Normal file
@@ -0,0 +1,4 @@
|
||||
output "leader_public_ip" {
|
||||
value = digitalocean_droplet.leader.ipv4_address
|
||||
description = "The public IP address of the leader node"
|
||||
}
|
||||
8
terraform/modules/digitalocean/provider.tf
Normal file
8
terraform/modules/digitalocean/provider.tf
Normal file
@@ -0,0 +1,8 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
digitalocean = {
|
||||
source = "digitalocean/digitalocean"
|
||||
version = "~> 2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
44
terraform/modules/digitalocean/variables.tf
Normal file
44
terraform/modules/digitalocean/variables.tf
Normal 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}"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user