Die serverlose End-to-End-Anwendung für die Fotofreigabe erstellt eine skalierbare End-to-End-Anwendung für das Teilen von Fotos mit 11 Google Cloud-Produkten, Terraform und Django.
Mit diesem Stack werden die folgenden Komponenten konfiguriert und erstellt:
- Cloud Run – führt die Anwendung als Hauptserver aus
- Cloud SQL – zum Speichern relationaler Datenbanken, z. B. Nutzerinformationen, Beiträge
- Cloud Storage zum Speichern nicht relationaler Datenbanken, z. B. Postmedien
- Cloud Load Balancer – zum Server-Traffic mit mehreren Regionen
- Cloud DNS – zum Zuordnen einer benutzerdefinierten Domain
- Cloud Build – zum automatischen Bereitstellen Ihrer Anwendung über gcloud
- Secret Manager – zur Verbesserung der Sicherheit der App
- Cloud VPC – Zum Verbinden von Cloud SQL mit Cloud Run über Privates verbessern
- Cloud DNS – zum Speichern eines statischen Cache für schnellere Verbindungen
- Translation API – zum Übersetzen der Bildunterschriften des Beitrags in einer anderen Sprache
Jetzt starten
Klicken Sie auf den folgenden Link, um den Quellcode in Cloud Shell zu kopieren. Dort wird mit einem einzigen Befehl eine funktionierende Kopie der Anwendung in Ihrem Projekt erstellt.
Serverlose Komponenten der End-to-End-Anwendung für die Fotofreigabe
Die serverlose Architektur der End-to-End-Anwendung für das Teilen von Fotos nutzt verschiedene Produkte. Im Folgenden finden Sie eine Liste der Komponenten sowie weitere Informationen zu den Komponenten, einschließlich Links zu ähnlichen Videos, Produktdokumentationen und interaktiven Schritt-für-Schritt-Anleitungen.Skripts
Das Installationsskript verwendet eine ausführbare Datei, die in go
und Terraform-Befehlszeilentools geschrieben ist, um ein leeres Projekt zu erstellen und die Anwendung darin zu installieren. Die Ausgabe sollte eine funktionierende Anwendung und eine URL für die Load-Balancing-IP-Adresse sein.
./main.tf
Dienste aktivieren
Google Cloud-Dienste sind in einem Projekt standardmäßig deaktiviert. Damit Sie eine der hier aufgeführten Lösungen verwenden können, müssen wir Folgendes aktivieren:
- Cloud Run – das serverlose Tool, das die Container hostet und URLs bereitstellt, über die auf die Anwendung zugegriffen werden kann.
- Cloud SQL – Datenbankspeicher für die Anwendung
- Compute Engine – Virtuelle Maschinen und Netzwerke
- Cloud Build – erstellt Container-Images und stellt sie in Cloud Run bereit.
- Cloud Secret Manager: Wird verwendet, um die Host-IP-Adressen für SQL und Redis in Cloud Build for Cloud Run einzufügen.
- Dienstnetzwerk und serverloser VPC-Zugriff: Ermöglicht Cloud Run die Kommunikation mit SQL und Redis in einem privaten Netzwerk, sodass diese Server für externe Aufrufe von der API nicht zugänglich sind.
- Cloud IAM – das Tool zum Verwalten des Zugriffs und der Berechtigungen auf Google Cloud-Ressourcen
# Step 2: Activate service APIs
resource "google_project_service" "run" {
service = "run.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "sql-component" {
service = "sql-component.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "sqladmin" {
service = "sqladmin.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "compute" {
service = "compute.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "cloudbuild" {
service = "cloudbuild.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "secretmanager" {
service = "secretmanager.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "vpcaccess" {
service = "vpcaccess.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "servicenetworking" {
service = "servicenetworking.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "iam" {
service = "iam.googleapis.com"
disable_on_destroy = false
}
Privates Netzwerk erstellen
Mit dem folgenden Befehl wird ein privates Netzwerk für alle Ressourcen erstellt, die für die sichere Kommunikation innerhalb der Anwendung verwendet werden können.
resource "google_compute_network" "main" {
provider = google
name = "social-media-network-${random_id.name.hex}"
depends_on = [google_project_iam_member.serviceagent]
}
Netzwerk für SQL-Instanz erstellen
Mit dem folgenden Befehl kann über Cloud Run auf Cloud SQL zugegriffen werden:
resource "google_compute_global_address" "private_ip_address" {
provider = google-beta
project = var.project
name = local.private_ip_name
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = google_compute_network.main.id
depends_on = [google_project_service.vpcaccess, google_project_iam_member.serviceagent]
}
resource "google_service_networking_connection" "private_vpc_connection" {
provider = google-beta
network = google_compute_network.main.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.private_ip_address.name]
depends_on = [google_project_service.vpcaccess, google_project_iam_member.serviceagent]
}
Dienst-Agent Zugriff auf das Projekt gewähren
Mit diesem Befehl kann der Dienst-Agent auf das Projekt zugreifen, um die Konfiguration des VPC-Connector zu aktivieren.
resource "google_project_iam_member" "serviceagent" {
project = data.google_project.project.number
role = "roles/editor"
member = local.serviceagent_serviceaccount
}
VPC-Zugriffs-Connector erstellen
Mit diesem Befehl wird Cloud Run mit Database verbunden
resource "google_vpc_access_connector" "connector" {
for_each = { "us-west1" : 8, "us-central1" : 9, "us-east1" : 10 }
name = "vpc-con-${each.key}"
ip_cidr_range = "10.${each.value}.0.0/28"
region = each.key
network = google_compute_network.main.name
depends_on = [google_project_service.vpcaccess, google_project_iam_member.serviceagent]
}
Dienstkonto erstellen
Mit diesem Befehl wird ein Dienstkonto erstellt, mit dem die Anwendung sich bei allen Ressourcen authentifizieren kann, die sie zum Bereitstellen der Anwendung benötigt.
# Step 4: Create a custom Service Account
resource "google_service_account" "django" {
account_id = "django"
depends_on = [
google_project_service.iam
]
}
SQL Server erstellen
Mit den folgenden Befehlen wird eine SQL Server-Instanz konfiguriert und initialisiert.
resource "random_string" "random" {
length = 4
special = false
}
resource "random_password" "database_password" {
length = 32
special = false
}
resource "random_id" "name" {
byte_length = 2
}
resource "random_id" "db_name_suffix" {
byte_length = 4
}
resource "google_sql_database_instance" "instance" {
name = local.sql_database_name
database_version = "MYSQL_8_0"
region = var.region
project = var.project
depends_on = [google_vpc_access_connector.connector]
settings {
tier = "db-f1-micro"
ip_configuration {
ipv4_enabled = "true"
private_network = google_compute_network.main.id
}
}
deletion_protection = false
}
resource "google_sql_database" "database" {
name = "django"
instance = google_sql_database_instance.instance.name
}
resource "google_sql_user" "django" {
name = "django"
instance = google_sql_database_instance.instance.name
password = random_password.database_password.result
}
Storage-Buckets erstellen
Erstellt den Speicherort für die Medien für die Anwendung, die gespeichert und den Nutzern bereitgestellt werden sollen.
resource "google_storage_bucket" "media" {
name = "${var.project}-bucket"
location = "US"
}
resource "google_storage_bucket_iam_binding" "main" {
bucket = google_storage_bucket.media.name
role = "roles/storage.objectViewer"
members = [
"allUsers",
]
}
Secrets erstellen
Mit den folgenden Befehlen werden vertrauliche Anwendungseinstellungen und -ressourcen erstellt, um sie in Cloud Secrets zu speichern.
resource "google_secret_manager_secret_version" "django_settings" {
secret = google_secret_manager_secret.django_settings.id
secret_data = templatefile("etc/env.tpl", {
bucket = google_storage_bucket.media.name
secret_key = random_password.django_secret_key.result
user = google_sql_user.django
instance = google_sql_database_instance.instance
database = google_sql_database.database
})
}
resource "random_password" "django_secret_key" {
special = false
length = 50
}
resource "google_secret_manager_secret" "django_settings" {
secret_id = "django_settings"
replication {
automatic = true
}
depends_on = [google_project_service.secretmanager]
}
Dienstkontoberechtigungen festlegen
Mit diesem Befehl können das Cloud Build- und das Anwendungsdienstkonto auf Inhalte in Cloud Secrets zugreifen.
resource "google_secret_manager_secret_iam_binding" "django_settings" {
secret_id = google_secret_manager_secret.django_settings.id
role = "roles/secretmanager.admin"
members = [local.cloudbuild_serviceaccount, local.django_serviceaccount]
}
Secrets füllen
Mit den folgenden Befehlen werden Anwendungs-Secrets in Cloud-Secrets gespeichert.
resource "google_secret_manager_secret" "main" {
for_each = {
"DATABASE_PASSWORD" : google_sql_user.django.password,
"DATABASE_USER" : google_sql_user.django.name,
"DATABASE_NAME" : google_sql_database.database.name,
"DATABASE_HOST_PROD" : google_sql_database_instance.instance.private_ip_address,
"DATABASE_PORT_PROD" : 3306,
"PROJECT_ID" : var.project,
"GS_BUCKET_NAME" : var.project,
}
secret_id = each.key
replication {
automatic = true
}
depends_on = [google_sql_user.django, google_sql_database.database, google_sql_database_instance.instance]
}
resource "google_secret_manager_secret" "network" {
for_each = {
"EXTERNAL_IP" : module.lb-http.external_ip,
}
secret_id = each.key
replication {
automatic = true
}
depends_on = [module.lb-http, google_compute_network.main, google_cloud_run_service.service]
}
resource "google_secret_manager_secret" "url" {
for_each = {
"WEBSITE_URL_US_CENTRAL1" : google_cloud_run_service.service["us-central1"].status[0].url,
"WEBSITE_URL_US_WEST1" : google_cloud_run_service.service["us-west1"].status[0].url,
"WEBSITE_URL_US_EAST1" : google_cloud_run_service.service["us-east1"].status[0].url,
}
secret_id = each.key
replication {
automatic = true
}
depends_on = [google_cloud_run_service.service]
}
resource "google_secret_manager_secret_version" "main" {
for_each = { "DATABASE_PASSWORD" : google_sql_user.django.password,
"DATABASE_USER" : google_sql_user.django.name,
"DATABASE_NAME" : google_sql_database.database.name,
"DATABASE_HOST_PROD" : google_sql_database_instance.instance.private_ip_address,
"DATABASE_PORT_PROD" : 3306,
"PROJECT_ID" : var.project,
"GS_BUCKET_NAME" : var.project,
}
secret = google_secret_manager_secret.main[each.key].id
secret_data = each.value
}
resource "google_secret_manager_secret_version" "network" {
for_each = {
"EXTERNAL_IP" : module.lb-http.external_ip,
}
secret = google_secret_manager_secret.network[each.key].id
secret_data = each.value
}
resource "google_secret_manager_secret_version" "url" {
for_each = {
"WEBSITE_URL_US_CENTRAL1" : google_cloud_run_service.service["us-central1"].status[0].url,
"WEBSITE_URL_US_WEST1" : google_cloud_run_service.service["us-west1"].status[0].url,
"WEBSITE_URL_US_EAST1" : google_cloud_run_service.service["us-east1"].status[0].url,
}
secret = google_secret_manager_secret.url[each.key].id
secret_data = each.value
}
resource "google_secret_manager_secret_iam_binding" "main" {
for_each = { "DATABASE_PASSWORD" : google_sql_user.django.password,
"DATABASE_USER" : google_sql_user.django.name,
"DATABASE_NAME" : google_sql_database.database.name,
"DATABASE_HOST_PROD" : google_sql_database_instance.instance.private_ip_address,
"DATABASE_PORT_PROD" : 3306,
"PROJECT_ID" : var.project,
"GS_BUCKET_NAME" : var.project,
}
secret_id = google_secret_manager_secret.main[each.key].id
role = "roles/secretmanager.secretAccessor"
members = [local.cloudbuild_serviceaccount]
}
resource "google_secret_manager_secret_iam_binding" "network" {
for_each = {
"EXTERNAL_IP" : module.lb-http.external_ip,
}
secret_id = google_secret_manager_secret.network[each.key].id
role = "roles/secretmanager.secretAccessor"
members = [local.cloudbuild_serviceaccount]
}
resource "google_secret_manager_secret_iam_binding" "url" {
for_each = {
"WEBSITE_URL_US_CENTRAL1" : google_cloud_run_service.service["us-central1"].status[0].url,
"WEBSITE_URL_US_WEST1" : google_cloud_run_service.service["us-west1"].status[0].url,
"WEBSITE_URL_US_EAST1" : google_cloud_run_service.service["us-east1"].status[0].url,
}
secret_id = google_secret_manager_secret.url[each.key].id
role = "roles/secretmanager.secretAccessor"
members = [local.cloudbuild_serviceaccount]
}
resource "random_password" "SUPERUSER_PASSWORD" {
length = 32
special = false
}
resource "google_secret_manager_secret" "SUPERUSER_PASSWORD" {
secret_id = "SUPERUSER_PASSWORD"
replication {
automatic = true
}
depends_on = [google_project_service.secretmanager]
}
resource "google_secret_manager_secret_version" "SUPERUSER_PASSWORD" {
secret = google_secret_manager_secret.SUPERUSER_PASSWORD.id
secret_data = random_password.SUPERUSER_PASSWORD.result
}
resource "google_secret_manager_secret_iam_binding" "SUPERUSER_PASSWORD" {
secret_id = google_secret_manager_secret.SUPERUSER_PASSWORD.id
role = "roles/secretmanager.secretAccessor"
members = [local.cloudbuild_serviceaccount]
}
Container Registry-Eintrag erstellen
Erstellt einen Eintrag in Container Registry, um die Bereitstellung eines Container-Images in Cloud Run zu ermöglichen
resource "google_container_registry" "main" {
project = var.project
location = "US"
}
Container Registry-Speicherort öffentlich machen
Gewährt allgemein lesbaren Zugriff auf den Container Registry-Speicherort, sodass jeder das Container-Image verwenden kann.
resource "google_storage_bucket_iam_member" "repo_public" {
bucket = google_container_registry.main.id
role = "roles/storage.objectViewer"
member = "allUsers"
}
Container-Image erstellen
Mit dem folgenden Befehl wird das Docker-Image erstellt und in Container Registry gehostet:
resource "null_resource" "cloudbuild_api" {
provisioner "local-exec" {
working_dir = path.module
command = "gcloud builds submit . "
}
depends_on = [
google_container_registry.main
]
}
Container in Cloud Run bereitstellen
Der folgende Befehl verwendet Cloud Build, um einen Dienst in Cloud Run mithilfe des soeben erstellten Containers zu starten.
resource "google_cloud_run_service" "service" {
for_each = toset([for location in local.runlocations : location if can(regex("us-(?:west|central|east)1", location))])
name = var.project
location = each.value
project = var.project
autogenerate_revision_name = true
depends_on = [
# google_sql_database_instance.instance,
google_service_account.django,
google_sql_database_instance.instance,
google_vpc_access_connector.connector,
]
template {
spec {
service_account_name = google_service_account.django.email
containers {
image = "gcr.io/${var.project}/${var.service}:latest"
env {
name = "PROJECT_ID"
value = var.project
}
}
}
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "100"
"run.googleapis.com/cloudsql-instances" = google_sql_database_instance.instance.connection_name
"run.googleapis.com/client-name" = "terraform"
"run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.connector[each.key].name
"run.googleapis.com/vpc-access-egress" = "all-traffic"
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
Öffnen Sie den Cloud Run API-Dienst, um allgemein lesbar zu sein.
Diese API-Ebene der Anwendung wird vom Browser des Nutzers aufgerufen, aber standardmäßig sind Cloud Run-Dienste nicht öffentlich. Damit Nutzer diesen Dienst nutzen können, müssen wir Berechtigungen für diese Dienste öffnen, damit sie weltweit zugänglich sind.
resource "google_cloud_run_service_iam_policy" "noauth" {
for_each = toset([for location in local.runlocations : location if can(regex("us-(?:west|central|east)1", location))])
location = google_cloud_run_service.service[each.key].location
project = google_cloud_run_service.service[each.key].project
service = google_cloud_run_service.service[each.key].name
policy_data = data.google_iam_policy.noauth.policy_data
}
Load-Balancer erstellen
Mit dem folgenden Befehl werden ein Load-Balancer erstellt und Systemdiagnosen und Back-End-Dienste implementiert. Der Load-Balancer wird so konfiguriert, dass eine Verbindung zum Cloud Run-Dienst hergestellt wird.
# Step 11: Create Load Balancer to handle traffics from multiple regions
resource "google_compute_region_network_endpoint_group" "default" {
for_each = toset([for location in local.runlocations : location if can(regex("us-(?:west|central|east)1", location))])
name = "${var.project}--neg--${each.key}"
network_endpoint_type = "SERVERLESS"
region = google_cloud_run_service.service[each.key].location
cloud_run {
service = google_cloud_run_service.service[each.key].name
}
depends_on = [google_cloud_run_service.service]
}
module "lb-http" {
source = "GoogleCloudPlatform/lb-http/google//modules/serverless_negs"
version = "~> 4.5"
project = var.project
name = var.project
ssl = false
https_redirect = true
managed_ssl_certificate_domains = []
use_ssl_certificates = false
backends = {
default = {
description = null
enable_cdn = true
custom_request_headers = null
log_config = {
enable = true
sample_rate = 1.0
}
groups = [
for neg in google_compute_region_network_endpoint_group.default :
{
group = neg.id
}
]
iap_config = {
enable = false
oauth2_client_id = null
oauth2_client_secret = null
}
security_policy = null
}
}
}
Verschiedene Berechtigungen gewähren
Mit den folgenden Befehlen werden den Datenbankkonten und den Cloud Build-Dienstkonten Berechtigungen gewährt.
# Step 12: Grant access to the database
resource "google_project_iam_member" "service_permissions_cb_django" {
for_each = toset([
"run.admin", "cloudsql.client", "editor", "secretmanager.admin"
])
role = "roles/${each.key}"
member = local.django_serviceaccount
}
resource "google_project_iam_member" "service_permissions_cb" {
for_each = toset([
"run.admin", "cloudsql.client", "editor", "secretmanager.admin"
])
role = "roles/${each.key}"
member = local.cloudbuild_serviceaccount
}
resource "google_service_account_iam_binding" "cloudbuild_sa" {
service_account_id = google_service_account.django.name
role = "roles/editor"
members = [local.cloudbuild_serviceaccount]
}
Fazit
Nach der Ausführung sollte eine vollständig installierte Anwendung in mehreren Regionen ausgeführt werden, die das Teilen in sozialen Medien ermöglicht. Außerdem sollten Sie über den gesamten Code verfügen, um diese Lösung an Ihre Umgebung anzupassen oder zu erweitern.