Using ccache when building inside of docker

You can still use ccache in conjunction with your build.

Create a Data Volume to allow data to persist between compilations/builds using the following command:

$ docker create -v /mnt/ccache:/ccache --name ccache debian

Then create your container that “mounts” the data container created above using the --volumes-from command line option.

$ docker run -e CCACHE_DIR=/ccache --volumes-from ccache -it debian

Now you'll be in the shell of the debian container and can install the required apps and test ccache:

root@15306d02505a:/# apt-get update && apt-get install -y gcc ccache    

Now at this point you can check the cache, and it will be empty as expected:

root@15306d02505a:/# ccache -s
cache directory                     /ccache
cache hit (direct)                     0
cache hit (preprocessed)               0
cache miss                             0
files in cache                         0
cache size                             0 Kbytes
max cache size                       1.0 Gbytes

The data volume will persist, so even after the container is terminated, the cache is still there. Future builds that mount the volume (and specify the -e ENV variable) will utilize the cache.

Then create a simple app, run it, and check the cache again:

root@15306d02505a:/# cat > foo.c << __EOF__
 int main(int argc, char **argv)
 {
     return 0;
 }
 __EOF__

root@15306d02505a:/# PATH=/usr/lib/ccache:$PATH gcc -o foo.o -c foo.c
root@15306d02505a:/# ccache -s
cache directory                     /ccache
cache hit (direct)                     1
cache hit (preprocessed)               0
cache miss                             1
files in cache                         2
cache size                             8 Kbytes
max cache size                       1.0 Gbytes

You can see the cache is now populated, and further builds will see performance improvements because of it.

The data volume will persist, so even after the container is terminated, the cache is still there. Future builds that mount the volume (and specify the -e ENV variable) will utilize the cache.

This blog post does a good job of explaining it:

Using Ccache with Docker


OK as promised.

Prereqs

  1. Be on Docker 18.06 or above

  2. Run in experimental mode, on client and server (at time of writing)

Setup experimental Mode

Server: I created the following systemd override file:

cat /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --experimental -H fd:// 

I then did a systemctl daemon-reload ; systemctl restart docker.service

Client side you need to set and env variable export DOCKER_BUILDKIT=1

Docker file

This is the basis of the docker file. I am building a php image, but you can build whatever you want. The key parts of the 1st line comment (needed to tell docker to parse the file in experimental mode), and the --mount=type=cache,target=/ccache/ option. This pulls in the cache folder for that stage of the build. Make sure you put it on every RUN line where you trigger a compile

# syntax = docker/dockerfile:experimental
FROM php:7.3-fpm-stretch

# Create app directory
WORKDIR /usr/src/app

# setup locales
RUN apt update && \
  apt install -y locales && \
  sed -i -e 's/# en_GB.UTF-8 UTF-8/en_GB.UTF-8 UTF-8/' /etc/locale.gen && \
  locale-gen

ENV LANGUAGE=en_GB.UTF-8
ENV LANG=en_GB.UTF-8
ENV LC_ALL=en_GB.UTF-8
ENV CCACHE_DIR=/ccache

RUN apt update ; apt install -yq \
        git \
        cloud-guest-utils \
        iproute2 \
        libxml2-dev \
        libxslt1-dev \
        libmemcached-dev \
        libgd-dev \
        libzip-dev \
        libmemcached-dev \
        ccache \
        awscli


# use ccache (make it appear in path earlier then /usr/bin/gcc etc)
RUN for p in gcc g++ cc c++; do ln -vs /usr/bin/ccache /usr/local/bin/$p;  done

# prod format
RUN --mount=type=cache,target=/ccache/ docker-php-source extract && \
    docker-php-ext-install \
    intl \
    bcmath  \
    calendar \
    exif \
    gd \
    gettext \
    mysqli \
    opcache \
    pcntl \
    pdo_mysql \
    shmop \
    soap \
    sockets \
    sysvmsg \
    sysvsem \
    sysvshm \
    xsl \
    zip \
  && \
  docker-php-source delete
RUN --mount=type=cache,target=/ccache/ ccache -s

Sample output

I am running the build with the --progress plain flag as the output in experimental is very different

#25 [16/16] RUN --mount=type=cache,target=/ccache/ ccache -s 
#25 digest: sha256:98c661a0404c71176a4bbf7d02123184524a784fabb2575d5210da088f16ee3a 
#25 name: "[16/16] RUN --mount=type=cache,target=/ccache/ ccache -s" 
#25 started: 2019-07-01 09:35:15.158199623 +0000 UTC 
#25 0.468 cache directory /ccache 
#25 0.468 primary config /ccache/ccache.conf 
#25 0.468 secondary config (readonly) /etc/ccache.conf 
#25 0.468 cache hit (direct) 2450 
#25 0.468 cache hit (preprocessed) 152 
#25 0.468 cache miss 590 
#25 0.468 cache hit rate 81.52 % 
#25 0.468 called for link 163 
#25 0.468 called for preprocessing 1011 
#25 0.468 compile failed 33 
#25 0.468 preprocessor error 3 
#25 0.468 bad compiler arguments 188 
#25 0.468 autoconf compile/link 943 #25 0.468 no input file 554 
#25 0.468 cleanups performed 0 
#25 0.468 files in cache 1288 #25 0.468 cache size 20.6 MB
#25 0.468 max cache size 5.0 GB 
#25 completed: 2019-07-01 09:35:15.732702254 +0000 UTC 
#25 duration: 574.502631ms

More readings here: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md https://docs.docker.com/engine/reference/commandline/dockerd/#description

Tags:

Docker

Ccache