서버리스 엔드 투 엔드 사진 공유 애플리케이션은 11개의 Google Cloud 제품, Terraform, Django로 확장 가능한 엔드 투 엔드 사진 공유 애플리케이션을 생성합니다.
이 스택은 다음과 같은 구성요소를 구성하고 생성합니다.
- Cloud Run - 앱을 기본 서버로 실행합니다.
- Cloud SQL - 사용자 정보, 게시물 등의 관계형 데이터베이스를 저장합니다.
- Cloud Storage - 게시물 미디어와 같은 비관계형 데이터베이스를 저장합니다.
- Cloud Load Balancer - 여러 리전에 트래픽을 제공합니다.
- Cloud DNS - 커스텀 도메인을 매핑합니다.
- Cloud Build - gcloud에서 앱을 자동으로 배포합니다.
- Secret Manager - 앱의 보안을 향상합니다.
- Cloud VPC - 비공개 향상을 통해 Cloud SQL과 Cloud Run을 연결합니다.
- Cloud DNS - 더 빠른 연결을 위해 정적 캐시를 저장합니다.
- Translation API - 게시물 캡션을 다른 언어로 번역합니다.
시작하기
Cloud Shell에서 소스 코드의 복사본에 대해 다음 링크를 클릭합니다. 그러면 단일 명령어로 프로젝트에서 애플리케이션의 작업 복사본이 작동합니다.
서버리스 엔드 투 엔드 사진 공유 애플리케이션 구성요소
서버리스 엔드 투 엔드 사진 공유 애플리케이션 아키텍처는 여러 제품을 사용합니다. 다음은 관련 동영상 링크, 제품 문서 및 대화형 둘러보기를 포함하여 구성 요소에 대한 자세한 정보와 함께 구성요소 목록을 보여줍니다.스크립트
설치 스크립트는 go
및 Terraform CLI 도구로 작성된 실행 파일을 사용하여 빈 프로젝트를 가져오고 여기에 애플리케이션을 설치합니다. 출력은 작동하는 애플리케이션과 부하 분산 IP 주소의 URL입니다.
./main.tf
서비스 사용 설정
Google Cloud 서비스는 기본적으로 프로젝트에서 사용 중지되어 있습니다. 여기에서 솔루션을 사용하려면 다음을 활성화해야 합니다.
- Cloud Run — 컨테이너를 호스팅하는 서버리스 도구이며 애플리케이션에 액세스하기 위한 URL을 제공합니다.
- Cloud SQL — 애플리케이션의 데이터베이스 스토리지
- Compute Engine - 가상 머신 및 네트워킹
- Cloud Build — 컨테이너 이미지를 만들고 Cloud Run에 배포
- Cloud Secret Manager - SQL 및 Redis에 대해 호스트 IP를 Cloud Run용 Cloud Build에 삽입하는 데 사용됩니다.
- 서비스 네트워킹 및 서버리스 VPC 액세스 - Cloud Run이 비공개 네트워크에서 SQL 및 Redis와 통신하도록 허용하고, API에서 들어오는 외부 호출이 이러한 서버에 액세스하지 못하도록 합니다.
- Cloud IAM — 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
}
비공개 네트워크를 만듭니다.
다음 명령어는 애플리케이션 내에서 안전하게 통신하는 데 사용할 모든 리소스에 대한 비공개 네트워크를 만듭니다.
resource "google_compute_network" "main" {
provider = google
name = "social-media-network-${random_id.name.hex}"
depends_on = [google_project_iam_member.serviceagent]
}
SQL 인스턴스의 네트워킹 만들기
다음 명령어는 Cloud Run에서 Cloud SQL에 액세스하도록 허용합니다.
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]
}
프로젝트에 대한 서비스 에이전트 액세스 허용
이 명령어를 사용하면 서비스 에이전트가 프로젝트에 액세스하여 VPC 커넥터 구성을 사용 설정할 수 있습니다.
resource "google_project_iam_member" "serviceagent" {
project = data.google_project.project.number
role = "roles/editor"
member = local.serviceagent_serviceaccount
}
VPC 액세스 커넥터 만들기
이 명령어는 Cloud Run을 데이터베이스에 연결합니다.
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]
}
서비스 계정 만들기
이 명령어는 애플리케이션이 앱을 제공하는 데 필요한 모든 리소스에 인증하는 데 사용할 서비스 계정을 만듭니다.
# Step 4: Create a custom Service Account
resource "google_service_account" "django" {
account_id = "django"
depends_on = [
google_project_service.iam
]
}
SQL 서버 만들기
다음 명령어 집합은 SQL 서버 인스턴스를 구성하고 초기화합니다.
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
}
스토리지 버킷 만들기
애플리케이션을 저장하고 사용자에게 제공할 미디어의 스토리지 위치를 만듭니다.
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",
]
}
보안 비밀 만들기
다음 명령어 집합은 민감한 애플리케이션 설정과 리소스를 만들어 Cloud Secret에 저장합니다.
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]
}
서비스 계정 권한 설정
이 명령어를 사용하면 Cloud Build 및 애플리케이션 서비스 계정이 Cloud Secret의 콘텐츠에 액세스할 수 있습니다.
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]
}
보안 비밀 채우기
다음 명령어 세트는 Cloud Secret에 애플리케이션 보안 비밀을 만듭니다.
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 레코드 만들기
Cloud Run에 컨테이너 이미지를 배포할 수 있도록 Container Registry에 레코드를 만듭니다.
resource "google_container_registry" "main" {
project = var.project
location = "US"
}
Container Registry 스토리지 위치를 공개로 설정
누구나 컨테이너 이미지를 사용할 수 있도록 Container Registry 위치에 대한 읽기 가능한 액세스 권한을 부여합니다.
resource "google_storage_bucket_iam_member" "repo_public" {
bucket = google_container_registry.main.id
role = "roles/storage.objectViewer"
member = "allUsers"
}
컨테이너 이미지 만들기
다음 명령어는 Docker 이미지를 만들어 Container Registry에 호스팅합니다.
resource "null_resource" "cloudbuild_api" {
provisioner "local-exec" {
working_dir = path.module
command = "gcloud builds submit . "
}
depends_on = [
google_container_registry.main
]
}
Cloud Run에 컨테이너 배포
다음 명령어는 Cloud Build를 사용하고 바로 전에 만든 컨테이너를 사용하여 Cloud Run에서 서비스를 가동합니다.
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
}
}
전 세계에서 읽을 수 있도록 Cloud Run API 서비스를 엽니다.
애플리케이션의 API 레이어가 사용자의 브라우저를 통해 호출되지만 기본적으로 Cloud Run 서비스는 공개되지 않습니다. 사용자가 이 서비스를 사용하려면 전 세계에서 액세스할 수 있도록 이러한 서비스에 대한 권한을 열어야 합니다.
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
}
부하 분산기 만들기
다음 명령어는 부하 분산기를 만들고 상태 점검 및 백엔드 서비스를 구현합니다. 부하 분산기가 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
}
}
}
다양한 권한 부여
다음 일련의 명령어는 데이터베이스 계정 및 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]
}
결론
실행이 완료되면 소셜 미디어 공유를 허용하는 여러 리전에서 애플리케이션이 완전히 설치된 것입니다. 또한 환경에 맞게 이 솔루션을 수정하거나 확장할 수 있도록 모든 코드가 준비되었습니다.