In this article, I will show you, A complete tutorial to build an Angular application to docker production-ready image with multi-stages. And I will also run this angular docker image in a docker container.
Final this project-related files and directories structure …
. ├── dist │ └── docker-angular-app ├── Dockerfile ├── nginx │ └── default.conf ├── package.json ├── package-lock.json ├── ... └── ...
Create a angular project
In this article, I am using the fresh new angular app. But If you have any existing angular then you skip this part and you can use your existing angular application.
ng new docker-angular-app cd docker-angular-app
Build Dockerfile
In the project directory, Create Dockerfile with no extension and first latter capital.
# STAGE 1: Build app # Step 1: Pull node offical image FROM node:16.13.2-alpine as builder # Step 2: Define working dir for builder container WORKDIR /usr/local/app # Step 3: Copy package.json and package-lock.json files COPY package*.json ./ # Step 4: Install dependancy RUN npm install # Step 5: Copy all source code COPY . . # Step 6: Generate build of application RUN $(npm bin)/ng build --configuration=production --source-map=false # STAGE 2: Setup Server # Step 7: Pull nginx image FROM nginx:1.21.5-alpine # Step 8: Copy default nginx config COPY nginx/default.conf /etc/nginx/conf.d/default.conf # Step 9: Copy builded application source code COPY --from=builder /app/dist/docker-angular-app /usr/share/nginx/html # step 10: Expose container port 80 EXPOSE 80 # Step 11: Switch nginx deamon off # Note: If your container serve multiple service than don't daemon off. CMD nginx -g "daemon off;"
That is a docker snippet, I use to build for angular application to the docker image. This docker snippet has a multi-stage build.
Stage #1: Build this angular app to production ready code
Step 1: FROM node:16.13.2-alpine as builder
Pulling node runtime image, Initialize the new build stage. In my case, I used node 16.13.2 alpine version. It’s better to use your machine node version which is the angular app developed. In the development case, some developers use the node’s latest version. Which is not for production.
In the snippet, I have used the alpine version, which is lightweight more than the row node version
Step 2: WORKDIR /usr/local/app
Sets the default working directory for the present building container. After this instruction is executed, If this directory doesn’t exist, this directory will be created. the subsequent steps in the Dockerfile
The defined directory(usr/local/app) is inside the container which has its own independent file system
Step 3: COPY package*.json ./
Copy the source application dependency files (package.json and package-lock.json) on the host machine to the specified working directory(usr/local/app)
Step 4: RUN npm install
In the working directory run command, “npm install
” to install all application dependencies.
Step 5: COPY . .
Copy all the current directory source codes excluding folder or directory which is defined on the .dockerigone file.
Multiple building dependencies install problem understanding*
Note: It’s solved in this snippet code.
imagine you are building the app for the first time, and it is ok to install all the dependencies. But you change a source code related to the angular app (not related to any dependencies) and then push again to build the image. and the run process installs all dependencies again. You probably don’t want to install the dependency unless some dependency is added or removed.
When your execution process copies all files and directories, Install dependency. that problem will be happen
This snippet code, step 3: copy package.json and package-lock.json which are application dependencies related. Then step 4: install dependencies and step 5: copy all source codes.
This execution process solve multiple build install dependencies, that’s used
Step 6: RUN $(npm bin)/ng build --configuration=production --source-map=false
Executes the angular build in a new layer on top of the base node image. After this instruction is executed, the build output is stored under usr/local/app/dist/docker-angular-app
and the compiled image will be used for the subsequent steps in the Dockerfile
Stage #2: Serving the angular production ready app to nginx server
Step 7: FROM nginx:1.21.5-alpine
Initialize final or secondary build stage. nginx:1.21.1.5-alpine image as the base image for executing subsequent instructions relevant to nginx configuration
Step 8: COPY nginx/default.conf /etc/nginx/conf.d/default.conf
Copy our own Nginx configuration file for the angular application to the docker Nginx server image.
Note: make sure your Nginx file is “nginx” directory. Or If it is inside others directory, so make sure you use right your configuration path
Step 9: COPY –from=builder /app/dist/docker-angular-app /usr/share/nginx/html
Copies the build output generated production-ready static resource in stage 1 (--from=build
er) to replace the default Nginx HTML contents.
Important Note: Angular builds production-ready static resources into ./dist/[project name]. In this example, the project name is docker-angular-app
so the output directory is docker-angular-app
. So make sure! you use your project name.
Step 10: EXPOSE 80
Exposing ports is a way of documenting which ports are used, but does not actually map or open any ports
Step 11: CMD nginx -g "daemon off;"
I use Nginx daemon off. Because for microservices, the directive tells Nginx to stay in the foreground. For the microservices, the best practice is for one container = one process. So one container server has only one service.
When normal production on a server, I use the default Nginx daemon on;
So the Nginx server will start in the background. In this way, Nginx and other services are running and talking to each other. In this case, one server runs many services.
.dockerignore file
If you had used git, then you are so familiar with the .gitignore file. The .dockerignore file is very similar to the .gitignore file. It allows specifying a list of files or directories that Docker is to ignore during the build process. Most importantly, the .dockerignore can help you reduce the size of the image and dramatically speed up the build process.
# compiled output /dist /tmp /out-tsc # dependencies /node_modules # IDEs and editors /.idea # IDE - VSCode .vscode/* # misc /.angular/cache /.sass-cache /connect.lock /coverage /libpeerconnection.log npm-debug.log yarn-error.log testem.log /typings
Nginx configuration
This is a custom Nginx configuration snippet for our production-ready angular app.
Make sure you are using the right root path, which is a copy production-ready static resource in the container nginx path.
Create docker-angular-app/nginx/default.conf file in nginx separate folder.
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; } location /health { return 200 'I am live :)'; } }
Docker build image
docker build -t image_name:tag . docker build -t angular-app:latest .
Check docker images
docker images
Run docker image
docker run --name container_name -d -p local_machine_port:container_port image_name:tag docker run --name docker-angular-container -d -p 4000:80 angular-app:latest
Note: nginx configuration file lister port(80) must be same of container port(80)
Check docker containers
The below command shows the running containers of docker
docker ps
Below command shows all inactive containers
docker ps -aq