Continuous Deployment with Angular CLI, Docker, Travis CI and Azure

These days when I start writing code for a new project, I feel a sense of urgency to create an end to end development workflow as soon as possible. I love the phrase “walking skeleton”. So think of this end to end workflow as a walking skeleton of a continuous delivery pipeline. It’s basically taking a smallest possible version of your application and setting up the necessary infrastructure to be able to continuously build, run analysis, test and deploy to a production-like environment.

Please don’t confuse this idea with a waterfall concept. Your build, test and deployment scripts don’t have to be future proof. They don’t have to cover all the possible scenarios that you might need in the future. Far from it. The idea is to have (just) enough capability in the pipeline to be able to cover a (vertical) slice of your application. It should have enough “meat” to get you going.

Also note that the definition of “vertical” has grown larger with the DevOps way of doing things. You should probably not stop at automated deployments either. Can you also monitor this first small version of your application already — both in terms of usage and errors ? Do you already have a central place to track bugs, features and collaborate with other stakeholders? So yes, the idea is to really get your whole cycle up and running as soon as possible. But I digress..

.travis.yml file

YAML files have become a standard way of configuring continuous integration & delivery pipelines. Travis CI is no different.

YAML file below tells Travis to;

  • (lines 4-5) Install node 7.7.4
  • (lines 7-8) Make docker available.
  • (lines 10-20) Install and configure the latest stable version of Chrome
  • (line 23) Download all javascript packages that the Angular app uses
  • (line 26) Run all Angular component tests and do not watch files, quit immediately after.
  • (line 27) Run all end to end protractor tests. Think of this like Selenium but a bit more specialised for Angular.
  • (lines 29–30) If the build is successful and branch is master, prepare for packaging and deployment.
  • (line 31) Build the angular app using the CLI. This will transpile all .ts files and create .js bundles using webpack. It will eventually create a folder called “dist” which will include all the release artefacts that are ready for deployment.
  • (line 32) Build a docker image with the specified name and based on the specified docker file. You can see the docker file and the nginx configuration if you’re interested.
  • (line 33) Tag the image that’s just been created using the SHA of the latest git commit. This way any docker file can be reverse engineered to it’s exact source version.
  • (lines 34-35) Login to docker hub and push the image (using secret environment variables which I’d set earlier on Travis). You can see a list of images that I’ve already pushed on docker hub.

At the time of this writing there were 20 builds in the history.

So last step above pushes the brand new docker image to Docker Hub but how does it get deployed on Azure ?

Continuous Deployment of Docker Images to Azure

There’s one more step which is kinda invisible and that’s the deployment of the new docker image to an Azure Web App as a container. This is achieved through a webhook that is setup behind the scenes in Docker Hub and Azure Portal. There are a few commands that need to be executed against the Azure CLI:

Enabling container continuous deployment feature:

az webapp deployment container config -n Roost-Angular -g Roost -e true

Obtaining the WebHook URL:

az webapp deployment container show-cd-url -n roost-angular -g roost

Pasting the WebHook URL on Docker Hub:

Once this is done, whenever a new image is pushed into Docker Hub, this webhook is automatically invoked. I’ve noticed that if I make a GET call to this URL manually, it also kicks off a new deployment. Straightforward.

And on Azure Portal there’s a designated “Docker Container” tab under the Azure Web App for this purpose. Notice the name of the image and the webhook url (which is concealed by default). One docker convention to keep in mind is that when a :tag is not specified it means :latest.