My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

So you think it would be easy to use Cloud SQL for your Grafana

Heechul Ryu's photo
Heechul Ryu
·Mar 29, 2020

Well... I thought so too and the spoiler alert is that it wasn't easy (at least for me)

The goal I tried to achieve was to let the Grafana running (installed via helm chart) on GKE to use Cloud SQL instead of default sqlite3 for data persistency and better management.

Grafana saves your data into a database. By default, it is configured to use sqlite3 (on local disk) which doesn't give you much control of data safety with various scenarios.

(sudden) Prerequisites

Sorry to interrupt you, but to see what I'm talking about in this post, you might need to be familiar with Kubernetes aka k8s, GCP (or AWS or other cloud providers), Cloud SQL (just a bit), Grafana, Helm, Terraform.

Just in case you are not familiar with any of those, here are my short descriptions for you:

- k8s lets your (docker) container run in multiple hosts for scalability and it's super cool.
- GCP is google's version of AWS and I find it pretty cool and modern (also few frustrations as well)
- Cloud SQL is managed relational database service, I think you can compare this to AWS RDS
- Grafana lets you visualize your time-series data into beautiful dashboards.
- Helm allows you to packages k8s manifests and there are tons are packagings (charts) done by many people you can find and use
- Terraform allow you to express your infrastructure/system as a code and it also helps you to actually create/update/delete the systems/resources respecting your code.

And an assumption

Also in this post I'm assuming that you are familiar with VPC and GKE and you already configured that with TF yourself. Even if you are not, it's a whole other topic, so I wouldn't go for that part in this post.

possibly like this:

# https://registry.terraform.io/modules/terraform-google-modules/kubernetes-engine/google
module "my_vpc" {
   # ...
}

https://registry.terraform.io/modules/terraform-google-modules/network/google
module "my_gke" {
   # ...
}

What I imagined it would be.

Simply spin up a Cloud SQL instance by adding few resources or a module as a Terraform code and feed some environment variables to Grafana, so it can start storing data into the Cloud SQL instance. I expected this would take maybe a half a day or a full day if I'm distracted with other things to do too. But that's not what happened and I admit I was quite naive.

What actually happened

I tried to spin up Cloud SQL instance first

Since I'm going to do this with Terraform there are at least two choices I was given:

I went with the module way. Also using a private IP (not using public IP) was a requirement, (sure, less exposure to the dangerous internet is more secure 😉) And I chose PostgreSQL over MySQL. I wrote some code looks like this below:

module "postgresql_db" {
  source  = "GoogleCloudPlatform/sql-db/google//modules/postgresql"
  version = "3.1.0"

  name             = "my-postgres"
  database_version = "POSTGRES_11"
  project_id       = var.project_id
  zone             = "c"
  region           = "us-central1"
  tier             = "db-f1-micro"

  backup_configuration = {
    enabled = true
  }

  ip_configuration = {
    ipv4_enabled        = false # to disable public ip
    # The VPC network from which the Cloud SQL instance is accessible for private IP.
    private_network     = module.my_vpc.network_self_link 
    require_ssl         = true
    authorized_networks = []
  }
}

And when I ran (apply) this code, I got an error looks something like this:

google_sql_database_instance.postgresql_db: Error waiting for Create Instance: Failed to create subnetwork. Please create Service Networking connection with service 'servicenetworking.googleapis.com' from consumer project 'my-project' network 'my-vpc' again

So I googled and I learned that in order to make private IP Cloud SQL instance that can be reachable from your existing network in GCP (VPC), you need to peer your VPC and Google's service network if you haven't already.

You also might need to enable this API as well.

If you have this configured already, then you can verify that by a few commands and it will look like this:

$ gcloud services vpc-peerings list --network=my-vpc
---
network: projects/xxxx/global/networks/my-vpc
peering: servicenetworking-googleapis-com
reservedPeeringRanges:
- my-peering
service: services/servicenetworking.googleapis.com
---
network: projects/xxxx/global/networks/my-vpc
peering: cloudsql-postgres-googleapis-com
reservedPeeringRanges:
- my-peering
service: services/servicenetworking.googleapis.com

$ gcloud compute addresses list
NAME            ADDRESS/RANGE   TYPE            PURPOSE          NETWORK    ... 
my-p-ip   172.x.y.0/24         INTERNAL   VPC_PEERING   my-vpc         ...

You can find more on details here


Peering to GCP's Service Networking

Since peering wasn't configured for my VPC, so I added this TF code below that is doing essentially this.

Which I could find on the Terraform google provider page

module "postgresql_db" {
  # ...
}

resource "google_compute_global_address" "private_ip_address" {
  name          = "my-p-ip"
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 24   # choose your ideal CIDR range
  network       = module.my_vpc.network_self_link
}

resource "google_service_networking_connection" "private_vpc_connection" {
  network                 = module.my_vpc.network_self_link
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.private_ip_address.name]
}

why not network = google_compute_network.private_network.self_link?

because I'm using this module instead of google_compute_network

This effectively will let you have a private IP range and service networking connection that you need to be able to spin up Cloud SQL instance peered to your VPC, so now the instance is created and running.

This post is already getting a bit long so I would like to split this up into multiple posts. Next one is here