In this Angular Docker Example, we will take an existing Angular 6 application and run it in a Docker container. We will show, how to create a Docker image in a modern two-stage process. We then will spin up the application in a corresponding Docker container. After that, we will save your work by pushing the project and the Docker image to GIT and Docker Hub, respectively.

The step-by-step instructions below apply to any Angular CLI projects and have been tested on Angular 4 and Angular 6.

Angular Docker Excample

tl;dr

In this section, we summarize the process of creating a Docker image from an Angular project within a few lines. The more elaborate version starts with the Prerequisites section below.

On a Docker host with >= 1.5 GB RAM, perform:

git clone <yourproject>
cd <yourproject>
curl https://raw.githubusercontent.com/oveits/angular4-docker-example/master/Dockerfile -o Dockerfile
mkdir nginx
curl https://raw.githubusercontent.com/oveits/angular4-docker-example/master/nginx/default.conf -o nginx/default.conf
docker build -t mytag
docker run -p80:80 mytag

Now access the application via a browser on http://<ip-of-docker-host>:

Done.

Prerequisites

  • Docker Host with 2 GB RAM and Docker Version 17.05
  • Git installed

Test Environment

  • CentOS 7 image on Hetzner Cloud with 4 GB RAM
  • Docker version 18.01.0-ce, build 03596f5 (minimum 17.05)

Step 0: Access a Docker Host

The Docker host must provide you with more than 1.5 GB free RAM. Since we will use a two-stage Dockerfile, the minimum Docker version is 17.05.

Step 1: Clone GIT Project

If you do not apply the instructions on your own project, you can clone the GIT project. In our example, we will apply the instructions on the Angular Tour of Heroes GIT project of John Papa:

git clone /angular-tour-of-heroes

# optional:
git checkout c6596771 # so, you use the same example as I did

Step 2: Copy Dockerfile and Nginx Config File

We now need to download the Dockerfile and the Nginx configuration file into your project:

cd angular-tour-of-heroes
curl https://raw.githubusercontent.com/oveits/angular4-docker-example/master/Dockerfile -o Dockerfile 
mkdir nginx 
curl https://raw.githubusercontent.com/oveits/angular4-docker-example/master/nginx/default.conf -o nginx/default.conf

Step 3: Build Docker Image

We now can build the Docker image. For convenience, we have tagged the image with a temporary tag “myimage”:

docker build . -t myimage
Sending build context to Docker daemon  1.108MB
Step 1/12 : FROM node:8-alpine as builder
 ---> ca148a52ea10
Step 2/12 : COPY package*.json ./
 ---> Using cache
 ---> 9dbbc06fae59
Step 3/12 : RUN npm set progress=false && npm config set depth 0 && npm cache clean --force
 ---> Using cache
 ---> e87f54fe705b
Step 4/12 : RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app
 ---> Running in e447c7c49bf1
npm WARN notice [SECURITY] timespan has the following vulnerability: 1 low. Go here for more details: https://nodesecurity.io/advisories?search=timespan&version=2.3.0 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] stringstream has the following vulnerability: 1 moderate. Go here for more details: https://nodesecurity.io/advisories?search=stringstream&version=0.0.5 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] randomatic has the following vulnerability: 1 low. Go here for more details: https://nodesecurity.io/advisories?search=randomatic&version=1.1.7 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] tunnel-agent has the following vulnerability: 1 moderate. Go here for more details: https://nodesecurity.io/advisories?search=tunnel-agent&version=0.4.3 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] hoek has the following vulnerability: 1 moderate. Go here for more details: https://nodesecurity.io/advisories?search=hoek&version=2.16.3 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] https-proxy-agent has the following vulnerability: 1 high. Go here for more details: https://nodesecurity.io/advisories?search=https-proxy-agent&version=1.0.0 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] http-proxy-agent has the following vulnerability: 1 high. Go here for more details: https://nodesecurity.io/advisories?search=http-proxy-agent&version=1.0.0 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.
npm WARN notice [SECURITY] debug has the following vulnerability: 1 low. Go here for more details: https://nodesecurity.io/advisories?search=debug&version=2.2.0 - Run `npm i npm@latest -g` to upgrade your npm version, and then `npm audit` to get more info.

> uws@9.14.0 install /node_modules/uws
> node-gyp rebuild > build_log.txt 2>&1 || exit 0


> node-sass@4.9.0 install /node_modules/node-sass
> node scripts/install.js

Downloading binary from https://github.com/sass/node-sass/releases/download/v4.9.0/linux_musl-x64-57_binding.node
Download complete
Binary saved to /node_modules/node-sass/vendor/linux_musl-x64-57/binding.node
Caching binary to /root/.npm/node-sass/4.9.0/linux_musl-x64-57_binding.node

> node-sass@4.9.0 postinstall /node_modules/node-sass
> node scripts/build.js

Binary found at /node_modules/node-sass/vendor/linux_musl-x64-57/binding.node
Testing binary
Binary is fine

> @angular/cli@6.0.0 postinstall /node_modules/@angular/cli
> node ./bin/ng-update-message.js

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.3 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

added 1190 packages in 41.51s
Removing intermediate container e447c7c49bf1
 ---> 1d74657b3668
Step 5/12 : WORKDIR /ng-app
Removing intermediate container 036674cd61f3
 ---> a6bece3fd503
Step 6/12 : COPY . .
 ---> 9bed1d805e2a
Step 7/12 : RUN $(npm bin)/ng build --prod --build-optimizer
 ---> Running in 41d54910ff3c

Date: 2018-07-21T15:43:48.497Z
Hash: 1756014f222e4d11585d
Time: 42800ms
chunk {0} runtime.6afe30102d8fe7337431.js (runtime) 1.05 kB [entry] [rendered]
chunk {1} styles.ad4081761244032ce894.css (styles) 168 bytes [initial] [rendered]
chunk {2} polyfills.0d74a55d0dbab6b8c32c.js (polyfills) 64.2 kB [initial] [rendered]
chunk {3} main.043ec7c5c72f6f08b454.js (main) 405 kB [initial] [rendered]
Removing intermediate container 41d54910ff3c
 ---> 3685c501bfe8
Step 8/12 : FROM nginx:1.13.3-alpine
1.13.3-alpine: Pulling from library/nginx
019300c8a437: Pull complete
a3fe4a77433d: Pull complete
a5443900e7f5: Pull complete
0ae275323c0f: Pull complete
Digest: sha256:24a27241f0450b465f9e9deb30628c524aa81a1aa6936daa41ef7c4345515272
Status: Downloaded newer image for nginx:1.13.3-alpine
 ---> ba60b24dbad5
Step 9/12 : COPY nginx/default.conf /etc/nginx/conf.d/
 ---> effb2a16266b
Step 10/12 : RUN rm -rf /usr/share/nginx/html/*
 ---> Running in c8f0c683c752
Removing intermediate container c8f0c683c752
 ---> 48141e3775a5
Step 11/12 : COPY --from=builder /ng-app/dist /usr/share/nginx/html
 ---> 19e7de13e9a4
Step 12/12 : CMD ["nginx", "-g", "daemon off;"]
 ---> Running in f11387789515
Removing intermediate container f11387789515
 ---> 7cd596569ef5
Successfully built 7cd596569ef5
Successfully tagged myimage:latest

When scrolling through the log, we can see that there are many warnings, which we ignore for now.

Step 4: Run the Docker Image

Once the image has been built successfully, we can test the image by running it with a map from Docker host TCP port 80 to the container port 80:

$ docker run --rm -d -p80:80 myimage
9ad68297195068823583f563c439a6778dfce4f82aacd8fa4887bbf8b047326c

Step 5: Test the Application

We now can access the application on port 80 of the Docker host http://<ip-of-docker-host>:

That works fine.

Excellent!

Step 6 (optional): Push the Docker Image to Docker Hub

In order to retain your results, you may want to push the newly created Docker image to Docker hub (registration required). For that, let us re-tag the image with the correct Docker Hub user and image. First, we need to log in (type your username and password on the prompt):

docker login

Then we can re-tag the image and push the image to Docker Hub:

docker tag myimage oveits/angular-cli-hello-world-with-docker:v0.1 
docker push oveits/angular-cli-hello-world-with-docker:v0.1 
docker tag myimage oveits/angular-cli-hello-world-with-docker:latest
docker push oveits/angular-cli-hello-world-with-docker:latest 

Once, this is done, you can start the image from any other Docker host by issuing the command

docker run --rm -d -p80:80 oveits/angular-cli-hello-world-with-docker

Step 7 (optional): Save your work on GIT

If this is your own project, you also may want to retain the work by committing the changed and pushing them to GIT:

git add .
git commit -m'dockerized the application'
git push

Summary

We have successfully dockerized an example Angular 6 project (John Papa’s Tour of Heroes) by downloading a Dockerfile and a Nginx configuration file, building the docker image and running it.

References

Appendix A: Dockerfile

Above, we have used a two-stage Dockerfile that requires a minimum Docker version 17.05. You can download the file from here:

### STAGE 1: Build ###

# We label our stage as 'builder'
FROM node:8-alpine as builder

COPY package*.json ./

RUN npm set progress=false && npm config set depth 0 && npm cache clean --force

## Storing node modules on a separate layer will prevent unnecessary npm installs at each build
RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app

WORKDIR /ng-app

COPY . .

## Build the angular app in production mode and store the artifacts in dist folder
RUN $(npm bin)/ng build --prod --build-optimizer


### STAGE 2: Setup ###

FROM nginx:1.13.3-alpine

## Copy our default nginx config
COPY nginx/default.conf /etc/nginx/conf.d/

## Remove default nginx website
RUN rm -rf /usr/share/nginx/html/*

## From 'builder' stage copy over the artifacts in dist folder to default nginx public folder
COPY --from=builder /ng-app/dist /usr/share/nginx/html

CMD ["nginx", "-g", "daemon off;"]

In stage 1, the Dockerfile spins up a NodeJS Docker container and builds the files with ng build. Once the files are created, they are copied to the Nginx image in Dockerfile stage 2.

When starting a Docker container from the Nginx image, the Docker container will start the Nginx server.

Docker build is designed as a two-stage process; a Docker feature that requires a newer version of Docker than the one I have used for months now. With the version described above, it works, though.

Appendix B: Nginx Config

The above Dockerfile expects the default Nginx config file to be located on nginx/default.conf. Let us place it there:

$ mkdir nginx
$ vi nginx/default.conf

The content of nginx/default.conf is:

server {

  listen 80;

  sendfile on;

  default_type application/octet-stream;


  gzip on;
  gzip_http_version 1.1;
  gzip_disable      "MSIE [1-6]\.";
  gzip_min_length   256;
  gzip_vary         on;
  gzip_proxied      expired no-cache no-store private auth;
  gzip_types        text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_comp_level   9;


  root /usr/share/nginx/html;


  location / {
    try_files $uri $uri/ /index.html =404;
  }

We have chosen to Nginx server to listen to port 80. This is, why we had to map the Docker host port to the Docker container port 80 on the docker run command above.

One comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.