Execute Spring Boot command line runner in Docker container

Scope

This post will list down the steps to run a Spring Boot (or any runnable jar) in a Docker container.

O/S: Ubuntu 20 LTS

Docker version: 20.10

After the development and unit testing is completed, build the runnable jar (using Maven clean package install goals or equivalent)

Build Dockerfile

Copy the jar to a clean directory of Ubuntu host. I keep all files at /home/pandian/feedparser. FeedParser-2.jar is my jar file name.

Create a Dockerfile (in the same name, where your jar exists) using your favorite editor.

# Alpine Linux with OpenJDK JRE
FROM openjdk:8-jre-alpine
# copy JAR into image
COPY  FeedParser-2.jar /FeedParser-2.jar
# run application with this command line
CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=default", "/FeedParser-2.jar"]

The lines starts with # are comments.

Firstly, it fetches a unix container with openjdk:8

Secondly, it copies our executable jar to / of the container

Finally, it runs our jar file

After you save and exit the editor, following is what you will see in this directory.

$ ls -alt
total 17680
drwxrwxr-x 2 pandian pandian     4096 Dec 19 03:24 .
drwxr-xr-x 8 pandian pandian     4096 Dec 19 03:20 ..
-rw-rw-r-- 1 pandian pandian      250 Dec 19 03:20 Dockerfile
-rw-r--r-- 1 pandian pandian 18091416 Dec 19 03:18 FeedParser-2.jar

Build your docker image

We need to build our image by issuing the following command

$ sudo docker build -t feedparser:latest /home/pandian/feedparser

feedparser is the image name, latest is the tag name. It will build the image by executing the commands provided in Dockerfile one by one.

$ sudo docker build -t feedparser:latest /home/pandian/feedparser
Sending build context to Docker daemon  18.09MB
Step 1/3 : FROM openjdk:8-jre-alpine
8-jre-alpine: Pulling from library/openjdk
e7c96db7181b: Pull complete
f910a506b6cb: Pull complete
b6abafe80f63: Pull complete
Digest: sha256:f362b165b870ef129cbe730f29065ff37399c0aa8bcab3e44b51c302938c9193
Status: Downloaded newer image for openjdk:8-jre-alpine
 ---> f7a292bbb70c
Step 2/3 : COPY  FeedParser-2.jar /FeedParser-2.jar
 ---> 7fb6417e9838
Step 3/3 : CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=default", "/FeedParser-2.jar"]
 ---> Running in 44b4e9443e63
Removing intermediate container 44b4e9443e63
 ---> 23879d1fc20e
Successfully built 23879d1fc20e
Successfully tagged feedparser:latest

If you list the images, you will see a new image by name feedparser got added.

$ sudo docker images
REPOSITORY    TAG            IMAGE ID       CREATED         SIZE
feedparser    latest         23879d1fc20e   2 minutes ago   103MB
hello-world   latest         feb5d9fea6a5   2 months ago    13.3kB
openjdk       8-jre-alpine   f7a292bbb70c   2 years ago     84.9MB

Run the container

Last step is to run the container. Issue the following command.

$ sudo docker run feedparser

This command will kick start the application in a container.

$ sudo docker run feedparser
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/FeedParser-2.jar!/BOOT-INF/lib/logback-classic-1.2.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/FeedParser-2.jar!/BOOT-INF/lib/slf4j-simple-1.7.32.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
19:31:10.253 [main] INFO org.grassfield.feed.parser.FeedParserApplication - Starting FeedParser

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.1)

19:31:11.508 [main] INFO  o.g.f.parser.FeedParserApplication:55 - Starting FeedParserApplication v2 using Java 1.8.0_212 on 867ffa00ea49 with PID 1 (/FeedParser-2.jar started by root in /)

Powershell: Cannot overwrite variable Host because it is read-only or constant

I got the following error while executing my powershell script.

Cannot overwrite variable host because it is read-only or constant.
At line:1 char:1

  • ~~~~~~~~~~~~~~~~~
    • CategoryInfo : WriteError: (host:String) [], SessionStateUnauthorizedAccessException
    • FullyQualifiedErrorId : VariableNotWritable

$host is a reserved word in powershell. So it worked after replacing $host with $hostname

phpMyAdmin – Error Incorrect format parameter

I got this error while importing a 150MB database to MariaDb/MySQL from phpMyAdmin.

phpMyAdmin – Error

Incorrect format parameter

This is a generic error. We may not understand in first shot. This was related to the upload limits specified in php.ini used by Apache. the upload max file size was set at 2 MB and post max size was set at 8 MB by default. My database is above this. So I changed the following parameters in php.ini.

sudo vi /etc/php/7.4/apache2/php.ini

Find the replace the values for the following parameters

upload_max_filesize=200M
post_max_size=200M

Restart apache service.

sudo service apache2 restart

The error will be resolved by now.

phpMyAdmin: Login without a password is forbidden by configuration (see AllowNoPassword)

Development environments will not have a root password, generally. But phpMyAdmin has a setting that discourages password-less login. If you do so, you will get the below error.

Login without a password is forbidden by configuration (see AllowNoPassword)

If you still want to setup the password-less login, some people suggests following option. But it didn’t work for me.

phpMyAdmin Config Change

Edit config.inc.php. (Ensure you have a backup before you do so. Otherwise, you may corrupt the whole config file.)

$ sudo vi /etc/phpmyadmin/config.inc.php

You will find a line like this

// $cfg['Servers'][$i]['AllowNoPassword'] = TRUE;

Uncomment this.

Still some of them say it works for their version and Operating system.

Creating a super user for phpMyAdmin

MySQL does not allow root user to login from phpMyAdmin. So you may create a super user for phpmyadmin using the following queries.

CREATE USER 'pmauser'@'localhost' IDENTIFIED WITH mysql_native_password BY '<your password>';
GRANT ALL PRIVILEGES ON . TO 'pmauser'@'localhost' WITH GRANT OPTION;
flush privileges

Feed in the username and password to phpMyAdmin. It worked for me.

ERROR 1698 (28000): Access denied for user ‘root’@’localhost’

This is the error I got while logging in as root user to MySQL database for the very first time.

ERROR 1698 (28000): Access denied for user ‘root’@’localhost’

I didn’t do anything wrong while logging in.

$ mysql -u root -p
Enter password: 
ERROR 1698 (28000): Access denied for user 'root'@'localhost'

Ubuntu uses Unix authorisation socket plugin, which will consider the unix user from which you launch mysql client. So I need to jump in as root to execute mysql client as shown below.

$ sudo mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.26-0ubuntu0.20.04.3 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

https://dev.mysql.com/doc/mysql-security-excerpt/8.0/en/socket-pluggable-authentication.html

Avoiding null variables while serializing with Jackson

I perform some CRUD operation by consuming REST APIs. Some of the entities I want to create do not have values for non-mandatory fields. When I pass those entities to servers, the call breaks with a NullPointerException. Hence I do not want to send a variable if it is null. We shall use JsonInclude annotation as shown below

@JsonInclude(JsonInclude.Include.NON_NULL)
String to;

Ansible: Missing sudo password

So here is the another error while getting the uname of the remote boxes

$ ansible webservers -b --become-user=myuser -m shell -a 'uname -a' -u myuser
192.168.1.139 | FAILED | rc=-1 >>
Missing sudo password

After executing it with explicit become password it worked.

$ ansible webservers -b --become-user=myuser -m shell -a 'uname -a' -u myuser --extra-vars "ansible_sudo_pass=mypassword"

Failed to connect to the host via ssh: user@IP: Permission denied (publickey,password).”

I tried to ping all servers in Ansible

This is what I have in /etc/ansible/hosts file

[webservers]
192.168.1.139

My ping command failed.

$ ansible webservers -m ping
192.168.10.139 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: pandian@192.168.10.139: Permission denied (publickey,password).",
    "unreachable": true
}

I updated the hosts file with credentials.

[webservers]
192.168.1.139 ansible_ssh_pass=password ansible_ssh_user=myuser

Does it work? Not yet

$ ansible webservers -m ping
192.168.10.139 | FAILED! => {
    "msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}

I need to install

$ sudo apt-get install sshpass

Finally it works!

$ ansible webservers -m ping
192.168.1.139 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Grassfield Tamil Blog Aggregator

I’m happy to introduce the brand new blog aggregator www.grassfield.org. Today is the day 1 🙂

This aggregates Tamil and English blogs. This platform is running using my opensource project jAtomRss. I’m happy to come out with a website, finally.

Thanking Sivakumar and Kannan for their technical support and Sindu for testing the same!