Reducing Docker image size for a Perl6 Cro webapp
Tags: perl6, docker
Published: 2018-08-21 09:15:09

---
title: Reducing Docker image size for a Perl6 Cro webapp
published: true
tags: perl6,docker
canonical_url: https://lancewicks.com/2018/05/25/tuning-a-production-perl6-web-application/#utm_source=rss&utm_medium=rss
---

My [https://myjudo.net?utm\_source=rss&utm\_medium=rss](https://myjudo.net?utm_source=rss&utm_medium=rss) Perl6 application has been online for while now; and recently I have ported it from the Bailador framework to Cro. My $dayjob colleague (and man behind [https://code-golf.io?utm\_source=rss&utm\_medium=rss](https://code-golf.io?utm_source=rss&utm_medium=rss) ) has always been helping and recently helped tune the app and specifically the docker container.

[Cro](https://cro.services/?utm_source=rss&utm_medium=rss), when you stub an application provides a Dockerfile, but it is not particularly optimised… so the new Docker file looks like this:

> FROM alpine:3.7
> 
> RUN apk add –no-cache curl gcc git libressl-dev linux-headers make musl-dev perl sqlite-libs
> 
> # Install Perl 6  
> RUN curl -L https://github.com/rakudo/rakudo/archive/2018.05.tar.gz?utm\_source=rss&utm\_medium=rss \  
> | tar xzf – \  
> && cd rakudo-2018.05 \  
> && perl Configure.pl –backend=moar –gen-moar –prefix=/usr \  
> && make -j`nproc` install
> 
> # Install zef  
> RUN git clone https://github.com/ugexe/zef.git?utm\_source=rss&utm\_medium=rss \  
> && cd zef \  
> && perl6 -Ilib bin/zef install –/test .
> 
> WORKDIR /app
> 
> COPY META6.json .
> 
> RUN /usr/share/perl6/site/bin/zef install –deps-only –/test .
> 
> # Avoid having “binaries” in the final image  
> RUN rm -r /usr/share/perl6/site/bin
> 
> FROM scratch
> 
> COPY –from=0 /bin/sh /bin/  
> COPY –from=0 /lib/ld-musl-x86\_64.so.1 \  
> /lib/libz.\* /lib/  
> COPY –from=0 /usr/bin/moar \  
> /usr/bin/perl6 /usr/bin/  
> COPY –from=0 /usr/lib/libmoar.so \  
> /usr/lib/libcrypto.\* \  
> /usr/lib/libsqlite3.\* \  
> /usr/lib/libssl.\* /usr/lib/  
> COPY –from=0 /usr/share/nqp /usr/share/nqp  
> COPY –from=0 /usr/share/perl6 /usr/share/perl6
> 
> WORKDIR /app
> 
> COPY . /app
> 
> CMD [“perl6”, “-Ilib”, “service.p6”]
> 
> ```
> 
> ```

You can see that it starts with [Alpine Linux](https://www.alpinelinux.org/?utm_source=rss&utm_medium=rss), a bare bones linux. From there we add some Linux tools we need, specifically gcc so we can build [Crypt::Bcrypt](https://github.com/skinkade/p6-Crypt-Bcrypt?utm_source=rss&utm_medium=rss) later along with libraries for SSL and SQLite.

Next… Perl 6 itself (actually, Perl 5 first, then Perl 6 as you need Perl 5 to build Perl 6).  
We get Perl6 direct from the Rakudo github repo and build it. Following that we build zef so we can install the Perl 6 modules we need.

At this point we jump into the /app directory and copy in the META6.json file. META6.json is similar to package.json in the node javascript world. It lists the modules that are required, so then we can call zef and get those installed (I turn off testing).

After this, we delete some perl 6 binaries to keep the total container size smaller.

Next…

We copy the things we need from the initial build to a new one again so size is minimised, this includes copying the required Linux system modules across that we need like ssl and sqlite libraries. Lastly we copy the application code across.

This Dockerfile we can build and share, it is 30MB total, which is considerably smaller than the original container generated by the Dockerfile created from the cro stub command.

At this stage I push it to docker hub and pull it down to the production server and run it, this is an example of how I call it:

> ```
> #!/bin/sh -e docker pull lancew/myjudo exec docker run \ -e MYJUDO\_TLS\_CERT=/etc/letsencrypt/live/myjudo.net/fullchain.pem \ -e MYJUDO\_TLS\_KEY=/etc/letsencrypt/live/myjudo.net/privkey.pem \ -p 80:1080 \ -p 443:1443 \ -w /app \ -v /app/db:/app/db \ -v /etc/letsencrypt:/etc/letsencrypt \ lancew/myjudo \ perl6 -Ilib service.p6
> ```

Here, I start by pulling the latest container, then run it.

The -e lines set environment variables, which cro uses, specifically the SSL certificates.

The -p lines set the ports linking externally available ports to ports inside the container, so port 80 points to 1080 inside the container.

The -v lines set mounts to the file system. In our case we map the host servers /app/db to /app/db inside the container, so that the cro application can read/write from the sqlite database file. We also map the letsencrypt ssl certificates.

This modernises the old deploy ideas, so rather than copy the source code to the production server, installing the modules and calling system Perl etc we simply copy (or pull in my case) the container that contains the code and modules and perl 6. Then just run that via Docker.

Because in dev I use a container also, we lose the classic “works on my machine” problem. This is pretty good.

I am no docker guru, so having some help has been super helpful and educational. I have what I hope is an easily maintainable and deploy-able application.

As I have said elsewhere, working with cro and perl 6 has been enjoyable and productive. Pairing this up with docker gives what seems a really modern web application platform that can hold its own against the trendier frameworks and languages.

If you have not tried the[Perl 6 programming language](https://perl6.org/?utm_source=rss&utm_medium=rss), then I think you should give it a go. It really is a great language that has nice tooling and feels modern and with some more time and development will I think/hope hold its own against the likes of Phoenix, Express, Etc etc etc

If you do want to try, please let me know!