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 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/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 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

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 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/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

Subscribe to Antler - Applied Tracing

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe