B4J Question B4J webapp Docker image created but it is huge

hatzisn

Well-Known Member
Licensed User
Longtime User
Today I have successfully created my first docker image of a webapp for my vps. The image had a total size of around 580 mb of which the 55 is the debian base image and the 430 is the java folder to run the app inside the container (I copied jdk-11.0.1). This size is unacceptable, and as I am not a totally docker aware guy I would like to ask for the alternative solutions to run the app inside the container. I tried with alpine base image (almost 5.6mb) and I was not able to run java in it. The main culprit though is the java folder which is extremely big. How can I reduce its size to only the needed files? Anyone has any solutions?
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Today I have successfully created my first docker image of a webapp for my vps.
Congratulations. Dont you want to share your experience with us about how you achieved this.

Sorry I could help if I knew, am curious and interested on the lessons you learned.

Thank you so much.
 
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
It was much easier than I originally thought. I created a Debian VM in virtualbox and installed docker in it.
I installed proftpd with sudo apt-get install proftpd
With this you have ftp access to the virtual machine.

I created a folder and have put in there the mywebappname.jar the www folder with all its contents and the logs folder.
I then uploaded in it also the jdk-11.0.1 folder.

Then I have created a shell file which I named start.sh (you can choose what ever you want as long as the extension is .sh).
Its contents were the following:

B4X:
/java/bin/java -jar /webapp/yourwebappname.jar

Then in order to create an image you will have to create a file named "Dockerfile" which is a plain txt file without any extension.
An image has to start with a base image if you do not want to make a lot of work. So every Dockerfile starts with the keyword FROM
and then you copy in it the contents and expose the ports you want (mine was 51042). The contents of the docker file in my case
were the following:

B4X:
FROM debian
COPY www /webapp/www
COPY yourwebappname.jar /webapp
COPY logs /webapp
COPY jdk-11.0.1 /java
COPY start.sh .
EXPOSE 51042
VOLUME /webapp/www/download
VOLUME /webapp/logs
WORKDIR /webapp
CMD ["/bin/sh", "/start.sh"]

It is easy to understand what it does for the most part.
The keyword VOLUME exposes a folder in the container to the host to mount it to a local folder so these specific container's folder can access outside (in the host) folders and/or save there for permanent data storage. This is because if you recreate the container and you have saved everything in it, it will be destroyed and lost.
The keyword WORKDIR is like a cd {folder} command in the /webapp folder. It is necessary for the jar to access the www folder and logs folder.
At last the keyword CMD has the format of a list of strings which the first is the executable and from the second and on the parameters. Here the executable is /bin/sh (in the container) and it executes the shell commands in /start.sh and starts thus the webapp.

After you have created the docker file you write in the folder that is located:
sudo chmod -R o=rwx *
This is a crucial step as it will render anything in this folder and subfolders including java readable, writable and executable by anyone inside the container.

When you have saved the Dockerfile you have to build the image:

If the name of Dockerfile is 'Dockerfile' you just have to do this:
docker build -t yourwebappnameinlowercase:latest .

If the name of Dockerfile is 'SomethingElse' you just have to do this:
docker build -t yourwebappnameinlowercase:latest -f SomethingElse .

Watch the dot at the end in the previous two commands.

The yourwebappnameinlowercase is the repository name and the latest is the tag. Both can be what ever you want (I think).
This will pull the debian base image and build your image based on it copying everything you have put in the Dockerfile

In order to transfer it to your vps from the virtualmachine you will have to copy it from the vm.
With this command according to digitalocean you create a tar file with all the files necessary for the image:

sudo docker save -o /home/yourusername/your_image.tar your_image_name
f.e. sudo docker save -o /home/yourusername/your_image.tar yourwebappnameinlowercase:latest

(I have not done this previous step as I have downloaded it from portainer web gui for docker.)

Then you get the tar file with ftp from the VM to your computer and you upload it to the vps.
There you connect with ssh, cd to the folder you have uploaded the tar and do this:
sudo docker load -i your_image.tar

When you do this you write:
docker images

This will present you the images saved in docker. You must see at this time your image among them. Get the hexadecimal id from this list for your image (f.e. abcdef0123456) and do the following in the vps:

We create a folder /home/myusername/FolderName
We cd into it and create folders "download" and "logs".

Then we write this (the following bold line is written all in one line):
docker run -d -p 51042:51042 -v /home/myusername/FolderName/download:/webapp/www/download -v /home/myusername/FolderName/logs:/webapp/logs abcdef0123456

The -d tells docker to run it as daemon (that is in background and not block the thread). The -p connects host ports to container ports in order to use it with a reverse proxy let's say. It can be different f.e. -p 51044:51042. The -v mounts host's folders to container's folders. With what we have written we can place in the /home/myusername/FolderName/download all the files we want and they will be accessible from the container. Also the logs it will save they will be saved in /home/myusername/FolderName/logs

Both -p and -v follow the same pattern (outsideofthecontainer : insidethecontainer). You just have created your first containerized B4J web app.
 
Last edited:
Upvote 0

tchart

Well-Known Member
Licensed User
Longtime User
hatzisn, well done for getting that working.

A couple of thoughts

#1
You should probably stop using jdk-11.0.1 - it is old and its starting to not trust new SSL certificates. Use the latest version of 11, see thread below.

#2
As far as I know you shouldnt be specifying "FROM debian" as the base image but rather pull a preconfigured JDK base image.

ie you shouldnt be copying the JRE/JDK directly to the image - this defeats the purpose of a container if you are copying a runtime into the image

You should probably pull this as the base image; https://hub.docker.com/_/eclipse-temurin

See links below.


#3
If I build a blank image with Open JDK 11 the image size is around 400mb, So 580mb is not terrible and probably expected.

There is lots of discusison around why they are so big.

 
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
Lovely thank you so much. You explain it so well, even a 5 year old will grasp as much as its all new to me. Will explore this.

I missed a crucial step. See it in red above. My way though might be obsolete as @tchart describes in the previous post.
 
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
hatzisn, well done for getting that working.

A couple of thoughts

#1
You should probably stop using jdk-11.0.1 - it is old and its starting to not trust new SSL certificates. Use the latest version of 11, see thread below.

#2
As far as I know you shouldnt be specifying "FROM debian" as the base image but rather pull a preconfigured JDK base image.

ie you shouldnt be copying the JRE/JDK directly to the image - this defeats the purpose of a container if you are copying a runtime into the image

You should probably pull this as the base image; https://hub.docker.com/_/eclipse-temurin

See links below.


#3
If I build a blank image with Open JDK 11 the image size is around 400mb, So 580mb is not terrible and probably expected.

There is lots of discusison around why they are so big.


Hi @tchart , thank you for the knowledge shared. I used jdk-11.0.1 because I had no other version for linux available at this time.
 
Last edited:
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
hatzisn, well done for getting that working.

A couple of thoughts

#1
You should probably stop using jdk-11.0.1 - it is old and its starting to not trust new SSL certificates. Use the latest version of 11, see thread below.

#2
As far as I know you shouldnt be specifying "FROM debian" as the base image but rather pull a preconfigured JDK base image.

ie you shouldnt be copying the JRE/JDK directly to the image - this defeats the purpose of a container if you are copying a runtime into the image

You should probably pull this as the base image; https://hub.docker.com/_/eclipse-temurin

See links below.


#3
If I build a blank image with Open JDK 11 the image size is around 400mb, So 580mb is not terrible and probably expected.

There is lots of discusison around why they are so big.


I tried with the following DockerFile and the total length of the image is 337MB. Great decrease... Thanks a lot.

B4X:
FROM openjdk:19-jdk-alpine
COPY www /webapp/www
COPY yourwebappname.jar /webapp
COPY logs /webapp
EXPOSE 51042
VOLUME /webapp/www/download
VOLUME /webapp/logs
WORKDIR /webapp
CMD ["java", "-jar", "/webapp/yourwebappname.jar"]
 
Last edited:
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
@tchart since it seems you are much more experienced than me in docker I would like to ask you something. In my virtual machine the image created with Dockerfile in the previous post works but not in my vps. I thought it was because the two containers (my initial with debian and this with alpine and java 19 jdk shared the same external resources but it seems this is not the case. What else can it be? I have connected the new container's network to the nginx proxy manager (NPM) container's network and I have set -p 51041:51042 for the new container and -p 51042:51042 for the initial. When I divert through NPM to the old debian container it works but when I divert to the new openjdk 19 alpine container it does not. Some help please would be highly appreciated.
 
Last edited:
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
@tchart since it seems you are much more experienced than me in docker I would like to ask you something. My VPS is Debian 10 and my virtual machine is Debian 11. In my virtual machine the image created with Dockerfile in the previous post works but not in my vps. I thought it was because the two containers (my initial with debian and this with alpine and java 19 jdk shared the same external resources but it seems this is not the case. What else can it be? I have connected the new container's network to the nginx proxy manager (NPM) container's network and I have set -p 51041:51042 for the new container and -p 51042:51042 for the initial. When I divert through NPM to the old debian container it works but when I divert to the new openjdk 19 alpine container it does not. Some help please would be highly appreciated.

I fixed this. It was my mistake due to not knowing completely docker. The images were working but what was not working was the ridirecting from Nginx Proxy Manager and this was completely my mistake. What I did was direct the flow directly inside the container (with the name of container) and use the connected port 51041 (-p 51041:51042) outside the container. Completely dumb thing. I should have used the inside the container port 51042.
 
Last edited:
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
Talking about large images, I decided to create a Windows container of the web app and it ended up being around 6 GB. The only base image available to start off is the Windows server which scores around 2,5 GB. I understand completely now why docker is definitely a Linux thing. It is far better to spend a fair amount of memory so you can use the Linux containers in Windows with WSL2.
 
Upvote 0
Top