MongoDB Ops Manager change URL root path - mongodb

I installed MongoDB Ops Manager in our on-prem kubernetes cluster following these steps: https://docs.mongodb.com/kubernetes-operator/stable/tutorial/deploy-om-container/.
It seems to work and it is accessible (at http://localhost:8080/) when doing:
kubectl -n mongodb port-forward service/ops-manager-svc 8080:8080
The problem is that I'd like to access the service it provides via our HAProxy ingress, not by port forwarding.
If I configure the ingress as follows:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: mongo-ops-manager-ingress
namespace: mongodb
spec:
rules:
- http:
paths:
- path: /mongodb
backend:
serviceName: ops-manager-svc
servicePort: 8080
I have to access the service at the http://<public ip>/mongodb URL, which confuses the Ops Manager since it looks in the root for its resources, not in /mongodb in order for haproxy to pick up the requests and route them accordingly.
I have two other applications set up this way (our application and Grafana) and both work fine.
Now to the question:
Is there a configuration directive (apart from mms.centralUrl which doesn't seem to work) that allows us to modify the root path of the Ops Manager application?
I'm looking for something similar to the root_url property of Grafana: https://grafana.com/tutorials/run-grafana-behind-a-proxy/#1

I do not use Ops Manager myself but looking at the source I see some asset references like this:
<img src="{{centralUrl}}/static/images/icon-alert.png" width="30"
And some references like this:
<img class="not-found-image" src="/static/images/sadface.gif" />
... suggesting that centralUrl is the right parameter to set but perhaps it is not respected everywhere, specifically for /static assets.
I suggest taking a closer look at your running installation to see if the path specified in centralUrl is respected anywhere at all, if it is then that would be consistent with it being partially respected.
You can try setting up aliasing/URL rewriting in your web server to alias /static to /mongodb/static.
Then, regardless of outcome, this entire issue seems appropriate to escalate to MongoDB via the official support channels if you are a paying customer, otherwise I would inquire at https://developer.mongodb.com/community/forums/.

Related

Kubernetes sub-domain provisioning

I'm working on a SaaS app that will be running in Kubernetes. We're using a Helm chart that deploys all the components into the cluster (for simplicity's sake let's assume it's a frontend service, a backend and a database). App architecture is multi-tenant (we have a single instance of each service that are being shared by all tenants) and we would like to keep it that way. What I'm currently struggling with and would like to ask for advice/best practice on is how does one go about automating the provisioning of custom sub-domains for the tenants?
Imagine the app is hosted at exampleapp.com.
A brand new customer comes and registers a new organisation some-company. At that moment, in addition to creating new tenant in the system, I would also like to provision a new subdomain some-company.exampleapp.com. I would like this provisioning to be done automatically and not require any manual intervention.
What options do I have for implementing automated sub-domain provisioning in Kubernetes?
How does our (exampleapp.com) domain registrar/nameserver provider fit into the solution?
Does it have to provide an API for dynamic DNS record creation/modification?
I appreciate that the questions I'm asking are quite broad so I'm not expecting anything more than a high-level conceptual answer or pointers to some services/libraries/tools that might help me achieve this.
Note: Since this is more of a theoretical question, I'll give you some points from a Kubernetes Engineer, I divided your question in blocks to ease the understanding.
About your multi-tenancy architecture:
Keeping "it that way" is achievable. It simplifies the Kubernetes structure, on the other hand it relies more on your app.
Question 1:
Imagine the app is hosted at exampleapp.com. A brand new customer comes and registers a new organisation some-company. At that moment, in addition to creating new tenant in the system, I would also like to provision a new subdomain some-company.exampleapp.com. I would like this provisioning to be done automatically and not require any manual intervention.
Suggestion:
For that, you will have to give your app admin privileges and the tools required for it to add Ingress Rules entries to your Ingress when a new client is added. A script using kubectl patch is the simpler solution from my viewpoint.
For this approach I suggest installing the Nginx Ingress Controller for it's versatility.
Here is an Example:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: <ingress-name>
spec:
rules:
- host: client1.exampleapp.com
http:
paths:
- path: /client1
backend:
serviceName: <main-service>
servicePort: <main-service-port>
- host: client2.exampleapp.com
http:
paths:
- path: /client2
backend:
serviceName: <main-service>
servicePort: <main-service-port>
And here is the one-liner command using kubectl patch on how to add new rules:
kubectl patch ingress demo-ingress --type "json" -p '[{"op":"add","path":"/spec/rules/-","value":{"host":"client3.exampleapp.com","http":{"paths":[{"path":"/client3","backend":{"serviceName":"main-service","servicePort":80}}]}}}]'
POC:
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
demo-ingress client1.exampleapp.com,client2.exampleapp.com 192.168.39.39 80 15m
$ kubectl patch ingress demo-ingress --type "json" -p '[{"op":"add","path":"/spec/rules/-","value":{"host":"client3.exampleapp.com","http":{"paths":[{"path":"/client3","backend":{"serviceName":"main-service","servicePort":80}}]}}}]'
ingress.extensions/demo-ingress patched
$ kubectl describe ingress demo-ingress
Rules:
Host Path Backends
---- ---- --------
client1.exampleapp.com
/client1 main-service:80 (<none>)
client2.exampleapp.com
/client2 main-service:80 (<none>)
client3.exampleapp.com
/client3 main-service:80 (<none>)
This rule redirects the traffic incoming from the subdomains to subpaths inside your main app.
Also, to add TLS handling, refer to: Nginx Ingress TLS Guide
Question2 :
How does our (exampleapp.com) domain registrar/nameserver provider fit into the solution? Does it have to provide an API for dynamic DNS record creation/modification?
Suggestion:
I believe you already has something similar, but you need a wildcard record in your DNS to point *.example.app to the IP of the ingress, I don't believe you need anything more than that, because it redirects all to the ingress and the ingress forwards it internally.
Question 3:
If there are some strong arguments why multi-tenancy + Kubernetes don't go along very well, those are welcome as well.
Opinion:
I don't see any major reason why it would be a problem. You just have, once again, to adequate your app to handle scaling because I believe in the long run you would want your app to be able to scale to multi-pod structure to provide elastic availability.
These are my 2 cents to your question, I hope it helps you!

Kubernetes (AKS) : Expose multiple ports of different service to common load balancer

I am setting up Kubernetes cluster on Azure (using AKS) to host Elasticsearch, Kibana, custom api, UI, nginx, etc.
As I don't want separate public IP per service, I need a way to setup a common load balancer/Ingress and then just add the port numbers to there and setup routing.
I tried using the approach mentioned in this stackoverflow question - How to expose multiple port using a load balancer services in kubernetes but didn't work out.
As there are technology clients connecting to my cluster, I need to have service per technology.
Basically I need to expose 9200, 5601, 80 - all on same IP but on accessing the IP with port, user must be re-directed to appropriate technology service.
Below is sample configuration for what am looking for.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myingress
spec:
rules:
- host: myurl.domain.com
http:
paths:
- path: /
backend:
serviceName: elasticsearch
servicePort: 9200
- path: /
backend:
serviceName: kibana
servicePort: 5602
Any thoughts?
With your issue, the ingress is what you want. You can create all your services as you want. And expose the ports for your service. Then create the ingress with a public IP and create the ingress route that routes the access from the ingress to your backend services.
Take a look at the example in Create an ingress controller in Azure Kubernetes Service (AKS). It will show you what the steps need to be done. And if you have more questions please let me know.
I just finished doing this on a mail server project using HAProxy ingress controller (https://github.com/helm/charts/tree/master/incubator/haproxy-ingress) in TCP mode. Works a treat. Working config can be found at https://github.com/funkypenguin/docker-mailserver/blob/fa9bd9c9ed9b66aa6ee1c36ca19a73c558682f24/helm-chart/docker-mailserver/values.yaml#L300
D
Sorry i posted another similar question. Finally was able to solve with issue with simple tagging.
No extra tools/code required.
Please refer my post for answer : Kubernetes: Expose multiple services internally & externally

cert-manager is creating new ingress with acme responder instead of modifying the existing

I'm trying to use cert-manager to issue a certificate via LetsEncrypt.
I've followed through with the steps here http://docs.cert-manager.io/en/latest/getting-started/index.html
However, my existing ingress is not being modified (I assume it needs to modify it due to adding a path for .well-known/....
Instead I see an ingress created for this with a name like: cm-acme-http-solver-kgpz6? Which is rather confusing?
If I get the yaml for that ingress I see the following for rules:
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: cm-acme-http-solver-2dd97
servicePort: 8089
path: /.well-known/acme-challenge/2T2D_XK1-zIJJ9_f2ANlwR-AcNTm3-WenOExNpmUytY
How exactly is this meant to work? As the documentation seems rather sparse.
The record you are seeing is for the challenge. It needs to succeed to configure the cert. If you are using "example.com" as the domain then it will not succeed. To get this to work you'll need to configure a DNS record for a valid hostname so that LetsEncrypt can resolve the domain and complete the check.
Usually you will not even see the challenge ingress resource. It usually runs the challenge and then removes itself as long as DNS and the hostname have been configured correctly. After it is removed the resource you created will get loaded into your ingress controller.
There are a few ingress controllers that do not support multiple ingress resources per hostname. They will load one ingress resource and ignore the other, so this is sort of a workaround/fix to the issue.

ingress-nginx - create one ingress per host? Or combine many hosts into one ingress and reload?

I'm building a service where users can build web apps - these apps will be hosted under a virtual DNS name *.laska.io
For example, if Tom and Jerry both built an app, they'd have it hosted under:
tom.laska.io
jerry.laska.io
Now, suppose I have 1000 users. Should I create one big ingress that looks like this?
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: tom.laska.io
http:
paths:
- backend:
serviceName: nginx-service
servicePort: 80
- host: jerry.laska.io
http:
paths:
- backend:
serviceName: nginx-service
servicePort: 80
...and so forth
I'm worried about downtime - if I have an app with websockets for example. Also the file will become huge with 1000 users. Will reloading the ingress go fast enough? Also, how should I reload it?
A second option in my mind is to simply create one ingress for every web app. My worry about that is, can ingress-nginx handle many ingresses? Or is this an anti-pattern?
Which one is better?
You can create one ingress resource for each web app. If you search the official public charts repo, you'll see that many of the charts define an ingress resource within them. It's normal for each app to define its own ingress resource.
It's worth being clear that an ingress resource is just a definition of a routing rule. (It doesn't add an extra ingress controller or any other extra runtime component.) So it makes a lot of sense for an app to define its own routing rule.
The example you've given has all the ingress routing in one resource definition. This approach is easy to grasp and makes a lot of sense when you've got several related applications as then you might want to see their routing configuration together. You can see this also in the fanout ingress example in the kubernetes docs.
I can't see any performance concerns with defining the rules separately in distinct resources. The ingress controller will listen for new rules and update its configuration only when a new rule is created so there shouldn't be a problem with reading the resources. And I'd expect the combined vs separated options to result in the same routing rules being set in the background in nginx.
The most common pattern in the official charts repo is that the chart for each app defines its ingress resource and also exposes it through the values.yaml so that users can choose to enable or customise it as they wish. You can then compose multiple charts together and configure the rules for each in the relevant section of the values.yaml. (Here's an example I've worked on that does this with wildcard dns.) Or you can deploy each app separately under its own helm release.
An ingress ressource is just a rule. It's better to split your ingress ressources in different files and just reapply the ressources that need change.
I never noticed a downtime when applying a ressource.

Kubernetes services for different application tracks

I'm working on an application deployment environment using Kubernetes where I want to be able to spin up copies of my entire application stack based on a Git reference for the primarily web application, e.g. "master" and "my-topic-branch". I want these copies of the app stack to coexist in the same cluster. I can create Kubernetes services, replication controllers, and pods that use a "gitRef" label to isolate the stacks from each other, but some of the pods in the stack depend on each other (via Kubernetes services), and I don't see an easy, clean way to restrict the services that are exposed to a pod.
There a couple ways to achieve it that I can think of, but neither are ideal:
Put each stack in a separate Kubernetes namespace. This provides the cleanest isolation, in that there are no resource name conflicts and the applications can have the DNS hostnames for the services they depend on hardcoded, but seems to violate what the documentation says about namespaces†:
It is not necessary to use multiple namespaces just to separate slightly different resources, such as different versions of the same software: use labels to distinguish resources within the same namespace.
This makes sense, as putting the app stacks in different resources would negate all the usefulness of label selectors. I'd just name the namespace with the Git reference and all the other resources wouldn't need to be filtered at all.
Create a copy of each service for each copy of the application stack, e.g. "mysql-master" and "mysql-my-topic-branch". This has the advantage that all the app stacks can coexist in the same namespace, but the disadvantage of not being able to hardcode the DNS hostname for the service in the applications that need them, e.g. having a web app target the hostname "mysql" regardless of which instance of the MySQL Kubernetes service it actually resolves to. I would need to use some mechanism of injecting the correct hostname into the pods or having them figure it out for themselves somehow.
Essentially what I want is a way of telling Kubernetes, "Expose this service's hostname only to pods with the given labels, and expose it to them with the given hostname" for a specific service. This would allow me to use the second approach without having to have application-level logic for determining the correct hostname to use.
What's the best way to achieve what I'm after?
[†] http://kubernetes.io/v1.1/docs/user-guide/namespaces.html
The documentation on putting different versions in different namespaces is a bit incorrect I think. It is actually the point of namespaces to separate things completely like this. You should put a complete version of each "track" or deployment stage of your app into its own namespace.
You can then use hardcoded service names - "http://myservice/" - as the DNS will resolve on default to the local namespace.
For ingresses I have copied my answer here from the GitHub issue on cross-namespace ingresses.
You should use the approach that our group is using for Ingresses.
Think of an Ingress not as much as a LoadBalancer but just a document specifying some mappings between URLs and services within the same namespace.
An example, from a real document we use:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress
namespace: dev-1
spec:
rules:
- host: api-gateway-dev-1.faceit.com
http:
paths:
- backend:
serviceName: api-gateway
servicePort: 80
path: /
- host: api-shop-dev-1.faceit.com
http:
paths:
- backend:
serviceName: api-shop
servicePort: 80
path: /
- host: api-search-dev-1.faceit.com
http:
paths:
- backend:
serviceName: api-search
servicePort: 8080
path: /
tls:
- hosts:
- api-gateway-dev-1.faceit.com
- api-search-dev-1.faceit.com
- api-shop-dev-1.faceit.com
secretName: faceitssl
We make one of these for each of our namespaces for each track.
Then, we have a single namespace with an Ingress Controller which runs automatically configured NGINX pods. Another AWS Load balancer points to these pods which run on a NodePort using a DaemonSet to run at most and at least one on every node in our cluster.
As such, the traffic is then routed:
Internet -> AWS ELB -> NGINX (on node) -> Pod
We keep the isolation between namespaces while using Ingresses as they were intended. It's not correct or even sensible to use one ingress to hit multiple namespaces. It just doesn't make sense, given how they are designed. The solution is to use one ingress per each namespace with a cluster-scope ingress controller which actually does the routing.
All an Ingress is to Kubernetes is an object with some data on it. It's up to the Ingress Controller to do the routing.
See the document here for more info on Ingress Controllers.