at 25.11-pre 5.9 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7with lib; 8let 9 cfg = config.services.nomad; 10 format = pkgs.formats.json { }; 11in 12{ 13 ##### interface 14 options = { 15 services.nomad = { 16 enable = mkEnableOption "Nomad, a distributed, highly available, datacenter-aware scheduler"; 17 18 package = mkPackageOption pkgs "nomad" { }; 19 20 extraPackages = mkOption { 21 type = types.listOf types.package; 22 default = [ ]; 23 description = '' 24 Extra packages to add to {env}`PATH` for the Nomad agent process. 25 ''; 26 example = literalExpression '' 27 with pkgs; [ cni-plugins ] 28 ''; 29 }; 30 31 dropPrivileges = mkOption { 32 type = types.bool; 33 default = true; 34 description = '' 35 Whether the nomad agent should be run as a non-root nomad user. 36 ''; 37 }; 38 39 enableDocker = mkOption { 40 type = types.bool; 41 default = true; 42 description = '' 43 Enable Docker support. Needed for Nomad's docker driver. 44 45 Note that the docker group membership is effectively equivalent 46 to being root, see https://github.com/moby/moby/issues/9976. 47 ''; 48 }; 49 50 extraSettingsPaths = mkOption { 51 type = types.listOf types.path; 52 default = [ ]; 53 description = '' 54 Additional settings paths used to configure nomad. These can be files or directories. 55 ''; 56 example = literalExpression '' 57 [ "/etc/nomad-mutable.json" "/run/keys/nomad-with-secrets.json" "/etc/nomad/config.d" ] 58 ''; 59 }; 60 61 extraSettingsPlugins = mkOption { 62 type = types.listOf (types.either types.package types.path); 63 default = [ ]; 64 description = '' 65 Additional plugins dir used to configure nomad. 66 ''; 67 example = literalExpression '' 68 [ "<pluginDir>" pkgs.nomad-driver-nix pkgs.nomad-driver-podman ] 69 ''; 70 }; 71 72 credentials = mkOption { 73 description = '' 74 Credentials envs used to configure nomad secrets. 75 ''; 76 type = types.attrsOf types.str; 77 default = { }; 78 79 example = { 80 logs_remote_write_password = "/run/keys/nomad_write_password"; 81 }; 82 }; 83 84 settings = mkOption { 85 type = format.type; 86 default = { }; 87 description = '' 88 Configuration for Nomad. See the [documentation](https://www.nomadproject.io/docs/configuration) 89 for supported values. 90 91 Notes about `data_dir`: 92 93 If `data_dir` is set to a value other than the 94 default value of `"/var/lib/nomad"` it is the Nomad 95 cluster manager's responsibility to make sure that this directory 96 exists and has the appropriate permissions. 97 98 Additionally, if `dropPrivileges` is 99 `true` then `data_dir` 100 *cannot* be customized. Setting 101 `dropPrivileges` to `true` enables 102 the `DynamicUser` feature of systemd which directly 103 manages and operates on `StateDirectory`. 104 ''; 105 example = literalExpression '' 106 { 107 # A minimal config example: 108 server = { 109 enabled = true; 110 bootstrap_expect = 1; # for demo; no fault tolerance 111 }; 112 client = { 113 enabled = true; 114 }; 115 } 116 ''; 117 }; 118 }; 119 }; 120 121 ##### implementation 122 config = mkIf cfg.enable { 123 services.nomad.settings = { 124 # Agrees with `StateDirectory = "nomad"` set below. 125 data_dir = mkDefault "/var/lib/nomad"; 126 }; 127 128 environment = { 129 etc."nomad.json".source = format.generate "nomad.json" cfg.settings; 130 systemPackages = [ cfg.package ]; 131 }; 132 133 systemd.services.nomad = { 134 description = "Nomad"; 135 wantedBy = [ "multi-user.target" ]; 136 wants = [ "network-online.target" ]; 137 after = [ "network-online.target" ]; 138 restartTriggers = [ config.environment.etc."nomad.json".source ]; 139 140 path = 141 cfg.extraPackages 142 ++ (with pkgs; [ 143 # Client mode requires at least the following: 144 coreutils 145 iproute2 146 iptables 147 ]); 148 149 serviceConfig = mkMerge [ 150 { 151 DynamicUser = cfg.dropPrivileges; 152 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 153 ExecStart = 154 let 155 pluginsDir = pkgs.symlinkJoin { 156 name = "nomad-plugins"; 157 paths = cfg.extraSettingsPlugins; 158 }; 159 in 160 "${cfg.package}/bin/nomad agent -config=/etc/nomad.json -plugin-dir=${pluginsDir}/bin" 161 + concatMapStrings (path: " -config=${path}") cfg.extraSettingsPaths 162 + concatMapStrings (key: " -config=\${CREDENTIALS_DIRECTORY}/${key}") ( 163 lib.attrNames cfg.credentials 164 ); 165 KillMode = "process"; 166 KillSignal = "SIGINT"; 167 LimitNOFILE = 65536; 168 LimitNPROC = "infinity"; 169 OOMScoreAdjust = -1000; 170 Restart = "on-failure"; 171 RestartSec = 2; 172 TasksMax = "infinity"; 173 LoadCredential = lib.mapAttrsToList (key: value: "${key}:${value}") cfg.credentials; 174 } 175 (mkIf cfg.enableDocker { 176 SupplementaryGroups = "docker"; # space-separated string 177 }) 178 (mkIf (cfg.settings.data_dir == "/var/lib/nomad") { 179 StateDirectory = "nomad"; 180 }) 181 ]; 182 183 unitConfig = { 184 StartLimitIntervalSec = 10; 185 StartLimitBurst = 3; 186 }; 187 }; 188 189 assertions = [ 190 { 191 assertion = cfg.dropPrivileges -> cfg.settings.data_dir == "/var/lib/nomad"; 192 message = "settings.data_dir must be equal to \"/var/lib/nomad\" if dropPrivileges is true"; 193 } 194 ]; 195 196 # Docker support requires the Docker daemon to be running. 197 virtualisation.docker.enable = mkIf cfg.enableDocker true; 198 }; 199}