Skip to content

Commit

Permalink
Terraform ILB example. (GoogleCloudPlatform#652)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliocc authored May 19, 2021
1 parent bea93d8 commit 30dcbd1
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The examples folder contains example solutions across a variety of Google Cloud
* [Spinnaker](examples/spinnaker) - Example pipelines for a Canary / Production deployment process.
* [TensorFlow Serving on GKE and Load Testing](examples/tf-load-testing) - Examples how to implement Tensorflow model inference on GKE and to perform a load testing of such solution.
* [TensorFlow Unit Testing](examples/tensorflow-unit-testing) - Examples how to write unit tests for TensorFlow ML models.
* [Terraform Internal HTTP Load Balancer](examples/terraform-ilb) - Terraform example showing how to deploy an internal HTTP load balancer.
* [Uploading files directly to Google Cloud Storage by using Signed URL](examples/direct-upload-to-gcs) - Example architecture to enable uploading files directly to GCS by using [Signed URL](https://cloud.google.com/storage/docs/access-control/signed-urls).

## Tools
Expand Down
90 changes: 90 additions & 0 deletions examples/terraform-ilb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Internal HTTP Load Balancer Terraform Example
This example shows how to deploy an internal HTTP load balancer using
plain terraform (i.e. without using any external modules). This
example will create all the required resources needed for a working
internal load balancer except the GCP project.

## Design
In this example we use simple application (an nginx serving a simple
HTML file) configured using a [startup
script](https://cloud.google.com/compute/docs/instances/startup-scripts/linux).
This application is deployed using a [Managed Instance
Group](https://cloud.google.com/compute/docs/instance-groups) with a
minimum of two instances.

Besides the computing resources, the code also deploys a VPC, the
required subnets, and all the components elements of a load balancer
(forwarding rule, URL map, health check, etc).

For testing purposes, an additional VM is also created. You can use
this VM to confirm the load balancer is correctly configured.

## Prerequisites
1. You must have a recent version of Terraform installed (0.14+).
1. A working GCP project with the GCE API enabled.

## Deploy
The terraform code defines multiple variables. Most of the variables
provide default values that can be safely used but you must provide
the project id where the resources will be created. Check the
Variables section below for more details.

In the following command, we define the `project_id` variable using
terraform's `-var` option. Make sure to replace `$MYPROJECT` with your
GCP project ID.

```console
$ terraform init
$ terraform apply -var project_id=$MYPROJECT
[Output omitted]
Apply complete! Resources: 13 added, 0 changed, 0 destroyed.

Outputs:

ilb_ip = "10.0.1.5"
```

After the apply completes, terraform will output the IP address of the load
balancer. You can connect to the test instance and use curl to test
the load balancer. You should see an output similar to this:
```console
user@local:~$ gcloud compute ssh --tunnel-through-iap vm-test
Last login: xxxx
user@vm-test:~$ curl 10.0.1.5
<pre>
Name: vm-snv1.europe-west1-c.c.$MYPROJECT.internal
IP: 10.0.1.4
Metadata: {
"created-by": "projects/xxxx/regions/europe-west1/instanceGroupManagers/mig",
"instance-template": "projects/xxxx/global/instanceTemplates/mig-template-20210518224515151200000001"
}
</pre>
user@vm-test:~$ curl 10.0.1.5
<pre>
Name: vm-tjg7.europe-west1-b.c.$MYPROJECT.internal
IP: 10.0.1.3
Metadata: {
"created-by": "projects/xxxx/regions/europe-west1/instanceGroupManagers/mig",
"instance-template": "projects/xxxx/global/instanceTemplates/mig-template-20210518224515151200000001"
}
</pre>
jccb@vm-test:~$
```

After you're finished, you can destroy all resources with `terraform destroy`.

<!-- BEGIN TFDOC -->
## Variables

| name | description | type | required | default |
|---|---|:---: |:---:|:---:|
| project_id | Project where resources will be created | <code title="">string</code> || |
| *ranges* | CIDR ranges for proxy and backend instances | <code title="object&#40;&#123;&#10;proxy &#61; string&#10;backend &#61; string&#10;&#125;&#41;">object({...})</code> | | <code title="&#123;&#10;proxy &#61; &#34;10.0.0.0&#47;24&#34;&#10;backend &#61; &#34;10.0.1.0&#47;24&#34;&#10;&#125;">...</code> |
| *region* | Default region for resources | <code title="">string</code> | | <code title="">europe-west1</code> |

## Outputs

| name | description | sensitive |
|---|---|:---:|
| ilb_ip | IP of the internal load balancer | |
<!-- END TFDOC -->
214 changes: 214 additions & 0 deletions examples/terraform-ilb/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# VPC
resource "google_compute_network" "default" {
provider = google-beta
project = var.project_id
name = "ilb-network"
auto_create_subnetworks = false
}

# proxy only subnet
resource "google_compute_subnetwork" "proxy" {
provider = google-beta
project = var.project_id
name = "lb"
ip_cidr_range = var.ranges.proxy
region = var.region
purpose = "INTERNAL_HTTPS_LOAD_BALANCER"
role = "ACTIVE"
network = google_compute_network.default.id
}

# backed subnet
resource "google_compute_subnetwork" "default" {
provider = google-beta
project = var.project_id
name = "backend"
ip_cidr_range = var.ranges.backend
region = var.region
network = google_compute_network.default.id
}

# forwarding rule
resource "google_compute_forwarding_rule" "default" {
provider = google-beta
project = var.project_id
region = var.region
depends_on = [google_compute_subnetwork.proxy]
name = "forwarding-rule"
ip_protocol = "TCP"
load_balancing_scheme = "INTERNAL_MANAGED"
port_range = "80"
target = google_compute_region_target_http_proxy.default.id
network = google_compute_network.default.id
subnetwork = google_compute_subnetwork.default.id
network_tier = "PREMIUM"
}

# http proxy
resource "google_compute_region_target_http_proxy" "default" {
provider = google-beta
project = var.project_id
region = var.region
name = "proxy"
url_map = google_compute_region_url_map.default.id
}

# url map
resource "google_compute_region_url_map" "default" {
provider = google-beta
project = var.project_id
region = var.region
name = "map"
default_service = google_compute_region_backend_service.default.id
}

# backend service
resource "google_compute_region_backend_service" "default" {
provider = google-beta
project = var.project_id
region = var.region
protocol = "HTTP"
name = "backend"
load_balancing_scheme = "INTERNAL_MANAGED"
timeout_sec = 10
health_checks = [google_compute_region_health_check.default.id]
backend {
group = google_compute_region_instance_group_manager.mig.instance_group
balancing_mode = "UTILIZATION"
capacity_scaler = 1.0
}
}

# instance template
resource "google_compute_instance_template" "instance_template" {
provider = google-beta
project = var.project_id
name_prefix = "mig-template-"
machine_type = "e2-small"
tags = ["http-server"]

network_interface {
network = google_compute_network.default.id
subnetwork = google_compute_subnetwork.default.id
access_config {
# add external ip to fetch packages
}
}
disk {
source_image = "debian-cloud/debian-10"
auto_delete = true
boot = true
}

# install nginx and serve a simple web page
metadata = {
startup-script = <<-EOF1
#! /bin/bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y nginx-light jq
NAME=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/hostname")
IP=$(curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/0/ip")
METADATA=$(curl -f -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=True" | jq 'del(.["startup-script"])')
cat <<EOF > /var/www/html/index.html
<pre>
Name: $NAME
IP: $IP
Metadata: $METADATA
</pre>
EOF
EOF1
}
lifecycle {
create_before_destroy = true
}
}

# health check
resource "google_compute_region_health_check" "default" {
provider = google-beta
project = var.project_id
region = var.region
name = "hc"
http_health_check {
port_specification = "USE_SERVING_PORT"
}
}

# MIG
resource "google_compute_region_instance_group_manager" "mig" {
provider = google-beta
project = var.project_id
region = var.region
name = "mig"
version {
instance_template = google_compute_instance_template.instance_template.id
name = "primary"
}
base_instance_name = "vm"
target_size = 2
}

# allow all access from IAP and health check ranges
resource "google_compute_firewall" "fw-iap" {
provider = google-beta
project = var.project_id
name = "allow-iap-hc"
direction = "INGRESS"
network = google_compute_network.default.id
source_ranges = ["130.211.0.0/22", "35.191.0.0/16", "35.235.240.0/20"]
allow {
protocol = "tcp"
}
}

# allow http from proxy subnet to backends
resource "google_compute_firewall" "fw-ilb-to-backends" {
provider = google-beta
project = var.project_id
name = "allow-ilb-to-backends"
direction = "INGRESS"
network = google_compute_network.default.id
source_ranges = [var.ranges.proxy]
target_tags = ["http-server"]
allow {
protocol = "tcp"
ports = ["80", "443", "8080"]
}
}

# test instance
resource "google_compute_instance" "vm-test" {
provider = google-beta
project = var.project_id
zone = "${var.region}-b"
name = "vm-test"
machine_type = "e2-small"
network_interface {
network = google_compute_network.default.id
subnetwork = google_compute_subnetwork.default.id
}
boot_disk {
initialize_params {
image = "debian-cloud/debian-10"
}
}
}
18 changes: 18 additions & 0 deletions examples/terraform-ilb/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

output "ilb_ip" {
description = "IP of the internal load balancer"
value = google_compute_forwarding_rule.default.ip_address
}
36 changes: 36 additions & 0 deletions examples/terraform-ilb/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

variable "project_id" {
description = "Project where resources will be created"
type = string
}

variable "region" {
description = "Default region for resources"
type = string
default = "europe-west1"
}

variable "ranges" {
description = "CIDR ranges for proxy and backend instances"
type = object({
proxy = string
backend = string
})
default = {
proxy = "10.0.0.0/24"
backend = "10.0.1.0/24"
}
}

0 comments on commit 30dcbd1

Please sign in to comment.