L'applicazione di condivisione delle foto end-to-end serverless crea un'applicazione di condivisione delle foto end-to-end scalabile con 11 prodotti Google Cloud, Terraform e Django.
Questo stack configurerà e creerà i seguenti componenti:
- Cloud Run, che eseguirà l'app come server principale
- Cloud SQL: per archiviare database relazionali come informazioni utente, post
- Cloud Storage: per archiviare un database non relazionale come il caricamento di contenuti multimediali
- Bilanciatore del carico Cloud - Al traffico del server con più regioni
- Cloud DNS - Per mappare un dominio personalizzato
- Cloud Build: per eseguire automaticamente il deployment dell'app da gcloud
- Secret Manager: per migliorare la sicurezza dell'app
- Cloud VPC - Per connettere Cloud SQL con Cloud Run tramite miglioramento privato
- Cloud DNS: per memorizzare la cache statica per connessioni più rapide
- API Translation: per tradurre la didascalia di un post, se è in un'altra lingua
Inizia
Fai clic sul link seguente per creare una copia del codice sorgente in Cloud Shell. Una volta lì, un singolo comando avvierà una copia funzionante dell'applicazione nel tuo progetto.
Visualizza il codice sorgente su GitHub
Componenti dell'applicazione di condivisione di foto end-to-end serverless
L'architettura dell'applicazione di condivisione di foto end-to-end serverless utilizza diversi prodotti. Di seguito sono elencati i componenti, insieme a ulteriori informazioni su di essi, inclusi link a video correlati, documentazione del prodotto e walkthrough interattivi.Script
Lo script di installazione usa un eseguibile scritto negli strumenti dell'interfaccia a riga di comando go
e Terraform per
installare l'applicazione al suo interno
in un progetto vuoto. L'output dovrebbe essere un
e un URL per l'indirizzo IP del bilanciamento del carico.
./main.tf
Attiva i servizi
I servizi Google Cloud sono disabilitati in un progetto per impostazione predefinita. Per utilizzare qualsiasi delle soluzioni, dobbiamo attivare le seguenti soluzioni:
- Cloud Run: lo strumento serverless che ospiterà i container e fornirà gli URL da cui accedere all'applicazione.
- Cloud SQL: spazio di archiviazione del database per l'applicazione
- Compute Engine: macchine virtuali e networking
- Cloud Build: crea immagini container ed esegue il deployment in Cloud Run
- Cloud Secret Manager: utilizzato per inserire gli IP dell'host per SQL e Redis in Cloud Build per Cloud Run.
- Service Networking e accesso VPC serverless: consente a Cloud Run di comunicare con SQL e Redis su una rete privata, mantenendo questi server inaccessibili dalle chiamate esterne provenienti dall'API.
- Cloud IAM: lo strumento per gestire l'accesso e le autorizzazioni alle risorse Google Cloud
# 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
}
Crea una rete privata
Il comando seguente crea una rete privata per tutte le risorse da utilizzare per comunicare in modo sicuro all'interno dell'applicazione.
resource "google_compute_network" "main" {
provider = google
name = "social-media-network-${random_id.name.hex}"
depends_on = [google_project_iam_member.serviceagent]
}
Crea la rete per l'istanza SQL
Il seguente comando consente di accedere a Cloud SQL da Cloud Run:
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]
}
Consenti all'agente di servizio di accedere al progetto
Questo comando consente all'agente di servizio di accedere al progetto per abilitare la configurazione del connettore VPC.
resource "google_project_iam_member" "serviceagent" {
project = data.google_project.project.number
role = "roles/editor"
member = local.serviceagent_serviceaccount
}
Crea un connettore di accesso VPC
Questo comando connette Cloud Run al database
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]
}
Crea service account
Questo comando crea un account di servizio che l'applicazione può utilizzare per autenticarsi in tutte le risorse di cui ha bisogno per pubblicare l'app.
# Step 4: Create a custom Service Account
resource "google_service_account" "django" {
account_id = "django"
depends_on = [
google_project_service.iam
]
}
Crea SQL Server
Il seguente insieme di comandi configura e inizializza un'istanza SQL Server.
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
}
Creazione di bucket di archiviazione
Crea la posizione di archiviazione dei contenuti multimediali da archiviare e pubblicare per gli utenti.
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",
]
}
Crea secret
Il seguente insieme di comandi crea impostazioni e risorse dell'applicazione sensibili per archiviarle in Cloud Secrets.
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]
}
Impostare le autorizzazioni dell'account di servizio
Questo comando consente all'account Cloud Build e al servizio delle applicazioni di accedere in Cloud Secrets.
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]
}
Compilare i secret
Il seguente insieme di comandi crea e archivia i secret dell'applicazione in Cloud Secrets.
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]
}
Creare un record di Container Registry
Crea un record in Container Registry per consentire il deployment di un'immagine container su Cloud Run
resource "google_container_registry" "main" {
project = var.project
location = "US"
}
Rendi pubblica la posizione di archiviazione di Container Registry
Concede l'accesso leggibile da chiunque alla posizione di Container Registry in modo che chiunque può utilizzare l'immagine container.
resource "google_storage_bucket_iam_member" "repo_public" {
bucket = google_container_registry.main.id
role = "roles/storage.objectViewer"
member = "allUsers"
}
Crea immagine container
Il seguente comando crea l'immagine Docker e la ospita su Container Registry:
resource "null_resource" "cloudbuild_api" {
provisioner "local-exec" {
working_dir = path.module
command = "gcloud builds submit . "
}
depends_on = [
google_container_registry.main
]
}
Esegui il deployment del container in Cloud Run
Il seguente comando utilizza Cloud Build per avviare un servizio su Cloud Run utilizzando il contenitore appena creato.
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
}
}
Apri il servizio API Cloud Run in modo che sia leggibile da tutti.
Questo livello API dell'applicazione verrà chiamato dal browser dell'utente, ma e i servizi Cloud Run predefiniti non sono pubblici. Affinché gli utenti utilizzare questo servizio, dobbiamo aprire le autorizzazioni su questi servizi per accessibile al mondo.
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
}
Crea bilanciatore del carico
Il comando seguente crea un bilanciatore del carico e implementa i controlli di integrità e i servizi di backend. Configura il bilanciatore del carico in modo che si connetta al servizio Cloud Run.
# 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
}
}
}
Concedi varie autorizzazioni
Il seguente set di comandi concede le autorizzazioni agli account del database e gli account di servizio Cloud Build.
# 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]
}
Conclusione
Una volta eseguita, dovresti avere un'applicazione completamente installata in esecuzione diverse regioni che consentono la condivisione sui social media. Inoltre dovresti avere tutto il codice per modificare o estendere questa soluzione in modo che si adatti al tuo ambiente.