0

Software versions are given below.

In DigitalOcean's managed Kubernetes, I have a running nginx-ingress with two containers routed properly with my DNS and a jetstack cert-manager. One container is running a test app and the other my API. They work great. I am trying to connect my Angular client in a third container, but the Ingress routing is not working. It returns a 502 Bad Gateway. After trying many things, I now think the problem is related to a specific nginx-ingress configuration that is needed for SPAs (Single Page Applications).

NanoDano on DevDungeon remarks, "Create a config in /etc/nginx/conf.d/. ... The most important part for Angular in particular is to include the try_files line which will ensure that even if someone visits a URL directly, the server will rewrite it properly so the Angular app behaves properly." This "try_files" configuration is also mentioned by Andre Dublin on his GitHub page, and by Niklas Heidloff on his website.

In examples I've found on how to do this, the explanation is given from the perspective that you are doing a multistage build to combine your application, ingress, and the ingress configuration into one docker container via Dockerfile, such as this example by Lukas Marx on Malcoded. Or that you manually edit the configurations after ingress is started, which is suggested in this unresolved Stackflow.

In my situation, I already have Nginx-Ingress and I only need to dynamically add a configuration to properly route the Angular SPA. Using kubectl port-forward, I have confirmed the Angular app is served both from its pod and from its cluster IP service. It is working. When I attempt to connect to the app via Ingress, I get a 502 Bad Gateway error, which I've discussed on this StackOverflow.

A configuration can be added to an existing Nginx-Ingress using a ConfigMap as described on this StackOverflow. My Nginx-Ingress was created and configured following this example from this DigitalOcean tutorial, see Step 2, which installs with the following:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/do/deploy.yaml

I attempted to add the "try_files" configuration. First, I created this ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    server {
      listen 80;
      server_name pvgd.markwilx.tech;
      index index.html index.htm;
      root /home/node/app;

      location / {
        try_files $uri $uri/ /index.html =404;
      }

      include /etc/nginx/extra-conf.d/*.conf;
    }

Second, I extracted Ingress Deployment from the deploy.yaml file in the kubectl command above and modified it (six lines are added toward the bottom, and marked by comment):

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    helm.sh/chart: ingress-nginx-3.27.0
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.45.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/component: controller
  revisionHistoryLimit: 10
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: controller
          image: k8s.gcr.io/ingress-nginx/controller:v0.45.0@sha256:c4390c53f348c3bd4e60a5dd6a11c35799ae78c49388090140b9d72ccede1755
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown
          args:
            - /nginx-ingress-controller
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
            - --election-id=ingress-controller-leader
            - --ingress-class=nginx
            - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
            - --validating-webhook=:8443
            - --validating-webhook-certificate=/usr/local/certificates/cert
            - --validating-webhook-key=/usr/local/certificates/key
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 101
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: LD_PRELOAD
              value: /usr/local/lib/libmimalloc.so
          livenessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 5
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
            - name: webhook
              containerPort: 8443
              protocol: TCP
          volumeMounts:
            - name: webhook-cert
              mountPath: /usr/local/certificates/
              readOnly: true
            - name: nginx-config                    # ADDED THIS CONFIGURATION
              mountPath: /etc/nginx/nginx.conf      # ADDED THIS CONFIGURATION
              subPath: nginx.conf                   # ADDED THIS CONFIGURATION
          resources:
            requests:
              cpu: 100m
              memory: 90Mi
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
        - name: webhook-cert
          secret:
            secretName: ingress-nginx-admission
        - name: nginx-config             # ADDED THIS CONFIGURATION
          configMap:                     # ADDED THIS CONFIGURATION
            name: nginx-config           # ADDED THIS CONFIGURATION

Third, I reapplied this code executing it with:

kubectl apply -f ingress-deployment-mod.yaml

This does not appear to be working. Indeed, it seems to have broken everything else. If you have any suggestions as to what I might be doing wrong to create a route between the Internet and my Angular app via Nginx-Ingress, I'd appreciate your insights.

Thanks, Mark

Versions

  • node.js 10.19.0
  • npm 7.7.6
  • yarn 1.22.10
  • ng 11.2.2
  • docker 19.03.8
  • kubectl 1.20.1
  • doctl 1.57.0
  • kubernetes 1.20.2-do.0
  • helm 3.5.3
  • nginx controller 0.45.0
  • jetstack 1.3.0
MarkWilx
  • 21
  • 8

1 Answers1

1

You can't use ingress-nginx to serve actual files like that. Those files live in some other container you've built that has your application code in it. The Ingress system in Kubernetes is only for proxying and routing HTTP connections. The actual web serving, even if that also happens to use Nginx, has to be deployed separately.

coderanger
  • 52,400
  • 4
  • 52
  • 75
  • I have four applications that each run within their own pods. Two are very simple "hello world" type applications. The third is a Node/Express API. The fourth is an Angular client. The hello world apps and Angular client each one run instance. The API runs two instances. Each of the four has its own Cluster IP service. I have four URIs at xxx.example.com, which Ingress routes to each of the four services. The two hello world and one API service work perfect. The Angular app throws a 502 error. I think the "try_file" Ingress configuration may be the reason. – MarkWilx Apr 16 '21 at 13:41
  • `try_file` in the Ingress will literally never work. The filesystem there is the one inside the ingress controller, not your application containers. – coderanger Apr 17 '21 at 02:00
  • 1
    Oh, that is really interesting. I was under the impression that I needed to use `try_files` in Ingress to configure Ingress. Does it instead go into my application’s pod? What is an acceptable best practice for setting up an Angular pod in a k8s cluster? – MarkWilx Apr 18 '21 at 05:27