/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_keysfile 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