diff --git a/README.asciidoc b/README.asciidoc index 459b0b8..f876ff5 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -1,274 +1,7 @@ -== 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 granularity: - - * <> - * <> - * <> - * <> - -== 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) {}).arion ]; - 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 {} -``` - -or more sophisticated (recommended) setup with https://github.com/nmattia/niv[Niv]. - -=== arion-compose.nix - -Describe containers using NixOS-style modules. There are a few options: - -==== 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. - -Arion has run successfully on Linux distributions other than NixOS, but we only perform CI for Arion on NixOS. - - -== 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 ` or if you've changed the image rebuild -them using `arion up -d --always-recreate-deps `. - -=== 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) +# [Intro and Documentation](https://docs.hercules-ci.com/arion/) diff --git a/antora-playbook.yml b/antora-playbook.yml new file mode 100644 index 0000000..7ef1a66 --- /dev/null +++ b/antora-playbook.yml @@ -0,0 +1,13 @@ +# This is not used when the docs are imported into the docs site. +# It is only here for development preview. +content: + sources: + - url: . + start_path: docs + branches: HEAD +ui: + bundle: + url: ./docs/ui-bundle.zip +output: + dir: ./public + diff --git a/doc/default.nix b/doc/default.nix deleted file mode 100644 index aedd7ff..0000000 --- a/doc/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ pkgs ? import ../nix {} }: -let - inherit (pkgs) recurseIntoAttrs callPackage; -in - -recurseIntoAttrs { - manual = callPackage ./manual {}; -} diff --git a/doc/manual/.gitignore b/doc/manual/.gitignore deleted file mode 100644 index 0c15756..0000000 --- a/doc/manual/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -manual.html -options-composition.xml diff --git a/doc/manual/Makefile b/doc/manual/Makefile deleted file mode 100644 index 656c0ab..0000000 --- a/doc/manual/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -xsltproc = xsltproc --nonet \ - --param section.autolabel 0 \ - --param section.label.includes.component.label 0 \ - --param chapter.autolabel 0 \ - --param chapter.label.includes.component.label 0 \ - --param appendix.autolabel 0 \ - --param appendix.label.includes.component.label 0 \ - --param generate.toc "'book toc,title chapter nop section nop sect1 nop sect2 nop sect3 nop sect4 nop sect5 nop'" \ - --param html.stylesheet \'style.css\' \ - --param xref.with.number.and.title 0 \ - --param toc.section.depth 3 \ - --param admon.style \'\' \ - --param callout.graphics.extension \'.gif\' \ - --param contrib.inline.enabled 0 - -docbookxsl = http://docbook.sourceforge.net/release/xsl/current - -all: manual.html - -manual.html: manual.xml options-composition.xml - $(xsltproc) --xinclude --stringparam profile.condition manual \ - $(docbookxsl)/profiling/profile.xsl manual.xml | \ - $(xsltproc) --output manual.html $(docbookxsl)/xhtml/docbook.xsl - - -# -e 's___' -e 's___' -%.xml: %.asciidoc - asciidoctor --backend docbook45 --doctype article $< - sed -e 's///' -i $@ - -options-composition.xml: - echo "options-composition.xml should be written by the derivation. Are you running in 'nix-shell -A manual'?"; exit 1; fi - -install: all - mkdir -p $(docdir) - cp manual.html style.css $(docdir) diff --git a/doc/manual/build b/doc/manual/build deleted file mode 100755 index 67c9285..0000000 --- a/doc/manual/build +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -nix-shell -A manual --run 'patchPhase && make' diff --git a/doc/manual/default.nix b/doc/manual/default.nix deleted file mode 100644 index 64b352b..0000000 --- a/doc/manual/default.nix +++ /dev/null @@ -1,110 +0,0 @@ -{ pkgs ? import ../../nix {} -, version ? "none" -# Default sourceUrl is for local development. Works with -# nix-build -A doc.manual -# For release, use: https://github.com/hercules-ci/arion/blob/${version} -, sourceUrl ? "../../.." -}: -let - inherit (pkgs) recurseIntoAttrs callPackage runCommand lib stdenv ; - - nixosManualPath = s: "${pkgs.path}/nixos/doc/manual/${s}"; - - # NixOS module system options in JSON format. - options = { moduleType, description, /*optionsList*/ optionsExpr }: recurseIntoAttrs rec { - optionsXML = - # builtins.toFile "options.xml" (builtins.toXML optionsList); - pkgs.runCommand "options.xml" { - buildInputs = [pkgs.nix pkgs.jq]; - inherit optionsExpr; - } '' - export NIX_LOG_DIR=$PWD - export NIX_STATE_DIR=$PWD - nix-instantiate \ - --option sandbox false \ - --readonly-mode \ - --eval \ - --expr "$optionsExpr" \ - --xml \ - --strict \ - --show-trace \ - >$out - ''; - - optionsDocBook = runCommand "options-db.xml" {} '' - optionsXML=${optionsXML} - ${pkgs.buildPackages.libxslt.bin}/bin/xsltproc \ - --stringparam revision '${version}' \ - --stringparam sourceUrl '${sourceUrl}' \ - -o intermediate.xml ${./options-to-docbook.xsl} $optionsXML - ${pkgs.buildPackages.libxslt.bin}/bin/xsltproc \ - -o "$out" ${nixosManualPath "postprocess-option-descriptions.xsl"} intermediate.xml - ''; - }; - - compositionOptions = options { - moduleType = "composition"; - description = "List of Arion composition-level options in JSON format"; - optionsExpr = let - src = ../../src/nix; - in '' - let pkgs = import ${pkgs.path} {}; - fixPaths = opt: opt // { - declarations = map (d: "src/nix" + (lib.strings.removePrefix (toString ${src}) (toString d))) opt.declarations; - }; - inherit (pkgs) lib; - composition = import ${src}/eval-composition.nix { inherit pkgs; }; - in map fixPaths (lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList composition.options)) - ''; - }; - - generatedDocBook = runCommand "generated-docbook" {} '' - mkdir $out - ln -s ${compositionOptions.optionsDocBook} $out/options-composition.xml - ''; - - manual = stdenv.mkDerivation { - src = lib.sourceByRegex ./. [ - "Makefile$" - ".*\.asciidoc$" - ".*\.xsl$" - ".*\.css$" - "^manual.xml$" - "^manual.xml$" - ]; - name = "arion-manual"; - version = version; - buildInputs = [ - (pkgs.libxslt.bin or pkgs.libxslt) - pkgs.asciidoctor - ]; - XML_CATALOG_FILES = "${pkgs.docbook_xsl}/xml/xsl/docbook/catalog.xml"; - inherit generatedDocBook; - configurePhase = '' - export docdir=$out/doc - ''; - postPatch = '' - substituteInPlace manual.xml --subst-var version - ''; - prePatch = '' - cp ${generatedDocBook}/* . - substituteInPlace options-composition.xml \ - --replace 'xml:id="appendix-configuration-options"' 'xml:id="appendix-composition-options"' \ - --replace 'Configuration Options' 'Composition Options' \ - --replace 'xml:id="configuration-variable-list"' 'xml:id="composition-variable-list"' \ - ; - ''; - shellHook = '' - live-build() { - patchPhase - inotifywait -e MODIFY -m -r . | while read; do - make - done - } - ''; - passthru = { - inherit generatedDocBook; - }; - }; -in - manual diff --git a/doc/manual/live-build b/doc/manual/live-build deleted file mode 100755 index 860d200..0000000 --- a/doc/manual/live-build +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env nix-shell -#!nix-shell -A manual -#!nix-shell --run live-build diff --git a/doc/manual/manual.xml b/doc/manual/manual.xml deleted file mode 100644 index 87d9616..0000000 --- a/doc/manual/manual.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Arion Manual - Version none - - - - Hercules Labs and other Arion contributors - - Author - - - - - - - diff --git a/doc/manual/options-to-docbook.xsl b/doc/manual/options-to-docbook.xsl deleted file mode 100644 index d6e28c1..0000000 --- a/doc/manual/options-to-docbook.xsl +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - Configuration Options - - - - - - - - - - - - - - - - - - - - Type: - - - - - (read only) - - - - - - - Default: - - - - - - - - Example: - - - - - - - - - - - - - - - Related packages: - - - - - - - - Declared by: - - - - - - - Defined by: - - - - - - - - - - - - - - - - - - - -'' -'' - - - - - - - - - - null - - - - - - - '''' - - - "" - - - - - - - - - - - - true - - - - - false - - - - - [ - - - - - ] - - - - - - - - - - { - - - = - ; - - } - - - - - (build of ) - - - - - - - - - - / - - - file:// - - - - - - </> - - - - - - - - - - - - - λ - - - - diff --git a/doc/manual/style.css b/doc/manual/style.css deleted file mode 100644 index 9b6e154..0000000 --- a/doc/manual/style.css +++ /dev/null @@ -1,159 +0,0 @@ - -hr { color: #ddd; margin-top: 3ex; } -h1, h2, h3, h4 { font-weight: bold; } -h1 { font-size: 200%; margin-top: 5ex; } -h2 { font-size: 160%; margin-top: 4ex; } -h3,h4 { font-size: 120%; margin-top: 3ex; } - -/* From Semantic UI */ -body { - font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif; - font-size: 14px; - line-height: 1.4285em; - color: rgba(0,0,0,.87); -} - -code.literal { - background-color: #f7f7f7; -} - -a { - background-color:transparent; - -webkit-text-decoration-skip:objects -} -a { - color:#4183c4; - text-decoration:none -} -a:hover { - color:#1e70bf; - text-decoration:none -} -::-webkit-selection { - background-color:#cce2ff; - color:rgba(0,0,0,.87) -} -::-moz-selection { - background-color:#cce2ff; - color:rgba(0,0,0,.87) -} -::selection { - background-color:#cce2ff; - color:rgba(0,0,0,.87) -} - -/* toc menu */ -@media screen and (min-width: 60em) { - body { - margin-left: 16.5em; - } - div.toc { - position: fixed; - top: 0pt; - left: 1em; - height: 100%; - overflow-y: auto; - width: 15.5em; - } -} -@media screen and (min-width: 90em) { - div.toc { - left: 5em; - } -} -/* hide per chapter toc */ -div.chapter div.toc { - display: none; -} - - -/* From Nixpkgs: */ - -div.book -{ - text-align: center; -} - -div.book > div -{ - /* - * based on https://medium.com/@zkareemz/golden-ratio-62b3b6d4282a - * we do 70 characters per line to fit code listings better - * 70 * (font-size / 1.618) - * expression for emacs: - * (* 70 (/ 1 1.618)) - */ - max-width: 43.2em; - text-align: left; - margin: auto; -} - - -/*************************************************************************** - Special elements: - ***************************************************************************/ - -.term -{ - font-weight: bold; - -} - -div.variablelist dd p, div.glosslist dd p -{ - margin-top: 0em; -} - -div.variablelist dd, div.glosslist dd -{ - margin-left: 1.5em; -} - -div.glosslist dt -{ - font-style: italic; -} - -.varname -{ - color: #004000; -} - -span.command strong -{ - font-weight: normal; - color: #004000; -} - -div.calloutlist table -{ - box-shadow: none; -} - -table -{ - border-collapse: collapse; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -table.simplelist -{ - text-align: left; - color: #005aa0; - border: 0; - padding: 5px; - background: #fffff5; - font-weight: normal; - font-style: italic; - box-shadow: none; - margin-bottom: 1em; -} - -div.navheader table, div.navfooter table { - box-shadow: none; -} - -div.affiliation -{ - font-style: italic; -} diff --git a/docs/antora.yml b/docs/antora.yml new file mode 100644 index 0000000..cc452f5 --- /dev/null +++ b/docs/antora.yml @@ -0,0 +1,6 @@ +name: arion +title: Arion Documentation +version: 'master' +nav: +- modules/ROOT/nav.adoc +- modules/reference/nav.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc new file mode 100644 index 0000000..02e2aed --- /dev/null +++ b/docs/modules/ROOT/nav.adoc @@ -0,0 +1,2 @@ +* xref:index.adoc[Getting Started] +* xref:options.adoc[Arion Options] diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc new file mode 100644 index 0000000..6d2e348 --- /dev/null +++ b/docs/modules/ROOT/pages/index.adoc @@ -0,0 +1,276 @@ += Welcome to Arion documentation + +== 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 granularity: + + * <> + * <> + * <> + * <> + +== 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) {}).arion ]; + 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 {} +``` + +or more sophisticated (recommended) setup with https://github.com/nmattia/niv[Niv]. + +=== arion-compose.nix + +Describe containers using NixOS-style modules. There are a few options: + +==== 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. + +Arion has run successfully on Linux distributions other than NixOS, but we only perform CI for Arion on NixOS. + + +== 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 ` or if you've changed the image rebuild +them using `arion up -d --always-recreate-deps `. + +=== 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) diff --git a/docs/modules/ROOT/pages/options.adoc b/docs/modules/ROOT/pages/options.adoc new file mode 100644 index 0000000..6bd05ec --- /dev/null +++ b/docs/modules/ROOT/pages/options.adoc @@ -0,0 +1 @@ +include::partial$NixOSOptions.adoc[] \ No newline at end of file diff --git a/docs/modules/ROOT/partials/NixOSOptions.adoc b/docs/modules/ROOT/partials/NixOSOptions.adoc new file mode 100644 index 0000000..d9f551f --- /dev/null +++ b/docs/modules/ROOT/partials/NixOSOptions.adoc @@ -0,0 +1,941 @@ += Arion options + +== docker-compose.extended + +Attribute set that will be turned into the x-arion section of the docker-compose.yaml file. + +[discrete] +=== details + +Type:: attribute set +No Default:: {blank} + +No Example:: {blank} + +== docker-compose.raw + +Attribute set that will be turned into the docker-compose.yaml file, using Nix's toJSON builtin. + +[discrete] +=== details + +Type:: attribute set +No Default:: {blank} + +No Example:: {blank} + +== host.nixStorePrefix + +Prefixes store paths on the host, allowing the Nix store to be +stored at an alternate location without altering the format of +store paths. + +For example: instead of mounting the host's /nix/store as the +container's /nix/store, this will mount /mnt/foo/nix/store +as the container's /nix/store. + + +[discrete] +=== details + +Type:: string +Default:: ++ +---- +"" +---- + + +Example:: ++ +---- +"/mnt/foo" +---- + + +== host.uid + +The numeric user id (UID) of the user running arion. + +This lets you to write modules that interact with the host +user's files, which is helpful for local development, but not +intended for production-like deployment scenarios. + + +[discrete] +=== details + +Type:: signed integer +No Default:: {blank} + +No Example:: {blank} + +== out.dockerComposeYaml + +A derivation that produces a docker-compose.yaml file for this composition. + +[discrete] +=== details + +Type:: package +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== out.dockerComposeYamlAttrs + +The text of out.dockerComposeYaml. + +[discrete] +=== details + +Type:: attribute set of unspecifieds +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== out.dockerComposeYamlText + +The text of out.dockerComposeYaml. + +[discrete] +=== details + +Type:: string +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== services + +An attribute set of service configurations. A service specifies how to run an image as a container. + +[discrete] +=== details + +Type:: attribute set of submodules +No Default:: {blank} + +No Example:: {blank} + +== services..composition + +The composition configuration. + + +[discrete] +=== details + +Type:: attribute set +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== services..host + +The composition-level host option values. + + +[discrete] +=== details + +Type:: attribute set +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== services..image.command + + + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..image.contents + +Top level paths in the container. + + +[discrete] +=== details + +Type:: list of packages +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..image.name + +A human readable name for the docker image. + +Shows up in the docker ps output in the +IMAGE column, among other places. + + +[discrete] +=== details + +Type:: string +Default:: ++ +---- +{"_type":"literalExample","text":"config.service.name"} +---- + + +No Example:: {blank} + +== services..image.nixBuild + +Whether to build this image with Nixpkgs' +dockerTools.buildLayeredImage +and then load it with docker load. + +By default, an image will be built with Nix unless +is set. See also , which defaults to +the service name. + + +[discrete] +=== details + +Type:: boolean +No Default:: {blank} + +No Example:: {blank} + +== services..image.rawConfig + +This is a low-level fallback for when a container option has not +been modeled in the Arion module system. + +This attribute set does not have an appropriate merge function. +Please use the specific image options instead. + +Run-time configuration of the container. A full list of the +options are available at in the Docker Image Specification +v1.2.0. + + +[discrete] +=== details + +Type:: attribute set of unspecifieds +Default:: ++ +---- +{} +---- + + +No Example:: {blank} + +== services..nixos.build + +NixOS build products from config.system.build, such as toplevel and etc. + +This option is unused by default, because not all images use NixOS. + +One way to use this is to enable nixos.useSystemd, but the +NixOS configuration can be used in other ways. + + +[discrete] +=== details + +Type:: attribute set +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== services..nixos.configuration + +Modules to add to the NixOS configuration. + +This option is unused by default, because not all images use NixOS. + +One way to use this is to enable nixos.useSystemd, but the +NixOS configuration can be used in other ways. + + +[discrete] +=== details + +Type:: list of unspecifieds or unspecified convertible to it +Default:: ++ +---- +{} +---- + + +No Example:: {blank} + +== services..nixos.evaluatedConfig + +Evaluated NixOS configuration, to be read by service-level modules. + +This option is unused by default, because not all images use NixOS. + +One way to use this is to enable nixos.useSystemd, but the +NixOS configuration can be used in other ways. + + +[discrete] +=== details + +Type:: attribute set +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== services..nixos.useSystemd + +When enabled, call the NixOS systemd-based init system. + +Configure NixOS with nixos.configuration. + + +[discrete] +=== details + +Type:: boolean +Default:: ++ +---- +false +---- + + +No Example:: {blank} + +== services..out.extendedInfo + +Information about a service to include in the Docker Compose file, +but that will not be used by the docker-compose command +itself. + +It will be inserted in x-arion.serviceInfo.<service.name>. + + +[discrete] +=== details + +Type:: attribute set of unspecifieds +Default:: ++ +---- +{} +---- + + +No Example:: {blank} + +== services..out.service + +Raw input for the service in docker-compose.yaml. + +You should not need to use this option. If anything is +missing, please contribute the missing option. + +This option is user accessible because it may serve as an +escape hatch for some. + + +[discrete] +=== details + +Type:: attribute set of unspecifieds +No Default:: {blank} + +No Example:: {blank} + +== services..service.build.context + +Locates a Dockerfile to use for creating an image to use in this service. + +See Docker Compose#context + + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.capabilities + +Enable/disable linux capabilities, or pick Docker's default. + +Setting a capability to true means that it will be +"added". Setting it to false means that it will be "dropped". +See Docker Compose#cap_add-cap_drop + +Omitted and null capabilities will therefore be set +according to Docker's default list of capabilities. + + +[discrete] +=== details + +Type:: attribute set of null or booleans +Default:: ++ +---- +{} +---- + + +Example:: ++ +---- +{"ALL":true,"NET_ADMIN":false,"SYS_ADMIN":false} +---- + + +== services..service.command + +See Docker Compose#command + +[discrete] +=== details + +Type:: null or unspecified +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.container_name + +See Docker Compose#container_name + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.defaultExec + +Container program and arguments to invoke when calling +arion exec <service.name> without further arguments. + + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +["/bin/sh"] +---- + + +No Example:: {blank} + +== services..service.depends_on + +See Docker Compose#depends_on + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.devices + +See docker run --device documentation + +See Docker Compose#devices + + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.entrypoint + +See Docker Compose#entrypoint + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.env_file + +See Docker Compose#env_file + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.environment + +See Docker Compose#environment + +[discrete] +=== details + +Type:: attribute set of string or signed integers +Default:: ++ +---- +{} +---- + + +No Example:: {blank} + +== services..service.expose + +See Docker Compose#expose + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.external_links + +See Docker Compose#external_links + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.extra_hosts + +See Docker Compose#extra_hosts + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.hostStoreAsReadOnly + +Adds a ':ro' (read-only) access mode to the host nix store bind mount. + +[discrete] +=== details + +Type:: boolean +Default:: ++ +---- +true +---- + + +No Example:: {blank} + +== services..service.hostname + +Analogous to the docker run counterpart. + +See Docker Compose#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir + + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.image + +See Docker Compose#image + +[discrete] +=== details + +Type:: string +No Default:: {blank} + +No Example:: {blank} + +== services..service.links + +See Docker Compose#links + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.name + +The name of the service - <name> in the composition-level services.<name> + + +[discrete] +=== details + +Type:: string +No Default:: {blank} +Read Only:: {blank} +No Example:: {blank} + +== services..service.network_mode + +See Docker Compose#network_mode + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.networks + +See Docker Compose#networks + +[discrete] +=== details + +Type:: null or list of strings +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.ports + +Expose ports on host. "host:container" or structured. + +See Docker Compose#ports + + +[discrete] +=== details + +Type:: list of unspecifieds +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.privileged + +Analogous to the docker run counterpart. + +See Docker Compose#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir + + +[discrete] +=== details + +Type:: null or boolean +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.restart + +See Docker Compose#restart + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.stop_signal + +See Docker Compose#stop_signal + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.sysctls + +See Docker Compose#sysctls + +[discrete] +=== details + +Type:: attribute set of string or signed integers +Default:: ++ +---- +{} +---- + + +No Example:: {blank} + +== services..service.tmpfs + +See Docker Compose#tmpfs + +[discrete] +=== details + +Type:: list of strings +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.tty + +Analogous to the docker run counterpart. + +See Docker Compose#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir + + +[discrete] +=== details + +Type:: null or boolean +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.useHostNixDaemon + +Make the host Nix daemon available. + +[discrete] +=== details + +Type:: boolean +Default:: ++ +---- +false +---- + + +No Example:: {blank} + +== services..service.useHostStore + +Bind mounts the host store if enabled, avoiding copying. + +[discrete] +=== details + +Type:: boolean +Default:: ++ +---- +false +---- + + +No Example:: {blank} + +== services..service.user + +Analogous to the docker run counterpart. + +See Docker Compose#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir + + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + +== services..service.volumes + +See Docker Compose#volumes + +[discrete] +=== details + +Type:: list of unspecifieds +Default:: ++ +---- +[] +---- + + +No Example:: {blank} + +== services..service.working_dir + +Analogous to the docker run counterpart. + +See Docker Compose#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir + + +[discrete] +=== details + +Type:: null or string +Default:: ++ +---- +null +---- + + +No Example:: {blank} + diff --git a/docs/options.nix b/docs/options.nix new file mode 100644 index 0000000..3d3c484 --- /dev/null +++ b/docs/options.nix @@ -0,0 +1,15 @@ +{ pkgs ? import ../nix {} }: + +let + eval = pkgs.lib.evalModules { + modules = import ../src/nix/modules.nix; + }; + options = pkgs.nixosOptionsDoc { + options = eval.options; + }; + +in pkgs.writeText "agent-options" '' + = Arion options + + ${options.optionsAsciiDoc} +'' diff --git a/docs/ui-bundle.zip b/docs/ui-bundle.zip new file mode 100644 index 0000000..259e1ff Binary files /dev/null and b/docs/ui-bundle.zip differ diff --git a/nix/ci.nix b/nix/ci.nix index a8bca06..ed61e2e 100644 --- a/nix/ci.nix +++ b/nix/ci.nix @@ -8,20 +8,15 @@ dimension "Nixpkgs version" { "nixos-19_03" = { nixpkgsSource = "nixpkgs"; isReferenceNixpkgs = true; + enableDoc = false; }; "nixos-19_09" = { nixpkgsSource = "nixos-19.09"; - - # Broken since 19.09, wontfix because doc tooling will be changed. - # TODO: reenable - enableDoc = false; + enableDoc = true; }; "nixos-unstable" = { nixpkgsSource = "nixos-unstable"; - - # Broken since 19.09, wontfix because doc tooling will be changed. - # TODO: reenable - enableDoc = false; + enableDoc = true; }; } ( _name: { nixpkgsSource, isReferenceNixpkgs ? false, enableDoc ? true }: @@ -39,7 +34,7 @@ dimension "Nixpkgs version" { { inherit (pkgs) arion tests; } // lib.optionalAttrs enableDoc { - doc = pkgs.recurseIntoAttrs (import ../doc { inherit pkgs; }); + inherit (pkgs) doc doc-options doc-options-check; } // lib.optionalAttrs isReferenceTarget { inherit (pkgs.arion-project.haskellPkgs) arion-compose-checked; } diff --git a/nix/default.nix b/nix/default.nix index 7393d12..921b747 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,5 +1,6 @@ { sources ? import ./sources.nix -, nixpkgsSrc ? sources.nixpkgs +, nixpkgsName ? "nixos-19.09" +, nixpkgsSrc ? sources.${nixpkgsName} , system ? builtins.currentSystem , ... }: diff --git a/nix/overlay.nix b/nix/overlay.nix index 5be1099..b058f58 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -5,12 +5,34 @@ let sources = import ./sources.nix; + fakeRepo = src: super.runCommand "source" { inherit src; buildInputs = [super.git]; } '' + cp -r --no-preserve=mode $src $out + git init + cp -r .git $out + ''; + in { inherit (import ./.. { pkgs = self; }) arion; tests = super.callPackage ../tests {}; - doc = super.callPackage ../doc {}; + + doc-options = import ../docs/options.nix {}; + doc-options-check = self.runCommand "doc-options-check" {} '' + diff --color -u ${../docs/modules/ROOT/partials/NixOSOptions.adoc} ${self.doc-options} + touch $out + ''; + doc = self.stdenv.mkDerivation { + name = "arion-documentation"; + buildInputs = [super.antora]; + src = fakeRepo ../.; + HOME = "."; + buildPhase = "antora antora-playbook"; + installPhase = '' + mkdir $out + mv public/* $out/ + ''; + }; arion-project = super.recurseIntoAttrs { haskellPkgs = super.haskellPackages.extend (import ./haskell-overlay.nix self super); diff --git a/nix/sources.json b/nix/sources.json index 7e4b088..25118fd 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -42,10 +42,10 @@ "homepage": "https://github.com/NixOS/nixpkgs", "owner": "NixOS", "repo": "nixpkgs-channels", - "rev": "6420e2649fa9e267481fb78e602022dab9d1dcd1", - "sha256": "1z3hx7gp8nxk3fspi8vik3j9zxpajj3s7nxvjvx3b5igndxwbp74", + "rev": "d15a31f88a261281cd7c79038ae860c5ed95507d", + "sha256": "038iqfwmppnxq6aa89qm6k98lhwg686bmc9qjifibddm8pcp2wd0", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs-channels/archive/6420e2649fa9e267481fb78e602022dab9d1dcd1.tar.gz", + "url": "https://github.com/NixOS/nixpkgs-channels/archive/d15a31f88a261281cd7c79038ae860c5ed95507d.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "project.nix": { diff --git a/src/nix/eval-composition.nix b/src/nix/eval-composition.nix index 1081864..7465fb7 100644 --- a/src/nix/eval-composition.nix +++ b/src/nix/eval-composition.nix @@ -18,12 +18,7 @@ let builtinModules = [ argsModule - ./modules/composition/docker-compose.nix - ./modules/composition/host-environment.nix - ./modules/composition/images.nix - ./modules/composition/service-info.nix - ./modules/composition/arion-base-image.nix - ]; + ] ++ import ./modules.nix; argsModule = { _file = ./eval-composition.nix; diff --git a/src/nix/modules.nix b/src/nix/modules.nix new file mode 100644 index 0000000..64f3650 --- /dev/null +++ b/src/nix/modules.nix @@ -0,0 +1,7 @@ +[ + ./modules/composition/docker-compose.nix + ./modules/composition/host-environment.nix + ./modules/composition/images.nix + ./modules/composition/service-info.nix + ./modules/composition/arion-base-image.nix +] \ No newline at end of file