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