at 25.11-pre 9.9 kB view raw
1{ 2 config, 3 lib, 4 options, 5 pkgs, 6 ... 7}: 8let 9 cfg = config.services.kubernetes; 10 opt = options.services.kubernetes; 11 12 defaultContainerdSettings = { 13 version = 2; 14 root = "/var/lib/containerd"; 15 state = "/run/containerd"; 16 oom_score = 0; 17 18 grpc = { 19 address = "/run/containerd/containerd.sock"; 20 }; 21 22 plugins."io.containerd.grpc.v1.cri" = { 23 sandbox_image = "pause:latest"; 24 25 cni = { 26 bin_dir = "/opt/cni/bin"; 27 max_conf_num = 0; 28 }; 29 30 containerd.runtimes.runc = { 31 runtime_type = "io.containerd.runc.v2"; 32 options.SystemdCgroup = true; 33 }; 34 }; 35 }; 36 37 mkKubeConfig = 38 name: conf: 39 pkgs.writeText "${name}-kubeconfig" ( 40 builtins.toJSON { 41 apiVersion = "v1"; 42 kind = "Config"; 43 clusters = [ 44 { 45 name = "local"; 46 cluster.certificate-authority = conf.caFile or cfg.caFile; 47 cluster.server = conf.server; 48 } 49 ]; 50 users = [ 51 { 52 inherit name; 53 user = { 54 client-certificate = conf.certFile; 55 client-key = conf.keyFile; 56 }; 57 } 58 ]; 59 contexts = [ 60 { 61 context = { 62 cluster = "local"; 63 user = name; 64 }; 65 name = "local"; 66 } 67 ]; 68 current-context = "local"; 69 } 70 ); 71 72 caCert = secret "ca"; 73 74 etcdEndpoints = [ "https://${cfg.masterAddress}:2379" ]; 75 76 mkCert = 77 { 78 name, 79 CN, 80 hosts ? [ ], 81 fields ? { }, 82 action ? "", 83 privateKeyOwner ? "kubernetes", 84 privateKeyGroup ? "kubernetes", 85 }: 86 rec { 87 inherit 88 name 89 caCert 90 CN 91 hosts 92 fields 93 action 94 ; 95 cert = secret name; 96 key = secret "${name}-key"; 97 privateKeyOptions = { 98 owner = privateKeyOwner; 99 group = privateKeyGroup; 100 mode = "0600"; 101 path = key; 102 }; 103 }; 104 105 secret = name: "${cfg.secretsPath}/${name}.pem"; 106 107 mkKubeConfigOptions = prefix: { 108 server = lib.mkOption { 109 description = "${prefix} kube-apiserver server address."; 110 type = lib.types.str; 111 }; 112 113 caFile = lib.mkOption { 114 description = "${prefix} certificate authority file used to connect to kube-apiserver."; 115 type = lib.types.nullOr lib.types.path; 116 default = cfg.caFile; 117 defaultText = lib.literalExpression "config.${opt.caFile}"; 118 }; 119 120 certFile = lib.mkOption { 121 description = "${prefix} client certificate file used to connect to kube-apiserver."; 122 type = lib.types.nullOr lib.types.path; 123 default = null; 124 }; 125 126 keyFile = lib.mkOption { 127 description = "${prefix} client key file used to connect to kube-apiserver."; 128 type = lib.types.nullOr lib.types.path; 129 default = null; 130 }; 131 }; 132in 133{ 134 135 imports = [ 136 (lib.mkRemovedOptionModule [ 137 "services" 138 "kubernetes" 139 "addons" 140 "dashboard" 141 ] "Removed due to it being an outdated version") 142 (lib.mkRemovedOptionModule [ "services" "kubernetes" "verbose" ] "") 143 ]; 144 145 ###### interface 146 147 options.services.kubernetes = { 148 roles = lib.mkOption { 149 description = '' 150 Kubernetes role that this machine should take. 151 152 Master role will enable etcd, apiserver, scheduler, controller manager 153 addon manager, flannel and proxy services. 154 Node role will enable flannel, docker, kubelet and proxy services. 155 ''; 156 default = [ ]; 157 type = lib.types.listOf ( 158 lib.types.enum [ 159 "master" 160 "node" 161 ] 162 ); 163 }; 164 165 package = lib.mkPackageOption pkgs "kubernetes" { }; 166 167 kubeconfig = mkKubeConfigOptions "Default kubeconfig"; 168 169 apiserverAddress = lib.mkOption { 170 description = '' 171 Clusterwide accessible address for the kubernetes apiserver, 172 including protocol and optional port. 173 ''; 174 example = "https://kubernetes-apiserver.example.com:6443"; 175 type = lib.types.str; 176 }; 177 178 caFile = lib.mkOption { 179 description = "Default kubernetes certificate authority"; 180 type = lib.types.nullOr lib.types.path; 181 default = null; 182 }; 183 184 dataDir = lib.mkOption { 185 description = "Kubernetes root directory for managing kubelet files."; 186 default = "/var/lib/kubernetes"; 187 type = lib.types.path; 188 }; 189 190 easyCerts = lib.mkOption { 191 description = "Automatically setup x509 certificates and keys for the entire cluster."; 192 default = false; 193 type = lib.types.bool; 194 }; 195 196 featureGates = lib.mkOption { 197 description = "List set of feature gates."; 198 default = { }; 199 type = lib.types.attrsOf lib.types.bool; 200 }; 201 202 masterAddress = lib.mkOption { 203 description = "Clusterwide available network address or hostname for the kubernetes master server."; 204 example = "master.example.com"; 205 type = lib.types.str; 206 }; 207 208 path = lib.mkOption { 209 description = "Packages added to the services' PATH environment variable. Both the bin and sbin subdirectories of each package are added."; 210 type = lib.types.listOf lib.types.package; 211 default = [ ]; 212 }; 213 214 clusterCidr = lib.mkOption { 215 description = "Kubernetes controller manager and proxy CIDR Range for Pods in cluster."; 216 default = "10.1.0.0/16"; 217 type = lib.types.nullOr lib.types.str; 218 }; 219 220 lib = lib.mkOption { 221 description = "Common functions for the kubernetes modules."; 222 default = { 223 inherit mkCert; 224 inherit mkKubeConfig; 225 inherit mkKubeConfigOptions; 226 }; 227 type = lib.types.attrs; 228 }; 229 230 secretsPath = lib.mkOption { 231 description = "Default location for kubernetes secrets. Not a store location."; 232 type = lib.types.path; 233 default = cfg.dataDir + "/secrets"; 234 defaultText = lib.literalExpression '' 235 config.${opt.dataDir} + "/secrets" 236 ''; 237 }; 238 }; 239 240 ###### implementation 241 242 config = lib.mkMerge [ 243 244 (lib.mkIf cfg.easyCerts { 245 services.kubernetes.pki.enable = lib.mkDefault true; 246 services.kubernetes.caFile = caCert; 247 }) 248 249 (lib.mkIf (lib.elem "master" cfg.roles) { 250 services.kubernetes.apiserver.enable = lib.mkDefault true; 251 services.kubernetes.scheduler.enable = lib.mkDefault true; 252 services.kubernetes.controllerManager.enable = lib.mkDefault true; 253 services.kubernetes.addonManager.enable = lib.mkDefault true; 254 services.kubernetes.proxy.enable = lib.mkDefault true; 255 services.etcd.enable = true; # Cannot mkDefault because of flannel default options 256 services.kubernetes.kubelet = { 257 enable = lib.mkDefault true; 258 taints = lib.mkIf (!(lib.elem "node" cfg.roles)) { 259 master = { 260 key = "node-role.kubernetes.io/master"; 261 value = "true"; 262 effect = "NoSchedule"; 263 }; 264 }; 265 }; 266 }) 267 268 (lib.mkIf (lib.all (el: el == "master") cfg.roles) { 269 # if this node is only a master make it unschedulable by default 270 services.kubernetes.kubelet.unschedulable = lib.mkDefault true; 271 }) 272 273 (lib.mkIf (lib.elem "node" cfg.roles) { 274 services.kubernetes.kubelet.enable = lib.mkDefault true; 275 services.kubernetes.proxy.enable = lib.mkDefault true; 276 }) 277 278 # Using "services.kubernetes.roles" will automatically enable easyCerts and flannel 279 (lib.mkIf (cfg.roles != [ ]) { 280 services.kubernetes.flannel.enable = lib.mkDefault true; 281 services.flannel.etcd.endpoints = lib.mkDefault etcdEndpoints; 282 services.kubernetes.easyCerts = lib.mkDefault true; 283 }) 284 285 (lib.mkIf cfg.apiserver.enable { 286 services.kubernetes.pki.etcClusterAdminKubeconfig = lib.mkDefault "kubernetes/cluster-admin.kubeconfig"; 287 services.kubernetes.apiserver.etcd.servers = lib.mkDefault etcdEndpoints; 288 }) 289 290 (lib.mkIf cfg.kubelet.enable { 291 virtualisation.containerd = { 292 enable = lib.mkDefault true; 293 settings = lib.mapAttrsRecursive (name: lib.mkDefault) defaultContainerdSettings; 294 }; 295 }) 296 297 (lib.mkIf (cfg.apiserver.enable || cfg.controllerManager.enable) { 298 services.kubernetes.pki.certs = { 299 serviceAccount = mkCert { 300 name = "service-account"; 301 CN = "system:service-account-signer"; 302 action = '' 303 systemctl restart \ 304 kube-apiserver.service \ 305 kube-controller-manager.service 306 ''; 307 }; 308 }; 309 }) 310 311 (lib.mkIf 312 ( 313 cfg.apiserver.enable 314 || cfg.scheduler.enable 315 || cfg.controllerManager.enable 316 || cfg.kubelet.enable 317 || cfg.proxy.enable 318 || cfg.addonManager.enable 319 ) 320 { 321 systemd.targets.kubernetes = { 322 description = "Kubernetes"; 323 wantedBy = [ "multi-user.target" ]; 324 }; 325 326 systemd.tmpfiles.rules = [ 327 "d /opt/cni/bin 0755 root root -" 328 "d /run/kubernetes 0755 kubernetes kubernetes -" 329 "d ${cfg.dataDir} 0755 kubernetes kubernetes -" 330 ]; 331 332 users.users.kubernetes = { 333 uid = config.ids.uids.kubernetes; 334 description = "Kubernetes user"; 335 group = "kubernetes"; 336 home = cfg.dataDir; 337 createHome = true; 338 homeMode = "755"; 339 }; 340 users.groups.kubernetes.gid = config.ids.gids.kubernetes; 341 342 # dns addon is enabled by default 343 services.kubernetes.addons.dns.enable = lib.mkDefault true; 344 345 services.kubernetes.apiserverAddress = lib.mkDefault ( 346 "https://${ 347 if cfg.apiserver.advertiseAddress != null then 348 cfg.apiserver.advertiseAddress 349 else 350 "${cfg.masterAddress}:${toString cfg.apiserver.securePort}" 351 }" 352 ); 353 } 354 ) 355 ]; 356 357 meta.buildDocsInSandbox = false; 358}