Josh Software

Creating {legacy} static build via docker and deploying with mina-scp – Josh Software

I am sure! Many of our applications have turned into a legacy codebase, such applications might have some outdated scripts or build processes that might need frequent maintenance and updates.

In this post, I will walk you through the steps with which we can move our frontend build creation script into the docker container and deploy through mina and mian-SCP gems seamlessly.

Even if your code is not a legacy codebase it is always better to build your application into a docker container because

  • You need to create a build production-like environments
  • If you want to do a production system upgrades, it’s a lot easier to test build creation in docker containers in advance
  • Avoid any package missing or mismatch issues at runtime, especially on older node versions < 6.x.x.

Brief background

I had a angular 1.x an application running with node 6.x.x, things were all good until we noticed following

  • The first issue was previously on servers node-modules and bower packages were moved on servers manually
  • We were having two(production/staging) servers with different node 5.x.x and 6.x.x setup receptively.
  • In older node <= 5.x.xnpm install does not support package.json
  • Package addition and updates were a nightmare
  • We were frequently facing package related issues because of a lack of proper package.json

We resolved all the above issues one by one after deciding to fix the node version and upgraded to version 6.x.x and moved the build creation process into a docker container to have a standard build creation and deployment process.

Creating a build-in docker container via docker-compose

If you are new to docker and docker-compose, I would advise you to get your hands dirty in docker concepts by referring it here and here

Dockerfile

Dockerfile: In this file, we are only specifying the required ubuntu, node, npm, and yarn versions that are required for application, and before that, we are installing few dependencies.

FROM ubuntu:18.04
SHELL ["/bin/bash", "-l", "-c"]

RUN mkdir /usr/local/nvm
ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 6.13.0
ENV NVM_INSTALL_PATH $NVM_DIR/versions/node/v$NODE_VERSION

# install ubuntu related dependencies
RUN apt-get update -q && 
    apt-get install -qy curl ca-certificates gnupg2 build-essential --no-install-recommends && apt-get clean
RUN apt-get install -y git zip

# install nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash

# install node version 6.13.0
RUN source $NVM_DIR/nvm.sh 
    && nvm install $NODE_VERSION 
    && nvm alias default $NODE_VERSION 
    && nvm use default

WORKDIR /usr/bullet

# copy dependencies json file to docker container
COPY package.json $WORKDIR
COPY yarn.lock $WORKDIR
COPY bower.json $WORKDIR

# install package manager
RUN source $NVM_DIR/nvm.sh && npm install -g yarn@1.17.0
RUN source $NVM_DIR/nvm.sh && npm install -g bower@1.8.0

RUN source $NVM_DIR/nvm.sh && npm install -g gulp@3.9.1

RUN source $NVM_DIR/nvm.sh && yarn install --quiet
RUN source $NVM_DIR/nvm.sh && bower install --quiet --allow-root

COPY . $WORKDIR

Once the file is ready we can use this in docker-compose with the following docker-compose.yml file to create multiple services

docker-compose.yml

version: '3.4'

services:  
  frontend-app: &frontend-app
    build:
      context: .
      dockerfile: ./Dockerfile
    image: my-frontend-app:0.0.1
    volumes:
      - ./:/usr/frontend-app/
      - ./static_build:/usr/frontend-app/static_build
      - nodemodules:/usr/frontend-app/node_modules  
      - bowercomponents:/usr/frontend-app/bower_components
  start:
    <<: *frontend-app   
    ports:
      - '8000:8000'
    command: bash -c "source /usr/local/nvm/nvm.sh && gulp dev"

  create_build:
    <<: *frontend-app 
    command: bash -c "source /usr/local/nvm/nvm.sh && gulp prod && rm -f static_build/build.zip && zip -rq static_build/build.zip build/"

volumes: 
  nodemodules: {}
  bowercomponents: {}

In the docker-compose file, we are creating start and create_build services, start is to start the application inside the docker container with the following command

#Running build on port 8080
source /usr/local/nvm/nvm.sh && gulp dev

create_build is to create and compress a build with the following command

#Creating prod build, removing old build and creating new build
source /usr/local/nvm/nvm.sh && gulp prod && rm -f static_build/build.zip && zip -rq static_build/build.zip build/

Important to note that we have created shared volumes and folders between host and container So that we can have build.zip to be created on host and node modules can be shared across the builds if no changes to package.json

Once the above setup is done we can easily create or start the build using the following commands

#Starting a build
docker-compose up start
#Creating a build
docker-compose up create_build

After the above setup is verified we can deploy this build via mina and mina-SCP gem as following

Note: Hope you have done the basic setup which is needed for mina deployment. (Let me know if you need any help!)

Deploying static build to servers

Following is the sample mina deployment script which is copying and zipping build to the server.

config/deploy.rb

#Mina, version v0.3.8
require 'mina/scp'

# Basic settings like branch, server repo settings goes here
# set :branch, staging
# gem install mina -v 0.3.8 (you can use latest mina version)
# gem install mina-scp -v 0.1.2

# Put any custom mkdir's in here for when `mina setup` is ran.
# all releases.
task :setup => :environment do
end

desc "create a build on the local"
task :create_build => :environment do
  to :before_hook do
    # Put things to run locally before ssh
    queue! %[echo "-----> creating static build..."]
    queue! %[sudo docker-compose up create_build]
  end
end

desc "Deploys the current version to the server."
task :deploy => :environment do

  deploy do
    # Put things that will set up an empty directory into a fully set-up
    # instance of your project.
    invoke :'deploy:link_shared_paths'
    queue! %[echo "-----> uploading build domain: #{domain} branch: #{branch}, deploy_to: #{deploy_to}"]
    scp_upload('./static_build/build.zip', "#{deploy_to}/build.zip", verbose: true) 
    invoke :'deploy:cleanup'

    to :launch do
      queue "echo '-----> Unziping build...'"
      queue "cp #{deploy_to}/build.zip #{deploy_to}/current/build.zip" 
      queue "unzip -q #{deploy_to}/current/build.zip"
      queue "echo '-----> Unzip completed'"
    end
  end
end

Here we are creating and deploying build via

mina create_build
mina deploy

If you have noticed we are copying the build ./static_build/build.zip created via mina-SCP’s scp_upload command and unzipping in deploy task.

scp_upload('./static_build/build.zip', "#{deploy_to}/build.zip", verbose: true)

That’s it, We have successfully created and deployed static build with the help of docker and mina. If you have reached here and have any suggestions or thoughts let me know in the comments section.

Leave a Comment

Your email address will not be published. Required fields are marked *