Skip to content
Snippets Groups Projects
Select Git revision
  • branchName
  • master default
  • quadriga
  • 3.0
  • 1.1
  • 1.0
6 results

springkubedemo

  • Clone with SSH
  • Clone with HTTPS
  • Forked from kubernetes / springkubedemo
    Up to date with the upstream repository.
    user avatar
    khofmans authored
    46407fe7
    History

    Tutorial: Deploying a Spring Boot app on Kubernetes

    I set up this project to demonstrate how to dockerize a Spring Boot app and deploy, configure and scale it on Kubernetes. In this tutorial I'm using minikube locally, you can also read my last post on how to run Kubernetes on AWS or try hosted Kubernetes in Google Container Engine.

    Prequisites

    To follow along, check out this repo and make sure you have the following tools ready:

    • docker - to build the docker images we want to deploy
    • minikube - a local Kubernetes environment
    • kubectl - the Kubernetes command line interface, on macOS you can brew install kubernetes-cli it

    The Spring Boot Service

    The details of this service don't matter much. I used the Spring Initializr to create a very simple Spring Boot app which answers on port 8090 to these routes:

    • /hello - which returns {"greeting": "hello world"}
    • /health - to report the app's health status

    The app can be built with gradle clean build which results in a standalone jar named demo-service-0.0.1-SNAPSHOT.jar in build/libs. The simplest way to run the app is with java -jar build/libs/demo-service-0.0.1-SNAPSHOT.jar.

    Creating a Docker image

    We need a container which has a JDK. If you just create an Ubuntu image with the standard Oracle JDK installation, you will end up with and image size of about 1 GB. Not nice to work with. There are better options though: Creating a minimial JDK installation based on an AlpineLinux image. The docker/minimal-java directory contains a Dockerfile I generated taking that approach.

    So we'll just create our own JDK base image first:

    cd docker/minimal-java
    docker build -t sbrosinski/minimal-java .

    Now we create a container for our demo service inherating from that base image:

    cd docker/demo-service
    docker build -t sbrosinski/demo-service .

    This Dockerfile is very simple and based on Spring Boot's docker intro:

    FROM sbrosinski/minimal-java
    VOLUME /tmp
    EXPOSE 8090
    ADD demo-service-0.0.1-SNAPSHOT.jar app.jar
    RUN sh -c 'touch /app.jar'
    ENV JAVA_OPTS=""
    ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]

    If you do this in production you should probably add some memory limiting options to the java call.

    Running the Docker image

    To try it out, we can run it locally just using docker:

    docker run -p 8090:8090 -t --name demo-service --rm b7i/demo-service:latest
    curl http://localhost:8090/hello => {"greeting":"hello world"}
    docker stop demo-service

    Publishing the Docker image

    Kubernetes will have to pull the docker image from a registry. For this example we can use a public repository on DockerHub. Register on docker.com to create a docker ID. You can now log into your DockerHub account from your machine with:

    docker login

    Push your image to DockerHub with:

    docker push sbrosinski/demo-service

    The image for the demo service is publicly available at https://hub.docker.com/r/sbrosinski/demo-service/.

    Setting up Kubernetes

    We're using the local Kubernetes cluster provided by minikube. Start your cluster with:

    minikube start

    You can take a look at the (still empty) Kubernetes dashboard with:

    minikube dashboard        

    Deploying the service to Kubernetes

    To run our application on the minikube cluster we need to specify a deployment. The deployment descriptor looks like this:

    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
    name: demo-service-deployment
    spec:
    replicas: 2 # tells deployment to run 2 pods matching the template
    template: # create pods using pod definition in this template
        metadata:
        labels:
            app: demo-service
        spec:
        containers:
        - name: demo-service
            image: sbrosinski/demo-service
            ports:
            - containerPort: 8090

    Create this deployment on the cluster using kubectl:

    kubectl create -f deployment.yml 

    You can look at the deployment with:

    kubectl describe deployment demo-service-deployment
    
    Name:  			demo-service-deployment
    Namespace:     		default
    CreationTimestamp:     	Fri, 18 Nov 2016 11:42:05 +0100
    Labels:			app=demo-service
    Selector:      		app=demo-service
    Replicas:      		2 updated | 2 total | 2 available | 0 unavailable
    StrategyType:  		RollingUpdate
    MinReadySeconds:       	0
    RollingUpdateStrategy: 	1 max unavailable, 1 max surge
    OldReplicaSets:		<none>
    NewReplicaSet: 		demo-service-deployment-1946011246 (2/2 replicas created)
    Events:
    FirstSeen    	LastSeen       	Count  	From   				SubobjectPath  	Type   		Reason 			Message
    ---------    	--------       	-----  	----   				-------------  	--------       	------ 			-------
    1m   		1m     		1      	{deployment-controller }       			Normal 		ScalingReplicaSet      	Scaled up replica set demo-service-deployment-1946011246 to 2

    Two pods have been created, a replica set, and the default rolling update strategy. You can also look at the pods with:

    kubectl get pods
    
    NAME                                       READY     STATUS    RESTARTS   AGE
    demo-service-deployment-1946011246-ap47n   1/1       Running   0          3m
    demo-service-deployment-1946011246-u3dcj   1/1       Running   0          3m

    We can join these pods as part of a service and expose it outside of our cluster. Create a service with:

    kubectl create -f service.yml

    The service descriptor looks like this:

    apiVersion: v1
    kind: Service
    metadata:
    name: demo-service
    spec:
    ports:
        - port: 8090
        targetPort: 8090
    selector:
        app: demo-service
    type: NodePort

    By specifying a service type of NodePort we declare to expose the service outside the cluster. Type LoadBalancewould create a load balancer (e.g. ELB on AWS, but this feature is not availabe for minikube), type ClusterIP would expose the service only within the cluster. We can look at the service details with:

    kubectl describe service demo-service
    
    Name:  			demo-service
    Namespace:     	default
    Labels:			<none>
    Selector:      	app=demo-service
    Type:  			NodePort
    IP:    			10.0.0.221
    Port:  			<unset>	8090/TCP
    NodePort:      	<unset>	31039/TCP
    Endpoints:     	172.17.0.6:8090,172.17.0.7:8090
    Session Affinity:  None
    No events.

    To now access the service, we can use a minikube command to tell us the exact service address:

    minikube service demo-service

    This would open your browser and point it, for example, to http://192.168.99.100:31039. Port 31039 is the NodePort we requested and the IP address is the address of our minikube cluster. We can now access the service routes:

    curl http://192.168.99.100:31039/hello => {"greeting":"hello world"}        

    That's it for now. To expand on this you can try the following: Make the service use a DB and play with Kubernetes persistent volumes and service discovery.