docker perl build

57
Thinking inside a box: Dockerizing Perl Steven Lembark Workhorse Computing [email protected]

Upload: workhorse-computing

Post on 25-Jul-2015

104 views

Category:

Technology


0 download

TRANSCRIPT

Thinking inside a box: Dockerizing Perl

Steven LembarkWorkhorse Computing

[email protected]

What's on your box?

Pre-compiled perl distros are terrible.

Bulky, slow, with annoying dependencies.

Not what you need...

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

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

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

Fresh, not frozen

perl for your architecture.

optimized.

only dependencies you use (need).

Q: How?

Try it, you'll like it...

Bad approach: virtual machines...

Try it, you'll like it...

Bad approach: virtual machines...

and version-dirs...

and recompiling for each distro, architecture...

and symlink hell(2)...

Try it, you'll like it...

False lazyness!

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.

Fly in the soup

Ever try to use lxc?

Let alone finish the manpages?

Kills my appetite.

RTFMlxc-attach.1lxc-autostart.1lxc-cgroup.1lxc-checkconfig.1lxc-checkpoint.1lxc-clone.1lxc-config.1lxc-console.1lxc-create.1lxc-destroy.1lxc-device.1lxc-execute.1lxc-freeze.1lxc-info.1lxc-ls.1lxc-monitor.1lxc-snapshot.1lxc-start-ephemeral.1lxc-start.1lxc-stop.1lxc-top.1lxc-unfreeze.1lxc-unshare.1lxc-user-nic.1lxc-usernet.5lxc-usernsexec.1lxc-wait.1lxc.7lxc.conf.5lxc.container.conf.5lxc.system.conf.5

Docker: MRE for linux

80/20 of lxc:

Layered filesystem + Repository + Command line.

More nutritious than exciting.

Still evolving.

Catch: Docker's doc is fattening!

Start with full Ubuntu image.

1.6GiB – Heart attack on a plate!

Includes X11 libs, lvm, mdadm, grub, parted... perl.

Q: How can we put docker on a healthy diet?

Start with something lighter-weight underneath?

Keep less of it in the container?

Well... since this is about perl:

A: There is more than one way to do it.

Build perl on a light[er] weight O/S.

Single package for perl + shared libs.

Shell tools available for qx{...}.

Still pretty heavy-weight.

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.

A: There is more than one way to do it.

Just perl

Minimal for distribution of local perl.

Lacks tools to inspect the build.

A: There is more than one way to do it.

Just the application.

No empty calories.

Requires local perl for distributed use.

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.

Avoids issues with apt/yum, not much value with emerge.

Need to validate kernel configs.

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;

Just a taste...

Minimal base container: busybox.

Good for experimenting:

$ docker pull busybox

Pulling repository busybox

fd5373b3d938: Download complete

...

f06b02872d52: Download complete

Getting inside the box

/bin/sh is default 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

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.

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.

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”

Building a perl container

Github has templates:

http://github.com/Perl/docker-perl

Dockerfiles like “5.020.000-64bit/Dockerfile”.

git acquires “5.020.0-64bit-optimized” directory.

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

Check the local arguments

$ perl -V;...

config_args='-de -Dprefix=/opt/perl/5.20 -Doptimize=-O3 -march=native -pipe'

perl -MConfig -E 'say $Config{ config_args }'

New DockerfileFROM jgkim/gentoo-stage3MAINTAINER Steven Lembark <[email protected]>WORKDIR /var/tmp/

RUN wget -O – http://www.cpan.org/src/5.0/perl-5.20.2.tar.gz \| gzip -dc tar | 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 -a -l;

CMD [ "/opt/perl/bin/perl", "-d", "-E", "42" ]

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.

Checking local containers

Intermediate images labeled with “<none>”.These were from prior tests.

This one didn't finish:$ docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE<none> <none> 5906d8edbb59 10 minutes ago 726.1 MB<none> <none> 10f5517fcada 23 minutes ago 726.1 MB<none> <none> 7d328e761704 26 minutes ago 725.5 MB<none> <none> 88f1e41aaed5 27 minutes ago 725.5 MB<none> <none> 28052ace7e04 28 minutes ago 725.5 MB<none> <none> 6f46836220d9 31 minutes ago 725.5 MB

$ docker build --rm=false –tag=lembark/gentoo-perl .;Step 0 : FROM rndevfx/gentoo-stage3-amd64-nomultilib ---> e9d0ce66148cStep 1 : MAINTAINER Steven Lembark ---> Using cache <-- recycled image ---> 41d5480e49e7Step 2 : WORKDIR /var/tmp ---> Using cache ---> 010d3d70ced1...

Step 7 : RUN make all test install; <-- first execution is here. ---> Running in 949c9ddfc2efStep 8 : RUN /opt/perl/bin/h2ph -r -a -l; ---> Running in b084569e8fc3-r and -a options are mutually exclusiveINFO[1342] The command [/bin/sh -c /opt/perl/bin/h2ph -r -a -l;] returned a non-zero code

oops...

Easy fix

RUN /opt/perl/bin/h2ph -r -l /usr/inlcude;

True Lazyness

More re-cycled images:

---> Using cache ---> cb4c5cd0607e

Step 8 : RUN /opt/perl/bin/h2ph -r -l /usr/include; ---> Running in 92efee8c31e2require '_h2ph_pre.ph';...

And impatience

$ time docker build --rm=false --tag=lembark/perl-gentoo .Sending build context to Docker daemon 4.096 kBSending build context to Docker daemonStep 0 : FROM rndevfx/gentoo-stage3-amd64-nomultilib...

Step 10 : CMD [ “/opt/perl/bin/perl” ] ---> Using cache ---> f7b83ecbe276Successfully built f7b83ecbe276

real 0m0.241suser 0m0.019ssys 0m0.012

Docker Images:

REPOSITORY VIRTUAL SIZElemark/gentoo-perl 884.4 MBjgkim/gentoo-stage3 741.2 MBlocalhost:5000/lembark/busybox 1.9 MB

Successful build: tagged image

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.44Editor support available....

main::(-e:1): 0 DB<1> x $^Vv5.20.2

Choose your toppings

Default for gentoo run /bin/bash <your command>.

Save typing with:

ENTRYPOINT [ “/opt/perl/bin/perl” ]

CMD [ "-d", "-E", "42" ]

Use –entrypoint='/bin/bash' if perl fails.

The next course

Stacked images inherit the ENTRYPOINT:

FROM lembark/gentoo-perl

CMD [ "/path/to/your/program" ]

runs

/opt/perl/bin/perl /path/to/your/program;

Test containers stack

Derive tests from the package.

Add ./t to another image.

WORKDIR [ “/path/to/your/code” ]

CMD [ "/opt/perl/bin/prove" ]

Result: no tests in product image:

docker run foo/bar; run application.

docker run foo/bar-test; run base tests.

Minimizing virtual size

Cannot remove inter-RUN space.

884.4 MB includes 100MB of /var/tmp/perl-5.20.2.

Avoiding it requires a single RUN.

No caching of intermediate steps.

Final size 787.8 MB.

Best for final construction.

Minimal Virtual Size: single "RUN" commandFROM jgkim/gentoo-stage3MAINTAINER Steven Lembark <[email protected]>WORKDIR /var/tmp/

# better yet, put this in a shell script and RUN ./build-perl! RUN wget -O – http://www.cpan.org/src/5.0/perl-5.20.2.tar.gz \| gzip -dc tar | tar xf - \&& cd perl-5.20.2 \&& Configure -de -Dprefix=/opt/perl \

-Dman1dir=none -Dman3dir=none \-Doptimize='-O3 -march=native -pipe' \

&& make all test install distclean \&& cd .. && rm -rf 5.20.2 ;

RUN /opt/perl/bin/h2ph -r -a -l;ENTRYPOINT [ "/opt/perl/bin/perl" ]CMD [ "-d", "-E", "42" ]

In most cases there is still a better way.

Note: You already have a local O/S.

Q: Why add another one to the container?

A: Because we are all used to virtual machines.

Time to reduce the calories...

Copy perl on top of busybox

Build & install into /opt/perl.

/opt/perl/Dockerfile

FROM lembark/busybox_x86

COPY [ “.”, “/opt/perl” ]

ENTRYPOINT [ “/opt/perl/bin/perl” ]

CMD [ , “-d”, “-E”, “0” ]

Check build: docker run –entrypoint='/bin/sh'

Nice combination

Useful shell tools for "qx".

/bin/sh in case of broken build.

Smaller package: REPOSITORY VIRTUAL SIZE

lembark/busybox-perl 67.5 MB

lembark/gentoo-perl 787.8 MB

jgkim/gentoo-stage3 741.2 MB

localhost:5000/lembark/busybox 1.9 MB

Local dir with modules

/opt/perl/5.20/Dockerfile for bare copy of perl:

$ docker build –tag='lembark/perl-5.20' /opt/perl/5.20;

FROM /lembark/busyboxMAINTAINER Steven Lembark <[email protected]>

COPY [ ".", "/opt/perl/5.20" ]WORKDIR /var/tmpENTRYPOINT [ "/opt/perl/5.20/bin/perl" ]CMD [ "-d", "-E", "0" ]

Portion control for perl

What if you don't want CPAN::Reporter in Docker?

A: Keep a “docker-perl” install.

Extract stage3 into /scratch/docker-build.

Build perl “chroot /scratch/docker-build”.

Single /opt/perl with vetted modules.

Works for just about anything.

Nothing artificial, nothing added

Skip the Dockerfile: import a tarball.

"—change" inserts Dockerfile syntax into image.

cd /opt/perl; find . | cpio -ov -Htar |

docker import –change=”VOLUME /lib64” \

--tag=”lembark/perl-5.20.2”;

Minimal base for Plack server.

Essential ingredients

Catch, this won't run as-is: since perl needs shared libs.

In my case, from /lib64.

Q: Where to get them without O/S image?

One way: Share libs

Run containers with read-only mount of /lib64:

docker run -v /lib64:/lib64:r …

Light-weight.

Fast: No images to ship.

Requires homogeneous lib's for distributed use.

Or "-v /var/tmp/$$:/var/tmp"

One way: bundle libs

“ldd” lists shared libs:

COPY them into the image with perl.

Build lib64 image from perl – order doesn't matter. 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)

Zero protein pill

Use "-v" to add /lib4 and /opt/perl.

Image: Single #! script to start the application.

Images are a few KB.

Requires homogenous install of perl, application.

One way: static perl

Build perl & modules “--static”.

No hetergenious server issues.

perl image is larger.

Best for tests: no issues with underlying images.

Result: portable, optimized, minimal perl.

Static perl with busybox: 68 MB.

Whole lot less than Ubuntu.

Not a virtual machine.

Plack web server FROM this perl.

Viola!, you're up.

What did all of this get us?

Mainly an optimized, current perl.

With all [and only] the modules you need.

You also save the overhead of shipping an O/S image.

Faster startup.

Easier updates.

Simpler deployment.

SummaryDocker makes lxc approachable.

You don't always need a full O/S distro in the container.

perl on busybox makes a tasty, low-calorie alternative.

Use “-v” to import /lib64 for a full meal.

Even testing gets simpler: derive tests from package.

Say goodby to brewing perl, managing multiple versions.

References

http://docs.docker.com/

.../builder Dockerfile directives

.../userguide What you think it is.