DEV Community

Nkere-Awaji Inwan
Nkere-Awaji Inwan

Posted on • Edited on

How to Deploy a Vue Application on Cloud Run

feature_image

Introduction

Deploying a vue application on cloud run is as straight forward as deploying a node application on cloud run. Deploying a vue application with access to environment variables? not so much. In this article, we will take a look at how we can deploy a vue application on cloud run as well as how we can access environment variables at run time.

Prerequisites

This tutorial uses the following:

  • Must have a user account on GCP (Google Cloud Platform)
  • Must have Docker installed (version >= 18.09.0)
  • Must have node installed (version >= 12.5.0)
  • Must have npm installed (version >= 6.9.0)
  • Must have vue-cli installed
  • Must have a basic knowledge of vue
  • Must have a basic knowledge of docker
  • Must have a basic knowledge of bash

If you have the first six prerequisites sorted out you can proceed to the next section.

Create a Vue Application

In this section, we are going to set up our vue application and build it into a docker image.

Let's create a vue application by running the following commands:

$ vue create <app-name>

Since this tutorial is geared towards deploying a vue application on cloud run (CR) we are not going all out on features, we will keep it simple.

vue app installation

If your installation is right, you should see the very familiar vue welcome page when you run $ npm run serve

To demonstrate the use of environment variables we are going to tweak
App.vue and HelloWorld.vue respectively like so:



//App.vue

<template>
  <div id="app">
    <img v-if="imgUrl" alt="env gif" :src="imgUrl">
    <img v-else="" alt="Vue logo" src="@/assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

...



Enter fullscreen mode Exit fullscreen mode

In the code snippet above we are displaying the original vue logo if the variable imgUrl is false. We are going to define imgUrl in the created hook like so:



//App.vue

...

data(){
  return {
    imgUrl: ''
  }
},

created(){
 this.imgUrl = process.env.VUE_APP_IMG_URL
}

...



Enter fullscreen mode Exit fullscreen mode

To keep things simple I cleaned up my HelloWorld component like so:



// @/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

...



Enter fullscreen mode Exit fullscreen mode

To use environment variables in your vue application
you should have a .env file in the root directory of your project
and a variable VUE_APP_IMG_URL which contains the URL to your image.

If you visit your vue application on http://localhost:8080 you should see an image like the one below:

fresh vue installation

This is because vue is compiled and bundled with webpack which means environment variables will only be made available at build time and not at run-time despite webpack's hot reload being used in development mode. To view changes to our env variables we will have to restart the dev server.
To deploy our vue app to cloud run, we will need a dockerise our application and we will know how to in the next section.

Build Application Image

In this section, we will learn how to build a docker image that can run our vue application in production.
To do this we will need a docker image with a web server (NGINX ) and node installed.
We can pull these packages in like so:




FROM nginx:alpine

# Install npm and node
RUN apk add --update npm

# Add bash
RUN apk add --no-cache bash

WORKDIR /app

COPY package.json ./

RUN npm install

COPY . .

# # Make our shell script executable
RUN chmod +x start.sh

COPY ./nginx.conf /etc/nginx/conf.d/default.conf


CMD ["/bin/bash", "-c", "/app/start.sh && nginx -g 'daemon off;'"]



Enter fullscreen mode Exit fullscreen mode

This dockerfile has some dependencies; the nginx.conf file and the bash script start.sh that builds our application at run time which is triggered by the docker CMD command. We will fulfil these dependencies by creating the above-said files in the root directory of our vue application like so:

nginx.conf



server {
    listen 8080;
    server_name _;

    charset utf-8;
      root /usr/share/nginx/html;    
      index index.html index.htm;

    location / {
      root /usr/share/nginx/html;      
      autoindex on;
      #try_files $uri $uri/ /index.html =404;    
      try_files $uri $uri/ /index.html =404;    
    }   

}



Enter fullscreen mode Exit fullscreen mode

We are listening on port 8080 because that's a cloud run requirement
as we can find in the cloud run contract.

start.sh



#!/bin/bash

if [ ! -d "/app/dist" ] 
then
    npm run build

    echo "Build finished...";

    echo "Delete node_modules folder";

    rm -rf node_modules

    echo "START COPY";

    cp -rf  /app/dist/. /usr/share/nginx/html/

    echo "END COPY";
fi



Enter fullscreen mode Exit fullscreen mode

In this file, Since we are building at runtime, we will wouldn't
want to build our application every single time a request is made to
our application. To avoid this, we will check if the dist directory
exists before we build the static assets and copy them into the nginx
web directory.

Now we have fulfilled our dockerfile dependencies we can now build the docker image and push to GCR (google container registry).

To adhere to the 12 Factor app rule no. 3 we are going to populate
our .env file with a default value which is going to be dynamic with
respect to different cloud run revisions.

In our application root directory, we will build the docker image like so:

$ docker build -t cr-tutorial .

When completed, we will tag the image and push it to gcr. To do so, you need to have auth for docker to use gcloud. You can find out more information on how to do so on the container registry page. If that's sorted, we can tag and push to gcr like so:

$ docker tag cr-tutorial gcr.io/[PROJECT_ID]/cr-tutorial

Where cr-tutorial is the local image name and
[PROJECT_ID] is your GCP project id

$docker push gcr.io/[PROJECT_ID]/cr-tutorial

When push is completed, gcr.io/[PROJECT_ID]/cr-tutorial
will be your application image URL which will come in
handy soon.

Deploying to cloud run

To deploy our vue application image to cloud run, we will visit the cloud run page on GCP and click on the create service option on the info panel.

create_service_image

In the create service panel, we are going to set our application settings as well as input the link to our application image (gcr.io/[PROJECT_ID]/cr-tutorial) like so:

create_service_panel

If you're ok with the config, you can click the create button below to deploy your application to cloud run. When the deployment is done, when you visit your cloud run app URL, you should see a screen like so:

cloud_run_app

Now to demonstrate our use of .env variables at run-time, we will deploy a new revision on cloud run and pass in our env variables.

Deploying a new CR revision

To deploy a new revision we will click on the Deploy revision option on the deployed service info panel

deploy_revision

Then we will fill up our VUE_APP_IMG_URL(https://bit.ly/2MiYZT2) value in the env section like so:

deploy_revision_panel

When the revision is done, you should see something that looks like the image below:

updated_revision_gif

Conclusion

In this tutorial, we have been able to deploy a vue application which receives environment variables at run-time. This is because the application is being built at run-time as well. You can find the repository for this project on GitHub.

Top comments (7)

Collapse
 
steren profile image
Steren

Doing the build as part of the start command likely hurts the startup time of your app.
You should do it as a line of your Dockerfile.

Collapse
 
enkaypeter profile image
Nkere-Awaji Inwan

Yeah it does, but building the app at build time will mean the static assets will not have access to the environment variables passed at run time. One way to optimise the application startup time is optimising the code base such that build time is as fast as possible.

Collapse
 
ricard0arabia profile image
ricard0arabia

Hi, I'm gettings this error:
"Cloud Run error: Container failed to start. Failed to start and then listen on the port defined by the PORT environment variable. "

I just changed the port to 80 instead of 8080 in nginx.conf

Collapse
 
noopurphalak profile image
Noopur Ramakant Phalak • Edited

You could have instead added

EXPOSE 8080
Enter fullscreen mode Exit fullscreen mode

in your Dockerfile and you are golden...

Collapse
 
sub8s profile image
Subrahmanya S M • Edited

How add caching for this deployed cloud run url using firebase hosting rewrites firbase.json

for firebase hosting hosted static files+ Cloud run with API we can use caching like this

"rewrites": [
{
"source": "/api/",
"run": {
"serviceId": "myservice-api",

"region": "europe-west1"

}
},
{
"source": "",
"destination": "/index.html"
}
]
and
[
{ "source":"
/user/**", "headers": [{"key": "Cache-Control", "value": "no-cache, no-store"}] }
]

what is if whole dynamic Vue with node runtime hosted in the cloud run and firabase.json
have
"rewrites": [ {
"source": "/helloworld",
"run": {
"serviceId": "helloworld", // "service name" (from when you deployed the container image)
"region": "us-central1" // optional (if omitted, default is us-central1)
}
} ]
how to do caching when we are using dynamic webapp with web firebase SDK ? and liked to firebase hosting ?

Collapse
 
pcrunn profile image
Alexander P.

Is there any reason to deploy it on an actual server rather than deploying it as a static website other than using environment variables?

Collapse
 
enkaypeter profile image
Nkere-Awaji Inwan

No reason at all depending on your needs. If you're deploying on an actual server, you will need to take a different approach. Serverless helps take some work off your hands when deploying applications. But if your use-case demands an actual server, then you go for it.