at 23.11-pre 6.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4let 5 cfg = config.services.k3s; 6 removeOption = config: instruction: 7 lib.mkRemovedOptionModule ([ "services" "k3s" ] ++ config) instruction; 8in 9{ 10 imports = [ 11 (removeOption [ "docker" ] "k3s docker option is no longer supported.") 12 ]; 13 14 # interface 15 options.services.k3s = { 16 enable = mkEnableOption (lib.mdDoc "k3s"); 17 18 package = mkOption { 19 type = types.package; 20 default = pkgs.k3s; 21 defaultText = literalExpression "pkgs.k3s"; 22 description = lib.mdDoc "Package that should be used for k3s"; 23 }; 24 25 role = mkOption { 26 description = lib.mdDoc '' 27 Whether k3s should run as a server or agent. 28 29 If it's a server: 30 31 - By default it also runs workloads as an agent. 32 - Starts by default as a standalone server using an embedded sqlite datastore. 33 - Configure `clusterInit = true` to switch over to embedded etcd datastore and enable HA mode. 34 - Configure `serverAddr` to join an already-initialized HA cluster. 35 36 If it's an agent: 37 38 - `serverAddr` is required. 39 ''; 40 default = "server"; 41 type = types.enum [ "server" "agent" ]; 42 }; 43 44 serverAddr = mkOption { 45 type = types.str; 46 description = lib.mdDoc '' 47 The k3s server to connect to. 48 49 Servers and agents need to communicate each other. Read 50 [the networking docs](https://rancher.com/docs/k3s/latest/en/installation/installation-requirements/#networking) 51 to know how to configure the firewall. 52 ''; 53 example = "https://10.0.0.10:6443"; 54 default = ""; 55 }; 56 57 clusterInit = mkOption { 58 type = types.bool; 59 default = false; 60 description = lib.mdDoc '' 61 Initialize HA cluster using an embedded etcd datastore. 62 63 If this option is `false` and `role` is `server` 64 65 On a server that was using the default embedded sqlite backend, 66 enabling this option will migrate to an embedded etcd DB. 67 68 If an HA cluster using the embedded etcd datastore was already initialized, 69 this option has no effect. 70 71 This option only makes sense in a server that is not connecting to another server. 72 73 If you are configuring an HA cluster with an embedded etcd, 74 the 1st server must have `clusterInit = true` 75 and other servers must connect to it using `serverAddr`. 76 ''; 77 }; 78 79 token = mkOption { 80 type = types.str; 81 description = lib.mdDoc '' 82 The k3s token to use when connecting to a server. 83 84 WARNING: This option will expose store your token unencrypted world-readable in the nix store. 85 If this is undesired use the tokenFile option instead. 86 ''; 87 default = ""; 88 }; 89 90 tokenFile = mkOption { 91 type = types.nullOr types.path; 92 description = lib.mdDoc "File path containing k3s token to use when connecting to the server."; 93 default = null; 94 }; 95 96 extraFlags = mkOption { 97 description = lib.mdDoc "Extra flags to pass to the k3s command."; 98 type = types.str; 99 default = ""; 100 example = "--no-deploy traefik --cluster-cidr 10.24.0.0/16"; 101 }; 102 103 disableAgent = mkOption { 104 type = types.bool; 105 default = false; 106 description = lib.mdDoc "Only run the server. This option only makes sense for a server."; 107 }; 108 109 environmentFile = mkOption { 110 type = types.nullOr types.path; 111 description = lib.mdDoc '' 112 File path containing environment variables for configuring the k3s service in the format of an EnvironmentFile. See systemd.exec(5). 113 ''; 114 default = null; 115 }; 116 117 configPath = mkOption { 118 type = types.nullOr types.path; 119 default = null; 120 description = lib.mdDoc "File path containing the k3s YAML config. This is useful when the config is generated (for example on boot)."; 121 }; 122 }; 123 124 # implementation 125 126 config = mkIf cfg.enable { 127 assertions = [ 128 { 129 assertion = cfg.role == "agent" -> (cfg.configPath != null || cfg.serverAddr != ""); 130 message = "serverAddr or configPath (with 'server' key) should be set if role is 'agent'"; 131 } 132 { 133 assertion = cfg.role == "agent" -> cfg.configPath != null || cfg.tokenFile != null || cfg.token != ""; 134 message = "token or tokenFile or configPath (with 'token' or 'token-file' keys) should be set if role is 'agent'"; 135 } 136 { 137 assertion = cfg.role == "agent" -> !cfg.disableAgent; 138 message = "disableAgent must be false if role is 'agent'"; 139 } 140 { 141 assertion = cfg.role == "agent" -> !cfg.clusterInit; 142 message = "clusterInit must be false if role is 'agent'"; 143 } 144 ]; 145 146 environment.systemPackages = [ config.services.k3s.package ]; 147 148 systemd.services.k3s = { 149 description = "k3s service"; 150 after = [ "firewall.service" "network-online.target" ]; 151 wants = [ "firewall.service" "network-online.target" ]; 152 wantedBy = [ "multi-user.target" ]; 153 path = optional config.boot.zfs.enabled config.boot.zfs.package; 154 serviceConfig = { 155 # See: https://github.com/rancher/k3s/blob/dddbd16305284ae4bd14c0aade892412310d7edc/install.sh#L197 156 Type = if cfg.role == "agent" then "exec" else "notify"; 157 KillMode = "process"; 158 Delegate = "yes"; 159 Restart = "always"; 160 RestartSec = "5s"; 161 LimitNOFILE = 1048576; 162 LimitNPROC = "infinity"; 163 LimitCORE = "infinity"; 164 TasksMax = "infinity"; 165 EnvironmentFile = cfg.environmentFile; 166 ExecStart = concatStringsSep " \\\n " ( 167 [ 168 "${cfg.package}/bin/k3s ${cfg.role}" 169 ] 170 ++ (optional cfg.clusterInit "--cluster-init") 171 ++ (optional cfg.disableAgent "--disable-agent") 172 ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}") 173 ++ (optional (cfg.token != "") "--token ${cfg.token}") 174 ++ (optional (cfg.tokenFile != null) "--token-file ${cfg.tokenFile}") 175 ++ (optional (cfg.configPath != null) "--config ${cfg.configPath}") 176 ++ [ cfg.extraFlags ] 177 ); 178 }; 179 }; 180 }; 181}