From 1a2c5c967b140647d167467c75d2b5483d0c092f Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Thu, 18 Feb 2021 21:02:49 +0100 Subject: [PATCH] Document mutable container image construction --- LICENSE | 2 +- README.md | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 192 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index b85841c..c75e388 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020 Sander van der Burg +Copyright (c) 2020-2021 Sander van der Burg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index ffe38a5..8731453 100644 --- a/README.md +++ b/README.md @@ -999,11 +999,201 @@ When interactive mode has been enabled, you should be able to "connect" to a container in which you can execute shell commands, for example to control the life-cycle of the sub processes: -``` +```bash $ docker exec -it mycontainer /bin/bash $ ps aux ``` +Building a mutable multi-process Docker container +------------------------------------------------- +A big drawback of the multi-process container deployed in the previous +section is that its deployment is *immutable* -- the deployment is done from an +image that configures all process instances, but when it is desired to change +the configuration, a new image needs to be generated and the container must +be discarded and redeployed from that new image. + +It is also possible to deploy *mutable* multi-process containers in which the +configuration of the managed system can be updated without the need to bring +the container down. + +The following Nix expression (`default.nix`) can be used to construct a mutable +multi-process image: + +```nix +{ pkgs ? import { inherit system; } +, system ? builtins.currentSystem +}: + +let + createMutableMultiProcessImage = import ../../nixproc/create-image-from-steps/create-mutable-multi-process-image-universal.nix { + inherit pkgs; + }; +in +createMutableMultiProcessImage { + name = "multiprocess"; + tag = "test"; + exprFile = ./processes.nix; + idResourcesFile = ./idresources.nix; + idsFile = ./ids.nix; + processManager = "supervisord"; + interactive = true; + manpages = false; + forceDisableUserChange = false; + bootstrap = true; +} +``` + +The Nix expression shown above invokes the `createMutableMultiProcessImage` +function that has a similar interface to the immutable variant shown in the +previous section, with the following differences: + +* The `exprFile` is also used for specifying the process model to deploy from, + but a notable difference is that for mutable containers this model is copied + into the container and deployed from within the container. +* To make deploying process models model possible that also use + `nixproc-id-assign` to automatically assign unique numeric IDs, the + `idResourcesFile` and `idsFile` parameters can be used to copy these models + into the container as well. These parameters are not mandatory. +* As a container entry point, a *bootstrap* script is executed, that on first + deployment, uses the Nix package manager, and the corresponding + `nixproc-*-switch` tool to deploy the system. +* The `bootstrap` parameter allows you to disable the bootstrap entry point. + By default, it is enabled. + +To make deployments in mutable containers possible, the processes model should +not contain any references to files on the local file system (with the exception +of the `ids.nix` model that should reside in the same base directory). + +The following process model (`processes.nix`) eliminates local file dependencies +by using `builtins.fetchGit` to obtain the Nix process management framework: + +```nix +{ pkgs ? import { inherit system; } +, system ? builtins.currentSystem +, stateDir ? "/var" +, runtimeDir ? "${stateDir}/run" +, logDir ? "${stateDir}/log" +, cacheDir ? "${stateDir}/cache" +, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp") +, forceDisableUserChange ? false +, processManager +, webappMode ? null +}: + +let + nix-processmgmt = builtins.fetchGit { + url = https://github.com/svanderburg/nix-processmgmt.git; + ref = "master"; + }; + + ids = if builtins.pathExists ./ids.nix then (import ./ids.nix).ids else {}; + + sharedConstructors = import "${nix-processmgmt}/examples/services-agnostic/constructors.nix" { + inherit pkgs stateDir runtimeDir logDir cacheDir tmpDir forceDisableUserChange processManager ids; + }; + + constructors = import "${nix-processmgmt}/examples/webapps-agnostic/constructors.nix" { + inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager webappMode ids; + }; +in +rec { + webapp = rec { + port = ids.webappPorts.webapp or 0; + dnsName = "webapp.local"; + + pkg = constructors.webapp { + inherit port; + }; + + requiresUniqueIdsFor = [ "webappPorts" "uids" "gids" ]; + }; + + nginx = rec { + port = ids.nginxPorts.nginx or 0; + + pkg = sharedConstructors.nginxReverseProxyHostBased { + webapps = [ webapp ]; + inherit port; + } {}; + + requiresUniqueIdsFor = [ "nginxPorts" "uids" "gids" ]; + }; +} +``` + +To deploy a mutable multi-process image container, we can run the following +command to build the image: + +```bash +$ nix-build +``` + +and load it into Docker as follows: + +```bash +$ docker load -i result +``` + +We can deploy a container instance from the image in interactive mode as +follows: + +```bash +$ docker run --name mycontainer --rm --network host -it multiprocess:test +``` + +On first startup, the container will carry out a bootstrap procedure that uses +the Nix process management framework to deploy all the processes in the +processes model. + +When the deployment of the system is complete, we can "connect" to the container +instance as follows: + +```bash +$ docker exec -it mycontainer /bin/bash +``` + +In the container, we can edit the process model (to for example, add a new +`webapp` instance): + +```bash +$ mcedit /etc/nixproc/processes.nix +``` + +and redeploy the new configuration with the following command: + +```bash +$ nixproc-supervisord-switch +``` + +then the new configuration should become active without the need to restart the +container. Moreover, when stopping the container and starting it again, the last +deployed configuration should become active again. + +Building a Nix-enabled Docker container +--------------------------------------- +One of the interesting aspects of a mutable multi-process image is that it +provides a fully featured Nix installation in a container that can be used +to deploy arbitrary Nix packages. + +It is also possible to generate an image whose only purpose is to provide a +working Nix installation: + +```nix +{ pkgs ? import { inherit system; } +, system ? builtins.currentSystem +}: + +let + createNixImage = import ../../nixproc/create-image-from-steps/create-nix-image.nix { + inherit pkgs; + }; +in +createNixImage { + name = "foobar"; + tag = "test"; +} +``` + Examples ======== This repository contains a number of example systems, that can be found in the