diff --git a/deploy/gcp/.gitignore b/deploy/gcp/.gitignore new file mode 100644 index 0000000..908b99c --- /dev/null +++ b/deploy/gcp/.gitignore @@ -0,0 +1,6 @@ +.env +.auto.tfvars +.terraform +terraform.tfstate +terraform.tfstate.* +.terraform.lock.hcl diff --git a/deploy/gcp/Makefile b/deploy/gcp/Makefile new file mode 100644 index 0000000..b8a0e59 --- /dev/null +++ b/deploy/gcp/Makefile @@ -0,0 +1,81 @@ +# USAGE: +# 0: Install `docker` and `terraform` (Process specific to your system) +# 1: Copy the sample.env file to .env +# 2: Fillout the GCP info in .env +# 3: Edit GCP_REPO to the correct docker image repo path if you are using something other than Container registry +# 4: Edit the PREFIX if you would like images and GKE entities to be prefixed with something else +# 5: Run `make init` to initialize terraform +# 6: Follow the normal Preplexica configuration steps outlined in the project readme +# 7: Run `make build-deplpy` to build and push the project images to the repo, create a GKE cluster and deploy the app +# +# NOTES/ WARNINGS: +# - The app endpoint is unsecured and exposed to the internet at large, you will need to implement your desired auth +# - No auto scaling is enabled for this cluster and deployments, edit the terraform files accordingly for that +include .env + +# Use `location-id-docker.pkg` for artifact registry Ex. west-1-docker.pkg +GCP_REPO=gcr.io +PREFIX=perplexica +SEARCH_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-searxng:latest +BACKEND_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-backend:latest +APP_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-app:latest +CLUSTER_NAME=$(PREFIX)-cluster + + +.PHONY: build-deplpy +build-deplpy: docker-build-all deploy + + +.PHONY: docker-build-all +docker-build-all: docker-build-push-searxng docker-build-push-backend docker-build-push-app + + +.PHONY: show_config +show_config: + @echo $(GCP_PROJECT_ID) \ + && echo $(CLUSTER_NAME) \ + && echo $(GCP_REGION) \ + && echo $(GCP_SERVICE_ACCOUNT_KEY_FILE) \ + && echo $(SEARCH_IMAGE_TAG) \ + && echo $(BACKEND_IMAGE_TAG) \ + && echo $(APP_IMAGE_TAG) + + +.PHONY: docker-build-push-searxng +docker-build-push-searxng: + cd ../../ && docker build -f ./searxng.dockerfile -t $(SEARCH_IMAGE_TAG) . --platform="linux/amd64" + docker push $(SEARCH_IMAGE_TAG) + + +.PHONY: docker-build-push-backend +docker-build-push-backend: + cd ../../ && docker build -f ./backed.dockerfile -t $(BACKEND_IMAGE_TAG) . --platform="linux/amd64" + docker push $(BACKEND_IMAGE_TAG) + + +.PHONY: docker-build-push-app +docker-build-push-app: + cd ../../ && docker build -f ./app.dockerfile -t $(APP_IMAGE_TAG) . --platform="linux/amd64" + docker push $(APP_IMAGE_TAG) + + +.PHONY: init +init: + terraform init + + +.PHONY: deploy +deploy: + export TF_VAR_project_id=$(GCP_PROJECT_ID) \ + && export TF_VAR_cluster_name=$(CLUSTER_NAME) \ + && export TF_VAR_region=$(GCP_REGION) \ + && export TF_VAR_key_file=$(GCP_SERVICE_ACCOUNT_KEY_FILE) \ + && export TF_VAR_search_image=$(SEARCH_IMAGE_TAG) \ + && export TF_VAR_backend_image=$(BACKEND_IMAGE_TAG) \ + && export TF_VAR_app_image=$(APP_IMAGE_TAG) \ + && terraform apply + + +.PHONY: teardown +teardown: + terraform destroy diff --git a/deploy/gcp/gke-cluster/main.tf b/deploy/gcp/gke-cluster/main.tf new file mode 100644 index 0000000..e879edd --- /dev/null +++ b/deploy/gcp/gke-cluster/main.tf @@ -0,0 +1,59 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = "5.28.0" + } + } +} + +variable "project_id" { + description = "The ID of the project in which resources will be deployed." + type = string +} + +variable "name" { + description = "The GKE Cluster name" + type = string +} + +variable "region" { + description = "The GCP region to deploy to." + type = string +} + +variable "key_file" { + description = "The path to the GCP service account key file." + type = string +} + +provider "google" { + credentials = file(var.key_file) + project = var.project_id + region = var.region +} + +resource "google_container_cluster" "cluster" { + name = var.name + location = var.region + initial_node_count = 1 + remove_default_node_pool = true +} + +resource "google_container_node_pool" "primary_preemptible_nodes" { + name = "${google_container_cluster.cluster.name}-node-pool" + location = var.region + cluster = google_container_cluster.cluster.name + node_count = 1 + + node_config { + machine_type = "n1-standard-4" + disk_size_gb = 25 + spot = true + oauth_scopes = [ + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + ] + } +} diff --git a/deploy/gcp/main.tf b/deploy/gcp/main.tf new file mode 100644 index 0000000..e821a6e --- /dev/null +++ b/deploy/gcp/main.tf @@ -0,0 +1,133 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = "5.28.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + } + } +} + +provider "google" { + credentials = file(var.key_file) + project = var.project_id + region = var.region +} + +data "google_client_config" "default" { + depends_on = [module.gke-cluster] +} + +# Defer reading the cluster data until the GKE cluster exists. +data "google_container_cluster" "default" { + name = var.cluster_name + depends_on = [module.gke-cluster] + location = var.region +} + +provider "kubernetes" { + host = "https://${data.google_container_cluster.default.endpoint}" + token = data.google_client_config.default.access_token + cluster_ca_certificate = base64decode( + data.google_container_cluster.default.master_auth[0].cluster_ca_certificate, + ) +} + +resource "kubernetes_deployment" "searxng" { + metadata { + name = "searxng" + labels = { + app = "searxng" + } + } + spec { + replicas = 1 + selector { + match_labels = { + component = "searxng" + } + } + template { + metadata { + labels = { + component = "searxng" + } + } + spec { + container { + image = var.search_image + name = "searxng-container" + port { + container_port = 8080 + } + } + } + } + } +} + +resource "kubernetes_service" "searxng_service" { + metadata { + name = "searxng-service" + namespace = "default" + } + + spec { + selector = { + component = "searxng" + } + + port { + port = 8080 + target_port = 8080 + } + + type = "LoadBalancer" + } +} + +variable "project_id" { + description = "The ID of the project in which the resources will be deployed." + type = string +} + +variable "key_file" { + description = "The path to the GCP service account key file." + type = string +} + +variable "region" { + description = "The GCP region to deploy to." + type = string +} + +variable "cluster_name" { + description = "The GCP region to deploy to." + type = string +} + +variable "search_image" { + description = "Tag for the searxng image" + type = string +} + +variable "backed_image" { + description = "Tag for the Perplexica backend image" + type = string +} + +variable "app_image" { + description = "Tag for the app image" + type = string +} + +module "gke-cluster" { + source = "./gke-cluster" + + project_id = var.project_id + name = var.cluster_name + region = var.region + key_file = var.key_file +} diff --git a/deploy/gcp/sample.env b/deploy/gcp/sample.env new file mode 100644 index 0000000..f85b878 --- /dev/null +++ b/deploy/gcp/sample.env @@ -0,0 +1,7 @@ +# Rename this file to .env +# 0: Update to your GCP project id +# 1: Update to the path where the GCP service account credential file is kept +# 2: Update the region to your desired GCP region +GCP_PROJECT_ID=name-of-your-gcp-project +GCP_SERVICE_ACCOUNT_KEY_FILE=/Path/to/your/gcp-service-account-key-file.json +GCP_REGION=us-east1