メモ - RyuSA

技術的なメモ書きとか試してみたこと

Raspberry Pi上にWi-Fi APを自動構築するKubernetes Operatorを作った話

元ネタ、というよりWhy/作ったモチベはこちらに書いてあります👀

eng-blog.iij.ad.jp

雑に言えば「自宅にメッシュWi-Fiを導入したかったので、Raspberry PiWi-Fi APを実装してみた」という感じ。

TL;DR

最終形として、こんなOperatorを作成しました。

f:id:RyuSA:20210702181459p:plain

github.com

これによりCRDであるAccessPointリソースをKubernetesにデプロイすることで、KubernetesにjoinしているRaspberry Pi上に適切に設定されたWi-Fiアクセスポイントを作成することができるようになりました。

これでkubectlひとつでメッシュなWi-Fiを構築したり来客向けのゲストWi-Fiを用意することができるようになりました、やったぜ✨

Rapsberry PiのAP化 #とは

Raspberry PiWi-FiのAPとして動かせる話は昔からあり、いろんな方が紹介してくださってます🥰

qiita.com

qiita.com

つまるところRaspberry Piのワイヤレスのネットワークインタフェースとイーサネットのネットワークインタフェースをブリッジでつなぎ、ワイヤレスネットワークインタフェースをAPモードで起動しようということですね。ソフトウェアとしてはhostapdがよく使われているみたいです🤔

Raspberry Pi自体は安価に調達できて、かつPoE Hatを利用することでLANケーブル一本で動かすことができるなど取り回しが良さそう……今回やりたいことにピッタリ。

hostapd

hostapdはネットワークインターフェースカードをアクセスポイントとして動かすためのソフトウェアで、設定ファイルを咬まして起動することで指定したSSID/パスワード/その他設定のアクセスポイントを起動することができます。すごく便利👏

とりあえず手動オペレーション💪

一度、手動でWi-Fi APをRaspberry Pi上に展開するための手順を確認してみます。

  1. ネットワーク内にDHCPを用意する
  2. Raspberry Piのネットワークインタフェースを変更する(ブリッジ作ったり)
  3. Raspberry Piにhostapdをインストール、デーモンとして登録する
  4. configファイルを作成する
  5. configファイルにSSIDとパスワード、その他設定を入れる
  6. デーモンのリロード&再起動

手動オペレーションの場合、この手順をAPにしたいRaspberry Piすべてに実施していくことになります。実際に自宅に転がってたRaspberry Pi上でオペレーションしてみたところ、1つあたり新規作成に1時間程度時間がかかりました。これで運用するのは……かなりいやですね🤔

hostapdのインストールの手順そのものはコンテナを配布することで解決自体はできます。ついでだったのでコンテナイメージを作りました

github.com

しかし、コンテナを起動したりconfigファイルを配布したりと運用作業を真心込めて1台1台手作業するのはちょっとありえないという気持ち🤮

Operatorにしていく🦾

ルーチンな運用作業はKubernetesのOperatorにするに限る。ということでOperatorで実装することを決意しました。

Kubernetes Operatorとは、KubernetesAPIリソースを監視して「現在の状況」と「理想の状況」を計算し、現在の状況をあるべき姿にドリブンしていくアプリケーションのことを指します。通常、後述するCRDと共に実装を行い日々の運用の自動化などを行うことが多いです。

有名なOperatorとしてはPrometheusのデプロイや設定をKubernetesリソースで行えるようにしたPrometheus Operatorや、RabbitMQのクラスターデプロイをKubernetesリソースで定義できるようにしたRabbitMQ Cluster Operatorなどがあります。

github.com

github.com

スコープ決め

手動オペレーションの各タスクのうち、自動化できそうな範囲を考えてみます。

  1. ネットワーク内にDHCPを用意する
    • これは各ネットワーク内で決定するべき事柄、ここまで責任を持つのは過剰
    • → スコープ外
  2. Raspberry Piのネットワークインタフェースを変更する
    • これはcloud-initを利用して自動化(=k8s関係ない範囲)
    • → スコープ外
  3. Raspberry Piにhostapdをインストール、デーモンとして登録する
    • 指定したノードにPodとして起動すれば……?
    • → DaemonSetとして配布します
  4. configファイルを作成する
    • 最低限の設定を外から渡せればConfigMapに入れるところまで自動化できそう
    • → CRDとして定義し、そこからConfigMapを作成&DaemonSetと一緒に配布します
  5. configファイルにSSIDとパスワード、その他設定を入れる
    • 4.と同様
  6. デーモンのリロード&再起動
    • Kubernetesの機能をそのまま利用します
    • → 実装しない(DaemonSetに任せる)

ということで、1.と2.以外のタスクを自動化する方針で実装していきます。

Kubebuilder

Operatorを実装するにあたり、ほしいのは"Kubernetesのエコシステム"であってOperatorのフルスクラッチではないです🤔

Kubernetes Operatorを実装するためのフレームワークとして有名なものが2つあります。

  • Kubebuilder
  • Operator SDK

それぞれを触ってみた感じ、個人的にはKubebuilderの方が使いやすそうに感じたので今回はKubebuilderを利用して実装していこうと思います🥰

こちら参考書籍です。KubernetesのOperatorとはなにか、KubebuilderとOperator SDKの違いとサンプル実装と非常にわかりやすく解説されててマジで助かりました。

CRD

KubernetesにはCuston Resource Definitionというリソースの拡張機能が存在しており、いわば「俺専用のKubernetesリソースを作ってやるぜ〜〜^^」ができます。CRD自体はKubernetes 1.16でGAとなっているAPIです。

今回のパターンでは2種類のCRDを実装しました。

  • AccessPoint
    • アクセスポイントそのものを表すリソース
    • SSIDの設定やパスワード、あと細かい設定とかと記述する
  • AccessPointDevice
    • アクセスポイントをデプロイする仮想的なデバイス
    • ブリッジの設定や操作するネットワークインタフェースなどを記述する
    • 用途としては"Aliceの部屋"とか"1F リビング"とかの単位で書いていく

ストーリーとしては

  • まずユーザーがAccessPointDeviceをデプロイ
  • ユーザーがAccessPointDeviceを指定したAccessPointをデプロイ
  • AccessPoint OperatorがAccessPointの作成イベントを受信
  • AccessPointDeviceと合わせてAccessPoint Operatorが適切なDaemonSetを作成してデプロイ
  • DaemonSetの力で各Raspberry Pi上にhostapdの設定とhostapdコンテナがデプロイされていく

……といった流れでリソースを操作していくかな?と考えています。

最終的に出来上がったもの

開発そのものについては(ただ実装しただけなので)割愛します。興味がある人が多そうであればどこかに書くなりしておきます。

最初にも紹介しましたが、こんな感じに完成しました。細かい使い方等は一応READMEに書いてあります。なお現時点ですでに可愛いバグ(AccessPointDevice消してもDaemonSetが消えない等)があったりするのは許してください。。。

f:id:RyuSA:20210702181459p:plain

github.com

開発の環境としては使い捨てできるKubernetesを作成するためのkindを利用しました。便利ですね〜

github.com

以前構築したRaspberry Piクラスターのネットワーク設定を変更して……

ryusa.hatenablog.com

作成したOperatorをデプロイしてみると……

f:id:RyuSA:20210703233015p:plain
Operatorで作成した"RASPBERRY" APに繋がった

手元のスマホからWi-Fiに接続&インターネットへアクセスできました!やったぜ🤪

ためしにhostapdがデプロイされている環境でRaspberry Piをインストールしてある部屋から部屋へ移動してみましたが、同じSSID/パスワードでうまくローミング(?)してシームレスに接続を継続してくれました。これはいい感じのモノが作れた……!

完走した感想

実はGo言語でがっつり開発したのは初挑戦だったので色々雑な作りになってしまいました。が、それでも今日も自宅で動いてくれています🥰毎日SSIDとパスワードを変えたい気持ちになりますねw

一方でKubebuilder側がまだM1 Mac(自分の開発環境)に正式対応しておらず、Operatorのテスト機構については全く手が出せていない状況です。ARM対応の実装を自分ですれば良いだけなのですが、なかなか食指が動かず……orz

定期的に開発できたらいいな〜と思ってます。