diff options
Diffstat (limited to 'mod/web')
| -rw-r--r-- | mod/web/blog/.gitignore | 2 | ||||
| -rw-r--r-- | mod/web/blog/config.toml | 8 | ||||
| -rw-r--r-- | mod/web/blog/content/log/_index.md | 7 | ||||
| -rw-r--r-- | mod/web/blog/content/log/deterministic-hostnames.md | 80 | ||||
| -rw-r--r-- | mod/web/blog/content/log/nohup.md | 26 | ||||
| -rw-r--r-- | mod/web/blog/content/posts/_index.md | 8 | ||||
| -rw-r--r-- | mod/web/blog/content/posts/stateless-compute-networks.md | 41 | ||||
| -rw-r--r-- | mod/web/blog/default.nix | 15 | ||||
| -rw-r--r-- | mod/web/blog/static/avatar.png | bin | 0 -> 1800 bytes | |||
| -rw-r--r-- | mod/web/blog/static/style.css | 85 | ||||
| -rw-r--r-- | mod/web/blog/templates/404.html | 9 | ||||
| -rw-r--r-- | mod/web/blog/templates/base.html | 26 | ||||
| -rw-r--r-- | mod/web/blog/templates/blog.html | 22 | ||||
| -rw-r--r-- | mod/web/blog/templates/index.html | 44 | ||||
| -rw-r--r-- | mod/web/blog/templates/post.html | 12 |
15 files changed, 385 insertions, 0 deletions
diff --git a/mod/web/blog/.gitignore b/mod/web/blog/.gitignore new file mode 100644 index 0000000..decc3f8 --- /dev/null +++ b/mod/web/blog/.gitignore @@ -0,0 +1,2 @@ +public/ +nohup.out diff --git a/mod/web/blog/config.toml b/mod/web/blog/config.toml new file mode 100644 index 0000000..69c389b --- /dev/null +++ b/mod/web/blog/config.toml @@ -0,0 +1,8 @@ +base_url = "https://4kb.net" +build_search_index = false +compile_sass = false +generate_feeds = true +minify_html = true + +[markdown] +highlight_code = false diff --git a/mod/web/blog/content/log/_index.md b/mod/web/blog/content/log/_index.md new file mode 100644 index 0000000..10f2311 --- /dev/null +++ b/mod/web/blog/content/log/_index.md @@ -0,0 +1,7 @@ ++++ +title = "Log" +sort_by = "date" +template = "blog.html" +page_template = "post.html" +description = "notes too short to post" ++++ diff --git a/mod/web/blog/content/log/deterministic-hostnames.md b/mod/web/blog/content/log/deterministic-hostnames.md new file mode 100644 index 0000000..43dc7d8 --- /dev/null +++ b/mod/web/blog/content/log/deterministic-hostnames.md @@ -0,0 +1,80 @@ +--- +title: "Deterministic and unique network hostnames" +date: "2024-11-10" +--- + +As part of building out a Kubernetes cluster, I wanted to build and distribute a +single OS image to create stateless worker nodes. Using network booting, and +some clever tricks to differentiate nodes, we can create a scaleable and +efficient farm of workers for a cluster that don't even need disks. + +The idea came from a plan to build a cluster using the +[compute blade](https://computeblade.com/), and a few Raspberry Pi SBCs I +already own. Running the cluster from an SD card is not recommended due to the +not-so-great reliability of the flash used by most manufacturers, so I wanted to +try PXE booting each Pi to save money rather than purchasing an SSD for each +one. The compute blades do support an NVMe disk, but I plan to use those for a +storage cluster later, so they need to remain empty. + +## Base image + +Alpine Linux has been my preferred server OS for a long time. It provides a very +lightweight base system, and bundles an excellent bootstrapping system, +[apkovl](https://wiki.alpinelinux.org/wiki/Alpine_local_backup), that allows the +user to save a set of customisations to an system as an overlay to a stock +Alpine live image. In other words, we can create our image once, save the +changes as an `apkovl.tar.gz` file, and apply the same changes to a base system +on boot. This file can even be provided as a +[kernel parameter](https://wiki.alpinelinux.org/wiki/PXE_boot#Guide_to_options) +and will be fetched from a remote webserver automatically! + +Since the image and configuration will be shipped to the node via the network, +an added benefit of using Alpine is its tiny space consumption. I'm not using +enough nodes for this to really matter, but it's a cool optimization regardless. + +## Differentiating the nodes + +One of the main goals of this project is that there should be no persistent +storage required outside the boot image itself. Since every node will download +and generate the same root file-system on startup, the first problem that arises +is how the nodes will identify themselves both on the network and the cluster, +given that it's not possible to name them ahead of time. In other words, any +given node has to generate a unique hostname that won't collide with other +workers, and that will be the same each time that node boots. + +Since these nodes will not have a predefined name, we have to rely on +characteristics of the hardware to differentiate each one. The hardware MAC +address is perfect for this, since it's unique to to each node and will not be +wiped away after the node reboots. On a system like Linux that exposes its +hardware through a _sysfs_, we can find a file containing the address at +`/sys/class/net/eth0/address`. I don't really like the idea of attaching the +literal MAC address of the node to its network hostname, since it's a security +risk, and a bit too verbose. Instead, we can transform it into something safer +using a `sha1sum`, which is already present on our Alpine base system: + +```console +sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' +``` + +### Applying the new name + +Ideally, the node should apply its generated hostname before reaching out for an +address over DHCP or joining the cluster. We can make sure it happens before any +traffic is sent out by adding a `pre-up` command to the right interface in +`/etc/network/interfaces`: + +``` + +... + +auto eth0 +iface eth0 inet dhcp + pre-up sha1sum /sys/class/net/eth0/address | head -c 6 | awk '{print "worker-" $0}' > /etc/hostname + +... +``` + +The VM I tested with looks outputs `worker-e2fae8`. Pretty clean result, and if +you want to know the physical node that maps to each hostname, you can take note +of the MAC address beforehand and generate the same hash on another computer to +match them up. diff --git a/mod/web/blog/content/log/nohup.md b/mod/web/blog/content/log/nohup.md new file mode 100644 index 0000000..64f7983 --- /dev/null +++ b/mod/web/blog/content/log/nohup.md @@ -0,0 +1,26 @@ +--- +title: "Spawning background processes" +date: "2024-11-17" +--- + +Working in a terminal, +I often pair my editor with a background process watching files. +Before reaching for terminal multiplexers, +see if you can get away with simple tty job control. +Spawn the background process, +still attached to the terminal instance: + +``` +program args & +``` + +Also redirect its output to a file, +for when the process writes to the tty from the background: + +``` +nohup program args & +``` + +Extra reading: + +- <https://jvns.ca/blog/2024/07/03/reasons-to-use-job-control/> diff --git a/mod/web/blog/content/posts/_index.md b/mod/web/blog/content/posts/_index.md new file mode 100644 index 0000000..1f4024a --- /dev/null +++ b/mod/web/blog/content/posts/_index.md @@ -0,0 +1,8 @@ ++++ +title = "Posts" +sort_by = "date" +template = "blog.html" +page_template = "post.html" ++++ + +Longer form writing. diff --git a/mod/web/blog/content/posts/stateless-compute-networks.md b/mod/web/blog/content/posts/stateless-compute-networks.md new file mode 100644 index 0000000..bac9e5d --- /dev/null +++ b/mod/web/blog/content/posts/stateless-compute-networks.md @@ -0,0 +1,41 @@ +--- +title: "On stateless compute networks" +date: "2024-11-10" +draft: true +--- + +On the topic of distributed systems and clustering, +I am quite invested in the idea of compute nodes that rely entirely on the network for configuration. +Arbitrary nodes can join a pre-existing cluster, +offering their CPU time and memory for computation without relying on any pre-existing configuration on the node itself. +In other words, any computer could pick up work, +only needing power and a network connection to the cluster. + +Perhaps this eventually leads into a "self-healing" cluster where only one node is manually bootstrapped, +which then serves a _configuration endpoint_ for other stateless nodes to reach out to for their instructions, +which they will then also serve once they are themselves ready. + +Early revisions of these notes mention Kubernetes, +but I am also trying to achieve similar results with NixOS on a custom project. +In any case, these are my ever-updating notes towards a general implementation of a stateless distributed systems architecture. + +## Self healing cluster + +Assuming control of an external DHCP server, +a self healing Kubernetes cluster would be feasible, +with the PXE boot artifacts supplied by the cluster itself. +That is, as long as one node is running the pod hosting the artifacts on a given endpoint, +other nodes can boot those artifacts and join the cluster, +thereby being able to host the artifacts as well. + +## Configuration endpoint + +The nodes shouldn't require a disk installed to be able to join the network. +Rather, the lofty goal of zero-configuration compute nodes passes the job of node initialization to the supporting network. +This is accomplished with PXE boot instructions supplied over DHCP. + +I delegate the following tasks to a single node in the subnet: + +- Gateway: Optional outbound connections if required +- DHCP server: Cluster IPAM +- TFTP and HTTP server: Serves iPXE firmware and kernel/initrd artifacts diff --git a/mod/web/blog/default.nix b/mod/web/blog/default.nix new file mode 100644 index 0000000..5d76e7a --- /dev/null +++ b/mod/web/blog/default.nix @@ -0,0 +1,15 @@ +{ pkgs, ... }: pkgs.stdenvNoCC.mkDerivation { + pname = "4kb.net"; + version = "1.0"; + src = ./.; + + nativeBuildInputs = with pkgs; [ + zola + ]; + + buildPhase = "zola build"; + installPhase = '' + mkdir -p $out + cp -r public/* $out/ + ''; +} diff --git a/mod/web/blog/static/avatar.png b/mod/web/blog/static/avatar.png Binary files differnew file mode 100644 index 0000000..91357dc --- /dev/null +++ b/mod/web/blog/static/avatar.png diff --git a/mod/web/blog/static/style.css b/mod/web/blog/static/style.css new file mode 100644 index 0000000..fdfd692 --- /dev/null +++ b/mod/web/blog/static/style.css @@ -0,0 +1,85 @@ +:root { + --bg: #181616; + --fg: #c5c9c5; + --link: #76946a; + --linkhover: #98bb6c; +} + +html { + scroll-behavior: smooth; + color-scheme: dark; +} + +body { + font-family: serif; + padding: 0 0.75em; + max-width: 42em; + margin: auto; + background: var(--bg); + color: var(--fg); +} + +p { + line-height: 1.3em; +} + +th, +td { + padding: 0.2em 0.4em; + border: thin solid; +} + +table { + border: thin solid; + border-collapse: collapse; +} + +article { + padding-top: 1em; +} + +footer { + margin: 3em auto; + text-align: center; +} + +a, +a:link { + color: var(--link); +} + +a:hover { + color: var(--linkhover); +} + +a[href^="http"]:where(:not([href*="4kb.net/"]))::after { + content: " \21e2"; +} + +article img { + display: block; + margin: 0 auto; + max-width: 80%; + height: auto; + object-fit: contain; +} + +nav { + margin-top: 1em; + display: flex; + justify-content: space-between; + align-items: center; + font-family: monospace; + font-weight: bold; + gap: 8px; +} + +nav div { + display: flex; + gap: 10px; +} + +pre { + padding: 1em; + overflow-x: scroll; +} diff --git a/mod/web/blog/templates/404.html b/mod/web/blog/templates/404.html new file mode 100644 index 0000000..a4669df --- /dev/null +++ b/mod/web/blog/templates/404.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block content %} +<head> + <title>Error 404!</title> +</head> + +404! +{% endblock content %} diff --git a/mod/web/blog/templates/base.html b/mod/web/blog/templates/base.html new file mode 100644 index 0000000..6757d02 --- /dev/null +++ b/mod/web/blog/templates/base.html @@ -0,0 +1,26 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <link href="/style.css" rel="stylesheet" /> + <link rel="preload" href="style.css" as="style" /> + <title>{% block title %}4kb.net{% endblock title %}</title> + </head> + <body> + <nav> + <span>4kb.net</span> + <div> + <a href="/">Home</a> + <a href="/posts">Posts</a> + <a href="/log">Log</a> + </div> + </nav> + <hr /> + <main>{% block content %} {% endblock %}</main> + <footer> + <hr /> + <span>© 2024 Kleidi Bujari</span> + </footer> + </body> +</html> diff --git a/mod/web/blog/templates/blog.html b/mod/web/blog/templates/blog.html new file mode 100644 index 0000000..6c92a07 --- /dev/null +++ b/mod/web/blog/templates/blog.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block content %} + +<head> + <title>4kb.net</title> +</head> + +<h2>{{ section.title }}</h2> + +{{ section.content | safe }} + +<ul> + {% for page in section.pages %} + <li> + <span>[{{ page.date }}]</span> + <a href="{{ page.permalink | safe }}">{{ page.title }}</a> + </li> + {% endfor %} +</ul> + +{% endblock content %} diff --git a/mod/web/blog/templates/index.html b/mod/web/blog/templates/index.html new file mode 100644 index 0000000..74e9a0f --- /dev/null +++ b/mod/web/blog/templates/index.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} {% block content %} + +<h2>welcome!</h2> + +<p> + I'm a computer engineering undergrad studying in Toronto. This is my personal + site where I sometimes publish long form posts on topics I'm interested in, or + short "logs" for information I want to remember. +</p> + +<span>Find me on:</span> +<ul> + <li>email: <a href="mailto:mail@4kb.net">mail@4kb.net</a></li> + <li>github: <a href="https://github.com/kbujari">kbujari</a></li> +</ul> + +<!--<h3>Recent Posts</h3>--> +<!----> +<!--{% set section = get_section(path="posts/_index.md") %}--> +<!----> +<!--<ul>--> +<!-- {% for page in section.pages | slice(end=4) %}--> +<!-- <li>--> +<!-- <span>[{{ page.date }}]</span>--> +<!-- <a href="{{ page.permalink | safe }}">{{ page.title }}</a>--> +<!-- </li>--> +<!-- {% endfor %}--> +<!--</ul>--> + +<h3>Recent Logs</h3> + +{% set section = get_section(path="log/_index.md") %} + +<ul> + {% for page in section.pages | slice(end=4) %} + <li> + <span>[{{ page.date }}]</span> + <a href="{{ page.permalink | safe }}">{{ page.title }}</a> + </li> + {% endfor %} +</ul> + + +{% endblock content %} diff --git a/mod/web/blog/templates/post.html b/mod/web/blog/templates/post.html new file mode 100644 index 0000000..b1981e4 --- /dev/null +++ b/mod/web/blog/templates/post.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block title%} + {{ page.title }} - {{ super() }} +{% endblock title %} + +{% block content %} + +<h1 style="text-align: center; margin-top: 1em;">{{ page.title }}</h1> +<article> + {{ page.content | safe }} +</article> +{% endblock content %} |
