Spring Boot - Packaging and Deployment
In this lesson, we will cover how to create a Spring Boot executable JAR and deploy our JAR as a Docker image. Docker is becoming insanely popular over time and this lesson will show you why!
An application is practically useless unless it is exposed to the world. Let us configure our Spring Boot app to be used over the cloud.
Making an executable JAR
Boot magic wouldn’t be complete without providing a good way to ship the entire app including all of its dependencies, resources, and so on in one composite, executable JAR. After the JAR file is created, it can simply be launched by running the following command:
java -jar <jar-name>.jar
Above command will run the JAR but there will a problem, as soon as you hit Ctrl (Cmd for Mac) + C, JAR will stop. For this, you can use slight modifications
java -jar <jar-name>.jar &
The ampersand(&) will ensure that the process doesn’t stop when you hit break.
- Let's go to our code directory, and execute ./gradlew clean build
- With the Uber JAR built, let's launch the application by executing java -jar build/libs/studentapplication-0.0.1-SNAPSHOT.jar
- This will result in our application running in the jar with the following console output:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.3.0.BUILD-SNAPSHOT)
...
(The rest is omitted for conciseness)
...
2015-05-08 INFO: Registering beans for JMX exposure on startup
2015-05-08 INFO: Tomcat started on port(s): 8080 (http) 8443 (https)
2015-05-08 INFO: Welcome to the Student Catalog System!
2015-05-08 INFO: StudentRepository has 1 entries
2015-05-08 INFO: ExamRepository has 0 entries
2015-05-08 INFO: TeacherRepository has 1 entries
2015-05-08 INFO: Started StudentApplication in 12.156 seconds (JVM running for 12.877)
2015-05-08 INFO: Number of students: 1
Creating Docker images
The secret for Docker's overwhelming success lies behind the ability that it provides to package and deploy applications in a form of self-contained containers. The containers are much more lightweight than the traditional virtual machines. Multiple numbers of containers can be run on top of a single OS instance, thus increasing the number of apps that can be deployed on the same platform as compared to traditional VMs.
Making a Docker image and running it on a local development machine is workable, but not as exciting as being able to expose it to the world. We will need to publish it somewhere for it to be deployable, especially if we are thinking of doing it on Amazon or some other cloud-like environment. Luckily, Docker provides us with not only the Container solution, but also with a repository service, DockerHub, located at https://hub.docker.com where we can create repositories and publish our Docker images. So think of it like Maven Central for Docker.
Let’s follow some simple Gradle-related tasks that we can do to achieve this:
- To begin, we will create an account on DockerHub so that we can publish images.
- Once you have an account, we will need to create a repository named studentdocker.
- With this account created, now is the time to create the image. For this, we will use one of the Gradle Docker plugins. We will begin by modifying build.gradle to modify the buildscript block with the following change:
-
buildscript { dependencies { classpath("org.springframework.boot:spring-boot-gradle- plugin:${springBootVersion}") classpath("se.transmode.gradle:gradle-docker:1.2") } }
- We will also need to apply this plugin by adding the apply plugin: 'docker' directive to the build.gradle file.
- Lastly, we will need to add the following Docker configuration to the build.gradle file as well:
-
distDocker { exposePort 8080 exposePort 8443 addFile file("${System.properties['user.home']}/.keystore"), "/root/" }
- Assuming that Docker is already installed, we can proceed to create the image by executing ./gradlew clean distDocker.
- If everything has worked, you should see the following output:
$ ./gradlew distDocker
…
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:findMainClass
:startScripts UP-TO-DATE
:distTar UP-TO-DATE
:distDocker
Sending build context to Docker daemon 30.46 MB
Sending build context to Docker daemon
Step 0 : FROM aglover/java8-pier
---> 69f4574a230e
Step 1 : EXPOSE 8080
---> Using cache
---> 725f1fd8c808
Step 2 : EXPOSE 8443
---> Using cache
---> d552630db9d5
Step 3 : ADD .keystore /root/
---> d0684cbd7fac
Removing intermediate container ef2dffe243f4
Step 4 : ADD ch6.tar /
---> a0963c837391
Removing intermediate container fb140e526e29
Step 5 : ENTRYPOINT /ch6/bin/ch6
---> Running in a769b6d6b40b
---> 778da2170839
Removing intermediate container a769b6d6b40b
Successfully built 778da2170839
BUILD SUCCESSFUL
Total time: 1 mins 0.009 secs
- We can also execute docker images command to see the newly created image:
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ch6 latest 778da2170839 17 minutes ago 1.04 GB
aglover/java8-pier latest 69f4574a230e 11 months ago 1.01 GB
- With the image built successfully, we are now ready to start it by executing the following command:
docker run -d -P ch6
- After the container has started, we can query the Docker registry for the port bindings so that we can access the HTTP endpoints for our service. This can be done by the docker ps command. If the container is running successfully, we should see the following result (names and ports will vary):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
37b37e411b9e ch6:latest "/ch6/bin/ch6" 10 minutes ago Up 10 minutes 0.0.0.0:32778->8080/tcp, 0.0.0.0:32779->8443/tcp drunk_carson
- From this output, we can tell that the port mapping for the internal port 8080 has been set up to be 32778 (your port will vary for every run). Let's open http://localhost:32778/students in the browser to see our application in action.
When launching a Docker image, we used the –d and –P command-line arguments. Their uses are as follows:
- -d: This indicates the desire to run the container in a detached mode where the process starts in the background.
- -P: This instructs Docker to publish all the internally exposed ports to the outside so that we can access them.
Let's examine other instructions in detail:
- The exposePort directive tells the plugin to add an EXPOSE <port> instruction to the Dockerfile so that when our container is started, it will expose these internal ports to the outside via port mapping. We have seen this mapping while running the docker ps command.
- The addFile directive tells the plugin to add an ADD <src> <dest> instruction to the Dockerfile so that when the container is being built, we will copy the file from the source filesystem in the filesystem in the container image. In our case, we will need to copy the .keystore certificate file that we configured in one of our previous recipes for the HTTPS connector, which we instructed in tomcat.https.properties to be loaded from ${user.home}/.keystore. Now, we need it to be in the /root/ directory as, in the container, our application will be executed under root. (This can be changed with more configurations.)
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment