Une application à trois niveaux est une application de tâches simple, conçue comme une application standard à trois niveaux:
- Backend
- Base de données - MySQL - Cloud SQL
- Mise en cache - Redis - Cloud Memorystore
- Intergiciel/API
- API Container Hosting – Golang – Cloud Run
- Frontal/UI
- UI hébergée en conteneur – Nginx + HTML/JS/CSS – Cloud Run
- Déploiement
- Déploiement continu : Cloud Build
- Gestion des secrets – Cloud Secret Manager
Commencer
Cliquez sur le lien suivant pour copier le code source dans Cloud Shell. Une seule commande permet alors de lancer une copie de travail de l'application dans votre projet.
Afficher le code source sur GitHub
Composants d'application à trois niveaux
L'architecture d'application à trois niveaux utilise plusieurs produits. Vous trouverez ci-dessous la liste des composants, ainsi que des informations supplémentaires et des liens vers des vidéos associées, de la documentation produit et des tutoriels interactifs.Scripts
Le script d'installation utilise un exécutable écrit dans go
et les outils de la CLI Terraform pour prendre un projet vide et y installer l'application. Le résultat doit correspondre à une application fonctionnelle et à une URL pour l'adresse IP de l'équilibrage de charge.
./main.tf
Activer les services
Les services Google Cloud sont désactivés par défaut dans les projets. La fonctionnalité ToDo nécessite l'activation des services suivants:
- Service Networking et accès au VPC sans serveur : permet à Cloud Run de communiquer avec SQL et Redis sur un réseau privé, ce qui les rend inaccessibles aux appels externes provenant de l'API.
- Cloud Build : crée des images de conteneurs et les déploie sur Cloud Run.
- Cloud Memorystore fournit une couche de mise en cache pour l'application.
- Cloud Run : outil sans serveur qui hébergera les conteneurs et fournira les URL permettant d'accéder à l'application.
- Cloud SQL : stockage de base de données pour l'application
- Cloud Storage : utilisé par Cloud Build et pour charger le schéma dans la base de données
- Cloud Secret Manager : permet d'injecter les adresses IP d'hôte pour SQL et Redis dans Cloud Build pour Cloud Run.
- Artifact Registry : stocke les images Docker pour les utiliser avec Cloud Build.
variable "gcp_service_list" {
description = "The list of apis necessary for the project"
type = list(string)
default = [
"compute.googleapis.com",
"cloudapis.googleapis.com",
"vpcaccess.googleapis.com",
"servicenetworking.googleapis.com",
"cloudbuild.googleapis.com",
"sql-component.googleapis.com",
"sqladmin.googleapis.com",
"storage.googleapis.com",
"secretmanager.googleapis.com",
"run.googleapis.com",
"artifactregistry.googleapis.com",
"redis.googleapis.com"
]
}
resource "google_project_service" "all" {
for_each = toset(var.gcp_service_list)
project = var.project_number
service = each.key
disable_on_destroy = false
}
Définir des autorisations
La commande suivante définit les rôles et les autorisations IAM qui permettent à Cloud Build de déployer des services.
- Activer le compte de service Cloud Build pour le déploiement sur Cloud Run
- Activez le compte de service Cloud Build pour définir l'accès VPN pour Cloud Run
- Activer le compte de service Cloud Build pour effectuer des activités de compte de service
- Permettre au compte de service Cloud Build d'agir au nom du compte de service Compute
- Activer le compte de service Cloud Build pour publier sur Cloud Run
- Activer le compte de service Cloud Build pour utiliser les secrets
- Activez le compte de service Cloud Build pour stocker des conteneurs dans Artifact Registry
variable "build_roles_list" {
description = "The list of roles that build needs for"
type = list(string)
default = [
"roles/run.developer",
"roles/vpaccess.user",
"roles/iam.serviceAccountUser",
"roles/run.admin",
"roles/secretmanager.secretAccessor",
"roles/artifactregistry.admin",
]
}
resource "google_project_iam_member" "allbuild" {
for_each = toset(var.build_roles_list)
project = var.project_number
role = each.key
member = "serviceAccount:${local.sabuild}"
depends_on = [google_project_service.all]
}
Créer une mise en réseau pour l'instance SQL
La commande suivante permet d'accéder à Cloud SQL depuis Cloud Run:
resource "google_compute_global_address" "google_managed_services_vpn_connector" {
name = "google-managed-services-vpn-connector"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = local.defaultnetwork
project = var.project_id
depends_on = [google_project_service.all]
}
resource "google_service_networking_connection" "vpcpeerings" {
network = local.defaultnetwork
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.google_managed_services_vpn_connector.name]
}
Créer un connecteur d'accès VPC
Connecte Cloud Run à la base de données et à la mise en cache
resource "google_vpc_access_connector" "connector" {
provider = google-beta
project = var.project_id
name = "vpc-connector"
ip_cidr_range = "10.8.0.0/28"
network = "default"
region = var.region
depends_on = [google_compute_global_address.google_managed_services_vpn_connector, google_project_service.all]
}
Créer un serveur Redis
Configure et initialise une instance de serveur Redis.
resource "google_redis_instance" "todo_cache" {
authorized_network = local.defaultnetwork
connect_mode = "DIRECT_PEERING"
location_id = var.zone
memory_size_gb = 1
name = "${var.basename}-cache"
project = var.project_id
redis_version = "REDIS_6_X"
region = var.region
reserved_ip_range = "10.137.125.88/29"
tier = "BASIC"
transit_encryption_mode = "DISABLED"
depends_on = [google_project_service.all]
}
Créer un serveur SQL
La commande suivante configure et initialise une instance SQL Server.
resource "google_sql_database_instance" "todo_database" {
name="${var.basename}-db-${random_id.id.hex}"
database_version = "MYSQL_5_7"
region = var.region
project = var.project_id
settings {
tier = "db-g1-small"
disk_autoresize = true
disk_autoresize_limit = 0
disk_size = 10
disk_type = "PD_SSD"
ip_configuration {
ipv4_enabled = false
private_network = local.defaultnetwork
}
location_preference {
zone = var.zone
}
}
deletion_protection = false
depends_on = [
google_project_service.all,
google_service_networking_connection.vpcpeerings
]
# This handles loading the schema after the database installs.
provisioner "local-exec" {
working_dir = "${path.module}/code/database"
command = "./load_schema.sh ${var.project_id} ${google_sql_database_instance.todo_database.name}"
}
}
Créer un dépôt Artifact Registry
La commande suivante stocke les images Docker à utiliser avec Cloud Run.
resource "google_artifact_registry_repository" "todo_app" {
provider = google-beta
format = "DOCKER"
location = var.region
project = var.project_id
repository_id = "${var.basename}-app"
depends_on = [google_project_service.all]
}
Créer des secrets
La commande suivante stocke les données de l'hôte Redis et SQL dans des secrets Cloud.
resource "google_secret_manager_secret" "redishost" {
project = var.project_number
replication {
automatic = true
}
secret_id = "redishost"
depends_on = [google_project_service.all]
}
resource "google_secret_manager_secret_version" "redishost" {
enabled = true
secret = "projects/${var.project_number}/secrets/redishost"
secret_data = google_redis_instance.todo_cache.host
depends_on = [google_project_service.all, google_redis_instance.todo_cache, google_secret_manager_secret.redishost]
}
resource "google_secret_manager_secret" "sqlhost" {
project = var.project_number
replication {
automatic = true
}
secret_id = "sqlhost"
depends_on = [google_project_service.all]
}
resource "google_secret_manager_secret_version" "sqlhost" {
enabled = true
secret = "projects/${var.project_number}/secrets/sqlhost"
secret_data = google_sql_database_instance.todo_database.private_ip_address
depends_on = [google_project_service.all, google_sql_database_instance.todo_database, google_secret_manager_secret.sqlhost]
}
Créer un artefact pour le middleware
La commande suivante crée l'image Docker et l'héberge sur Artifact Registry : ./code/frontend/clouldbuild.yaml
resource "null_resource" "cloudbuild_api" {
provisioner "local-exec" {
working_dir = "${path.module}/code/middleware"
command = "gcloud builds submit . --substitutions=_REGION=${var.region},_BASENAME=${var.basename}"
}
depends_on = [
google_artifact_registry_repository.todo_app,
google_secret_manager_secret_version.redishost,
google_secret_manager_secret_version.sqlhost,
google_project_service.all
]
}
Déployer un conteneur d'API dans Cloud Run
La commande suivante utilise Cloud Build pour lancer un service sur Cloud Run à l'aide du conteneur que vous venez de créer.
resource "google_cloud_run_service" "api" {
name = "${var.basename}-api"
location = var.region
project = var.project_id
template {
spec {
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.basename}-app/api"
env {
name = "REDISHOST"
value_from {
secret_key_ref {
name = google_secret_manager_secret.redishost.secret_id
key = "latest"
}
}
}
env {
name = "todo_host"
value_from {
secret_key_ref {
name = google_secret_manager_secret.sqlhost.secret_id
key = "latest"
}
}
}
env {
name = "todo_user"
value = "todo_user"
}
env {
name = "todo_pass"
value = "todo_pass"
}
env {
name = "todo_name"
value = "todo"
}
env {
name = "REDISPORT"
value = "6379"
}
}
}
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "1000"
"run.googleapis.com/cloudsql-instances" = google_sql_database_instance.todo_database.connection_name
"run.googleapis.com/client-name" = "terraform"
"run.googleapis.com/vpc-access-egress" = "all"
"run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.connector.id
}
}
}
autogenerate_revision_name = true
depends_on = [
null_resource.cloudbuild_api,
google_project_iam_member.secretmanager_secretAccessor
]
}
Ouvrez le service d'API Cloud Run pour qu'il soit lisible par tous.
Cette couche API de l'application sera appelée par le navigateur de l'utilisateur, mais par défaut, les services Cloud Run ne sont pas publics. Pour que les utilisateurs puissent utiliser ce service, nous devons ouvrir des autorisations sur ces services afin qu'ils soient accessibles à tous.
resource "google_cloud_run_service_iam_policy" "noauth_api" {
location = google_cloud_run_service.api.location
project = google_cloud_run_service.api.project
service = google_cloud_run_service.api.name
policy_data = data.google_iam_policy.noauth.policy_data
}
Créer un artefact pour l'interface
La commande suivante crée l'image Docker et l'héberge sur Artifact Registry : ./code/frontend/clouldbuild.yaml
resource "null_resource" "cloudbuild_fe" {
provisioner "local-exec" {
working_dir = "${path.module}/code/frontend"
command = "gcloud builds submit . --substitutions=_REGION=${var.region},_BASENAME=${var.basename}"
}
depends_on = [
google_artifact_registry_repository.todo_app,
google_cloud_run_service.api
]
}
Déployer un conteneur d'interface dans Cloud Run
La commande suivante utilise Cloud Build pour lancer un service sur Cloud Run à l'aide du conteneur que nous venons de créer.
resource "google_cloud_run_service" "fe" {
name = "${var.basename}-fe"
location = var.region
project = var.project_id
template {
spec {
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.basename}-app/fe"
ports {
container_port = 80
}
}
}
}
depends_on = [null_resource.cloudbuild_fe]
}
Ouvrir le service d'interface Cloud Run pour qu'il soit lisible par tous
Il s'agit de l'interface de l'application, qui affiche le code HTML/JS/CSS via lequel l'utilisateur interagit avec l'application. Par défaut, les services Cloud Run ne sont pas publics. Pour que cette application fonctionne, nous devons ouvrir des autorisations sur ces services afin qu'ils soient accessibles à tous.
resource "google_cloud_run_service_iam_policy" "noauth_fe" {
location = google_cloud_run_service.fe.location
project = google_cloud_run_service.fe.project
service = google_cloud_run_service.fe.name
policy_data = data.google_iam_policy.noauth.policy_data
}
./code/database/load_schema.sh
Initialiser le schéma de base de données
Cette commande crée un bucket Cloud Storage temporaire pour importer le schéma dans Cloud SQL.
PROJECT=$1
SQLNAME=$2
SQLSERVICEACCOUNT=$(gcloud sql instances describe $SQLNAME --format="value(serviceAccountEmailAddress)" | xargs)
gsutil mb gs://$PROJECT-temp
gsutil cp schema.sql gs://$PROJECT-temp/schema.sql
gsutil iam ch serviceAccount:$SQLSERVICEACCOUNT:objectViewer gs://$PROJECT-temp/
gcloud sql import sql $SQLNAME gs://$PROJECT-temp/schema.sql -q
gsutil rm gs://$PROJECT-temp/schema.sql
gsutil rb gs://$PROJECT-temp
./code/middleware/clouldbuild.yaml
Créer un conteneur d'API
Ce code crée une image Docker pour la couche de middleware.
name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/api', '.' ]
```
#### Push API container to Artifact Registry
Pushing the container to Artifact Registry makes it possible for Cloud Run to
get the image and serve it.
``` yaml
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/api']
Substitutions
Le code suivant crée des variables avec des valeurs par défaut afin que ces valeurs puissent être modifiées au moment du déploiement.
substitutions:
_REGION: us-central1
_BASENAME: todo
./code/frontend/clouldbuild.yaml
Contenu du code de massage
L'interface est entièrement statique (HTML/JS/CSS). L'application doit pointer vers l'URL du service d'API que nous venons de créer, mais une URL avec une chaîne aléatoire est attribuée aux services Cloud Run. Ce script de massage capture cette URL aléatoire et l'injecte dans le code du JavaScript statique dans ce conteneur.
name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
entrypoint: bash
args: [ './massage.sh', '$_REGION' ]
Créer un conteneur d'API
Le code suivant crée une image Docker pour la couche de middleware:
name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe', '.' ]
Transférer le conteneur d'API vers Artifact Registry
Le transfert du conteneur vers Artifact Registry permet à Cloud Run d'obtenir l'image et de la diffuser.
name: 'gcr.io/cloud-builders/docker'
args: ['push', '$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/fe']
Substitutions
Créez une variable avec une valeur par défaut afin que ces valeurs puissent être modifiées au moment du déploiement.
substitutions:
_REGION: us-central1
_BASENAME: todo
./code/frontend/massage.sh
Modifier le code JavaScript
Cette commande injecte le point de terminaison du middleware dans le code JavaScript de l'interface.
API=$(gcloud run services describe todo-api --region=$1 --format="value(status.url)")
stripped=$(echo ${API/https:\/\//})
sed -i"" -e "s/127.0.0.1:9000/$stripped/" www/js/main.js
Conclusion
Vous disposez désormais d'une application de tâches simple à trois niveaux exécutée sur Cloud Run dans votre projet. Vous disposez également de tout le code nécessaire pour modifier ou étendre cette solution afin de l'adapter à votre environnement.