2019-04-30 06:40:25 +02:00
|
|
|
|
== Introduction
|
|
|
|
|
|
|
|
|
|
Arion is a tool for building and running applications that
|
|
|
|
|
consist of multiple docker containers using NixOS modules.
|
|
|
|
|
It has special support for docker images that are built with Nix,
|
|
|
|
|
for a smooth development experience and improved performance.
|
|
|
|
|
|
|
|
|
|
It is built on top of https://docs.docker.com/compose/overview/[Docker
|
|
|
|
|
Compose], which implements the container orchestration functionality.
|
|
|
|
|
|
|
|
|
|
Instead of configuring the compositions in YAML files like
|
|
|
|
|
`docker-compose.yaml`, Arion uses the https://nixos.org/nix/[Nix]
|
|
|
|
|
language to declare the compositions. Because of this, Arion gives you
|
|
|
|
|
the ability to declare your deployments, configuration and packaging
|
|
|
|
|
in the same language. By replacing multiple tools with a single
|
|
|
|
|
language, you decrease your mental load and you can more easily
|
|
|
|
|
refactor and maintain your configurations.
|
|
|
|
|
|
|
|
|
|
Although Arion can be used as a Docker Compose with an improved
|
|
|
|
|
configuration front end, there is more to be gained from integrating
|
|
|
|
|
with Nix. In particular, the more structured approach of Nix compared
|
|
|
|
|
to Dockerfiles allows the following:
|
|
|
|
|
|
|
|
|
|
* Build components of your image in *parallel, automatically*
|
|
|
|
|
* *Share packages between images*, regardless of the order they were
|
|
|
|
|
added
|
|
|
|
|
* Improve performance by *skipping container
|
|
|
|
|
image creation*
|
|
|
|
|
* Work with *structured data instead of strings*,
|
|
|
|
|
templates and a multitude of expression languages
|
|
|
|
|
* Refactor across deployments, configuration and packaging
|
|
|
|
|
|
|
|
|
|
Arion allows to compose containers with different granuality:
|
|
|
|
|
|
|
|
|
|
* <<Minimal: Plain command using nixpkgs>>
|
|
|
|
|
* <<NixOS: run only one systemd service>>
|
|
|
|
|
* <<NixOS: run full OS>>
|
|
|
|
|
* <<Docker image from DockerHub>>
|
|
|
|
|
|
|
|
|
|
== Installation
|
|
|
|
|
|
|
|
|
|
=== Nix
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ nix-env -iA arion -f https://github.com/hercules-ci/arion/tarball/master
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
=== NixOS
|
|
|
|
|
|
|
|
|
|
Add this module to your NixOS configuration:
|
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
{ ... }: {
|
|
|
|
|
environment.systemPackages = [ (import (builtins.fetchTarball https://github.com/hercules-ci/arion/tarball/master) {}) ];
|
|
|
|
|
virtualisation.docker.enable = true;
|
|
|
|
|
users.extraUsers.myuser.extraGroups = ["docker"];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
////
|
|
|
|
|
|
|
|
|
|
== Not installing: use it in a project
|
|
|
|
|
|
|
|
|
|
TODO: describe: using nix-shell or in a script, building images as
|
|
|
|
|
part of nix-build, pinning, see also todomvc-nix.
|
|
|
|
|
|
|
|
|
|
TODO: exposed Nix functions: arion.build, arion.eval (a bit of IFD)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
== Usage
|
|
|
|
|
|
|
|
|
|
Arion is configured declaratively with two files:
|
|
|
|
|
|
|
|
|
|
=== arion-pkgs.nix
|
|
|
|
|
|
|
|
|
|
Arion needs `arion-pkgs.nix` to import nixpkgs, it's contents can be as simple as:
|
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
import <nixpkgs> {}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
or more sophisticated (recommended) setup with https://github.com/nmattia/niv[Niv].
|
|
|
|
|
|
|
|
|
|
=== arion-compose.nix
|
|
|
|
|
|
2019-05-01 18:19:29 +02:00
|
|
|
|
Describe containers using NixOS-style modules. There are a few options:
|
2019-04-30 06:40:25 +02:00
|
|
|
|
|
|
|
|
|
==== Minimal: Plain command using nixpkgs
|
|
|
|
|
|
|
|
|
|
`examples/minimal/arion-compose.nix`:
|
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
{ pkgs, ... }:
|
|
|
|
|
{
|
|
|
|
|
config.docker-compose.services = {
|
|
|
|
|
|
|
|
|
|
webserver = {
|
|
|
|
|
service.useHostStore = true;
|
|
|
|
|
service.command = [ "sh" "-c" ''
|
|
|
|
|
cd "$$WEB_ROOT"
|
|
|
|
|
${pkgs.python3}/bin/python -m http.server
|
|
|
|
|
'' ];
|
|
|
|
|
service.ports = [
|
|
|
|
|
"8000:8000" # host:container
|
|
|
|
|
];
|
|
|
|
|
service.environment.WEB_ROOT = "${pkgs.nix.doc}/share/doc/nix/manual";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
==== NixOS: run only one systemd service
|
|
|
|
|
|
|
|
|
|
`examples/nixos-unit/arion-compose.nix`:
|
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
docker-compose.services.webserver = { config, pkgs, ... }: {
|
|
|
|
|
|
|
|
|
|
nixos.configuration = {config, pkgs, ...}: {
|
|
|
|
|
boot.isContainer = true;
|
|
|
|
|
services.nginx.enable = true;
|
|
|
|
|
services.nginx.virtualHosts.localhost.root = "${pkgs.nix.doc}/share/doc/nix/manual";
|
|
|
|
|
system.build.run-nginx = pkgs.writeScript "run-nginx" ''
|
|
|
|
|
#!${pkgs.bash}/bin/bash
|
|
|
|
|
PATH='${config.systemd.services.nginx.environment.PATH}'
|
|
|
|
|
echo nginx:x:${toString config.users.users.nginx.uid}:${toString config.users.groups.nginx.gid}:nginx web server user:/var/empty:/bin/sh >>/etc/passwd
|
|
|
|
|
echo nginx:x:${toString config.users.groups.nginx.gid}:nginx >>/etc/group
|
|
|
|
|
${config.systemd.services.nginx.runner}
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
service.command = [ config.nixos.build.run-nginx ];
|
|
|
|
|
service.useHostStore = true;
|
|
|
|
|
service.ports = [
|
|
|
|
|
"8000:80" # host:container
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
==== NixOS: run full OS
|
|
|
|
|
|
|
|
|
|
`examples/full-nixos/arion-compose.nix`:
|
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
{
|
|
|
|
|
docker-compose.services.webserver = { pkgs, ... }: {
|
|
|
|
|
nixos.useSystemd = true;
|
|
|
|
|
nixos.configuration.boot.tmpOnTmpfs = true;
|
|
|
|
|
nixos.configuration.services.nginx.enable = true;
|
|
|
|
|
nixos.configuration.services.nginx.virtualHosts.localhost.root = "${pkgs.nix.doc}/share/doc/nix/manual";
|
|
|
|
|
service.useHostStore = true;
|
|
|
|
|
service.ports = [
|
|
|
|
|
"8000:80" # host:container
|
|
|
|
|
];
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
==== Docker image from DockerHub
|
|
|
|
|
|
|
|
|
|
```nix
|
|
|
|
|
{
|
|
|
|
|
docker-compose.services.postgres = {
|
|
|
|
|
service.image = "postgres:10";
|
|
|
|
|
service.volumes = [ "${toString ./.}/postgres-data:/var/lib/postgresql/data" ];
|
|
|
|
|
service.environment.POSTGRES_PASSWORD = "mydefaultpass";
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
=== Run
|
|
|
|
|
|
|
|
|
|
Start containers and watch their logs:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ arion up -d
|
|
|
|
|
$ arion logs -f
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can go to `examples/*/` and run these commands to give it a quick try.
|
|
|
|
|
|
|
|
|
|
== A full featured Nix command example
|
|
|
|
|
|
|
|
|
|
To see how Arion can be used in a project, have a look at
|
|
|
|
|
https://github.com/nix-community/todomvc-nix/tree/master/deploy/arion[todomvc-nix].
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
$ git clone https://github.com/nix-community/todomvc-nix
|
|
|
|
|
$ cd todomvc-nix/deploy/arion
|
|
|
|
|
$ arion up
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
== Project Status
|
|
|
|
|
|
|
|
|
|
This project was born out of a process supervision need for local
|
|
|
|
|
development environments while working on
|
|
|
|
|
https://www.hercules-ci.com[Hercules CI]. (It was also born out of
|
|
|
|
|
ancient Greek deities disguised as horses. More on that later.)
|
|
|
|
|
|
|
|
|
|
If you do want to use Arion for production environments, you’ll probably
|
|
|
|
|
want to either build normal container images or manage garbage
|
|
|
|
|
collection roots if you control the deployment host. Neither scenario is
|
|
|
|
|
made easier by arion at this time.
|
|
|
|
|
|
2019-05-01 18:19:56 +02:00
|
|
|
|
Arion has run successfully on Linux distributions other than NixOS, but we only perform CI for Arion on NixOS.
|
2019-04-30 06:40:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
== How it works
|
|
|
|
|
|
|
|
|
|
Arion is essentially a thin wrapper around Nix and docker-compose. When
|
|
|
|
|
it runs, it does the following:
|
|
|
|
|
|
|
|
|
|
* Evaluate the configuration using Nix, producing a
|
|
|
|
|
`docker-compose.yaml` and a garbage collection root
|
|
|
|
|
* Invoke `docker-compose`
|
|
|
|
|
* Clean up the garbage collection root
|
|
|
|
|
|
|
|
|
|
Most of the interesting stuff happens in Arion’s Nix expressions, where
|
|
|
|
|
it runs the module system (known from NixOS) and provides the
|
|
|
|
|
configuration that makes the Docker Compose file do the things it needs
|
|
|
|
|
to do.
|
|
|
|
|
|
|
|
|
|
One of the more interesting built-in modules is the
|
|
|
|
|
link:src/nix/modules/service/host-store.nix[host-store.nix module] which
|
|
|
|
|
performs the bind mounts to make the host Nix store available in the
|
|
|
|
|
container.
|
|
|
|
|
|
|
|
|
|
== FAQ
|
|
|
|
|
|
|
|
|
|
=== Do I need to use Hercules CI?
|
|
|
|
|
|
|
|
|
|
Nope, it’s just Nix and Docker Compose under the hood.
|
|
|
|
|
|
|
|
|
|
=== What about garbage collection?
|
|
|
|
|
|
|
|
|
|
Arion removes the need for garbage collecting docker images, delegating
|
|
|
|
|
this task to Nix.
|
|
|
|
|
|
|
|
|
|
Arion creates a garbage collection root and cleans it up after
|
|
|
|
|
completing the command. This means that `arion up` without `-d` is safe
|
|
|
|
|
with respect to garbage collection. A deployment that is more serious
|
|
|
|
|
than local development must leave a GC root on the deployment host. This
|
|
|
|
|
use case is not supported as of now.
|
|
|
|
|
|
|
|
|
|
=== Why is my container not running latest code?
|
|
|
|
|
|
|
|
|
|
Restart it with `arion restart <name>` or if you've changed the image rebuild
|
|
|
|
|
them using `arion up -d --always-recreate-deps <name>`.
|
|
|
|
|
|
|
|
|
|
=== What is messing with my environment variables?
|
|
|
|
|
|
|
|
|
|
Docker Compose performs its own environment variable substitution. This
|
|
|
|
|
can be a little annoying in `services.command` for example. Either
|
|
|
|
|
reference a script from `pkgs.writeScript` or escape the dollar sign as
|
|
|
|
|
`$$`.
|
|
|
|
|
|
|
|
|
|
=== Why name it ``Arion``?
|
|
|
|
|
|
|
|
|
|
Arion comes from Greek mythology. Poseidon, the god of ~Docker~ the seas
|
|
|
|
|
had his eye on Demeter. Demeter tried to trick him by disguising as a
|
|
|
|
|
horse, but Poseidon saw through the deception and they had Arion.
|
|
|
|
|
|
|
|
|
|
So Arion is a super fast divine horse; the result of some weird mixing.
|
|
|
|
|
Also it talks.
|
|
|
|
|
|
|
|
|
|
(And we feel morally obliged to name our stuff after Greek mythology)
|