3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Raspberry PiでおうちKubernetesを構築した際のメモ書き

Last updated at Posted at 2025-12-22

はじめに

この記事は 「ラクス Advent Calendar 2025」 23日目の記事です。
趣味で「おうちKubernetes」の構築をRaspberry Piでしたので、その時の構築手順をまとめました。
多くの方が同じような記事を上げているので何番煎じなのか分かりませんが、
自分用の作業メモとして残しておきたいと思います。

元になったスクラップ(構築時の生ログ寄りメモ)
https://zenn.dev/imamoto_hikaru/scraps/384ee0ae3408cd


全体方針

  • 宅内ネットワーク上で、Raspberry Pi 複数台の Kubernetes クラスタを構築する
  • kubeadm を使って Kubernetes を構築する
  • コンテナランタイムは containerd
  • kube-vip を使って Control Plane の接続先を VIP 化(--control-plane-endpoint
  • CNI は Calico
項目 方針
OS Ubuntu Server 24.04.1 LTS
Kubernetes 構築 kubeadm
コンテナランタイム containerd + runc
Control Plane Endpoint kube-vip(static pod / ARP)で VIP を持たせる
Pod CIDR 10.1.0.0/16
CNI Calico
リモートアクセス Cloudflare Tunnel(Zero Trust)で SSH(ポート開放しない)

0. Raspberry PiでUbuntuの初期セットアップを実施

0-1. 事前準備

  • microSD に OS を焼く
  • microSD と SSD を Raspberry Pi に接続

0-2. Raspberry Pi Imager で Ubuntu Server を用意

Raspberry Pi Imager で Ubuntu Server 24.04.1 LTS を選択して焼きました。

  • Timezone: Asia/Tokyo
  • Keyboard: us
  • User/PW 設定
  • SSH 有効化

0-3. (SSD起動向け)config.txt の編集

Raspberry Pi OS 側で system-bootconfig.txt に以下を追記しました(環境により pciex1 / nvme が変わる想定)。

dtparam=pciex1(またはdtparam=nvme)
dtparam=pciex1_gen=3

1. Ubuntu 側の基本設定

1-1. ホスト名変更

hostnamectl set-hostname rasp001

(ノードごとに rasp001, rasp002…のように振ると後が楽です)

1-2. net-tools 入れる(任意)

sudo apt update
sudo apt install -y net-tools

1-3. (必要なら)日本語化

sudo apt -y install language-pack-ja-base language-pack-ja
localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
source /etc/default/locale
echo $LANG

2. 宅内ネットワークの整理(静的IP)

まずは 現在のIP/サブネット/インタフェース を確認。

ip a
route -n

2-1. netplan で静的IPを設定(Ethernet利用)

/etc/netplan/51-manual.yamlパーミッション600 で作成し、以下のように設定しました。

設定項目 意味
dhcp4: no DHCP を使わない
addresses 固定IP/プレフィックス
routes デフォルトゲートウェイ指定
nameservers DNS(例では Google DNS)
network:
    version: 2
    ethernets:
        eth0:
            dhcp4: no
            addresses: [192.168.130.1/20]
            routes:
                - to: default
                  via: 192.168.128.2
            nameservers:
                addresses: [8.8.8.8,8.8.8.4]

反映前にエラーがないか確認:

sudo netplan try

3. Cloudflare Tunnel で自宅Raspberry PiへSSH(ポート開放しない)

自宅ネットワークに SSH を直接開ける必要がないよう、
Cloudflare Zero Trust(Tunnel + Access) で入る形にしました。

大まかな流れは以下です。

  1. Cloudflare アカウント作成
  2. Cloudflare でドメイン取得
  3. Zero Trust → Networks → Tunnels でトンネル作成
  4. Raspberry Pi に cloudflared を入れてトンネルを張る
  5. Zero Trust → Access → Applications でアプリ定義
  6. 接続元PCにも cloudflared を入れて、ドメイン宛に SSH
  7. ブラウザが立ち上がるので認証して接続

参考にした記事:


4. コンテナランタイム(containerd + runc)を入れる

Kubernetes 公式手順に沿って、まずはカーネル設定周りから。

4-1. IPv4フォワーディング + br_netfilter

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# この構成に必要なカーネルパラメーター、再起動しても値は永続します
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# 再起動せずにカーネルパラメーターを適用
sudo sysctl --system

確認:

# 各モジュールが読み込まれていることを確認
lsmod | grep br_netfilter
lsmod | grep overlay

# 各カーネルパラメータが1に設定されていることを確認
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

4-2. containerd / runc のインストール(公式バイナリ)

Docker 配布の containerd もありますが、今回は 公式のバイナリで入れました。

# containerd
wget https://github.com/containerd/containerd/releases/download/v2.0.1/containerd-2.0.1-linux-arm64.tar.gz
sudo tar Cxzvf /usr/local containerd-2.0.1-linux-arm64.tar.gz

# systemd service
wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
sudo mv containerd.service /lib/systemd/system/containerd.service
sudo systemctl daemon-reload
sudo systemctl enable --now containerd

# runc
wget https://github.com/opencontainers/runc/releases/download/v1.2.3/runc.arm64
sudo install -m 755 runc.arm64 /usr/local/sbin/runc

4-3. containerd の設定ファイル生成

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml

4-4. runc の cgroup ドライバを systemd に

Ubuntu Server(systemd)かつ cgroup v2 なので、SystemdCgroup = true を設定。

/etc/containerd/config.toml の以下へ追記:

[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.runc.options]
  SystemdCgroup = true

反映:

sudo systemctl restart containerd

5. kubeadm / kubelet / kubectl をインストール

sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl gpg

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.32/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.32/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

6. kubelet の cgroup ドライバも systemd に揃える(重要)

kubelet と container runtime の cgroup driver がズレるとハマります。

/etc/default/kubelet を編集:

KUBELET_EXTRA_ARGS=--cgroup-driver=systemd

反映:

sudo systemctl daemon-reload
sudo systemctl restart kubelet

7. kubeadm でクラスタ構築(Control Plane)

7-1. 必要 image を pull できるか確認

sudo kubeadm config images pull

結果の例

[config/images] Pulled registry.k8s.io/kube-apiserver:v1.32.0
[config/images] Pulled registry.k8s.io/kube-controller-manager:v1.32.0
[config/images] Pulled registry.k8s.io/kube-scheduler:v1.32.0
[config/images] Pulled registry.k8s.io/kube-proxy:v1.32.0
[config/images] Pulled registry.k8s.io/coredns/coredns:v1.11.3
[config/images] Pulled registry.k8s.io/pause:3.10
[config/images] Pulled registry.k8s.io/etcd:3.5.16-0

7-2. kube-vip で Control Plane VIP を作る(static pod / ARP)

Control Plane の接続先を固定したくて、kube-vip を使って VIP を持たせました。

# VIP(宅内で未使用のIPを選ぶ)
export VIP=192.168.130.21
# インタフェース名(ip a で確認)
export INTERFACE=eth0
# 最新版の取得
KVVERSION=$(curl -sL https://api.github.com/repos/kube-vip/kube-vip/releases | jq -r ".[0].name")

# containerd の場合
alias kube-vip="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"

# manifest 作成(static pod)
kube-vip manifest pod \
  --interface $INTERFACE \
  --address $VIP \
  --controlplane \
  --services \
  --arp \
  --leaderElection | sudo tee /etc/kubernetes/manifests/kube-vip.yaml

※ 私はここで一度、manifest を一時的に修正しました(super-admin.conf を指定するやつ)。このあたりは環境依存で詰まりやすいので、必要なら以下の Issue comment を参照してください。

7-3. kubeadm init

Pod CIDR は 10.1.0.0/16 にしました。

sudo kubeadm init \
  --control-plane-endpoint 192.168.130.21:6443 \
  --pod-network-cidr=10.1.0.0/16 \
  --upload-certs

(もし kube-vip.yaml を一時的に super-admin.conf で動かしていたら、ここで admin.conf に戻す)


8. kubectl を一般ユーザーで叩けるようにする

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

9. Worker Node をクラスタへ参加させる

Control Plane の kubeadm init 実行時に出力された kubeadm join ... を Worker 側で実行します。

sudo kubeadm join 192.168.130.21:6443 --token <token> \
  --discovery-token-ca-cert-hash <hash値>

10. CNI(Calico)を入れる

Calico 公式手順に沿って Operator → CRD → 本体を入れます。

10-1. Operator のデプロイ

kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/tigera-operator.yaml

10-2. custom-resources.yaml を適用

curl https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/custom-resources.yaml -O
kubectl create -f custom-resources.yaml

10-3. calico.yaml を適用(Pod CIDR を合わせる)

kubeadm init--pod-network-cidr と、Calico 側の CALICO_IPV4POOL_CIDR を一致させます。

curl https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/calico.yaml -O
# calico.yaml 内の CALICO_IPV4POOL_CIDR を 10.1.0.0/16 に合わせる
kubectl apply -f calico.yaml

11. 動作確認(最低限)

kubectl get nodes -o wide
kubectl get pods -A

ここまでで、少なくとも以下が揃っている状態になります。

  • Control Plane が Ready
  • Worker が Ready
  • calico-system 系の Pod が Running

12. ハマりどころ(自分用メモを共有)

症状 ありがちな原因 / 対処
kubelet が立ち上がらない/不安定 containerd と kubelet の cgroup driver 不一致 を疑う。どちらも systemd に揃える
kubeadm init 周りで VIP がうまく効かない kube-vip の manifest(static pod)生成/配置、参照する kubeconfig(必要なら super-admin.conf)を確認
ノードのIPが変わって辛い netplan で静的IPにする(宅内 DHCP 範囲と衝突しないところへ)
外から入るために 22 番を開けたくない Cloudflare Tunnel + Access で SSH(ポート開放しない)

おわりに

おうちKubernetesを構築すると、普段は意識しづらい cgroup / runtime / CNI / endpoint あたりが一気に具体化して良かったです。

クラスタ構築が一段落した後、久しく放置してしまっていたのですが、来年はこの辺をやってみたいです。

  • Control Planeの冗長化
  • cloudflaredをKubernetesで管理
  • ArgoCDでのGitOpsやObservabilityの対応

以上です、メリークリスマス!!!

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?