1 { config, lib, options, pkgs, ... }:
6 cfg = config.services.kubernetes;
7 opt = options.services.kubernetes;
9 defaultContainerdSettings = {
11 root = "/var/lib/containerd";
12 state = "/run/containerd";
16 address = "/run/containerd/containerd.sock";
19 plugins."io.containerd.grpc.v1.cri" = {
20 sandbox_image = "pause:latest";
23 bin_dir = "/opt/cni/bin";
27 containerd.runtimes.runc = {
28 runtime_type = "io.containerd.runc.v2";
29 options.SystemdCgroup = true;
34 mkKubeConfig = name: conf: pkgs.writeText "${name}-kubeconfig" (builtins.toJSON {
39 cluster.certificate-authority = conf.caFile or cfg.caFile;
40 cluster.server = conf.server;
45 client-certificate = conf.certFile;
46 client-key = conf.keyFile;
56 current-context = "local";
61 etcdEndpoints = ["https://${cfg.masterAddress}:2379"];
63 mkCert = { name, CN, hosts ? [], fields ? {}, action ? "",
64 privateKeyOwner ? "kubernetes", privateKeyGroup ? "kubernetes" }: rec {
65 inherit name caCert CN hosts fields action;
67 key = secret "${name}-key";
69 owner = privateKeyOwner;
70 group = privateKeyGroup;
76 secret = name: "${cfg.secretsPath}/${name}.pem";
78 mkKubeConfigOptions = prefix: {
80 description = "${prefix} kube-apiserver server address.";
85 description = "${prefix} certificate authority file used to connect to kube-apiserver.";
86 type = types.nullOr types.path;
88 defaultText = literalExpression "config.${opt.caFile}";
92 description = "${prefix} client certificate file used to connect to kube-apiserver.";
93 type = types.nullOr types.path;
98 description = "${prefix} client key file used to connect to kube-apiserver.";
99 type = types.nullOr types.path;
106 (mkRemovedOptionModule [ "services" "kubernetes" "addons" "dashboard" ] "Removed due to it being an outdated version")
107 (mkRemovedOptionModule [ "services" "kubernetes" "verbose" ] "")
112 options.services.kubernetes = {
115 Kubernetes role that this machine should take.
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.
122 type = types.listOf (types.enum ["master" "node"]);
125 package = mkPackageOption pkgs "kubernetes" { };
127 kubeconfig = mkKubeConfigOptions "Default kubeconfig";
129 apiserverAddress = mkOption {
131 Clusterwide accessible address for the kubernetes apiserver,
132 including protocol and optional port.
134 example = "https://kubernetes-apiserver.example.com:6443";
139 description = "Default kubernetes certificate authority";
140 type = types.nullOr types.path;
145 description = "Kubernetes root directory for managing kubelet files.";
146 default = "/var/lib/kubernetes";
150 easyCerts = mkOption {
151 description = "Automatically setup x509 certificates and keys for the entire cluster.";
156 featureGates = mkOption {
157 description = "List set of feature gates.";
159 type = types.attrsOf types.bool;
162 masterAddress = mkOption {
163 description = "Clusterwide available network address or hostname for the kubernetes master server.";
164 example = "master.example.com";
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;
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;
181 description = "Common functions for the kubernetes modules.";
184 inherit mkKubeConfig;
185 inherit mkKubeConfigOptions;
190 secretsPath = mkOption {
191 description = "Default location for kubernetes secrets. Not a store location.";
193 default = cfg.dataDir + "/secrets";
194 defaultText = literalExpression ''
195 config.${opt.dataDir} + "/secrets"
200 ###### implementation
204 (mkIf cfg.easyCerts {
205 services.kubernetes.pki.enable = mkDefault true;
206 services.kubernetes.caFile = caCert;
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)) {
220 key = "node-role.kubernetes.io/master";
222 effect = "NoSchedule";
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;
234 (mkIf (elem "node" cfg.roles) {
235 services.kubernetes.kubelet.enable = mkDefault true;
236 services.kubernetes.proxy.enable = mkDefault true;
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;
246 (mkIf cfg.apiserver.enable {
247 services.kubernetes.pki.etcClusterAdminKubeconfig = mkDefault "kubernetes/cluster-admin.kubeconfig";
248 services.kubernetes.apiserver.etcd.servers = mkDefault etcdEndpoints;
251 (mkIf cfg.kubelet.enable {
252 virtualisation.containerd = {
253 enable = mkDefault true;
254 settings = mapAttrsRecursive (name: mkDefault) defaultContainerdSettings;
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";
265 kube-apiserver.service \
266 kube-controller-manager.service
273 cfg.apiserver.enable ||
274 cfg.scheduler.enable ||
275 cfg.controllerManager.enable ||
276 cfg.kubelet.enable ||
278 cfg.addonManager.enable
280 systemd.targets.kubernetes = {
281 description = "Kubernetes";
282 wantedBy = [ "multi-user.target" ];
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 -"
291 users.users.kubernetes = {
292 uid = config.ids.uids.kubernetes;
293 description = "Kubernetes user";
294 group = "kubernetes";
299 users.groups.kubernetes.gid = config.ids.gids.kubernetes;
301 # dns addon is enabled by default
302 services.kubernetes.addons.dns.enable = mkDefault true;
304 services.kubernetes.apiserverAddress = mkDefault ("https://${if cfg.apiserver.advertiseAddress != null
305 then cfg.apiserver.advertiseAddress
306 else "${cfg.masterAddress}:${toString cfg.apiserver.securePort}"}");
310 meta.buildDocsInSandbox = false;