SK.
SK.

Reputation: 1510

Build Angular project in Docker - Environment specific

This is the fist time I am writing a Dockerfile. I have an application in Angular that connects to different backends (Spring Boot Rest services). I mean to say the spring boot application has been deployed in many different sites/locations. They all have different URLs. These Rest services are already there (I didn't write these rest services). I was getting CORS error when I tried to call these Rest services. So I had to us the below xyx.proxy.conf.json

Below is the configurations I have:

package.json

  "scripts": {
    "ng": "ng",
    "start:localhost": "ng serve --proxy-config localhost.proxy.conf.json",
    "start:site1qa": "ng serve --proxy-config site1qa.proxy.conf.json",
    "start:site2qa": "ng serve --proxy-config site2qa.proxy.conf.json",
    "start:site1prod": "ng serve --proxy-config site1qa.proxy.conf.json",
    "start:site2prod": "ng serve --proxy-config site2prod.proxy.conf.json",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e --proxy-config site1qa.proxy.conf.json"
  },

site1qa.proxy.conf.json Note: I must have to use proxy as I am getting CORS error

{
  "/RestWeb/*": {
    "target": "http://site1qa:8005",
    "secure": false,
    "changeOrigin": true,
    "logLevel": "debug"
  }
}

Angular Service.ts

  findAllByModelYear(): Observable<string[]> {
    return this.httpClient.get<string[]>('/RestWeb/model/findAllModelYearCodes');
  }

I tested the application using below commands in my local pointing to different backends like this:

npm run start:localhost
OR
npm run start:site1qa
OR
npm run start:site2prod

My current Dockerfile is like this:

# Stage 1: Compile and Build angular codebase

# Use official node image as the base image
FROM node:latest as build

# Set the working directory
WORKDIR /usr/local/app

# Add the source code to app
COPY ./ /usr/local/app/

# Install all the dependencies
RUN npm install

# Generate the build of the application
RUN npm run build

# Stage 2: Serve app with nginx server

# Use official nginx image as the base image
FROM nginx:latest

# Copy the build output to replace the default nginx contents.
COPY --from=build /usr/local/app/dist/my-projectt /usr/share/nginx/html

# Expose port 80
EXPOSE 80

Currently am I build like this:

docker build -t dockerangular .

And run like this:

docker run -it -p 8000:80 --name angulardocker1 my-first-app

Question:

How do I pass argument (while building and/or running the application), so I can connect to different sites (as mentioned in package.json i.e. using xyx.proxy.conf.json)

Upvotes: 3

Views: 1891

Answers (2)

Nederxus
Nederxus

Reputation: 83

Did you try to add the site1qa.proxy.conf.json to angular.json file like this?:

"serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "projectname:build",
            "proxyConfig": "./site1qa.proxy.conf.json"
          },

Upvotes: 0

Kenan G&#252;ler
Kenan G&#252;ler

Reputation: 2003

Since you seem to use NGINX as HTTP server, you can use the proxy_pass directive for the proxy purpose.

Also, you can create your own NGINX template configuration files, i.e. during bootstrap NGINX looks in the /etc/nginx/templates/ folder, and if any .template files are present, NGINX outputs the result of executing envsubst to /etc/nginx/conf.d, for example:

If you place a file in /etc/templates/default.conf.template, that contains variable references like this:

listen  ${MY_NGINX_PORT};

the output would go to /etc/nginx/conf.d/default.conf and look like this (let's say MY_NGINX_PORT is 8080):

listen 8080;

Ref: https://hub.docker.com/_/nginx

The solution:

Since NGINX's default configuration file, located in /etc/nginx/conf.d/default.conf, contains a very simple setup, you can create a template file and let NGINX use it to override the default conf file during bootstrap:

/etc/templates/default.conf.template --(would become after envsubst)--> /etc/nginx/conf.d/default.conf

  1. Create a file named nginx-default.conf.template and fill it with this content and then put it in your project root folder:
server {
    listen       80;
    server_name  _;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location /RestWeb/ {
        proxy_pass ${REMOTE_API_URL};
    }
}

Note the /RestWeb/ path and the ${REMOTE_API_URL} custom environment variable, i.e. any request starting with the /RestWeb/ path will be seamlessly proxied to the ${REMOTE_API_URL}, just like ng cli / webpack's proxy dev server does.

  1. Adjust your Dockerfile as follows:
...
# Add this line right before `EXPOSE 80`
COPY nginx-default.conf.template /etc/nginx/templates/default.conf.template
...
  1. Next time you pass the environment variable REMOTE_API_URL to docker run, its value will be used automatically when the NGINX server starts up, e.g.
docker run -p 8000:80 -e REMOTE_API_URL="http://site1qa:8005" my-first-app

or for a secure API:

docker run -p 8000:80 -e REMOTE_API_URL="https://site1prod:8005" my-first-app

A few tips

  • You don't need EXPOSE 80. It is only for documentation purposes, and does actually nothing.

  • Avoid using the latest version of Docker images, like nginx:latest or node:latest. The latest, as the name suggests, always points to the latest version.

    • With every docker build instruction the respective image with the latest version will be pulled from the Docker Hub, which, however, may not be what you actually want/need. Imagine, today the current version of node is 12 and after a few days/weeks version 13 comes out and with the tag node:latest you would pull it and use it under the hood. And that would be risky, because it could possibly break something
    • So it is better to stick to a particular version and increase the version yourself over time after you have tested the new version through (e.g. you can use node:14-alpine and nginx:1.19.9-alpine. See the Docker Hub for more tags)
  • These are equivalent:

    WORKDIR /usr/local/app
    COPY ./ /usr/local/app/
    
    WORKDIR /usr/local/app
    COPY ./ .
    

See: https://docs.docker.com/engine/reference/builder/#workdir

  • Modify your Dockerfile as follows to utilize the Docker layer caching mechanism to make subsequent Docker builds run faster
...
WORKDIR /usr/local/app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
...

See: Why COPY package*.json ./ precedes COPY . .?

Upvotes: 2

Related Questions