Home > Software design >  Kubernetes AKS ingress & MVC routing
Kubernetes AKS ingress & MVC routing

Time:07-04

I'm currently in the process of setting up a Kubernetes AKS cluster, mostly for learning purposes for myself and as a proof of concept. My goal is like this:

  • CI / CD with Azure DevOps
    • Every commit to the master-branch triggers an automated release to Kubernetes in the namespace "production"
    • Every commit to a non-master-branch triggers an automated release to Kubernetes in the namespace "development"
  • Kubernetes management
    • Resources are created via Helm
    • Azure DevOps task is used to deploy these resources in combination with variable-groups per environment
  • Application
    • Simple MVC app
    • Real database outside of Kubernetes, Azure SQL Server

Now, everything works smoothly, but I really struggle with the logical concept of "namespaces as environments". I know, this isn't a great idea in the first place, as for example, the development-environment could use up all the resources. But as I'm hosting stuff myself, I didn't want to create multiple clusters and I think for starters, having namespaces as environments is reasonable. My problem comes with the routing: my pretty naive approach is explained here Kubernetes Cross Namespace Ingress Network:

  • One ingress in the default namespace
  • Per environment, one ExternalName-Service in the default namespace with a suffix, targetting the CNAME of the service in the other namespaces
  • Per environment namespace, one ClusterIP service

The ingress looks like this:

  rules:
    - host: 09ab799fd5674c4594a5.centralus.aksapp.io
      http:
        paths:
          - path: /dev
            pathType: Prefix
            backend:
              service:
                name: {{include "helmakskubernetespoc-chart.fullname" .}}external{{ .Values.namespaces.development }}
                port:
                  number: {{ .Values.externalService.port }}
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ include "helmakskubernetespoc-chart.fullname" .}}external{{ .Values.namespaces.production }}
                port:
                  number: {{ .Values.externalService.port }}

My goal being, that no subroute targets production and the subroute /dev targeting development.

This works as long as there isn't a subroute aka for production. But as I've learned here Kubernetes Ingress non-root path 404 Not Found, the ingress takes the route and passes it 1:1 to the internal service. As my little MVC app doesn't know "/dev", I get a 404. So far so good, I can change this via annotation:

nginx.ingress.kubernetes.io/rewrite-target: /

Now, every call to /dev does in fact route to the correct app, BUT every click on a button routes back to production, as the initially added /dev is lost. Also, trying this one Ingress don't load the website assets (Css files and Javascript files), I have the same problem.

I'm wondering if I'm conceptually making a mistake: Ingress rules are, as far as I know, only working with subroutes, therefore I can't for example use a port. Is possibly ingress the wrong way to split the environments? As the MVC app either never receives the /dev or doesn't know what to do with it, I don't see an elegant solution for this problem?

CodePudding user response:

You probably wanna use different hosts per environment. Something like:

rules:
    - host: dev.09ab799fd5674c4594a5.centralus.aksapp.io
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{include "helmakskubernetespoc-chart.fullname" .}}external{{ .Values.namespaces.development }}
                port:
                  number: {{ .Values.externalService.port }}
    - host: 09ab799fd5674c4594a5.centralus.aksapp.io
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: {{ include "helmakskubernetespoc-chart.fullname" .}}external{{ .Values.namespaces.production }}
                port:
                  number: {{ .Values.externalService.port }}

CodePudding user response:

Max's solution is for sure the cleaner one, but as I wanted to have something in place to show without starting with the DNS stuff, I went with a workaround: I let the ingress forward the route 1:1 with the sub-path, but I configure ASP.Net Core MVC to see this route as the base-route. To do this, I set an environment variable named "AppSettings.AppBasePath" in the container and set it in the code via:

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            var config = Configuration.GetSection($"{AppSettings.SectionKey}:{nameof(AppSettings.AppBasePath)}").Get<string>();
            app.UsePathBase(config);
...

Adding this middleware does two things:

  • The PathBaseMiddleware moves the specified prefix from Path to PathBase if the request has the prefix. If the request does not have the prefix, the PathBaseMiddleware does nothing.
  • When a request takes a Map() branch in the pipeline, it moves the prefix from Path to PathBase

A very good description of PathBase is here https://andrewlock.net/understanding-pathbase-in-aspnetcore/.

  • Related