288 lines
8.2 KiB
Nix
288 lines
8.2 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.kubernetes;
|
|
|
|
mkKubeConfig = name: conf: pkgs.writeText "${name}-kubeconfig" (builtins.toJSON {
|
|
apiVersion = "v1";
|
|
kind = "Config";
|
|
clusters = [{
|
|
name = "local";
|
|
cluster.certificate-authority = conf.caFile or cfg.caFile;
|
|
cluster.server = conf.server;
|
|
}];
|
|
users = [{
|
|
inherit name;
|
|
user = {
|
|
client-certificate = conf.certFile;
|
|
client-key = conf.keyFile;
|
|
};
|
|
}];
|
|
contexts = [{
|
|
context = {
|
|
cluster = "local";
|
|
user = name;
|
|
};
|
|
current-context = "local";
|
|
}];
|
|
});
|
|
|
|
caCert = secret "ca";
|
|
|
|
etcdEndpoints = ["https://${cfg.masterAddress}:2379"];
|
|
|
|
mkCert = { name, CN, hosts ? [], fields ? {}, action ? "",
|
|
privateKeyOwner ? "kubernetes" }: rec {
|
|
inherit name caCert CN hosts fields action;
|
|
cert = secret name;
|
|
key = secret "${name}-key";
|
|
privateKeyOptions = {
|
|
owner = privateKeyOwner;
|
|
group = "nogroup";
|
|
mode = "0600";
|
|
path = key;
|
|
};
|
|
};
|
|
|
|
secret = name: "${cfg.secretsPath}/${name}.pem";
|
|
|
|
mkKubeConfigOptions = prefix: {
|
|
server = mkOption {
|
|
description = "${prefix} kube-apiserver server address.";
|
|
type = types.str;
|
|
};
|
|
|
|
caFile = mkOption {
|
|
description = "${prefix} certificate authority file used to connect to kube-apiserver.";
|
|
type = types.nullOr types.path;
|
|
default = cfg.caFile;
|
|
};
|
|
|
|
certFile = mkOption {
|
|
description = "${prefix} client certificate file used to connect to kube-apiserver.";
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
};
|
|
|
|
keyFile = mkOption {
|
|
description = "${prefix} client key file used to connect to kube-apiserver.";
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
};
|
|
};
|
|
in {
|
|
|
|
imports = [
|
|
(mkRemovedOptionModule [ "services" "kubernetes" "verbose" ] "")
|
|
];
|
|
|
|
###### interface
|
|
|
|
options.services.kubernetes = {
|
|
roles = mkOption {
|
|
description = ''
|
|
Kubernetes role that this machine should take.
|
|
|
|
Master role will enable etcd, apiserver, scheduler, controller manager
|
|
addon manager, flannel and proxy services.
|
|
Node role will enable flannel, docker, kubelet and proxy services.
|
|
'';
|
|
default = [];
|
|
type = types.listOf (types.enum ["master" "node"]);
|
|
};
|
|
|
|
package = mkOption {
|
|
description = "Kubernetes package to use.";
|
|
type = types.package;
|
|
default = pkgs.kubernetes;
|
|
defaultText = "pkgs.kubernetes";
|
|
};
|
|
|
|
kubeconfig = mkKubeConfigOptions "Default kubeconfig";
|
|
|
|
apiserverAddress = mkOption {
|
|
description = ''
|
|
Clusterwide accessible address for the kubernetes apiserver,
|
|
including protocol and optional port.
|
|
'';
|
|
example = "https://kubernetes-apiserver.example.com:6443";
|
|
type = types.str;
|
|
};
|
|
|
|
caFile = mkOption {
|
|
description = "Default kubernetes certificate authority";
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
};
|
|
|
|
dataDir = mkOption {
|
|
description = "Kubernetes root directory for managing kubelet files.";
|
|
default = "/var/lib/kubernetes";
|
|
type = types.path;
|
|
};
|
|
|
|
easyCerts = mkOption {
|
|
description = "Automatically setup x509 certificates and keys for the entire cluster.";
|
|
default = false;
|
|
type = types.bool;
|
|
};
|
|
|
|
featureGates = mkOption {
|
|
description = "List set of feature gates.";
|
|
default = [];
|
|
type = types.listOf types.str;
|
|
};
|
|
|
|
masterAddress = mkOption {
|
|
description = "Clusterwide available network address or hostname for the kubernetes master server.";
|
|
example = "master.example.com";
|
|
type = types.str;
|
|
};
|
|
|
|
path = mkOption {
|
|
description = "Packages added to the services' PATH environment variable. Both the bin and sbin subdirectories of each package are added.";
|
|
type = types.listOf types.package;
|
|
default = [];
|
|
};
|
|
|
|
clusterCidr = mkOption {
|
|
description = "Kubernetes controller manager and proxy CIDR Range for Pods in cluster.";
|
|
default = "10.1.0.0/16";
|
|
type = types.nullOr types.str;
|
|
};
|
|
|
|
lib = mkOption {
|
|
description = "Common functions for the kubernetes modules.";
|
|
default = {
|
|
inherit mkCert;
|
|
inherit mkKubeConfig;
|
|
inherit mkKubeConfigOptions;
|
|
};
|
|
type = types.attrs;
|
|
};
|
|
|
|
secretsPath = mkOption {
|
|
description = "Default location for kubernetes secrets. Not a store location.";
|
|
type = types.path;
|
|
default = cfg.dataDir + "/secrets";
|
|
};
|
|
};
|
|
|
|
###### implementation
|
|
|
|
config = mkMerge [
|
|
|
|
(mkIf cfg.easyCerts {
|
|
services.kubernetes.pki.enable = mkDefault true;
|
|
services.kubernetes.caFile = caCert;
|
|
})
|
|
|
|
(mkIf (elem "master" cfg.roles) {
|
|
services.kubernetes.apiserver.enable = mkDefault true;
|
|
services.kubernetes.scheduler.enable = mkDefault true;
|
|
services.kubernetes.controllerManager.enable = mkDefault true;
|
|
services.kubernetes.addonManager.enable = mkDefault true;
|
|
services.kubernetes.proxy.enable = mkDefault true;
|
|
services.etcd.enable = true; # Cannot mkDefault because of flannel default options
|
|
services.kubernetes.kubelet = {
|
|
enable = mkDefault true;
|
|
taints = mkIf (!(elem "node" cfg.roles)) {
|
|
master = {
|
|
key = "node-role.kubernetes.io/master";
|
|
value = "true";
|
|
effect = "NoSchedule";
|
|
};
|
|
};
|
|
};
|
|
})
|
|
|
|
|
|
(mkIf (all (el: el == "master") cfg.roles) {
|
|
# if this node is only a master make it unschedulable by default
|
|
services.kubernetes.kubelet.unschedulable = mkDefault true;
|
|
})
|
|
|
|
(mkIf (elem "node" cfg.roles) {
|
|
services.kubernetes.kubelet.enable = mkDefault true;
|
|
services.kubernetes.proxy.enable = mkDefault true;
|
|
})
|
|
|
|
# Using "services.kubernetes.roles" will automatically enable easyCerts and flannel
|
|
(mkIf (cfg.roles != []) {
|
|
services.kubernetes.flannel.enable = mkDefault true;
|
|
services.flannel.etcd.endpoints = mkDefault etcdEndpoints;
|
|
services.kubernetes.easyCerts = mkDefault true;
|
|
})
|
|
|
|
(mkIf cfg.apiserver.enable {
|
|
services.kubernetes.pki.etcClusterAdminKubeconfig = mkDefault "kubernetes/cluster-admin.kubeconfig";
|
|
services.kubernetes.apiserver.etcd.servers = mkDefault etcdEndpoints;
|
|
})
|
|
|
|
(mkIf cfg.kubelet.enable {
|
|
virtualisation.docker = {
|
|
enable = mkDefault true;
|
|
|
|
# kubernetes needs access to logs
|
|
logDriver = mkDefault "json-file";
|
|
|
|
# iptables must be disabled for kubernetes
|
|
extraOptions = "--iptables=false --ip-masq=false";
|
|
};
|
|
})
|
|
|
|
(mkIf (cfg.apiserver.enable || cfg.controllerManager.enable) {
|
|
services.kubernetes.pki.certs = {
|
|
serviceAccount = mkCert {
|
|
name = "service-account";
|
|
CN = "system:service-account-signer";
|
|
action = ''
|
|
systemctl reload \
|
|
kube-apiserver.service \
|
|
kube-controller-manager.service
|
|
'';
|
|
};
|
|
};
|
|
})
|
|
|
|
(mkIf (
|
|
cfg.apiserver.enable ||
|
|
cfg.scheduler.enable ||
|
|
cfg.controllerManager.enable ||
|
|
cfg.kubelet.enable ||
|
|
cfg.proxy.enable ||
|
|
cfg.addonManager.enable
|
|
) {
|
|
systemd.targets.kubernetes = {
|
|
description = "Kubernetes";
|
|
wantedBy = [ "multi-user.target" ];
|
|
};
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d /opt/cni/bin 0755 root root -"
|
|
"d /run/kubernetes 0755 kubernetes kubernetes -"
|
|
"d /var/lib/kubernetes 0755 kubernetes kubernetes -"
|
|
];
|
|
|
|
users.users.kubernetes = {
|
|
uid = config.ids.uids.kubernetes;
|
|
description = "Kubernetes user";
|
|
extraGroups = [ "docker" ];
|
|
group = "kubernetes";
|
|
home = cfg.dataDir;
|
|
createHome = true;
|
|
};
|
|
users.groups.kubernetes.gid = config.ids.gids.kubernetes;
|
|
|
|
# dns addon is enabled by default
|
|
services.kubernetes.addons.dns.enable = mkDefault true;
|
|
|
|
services.kubernetes.apiserverAddress = mkDefault ("https://${if cfg.apiserver.advertiseAddress != null
|
|
then cfg.apiserver.advertiseAddress
|
|
else "${cfg.masterAddress}:${toString cfg.apiserver.securePort}"}");
|
|
})
|
|
];
|
|
}
|