Skip to content

Containers

Anil Madhavapeddy edited this page Nov 16, 2018 · 18 revisions

OCaml Container Infrastructure

This repository contains a set of Docker container definitions for various combination of OCaml and the opam package manager. The containers come preinstalled with an opam2 environment, and are particularly suitable for use with continuous integration systems such as Travis CI. All the containers are hosted on the Docker Hub ocaml/opam2 repository.

Using it is as simple as:

docker pull ocaml/opam2
docker run -it ocaml/opam2 bash

This will get you an interactive development environment (including on Docker for Mac). You can grab a specific OS distribution and test out external dependencies as well:

docker run ocaml/opam2:ubuntu opam depext -i cohttp-lwt-unix tls

There are a number of different variants available that are regularly rebuilt on the ocaml.org infrastructure and pushed to the Docker Hub. Each of the containers contains the Dockerfile used to build it in /Dockerfile within the container image.

Using The Defaults

The ocaml/opam2 Docker remote has a default latest tag that provides the Debian Stable Linux distribution with the latest release of the OCaml compiler (4.07.1). The opam-depext plugin can be used to install external system libraries in a distro-portable way.

The default user is opam in the /home/opam directory, with a copy of the opam-repository checked out in /home/opam/opam-repository. You can supply your own source code by volume mounting it anywhere in the container, but bear in mind that it should be owned by the opam user (uid 1000 in all distributions).

Selecting a Specific Compiler

The default container comes with the latest compiler activated, but also a number of other switches for older revisions of OCaml. You can switch to these to test compatibility in CI by iterating through older revisions.

For example:

$ docker run ocaml/opam2 opam switch
    switch  compiler                    description
    4.02    ocaml-base-compiler.4.02.3  4.02
    4.03    ocaml-base-compiler.4.03.0  4.03
    4.04    ocaml-base-compiler.4.04.2  4.04
    4.05    ocaml-base-compiler.4.05.0  4.05
->  4.06    ocaml-base-compiler.4.06.1  4.06

Note that the name of the switch drops the minor patch release (e.g. 4.06 vs 4.06.1), since you should always be using the latest patch revision of the compiler.

Accessing Compiler Variants

Modern versions of OCaml also feature a number of variants, such as the experimental flambda inliner or AFL fuzzing support. These are also conveniently available using the <VERSION> tag. For example:

$ docker run ocaml/opam2:4.06 opam switch
    switch                      compiler                                     description
->  4.06                        ocaml-base-compiler.4.06.1                   4.06
    4.06+afl                    ocaml-variants.4.06.1+afl                    4.06+afl
    4.06+default-unsafe-string  ocaml-variants.4.06.1+default-unsafe-string  4.06+default-unsafe-string
    4.06+flambda                ocaml-variants.4.06.1+flambda                4.06+flambda
    4.06+force-safe-string      ocaml-variants.4.06.1+force-safe-string      4.06+force-safe-string

In this case, the 4.06 container has the latest patch release (4.06.1) activated by default, but the other variant compilers are available easily via opam switch without having to compile them yourself. Using this more specific tag also helps you pin the version of OCaml that your CI system will be testing with, as the default latest tag will be regularly upgraded to keep up with upstream OCaml releases.

Selecting Linux Distributions

There are also tags available to select other Linux distributions, which is useful to validate and test the behaviour of your package in CI.

Distribution Tag Architectures Command
Alpine 3.8 alpine amd64 arm64 docker run ocaml/opam2:alpine
CentOS 7 centos amd64 docker run ocaml/opam2:centos
Debian Stable debian-stable amd64 arm64 ppc64le arm32v7 docker run ocaml/opam2:debian-stable
OracleLinux 7 oraclelinux amd64 docker run ocaml/opam2:oraclelinux
OpenSUSE 15.0 (Leap) opensuse amd64 docker run ocaml/opam2:opensuse
Fedora 29 fedora amd64 docker run ocaml/opam2:fedora
Ubuntu 18.10 ubuntu amd64 arm64 docker run ocaml/opam2:ubuntu
Ubuntu 18.04 ubuntu-lts amd64 arm64 docker run ocaml/opam2:ubuntu-lts

The tags above are for the latest version of the distribution, and are upgraded to the latest upstream stable releases. You can also select a specific version number in the tag name to obtain a particular OS release. However, these will eventually time out once they are out of their support window, so try to use the version-free aliases described earlier unless you really know that you want a specific release. When a specific release does time out, the container will be replaced by one that always displays an error message requesting you to upgrade your CI script.

Distribution Tag Architectures Command
Alpine 3.7 alpine-3.7 amd64 arm64 docker run ocaml/opam2:alpine-3.7
Alpine 3.8 alpine-3.8 amd64 arm64 docker run ocaml/opam2:alpine-3.8
CentOS 7 centos-7 amd64 docker run ocaml/opam2:centos-7
Debian 9 (Stretch) debian-9 amd64 arm64 ppc64le arm32v7 docker run ocaml/opam2:debian-9
Debian 8 (Jessie) debian-8 amd64 docker run ocaml/opam2:debian-8
Debian Testing debian-testing amd64 docker run ocaml/opam2:debian-testing
Debian Unstable debian-unstable amd64 docker run ocaml/opam2:debian-unstable
Fedora 27 fedora-27 amd64 docker run ocaml/opam2:fedora-27
Fedora 28 fedora-28 amd64 docker run ocaml/opam2:fedora-28
Fedora 29 fedora-29 amd64 docker run ocaml/opam2:fedora-29
OracleLinux 7 oraclelinux-7 amd64 docker run ocaml/opam2:oraclelinux-7
OpenSUSE 42.3 opensuse-42.3 amd64 docker run ocaml/opam2:opensuse-42.3
OpenSUSE 15.0 (Leap) opensuse-15.0 amd64 docker run ocaml/opam2:opensuse-15.0
Ubuntu 14.04 ubuntu-14.04 amd64 docker run ocaml/opam2:ubuntu-14.04
Ubuntu 16.04 ubuntu-16.04 amd64 arm64 docker run ocaml/opam2:ubuntu-16.04
Ubuntu 18.04 ubuntu-18.04 amd64 arm64 docker run ocaml/opam2:ubuntu-18.04
Ubuntu 18.10 ubuntu-18.10 amd64 arm64 docker run ocaml/opam2:ubuntu-18.10

Multi-architecture Containers

The observant reader will notice that the distributions listed above have more than one architecture. We are building an increasing number of packages on non-x86 containers, starting with ARM64 and soon to include PPC64.

Using the multiarch images is simple, as the correct one will be selected depending on your host architecture. The images are built using docker manifest.

Development Versions of the Compiler

You can also access development versions of the OCaml compiler (currently 4.08.0) that have not yet been released. These are rebuilt around once a day, so you may lag a few commits behind the main master branch. You can reference it via the version v4.08 tag as with other compilers, but please do bear in mind that this is a development version and so subject to more breakage than a stable release.

$ docker run -it ocaml/opam2:v4.08 switch
    switch              compiler                             description
->  4.08                ocaml-variants.4.08.0+trunk          4.08
    4.08+trunk+afl      ocaml-variants.4.08.0+trunk+afl      4.08+trunk+afl
    4.08+trunk+flambda  ocaml-variants.4.08.0+trunk+flambda  4.08+trunk+flambda
$ docker run -it ocaml/opam2:v4.08 ocaml --version
The OCaml toplevel, version 4.08.0+dev7-2018-11-10

Just the Binaries Please

All of the OCaml containers here are built over a smaller container that just installs the opam binary first, without having an OCaml compiler installed. Sometimes for advanced uses, you may want to initialise your own opam environment. In this case, you can access the base container directly via the <DISTRO>-opam tag (e.g. debian-9-opam). Bear in mind that this base image will be deleted in the future when the distribution goes out of support, so please only use these low-level opam containers if one of the options listed above isn't sufficient for your usecase.

There are a large number of distribution and OCaml version combinations that are regularly built that are not mentioned here. For the advanced user who needs a specific combination, the full current list can be found on the Docker Hub. However, please try to use the shorter aliases rather than these explicit versions if you can, since then your builds will not error as the upstream versions advance.

Package Sandboxing

The Docker containers install opam2's Bubblewrap tool that is used for sandboxing builds. However, due to the way that Linux sandboxing works, this may not work with all Docker installations since unprivileged containers cannot create new Linux namespaces on some installations. Thus, sandboxing is disabled by default in the containers that have opam initialised.

If you can run containers with docker run --privileged, then you can enable opam sandboxing within the container by running opam-sandbox-enable within the container. This will ensure that every package is restricted to only writing within ~/.opam and is the recommended way of doing testing.

Questions and Feedback

We are constantly improving and maintaining this infrastructure, so please get in touch with Anil Madhavapeddy <anil@recoil.org> if you have any questions or requests for improvement. Note that until opam 2.0 is released, this infrastructure is considered to be in a beta stage and subject to change.

This is all possible thanks to generous infrastructure contributions from Packet.net, IBM, Azure and Rackspace, as well as a dedicated machine cluster funded by Jane Street. The Docker Hub also provides a huge amount of storage space for our containers. We use hundreds of build agents running on GitLab in order to regularly generate the large volume of updates that this infrastructure needs, including the multiarch builds.