Using Docker multi-stage build

Docker multi-stage build is a perfect way to build software from source into Docker images.
Lets take a closer look how we build Docker images for SkyWalking distributed tracing.
We build our images ready to be deployed into Kubernetes. They are stripped off of all config files and only contain necessary binary files for services to run.
We are trying to use minimal required base images like "scratch" for Go applications or Alpine for everything else.
Alpine-based images from openjdk are perfect candidates for a quick re-use.
In our build we'd like to:
a) build required version of the app from source code
b) package ready binary files into Docker containers
c) tag newly created images and push them into Docker Hub
Stage A - Build
Prerequisites for SkyWalking build are quite well documented and all steps are clear. Lets put them all into a Dockerfile:
FROM maven:3.3.3-jdk-8 as builder LABEL image=skywalking-build ARG RELEASE_VERSION=v5.0.0-GA ENV TAG_NAME=$RELEASE_VERSION ENV SKYWALKING_BUILD_HOME=/usr/local/skywalking RUN git clone https://github.com/apache/incubator-skywalking.git $SKYWALKING_BUILD_HOME WORKDIR $SKYWALKING_BUILD_HOME/ RUN git checkout $TAG_NAME RUN git submodule init RUN git submodule update RUN set -ex; \ ./mvnw clean package -DskipTests
Run "docker build" in the same directory as a Dockerfile to start the build. It will use Base Image from maven and assign a temporary name "builder" to the image(later it will help us to extract build artefacts).
Name "builder" will be available only during the "build" stage, so to identify ready Docker images we will use keyword "LABEL".
Stage B - Prepare containers
Our source code build produces 2 applications: collector and webapp. We are going to package each of them into a separate Docker Image.
skywalking-collector
FROM openjdk:8-jre-alpine LABEL image=skywalking-collector MAINTAINER Andrey KozichevENV JAVA_OPTS="-Xms256M -Xmx512M" ENV DIST_NAME=apache-skywalking-apm-incubating ENV SKYWALKING_BUILD_HOME=/usr/local/skywalking ENV SKYWALKING_HOME /opt/skywalking/ COPY --from=builder $SKYWALKING_BUILD_HOME/dist/$DIST_NAME.tar.gz $SKYWALKING_HOME WORKDIR $SKYWALKING_HOME/ RUN set -ex; \ tar -xzf "$DIST_NAME.tar.gz"; \ rm -rf "$DIST_NAME.tar.gz"; rm -rf "$DIST_NAME/config/log4j2.xml"; \ rm -rf "$DIST_NAME/bin"; rm -rf "$DIST_NAME/webapp"; rm -rf "$DIST_NAME/agent"; ENV APP_HOME $SKYWALKING_HOME/apache-skywalking-apm-incubating/ CMD /usr/bin/java $JAVA_OPTS -cp "$APP_HOME/collector-libs/*:$APP_HOME/config" org.apache.skywalking.apm.collector.boot.CollectorBootStartUp
skywalking-webapp
FROM openjdk:8-jre-alpine LABEL image=skywalking-ui MAINTAINER Andrey KozichevENV JAVA_OPTS="-Xms256M -Xmx512M" ENV DIST_NAME=apache-skywalking-apm-incubating ENV SKYWALKING_BUILD_HOME=/usr/local/skywalking ENV SKYWALKING_HOME /opt/skywalking/ COPY --from=builder $SKYWALKING_BUILD_HOME/dist/$DIST_NAME.tar.gz $SKYWALKING_HOME
The name "builder" we are using to extract ready artefact from the "builder" image.
To start the build we can run "docker built ." in the Dockerfile directory.
Stage C - Tag and Push
After the build part completed we will get 3 new images:
3dbfa1cdf15a 5 minutes ago 278MB 9afaf725371a 5 minutes ago 244MB c08b4dc5e233 7 minutes ago 2.19GB
The biggest image is our "build" - it has all source code and build artefacts.
To identify other images we will use Labels which we assigned in Dockerfile:
docker images --filter 'label=image=skywalking-collector' docker images --filter 'label=image=skywalking-ui' -q
The remaining part is to run "docker tag" to assign correct names of the repositories and docker push to upload them to our Docker Registry.
Few final touches to put all steps into a Dockerfile and simple shell script:
Dockerfile
FROM maven:3.3.3-jdk-8 as builder LABEL image=skywalking-build ARG RELEASE_VERSION=v5.0.0-GA ENV TAG_NAME=$RELEASE_VERSION ENV SKYWALKING_BUILD_HOME=/usr/local/skywalking RUN git clone https://github.com/apache/incubator-skywalking.git $SKYWALKING_BUILD_HOME WORKDIR $SKYWALKING_BUILD_HOME/ RUN git checkout $TAG_NAME RUN git submodule init RUN git submodule update RUN set -ex; \ ./mvnw clean package -DskipTests FROM openjdk:8-jre-alpine LABEL image=skywalking-collector MAINTAINER Andrey KozichevENV JAVA_OPTS="-Xms256M -Xmx512M" ENV DIST_NAME=apache-skywalking-apm-incubating ENV SKYWALKING_BUILD_HOME=/usr/local/skywalking ENV SKYWALKING_HOME /opt/skywalking/ COPY --from=builder $SKYWALKING_BUILD_HOME/dist/$DIST_NAME.tar.gz $SKYWALKING_HOME WORKDIR $SKYWALKING_HOME/ RUN set -ex; \ tar -xzf "$DIST_NAME.tar.gz"; \ rm -rf "$DIST_NAME.tar.gz"; rm -rf "$DIST_NAME/config/log4j2.xml"; \ rm -rf "$DIST_NAME/bin"; rm -rf "$DIST_NAME/webapp"; rm -rf "$DIST_NAME/agent"; ENV APP_HOME $SKYWALKING_HOME/apache-skywalking-apm-incubating/ CMD /usr/bin/java $JAVA_OPTS -cp "$APP_HOME/collector-libs/*:$APP_HOME/config" org.apache.skywalking.apm.collector.boot.CollectorBootStartUp FROM openjdk:8-jre-alpine LABEL image=skywalking-ui MAINTAINER Andrey Kozichev ENV JAVA_OPTS="-Xms256M -Xmx512M" ENV DIST_NAME=apache-skywalking-apm-incubating ENV SKYWALKING_BUILD_HOME=/usr/local/skywalking ENV SKYWALKING_HOME /opt/skywalking/ COPY --from=builder $SKYWALKING_BUILD_HOME/dist/$DIST_NAME.tar.gz $SKYWALKING_HOME WORKDIR $SKYWALKING_HOME/ RUN set -ex; \ tar -xzf "$DIST_NAME.tar.gz"; \ rm -rf "$DIST_NAME.tar.gz"; rm -rf "$DIST_NAME/config"; \ rm -rf "$DIST_NAME/bin"; rm -rf "$DIST_NAME/collector-libs"; rm -rf "$DIST_NAME/agent"; ENV APP_HOME $SKYWALKING_HOME/apache-skywalking-apm-incubating/ CMD /usr/bin/java $JAVA_OPTS -jar $APP_HOME/webapp/skywalking-webapp.jar --spring.config.location=$APP_HOME/webapp/config/webapp.yml
build.sh
#!/bin/bash VERSION=v5.0.0-GA docker build --build-arg RELEASE_VERSION=$VERSION . DOCKER_ORG=antler DOCKER_REPO=skywalking-collector SKYWALKING_COLLECTOR=$(docker images --filter 'label=image=skywalking-collector' -q | head -1) docker tag $SKYWALKING_COLLECTOR $DOCKER_ORG/$DOCKER_REPO:$VERSION docker push $DOCKER_ORG/$DOCKER_REPO:$VERSION DOCKER_REPO=skywalking-ui SKYWALKING_UI=$(docker images --filter 'label=image=skywalking-ui' -q | head -1) docker tag $SKYWALKING_UI $DOCKER_ORG/$DOCKER_REPO:$VERSION docker push $DOCKER_ORG/$DOCKER_REPO:$VERSION
All build files available in Github https://github.com/antler-apm/skywalking-docker