JavaRush /Java Blog /Random EN /We implement application deployment - "Java project from ...

We implement application deployment - "Java project from A to Z"

Published in the Random EN group
Hi all. We continue the series of articles on writing your project. “Java project from A to Z”: Implementing application deployment - 1

Sort the branches

Of the importance, in order not to get lost in the branches and their order in the repository, I decided to rename them by adding the STEP_{number} prefix . For example, we have three branches in addition to the main one:
  • JRTB-0
  • JRTB-2
  • JRTB-3
You won’t immediately understand which one should go after which. So I'll rename them as follows:
  • STEP_1_JRTB-0 - first step
  • STEP_2_JRTB-2 - second step
  • STEP_3_JRTB-3 - third step
And so on for the next articles. To rename branches, go to the repository page , find the branches box , follow it: “Java project from A to Z”: Implementing application deployment - 2Under each branch, click on the pencil and rename the branch: “Java project from A to Z”: Implementing application deployment - 3And as a result we get: “Java project from A to Z”: Implementing application deployment - 4By the way, everyone who subscribed to my telegram channel found out immediately that I renamed the branches.

A little about Docker

What is Docker? In short, it is a tool with which you can quickly and securely deploy (deploy) applications, creating a closed infrastructure for them, necessary only for them. It's still difficult, I understand. In general, Docker can be understood as a development platform where you can work quickly and efficiently. Docker can be understood as a program that runs on a server. This program has the ability to store containers with applications. What is a container? This is a separate infrastructure into which you can add everything you need. For example, for a Java application we need a JRE to run the application, the container will have this, we will need some other software - we can add this. Or maybe we need Linux and a Tomcat servlet container. This can also be done. Containers are created based on an image: that is, this is a specific template that contains everything necessary to create a Docker container. How to create this image? In our case, we will need to create a Dockerfile at the root of the project describing what should be in the container. Since we don't want to expose the bot's token anywhere, we'll have to resort to passing it along every time we want to deploy the application. You can read more about this topic here and here .

We write JRTB-13

We need to set up a quick and easy deployment process for our application to the server. That is, for a machine that works 24/7. Let's take Docker as a basis. But there is no task on our list that would be responsible for adding this functionality. Somehow I missed it when creating it. No problem, we’ll create it now. We go to the issue creation tab on GitHub and select Feature Request: “Java project from A to Z”: Implementing application deployment - 5Add a description of the task, criteria for its acceptance, set which project this issue belongs to and you can create a new issue: “Java project from A to Z”: Implementing application deployment - 6Now, to show that the task has been accepted for work, change the status of the task from To do on In Progress: “Java project from A to Z”: Implementing application deployment - 7This is going to be a difficult article. If you have any problems, write in the comments: I will monitor and answer them to the best of my ability. This will be a small Customer Support :D

Creating a Dockerfile

What is a dockerfile? For Docker, this is a script (step-by-step instructions) on how to create an image for a Docker container. For our application to work, we need the JDK, version 11 at that. That is, we need to find the JDK 11 docker image and add it to our image. This is something akin to how we add a dependency to a memory. Docker has DockerHub for this purpose . To download images locally, you need to register there. After registration, let's go look for JDK11. From what I found, this is the container: adoptopenjdk/openjdk11 . The description of this container has what is needed for the dockerfile:
FROM adoptopenjdk/openjdk11:ubi
RUN mkdir /opt/app
COPY japp.jar /opt/app
CMD ["java", "-jar", "/opt/app/japp.jar"]
Let's fix the folder from which we take the jar file. We have it in the target folder after we run the mvn package maven task. Before doing all this, based on the updated main branch, we create a new one for our task: STEP_4_JRTB-13 . Now you can work. In the root of the project, create a file without the Dockerfile extension and add the following inside:
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
The first line is what the image will be based on - adoptopenjdk/openjdk11. The second line is to add an argument to the image named JAR_FILE, which is located in the target folder. Moreover, the current folder is determined by the location of the Dockerfile. Third line - copy the jar of our project into the docker image. The last line essentially contains an array created from the command in the terminal, which was separated by space. That is, in the end the following will be executed: “java -jar /app.jar” To keep the bot token secret, when starting the container we will need to pass two variables - the name of the bot and its token. To do this, we will write a query that should launch our project with variables. And how to do it? You need to Google it: here is the first link with a normal description. What do we want to do? We have two variables in the application.properties file that we define there:
  • bot.username
  • bot.token
I want to run a docker container and pass my value there every time so that no one can see these values. I know that in SpringBoot, environment variables that are set when the jar project is launched will take precedence over those in the application.properties file. To pass a variable in a request, you need to add the following construction: -D{variable name}=”{variable value}” . We do not add curly braces ;) We will receive a request that will launch our application with predefined values ​​- the name and token of the bot: java -jar -Dbot.username=”test.javarush.community_bot” -Dbot.token=”dfgkdjfglkdjfglkdjfgk” *.jar Now we need to pass these variables inside the docker container. This is an environment variable. To ensure that in the future our database works smoothly and without problems with our application, we will use docker-compose. This is a separate tool in which you can organize work, startup, and dependencies between containers. In other words, it is an add-on on top of Docker to manage containers of one infrastructure. Plus, before running docker-compose, we need to be sure that we have pulled all code changes from the server, built the application and stopped the old version. For this we will use a bash script. Wow... It all sounds difficult, I agree. But working with setting up application deployment is always a tedious and complex process. Therefore, we have a pretty good scheme:
  1. Let's run the bash script.
  2. The bash script runs docker-compose.
  3. Docker-compose launches a docker container with our application.
  4. The Docker container runs our application.
And now we need to make sure that two variables - the name of the bot and its token - go from point 1 to point 4. And so that these two variables are used when launching our java application. Let's go from the end to the beginning. We already know what command needs to be executed to start the jarnik. Therefore, we will configure the Dockerfile so that it learns to accept two variables and pass them to the request. To do this, let's reduce the Dockerfile to the following form:
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
ENV BOT_NAME=test.javarush_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}", "-jar", "/app.jar"]
You can see that we added two lines and updated ENTRYPOINT. Lines:
ENV BOT_NAME=test.javarush_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
declare variables inside the encoder file. By default they have a value specified. If, when creating an image from this dockerfile, environment variables with such names are passed, the values ​​will be different. And in ENTRYPOINT we added a few more elements that will read these environment variables:
"-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}"
Here you can see that inside the line, using the ${} construct, the values ​​of BOT_NAME and BOT_TOKEN will be passed. Next, we need to teach how to receive and pass these variables to docker-compose.

Create docker-compose.yml

It would be good for you to read about the YAML format separately, otherwise the article is already growing by leaps and bounds. For us, this is just another description of variables of the .properties type. Only in properties it is written through a dot, but in YAML this is done a little more beautifully. For example, like this. Two variables in .properties: javarush.telegram.bot.name=ivan javarush.telegram.bot.token=pupkin But in .yaml (the same as .yml) it will be like this:
javarush:
	telegram:
		bot:
		  name: ivan
		  token: pupkin
The second option is more beautiful and understandable. The spaces should be exactly as indicated above. Let's translate our application.properties and application.yml somehow. First you need to create it. In the root of the project, create a file docker-compose.yml and write the following there:
version: '3.1'

services:
 jrtb:
   build:
     context: .
   environment:
     - BOT_NAME=${BOT_NAME}
     - BOT_TOKEN=${BOT_TOKEN}
   restart: always
The first line is the docker-compose version. services: says that all the following lines after this (will be shifted) refer to the services that we are configuring. We have only one of these so far - a java application called jrtb . And already under it will be all its settings. For example, build: context: . says that we will look for the Dockerfile in the same directory as docker-compose.yml. But the environment: section will be responsible for ensuring that we pass the necessary environment variables to the Dockerfile. Just what we need. Therefore, we pass variables below. Docker-compose will look for them in the server operating environment variables. Let's add them to the bash script.

Creating bash scripts

The last step is to create a bash script. Create a file called start.sh in the root of the project and write the following there:
#!/bin/bash

# Pull new changes
git pull

# Prepare Jar
mvn clean
mvn package

# Ensure, that docker-compose stopped
docker-compose stop

# Add environment variables
export BOT_NAME=$1
export BOT_TOKEN=$2

# Start new deployment
docker-compose up --build -d
The first line is needed for all bash scripts: it won’t work without it. And then - just a set of commands in the terminal that need to be executed. I've added comments in each command so it should be clear. The only thing I want to explain is what $1 and $2 mean. These are two variables that will be passed when the bash script is launched. Using the export command, they will be added to server variables and read in docker-compose. This works for Ubuntu, probably not for Windows, but I'm not sure. Now you need to add the stop.sh script, which will stop the work. It will contain several lines:
#!/bin/bash

# Ensure, that docker-compose stopped
docker-compose stop

# Ensure, that the old application won't be deployed again.
mvn clean
Here we stop docker-compose and clean up the project jarnik, which has been lying around since the last build. We do this to ensure that our project is rebuilt accurately. There were precedents, that’s why I’m adding) As a result, we end up with 4 new files:
  • Dockerfile - a file for creating an image of our application;
  • docker-compose.yml - a file with settings for how we will launch our containers;
  • start.sh - bash script for deploying our application;
  • stop.sh is a bash script to stop our application.
We will also update the version of our application from 0.2.0-SNAPSHOT to 0.3.0-SNAPSHOT. Let's add a description of the new version to RELEASE_NOTES and slightly refactor what was there:
# Release Notes ## 0.3.0-SNAPSHOT * JRTB-13: added deployment process to the project ## 0.2.0-SNAPSHOT * JRTB-3: implemented Command pattern for handling Telegram Bot commands ## 0.1.0-SNAPSHOT * JRTB -2: added stub telegram bot * JRTB-0: added SpringBoot skeleton project
And in the README we will add a new paragraph describing how to deploy our application:
## Deployment Deployment process as easy as possible: Required software: - terminal for running bash scripts - docker - docker-compose to deploy application, switch to needed branch and run bash script: $ bash start.sh ${bot_username} ${bot_token } That's all.
Of course, everything is written in English. As usual, in our newly created branch STEP_4_JRTB-13 we create a new commit with the name: JRTB-13: implement deployment process via docker and push it. I stop dwelling in detail on things that I have already described in previous articles. I don't see the point in repeating the same thing. Besides, those who figured it out and did it themselves will not have any questions. This is me talking about how to create a new branch, how to create a commit, how to push a commit to the repository.

Bottom line

Today I have shown a ton of new information that needs to be carefully considered and expanded upon with additional reading. The most important thing: with the help of ONE (!!!) command, everything necessary to deploy our application will be done. This is so cool that I can’t even tell you. Yes, I had to spend a decent amount of time in the Docker documentation to understand how to correctly forward variables. From now on, the telegram bot will always work on the latest version of the main branch. Link to telegram bot. Today there will be no links to materials that would be good to read: the responsibility lies with you. You need to learn to search for information. Everyone who subscribed to my telegram channel learned about the bot deployment almost immediately. Friends, do you like the project? Give him a star ! This way it will become more popular and more people will be able to learn about it and learn from it. As usual, I suggest registering on GitHub and following my account to follow this series and my other projects that I work on there. Now we are ready to connect the database. The next article will be longer and in it we will do everything necessary to work with the database. All descriptions are in JRTB-1 .

A list of all materials in the series is at the beginning of this article.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION