With the release of NixOS 19.09 any second now, I thought I wanted to
blog about something that I've been working on, that recently
made it into master
, and thus the new stable channel.
What are OCI tools?
Open Container Initiative (or OCI) produced a spec that
standardised what format containers should use. It is implemented by a
bunch of runners, such as runc
(the Docker/ standard Kubernetes
backend) and railcar
(more to that later) and outlines in exactly
what format a containers metadata and filesystem are to be stored, so
to achieve the largest possible reusability.
The spec is pretty long and in some places not very great. There's even a blog post from Oracle, talking about how implementing an OCI runner in Rust made them find bugs in the specification.
What are ociTools?
So now the question is, what does that have to do with NixOS/nixpkgs. The answer is simple: I wanted to be able to containerise single applications on my server, without requiring a container daemon (such as docker) or relying on externally built "Docker containers" from a registry.
So, ociTools.buildContainer
was recently merged into nixpkgs/master
, allowing you to do exactly that. It's usage is farely
straight forward
with pkgs; ociTools.buildContainer {
args = [
(writeShellScript "run.sh" ''
${hello}/bin/hello -g "Hello from OCI container!"
'').outPath
];
}
The args
parameter refers to a list of paths and arguments that are
handed to a container runner to run as init. In this case it's
creating a shell script with some commands in it, then getting the
output derivation path. Alternatively, if you only want to run a
single application, you can pass it <package>.outPath
directly
instead.
There's other options available, such as the os
, arch
and
readonly
flags (which aren't very interesting and have sane
defaults). Additionally to that there's mounts
.
Simply specify any bind-mount you wish to setup at container init in a
similar way you would describe your filesystem with nix
already:
with pkgs; ociTools.buildContainer {
args = [
(writeShellScript "run.sh" ''
${hello}/bin/hello -g "Hello from OCI container!"
'').outPath
];
mounts."/data" = {
source = "/var/lib/mydata";
};
}
Railcar + ociTools
So that's all nice and good. But what about actually running these
containers. Well, as I previously said I didn't want to have a
dependency on a management daemon such as docker
. Instead, I also
added a module for the afromentioned railcar
container runner
(Oracle please merge my PR, thank you).
It wraps very cleanly around ociTools
and generates systemd
units
to start containers, restarting them if they crash. This way you can
express applications purely in nix
, give them access to only the
things they need, and be sure that their configuration is in line with
the rest of your system rebuild.
services.railcar = {
enable = true;
containers = {
"hello" = {
cmd = ''
${pkgs.hello}/bin/hello -g "Hello railcar!"
'';
};
};
};
The metadata interface for mounts
, etc is the same for railcar
as
for ociTools
.
Anyway, I hope you enjoy. There is definitely things to improve,
especially considering the vastness of the OCI spec. Plus, at the
moment ociTools
does require a bunch of manual setup work for an
application to function, if it, say, runs a webserver. It would be
cool if some NixOS modules could be re-used to make this configuration
easier. But I'm sure someone else is gonna have fun figuring that out.