Friday, June 30, 2017

Create a Developer Build Workflow with Docker and Multi-Stage Builds

I meet with my application development team every week to review how everyone is doing, go through active projects, discuss revenue and track the sales pipeline. To facilitate the meeting, I wrote an Angular 2 application that interfaces with exported files and APIs to pull together the information in a central, cohesive format. Angular is changing rapidly and sometimes that can create conflicts.

The Node.js package manager npm uses semantic versioning. This means some packages might specify a range or “greater than a version.” If someone pulls down the package after the dependent packages have been updated, different users may end up with different builds on their machines. I recently ran into this trying to get the application development project to build for a team member who is taking it over. We tried installing dependent packages, re-installing Node, updating npm, and several other options but the behavior on their machine just wasn’t the same as the behavior on mine.

That’s when it clicked. What is one of the main advantages of the Docker that I discuss when I present at conferences? The fact that “it works on my machine” is no longer a valid excuse. Instead, we use:

“It works on Docker.”

If you can get your container to run in Docker, it stands to reason it should run consistently on any Docker host.

The recent Docker release supports a concept called multi-stage builds. This allows you to create interim containers to perform work, then use output from those containers to build other images. This is perfect for building the sync project (and in the future means developers won’t even need Node on their machine to build it, although who doesn’t want Node?)

First, I created a .dockerignore file. I don’t use end-to-end testing and the build machine will install its own dependencies, so I ignored both of those folders.

dockerignore

Next, I got to work on the Dockerfile.

dockerfile

The Angular Command Line Interface (CLI) uses Node, so it made sense to start with a Node image. I am giving this container the alias build so I can reference it later. On the image, I install the specific version of the CLI I am using. Then I create a directory, copy the Angular source to that directory, install dependencies and build for production. This creates a folder named dist on the container with the assets I need to run my application.

dist

Next, in the same Dockerfile I continue with my target image. The app is run locally for the team so I don’t need massive scale, therefore I started with the simple and extremely small busybox image.

busybox

I create a directory for the web, then copy the assets from the previous image (remember, I gave it the alias build) into the new directory. I expose the HTTP port and instruct the container to run the http daemon in the foreground (so the container keeps running) on port 80 with a home directory of www.

Next is a simple command I run from the root of the project to build the image:

docker build -t ng2appdev .

docker-build

The first time takes a bit of time as it pulls the images and preps them, but eventually it successfully builds and generates an image that has everything needed to run the app. I can launch it like this:

docker run -d -p 80:80 ng2appdev

That instructs Docker to run the image I just created in “detached” mode (in the background) and expose port 80. Then I browse to localhost and the app is there, ready and waiting! The image doesn’t take up too much space, either, so it’s easy to pass around:

image-size

In fact, why even make anyone bother with the build? To improve the process we can do two things:

1. Create an image called ng2appdevbuilder that has the CLI installed. That way we don’t have to reinstall it every time or worry about it becoming deprecated in the future. It encapsulates a stable, consistent build environment.

2. On a build machine, automate it to pull down the latest application source code and assets, use the build image to build the app, then publish the result. Trigger this each time the source code is changed.

Now anyone who wants to run the latest can simply pull the most recent image and go for it. That’s the power of Docker!

Saturday, June 10, 2017

Stuffing Angular into a Tiny Docker Container (< 2 MB)

I’ve been doing a lot of work with Angular and Docker lately. In one of my workshops I demonstrate how to take an Angular app and related services then package them as containers and leverage Docker Compose to spin them up with a single command. You can read about a more involved example at Build and Deploy a MongoDB Angular NodeJS App using nginx in Three Steps with Docker.

Using the nginx container, my Angular images average several hundred megabytes in size (click to view full size).

bigimages

Microservices have transformed the way modern apps are architected. For example, Angular can produce a highly optimized production distribution of its assets as static files. The website for Angular can be incredibly simple because it relies on separate services and APIs to do the heavy lifting, and any browser-based logic is encapsulated in the JavaScript libraries that are included.

Therefore, standing up the front end should be a lot easier! In fact, even with a minimal web server, containers are easy to spin up and scale out, so load doesn’t necessarily have to be a concern of the web server now when it can be managed by the orchestrator. As an experiment, I set out to see how small I can make an Angular app.

If you have Docker installed, you can run the tiny Angular app yourself. Instructions are available at this link.

In searching for a solution I came across BusyBox, a set of Unix commands in an extremely small container that is around a megabyte in size. BusyBox contains httpd, an HTTP daemon. Let’s see how small we can make our Angular app!

The app we’ll target is the simple Angular app I built for Music City Code. You can clone the repo from GitHub here. When you build the app, it creates a simple interactive fractal app using a bifurcation diagram.

Let’s build an optimized distribution of our Angular app. This generates about 400kb of assets.

ngbuild

Next, create a Dockerfile with these contents:

from busybox:latest
run mkdir /www
copy /dist /www
expose 80
cmd ["httpd", "-f", "-p", "80", "-h", "/www"]

The steps are straightforward. It pulls down the latest busybox image, creates a directory, copies the Angular assets into the directory, exposes the web port and instructs the container to run the httpd daemon on startup.

Add a .dockerignore and ignore the src, e2e, and node_modules directories.

Let’s build our container:

dockerbuild

To see the image that was built, you can type:

docker images | grep "tinyng"

(Replace grep with find on Windows machines) and on my machine it is 1.57 MB.

Running it and browsing to localhost proves it is working:

docker run –d –p 80:80 jlikness/tinyng

So I push to Docker Hub, and confirm the size there:

dockerhub

The experiment is finished. Success! Of course now we have to try it under load and scale, but it’s good to know there is a path to optimize the size of your containers!

Wednesday, June 7, 2017

Music City Code 2017

This was my first year to attend the Music City Code conference in “the Music City” Nashville, Tennessee. It was held on the beautiful Vanderbilt University campus, where they advertise a 3-to-1 squirrel to student ratio. I imagine it was about 5-to-1 squirrels to conference attendees.

vanderbilt

I presented two talks there, the last talk of the first day and the first talk of the last day.

Rapid Development with Angular and TypeScript

This is a talk focused on demonstrating just how powerful Angular, TypeScript, and the Angular CLI are to rapidly build apps. Don’t be scared by this 360 photo, if you can’t see the audience just scroll it around.

The first half of the talk focused on the features Angular provides, as well as an overview of TypeScript. The second is a hands-on demo building an app using services, settings, rendering, data-binding, and a few other features.

You can access the deck and source code here.

Contain Your Excitement

The next talk focused on containers. In true “Inception” style, the container talk itself can be run from a container.

The talk briefly covers the difference between “metal” in your data center and Docker containers, then walks through building simple containers and evolves to multi-stage containers, using Docker compose, and an overview of orchestrators like Kubernetes.

As part of the talk, I took a 360 degree picture with my Samsung Gear 360 (I have the older model, there is a newer 2017 version). I updated the source with the embed link, then synced my changes to GitHub. This triggered an automated build that prepared a Node.js environment, ran unit tests, then built and published the Docker image to demonstrate the continuous integration and deployment pipeline that is possible with containers.

You can access the source code here and run the container from here.

Parting Thoughts

As far as conferences go, this is one of my favorites. There were great people, terrific speakers, friendly and helpful volunteers, and a fun venue. The food was awesome and speakers were able to stay in the dormitories on campus which was a fun experience. I definitely look forward to coming back in future years!