Building minimal docker images with buildroot

Auke Willem Oosterhoff

Auke willem oosterhoff

auke@orangetux.nl

github.com/OrangeTux

Advanced climate systems

Climatize room

REMOTE IO

BUildroot

Buildroot is a set of Makefiles and patches that [...] can generate any or all of a cross-compilation toolchain, a root filesystem, a kernel image and a bootloader image.

http://buildroot.uclibc.org/about.html

Docker

Docker containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.

https://www.docker.com/whatisdocker

A running container

Buildroot in a docker image

github.com/AdvancedClimateSystems/docker-buildroot

Dockerfile 1/2

FROM ubuntu:14.10
MAINTAINER Auke Willem Oosterhoff <auke@orangetux.nl>

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y \
    build-essential \
    bash \
    bc \
    binutils \
    build-essential \
    bzip2 \
    cpio \
    g++ \
    gcc \
    git \
    gzip \
    libncurses5-dev \
    make \
    mercurial \
    whois \         
    patch \
    perl \
    python \
    rsync \
    sed \
    tar \
    unzip \
    wget
...

Dockerfile 2/2

...

# Sometimes Buildroot need proper locale, e.g. when using a toolchain
# based on glibc.
RUN locale-gen en_US.utf8

WORKDIR /root
RUN git clone git://git.buildroot.net/buildroot --depth=1
WORKDIR /root/buildroot

VOLUME /root/buildroot/dl
VOLUME /buildroot_output

CMD ["/bin/bash"]

Build image

stateless containers

Data-only containers with volumes 

Changes to a data volume are made directly.

Data volumes persist even if the container itself is deleted.

https://docs.docker.com/userguide/dockervolumes/

Define data volume

...

# Sometimes Buildroot need proper locale, e.g. when using a toolchain
# based on glibc.
RUN locale-gen en_US.utf8

WORKDIR /root
RUN git clone git://git.buildroot.net/buildroot --depth=1
WORKDIR /root/buildroot

VOLUME /root/buildroot/dl
VOLUME /buildroot_output

CMD ["/bin/bash"]

Data volumes

Text

Mount local folder

Make life easy

01: #/bin/bash
02: set -e
03: 
04: OUTPUT_DIR=/buildroot_output
05: BUILDROOT_DIR=/root/buildroot
06: 
07: DOCKER_RUN="docker run 
08:    --rm 
09:    -ti 
10:    --volumes-from buildroot_output
11:    -v $(pwd)/data:$BUILDROOT_DIR/data
12:    -v $(pwd)/external:$BUILDROOT_DIR/external
13:    -v $(pwd)/rootfs_overlay:$BUILDROOT_DIR/rootfs_overlay
14:    -v $(pwd)/images:$OUTPUT_DIR/images
15:    advancedclimatesystems/buildroot"
16:
17: make() {
18:   echo "make O=$OUTPUT_DIR"
19: }
20:
21: if [ "$1" == "make" ]; then
22:     eval $DOCKER_RUN $(make) ${@:2}
23: else
24:     eval $DOCKER_RUN $@
25: fi

scripts/run.sh

Configure buildroot

create defconfig

Build rootfs

Create container

Why do small images matter?

"Over 30% of Official Images in Docker Hub Contain High Priority Security Vulnerabilities" 1

1: http://www.banyanops.com/blog/analyzing-docker-hub/

"Avoid installing unnecessary packages."2

Less resources

2: https://docs.docker.com/articles/dockerfile_best-practices/

add pip (and wheel)

config BR2_PACKAGE_PYTHON_PIP
    bool "python-pip"
    select BR2_PACKAGE_PYTHON_PYEXPAT 
    select BR2_PACKAGE_PYTHON_SETUPTOOLS
    depends on BR2_PACKAGE_PYTHON
    help
      The PyPA recommended tool for installing Python packages.

      https://pip.pypa.io/en/stable/index.html
#################################################################################
#
## python-pip
#
#################################################################################

PYTHON_PIP_VERSION = 7.1.2
PYTHON_PIP_SOURCE = pip-$(PYTHON_PIP_VERSION).tar.gz
PYTHON_PIP_SITE = http://pypi.python.org/packages/source/p/pip/
PYTHON_PIP_SETUP_TYPE = setuptools
PYTHON_PIP_DEPENDENCIES = python python-setuptools
PYTHON_PIP_LICENSE = MIT
PYTHON_PIP_LICENSE_FILES = LICENSE.txt

$(eval $(python-package))

external/packages/python-pip.mk

external/packages/Config.in

Full defconfig

BR2_x86_64=y
BR2_TOOLCHAIN_BUILDROOT_GLIBC=y
BR2_ROOTFS_OVERLAY="/root/buildroot/rootfs_overlay"
BR2_PACKAGE_PYTHON=y
BR2_PACKAGE_PYTHON_READLINE=y
BR2_PACKAGE_CA_CERTIFICATES=y
BR2_PACKAGE_OPENSSL=y
BR2_PACKAGE_OPENSSL_BIN=y
BR2_PACKAGE_PYTHON_PIP=y
BR2_PACKAGE_PYTHON_WHEEL=y

external/configs/docker_python2_defconfig

Minimal setup with Nginx-uwsgi-flask-redis

github.com/OrangeTux/minimal-docker-python-setup

server

from flask import Flask, request, jsonify
from flask.ext.redis import FlaskRedis

app = Flask(__name__)
app.config.update(
    REDIS_URL="redis://redis:6379/0"
)

redis_store = FlaskRedis(app)


@app.route('/')
def index():
    addr = request.remote_addr
    redis_store.incr(addr)
    visits = redis_store.get(addr)

    return jsonify({
        'ip': addr,
        'visits': visits,
    })

C Extension 1/2

user@host$ pip install --upgrade pip setuptools wheel
user@host$ pip wheel --wheel-dir=wheelhouse flask flask-redis uwsgi
user@host$ ls -al app/wheelhouse
total 1312
drwxr-xr-x 2 auke auke   4096 Sep  5 11:38 .
drwxr-xr-x 5 auke auke   4096 Sep  5 09:20 ..
-rw-r--r-- 1 auke auke 115938 Sep  5 11:37 Flask-0.10.1-py2-none-any.whl
-rw-r--r-- 1 auke auke   8667 Sep  5 11:37 Flask_Redis-0.1.0-py2-none-any.whl
-rw-r--r-- 1 auke auke  10432 Sep  5 11:38 itsdangerous-0.24-py2-none-any.whl
-rw-r--r-- 1 auke auke 263888 Sep  5 09:20 Jinja2-2.8-py2.py3-none-any.whl
-rw-r--r-- 1 auke auke  26013 Sep  5 11:38 MarkupSafe-0.23-cp27-none-linux_x86_64.whl
-rw-r--r-- 1 auke auke  59643 Sep  5 11:38 redis-2.10.3-py2-none-any.whl
-rw-r--r-- 1 auke auke 538564 Sep  5 11:38 uWSGI-2.0.11.1-cp27-none-linux_x86_64.whl
-rw-r--r-- 1 auke auke 293089 Sep  5 09:20 Werkzeug-0.10.4-py2.py3-none-any.whl

C ExtenSion 2/2

FROM advancedclimatesystems/python:2.7.10
MAINTAINER Auke Willem Oosterhoff <auke@orangetux.nl>

COPY wheelhouse /wheelhouse

RUN pip install --find-links wheelhouse \
    flask \
    flask-redis \
    uwsgi

COPY src /app
COPY uwsgi.ini /etc/uwsgi.ini
COPY shared_libs/* /lib/

CMD uwsgi --ini /etc/uwsgi.ini
user@host$ ldd uwsgi | grep -e lzma -e xml -e pcre
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f2ad8b91000)
libxml2.so.2 => /usr/lib/x86_64-linux-gnu/libxml2.so.2 (0x00007f2ad81d7000)
liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f2ad72ed000)

app/Dockerfile

nignx and redis

BR2_x86_64=y
BR2_PACKAGE_NGINX=y

nginx/docker_nginx_defconfig

BR2_x86_64=y
BR2_PACKAGE_ZLIB=y
BR2_PACKAGE_REDIS=y
BR2_PACKAGE_PCRE=y

redis/docker_redis_defconfig

FROM orangetux/redis
MAINTAINER Auke Willem Oosterhoff <auke@orangetux.nl>

CMD redis-server --bind 0.0.0.0

redis/Dockerfile

FROM orangetux/nginx 
MAINTAINER Auke Willem Oosterhoff <auke@orangetux.nl>

COPY nginx.conf /etc/nginx/conf.d/default.conf

CMD nginx -c /etc/nginx/conf.d/default.conf

nginx/Dockerfile

Docker-compose

app:
    build: app
    links:
        - redis

nginx:
    build: nginx
    ports:
        - "1337:80"
    links:
        - app

redis:
    build: redis

docker-compose.yml

Show us!

Size

(Re)sources

  • github.com/OrangeTux/minimal-docker-python-setup
  • github.com/AdvancedClimateSystems/docker-buildroot
  • blog.docker.com/2013/06/create-light-weight-docker-containers-buildroot/
  • hub.docker.com/r/advancedclimatesystems/python/
  • buildroot.uclibc.org/downloads/manual/manual.html

Building minimal Docker images with Buildroot

By Auke Willem

Building minimal Docker images with Buildroot

  • 3,523