What is a Container?
A container can be thought of a package which allows developer to pack all the necessary libraries and dependencies for running an application without any issues and deploy the entire container as an instance.
This ensures that the application runs without any environmental issues which might otherwise arise in an application deployment.
What is Docker?
Docker is a tool which helps in creating, deploying, and running applications by means of isolated environments called as containers.
For creating a container using docker, we make use of a build script called Dockerfile which contains instructions for docker on how to build the application and what all the dependencies and libraries that need to be packed into the container before packing it into a complete entity.
Once the container has been created, we then run the docker command to boot up the container with instructions on how the application should behave on startup and so on.
Now that we have an idea about what Docker is and how we shall proceed, we begin by understanding how we need to build and pack our angular application for containerized use.
How an Angular SPA should be deployed?
So it is clear that we also need to pack a webserver along with our angular application for it run inside a container.
Steps to Deploy Angular SPA into Docker
To release build an angular application, we go through the below steps:
- Install dependencies
- Run angular build with release flag
- Copy the output files onto the webserver
Let’s begin by scripting the Dockerfile we are to use for building the container. A Dockerfile generally contains three segments, A base image declaration, some script instructions and a startup command.
We shall choose a base image which can provide us with necessary prerequisites such as nodejs, angular and npm for us begin with the build. Unfortunately, we would need to prepare one such image for us as there are not many docker images which come with angular out-of-the-box.
Instead, we choose a docker image which comes with nodejs so that we can continue on top of it by installing angular and so on.
Step 1 – Selecting Base Image and Installing Angular
For this, we choose node:14.2.0-alpine3.11, which is an alpine image with nodejs preinstalled. For starters, alpine is a light-weight linux image which supports installing necessary dependencies by means of its own package manager known as apk (Alpine Package Manager).
We shall write instructions on top of the node:14.2.0-alpine3.11 image we have chosen inline with the steps mentioned above as:
# BASE IMAGE with an alias # FROM node:14.2.0-alpine3.11 as build WORKDIR /app # Install Angular CLI to run Build # RUN npm install -g @angular/cli
As described in the comments, so far we have specified our base image with a tagged “alias” which we shall use in the next steps. Then we added an instruction to run install angular/cli via npm.
Step 2 – Copying the files and Angular build
After these steps are executed, we will have a node-alpine image with angular/cli installed. We can now run angular commands over our angular project for build and deploy.
COPY ./package.json . RUN npm install COPY . . RUN ng build
In the next steps, we begin by copying the package.json file alone from our angular project and copy it onto the container directory. This is followed by an instruction to run “npm install” that installs all the necessary dependencies for our angular project onto the container.
Once this is done, we just copy all the contents in our angular project onto the container. This includes the src, e2c, tsconfig and other files which are in the angular project directory.
We now have our angular application built and the binaries available under /app/dist/MyAngularApp in the container.
Step 3 – Copying the dist files onto NGINX
Next is deploying the contents of this folder in a webserver. We choose nginx for this job, because it’s lightweight and easy to use. The steps to copy the contents from /app/dist/MyAngularApp onto another container which is an nginx webserver is what we called a “build step”.
# BASE IMAGE with an alias # FROM nginx as runtime # Copy contents from the other container with alias "build" # # onto the specified path in the current container# COPY --from=build /app/dist/MyAngularApp /usr/share/nginx/html
As described in the comments, we make use of the alias “build” we tagged for the container in the “previous step” which generated the output angular files and copy those output files into the path “/usr/share/nginx/html” within the current container.
According to the nginx documentation, in order for a file to be hosted in the nginx webserver we would need to paste the contents under “/usr/share/nginx/html” path.
Whenever Docker comes across a second FROM instruction, it treats it as a new “build step” meaning that the container instructions have been completed.
It now starts with the new container and in this case, copies the contents from the previous container and into the current container in the specified path. This completes our build and deploy “script”.
But we haven’t instructed on how the “application” should behave when it starts up as a container. In this case, since we’re using a webserver all it does is listen on the port 80 and so nothing in particular needs to be done for us here.
The complete script:
FROM node:14.2.0-alpine3.11 as build WORKDIR /app RUN npm install -g @angular/cli COPY ./package.json . RUN npm install COPY . . RUN ng build FROM nginx as runtime COPY --from=build /app/dist/MyAngularApp /usr/share/nginx/html
Building and running a Container
To run this recepie, we run the below command in the same directory as the Dockerfile, which will build this script into a container.
> docker build .
Which returns us a container_id. Then we run,
docker run -p 8080:80 container_id
Shortcut with Docker Compose
This way, we specify that all the requests to port 8080 onto the container from the outside network shall be internally routed to port 80 where the angular application runs.
Alternatively, we have the docker-compose route which simplifies our “docker run” instructions in the form of a docker-compose.yaml file in the same directory as of the Dockerfile as below:
version: "3" services: angular-app: build: . ports: - 8080:80
And to run this, we simply call:
> docker-compose up
Now we open localhost:8080 in our local machine and can see that the Angular application works quite fine. That’s all!