<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ville-Veikko Kovalainen</title>
    <description>The latest articles on Forem by Ville-Veikko Kovalainen (@weeee).</description>
    <link>https://forem.com/weeee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F172797%2F5f6b0c30-cbbe-47e7-af00-e5f8c9503e4e.png</url>
      <title>Forem: Ville-Veikko Kovalainen</title>
      <link>https://forem.com/weeee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/weeee"/>
    <language>en</language>
    <item>
      <title>Raspberry Pi cluster part 1: The Boot</title>
      <dc:creator>Ville-Veikko Kovalainen</dc:creator>
      <pubDate>Mon, 23 Sep 2019 18:26:29 +0000</pubDate>
      <link>https://forem.com/weeee/raspberry-pi-cluster-part-1-the-boot-2fe5</link>
      <guid>https://forem.com/weeee/raspberry-pi-cluster-part-1-the-boot-2fe5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is the first part in an &lt;code&gt;n+1&lt;/code&gt; part series of my journey on bringing up a Raspberry Pi cluster for hosting a few services I use at my home network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;4x &lt;a href="https://www.amazon.de/gp/product/B07BDR5PDW/" rel="noopener noreferrer"&gt;Raspberry Pi 3 Model B+&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;6-port USB power supply (&lt;a href="https://www.amazon.de/gp/product/B00PTLSH9G/" rel="noopener noreferrer"&gt;Anker PowerPort 6&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Bunch of USB cables (&lt;a href="https://www.amazon.de/gp/product/B016BEVNK4/" rel="noopener noreferrer"&gt;Anker Power Line Micro USB Cable&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Bunch of short Ethernet cables&lt;/li&gt;
&lt;li&gt;Desktop PC running the required servers&lt;/li&gt;
&lt;li&gt;(Ubiquiti USG for DHCP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The desktop PC will be replaced by a dedicated NAS in the near future. Look for an update to this post soonish with the details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F25kmvgppeo33ajp8rgd7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F25kmvgppeo33ajp8rgd7.jpg" alt="Cluster Pi setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Software
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-04-09/" rel="noopener noreferrer"&gt;Raspbian Stretch Lite 2019-04-09&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Public &lt;strong&gt;third party&lt;/strong&gt; Docker images

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/pghalliday/tftp" rel="noopener noreferrer"&gt;pghalliday/tftp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/r/itsthenetwork/nfs-server-alpine/" rel="noopener noreferrer"&gt;itsthenetwork/nfs-server-alpine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Some helper &lt;code&gt;bash&lt;/code&gt; scripts and &lt;code&gt;docker-compose&lt;/code&gt; glue can be found in my repo below&lt;/li&gt;

&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/weeeedev" rel="noopener noreferrer"&gt;
        weeeedev
      &lt;/a&gt; / &lt;a href="https://github.com/weeeedev/raspberry-cluster-pxe" rel="noopener noreferrer"&gt;
        raspberry-cluster-pxe
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Raspberry Pi Cluster network boot over TFTP/NFS4
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Raspberry Cluster with PXE boot over TFTP/NFS4&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Basic example of how to boot a bunch of Raspberry Pis over network with no SD cards.&lt;/p&gt;
&lt;p&gt;This is written with Raspberry Pi 3 Model B+ and Raspbian Stretch in mind.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; existing DHCP server with ability to set the required TFTP boot options is expected.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Prerequisites&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;(Linux) PC running Docker with &lt;code&gt;docker-compose&lt;/code&gt; installed&lt;/li&gt;
&lt;li&gt;Raspbian Stretch Lite zip file&lt;/li&gt;
&lt;li&gt;Serial numbers for the Pis in use&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Docker setup&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;A Dockerized TFTP and NFS4 server are used to provide boot/root files for the Pis. The example here uses public, 3rd party images from Docker Hub. Please direct any issues with those to the respective maintainers.&lt;/p&gt;
&lt;p&gt;Setting up the contents for TFTP/NFS:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;./setup_docker_content.sh path-to-raspbian-zip hostname serial nfsip tftpip&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This will extract, and modify, the contents from Raspbian image to dir the included &lt;code&gt;docker-compose.yaml&lt;/code&gt; expects. &lt;code&gt;hostname&lt;/code&gt; here will be the Pis hostname, whereas &lt;code&gt;nfsip&lt;/code&gt; and &lt;code&gt;tftpip&lt;/code&gt;…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/weeeedev/raspberry-cluster-pxe" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Raspbian Stretch is used here since I could not get the latest (2019-07-10) Buster to boot from NFS. I'll give that another try on a later date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Setting this up is, in the end, fairly straight-forward once you know what you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pi setup
&lt;/h3&gt;

&lt;p&gt;Since I'm running using 3B+, no setup for the Pi itself is needed. Network boot is enabled by default (see &lt;sup id="fnref1"&gt;1&lt;/sup&gt;).&lt;/p&gt;

&lt;p&gt;The only thing you might be interested in from the Pi itself is its serial number which we'll need later in the TFTP boot part. There are couple ways to find it: you can either boot the Pi up using a regular Raspbian SD card and do &lt;code&gt;cat /proc/cpuinfo&lt;/code&gt; or, if you can capture the network traffic from the TFTP boot, you also can find it from the TFTP requests the Pi makes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extracting content from Raspbian image
&lt;/h3&gt;

&lt;p&gt;You can get the required file content from an installed Raspbian SD card like the official guide &lt;sup id="fnref1"&gt;1&lt;/sup&gt; does. &lt;strong&gt;Or&lt;/strong&gt; you can simply extract them from the Raspbian image. That's what we'll go with here.&lt;/p&gt;

&lt;p&gt;The official Raspbian image has two partitions &lt;code&gt;/boot&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt; respectively. We can mount these and copy the files directly without having to write them to an SD card:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LOOP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;losetup &lt;span class="nt"&gt;--show&lt;/span&gt; &lt;span class="nt"&gt;-fP&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_IMG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;raspbian_root,raspbian_boot&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOOP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;p1 raspbian_boot/
&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOOP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;p2 raspbian_root/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here you can &lt;code&gt;rsync&lt;/code&gt; these to where ever you will serve them over TFTP and NFS. You may want to do couple modifications to the content, I'll cover those in the NFS chapter.&lt;/p&gt;

&lt;h3&gt;
  
  
  TFTP
&lt;/h3&gt;

&lt;p&gt;TFTP is needed for loading the kernel and whatnot. This content usually lives in &lt;code&gt;/boot/&lt;/code&gt; on the SD card. For TFTP boot, you simply need to serve these files over TFTP. You can see how to get the files in the previous chapter.&lt;/p&gt;

&lt;p&gt;Once you have the files, you'll need to setup a directory structure so that the TFTP root has &lt;code&gt;bootcode.bin&lt;/code&gt; and all other files are a directory that's name is the &lt;strong&gt;serial number&lt;/strong&gt;. The Raspbian boot will first fetch &lt;code&gt;bootcode.bin&lt;/code&gt; and then automatically try for the next files from &lt;code&gt;&amp;lt;serial&amp;gt;/start.elf&lt;/code&gt; and so on.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; It will default back to looking for the files from the TFTP root if they are not found from &lt;code&gt;&amp;lt;serial&amp;gt;/*&lt;/code&gt;, but since the aim is to boot &lt;strong&gt;multiple&lt;/strong&gt; Pis, and to also reserve the possibility to run different OS versions, it's better to have them in separate dirs for all Pis (hint: you could always symlink/bind mount these if you don't want to multiply the files).&lt;/p&gt;

&lt;p&gt;Couple mods needed for the files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable first boot SSH
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_BOOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set up the &lt;code&gt;cmdline.txt&lt;/code&gt; to look for &lt;code&gt;rootfs&lt;/code&gt; from NFS
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NFS_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_HOSTNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/,vers=4.1,proto=tcp,port=2049 rw ip=dhcp elevator=deadline rootwait plymouth.ignore-serial-consoles"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_BOOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/cmdline.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Couple things to note here on the &lt;code&gt;cmdline.txt&lt;/code&gt; mods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NFSv4 is used, as opposed to the usual NFSv3 other guides use

&lt;ul&gt;
&lt;li&gt;NFS protocol and port are explicitly set, eliminating the need for &lt;code&gt;portmap&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Version &lt;code&gt;4.1&lt;/code&gt; is explicitly set, you can also set this to simply &lt;code&gt;4&lt;/code&gt; if that's what you have&lt;/li&gt;
&lt;li&gt;The share with the root &lt;code&gt;/&lt;/code&gt; file system content expected on the NFS server is named after the hostname. See the NFS chapter for notes on this.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  NFS
&lt;/h3&gt;

&lt;p&gt;We'll use NFS to serve all file system content completely replacing the usual SD card. As opposed to most other guides I found, NFSv4 will be used instead of NFSv3. I won't go into details on what v4 does better than v3, other than that it's newer, and it eliminates the need for running &lt;code&gt;portmap&lt;/code&gt;, &lt;code&gt;rcpbind&lt;/code&gt; and all other useless crap on the server side.&lt;/p&gt;

&lt;p&gt;NFS will be used to serve &lt;strong&gt;both&lt;/strong&gt; &lt;code&gt;/boot&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt; content for the Pis. You can get away without actually mounting &lt;code&gt;/boot&lt;/code&gt;, but that will make OS upgrades (and first boot SSH) trickier. You can get the files from the Raspbian image as shown previously, simply &lt;code&gt;rsync&lt;/code&gt; them to separate subdirs under your NFS share.&lt;/p&gt;

&lt;p&gt;The Docker example in my repo &lt;sup id="fnref3"&gt;3&lt;/sup&gt; uses a third party Docker image for the NFS part which basically creates a share with the following params:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/share *(rw,fsid=0,sync,no_subtree_check,no_auth_nlm,insecure,no_root_squash)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing here is to use &lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;rw&lt;/code&gt;. Having &lt;code&gt;no_root_squash&lt;/code&gt; will allow files made by &lt;code&gt;root&lt;/code&gt; to remain owned by &lt;code&gt;root&lt;/code&gt; (as opposed to the usual NFS way of mapping &lt;code&gt;root&lt;/code&gt; to &lt;code&gt;nobody&lt;/code&gt; or equivalent). This, together with &lt;code&gt;no_auth_nlm&lt;/code&gt;, &lt;code&gt;insecure&lt;/code&gt;, and letting anyone mount this (&lt;code&gt;*&lt;/code&gt;) make this very &lt;strong&gt;insecure&lt;/strong&gt; NFS setup, but at this point this is only meant for proof-of-concept level setup. I'll address this when updating to a "real" NFS server.&lt;/p&gt;

&lt;p&gt;As hinted earlier, couple mods to the files are suggested before booting up the Pis for the first time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set hostname
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_HOSTNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_ROOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/etc/hostname
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"s/raspberrypi/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_HOSTNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/g"&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_ROOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Remove SD card mounts
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"/mmcblk/d"&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_ROOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/etc/fstab
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"/PARTUUID/d"&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_ROOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;/boot&lt;/code&gt; mount
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NFS_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:tftp/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_SERIAL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; /boot nfs4 defaults,nofail,noatime 0 2"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RASPBIAN_ROOT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DHCP
&lt;/h3&gt;

&lt;p&gt;To tie all this together, DHCP needs to point our Pis to the TFTP server for boot. The official tutorial &lt;sup id="fnref1"&gt;1&lt;/sup&gt; shows how to do this using &lt;code&gt;dnsmasq&lt;/code&gt; but since I already had a working DHCP via Unifi USG, I simply used that to point towards my TFTP:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhj4x4swyawn55r82on65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhj4x4swyawn55r82on65.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unless you have a readily available DHCP server already that can do this, &lt;code&gt;dnsmasq&lt;/code&gt; is not a bad choice and works perfect for completely closed off clusters, too.&lt;/p&gt;

&lt;p&gt;Apart from pointing the DHCP clients towards the TFTP server, I chose to setup static leases for them just to make life in future a little simpler if I end up in a situation where DNS names won't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  It lives!
&lt;/h2&gt;

&lt;p&gt;After all this setup is done, all that is left to do is power up the Pis with Ethernet connect and watch the blinkenlights:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fh9bti7ym7oqgysz00ln7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fh9bti7ym7oqgysz00ln7.gif" alt="Blinkenlights"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the Pis starting to query TFTP right away:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fu2ylp9zdd887vsi4qm8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fu2ylp9zdd887vsi4qm8q.png" alt="Pi TFTP boot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After loading the kernel and other basics, they start to load the NFS content and take couple minutes to boot up completely after which they are connectable. Throw in some SSH keys and you're ready to roll:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;01..04&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;ssh pi@rpi-k8s-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.weeee.lan &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done
&lt;/span&gt;Linux rpi-k8s-01 4.14.98-v7+ &lt;span class="c"&gt;#1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux&lt;/span&gt;
Linux rpi-k8s-02 4.14.98-v7+ &lt;span class="c"&gt;#1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux&lt;/span&gt;
Linux rpi-k8s-03 4.14.98-v7+ &lt;span class="c"&gt;#1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux&lt;/span&gt;
Linux rpi-k8s-04 4.14.98-v7+ &lt;span class="c"&gt;#1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hostnames might give away the purpose of this cluster. Up next, sprinkle in some Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Random notes and gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official guides &lt;sup id="fnref2"&gt;2&lt;/sup&gt; mention DHCP Vendor-Option Option 43 with string &lt;code&gt;Raspberry Pi Boot&lt;/code&gt; as a requirement for the TFTP boot. I didn't need to set this.&lt;/li&gt;
&lt;li&gt;Booting without setting up a static lease on the DHCP server side resulted in having two IPs for the Raspbian once booted up. This may be an issue in my own DHCP server, though.&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net_tutorial.md" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net_tutorial.md&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net.md" rel="noopener noreferrer"&gt;https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net.md&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://github.com/weeeedev/raspberry-cluster-pxe" rel="noopener noreferrer"&gt;https://github.com/weeeedev/raspberry-cluster-pxe&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>showdev</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
