1 { config, lib, options, pkgs, ... }:
6 top = config.services.kubernetes;
7 otop = options.services.kubernetes;
11 if cfg.cni.config != [] && cfg.cni.configDir != null then
12 throw "Verbatim CNI-config and CNI configDir cannot both be set."
13 else if cfg.cni.configDir != null then
17 name = "kubernetes-cni-config";
18 paths = imap (i: entry:
19 pkgs.writeTextDir "${toString (10+i)}-${entry.type}.conf" (builtins.toJSON entry)
23 infraContainer = pkgs.dockerTools.buildImage {
26 copyToRoot = pkgs.buildEnv {
28 pathsToLink = [ "/bin" ];
29 paths = [ top.package.pause ];
31 config.Cmd = ["/bin/pause"];
34 kubeconfig = top.lib.mkKubeConfig "kubelet" cfg.kubeconfig;
36 # Flag based settings are deprecated, use the `--config` flag with a
37 # `KubeletConfiguration` struct.
38 # https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/
40 # NOTE: registerWithTaints requires a []core/v1.Taint, therefore requires
41 # additional work to be put in config format.
43 kubeletConfig = pkgs.writeText "kubelet-config" (builtins.toJSON ({
44 apiVersion = "kubelet.config.k8s.io/v1beta1";
45 kind = "KubeletConfiguration";
46 address = cfg.address;
49 x509 = lib.optionalAttrs (cfg.clientCaFile != null) { clientCAFile = cfg.clientCaFile; };
58 cgroupDriver = "systemd";
59 hairpinMode = "hairpin-veth";
60 registerNode = cfg.registerNode;
61 containerRuntimeEndpoint = cfg.containerRuntimeEndpoint;
62 healthzPort = cfg.healthz.port;
63 healthzBindAddress = cfg.healthz.bind;
64 } // lib.optionalAttrs (cfg.tlsCertFile != null) { tlsCertFile = cfg.tlsCertFile; }
65 // lib.optionalAttrs (cfg.tlsKeyFile != null) { tlsPrivateKeyFile = cfg.tlsKeyFile; }
66 // lib.optionalAttrs (cfg.clusterDomain != "") { clusterDomain = cfg.clusterDomain; }
67 // lib.optionalAttrs (cfg.clusterDns != []) { clusterDNS = cfg.clusterDns; }
68 // lib.optionalAttrs (cfg.featureGates != {}) { featureGates = cfg.featureGates; }
69 // lib.optionalAttrs (cfg.extraConfig != {}) cfg.extraConfig
72 manifestPath = "kubernetes/manifests";
74 taintOptions = with lib.types; { name, ... }: {
77 description = "Key of taint.";
79 defaultText = literalMD "Name of this submodule.";
83 description = "Value of taint.";
87 description = "Effect of taint.";
88 example = "NoSchedule";
89 type = enum ["NoSchedule" "PreferNoSchedule" "NoExecute"];
94 taints = concatMapStringsSep "," (v: "${v.key}=${v.value}:${v.effect}") (mapAttrsToList (n: v: v) cfg.taints);
98 (mkRemovedOptionModule [ "services" "kubernetes" "kubelet" "applyManifests" ] "")
99 (mkRemovedOptionModule [ "services" "kubernetes" "kubelet" "cadvisorPort" ] "")
100 (mkRemovedOptionModule [ "services" "kubernetes" "kubelet" "allowPrivileged" ] "")
101 (mkRemovedOptionModule [ "services" "kubernetes" "kubelet" "networkPlugin" ] "")
102 (mkRemovedOptionModule [ "services" "kubernetes" "kubelet" "containerRuntime" ] "")
106 options.services.kubernetes.kubelet = with lib.types; {
109 description = "Kubernetes kubelet info server listening address.";
114 clusterDns = mkOption {
115 description = "Use alternative DNS.";
116 default = [ "10.1.0.1" ];
120 clusterDomain = mkOption {
121 description = "Use alternative domain.";
122 default = config.services.kubernetes.addons.dns.clusterDomain;
123 defaultText = literalExpression "config.${options.services.kubernetes.addons.dns.clusterDomain}";
127 clientCaFile = mkOption {
128 description = "Kubernetes apiserver CA file for client authentication.";
129 default = top.caFile;
130 defaultText = literalExpression "config.${otop.caFile}";
135 packages = mkOption {
136 description = "List of network plugin packages to install.";
137 type = listOf package;
142 description = "Kubernetes CNI configuration.";
145 example = literalExpression ''
147 "cniVersion": "0.3.1",
154 "type": "host-local",
155 "subnet": "10.22.0.0/16",
157 { "dst": "0.0.0.0/0" }
161 "cniVersion": "0.3.1",
167 configDir = mkOption {
168 description = "Path to Kubernetes CNI configuration directory.";
174 containerRuntimeEndpoint = mkOption {
175 description = "Endpoint at which to find the container runtime api interface/socket";
177 default = "unix:///run/containerd/containerd.sock";
180 enable = mkEnableOption "Kubernetes kubelet";
182 extraOpts = mkOption {
183 description = "Kubernetes kubelet extra command line options.";
185 type = separatedString " ";
188 extraConfig = mkOption {
189 description = "Kubernetes kubelet extra configuration file entries.";
191 type = attrsOf attrs;
194 featureGates = mkOption {
195 description = "Attribute set of feature gate";
196 default = top.featureGates;
197 defaultText = literalExpression "config.${otop.featureGates}";
203 description = "Kubernetes kubelet healthz listening address.";
204 default = "127.0.0.1";
209 description = "Kubernetes kubelet healthz port.";
215 hostname = mkOption {
216 description = "Kubernetes kubelet hostname override.";
217 defaultText = literalExpression "config.networking.fqdnOrHostName";
221 kubeconfig = top.lib.mkKubeConfigOptions "Kubelet";
223 manifests = mkOption {
224 description = "List of manifests to bootstrap with kubelet (only pods can be created as manifest entry)";
225 type = attrsOf attrs;
230 description = "IP address of the node. If set, kubelet will use this IP address for the node.";
235 registerNode = mkOption {
236 description = "Whether to auto register kubelet with API server.";
242 description = "Kubernetes kubelet info server listening port.";
247 seedDockerImages = mkOption {
248 description = "List of docker images to preload on system";
250 type = listOf package;
254 description = "Node taints (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/).";
256 type = attrsOf (submodule [ taintOptions ]);
259 tlsCertFile = mkOption {
260 description = "File containing x509 Certificate for HTTPS.";
265 tlsKeyFile = mkOption {
266 description = "File containing x509 private key matching tlsCertFile.";
271 unschedulable = mkOption {
272 description = "Whether to set node taint to unschedulable=true as it is the case of node that has only master role.";
277 verbosity = mkOption {
279 Optional glog verbosity level for logging statements. See
280 <https://github.com/kubernetes/community/blob/master/contributors/devel/logging.md>
288 ###### implementation
292 environment.etc."cni/net.d".source = cniConfig;
294 services.kubernetes.kubelet.seedDockerImages = [infraContainer];
296 boot.kernel.sysctl = {
297 "net.bridge.bridge-nf-call-iptables" = 1;
298 "net.ipv4.ip_forward" = 1;
299 "net.bridge.bridge-nf-call-ip6tables" = 1;
302 systemd.services.kubelet = {
303 description = "Kubernetes Kubelet Service";
304 wantedBy = [ "kubernetes.target" ];
305 after = [ "containerd.service" "network.target" "kube-apiserver.service" ];
312 thin-provisioning-tools
315 ] ++ lib.optional config.boot.zfs.enabled config.boot.zfs.package ++ top.path;
317 ${concatMapStrings (img: ''
318 echo "Seeding container image: ${img}"
319 ${if (lib.hasSuffix "gz" img) then
320 ''${pkgs.gzip}/bin/zcat "${img}" | ${pkgs.containerd}/bin/ctr -n k8s.io image import --all-platforms -''
322 ''${pkgs.coreutils}/bin/cat "${img}" | ${pkgs.containerd}/bin/ctr -n k8s.io image import --all-platforms -''
324 '') cfg.seedDockerImages}
326 rm /opt/cni/bin/* || true
327 ${concatMapStrings (package: ''
328 echo "Linking cni package: ${package}"
329 ln -fs ${package}/bin/* /opt/cni/bin
330 '') cfg.cni.packages}
333 Slice = "kubernetes.slice";
334 CPUAccounting = true;
335 MemoryAccounting = true;
336 Restart = "on-failure";
337 RestartSec = "1000ms";
338 ExecStart = ''${top.package}/bin/kubelet \
339 --config=${kubeletConfig} \
340 --hostname-override=${cfg.hostname} \
341 --kubeconfig=${kubeconfig} \
342 ${optionalString (cfg.nodeIp != null)
343 "--node-ip=${cfg.nodeIp}"} \
344 --pod-infra-container-image=pause \
345 ${optionalString (cfg.manifests != {})
346 "--pod-manifest-path=/etc/${manifestPath}"} \
347 ${optionalString (taints != "")
348 "--register-with-taints=${taints}"} \
349 --root-dir=${top.dataDir} \
350 ${optionalString (cfg.verbosity != null) "--v=${toString cfg.verbosity}"} \
353 WorkingDirectory = top.dataDir;
356 StartLimitIntervalSec = 0;
360 # Always include cni plugins
361 services.kubernetes.kubelet.cni.packages = [pkgs.cni-plugins pkgs.cni-plugin-flannel];
363 boot.kernelModules = ["br_netfilter" "overlay"];
365 services.kubernetes.kubelet.hostname =
366 mkDefault (lib.toLower config.networking.fqdnOrHostName);
368 services.kubernetes.pki.certs = with top.lib; {
371 CN = top.kubelet.hostname;
372 action = "systemctl restart kubelet.service";
375 kubeletClient = mkCert {
376 name = "kubelet-client";
377 CN = "system:node:${top.kubelet.hostname}";
381 action = "systemctl restart kubelet.service";
385 services.kubernetes.kubelet.kubeconfig.server = mkDefault top.apiserverAddress;
388 (mkIf (cfg.enable && cfg.manifests != {}) {
389 environment.etc = mapAttrs' (name: manifest:
390 nameValuePair "${manifestPath}/${name}.json" {
391 text = builtins.toJSON manifest;
397 (mkIf (cfg.unschedulable && cfg.enable) {
398 services.kubernetes.kubelet.taints.unschedulable = {
400 effect = "NoSchedule";
406 meta.buildDocsInSandbox = false;