Raspberry Piを買っておうちKubernetesクラスタを作る

  1. Raspberry Piを買っておうちKubernetesクラスタを作る <- イマココ
  2. おうちKubernetesにSealedSecretsを入れる

6月の初頭に興味本位でRaspberry Pi4を買ってみた.特に何がしたかったわけではないけど,最近Kubernetesクラスタを作っていなかったのでクラスタが作りたくなった. しかしどうせ作るならControlPlaneは3台ほしい.と思っていた矢先に

pc.watch.impress.co.jp

こういう記事が出ていたので,これは朗報.とりあえずControlPlane1台で作って,後で2台買い増そう.きっと夏くらいになったら買えるようになるはず.

というわけで初手で買ったもの.

最初に目指す構成.

Name IP OS Machine
master01 192.168.0.50 Ubuntu Server 22.04 LTS Raspberry Pi4
worker01 192.168.60 Ubuntu Server 22.04 LTS 古いIntelデスクトップ
flowchart TB
    Admin -- kubectl -->VIP
    subgraph Master01
      keepalived-->kube-apiserver
      VIP-->HAProxy
      HAProxy-->kube-apiserver
      etcd
    end
    subgraph Worker01
    kubelet-->VIP
    end

Raspberry Pi4にOSを入れる

KIOXIAのmicroSDカードには,SD変換アダプタがついていたのでPC側のSDカード刺し口にそのまま刺せた.

www.raspberrypi.com

Raspberry Pi用にOSを書き込むにはrpi-imagerが要る.公式から落としてもいいし,

$ yay -S rpi-imager

俺はこうした.

あとは好きなOSを選べばいいのだが,OS周りであまり苦労したくないのでいつもどおりUbuntu Server 22.04 LTSを選んでおく.

で,書き込むだけ.

あとはmicroSDカードをRaspberry Piに挿して電源を入れるのみ.これで勝手にOSが起動する.

SSHする

最近のUbuntu ServerはHDMIケーブルで画面出力しなくてもSSHの設定くらいはオンラインでできてしまう.

まずLANケーブルを接続する.この時点でDHCPで適当なIPが割り振られるはずである.あとは同じLAN内から

$ arp -a
...
? (192.168.0.50) at d8:3a:dd:xx:xx:xx [ether] on enp31s0

すると,接続されている機器が出てくる.

Raspberry PiMACアドレス

udger.com

ここのどれかなので,それに該当しそうなものからアタリをつける. あとはそこにSSHしてみる.

$ ssh ubuntu@192.168.0.50

初期パスワードはubuntuになっており,認証すると最初にパスワード変更を求められる.その後は,変更後のパスワードを使ってSSHできる. このタイミングで公開鍵を ~/.ssh/authorized_keys に入れてしまえば,あとは公開鍵認証でSSHできるようになる.

IP固定とWiFi設定

IPを固定したいのと,LANケーブルではなくWiFi接続にしておきたい.

というわけで

/etc/netplan/99-manual-conf.yaml

network:
  wifis:
    wlan0:
      dhcp4: false
      access-points:
        "access_point":
          password: "password"
      addresses:
      - 192.168.0.50/24
      routes:
        - to: default
          via: 192.168.0.1
      nameservers:
        addresses:
        - 192.168.0.1
        - 8.8.8.8
        search: []
  version: 2

としておくことで,IPは192.168.0.50となり,WiFiで接続できるようになる.

ついでに /etc/hosts に今回使うIPを書いておく.

192.168.0.40  kube-apiserver.h3poteto.local
192.168.0.50  master01.h3poteto.local
192.168.0.60  worker01.h3poteto.local

192.168.0.40 は将来的にHA構成にしたいので,master01のIPとは別のIPにしておいて,後述するkeepalivedのVIPとして使う.

Kubeadmを使える状態にする

基本的にはすべてここに従っている.

kubernetes.io

Swapを切る

https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#before-you-begin

Kubeletを正しく動作させるためにはswapを切る必要がある.

$ swapoff -a

IPv4フォワーディングを有効化する

https://kubernetes.io/docs/setup/production-environment/container-runtimes/#forwarding-ipv4-and-letting-iptables-see-bridged-traffic

コンテナランタイムを入れる前提条件.

$ 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

containerdを入れる

CRIはcontainerdを使う.containerd自体はdockerと一緒にインストールできる.

docs.docker.com

$ sudo apt-get install ca-certificates curl gnupg
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
$ echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install containerd.io

その上で,containerdの設定を書き換えておく.こうしておかないとsystemd cgroup driverが使えない.

https://kubernetes.io/docs/setup/production-environment/container-runtimes/#containerd-systemd

/etc/containerd/config.toml

version = 2
[plugins]
  [plugins."io.containerd.grpc.v1.cri"]
   [plugins."io.containerd.grpc.v1.cri".containerd]
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
          runtime_type = "io.containerd.runc.v2"
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            SystemdCgroup = true

nerdctlを入れる

ホスト上でのデバッグが便利なので,containerdのインターフェースを叩けるCLIがあると良い.最近はnerdctlを使っている.

github.com

HAProxyを入れておく

本来ControlPlane1台のKubernetesであれば不要だけれど,将来的に拡張する気なので予めHAProxyを入れておく. こうしておかないと,将来的にControlPlaneノードを追加したときにAPIServerへのリクエストを分散させる方法がなくなってしまう.

$ sudo apt-get install haproxy keepalived

/etc/haproxy/haproxy.cfg

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        maxconn 4000
        daemon

defaults
        log global
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client 50000
        timeout server 50000

frontend kube-apiserver
        bind *:10443
        mode tcp
        option tcplog
        default_backend kube-apiserver

backend kube-apiserver
        mode tcp
        option tcplog
        option tcp-check
        balance roundrobin
        default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
        server kube-apiserver-1 192.168.0.50:6443 check

kube-apiserverはデフォルトで6443で起動するので,そこをバックエンドにしておく.あとでノードを追加した際にはbackendにserverを追加していく.

/etc/keepalived/keepalived.conf

global_defs {
  notification_email {
  }
  router_id LVS_DEVEL
  vrrp_skip_check_adv_addr
  vrrp_garp_interval 0
  vrrp_gna_interval 0
}

vrrp_script chk_haproxy {
  script "killall -0 haproxy"
  interval 2
  weight 2
}

vrrp_instance haproxy-vip {
  state MASTER
  priority 100
  interface wlan0                       # Network card
  virtual_router_id 60
  advert_int 1
  authentication {
    auth_type PASS
    auth_pass 1111
  }

  virtual_ipaddress {
    192.168.0.40/24                  # The VIP address
  }

  track_script {
    chk_haproxy
  }
}

で,こういうkeepalivedを作っておくと,VIPでアクセスできる.

kubeadm, kubelet, kubectlを入れる

$ sudo apt-get install apt-transport-https linux-modules-extra-raspi

linux-modules-extra-raspi を入れておかないと,VXLANが作れないので,いざCNIを入れたときにCNIが動かない.

qiita.com

$ curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
$ sudo apt-get install -y kubelet kubeadm kubectl
$ sudo apt-mark hold kubelet kubeadm kubectl

これで終わり.

kubeadmを叩く

あとは叩くだけ.

$ sudo kubeadm init --pod-network-cidr=10.0.0.0/8 --control-plane-endpoint=kube-apiserver.h3poteto.local --apiserver-cert-extra-sans=kube-apiserver.h3poteto.local



Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

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

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join kube-apiserver.h3poteto.local:6443 --token xxxx \
        --discovery-token-ca-cert-hash sha256:xxxx \
        --control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join kube-apiserver.h3poteto.local:6443 --token xxxx \
        --discovery-token-ca-cert-hash sha256:xxxx

kubernetes.io

--control-plane-endpointは複数ControlPlaneにするのであれば指定したほうがいい.というか

Turning a single control plane cluster created without --control-plane-endpoint into a highly available cluster is not supported by kubeadm.

なので指定しないと無理なんじゃないだろうか.ここはIPでもドメインでもどちらでも受け取れるのだが,まぁhostsファイルを書き換える手間を惜しまないのであればドメインの方が楽なんじゃないかな…….あとあとkeepalived周りの構成を変えたりしたときに,IPが変わったりすると厄介なので.

--pod-network-cidr は入れるCNIによっては強制されるので,flannelとか入れる場合は注意したほうがいい.今回はCiliumを入れるので,気にせずでかいCIDRをもらう.

あと,デフォルトではkube-apiserverは6443ポートを指定される.これはHAProxyを設定した際に10443で受けるようにしたので,実際にこのコマンドを使う場合は10443ポートを指定する.

とりあえずこれでControlPlaneは完成. この状態だとまだNodeがRunningにはならない.

Worker

kubeadmの準備までほぼ同じ.たまにaarch64向けのパッケージはamd64に変えなきゃいけないところがあるくらい.

で,

$ kubeadm join kube-apiserver.h3poteto.local:10443 --token xxxx \
        --discovery-token-ca-cert-hash sha256:xxxx

こうするだけ.

CNIを入れる

今回はCilium.

docs.cilium.io

といってもhelmで入れるだけなんだけどね.

まとめ

できあがり.

$ k get node
NAME                      STATUS   ROLES           AGE   VERSION
master01.h3poteto.local   Ready    control-plane   18d   v1.27.2
worker01.h3poteto.local   Ready    <none>          18d   v1.27.2

なぜHDMIケーブルを買ったのだろう.一度も出番がなかった.それもこれもUbuntu ServerがLANケーブル接続だけでSSH設定を完結できてしまったからなのだが. 今の所デバッグ用途ですら登場シーンがない.本当に何故買ったのだろう.

で,このままだとまだSecretManagerも入れていないし,PVも使えないしで,あんまり動かせるものがない. というわけで,この先の作業は次回に続く.