Add CGP Cloud Run deployment documentation

* Add docs for gcp cloud run

* terraform gcp cloud run

* Edit GCP Clound Run documentation for clarity

---------

Co-authored-by: Christopher Charbonneau Wells <10456740+cdubz@users.noreply.github.com>
This commit is contained in:
Edvin N 2023-12-22 18:53:36 +01:00 committed by GitHub
parent 9d3501d366
commit d053050ed5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 282 additions and 11 deletions

View File

@ -89,9 +89,30 @@ for your instance of babybuddy.
After that, you just have to push babybuddy code repository to the Git
deployment URL of your Clever Cloud Python application.
## GCP Cloud Run
Baby Buddy can be hosted serverless in GCP Cloud Run using configuration provided at
`terraform/gcp-cloud-run`. The configuration scales down to zero for cost effectiveness.
With this approach initial requests to the service after a long period will be slow but
subsequent requests will be much faster. A [billing account](https://cloud.google.com/billing/docs/how-to/create-billing-account)
mut be configured in GCP to use the configuration.
The terraform code isn't production ready and is meant to be a good way of getting started.
No state strage is configured. See [storage options](https://cloud.google.com/run/docs/storage-options)
for information about how to configure persistant storage.
Run `terraform init` from the configurtion directory to get started:
```shell
git clone https://github.com/babybuddy/babybuddy.git
cd babybuddy/terraform/gcp-cloud-run
terraform init
terraform apply -var project_id=<project-id> -var project_name=<project-name> -var billing_account=<billing-account-id>
```
## Manual
There are many ways to deploy Baby Buddy manually to any server/VPS. The basic
There are many ways to deploy Baby Buddy manually to any server/VPS. The basic
requirements are Python, a web server, an application server, and a database.
### Requirements
@ -139,7 +160,7 @@ and any number of children).
```shell
cd /var/www/babybuddy/public
```
6. Initiate and enter a Python environment with Pipenv locally.
```shell
@ -183,7 +204,7 @@ and any number of children).
plugins = python3
project = babybuddy
base_dir = /var/www/babybuddy
chdir = %(base_dir)/public
virtualenv = %(chdir)/.venv
module = %(project).wsgi:application
@ -191,10 +212,10 @@ and any number of children).
master = True
vacuum = True
```
See the [uWSGI documentation](http://uwsgi-docs.readthedocs.io/en/latest/)
for more advanced configuration details.
See [Subdirectory configuration](subdirectory.md) for additional configuration
required if Baby Buddy will be hosted in a subdirectory of another server.
@ -212,30 +233,30 @@ and any number of children).
```
Example config:
```nginx
upstream babybuddy {
server unix:///var/run/uwsgi/app/babybuddy/socket;
}
server {
listen 80;
server_name babybuddy.example.com;
location / {
uwsgi_pass babybuddy;
include uwsgi_params;
}
location /media {
alias /var/www/babybuddy/data/media;
}
}
```
See the [nginx documentation](https://nginx.org/en/docs/) for more advanced
configuration details.
See [Subdirectory configuration](subdirectory.md) for additional configuration
required if Baby Buddy will be hosted in a subdirectory of another server.

View File

@ -0,0 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/google" {
version = "5.6.0"
hashes = [
"h1:eZMUVtLYrZMNb5QeqWpWfeQv5wNWQMRhzVtr/Fo5Wis=",
"zh:102b6a2672fade82114eb14ed46923fb1b74be2aaca3a50b4f35f7057a9a94b9",
"zh:1a56b63175068c67efbe7d130986ba2839a938f5ffc96a14fd450153174dbfa3",
"zh:1ba1c5e0c86e8aaa8037406390846e78c89b63faf9e527c7874641f35d436e1b",
"zh:3f7161b9288b47cbe89d2f9675f78d83b58ad5880c793b01f50a71ee2583844b",
"zh:66912d6e4180dac37185d17424b345a9d4e3c3c791d45e0737b35e32c9536b35",
"zh:6f06f56e9fac2e55b50e74ffac42d9522bb379394e51dca1eddd4c3b7a68545c",
"zh:8741861ebfa13bb1ed74ea7f4865388a0725ca3a781b6d873ce45e6a4630fe41",
"zh:ae89a9c538665fbc30bb83aa3b13acb18d8380e551ccf242e1c0ab4d626089ab",
"zh:c510f8321c7599aa601b1870fdc0c76cbad3054ed5cc70fe8e37a13a8046a71f",
"zh:cf143a53d5a25c6216d09a9c0b115bb473ffcebd5c4c62b2b2594b1ebc13e662",
"zh:de05b957e5dfdbaf92db47cd9b3ef46a0f8d94599eea6d472928f33058856add",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
hashes = [
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}

View File

@ -0,0 +1,187 @@
provider "google" {
region = var.region
}
resource "google_project" "baby_buddy" {
name = var.project_name
project_id = var.project_id
billing_account = var.billing_account
auto_create_network = false
}
locals {
services = toset(["run.googleapis.com",
"sqladmin.googleapis.com",
"sql-component.googleapis.com",
"secretmanager.googleapis.com"
])
}
resource "google_project_service" "project_services" {
for_each = local.services
project = google_project.baby_buddy.project_id
service = each.value
disable_on_destroy = true
disable_dependent_services = true
}
resource "random_password" "root_password" {
min_lower = 1
min_numeric = 1
min_upper = 1
length = 19
special = true
min_special = 1
lifecycle {
ignore_changes = [
min_lower, min_upper, min_numeric, special, min_special, length
]
}
}
resource "random_password" "django_secret_key" {
min_lower = 1
min_numeric = 1
min_upper = 1
length = 20
special = true
min_special = 1
lifecycle {
ignore_changes = [
min_lower, min_upper, min_numeric, special, min_special, length
]
}
}
resource "google_sql_database_instance" "baby_buddy" {
name = "babybuddy"
database_version = "POSTGRES_15"
root_password = random_password.root_password.result
settings {
tier = "db-f1-micro"
disk_autoresize = false
deletion_protection_enabled = false
insights_config {
query_insights_enabled = false
}
maintenance_window {
day = 1
hour = 0
}
}
deletion_protection = "true"
}
resource "google_secret_manager_secret" "postgres_password" {
secret_id = "postgres-password"
project = google_project.baby_buddy.project_id
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "postgres_password" {
secret = google_secret_manager_secret.postgres_password.name
secret_data = google_sql_database_instance.baby_buddy.root_password
}
resource "google_secret_manager_secret_iam_member" "postgres_password" {
secret_id = google_secret_manager_secret.postgres_password.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_project.baby_buddy.number}-compute@developer.gserviceaccount.com"
depends_on = [google_secret_manager_secret.postgres_password]
}
resource "google_secret_manager_secret" "django_secret_key" {
secret_id = "django-secret-key"
project = google_project.baby_buddy.project_id
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "django_secret_key" {
secret = google_secret_manager_secret.django_secret_key.name
secret_data = random_password.django_secret_key.result
}
resource "google_secret_manager_secret_iam_member" "django_secret_key" {
secret_id = google_secret_manager_secret.django_secret_key.name
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_project.baby_buddy.number}-compute@developer.gserviceaccount.com"
depends_on = [google_secret_manager_secret.django_secret_key]
}
resource "google_cloud_run_v2_service" "baby_buddy" {
name = "babybuddy"
location = var.region
ingress = "INGRESS_TRAFFIC_ALL"
project = google_project.baby_buddy.project_id
template {
scaling {
max_instance_count = 2
}
volumes {
name = "cloudsql"
cloud_sql_instance {
instances = [google_sql_database_instance.baby_buddy.connection_name]
}
}
containers {
name = "babybuddy-1"
image = "docker.io/linuxserver/babybuddy:latest"
env {
name = "DB_HOST"
value = "/cloudsql/${google_project.baby_buddy.project_id}:${var.region}:${google_sql_database_instance.baby_buddy.name}"
}
env {
name = "DB_USER"
value = "postgres"
}
env {
name = "DB_NAME"
value = "postgres"
}
env {
name = "DB_ENGINE"
value = "django.db.backends.postgresql"
}
env {
name = "DB_PASSWORD"
value_source {
secret_key_ref {
secret = google_secret_manager_secret.postgres_password.secret_id
version = google_secret_manager_secret_version.postgres_password.version
}
}
}
env {
name = "SECRET_KEY"
value_source {
secret_key_ref {
secret = google_secret_manager_secret.django_secret_key.secret_id
version = google_secret_manager_secret_version.django_secret_key.version
}
}
}
volume_mounts {
name = "cloudsql"
mount_path = "/cloudsql"
}
}
}
annotations = {
"foo" = "bar"
}
traffic {
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
percent = 100
}
depends_on = [google_secret_manager_secret_version.postgres_password, google_secret_manager_secret_version.django_secret_key]
}

View File

@ -0,0 +1,3 @@
output "url" {
value = google_cloud_run_v2_service.baby_buddy.uri
}

View File

@ -0,0 +1,20 @@
variable "project_name" {
description = "project name"
type = string
}
variable "project_id" {
description = "project id, remember the project id has to be unique"
type = string
}
variable "billing_account" {
description = "value of the billing account id"
type = string
}
variable "region" {
description = "which region to deploy to"
type = string
default = "europe-north1"
}