Red Green Repeat Adventures of a Spec Driven Junkie

Angular & Docker

In my previous Angular article I went over how to setup a virtualbox for developing an Angular application.

Docker has been on my radar for a long time and I’m working with it more recently, as well as Angular.

This time, I will go over how to setup Docker with Angular and use it for development.

Requirements

Install Docker

I am using Docker Community Edition, its installation instructions are here. I expect this article to also apply to Docker Enterprise Edition.

Make sure docker image works on your system before moving on. If you need help setting up Docker, please feel free to contact me

Internet Connection

Initial build will download at around two gigabytes of data. Having a fast connection will make the initial build a pleasant experience.

Storage

The build processes downloads two gigabytes of data, the final Docker image file requires approximately two gigabytes of storage space.

Concepts

This article builds on previous concepts I wrote about before. If there is a concept not clear, contact me or check out my previous articles on the topic.

Dockerfile

This is the first time I have created a Dockerfile and it basically is like using the shell provisioner in Vagrant to automate the setup of the Docker image.

This is the Dockerfile described in this article:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM node:9.5.0

RUN npm install npm --global

RUN npm install --unsafe-perm -g @angular/cli

RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list
RUN apt-get update && apt-get install -y google-chrome-stable

RUN mkdir -p /angular
WORKDIR /angular

RUN ng new cache-temp
RUN rm -rf cache-temp

I will go over each section of the file in the following sections.

Files associated with this article are here: here and the complete image are on docker hub

Install node.js

FROM node:9.5.0

A Docker image builds from another docker image. This increases reuse and saves headache from building from scratch.

In this case, I use the docker image for node version 9.5.0 as the base. I chose this image as Angular uses node and this is one of the newest versions of node.

We can use another image, such as the ubuntu image, but would additional require work, such as installing node, to get to the same state.

I chose to build off of the work of the node.js docker team in this case.

Install npm

RUN npm install npm --global

The above command installs the associated version of npm with node.

RUN is a command in a Dockerfile to execute a command inside the docker image, just like on the command line of the system, as root.

Install Angular

RUN npm install --unsafe-perm -g @angular/cli

The above command uses npm to install the current version of Angular, which as of this writing is version five.

unsafe-perm

This build requires the option: unsafe-perm as the user installing is root, and this is a known error with npm.

As the executing user in a docker container is root, this is the easiest way I have found to workaround this issue for now.

At the same time, I consider this a safe workaround as the system inside a docker image/container.

Chrome

RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
RUN echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | tee /etc/apt/sources.list.d/google-chrome.list
RUN apt-get update && \
    apt-get install -y google-chrome-stable

The above commands sets up items needed to install the latest version of Google Chrome browser.

  • get the public key for the repository
  • adds the repository to the internal list of repositories
  • updates repository listings
  • installs Google Chrome browser

This adds 500MB to the docker image and is optional. I recommend installing is as this will give the ability to run all the tests inside the container. ng test will use the internally installed Google Chrome to run the tests instead of requiring a connected browser. This is also known as headless testing.

A major benefit of having headless testing is the continuous integration service such as travis or circleci can run tests.

Working Directory

RUN mkdir -p /angular
WORKDIR /angular

The above commands will creates and uses a directory on the system as working directory. This gives a basic entry point to mount folders to share data between containers and your system.

Cache Angular Packages

RUN ng new cache-temp
RUN rm -rf cache-temp

The commands seem redundant: create a new Angular project in the current folder and then immediately remove it.

Main reason for this is to cache additional Angular packages not installed by npm. This guarantees any new Angular projects created will work when the system does not have a connection to node packages.

Build Docker Image

With the above contents in a Dockerfile, build the docker image by running the command:

docker build . -t angular-rgr

With successful build, docker ps will show:

$ docker images
REPOSITORY                               TAG                 IMAGE ID            CREATED             SIZE
angular-rgr                              latest              d0a8a1318d5c        6 seconds ago       1.57GB

Let’s start using this image!

Developing with angular-rgr

With the Angular image working, let’s do some basic development with it. The following commands will:

  • make a new Angular project
  • serve the project for development
  • run the project’s tests
  • run the project’s tests inside the container

Create New Project

To make a new project named: “new-app” using the angular-rgr docker image, run this command:

$ docker run --mount type=bind,source="$(pwd)",target=/angular angular-rgr ng new new-app

Description:

–mount type=bind,source=”$(pwd)”,target=/angular Shares the current folder to /angular inside the container.
angular-rgr The docker image name.
ng new new-app Angular CLI command to create a new Angular application.

Run Project

To run a project named: “new-app”, using the angular-rgr docker image, run at the command line:

$ docker run --mount type=bind,source="$(pwd)/new-app",target=/angular -p 4200:4200 angular-test ng serve --host 0.0.0.0

Description:

–mount type=bind,source=”$(pwd)/new-app”,target=/angular Shares the “new-app” folder to /angular inside the container.
-p 4200:4200 Expose the host port, 4200, and link to the docker container port 4200.
angular-test The Docker image name.
ng serve The angular command to serve the application.
–host 0.0.0.0 Have the Angular serve process listen to requests on 0.0.0.0 instead of localhost.

The reason to use --host 0.0.0.0 is the default of localhost will not work as the server by default binds to localhost and in this case, the server needs to listen to all requests, not just ones coming to localhost.

Application in
Browser

Running Project Tests

To run the automated tests for a project named “new-app”, using the angular-rgr docker image, run the following command:

docker run --mount type=bind,source="$(pwd)/new-app",target=/angular -p 9876:9876 angular-test ng test
–mount type=bind,source=”$(pwd)/new-app”,target=/angular Shares the “new-app” folder to /angular inside the container.
-p 9876:9876 Expose the host port, 9876, and link to the docker container port 9876.
angular-rgr The Docker image name.
ng test The Angular command to listen for a browser to serve test files.

Now, open a browser and go to: localhost:9876 and the browser will run tests.

Testing with
Browser

Running Project Tests in Container

To run the automated tests inside the container using the installed Google Chrome browser, there needs to be an adjustment to the test configuration settings and then the tests will run smoothly inside the container.

Configure test settings

In the karma.conf.js file inside the project, add the customLaunchers section after the singleRun option:

customLaunchers: {
  HeadlessChrome: {
    base: 'ChromeHeadless',
    flags: ['--no-sandbox']
  }
}

The main reason for this is that Chrome will not be able to run by root user, the docker default, without the --no-sandbox option.

$ docker run --mount type=bind,source="$(pwd)/new-app",target=/angular -p 4200:4200 angular-rgr ng test --browser ChromeHeadless
23 02 2018 03:53:04.595:WARN [karma]: No captured browser, open http://localhost:9876/
23 02 2018 03:53:04.606:INFO [karma]: Karma v2.0.0 server started at http://0.0.0.0:9876/
23 02 2018 03:53:04.607:INFO [launcher]: Launching browser ChromeHeadless with unlimited concurrency
23 02 2018 03:53:04.627:INFO [launcher]: Starting browser ChromeHeadless
23 02 2018 03:53:05.500:ERROR [launcher]: Cannot start ChromeHeadless
...
        [0223/035306.252748:ERROR:zygote_host_impl_linux.cc(90)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.

23 02 2018 03:53:06.268:ERROR [launcher]: ChromeHeadless stdout:
23 02 2018 03:53:06.268:ERROR [launcher]: ChromeHeadless stderr: [0223/035306.252748:ERROR:zygote_host_impl_linux.cc(90)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.

23 02 2018 03:53:06.290:ERROR [launcher]: ChromeHeadless failed 2 times (cannot start). Giving up.
23 02 2018 03:53:08.472:WARN [karma]: No captured browser, open http://localhost:9876/

Running Tests

Now, run all tests in the container, run the command:

docker run --mount type=bind,source="$(pwd)/new-app",target=/angular -p 9876:9876 angular-test ng test --browser HeadlessChrome
–mount type=bind,source=”$(pwd)/new-app”,target=/angular Shares the “new-app” folder to /angular inside the container.
-p 9876:9876 Expose the host port, 9876, and link to the docker container port 9876.
angular-rgr The Docker image name.
ng test The Angular command to listen for a browser to serve test files.
–browser HeadlessChrome Use the browser HeadlessChrome instead of serving.

Testing
Headless

Conclusion

I have covered how to build a Docker image for angular and using the image for development tasks such as: making a new application, running the application, and testing the application.