ruskindex.html

Deploying a Vue-cli 3 app with all the bells and whistles

…as painfully as possible.

I started a fresh project that I wanted to use as an excuse to dabble in some new tooling and technologies that I had on my “to-try” list. A. mixture of chats with colleagues and articles from weekly newsletters for devops and javascript had got me pretty hyped to upgrade how I deploy webapp projects going forward.

However, after spinning up a new vue app complete with Typescript, vue-class etc. I needed to deploy my app with a minimal server and suddenly the articles and docs were less obvious.

Below are the steps that I wanted to follow:

  1. Build production version of the app npm run build
  2. Serve static assets and main app from production dist folder locally with a minimal server.
  3. Dockerize the dev and production builds with a multistage Dockerfile and recommended best practices
  4. Spin up dockerized version of app with Kubernetes
  5. Deploy kubernetes version to Google Cloud or similar
  6. Automate future deployments
  7. Enjoy brief joy before completely new hype train arrives.

This article aims to capture my journey through these recipe steps. I hope that writing about my experience with each step (debugging + blogging = deblogging? 🤔) as I’m developing helps someone out there apart from myself as they too google their way through some hype-driven-development. At the very least it helps force me to explain my actions to myself as I go…like pair-coding…with myself.

1. Build production version of app ERROR Failed to compile with 39 errors

Tslint errors to be specific. I dealt with them as follows:

2. Serve static assets and main app from production dist folder locally with a minimal server.

After all that bruteforcing through foreign error messages this one definitely felt like a doddle. I popped the following minimal Express script into the root project folder, copied from this gist

const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()

// serve static assets normally
app.use(express.static(__dirname + '/dist'))

// handle every other route with index.html, which will contain
// a script tag to your application's JavaScript file(s).
app.get('*', function(request, response) {
  response.sendFile(path.resolve(__dirname, '/dist/index.html'))
})

app.listen(port)
console.log('server started on port ' + port)

Following the steps in the linked guide above was simple enough. I ended up with a Dockerfile that looked like the following sitting in my root project folder:

FROM node:11

WORKDIR /home/nodejs/app

# Enable caching of node_modules if possible
COPY package.json .
COPY package-lock.json .
RUN npm ci --production

COPY dist/ .
COPY prod_simple_server.js .
ENV NODE_ENV production

# Run as a non-root user (see https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#non-root-user)
USER node

# This 'prod' version of the script above has the file paths adjusted for the fact that we're only copying across the dist/ folder instead of the entire source code.
CMD ["node", "prod_simple_server.js"]

I’ve heard about multi-stage docker builds (i.e. one for local development, another for tests, another for the actual deployment etc.) but I’ll get back to that once I’ve got this thing actually deployed.

4. & 5. Spin up dockerized version of app with kubernetes, deploy that to Google Cloud or similar

TBC