How to make a request from a Flask API in a Kubernetes Namespace to another Flask API in another namespace internally (Using the DNS) using minikube

1/17/2020

I read that I needed to run another service in Namespace B which maps to Namespace A loadbalancer service. (You can see this service on my yaml b).

I don't want to use the external IPs given by the loadbalancers (If my scenario was replicated on the cloud using GCP/Azure/AWS/IBM cloud/OpenShift).

I deployed the next scenario using minikube v1.6.2 and Kubernetes v1.17:

Namespace a:

deployment/python-hello-world  READY: 1/1

service/python-hello-world  
TYPE: Loadbalancer CLUSTER-IP: 10.96.167.227 EXT-IP: <pending> PORTS: 80:31603/TCP

Namespace b:

deployment/python-hello-world  READY: 1/1

service/python-hello-world  
TYPE: Loadbalancer CLUSTER-IP: 10.96.67.10 EXT-IP: <pending> PORTS: 80:31595/TCP

service/connection 
TYPE: ExternalName CLUSTER-IP: <none> EXTERNAL-IP: python-hello-world-external.a.svc.cluster.local PORT: 31603/TCP

Kubernetes yaml a:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-hello-world
  namespace: a
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-hello-world
  template:
    metadata:
      labels:
        app: python-hello-world
    spec:
      containers:
      - name: server
        image: python-hello-world
        ports:
        - containerPort: 9090
        env:
        - name: PORT
          value: "9090"

---

apiVersion: v1
kind: Service
metadata:
  name: python-hello-world-external
  namespace: a
spec:
  type: LoadBalancer
  selector:
    app: python-hello-world
  ports:
  - name: http
    port: 80
    targetPort: 9090

Kubernetes yaml b:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: python-hello-world
  namespace: b
spec:
  replicas: 1
  selector:
    matchLabels:
      app: python-hello-world
  template:
    metadata:
      labels:
        app: python-hello-world
    spec:
      containers:
      - name: server
        image: python-hello-world
        ports:
        - containerPort: 9091
        env:
        - name: PORT
          value: "9091"

---

apiVersion: v1
kind: Service
metadata:
  name: python-hello-world-external
  namespace: b
spec:
  type: LoadBalancer
  selector:
    app: python-hello-world
  ports:
  - name: http
    port: 80
    targetPort: 9091

---

apiVersion: v1
kind: Service
metadata:
  name: connection
  namespace: b
spec:
  type: ExternalName
  externalName: python-hello-world-external.a.svc.cluster.local
  ports:
  - port: 31603

For accessing API A in my browser I ran in the terminal:

$ minikube service python-hello-world-external -n a
|-----------|-----------------------------|-------------|-----------------------------|
| NAMESPACE |            NAME             | TARGET PORT |             URL             |
|-----------|-----------------------------|-------------|-----------------------------|
| a         | python-hello-world-external | http        | http://192.168.39.196:31603 |
|-----------|-----------------------------|-------------|-----------------------------|

For accessing API B:

$ minikube service python-hello-world-external -n b
|-----------|-----------------------------|-------------|-----------------------------|
| NAMESPACE |            NAME             | TARGET PORT |             URL             |
|-----------|-----------------------------|-------------|-----------------------------|
| b         | python-hello-world-external | http        | http://192.168.39.196:31595 |
|-----------|-----------------------------|-------------|-----------------------------|

Flask API A:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Hello World A"

if __name__ == '__main__':
    app.run(debug=False, port=9090, host='0.0.0.0')

Flask API B:

import requests
from flask import Flask

app = Flask(__name__)

@app.route('/a')
def call_a():
    response = requests.get("I DONT KNOW WHAT TO PUT HERE")
    return response

if __name__ == '__main__':
    app.run(debug=False, port=9091, host='0.0.0.0') 
  1. Is it possible to send the request internally using minikube?
  2. If so, am I missing something in my configuration?
  3. If so, what URL do I write in the requests.get("")?
-- Arturo Avilés
docker
flask
kubernetes
minikube
python

2 Answers

1/20/2020

As guys already suggested in comments LoadBalancer isn't the best choice here. Actually it has completely different purpose, namely it serves for exposing your app externally which by the way will not work in Minikube the intended way (instead simple NodePort service is created). To use fully functional LoadBalancer you would need to use some cloud provider's services. All major players such as GCP, AWS or Azure support this functionality. Probably you decided to use LoadBalancer in order to have load balancer functionality implemented, but don't worry, simple ClusterIP also can do that. If you have let's say 3 fron-end Pods (usually managed by Deployment) and they need to communicate with 3 other Pods which are providing back-end-service, simple ClusterIP Service (you can omit the type as this is the default one) will do the job, including proper load balancing of requests to all underlying back-end Pods. As @David Maze suggested, familiarize with this article in official Kubernetes docs and you should grasp the whole concept easily.

Here you can find one of my old answers on a very similar topic. I guess you may find it useful as it explains in detail how communication between different sets of Pods, representing different microservices in your application architecture, should be done within Kubernetes Cluster.

As to communication between resources living in different namespaces, it can be also easily done in Kubernetes using short form suggested by @jordanm: <service_name>.<namespace> or fully qualified domain name <service-name>.<namespace-name>.svc.cluster.local (even more recommended). More details you can find here.

When you create a Service, it creates a corresponding DNS entry. This entry is of the form <service-name>.<namespace-name>.svc.cluster.local, which means that if a container just uses <service-name>, it will resolve to the service which is local to a namespace. This is useful for using the same configuration across multiple namespaces such as Development, Staging and Production. If you want to reach across namespaces, you need to use the fully qualified domain name (FQDN).

-- mario
Source: StackOverflow

4/10/2020

Here is 1 example of accessing 1 API service from 2nd API service but within same namespace. Maybe it can help.

Create your 2 API servers in Python in following manner, tried and tested on my Minikube. Host the 1st one in Kubernetes as a ClusterIp service. Host the 2nd one using NodePort to access outside Minikube cluster.

The 1st service is accessed using it's ClusterIp inside the 2nd service as shown in the Python code below, I tried to access using service name but did not work.


Here is 1st flask api server (.py file) which would be called from with in the 2nd flask api server:

import flask
from flask import Flask
import json
from flask import request


application = Flask(__name__)


@application.route('/api/v1/square', methods=['GET'])
def square():
    # sample api call: http://0.0.0.0:8003/api/v1/square?nb1=11
    nb1 = request.args.get('nb1')
    sq_nb1 = int(nb1)*int(nb1)
    return flask.jsonify(sq_nb1)


if __name__ == '__main__':
    application.run(debug=True, host="0.0.0.0", port=8003)

2nd API server (.py file) which calls the 1st API server hosted as ClusterIP in Kubernetes

import flask
from flask import Flask
import json
from flask import request
import requests


application = Flask(__name__)


@application.route('/api/v1/sumsq', methods=['GET'])
def sumSq():
    # sample api call: http://0.0.0.0:8002/api/v1/sumsq?nb1=111&nb2=222
    nb1 = request.args.get('nb1')
    nb2 = request.args.get('nb2')
    sum_val = int(nb1) + int(nb2)

    #  call square micro-service (Svc: 3-Internal-API-Server)
    api_url = "http://10.96.55.98/api/v1/square?nb1={v}".format(v=sum_val)
    # get IP using 'kubectl get svc -n <ns_name>' command

    res = requests.get(url=api_url)
    sum_sq_val = res.json()
    return flask.jsonify(sum_sq_val)


if __name__ == '__main__':
    application.run(debug=True, host="0.0.0.0", port=8002)
-- Anurag Trivedi
Source: StackOverflow