From 87b05f2777e705d9d2e481ef3dd3e4830133c80d Mon Sep 17 00:00:00 2001 From: YYx00xZZ Date: Wed, 9 Apr 2025 14:01:07 +0300 Subject: [PATCH] Add IaC and docs --- README.md | 39 +++++ infrastructure/.terraform.lock.hcl | 41 +++++ infrastructure/main.tf | 32 ++++ infrastructure/modules/gcp-apis/main.tf | 7 + infrastructure/modules/gcp-apis/outputs.tf | 0 infrastructure/modules/gcp-apis/variables.tf | 41 +++++ .../modules/gcp-gke-cluster/main.tf | 160 ++++++++++++++++++ .../modules/gcp-gke-cluster/outputs.tf | 4 + .../modules/gcp-gke-cluster/variables.tf | 46 +++++ infrastructure/modules/gcp-networking/main.tf | 37 ++++ .../modules/gcp-networking/outputs.tf | 0 .../modules/gcp-networking/variables.tf | 10 ++ infrastructure/variables.tf | 12 ++ 13 files changed, 429 insertions(+) create mode 100644 infrastructure/.terraform.lock.hcl create mode 100644 infrastructure/main.tf create mode 100644 infrastructure/modules/gcp-apis/main.tf create mode 100644 infrastructure/modules/gcp-apis/outputs.tf create mode 100644 infrastructure/modules/gcp-apis/variables.tf create mode 100644 infrastructure/modules/gcp-gke-cluster/main.tf create mode 100644 infrastructure/modules/gcp-gke-cluster/outputs.tf create mode 100644 infrastructure/modules/gcp-gke-cluster/variables.tf create mode 100644 infrastructure/modules/gcp-networking/main.tf create mode 100644 infrastructure/modules/gcp-networking/outputs.tf create mode 100644 infrastructure/modules/gcp-networking/variables.tf create mode 100644 infrastructure/variables.tf diff --git a/README.md b/README.md index 1b57de55b7..4ce2ef534d 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,45 @@ I've provided the same tests reworked to be compatible with Geth. When running the docker container with Geth, I do not use bind mount and I am making sure that the geth_data dir from previous steps is cleaned so I can be sure that I am working with the contract previously deployed in the container. +### Infrastructure +All Infrastructure as a Code is stored inside `infrastructure` dir. I went with GCP because of the free trial (AWS was exhausted). + +Currently, the IaC is ment to be used locally. Later, a pipeline should be create and the state must be stored remotely and securely. + +The `secrets` directory is holding all infrastrucutre related sensitive data. It is excluded from source control. + +To authenicate the provider, place your service account key JSON file in the `secrets` dir and name it `svc_acc_key.json`. +This is not a requirement and you can change this as you like, but this is the fastest and easiest way to get up and running. + +Please name your service account `terraformer` when creating it in the GCP WEB UI for ease of use. + +#### Modules + +**gcp-apis** +This module is responsible for enabling all APIs that will be needed for the project. + +Required inputs: +- `project_id` + +!NB There is known issues with enabling APIs through Terraform. Sometimes the backend does not return a response quickly enough which results in the API being enabled really but terraform apply fails. Rerun the pipeline/command. +There are workaround but for the sake of this demo please use the solution described above. + +**gcp-networking** +Holds code responsible for setting up all networking resource needed for a k8s cluster to operate - router and a NAT gateway. + +Required inputs: +- `project_id` + +**gcp-gke-cluster** +The quota for the free trial is being an issue for the demo. I can't raise the quota limit and I am using the min. disk size (10GB). +To overcome this limitation I had to use zonal cluster with only 2 nodes. + +Required inputs: +- `project_id` + + +--- + ## Go Ethereum Golang execution layer implementation of the Ethereum protocol. diff --git a/infrastructure/.terraform.lock.hcl b/infrastructure/.terraform.lock.hcl new file mode 100644 index 0000000000..0dd152a310 --- /dev/null +++ b/infrastructure/.terraform.lock.hcl @@ -0,0 +1,41 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/google" { + version = "6.29.0" + constraints = "6.29.0" + hashes = [ + "h1:U0Ca/+zZMMuea+r80qu9SRzWu8Waxny5aWGZXn+kVhc=", + "zh:01a501df2fb9ecbf0935b27e588bc7b6bcaf4ab0043747f4229c25e4ba47dadb", + "zh:056f8ab2b73755cf5a67228ab4a07882e76265fa25b07f2794d9939288164f48", + "zh:0dbdfa564f7db8a2e6f7e76437a9850b6101450120c08e87cf9846330736c0c6", + "zh:3c3e4ee801de22812bd07cb3d36b227f8057d7825998fb4d97051764a565b89b", + "zh:4e440eb4c60da9cd7d23b3b99be54c869472fd70006c39639a04b9a51248929c", + "zh:659490efd20b3e98e4166b2925baa18549d82e4c16751bc92baed0185d22d108", + "zh:9ae24b98a3a3346b8004c6b87e3b59decba2f64c7407e106263859c275105ef8", + "zh:c64cff9c17e302236bb9e0a6d5eff4c92540ce3947269f3db94e2b9a1b1a3f4e", + "zh:d2fd0aecbbbba463bcb0640f54c8df5e7a63918bdd44236dcd36dff33bb8de09", + "zh:f022316369ea676f5f9d11768b4c621abd3304c1e6d1f0c2361e4e2620c4b65d", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:feb7d4fdbebdfabac2aaa73376f754736ccf089fa90adf6388701f89801188e6", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.7.1" + hashes = [ + "h1:t152MY0tQH4a8fLzTtEWx70ITd3azVOrFDn/pQblbto=", + "zh:3193b89b43bf5805493e290374cdda5132578de6535f8009547c8b5d7a351585", + "zh:3218320de4be943e5812ed3de995946056db86eb8d03aa3f074e0c7316599bef", + "zh:419861805a37fa443e7d63b69fb3279926ccf98a79d256c422d5d82f0f387d1d", + "zh:4df9bd9d839b8fc11a3b8098a604b9b46e2235eb65ef15f4432bde0e175f9ca6", + "zh:5814be3f9c9cc39d2955d6f083bae793050d75c572e70ca11ccceb5517ced6b1", + "zh:63c6548a06de1231c8ee5570e42ca09c4b3db336578ded39b938f2156f06dd2e", + "zh:697e434c6bdee0502cc3deb098263b8dcd63948e8a96d61722811628dce2eba1", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:a0b8e44927e6327852bbfdc9d408d802569367f1e22a95bcdd7181b1c3b07601", + "zh:b7d3af018683ef22794eea9c218bc72d7c35a2b3ede9233b69653b3c782ee436", + "zh:d63b911d618a6fe446c65bfc21e793a7663e934b2fef833d42d3ccd38dd8d68d", + "zh:fa985cd0b11e6d651f47cff3055f0a9fd085ec190b6dbe99bf5448174434cdea", + ] +} diff --git a/infrastructure/main.tf b/infrastructure/main.tf new file mode 100644 index 0000000000..09124b4df7 --- /dev/null +++ b/infrastructure/main.tf @@ -0,0 +1,32 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = "6.29.0" + } + } + + required_version = ">= 1.5.0" +} + +provider "google" { + credentials = file("./secrets/svc_acc_key.json") + project = var.project_id +} + +module "gcp_apis" { + source = "./modules/gcp-apis" + project_id = var.project_id +} + +module "gcp_networking" { + source = "./modules/gcp-networking" + project_id = var.project_id + depends_on = [ module.gcp_apis ] +} + +module "gcp_gke_cluster" { + source = "./modules/gcp-gke-cluster" + project_id = var.project_id + depends_on = [ module.gcp_networking ] +} diff --git a/infrastructure/modules/gcp-apis/main.tf b/infrastructure/modules/gcp-apis/main.tf new file mode 100644 index 0000000000..800e626ff9 --- /dev/null +++ b/infrastructure/modules/gcp-apis/main.tf @@ -0,0 +1,7 @@ +# API's to enable +resource "google_project_service" "api_services" { + project = var.project_id + for_each = toset(var.api_service) + service = each.key + disable_dependent_services = true +} diff --git a/infrastructure/modules/gcp-apis/outputs.tf b/infrastructure/modules/gcp-apis/outputs.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/infrastructure/modules/gcp-apis/variables.tf b/infrastructure/modules/gcp-apis/variables.tf new file mode 100644 index 0000000000..d0a9bd5e28 --- /dev/null +++ b/infrastructure/modules/gcp-apis/variables.tf @@ -0,0 +1,41 @@ +variable "project_id" { + description = "The GCP project ID" + type = string +} + +variable "api_service" { + description = "List of strings. Contain API services to be enabled." + type = list(string) + default = [ + "cloudresourcemanager.googleapis.com", + "servicenetworking.googleapis.com", + "cloudbilling.googleapis.com", + "autoscaling.googleapis.com", + "bigquery.googleapis.com", + "bigquerymigration.googleapis.com", + "bigquerystorage.googleapis.com", + "cloudapis.googleapis.com", + "cloudtrace.googleapis.com", + "compute.googleapis.com", + "container.googleapis.com", + "containerfilesystem.googleapis.com", + "containerregistry.googleapis.com", + "datastore.googleapis.com", + "iam.googleapis.com", + "iamcredentials.googleapis.com", + "logging.googleapis.com", + "monitoring.googleapis.com", + "networkmanagement.googleapis.com", + "osconfig.googleapis.com", + "oslogin.googleapis.com", + "pubsub.googleapis.com", + "secretmanager.googleapis.com", + "servicemanagement.googleapis.com", + "serviceusage.googleapis.com", + "storage-api.googleapis.com", + "storage-component.googleapis.com", + "storage.googleapis.com", + "workloadmanager.googleapis.com", + "networkconnectivity.googleapis.com" + ] +} diff --git a/infrastructure/modules/gcp-gke-cluster/main.tf b/infrastructure/modules/gcp-gke-cluster/main.tf new file mode 100644 index 0000000000..0f941fd713 --- /dev/null +++ b/infrastructure/modules/gcp-gke-cluster/main.tf @@ -0,0 +1,160 @@ +locals { + # Defining a service account to grant necessary GKE cluster permisisons + service_account_email = "terraformer@${var.project_id}.iam.gserviceaccount.com" +} + +resource "random_string" "random_node_pool_suffix" { + lower = true + length = 5 + special = false + upper = false + keepers = { + node_pool_machine_type = var.node_pool_machine_type + node_pool_disk_size_gb = var.node_pool_disk_size_gb + } +} + + +resource "google_container_cluster" "app_cluster" { + project = var.project_id + name = var.cluster_name + location = var.cluster_location + # node_locations = slice(data.google_compute_zones.region_zones.names, 0, 3) + remove_default_node_pool = true + initial_node_count = 1 + deletion_protection = false # Change this to true when going to production :) + + # Automation section from the UI + maintenance_policy { + recurring_window { + end_time = "2023-10-02T15:00:00Z" + recurrence = "FREQ=WEEKLY;BYDAY=WE,TH" + start_time = "2023-10-02T07:00:00Z" + } + } + + notification_config { + pubsub { + enabled = false + } + } + + vertical_pod_autoscaling { + enabled = false + } + + cluster_autoscaling { + enabled = false + # autoscaling_profile = "BALANCED" # Defaults to BALANCED + } + + # Networking + private_cluster_config { + enable_private_nodes = true + enable_private_endpoint = false + master_ipv4_cidr_block = "172.16.0.0/28" + } + network = "projects/${var.project_id}/global/networks/default" # "default" + subnetwork = "default" # "default" + networking_mode = "VPC_NATIVE" + ip_allocation_policy { + cluster_ipv4_cidr_block = "10.16.0.0/14" # The IP address range for the cluster pod IPs + services_ipv4_cidr_block = "10.20.0.0/20" # The IP address range of the services IPs in this cluster + } + + default_max_pods_per_node = 110 + enable_intranode_visibility = false + + addons_config { + dns_cache_config { + enabled = true + } + http_load_balancing { + disabled = false + } + gce_persistent_disk_csi_driver_config { + enabled = true + } + } + + # SECURITY TAB FROM THE UI + binary_authorization { + evaluation_mode = "DISABLED" + } + + enable_shielded_nodes = true + + confidential_nodes { + enabled = false + } + + logging_config { + enable_components = ["SYSTEM_COMPONENTS", "WORKLOADS"] + } + + monitoring_config { + enable_components = ["SYSTEM_COMPONENTS"] + } + + release_channel { + channel = "STABLE" + } + + lifecycle { + ignore_changes = [ + maintenance_policy + ] + } +} + +resource "google_container_node_pool" "node_pool" { + name = "pool-${random_string.random_node_pool_suffix.result}" + cluster = google_container_cluster.app_cluster.id + node_count = var.node_pool_node_count + location = var.cluster_location + management { + auto_repair = true + auto_upgrade = true + } + + upgrade_settings { + max_surge = 1 + max_unavailable = 0 + } + + node_config { + machine_type = var.node_pool_machine_type + disk_size_gb = var.node_pool_disk_size_gb + disk_type = var.node_pool_disk_type + service_account = local.service_account_email + oauth_scopes = [ + # The set of Google API scopes to be made available on all of the node VMs under the "default" service account. + # Use the "https://www.googleapis.com/auth/cloud-platform" scope to grant access to all APIs. + # It is recommended that you set service_account to a non-default service account and grant IAM roles to that service account for only the resources that it needs. + "https://www.googleapis.com/auth/servicecontrol", + "https://www.googleapis.com/auth/service.management.readonly", + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + "https://www.googleapis.com/auth/devstorage.read_only", + "https://www.googleapis.com/auth/trace.append" + # "https://www.googleapis.com/auth/cloud-platform" + ] + metadata = { + "disable-legacy-endpoints" = "true" + } + # kubelet_config - enabled or disabled + # Render this block only if variable app_cluster_kubelet_config is true + dynamic "kubelet_config" { + for_each = var.cluster_kubelet_config == true ? [1] : [] + content { + cpu_cfs_quota = false + insecure_kubelet_readonly_port_enabled = "FALSE" + pod_pids_limit = 0 + } + } + } + + lifecycle { + create_before_destroy = true + } +} diff --git a/infrastructure/modules/gcp-gke-cluster/outputs.tf b/infrastructure/modules/gcp-gke-cluster/outputs.tf new file mode 100644 index 0000000000..7e2692032a --- /dev/null +++ b/infrastructure/modules/gcp-gke-cluster/outputs.tf @@ -0,0 +1,4 @@ +output "random_node_pool_suffix_output" { + description = "The random generated suffix for app cluster's node pool" + value = random_string.random_node_pool_suffix.result +} diff --git a/infrastructure/modules/gcp-gke-cluster/variables.tf b/infrastructure/modules/gcp-gke-cluster/variables.tf new file mode 100644 index 0000000000..8b9f547d28 --- /dev/null +++ b/infrastructure/modules/gcp-gke-cluster/variables.tf @@ -0,0 +1,46 @@ +variable "project_id" { + description = "The GCP project ID" + type = string +} + +variable "node_pool_node_count" { + description = "If it is regional cluster it is nodes per zone. Otherwise number of total nodes" + type = number + default = 2 +} + +variable "node_pool_machine_type" { + description = "The machine type used for pool-* nodes." + type = string + default = "e2-small" +} + +variable "node_pool_disk_size_gb" { + description = "The size of the nodes' disk in GB. Minimum 10G" + type = number + default = 10 +} + +variable "node_pool_disk_type" { + description = "The disk type used for nodes." + type = string + default = "pd-standard" +} + +variable "cluster_kubelet_config" { + description = "Enable or disable cluster node pool kubelet_config" + type = bool + default = false +} + +variable "cluster_name" { + description = "The name of the cluster" + type = string + default = "test-cluster" +} + +variable "cluster_location" { + description = "the locaion of the cluster; region" + type = string + default = "europe-west1-b" # "europe-west1" +} \ No newline at end of file diff --git a/infrastructure/modules/gcp-networking/main.tf b/infrastructure/modules/gcp-networking/main.tf new file mode 100644 index 0000000000..e7f079845e --- /dev/null +++ b/infrastructure/modules/gcp-networking/main.tf @@ -0,0 +1,37 @@ +data "google_compute_network" "default" { + project = var.project_id + name = "default" +} + + +# ============================================================================= +# === NAT Gateway + +# Router +resource "google_compute_router" "default_nat_router" { + name = "router" + network = "default" + region = var.region +} + +# NAT addresses +resource "google_compute_address" "nat_1" { + name = "nat-1-address" + address_type = "EXTERNAL" + region = var.region + network_tier = "PREMIUM" +} + +# Gateway +resource "google_compute_router_nat" "nat_gateway" { + name = "nat-1" + router = google_compute_router.default_nat_router.name + region = var.region + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" + nat_ip_allocate_option = "MANUAL_ONLY" + nat_ips = google_compute_address.nat_1.*.self_link + enable_dynamic_port_allocation = true + min_ports_per_vm = 1024 + max_ports_per_vm = 4096 + enable_endpoint_independent_mapping = false +} diff --git a/infrastructure/modules/gcp-networking/outputs.tf b/infrastructure/modules/gcp-networking/outputs.tf new file mode 100644 index 0000000000..e69de29bb2 diff --git a/infrastructure/modules/gcp-networking/variables.tf b/infrastructure/modules/gcp-networking/variables.tf new file mode 100644 index 0000000000..555403d4d8 --- /dev/null +++ b/infrastructure/modules/gcp-networking/variables.tf @@ -0,0 +1,10 @@ +variable "project_id" { + description = "The GCP project ID" + type = string +} + +variable "region" { + description = "The VPC region" + type = string + default = "europe-west1" +} diff --git a/infrastructure/variables.tf b/infrastructure/variables.tf new file mode 100644 index 0000000000..2a88456bb4 --- /dev/null +++ b/infrastructure/variables.tf @@ -0,0 +1,12 @@ +variable "project_id" { + description = "The GCP project ID" + type = string + default = "lime-demo-aap" +} + +variable "gke_cluster_location" { + description = "the locaion of the cluster; region" + type = string + default = "europe-west1" +} +