add docker-stack nixos module and deploy traefik
This commit is contained in:
parent
d7dfeb35fd
commit
c5bba581f5
4 changed files with 197 additions and 14 deletions
|
@ -41,6 +41,7 @@ in
|
|||
|
||||
extraModules = [
|
||||
inputs.mailserver.nixosModule
|
||||
./modules/docker-stack.nix
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ let
|
|||
|
||||
staticConfigFile = pkgs.runCommand "config.toml"
|
||||
{
|
||||
buildInputs = [ pkgs.yj ];
|
||||
buildInputs = [ pkgs.remarshal ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
''
|
||||
yj -jt -i \
|
||||
remarshal -if json -of toml \
|
||||
< ${
|
||||
pkgs.writeText "static_config.json" (builtins.toJSON
|
||||
(lib.recursiveUpdate traefikCfg.staticConfigOptions {
|
||||
|
@ -59,25 +59,20 @@ in
|
|||
inherit group;
|
||||
};
|
||||
|
||||
virtualisation.oci-containers = {
|
||||
backend = "docker";
|
||||
containers.traefik = {
|
||||
virtualisation.docker.stacks.traefik = {
|
||||
networks.traefik_public.external = true;
|
||||
services.traefik = {
|
||||
image = "traefik:v2.9";
|
||||
cmd = [
|
||||
command = [
|
||||
"--configFile=${staticConfigFile}"
|
||||
];
|
||||
extraOptions = [
|
||||
# enable host.docker.internal
|
||||
"--add-host=host.docker.internal:host-gateway"
|
||||
# attach to overlay network. To create, run the following command:
|
||||
# docker network create --driver=overlay --attachable traefik_public
|
||||
"--network=traefik_public"
|
||||
];
|
||||
ports = [
|
||||
"80:80"
|
||||
"443:443"
|
||||
"8080:8080"
|
||||
];
|
||||
extra_hosts = [ "host.docker.internal:host-gateway" ];
|
||||
networks = [ "traefik_public" ];
|
||||
volumes = [
|
||||
"${mirrorVolume "/var/run/docker.sock"}:ro"
|
||||
"${mirrorVolume dataDir}"
|
||||
|
|
187
machines/modules/docker-stack.nix
Normal file
187
machines/modules/docker-stack.nix
Normal file
|
@ -0,0 +1,187 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.virtualisation.docker;
|
||||
|
||||
proxyEnv = config.networking.proxy.envVars;
|
||||
|
||||
serviceOptions = { ... }: {
|
||||
options = with lib; {
|
||||
image = mkOption {
|
||||
type = types.str;
|
||||
description = lib.mdDoc "OCI image to run.";
|
||||
example = "library/hello-world";
|
||||
};
|
||||
|
||||
command = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = lib.mdDoc "Overrides the default command declared by the image (i.e. by Dockerfile's CMD).";
|
||||
example = literalExpression ''
|
||||
[
|
||||
"--port=9000"
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
Network ports to publish from the container to the outer host.
|
||||
Valid formats:
|
||||
- `<ip>:<hostPort>:<containerPort>`
|
||||
- `<ip>::<containerPort>`
|
||||
- `<hostPort>:<containerPort>`
|
||||
- `<containerPort>`
|
||||
Both `hostPort` and `containerPort` can be specified as a range of
|
||||
ports. When specifying ranges for both, the number of container
|
||||
ports in the range must match the number of host ports in the
|
||||
range. Example: `1234-1236:1234-1236/tcp`
|
||||
When specifying a range for `hostPort` only, the `containerPort`
|
||||
must *not* be a range. In this case, the container port is published
|
||||
somewhere within the specified `hostPort` range.
|
||||
Example: `1234-1236:1234/tcp`
|
||||
Refer to the
|
||||
[Docker engine documentation](https://docs.docker.com/engine/reference/run/#expose-incoming-ports) for full details.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[
|
||||
"8080:9000"
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
volumes = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
List of volumes to attach to this container.
|
||||
Note that this is a list of `"src:dst"` strings to
|
||||
allow for `src` to refer to `/nix/store` paths, which
|
||||
would be difficult with an attribute set. There are
|
||||
also a variety of mount options available as a third
|
||||
field; please refer to the
|
||||
[docker engine documentation](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems) for details.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[
|
||||
"volume_name:/path/inside/container"
|
||||
"/path/on/host:/path/inside/container"
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
networks = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[
|
||||
"backend_internal"
|
||||
"traefik_public"
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
extra_hosts = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = lib.mdDoc ''
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[
|
||||
"host.docker.internal:host-gateway"
|
||||
]
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networkOptions = { ... }: {
|
||||
options = with lib; {
|
||||
external = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.bool;
|
||||
description = lib.mdDoc "";
|
||||
example = "true";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
stackOptions = { ... }: {
|
||||
options = with lib; {
|
||||
version = mkOption {
|
||||
type = types.str;
|
||||
default = "3.8";
|
||||
};
|
||||
|
||||
services = mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule serviceOptions);
|
||||
description = lib.mdDoc "";
|
||||
};
|
||||
|
||||
networks = mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule networkOptions);
|
||||
description = lib.mdDoc "";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mkComposeFile = stack:
|
||||
pkgs.runCommand "compose.yml"
|
||||
{
|
||||
buildInputs = [ pkgs.remarshal ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
''
|
||||
remarshal -if json -of yaml \
|
||||
< ${ pkgs.writeText "compose.json" (builtins.toJSON stack)} \
|
||||
> $out
|
||||
'';
|
||||
|
||||
mkStackService = stackName: stack:
|
||||
let
|
||||
escapedStackName = lib.escapeShellArg stackName;
|
||||
composeFile = mkComposeFile stack;
|
||||
in
|
||||
{
|
||||
description = "Deploy ${escapedStackName} stack";
|
||||
|
||||
after = [ "docker.service" "docker.socket" ];
|
||||
environment = proxyEnv;
|
||||
|
||||
path = [ config.virtualisation.docker.package ];
|
||||
|
||||
script = lib.concatStringsSep " \\\n " ([
|
||||
"exec docker stack deploy"
|
||||
"--compose-file=${composeFile}"
|
||||
escapedStackName
|
||||
]);
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
};
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
options.virtualisation.docker.stacks = with lib; mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (types.submodule stackOptions);
|
||||
description = lib.mdDoc "Docker stacks to deploy using systemd services.";
|
||||
};
|
||||
|
||||
config = lib.mkIf (cfg.stacks != { }) {
|
||||
systemd.services = lib.mapAttrs' (n: v: lib.nameValuePair "docker-stack-${n}" (mkStackService n v)) cfg.stacks;
|
||||
|
||||
virtualisation.docker = {
|
||||
enable = true;
|
||||
liveRestore = false;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
nix.gc = {
|
||||
automatic = true;
|
||||
dates = "weekly";
|
||||
options = "--delete-older-than 7d";
|
||||
options = "--delete-older-than 14d";
|
||||
};
|
||||
|
||||
virtualisation.docker.autoPrune = {
|
||||
|
|
Loading…
Reference in a new issue