Mit der serverlosen End-to-End-Fotofreigabeanwendung wird eine skalierbare End-to-End-Fotofreigabeanwendung mit 11 Google Cloud-Produkten, Terraform und Django erstellt.
Mit diesem Stack werden die folgenden Komponenten konfiguriert und erstellt:
- Cloud Run – die App wird als Hauptserver ausgeführt
- Cloud SQL: Zum Speichern relationaler Datenbanken wie Nutzerinformationen und Beiträge
- Cloud Storage: Zum Speichern nicht relationaler Datenbanken wie Postmedien
- Cloud Load Balancer – Traffic für mehrere Regionen bereitstellen
- Cloud DNS – Benutzerdefinierte Domain zuordnen
- Cloud Build – Automatische Bereitstellung Ihrer App über gcloud
- Secret Manager: Zur Verbesserung der Sicherheit der App
- Cloud VPC – Cloud SQL über Private Connect mit Cloud Run verbinden
- Cloud DNS: zum Speichern von statischem Cache für schnellere Verbindungen
- Translation API: Übersetzt den Beitragstitel, wenn er in einer anderen Sprache verfasst ist.
Jetzt starten
Klicken Sie auf den folgenden Link, um eine Kopie des Quellcodes in Cloud Shell aufzurufen. Dort können Sie mit einem einzigen Befehl eine funktionierende Kopie der Anwendung in Ihrem Projekt erstellen.
Serverlose Komponenten für eine End-to-End-Fotofreigabeanwendung
Die serverlose End-to-End-Architektur der Fotofreigabe-Anwendung nutzt mehrere 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 in go
geschriebene ausführbare Datei und Terraform-Befehlszeilentools, 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 wir die folgenden 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 einzuschleusen.
- Service Networking und serverloser VPC-Zugriff: Damit kann Cloud Run über ein privates Netzwerk mit SQL und Redis kommunizieren, während diese Server für externe Aufrufe von der API aus nicht zugänglich sind.
- Cloud IAM: Tool zum Verwalten von Zugriff und Berechtigungen für 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, über das sie innerhalb der Anwendung sicher kommunizieren 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 können Sie Cloud SQL von Cloud Run aus erreichen:
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 Kundenservicemitarbeiter auf das Projekt zugreifen, um die Konfiguration des VPC-Connectors zu ermöglichen.
resource "google_project_iam_member" "serviceagent" {
project = data.google_project.project.number
role = "roles/editor"
member = local.serviceagent_serviceaccount
}
VPC-Zugangsconnector erstellen
Mit diesem Befehl wird Cloud Run mit der Datenbank 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 für die Anwendung erstellt, mit dem sie sich bei allen Ressourcen authentifizieren kann, die für die Bereitstellung der App erforderlich sind.
# 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-Serverinstanz 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, die von der Anwendung gespeichert und für Nutzer 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, die in Cloud Secrets gespeichert werden.
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 kann 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 einfügen
Mit den folgenden Befehlen werden Anwendungssecrets in Cloud Secrets erstellt.
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 das Bereitstellen eines Container-Images in Cloud Run zu ermöglichen
resource "google_container_registry" "main" {
project = var.project
location = "US"
}
Speicherort der Container Registry öffentlich machen
Gewährt Lesezugriff für alle auf den Speicherort der Container Registry, damit 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
Mit dem folgenden Befehl wird mit Cloud Build ein Dienst in Cloud Run mit dem gerade erstellten Container gestartet.
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, damit er von allen gelesen werden kann.
Diese API-Ebene der Anwendung wird vom Browser des Nutzers aufgerufen. Cloud Run-Dienste sind jedoch standardmäßig nicht öffentlich. Damit Nutzer diesen Dienst nutzen können, müssen wir Berechtigungen für diese Dienste erteilen, 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 wird ein Load Balancer erstellt und Systemdiagnosen und Back-End-Dienste implementiert. Der Load Balancer wird so konfiguriert, dass er eine Verbindung zum Cloud Run-Dienst herstellt.
# 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 erteilen
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 jetzt eine vollständig installierte Anwendung in mehreren Regionen ausgeführt werden, die die Freigabe in sozialen Medien ermöglicht. Außerdem sollten Sie den gesamten Code haben, um diese Lösung an Ihre Umgebung anzupassen oder zu erweitern.