Is it possible to have hostname based routing for MySQL in kubernetes?

2/17/2020

I have a scenario where I have multiple mysql servers running in different namespaces in single kubernetes cluster. All mysql servers belong to different departments.

My requirement is I should be able to connect to different mysql servers using hostname, i.e.,

mysqlServerA running in ServerA namespace should be reachable from outside the cluster using:

mysql -h mysqlServerA.mydomain.com -A

mysqlServerB running in ServerB namespace should be reachable from outside the cluster using:

mysql -h mysqlServerB.mydomain.com -A

and so on.

I have tried TCP based routing using config maps of Nginx Ingress controller, where I am routing traffic from clients to different mysql servers by assigning different port numbers:

for mysqlServerA:

mysql -h mysqlServer.mydomain.com -A -P 3301

for mysqlServerB:

mysql -h mysqlServer.mydomain.com -A -P 3302

this works perfectly. But I want to know if hostname based routing is possible or not, because I don't want separate load balancer for each mysql service.

Thanks

-- Ammar Taj
kubernetes
mysql
nginx-ingress
routing
traefik-ingress

1 Answer

2/19/2020

General info

I routing traffic by different port numbers

You are right, the reason for that is that connection to Mysql is done via TCP. That is why it is definitely not possible to have two simultaneous connections to two servers on the same IP:port.

Unlike HTTP, the TCP don't have headers that allows distinguishing the host the traffic shall be routed to. However, still there are at least two ways to achieve the functionality you'd like to achieve :) I'll describe that later.

I want to know if hostname based routing is possible or not I don't want separate load balancer for each mysql service.

K8s allows a few methods for service to be reachable outside the cluster (namely hostNetwork, hostPort, NodePort , LoadBalancer, Ingress )

The LoadBalancer is the simplest way to serve traffic on LoadBalancerIP:port ; however, due to TCP nature of connection you'll have to use one LoadBalancer per one mysql instance.

kind: Service 
apiVersion: v1 
metadata: 
  name: mysql 
spec: 
  type: LoadBalancer 
  ports: 
    - port: 3306 
  selector: 
    name: my-mysql

The NodePort looks good, but it allows you connecting only when you know port (which can be tedious work for clients)

Proposed solutions

If there are external IPs that route to one or more cluster nodes, Kubernetes Services can be exposed on those externalIPs. Traffic that ingresses into the cluster with the external IP (as destination IP), on the Service port, will be routed to one of the Service endpoints. externalIPs are not managed by Kubernetes and are the responsibility of the cluster administrator.

In the Service spec, externalIPs can be specified along with any of the ServiceTypes. In the example below, mysql-1 can be accessed by clients on 1.2.3.4:3306 (externalIP:port) and mysql-2 can be accessed by clients on 4.3.2.1:3306

$ cat stereo-mysql-3306.yaml 
apiVersion: v1
kind: Service
metadata:
  name: mysql-1234-inst-1
spec:
  selector:
    app: mysql-prod
  ports:
    - name: mysql
      protocol: TCP
      port: 3306
      targetPort: 3306
  externalIPs:
    - 1.2.3.4 
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-4321-inst-1
spec:
  selector:
    app: mysql-repl
  ports:
    - name: mysql
      protocol: TCP
      port: 3306
      targetPort: 3306
  externalIPs:
    - 4.3.2.1

Note: you need to have 1.2.3.4 and 4.3.2.1 assigned to your Nodes (and resolve mysqlServerA / mysqlserverB at mydomain.comto these IPs as well). I've tested that solution on my GKE cluster and it works :).

With that config all the requests for mysqlServerA.mydomain.com:3306 that resolves to 1.2.3.4 are going to be routed to the Endpoints for service mysql-1234-inst-1 with the app: mysql-prod selector, and mysqlServerA.mydomain.com:3306 will be served by app: mysql-repl.

Of course it is possible to split that config for 2 namespaces (one namespace - one mysql - one service per one namespace).

Taking into consideration that your mysql pods have ClusterIPs, it is possible to spawn additional VPN pod in cluster and connect to mysqls through it.

As a result, you can establish VPN connection and have access to all the cluster resources. That is very limited solution which requires establishing the VPN connection for anyone who needs access to mysql.

Good practice is to add a bastion server on top of that solution. That server will be responsible for providing access to cluster services via VPN.

Hope that helps.

-- Nick
Source: StackOverflow