Accessing service from another service in Kubernetes

8/6/2020

I have a spring boot service which I want to connect to a mongodb in Kubernetes. So far, I have built the docker image of the app and created a Kubernetes deployment, specifying both the images for the app and mongodb in the same deployment YAML file. Also, I have created a service YAML for the app and it works fine.

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: springbootmongodb
  labels:
    app: springbootmongodb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: springbootmongodb
  template:
    metadata:
      labels:
        app: springbootmongodb
    spec:
      containers:
      - name: springbootmongodb
        image: mytest/springbootmongodb
      - name: mongo
        image: mongo

--service.yaml

apiVersion: v1
kind: Service
metadata:
  name: springbootmongodb
  labels:
    app: springbootmongodb
spec:
  type: ClusterIP
  ports:
  - name: 8080-8080
    port: 8080
    targetPort: 8080
    protocol: TCP
  selector:
    app: springbootmongodb

port forward command:

kubectl port-forward svc/springbootmongodb 8080:8080

Everything works with the above config.

Now, I want to create separate deployments for the app and created two deployments and service YAMLs, but it doesn't work. Can someone please help me?

app-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: springbootmongodb
  labels:
    app: springbootmongodb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: springbootmongodb
  template:
    metadata:
      labels:
        app: springbootmongodb
    spec:
      containers:
      - name: springbootmongodb
        image: mytest/springbootmongodb

mongo-deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongodb
  labels:
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      labels:
        app: mongo
    spec:
      containers:
        - name: mongo
          image: mongo
          ports:
            - containerPort: 27017

appservice.yml

apiVersion: v1
kind: Service
metadata:
  name: springbootmongodb
  labels:
    app: springbootmongodb
spec:
  type: NodePort
  ports:
  - name: 8080-8080
    port: 8080
    targetPort: 8080
    protocol: TCP
  selector:
    app: springbootmongodb

mongoservice.yml

apiVersion: v1
kind: Service
metadata:
  name: mongodb
  labels:
    app: mongo
spec:
  type: ClusterIP
  ports:
    - port: 27017
      targetPort: 27017
      protocol: TCP
  selector:
    app: mongo

I see all created objects:

kubectl get pods NAME READY STATUS RESTARTS AGE mongodb-686dd5cb7f-dr9hq 1/1 Running 0 3m12s springbootmongodb-7ccbc488fb-vtgw5 1/1 Running 0 115s

kubectl get all

NAME                                     READY   STATUS    RESTARTS   AGE
pod/mongodb-686dd5cb7f-dr9hq             1/1     Running   0          3m16s
pod/springbootmongodb-7ccbc488fb-vtgw5   1/1     Running   0          119s

NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/kubernetes          ClusterIP   10.96.0.1       <none>        443/TCP          59d
service/mongodb             ClusterIP   10.111.83.192   <none>        27017/TCP        2m35s
service/springbootmongodb   NodePort    10.103.18.137   <none>        8080:31015/TCP   8s

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mongodb             1/1     1            1           3m16s
deployment.apps/springbootmongodb   1/1     1            1           119s

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/mongodb-686dd5cb7f             1         1         1       3m16s
replicaset.apps/springbootmongodb-7ccbc488fb   1         1         1       119s

Error logs for service:

2020-08-06 23:19:01.526  INFO 1 --- [nio-8080-exec-8] org.mongodb.driver.cluster               : Cluster description not yet available. Waiting for 30000 ms before timing out
2020-08-06 23:19:07.048 ERROR 1 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Timed out after 30000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: Connection refused (Connection refused)}}]; nested exception is com.mongodb.MongoTimeoutException: Timed out after 30000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: Connection refused (Connection refused)}}]] with root cause

com.mongodb.MongoTimeoutException: Timed out after 30000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27017, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: Connection refused (Connection refused)}}]
        at com.mongodb.internal.connection.BaseCluster.getDescription(BaseCluster.java:179) ~[mongodb-driver-core-3.8.2.jar!/:na]
        at com.mongodb.internal.connection.SingleServerCluster.getDescription(SingleServerCluster.java:41) ~[mongodb-driver-core-3.8.2.jar!/:na]
        at com.mongodb.client.internal.MongoClientDelegate.getConnectedClusterDescription(MongoClientDelegate.java:136) ~[mongodb-driver-3.8.2.jar!/:na]
        at com.mongodb.client.internal.MongoClientDelegate.createClientSession(MongoClientDelegate.java:94) ~[mongodb-driver-3.8.2.jar!/:na]
        at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.getClientSession(MongoClientDelegate.java:249) ~[mongodb-driver-3.8.2.jar!/:na]
        at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:172) ~[mongodb-driver-3.8.2.jar!/:na]
        at com.mongodb.client.internal.MongoIterableImpl.execute(MongoIterableImpl.java:132) ~[mongodb-driver-3.8.2.jar!/:na]
        at com.mongodb.client.internal.MongoIterableImpl.iterator(MongoIterableImpl.java:86) ~[mongodb-driver-3.8.2.jar!/:na]
        at org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:2643) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2380) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:2363) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:820) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:360) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:194) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:51) ~[spring-data-mongodb-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_212]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_212]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_212]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_212]
        at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.2.RELEASE.jar!/:5.1.2.RELEASE]
        at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.2.RELEASE.jar!/:2.1.2.RELEASE]
-- user1318369
kubernetes
mongodb
spring-boot

2 Answers

8/7/2020

It looks like you need to change the config of your Springboot app to use the mongodb endpoint: mongodb:27017 rather than localhost:27017❓🤔. Since mongo is not running on the same container/pod anymore.

In the application.properties file something like this:

spring.data.mongodb.uri=mongodb://mongodb:27017/<dbname>

✌️

-- Rico
Source: StackOverflow

7/8/2021

Like Rico already mentioned you have the wrong address in the configuration file.

Please be careful, there are other issues, you might want to solve as well.

Rather than Deployment, you should use Kubernetes StatefulSet because they were designed for workloads with the state in mind, and they have better state and identity management which means better management for your stateful data.

The main problem will happen when you create more than 1 replica for your MongoDB deployment, All deployments will point to the same PV (Persistent Volume) which might cause data inconsistencies due to many workloads changing the same volume.

StatefulSet will create a PV per replica. then you will have two databases with different data still with inconsistency problems.

If you are building an enterprise app then it's good to implement your database outside Kubernetes; that way your Kubernetes cluster becomes ephemeral and contains no state, and you can spin another cluster if something happens to your cluster.

If you want to implement a DB for an enterprise app inside a k8s cluster, you still have to implement it using multiple replicas with Master/Follower architecture and replication manager.

MongoDB implemented an operator that helps manage this for you Here is a Link.

An additional thing worth mentioning is that you are not using environment variables to pass credentials/addresses to your database. It is generally good practice to separate configuration from workloads that way you don't have to change deployment files for every environment you have (dev staging prod for example).

-- Dawid Świtoń-Maniakowski
Source: StackOverflow