<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/building-a-repository-of-alpine-based-docker-images-for-r-part-i/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://velaco.github.io/my-dockerfile-for-r-shiny-based-on-alpine-linux/"> Curiosity Killed the Cat</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p><img alt="Shiny Alpine Docker" src="https://i2.wp.com/velaco.github.io/images/shiny-alpine-docker/alpine-shiny.png?w=456&ssl=1" data-recalc-dims="1"></p>
<p>The <a rel="nofollow" href="https://hub.docker.com/u/rocker/" target="_blank">Rocker Project</a> maintains the official Docker images of interest to R users. I use their images as a base to deploy containerized Shiny apps, but the virtual size of the images I build tends to fall in the range between 400 and 600 MB. To reduce the size of my images, I decided to try building a Shiny Server on Alpine Linux as an alternative to Rocker’s Debian-based images. In this series of articles, I’ll document my progress from building a base image with R to building an image with Shiny Server. The Dockerfiles included in this article can be found at the <a rel="nofollow" href="https://github.com/velaco/alpine-r" target="_blank">velaco/alpine-r repository</a>.</p>
<p><span></span></p>
<h2>About Alpine Linux</h2>
<p><a rel="nofollow" href="https://alpinelinux.org/" target="_blank">Alpine Linux</a> is a lightweight distribution that is popular for deploying containerized apps because the virtual size of an Alpine Docker image starts at 5 MB. The virtual size of images based on other distributions exceeds 100 MB before adding anything else to them, so Alpine achieves a significant reduction in size.</p>
<p>Some key differences between Alpine and other distributions that are relevant to my project are:</p>
<ul>
<li>Alpine uses <em>musl lib</em> as a lightweight alternative to <em>glibc</em>. That means precompiled installers built on platforms with <em>glibc</em> could be useless, even if I install <em>libc6-compat</em> or <em>glibc</em> packages for Alpine Linux.</li>
<li>Alpine drops old versions of packages when new versions become available, making version pinning impossible, so I probably won’t be able to use nodejs and npm packages from the official repositories to build Shiny Server.</li>
</ul>
<p>Because of those characteristics, I assume that building the images will require compiling most key dependencies from source. I’ve never done that before, so working on this project will be a great (and painful) learning experience.</p>
<h2>Build from Native Packages</h2>
<p>Building from source can often take up a lot of time, so I decided to start with the native packages to build a working image as soon as possible. Here is what my Dockerfile looked like:</p>
<div>
<div>
<pre><code><span>FROM</span><span> alpine:3.8</span>

<span>MAINTAINER</span><span> Aleksandar Ratesic &quot;aratesic@gmail.com&quot;</span>

<span># Declare environment variables ------------------</span>

<span>ENV</span><span> LC_ALL en_US.UTF-8</span>
<span>ENV</span><span> LANG en_US.UTF-8</span>

<span>ENV</span><span> BUILD_DEPS \</span>
    cairo-dev \ 
    libxmu-dev \
    openjdk8-jre-base \ 
    pango-dev \
    perl \
    tiff-dev \
    tk-dev

<span>ENV</span><span> PERSISTENT_DEPS \</span>
    R-mathlib \
    gcc \
    gfortran \
    icu-dev \
    libjpeg-turbo \
    libpng-dev \
    make \
    openblas-dev \
    pcre-dev \
    readline-dev \
    xz-dev \
    zlib-dev \
    bzip2-dev \
    curl-dev

<span># Install R and R-dev ------------------</span>

<span>RUN </span>apk upgrade <span>--update</span> <span>&amp;&amp;</span> <span>\
</span>    apk add <span>--no-cache</span> <span>--virtual</span> .build-deps <span>$BUILD_DEPS</span> <span>&amp;&amp;</span> <span>\
</span>    apk add <span>--no-cache</span> <span>--virtual</span> .persistent-deps <span>$PERSISTENT_DEPS</span> <span>&amp;&amp;</span> <span>\
</span>    apk add <span>--no-cache</span> R R-dev <span>&amp;&amp;</span> <span>\
</span>    apk del .build-deps

<span>CMD</span><span> [&quot;R&quot;, &quot;--no-save&quot;]</span>
</code></pre>
</div>
</div>
<p>The resulting image has a virtual size of 136.4 MB. That’s an improvement in size compared to the rocker/r-base image, which is 277.4MB according to <a rel="nofollow" href="https://microbadger.com/images/rocker/r-base" target="_blank">MicroBadger</a>. However, I should mention that the rocker/r-base image includes some packages like <em>litter</em> that my Dockerfile doesn’t, so it has additional features that could be of interest to R users. (Not to mention that it is also more stable for use in production than my image.)</p>
<h2>Build from Source Code</h2>
<p>I recently learned that Alpine drops old versions of packages from the repository when new versions become available ( <a rel="nofollow" href="https://medium.com/@stschindler/the-problem-with-docker-and-alpines-package-pinning-18346593e891" target="_blank">Source</a> ). That means some versions of R won’t always be available from the official repositories, so I decided to create a Dockerfile for building R from source as well. I made the following changes to the previous Dockerfile:</p>
<ul>
<li>Declared environment variables for storing the R version and target directory for the source code.</li>
<li>Added <em>wget</em> and <em>tar</em> to build dependencies for getting and unpacking the sources.</li>
<li>Added <em>libint</em> and <em>g++</em> to runtime dependencies. I learned that <em>g++</em> is necessary for building from source after the first attempt raised an error while setting up the configuration options. The first container I started failed to run R because package <em>libint</em> was not installed, so I added it later.</li>
<li>Removed the <em>R-mathlib</em> package from runtime dependencies. Rmath is installed as a standalone library from source in this image.</li>
<li>Finally, I changed the RUN command accordingly to get, configure, and install R from source instead of adding the native packages.</li>
</ul>
<p>This is what the final version of the file looked like:</p>
<div>
<div>
<pre><code><span>FROM</span><span> alpine:3.8</span>

<span>MAINTAINER</span><span> Aleksandar Ratesic &quot;aratesic@gmail.com&quot;</span>

<span>ENV</span><span> LC_ALL en_US.UTF-8</span>
<span>ENV</span><span> LANG en_US.UTF-8</span>

<span>ENV</span><span> R_VERSION 3.5.1</span>
<span>ENV</span><span> R_SOURCE /usr/local/src/R</span>

<span>ENV</span><span> BUILD_DEPS \</span>
    cairo-dev \ 
    libxmu-dev \
    openjdk8-jre-base \ 
    pango-dev \
    perl \
    tiff-dev \
    tk-dev \
    wget \
    tar

<span>ENV</span><span> PERSISTENT_DEPS \</span>
    libint \
    gcc \
    g++ \
    gfortran \
    icu-dev \
    libjpeg-turbo \
    libpng-dev \
    make \
    openblas-dev \
    pcre-dev \
    readline-dev \
    xz-dev \
    zlib-dev \
    bzip2-dev \
    curl-dev

<span>RUN </span>apk upgrade <span>--update</span> <span>&amp;&amp;</span> <span>\
</span>    apk add <span>--no-cache</span> <span>--virtual</span> .build-deps <span>$BUILD_DEPS</span> <span>&amp;&amp;</span> <span>\
</span>    apk add <span>--no-cache</span> <span>--virtual</span> .persistent-deps <span>$PERSISTENT_DEPS</span> <span>&amp;&amp;</span> <span>\
</span>    mkdir <span>-p</span> <span>$R_SOURCE</span> <span>&amp;&amp;</span> <span>cd</span> <span>$R_SOURCE</span> <span>&amp;&amp;</span> <span>\
</span>    wget https://cran.r-project.org/src/base/R-3/R-<span>${</span><span>R_VERSION</span><span>}</span>.tar.gz <span>&amp;&amp;</span> <span>\
</span>    <span>tar</span> <span>-zxvf</span> R-<span>${</span><span>R_VERSION</span><span>}</span>.tar.gz <span>&amp;&amp;</span> <span>\
</span>    <span>cd </span>R-<span>${</span><span>R_VERSION</span><span>}</span> <span>&amp;&amp;</span> <span>\
</span>    ./configure <span>--prefix</span><span>=</span>/usr/local <span>\
</span>                <span>--without-x</span> <span>&amp;&amp;</span> <span>\
</span>    make <span>&amp;&amp;</span> make install <span>&amp;&amp;</span> <span>\
</span>    <span>cd </span>src/nmath/standalone <span>&amp;&amp;</span> make <span>&amp;&amp;</span> make install <span>&amp;&amp;</span> <span>\
</span>    apk del .build-deps

<span>CMD</span><span> [&quot;R&quot;]</span>
</code></pre>
</div>
</div>
<p>The virtual size of the image it built was 311.6 MB, but it should be possible to reduce the image even further by cleaning up after building R. I didn’t want to do that yet because I still had to run the container and perform <code>make check</code> from the shell to test the installation.</p>
<p>There was one non-fatal error (raised by <code>shownNonASCIIfile()</code>) and two fatal errors raised by timezone.R and reg-tests-1c.R tests:</p>
<div>
<div>
<pre><code>  comparing ‘tools-Ex.Rout’ to ‘tools-Ex.Rout.save’ ... NOTE
  <span>---</span> /tmp/RtmpdpmEgM/Rdiffa445b3d67023
  +++ /tmp/RtmpdpmEgM/Rdiffb445b7ac41733
  @@ <span>-796</span>,8 +796,8 @@
   <span>></span> <span>cat</span><span>(</span>out, file <span>=</span> f, sep <span>=</span> <span>&quot;</span><span>\n</span><span>&quot;</span><span>)</span>
   <span>></span>
   <span>></span> showNonASCIIfile<span>(</span>f<span>)</span>
  <span>-1</span>: fa<span>*</span>ile <span>test </span>of showNonASCII<span>()</span>:
  <span>-4</span>:    This has an <span>*</span>mlaut <span>in </span>it.
  +1: fa</code></pre></div></div>