463 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			463 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, lib, pkgs, ... }:
 | |
| 
 | |
| with lib;
 | |
| 
 | |
| let
 | |
|   cfg = config.services.kubernetes;
 | |
| 
 | |
| in {
 | |
| 
 | |
|   ###### interface
 | |
| 
 | |
|   options.services.kubernetes = {
 | |
|     package = mkOption {
 | |
|       description = "Kubernetes package to use.";
 | |
|       type = types.package;
 | |
|     };
 | |
| 
 | |
|     verbose = mkOption {
 | |
|       description = "Kubernetes enable verbose mode for debugging";
 | |
|       default = false;
 | |
|       type = types.bool;
 | |
|     };
 | |
| 
 | |
|     etcdServers = mkOption {
 | |
|       description = "Kubernetes list of etcd servers to watch.";
 | |
|       default = [ "127.0.0.1:4001" ];
 | |
|       type = types.listOf types.str;
 | |
|     };
 | |
| 
 | |
|     roles = mkOption {
 | |
|       description = ''
 | |
|         Kubernetes role that this machine should take.
 | |
| 
 | |
|         Master role will enable etcd, apiserver, scheduler and controller manager
 | |
|         services. Node role will enable etcd, docker, kubelet and proxy services.
 | |
|       '';
 | |
|       default = [];
 | |
|       type = types.listOf (types.enum ["master" "node"]);
 | |
|     };
 | |
| 
 | |
|     dataDir = mkOption {
 | |
|       description = "Kubernetes root directory for managing kubelet files.";
 | |
|       default = "/var/lib/kubernetes";
 | |
|       type = types.path;
 | |
|     };
 | |
| 
 | |
|     apiserver = {
 | |
|       enable = mkOption {
 | |
|         description = "Whether to enable kubernetes apiserver.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       address = mkOption {
 | |
|         description = "Kubernetes apiserver listening address.";
 | |
|         default = "127.0.0.1";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       publicAddress = mkOption {
 | |
|         description = ''
 | |
|           Kubernetes apiserver public listening address used for read only and
 | |
|           secure port.
 | |
|         '';
 | |
|         default = cfg.apiserver.address;
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       port = mkOption {
 | |
|         description = "Kubernets apiserver listening port.";
 | |
|         default = 8080;
 | |
|         type = types.int;
 | |
|       };
 | |
| 
 | |
|       readOnlyPort = mkOption {
 | |
|         description = "Kubernets apiserver read-only port.";
 | |
|         default = 7080;
 | |
|         type = types.int;
 | |
|       };
 | |
| 
 | |
|       securePort = mkOption {
 | |
|         description = "Kubernetes apiserver secure port.";
 | |
|         default = 6443;
 | |
|         type = types.int;
 | |
|       };
 | |
| 
 | |
|       tlsCertFile = mkOption {
 | |
|         description = "Kubernetes apiserver certificate file.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       tlsPrivateKeyFile = mkOption {
 | |
|         description = "Kubernetes apiserver private key file.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       tokenAuth = mkOption {
 | |
|         description = ''
 | |
|           Kubernetes apiserver token authentication file. See
 | |
|           <link xlink:href="https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/authentication.md"/>
 | |
|         '';
 | |
|         default = {};
 | |
|         example = literalExample ''
 | |
|           {
 | |
|             alice = "abc123";
 | |
|             bob = "xyz987";
 | |
|           }
 | |
|         '';
 | |
|         type = types.attrsOf types.str;
 | |
|       };
 | |
| 
 | |
|       authorizationMode = mkOption {
 | |
|         description = ''
 | |
|           Kubernetes apiserver authorization mode (AlwaysAllow/AlwaysDeny/ABAC). See
 | |
|           <link xlink:href="https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/authorization.md"/>
 | |
|         '';
 | |
|         default = "AlwaysAllow";
 | |
|         type = types.enum ["AlwaysAllow" "AlwaysDeny" "ABAC"];
 | |
|       };
 | |
| 
 | |
|       authorizationPolicy = mkOption {
 | |
|         description = ''
 | |
|           Kubernetes apiserver authorization policy file. See
 | |
|           <link xlink:href="https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/authorization.md"/>
 | |
|         '';
 | |
|         default = [];
 | |
|         example = literalExample ''
 | |
|           [
 | |
|             {user = "admin";}
 | |
|             {user = "scheduler"; readonly = true; kind= "pods";}
 | |
|             {user = "scheduler"; kind = "bindings";}
 | |
|             {user = "kubelet";  readonly = true; kind = "bindings";}
 | |
|             {user = "kubelet"; kind = "events";}
 | |
|             {user= "alice"; ns = "projectCaribou";}
 | |
|             {user = "bob"; readonly = true; ns = "projectCaribou";}
 | |
|           ]
 | |
|         '';
 | |
|         type = types.listOf types.attrs;
 | |
|       };
 | |
| 
 | |
|       allowPrivileged = mkOption {
 | |
|         description = "Whether to allow privileged containers on kubernetes.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       portalNet = mkOption {
 | |
|         description = "Kubernetes CIDR notation IP range from which to assign portal IPs";
 | |
|         default = "10.10.10.10/16";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       extraOpts = mkOption {
 | |
|         description = "Kubernetes apiserver extra command line options.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
|     };
 | |
| 
 | |
|     scheduler = {
 | |
|       enable = mkOption {
 | |
|         description = "Whether to enable kubernetes scheduler.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       address = mkOption {
 | |
|         description = "Kubernetes scheduler listening address.";
 | |
|         default = "127.0.0.1";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       port = mkOption {
 | |
|         description = "Kubernets scheduler listening port.";
 | |
|         default = 10251;
 | |
|         type = types.int;
 | |
|       };
 | |
| 
 | |
|       master = mkOption {
 | |
|         description = "Kubernetes apiserver address";
 | |
|         default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       extraOpts = mkOption {
 | |
|         description = "Kubernetes scheduler extra command line options.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
|     };
 | |
| 
 | |
|     controllerManager = {
 | |
|       enable = mkOption {
 | |
|         description = "Whether to enable kubernetes controller manager.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       address = mkOption {
 | |
|         description = "Kubernetes controller manager listening address.";
 | |
|         default = "127.0.0.1";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       port = mkOption {
 | |
|         description = "Kubernets controller manager listening port.";
 | |
|         default = 10252;
 | |
|         type = types.int;
 | |
|       };
 | |
| 
 | |
|       master = mkOption {
 | |
|         description = "Kubernetes apiserver address";
 | |
|         default = "${cfg.apiserver.address}:${toString cfg.apiserver.port}";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       machines = mkOption {
 | |
|         description = "Kubernetes apiserver list of machines to schedule to schedule onto";
 | |
|         default = [];
 | |
|         type = types.listOf types.str;
 | |
|       };
 | |
| 
 | |
|       extraOpts = mkOption {
 | |
|         description = "Kubernetes scheduler extra command line options.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
|     };
 | |
| 
 | |
|     kubelet = {
 | |
|       enable = mkOption {
 | |
|         description = "Whether to enable kubernetes kubelet.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       address = mkOption {
 | |
|         description = "Kubernetes kubelet info server listening address.";
 | |
|         default = "0.0.0.0";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       port = mkOption {
 | |
|         description = "Kubernets kubelet info server listening port.";
 | |
|         default = 10250;
 | |
|         type = types.int;
 | |
|       };
 | |
| 
 | |
|       hostname = mkOption {
 | |
|         description = "Kubernetes kubelet hostname override";
 | |
|         default = config.networking.hostName;
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       allowPrivileged = mkOption {
 | |
|         description = "Whether to allow kubernetes containers to request privileged mode.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       extraOpts = mkOption {
 | |
|         description = "Kubernetes kubelet extra command line options.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
|     };
 | |
| 
 | |
|     proxy = {
 | |
|       enable = mkOption {
 | |
|         description = "Whether to enable kubernetes proxy.";
 | |
|         default = false;
 | |
|         type = types.bool;
 | |
|       };
 | |
| 
 | |
|       address = mkOption {
 | |
|         description = "Kubernetes proxy listening address.";
 | |
|         default = "0.0.0.0";
 | |
|         type = types.str;
 | |
|       };
 | |
| 
 | |
|       extraOpts = mkOption {
 | |
|         description = "Kubernetes proxy extra command line options.";
 | |
|         default = "";
 | |
|         type = types.str;
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   ###### implementation
 | |
| 
 | |
|   config = mkMerge [
 | |
|     (mkIf cfg.apiserver.enable {
 | |
|       systemd.services.kubernetes-apiserver = {
 | |
|         description = "Kubernetes Api Server";
 | |
|         wantedBy = [ "multi-user.target" ];
 | |
|         after = [ "network-interfaces.target" "etcd.service" ];
 | |
|         serviceConfig = {
 | |
|           ExecStart = let
 | |
|             authorizationPolicyFile =
 | |
|               pkgs.writeText "kubernetes-policy"
 | |
|                 (builtins.toJSON cfg.apiserver.authorizationPolicy);
 | |
|             tokenAuthFile =
 | |
|               pkgs.writeText "kubernetes-auth"
 | |
|                 (concatImapStringsSep "\n" (i: v: v + "," + (toString i))
 | |
|                     (mapAttrsToList (name: token: token + "," + name) cfg.apiserver.tokenAuth));
 | |
|           in ''${cfg.package}/bin/kube-apiserver \
 | |
|             -etcd_servers=${concatMapStringsSep "," (f: "http://${f}") cfg.etcdServers} \
 | |
|             -address=${cfg.apiserver.address} \
 | |
|             -port=${toString cfg.apiserver.port} \
 | |
|             -read_only_port=${toString cfg.apiserver.readOnlyPort} \
 | |
|             -public_address_override=${cfg.apiserver.publicAddress} \
 | |
|             -allow_privileged=${if cfg.apiserver.allowPrivileged then "true" else "false"} \
 | |
|             ${optionalString (cfg.apiserver.tlsCertFile!="")
 | |
|               "-tls_cert_file=${cfg.apiserver.tlsCertFile}"} \
 | |
|             ${optionalString (cfg.apiserver.tlsPrivateKeyFile!="")
 | |
|               "-tls_private_key_file=${cfg.apiserver.tlsPrivateKeyFile}"} \
 | |
|             ${optionalString (cfg.apiserver.tokenAuth!=[])
 | |
|               "-token_auth_file=${tokenAuthFile}"} \
 | |
|             -authorization_mode=${cfg.apiserver.authorizationMode} \
 | |
|             ${optionalString (cfg.apiserver.authorizationMode == "ABAC")
 | |
|               "-authorization_policy_file=${authorizationPolicyFile}"} \
 | |
|             ${optionalString (cfg.apiserver.tlsCertFile!="" && cfg.apiserver.tlsCertFile!="")
 | |
|               "-secure_port=${toString cfg.apiserver.securePort}"} \
 | |
|             -portal_net=${cfg.apiserver.portalNet} \
 | |
|             -logtostderr=true \
 | |
|             ${optionalString cfg.verbose "-v=6 -log_flush_frequency=1s"} \
 | |
|             ${cfg.apiserver.extraOpts}
 | |
|           '';
 | |
|           User = "kubernetes";
 | |
|         };
 | |
|         postStart = ''
 | |
|           until ${pkgs.curl}/bin/curl -s -o /dev/null 'http://${cfg.apiserver.address}:${toString cfg.apiserver.port}/'; do
 | |
|             sleep 1;
 | |
|           done
 | |
|         '';
 | |
|       };
 | |
|     })
 | |
| 
 | |
|     (mkIf cfg.scheduler.enable {
 | |
|       systemd.services.kubernetes-scheduler = {
 | |
|         description = "Kubernetes Scheduler Service";
 | |
|         wantedBy = [ "multi-user.target" ];
 | |
|         after = [ "network-interfaces.target" "kubernetes-apiserver.service" ];
 | |
|         serviceConfig = {
 | |
|           ExecStart = ''${cfg.package}/bin/kube-scheduler \
 | |
|             -address=${cfg.scheduler.address} \
 | |
|             -port=${toString cfg.scheduler.port} \
 | |
|             -master=${cfg.scheduler.master} \
 | |
|             -logtostderr=true \
 | |
|             ${optionalString cfg.verbose "-v=6 -log_flush_frequency=1s"} \
 | |
|             ${cfg.scheduler.extraOpts}
 | |
|           '';
 | |
|           User = "kubernetes";
 | |
|         };
 | |
|       };
 | |
|     })
 | |
| 
 | |
|     (mkIf cfg.controllerManager.enable {
 | |
|       systemd.services.kubernetes-controller-manager = {
 | |
|         description = "Kubernetes Controller Manager Service";
 | |
|         wantedBy = [ "multi-user.target" ];
 | |
|         after = [ "network-interfaces.target" "kubernetes-apiserver.service" ];
 | |
|         serviceConfig = {
 | |
|           ExecStart = ''${cfg.package}/bin/kube-controller-manager \
 | |
|             -address=${cfg.controllerManager.address} \
 | |
|             -port=${toString cfg.controllerManager.port} \
 | |
|             -master=${cfg.controllerManager.master} \
 | |
|             ${optionalString (cfg.controllerManager.machines != [])
 | |
|                 "-machines=${concatStringsSep "," cfg.controllerManager.machines}"} \
 | |
|             -logtostderr=true \
 | |
|             ${optionalString cfg.verbose "-v=6 -log_flush_frequency=1s"} \
 | |
|             ${cfg.controllerManager.extraOpts}
 | |
|           '';
 | |
|           User = "kubernetes";
 | |
|         };
 | |
|       };
 | |
|     })
 | |
| 
 | |
|     (mkIf cfg.kubelet.enable {
 | |
|       systemd.services.kubernetes-kubelet = {
 | |
|         description = "Kubernetes Kubelet Service";
 | |
|         wantedBy = [ "multi-user.target" ];
 | |
|         after = [ "network-interfaces.target" "etcd.service" "docker.service" ];
 | |
|         serviceConfig = {
 | |
|           ExecStart = ''${cfg.package}/bin/kubelet \
 | |
|             -etcd_servers=${concatMapStringsSep "," (f: "http://${f}") cfg.etcdServers} \
 | |
|             -address=${cfg.kubelet.address} \
 | |
|             -port=${toString cfg.kubelet.port} \
 | |
|             -hostname_override=${cfg.kubelet.hostname} \
 | |
|             -allow_privileged=${if cfg.kubelet.allowPrivileged then "true" else "false"} \
 | |
|             -root_dir=${cfg.dataDir} \
 | |
|             -logtostderr=true \
 | |
|             ${optionalString cfg.verbose "-v=6 -log_flush_frequency=1s"} \
 | |
|             ${cfg.kubelet.extraOpts}
 | |
|           '';
 | |
|           User = "kubernetes";
 | |
|           PermissionsStartOnly = true;
 | |
|           WorkingDirectory = cfg.dataDir;
 | |
|         };
 | |
|       };
 | |
|     })
 | |
| 
 | |
|     (mkIf cfg.proxy.enable {
 | |
|       systemd.services.kubernetes-proxy = {
 | |
|         description = "Kubernetes Proxy Service";
 | |
|         wantedBy = [ "multi-user.target" ];
 | |
|         after = [ "network-interfaces.target" "etcd.service" ];
 | |
|         serviceConfig = {
 | |
|           ExecStart = ''${cfg.package}/bin/kube-proxy \
 | |
|             -etcd_servers=${concatMapStringsSep "," (s: "http://${s}") cfg.etcdServers} \
 | |
|             -bind_address=${cfg.proxy.address} \
 | |
|             -logtostderr=true \
 | |
|             ${optionalString cfg.verbose "-v=6 -log_flush_frequency=1s"} \
 | |
|             ${cfg.proxy.extraOpts}
 | |
|           '';
 | |
|         };
 | |
|       };
 | |
|     })
 | |
| 
 | |
|     (mkIf (any (el: el == "master") cfg.roles) {
 | |
|       services.kubernetes.apiserver.enable = mkDefault true;
 | |
|       services.kubernetes.scheduler.enable = mkDefault true;
 | |
|       services.kubernetes.controllerManager.enable = mkDefault true;
 | |
|     })
 | |
| 
 | |
|     (mkIf (any (el: el == "node") cfg.roles) {
 | |
|       virtualisation.docker.enable = mkDefault true;
 | |
|       services.kubernetes.kubelet.enable = mkDefault true;
 | |
|       services.kubernetes.proxy.enable = mkDefault true;
 | |
|     })
 | |
| 
 | |
|     (mkIf (any (el: el == "node" || el == "master") cfg.roles) {
 | |
|       services.etcd.enable = mkDefault true;
 | |
|     })
 | |
| 
 | |
|     (mkIf (
 | |
|         cfg.apiserver.enable ||
 | |
|         cfg.scheduler.enable ||
 | |
|         cfg.controllerManager.enable ||
 | |
|         cfg.kubelet.enable ||
 | |
|         cfg.proxy.enable
 | |
|     ) {
 | |
|       services.kubernetes.package = mkDefault pkgs.kubernetes;
 | |
| 
 | |
|       environment.systemPackages = [ cfg.package ];
 | |
| 
 | |
|       users.extraUsers = singleton {
 | |
|         name = "kubernetes";
 | |
|         uid = config.ids.uids.kubernetes;
 | |
|         description = "Kubernetes user";
 | |
|         extraGroups = [ "docker" ];
 | |
|         group = "kubernetes";
 | |
|         home = cfg.dataDir;
 | |
|         createHome = true;
 | |
|       };
 | |
|       users.extraGroups.kubernetes.gid = config.ids.gids.kubernetes;
 | |
|     })
 | |
| 
 | |
|   ];
 | |
| }
 | 
