From 538fed9a6610c1a2495ec7ec74649fc728029b7c Mon Sep 17 00:00:00 2001 From: jon brookes Date: Mon, 8 Sep 2025 12:15:29 +0100 Subject: [PATCH] feat: Add Terraform configuration for GCP infrastructure and Docker setup --- .gitignore | 5 ++ gcloud/tf/Dockerfile | 10 +++ gcloud/tf/firewall.tf | 17 +++++ gcloud/tf/main.tf | 98 +++++++++++++++++++++++++++++ gcloud/tf/provider.tf | 24 +++++++ gcloud/tf/registry.tf | 14 +++++ gcloud/tf/remote_state.tf | 37 +++++++++++ gcloud/tf/scripts/k3s-vm-startup.sh | 33 ++++++++++ gcloud/tf/terraform.tfvars.example | 14 +++++ gcloud/tf/vars.tf | 31 +++++++++ 10 files changed, 283 insertions(+) create mode 100644 gcloud/tf/Dockerfile create mode 100644 gcloud/tf/firewall.tf create mode 100644 gcloud/tf/main.tf create mode 100644 gcloud/tf/provider.tf create mode 100644 gcloud/tf/registry.tf create mode 100644 gcloud/tf/remote_state.tf create mode 100644 gcloud/tf/scripts/k3s-vm-startup.sh create mode 100644 gcloud/tf/terraform.tfvars.example create mode 100644 gcloud/tf/vars.tf diff --git a/.gitignore b/.gitignore index 644c688..f38ab5f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ vagrant/dev/ubuntu/ansible/ansible_inventory.ini *.cast vagrant/dev/ubuntu/certs/ vagrant/dev/ubuntu/config-dev +.terraform* +registry*.json* +terraform.tfstate** +*history*.txt +*.tfvars diff --git a/gcloud/tf/Dockerfile b/gcloud/tf/Dockerfile new file mode 100644 index 0000000..1f3d329 --- /dev/null +++ b/gcloud/tf/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.12-slim + +# Install dependencies +RUN pip install --no-cache-dir gunicorn httpbin + +# Expose the application port +EXPOSE 80 + +# Launch the application +CMD ["gunicorn", "-b", "0.0.0.0:80", "httpbin:app"] diff --git a/gcloud/tf/firewall.tf b/gcloud/tf/firewall.tf new file mode 100644 index 0000000..8c9e77e --- /dev/null +++ b/gcloud/tf/firewall.tf @@ -0,0 +1,17 @@ +// Firewall +// ---------------------------------- +resource "google_compute_firewall" "allow_http" { + name = "allow-http" + network = "default" + + allow { + protocol = "tcp" + ports = [ + "80", "443", // http/https + "30080" // ports opened to access the python API via NodePort + ] + } + + source_ranges = ["0.0.0.0/0"] + target_tags = ["web"] +} diff --git a/gcloud/tf/main.tf b/gcloud/tf/main.tf new file mode 100644 index 0000000..bd8f6e6 --- /dev/null +++ b/gcloud/tf/main.tf @@ -0,0 +1,98 @@ + + +// Compute +// ---------------------------------- + +// The instance for K3S +resource "google_compute_instance" "k3s" { + name = "k3s-vm-1" + machine_type = "e2-small" # This instance will have 2 Gb of RAM + zone = var.zone + + tags = ["web"] + + // Set the boot disk and the image (10 Gb) + boot_disk { + initialize_params { + image = "debian-cloud/debian-12" + size = 10 + } + } + + // Configuration to be a Spot Instance, to reduce costs + scheduling { + automatic_restart = true + } + # scheduling { + # preemptible = false + # automatic_restart = true + # provisioning_model = "SPOT" + # instance_termination_action = "STOP" + # } + + // attach a disk for K3S + attached_disk { + source = google_compute_disk.k3s_disk.id + device_name = "k3s-disk" + } + + // attach a disk for app data + attached_disk { + source = google_compute_disk.app_data_disk.id + device_name = "app-data-disk" + } + + network_interface { + network = "default" + + // enable ephemeral ip + access_config {} + } + + labels = { + env = var.env + region = var.region + app = var.app_name + sensitive = "false" + } + + metadata_startup_script = file("scripts/k3s-vm-startup.sh") + allow_stopping_for_update = true +} + + +// Storage +// ---------------------------------- + +// The disk attached to the instance (15 Gb) +resource "google_compute_disk" "k3s_disk" { + name = "k3s-disk" + size = 15 + type = "pd-standard" + zone = var.zone +} + +// The disk for app data (20 Gb) +resource "google_compute_disk" "app_data_disk" { + name = "app-data-disk" + size = 20 + type = "pd-standard" + zone = var.zone +} + + + + + +data "google_project" "project" { + project_id = var.project_name # Use variable from tfvars +} + +output "project_number" { + value = data.google_project.project.number +} + +output "k3s_vm_public_ip" { + value = google_compute_instance.k3s.network_interface[0].access_config[0].nat_ip + description = "Ephemeral public IP of the k3s VM" +} diff --git a/gcloud/tf/provider.tf b/gcloud/tf/provider.tf new file mode 100644 index 0000000..ff902f5 --- /dev/null +++ b/gcloud/tf/provider.tf @@ -0,0 +1,24 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = "~> 4.0" + } + } +} + +// Provider +// ---------------------------------- + +// Connect to the GCP project +provider "google" { + # Configuration options + project = var.project_name # Use variable from tfvars + region = "us-central1" # Replace with your desired region +} +# provider "google" { +# credentials = file(".json") +# project = var.project_name +# region = var.region +# zone = var.zone +# } diff --git a/gcloud/tf/registry.tf b/gcloud/tf/registry.tf new file mode 100644 index 0000000..75e374d --- /dev/null +++ b/gcloud/tf/registry.tf @@ -0,0 +1,14 @@ +// Registry +// ---------------------------------- + +// The Artifact Registry repository for our app +resource "google_artifact_registry_repository" "app-repo" { + location = vas.region + repository_id = "app-repo" + description = "App Docker repository" + format = "DOCKER" + + docker_config { + immutable_tags = true + } +} diff --git a/gcloud/tf/remote_state.tf b/gcloud/tf/remote_state.tf new file mode 100644 index 0000000..c58973d --- /dev/null +++ b/gcloud/tf/remote_state.tf @@ -0,0 +1,37 @@ +// Remote state +// ---------------------------------- +# variable "bucket_name" { +# type = string +# default = "headshed-dev-lab-k3s-bucket" +# description = "headshed-dev-lab k3s Bucket" +# } + +# terraform { +# # Use a shared bucket (wich allows collaborative work) +# backend "gcs" { +# bucket = "" +# prefix = "k3s-infra" +# } + +# // Set versions +# required_version = ">=1.8.0" +# required_providers { +# google = { +# source = "hashicorp/google" +# version = ">=4.0.0" +# } +# } +# } + +// The bucket where you can store other data +# resource "google_storage_bucket" "k3s-storage" { +# name = var.bucket_name +# location = var.region + +# labels = { +# env = var.env +# region = var.region +# app = var.app_name +# sensitive = "false" +# } +# } diff --git a/gcloud/tf/scripts/k3s-vm-startup.sh b/gcloud/tf/scripts/k3s-vm-startup.sh new file mode 100644 index 0000000..2753ce5 --- /dev/null +++ b/gcloud/tf/scripts/k3s-vm-startup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Format the disk if not already formatted +if ! lsblk | grep -q "/mnt/disks/k3s"; then + mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-k3s-disk + mkdir -p /mnt/disks/k3s + mount -o discard,defaults /dev/disk/by-id/google-k3s-disk /mnt/disks/k3s + chmod a+w /mnt/disks/k3s +fi + +# ensure only run once +if [[ -f /etc/startup_was_launched ]]; then exit 0; fi +touch /etc/startup_was_launched + +# apt install +apt update +apt install -y ncdu htop + +# helm install +curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 +chmod 700 get_helm.sh +/bin/bash get_helm.sh + +# bashrc config +rc=/root/.bashrc +echo "alias l='ls -lah'" >> $rc +echo "alias ll='ls -lh'" >> $rc +echo "alias k=kubectl" >> $rc +echo "export dry='--dry-run=client'" >> $rc +echo "export o='-oyaml'" >> $rc + +# Install k3s and configure it to use the persistent disk for data storage +curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--data-dir /mnt/disks/k3s" sh - diff --git a/gcloud/tf/terraform.tfvars.example b/gcloud/tf/terraform.tfvars.example new file mode 100644 index 0000000..8612137 --- /dev/null +++ b/gcloud/tf/terraform.tfvars.example @@ -0,0 +1,14 @@ +// Your GCP project name +// it will be refererred as the project id +// in Google Cloud +// ---------------------------------- +project_name = "" + +// application name +app_name = "your-projects-k3s-cluster" + +// where to deploy to +// region +region = "us-central1" +zone = "us-central1-a" + diff --git a/gcloud/tf/vars.tf b/gcloud/tf/vars.tf new file mode 100644 index 0000000..8f85371 --- /dev/null +++ b/gcloud/tf/vars.tf @@ -0,0 +1,31 @@ +// Env vars +// ---------------------------------- + +variable "project_name" { + type = string +} + +variable "env" { + type = string + default = "dev" + description = "Environment" +} + +variable "region" { + type = string + default = vars.region + description = "GCP Region" +} + +variable "zone" { + type = string + default = vars.zone + description = "GCP Zone" +} + +variable "app_name" { + type = string + default = var.app_name + description = "Application name" +} +