Routing issues on Angular + Kubernetes + Nginx

1/17/2020

In a project that I am currently working I am facing a lot of issues related to routing. I think it is a configuration issue and anyone having similar experience can share his experiences..

Project Overview:

  • Angular SPA Project
  • Docker Container
  • deployed to Kubernetes Cluster (on Google cloud)
  • running on nginx server.
  • The Code repository and the build process is in Azure Git Repo

    Project builds and runs perfectly on the local machine.

    Project Details:

    1. The Angular SPA Project Details:

  • simple angular project with plain vanilla Angular Bootstrap

  • employs the RoutingModule to navigate between the different forms.
  • Has 2 forms (one is secured and the other open, details later)
  • structure

             Devops/
                 src/
                      index.html
                      app/
                          app-routing.module.ts
                          app.component.ts
                          app.component.html
                          home/
                            home.component.html
                            home.component.ts
                          dashboard/
                            dashboard.component.html
                            dashboard.component.ts

    In the app-routing.module.ts all the routes are set up where all the paths are mapped to respective components. Also, the default is set to the Home component.
    As per standard setup, the index.html has the following tag and value defined by default
    <base href="/">
    During the build process, The namespace and the app name is stored in a environment variable (ENVVALUES).


    2. Kubernetes Details

    In kubernetes, lets say, the domain is www.xcompany.com, my namespace is MaxxD and the Deployed app is Devops, then the uri to the hosted site will be www.xcompany.com/maxxd/devops. It is configured that way. Also, as per the Kubernetes standards, a health check webpage should also be present to determine the availability and health of the pod. The path to the health page is: www.xcompany.com/maxxd/devops/api/health


    3. NGinx Details

    The nginx related settings are stored in a configuration file called default.conf Following are the contents:

    server {
        listen       80;
        server_name  localhost;
    
        location ${ENVVALUES}/ {
            alias   /usr/share/nginx/html/;
            index  index.html index.htm;
    
            #try_files $uri $uri/ index.html
        }
    
        location  /api/health/ {
            alias   /usr/share/nginx/html/health/;
            index  health.html;
        }
    
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    
    }
    

    As we can see the default domain name is appended with the ENVVALUES, which contains the information of the namespace and the App Name ("MaxxD/Devops")


    3. Docker Details

  • The Docker Build is a multi-stage one, comprising of 2 steps.

  • In the first step, files are copied over from the Code repo to the build server and then ng build is run
  • In the second step, then the Dist files are moved to a nginx image where the nginx conf file is modified and then started.

    Following is the content of the DockerFile:

    ### STAGE 1: Build ###
    # We label our stage as ‘builder’
    FROM node as builder
    
    # Create the working folder
    RUN mkdir -p /usr/src/app
    
    # We define our current path inside of the container
    WORKDIR /usr/src/app
    
    # We copy both the package.json and the package.lock.json into the image
    COPY package*.json /usr/src/app/
    
    # run npm install to install Angular CLI
    RUN npm install @angular/cli -g
    
    # run npm install to install all dependencies of the project
    RUN npm ci
    
    # copy all file from repo to build machine
    COPY . /usr/src/app/
    
    ## Build the angular app in production mode and store the artifacts in dist folder
    RUN ng build --prod
    
    ### STAGE 2: Setup ###
    FROM nginx
    # Expose the port 80 of the running container 
    EXPOSE 80
    
    ## Copy our default nginx config
    COPY conf/default.conf /etc/nginx/conf.d/
    
    ## Remove default nginx website
    RUN rm -rf /usr/share/nginx/html/*
    
    ## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder
    COPY --from=builder /usr/src/app/dist/devops /usr/share/nginx/html
    
    ## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder
    COPY --from=builder /usr/src/app/health /usr/share/nginx/html/health
    
    
    CMD /bin/bash -c "envsubst < /etc/nginx/conf.d/default.conf > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'"

    In the last step the nginx conf file is modified and the ENVVALUES is transposed with the "{namespace/AppName}" value.


    The Issue

    After the deployment to the kubernetes cluster we saw that the index.html was loading but with a blank screen. On the chrome debug window we saw the supporting files like .css, .js and images were all throwing 404s even though the home pae was showing the correct uri.
    After losing considerable time in trouble shooting, we figured that it was a routing issue which needed the base href to be set with "./" instead of the default value of "/".
    The site became available after this and all the components were navigable.

    We had to secure the site from unauthorized access and we used the Okta Authorization components for that.
    All the required dependencies for Okta were installed and and the configurations completed.

    When running locally we found that after a successful authentication, the redirection was not working properly. The implicit/callback which is mapped to a "OktaCallbackComponent" was not getting peroperly loaded.

    It was showing an error and it was related to the value set in the base href tag again.
    We changed it back to "/" and the full flow of the Authntication and the redirection to the secured sections were working absolutely fine.
    But now after a new deployment, the site was again throwing up the same routing related issues.

    So the way it stands, if we set the Base href value to "/", then the site works fine locally but not in Kubernetes.
    But, if set with "./", it loads the unsecured files locally, doesnt load the secured ones (due to the Okta implicit/callback component as discussed above) where as in Kubernetes, it loads the unsecured files but as like the local instance, after the successful authentication, the redirection of Okta implcit/callback does not work successfully.


  • I know it has to do with nginx routing and how the server handles the urls and loads the appropriate sections.

  • But dont have an idea about what to chane to get the desired results.
  • I tried many combinations of ideas shared in the internet, but none worked so far.. Shouldn't be too complicated because most are using standard implementation guidelines.
  • My primary line of resolution is to find a way to run the website with the base href set to the default value ("/")

  • Another Observation: Even when the routing seems to be working, it is navigable only through the root. That is "www.xcompany.com/MaxxD/DevOps/" whic automatically redirects to the "www.xcompany.com/MaxxD/DevOps/home" based on the route rules defined. But if I put "www.xcompany.com/MaxxD/DevOps/home" directly on the address bar then I get a 404..

    Any help is much appreciated

-- MaxxD
angular
docker
kubernetes
nginx
okta

0 Answers