<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>blog.gadfly.ai Reader</title>
    <link>https://blog.gadfly.ai</link>
    <description>Read the latest posts from blog.gadfly.ai.</description>
    <pubDate>Sun, 24 May 2026 19:29:31 +0000</pubDate>
    <item>
      <title>Install A Kubernetes Cluster | How to setup a basic Kubernetes Cluster using KubeADM</title>
      <link>https://blog.gadfly.ai/vaishali/install-a-kubernetes-cluster-how-to-setup-a-basic-kubernetes-cluster-using</link>
      <description>&lt;![CDATA[Kubernetes is the backbone of modern container orchestration, powering everything from small projects to enterprise-scale applications. &#xA;&#xA;Whether you&#39;re a beginner or a seasoned engineer, understanding how to set up a Kubernetes cluster is a fundamental skill. In this tutorial, we&#39;ll take a hands-on approach using Kubeadm, one of the most popular tools for bootstrapping a Kubernetes cluster.&#xA;&#xA;  ⚠️ WARNING: Single Control Plane = Single Point of Failure!&#xA;🚨 A cluster with only one control plane is not highly available. If the control plane node goes offline, the entire cluster becomes unmanageable—no scheduling, no updates, no kubectl commands. This setup is fine for learning and testing but not recommended for production.&#xA;&#xA;In our next tutorial, we will add more control planes to achieve high availability and a production-ready cluster.&#xA;&#xA;This tutorial is an extension of Drew&#39;s Playlist. Check it out for more background on Kubernetes setup and best practices.&#xA;---&#xA;&#xA;Introduction&#xA;&#xA;Kubernetes is a platform that helps you run and manage containers (like Docker) at scale across multiple machines. It handles scheduling, networking, scaling, and fault tolerance for applications.&#xA;&#xA;Kubeadm is a powerful tool that simplifies Kubernetes cluster setup. It provides best-practice defaults while ensuring a secure and production-ready environment. In this guide, we will walk through setting up a Kubernetes cluster using Kubeadm, discuss networking options, security considerations, common pitfalls, and next steps for deploying workloads.&#xA;&#xA;There are multiple ways to set up a Kubernetes cluster. While Minikube and kind are great for local development and testing, Kubeadm is a more stable and production-ready option for setting up real multi-node clusters. If you&#39;re looking for alternative approaches, you can check out tutorials on Minikube and kind&#xA;&#xA;---&#xA;&#xA;SCENE 1: Pre-requisites&#xA;&#xA;Note: With VirtualBox&#39;s help for these tutorials, there&#39;s no need to install them on physical servers. In terms of just setting things up, unless you&#39;re using managed servers, this should get you going! &#xA;&#xA;1.Setting up the VM&#xA;&#xA;We&#39;re going to start with one control plane node and three worker nodes, all set up using this Ubuntu tutorial. The only difference between this setup and the tutorial is that we are not using RAID and do not have a swap partition. Instead, each VM consists of just a root partition and a boot partition.&#xA;&#xA;🔴 Ubuntu Version: This guide uses Ubuntu 22.04 LTS, ensuring compatibility with the latest Kubernetes versions. Using different versions may lead to unexpected issues.&#xA;&#xA;🔴 Recommended VM Resources:&#xA;For a smooth Kubernetes setup, each node should have at least:&#xA;2 CPUs&#xA;2 GB RAM (4 GB recommended for better performance)&#xA;10 GB of disk space&#xA;&#xA;VMk8snodes&#xA;&#xA;Before proceeding, check the official Kubernetes Docs for the latest prerequisites.&#xA;&#xA;2. Time to Play with the Terminal 🖥️&#xA;&#xA;We’re working with four nodes in this setup (Since the author of this blog has ADHD, multitasking is a must!) To manage all four nodes efficiently, we’ll use TMUX to synchronize our terminals, allowing us to run the same commands across multiple machines simultaneously.&#xA;&#xA;After following this tutorial, your terminal should look something like this:&#xA;&#xA;TMUX Screen&#xA;&#xA;All the nodes are synchronized by panes, making it easier to execute commands across all four machines.&#xA;&#xA;🔴 Follow this TMUX Tutorial: Link to TMUX tutorial&#xA;&#xA;🔹 Alternative Tools: TMUX is powerful, but it has a learning curve. If you’re new to terminal multiplexing, you might prefer:&#xA;&#xA;GNU Screen (Simpler, built-in on many systems)&#xA;&#xA;Byobu (User-friendly wrapper around TMUX &amp; Screen)&#xA;&#xA;🧭 TMUX Navigation Tips:&#xA;&#xA;Split panes vertically → Ctrl + B, then %&#xA;Split panes horizontally → Ctrl + B, then &#34;&#xA;Detach from a session → Ctrl + B, then D&#xA;Reattach to a session → Run tmux attach&#xA;&#xA;Now that we’re set up, let&#39;s move on to configuring KubeADM !!&#xA;&#xA;3.Looking at KubeADM doc&#xA;&#xA;Before proceeding, make sure your system meets all the necessary requirements by checking out the official Kubeadm installation guide:&#xA;&#xA;🔴 Kubeadm Installation Docs&#xA;&#xA;KubeADMdocs&#xA;&#xA;Going through these steps will help us efficiently install all required dependencies. Let&#39;s get started! &#xA;---&#xA;&#xA;SCENE 2: Installing a Container Runtime (ContainerD)&#xA;&#xA;One thing that&#39;s gonna be common in every tutorial and installations is pre-requisites, which will appear again and again and again specially when we&#39;re setting up Kubernetes cluster. So bare with me coz I&#39;m dropping another pre-req but it&#39;s important to look out for any.&#xA;&#xA;Step 1: Install and Configure Prerequisites for containerD&#xA;&#xA;Before we install Kubernetes, we need to configure our system properly.&#xA;&#xA;Enable IPv4 Packet Forwarding&#xA;&#xA;Kubernetes requires packet forwarding to allow network traffic between pods across different nodes. Without it, pods on one node won&#39;t be able to communicate with pods on another node.&#xA;&#xA;Run the following command to enable IPv4 forwarding:&#xA;sysctl params required by setup, params persist across reboots&#xA;cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf&#xA;net.ipv4.ipforward = 1&#xA;EOF&#xA;&#xA;🔴 Paste it into your terminal:&#xA;Terminal1&#xA;&#xA;Now, apply the changes without rebooting:&#xA;&#xA;Apply sysctl params without reboot&#xA;sudo sysctl --system&#xA;&#xA;To verify if IPv4 forwarding is enabled, run:&#xA;&#xA;sysctl net.ipv4.ipforward&#xA;It should return 1, indicating that forwarding is active.&#xA;&#xA;Disable Swap (Why It&#39;s Necessary)&#xA;&#xA;Kubernetes requires swap to be disabled because the Kubernetes scheduler relies on precise memory allocation. If swap is enabled, the system may overcommit memory, causing pods to crash unexpectedly.&#xA;&#xA;To disable swap, open the fstab file:&#xA;sudo vim /etc/fstab&#xA;Find the line containing /swap.img and comment it out by adding # at the beginning:&#xA;(in shell)&#xA;/swap.img&#xA;Then, run:&#xA;sudo swapoff -a&#xA;To confirm that swap is disabled, check the memory usage:&#xA;&#xA;free -h&#xA;You should see 0 in the swap column:&#xA;&#xA;Now, your system is ready for Kubeadm! 🍭 Skipping these steps may cause Kubeadm setup failures, so make sure they are properly configured.&#xA;&#xA;--- &#xA;&#xA;Step 2: Install ContainerD&#xA;&#xA;I am bad at pointing out locations in real life—LOL. But I’ll try my best to guide you through this documentation step-by-step. Kindly bear with me, and don’t come for me in the comments! 😂&#xA;&#xA;Where to Go&#xA;Head over to: ContainerD Documentation&#xA;Check out: Getting Started with ContainerD (GitHub Repo)&#xA;Find the Latest Release: ContainerD Releases&#xA;Scroll down to find the Assets section.&#xA;Choose the right binary for your machine.&#xA;DO NOT double-click! Instead, copy the link address of the release—we’ll use it in the terminal.&#xA;&#xA;Phew! 😮‍💨 Now, let&#39;s get into it.&#xA;&#xA;Download &amp; Extract ContainerD&#xA;&#xA;1. Set Version &amp; Detect Architecture (Recommended)&#xA;Using environment variables makes future updates easier. Run the following commands:&#xA;export CONTAINERDVERSION=$(curl -s https://api.github.com/repos/containerd/containerd/releases/latest | grep -oP &#39;&#34;tagname&#34;: &#34;v\K+&#39;)&#xA;export ARCH=$(uname -m)&#xA;if [ &#34;$ARCH&#34; == &#34;x8664&#34; ]; then ARCH=&#34;amd64&#34;; fi&#xA;&#xA;2. Download ContainerD&#xA;wget https://github.com/containerd/containerd/releases/download/v$CONTAINERDVERSION/containerd-$CONTAINERDVERSION-linux-$ARCH.tar.gz&#xA;&#xA;3. Extract to /usr/local&#xA;sudo su  # Switch to root user (optional)&#xA;tar Cxzvf /usr/local containerd-$CONTAINERDVERSION-linux-$ARCH.tar.gz&#xA;&#xA;---&#xA;&#xA;Common FAQ 🧐&#xA;&#xA;Enable ContainerD as a Systemd Service&#xA;If you plan to start ContainerD via systemd, download the service unit file and enable it:&#xA;&#xA;Download the service file:&#xA;wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -O /usr/lib/systemd/system/containerd.service&#xA;&#xA;Reload systemd &amp; enable the service:&#xA;systemctl daemon-reload&#xA;systemctl enable --now containerd&#xA;&#xA;Check ContainerD status:&#xA;systemctl status containerd&#xA;&#xA;If everything is running fine, you’re all set! 🚀 &#xA;&#xA;Using version variables &amp; architecture detection ensures users download the correct binary and keeps this guide easy to update. Hope this helps! 💡&#xA;&#xA;---&#xA;&#xA;Step 3: Install runc  &#xA;&#xA;What is runc and Why is it Needed?  &#xA;runc is a lightweight command-line tool used to spawn and run containers on Linux systems according to the Open Container Initiative (OCI) specification. It is the underlying runtime that most container engines, like Docker and containerd, rely on to execute containers.  &#xA;&#xA;Why is runc Needed?  &#xA;Low-level container runtime: runc sets up the container’s environment, including namespaces and cgroups, and starts the process inside the container.  &#xA;Standardized and OCI-Compliant: It follows the OCI runtime spec, making it compatible across different container orchestration systems.  &#xA;Used by Higher-Level Container Runtimes: Tools like containerd and CRI-O use runc to actually start and manage container processes.  &#xA;Security &amp; Isolation: It ensures proper container isolation using Linux security features.  &#xA;&#xA;How containerd Uses runc  &#xA;containerd does not run containers directly; instead, it delegates container execution to runc.  &#xA;runc handles the low-level execution, while containerd focuses on container lifecycle management.  &#xA;&#xA;  If containerd is the brain, runc is the hands that actually do the work of running containers. 🚀  &#xA;&#xA;---&#xA;&#xA;Install runc  &#xA;&#xA;Download the right version (for my machine, it&#39;s runc.amd64):  &#xA;   Copy the link address and run:  &#xA;&#xA;Install runc:  &#xA;install -m 755 runc.amd64 /usr/local/sbin/runc&#xA;&#xA;Verify Installation:  &#xA;   Run runc to check if it&#39;s installed:  &#xA;&#xA;---&#xA;&#xA;Step 4: Install CNI Plugin  &#xA;&#xA;Why Do We Need CNI Plugins?  &#xA;The Container Network Interface (CNI) plugin is required to enable networking for containers. It ensures that containers can communicate with each other and with external networks.  &#xA;&#xA;Create a directory for CNI plugins:  &#xA;      mkdir -p /opt/cni/bin&#xA;   &#xA;Download the CNI plugin:  &#xA;   Head over to Getting Started with containerd   Step 3 Install CNI Plugin  &#xA;   Go to Releases and choose the right plugin.  &#xA;   For Linux (amd64), run:  &#xA;wget https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-amd64-v1.6.2.tgz&#xA;&#xA;Extract and Install CNI Plugins:  &#xA;      tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.6.2.tgz&#xA;   &#xA;  Ensure everything is updated to the latest version.&#xA;&#xA;---&#xA;Step 5: Exploring config.toml in ContainerD  &#xA;&#xA;What is config.toml?  &#xA;The config.toml file is ContainerD’s main configuration file. It controls networking, container isolation, storage, logging, and security settings.&#xA;&#xA;Generate &amp; Edit config.toml  &#xA;&#xA;🍭 Create the directory (if not exists):  &#xA;mkdir -p /etc/containerd&#xA;&#xA;🍭 Generate the default config:  &#xA;containerd config default   /etc/containerd/config.toml&#xA;&#xA;🍭 Open the file to edit:  &#xA;vim /etc/containerd/config.toml&#xA;If you see TOML configurations, you’re in the right place! Exit the file (:q!) and proceed to Step 6. 🚀&#xA;&#xA;Step 6: Configuring the systemd cgroup driver&#xA;&#xA;To use the systemd cgroup driver in /etc/containerd/config.toml with runc, head over to the config.toml file.&#xA;&#xA;Open the config file:  &#xA;vim /etc/containerd/config.toml&#xA;&#xA;Locate the following section:  &#xA;[plugins.&#34;io.containerd.grpc.v1.cri&#34;.containerd.runtimes.runc]&#xA;&#xA;Inside this section, find:  &#xA;[plugins.&#34;io.containerd.grpc.v1.cri&#34;.containerd.runtimes.runc.options]&#xA;&#xA;Modify the SystemdCgroup setting:  &#xA;Change:&#xA;SystemdCgroup = false&#xA;to:&#xA;SystemdCgroup = true&#xA;&#xA;💡 Why is this important?  &#xA;The kubelet and containerd must use the same cgroup driver for stability.&#xA;systemd is the default on most Linux distros and integrates better with cgroup v2.&#xA;&#xA;---&#xA;&#xA;Check for cgroup v2 Support&#xA;Run:&#xA;stat -fc %T /sys/fs/cgroup&#xA;If the output is cgroup2fs, then cgroup v2 is enabled. &#xA;&#xA;💡 Why prefer cgroup v2?  &#xA;Simplifies resource management.&#xA;Avoids inconsistencies seen in cgroup v1.&#xA;Recommended by Kubernetes for better stability.&#xA;&#xA;---&#xA;&#xA;Restart &amp; Verify ContainerD&#xA;Restart containerd:  &#xA;systemctl restart containerd&#xA;&#xA;Check the status:  &#xA;systemctl status containerd&#xA;If everything is active ✅, congrats! 🎉 Your setup is good to go!&#xA;&#xA;---&#xA;&#xA;SCENE 3: Install Kubeadm, Kubelet, and Kubectl  &#xA;&#xA;We&#39;ve warmed up—now it&#39;s time for the real workout! But don&#39;t worry, it&#39;s pretty straightforward. (At least, I hope so! 😆)  &#xA;&#xA;---&#xA;&#xA;Step 1: Update apt and Install Dependencies  &#xA;&#xA;First, let&#39;s update the package list to ensure we&#39;re working with the latest versions:  &#xA;&#xA;apt-get update&#xA;&#xA;Now, install the necessary dependencies:  &#xA;&#xA;apt-transport-https may be a dummy package; if so, you can skip that package&#xA;apt-get install -y apt-transport-https ca-certificates curl gpg&#xA;&#xA;📝 Why these packages?  &#xA;apt-transport-https → Allows APT to fetch packages over HTTPS (secure connections).  &#xA;ca-certificates → Ensures system trusts SSL certificates.  &#xA;curl → Fetches files from the internet.  &#xA;gpg → Verifies package signatures for security.  &#xA;&#xA;---&#xA;&#xA;Step 2: Download the Google Cloud Public Signing Key  &#xA;&#xA;Run the following command to add Google&#39;s public signing key:  &#xA;&#xA;If the directory /etc/apt/keyrings does not exist, it should be created before the curl command, read the note below.&#xA;sudo mkdir -p -m 755 /etc/apt/keyrings&#xA;curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg&#xA;&#xA;---&#xA;&#xA;Step 3: Add the Kubernetes APT Repository  &#xA;&#xA;Run the following command to add the Kubernetes repository:  &#xA;&#xA;This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list&#xA;echo &#39;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /&#39; | sudo tee /etc/apt/sources.list.d/kubernetes.list&#xA;&#xA;  Along with following this tutorial, I would highly suggest opening up the official documentation alongside to get the right version at all times.&#xA;&#xA;📝 Step 3: Install Tools and Pin Versions  &#xA;&#xA;🔹 Why is pinning important?  &#xA;If you don’t pin versions, an upgrade to a newer Kubernetes version might happen during a system update, which can break compatibility in a multi-node setup. AND THIS IS BAD AND SAD AND AWFUL FOR CYBERSECURITY—DO NOT DO IT OR I WILL BE DISAPPOINTED. 😤 But fear not, this is what Dependabot was made for! Look into it—even though it’s not totally secure either, but at least it’s a start.  &#xA;&#xA;🔹 Quick Definitions:  &#xA;kubelet → The agent that manages containers on each node.  &#xA;kubeadm → Initializes the cluster.  &#xA;kubectl → The CLI you use to interact with the cluster.  &#xA;&#xA;---&#xA;&#xA;Step 4: Update the apt Package Index, Install Kubernetes Components, and Pin Their Versions  &#xA;&#xA;Run the following commands to install kubelet, kubeadm, and kubectl:  &#xA;&#xA;apt-get update&#xA;&#xA;apt-get install -y kubelet kubeadm kubectl&#xA;&#xA;apt-mark hold kubelet kubeadm kubectl&#xA;&#xA;📝 Component Responsibilities:  &#xA;Kubelet → Ensures that containers are running in a node.  &#xA;Kubeadm → Responsible for initializing a Kubernetes cluster.  &#xA;Kubectl → CLI tool for interacting with the Kubernetes API.  &#xA;&#xA;Next up: Verifying installation and configuring your cluster! 🚀&#xA;&#xA;---&#xA;&#xA;SCENE 4: Creating a Cluster&#xA;&#xA;Initializing Your Control Plane Mode&#xA;We want to set up a Highly Available (HA) control plane and add worker nodes. To achieve this, we need to define a --control-plane-endpoint. If we were in the cloud, we could use a load balancer&#39;s IP. But we are setting up on our machines, so we need an alternative.&#xA;&#xA;  Enter KubeVIP! ✨&#xA;&#xA;KubeVIP allows us to use a virtual IP address (VIP) for the control plane without requiring an external cloud-based load balancer.&#xA;&#xA;Step 1: Preparing for HA Control Plane with KubeVIP&#xA;&#xA;🔹 Why HA Matters?&#xA;The control plane schedules workloads and manages cluster state.&#xA;In an HA setup, multiple control planes prevent downtime in case one fails.&#xA;&#xA;Follow the KubeVIP documentation to get started.&#xA;&#xA;📝 If you&#39;re NOT worried about HA (which you should be), you can skip this. But be warned: without HA, you&#39;ll need to configure external load balancers later!&#xA;&#xA;Step 2: Generating a KubeVIP Manifest&#xA;&#xA;First, find a free IP in your network:&#xA;ip a&#xA;&#xA;Set a VIP address (ensure it doesn&#39;t conflict with existing IPs):&#xA;export VIP=xxx.xxx.x.xxx&#xA;&#xA;Set the interface name where KubeVIP will announce the VIP:&#xA;export INTERFACE=interface_name&#xA;&#xA;Install jq to parse the latest KubeVIP release:&#xA;apt install jq -y&#xA;&#xA;Fetch the latest version:&#xA;KVVERSION=$(curl -sL https://api.github.com/repos/kube-vip/kube-vip/releases | jq -r &#34;.[0].name&#34;)&#xA;&#xA;  Note: KubeVIP setup only applies to the control plane node, not worker nodes.&#xA;&#xA;Step 3: Creating the KubeVIP Manifest&#xA;&#xA;For containerd, run:&#xA;alias kube-vip=&#34;ctr image pull ghcr.io/kube-vip/kube-vip:$KVVERSION; ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip /kube-vip&#34;&#xA;&#xA;Ensure the manifest directory exists:&#xA;mkdir -p /etc/kubernetes/manifests&#xA;&#xA;Generate the manifest:&#xA;kube-vip manifest pod \&#xA;    --interface $INTERFACE \&#xA;    --address $VIP \&#xA;    --controlplane \&#xA;    --services \&#xA;    --arp \&#xA;    --leaderElection | tee /etc/kubernetes/manifests/kube-vip.yaml&#xA;&#xA;Step 4: Initializing the Control Plane&#xA;&#xA;Run:&#xA;kubeadm init --control-plane-endpoint $VIP&#xA;&#xA;🔹 Breaking Down kubeadm init Flags:&#xA;--control-plane-endpoint → Defines the shared endpoint for all control planes.&#xA;--pod-network-cidr → Defines the pod network range (must match CNI plugin requirements).&#xA;&#xA;Step 5: Setting Up kubeconfig&#xA;&#xA;🔹 What is kubeconfig?&#xA;This file stores credentials and cluster info so kubectl can securely communicate with the cluster.&#xA;&#xA;mkdir -p $HOME/.kube&#xA;cp -i /etc/kubernetes/admin.conf $HOME/.kube/config&#xA;chown $(id -u):$(id -g) $HOME/.kube/config&#xA;&#xA;🔴 Next: Adding Worker Nodes and Deploying a Pod Network!&#xA;&#xA;---&#xA;Kubernetes Cluster Setup with Calico Networking&#xA;&#xA;Step 1: Choose a Pod Network Add-on&#xA;&#xA;A Pod Network Add-on enables communication between pods in your cluster. We are using Calico, a widely adopted choice due to its scalability and support for network policies.&#xA;&#xA;Some network providers require you to pass specific flags to kubeadm init. For Calico, you must set --pod-network-cidr appropriately.&#xA;&#xA;Download the Calico Networking Manifest&#xA;Run the following command to download the Calico manifest:&#xA;&#xA;curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.29.2/manifests/calico.yaml&#xA;&#xA;---&#xA;Step 2: Understanding Pod CIDR&#xA;&#xA;CIDR (Classless Inter-Domain Routing) assigns IP addresses in a structured manner. Each pod in your cluster requires a unique IP from the CIDR block. This ensures that pods can communicate across nodes within the cluster.&#xA;&#xA;---&#xA;Step 3: Initialize the Kubernetes Cluster&#xA;&#xA;To initialize your cluster, use the kubeadm init command. Set the --pod-network-cidr to match Calico&#39;s expected value:&#xA;&#xA;kubeadm init --pod-network-cidr=192.168.0.0/16 --control-plane-endpoint=192.168.0.200 --apiserver-advertise-address=192.168.0.201&#xA;&#xA;Note:&#xA;The control-plane-endpoint should ideally be a DNS name or a static IP that won’t change. If the IP changes later, the cluster may become inaccessible due to certificate mismatches.&#xA;&#xA;Once the initialization is complete, configure your kubeconfig:&#xA;&#xA;export KUBECONFIG=/etc/kubernetes/admin.conf&#xA;&#xA;---&#xA;Step 4: Set Up a DNS Record&#xA;&#xA;To ensure smooth communication within the cluster, add a DNS record in /etc/hosts:&#xA;&#xA;vim /etc/hosts&#xA;&#xA;Add the following entry:&#xA;&#xA;192.168.0.200 kube-api-server&#xA;&#xA;Test the connection:&#xA;&#xA;apt install iputils-ping&#xA;ping kube-api-server&#xA;&#xA;---&#xA;Step 5: Deploy Calico Networking&#xA;&#xA;Apply the Calico manifest to configure networking:&#xA;&#xA;kubectl apply -f calico.yaml&#xA;&#xA;Verify installation:&#xA;&#xA;kubectl get -f calico.yaml&#xA;kubectl get po -n kube-system&#xA;&#xA;If all pods are running successfully, your network setup is complete.&#xA;&#xA;---&#xA;Step 6: Add Worker Nodes to the Cluster&#xA;&#xA;To join worker nodes, first retrieve the join command from the control plane:&#xA;&#xA;kubeadm token create --print-join-command&#xA;&#xA;Run the command outputted on each worker node:&#xA;&#xA;kubeadm join 192.168.0.200:6443 --token your-token \&#xA;    --discovery-token-ca-cert-hash sha256:your-ca-cert-hash&#xA;&#xA;Important Notes:&#xA;kubeadm tokens expire after 24 hours. If the token expires, use kubeadm token create --print-join-command to generate a new one.&#xA;The ca-cert-hash and token values are unique to your cluster. Do not reuse sample values from tutorials.&#xA;&#xA;Once the nodes have joined, verify the cluster status:&#xA;&#xA;kubectl get nodes -o wide&#xA;&#xA;---&#xA;Troubleshooting&#xA;&#xA;Common Issues:&#xA;Node Not Ready:&#xA;      kubectl get nodes&#xA;   journalctl -xeu kubelet&#xA;      Check the logs for errors and ensure that kubelet is running properly.&#xA;&#xA;Pod Stuck in Pending State:&#xA;      kubectl describe pod pod-name -n kube-system&#xA;   kubectl logs pod-name -n kube-system&#xA;      Look for networking issues or missing node components.&#xA;&#xA;Control Plane Inaccessible:&#xA;   Ensure the control-plane-endpoint is resolvable.&#xA;   Check that the API server is running: kubectl cluster-info.&#xA;&#xA;---&#xA;Security Considerations&#xA;&#xA;Do not expose the Kubernetes API server to the internet without proper authentication and firewall rules.&#xA;Use RBAC (Role-Based Access Control) to limit access to cluster resources.&#xA;Implement TLS encryption for secure communication between components.&#xA;Regularly update your cluster and monitor for security vulnerabilities.&#xA;&#xA;---&#xA;Next Steps&#xA;&#xA;Now that your cluster is up and running, here are some recommended next steps:&#xA;&#xA;Deploy a sample application:&#xA;      kubectl create deployment nginx --image=nginx&#xA;   kubectl expose deployment nginx --type=NodePort --port=80&#xA;   Install Kubernetes Dashboard to monitor your cluster visually.&#xA;Explore Kubernetes Concepts like Deployments, Services, and RBAC.&#xA;&#xA;🚀 Congratulations! Your Kubernetes cluster with Calico networking is ready for use!&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Kubernetes is the backbone of modern container orchestration, powering everything from small projects to enterprise-scale applications.</p>

<p>Whether you&#39;re a beginner or a seasoned engineer, understanding how to set up a Kubernetes cluster is a fundamental skill. In this tutorial, we&#39;ll take a hands-on approach using Kubeadm, one of the most popular tools for bootstrapping a Kubernetes cluster.</p>

<blockquote><p>⚠️ WARNING: Single Control Plane = Single Point of Failure!
🚨 A cluster with only one control plane is not highly available. If the control plane node goes offline, the entire cluster becomes unmanageable—no scheduling, no updates, no kubectl commands. This setup is fine for learning and testing but not recommended for production.</p></blockquote>

<p>In our next tutorial, we will add more control planes to achieve high availability and a production-ready cluster.</p>

<p>This tutorial is an extension of <a href="https://www.youtube.com/watch?v=ASZCy5LDWvw&amp;list=PLnKy4XevqUM8fWHuvpcgHwA8tmmvd5WLZ" rel="nofollow">Drew&#39;s Playlist</a>. Check it out for more background on Kubernetes setup and best practices.</p>

<hr>

<h2 id="introduction" id="introduction">Introduction</h2>

<p>Kubernetes is a platform that helps you run and manage containers (like Docker) at scale across multiple machines. It handles scheduling, networking, scaling, and fault tolerance for applications.</p>

<p>Kubeadm is a powerful tool that simplifies Kubernetes cluster setup. It provides best-practice defaults while ensuring a secure and production-ready environment. In this guide, we will walk through setting up a Kubernetes cluster using Kubeadm, discuss networking options, security considerations, common pitfalls, and next steps for deploying workloads.</p>

<p>There are multiple ways to set up a Kubernetes cluster. While Minikube and kind are great for local development and testing, Kubeadm is a more stable and production-ready option for setting up real multi-node clusters. If you&#39;re looking for alternative approaches, you can check out tutorials on <a href="https://minikube.sigs.k8s.io/docs/start/?arch=%2Fwindows%2Fx86-64%2Fstable%2F.exe+download" rel="nofollow">Minikube</a> and <a href="https://kind.sigs.k8s.io/docs/user/quick-start/" rel="nofollow">kind</a></p>

<hr>

<h2 id="scene-1-pre-requisites" id="scene-1-pre-requisites">SCENE 1: Pre-requisites</h2>
<ul><li><strong>Note:</strong> With VirtualBox&#39;s help for these tutorials, there&#39;s no need to install them on physical servers. In terms of just setting things up, unless you&#39;re using managed servers, this should get you going!</li></ul>

<p><strong>1.Setting up the VM</strong></p>

<p>We&#39;re going to start with one control plane node and three worker nodes, all set up using this Ubuntu tutorial. The only difference between this setup and the tutorial is that we are not using RAID and do not have a swap partition. Instead, each VM consists of just a root partition and a boot partition.</p>

<p>🔴 Ubuntu Version: This guide uses Ubuntu 22.04 LTS, ensuring compatibility with the latest Kubernetes versions. Using different versions may lead to unexpected issues.</p>

<p>🔴 Recommended VM Resources:
For a smooth Kubernetes setup, each node should have at least:
– 2 CPUs
– 2 GB RAM (4 GB recommended for better performance)
– 10 GB of disk space</p>

<p><img src="https://cdn.gadfly.ai/vaishali/images/VM_k8s_nodes.png" alt="VM_k8s_nodes"></p>

<p>Before proceeding, check the official Kubernetes Docs for the latest prerequisites.</p>

<p><strong>2. Time to Play with the Terminal 🖥️</strong></p>

<p>We’re working with four nodes in this setup (Since the author of this blog has ADHD, multitasking is a must!) To manage all four nodes efficiently, we’ll use TMUX to synchronize our terminals, allowing us to run the same commands across multiple machines simultaneously.</p>

<p>After following this tutorial, your terminal should look something like this:</p>

<p><img src="https://cdn.gadfly.ai/vaishali/images/TMUX_multitask_screens.png" alt="TMUX Screen"></p>

<p>All the nodes are synchronized by panes, making it easier to execute commands across all four machines.</p>

<p>🔴 Follow this TMUX Tutorial: <a href="https://youtu.be/JUQfi3JYGjY?si=SN831akRnup0PeuF" rel="nofollow">Link to TMUX tutorial</a></p>

<p>🔹 Alternative Tools: TMUX is powerful, but it has a learning curve. If you’re new to terminal multiplexing, you might prefer:</p>

<p><a href="https://www.youtube.com/watch?v=HomIzLB-HBc" rel="nofollow">GNU Screen</a> (Simpler, built-in on many systems)</p>

<p><a href="https://www.youtube.com/watch?v=byPKe7-mNvk" rel="nofollow">Byobu</a> (User-friendly wrapper around TMUX &amp; Screen)</p>

<p>🧭 TMUX Navigation Tips:</p>

<p>Split panes vertically → Ctrl + B, then %
Split panes horizontally → Ctrl + B, then “
Detach from a session → Ctrl + B, then D
Reattach to a session → Run tmux attach</p>

<p>Now that we’re set up, let&#39;s move on to configuring KubeADM !!</p>

<p><strong>3.Looking at KubeADM doc</strong></p>

<p>Before proceeding, make sure your system meets all the necessary requirements by checking out the official Kubeadm installation guide:</p>

<p>🔴 <a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/" rel="nofollow">Kubeadm Installation Docs</a></p>

<p><img src="https://cdn.gadfly.ai/vaishali/images/Install_Kubeadm_doc.png" alt="KubeADM_docs"></p>

<p>Going through these steps will help us efficiently install all required dependencies. Let&#39;s get started!</p>

<hr>

<h2 id="scene-2-installing-a-container-runtime-containerd" id="scene-2-installing-a-container-runtime-containerd">SCENE 2: Installing a Container Runtime (ContainerD)</h2>

<p>One thing that&#39;s gonna be common in every tutorial and installations is pre-requisites, which will appear again and again and again specially when we&#39;re setting up Kubernetes cluster. So bare with me coz I&#39;m dropping another pre-req but it&#39;s important to look out for any.</p>

<p><strong>Step 1: Install and Configure Prerequisites for containerD</strong></p>

<p>Before we install Kubernetes, we need to configure our system properly.</p>

<h3 id="enable-ipv4-packet-forwarding" id="enable-ipv4-packet-forwarding"><strong>Enable IPv4 Packet Forwarding</strong></h3>

<p>Kubernetes requires packet forwarding to allow network traffic between pods across different nodes. Without it, pods on one node won&#39;t be able to communicate with pods on another node.</p>

<p>Run the following command to enable IPv4 forwarding:</p>

<pre><code># sysctl params required by setup, params persist across reboots
cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
</code></pre>

<p>🔴 Paste it into your terminal:
<img src="https://cdn.gadfly.ai/vaishali/images/Terminal1.png" alt="Terminal_1"></p>

<p>Now, apply the changes without rebooting:</p>

<pre><code># Apply sysctl params without reboot
sudo sysctl --system
</code></pre>

<p>To verify if IPv4 forwarding is enabled, run:</p>

<pre><code>sysctl net.ipv4.ip_forward
</code></pre>

<p>It should return <code>1</code>, indicating that forwarding is active.</p>

<p><strong>Disable Swap (Why It&#39;s Necessary)</strong></p>

<p><strong>Kubernetes requires swap to be disabled</strong> because the Kubernetes scheduler relies on precise memory allocation. If swap is enabled, the system may overcommit memory, causing pods to crash unexpectedly.</p>

<p>To disable swap, open the <code>fstab</code> file:</p>

<pre><code>sudo vim /etc/fstab
</code></pre>

<p>Find the line containing <code>/swap.img</code> and comment it out by adding # at the beginning:</p>

<pre><code>(in shell)
#/swap.img
</code></pre>

<p>Then, run:</p>

<pre><code>sudo swapoff -a
</code></pre>

<p>To confirm that swap is disabled, check the memory usage:</p>

<pre><code>free -h
</code></pre>

<p>You should see <code>0</code> in the swap column:</p>

<p><img src="https://cdn.gadfly.ai/vaishali/images/terminal2.png" alt=""></p>

<p>Now, your system is ready for Kubeadm! 🍭 Skipping these steps may cause Kubeadm setup failures, so make sure they are properly configured.</p>

<hr>

<p><strong>Step 2: Install ContainerD</strong></p>

<p>I am bad at pointing out locations in real life—LOL. But I’ll try my best to guide you through this documentation step-by-step. Kindly bear with me, and don’t come for me in the comments! 😂</p>

<h2 id="where-to-go" id="where-to-go">Where to Go</h2>
<ol><li><strong>Head over to</strong>: <a href="https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd" rel="nofollow">ContainerD Documentation</a></li>
<li><strong>Check out</strong>: <a href="https://github.com/containerd/containerd/blob/main/docs/getting-started.md" rel="nofollow">Getting Started with ContainerD (GitHub Repo)</a></li>
<li><strong>Find the Latest Release</strong>: <a href="https://github.com/containerd/containerd/releases" rel="nofollow">ContainerD Releases</a></li>
<li><strong>Scroll down</strong> to find the <strong>Assets</strong> section.</li>
<li><strong>Choose the right binary</strong> for your machine.</li>
<li><strong>DO NOT</strong> double-click! Instead, <strong>copy the link address</strong> of the release—we’ll use it in the terminal.</li></ol>

<p>Phew! 😮‍💨 Now, let&#39;s get into it.</p>

<h2 id="download-extract-containerd" id="download-extract-containerd">Download &amp; Extract ContainerD</h2>

<h3 id="1-set-version-detect-architecture-recommended" id="1-set-version-detect-architecture-recommended">1. Set Version &amp; Detect Architecture (Recommended)</h3>

<p>Using environment variables makes future updates easier. Run the following commands:</p>

<pre><code>export CONTAINERD_VERSION=$(curl -s https://api.github.com/repos/containerd/containerd/releases/latest | grep -oP &#39;&#34;tag_name&#34;: &#34;v\K[^&#34;]+&#39;)
export ARCH=$(uname -m)
if [ &#34;$ARCH&#34; == &#34;x86_64&#34; ]; then ARCH=&#34;amd64&#34;; fi
</code></pre>

<h3 id="2-download-containerd" id="2-download-containerd">2. Download ContainerD</h3>

<pre><code>wget https://github.com/containerd/containerd/releases/download/v$CONTAINERD_VERSION/containerd-$CONTAINERD_VERSION-linux-$ARCH.tar.gz
</code></pre>

<h3 id="3-extract-to-usr-local" id="3-extract-to-usr-local">3. Extract to <code>/usr/local</code></h3>

<pre><code>sudo su  # Switch to root user (optional)
tar Cxzvf /usr/local containerd-$CONTAINERD_VERSION-linux-$ARCH.tar.gz
</code></pre>

<hr>

<h2 id="common-faq" id="common-faq">Common FAQ 🧐</h2>

<h3 id="enable-containerd-as-a-systemd-service" id="enable-containerd-as-a-systemd-service">Enable ContainerD as a Systemd Service</h3>

<p>If you plan to start <strong>ContainerD via systemd</strong>, download the service unit file and enable it:</p>
<ol><li><p><strong>Download the service file</strong>:</p>

<pre><code class="language-bash">wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -O /usr/lib/systemd/system/containerd.service
</code></pre></li>

<li><p><strong>Reload systemd &amp; enable the service</strong>:</p>

<pre><code class="language-bash">systemctl daemon-reload
systemctl enable --now containerd
</code></pre></li>

<li><p><strong>Check ContainerD status</strong>:</p>

<pre><code class="language-bash">systemctl status containerd
</code></pre></li></ol>

<p>If everything is running fine, you’re all set! 🚀</p>

<p>Using <strong>version variables &amp; architecture detection</strong> ensures users download the correct binary and keeps this guide easy to update. Hope this helps! 💡</p>

<hr>

<h2 id="step-3-install-runc" id="step-3-install-runc"><strong>Step 3: Install runc</strong></h2>

<h3 id="what-is-runc-and-why-is-it-needed" id="what-is-runc-and-why-is-it-needed"><strong>What is runc and Why is it Needed?</strong></h3>

<p><code>runc</code> is a <strong>lightweight command-line tool</strong> used to spawn and run containers on Linux systems according to the <strong>Open Container Initiative (OCI) specification</strong>. It is the <strong>underlying runtime</strong> that most container engines, like Docker and containerd, rely on to execute containers.</p>

<h4 id="why-is-runc-needed" id="why-is-runc-needed"><strong>Why is runc Needed?</strong></h4>
<ul><li><strong>Low-level container runtime:</strong> <code>runc</code> sets up the container’s environment, including namespaces and cgroups, and starts the process inside the container.<br></li>
<li><strong>Standardized and OCI-Compliant:</strong> It follows the <strong>OCI runtime spec</strong>, making it compatible across different container orchestration systems.<br></li>
<li><strong>Used by Higher-Level Container Runtimes:</strong> Tools like <strong>containerd</strong> and <strong>CRI-O</strong> use <code>runc</code> to actually start and manage container processes.<br></li>
<li><strong>Security &amp; Isolation:</strong> It ensures proper container isolation using Linux security features.<br></li></ul>

<h4 id="how-containerd-uses-runc" id="how-containerd-uses-runc"><strong>How containerd Uses runc</strong></h4>
<ul><li><code>containerd</code> does not run containers directly; instead, it <strong>delegates</strong> container execution to <code>runc</code>.<br></li>
<li><code>runc</code> handles the low-level execution, while containerd focuses on container lifecycle management.<br></li></ul>

<blockquote><p><strong>If containerd is the brain, runc is the hands that actually do the work of running containers.</strong> 🚀</p></blockquote>

<hr>

<h3 id="install-runc" id="install-runc"><strong>Install runc</strong></h3>
<ol><li><p><strong>Download the right version</strong> (for my machine, it&#39;s <code>runc.amd64</code>):</p>
<ul><li>Copy the link address and run:<br>
<code>wget https://github.com/opencontainers/runc/releases/download/v1.2.6/runc.amd64
</code></li></ul></li>

<li><p><strong>Install runc</strong>:</p>

<pre><code>install -m 755 runc.amd64 /usr/local/sbin/runc
</code></pre></li>

<li><p><strong>Verify Installation</strong>:</p>
<ul><li>Run <code>runc</code> to check if it&#39;s installed:<br>
<code>runc
</code></li></ul></li></ol>

<hr>

<h2 id="step-4-install-cni-plugin" id="step-4-install-cni-plugin"><strong>Step 4: Install CNI Plugin</strong></h2>

<h3 id="why-do-we-need-cni-plugins" id="why-do-we-need-cni-plugins"><strong>Why Do We Need CNI Plugins?</strong></h3>

<p>The <strong>Container Network Interface (CNI)</strong> plugin is required to <strong>enable networking</strong> for containers. It ensures that containers can communicate with each other and with external networks.</p>
<ol><li><p><strong>Create a directory for CNI plugins</strong>:</p>

<pre><code>mkdir -p /opt/cni/bin
</code></pre></li>

<li><p><strong>Download the CNI plugin</strong>:</p>
<ul><li>Head over to <a href="https://github.com/containerd/containerd/blob/main/docs/getting-started.md" rel="nofollow">Getting Started with containerd</a> &gt; Step 3 Install CNI Plugin<br></li>
<li>Go to <a href="https://github.com/containernetworking/plugins/releases" rel="nofollow">Releases</a> and choose the right plugin.<br></li>
<li>For Linux (amd64), run:<br>
<code>
wget https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-amd64-v1.6.2.tgz
</code></li></ul></li>

<li><p><strong>Extract and Install CNI Plugins</strong>:</p>

<pre><code>tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.6.2.tgz
</code></pre></li></ol>

<blockquote><p><strong>Ensure everything is updated to the latest version.</strong></p></blockquote>

<hr>

<h3 id="step-5-exploring-config-toml-in-containerd" id="step-5-exploring-config-toml-in-containerd"><strong>Step 5: Exploring <code>config.toml</code> in ContainerD</strong></h3>

<h4 id="what-is-config-toml" id="what-is-config-toml"><strong>What is <code>config.toml</code>?</strong></h4>

<p>The <code>config.toml</code> file is ContainerD’s main configuration file. It controls networking, container isolation, storage, logging, and security settings.</p>

<h4 id="generate-edit-config-toml" id="generate-edit-config-toml"><strong>Generate &amp; Edit <code>config.toml</code></strong></h4>

<p>🍭 <strong>Create the directory (if not exists):</strong></p>

<pre><code>mkdir -p /etc/containerd
</code></pre>

<p>🍭 <strong>Generate the default config:</strong></p>

<pre><code>containerd config default &gt; /etc/containerd/config.toml
</code></pre>

<p>🍭 <strong>Open the file to edit:</strong></p>

<pre><code>vim /etc/containerd/config.toml
</code></pre>

<p>If you see TOML configurations, you’re in the right place! Exit the file (<code>:q!</code>) and proceed to Step 6. 🚀</p>

<h3 id="step-6-configuring-the-systemd-cgroup-driver" id="step-6-configuring-the-systemd-cgroup-driver"><strong>Step 6: Configuring the <code>systemd</code> cgroup driver</strong></h3>

<p>To use the <code>systemd</code> cgroup driver in <code>/etc/containerd/config.toml</code> with <code>runc</code>, head over to the <code>config.toml</code> file.</p>
<ol><li><p><strong>Open the config file:</strong></p>

<pre><code>vim /etc/containerd/config.toml
</code></pre></li>

<li><p><strong>Locate the following section:</strong></p>

<pre><code class="language-toml">[plugins.&#34;io.containerd.grpc.v1.cri&#34;.containerd.runtimes.runc]
</code></pre></li>

<li><p><strong>Inside this section, find:</strong></p>

<pre><code class="language-toml">[plugins.&#34;io.containerd.grpc.v1.cri&#34;.containerd.runtimes.runc.options]
</code></pre></li>

<li><p><strong>Modify the <code>SystemdCgroup</code> setting:</strong><br>
Change:</p>

<pre><code class="language-toml">SystemdCgroup = false
</code></pre>

<p>to:</p>

<pre><code class="language-toml">SystemdCgroup = true
</code></pre></li></ol>

<p>💡 <strong>Why is this important?</strong><br>
– The <strong>kubelet and containerd must use the same cgroup driver</strong> for stability.
– <code>systemd</code> is the default on most Linux distros and integrates better with <code>cgroup v2</code>.</p>

<hr>

<h3 id="check-for-cgroup-v2-support" id="check-for-cgroup-v2-support"><strong>Check for <code>cgroup v2</code> Support</strong></h3>

<p>Run:</p>

<pre><code>stat -fc %T /sys/fs/cgroup
</code></pre>

<p>If the output is <code>cgroup2fs</code>, then <code>cgroup v2</code> is enabled.</p>

<p>💡 <strong>Why prefer <code>cgroup v2</code>?</strong><br>
– Simplifies resource management.
– Avoids inconsistencies seen in <code>cgroup v1</code>.
– Recommended by Kubernetes for better stability.</p>

<hr>

<h3 id="restart-verify-containerd" id="restart-verify-containerd"><strong>Restart &amp; Verify ContainerD</strong></h3>
<ol><li><p><strong>Restart containerd:</strong></p>

<pre><code>systemctl restart containerd
</code></pre></li>

<li><p><strong>Check the status:</strong></p>

<pre><code>systemctl status containerd
</code></pre>

<p>If everything is <strong>active ✅</strong>, congrats! 🎉 Your setup is good to go!</p></li></ol>

<hr>

<h3 id="scene-3-install-kubeadm-kubelet-and-kubectl" id="scene-3-install-kubeadm-kubelet-and-kubectl"><strong>SCENE 3: Install Kubeadm, Kubelet, and Kubectl</strong></h3>

<p>We&#39;ve warmed up—now it&#39;s time for the real workout! But don&#39;t worry, it&#39;s pretty straightforward. (At least, I hope so! 😆)</p>

<hr>

<h3 id="step-1-update-apt-and-install-dependencies" id="step-1-update-apt-and-install-dependencies"><strong>Step 1: Update <code>apt</code> and Install Dependencies</strong></h3>

<p>First, let&#39;s update the package list to ensure we&#39;re working with the latest versions:</p>

<pre><code>apt-get update
</code></pre>

<p>Now, install the necessary dependencies:</p>

<pre><code># apt-transport-https may be a dummy package; if so, you can skip that package
apt-get install -y apt-transport-https ca-certificates curl gpg
</code></pre>

<p>📝 <strong>Why these packages?</strong><br>
– <code>apt-transport-https</code> → Allows APT to fetch packages over HTTPS (secure connections).<br>
– <code>ca-certificates</code> → Ensures system trusts SSL certificates.<br>
– <code>curl</code> → Fetches files from the internet.<br>
– <code>gpg</code> → Verifies package signatures for security.</p>

<hr>

<h3 id="step-2-download-the-google-cloud-public-signing-key" id="step-2-download-the-google-cloud-public-signing-key"><strong>Step 2: Download the Google Cloud Public Signing Key</strong></h3>

<p>Run the following command to add Google&#39;s public signing key:</p>

<pre><code># If the directory `/etc/apt/keyrings` does not exist, it should be created before the curl command, read the note below.
# sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
</code></pre>

<hr>

<h3 id="step-3-add-the-kubernetes-apt-repository" id="step-3-add-the-kubernetes-apt-repository"><strong>Step 3: Add the Kubernetes APT Repository</strong></h3>

<p>Run the following command to add the Kubernetes repository:</p>

<pre><code># This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list
echo &#39;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /&#39; | sudo tee /etc/apt/sources.list.d/kubernetes.list
</code></pre>

<blockquote><p>Along with following this tutorial, I would highly suggest opening up the <a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/" rel="nofollow">official documentation</a> alongside to get the right version at all times.</p></blockquote>

<p>📝 <strong>Step 3: Install Tools and Pin Versions</strong></p>

<p>🔹 <strong>Why is pinning important?</strong><br>
If you don’t pin versions, an upgrade to a newer Kubernetes version might happen during a system update, which can break compatibility in a multi-node setup. <strong>AND THIS IS BAD AND SAD AND AWFUL FOR CYBERSECURITY—DO NOT DO IT OR I WILL BE DISAPPOINTED.</strong> 😤 But fear not, this is what <strong>Dependabot</strong> was made for! Look into it—even though it’s not totally secure either, but at least it’s a start.</p>

<p>🔹 <strong>Quick Definitions:</strong><br>
– <code>kubelet</code> → The agent that manages containers on each node.<br>
– <code>kubeadm</code> → Initializes the cluster.<br>
– <code>kubectl</code> → The CLI you use to interact with the cluster.</p>

<hr>

<h3 id="step-4-update-the-apt-package-index-install-kubernetes-components-and-pin-their-versions" id="step-4-update-the-apt-package-index-install-kubernetes-components-and-pin-their-versions"><strong>Step 4: Update the <code>apt</code> Package Index, Install Kubernetes Components, and Pin Their Versions</strong></h3>

<p>Run the following commands to install <code>kubelet</code>, <code>kubeadm</code>, and <code>kubectl</code>:</p>

<pre><code>apt-get update
</code></pre>

<pre><code>apt-get install -y kubelet kubeadm kubectl
</code></pre>

<pre><code>apt-mark hold kubelet kubeadm kubectl
</code></pre>

<p>📝 <strong>Component Responsibilities:</strong><br>
– <strong>Kubelet</strong> → Ensures that containers are running in a node.<br>
– <strong>Kubeadm</strong> → Responsible for initializing a Kubernetes cluster.<br>
– <strong>Kubectl</strong> → CLI tool for interacting with the Kubernetes API.</p>

<p>Next up: Verifying installation and configuring your cluster! 🚀</p>

<hr>

<h3 id="scene-4-creating-a-cluster" id="scene-4-creating-a-cluster"><strong>SCENE 4: Creating a Cluster</strong></h3>

<h4 id="initializing-your-control-plane-mode" id="initializing-your-control-plane-mode"><strong>Initializing Your Control Plane Mode</strong></h4>

<p>We want to set up a <strong>Highly Available (HA) control plane</strong> and add worker nodes. To achieve this, we need to define a <code>--control-plane-endpoint</code>. If we were in the cloud, we could use a load balancer&#39;s IP. But <strong>we are setting up on our machines</strong>, so we need an alternative.</p>

<blockquote><p><strong>Enter KubeVIP! ✨</strong></p></blockquote>

<p><strong>KubeVIP</strong> allows us to use a <strong>virtual IP address (VIP)</strong> for the control plane without requiring an external cloud-based load balancer.</p>

<h4 id="step-1-preparing-for-ha-control-plane-with-kubevip" id="step-1-preparing-for-ha-control-plane-with-kubevip"><strong>Step 1: Preparing for HA Control Plane with KubeVIP</strong></h4>

<p>🔹 <strong>Why HA Matters?</strong>
– The <strong>control plane</strong> schedules workloads and manages cluster state.
– In an HA setup, multiple control planes prevent downtime in case one fails.</p>

<p>Follow the <a href="https://kube-vip.io/docs/installation/static/" rel="nofollow">KubeVIP documentation</a> to get started.</p>

<p>📝 <strong>If you&#39;re NOT worried about HA (which you should be), you can skip this. But be warned: without HA, you&#39;ll need to configure external load balancers later!</strong></p>

<h4 id="step-2-generating-a-kubevip-manifest" id="step-2-generating-a-kubevip-manifest"><strong>Step 2: Generating a KubeVIP Manifest</strong></h4>

<p>First, find a free IP in your network:</p>

<pre><code>ip a
</code></pre>

<p>Set a <strong>VIP address</strong> (ensure it doesn&#39;t conflict with existing IPs):</p>

<pre><code>export VIP=xxx.xxx.x.xxx
</code></pre>

<p>Set the <strong>interface name</strong> where KubeVIP will announce the VIP:</p>

<pre><code>export INTERFACE=interface_name
</code></pre>

<p>Install <code>jq</code> to parse the latest KubeVIP release:</p>

<pre><code>apt install jq -y
</code></pre>

<p>Fetch the latest version:</p>

<pre><code>KVVERSION=$(curl -sL https://api.github.com/repos/kube-vip/kube-vip/releases | jq -r &#34;.[0].name&#34;)
</code></pre>

<blockquote><p><strong>Note:</strong> KubeVIP setup <strong>only applies to the control plane node</strong>, not worker nodes.</p></blockquote>

<h4 id="step-3-creating-the-kubevip-manifest" id="step-3-creating-the-kubevip-manifest"><strong>Step 3: Creating the KubeVIP Manifest</strong></h4>

<p>For <strong>containerd</strong>, run:</p>

<pre><code>alias kube-vip=&#34;ctr image pull ghcr.io/kube-vip/kube-vip:$KVVERSION; ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip /kube-vip&#34;
</code></pre>

<p>Ensure the manifest directory exists:</p>

<pre><code>mkdir -p /etc/kubernetes/manifests
</code></pre>

<p>Generate the manifest:</p>

<pre><code class="language-sh">kube-vip manifest pod \
    --interface $INTERFACE \
    --address $VIP \
    --controlplane \
    --services \
    --arp \
    --leaderElection | tee /etc/kubernetes/manifests/kube-vip.yaml
</code></pre>

<h4 id="step-4-initializing-the-control-plane" id="step-4-initializing-the-control-plane"><strong>Step 4: Initializing the Control Plane</strong></h4>

<p>Run:</p>

<pre><code>kubeadm init --control-plane-endpoint $VIP
</code></pre>

<p>🔹 <strong>Breaking Down <code>kubeadm init</code> Flags:</strong>
– <code>--control-plane-endpoint</code> → Defines the shared endpoint for all control planes.
– <code>--pod-network-cidr</code> → Defines the pod network range (must match CNI plugin requirements).</p>

<h4 id="step-5-setting-up-kubeconfig" id="step-5-setting-up-kubeconfig"><strong>Step 5: Setting Up <code>kubeconfig</code></strong></h4>

<p>🔹 <strong>What is kubeconfig?</strong>
This file stores credentials and cluster info so <code>kubectl</code> can securely communicate with the cluster.</p>

<pre><code class="language-sh">mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
</code></pre>

<p>🔴 Next: Adding Worker Nodes and Deploying a Pod Network!</p>

<hr>

<h1 id="kubernetes-cluster-setup-with-calico-networking" id="kubernetes-cluster-setup-with-calico-networking">Kubernetes Cluster Setup with Calico Networking</h1>

<h2 id="step-1-choose-a-pod-network-add-on" id="step-1-choose-a-pod-network-add-on">Step 1: Choose a Pod Network Add-on</h2>

<p>A Pod Network Add-on enables communication between pods in your cluster. We are using <strong>Calico</strong>, a widely adopted choice due to its scalability and support for network policies.</p>

<p>Some network providers require you to pass specific flags to <code>kubeadm init</code>. For Calico, you must set <code>--pod-network-cidr</code> appropriately.</p>

<h3 id="download-the-calico-networking-manifest" id="download-the-calico-networking-manifest">Download the Calico Networking Manifest</h3>

<p>Run the following command to download the Calico manifest:</p>

<pre><code>curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.29.2/manifests/calico.yaml
</code></pre>

<hr>

<h2 id="step-2-understanding-pod-cidr" id="step-2-understanding-pod-cidr">Step 2: Understanding Pod CIDR</h2>

<p><strong>CIDR (Classless Inter-Domain Routing)</strong> assigns IP addresses in a structured manner. Each pod in your cluster requires a unique IP from the CIDR block. This ensures that pods can communicate across nodes within the cluster.</p>

<hr>

<h2 id="step-3-initialize-the-kubernetes-cluster" id="step-3-initialize-the-kubernetes-cluster">Step 3: Initialize the Kubernetes Cluster</h2>

<p>To initialize your cluster, use the <code>kubeadm init</code> command. Set the <code>--pod-network-cidr</code> to match Calico&#39;s expected value:</p>

<pre><code>kubeadm init --pod-network-cidr=192.168.0.0/16 --control-plane-endpoint=192.168.0.200 --apiserver-advertise-address=192.168.0.201
</code></pre>

<p><strong>Note:</strong>
The <code>control-plane-endpoint</code> should ideally be a DNS name or a static IP that won’t change. If the IP changes later, the cluster may become inaccessible due to certificate mismatches.</p>

<p>Once the initialization is complete, configure your kubeconfig:</p>

<pre><code>export KUBECONFIG=/etc/kubernetes/admin.conf
</code></pre>

<hr>

<h2 id="step-4-set-up-a-dns-record" id="step-4-set-up-a-dns-record">Step 4: Set Up a DNS Record</h2>

<p>To ensure smooth communication within the cluster, add a DNS record in <code>/etc/hosts</code>:</p>

<pre><code>vim /etc/hosts
</code></pre>

<p>Add the following entry:</p>

<pre><code>192.168.0.200 kube-api-server
</code></pre>

<p>Test the connection:</p>

<pre><code>apt install iputils-ping
ping kube-api-server
</code></pre>

<hr>

<h2 id="step-5-deploy-calico-networking" id="step-5-deploy-calico-networking">Step 5: Deploy Calico Networking</h2>

<p>Apply the Calico manifest to configure networking:</p>

<pre><code>kubectl apply -f calico.yaml
</code></pre>

<p>Verify installation:</p>

<pre><code>kubectl get -f calico.yaml
kubectl get po -n kube-system
</code></pre>

<p>If all pods are running successfully, your network setup is complete.</p>

<hr>

<h2 id="step-6-add-worker-nodes-to-the-cluster" id="step-6-add-worker-nodes-to-the-cluster">Step 6: Add Worker Nodes to the Cluster</h2>

<p>To join worker nodes, first retrieve the join command from the control plane:</p>

<pre><code>kubeadm token create --print-join-command
</code></pre>

<p>Run the command outputted on each worker node:</p>

<pre><code>kubeadm join 192.168.0.200:6443 --token &lt;your-token&gt; \
    --discovery-token-ca-cert-hash sha256:&lt;your-ca-cert-hash&gt;
</code></pre>

<h3 id="important-notes" id="important-notes">Important Notes:</h3>
<ul><li><code>kubeadm</code> tokens expire after <strong>24 hours</strong>. If the token expires, use <code>kubeadm token create --print-join-command</code> to generate a new one.</li>
<li>The <code>ca-cert-hash</code> and <code>token</code> values are <strong>unique</strong> to your cluster. Do not reuse sample values from tutorials.</li></ul>

<p>Once the nodes have joined, verify the cluster status:</p>

<pre><code>kubectl get nodes -o wide
</code></pre>

<hr>

<h2 id="troubleshooting" id="troubleshooting">Troubleshooting</h2>

<h3 id="common-issues" id="common-issues">Common Issues:</h3>
<ol><li><p><strong>Node Not Ready:</strong></p>

<pre><code>kubectl get nodes
journalctl -xeu kubelet
</code></pre>

<p>Check the logs for errors and ensure that <code>kubelet</code> is running properly.</p></li>

<li><p><strong>Pod Stuck in Pending State:</strong></p>

<pre><code>kubectl describe pod &lt;pod-name&gt; -n kube-system
kubectl logs &lt;pod-name&gt; -n kube-system
</code></pre>

<p>Look for networking issues or missing node components.</p></li>

<li><p><strong>Control Plane Inaccessible:</strong></p>
<ul><li>Ensure the <code>control-plane-endpoint</code> is resolvable.</li>
<li>Check that the API server is running: <code>kubectl cluster-info</code>.</li></ul></li></ol>

<hr>

<h2 id="security-considerations" id="security-considerations">Security Considerations</h2>
<ul><li><strong>Do not expose the Kubernetes API server to the internet</strong> without proper authentication and firewall rules.</li>
<li>Use <strong>RBAC (Role-Based Access Control)</strong> to limit access to cluster resources.</li>
<li>Implement <strong>TLS encryption</strong> for secure communication between components.</li>
<li>Regularly update your cluster and monitor for security vulnerabilities.</li></ul>

<hr>

<h2 id="next-steps" id="next-steps">Next Steps</h2>

<p>Now that your cluster is up and running, here are some recommended next steps:</p>
<ol><li><strong>Deploy a sample application:</strong>
<code>
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --type=NodePort --port=80
</code></li>
<li><strong>Install Kubernetes Dashboard</strong> to monitor your cluster visually.</li>
<li><strong>Explore Kubernetes Concepts</strong> like Deployments, Services, and RBAC.</li></ol>

<p>🚀 Congratulations! Your Kubernetes cluster with Calico networking is ready for use!</p>
]]></content:encoded>
      <author>Vaishali Rawat</author>
      <guid>https://blog.gadfly.ai/read/a/fov3xvrk8r</guid>
      <pubDate>Mon, 24 Mar 2025 06:28:44 +0000</pubDate>
    </item>
    <item>
      <title>What is Kubernetes? A Beginner’s Guide</title>
      <link>https://blog.gadfly.ai/vaishali/what-is-kubernetes-061m</link>
      <description>&lt;![CDATA[Introduction&#xA;&#xA;Kubernetes, often abbreviated as K8s, is an open-source platform for automating the deployment, scaling, and management of containerized applications. Originally developed by Google and now maintained by the Cloud Native Computing Foundation (CNCF), Kubernetes has become the industry standard for managing applications in cloud environments.&#xA;&#xA;If you&#39;re new to Kubernetes, don’t worry! This guide will break it down in a simple way so that even beginners can understand what it is, why it&#39;s important, and how to get started.&#xA;&#xA;Why Do We Need Kubernetes?&#xA;&#xA;Before Kubernetes, managing applications across multiple servers was a complicated task. Organizations used to run applications on physical servers or virtual machines, leading to inefficiencies such as:&#xA;&#xA;Over-provisioning (allocating more resources than necessary to handle peak loads)&#xA;&#xA;Under-utilization (wasting resources when demand is low)&#xA;&#xA;Scaling challenges (difficulty in managing application traffic efficiently)&#xA;&#xA;Kubernetes solves these problems by providing an intelligent orchestration system that efficiently manages workloads, ensuring applications run reliably across different environments.&#xA;&#xA;Key Features of Kubernetes&#xA;&#xA;1. Container Orchestration&#xA;&#xA;Kubernetes automates the deployment and scaling of containers, ensuring applications run efficiently without manual intervention.&#xA;&#xA;2. Automatic Scaling&#xA;&#xA;It automatically increases or decreases application instances based on demand, optimizing resource usage.&#xA;&#xA;3. Self-Healing&#xA;&#xA;If a container crashes, Kubernetes automatically restarts or replaces it to maintain application availability.&#xA;&#xA;4. Service Discovery &amp; Load Balancing&#xA;&#xA;Kubernetes efficiently distributes network traffic between application instances to ensure smooth performance.&#xA;&#xA;5. Rolling Updates &amp; Rollbacks&#xA;&#xA;Kubernetes enables seamless application updates without downtime and allows reverting to previous versions if needed.&#xA;&#xA;Core Components of Kubernetes&#xA;&#xA;To understand how Kubernetes works, let&#39;s break down its key components:&#xA;&#xA;Cluster&#xA;&#xA;A Kubernetes cluster consists of multiple machines (nodes) that work together to run applications.&#xA;&#xA;Nodes&#xA;&#xA;Master Node: Controls the cluster and manages workload scheduling.&#xA;&#xA;Worker Nodes: Run the application workloads.&#xA;&#xA;Pods&#xA;&#xA;The smallest deployable unit in Kubernetes. A pod contains one or more containers that share resources.&#xA;&#xA;Deployments&#xA;&#xA;Manage application rollouts, ensuring reliable and automated updates.&#xA;&#xA;Services&#xA;&#xA;Provide a stable networking endpoint, allowing different parts of an application to communicate with each other.&#xA;&#xA;ConfigMaps &amp; Secrets&#xA;&#xA;Help manage configuration data and sensitive information securely.&#xA;&#xA;Getting Started with Kubernetes&#xA;&#xA;Step 1: Install Kubernetes Locally&#xA;&#xA;For local development, you can use tools like:&#xA;&#xA;Minikube: A lightweight Kubernetes cluster for local testing.&#xA;&#xA;Kind: Runs Kubernetes clusters inside Docker containers.&#xA;&#xA;Step 2: Deploy Your First Application&#xA;&#xA;Run a simple Nginx web server using Kubernetes with these steps:&#xA;&#xA;Create a deployment:&#xA;&#xA;kubectl create deployment nginx --image=nginx&#xA;Expose the deployment as a service:&#xA;&#xA;kubectl expose deployment nginx --type=LoadBalancer --port=80&#xA;3.Get the service details:&#xA;&#xA;kubectl get services&#xA;Access your application using the provided URL.&#xA;&#xA;Step 3: Learn Kubernetes Concepts in Depth&#xA;&#xA;To deepen your understanding, check out online courses, YouTube playlists, and hands-on labs. The Kubernetes tutorial playlist is a great place to start.&#xA;&#xA;Conclusion&#xA;&#xA;Kubernetes is a powerful tool that simplifies container management, making it easier to deploy and scale applications efficiently. Whether you’re a beginner or an experienced developer, learning Kubernetes will open doors to cloud-native development and DevOps practices.&#xA;&#xA;Start small, practice regularly, and soon you&#39;ll be managing Kubernetes like a pro!&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<h2 id="introduction" id="introduction">Introduction</h2>

<p>Kubernetes, often abbreviated as K8s, is an open-source platform for automating the deployment, scaling, and management of containerized applications. Originally developed by Google and now maintained by the Cloud Native Computing Foundation (CNCF), Kubernetes has become the industry standard for managing applications in cloud environments.</p>

<p>If you&#39;re new to Kubernetes, don’t worry! This guide will break it down in a simple way so that even beginners can understand what it is, why it&#39;s important, and how to get started.</p>

<h2 id="why-do-we-need-kubernetes" id="why-do-we-need-kubernetes">Why Do We Need Kubernetes?</h2>

<p>Before Kubernetes, managing applications across multiple servers was a complicated task. Organizations used to run applications on physical servers or virtual machines, leading to inefficiencies such as:</p>
<ul><li><p>Over-provisioning (allocating more resources than necessary to handle peak loads)</p></li>

<li><p>Under-utilization (wasting resources when demand is low)</p></li>

<li><p>Scaling challenges (difficulty in managing application traffic efficiently)</p></li></ul>

<p>Kubernetes solves these problems by providing an intelligent orchestration system that efficiently manages workloads, ensuring applications run reliably across different environments.</p>

<h2 id="key-features-of-kubernetes" id="key-features-of-kubernetes">Key Features of Kubernetes</h2>

<p><strong>1. Container Orchestration</strong></p>

<p>Kubernetes automates the deployment and scaling of containers, ensuring applications run efficiently without manual intervention.</p>

<p><strong>2. Automatic Scaling</strong></p>

<p>It automatically increases or decreases application instances based on demand, optimizing resource usage.</p>

<p><strong>3. Self-Healing</strong></p>

<p>If a container crashes, Kubernetes automatically restarts or replaces it to maintain application availability.</p>

<p><strong>4. Service Discovery &amp; Load Balancing</strong></p>

<p>Kubernetes efficiently distributes network traffic between application instances to ensure smooth performance.</p>

<p><strong>5. Rolling Updates &amp; Rollbacks</strong></p>

<p>Kubernetes enables seamless application updates without downtime and allows reverting to previous versions if needed.</p>

<h2 id="core-components-of-kubernetes" id="core-components-of-kubernetes">Core Components of Kubernetes</h2>

<p>To understand how Kubernetes works, let&#39;s break down its key components:</p>
<ol><li>Cluster</li></ol>

<p>A Kubernetes cluster consists of multiple machines (nodes) that work together to run applications.</p>
<ol><li>Nodes</li></ol>
<ul><li><p>Master Node: Controls the cluster and manages workload scheduling.</p></li>

<li><p>Worker Nodes: Run the application workloads.</p></li></ul>
<ol><li>Pods</li></ol>

<p>The smallest deployable unit in Kubernetes. A pod contains one or more containers that share resources.</p>
<ol><li>Deployments</li></ol>

<p>Manage application rollouts, ensuring reliable and automated updates.</p>
<ol><li>Services</li></ol>

<p>Provide a stable networking endpoint, allowing different parts of an application to communicate with each other.</p>
<ol><li>ConfigMaps &amp; Secrets</li></ol>

<p>Help manage configuration data and sensitive information securely.</p>

<h2 id="getting-started-with-kubernetes" id="getting-started-with-kubernetes">Getting Started with Kubernetes</h2>

<p><strong>Step 1: Install Kubernetes Locally</strong></p>

<p>For local development, you can use tools like:</p>
<ul><li><p>Minikube: A lightweight Kubernetes cluster for local testing.</p></li>

<li><p>Kind: Runs Kubernetes clusters inside Docker containers.</p></li></ul>

<p><strong>Step 2: Deploy Your First Application</strong></p>

<p>Run a simple Nginx web server using Kubernetes with these steps:</p>
<ol><li>Create a deployment:</li></ol>

<pre><code>kubectl create deployment nginx --image=nginx
</code></pre>
<ol><li>Expose the deployment as a service:</li></ol>

<pre><code>kubectl expose deployment nginx --type=LoadBalancer --port=80
</code></pre>

<p>3.Get the service details:</p>

<pre><code>kubectl get services
</code></pre>

<p>Access your application using the provided URL.</p>

<p>Step 3: Learn Kubernetes Concepts in Depth</p>

<p>To deepen your understanding, check out online courses, YouTube playlists, and hands-on labs. The Kubernetes tutorial playlist is a great place to start.</p>

<p>Conclusion</p>

<p>Kubernetes is a powerful tool that simplifies container management, making it easier to deploy and scale applications efficiently. Whether you’re a beginner or an experienced developer, learning Kubernetes will open doors to cloud-native development and DevOps practices.</p>

<p>Start small, practice regularly, and soon you&#39;ll be managing Kubernetes like a pro!</p>
]]></content:encoded>
      <author>Vaishali Rawat</author>
      <guid>https://blog.gadfly.ai/read/a/p9abol8kgt</guid>
      <pubDate>Mon, 03 Mar 2025 10:03:02 +0000</pubDate>
    </item>
    <item>
      <title>Fun with content #</title>
      <link>https://blog.gadfly.ai/sen/fun-with-content</link>
      <description>&lt;![CDATA[I just set up some fun nonsense today to handle images, so please enjoy this fun public domain picture of a waterfall:&#xA;&#xA;public domain picture of ChōshiFalls&#xA;&#xA;Ok, so what is going on here?&#xA;&#xA;The blog engine we use here is called writefreely, it&#39;s federated and generates the blogposts themselves from a markdown texteditor in the browser. images can be added via link syntax, so literally:&#xA;&#xA;public domain picture of ChōshiFalls&#xA;&#xA;Now we uh, just have to host the pictures somehow.&#xA;&#xA;enter the CDN &#xA;&#xA;The folks behind writefreely maintain a hosted version called write.as and if you are a Write.as Pro member you can use their snap.as service which is, you guessed it, a hosted version of their picture hosting project called snapfreely, so we just head on over to the repo and...&#xA;&#xA;A screenshot of the snapfreely repository on github, its EMPTY. &#xA;&#xA;oh.&#xA;&#xA;Well the good news is that we already have a perfectly good webserver (several actually, most of the backdoor backend services used by The Gadfly Horde&amp;trade; here at HQ use nginx as a reverse proxy, the others ARE just nginx) that we use to serve our main site gadfly.ai.&#xA;&#xA;Now we just need to put the files there somehow.&#xA;&#xA;TBQH I am a big fan of scp but I also live in the terminal, guzzle litres of pourover and read man pages voluntarily(ok that last one was a lie, but you get my point) &#xA;&#xA;but I don&#39;t always have a console at hand, or it&#39;s not always convenient and neither is giving everyone shell access to a machine so it wold actually be nice if I could upload files the same way I am writing this blogpost, with several million lines of C++ a web browser.&#xA;&#xA;The Rabbithole &#xA;this actually was one of the shallower ones I went down but still I learned a few things.&#xA;&#xA;I had 3-ish main goals, I wanted to:&#xA;&#xA;upload files thru the browser (duh)&#xA;use nginx&#xA;have authentication (for upload) &#xA;&#xA;More specifically I wanted the files in a bare dir so I could write an nginx config pointing to it as the document root, easy peasy.&#xA;&#xA;This proved difficult.&#xA;&#xA;lots of photohosty and other cdn things do cool image re-encoding stuff, but they also store things in databases and I already have enoughofthosethankyouverymuch. &#xA;&#xA;awesome-selfhosted was a good resource to look thru all this stuff, and after I sort of learned that what i wanted was a web file manager I took to the high seas.⛵&#xA;&#xA;and returned to port immediately.&#xA;&#xA;I found DAMS or &#34;Digital Asset Management Software&#34; which reeks of Enterprise&amp;trade; so I avoided it, and most of the big FTP clients were desktop based, so bit of a no-go.&#xA;&#xA;back to the awesome-selfhosted list again there were some cool small things but they either used their own server, database or both.&#xA;&#xA;The outliers though,&#xA;&#xA;sigh&#xA;&#xA;were written in PHP.&#xA;&#xA;here we go &#xA;&#xA;I actually pivoted at the last minute (hour, day, whatever).&#xA;&#xA;originally i was going to use IFM but I finally chickened out because the authentication infra was uh... barebones.&#xA;&#xA;I then somehow stumbled across filebrowser(inspired name, I know),&#xA;and actually manged to wrestle an nginx config into shape for it.&#xA;&#xA;Wound up having to install a bunch more packages (go figure) and then mess with permissions and ownership around unix sockets and phpfpm (did you know nginx runs as nginx:nginx on debian things now? what a time to be alive...) but I got it up and running behind a subdomain.&#xA;&#xA;We still have a problem tho.&#xA;&#xA;The cdn (no really) &#xA;&#xA;For my own sake (and others) I wanted to make it really easy to find and copy a link to the bare image file for use in the blog, and uh now maybe I&#39;m just not up on my nginx-fu when it comes to url rewriting (ok face it, I&#39;m not) and also not willing to deeply mess with the internals of the file browsers routes so their arent any collisons, but this means I probably can&#39;t (easily) host the filemanager interface on the same subdomain as the files themselves.&#xA; &#xA;This means I need 2 subdomains (which is fine) and to somehow have the file browser UI give me a link to that same file, but from the other subdomain. That should be straightforward at least.&#xA;&#xA;Well, the jokes on me bc i downloaded the release tarball of &#34;file manager&#34; and so I was met with a blob of post-masticated JS, this caused me to retreat into the sources where I learned...&#xA;&#xA;that &#34;file browser&#34; was just a fork of FileGator&#xA;&#xA;sigh&#xA;&#xA;I had talked myself into using &#34;file browser&#34; even though it hadn&#39;t been updated in 2 years bc it had the best auth infrastructure of anything I could find. and here&#39;s filegator that just got a new release last week&#xA;&#xA;queue the fury of me ripping up /var/www/filebrowser and installing /var/www/filegator, the good news is that all the work I put into the nginx configs was still valid, I just had to do a find-replace. it of course didn&#39;t start up and I had once again installed a release tarball. Then I did some more stuff that didn&#39;t work until I pulled down the main git branch and followed the install instructions, then it worked. Hooray!!!&#xA;&#xA;Ok now uh where was I...&#xA;&#xA;Oh yeah, cdn link.&#xA;&#xA;Now I have the source so I can dig through and look for pieces of user interface to modify.&#xA;&#xA;HERE!&#xA;&#xA; `b-dropdown-item v-if=&#34;props.row.type == &#39;file&#39; &amp;&amp; can(&#39;download&#39;)&#34; v-clipboard:copy=&#34;getDownloadLink(props.row.path)&#34; aria-role=&#34;listitem&#34;&#xA;                  b-icon icon=&#34;clipboard&#34; size=&#34;is-small&#34; / {{ lang(&#39;Copy link&#39;) }}`&#xA;&#xA;its the bit of code responsible for the &#34;copy link&#34; button from the per file dropdown menu on the filegator ux.&#xA;&#xA;The &#34;Copy Link&#34; button from the per file dropdown menu on the filegator ux&#xA;&#xA;queue some trepidation about plumbing the depths of vue.js docs and second-guessing myself about JS syntax, oh and several npm run build errors before I got something workable.&#xA;&#xA;`b-dropdown-item v-if=&#34;props.row.type == &#39;file&#39; &amp;&amp; can(&#39;download&#39;)&#34; v-clipboard:copy=&#34;&#39;https://cdn.gadfly.ai/&#39; + props.row.name&#34; aria-role=&#34;listitem&#34;&#xA;                  b-icon icon=&#34;clipboard&#34; size=&#34;is-small&#34; / {{ lang(&#39;Copy cdn link&#39;) }}`&#xA;&#xA;taa-daa!&#xA;&#xA;Now I have another button in the drop down, that says &#34;Copy cdn Link&#34; which uh, does that. There are certainly more elegant, more configurable ways of doing this that I would implement if i wanted to get this code upstreamed, but it works for now. Also, you still have to write the markdown around the link when using it on the blog. Who knows maybe I will add a &#34;Copy markdown Link&#34; button in the future.  &#xA;&#xA;until next time... EOL.]]&gt;</description>
      <content:encoded><![CDATA[<p>I just set up some <em>fun</em> nonsense today to handle images, so please enjoy this fun public domain picture of a waterfall:</p>

<p><img src="https://cdn.gadfly.ai/Ch%C5%8Dshi_Falls.jpg" alt="public domain picture of Chōshi_Falls"></p>

<p>Ok, so what is going on here?</p>

<p>The blog engine we use here is called <a href="https://writefreely.org/" rel="nofollow">writefreely</a>, it&#39;s federated and generates the blogposts themselves from a markdown texteditor in the browser. images can be added via <a href="https://howto.write.as/adding-photos" rel="nofollow">link</a> syntax, so literally:</p>

<p><code>![public domain picture of Chōshi_Falls](https://cdn.gadfly.ai/Chōshi_Falls.jpg)</code></p>

<p>Now we uh, just have to host the pictures somehow.</p>

<h1 id="enter-the-cdn" id="enter-the-cdn">enter the CDN</h1>

<p>The folks behind <a href="https://writefreely.org/" rel="nofollow">writefreely</a> maintain a hosted version called <a href="https://write.as" rel="nofollow">write.as</a> and if you are a Write.as <em>Pro</em> member you can use their <a href="https://snap.as/" rel="nofollow">snap.as</a> service which is, you guessed it, a hosted version of their picture hosting project called <a href="https://github.com/snapas/snapfreely" rel="nofollow">snapfreely</a>, so we just head on over to the repo and...</p>

<p><img src="https://cdn.gadfly.ai/snapas_repo_screenshot.png" alt="A screenshot of the snapfreely repository on github, its EMPTY. "></p>

<p>oh.</p>

<p>Well the good news is that we already have a perfectly good webserver (several actually, most of the <del>backdoor</del> backend services used by The Gadfly Horde™ here at HQ use nginx as a reverse proxy, the others ARE just nginx) that we use to serve our main site <a href="https://www.gadfly.ai" rel="nofollow">gadfly.ai</a>.</p>

<p>Now we just need to <em>put</em> the files there somehow.</p>

<p>TBQH I am a big fan of <a href="https://man.openbsd.org/scp" rel="nofollow">scp</a> but I also live in the terminal, guzzle litres of <a href="https://en.wikipedia.org/wiki/Drip_coffee#Manual_pour-over_coffee_preparation" rel="nofollow">pourover</a> and read man pages voluntarily(ok that last one was a lie, but you get my point)</p>

<p>but I don&#39;t always have a console at hand, or it&#39;s not always convenient and neither is giving everyone shell access to a machine so it wold <em>actually</em> be nice if I could upload files the same way I am writing this blogpost, with <del>several million lines of C++</del> a web browser.</p>

<h1 id="the-rabbithole" id="the-rabbithole">The Rabbithole</h1>

<p>this actually was one of the shallower ones I went down but still I learned a few things.</p>

<p>I had 3-ish main goals, I wanted to:</p>
<ol><li>upload files thru the browser (duh)</li>
<li>use nginx</li>
<li>have authentication (for upload)</li></ol>

<p>More specifically I wanted the files in a <em>bare</em> dir so I could write an nginx config pointing to it as the document root, easy peasy.</p>

<p>This proved difficult.</p>

<p>lots of photohosty and other cdn things do cool image re-encoding stuff, but they also store things in databases and I already have enoughofthosethankyouverymuch.</p>

<p><a href="https://github.com/awesome-selfhosted/awesome-selfhosted" rel="nofollow">awesome-selfhosted</a> was a good resource to look thru all this stuff, and after I sort of learned that what i wanted was a web <em>file manager</em> I took to the high seas.⛵</p>

<p>and returned to port immediately.</p>

<p>I found DAMS or “Digital Asset Management Software” which reeks of Enterprise™ so I avoided it, and most of the big FTP clients were desktop based, so bit of a no-go.</p>

<p>back to the awesome-selfhosted list again there were some cool small things but they either used their own server, database or both.</p>

<p>The outliers though,</p>

<p><em>sigh</em></p>

<p>were written in PHP.</p>

<h1 id="here-we-go" id="here-we-go">here we go</h1>

<p>I actually pivoted at the last minute (hour, day, whatever).</p>

<p>originally i was going to use <a href="https://github.com/misterunknown/ifm" rel="nofollow">IFM</a> but I finally chickened out because the authentication infra was uh... <a href="https://github.com/misterunknown/ifm/wiki/Authentication" rel="nofollow">barebones</a>.</p>

<p>I then some<em>how</em> stumbled across <a href="https://filebrowser.linuxforphp.net/" rel="nofollow">filebrowser</a>(inspired name, I <em>know</em>),
and actually manged to wrestle an nginx config into shape for it.</p>

<p>Wound up having to install a bunch more packages (go figure) and then mess with permissions and ownership around unix sockets and php_fpm (did you know nginx runs as nginx:nginx on debian things now? what a time to be alive...) but I got it up and running behind a subdomain.</p>

<p>We still have a problem tho.</p>

<h1 id="the-cdn-no-really" id="the-cdn-no-really">The cdn (no <em>really</em>)</h1>

<p>For my own sake (and others) I wanted to make it <em>really</em> easy to find and copy a link to the bare image file for use in the blog, and uh now maybe I&#39;m just not up on my nginx-fu when it comes to url rewriting (ok face it, I&#39;m <em>not</em>) and also not willing to deeply mess with the internals of the file browsers routes so their arent any collisons, but this means I probably can&#39;t (easily) host the filemanager interface on the same subdomain as the files themselves.</p>

<p>This means I need 2 subdomains (which is fine) and to <em>somehow</em> have the file browser UI give me a link to that <em>same</em> file, but from the <em>other</em> subdomain. That <em>should</em> be straightforward at least.</p>

<p>Well, the jokes on me bc i downloaded the release tarball of “file manager” and so I was met with a blob of post-masticated JS, this caused me to retreat into the sources where I learned...</p>

<p>that “file browser” was just a fork of <a href="https://github.com/filegator/filegator" rel="nofollow">FileGator</a></p>

<p><em>sigh</em></p>

<p>I had talked myself into using “file browser” even though it hadn&#39;t been updated in 2 years bc it had the best auth infrastructure of anything I could find. and here&#39;s filegator that just got a new release <em>last week</em></p>

<p>queue the fury of me ripping up /var/www/filebrowser and installing /var/www/filegator, the good news is that all the work I put into the nginx configs was still valid, I just had to do a <em>find-replace</em>. it of course <em>didn&#39;t start up</em> and I had <em>once again</em> installed a release tarball. Then I did some more stuff that didn&#39;t work until I pulled down the main git branch and followed the install instructions, then it worked. Hooray!!!</p>

<p>Ok now uh where was I...</p>

<p>Oh yeah, cdn link.</p>

<p>Now I have the source so I can dig through and look for pieces of user interface to modify.</p>

<p>HERE!</p>

<p> <code>&lt;b-dropdown-item v-if=&#34;props.row.type == &#39;file&#39; &amp;&amp; can(&#39;download&#39;)&#34; v-clipboard:copy=&#34;getDownloadLink(props.row.path)&#34; aria-role=&#34;listitem&#34;&gt;
                  &lt;b-icon icon=&#34;clipboard&#34; size=&#34;is-small&#34; /&gt; {{ lang(&#39;Copy link&#39;) }}</code></p>

<p>its the bit of code responsible for the “copy link” button from the per file dropdown menu on the filegator ux.</p>

<p><img src="https://cdn.gadfly.ai/filegator_copy_link_button.png" alt="The &#34;Copy Link&#34; button from the per file dropdown menu on the filegator ux"></p>

<p>queue some trepidation about plumbing the depths of vue.js docs and second-guessing myself about JS syntax, oh and <em>several</em> <code>npm run build</code> errors before I got something workable.</p>

<p><code>&lt;b-dropdown-item v-if=&#34;props.row.type == &#39;file&#39; &amp;&amp; can(&#39;download&#39;)&#34; v-clipboard:copy=&#34;&#39;https://cdn.gadfly.ai/&#39; + props.row.name&#34; aria-role=&#34;listitem&#34;&gt;
                  &lt;b-icon icon=&#34;clipboard&#34; size=&#34;is-small&#34; /&gt; {{ lang(&#39;Copy cdn link&#39;) }}</code></p>

<p>taa-daa!</p>

<p>Now I have <em>another</em> button in the drop down, that says “Copy cdn Link” which uh, does that. There are certainly more elegant, more configurable ways of doing this that I would implement if i wanted to get this code upstreamed, but it works for now. Also, you still have to write the markdown <em>around</em> the link when using it on the blog. Who knows maybe I will add a “Copy markdown Link” button in the future.</p>

<p>until next time... EOL.</p>
]]></content:encoded>
      <author>sen</author>
      <guid>https://blog.gadfly.ai/read/a/5h9o5v2szt</guid>
      <pubDate>Sat, 01 Mar 2025 01:52:02 +0000</pubDate>
    </item>
    <item>
      <title>The Backend #</title>
      <link>https://blog.gadfly.ai/sen/the-backend</link>
      <description>&lt;![CDATA[lots of fun and exciting things have been happening over here at gadfly ai HQ, we have added a bunch of services, both public and private facing.]]&gt;</description>
      <content:encoded><![CDATA[<p>lots of fun and exciting things have been happening over here at gadfly ai HQ, we have added a <em>bunch</em> of services, both public and private facing.</p>
]]></content:encoded>
      <author>sen</author>
      <guid>https://blog.gadfly.ai/read/a/ovp3zucosh</guid>
      <pubDate>Fri, 28 Feb 2025 04:55:31 +0000</pubDate>
    </item>
    <item>
      <title>A new beginning #</title>
      <link>https://blog.gadfly.ai/sen/a-new-beginning</link>
      <description>&lt;![CDATA[not sure If I have ever had a blog before but this seems like as good a time as any I suppose]]&gt;</description>
      <content:encoded><![CDATA[<p>not sure If I have ever had a blog before but this seems like as good a time as any I suppose</p>
]]></content:encoded>
      <author>sen</author>
      <guid>https://blog.gadfly.ai/read/a/26k2ga4ins</guid>
      <pubDate>Fri, 28 Feb 2025 03:28:09 +0000</pubDate>
    </item>
    <item>
      <title>hello world!</title>
      <link>https://blog.gadfly.ai/sen/hello-world</link>
      <description>&lt;![CDATA[hello world!]]&gt;</description>
      <content:encoded><![CDATA[<p>hello world!</p>
]]></content:encoded>
      <author>sen</author>
      <guid>https://blog.gadfly.ai/read/a/rcdkbhsv0t</guid>
      <pubDate>Fri, 28 Feb 2025 02:46:40 +0000</pubDate>
    </item>
  </channel>
</rss>