Raspberry PiでKubernetesクラスタを組む(その1: 物理・OS編)

背景

Kubernetesがイケイケの技術と言われてから早数年が経ち、とりあえずコンテナ乗っけとけみたいな感覚でアプリが船の上に乗せられる時代になったが、業務では社内で提供されるPaaSか、諸々の要件の問題で昔ながらの仮想マシンを使うことがほとんどだった。このままではk8sのkの字も分からないまま死んでしまうという危惧を抱き、自前で組んでみることにした。

組むにあたっては仮想マシンを立てまくってもよいのだが、せっかくなので(?)Raspberry Piを使って実機で構成することにした。

用意したもの

この他、元々契約していたさくらのVPS 2Gのインスタンスを使用した。

今となってはとにかくRaspberry Piの調達が困難で、スペースの問題さえなんとかなるなら中古の小型PC(DELLのOptiplex MicroとかHPのEliteDesk GMとか)を並べた方が楽な気がする。中古品なら2万円そこらで手に入るので、RasPi4 8GBが1.2万円と考えるとそこまで価格差が大きいわけでもない。……なんでRaspberry Piでクラスタ組もうとしてるんだっけ……。

OSの準備と設定

Raspberry Pi Imagerを使用してSDカードにOSを焼く。

OSはRaspberry Pi OS Lite 64bitを選ぶ。OS選択画面に最初に出てくるのは通常のRaspberry Pi OSでしかも32bit版なので、“Raspberry Pi OS (other)“から選択する。

焼いた直後の状態での設定だが、通常であれば初回起動時に設定ウィザードがあるが今回はヘッドレス運用なのでそれがない。Raspberry Pi公式サイトにSetting up a Headless Raspberry Piという項目があるので、これに従って設定してもよいのだが、Raspberry Pi Imagerにはその辺をやってくれる機能がある。右下の歯車アイコンをクリックしてAdvanced Optionsを表示すると色々と設定できる。今回はホスト名設定、SSH設定(SSH有効化と公開鍵の登録)、初期ユーザーの名前とパスワードの設定、ロケール設定を行った。特にヘッドレスセットアップの場合初期ユーザー名とパスワードの設定は必須なので注意。あとは書き込み先を指定して書き込めばよい。

Raspberry Pi Imager Advanced Options

焼いたSDをRaspberry Piに差し込んで起動する。

まずIPアドレスを固定する。実のところRaspberry Pi OSでは最初からavahi-daemonが動いていて、$(hostname).localという名前で繋がるようになっている(よくpi@raspberrypi.localでSSHできると言っているのは初期状態でpiユーザーがあり、デフォルトのホスト名がraspberrypiだから)。ただサーバーはIPアドレスを固定しておく方が何かと便利である。どうせローカルネットワークだし。/etc/dhcpcd.confを開いて# Example static IP configuration:という行を探す。この下にコメントアウトされているのが固定IP時の設定なので、コメントアウトを外して適宜設定する。IPv6アドレスについては固定せずにコメントアウトしたままにしておく。

# Example static IP configuration:
interface eth0
static ip_address=192.168.0.20/24
#static ip6_address=fd51:42f8:caae:d92e::ff/64
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

さらにNTPの設定をしておく。systemd-timesyncdが動いていて、デフォルトではDebianのサーバーを参照しにいくようになっている。実際に何も設定しない状態でのtimesyncdの構成を表示させると次のように表示される。

$ timedatectl show-timesync --all
LinkNTPServers=
SystemNTPServers=
FallbackNTPServers=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
ServerName=0.debian.pool.ntp.org
ServerAddress=129.250.35.250
RootDistanceMaxUSec=5s
PollIntervalMinUSec=32s
PollIntervalMaxUSec=34min 8s
PollIntervalUSec=34min 8s
NTPMessage={ Leap=0, Version=4, Mode=4, Stratum=2, Precision=-25, RootDelay=98.312ms, RootDispersion=2.365ms, Reference=81FA23DE, OriginateTimestamp=Thu 2022-06-02 10:56:39 JST, ReceiveTimestamp=Thu 2022-06-02 10:56:39 JST, TransmitTimestamp=Thu 2022-06-02 10:56:39 JST, DestinationTimestamp=Thu 2022-06-02 10:56:39 JST, Ignored=no PacketCount=38, Jitter=3.260ms }

/etc/systemd/timesyncd.confNTP=の行のコメントアウトを外し、ntp.nict.jpを追加する。

[Time]
NTP=ntp.nict.jp
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048

sudo systemctl restart systemd-timesyncdでサービスを再起動してから構成を表示すると次のようになった。

$ timedatectl show-timesync --all
LinkNTPServers=
SystemNTPServers=ntp.nict.jp
FallbackNTPServers=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
ServerName=ntp.nict.jp
ServerAddress=2001:df0:232:eea0::fff3
RootDistanceMaxUSec=5s
PollIntervalMinUSec=32s
PollIntervalMaxUSec=34min 8s
PollIntervalUSec=2min 8s
NTPMessage={ Leap=0, Version=4, Mode=4, Stratum=1, Precision=-20, RootDelay=0, RootDispersion=0, Reference=NICT, OriginateTimestamp=Thu 2022-06-02 11:13:43 JST, ReceiveTimestamp=Thu 2022-06-02 11:13:43 JST, TransmitTimestamp=Thu 2022-06-02 11:13:43 JST, DestinationTimestamp=Thu 2022-06-02 11:13:43 JST, Ignored=no PacketCount=2, Jitter=615us }
Frequency=-1495513

PoE動作の検証・改造

検証といってもHATを搭載してLANケーブルを挿してPoEスイッチに電源を入れればRaspberry Piの電源も入る。その状態でひとまずapt update && apt upgradeを走らせて、問題無く動作することを確認した。

PoE HATにはSoC(とたぶんDRAM)を冷却するための小型のファンが組み付けられているが、このファンが小さい割に猛スピードでブン回るので結構うるさい。サーバーとして常時動かすにはしんどいレベルなので、今回は(個々のRaspberry Piは)ファンレス運用することにした。

商品写真を見ると分かるように、どのPoE HATの冷却ファンも単にネジ止めされているだけで、ファンの電源はHATの基板上に実装されている3pinコネクタから取られている(GeeekPiのだけGNDと5VのピンがHATから生えていて、ファンから2本のケーブルが出て、それらのピンに接続されている形)。ということは外すことも難しくないだろうと考え試してみた。

UD-RPPOEP

I-O DATA UD-RPPOEP商品サイトより引用)

結果としてはファン本体は2箇所ネジを外すだけでよく、またコネクタは小さいので若干力が入れにくいものの引き抜くことができた。今回3種類のPoE HATを使用しているが、端子の形状は違えど同じような作りだった。

さて、ファンを取り外して本体に装着した状態が次の写真である(外したファンやらネジやらが散らかっているが見なかったことにしてほしい)。

PoE HAT w/o fan

左がRaspberry Pi公式、右がinnomakerのものである。ファンの位置が違っていて、公式のHATはSoCとDRAM両方に風が当たりそうな雰囲気を感じる(3B+に搭載すると位置関係が変わるのだろうか?)。一方innomakerのものはSoCの真上にファンが来る形になっている。

Raspberry Piでは熱の問題もそれなりに聞くし、PoE周りの発熱もあるのではないか、と考えるとさすがにこのままは心配なのでヒートシンクを取り付けた。ここで公式のものは高さが低いものを選ばないと干渉してしまう。雑にメジャーを当てて5mmくらいは行けるだろうと思って千石電商で買ってきたが、とりあえず干渉せずに装着できている(ように見える)。なおDRAMやUSBコントローラーについてはいずれもヒートシンク装着は不可能である。

  • 公式HATはHATの本体に向かう面にも部品が実装されている関係からSoC以上に空間がないので無理
  • innomakerのものはそもそもHATと本体がかなり接近する作りなので無理

ちなみにGeeekPiのものは商品にヒートシンクが同梱されていた。本体とHATの高さもぼちぼちあるので特に干渉しないで装着できる。

ケースの組み立て・組み込み

写真を撮ってないので書くこともあまりないが、ケースに説明書がついているのでその通りに組み上げるだけ。アクリル板の保護シート剥がすのが一番面倒だったと思う(特に機器を搭載するプレートは細かい文字の刻印が多くて剥がれないので爪でこすって削り取った)。あと説明書の図だとLEDの電源線を繋げばファンも回っているが、当然そんなはずはないので光らせたい場合は全部繋ぐ必要がある。私は最初だけ光らせて「綺麗だねー」と言って10秒程で外した。サーバーは光らなくていいので……。

あと冷却ファンはむき出しなので、ファンガードを追加した方がよいだろう。AinexのCFG-120B-BKを買って取り付けたが、ケース付属のネジでは長さが足りないのとネジ頭のサイズが小さいためにファンガードの穴に引っかからず装着できなかった。そのため、別途M3x35mmのトラス頭小ネジを買って装着した。

今回使ったPoE HATはすべてRaspberry PiのGPIO端子を塞いでしまう(GeeekPiのみ1-6番ピン、それ以外は全てのピン)。そのため、通常であればケースに付属するファンの電源をGPIO端子から取るのだが、それができない。今回はGeeekPiのPoE HATがHAT付属ファン用に5VとGNDのピンをHAT上面に出していたので(こちらから画像を参照のこと)、付属ファンを取り外してクラスターケースのケーブルをここに接続した。これ以外のPoE HATケースの場合は連結ピンソケットなどを使ってHATの上にピンが突き出すように伸ばすことで対応できると思う。ファンの電源を取るRaspberry Piは最上段に配置すると思うので、高さは特に問題にならないだろう。

ストレージの準備

永続ストレージ用にSanDiskの2.5inch SSDを用意した。今回のクラスターケースは2.5inchドライブも固定できる……のだが、ネジの長さが足りなくて全然固定できなかった。ちなみに今回使ったSanDisk Ultra 3DはWestern DigitalのWD Blue 3Dと全く同じ製品らしく、安い方を選べばよいとのこと。

適当に初期化しておく。当初USBブートを試みた痕跡があるが、上手く行かなかったので追加のSSDは大人しく単なるデータ領域として使うことにした。 下の手順ではパーティションテーブルを作り直しているので、既存のデータにはアクセスできなくなる。

$ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0 931.5G  0 disk
├─sda1        8:1    0   256M  0 part
├─sda2        8:2    0 119.2G  0 part
└─sda3        8:3    0   812G  0 part
mmcblk0     179:0    0  14.7G  0 disk
├─mmcblk0p1 179:1    0   256M  0 part /boot
└─mmcblk0p2 179:2    0  14.5G  0 part /

$ sudo parted /dev/sda
GNU Parted 3.4
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: ASMT 2235 (scsi)
Disk /dev/sda: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size   Type     File system  Flags
 1      4194kB  273MB   268MB  primary  fat32        lba
 2      273MB   128GB   128GB  primary  ext4
 3      128GB   1000GB  872GB  primary

(parted) mktable gpt
Warning: The existing disk label on /dev/sda will be destroyed and all data on this
disk will be lost. Do you want to continue?
Yes/No? yes
(parted) mkpart
Partition name?  []?
File system type?  [ext2]? ext4
Start? 0%
End? 100%
(parted) quit
Information: You may need to update /etc/fstab.

mayth@geranium:~ $ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0 931.5G  0 disk
└─sda1        8:1    0 931.5G  0 part
mmcblk0     179:0    0  14.7G  0 disk
├─mmcblk0p1 179:1    0   256M  0 part /boot
└─mmcblk0p2 179:2    0  14.5G  0 part /

物理的な設置、OSの基本的な設定を済ませたので今回はここまで。