feat: Add Terraform configuration for GCP infrastructure and Docker setup

This commit is contained in:
jon brookes 2025-09-08 12:15:29 +01:00
parent 94499fd16e
commit 538fed9a66
10 changed files with 283 additions and 0 deletions

10
gcloud/tf/Dockerfile Normal file
View file

@ -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"]

17
gcloud/tf/firewall.tf Normal file
View file

@ -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"]
}

98
gcloud/tf/main.tf Normal file
View file

@ -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"
}

24
gcloud/tf/provider.tf Normal file
View file

@ -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("<my-gcp-creds>.json")
# project = var.project_name
# region = var.region
# zone = var.zone
# }

14
gcloud/tf/registry.tf Normal file
View file

@ -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
}
}

37
gcloud/tf/remote_state.tf Normal file
View file

@ -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 = "<my-bucket-for-states>"
# 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"
# }
# }

View file

@ -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 -

View file

@ -0,0 +1,14 @@
// Your GCP project name
// it will be refererred as the project id
// in Google Cloud
// ----------------------------------
project_name = "<your gpc project name>"
// application name
app_name = "your-projects-k3s-cluster"
// where to deploy to
// region
region = "us-central1"
zone = "us-central1-a"

31
gcloud/tf/vars.tf Normal file
View file

@ -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"
}