diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix index bf982ba0d7e2..161b4f6027b2 100644 --- a/nixos/modules/virtualisation/oci-containers.nix +++ b/nixos/modules/virtualisation/oci-containers.nix @@ -33,6 +33,25 @@ let example = literalExpression "pkgs.dockerTools.buildImage {...};"; }; + imageStream = mkOption { + type = with types; nullOr package; + default = null; + description = '' + Path to a script that streams the desired image on standard output. + + This option is mainly intended for use with + `pkgs.dockerTools.streamLayeredImage` so that the intermediate + image archive does not need to be stored in the Nix store. For + larger images this optimization can significantly reduce Nix store + churn compared to using the `imageFile` option, because you don't + have to store a new copy of the image archive in the Nix store + every time you change the image. Instead, if you stream the image + then you only need to build and store the layers that differ from + the previous image. + ''; + example = literalExpression "pkgs.dockerTools.streamLayeredImage {...};"; + }; + login = { username = mkOption { @@ -275,6 +294,9 @@ let ${optionalString (container.imageFile != null) '' ${cfg.backend} load -i ${container.imageFile} ''} + ${optionalString (container.imageStream != null) '' + ${container.imageStream} | ${cfg.backend} load + ''} ${optionalString (cfg.backend == "podman") '' rm -f /run/podman-${escapedName}.ctr-id ''} @@ -282,10 +304,10 @@ let }; in { wantedBy = [] ++ optional (container.autoStart) "multi-user.target"; - wants = lib.optional (container.imageFile == null) "network-online.target"; + wants = lib.optional (container.imageFile == null && container.imageStream == null) "network-online.target"; after = lib.optionals (cfg.backend == "docker") [ "docker.service" "docker.socket" ] - # if imageFile is not set, the service needs the network to download the image from the registry - ++ lib.optionals (container.imageFile == null) [ "network-online.target" ] + # if imageFile or imageStream is not set, the service needs the network to download the image from the registry + ++ lib.optionals (container.imageFile == null && container.imageStream == null) [ "network-online.target" ] ++ dependsOn; requires = dependsOn; environment = proxy_env; @@ -393,6 +415,17 @@ in { config = lib.mkIf (cfg.containers != {}) (lib.mkMerge [ { systemd.services = mapAttrs' (n: v: nameValuePair "${cfg.backend}-${n}" (mkService n v)) cfg.containers; + + assertions = + let + toAssertion = _: { imageFile, imageStream, ... }: + { assertion = imageFile == null || imageStream == null; + + message = "You can only define one of imageFile and imageStream"; + }; + + in + lib.mapAttrsToList toAssertion cfg.containers; } (lib.mkIf (cfg.backend == "podman") { virtualisation.podman.enable = true; diff --git a/nixos/tests/cntr.nix b/nixos/tests/cntr.nix index 598143beb6c0..2166fb8f9b09 100644 --- a/nixos/tests/cntr.nix +++ b/nixos/tests/cntr.nix @@ -18,7 +18,7 @@ let inherit backend; containers.nginx = { image = "nginx-container"; - imageFile = pkgs.dockerTools.examples.nginx; + imageStream = pkgs.dockerTools.examples.nginxStream; ports = [ "8181:80" ]; }; }; diff --git a/nixos/tests/oci-containers.nix b/nixos/tests/oci-containers.nix index 1f8e276204a8..9adceb11f18c 100644 --- a/nixos/tests/oci-containers.nix +++ b/nixos/tests/oci-containers.nix @@ -20,7 +20,7 @@ let inherit backend; containers.nginx = { image = "nginx-container"; - imageFile = pkgs.dockerTools.examples.nginx; + imageStream = pkgs.dockerTools.examples.nginxStream; ports = ["8181:80"]; }; }; diff --git a/nixos/tests/traefik.nix b/nixos/tests/traefik.nix index ce808e6ec95a..f26b79a0fa4d 100644 --- a/nixos/tests/traefik.nix +++ b/nixos/tests/traefik.nix @@ -23,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ... }: { "traefik.http.routers.nginx.rule=Host(`nginx.traefik.test`)" ]; image = "nginx-container"; - imageFile = pkgs.dockerTools.examples.nginx; + imageStream = pkgs.dockerTools.examples.nginxStream; }; }; diff --git a/pkgs/build-support/docker/examples.nix b/pkgs/build-support/docker/examples.nix index 8717259cd189..af8be8d79f24 100644 --- a/pkgs/build-support/docker/examples.nix +++ b/pkgs/build-support/docker/examples.nix @@ -17,6 +17,52 @@ let }; evalMinimalConfig = module: nixosLib.evalModules { modules = [ module ]; }; + nginxArguments = let + nginxPort = "80"; + nginxConf = pkgs.writeText "nginx.conf" '' + user nobody nobody; + daemon off; + error_log /dev/stdout info; + pid /dev/null; + events {} + http { + access_log /dev/stdout; + server { + listen ${nginxPort}; + index index.html; + location / { + root ${nginxWebRoot}; + } + } + } + ''; + nginxWebRoot = pkgs.writeTextDir "index.html" '' +

Hello from NGINX

+ ''; + in + { name = "nginx-container"; + tag = "latest"; + contents = [ + fakeNss + pkgs.nginx + ]; + + extraCommands = '' + mkdir -p tmp/nginx_client_body + + # nginx still tries to read this directory even if error_log + # directive is specifying another file :/ + mkdir -p var/log/nginx + ''; + + config = { + Cmd = [ "nginx" "-c" nginxConf ]; + ExposedPorts = { + "${nginxPort}/tcp" = {}; + }; + }; + }; + in rec { @@ -60,52 +106,10 @@ rec { }; # 3. another service example - nginx = let - nginxPort = "80"; - nginxConf = pkgs.writeText "nginx.conf" '' - user nobody nobody; - daemon off; - error_log /dev/stdout info; - pid /dev/null; - events {} - http { - access_log /dev/stdout; - server { - listen ${nginxPort}; - index index.html; - location / { - root ${nginxWebRoot}; - } - } - } - ''; - nginxWebRoot = pkgs.writeTextDir "index.html" '' -

Hello from NGINX

- ''; - in - buildLayeredImage { - name = "nginx-container"; - tag = "latest"; - contents = [ - fakeNss - pkgs.nginx - ]; + nginx = buildLayeredImage nginxArguments; - extraCommands = '' - mkdir -p tmp/nginx_client_body - - # nginx still tries to read this directory even if error_log - # directive is specifying another file :/ - mkdir -p var/log/nginx - ''; - - config = { - Cmd = [ "nginx" "-c" nginxConf ]; - ExposedPorts = { - "${nginxPort}/tcp" = {}; - }; - }; - }; + # Used to demonstrate how virtualisation.oci-containers.imageStream works + nginxStream = pkgs.dockerTools.streamLayeredImage nginxArguments; # 4. example of pulling an image. could be used as a base for other images nixFromDockerHub = pullImage {