I have some projects where I need bison. I decided to build my image when I didn't find official or fresh unofficial docker images.
To compile bison you need to install some apt dependencies and compile the last version of autoconf
from the source code.
I decided to make it from debian:bullseye-slim
image. It is the latest Debian light image which size is 80.5MB (without compression).
docker images --format="table {{.Repository}}\t{{.Tag}}\t{{ .Size }}" | grep "REPOSITORY\|debian"
REPOSITORY TAG SIZE
debian bullseye-slim 80.5MB
bison-test:1.0
First I make a simple Dockerfile
and build it.
FROM debian:bullseye-slim
ENV BISON_VERSION v3.8.2
ENV AUTOCONF_VERSION 2.71
# install apt dependencies
RUN set -eux; \
apt-get update; \
apt-get install -y \
automake \
autopoint \
ca-certificates \
flex \
gettext \
gcc \
git \
gperf \
graphviz \
help2man \
libc6-dev \
m4 \
make \
texinfo \
wget \
xsltproc
# compile autoconf
RUN wget ftp://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz
RUN tar -xvzf autoconf-$AUTOCONF_VERSION.tar.gz
WORKDIR /autoconf-$AUTOCONF_VERSION
RUN ./configure
RUN make
RUN make install
WORKDIR /
# compile bison
RUN git clone --branch=$BISON_VERSION --depth=1 https://github.com/akimd/bison.git /bison
WORKDIR /bison
RUN git submodule update --init --recursive
RUN ./bootstrap
RUN ./configure
RUN make
RUN make install
WORKDIR /
docker build -t bison-test:1.0 .
Ok. Image contains many layers
docker history bison-test:1.0 --format="table {{.CreatedBy }}\t{{ .Size }}" | grep -v 0B
CREATED BY SIZE
RUN /bin/sh -c make install # buildkit 5.86MB
RUN /bin/sh -c make # buildkit 22.3MB
RUN /bin/sh -c ./configure # buildkit 2.02MB
RUN /bin/sh -c ./bootstrap # buildkit 18.1MB
RUN /bin/sh -c git submodule update --init -… 137MB
RUN /bin/sh -c git clone --branch=$BISON_VER… 6.55MB
RUN /bin/sh -c make install # buildkit 3.46MB
RUN /bin/sh -c make # buildkit 990kB
RUN /bin/sh -c ./configure # buildkit 127kB
RUN /bin/sh -c tar -xvzf autoconf-$AUTOCONF_… 6.88MB
RUN /bin/sh -c wget ftp://ftp.gnu.org/gnu/au… 2MB
RUN /bin/sh -c set -eux; apt-get update;… 348MB
/bin/sh -c #(nop) ADD file:3ea7c69e4bfac2ebb… 80.5MB
and the total size is 633MB.
docker images --format="table {{.Repository}}\t{{.Tag}}\t{{ .Size }}" | grep "REPOSITORY\|bison-test"
REPOSITORY TAG SIZE
bison-test 1.0 633MB
bison-test:2.0
Let's reduce layers.
Instead of creating many separate RUN
commands, I wrote two scripts.
RUN set -eux; \
wget ftp://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz; \
tar -xvzf autoconf-$AUTOCONF_VERSION.tar.gz; \
cd /autoconf-$AUTOCONF_VERSION; \
./configure; \
make; \
make install
RUN set -eux; \
git clone --branch=$BISON_VERSION --depth=1 https://github.com/akimd/bison.git /bison; \
cd /bison; \
git submodule update --init --recursive; \
./bootstrap; \
./configure; \
make; \
make install
The number of layers has decreased
docker history bison-test:2.0 --format="table {{.CreatedBy }}\t{{ .Size }}" | grep -v 0B
CREATED BY SIZE
RUN /bin/sh -c set -eux; git clone --branch… 188MB
RUN /bin/sh -c set -eux; wget ftp://ftp.gnu… 13.5MB
RUN /bin/sh -c set -eux; apt-get update;… 348MB
/bin/sh -c #(nop) ADD file:3ea7c69e4bfac2ebb… 80.5MB
but the total size is only 3 MB smaller.
docker images --format="table {{.Repository}}\t{{.Tag}}\t{{ .Size }}" | grep "REPOSITORY\|bison-test"
REPOSITORY TAG SIZE
bison-test 2.0 630MB
bison-test:3.0
Now I'll try to remove all the source files and add another RUN
command to remove the apt dependencies and the apt cache.
RUN set -eux; \
wget ftp://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz; \
tar -xvzf autoconf-$AUTOCONF_VERSION.tar.gz; \
cd /autoconf-$AUTOCONF_VERSION; \
./configure; \
make; \
make install; \
rm -rf /autoconf-$AUTOCONF_VERSION.tar.gz; \ # remove unnecessary source files
rm -rf /autoconf-$AUTOCONF_VERSION # remove unnecessary source files
RUN set -eux; \
git clone --branch=$BISON_VERSION --depth=1 https://github.com/akimd/bison.git /bison; \
cd /bison; \
git submodule update --init --recursive; \
./bootstrap; \
./configure; \
make; \
make install; \
rm -rf /bison # remove unnecessary source files
# remove apt dependencies and cache
RUN set -eux; \
apt-mark auto '.*' > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
rm -rf /var/lib/apt/lists/*
docker history bison-test:3.0 --format="table {{.CreatedBy }}\t{{ .Size }}" | grep -v 0B
CREATED BY SIZE
RUN /bin/sh -c set -eux; apt-mark auto '… 1.96MB
RUN /bin/sh -c set -eux; git clone --branch… 5.83MB
RUN /bin/sh -c set -eux; wget ftp://ftp.gnu… 3.46MB
RUN /bin/sh -c set -eux; apt-get update;… 330MB
/bin/sh -c #(nop) ADD file:3ea7c69e4bfac2ebb… 80.5MB
The total size is 422MB. It's 211MB smaller than the first image bison-test:1.0
docker images --format="table {{.Repository}}\t{{.Tag}}\t{{ .Size }}" | grep "REPOSITORY\|bison-test"
REPOSITORY TAG SIZE
bison-test 3.0 422MB
bison-test:4.0
The last image is smaller, but too large compared to the original debian:bullseye-slim
image.
When you see the previous image layers, you may notice that the apt-install
layer is very big and the last command does not reduce the size of the image.
I need to install dependencies, compile bison and remove dependencies inside one layer.
Let's do it.
FROM debian:bullseye-slim
ENV BISON_VERSION v3.8.2
ENV AUTOCONF_VERSION 2.71
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
automake \
autopoint \
ca-certificates \
flex \
gettext \
gcc \
git \
gperf \
graphviz \
help2man \
libc6-dev \
m4 \
make \
texinfo \
wget \
xsltproc; \
\
wget ftp://ftp.gnu.org/gnu/autoconf/autoconf-$AUTOCONF_VERSION.tar.gz; \
tar -xvzf autoconf-$AUTOCONF_VERSION.tar.gz; \
cd /autoconf-$AUTOCONF_VERSION; \
./configure; \
make; \
make install; \
rm -rf /autoconf-$AUTOCONF_VERSION.tar.gz; \
rm -rf /autoconf-$AUTOCONF_VERSION; \
cd /; \
\
git clone --branch=$BISON_VERSION --depth=1 https://github.com/akimd/bison.git /bison; \
cd /bison; \
git submodule update --init --recursive; \
./bootstrap; \
./configure; \
make; \
make install; \
rm -rf /bison; \
cd /; \
\
apt-mark auto '.*' > /dev/null; \
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
rm -rf /var/lib/apt/lists/*
Great! The image has only two layers
docker history bison-test:4.0 --format="table {{.CreatedBy }}\t{{ .Size }}" | grep -v 0B
CREATED BY SIZE
RUN /bin/sh -c set -eux; apt-get update;… 11.3MB
/bin/sh -c #(nop) ADD file:3ea7c69e4bfac2ebb… 80.5MB
and the total size is 91.8 MB!
docker images --format="table {{.Repository}}\t{{.Tag}}\t{{ .Size }}" | grep "REPOSITORY\|bison-test"
REPOSITORY TAG SIZE
bison-test 4.0 91.8MB
Now you know a few simple steps to make your image smaller:
- reduce the number of layers
- remove unnecessary files/dependencies inside the layer where you created it
You can see the original bison Dockerfile here.
Top comments (3)
Great tutorial on reducing the size of Docker images by optimizing layers and using scripts to minimize dependencies. Well done!
Hi Anton. Thanks for sharing this with the community.
I am not too experienced into the process of optimizing the size of container image but I am surprised that the size really depends on how many layers you have.
This is a great set of instructions, thank you for sharing. I can't wait to try these out on the next image I create.