API gateway module and example that creates a multi-region deployment for API gateway

This commit is contained in:
apichick
2022-02-20 21:22:41 +01:00
committed by Julio Castillo
parent c4b2f85a3b
commit 00473fda64
23 changed files with 3473 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
# Creating multi-region deployments for API Gateway
This tutorial shows you how to configure an HTTP(S) load balancer to enable multi-region deployments for API Gateway. For more details on how this set up work have a look at the article [here](https://cloud.google.com/api-gateway/docs/multi-region-deployment).
The diagram below depicts the architecture that this example sets up.
![Architecture](architecture.png)
# Running the example
Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=examples%2Fserverless%2Fapi-gateway), then go through the following steps to create resources:
* `terraform init`
* `terraform apply -var project_id=my-project-id`
## Testing the example
1. Copy the IP address returned as output
2. Execute the following command
curl -v http://<IP_ADDRESS>/hello
Once done testing, you can clean up resources by running `terraform destroy`.
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L26) | Identifier of the project. | <code>string</code> | ✓ | |
| [regions](variables.tf#L31) | List of regions to deploy the proxy in. | <code>list&#40;string&#41;</code> | ✓ | |
| [project_create](variables.tf#L17) | Parameters for the creation of the new project. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [ip_address](outputs.tf#L17) | The reserved global IP address. | |
<!-- END TFDOC -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,7 @@
const functions = require('@google-cloud/functions-framework');
// Register an HTTP function with the Functions Framework that will be executed
// when you make an HTTP request to the deployed function's endpoint.
functions.http('helloGET', (req, res) => {
res.send('Hello World!');
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
{
"name": "function",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"engines": {
"node": ">=16.0.0"
},
"dependencies": {
"@google-cloud/functions-framework": "^3.0.0",
"express": "^4.17.3"
}
}

View File

@@ -0,0 +1,145 @@
/**
* Copyright 2022 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.
*/
locals {
api_id_prefix = "api"
function_name_prefix = "cf-hello"
specs = { for region in var.regions : region =>
templatefile("${path.module}/spec.yaml", {
api_id = "${local.api_id_prefix}-${region}"
function_name = "${local.function_name_prefix}-${region}"
region = region
project_id = var.project_id
})
}
backends = [for region in var.regions : {
group = google_compute_region_network_endpoint_group.serverless-negs[region].id
options = null
}
]
}
module "project" {
source = "../../../modules/project"
billing_account = (var.project_create != null
? var.project_create.billing_account_id
: null
)
parent = (var.project_create != null
? var.project_create.parent
: null
)
name = var.project_id
services = [
"apigateway.googleapis.com",
"cloudbuild.googleapis.com",
"cloudfunctions.googleapis.com",
"compute.googleapis.com",
"servicemanagement.googleapis.com",
"servicecontrol.googleapis.com"
]
project_create = var.project_create != null
}
module "sa" {
source = "../../../modules/iam-service-account"
project_id = module.project.project_id
name = "sa-api"
}
module "functions" {
for_each = toset(var.regions)
source = "../../../modules/cloud-function"
project_id = module.project.project_id
name = "${local.function_name_prefix}-${each.value}"
bucket_name = "bkt-${module.project.project_id}-${each.value}"
region = each.value
ingress_settings = "ALLOW_ALL"
bucket_config = {
location = null
lifecycle_delete_age = 1
}
bundle_config = {
source_dir = "${path.module}/function"
output_path = "${path.module}/bundle.zip"
excludes = null
}
function_config = {
entry_point = "helloGET"
instances = null
memory = null
runtime = "nodejs16"
timeout = null
}
service_account_create = true
iam = {
"roles/cloudfunctions.invoker" = [module.sa.iam_email]
}
}
module "gateways" {
for_each = toset(var.regions)
source = "../../../modules/api-gateway"
project_id = module.project.project_id
api_id = "${local.api_id_prefix}-${each.value}"
region = each.value
spec = local.specs[each.value]
service_account_email = module.sa.email
}
module "glb" {
source = "../../../modules/net-glb"
name = "glb"
project_id = module.project.project_id
# This is important as serverless backends require no HCs
health_checks_config_defaults = null
reserve_ip_address = true
backend_services_config = {
serverless-backend = {
bucket_config = null
enable_cdn = false
cdn_config = null
group_config = {
backends = [for region in var.regions : {
group = google_compute_region_network_endpoint_group.serverless-negs[region].id
options = null
}
],
health_checks = []
log_config = null
options = null
}
}
}
}
resource "google_compute_region_network_endpoint_group" "serverless-negs" {
for_each = toset(var.regions)
provider = google-beta
name = "serverless-neg-${module.gateways[each.value].gateway_id}"
project = module.project.project_id
network_endpoint_type = "SERVERLESS"
region = each.value
serverless_deployment {
platform = "apigateway.googleapis.com"
resource = module.gateways[each.value].gateway_id
url_mask = ""
}
lifecycle {
create_before_destroy = true
}
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2022 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 "ip_address" {
description = "The reserved global IP address."
value = module.glb.ip_address
}

View File

@@ -0,0 +1,35 @@
# Copyright 2022 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.
swagger: '2.0'
info:
title: ${api_id} Sample API
description: Sample API on API Gateway with a Google Cloud Functions backend
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/hello:
get:
summary: Greet a user
operationId: hello
x-google-backend:
address: https://${region}-${project_id}.cloudfunctions.net/${function_name}
responses:
'200':
description: A successful response
schema:
type: string

View File

@@ -0,0 +1,34 @@
/**
* Copyright 2022 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_create" {
description = "Parameters for the creation of the new project."
type = object({
billing_account_id = string
parent = string
})
default = null
}
variable "project_id" {
description = "Identifier of the project."
type = string
}
variable "regions" {
description = "List of regions to deploy the proxy in."
type = list(string)
}