Effective Java
Docker Pipeline
Effective build for lean and secure Java Docker images
About me…
•Chief of Research @codefresh.io
•github.com/alexei-led/pumba
•#docker, #golang, #aws
•medium.com/@alexeiled
•@alexeiled
The “Naive” Approach
Follow familiar VM build and install flow
# start from ubuntu
FROM ubuntu:14.04
# add required packages and java repository
RUN apt-get update && apt-get install -y python-software-properties software-properties-
common
RUN add-apt-repository ppa:webupd8team/java
# install Oracle JDK 8 with auto-accept license agreement
RUN echo "oracle-java8-installer shared/accepted-oracle-license-v1-1 boolean true" |
debconf-set-selections
RUN apt-get update && apt-get install -y oracle-java8-installer maven
# add ALL project files
ADD . /usr/local/app
# build Java application with Maven (fetch packages, compile, test and deploy)
RUN cd /usr/local/app && mvn install
# define default command to run the application
CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=test", "/usr/local/app/target/
spring-boot-rest-example-0.3.0.war"]
Docker Image Size = 1.3 GB
Docker Built Time = 27 min
“Standard” Approach
FROM java:8
# Install maven
RUN apt-get update
RUN apt-get install -y maven
WORKDIR /code
# Prepare by downloading dependencies
ADD pom.xml /code/pom.xml
RUN ["mvn", "dependency:resolve"]
RUN ["mvn", "verify"]
# Adding source, compile and package into a fat jar
ADD src /code/src
RUN ["mvn", "package"]
EXPOSE 4567
CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=test", "target/spring-boot-rest-
example-0.3.0.war"]
Docker Image Size = 1.2 GB
Docker Built Time = 15 min
Take Library OpenJDK image, add pom.xml first…
Stay away from these abstractions
Containers are not VM
and
they are not like “lightweight” VM
–Joe Fernandes, senior director, OpenShift Product Management, Red Hat
“A Linux container is nothing more than a
process that runs on Linux. It shares a host
kernel with other containerized processes.”
What is the bare minimum
required to run Java App?
1. Base Image with C Runtime and Posix shell (Alpine)
2. Java Runtime Environment (OpenJDK JRE)
3. Application byte-code and resources (app.jar)
4. 3rd Party Libraries (lib/*.jar)
5. Optionally HTTP server (Tomcat/Jetty/Netty/…)
Docker Image Size
• time to build
• network latency
• storage
• service availability and elasticity
• security
• development agility
Docker Builder Pattern
• 2 Dockerfiles
• 1st for build tools
• 2nd for runtime
Java Docker Builder
1. Base Image with C Runtime and Posix shell (Alpine)
2. Java Development Kit (OpenJDK)
3. Javac or other JVM compiler (Scala, Kotlin, …)
4. Build Management Tool (Maven, SBT, Gradle, …)
5. Linters, code scanners, test frameworks, test tools, …
Maven Builder Dockerfile
FROM openjdk:8-jdk-alpine
RUN apk add --no-cache curl tar bash
ARG MAVEN_VERSION=3.3.9
ARG USER_HOME_DIR="/root"
RUN mkdir -p /usr/share/maven && 
curl -fsSL http://apache.osuosl.org/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-
$MAVEN_VERSION-bin.tar.gz | tar -xzC /usr/share/maven --strip-components=1 && 
ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"
# speed up Maven JVM a bit
ENV MAVEN_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"
ENTRYPOINT ["/usr/bin/mvn"]
# make source folder
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# install maven dependency packages (keep in image)
COPY pom.xml /usr/src/app
RUN mvn -T 1C install && rm -rf target
# copy other source files (keep in image)
COPY src /usr/src/app/src
Java App Dockerfile
FROM openjdk:8-jre-alpine
COPY spring-boot-*.war /app.war
CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=test", "/app.war"]
263 MB 143 MB
Build Pipeline Orchestration
GNU make
Docker multi-stage build
Next Container101 Webinar
Makefile
…
builder:
    docker build -t $(NS)/builder:mvn -f Dockerfile.build .
mvn-package: builder
    docker run -it --rm -v $(shell pwd)/target:/usr/src/app/target $(NS)/builder:mvn
package -T 1C -o -Dmaven.test.skip=true
mvn-test: builder
    docker run -it --rm -v $(shell pwd)/target:/usr/src/app/target $(NS)/builder:mvn
-T 1C -o test
docker:
    docker build -t $(NS)/$(REPO):$(VERSION) target
build: builder
    make docker
push:
    docker push $(NS)/$(REPO):$(VERSION)
release: build
    make push -e VERSION=$(VERSION)
…
default: build
version: '1.0'
steps:
mvn_builder:
type: build
description: create Maven builder
dockerfile: Dockerfile.build
image_name: alexeiled/mvn-builder
mvn_test:
description: run unit tests
image: ${{mvn_builder}}
commands:
- mvn -T 1C -o test
mvn_package:
description: package application WAR
image: ${{mvn_builder}}
commands:
- mvn package -T 1C -o -Dmaven.test.skip=true
build_image:
type: build
description: create Docker image with application WAR
dockerfile: Dockerfile
working_directory: ${{main_clone}}/target
image_name: alexei-led/sbdemo
image_push:
type: push
description: push to DockerHub
candidate: '${{build_image}}'
tag: ‘${{CF_BRANCH}}'
Codefresh YAML
https://codefresh.io/blog/java_docker_pipeline/
Next Steps
• Try Codefresh free- www.codefresh.io
• Additional info on Docker - www.codefresh.io/blog
• Meetups & Webinars - www.codefresh.io/meetups
• Twitter - @codefresh

Webinar: Creating an Effective Docker Build Pipeline for Java Apps

  • 1.
    Effective Java Docker Pipeline Effectivebuild for lean and secure Java Docker images
  • 2.
    About me… •Chief ofResearch @codefresh.io •github.com/alexei-led/pumba •#docker, #golang, #aws •medium.com/@alexeiled •@alexeiled
  • 3.
    The “Naive” Approach Followfamiliar VM build and install flow # start from ubuntu FROM ubuntu:14.04 # add required packages and java repository RUN apt-get update && apt-get install -y python-software-properties software-properties- common RUN add-apt-repository ppa:webupd8team/java # install Oracle JDK 8 with auto-accept license agreement RUN echo "oracle-java8-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections RUN apt-get update && apt-get install -y oracle-java8-installer maven # add ALL project files ADD . /usr/local/app # build Java application with Maven (fetch packages, compile, test and deploy) RUN cd /usr/local/app && mvn install # define default command to run the application CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=test", "/usr/local/app/target/ spring-boot-rest-example-0.3.0.war"] Docker Image Size = 1.3 GB Docker Built Time = 27 min
  • 4.
    “Standard” Approach FROM java:8 #Install maven RUN apt-get update RUN apt-get install -y maven WORKDIR /code # Prepare by downloading dependencies ADD pom.xml /code/pom.xml RUN ["mvn", "dependency:resolve"] RUN ["mvn", "verify"] # Adding source, compile and package into a fat jar ADD src /code/src RUN ["mvn", "package"] EXPOSE 4567 CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=test", "target/spring-boot-rest- example-0.3.0.war"] Docker Image Size = 1.2 GB Docker Built Time = 15 min Take Library OpenJDK image, add pom.xml first…
  • 5.
    Stay away fromthese abstractions Containers are not VM and they are not like “lightweight” VM
  • 6.
    –Joe Fernandes, seniordirector, OpenShift Product Management, Red Hat “A Linux container is nothing more than a process that runs on Linux. It shares a host kernel with other containerized processes.”
  • 7.
    What is thebare minimum required to run Java App? 1. Base Image with C Runtime and Posix shell (Alpine) 2. Java Runtime Environment (OpenJDK JRE) 3. Application byte-code and resources (app.jar) 4. 3rd Party Libraries (lib/*.jar) 5. Optionally HTTP server (Tomcat/Jetty/Netty/…)
  • 8.
    Docker Image Size •time to build • network latency • storage • service availability and elasticity • security • development agility
  • 9.
    Docker Builder Pattern •2 Dockerfiles • 1st for build tools • 2nd for runtime
  • 10.
    Java Docker Builder 1.Base Image with C Runtime and Posix shell (Alpine) 2. Java Development Kit (OpenJDK) 3. Javac or other JVM compiler (Scala, Kotlin, …) 4. Build Management Tool (Maven, SBT, Gradle, …) 5. Linters, code scanners, test frameworks, test tools, …
  • 11.
    Maven Builder Dockerfile FROMopenjdk:8-jdk-alpine RUN apk add --no-cache curl tar bash ARG MAVEN_VERSION=3.3.9 ARG USER_HOME_DIR="/root" RUN mkdir -p /usr/share/maven && curl -fsSL http://apache.osuosl.org/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven- $MAVEN_VERSION-bin.tar.gz | tar -xzC /usr/share/maven --strip-components=1 && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn ENV MAVEN_HOME /usr/share/maven ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" # speed up Maven JVM a bit ENV MAVEN_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1" ENTRYPOINT ["/usr/bin/mvn"] # make source folder RUN mkdir -p /usr/src/app WORKDIR /usr/src/app # install maven dependency packages (keep in image) COPY pom.xml /usr/src/app RUN mvn -T 1C install && rm -rf target # copy other source files (keep in image) COPY src /usr/src/app/src
  • 12.
    Java App Dockerfile FROMopenjdk:8-jre-alpine COPY spring-boot-*.war /app.war CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=test", "/app.war"]
  • 13.
  • 14.
    Build Pipeline Orchestration GNUmake Docker multi-stage build Next Container101 Webinar
  • 15.
    Makefile … builder:     docker build -t$(NS)/builder:mvn -f Dockerfile.build . mvn-package: builder     docker run -it --rm -v $(shell pwd)/target:/usr/src/app/target $(NS)/builder:mvn package -T 1C -o -Dmaven.test.skip=true mvn-test: builder     docker run -it --rm -v $(shell pwd)/target:/usr/src/app/target $(NS)/builder:mvn -T 1C -o test docker:     docker build -t $(NS)/$(REPO):$(VERSION) target build: builder     make docker push:     docker push $(NS)/$(REPO):$(VERSION) release: build     make push -e VERSION=$(VERSION) … default: build
  • 16.
    version: '1.0' steps: mvn_builder: type: build description:create Maven builder dockerfile: Dockerfile.build image_name: alexeiled/mvn-builder mvn_test: description: run unit tests image: ${{mvn_builder}} commands: - mvn -T 1C -o test mvn_package: description: package application WAR image: ${{mvn_builder}} commands: - mvn package -T 1C -o -Dmaven.test.skip=true build_image: type: build description: create Docker image with application WAR dockerfile: Dockerfile working_directory: ${{main_clone}}/target image_name: alexei-led/sbdemo image_push: type: push description: push to DockerHub candidate: '${{build_image}}' tag: ‘${{CF_BRANCH}}' Codefresh YAML
  • 17.
  • 18.
    Next Steps • TryCodefresh free- www.codefresh.io • Additional info on Docker - www.codefresh.io/blog • Meetups & Webinars - www.codefresh.io/meetups • Twitter - @codefresh