Showing posts with label Docker. Show all posts
Showing posts with label Docker. Show all posts

Monday, 14 November 2022

Dockerize VueJS or Angular application


 

For the past couple of months, I spent my time heavily on infrastructure and CICD DevOps activity to host my applications, web, and backend application on Azure Kubernetes Cluster. Also if you follow me, my last few articles were all about a complete setup overview regarding CICD and security.

This article is about dockerizing a VueJS or Angular web application, though my web application here was on VueJS, the same technique applies to Angular applications to dockerize the application.

Before we begin, we need to take a decision on the server that will serve the application like http-server, Nginx, Apache etc. For development/testing we may use zero-configuration command-line http-server to server our web application but not recommended for production as the documentation says:

So, in my case, I decided to use Nginx and this is all you need to do.

First, we need to create the DockerFile so add a file name “Dockefile” in your project root folder where package.json or config file exists and add the below code:

FROM node:16.15.0-alpine as build
WORKDIR /app

COPY package.*json ./
COPY .npmrc ./
COPY .env.production ./

RUN npm install

COPY . .

WORKDIR /app

RUN npm run build:production
RUN rm -f .npmrc

# production
FROM nginx:stable-alpine as production
COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf

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

COPY --from=build /app/dist /usr/share/nginx/html
# Expose 8030 this is optional
CMD ["nginx", "-g", "daemon off;"]

From the above file, first, we mention the build server as Node. In my case, I’m using a specific version of Node which you may change based on your need. Next line set the working folder as app, where the dist folder along with other config files will be created.

in the next step, we copied our required config files as package.*json (also includes package.lock.json), .npmrc (which contains token to connect and pull dependencies from my repo), .env files. You may add all your config files needed here to keep the code related to copying config files in one place otherwise in the next step (npm install), only package.json and .npmrc files are required.

So next we did, npm install and copied all (which include dist folder) to the working directory app folder.

In the next step, we build the application using the npm run build command and delete the .npmrc file copied from the first step required for the npm install.

Now we need to set the web server and we set it here as “nginx:stable-alpine”, you may choose other versions too based on your need. Here we need the Nginx configuration file to tell the Nginx server where to start i.e. root, port, no. of worker process, etc. We have not created this file yet so let’s do it by creating a file name nginx.conf with the below code inside the .nginx folder in your project root path as .nginx=>nginx.conf.

worker_processes 4;

events { worker_connections 1024; }

http {
server {
# listen 80; this is optional, it will be default to 80
root /usr/share/nginx/html;
include /etc/nginx/mime.types;

location /appui {
try_files $uri /index.html;
}
}
}

Now we will go back to the Dockerfile and copy this nginx.conf file to the Nginx server inside an etc folder and also remove the default index page from the location usr/share/nginx/html/*

FROM nginx:stable-alpine as production
COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf

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

and then we copy all the contents of the dist folder from the build server to Nginx server inside /usr/share/nginx/html and mention the command mentioned below to start the nginx.

COPY --from=build /app/dist /usr/share/nginx/html
# Expose 8030
CMD ["nginx", "-g", "daemon off;"]

Testing

If you have a docker desktop installed, follow the steps below to test.

step 1: run the below command using the command prompt from your project root folder where the package.json file exists.

docker build  -t test/webapp_v1 .

After the successful execution of the above command, you will see the image created in the docker desktop under Images.

step 2: Click on Run and change the port setting from the host server and container as per our docker file and nginx.conf file where our container is exposed through port 8030 and the host server is at port 80. Please refer to the below screenshot:

Now open the browser and type http://localhost:8090/ to browse the application and your web app is up.

Bonus

Please follow my previous articles to push this image to Azure Container Registery and deploy it on Azure Kubernetes Cluster using GitHub Actions. Though these articles are for hosting .net core app but the steps remain the same with little modification as per the web app.

  1. CI/CD with GitHub Actions to deploy Applications to Azure Kubernetes Cluster
  2. Securing secrets with Azure Key Vault for GitHub Actions

Wednesday, 15 December 2021

Tune up your Docker file — Best Practices

 Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers.

1. Use the appropriate specific version image as base image instead of using generalized base image and start installing required packages.

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
From node:17.2.0

2. Always try to use the minimum light weight image as suits your requirements.

docker image inspect mcr.microsoft.com/dotnet/aspnet:5.0

3. Optimize caching image layer

FROM node:17.2.0-alpineWORKDIR /appCOPY package.json package-lock.json .RUN npm install --productionCOPY myapp /appCMD ["node", "src/index.js"] 

4. Avoid files/folder to copy to image not required

**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

5. Use the Multi-stage builds concepts.

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["CoreWebAPIDemo.csproj", "."]
RUN dotnet restore "./CoreWebAPIDemo.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "CoreWebAPIDemo.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "CoreWebAPIDemo.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "CoreWebAPIDemo.dll"]

6. Use the least privileged user to start the application

USER ContainerUserENTRYPOINT ["dotnet", "CoreWebAPIDemo.dll"]

7. Perform Vulnerability scanning for Docker image

Monday, 25 October 2021

Unable to ping host.docker.internal

 This is the problem people out there facing a lot where their docker containers are unable to connect to docker host.

In general, host IP will be changing specially in dev machine and this create trouble for docker to resolve the host DNS and establish the connection. Hence Docker recommend a special DNS name host.docker.internal to use which resolves to the internal IP address used by the host.

In windows, we need to add below entry to the C:\Windows\System32\drivers\etc\hosts file as:
192.XXX.XX.XX host.docker.internal

All good but there is a problem where docker containers are unable to ping host.docker.internal and I too face the problem every time I restart the system. There is a huge discussion here and the channel is closed and I don’t see a working solution.

After struggling a lot, finally I figured out a working solution for me which I’m using it since last one month from the date I’m writing this post. Solution is, run the below command once when you restart your system, basically it is a cleanup command to clean unused resources like unused network, cache, unused & dangling containers. Read here more about this.

docker system prune

My Environment:
OS: Windows 10 Enterprise
Docker Desktop Version: 4.1.1

Hope this helps you. Thank you for reading.

Saturday, 26 June 2021

Defining Custom IP range for Docker Containers

 I feel or I would recommend, it is always good to go with custom subnet IP range for your docker containers and you would need it for many reasons:

  1. What id default IP ranges are not available
  2. You need to add the range/IP to your firewall but don’t know because of it’s dynamic nature. etc.

There are two ways you can do it and I’m saying it two ways because you would need it if you are dealing with docker containers in windows OS.

  1. Define it with Docker-Compose.yaml file.
For Linux:networks:
mycustomnet:
driver: bridge
ipam:
config:
- subnet: 20.5.0.0/24
For Windows:networks:
mycustomnet:
driver: nat
ipam:
config:
- subnet: 20.5.0.0/24

So above definition will create a name subnet under bridge/nat driver and you can use this defined subnet for your containers and your containers will always use IP from the above mentioned range only.

But this is a problem with Windows OS. In windows OS this defined network (in this case mycustomnet) will get disappear with restarting of windows OS. Aah it makes me frustrated when I faced this and unfortunately there is no solution available neither from Docker or Windows (at lease I didn’t find it as of now) and this is where I was forced to choose second approach which preferably I won’t recommend.

2. Define it via Docker Daemon.json

For windows:{"fixed-cidr": "20.5.0.0/16"}For Linux:{"default-address-pools":[
{"base":"20.5.0.0/16","size":24}
]
}

Above settings will change the default network IP range to 20.5.0.0/16 in windows as well as in Linux but in Linux I get extra advantage to mention the /24 range to be used out of /16 by creating a new named subnet. So in linux if you do >>docker network ls then you will see the defined name network as dockerartifacts_service or similar.

Since there is no problem with Linux so I would always recommend the first approach for Linux machine.

Important Note:
If you are doing the second approach for any reason, then don’t forget to delete the existing net and restart the docker engine and to do this you can use powershell command for windows as:

Get-HNSNetwork | Remove-HNSNetwork -Verbose
Restart-Service Docker -Verbose
Verbose will display the result and it is optional in command