Thinking inside the box: Dockerizing Perl
Steven Lembark
Workhorse Computing
[email protected]
Slide 2
Slide 2 text
What's on your box?
Pre-compiled perl distros are terrible.
Bulky, slow, with annoying dependencies.
Not what you need...
Slide 3
Slide 3 text
Example: Red Hat Enterprize 7.0
5.16 == “end of life”
-Doptimize=-O2 -g -pipe -fstack-
protector-strong -mtune=generic -
Dversion=5.16.3 -Dmyhostname=localhost
-Dperladmin=root@localhost -
Dusethreads -Duseithreads -
Ubincompat5005
Slide 4
Slide 4 text
Example: Red Hat Enterprize 7.0
5.16 == “end of life”
Not exactly built for speed.
-Doptimize=-O2 -g -pipe -fstack-
protector-strong -mtune=generic -
Dversion=5.16.3 -Dmyhostname=localhost
-Dperladmin=root@localhost -
Dusethreads -Duseithreads -
Ubincompat5005
Slide 5
Slide 5 text
Example: Red Hat Enterprize 7.0
5.16 == “end of life”
Not exactly built for speed.
Thread overhead, even if you don't use them.
-Doptimize=-O2 -g -pipe -fstack-
protector-strong -mtune=generic -
Dversion=5.16.3 -Dmyhostname=localhost
-Dperladmin=root@localhost -
Dusethreads -Duseithreads -
Ubincompat5005
Slide 6
Slide 6 text
Fresh, not frozen
perl for your architecture.
optimized.
only dependencies you use (need).
Q: How?
Slide 7
Slide 7 text
Try it, you'll like it...
Bad approach: virtual machines...
Slide 8
Slide 8 text
Try it, you'll like it...
Bad approach: virtual machines...
and version-dirs...
and recompiling for each distro, architecture...
and symlink hell(2)...
and …
Slide 9
Slide 9 text
Try it, you'll like it...
False lazyness!
Slide 10
Slide 10 text
Tasty alternative: lxc
Essentially an LPAR – we've come full circle.
Use a process to isolate & localize code.
Share the kernel.
Light weight, fast startup, easy to ship.
Slide 11
Slide 11 text
Fly in the soup
Ever try to use lxc?
Let alone finish the manpages?
Kills my appetite.
RTFM
lxc-attach.1
lxc-autostart.1
lxc-cgroup.1
lxc-checkconfig.1
lxc-checkpoint.1
lxc-clone.1
lxc-config.1
lxc-console.1
lxc-create.1
lxc-destroy.1
lxc-device.1
lxc-execute.1
lxc-freeze.1
lxc-info.1
lxc-ls.1
lxc-monitor.1
lxc-snapshot.1
lxc-start-ephemeral.1
lxc-start.1
lxc-stop.1
lxc-top.1
lxc-unfreeze.1
lxc-unshare.1
lxc-user-nic.1
lxc-usernet.5
lxc-usernsexec.1
lxc-wait.1
lxc.7
lxc.conf.5
lxc.container.conf.5
lxc.system.conf.5
Slide 12
Slide 12 text
Docker: MRE for linux
80/20 of lxc:
Layered filesystem + Repository + Command line.
More nutritious than exciting.
Still evolving.
Slide 13
Slide 13 text
Catch: Docker's Docs aren't lean!
Docker doc's start with ubuntu image.
1.6GiB – Heart attack on a plate!
Includes X11 libs, lvm, mdadm, grub, parted... perl.
Slide 14
Slide 14 text
A: There is more than one way to do it.
Dockerfile builds perl on a light[er] weight O/S.
Single package for perl + shared libs.
Shell tools available for qx{...}.
Still pretty heavy-weight.
Slide 15
Slide 15 text
A: There is more than one way to do it.
Copy perl on top of busybox.
Much leaner cuisine.
Decent collection of shell tools.
Shared lib's as layer or via -v.
Slide 16
Slide 16 text
A: There is more than one way to do it.
Just perl
No empty calories.
Dodge library issues with -v or static compile.
Lacks tools to inspect the build.
Slide 17
Slide 17 text
Start by getting docker.
They provide shell code for the basics:
$curl -sL https://get.docker.io/ | sh;
$wget -qO- https://get.docker.io/ | sh;
will do the deed on Fedora, Ubuntu/Debian, or Gentoo.
Also need to sanity check the kernel.
Slide 18
Slide 18 text
Be yourself
Don't run as su.
Add your users to “docker” in /etc/group.
After that check that docker is running:
$ docker ps;
Get access to the repository:
$ docker login;
Slide 19
Slide 19 text
Just a taste...
Minimal base container: busybox.
Good for experimenting:
$ docker pull busybox
Pulling repository busybox
fd5373b3d938: Download complete
...
f06b02872d52: Download complete
Slide 20
Slide 20 text
Getting inside the box
# /bin/sh is entrypoint
$ docker run -t -i busybox;
# <-- su in box, login id out.
# ping 8.8.8.8; <-- network available.
...
# exit; <-- exit docker process
$ <-- original EUID
Slide 21
Slide 21 text
Gentoo is easy to dockerize
Common solution is a “stage-3” system.
Shell + libs + build tools.
Not much else.
About half the size of Ubuntu.
Slide 22
Slide 22 text
Finding a distribution
Start at the docker registry
https://registry.hub.docker.com/
Looking for stage-3 builds:
https://registry.hub.docker.com/search?q=gentoo+stage3
I find:
jgkim/gentoo-stage3 741.2 MB
Reasonable start.
Slide 23
Slide 23 text
Grabbing an O/S
Get the image:
$ docker pull jgkim/gentoo-stage3;
Run a container:
$ docker run –rm -i -t jgkim/gentoo-stage3;
# gcc --version;
gcc 4.8.4 good supports “--arch=native”
Slide 24
Slide 24 text
Building a perl container
Github has templates:
http://github.com/Perl/docker-perl
Dockerfiles like “5.020.000-64bit/Dockerfile”.
git acquires “5.02.0-64bit-optimized” directory.
Slide 25
Slide 25 text
FROM buildpack-deps # parent container
RUN apt-get update && apt-get install -y curl procps # commands to pull perl
RUN mkdir /usr/src/perl
WORKDIR /usr/src/perl # build dir within the container
RUN curl -SL http://www.cpan.org/src/5.0/perl-5.20.0.tar.gz | tar -xz --strip-components=1
RUN ./Configure -Duse64bitall -des \
&& make -j$(nproc) && TEST_JOBS=$(nproc) make test_harness \
&& make install && make veryclean
WORKDIR /root
CMD ["perl5.20.0","-de0"] # /bin/sh perl5.20.0 -de0
Distro's Dockerfile
New Dockerfile
FROM jgkim/gentoo-stage3
MAINTAINER Steven Lembark
WORKDIR /var/tmp/
RUN wget http://www.cpan.org/src/5.0/perl-5.20.2.tar.gz;
RUN gzip -dc < perl-5.20.2.tar.gz | tar xf -;
RUN cd perl-5.20.2 && \
./Configure -de -Dprefix=/opt/perl \
-Dman1dir=none -Dman3dir=none \
-Doptimize='-O3 -march=native -pipe' ;
RUN make -C perl-5.20.2 all test install distclean;
RUN /opt/perl/bin/h2ph -r -l /usr/include;
CMD [ "/opt/perl/bin/perl", "-d", "-E", "42" ]
Slide 28
Slide 28 text
Building Perl
The build takes input and optional repository tag.
Input is a directory not “Dockerfile”
$ cd /scratch/docker/gentoo+perl;
$ docker build –tag='lembark/perl-gentoo' . ;
Each step in the Dockerfile is an intermediate image.
Slide 29
Slide 29 text
Checking local containers
Intermediate images labled with “”.
These were from prior tests.
This one didn't finish:
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
5906d8edbb59 10 minutes ago 726.1 MB
10f5517fcada 23 minutes ago 726.1 MB
7d328e761704 26 minutes ago 725.5 MB
88f1e41aaed5 27 minutes ago 725.5 MB
28052ace7e04 28 minutes ago 725.5 MB
6f46836220d9 31 minutes ago 725.5 MB
Slide 30
Slide 30 text
$ docker build --rm=false –tag=lembark/perl:5.20 .;
Step 0 : FROM rndevfx/gentoo-stage3-amd64-nomultilib
---> e9d0ce66148c
Step 1 : MAINTAINER Steven Lembark
---> Using cache <-- recycled image
---> 41d5480e49e7
Step 2 : WORKDIR /var/tmp
---> Using cache
---> 010d3d70ced1
...
Step 7 : RUN make all test install; <-- first execution is here.
---> Running in 949c9ddfc2ef
Step 8 : RUN /opt/perl/bin/h2ph -r -a -l;
---> Running in b084569e8fc3
-r and -a options are mutually exclusive
INFO[1342] The command [/bin/sh -c /opt/perl/bin/h2ph -r -a -l;] returned a non-zero code
oops...
Slide 31
Slide 31 text
Easy fix
RUN /opt/perl/bin/h2ph -r -l /usr/inlcude;
Slide 32
Slide 32 text
True Lazyness
Re-running “build” re-cycles images:
...
Step 7 : RUN make all test install;
---> Using cache
---> cb4c5cd0607e
Step 8 : RUN /opt/perl/bin/h2ph -r -l /usr/include;
---> Running in 92efee8c31e2
require '_h2ph_pre.ph';
...
Slide 33
Slide 33 text
And impatience
$ time docker build --rm=false --tag=lembark/perl-gentoo .
Sending build context to Docker daemon 4.096 kB
Sending build context to Docker daemon
Step 0 : FROM rndevfx/gentoo-stage3-amd64-nomultilib
...
Step 10 : CMD [ “/opt/perl/bin/perl” ]
---> Using cache
---> f7b83ecbe276
Successfully built f7b83ecbe276
real 0m0.241s
user 0m0.019s
sys 0m0.012
Welcome to perl
“CMD” gets run with an interactive container:
$ docker run –rm -i -t lembark/gentoo-perl;
Loading DB routines from perl5db.pl version 1.44
Editor support available.
...
main::(-e:1): 0
DB<1> x $^V
v5.20.2
Slide 36
Slide 36 text
Choose your toppings
Default for gentoo run /bin/bash .
Save typing with:
ENTRYPOINT [ “/opt/perl/bin/perl” ]
CMD [ "-d", "-E", "42" ]
And then:
docker run –rm -i -t lembark/docker-perl;
Use –entrypoint='/bin/bash' if perl fails.
Slide 37
Slide 37 text
The next course
Stacked images inherit the ENTRYPOINT:
FROM lembark/gentoo-perl
CMD [ "/path/to/your/perl_code" ]
Slide 38
Slide 38 text
Test containers stack
Derive tests from the package.
Add ./t to another image.
ENTRYPOINT [ "/opt/perl/bin/prove" ]
Result: no tests in product image:
docker run foo/bar; run application.
docker run foo/bar-test; run base tests.
Slide 39
Slide 39 text
Lighter, reasonably tasty...
But still way, way too high-calorie.
You already have a local O/S.
Q: Why add one to the container?
A: Because we are all used to virtual machines.
Time for really lean cuisine.
Slide 40
Slide 40 text
Copy perl on top of busybox
Build & install into /opt/perl.
Add /opt/perl/Dockerfile:
FROM lembark/busybox_x86
COPY [ “.”, “/opt/perl” ]
$ docker build /opt/perl lembark/busybox-perl;
Slide 41
Slide 41 text
Nice combination
Useful shell tools for "qx".
Examine broken perl with /bin/sh entrypoint.
Slide 42
Slide 42 text
Nothing artificial, nothing added
Build a naked /opt/perl.
Leave out “busybox”:
FROM lembark/empty;
COPY [ “.”, “/opt/perl” ]
Minimal base for Plack or coros.
Slide 43
Slide 43 text
Shared libs
Catch: perl usually needs them.
In my case, from /lib64.
One way: O/S base image.
Q: How else?
Slide 44
Slide 44 text
Fix: Share libs
Run containers with read-only mount of /lib64:
docker run -v /lib64:/lib64:r …
Light-weight.
Fast: No images to ship.
Homogenous lib's.
Slide 45
Slide 45 text
Fix: Add libs
“ldd” lists shared libs.
COPY them into the image with perl.
Build perl from a lib64 image.
linux-vdso.so.1 (0x00007ffdfbbcb000)
libperl.so => /opt/perl/5.20/lib/5.20.2/x86_64-linux/CORE/libperl.so
(0x00007f40f8868000)
libnsl.so.1 => /lib64/libnsl.so.1 (0x00007f40f8650000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f40f844c000)
libm.so.6 => /lib64/libm.so.6 (0x00007f40f8153000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f40f7f1c000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007f40f7d19000)
libc.so.6 => /lib64/libc.so.6 (0x00007f40f7981000)
/lib64/ld-linux-x86-64.so.2 (0x00007f40f8c2b000)
Slide 46
Slide 46 text
Fix: static perl
Add “--static”.
No hetergenious server issues.
perl image is larger.
Container has same virtual size.
Best for tests: no issues with underlying images.
Slide 47
Slide 47 text
Result: portable, optimized, minimal perl.
Static perl with busybox: < 250 MiB.
Including Dancer, mod_perl, and friends.
Whole lot less than Ubuntu.
Not a virtual machine.
Plack web server FROM this perl.
Viola!, you're up.
Slide 48
Slide 48 text
References
http://docs.docker.com/
.../builder Dockerfile directives
.../userguide What you think it is.
Slide 49
Slide 49 text
Summary
Docker makes lxc approachable.
perl on busybox is tasty, low-calorie.
Add /lib64 for a complete meal.