I have a springboot app which I want to deploy on Kubernetes (I'm using minikube) with a custom context path taken from the environment variables.
I've compiled an app.war file. exported an environment variable in Linux as follow:
export SERVER_SERVLET_CONTEXT_PATH=/app
And then started my app on my machine as follow:
java -jar app.war --server.servlet.context-path=$(printenv CONTEXT_PATH)
and it works as expected, I can access my app throw browser using the url localhost:8080/app/
I want to achieve the same thing on minikube so I prepared those config files:
Dockerfile:
FROM openjdk:8
ADD app.war app.war
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.war", "--server.servlet.context-path=$(printenv CONTEXT_PATH)"]
deployment config file:
apiVersion: apps/v1
kind: Deployment
metadata:
name: esse-deployment-1
labels:
app: esse-1
spec:
replicas: 1
selector:
matchLabels:
app: esse-1
template:
metadata:
labels:
app: esse-1
spec:
containers:
- image: mysql:5.7
name: esse-datasource
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: root
- image: esse-application
name: esse-app
imagePullPolicy: Never
ports:
- containerPort: 8080
env:
- name: server.servlet.context-path
value: /esse-1
volumes:
- name: esse-1-mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-persistent-storage-claim
---
apiVersion: v1
kind: Service
metadata:
name: esse-service-1
labels:
app: esse-1
spec:
selector:
app: esse-1
ports:
- protocol: TCP
port: 8080
targetPort: 8080
type: NodePort
However, the java container inside the pod fails to start and here's the exception is thrown by spring:
Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: ContextPath must start with '/' and not end with '/'
Looks like what you want is SERVER_SERVLET_CONTEXT_PATH
variable defined in your container spec:
apiVersion: apps/v1
kind: Deployment
metadata:
name: esse-deployment-1
labels:
app: esse-1
spec:
replicas: 1
selector:
matchLabels:
app: esse-1
template:
metadata:
labels:
app: esse-1
spec:
containers:
- image: mysql:5.7
name: esse-datasource
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: root
- image: esse-application
name: esse-app
imagePullPolicy: Never
ports:
- containerPort: 8080
env:
- name: SERVER_SERVLET_CONTEXT_PATH <== HERE
value: /esse-1
volumes:
- name: esse-1-mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-persistent-storage-claim
Note that in your Pod spec you are using /esse-1
while on your local setup you have /app
Make use of configmaps.
The configmap will holds application.properties of your springboot application.
---
apiVersion: v1
kind: ConfigMap
metadata:
name: esse-config
data:
application-dev.properties: |
spring.application.name=my-esse-service
server.port=8080
server.servlet.context-path=/esse-1
NOTE: server.servlet.context-path=/esse-1
will override context-path of your springboot application.
Now refer this configmap in your deployment yaml.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: esse-deployment-1
labels:
app: esse-1
spec:
replicas: 1
selector:
matchLabels:
app: esse-1
template:
metadata:
labels:
app: esse-1
spec:
containers:
- image: mysql:5.7
name: esse-datasource
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: root
- image: esse-application
name: esse-app
imagePullPolicy: Never
command: [ "java", "-jar", "app.war", "--spring.config.additional-location=/config/application-dev.properties" ]
ports:
- containerPort: 8080
volumeMounts:
- name: esse-application-config
mountPath: "/config"
readOnly: true
volumes:
- name: esse-application-config
configMap:
name: esse-config
items:
- key: application-dev.properties
path: application-dev.properties
- name: esse-1-mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-persistent-storage-claim
NOTE: Here we are mounting configmap inside your springboot application container at /config
folder. Also --spring.config.additional-location=/config/application-dev.properties
is pointing to the application.properties config file.
In future if you want to add any new config or update the value of existing config that just make the change in configmap and kubectl apply
it. Then to reflect those new config changes, just scale down and scale up the deployment.
Hope this helps.
Finally, I found a solution.
I configured my application to startup with a value for the context path taken from the environment variables by adding this line inside my application.properties:
server.servlet.context-path=${ESSE_APPLICATION_CONTEXT}
And the rest remains as it was, means I'm giving the value of the variable ESSE_APPLICATION_CONTEXT throw the config
env:
- name: ESSE_APPLICATION_CONTEXT
value: /esse-1
And then starting the application without the --server.servlet.context-path parameeter, which means like that:
java -jar app.war
NOTE: as pointed by @mchawre's answer, it's also possible to make use of ConfigMap as documented in Kubernetes docs.