<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Paul Craig</title>
    <description>The latest articles on Forem by Paul Craig (@pcraig3).</description>
    <link>https://forem.com/pcraig3</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F502760%2Fa192ee9d-34f8-47c2-9e82-282303751892.jpeg</url>
      <title>Forem: Paul Craig</title>
      <link>https://forem.com/pcraig3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pcraig3"/>
    <language>en</language>
    <item>
      <title>Quickstart: Continuous deployment to Google Cloud Run using Github Actions</title>
      <dc:creator>Paul Craig</dc:creator>
      <pubDate>Thu, 12 Nov 2020 23:09:00 +0000</pubDate>
      <link>https://forem.com/pcraig3/quickstart-continuous-deployment-to-google-cloud-run-using-github-actions-fna</link>
      <guid>https://forem.com/pcraig3/quickstart-continuous-deployment-to-google-cloud-run-using-github-actions-fna</guid>
      <description>&lt;h2&gt;
  
  
  Preamble
&lt;/h2&gt;

&lt;p&gt;If you’re a hobbyist developer and you want to host your fun app for &lt;a href="https://dev.to/pcraig3/cloud-run-vs-app-engine-a-head-to-head-comparison-using-facts-and-science-1225"&gt;next-to-free&lt;/a&gt;, you should definitely use Google &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;. Once you set it up, Cloud Run is a dream to work with, but getting started can mean a lot of trial and error.&lt;/p&gt;

&lt;p&gt;In this post, I will go step-by-step through setting up &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; to automatically deploy your cool app to Cloud Run.&lt;/p&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install &lt;code&gt;gcloud&lt;/code&gt; and do a local deployment
&lt;/h3&gt;

&lt;p&gt;My initial idea was to write a “how to get started with Cloud Run” post, but it turns out &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy" rel="noopener noreferrer"&gt;the official Quickstart&lt;/a&gt; is excellent.&lt;/p&gt;

&lt;p&gt;If you’re just starting out, follow Google’s Quickstart. It covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;Installing and initializing the Google Cloud SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Creating your first project on Google Cloud Platform (GCP)&lt;/li&gt;
&lt;li&gt;Building and pushing a container&lt;/li&gt;
&lt;li&gt;Deploying your container to Cloud Run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of the tutorial, you will have installed the &lt;code&gt;gcloud&lt;/code&gt; command-line interface (CLI) and used it to deploy your first container to Cloud Run. &lt;strong&gt;This article picks up after the Quickstart material&lt;/strong&gt;: it’s intended for anyone looking to add a bit more automation to their deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create a GitHub repository
&lt;/h3&gt;

&lt;p&gt;You will also need a GitHub repository — you know, for the GitHub Actions to work. If you’re more of a “see the code”  learner, I created an example repo — &lt;a href="https://github.com/pcraig3/hello-cr" rel="noopener noreferrer"&gt;pcraig3/hello-cr&lt;/a&gt; — as part of writing the article. Feel free to clone or fork it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service accounts
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;gcloud&lt;/code&gt; commands locally, you’re probably using your root account (ie, tied to your email address) because that’s the default account you create when you sign up to GCP. For local development, that’s fine: your root account is a superuser with lots of permissions, so you can do whatever you need to.&lt;/p&gt;

&lt;p&gt;However, you should really avoid using your root account on  other platforms. If your account password is ever stolen, an evildoer can take over your entire G-Cloud setup and use it to host &lt;a href="https://theannoyingsite.com" rel="noopener noreferrer"&gt;crappy websites&lt;/a&gt;, run up a huge tab, or steal your data. &lt;/p&gt;

&lt;p&gt;Instead, you should &lt;a href="https://cloud.google.com/iam/docs/understanding-service-accounts" rel="noopener noreferrer"&gt;create a "service account"&lt;/a&gt;: a separate account with limited permissions intended for specialized tasks. If your service account is compromised, the aforementioned evildoer can take down your Cloud Run app, but they won’t be able to mess up too much else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a service account using the Console
&lt;/h3&gt;

&lt;p&gt;I’m most familiar with creating service accounts by logging in and clicking around, so that’s what I’m going to do here. If you’re smarter than me, you might prefer the command line, but all the same stuff will apply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to &lt;a href="https://console.cloud.google.com/home/dashboard" rel="noopener noreferrer"&gt;Google’s backend console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Using the drop-down selector in the header, pick the project where your Cloud Run app lives. (Mine is called &lt;code&gt;hello-cr&lt;/code&gt;, it’ll be in a few screenshots)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr6i6px4106xoxv0e1w1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr6i6px4106xoxv0e1w1c.png" alt="Project: hello-cr"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the left-hand navigation menu and select "IAM &amp;amp; Admin" &amp;gt; "Service Accounts"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzhk7ds0yu19356p6et7s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzhk7ds0yu19356p6et7s.png" alt="Service accounts"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "+ Create service account": it will be in the contextual header near the top of the screen&lt;/li&gt;
&lt;li&gt;The first section is titled "Service account details". Enter a "Service account name" (it will also be used for the id). Adding a description is optional. Click "Create" when you are finished.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6s589mkdno1f682qjoft.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6s589mkdno1f682qjoft.png" alt="Service account details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next, assign permissions to your new service account. This part was pretty tricky to figure out, but after a lot of trial and error, I found this combination works.&lt;/li&gt;
&lt;li&gt;Add the following permissions: 

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Cloud Run Admin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloud Run Service Agent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloud Build Service Agent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Viewer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Click "Continue"&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F769naftauggot8kek7ex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F769naftauggot8kek7ex.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The final section ("Grant users access to this service account") is not relevant to this tutorial, so you can skip it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hooray! Now that you’ve created your service account, let’s make sure that everything works as expected by trying a local deployment as your new service account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authenticating on the command line as a service account
&lt;/h3&gt;

&lt;p&gt;Show all of your service accounts with &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud iam service-accounts list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The account you just created should be listed there. &lt;/p&gt;

&lt;p&gt;In order to run commands as the service account, we will need to download a &lt;a href="https://cloud.google.com/iam/docs/creating-managing-service-account-keys" rel="noopener noreferrer"&gt;service account key&lt;/a&gt;. The service account key is a JSON file containing credentials for the account, so be careful with it (eg, don’t commit it to your repository 😬).&lt;/p&gt;

&lt;p&gt;Get the service account key with&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud iam service-accounts keys create ./NAME-OF-KEY-FILE.json --iam-account EMAIL-ADDRESS&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Replace &lt;code&gt;EMAIL-ADDRESS&lt;/code&gt; with the email for your service account. Also, you probably want to rename the &lt;code&gt;.json&lt;/code&gt; file to something less shouty. If you store it in your app directory, make sure to add it to your &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now that you have the key file, you can authenticate yourself as this service account.&lt;/p&gt;

&lt;p&gt;Authenticate yourself with&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud auth activate-service-account --key-file=NAME-OF-KEY-FILE.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, you can run commands as the service account. &lt;/p&gt;

&lt;p&gt;You can see which account you are logged into with&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;gcloud auth list&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can change back to your original account at any time with&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gcloud config set account YOUR-EMAIL-ADDRESS&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deploying your app with your service account
&lt;/h3&gt;

&lt;p&gt;You should be able to build and deploy your app using your service account. To do so, follow the steps outlined in the &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy#containerizing" rel="noopener noreferrer"&gt;Cloud Run Quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build your container image using Cloud Build, by running the following command from the directory containing the Dockerfile:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gcloud builds submit --tag gcr.io/PROJECT-ID/helloworld&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where PROJECT-ID is your GCP project ID.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once built, let’s deploy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deploy using the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gcloud run deploy --image gcr.io/PROJECT-ID/helloworld --platform managed&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replace PROJECT-ID with your GCP project ID. You can view your project ID by running the command gcloud config get-value project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nice! If the deploy succeeded, you know this service account has the permissions it needs to automate future deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up a "deploy" GitHub Action
&lt;/h3&gt;

&lt;p&gt;Phew, now we’re on the home stretch. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt; automate various parts of your development workflow: you can run tests, do linting, or trigger notifications based on certain conditions. You can also set up rules to deploy your app when your &lt;code&gt;main&lt;/code&gt; branch is updated, which is what we want to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace" rel="noopener noreferrer"&gt;GitHub maintains a Marketplace of third-party Actions&lt;/a&gt; that we can use to plug into third-party services. Luckily for us, the folks at GCP have built a GitHub Action we can use to log in and run CLI commands: &lt;a href="https://github.com/marketplace/actions/setup-gcloud-environment" rel="noopener noreferrer"&gt;https://github.com/marketplace/actions/setup-gcloud-environment&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;GitHub Actions’ syntax&lt;/a&gt; is &lt;code&gt;.yml&lt;/code&gt;-based, so let’s see what our deploy workflow will look like.&lt;/p&gt;

&lt;p&gt;In the root directory of your app, create a file at &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloud Run&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_PROJECT_ID }}&lt;/span&gt;
  &lt;span class="na"&gt;RUN_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east1&lt;/span&gt;
  &lt;span class="na"&gt;SA_KEY_JSON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_SA_KEY_JSON }}&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloud Run&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="c1"&gt;# Setup gcloud CLI&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GoogleCloudPlatform/github-actions/setup-gcloud@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;290.0.1"&lt;/span&gt;
          &lt;span class="na"&gt;service_account_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_SA_KEY_JSON }}&lt;/span&gt;
          &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_PROJECT_ID }}&lt;/span&gt;

      &lt;span class="c1"&gt;# Build and push image to Google Container Registry&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcloud builds submit --tag gcr.io/$PROJECT_ID/$PROJECT_ID:$GITHUB_SHA&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcloud run deploy $PROJECT_ID --image gcr.io/$PROJECT_ID/$PROJECT_ID:$GITHUB_SHA --platform managed --region $RUN_REGION&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;Build&lt;/code&gt; and &lt;code&gt;Deploy&lt;/code&gt; commands are pretty similar to those we just ran above, although there are a couple of differences. Let’s take a closer look at the build command.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;gcloud builds submit --tag gcr.io/$PROJECT_ID/$PROJECT_ID:$GITHUB_SHA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;The dollar-sign syntax (eg, &lt;code&gt;$PROJECT_ID&lt;/code&gt;) represents an &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables" rel="noopener noreferrer"&gt;environment variable&lt;/a&gt;. All of our env vars are set at the top of the file, except &lt;code&gt;$GITHUB_SHA&lt;/code&gt;, which is &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables#default-environment-variables" rel="noopener noreferrer"&gt;a default environment variable&lt;/a&gt; referencing the current commit hash&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gcr.io/$PROJECT_ID/$PROJECT_ID&lt;/code&gt;: My project name is also the same as my app name (&lt;code&gt;hello-cr&lt;/code&gt;). If your "project" name is different from your app name, you will need to add a new variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:$GITHUB_SHA&lt;/code&gt;: Adding a colon and a string is a way of &lt;a href="https://docs.docker.com/engine/reference/commandline/tag/" rel="noopener noreferrer"&gt;tagging containers&lt;/a&gt; so we can distinguish between versions. In this case, the git SHA is appended, so that we can identify the current version of the app by referencing our commit history.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Setting up app secrets
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/disabling-or-limiting-github-actions-for-a-repository" rel="noopener noreferrer"&gt;If Actions are enabled for your repo&lt;/a&gt;, pushing your new &lt;code&gt;deploy.yml&lt;/code&gt; file will attempt a deployment but it will fail because we need to add the secrets for our service account. The secrets we need to add are referred to in the &lt;code&gt;env:&lt;/code&gt; section near the top of the file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_PROJECT_ID }}&lt;/span&gt;
  &lt;span class="na"&gt;RUN_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east1&lt;/span&gt;
  &lt;span class="na"&gt;SA_KEY_JSON&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GCP_SA_KEY_JSON }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;RUN_REGION&lt;/code&gt; is hard-coded, but we have to create the other two secrets. &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository" rel="noopener noreferrer"&gt;Github’s documentation for creating secrets&lt;/a&gt; is actually really good: you can add them under "Settings" &amp;gt; "Secrets" &amp;gt; "New secret".&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;GCP_PROJECT_ID&lt;/code&gt;: The project ID is just a string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GCP_SA_KEY_JSON&lt;/code&gt;: For the service account key, copy the entire JSON file into the textarea. It seems a bit weird but it works just fine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Secrets are encrypted as soon as they are created, and there’s no way to reveal the original values once saved, so if you are adding sensitive values in the future, make sure to add them as secrets rather than accidentally committing them to your repo.&lt;/p&gt;

&lt;p&gt;Once you’ve created your secrets and committed your &lt;code&gt;deploy.yml&lt;/code&gt; file, your next push to the &lt;code&gt;main&lt;/code&gt; branch will  trigger an automated deployment. It usually takes a few minutes to deploy a new version of your application (depending mostly on how long it takes to build your container).&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;a href="https://github.com/pcraig3/hello-cr" rel="noopener noreferrer"&gt;&lt;code&gt;pcraig3/hello-cr&lt;/code&gt;&lt;/a&gt; on GitHub
&lt;/h4&gt;

&lt;p&gt;While writing this post, I created an example repository using &lt;a href="(https://cloud.google.com/run/docs/quickstarts/build-and-deploy#writing)"&gt;Google’s Node.JS sample application&lt;/a&gt; and added &lt;a href="(https://github.com/pcraig3/hello-cr/blob/main/.github/workflows/deploy.yml)"&gt;my workflow file&lt;/a&gt; to it. It’s super simple, and might help you out if you need an tangible example of these concepts. Feel free to clone, fork, or &lt;a href="https://github.com/pcraig3/hello-cr/issues" rel="noopener noreferrer"&gt;raise an issue&lt;/a&gt; if you have any questions.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pcraig3" rel="noopener noreferrer"&gt;
        pcraig3
      &lt;/a&gt; / &lt;a href="https://github.com/pcraig3/hello-cr" rel="noopener noreferrer"&gt;
        hello-cr
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      demo autodeployments to Cloud Run
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;hello-cr&lt;/h1&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Demo URL: &lt;a href="https://hello-cr-3bjudp7n7q-ue.a.run.app/" rel="nofollow noopener noreferrer"&gt;https://hello-cr-3bjudp7n7q-ue.a.run.app/&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blog post: &lt;a href="https://dev.to/pcraig3/quickstart-continuous-deployment-to-google-cloud-run-using-github-actions-fna" rel="nofollow"&gt;Quickstart: Continuous deployment to Google Cloud Run using Github Actions&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;This repository is a reference implementation for using Github Actions to continuously deploy a Node.JS application.&lt;/p&gt;

&lt;p&gt;The deployment configuration is described in &lt;a href="https://github.com/pcraig3/hello-cr/blob/main/.github/workflows/deploy.yml" rel="noopener noreferrer"&gt;&lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;&lt;/a&gt;. Whenever the &lt;code&gt;main&lt;/code&gt; branch is updated, it will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;log in to GCP as &lt;a href="https://cloud.google.com/iam/docs/understanding-service-accounts" rel="nofollow noopener noreferrer"&gt;a service account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;build and push a container, tagging it with the git &lt;code&gt;SHA&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;deploy the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app itself is based on the sample application used in &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy#writing" rel="nofollow noopener noreferrer"&gt;Google's "Build and Deploy" Quickstart for Cloud Run&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The repo is MIT-licensed, so you are free to use or modify it however you like.&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pcraig3/hello-cr" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h4&gt;
  
  
  All done!
&lt;/h4&gt;

&lt;p&gt;You did it! Get yerself light lager and a pizza. 🍕 &lt;/p&gt;

</description>
      <category>github</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cloud Run vs App Engine: a head-to-head comparison using facts and science</title>
      <dc:creator>Paul Craig</dc:creator>
      <pubDate>Thu, 12 Nov 2020 22:28:07 +0000</pubDate>
      <link>https://forem.com/pcraig3/cloud-run-vs-app-engine-a-head-to-head-comparison-using-facts-and-science-1225</link>
      <guid>https://forem.com/pcraig3/cloud-run-vs-app-engine-a-head-to-head-comparison-using-facts-and-science-1225</guid>
      <description>&lt;p&gt;&lt;em&gt;For low-traffic applications, Cloud Run is dramatically cheaper than App Engine.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract
&lt;/h2&gt;

&lt;p&gt;I was hosting a small web app as a side-project and looking to spend less money. I started out using Heroku, then moved to Google’s Cloud Platform. Using rigorous methods and markdown tables, I performed a science-inspired “how much does this cost?” comparison between App Engine and Cloud Run. &lt;strong&gt;This study finds that Cloud Run is usually the best option&lt;/strong&gt;, although if you &lt;del&gt;have money to burn&lt;/del&gt; are a “price insensitive consumer,” then App Engine is a bit zippier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine you have a side-project-type web app and you’re looking to host it on &lt;a href="https://cloud.google.com/gcp"&gt;Google’s Cloud Platform (GCP)&lt;/a&gt; but you don’t want to spend too much ca$h. Which GCP service do 4 out of 5 scientists recommend? Let’s find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/pcraig3/google-cloud-run-the-best-host-platform-for-dynamic-apps-4ma6"&gt;My incredible journey&lt;/a&gt; went basically thus: I built a small &lt;a href="https://expressjs.com/"&gt;express&lt;/a&gt; app for upcoming &lt;a href="https://canada-holidays.ca/"&gt;Canadian holidays&lt;/a&gt; and wanted cheap but usable hosting. Initially, I was using &lt;a href="https://www.heroku.com/pricing"&gt;Heroku’s $7/month Hobby Plan&lt;/a&gt; because at the end of the &lt;del&gt;day&lt;/del&gt; month, it’s only $7. (ie, that’s like 3 coffees: ‘a coffee’ being the base unit of diminutive purchases.) &lt;/p&gt;

&lt;p&gt;Heroku was really easy to get going with, to integrate with &lt;a href="https://github.com/actions/"&gt;GitHub Actions&lt;/a&gt;, and to &lt;code&gt;ssh&lt;/code&gt; into when I needed to fiddle with something. But around month five, it dawned on me that it was going to cost $7/month for the rest of my life, so I started looking for other options. &lt;/p&gt;

&lt;h2&gt;
  
  
  Pivoting to Google Cloud Platform (GCP)
&lt;/h2&gt;

&lt;p&gt;GCP was the cloud vendor with the most bonus cash on sign-up, so I figured that was a pretty neutral and unbiased reason to pick it. However, as a hapless first-time user, there are a lot of “&lt;a href="https://cloud.google.com/solutions"&gt;solutions&lt;/a&gt;” to choose from.&lt;/p&gt;

&lt;p&gt;It seems like you’re not a real cloud vendor unless you can bury newcomers under an avalanche of vaguely differentiated products with abstract geometrical logos, so a straightforward question like “where do I host a basic express app?” didn’t have an obvious answer. &lt;/p&gt;

&lt;p&gt;Cutting through the media bias with facts and logic, I was able to narrow it down by following the research methodology of googling “&lt;a href="https://www.google.com/search?safe=off&amp;amp;source=hp&amp;amp;ei=A5-nX97cGeSi_Qb36LfQBQ&amp;amp;q=google+cloud+how+do+i+host+express+app&amp;amp;oq=google+cloud+how+do+i+host+express+app"&gt;google cloud how do I host express app&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;The two options that popped up were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine"&gt;App Engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both services will run apps and I had an app to run. Seemed perfect: they anticipated me like how I anticipated Canadians are looking for information about holidays.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;p&gt;By signing up, I was granted 300 (!!) GCP bucks, and as a long-time government employee I knew this meant I had to find a creative way to spend it before the end of the fiscal year. Are you thinking what I’m thinking? Let’s run a research study!&lt;/p&gt;

&lt;p&gt;(This is where the science comes in.) &lt;/p&gt;

&lt;p&gt;My research question was “Should I use App Engine or Cloud Run to host my fun but unprofitable app?”, and to investigate that I opted for the immersion method where I would assume the role of a developer trying to host an app on Google Cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;As a precursor, I needed to set up my app on both services simultaneously. For the initial setup, I used the Quickstart material provided by Google at no cost to embedded researchers like me. (Both Quicks-start are pretty easy to follow once you have &lt;a href="https://cloud.google.com/sdk/docs/install"&gt;the &lt;code&gt;gcloud&lt;/code&gt; command-line tools installed&lt;/a&gt;.)&lt;/p&gt;

&lt;h4&gt;
  
  
  Overview: App Engine (AE)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/appengine/docs/standard/nodejs/quickstart"&gt;Node.js Quickstart for App Engine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On AE, my express app runs as a node process, like booting it up with &lt;code&gt;npm start&lt;/code&gt; locally. AE is a traditional hosting platform: it runs continuously and serves requests as they come in. At the end of the month, you pay for the amount of time it was running, which is typically “the entire month”.&lt;/p&gt;

&lt;h4&gt;
  
  
  Overview: Cloud Run
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy"&gt;“Build and Deploy” Quickstart for Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloud Run runs containers, so for each release you have to build a container and push it to GCP. Unlike App Engine, Cloud Run &lt;em&gt;only runs&lt;/em&gt; when requests come in, so you don’t pay for time spent idling. &lt;/p&gt;

&lt;p&gt;Containerized apps are more portable but not always something you focus on during development. It’s worth noting that &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy#containerizing"&gt;the Cloud Run Quickstart provides 9 example Dockerfiles&lt;/a&gt; depending on your language of choice. (I used the Node.js one as a basis.)&lt;/p&gt;

&lt;h4&gt;
  
  
  Simulating traffic
&lt;/h4&gt;

&lt;p&gt;At this point in the study, I had 2 instances of my app running: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In App Engine: &lt;code&gt;https://hols-ae.nn.r.appspot.com/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In Cloud Run: &lt;code&gt;https://hols-hzlcxvebra-ue.a.run.app/&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because real applications have real traffic, I set up &lt;a href="https://uptimerobot.com/"&gt;a ping service&lt;/a&gt; to send requests to each site exactly once every 47 minutes for the rest of time, just like how a Real Human Being™️ would browse. &lt;/p&gt;

&lt;p&gt;Having completed my setup, it was time to let the experiment run its course, so I passed the time doing highly academic things like rinsing noobs at &lt;a href="https://dominion.games/"&gt;dominion.games&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duration
&lt;/h2&gt;

&lt;p&gt;2 months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;p&gt;There were 2 principal findings of the study.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For a low-traffic application, Cloud Run is dramatically cheaper than App Engine&lt;/li&gt;
&lt;li&gt;App Engine seems to respond slightly faster&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Ongoing costs — Cloud Run wins ✅
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cloud Run&lt;/th&gt;
&lt;th&gt;App Engine&lt;/th&gt;
&lt;th&gt;Heroku Hobby Plan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Monthly cost&lt;/td&gt;
&lt;td&gt;$0.09&lt;/td&gt;
&lt;td&gt;$11.29&lt;/td&gt;
&lt;td&gt;$7.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wow. &lt;/p&gt;

&lt;p&gt;App Engine runs 24/7 for the entire month whereas Cloud Run only runs when serving requests, and the difference is startling.&lt;/p&gt;

&lt;p&gt;Previously, I had been paying $7 a month for &lt;a href="https://www.heroku.com/pricing"&gt;Heroku’s Hobby Plan&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App Engine would cost me about 50% more &lt;/li&gt;
&lt;li&gt;Cloud Run costs &lt;strong&gt;99% less&lt;/strong&gt;, oh my &lt;em&gt;goodness&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So basically it’s a blowout win for Cloud Run here.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Request latency — App Engine (usually) wins ✅
&lt;/h3&gt;

&lt;p&gt;I also used some online speed test tools to measure the response times of my 2 instances. The results weren’t totally consistent, but App Engine generally responded more quickly. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://tools.pingdom.com/"&gt;Pingdom Speed test&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;(Results of 3 runs from São Paulo)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cloud Run&lt;/th&gt;
&lt;th&gt;App Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;632 ms&lt;/td&gt;
&lt;td&gt;471 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;485 ms&lt;/td&gt;
&lt;td&gt;568 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;562 ms&lt;/td&gt;
&lt;td&gt;470 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;559 ms&lt;/td&gt;
&lt;td&gt;503 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here we see App Engine responding on average 56 ms faster than Cloud Run (although in 1 case, Cloud Run was faster). The huge caveat here is that these times vary widely between runs, sometimes tripling or quadrupling depending on Who The F*ck Knows.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.webpagetest.org/"&gt;WebPageTest&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;(Results of 3 runs using “3G” download speed.)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cloud Run&lt;/th&gt;
&lt;th&gt;App Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run 1&lt;/td&gt;
&lt;td&gt;5.217 s&lt;/td&gt;
&lt;td&gt;5.010 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 2&lt;/td&gt;
&lt;td&gt;5.310 s&lt;/td&gt;
&lt;td&gt;4.922 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run 3&lt;/td&gt;
&lt;td&gt;5.353 s&lt;/td&gt;
&lt;td&gt;5.089 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average&lt;/td&gt;
&lt;td&gt;5.293 s&lt;/td&gt;
&lt;td&gt;5.007 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Again, keep in mind that these numbers shift around between runs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why is App Engine faster?
&lt;/h4&gt;

&lt;p&gt;This isn’t totally clear to me, but I can speculate. &lt;/p&gt;

&lt;p&gt;The one measurable difference I noticed is that that the total request size from Cloud Run was larger because it doesn’t gzip files by default.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cloud Run&lt;/th&gt;
&lt;th&gt;App Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Page size&lt;/td&gt;
&lt;td&gt;125.8 KB&lt;/td&gt;
&lt;td&gt;119.4 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Pingdom Speed Test for Cloud Run recommended I &lt;code&gt;Compress components with gzip&lt;/code&gt;, and looking through the requests, my combined &lt;code&gt;.js&lt;/code&gt; assets are indeed about 6 KB larger.&lt;/p&gt;

&lt;p&gt;Downloading bigger files makes your site slower, but I don’t think that’s the whole story. &lt;/p&gt;

&lt;p&gt;The big difference between the two services is that Cloud Run doesn’t run your container unless it’s getting requests. When a request comes in, it does 3 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;boots up the container&lt;/li&gt;
&lt;li&gt;serves the request&lt;/li&gt;
&lt;li&gt;shuts down the container &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It seems likely that the extra time needed to boot up the container adds to the total request time, leading to an average slower response time from Cloud Run.&lt;/p&gt;

&lt;p&gt;Of course, you also save a lot of money doing it this way, so the tradeoff here is whether you care more about optimizing your speed or your cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Findings
&lt;/h3&gt;

&lt;p&gt;For me, the findings are decisive. &lt;strong&gt;If you’re a hobbyist developer and you want to host your fun app for next-to-free, you should definitely use Google Cloud Run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, if money is no object, then you can pay exponentially more per month for a marginal speed boost on App Engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Read more about why &lt;a href="https://dev.to/pcraig3/google-cloud-run-the-best-host-platform-for-dynamic-apps-4ma6"&gt;Google Cloud Run is better than other hosting options&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For an excellent intro to Docker, &lt;a href="https://www.robertcooper.me/docker-guide"&gt;check out this excellent guide by Robert Cooper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check out Google’s &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy"&gt;“Build and Deploy” Quickstart for Cloud Run&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/pcraig3/quickstart-continuous-deployment-to-google-cloud-run-using-github-actions-fna"&gt;Use Github Actions to deploy automatically to Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>googlecloud</category>
      <category>docker</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Google Cloud Run: the best  hosting platform for dynamic apps</title>
      <dc:creator>Paul Craig</dc:creator>
      <pubDate>Thu, 12 Nov 2020 22:07:20 +0000</pubDate>
      <link>https://forem.com/pcraig3/google-cloud-run-the-best-host-platform-for-dynamic-apps-4ma6</link>
      <guid>https://forem.com/pcraig3/google-cloud-run-the-best-host-platform-for-dynamic-apps-4ma6</guid>
      <description>&lt;p&gt;Hello and welcome to my TED Talk.&lt;/p&gt;

&lt;p&gt;Today I will be talking about a problem that all of us can relate to as developers with ultra-cool side projects in GitHub that have 0 stars: how to host lots of half-baked tiny apps for as cheaply as possible.&lt;/p&gt;

&lt;p&gt;As a public-sphere-oriented side project, I built &lt;a href="https://canada-holidays.ca"&gt;a small web app to show Canadians their next holiday&lt;/a&gt;. It’s a zippy little express app with a bunch of server-rendered pages and an embedded DB, and it’s &lt;a href="https://github.com/pcraig3/hols/"&gt;open-source&lt;/a&gt; so anyone can use it (even non-Canadians).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://expressjs.com/"&gt;express&lt;/a&gt; is a popular JavaScript framework that’s fun to use and lets devs like us build simple applications quickly, which is one of the joyful things about programming. But as cool as it is when your app works on &lt;code&gt;http://localhost:3000&lt;/code&gt;, the point of websites is really that other people can see them, so you gotta find somewhere to host them. But not every app is gonna make you a ton of cash or be an ad vehicle — sometimes you just want to host a cutesy little app without having to come up with a business plan.&lt;/p&gt;

&lt;p&gt;So what’s the best option for hosting a basic web app while paying as little as possible? Well, if this was a mystery novel, I would introduce 12 shifty characters right around now, but the answer is &lt;strong&gt;you should use &lt;a href="https://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud Run is easy to set up and it’s &lt;a href="https://dev.to/pcraig3/cloud-run-vs-app-engine-a-head-to-head-comparison-using-facts-and-science-1225"&gt;basically free&lt;/a&gt; for low-volume apps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, anyone who’s ever had a Grade 3 math teacher knows it’s important to show your work, so I’m going to go over some of the options I looked at and how they stack up against each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free hosting
&lt;/h2&gt;

&lt;p&gt;In roughly chronological order, I started with free hosting options and then moved on to paid options. In general, free hosting comes with limitations that you can either accept or pay your way out of, so what you end up looking for is a good balance of uptime vs cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Totally free static hosting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  eg, &lt;a href="https://pages.github.com/"&gt;Github Pages&lt;/a&gt; or &lt;a href="https://surge.sh/"&gt;Surge&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Static hosting services are super reliable and super free (😻), but the major caveat here is that they restrict you to static sites (or in-browser JS, to split a hair). Does this work for you? Maybe, if you’re using a “compiles to static” framework like &lt;a href="https://www.gatsbyjs.com/"&gt;Gatsby&lt;/a&gt; or &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; (both Very Fun™️ in their own right). But not if you have picked up a more traditional MVC framework,  like &lt;a href="https://flask.palletsprojects.com/en/1.1.x/"&gt;Flask&lt;/a&gt;, &lt;a href="https://rubyonrails.org/"&gt;Rails&lt;/a&gt;, &lt;a href="https://laravel.com/"&gt;Laravel&lt;/a&gt;, or, in my case, express. &lt;/p&gt;

&lt;p&gt;If all you need is a totally static site — a bunch of HTML files — or you’re building a Single-Page App that offloads the logic onto a web browser, then you should probably just use a static hosting service and call it a day. (eg, &lt;a href="https://pcraig3.ca"&gt;My personal website&lt;/a&gt; is running on GitHub Pages). &lt;/p&gt;

&lt;p&gt;However, if you’re writing server-side logic to listen for requests and build your pages, you might have leveled-up and out of this category.&lt;/p&gt;

&lt;h3&gt;
  
  
  Totally free dynamic app hosting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  eg, &lt;a href="https://www.heroku.com/dynos"&gt;Heroku&lt;/a&gt; or &lt;a href="https://glitch.com/"&gt;Glitch&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Usually you have to pay for server uptime, but a few prominent companies have a category called “the free tier”. PaaS products like Heroku or Glitch offer this: they will host your app for free (!!) but — like &lt;a href="https://www.youtube.com/watch?v=T4kBRC2co7Y"&gt;Jurassic Park&lt;/a&gt; after Dennis Nedry leaves — will &lt;strong&gt;shut it down&lt;/strong&gt; after [x] minutes without traffic. Once your app is down, the next request that comes in can take 20 or 30 seconds to load because it needs to spin up &lt;em&gt;a whole server&lt;/em&gt; for the first request. For not-serious prototypes or little “Hello World!” tutorial apps, this is probably fine because your entire audience is your roommate and your mom. But once you want a site that will actually be usable, you come to two realizations:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;this is untenable, and &lt;/li&gt;
&lt;li&gt;more like “the free tear 😢” because it’s what happens to your eyes after staring at 20 seconds of a white page&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Paid hosting
&lt;/h2&gt;

&lt;p&gt;Maybe it’s time to bite the bullet and pay to host. &lt;em&gt;Okay fine&lt;/em&gt;, I reason to myself, &lt;em&gt;I am not bankrupt&lt;/em&gt;. Truth be told, I regularly pay money for things (eg, &lt;a href="https://us.mattandsteve.com/products/the-extreme-bean-hot-spicy-3-x-1l"&gt;groceries&lt;/a&gt; and &lt;a href="https://www.amazon.ca/s?k=round+ice+cube+tray&amp;amp;rh=n%3A6647610011&amp;amp;ref=nb_sb_noss"&gt;other necessities&lt;/a&gt;). But I’m also not Geoff Bezos, &lt;a href="https://simpletexting.com/tech-ceo-salary/"&gt;who makes more money&lt;/a&gt; in the time it takes for him to read this article than you or I will make in a year. So let’s see what’s on offer, but let’s also try to be cost-conscious.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paid server hosting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  eg, Upgrading Heroku/Glitch, using &lt;a href="https://www.digitalocean.com/products/droplets/"&gt;Digital Ocean&lt;/a&gt;, or  &lt;a href="https://www.hostgator.com/"&gt;GoDaddy&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Traditional server hosting is a well-trod path here, with many commodity services competing to sell you  uptime. Whether you want to upgrade your Heroku or Glitch plan, squeeze out a &lt;a href="https://www.digitalocean.com/docs/droplets/#:~:text=DigitalOcean%20Droplets%20are%20Linux-based%20virtual%20machines%20%28VMs%29%20that,or%20as%20part%20of%20a%20larger%2C%20cloud-based%20infrastructure."&gt;Digital Ocean&lt;/a&gt; droplet or poach yourself a big ol’ &lt;a href="https://www.hostgator.com/web-hosting"&gt;HostGator&lt;/a&gt;, the principle is pretty much the same. You pay a fixed rate for permanent server uptime and then your app runs all the time until either you die or you run out of money. If you’re looking for the cheap plans, they’re usually around $5-9 (USD!!) a month. &lt;/p&gt;

&lt;p&gt;Sure, not like it’s the end of the world, but I can also buy Netflix for $10/month (CAD) and get like a million shows I’ll never watch. So it would be good if there were some cheaper options here, because $10/month quickly adds up to $9,360.00 (79 years being the average human lifespan).&lt;/p&gt;

&lt;h4&gt;
  
  
  Splitting the difference; axing our bills
&lt;/h4&gt;

&lt;p&gt;The reason we’re getting such crap experiences with the free tier is that dynamic sites need a server to do some computation before they return pages. To use &lt;a href="https://canada-holidays.ca/"&gt;my Canadian holidays site&lt;/a&gt; as an example, I look up how many days until the next holiday, so I need to check the current date every time a request comes in.&lt;/p&gt;

&lt;p&gt;Building a page itself is pretty quick, but booting up a hibernating server takes a while. For the fastest responses, we end up keeping the engine idling 24/7. This way, our server is always ready to respond to incoming requests, but at the cost of a lot of uptime we don’t need.&lt;/p&gt;

&lt;p&gt;So how do we split the difference? If we could find a way to pay &lt;em&gt;only for the time that we are serving web traffic&lt;/em&gt; (💡) then we can launch fleets of rarely-visited websites for next-to-free. Sounds simple enough, but how does it work in practice?&lt;/p&gt;

&lt;h3&gt;
  
  
  Paid “serverless” hosting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  eg, &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; or Google &lt;a href="https://cloud.google.com/functions/"&gt;Cloud Functions&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Probably you’ve heard of serverless functions, aka Lambdas, aka Functions as a Service (FaaS): the idea is you run individual functions rather than a big fat web framework. “Server-less” makes more sense as two words in this context, because we need less of a server than all of one. What if, instead of our web server listening 24 hours a day for requests, we handled each request with a function that only runs when requests come in, and we pay &lt;em&gt;only&lt;/em&gt; for the time it took to run those functions?&lt;/p&gt;

&lt;p&gt;What we’re talking about here is &lt;strong&gt;function invocations&lt;/strong&gt;, which, for small projects, end up being cheap as chips. At 10k requests a month, you’re looking at &lt;a href="https://cost-calculator.bref.sh/"&gt;an estimate&lt;/a&gt; of ~21 cents. There’re a lot of options in this space: the Big Three (&lt;a href="https://aws.amazon.com/lambda/"&gt;AWS&lt;/a&gt;, &lt;a href="https://azure.microsoft.com/en-us/services/functions/"&gt;Azure&lt;/a&gt;, &lt;a href="https://cloud.google.com/functions/"&gt;GCP&lt;/a&gt;) all offer FaaS services, as well as more upstart-y platforms like &lt;a href="https://vercel.com/docs"&gt;Vercel&lt;/a&gt; or &lt;a href="https://functions.netlify.com/"&gt;Netlify&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But wait!&lt;/strong&gt; Is this going to work with express or Rails or whatever? Erm, there are options I suppose (eg, &lt;a href="https://vercel.com/guides/using-express-with-vercel"&gt;serverless express&lt;/a&gt;). But out of the box, no. &lt;/p&gt;

&lt;p&gt;To be clear, if this works for you, then this is a great option. For low-volume sites, you are looking at pennies a month. But if you already have a conventional web app, it probably means a big, fat rewrite: a thing I was definitely &lt;em&gt;not&lt;/em&gt; interested in, and I would posit that you shouldn’t be either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Containerized serverless (aka, the Holy Grail)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  at long last, &lt;a href="https://cloud.google.com/run/"&gt;Cloud Run&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;If you read the title of this post or the earlier-mentioned ‘TL;DR’, you might have guessed this, but for super unobservant people, the moment you have been waiting for is here. &lt;/p&gt;

&lt;p&gt;🥁🥁🥁&lt;/p&gt;

&lt;p&gt;✨ Cloud Run ✨ is the gold at the end of this rainbow. Instead of paying for the time it takes to run function invocations, Cloud Run charges you for time spent running your app as a container — but the trick is that the container &lt;em&gt;only runs&lt;/em&gt; when requests come in. Unlike traditional server-hosting, the time to boot up a container is dramatically less than it takes to spin up a server. You don’t have to provision a VM or download anything: containers bundle together everything they need at build time. Building a container can take a while (sometimes several minutes), but starting them up is pretty zippy. &lt;/p&gt;

&lt;p&gt;It’s a really neat way to slice the Gordian knot here. As we’ve seen, the other options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;be static&lt;/li&gt;
&lt;li&gt;deal with downtime&lt;/li&gt;
&lt;li&gt;pay a lot for uptime you don’t need&lt;/li&gt;
&lt;li&gt;rewrite your whole damn app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Cloud Run, get the best of both worlds: you get the pay-per-use model of serverless functions without overly constraining how you build your app. And since we’re talking about low-volume side projects presumably without much in the way of growth hacking, most of the time you’re not paying anything. &lt;/p&gt;

&lt;p&gt;You know the phrase, “make money while you sleep”? Well, this is similar: only it’s more like “most of the time you’re asleep, you’re not paying for Cloud Run.” And Cloud Run has a free tier too, which means if you’re really unpopular, hosting is free! &lt;/p&gt;

&lt;h3&gt;
  
  
  Okay, I’m interested. What’s the catch?
&lt;/h3&gt;

&lt;p&gt;Glad you asked! Basically, the catch is you need a &lt;a href="https://medium.com/bb-tutorials-and-thoughts/docker-a-beginners-guide-to-dockerfile-with-a-sample-project-6c1ac1f17490"&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;/a&gt;. Unlike with FaaS platforms, you don’t have to carve up your app like an LA plastic surgeon, but you &lt;strong&gt;do&lt;/strong&gt; need to be able to run it as a container, which isn’t always part of the “Hello World!” tutorial you started with. &lt;/p&gt;

&lt;p&gt;The good news is that if you’re using a pretty established framework, examples abound. Google’s excellent “Build and Deploy” Quickstart tutorial &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy#containerizing"&gt;includes sample Dockerfiles&lt;/a&gt; for 8 major programming languages. The JS one is 6 lines of code but I am a little more &lt;code&gt;l33t&lt;/code&gt; than they expected so I got mine up to 15.&lt;/p&gt;

&lt;p&gt;If you need an intro to Docker, &lt;a href="https://www.robertcooper.me/docker-guide"&gt;check out this excellent guide by Robert Cooper&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;If you’re still not convinced and you want to do more research, then that’s fine too. A good place to start is at the beginning of this article. Otherwise, you should give it a shot! Here are a few more resources to help you on your way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hit up Google’s &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy"&gt;“Build and Deploy” Quickstart for Cloud Run&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn &lt;a href="https://dev.to/pcraig3/cloud-run-vs-app-engine-a-head-to-head-comparison-using-facts-and-science-1225"&gt;how much cheaper it is to use Cloud Run than App Engine&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/pcraig3/quickstart-continuous-deployment-to-google-cloud-run-using-github-actions-fna"&gt;Deploy automatically to Cloud Run using Github Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! Happy devving!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>googlecloud</category>
      <category>docker</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
