Thursday, October 20, 2016

Docker for Angular 2 devs

Docker is a Virtual Environment
Docker containers are great for adding new developers to existing projects, or for learning new technologies without polluting your existing developer machine/host. Docker allows you to put a fence around the environment while still using it.

Why Docker for Angular 2?
Docker is an easy way to get up and going on a new stack, environment, tool, or operating system without having to learn how to install and configure the new stack. A collection of docker images are available from Docker Hub ranging from simple to incredibly complex -- saving you the time and energy.

Angular 2 examples frequently include a Dockerfile in the repository which makes getting the example up and running much quicker -- if you don't have to focus on package installation and configuration.

The base Angular 2 development stack uses Node, TypeScript, Typings, and a build system (such as SystemJs or Webpack). Instead of learning each stack element before/while learning Angular 2, just focus on Angular 2 itself -- by using a Dockerfile to bring up a working environment.

The repositories for Angular 2 projects will have a package.json file at the root which is common for NodeJs/NPM package management projects. The Docker build will install the packages in the package management system as part of the build. The build can also transpile the typescript code , and start a static file web server -- if the package.json has a start script.

In order to get a new development environment up and a new project in the browser, you just need to build the Dockerfile, then run it. Running these two commands at the terminal/cli saves you time in find and learning the Angular 2 stack, and then building and running the project.

The Angular 2 Quickstart
For this article, I use the Angular 2 Quickstart repository including the Dockerfile found in the repository.

I use a Macintosh laptop. If you are using a Windows-based computer/host, you may have more or different issues than this article.

Docker via Terminal/Cli
I prefer the code-writing environment and web browser already installed and configured on my developer laptop/host. I configure the Docker container to share the hosted files. The changes are reflected in the container – and I run the Angular 2 project in watch mode so the changes immediately force a recompile in the container.

Viewing the Angular Project in a Browser
Since the Angular 2 project is a website, I access the container by the port and map the container's port to the host's port – so access to the running Angular project is from a web browser on the host laptop with http://localhost:3000.

Install Docker
Before you install Docker, make sure you have a bit of space on the computer. Docker, like Vagrant
and VirtualBox, uses a lot of space.

Go to Docker and install it. Start Docker up.

Check Docker
Open a terminal/cli and check the install worked and Docker started by requesting the Docker version

docker –v
>Docker version 1.12.1, build 6f9534c 

If you get a docker version as a response, you installed and started Docker correctly.

Images and Containers
Docker Images are defined in the Dockerfile and represent the virtual machine to be built. The instantiation of the image is a container. You can have many containers based on one image.

Each image is named and each container can also be named. You can use these names to indicate ownership (who created it), as well as base image (node), and purpose (xyzProject).

Pick a naming schema for your images and containers and stick with it.

I like to name my images with my github name and the general name such as dfberry/quickstart. I like to name the containers with as specific a name as possible such as ng2-quickstart.

The list of containers (running or stopped) shows both names which can help you organize find the container you want.

The Angular 2 Docker Image
The fastest way to get going with Docker for Angular 2 projects is to use the latest node as your base image -- which is also what the Angular 2 quickstart uses.

The image has the latest node, npm, and git. Docker hub hosts the base image and Node keeps it up to date.

Docker's philosophy is that the containers are meant to execute then terminate with the least privileges possible. In order to make a container work as a development container (i.e. stay up and running), I'll show some not-best-practice choices. This will allow you to get up and going quickly. When you understand the Docker flow, you can implement your own security.

The Docker Images
Docker provides no images on installation. I can see that using the command

docker images 

When I build the nodejs image, it will appear in the list with information about the image.

For now, the two most important columns are the REPOSITORY and IMAGE ID. The REPOSITORY field is the image name I used to build the image. My naming schema indicates my user account (dfberry) and the base image or purpose (node). This helps me find it in the image list.

The IMAGE ID is the unique id used to identify the image.

The Dockerfile
In order to create a docker image, you need a Dockerfile (notice the filename has no extension). This is the file the docker cli will assume you want to use. For this example, the Dockerfile is small. It has the following features:
  • creates a group
  • creates a user
  • creates a directory structure with appropriate permissions
  • copies over the package.json file from the host
  • installs the npm packages listed in the package.json
  • runs the package.json's "start" script – which should start the website

For now, make sure this is the only Dockerfile in the root of the project, or anywhere below the root.

# To build and run with Docker:
#  $ docker build -t ng-quickstart .
#  $ docker run -it --rm -p 3000:3000 -p 3001:3001 ng-quickstart
FROM node:latest

RUN mkdir -p /quickstart /home/nodejs && \
groupadd -r nodejs && \
useradd -r -g nodejs -d /home/nodejs -s /sbin/nologin nodejs && \
chown -R nodejs:nodejs /home/nodejs

WORKDIR /quickstart
COPY package.json typings.json /quickstart/
RUN npm install --unsafe-perm=true

COPY . /quickstart
RUN chown -R nodejs:nodejs /quickstart
USER nodejs

CMD npm start

The nodejs base image will install nodejs, npm and git. The image will just be used for building and hosting the Angular 2 project.

If you have scripts that do the bulk of your build/startup/run process, change the Dockerfile to copy that file to the container and execute it as part of the build.

Build the Image
Usage: docker build [OPTIONS] PATH | URL | -

In order to build the image, use the docker cli.

docker build –t <user>/<yourimagename> .
Example $: docker build –t dfberry/ng-quickstart .

If you don't want to annotate the user, just leave that off.

docker build –t <yourimagename> .
Example $: docker build –t ng-quickstart .

Note: the '.' at the end of the string is the url/location of the Dockerfile. I could have used a Github repository url instead of the local folder.

In the above examples, the REPOSITORY name is 'ng-quickstart'. If you don't use the –t naming param, your image will have a name of <none> which is annoying when they pile up on a team server.

The build will give you some feedback to let you know how it is going.

Sending build context to Docker daemon 3.072 kB 
Step 1 : FROM node:latest 


Removing intermediate container 2cb50f334393 
Successfully built 1265b22b5b90

Since the build can return a lot of information, I didn't include the entire response.

The build of the quickstart takes less than a minute on my Mac.

The last line gives you the IMAGE ID. Remember to view all docker images after building to check it worked as expected.

docker images

Run the Container
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 

Now that the image is built, I want to run the image to see the website.

If you don't have an Angular 2/Typescript website, use the ng2 Quickstart.

Run switches
The run command has a lot of switches and configurations. I'll walk you through the choices for this container.

I want to name the container so that I remember the purpose. This is optional but helpful when you have a long list of containers.

--name ng2-quickstart 

I want to make sure the container's web ports are matched to my host machine's port so I can see the website as http://localhost:3000. Make sure the port isn't already in use on your host machine.

-p 3000:3000 

I want to map my HOST directory (/Users/dfberry/quickstart to the container's directory (/home/nodejs/quickstart) that was created in the build so I can edit on my laptop and the changes are reflected in the container. The /home/nodejs/quickstart directory was created as part of the Dockerfile.

-v /Users/dfberry/quickstart/:/home/nodejs/quickstart 

I want the terminal/cli to show the container's responses including transpile status and the file requests.


The full command is:

docker run -it -p 3000:3000 -v /Users/dfberry/quickstart:/home/nodejs/quickstart --name ng2-quickstart dfberry/ng-quickstart

Notice the image is named dfberry/ng-quickstart while the container is named ng2-quickstart.

Run the "docker run" command at the terminal/cli.

The container should be up and the website should be transpiled and running.

At this point, you should be able to work on the website code on your host with your usual editing software and the changes will reflect in the container (re-transpile changes) and in the web browser.

List Docker Containers
In order to see all the containers, use

docker ps -a

If you only want to see the running containers, leave the -a off.

docker ps

At this point, the easy docker commands are done and you can work on the website and forget about Docker for a while. When you are ready to stop the container, stop Docker or get out of interactive mode, read on.

Interactive Mode (-it) versus Detached Mode (-d)
Interactive Mode means the terminal/cli shows what is happening to your website in the container. Detached mode means the terminal/cli doesn't show what is happening and the terminal/cli returns to your control for other commands on your host.

To move from interactive to detached mode, use control + p + control + q.

This leaves the container up but you have no visual/textual feedback about how the website is working from the container. You can use the developer tools/F12 in the browser to get a sense, but won't be able to see http requests and transpiles.

You are either comfortable with that or not.

If you want the interactive mode and the website transpile/http request information, don't exit interactive mode. Instead, use control + c. This command stops and removes the container from Docker, but doesn't remove the image. You can re-enter interactive mode with the same run command above.

If you are more comfortable in detached mode, where the website gives transpiles and http request information via a different method such as logging to a file or cloud service, change the docker run command.

Instead of using –it as part of the "docker run" command, use –d for detached mode.

Exec Mode to run commands on container
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

When you want to connect to the container, you the same -it for interactive mode but with "docker exec."  The end command tells the docker container what environment to enter in the container -- such as the bash shell.

docker exec –it ng2-quickstart /bin/bash

You can log in as root if you need elevated privileges.

docker exec –it –u root ng2-quickstart /bin/bash 

The terminal/cli should now show the prompt changed to indicate you are now on the container:


When you are done running the commands, use control + p + control + q to exit. The container is still running.

Sudo or Root
In this particular quickstart nodejs docker container, sudo has not been installed. Sudo may be your first choice and you can install. Or you could use the "docker exec" with root. Either way has pros and cons.

Stopping and Starting the Container
When you are done with the container, you need to stop it. You can stop, and restart it as you need by container id or name.

docker stop ng2-quickstart 
docker stop 7449222ec26b 

docker start ng2-quickstart 
docker start 7449222ec26b 

Stopping the container may take some time – be patient. Mine takes up to 10 seconds on my Mac. When you restart the container, it is in detached mode. If you really want interactive mode, remove the container, and use docker run again with –it.

Make sure to stop all containers when they are not needed. When you are done with a container, you can remove it

docker rm –fv ng2-quickstart 

When you are done with an image, you can remove that as well

docker rmi ng-quickstart 

Stop Docker
Remember to stop Docker when you are done with the containers for the moment or day.