Automate everything - A journey from github over codeship to docker on azure

Wanna go from code to (pre)production in a few seconds? No problem. I've used the last night to get that working. I'd like to share the journey with you guys right here.

There are several services involved but the glue between those services are a set of good old shell scripts :) So unfortunately no introduction for services like

  • github
  • codeship
  • azure
  • docker

If you don't know those things go for a google. There are tons of introductions for each of those.

The Sample App

Is hosted on github at https://github.com/thorstenhans/ci-test, nothing special... a almost plain website. JS is written using ECMAScript6 features... just to have some tasks that have to be done during build time... See Gulpfile.js in the repo for more information...

CodeShip CI

From github to Codeship is a no-brainer. Trigger is configured to do the CI on every push on each branch.

The Codeship setup command looks like this

$ nvm install 0.10.25
$ nvm use 0.10.25
$ npm i gulp babel -g
$ npm i

Is NVM not yet installed on your system? No Problem, I've written an article on how to do it the right way. Read it here.

The actual test command is even simpler

$ gulp build

Docker on Azure

To have a staging environment for my ultra-awesome app, I'm using a Docker server hosted on Azure. You can create such a virtual machine directly from the gallery.

Once the machine is created, map the internal port 80 to the external port 80 an ensure that you can logon using ssh. You should also configure authentication using your ssh-key. There are plenty of tutorials available that describe how to configure authentication using ssh keys instead of passwords. Go and ask google for it.

Docker Base Image

I've created a simple docker image for hosting my app. The docker image itself is derived from the default ubuntu docker image. On top of that I've installed 'nvm'

Here the dockerfile (see inline comments for more information)

FROM ubuntu:14.04  
MAINTAINER Thorsten Hans <[email protected]>

RUN sudo apt-get update --yes

#ensure required bits
RUN sudo apt-get install build-essential libssl-dev curl --yes

# download and invoke NVM installer from github
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.25.2/install.sh | bash

# set nvm dir 
ENV NVM_DIR /home/node/.nvm

# install stable node and use it
RUN . ~/.nvm/nvm.sh && nvm install stable && nvm use stable

# expose port 8080 (default port when running http-server)
EXPOSE 8080

# once an instance will be created invoke the run-app script
CMD ["/bin/bash", "/opt/my-app/scripts/run-app.sh"]  

Create a new image using that Dockerfile and provide a proper name for it, mine is named thorstenhans/simple-node

Connecting Codeship and the Docker server

Codeship is also exposing a ssh-key for each project you create, this can easily be added to your authorized_keys file on the docker server.

Once this key is 'marked' as authorized-key, you can add a custom Deployment Script to your codeship project.

The script looks like the following for my configuration (again see comments in the script for hints and clarification)

Replace ##UID## with your username, ##DOCKER-NAME## with your server name and ##SSH-PORT## with the port you've configured for SSH.

#remove all node_modules before copying all the stuff to the docker server ...

cd ~/clone/  
# npm prune --production
# !!! prune --production doesn't work on Codeships servers... 
# looks like they're pulling with a different user .... 
# ticket is already opened...
# so let's hack that :) for now
rm -rf node_modules

targetfolder=/sample-product/builds/$CI_COMMIT_ID/  
scriptfolder=/sample-product/scripts/

# use scp to push the project to docker server
# create folder structure on remote host (prevent exceptions...)
ssh ##UID##@##DOCKER-NAME##.cloudapp.net -p ##SSH-PORT## "mkdir -p $targetfolder" && scp -rp -P ##SSH-PORT## ~/clone/* ##UID##@##DOCKER-NAME##.cloudapp.net:$targetfolder

# kick of shell script which will instanciate the docker container!
ssh ##UID##@##DOCKER-NAME##.cloudapp.net -p ##SSH-PORT## "cd $scriptfolder && ./deploy.sh $CI_COMMIT_ID"  

Deploy.sh

Deploy.sh is responsible for creating new containers once codeship has finished work.

#!/bin/bash
echo Using Commit $1  
cd /sample-product/builds/  
docker kill stage && docker rm stage  
rm -rf current && ln -s $1/ current

# everything is fine... fire up a container...
docker run -d -P --name stage -e "commit=$1" -v /sample-product/:/opt/my-app/ -p 80:8080 thorstenhans/simple-node  

run-app.sh

The run-app script is referenced within the Dockerfile. It is actually responsible for pulling all the dependencies and kicking the http-server.

#!/bin/bash
. ~/.nvm/nvm.sh && nvm use stable;
cd /opt/my-app/builds/$commit/  
npm install -g http-server  
npm install  
http-server .  

That's it!

Now you've your app up and running in the lastet version, directly from github over codeship to docker. As you can see in the pic, the entire test and deployment (to an public accessible address) took roughly 60 seconds. That's cute :)

Don't blame me too much for this rough writing style. It's right now 5:30 in the morning and I've to grab some hours of sleep NOW :) But I wanted to share this within the second I finished it :) Perhaps I'll update the post tomorrow and provide a nicer description for some of the parts.

Good night and happy coding

Comments

comments powered by Disqus