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]
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>
✌️
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).