commit b023369500467d16f7e19b6373a87439277fbf2c Author: Pim Kunis Date: Sat Nov 2 16:30:24 2024 +0100 Publish diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09cb062 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.direnv/ +result +ideas.txt +notes.txt \ No newline at end of file diff --git a/assets/cat-chewing-cable.jpg b/assets/cat-chewing-cable.jpg new file mode 100644 index 0000000..d157995 Binary files /dev/null and b/assets/cat-chewing-cable.jpg differ diff --git a/assets/helm-example/base.png b/assets/helm-example/base.png new file mode 100644 index 0000000..55b1a99 Binary files /dev/null and b/assets/helm-example/base.png differ diff --git a/assets/helm-example/highlight.png b/assets/helm-example/highlight.png new file mode 100644 index 0000000..6759b7f Binary files /dev/null and b/assets/helm-example/highlight.png differ diff --git a/assets/helm-example/watermark.png b/assets/helm-example/watermark.png new file mode 100644 index 0000000..ce3a847 Binary files /dev/null and b/assets/helm-example/watermark.png differ diff --git a/assets/helm.png b/assets/helm.png new file mode 100644 index 0000000..b2c6859 Binary files /dev/null and b/assets/helm.png differ diff --git a/assets/kubenix-example/base.png b/assets/kubenix-example/base.png new file mode 100644 index 0000000..209a348 Binary files /dev/null and b/assets/kubenix-example/base.png differ diff --git a/assets/kubenix-example/highlight1.png b/assets/kubenix-example/highlight1.png new file mode 100644 index 0000000..7a414c3 Binary files /dev/null and b/assets/kubenix-example/highlight1.png differ diff --git a/assets/kubenix-example/highlight2.png b/assets/kubenix-example/highlight2.png new file mode 100644 index 0000000..006e71b Binary files /dev/null and b/assets/kubenix-example/highlight2.png differ diff --git a/assets/kubenix-example/highlight3.png b/assets/kubenix-example/highlight3.png new file mode 100644 index 0000000..3da2c58 Binary files /dev/null and b/assets/kubenix-example/highlight3.png differ diff --git a/assets/kubenix-example/highlight4.png b/assets/kubenix-example/highlight4.png new file mode 100644 index 0000000..d563510 Binary files /dev/null and b/assets/kubenix-example/highlight4.png differ diff --git a/assets/kubenix-github.png b/assets/kubenix-github.png new file mode 100644 index 0000000..79f5b5a Binary files /dev/null and b/assets/kubenix-github.png differ diff --git a/assets/kubenix.svg b/assets/kubenix.svg new file mode 100644 index 0000000..84d5968 --- /dev/null +++ b/assets/kubenix.svg @@ -0,0 +1,162 @@ + + + +image/svg+xml diff --git a/assets/kubernetes.png b/assets/kubernetes.png new file mode 100644 index 0000000..c0ebe64 Binary files /dev/null and b/assets/kubernetes.png differ diff --git a/assets/me.jpg b/assets/me.jpg new file mode 100644 index 0000000..827abb7 Binary files /dev/null and b/assets/me.jpg differ diff --git a/assets/nix-snapshotter.png b/assets/nix-snapshotter.png new file mode 100644 index 0000000..7da562c Binary files /dev/null and b/assets/nix-snapshotter.png differ diff --git a/assets/nixng.png b/assets/nixng.png new file mode 100644 index 0000000..7e86892 Binary files /dev/null and b/assets/nixng.png differ diff --git a/assets/openapi-spec-example/base.png b/assets/openapi-spec-example/base.png new file mode 100644 index 0000000..04f5dcb Binary files /dev/null and b/assets/openapi-spec-example/base.png differ diff --git a/assets/openapi-spec-example/highlight1.png b/assets/openapi-spec-example/highlight1.png new file mode 100644 index 0000000..07ab989 Binary files /dev/null and b/assets/openapi-spec-example/highlight1.png differ diff --git a/assets/openapi-spec-example/highlight2.png b/assets/openapi-spec-example/highlight2.png new file mode 100644 index 0000000..6c3b863 Binary files /dev/null and b/assets/openapi-spec-example/highlight2.png differ diff --git a/assets/openapi-spec-gen.png b/assets/openapi-spec-gen.png new file mode 100644 index 0000000..0bf98d7 Binary files /dev/null and b/assets/openapi-spec-gen.png differ diff --git a/assets/self-hosting/highlight.png b/assets/self-hosting/highlight.png new file mode 100644 index 0000000..7ee7313 Binary files /dev/null and b/assets/self-hosting/highlight.png differ diff --git a/assets/self-hosting/logos.png b/assets/self-hosting/logos.png new file mode 100644 index 0000000..aa7b0ef Binary files /dev/null and b/assets/self-hosting/logos.png differ diff --git a/assets/self-hosting/logos.xcf b/assets/self-hosting/logos.xcf new file mode 100644 index 0000000..7660d96 Binary files /dev/null and b/assets/self-hosting/logos.xcf differ diff --git a/assets/servers-k8s-kill.jpg b/assets/servers-k8s-kill.jpg new file mode 100644 index 0000000..b14b7cd Binary files /dev/null and b/assets/servers-k8s-kill.jpg differ diff --git a/assets/servers-k8s-revive.jpg b/assets/servers-k8s-revive.jpg new file mode 100644 index 0000000..2fb08a1 Binary files /dev/null and b/assets/servers-k8s-revive.jpg differ diff --git a/assets/servers-k8s.jpg b/assets/servers-k8s.jpg new file mode 100644 index 0000000..e20a606 Binary files /dev/null and b/assets/servers-k8s.jpg differ diff --git a/assets/servers-kill.jpg b/assets/servers-kill.jpg new file mode 100644 index 0000000..f5b0323 Binary files /dev/null and b/assets/servers-kill.jpg differ diff --git a/assets/servers-services.jpg b/assets/servers-services.jpg new file mode 100644 index 0000000..c333c40 Binary files /dev/null and b/assets/servers-services.jpg differ diff --git a/assets/servers.jpg b/assets/servers.jpg new file mode 100644 index 0000000..6d56219 Binary files /dev/null and b/assets/servers.jpg differ diff --git a/assets/servers.jpg-autosave.kra b/assets/servers.jpg-autosave.kra new file mode 100644 index 0000000..e5ca873 Binary files /dev/null and b/assets/servers.jpg-autosave.kra differ diff --git a/assets/yaml-manifest/base.png b/assets/yaml-manifest/base.png new file mode 100644 index 0000000..1dc2f24 Binary files /dev/null and b/assets/yaml-manifest/base.png differ diff --git a/assets/yaml-manifest/highlight1.png b/assets/yaml-manifest/highlight1.png new file mode 100644 index 0000000..7c55680 Binary files /dev/null and b/assets/yaml-manifest/highlight1.png differ diff --git a/assets/yaml-manifest/highlight2.png b/assets/yaml-manifest/highlight2.png new file mode 100644 index 0000000..ae272d6 Binary files /dev/null and b/assets/yaml-manifest/highlight2.png differ diff --git a/assets/yaml-manifest/highlight3.png b/assets/yaml-manifest/highlight3.png new file mode 100644 index 0000000..a890766 Binary files /dev/null and b/assets/yaml-manifest/highlight3.png differ diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..873d3e5 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1730200266, + "narHash": "sha256-l253w0XMT8nWHGXuXqyiIC/bMvh1VRszGXgdpQlfhvU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "807e9154dcb16384b1b765ebe9cd2bba2ac287fd", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..60cc7a2 --- /dev/null +++ b/flake.nix @@ -0,0 +1,36 @@ +{ + description = "Presentation for NixCon 2024"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f { + pkgs = import nixpkgs { inherit system; }; + }); + in + { + packages = forEachSupportedSystem ({ pkgs }: { + default = pkgs.stdenv.mkDerivation { + name = "nixcon-2024-slides"; + buildInputs = with pkgs; [ marp-cli ]; + src = self; + dontUnpack = true; + + buildPhase = '' + marp $src --output $out/presentation.html --html + cp -r $src/assets $out/assets + ''; + }; + }); + + devShells = forEachSupportedSystem ({ pkgs }: { + default = pkgs.mkShell { + packages = with pkgs; [ marp-cli ]; + }; + }); + }; +} diff --git a/presentation.md b/presentation.md new file mode 100644 index 0000000..8188c5e --- /dev/null +++ b/presentation.md @@ -0,0 +1,487 @@ +--- +marp: true +theme: gaia +paginate: true +footer: "October 26th, 2024" +--- + +# Kubenix: Leveraging NixOS modules to generate Kubernetes manifests + +![bg right:35% 100% Kubenix logo](assets/kubenix.svg) + +**Pim Kunis** + +--- + +# Agenda + +![bg right:60% 80%](assets/kubenix-github.png) + + +1. My background +2. Kubernetes +3. Kubenix + +--- + +# `$ whoami` + +![bg right 60% drop-shadow:0,5px,10px,rgba(0,0,0,.7)](assets/me.jpg) + +- Pim Kunis + - Matrix: @pim:envs.net + - GitHub: @pizzapim +- Consultant @ [Sue](https://sue.nl) + - Currently doing Linux + Ansible stuff +- Relatively new to Nix +- Avid self-hoster + +--- + +# Self-hosting + +![bg Logos of services I self-host](assets/self-hosting/logos.png) + +--- + +# Self-hosting + +3 mini PCs (Gigabyte Brix with Intel Celeron J4105) + +![w:900 My servers in their closet](assets/servers-services.jpg) + +--- + +# NixOS for a cluster? + +![w:900 My servers in their closet](assets/servers-kill.jpg) + +--- + +# NixOS for a cluster? + +![bg right assets/cat-chewing-cable.jpg](assets/cat-chewing-cable.jpg) + +- Balance services +- Maintenance +- Services are tied to a particular host + - Pets vs cattle + +--- + +# Agenda + +1. My background +2. **Kubernetes** +3. Kubenix + +--- + +# Enter: Kubernetes + +![bg left:40% 100% assets/cat-chewing-cable.jpg](assets/kubernetes.png) + +- Abstracts infrastructure +- Container orchestration + - Workloads are containers (pods) +- Distributed state (etcd) + - Actual state + - Desired state + - Reconciliation + +--- + +# Kubernetes + +![w:900 My servers in their closet](assets/servers-k8s.jpg) + +--- + +# Kubernetes + +![w:900 My servers in their closet](assets/servers-k8s-kill.jpg) + +--- + +# Kubernetes + +![w:900 My servers in their closet](assets/servers-k8s-revive.jpg) + +--- + +# Mutating state in Kubernetes + +![bg 80% hi](assets/yaml-manifest/base.png) + +--- + +# Mutating state in Kubernetes + +![bg 80% hi](assets/yaml-manifest/highlight1.png) + +--- + +# Mutating state in Kubernetes + +![bg 80% hi](assets/yaml-manifest/highlight2.png) + +--- + +# Mutating state in Kubernetes + +![bg 80% hi](assets/yaml-manifest/highlight3.png) + +--- + +# Helm + +![bg right:50% 60%](assets/helm.png) + +- Package manager +- Templating YAML +- Cursed + +--- + +# Helm + + +![bg 80% hi](assets/helm-example/base.png) + +--- + +# Helm + + +![bg 80% hi](assets/helm-example/highlight.png) + +--- + +# Helm + +![bg 80% hi](assets/helm-example/watermark.png) + +--- + +# Agenda + +1. My background +2. Kubernetes +3. **Kubenix** + +--- + +# Kubenix ✨ + +- Generate manifests using NixOS modules! +- Benefits: + - No ugly YAML templates + - Functional language + - Type and structure checking + - Modularity + +--- + +# How Kubenix works + +1. My background +2. Kubernetes +3. **Kubenix** + - **OpenAPI specification** + - Generate NixOS module + - Use generated module + - Generate manifest + +--- + + +# The OpenAPI spec + +- Standard for describing APIs +- YAML or JSON +- Specifies how to interact with Kubernetes API + +--- + +# The OpenAPI spec + + +![bg 75% hi](assets/openapi-spec-example/base.png) + +--- + +# The OpenAPI spec + +![bg 75% hi](assets/openapi-spec-example/highlight1.png) + +--- + +# The OpenAPI spec + +![bg 75% hi](assets/openapi-spec-example/highlight2.png) + +--- + +# How Kubenix works + +1. My background +2. Kubernetes +3. **Kubenix** + - OpenAPI specification + - **Generate NixOS module** + - Use generated module + - Generate manifest + +--- + +# Generating the NixOS module + +- Generate Nix code + - Templating? + +--- + +# Generating the NixOS module + +```nix +'' +definitions = { + # Here we loop over each "definition" + ${concatStrings (mapAttrsToList (name: value: '' + "${name}" = { + ${optionalString (hasAttr "options" value) " + options = {${concatStrings (mapAttrsToList (name: value: '' + "${name}" = ${value}; + '') + value.options)}}; + "} + + ${optionalString (hasAttr "config" value) '' + config = {${concatStrings (mapAttrsToList (name: value: '' + "${name}" = ${value}; + '') + value.config)}}; + ''} + }; +'') +definitions)} +}; +'' +``` + +--- + +# The generated NixOS module + +```nix + "io.k8s.api.core.v1.Pod" = { + options = { + "apiVersion" = mkOption { + description = "..."; + type = (types.nullOr types.str); + }; + "kind" = mkOption { + description = "..."; + type = (types.nullOr types.str); + }; + "metadata" = mkOption { + description = "..."; + type = (types.nullOr (submoduleOf "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta")); + }; + "spec" = mkOption { + description = "..."; + type = (types.nullOr (submoduleOf "io.k8s.api.core.v1.PodSpec")); + }; + "status" = mkOption { + description = "..."; + type = (types.nullOr (submoduleOf "io.k8s.api.core.v1.PodStatus")); + }; + }; + }; +``` + +--- + +# How Kubenix works + +1. My background +2. Kubernetes +3. **Kubenix** + - OpenAPI specification + - Generate NixOS module + - **Use generated module** + - Generate manifest + +--- + +# Using the module + +![bg 70% hi](assets/kubenix-example/base.png) + +--- + +# Using the module + +![bg 70% hi](assets/kubenix-example/highlight1.png) + +--- + +# Using the module + +![bg 70% hi](assets/kubenix-example/highlight2.png) + +--- + +# Using the module + +![bg 70% hi](assets/kubenix-example/highlight3.png) + +--- + +# Using the module + +![bg 70% hi](assets/kubenix-example/highlight4.png) + +--- + +# Example of type checking + +```nix +{ + kubernetes.resources.pods.example-pod = { + spec.containers.jellyfin-container = { + image = "jellyfin/jellyfin:latest"; + ports = [{ containerPort = "foo"; }]; + }; + }; +}; +``` + +``` + error: A definition for option `kubernetes.(...).containerPort' is not of type `signed integer'. +``` + +--- + +# An undefined option + +```nix +{ + kubernetes.resources = { + foo.bar = "baz"; + } +} +``` + +``` +error: The option `kubernetes.api.resources.foo' does not exist. +``` + +--- + +# How Kubenix works + +1. My background +2. Kubernetes +3. **Kubenix** + - OpenAPI specification + - Generate NixOS module + - Use generated module + - **Generate manifest** + +--- + +# `kubenix.evalModules` + +```nix +{ + inputs.kubenix.url = "github:hall/kubenix"; + outputs = {self, kubenix, ... }@inputs: let + system = "x86_64-linux"; + in { + packages.${system}.default = (kubenix.evalModules.${system} { + module = { kubenix, ... }: { + imports = [ kubenix.modules.k8s ]; + kubernetes.resources.pods.example.spec.containers.jellyfin.image = "jellyfin/jellyfin:latest"; + }; + }).config.kubernetes.result; + }; +} +``` + +--- + +# `kubenix.evalModules` + + +```json +{ + "apiVersion": "v1", + "items": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "kubenix/k8s-version": "1.30", + "kubenix/project-name": "kubenix" + }, + "labels": { + "kubenix/hash": "84130bb6a26b9066eb2bdcaf2e68148a6e0648b0" + }, + "name": "example" + }, + "spec": { + "containers": [ + { + "image": "jellyfin/jellyfin:latest", + "name": "jellyfin" + } + ] + } + } + ], + "kind": "List", + "labels": { + "kubenix/hash": "84130bb6a26b9066eb2bdcaf2e68148a6e0648b0", + "kubenix/k8s-version": "1.30", + "kubenix/project-name": "kubenix" + } +} +``` + +--- + +# Summary + +- Kubenix generates Kubernetes manifests +- Converts OpenAPI spec to a NixOS module +- Build Kubernetes deployments with: + - No ugly YAML templates + - Modularity + - In a functional language + - Type and structure checking + +--- + +# Terraform: OpenAPI Provider Spec Generator + +![hoi](assets/openapi-spec-gen.png) + +--- + +# Bonus: nix-snapshotter + +![bg right:50% 100%](assets/nix-snapshotter.png) + +- Images are in Nix store +- Fully declarative + +--- + +# Bonus: NixNG + +![bg right:50% 100%](assets/nixng.png) + +- Nix~~OS~~ in a container +- init-system independent \ No newline at end of file