aboutsummaryrefslogtreecommitdiff
path: root/content/blog/109_nix_ocitools.md
blob: 2193e7cac47b045d18c7feed11862bb210800c80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
Title: ociTools in NixOS
Category: Blog
Date: 2019-09-09 18:00
Tags: /dev/diary, NixOS, Virtualisation

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][0]
made it into `master`, and thus the new stable channel. So I thought,
why not blog about it a bunch.

[0]: https://github.com/NixOS/nixpkgs/pull/56411

## What are OCI tools?

[Open Container Initiative][1] (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.

[1]: https://www.opencontainers.org/

The spec is pretty [long][3] and in some places not very
great. There's even a [blog post][4] from Oracle, talking about how
implementing an OCI runner in Rust made them find bugs in the
specification.

[3]: https://github.com/opencontainers/runtime-spec
[4]: https://blogs.oracle.com/developers/building-a-container-runtime-in-rust

## 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

```nix
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:

```nix
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.

```nix
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.