A Dockerfile for Perl 5.36 / Alpine, with working SSL

A new and shiny version of Perl, 5.36 has been released last week. It contains a lot of very nice improvements and new features. As I'm also currently starting a fresh project, I decided it's a good time to build a new docker (in fact podman) base image to use for some new apps / scripts etc.

Spoiler: Here's my (current) final Dockerfile! Or read on for the long story...

There are of course official Perl Docker images available, in two variants: There is perl:5.36.0 and perl:5.36.0-slim. The first one is nearly 900MB big, while slim is slimmer with 144MB. I don't like big images, so I first tried slim, but it is so slim because it's rather unusable.

The curse of Net::SSLeay

Net::SSLeay allows you to use openssl in your web clients, and as most APIs run on https, it is an absolute required module for all of my projects (a lot of which are web API clients). It is not that easy to install, because it needs some openssl header files to compile. And those are missing in perl:5.36.0-slim (to keep it slim..)

But let's give it a try and build an image based on a very simple Dockerfile

~/perl/perl-docker$ docker build -t perl_5_36-slim -f Dockerfile.5.36-slim .
Step 1/1 : FROM perl:5.36-slim
 ---> 0d52cf67797d
Successfully built 0d52cf67797d
Successfully tagged perl_5_36-slim:latest

Take a look at the size:

docker images --filter=reference='perl_5_36-slim' --format "{{.Repository}} {{.Size}} {{.ID}}"
perl_5_36-slim 144MB 0d52cf67797d

144MB, ok, nice. Now let's check Net::SSLeay:

docker run perl_5_36-slim  perl -MNet::SSLeay -E 'say Net::SSLeay->VERSION'
Can't locate Net/SSLeay.pm in @INC

Can we install it?

docker run --rm -ti perl_5_36-slim /bin/bash
root@8e4ec3733b01:/# cpanm Net::SSLeay
--> Working on Net::SSLeay
Fetching https://www.cpan.org/authors/id/C/CH/CHRISN/Net-SSLeay-1.92.tar.gz ... OK
Configuring Net-SSLeay-1.92 ... N/A
! Configure failed for Net-SSLeay-1.92. See /root/.cpanm/work/1654449406.7/build.log for details.

No, why not?

root@8e4ec3733b01:/# cat  /root/.cpanm/work/1654449406.7/build.log
******************************************************************************
* COULD NOT FIND LIBSSL HEADERS                                              *
*                                                                            *
* The libssl header files are required to build Net-SSLeay, but they are     *
* missing from /usr. They would typically reside in /usr/include/openssl.    *
*                                                                            *
* If you are using the version of OpenSSL/LibreSSL packaged by your Linux    *
* distribution, you may need to install the corresponding "development"      *
* package via your package manager (e.g. libssl-dev for OpenSSL on Debian    *
* and Ubuntu, or openssl-devel for OpenSSL on Red Hat Enterprise Linux and   *
* Fedora).                                                                   *
******************************************************************************

But just installing libssl-dev is not enough, because we need a whole build environment, and all of that stuff takes so much disk space (and installation time) that we might just as well got with the non-slim version:

~/perl/perl-docker$ docker build -t perl_5_36-fat -f Dockerfile.5.36-fat .
Step 1/1 : FROM perl:5.36
 ---> 9a373d352e6f
Successfully built 9a373d352e6f
Successfully tagged perl_5_36-fat:latest

Take a look at the size:

docker images --filter=reference='perl_5_36-fat' --format "{{.Repository}} {{.Size}} {{.ID}}"
perl_5_36-fat 891MB 9a373d352e6f

891MB - fat indeed. But let's check Net::SSLeay anyway:

docker run perl_5_36-fat  perl -MNet::SSLeay -E 'say Net::SSLeay->VERSION'
Can't locate Net/SSLeay.pm in @INC

Can we install it?

docker run --rm -ti perl_5_36-fat /bin/bash
root@8e4ec3733b01:/# cpanm -n Net::SSLeay
--> Working on Net::SSLeay
Fetching https://www.cpan.org/authors/id/C/CH/CHRISN/Net-SSLeay-1.92.tar.gz ... OK
Configuring Net-SSLeay-1.92 ... OK
Building Net-SSLeay-1.92 ... OK
Successfully installed Net-SSLeay-1.92
1 distribution installed

Yes! And it works (after also installing IO::Socket::SSL)

root@92ef4d4f5cab:/# perl -MHTTP::Tiny -E 'say HTTP::Tiny->new->get("https://domm.plix.at:")->{status}'
200

Now I could build some custom images based on perl:5.36. Or ..

Build your own

I want:

  • a small-ish base image,
  • which should include a bunch of CPAN modules I'll use in all projects (this list will grow..)
  • I prefer cpm to cpanm
  • Perl should be installed to /opt so I can later easily copy the whole installation (in multi stage builds derived from the base image)

We used alpine in a few projects, it seems to work, so I'm sticking with it:

FROM alpine

Then I install a bunch of things we need, most notably zlib and openssl (and their -dev packages)

RUN apk update && apk upgrade && apk add --no-cache \
    curl tar make gcc build-base wget gnupg vim tree bash \
    zlib zlib-dev openssl openssl-dev

The next few lines are lifted from https://github.com/scottw/alpine-perl/blob/master/Dockerfile, with a few adaptions (most relevant -Dprefix='/opt/perl'). I removed the checksum test (and might add it again, now that I got the basic setup working); and I don't run the test suite (I don't have that many idle cycles to burn)

RUN mkdir -p /usr/src/perl

WORKDIR /usr/src/perl

RUN curl -SLO https://www.cpan.org/src/5.0/perl-5.36.0.tar.gz \
    && tar --strip-components=1 -xaf perl-5.36.0.tar.gz -C /usr/src/perl \
    && rm perl-5.36.0.tar.gz \
    && ./Configure -des \
        -Duse64bitall \
        -Dcccdlflags='-fPIC' \
        -Dcccdlflags='-fPIC' \
        -Dccdlflags='-rdynamic' \
        -Dlocincpth=' ' \
        -Duselargefiles \
        -Dusethreads \
        -Duseshrplib \
        -Dd_semctl_semun \
        -Dusenm \
        -Dprefix='/opt/perl' \
    && make -j$(nproc) \
    && make install \
    && rm -fr /usr/src/perl

Now I change the working directory to /opt/perl and add it to the PATH.

WORKDIR /opt/perl
ENV PATH="/opt/perl/bin:${PATH}"
RUN cd /opt/perl

And finally I download a copy cpm into /tmp/cmp and use it to properly install App::cpm, IO::Socket::SSL (which will pull in Net::SSLeay), and other stuff I'm bound to need

RUN curl -o /tmp/cpm -sL --compressed https://git.io/cpm \
    && chmod 755 /tmp/cpm \
    && /tmp/cpm install -g App::cpm IO::Socket::SSL Cpanel::JSON::XS \
    && rm -fr /root/.perl-cpm

So, let's run it:

docker build -t perl_5_36-custom -f Dockerfile.perl5.36-app-base .
Step 1/9 : FROM alpine
 ---> e66264b98777
Step 2/9 : RUN apk update && apk upgrade && apk add --no-cache curl tar make gcc build-base wget gnupg vim t...
 ---> ed8080081bc4
Step 3/9 : RUN mkdir -p /usr/src/perl
 ---> 7bab08167507
Step 4/9 : WORKDIR /usr/src/perl
 ---> f9ccbaa25578
Step 5/9 : RUN curl -SLO https://www.cpan.org/src/5.0/perl-5.36.0.tar.gz     && tar --strip-components=1 -xa...
 ---> ccb8e8a97ad8
Step 6/9 : WORKDIR /opt/perl
 ---> 94356542d4ed
Step 7/9 : ENV PATH="/opt/perl/bin:${PATH}"
 ---> f3c1c9451e40
Step 8/9 : RUN cd /opt/perl
 ---> bc651d01629c
Step 9/9 : RUN curl -o /tmp/cpm -sL --compressed https://git.io/cpm     && chmod 755 /tmp/cpm     && /tmp/cp...
 ---> f6d98e961143
Successfully built f6d98e961143
Successfully tagged perl_5_36-custom:latest

How big is it?

~/perl/perl-docker$ docker images --filter=reference='perl_5_36-custom' --format "{{.Repository}} {{.Size}} {{.ID}}"
perl_5_36-custom 300MB f6d98e961143

300MB, not that bad (and could probably be improved later). Do we have Net::SSLeay?

docker run perl_5_36-custom  perl -MNet::SSLeay -E 'say Net::SSLeay->VERSION'
1.92

Nice, Net::SSLeay is installed! But does it work?

docker run perl_5_36-custom perl -MHTTP::Tiny -E 'say HTTP::Tiny->new->get("https://domm.plix.at:")->{status}'
200

Yes!!

Now the next steps are to build a Dockerfile for an actual app that's based on this image.

You can find the whole Dockerfile here.

And if you have any suggestions or questions, drop me a line!