Developing service mesh with Nomad and Nix
Nomad is a very special service orchestrator, because it can be used without any 3rd party container runtime. Of course, Nomad supports the usual suspects like Docker and Podman, but its pluggable architecture supports also many other ways to run applications. My immediate favorite has been its Isolated Fork/Exec Driver, which is a perfect fit for Nix packaged applications.
Nomad’s Exec driver can download a compressed tar archive (.tar.gz
), extract its contents, and execute any path from the extracted archive. The catch, of course, is that the downloaded archive should be self-containing enough to run successfully in the defined chroot configuration. And the smaller the chroot is, the faster and more lean the deployment is.
Nix is able to package anything into a fully self-contained Linux or MaxOS archive, which makes it perfect pair for Nomad’s Exec driver. I crafted a simple example demonstrating my current approach, which uses Nix for development and packaging, but requires Nix knowledge from our Nomad nodes: https://github.com/datakurre/nomad-with-nix/.
Below are some of the highlights:
Bootstrapping development
Domen Kožar’s awesome nix.dev guide for getting things done using the Nix ecosystem, pushed me over to stop worrying and love Niv and direnv for seamless developer experience with Nix managed projects.
With only having Nix pre-installed, this is how I bootstrapped my example project:
At first, I initialized the project with Niv, which created ./nix
folder to contain version pins to external dependencies like Nix Packages collection (nixpkgs
).
$ nix-shell -p niv --run "niv init"
$ nix-shell -p niv --run "niv update nixpkgs -b nixos-20.09"
Next, I made ./nix
easily importable with extensible default nixpkgs set.
$ cat > nix/default.nix << EOF
{ sources ? import ./sources.nix }:
let
overlay = _: pkgs: {
# custom packages
};
pkgs = import sources.nixpkgs {
overlays = [ overlay ];
config = {};
};
in pkgs
EOF
Then, I crafted a simple ./shell.nix
expression to define my initial development shell from Niv managed dependencies.
$ cat > shell.nix << EOF
{ pkgs ? import ./nix {}
}:
pkgs.mkShell {
buildInputs = with pkgs; [
niv
nomad_0_12
];
shellHook = ''
export NOMAD_ADDR=http://127.0.0.1:4646
'';
}
EOF
Finally, because I had already configured direnv on my system, I could easily active dirvenv
for the project.
$ echo "use nix" > .envrc && direnv allow
$ nix-shell