MongoDB Cluster In Kubernetes(3): Expose UserDB to Public
This is part3, we will expose the user database pods to the public so that Mongo client is able to access it.
MongoDB Ops Manager Series:
- Install MongoDB Ops Manager
- Create a UserDB ReplicaSet
- Expose UserDB to Public
- Openssl Generates Self-signed Certificates
- Enable UserDB TLS and Auth
So far, the user database can be accessed only inside the kubernetes cluster. The official blog’s approach is to expose the pods by NodePort: # Connect to a MongoDB Database Resource from Outside Kubernetes
I don’t know why the official blog recommend NodePort. Obviously, LoadBalancer is a better way to expose MongoDB. We will use LoadBalancer to expose userdb pods.
Create LoadBalancer Service
Configure userdb0service.yaml:
| |
Apply it:
After a while, the service userdb-0-svc-ext will be assigned a public IP. Then we can bind a domain name to the IP (optional). Suppose the domain name is userdb0.com.
Modify the above yaml and create another two services userdb-1-svc-ext and userdb-2-svc-ext to expose the two remaining pods.
MongoClient Connection Test, getaddrinfo ENOTFOUND Error
Now we can use mongo shell to access userdb:
| |
Looks something wrong. Why the prompt shows that we connect to a SECONDARY node? The default mongo readPreference is PRIMARY.
If you connect the DB through MongoDB Compass, the following error appears:
| |
After investigation, this article # Connecting from external sources to MongoDB replica set in Kubernetes fails with getaddrinfo ENOTFOUND error but standalone works answered my question:
When connecting to a replica set, the host:port pairs in the connection string are a seedlist.
The driver/client will attempt to connect to each host in the seedlist in turn until it gets a connection.
It runs the isMaster command to determine which node is primary, and to get a list of all replica set members.
Then is drops the original seedlist connection, and attempts to connect to each replica set member using the host and port information retrieved.
The host information returned by the isMaster usually matches the entry in rs.conf(), which are the hostnames used to initiate the replica set.
In your Kubernetes cluster, the nodes have internal hostnames that are used to initiate the replica set, but that your external clients can’t resolve.
In order to get this to work, you will need to have the mongod nodes isMaster command return a different set of hostnames depending on where the client request is coming from. This is similar to split-horizon DNS.
Look over the Deploy a Replica Set documentation for mongodb/kubernetes, and the replicaSetHorizons setting.
Verify by running rs.conf() in the mongo shell:
| |
Besides, mongo shell and MongoDB Compass have different behaviors when connecting to multiple hosts:
- mongo shell: show connect success but it might not be true: only one node is connected
- MongoDB Compass: connect failed The behavior is caused by different connection policies.
To solve the problem, we need to use
spec.connectivity.replicaSetHorizons
to specify the public address. However, to use this setting, spec.security.tls must be enabled first:
The bad news is that enable TLS is more complicated than I thought before. Here we use self-signed certificates to encrypt the transport layer communication. Four certificates are required: 1 CA certificate and 3 server certificates.
Let’s create the certificates step by step: Openssl Generates Self-signed Certificates