Angular plus Docker

Building an Angular App in a Secure and Compact Docker Image (Part 3)

This article will teach you how you can use Docker to build and run an Angular application.

We will create a lightweight and efficient Docker image using Docker’s multi-stage builds feature to serve the app using NGINX.

This post is part of the Dockerized Django Back-end API with Angular Front-end Tutorial. Check out all the parts of the tutorial there.

In the last part of the tutorial, we’ve created a simple dockerized Django API that can create Todo tasks. In this blog post, we’ll move towards the front-end and build the basis of our Angular app to interact with our API.

To get the code to where we left off in the last blog post, use:

$ git checkout v1.4

Creating an Empty Angular App to Dockerize

Firstly, let’s create a new directory in our repo root to hold all Angular related code and cd inside it:

$ mkdir angular
$ cd angular

Next, let’s start an initial Angular project using the Angular CLI.

If you don’t already have the Angular CLI, install it using:

$ npm install -g @angular/cli@latest

Next, create a new application using:

$ ng new angular-app

Select Yes for Angular routing. For the stylesheet format, I chose CSS, but you can select whatever you want.

Run the app to ensure it compiles and works correctly.

$ cd angular-app
$ ng serve

Visit http://localhost:4200 in your browser to confirm the skeleton app works.

You can also checkout the code with the skeleton app using:

$ git checkout v1.5

When developing the app, it’s sufficient to just use ng serve, as the app is dynamically rebuilt on file changes.

When it comes to moving towards a production deployment, setting up a Docker image makes sense as you’ll see shortly.

Creating the Docker Image in Stages

In this section, we will build a Docker image that can be used to serve our Angular app.

The image will be built in two stages:

  1. Build Stage – use a Node Alpine Docker image to compile our app.

  2. Delivery Stage – serve files from build stage using an NGINX Alpine Docker image.

Stage 1 – Compiling the Angular App

Create a Dockerfile file in the angular directory of the repo root.

In the first stage, we will use a node Alpine image as our base Docker image:

FROM node:11.4.0-alpine as builder

We name this stage builder so we can reference it later in the second stage.

Then, we create and set a working directory:

RUN mkdir /app
WORKDIR /app

After, we copy and install our app dependencies:

# Copy app dependencies.
COPY angular-app/package.json angular-app/package-lock.json /app/angular-app/

# Install app dependencies.
RUN npm install --prefix angular-app

The --prefix option installs the dependencies in the angular-app directory.

Lastly, we copy the app files and build it in ./dist/out:

# Copy app files.
COPY . /app

# Build app
RUN npm run build --prefix angular-app -- --output-path=./dist/out

We copy the app files after installing dependencies so that if package.json doesn’t change, Docker will cache the installation step so later image builds will be faster.

Stage 1 looks as follows:

Ignoring files using .dockerignore

Before moving on to the second stage, there is one issue that can cause potential problems.

The second COPY command copies everything, including the node_modules. This means that the dependencies we’ve installed on the Docker image using RUN npm install will be overwritten.

We can create a .dockerignore file in the angular directory to tell Docker that the node_modules directory should be ignored when copying files. Just create the file with the contents:

Thanks to Lukas for this tip from his article.

Stage 2 – Serving the Angular App Using NGINX

Using multi-stage builds, we can start from a new image with NGINX installed and just copy the built app from the builder stage.

Therefore, we use an NGINX Alpine image as the base:

FROM nginx:1.15.7-alpine

Then, we remove the default NGINX website:

RUN rm -rf /usr/share/nginx/html/*

After, we copy the compiled app from the builder image to the NGINX public folder:

COPY --from=builder /app/angular-app/dist/out /usr/share/nginx/html

Lastly, we need to copy an NGINX configuration file to the Docker image. We’ll create this file next.

COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf

In the end, stage 2 looks as follows:

Creating a Default NGINX Configuration File

We need to inform NGINX which files to serve. Create a new directory under the angular one called nginx.

$ mkdir nginx

Inside this directory, create a NGINX configuration file called nginx.conf with the contents:

Here, we tell NGINX to listen on port 80 and for requested files that can’t be found, serve index.html.

Updating the docker-compose File

We can now spin up a container serving the Angular app via NGINX using the docker-compose.yaml file we created in the first part of the tutorial.

In the repo root, update docker-compose.yaml as follows:

We’ve just added a new service called ng and told Docker to map port 8080 on the host to port 80 on the container, where NGINX listens for requests.

If you execute docker-compose up from the repo root, you can then see a new ng container running concurrently with the Django and the PostgreSQL ones:

CONTAINER ID  IMAGE  COMMAND  CREATED STATUS  PORTS  NAMES

67933b65acac  drf-angular-docker-tutorial_dj "python manage.py ru…" 5 minutes ago Up 5 minutes  0.0.0.0:80->80/tcp dj
c478693e2dd3  postgres "docker-entrypoint.s…" 5 minutes ago Up 5 minutes  5432/tcp drf-angular-docker-tutorial_db_1
a6b6c7dc7ab8  drf-angular-docker-tutorial_ng "nginx -g 'daemon of…" 5 minutes ago Up 5 minutes  0.0.0.0:8080->80/tcp ng

If you visit http://localhost:8080, you can see the app being served via NGINX.

Angular running

Awesome! To get to this point of the tutorial, just:

$ git checkout v1.6

Summary

In this part of the tutorial, we’ve learned how to build an Angular application using Docker and how to serve it in an NGINX container.

In the next blog post, we’ll see how we can consume the Django REST API using the Angular app.

Credit: I used the following resources as a basis for this article:

About the Author Dragos Stanciu

follow me on:

Subscribe

Like this article? Stay updated by subscribing to my weekly newsletter:

Leave a Comment: