Spring Boot is a very popular framework in java-world now. If you package it to docker-images in common style, it will be so heavy. And it can solved!
In this post I want to discuss image size. What size affects:
- Occupied space in storage. Now, hard disks are cheap, but if each image has size 2GB, it will occupy a big volume in the future.
- Downloading speed in the update process.
- Traffic volume in the update process.
- Data duplication in an image. E.g. spring boot fat jar. It consists of all the dependencies. And each of image consists its. But we can store it to separate layers.
What can we do?
- Push each dependency as layer to docker registry.
- Use distro less base images.
- Use cropped java with only needed dependency.
How can we do?
Use Jib Jib.
OK, do that!
Initial state
I have a backend for my aviation application. Docker image has a size 354.12 MiB.
Its Dockerfile:
FROM openjdk:11.0.3-jdk-stretch
MAINTAINER Alexey Nevinsky <alexey@nevinsky.net>
COPY devops/docker/backend/runApp.sh /opt/backend/runApp.sh
ADD projects/app-backend/build/libs/app-backend.jar /opt/backend/app.jar
CMD opt/backend/run_app.sh
Its runApp.sh file:
#!/bin/bash
echo "Application Name: backfire backend"
java ${JVM_OPTS} \
-Djava.security.egd=file:/dev/./urandom \
-jar ./opt/backend/app.jar \
File .gitlab-ci.yml for build:
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
stages:
- build
- deploy
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
cache:
paths:
- .gradle/wrapper
- .gradle/caches
- build/libs
build:
image: openjdk:11.0.3-jdk-slim-stretch
stage: build
script: ./gradlew build -xtest
artifacts:
paths:
- projects/app-*/build/libs/*.jar
expire_in: 1 week
only:
- develop
deploy:
image: docker:stable
services:
- docker:dind
stage: deploy
script:
- docker info
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- export DOCKER_IMAGE="backend"
- export DOCKER_IMAGE_NAME=${CI_REGISTRY_IMAGE}/${DOCKER_IMAGE}
- docker build -t ${DOCKER_IMAGE} -f ./devops/docker/${DOCKER_IMAGE}/Dockerfile .
- docker tag ${DOCKER_IMAGE} ${DOCKER_IMAGE_NAME}:latest
- docker push ${DOCKER_IMAGE_NAME}:latest
only:
- develop
Build time with compilation, build docker image, and push to the registry is 06:18.
Well, rewrite the project to build with jib and see results again.
One of the jib’s benefits is: we don’t need docker on build machine!
Rewrite Gradle build scripts
- Add a plugin to build.gradle in root:
plugins {
id 'info.solidsoft.pitest' version '1.4.0'
id "net.ltgt.apt" version "0.21"
id 'com.palantir.git-version' version '0.11.0'
id 'com.google.cloud.tools.jib' version '2.1.0' //<<<<<< here
}
- Add a plugin to the project’s build.gradle and jib config:
project.description = 'Aviation backend project'
apply plugin: "com.google.cloud.tools.jib" // <<<< here
dependencies {
implementation libraries.spring_boot_starter_web
implementation libraries.java_mail
}
/// And here
jib {
container.mainClass = "net.nevinsky.BackendApp"
to {
image = "registry.gitlab.com/inver/backfire/backend"
}
}
Rewrite .gitlab-ci.yml
image: openjdk:11-jdk-slim
stages:
- build
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
cache:
paths:
- .gradle/wrapper
- .gradle/caches
build:
stage: build
script:
- ./gradlew build :backend:jib -Djib.to.auth.username="$CI_REGISTRY_USER" -Djib.to.auth.password="$CI_REGISTRY_PASSWORD" -Djib.httpTimeout=120000
after_script:
- echo "End CI"
Please, do not forget double quotes, then you will specify username and password!
Commit this file to GitLab and wait for the success ci pipeline.
For my project, the time of compilation and push be 01:57, size of docker image: 85.50 MiB. PROFIT! =)
P.s. Also, you can use this tool for exploring docker images: Dive
Previous post » A simple seo checker