{"id":15365451,"url":"https://github.com/epiphone/gke-terraform-example","last_synced_at":"2025-04-15T07:30:58.862Z","repository":{"id":151574959,"uuid":"167936953","full_name":"epiphone/gke-terraform-example","owner":"epiphone","description":"A sample web app deployment on Google Kubernetes Engine","archived":false,"fork":false,"pushed_at":"2019-02-19T18:10:27.000Z","size":216,"stargazers_count":48,"open_issues_count":1,"forks_count":30,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-28T18:14:54.785Z","etag":null,"topics":["ci-pipeline","cloud-sql","gke","google-cloud","google-kubernetes-engine","terraform","terraform-gke"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/epiphone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-28T09:44:16.000Z","updated_at":"2025-03-15T23:20:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"72f476cc-eeba-4866-8b88-401e4246837b","html_url":"https://github.com/epiphone/gke-terraform-example","commit_stats":{"total_commits":22,"total_committers":2,"mean_commits":11.0,"dds":0.09090909090909094,"last_synced_commit":"0b27803725223c40e54a1983cc502e5001f47a3e"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epiphone%2Fgke-terraform-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epiphone%2Fgke-terraform-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epiphone%2Fgke-terraform-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epiphone%2Fgke-terraform-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/epiphone","download_url":"https://codeload.github.com/epiphone/gke-terraform-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249026700,"owners_count":21200493,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci-pipeline","cloud-sql","gke","google-cloud","google-kubernetes-engine","terraform","terraform-gke"],"created_at":"2024-10-01T13:14:47.241Z","updated_at":"2025-04-15T07:30:58.846Z","avatar_url":"https://github.com/epiphone.png","language":"HCL","funding_links":[],"categories":["HCL"],"sub_categories":[],"readme":"# Google Kubernetes Engine full-stack example\n[![CircleCI](https://circleci.com/gh/epiphone/gke-terraform-example/tree/master.svg?style=svg)](https://circleci.com/gh/epiphone/gke-terraform-example/tree/master)\n\nAn example of deploying a web app on GKE. Consists of\n- **GKE cluster** with a single node pool\n  - [VPC-native](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips), [private](https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters) and using [container-native load-balancing](https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing)\n  - access to cluster master is limited to a single whitelisted IP: check the `K8S_MASTER_ALLOWED_IP` env variable below\n- **Cloud SQL Postgres** instance with [private networking](https://cloud.google.com/blog/products/databases/introducing-private-networking-connection-for-cloud-sql)\n  - connects to GKE through a [private IP](https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine#private-ip), ensuring traffic is never exposed to the public internet\n- **Cloud Storage** and **Cloud CDN** for serving static assets\n- **Cloud Load Balancing** routing `/api/*` to GKE and the rest to the static assets bucket\n  - implemented in a bit of a roundabout way since `ingress-gce` [lacks support for backend buckets](https://github.com/kubernetes/ingress-gce/issues/33): we're [passing](.circleci/config.yml#L132) GKE backend's name in Terraform variables and attaching it to our default URL map\n- **Cloud DNS** for domain management\n    - check `ROOT_DOMAIN_NAME_\u003cENV\u003e` below\n- **Terraform**-defined infrastructure\n  - using `kubectl` directly instead of the [Kubernetes Terraform provider](https://github.com/terraform-providers/terraform-provider-kubernetes) as the latter is missing an Ingress type, among others\n- **CircleCI** pipeline\n  - push to any non-master branch triggers `dev` deployment \u0026 push to `master` branch triggers `test` deployment\n  - `prod` deployment triggered by an additional approval step at CircleCI UI\n\n![architecture sketch](/doc/gke-sample-app.png)\n\n## Setup\n\nThe following steps need to be completed manually before automation kicks in:\n\n1. Create a new Google Cloud project per each environment\n2. For each Google Cloud project,\n    - set up a Cloud Storage bucket for storing [remote Terraform state](https://www.terraform.io/docs/backends/types/gcs.html)\n    - set up a service IAM account to be used by Terraform. Attach the `Editor` and `Compute Network Agent` roles to the created user\n3. Set environment variables in your CircleCI project (replacing `ENV` with an uppercase `DEV`, `TEST` and `PROD`):\n    - `GOOGLE_PROJECT_ID_\u003cENV\u003e`: env-specific Google project id\n    - `GCLOUD_SERVICE_KEY_\u003cENV\u003e`: env-specific service account key\n    - `DB_PASSWORD_\u003cENV\u003e`: env-specific password for the Postgres user that the application uses\n    - `ROOT_DOMAIN_NAME_\u003cENV\u003e`: env-specific root domain name, e.g. `dev.example.com`\n    - `K8S_MASTER_ALLOWED_IP`: IP from which to access cluster master's public endpoint, i.e. the IP where you run `kubectl` at ([read more](https://cloud.google.com/kubernetes-engine/docs/how-to/authorized-networks))\n      - In CircleCI we temporarily whitelist the test host IP in order to run `kubectl`\n4. [Enable](https://cloud.google.com/apis/docs/enable-disable-apis) the following Google Cloud APIs:\n    - `cloudresourcemanager.googleapis.com`\n    - `compute.googleapis.com`\n    - `container.googleapis.com`\n    - `containerregistry.googleapis.com`\n    - `dns.googleapis.com`\n    - `servicenetworking.googleapis.com`\n    - `sqladmin.googleapis.com`\n\nYou might also want to acquire a domain and [update your domain registration](https://cloud.google.com/dns/docs/update-name-servers) to point to Google Cloud DNS name servers.\n\n## Manual deployment\n\nYou can also sidestep CI and deploy locally:\n\n1. Install [terraform](https://learn.hashicorp.com/terraform/getting-started/install.html), [gcloud](https://cloud.google.com/sdk/#Quick_Start) and [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)\n2. [Login](https://www.terraform.io/docs/providers/google/provider_reference.html) to Google Cloud: `gcloud auth application-default login`\n3. Update infra: `cd terraform/dev \u0026\u0026 terraform init \u0026\u0026 terraform apply`\n4. Follow [instructions](https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app) on building and pushing a Docker image to GKE:\n    - `cd app`\n    - `export PROJECT_ID=\"$(gcloud config get-value project -q)\"`\n    - `docker build -t gcr.io/${PROJECT_ID}/gke-app:v1 .`\n    - `gcloud docker -- push gcr.io/${PROJECT_ID}/gke-app:v1`\n5. Authenticate `kubectl`: `gcloud container clusters get-credentials $(terraform output cluster_name) --zone=$(terraform output cluster_zone)`\n6. Render Kubernetes config template: `terraform output k8s_rendered_template \u003e k8s.yml`\n7. Update Kubernetes resources: `kubectl apply -f k8s.yml`\n\nRead [here](https://cloud.google.com/sql/docs/postgres/quickstart-proxy-test) on how to connect to the Cloud SQL instance with a local `psql` client.\n\n## Further work\n\n- Cloud SQL high availability \u0026 automated backups\n- [regional GKE cluster](https://cloud.google.com/kubernetes-engine/docs/concepts/regional-clusters)\n- GKE autoscaling\n- Cloud Armor DDoS protection\n- SSL\n- tune down service accounts privileges\n- possible CI improvements:\n    - add a step to [clean up old container images from GCR](https://gist.github.com/ahmetb/7ce6d741bd5baa194a3fac6b1fec8bb7)\n    - prompt for extra approval on infra changes in master\n    - don't rebuild docker image from `test` to `prod`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepiphone%2Fgke-terraform-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fepiphone%2Fgke-terraform-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepiphone%2Fgke-terraform-example/lists"}