Deploying a Mostly Serverless Website on GCP
With the technology that we have today there are many options for hosting a website. We are particularly interested in Serverless Computing and opted to run our website in that fashion; well mostly. When we chose Google Cloud Platform, we were unable to find a serverless relational database system. Our Java Spring Boot application was constructed to use a relational database so shifting to another database offering wasn’t something we wanted to do at this time. The architecture that we chose to implement consisted of the following GCP services.
Cloud CDN for distribution and caching
Cloud DNS for DNS registration and routing
Cloud Load Balancing for distribution
Cloud Run for the backend logic
Cloud SQL for persistent storage
Cloud Storage for static website hosting
This Cloud SQL service is the only non-serverless part of the architecture.
When exploring options on how to deploy the architecture we briefly considered Google Cloud Deployment Manager. Upon further investigation we were led to use Terraform instead. It was evident that Cloud Deployment Manager did not have the necessary support for the resource types that we were trying to create. Many of the supported resource types were still listed in beta.
Terraform had complete support for all the necessary resources listed above. We used Terraform Cloud to track the configuration changes and store the state.
We realized that if we did not automate code deployments our deployed resources would quickly become stale. In the continued spirit to try something new, we decided to use Google Cloud Build for our code automation. With the lack of our code changes this approach turned out to be free for us. We created a build trigger that would watch our GitHub repository and created a cloudbuild.yaml file that would configure the build job. The documentation claimed that just the Dockerfile was necessary, however, in practice we could not get that to work. We were required to create the yaml file for Cloud Build to successfully update the revision on Cloud Run.
options: logging: CLOUD_LOGGING_ONLY steps: - name: 'gcr.io/cloud-builders/docker' args: ['build', '-t', 'gcr.io/$PROJECT_ID/rest-java:$COMMIT_SHA', '.'] timeout: 300s - name: 'gcr.io/cloud-builders/docker' args: ['push', 'gcr.io/$PROJECT_ID/rest-java:$COMMIT_SHA'] timeout: 300s - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: gcloud args: ['run', 'deploy', 'rest-java', '--image', 'gcr.io/$PROJECT_ID/rest-java:$COMMIT_SHA', '--region', 'us-central1'] timeout: 300s
Serverless Relational Database Offerings
When debating the database solution for our application we were really seeking for a scalable serverless database that wouldn’t bill us for idle time. Options like AWS Athena, AWS Aurora Serverless, and Azure Cosmos DB immediately came to mind. We believed that GCP would have a comparable service, yet we could not find one. Even after consulting the GCP cloud service comparison documentation we were still unable to find any serverless relational database offering. For these reasons we chose the path of least resistance and opted to use a micro instance of Cloud SQL for MySQL.
With our basic application running on GCP for a couple months we have been pleased with the performance and stability of the architecture. Our Cloud Run scaling settings were intentionally set low to meet application expectations as well as to avoid potential bill shock from an autoscaling fiasco. Like AWS App Runner and AWS Lambda we faced cold start delays when scaling up from zero. We could’ve tried to architect around this with something like an automated trigger or always-running instances. Although, the cold start was not a problem for our use-case. We also felt that provisioning always-running instances defeats the purpose of a serverless service. Can a service be considered “serverless” if it can’t scale down to zero so that you’re not paying for idle compute? We said no.
We found that GCP costs were comparable to AWS when the services were matched one-to-one. However, because the AWS CDN service CloudFront is a standalone product a simpler, cheaper, alternative architecture is possible on AWS. The GCP CDN service Cloud CDN requires usage in parallel with their Cloud Load Balancer service, which has a minimum operating cost of ~$18/month, even with no traffic. Finally, our Cloud SQL instance bills hourly as well costing us about ~$9/month. This combined ~$27 doesn’t seem like a lot, yet is drastic when comparing low traffic serverless applications that would cost pennies to host on another cloud provider.
In the end we’re glad that we explored this avenue. Our in-depth analysis for this project confirmed that GCP is not the ideal hosting solution for us. The cons for this low traffic serverless application outweigh the pros. We could build a simpler application on AWS with the following changes that would improve the scalability and reduce the cost. Using CloudFront as the CDN removes the need to use the expensive Load Balancing service. Using Aurora Serverless provides us a relational database that can scale to zero so that we’re not paying for idle time. It’s true that Aurora Serverless may face slower query response times when scaling, still we believe it’s worth the cost savings. Lastly, Aurora Serverless would provide the ability to scale past the performance we could have achieved with our single Cloud SQL instance and provided high availability so that we would not be worried about a single point of failure on the database. Perhaps we’ll explore GCP again in the future for another use-case.
Written by Justin Wheeler, Senior Software Developer, and Kingslee Velu, Senior EDI Systems Analyst