Let's talk about how we can use docker in building and hosting an Angular application in a container. Docker is a tool which helps in creating, deploying, and running applications by means of isolated environments called as containers. 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.
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.
To release build an angular application, we would go through the below steps:
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 would choose a docker image which comes with nodejs so that we can continue on top of it by installing angular and so on.
For this, we would 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. 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
We now have our angular application built and the binaries available under /app/dist/MyAngularApp in the container. 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 the above 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
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
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