Türchen #17: Docker Multistage Builds


Wegweisende Architektur

Docker Architekturen erleichtern das Bereitstellen hochkomplexer Anwendungen. Für uns Entwickler bedeutet das, effizient und wiederverwendbar Systeme aufbauen zu können, die auch lokal während der Entwicklungsphase identisch verwendet werden.

Mit ein paar Kniffen können wir zudem auch sehr schlanke Docker Images bereitstellen, die tatsächlich nur das enthalten, was die Applikation verlangt.

Einsatz-Szenario 1 - Building Dependencies

Stellt Euch eine Java Application vor, welche nach dem Compile lediglich das fertige .jar File benötigt. Dieses sogenannte “Artefakt” ist das Einzige, was später in Zielumgebungen wie “Staging” und “Production” relevant ist. Somit können wir zwar beim Build-Prozess nicht auf Tools wie Maven oder Gradle verzichten, aber im Ziel-Container-Image ist neben der Java Runtime nur noch das fertiggebaute .jar File nötig:

Erstellen wir uns also im ersten Schritt ein Build-Image auf Basis von openJDK 8 und kopieren uns am Ende nur das resultierende .jar File daraus:

## unser Basis Image, welches die Java App baut:
FROM openjdk:8 AS BUILD_IMAGE
ENV APP_HOME=/root/dev/myapp/
RUN mkdir -p $APP_HOME/src/main/java
WORKDIR $APP_HOME
COPY build.gradle gradlew gradlew.bat $APP_HOME
COPY gradle $APP_HOME/gradle
# Dependencies
RUN ./gradlew build -x :bootRepackage -x test --continue
COPY . .
RUN ./gradlew build

## ab hier interessiert uns nur noch das .jar File:
FROM openjdk:8-jre
WORKDIR /root/
COPY --from=BUILD_IMAGE /root/dev/myapp/build/libs/myapp.jar .
EXPOSE 8080
CMD ["java","-jar","myapp.jar"]

Einsatz-Szenario 2 - Wiederverwendbarkeit und Image-Flavors

Angenommen, wir wollen PHP Images in verschiedenen Versionen und Varianten bauen. Da sich der Code im Dockerfile doch recht deutlich wiederholt und überflüssig aufbläht, kürzen wir es wie folgt: Wir bauen uns ein schlankes Alpine Image (“base”) und leiten davon dann Varianten ab, etwa “php-dev” oder “php-fpm”.

ARG ALPINE_VERSION

###########################################################

FROM alpine:${ALPINE_VERSION} as base

LABEL maintainer="Jérôme Gamez <jerome@kreait.com>"

COPY docker /docker/

# See https://github.com/gliderlabs/docker-alpine/issues/184
RUN \
  sed -i 's/http\:\/\/dl-cdn.alpinelinux.org/https\:\/\/alpine.global.ssl.fastly.net/g' /etc/apk/repositories && \
  apk update && apk upgrade

RUN /docker/scripts/install-packages.sh \
    && /docker/scripts/ensure-www-data.sh \
    && mv /docker/php-entrypoint /usr/local/bin/

ENTRYPOINT ["php-entrypoint"]

CMD ["php", "-a"]

###########################################################

FROM base AS php

RUN rm -rf /docker

###########################################################

FROM base as php-dev

RUN /docker/scripts/install-dev-packages.sh \
    && rm -rf /docker

###########################################################

FROM base AS base-fpm

RUN apk --no-cache add php7-fpm \
    && mv /docker/www.conf /etc/php7/php-fpm.d/www.conf

EXPOSE 9000

CMD ["php-fpm7"]

###########################################################

FROM base-fpm as php-fpm

RUN rm -rf /docker

###########################################################

FROM base-fpm as php-fpm-dev

RUN /docker/scripts/install-dev-packages.sh \
    && rm -rf /docker

Mit diesem einen Dockerfile können wir nun z. B. zwei PHP-Versionen (7.2 und 7.3) bauen (wichtig ist hier die --target Option sowie die Angabe der ALPINE_VERSION):


  docker build --build-arg ALPINE_VERSION=3.9 --target php -t php:7.2 -f Dockerfile
  docker build --build-arg ALPINE_VERSION=3.10 --target php -t php:7.3 -f Dockerfile

Den gesamten Sourcecode gibt es übrigens auf Github, inspiriert vom geschätzten Kollegen Jérôme Gamez.

Zurück zu den Blogbeiträgen

Trainings und Workshops

Vom Kurs für Einsteiger bis hin zu Experten-Workshops mit Tiefgang können wir Euch mit technischen Trainings unterstützen. Darüber hinaus bieten wir auch Workshops mit Fokus auf Cloud-Strategie oder für die Vertriebsmannschaft an.

Mehr...