/dev/sdX
in the example above – will be overwritten
and cannot be restored afterwards.
blog
simple raspi for k8s setup
Since I've been setting up a lot of kubernetes installations on raspberry pi machines and still doing a lot of these I've been looking for a speedier way to get the hardware up and running. There's already the raspberry pi imager which helps a lot, but I've been looking for something which automizes even more steps. I ended up putting a small shell script together which allows writing the image in a single step and have everything in place to jump right in using ssh and start with the k8s setup itself.
For every machine there's a separate configuration file. That means the following setup steps can be implemented:
- Enable ssh login with the correct
authorized_keys
file in place, so there's no need to login with a predefined password first in order to install the list of authorized keys. - The machine now already has a static ip configuration in place.
- Allow ssh login for root, since for my kind of setup this is the only one I need. This saves all the steps required to allow root login.
- After installing all
cgroup
-like options which are required for kubernetes have been already written tocmdline.txt
.
Here's an example for this kind of configuration:
raspi-01.cfg
RASPI_HOSTNAME="raspi-01" RASPI_DOMAINNAME="example.org" RASPI_NETWORK_CONFIG="auto eth0 iface eth0 inet static address 192.0.2.10/24 gateway 192.0.2.1 dns-nameservers 192.0.2.20 192.0.2.21 dns-search example.org" RASPI_AUTHORIZED_KEYS="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIh/Lm3JzOYqNL2dHWAOJnTjuJhdx76VtkuPS26/OYiI my-public-ssh-key"
Invocation of the script requires a raspbian OS image, the device name for the raspi's sd card, usb stick or similar, and the name of the configuration file:
./simple-raspi-setup.sh \ 2024-07-04-raspios-bookworm-arm64-lite.img \ /dev/sdX \ raspi-01.cfg
The simple-raspi-setup.sh
looks like this:
simple-raspi-setup.sh
#!/bin/bash set -e # Exit immediately if a command exits with a non-zero status. set -o pipefail # Prevents errors in a pipeline from being masked. set -u # Fail when using undefined values. if [[ $# -ne 3 ]] ; then echo "Syntax: ${0} <image> <sdcard-devicename> <cfg-file>" exit 1 fi ISO_IMAGE=${1} SDCARD_DEVICE=${2} CFG_FILENAME=${3} source ${CFG_FILENAME} CMDLINE_OPTIONS="cgroup_memory=1 cgroup_enable=memory noswap" if [[ $(grep ${SDCARD_DEVICE} /proc/mounts | wc -l) -gt 0 ]]; then echo "${SDCARD_DEVICE} is already mounted." exit 1 fi function log() { echo [`date "+%Y-%m-%d %H:%M:%S"`] $* } log "Starting raspi setup." log "Hostname: \"${RASPI_HOSTNAME}\"." log "Network config: \"${RASPI_NETWORK_CONFIG}\"." # the directory of the script SCRIPT_DIR=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) # the temp directory used # omit the -p parameter to create a temporal directory in the default location WORK_DIR=$(mktemp -d) log "Working dir is \"${WORK_DIR}\"." # check if tmp dir was created if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then echo "Couldn't create tmp dir \"${WORK_DIR}\”." exit 1 fi function cleanup { log "Unmounting \"${WORK_DIR}\"." umount ${WORK_DIR} rm -rf "${WORK_DIR}" echo log "Deleted temp working directory \"${WORK_DIR}\"." } # register the cleanup function to be called on the EXIT signal trap cleanup EXIT log "Writing image to \"${SDCARD_DEVICE}\"." dd if=${ISO_IMAGE} of=${SDCARD_DEVICE} status=progress bs=1048576 log "Mounting \"${SDCARD_DEVICE}1\" to \"${WORK_DIR}\"." mount ${SDCARD_DEVICE}1 ${WORK_DIR} log "Creating empty \"ssh\" file for headless login." touch ${WORK_DIR}/ssh log "Appending options \"${CMDLINE_OPTIONS}\" to \"cmdline.txt\"." CURRENT_CMDLINE=$(cat ${WORK_DIR}/cmdline.txt) echo "${CURRENT_CMDLINE} ${CMDLINE_OPTIONS}" > ${WORK_DIR}/cmdline.txt log "Unmounting \"${WORK_DIR}\"." umount ${WORK_DIR} log "Mounting \"${SDCARD_DEVICE}2\" to \"${WORK_DIR}\"." mount ${SDCARD_DEVICE}2 ${WORK_DIR} log "Writing network config." echo "${RASPI_NETWORK_CONFIG}" >> ${WORK_DIR}/etc/network/interfaces log "Setting hostname to \"${RASPI_HOSTNAME}\"." echo "${RASPI_HOSTNAME}" > ${WORK_DIR}/etc/hostname sed -i "s/^127.0.1.1.*/127.0.1.1 ${RASPI_HOSTNAME}.${RASPI_DOMAINNAME} ${RASPI_HOSTNAME}/" ${WORK_DIR}/etc/hosts log "Enabling root ssh login." sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' ${WORK_DIR}/etc/ssh/sshd_config log "Writing root's \".ssh/authorized_key\" file." mkdir -p ${WORK_DIR}/root/.ssh chmod 700 ${WORK_DIR}/root/.ssh echo ${RASPI_AUTHORIZED_KEYS} > ${WORK_DIR}/root/.ssh/authorized_keys # This will remove the login banner saying: # "Please note that SSH may not work until a valid user has been set up." log "Removing default raspi ssh valid user warning." rm ${WORK_DIR}/etc/ssh/sshd_config.d/rename_user.conf sync log "Done." exit 0