Kubernetes at Home on Raspberry Pi 4, Part 1

3 x Raspberry Pi 4

I mostly followed/was inspired by this tutorial but with some tweak/fix to recent(Sep 2019) software versions. Also this is a pure Linux walk-through as I don’t use a Mac.

I planned to build a home Kubernetes(k8s) cluster and migrate home servers including the one this blog is running on to the k8s cluster, for a long time. But the Raspberry Pi 2 has 1GB of memory and is not quite appealing for any practical purpose. (I know, I know, we used to run a computer with mega bytes of memory…) When Raspberry Pi 4 with 4GB of memory is available, I believed I need to wait no more.

The 3 Pi 4s I got are from eBay, surprisingly this time the offer in eBay was better than Amazon! I didn’t think I need the cases for the Pis, because I heard the Pi 4 is more powerful and can get hot comparing to previous ones.

I chose Raspbian for now, as it supports all devices in the Pi 4. Ubuntu Server could be a better choice but it only support up to Pi 3. And as a command line veteran I use this line to flash the MicroSD cards:

# if you copy and paste you may need to verify the file name and the card reader device in your computer, ie. I'm not responsible for anything :)
$ unzip -p 2019-07-10-raspbian-buster-lite.zip |sudo dd bs=4M of=/dev/mmcblk0

To enable SSH access at first boot, create an empty file called ssh in /boot partition:

# once again, this path could be different on your system.
$ sudo touch /run/media/raynix/boot/ssh

After this, use the sync command to make sure everything has been written to the card. Then you can pull the MicroSD card out of your card reader slot and put it into the Pi 4.

Something required before powering up the Pi 4:

  • Pi 4 connected to the router/switch with Ethernet cable
  • 5V power supply with USB-C connector
  • DHCP enabled in LAN

After the Pi 4 is powered up, the green LED should flash a bit before you can see raspberrypi.localdomain online( the localdomain part is usually the default for some routers, but can be something else depending on your router setup). Then you should be able to:

# default user is pi, and password is raspberry
$ ssh [email protected]
$ cat <<EOF |sudo tee -a /etc/dhcpcd.conf
interface eth0
static ip_address=192.168.1.200/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
EOF

This will set the Pi 4 to a static IP address after reboot. Repeat this step for each Pi 4 but obviously they should have different IPs, eg. master has 192.168.1.200 and node1 has 192.168.1.201, etc.

TBC.

Use FZF to Speed Up KUBECTL

FZF is general purpose option selector for Linux command line. By default fzf is an super-upgrade for the ctrl-r command history finder, but it is helpful for almost any scenario where you need to pick 1 item from a list.

Here’s a tiny use case where I use fzf to select a kubernetes namespace and set it as the default namespace, just to save the effort to type --namespace flag everytime when I need to do something not in the default namespace.

#!/bin/bash
function kns() {
cns=$(kubectl get namespaces --output=jsonpath='{range .items[*]}{.metadata.name}{"\n"}'|fzf)
kubectl config set-context --current --namespace=$cns
}

Let me explain the script above step by step:

  1. kubectl get namespaces will list all namespaces currently accessible
  2. --output=jsonpath='{range .items[*]}{.metadata.name}{"\n"}' will only output the name of namespaces and 1 in each line
  3. |fzf to pipe the result from step 2 to fzf, a list will popup and becomes interactive for the user to choose one.
  4. kubectl config set-context --current --namespace=$cns will set the chosen namespace from step 3 as the default namespace

This is very convenient when there are not too many namespaces and when you need to work in different namespaces randomly. 🙂

AWS Lambda with Single CloudFormation Template

This is just a quick snippet of CloudFormation template to deploy a Python 3.7 Lambda function embedded in the template. The source code inside ZipFile can contain up to 4KB.

# CloudFormation template
AWSTemplateFormatVersion: 2010-09-09
Resources:
LambdaCode:
Type: AWS::Lambda::Function
DependsOn:
- LambdaRole
- LambdaPolicy
Properties:
Code:
ZipFile: |
import boto3
def handler(event, context):
...
Role: !GetAtt LambdaRole.Arn
Runtime: python3.7
Handler: index.handler

Note: the depending LambdaRole is not included, but can be found easily here .

🙂

Golang and Docker Multi-Stage Build

I have noticed a common pattern amonst some new utilities such as kubectl, kops and terraform: There’s only 1 single executable file to install, and by ‘install’ it can be put anywhere as long as it’s in $PATH. This was before I learned some Golang but it’s easy to find out that the reason behind this pattern is that they are all written in Go.

And in the containers’ realm, the new-ish multi-stage build steps of Docker released in 2017 are super beneficial to Golang containers. A TL;DR example looks like:

  1. use a 1GB Debian container with all Golang tools and build dependencies to build the Golang executable( FROM ... AS in the sample ).
  2. put the executable into a tiny run-time container such as Alpine Linux, resulting in a < 20MB container image(depending on the size of the app obviously) ( COPY --FROM in the sample )

A multi-stage ‘hello world’ Dockerfile looks like:

FROM golang:1.12.5-alpine3.9 as builder
ENV GO111MODULE=on
RUN apk update --no-cache && \
apk add git
WORKDIR /app
ADD ./ /app
RUN go build -o golang-test .

FROM alpine:3.9.4
WORKDIR /app
RUN addgroup -g 2000 golang && \
adduser -D -u 2000 -G golang golang
USER golang
COPY --from=builder /app/golang-test .
CMD ["/app/golang-test"]
EXPOSE 8000

Note: To be able to use the multi-stage feature, the Docker version has to be > 17.06.

🙂