nixos/docker-containers: Rename to virtualisation.oci-containers.containers.

And allow the runtime to be configurable via the
`virtualisation.oci-containers.backend` option.

Valid choices are "podman" and "docker".
This commit is contained in:
adisbladis 2020-04-20 12:31:07 +01:00
parent 2fb5dac372
commit 2f7747526c
No known key found for this signature in database
GPG Key ID: 110BFAD44C6249B7
7 changed files with 142 additions and 68 deletions

View File

@ -55,6 +55,12 @@
The new <varname>virtualisation.containers</varname> module manages configuration shared by the CRI-O and Podman modules. The new <varname>virtualisation.containers</varname> module manages configuration shared by the CRI-O and Podman modules.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
Declarative Docker containers are renamed from <varname>docker-containers</varname> to <varname>virtualisation.oci-containers.containers</varname>.
This is to make it possible to use <literal>podman</literal> instead of <literal>docker</literal>.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</section> </section>

View File

@ -984,9 +984,9 @@
./virtualisation/container-config.nix ./virtualisation/container-config.nix
./virtualisation/containers.nix ./virtualisation/containers.nix
./virtualisation/nixos-containers.nix ./virtualisation/nixos-containers.nix
./virtualisation/oci-containers.nix
./virtualisation/cri-o.nix ./virtualisation/cri-o.nix
./virtualisation/docker.nix ./virtualisation/docker.nix
./virtualisation/docker-containers.nix
./virtualisation/ecs-agent.nix ./virtualisation/ecs-agent.nix
./virtualisation/libvirtd.nix ./virtualisation/libvirtd.nix
./virtualisation/lxc.nix ./virtualisation/lxc.nix

View File

@ -1,17 +1,20 @@
{ config, lib, pkgs, ... }: { config, options, lib, pkgs, ... }:
with lib; with lib;
let let
cfg = config.docker-containers; cfg = config.virtualisation.oci-containers;
proxy_env = config.networking.proxy.envVars;
dockerContainer = defaultBackend = options.virtualisation.oci-containers.backend.default;
containerOptions =
{ ... }: { { ... }: {
options = { options = {
image = mkOption { image = mkOption {
type = with types; str; type = with types; str;
description = "Docker image to run."; description = "OCI image to run.";
example = "library/hello-world"; example = "library/hello-world";
}; };
@ -58,18 +61,19 @@ let
log-driver = mkOption { log-driver = mkOption {
type = types.str; type = types.str;
default = "none"; default = "journald";
description = '' description = ''
Logging driver for the container. The default of Logging driver for the container. The default of
<literal>"none"</literal> means that the container's logs will be <literal>"journald"</literal> means that the container's logs will be
handled as part of the systemd unit. Setting this to handled as part of the systemd unit.
<literal>"journald"</literal> will result in duplicate logging, but
the container's logs will be visible to the <command>docker
logs</command> command.
For more details and a full list of logging drivers, refer to the For more details and a full list of logging drivers, refer to respective backends documentation.
<link xlink:href="https://docs.docker.com/engine/reference/run/#logging-drivers---log-driver">
Docker engine documentation</link> For Docker:
<link xlink:href="https://docs.docker.com/engine/reference/run/#logging-drivers---log-driver">Docker engine documentation</link>
For Podman:
Refer to the docker-run(1) man page.
''; '';
}; };
@ -172,10 +176,10 @@ let
description = '' description = ''
Define which other containers this one depends on. They will be added to both After and Requires for the unit. Define which other containers this one depends on. They will be added to both After and Requires for the unit.
Use the same name as the attribute under <literal>services.docker-containers</literal>. Use the same name as the attribute under <literal>virtualisation.oci-containers</literal>.
''; '';
example = literalExample '' example = literalExample ''
services.docker-containers = { virtualisation.oci-containers = {
node1 = {}; node1 = {};
node2 = { node2 = {
dependsOn = [ "node1" ]; dependsOn = [ "node1" ];
@ -184,10 +188,10 @@ let
''; '';
}; };
extraDockerOptions = mkOption { extraOptions = mkOption {
type = with types; listOf str; type = with types; listOf str;
default = []; default = [];
description = "Extra options for <command>docker run</command>."; description = "Extra options for <command>${defaultBackend} run</command>.";
example = literalExample '' example = literalExample ''
["--network=host"] ["--network=host"]
''; '';
@ -205,24 +209,31 @@ let
}; };
mkService = name: container: let mkService = name: container: let
mkAfter = map (x: "docker-${x}.service") container.dependsOn; dependsOn = map (x: "${cfg.backend}-${x}.service") container.dependsOn;
in rec { in {
wantedBy = [] ++ optional (container.autoStart) "multi-user.target"; wantedBy = [] ++ optional (container.autoStart) "multi-user.target";
after = [ "docker.service" "docker.socket" ] ++ mkAfter; after = lib.optionals (cfg.backend == "docker") [ "docker.service" "docker.socket" ] ++ dependsOn;
requires = after; requires = dependsOn;
path = [ pkgs.docker ]; environment = proxy_env;
path =
if cfg.backend == "docker" then [ pkgs.docker ]
else if cfg.backend == "podman" then [ config.virtualisation.podman.package ]
else throw "Unhandled backend: ${cfg.backend}";
preStart = '' preStart = ''
docker rm -f ${name} || true ${cfg.backend} rm -f ${name} || true
${optionalString (container.imageFile != null) '' ${optionalString (container.imageFile != null) ''
docker load -i ${container.imageFile} ${cfg.backend} load -i ${container.imageFile}
''} ''}
''; '';
postStop = "docker rm -f ${name} || true"; postStop = "${cfg.backend} rm -f ${name} || true";
serviceConfig = { serviceConfig = {
StandardOutput = "null";
StandardError = "null";
ExecStart = concatStringsSep " \\\n " ([ ExecStart = concatStringsSep " \\\n " ([
"${pkgs.docker}/bin/docker run" "${config.system.path}/bin/${cfg.backend} run"
"--rm" "--rm"
"--name=${name}" "--name=${name}"
"--log-driver=${container.log-driver}" "--log-driver=${container.log-driver}"
@ -233,12 +244,12 @@ let
++ optional (container.user != null) "-u ${escapeShellArg container.user}" ++ optional (container.user != null) "-u ${escapeShellArg container.user}"
++ map (v: "-v ${escapeShellArg v}") container.volumes ++ map (v: "-v ${escapeShellArg v}") container.volumes
++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}" ++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}"
++ map escapeShellArg container.extraDockerOptions ++ map escapeShellArg container.extraOptions
++ [container.image] ++ [container.image]
++ map escapeShellArg container.cmd ++ map escapeShellArg container.cmd
); );
ExecStop = ''${pkgs.bash}/bin/sh -c "[ $SERVICE_RESULT = success ] || docker stop ${name}"''; ExecStop = ''${pkgs.bash}/bin/sh -c "[ $SERVICE_RESULT = success ] || ${cfg.backend} stop ${name}"'';
### There is no generalized way of supporting `reload` for docker ### There is no generalized way of supporting `reload` for docker
### containers. Some containers may respond well to SIGHUP sent to their ### containers. Some containers may respond well to SIGHUP sent to their
@ -263,19 +274,50 @@ let
}; };
in { in {
imports = [
(
lib.mkChangedOptionModule
[ "docker-containers" ]
[ "virtualisation" "oci-containers" ]
(oldcfg: {
backend = "docker";
containers = lib.mapAttrs (n: v: builtins.removeAttrs (v // {
extraOptions = v.extraDockerOptions or [];
}) [ "extraDockerOptions" ]) oldcfg.docker-containers;
})
)
];
options.docker-containers = mkOption { options.virtualisation.oci-containers = {
default = {};
type = types.attrsOf (types.submodule dockerContainer);
description = "Docker containers to run as systemd services.";
};
config = mkIf (cfg != {}) { backend = mkOption {
type = types.enum [ "podman" "docker" ];
default =
# TODO: Once https://github.com/NixOS/nixpkgs/issues/77925 is resolved default to podman
# if versionAtLeast config.system.stateVersion "20.09" then "podman"
# else "docker";
"docker";
description = "The underlying Docker implementation to use.";
};
systemd.services = mapAttrs' (n: v: nameValuePair "docker-${n}" (mkService n v)) cfg; containers = mkOption {
default = {};
virtualisation.docker.enable = true; type = types.attrsOf (types.submodule containerOptions);
description = "OCI (Docker) containers to run as systemd services.";
};
}; };
config = lib.mkIf (cfg.containers != {}) (lib.mkMerge [
{
systemd.services = mapAttrs' (n: v: nameValuePair "${cfg.backend}-${n}" (mkService n v)) cfg.containers;
}
(lib.mkIf (cfg.backend == "podman") {
virtualisation.podman.enable = true;
})
(lib.mkIf (cfg.backend == "docker") {
virtualisation.docker.enable = true;
})
]);
} }

View File

@ -88,11 +88,21 @@ in
}; };
}; };
package = lib.mkOption {
type = types.package;
default = podmanPackage;
internal = true;
description = ''
The final Podman package (including extra packages).
'';
};
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
environment.systemPackages = [ podmanPackage ] environment.systemPackages = [ cfg.package ]
++ lib.optional cfg.dockerCompat dockerCompat; ++ lib.optional cfg.dockerCompat dockerCompat;
environment.etc."containers/libpod.conf".text = '' environment.etc."containers/libpod.conf".text = ''

View File

@ -70,7 +70,7 @@ in
dhparams = handleTest ./dhparams.nix {}; dhparams = handleTest ./dhparams.nix {};
dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {}; dnscrypt-proxy2 = handleTestOn ["x86_64-linux"] ./dnscrypt-proxy2.nix {};
docker = handleTestOn ["x86_64-linux"] ./docker.nix {}; docker = handleTestOn ["x86_64-linux"] ./docker.nix {};
docker-containers = handleTestOn ["x86_64-linux"] ./docker-containers.nix {}; oci-containers = handleTestOn ["x86_64-linux"] ./oci-containers.nix {};
docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {}; docker-edge = handleTestOn ["x86_64-linux"] ./docker-edge.nix {};
docker-preloader = handleTestOn ["x86_64-linux"] ./docker-preloader.nix {}; docker-preloader = handleTestOn ["x86_64-linux"] ./docker-preloader.nix {};
docker-registry = handleTest ./docker-registry.nix {}; docker-registry = handleTest ./docker-registry.nix {};

View File

@ -1,27 +0,0 @@
# Test Docker containers as systemd units
import ./make-test-python.nix ({ pkgs, lib, ... }: {
name = "docker-containers";
meta = {
maintainers = with lib.maintainers; [ benley mkaito ];
};
nodes = {
docker = { pkgs, ... }: {
virtualisation.docker.enable = true;
docker-containers.nginx = {
image = "nginx-container";
imageFile = pkgs.dockerTools.examples.nginx;
ports = ["8181:80"];
};
};
};
testScript = ''
start_all()
docker.wait_for_unit("docker-nginx.service")
docker.wait_for_open_port(8181)
docker.wait_until_succeeds("curl http://localhost:8181 | grep Hello")
'';
})

View File

@ -0,0 +1,43 @@
{ system ? builtins.currentSystem
, config ? {}
, pkgs ? import ../.. { inherit system config; }
, lib ? pkgs.lib
}:
let
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
mkOCITest = backend: makeTest {
name = "oci-containers-${backend}";
meta = {
maintainers = with lib.maintainers; [ adisbladis benley mkaito ];
};
nodes = {
${backend} = { pkgs, ... }: {
virtualisation.oci-containers = {
inherit backend;
containers.nginx = {
image = "nginx-container";
imageFile = pkgs.dockerTools.examples.nginx;
ports = ["8181:80"];
};
};
};
};
testScript = ''
start_all()
${backend}.wait_for_unit("${backend}-nginx.service")
${backend}.wait_for_open_port(8181)
${backend}.wait_until_succeeds("curl http://localhost:8181 | grep Hello")
'';
};
in
lib.foldl' (attrs: backend: attrs // { ${backend} = mkOCITest backend; }) {} [
"docker"
"podman"
]