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.
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.
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. |
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.