2026年3月29日日曜日

M4 MaxのRoo Codeから自宅ラズパイk8sクラスターをMCPで「完落ち」させるまで

1. はじめに(背景)

最新の M4 Max搭載 Mac Studio を導入しました。この圧倒的なパワーをローカルLLMの実行だけに使うのはもったいない。そこで、長年運用している 自作のラズパイ・クラスター(3台構成) を、VS CodeのAIエージェント「Roo Code」から直接操作・管理できる環境を構築することにしました。

AIに「インフラの目と手」を与えるための架け橋として、GoogleやAnthropicが推進する Model Context Protocol (MCP) を活用します。

技術スタック

  • Client: Roo Code (VS Code Extension)

  • Protocol: Model Context Protocol (MCP)

  • Runtime: Node.js

  • Infrastructure: Kubernetes (Raspberry Pi Cluster), kubectl


    [構成図]


























2. フェーズ1:MCPブリッジの構築(疎通編)

AIとKubernetesを繋ぐ既成のMCPサーバーが見当たらなかったため、Node.jsで簡易ブリッジ k8s-bridge.js を自作しました。

ハマりポイント:環境変数PATHの壁

ターミナルでは動くのに、Roo Code経由だと kubectl が見つからない問題が発生。VS Codeから起動されるMCPプロセスには、普段のシェル(.zshrc等)の PATH が引き継がれないのが原因でした。

対策: * kubectl をフルパス(/opt/homebrew/bin/kubectl)で指定。

  • mcp_settings.json の env セクションにも明示的に PATH を記述。


3. フェーズ2:JSONとハンドシェイクの格闘(接続編)

Roo Codeの設定画面でランプが「黄色(再試行中)」から変わらず、接続に苦労しました。

ハマりポイント:JSON-RPCプロトコルの厳密さ

MCPは標準入出力を介した JSON-RPC 通信です。AIからの initialize リクエストに対し、仕様に沿ったレスポンスを即座に返さないと、AIは接続先が生きていると認識してくれません。

対策: 最小限の initialize 応答と tools/list 応答を実装。ついにランプが 「緑(Connected)」 へ変わり、AIがクラスターを認識しました。


4. フェーズ3:万能ツールへの進化(実行編)

当初は「ノード一覧を取得するだけ」のReadOnlyなツールでしたが、これではPodの生成ができません。そこで、引数を自由に渡せる汎用ツール run_kubectl へ刷新しました。

実装の肝

Node.jsの spawn を使い、stdout(標準出力)と stderr(標準エラー)の両方をバッファリングしてAIに返すロジックを実装しました。これにより、AIが「自分でYAMLを書き、自分でapplyし、エラーが出れば自分で修正する」という自律的なループが可能になりました。


5. 動作確認:AIにPodを作らせてみた

実験指示: 「NginxのPodを作って」 結果: AIが run_kubectl を使って default namespaceに my-web Podを生成。

感動ポイント: get pods -A を実行した際、自分が長年運用してきた Argo CD や Cilium と並んで、AIが生成した my-web が Running になっている のを見た瞬間、インフラ運用の新しい時代を感じました。


6. まとめ:AI時代のインフラ管理

M4 Maxのパワーがあれば、ローカルでAIを回しながら、物理インフラの運用(ログ解析、トラブルシュート、デプロイ)を対話形式で自動化できます。

インフラエンジニアにとってのMCPは、単なる便利ツールではありません。「自分の長年の経験とスキルを、AIという強力な相棒に移植するためのプラグ」 なのだと確信しました。


🛠 実装リソース

万能型MCPブリッジ:k8s-bridge.js

標準モジュールのみで構成しているため、npm install 不要で動作します。

JavaScript
#!/usr/bin/env node
const { spawn } = require('child_process');

// ※各自の環境に合わせて kubectl の絶対パスを確認してください
const KUBECTL_PATH = '/opt/homebrew/bin/kubectl'; 

let buffer = '';
process.stdin.on('data', (chunk) => {
  buffer += chunk.toString();
  let lines = buffer.split('\n');
  buffer = lines.pop(); 

  for (let line of lines) {
    if (!line.trim()) continue;
    try {
      handleMessage(JSON.parse(line));
    } catch (e) {
      console.error("JSON Parse Error:", e);
    }
  }
});

function handleMessage(message) {
  const respond = (result) => {
    process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: message.id, result }) + '\n');
  };

  if (message.method === 'initialize') {
    respond({
      protocolVersion: "2024-11-05",
      capabilities: { tools: {} },
      serverInfo: { name: "k8s-universal-bridge", version: "1.1.1" }
    });
  } else if (message.method === 'tools/list') {
    respond({
      tools: [{
        name: "run_kubectl",
        description: "任意の kubectl コマンドを実行し結果を返します",
        inputSchema: {
          type: "object",
          properties: {
            args: { type: "array", items: { type: "string" } }
          },
          required: ["args"]
        }
      }]
    });
  } else if (message.method === 'tools/call' && message.params.name === 'run_kubectl') {
    const k8s = spawn(KUBECTL_PATH, message.params.arguments.args);
    let output = '';
    k8s.stdout.on('data', (d) => { output += d.toString(); });
    k8s.stderr.on('data', (d) => { output += d.toString(); });
    k8s.on('close', (code) => {
      respond({ content: [{ type: "text", text: output.trim() || `Code: ${code}` }] });
    });
  }
}

Roo Code設定:mcp_settings.json

設定ファイルの場所: ~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json

JSON
{
  "mcpServers": {
    "kubernetes": {
      "command": "node",
      "args": ["/Users/hidenari/src/k8s-bridge.js"],
      "env": {
        "KUBECONFIG": "/Users/hidenari/.kube/config",
        "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
      }
    }
  }
}



実際の設定の様子。
MCPサーバーの状態もグリーンランプが表示されていることを確認します。










💡 技術Tips

  • pkillの重要性: コード変更後は pkill -f k8s-bridge.js で旧プロセスを掃除しないと、新旧コードが混在してハマります。

  • バッファリングの罠: Node.jsの data イベントは分割されて届くため、lines.pop() によるバッファ保持がプロトコル通信の生命線です。


    [検証]

    検証として、Podの作成をチャットを通してお願いした結果になります。



2026年3月3日火曜日

Pod Security Admission

[用途]
ルール対象外のマニフェストをデプロイさせないための
防止策で利用するイメージになる

[検証]
以下のコマンドを投入する
kubectl label ns default pod-security.kubernetes.io/warn=baseline
[各種コマンドの意味]
1. kubectl label ns default

  • 意味: default という名前空間(Namespace)に「付箋(ラベル)」を貼るという指示です。
  • ポイント: 新しい設定ファイルを作るのではなく、既存の「箱(Namespace)」の属性を書き換えるだけで設定が完了するのが、最新の PSA (Pod Security Admission) の特徴です。

2. pod-security.kubernetes.io/
  • 意味: これは「Podセキュリティ機能に対する設定ですよ」という接頭辞(プレフィックス)です。
  • 役割: k8sには多くのラベルがありますが、この名前で始まるラベルを貼ることで、k8sのエンジンが自動的に「セキュリティのチェックを開始しなきゃ!」と判断します。

3. warn (コントロール・モード)
PSAには3つのモードがありますが、今回はその中の 警告モード を選びました。
  • enforce (強制): 基準に合わないPodは起動させない
  • audit (監査): ログに記録するだけ。
  • warn (警告): 今回の結果の通り、Podは起動させるが、実行者に警告を出す

4. baseline (ポリシー・レベル)
セキュリティの「厳しさ」のレベルです。
  • Privileged: 無制限。何でも許容する。
  • Baseline: 今回の設定。 最小限の「危ない設定」を禁止する標準レベル。
  • Restricted: 非常に厳しい。Podを動かすために、細かいセキュリティ設定が必須。


実施結果:








問題があるマニフェストをデプロイしてみる

apiVersion: v1
kind: Pod
metadata:
name: security-test-pod
spec:
hostNetwork: true # ← これが基準違反のポイント
containers:
- name: nginx
image: nginx:alpine



以下の警告表示が出ることが確認できる




2026年2月18日水曜日

AIエージェント(Cline)

今更ながら感は、ありますがAIエージェントとローカルLLMで

MCPの設定を行ってみたいと思います。


1)ローカルLLMを使う上で、Ollamaと qwen2.5-coder:14bをいれました。











3)MCPを動かす上で、以下の導入を行います。

①nodeのインストール:

brew install node



②uvx / uvのインストール:

curl -LsSf https://astral.sh/uv/install.sh | sh

source $HOME/.local/bin/env


③Vs-Codedの拡張でCLINEを入れます。









4)MCP serverの設定を行います。

Configure > Configure MCP Serversを選択





















5)jsonファイルの記載を以下にします。

記載が終わったら、Doneを押す












以下の質問をしてみました。


「この URL を読んで、最新の Terraform の S3 命名規則を教えて: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket 」



結果:







































同時に、ブラウザ上で、以下の内容も表示されることを確認




VS-Code(Copilot Agent)環境にて

過去に作成済みのソースコードに対してファイル単位ではなく

広範囲で(上位のフォルダー)質問をしてみる















質問内容:

searchツールを使って、現在のワークスペースにある setup_raspberry.sh の内容を確認して。その上で、このスクリプトで設定しているOSの状態を前提とした、main.tf への追加リソース(例:監視用のPrometheusなど)を提案して。僕の過去の書き方を真似して、コレジャナイ感がないようにしてね。


AI側の調査:

AI側で調査するのに権限の付与が必要になるので

以下の確認表示がでるので、Allowにして、進める






















AI側の回答:









2026年2月15日日曜日

Raspberry Pi 5 SSDブート設定まとめ (2026年版)


1. ブート順序の変更 (EEPROM)

• `sudo raspi-config` を実行

• `Advanced Options` > `Boot Order` > `NVMe/USB Boot` を選択

• 再起動して設定を反映


2. SSD起動時の重要パス

• 設定ファイルの場所が従来と異なるので注意

• 正解: `/boot/firmware/cmdline.txt`

• ※`/boot/cmdline.txt` を書き換えても反映されない場合が多い


3. NVMe Gen3 の有効化 (Pi 5限定)

`/boot/firmware/config.txt` に以下を追記して高速化:

```

dtparam=pciex1_gen=3

```


4. 構築のヒント

• SSD起動にするとディスクI/Oが劇的に速くなり、K8sの動作が安定する

• 2026年現在の最新ファームウェアでは、`/boot/firmware/` 配下の管理がより厳格化されているため、パスの確認は必須。


2026年版 Raspberry Pi 5 + SSDでKubernetesクラスター構築!cgroupの罠と解決策




Kubernetesクラスター構築!cgroupの罠と解決策

1. はじめに(今回の環境)

• ハードウェア: Raspberry Pi 5 (3台構成)

• ストレージ: SSD起動(NVMe/USB)

• OS: Raspberry Pi OS (Debian 13 Trixieベース)

• K8sバージョン: v1.31 (最新)

2. 直面した3つの大きな壁

1. SSD起動特有の「パス」問題

• 従来の /boot/cmdline.txt はもはや古い。

• 最新のSSD起動環境では /boot/firmware/cmdline.txt を書き換えないと、設定が一切反映されない。

2. cgroup v2 への完全移行

• 最新OSでは cgroup 管理が厳格化。

• cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1 の追記が必須。

• これを忘れると、kubeadm は進めてもノードが永遠に NotReady になる。

3. 2026年リポジトリ署名期限問題

• Kubernetesのインストール用リポジトリの署名が変わっており、古い手順のままだと apt update でエラーが出る。

3. 解決の決め手となったスクリプトのポイント

• パスの自動判別: /boot/firmware/cmdline.txt があればそちらを優先して書き換える。

• 冪等性(べきとうせい)の確保: 同じ設定を二重に書き込まないスマートな追記。

• 最新の署名回避: [trusted=yes] を活用して、2026年のリポジトリ変更を突破。

4. 構築結果

• kubectl get nodes で、Master/Node1/Node2 すべてが Ready に!

• SSD起動の爆速環境で、ネットワーク(Flannel)も正常稼働。

5. まとめ(教訓)

• 「去年動いた手順」は、最新OS(Trixie)とSSD起動の前では通用しない。

• cg

M4 MaxのRoo Codeから自宅ラズパイk8sクラスターをMCPで「完落ち」させるまで

1. はじめに(背景) 最新の  M4 Max搭載 Mac Studio  を導入しました。この圧倒的なパワーをローカルLLMの実行だけに使うのはもったいない。そこで、長年運用している  自作のラズパイ・クラスター(3台構成)  を、VS CodeのAIエージェント「Roo Co...