はじめに
ローカル LLM は、2023年の llama.cpp や Ollama の登場によって、個人の PC でも動かしやすくなった
2024年11月の M4 Mac mini 発売以降は、Apple Silicon のユニファイドメモリや電力効率を活かしたローカル LLM 環境にも関心が集まっている。2025年には DeepSeek-R1 や gpt-oss などのモデルも登場し、個人で試せる選択肢が広がった
やや流行りに乗り遅れたが、今回は2026年9月発売の Mac mini M6 を購入し、ローカル LLM の検証環境を自宅ラボに構築した。本記事では、環境構築から初期動作確認までの手順を、実際の出力結果とともにまとめる
使用するのは Mac mini(M6 / 32GB)で、Ollama + gpt-oss 20B と Splash + Qwen3.8-27B の2構成を試す。購入直後のため、Homebrew 未導入の状態から準備を進めた
■ 概要図
同一 Mac 内の試験では 127.0.0.1、LAN 試験では今回の Mac mini に割り当てた IP アドレス 192.168.129.6 を待ち受け先に使う。LAN IP アドレスは利用環境に合わせて読み替える。2つのモデルは同時にロードせず、順番に検証する
■ Splash の WebUI
本記事で実施する内容は下記のとおり。
- 環境準備
- Homebrew のインストールと PATH 設定
- macOS ファイアウォールと API 接続元の制限
- インストール
- Ollama + gpt-oss 20B のインストールと起動
- Splash + Qwen3.8-27B のインストールと起動
- 動作確認
- 日本語での質問と回答内容の確認
- GPU の利用状況とメモリ使用量の確認
- 同じ Mac と LAN 内の別PC・サーバからの API 呼び出し
- API の応答時間と導入前後のディスク容量の確認
両構成で日本語の回答と API 経由の応答を確認できたが、技術的な説明には一部誤りがあった。設計や設定に使う内容は、公式資料との照合が必要となる
今回の結果は少数の質問による初期確認として掲載する。モデル・量子化・思考設定が異なる構成のため、モデルや実行ツール単体の性能・優劣を判断するものではない
実施環境
使用する環境は下記のとおり (2026.10.18 実施時点)
| 項目 | 内容 |
|---|---|
| 本体 | Mac mini (Mac18,5) |
| チップ | Apple M6 |
| CPU | 12コア(スーパーコア × 2、高性能コア × 4、高効率コア × 6) |
| GPU | 12コア |
| ユニファイドメモリ | 32GB(CPU・GPU で共有) |
| メモリ帯域幅 | 170GB/s(32GB構成の公称値) |
| 内蔵 SSD | 512GB |
| OS | macOS 27 Golden Gate 27.0.1 |
| Ollama | 0.40.6 |
| Splash | 1.3.2 |
| Homebrew | 7.0.9 |
| Command Line Tools for Xcode | 27.0(Homebrew インストーラで導入) |
| Ollama のモデル |
gpt-oss:20b / MXFP4 |
| Splash のモデル | Qwen3.8-27B / Unsloth UD-IQ4_XS
|
| Mac mini の LAN IP | 192.168.129.6 |
| API 接続を許可するネットワーク | 192.168.129.0/24 |
使用する実行ツール
今回は Ollama と Splash を使う。どちらもローカルで LLM を動かし、HTTP API で呼び出せる。まず各ツールの役割と選定理由を整理する
普及状況の参考として GitHub のスター数や公式組織のフォロワー数も記載する。いずれも関心の目安で、実際の利用者数や利用率を示すものではなく、異なる指標同士の単純比較はできない
Ollama
Ollama は、LLM のダウンロードや実行、API 経由での利用ができるツールとなる
Ollama チームが開発・公開するオープンソースのツールで、macOS・Windows・Linux に対応している。2026年10月18日の確認時点で GitHub のスターは約18.4万あり、公式 README には多数の連携ツールも掲載されている。公式リポジトリ
今回は、モデルの取得から API サーバの操作までコマンドで手順を残せる Homebrew の CLI 版を選んだ。Splash と導入・更新方法を揃え、最初のチャット・API 試験の比較基準にする。Homebrew の Ollama
Splash
Splash は、Apple Silicon 向けの推論エンジン兼 API サーバとなる
Inco AI が開発・公開するオープンソースのソフトウェアとなる。2026年10月18日の確認時点で GitHub のスターは約1,500となる。公式リポジトリ
対応モデル向けの Metal カーネルや、ドラフトモデルの先読み候補を対象モデルで検証する投機的デコードなどで高速化する。この最適化を使った構成の使用感を確認するために選んだ。OpenAI 互換 API にも対応し、Ollama と同じ形式で質問を送れる
LM Studio(今回は未実施)
LM Studio は、GUI でモデルを検索し、設定や回答を確認しながら試せる。API 提供にも対応しており、llmster を使えば GUI なしでの運用もできる。ヘッドレス運用
Element Labs, Inc. が開発するアプリケーションで、公式 GitHub では CLI や SDK なども公開している。2026年10月18日の確認時点で、GitHub の公式組織のフォロワーは約1.23万となる。
今回は CLI・API 中心で導入する Ollama と、Apple Silicon 向けの高速化を試す Splash に絞るため、試してはいない
API の利用と認証
API では、モデル名と質問を JSON 形式で HTTP サーバに送り、生成された回答を受け取る。今回は両方が対応する POST /v1/chat/completions に curl からリクエストを送る。OpenAI 互換の形式を使うが、処理は本機にダウンロードしたモデルで行う
| 項目 | Ollama | Splash |
|---|---|---|
| 既定の待ち受け先 | 127.0.0.1:11434 |
127.0.0.1:8000 |
| 今回使う API | http://127.0.0.1:11434/v1/chat/completions |
http://127.0.0.1:8000/v1/chat/completions |
| 既定の認証 | ローカル API は認証不要 | API キー未設定では認証なし |
| 認証を付ける場合 | 認証付きリバースプロキシなどを別途設ける |
--api-key または SPLASH_API_KEY で設定できる |
Ollama のローカル API は API キーを必要としない。Ollama のクラウド API 用キーは別の仕組みで、発行しても本機のローカル API に認証が追加されるわけではない。Ollama の認証仕様
Splash はキーを設定すると、API リクエストに Authorization: Bearer <キー> または x-api-key: <キー> が必要になる。ただしチャット画面・ヘルスチェックなど、一部の経路は認証の対象外となる。Splash の認証仕様
最初の同一 Mac 内での試験は既定のローカル待ち受けを使い、curl に認証ヘッダーを付けない。127.0.0.1 は同じ Mac から接続するアドレスで、既定では別端末から直接接続できない。ただし同じ Mac 内のプログラムからは呼び出せるため、ローカル待ち受け自体がユーザー認証になるわけではない。Ollama の待ち受け仕様
後半では待ち受けを Mac Mini の NIC IP アドレスの 192.168.129.6 に変更し、LAN 内からも応答確認する。Splash は API キー認証を有効にする。Ollama の直接接続は認証なしのため、PF で 192.168.129.0/24 に接続元を制限した検証用 LAN で短時間の疎通確認に使う。継続運用やインターネット経由の利用では、認証・接続制限・TLS または VPN による通信保護の別途設計が必要(今回は対象外)
試験モデル
初回は「API 提供の基準を作る構成」と「Apple Silicon 向けの高速化を試す構成」の2つに絞っている
| 構成 | 選定理由・補足 | 配布サイズの目安 |
|---|---|---|
| Ollama + gpt-oss 20B / MXFP4 | 公式導入ガイドで16GB以上の VRAM またはユニファイドメモリでの利用が案内されており、32GB環境の初期候補とした。テキスト API の比較基準にし、今後は関数呼び出しや構造化出力も評価したい | 約14GB |
| Splash + Qwen3.8-27B / Unsloth UD-IQ4_XS | Splash 1.3.2 の対応モデルで、技術的な質問への回答と投機的デコードを使った構成の使用感を確認するために選定。32GB環境でのメモリの余裕を重視し、UD-Q4_K_M(約16.5GB)より約2.2GB小さい UD-IQ4_XS を採用した | 対象 GGUF 約14.3GB。ドラフトモデル(先読み用)約3.85GBは別途必要で、合計約18.1GB |
配布サイズは実行時メモリとは異なり、小さい方が必ず高速とは限らない。
モデル選定は詳しくないので別途試験が必要で今回は試すこと優先で選んだだけとなっている
事前準備
購入直後の環境を整えるため、Ollama と Splash の導入に使う Homebrew を先に準備する
利用バージョンの方針
検証には実施環境の表に記載した安定版を使用した。Homebrew の配布版は更新されるため、後日同じコマンドを実行しても同じ版が入るとは限らない。導入時にバージョンを確認し、比較期間中は brew pin で更新を止める。具体的な確認・設定は各ツールのインストール手順に記載する
モデルもタグの内容が更新される場合があるため、Ollama のモデル ID と Splash の対象・ドラフトのリビジョンを記録する。Splash の SSD キャッシュはディスク容量の見積もりに含める
ディスク容量の見積もり
導入時の容量を下表のとおり見積もり、更新や一時ファイルの余裕を含めて100GB程度の空きを確保する
| 用途 | ディスク容量の目安 | 補足 |
|---|---|---|
| Ollama の gpt-oss 20B | 約14GB | モデルの配布サイズ |
| Splash の Qwen3.8 とドラフトなど | 約17〜22GB | 対象 GGUF 約14.3GBとドラフトなどの概算。保存済みの重みを再利用するため、モデルとダウンロードキャッシュを二重計上しない |
| アプリ・Homebrew・依存関係など | 約5〜10GB | Command Line Tools などを含む概算 |
| Splash の SSD キャッシュ | 上限32GiB(約34GB) | 今回の既定設定。空き容量に応じて自動調整され、常に全量を使うわけではない。今回の起動方法では終了時に解放される |
| 合計 | 初期導入:約36〜46GB/キャッシュ込み:約70〜80GB | キャッシュ込みは上限の約34GBを加えた場合 |
上記は概算で、実測値や最低動作要件ではない。macOS 本体・他のアプリ・スワップ・追加モデルは含めず、実際の空き容量の変化は導入前後に測定する
初期起動からセットアップ
購入直後の Mac は、下記の設定を済ませておく
| 設定項目 | 操作 | 補足 |
|---|---|---|
| 初回起動の設定 | 言語設定(日本語)、アカウント作成、Apple Account 認証などを進める | 画面の案内に従う |
| OS アップデート | 「システム設定 → 一般 → ソフトウェアアップデート」で最新版に更新する | 更新後の OS バージョンを記録する |
| 自動スリープの解除 | 「システム設定 → エネルギー」で「ディスプレイがオフの時に自動でスリープさせない」をオンにする | API サーバの利用中にスリープしないようにする |
| ネットワーク設定 | 環境に合わせて固定 IP などを設定し、Wi-Fi が不要ならオフにする | 必要に応じて設定する |
| 画面共有 | 「システム設定 → 一般 → 共有 → 画面共有」をオンにする | リモートで画面操作する場合に設定する |
| SSH 接続 | 「システム設定 → 一般 → 共有 → リモートログイン」をオンにする | リモートでターミナル操作する場合に設定する |
導入前のディスク容量を記録する
Homebrew やモデルのインストールを始める前に、Mac mini 側でデータボリュームの空き容量を保存する。最後に同じボリュームを測定し、空き容量の減少分を表示する
df -h /System/Volumes/Data
mkdir -p "$HOME/local-llm-measurements"
LC_ALL=C df -kP /System/Volumes/Data > "$HOME/local-llm-measurements/disk-before.txt"
cat "$HOME/local-llm-measurements/disk-before.txt"
df -h は目視確認用、保存する df -kP は差分計算用となる。記録はファイルに残すため、途中でターミナルを閉じても比較できる。導入後にこの手順を再実行して、導入前の記録を上書きしないようにする
導入前のディスク容量の測定ログ
suzuyu@macmini-m6 ~ % df -h /System/Volumes/Data
mkdir -p "$HOME/local-llm-measurements"
LC_ALL=C df -kP /System/Volumes/Data > "$HOME/local-llm-measurements/disk-before.txt"
cat "$HOME/local-llm-measurements/disk-before.txt"
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk3s5 460Gi 35Gi 400Gi 8% 343k 4.2G 0% /System/Volumes/Data
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/disk3s5 482746452 36382052 419935276 8% /System/Volumes/Data
suzuyu@macmini-m6 ~ %
Homebrew のインストール
Splash のインストールに使う Homebrew を導入する。macOS の標準ターミナル(zsh)で、公式サイト に掲載されているコマンドを実行する
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Homebrew のインストールログ
suzuyu@macmini-m6 ~ % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
==> Checking for `sudo` access (which may request your password)...
Password:
==> This script will install:
/opt/homebrew/bin/brew
/opt/homebrew/share/doc/homebrew
/opt/homebrew/share/man/man1/brew.1
/opt/homebrew/share/zsh/site-functions/_brew
/opt/homebrew/etc/bash_completion.d/brew
/opt/homebrew
/etc/paths.d/homebrew
==> The following new directories will be created:
/opt/homebrew/bin
/opt/homebrew/etc
/opt/homebrew/include
/opt/homebrew/lib
/opt/homebrew/sbin
/opt/homebrew/share
/opt/homebrew/var
/opt/homebrew/opt
/opt/homebrew/share/zsh
/opt/homebrew/share/zsh/site-functions
/opt/homebrew/var/homebrew
/opt/homebrew/var/homebrew/linked
/opt/homebrew/Cellar
/opt/homebrew/Caskroom
/opt/homebrew/Frameworks
==> The Xcode Command Line Tools will be installed.
Press RETURN/ENTER to continue or any other key to abort:
==> /usr/bin/sudo /usr/bin/install -d -o suzuyu -g admin -m 0755 /opt/homebrew
==> Searching online for the Command Line Tools
==> Installing Command Line Tools for Xcode 27.0-27.0
Software Update Tool
Finding available software
Downloading Command Line Tools for Xcode 27.0
Downloaded Command Line Tools for Xcode 27.0
Installing Command Line Tools for Xcode 27.0
Done with Command Line Tools for Xcode 27.0
Done.
==> /usr/bin/sudo /usr/bin/xcode-select --switch /Library/Developer/CommandLineTools
==> Found Git: /Library/Developer/CommandLineTools/usr/bin/git
==> Downloading and installing Homebrew...
remote: Enumerating objects: 367837, done.
remote: Counting objects: 100% (997/997), done.
remote: Compressing objects: 100% (219/219), done.
remote: Total 367837 (delta 870), reused 823 (delta 778), pack-reused 366840 (from 2)
remote: Enumerating objects: 55, done.
remote: Counting objects: 100% (33/33), done.
remote: Total 55 (delta 33), reused 33 (delta 33), pack-reused 22 (from 1)
==> /usr/bin/sudo /usr/bin/install -o root -g wheel -m 0644 /var/folders/vm/x0l5wqcx7njgdqpfvbpp20n00000gn/T/tmp.vXAXi5cpXX /etc/paths.d/homebrew
==> Updating Homebrew...
==> Downloading https://ghcr.io/v2/homebrew/core/portable-ruby/blobs/sha256:e0088dff5614b39387300136ec7a5f95bf1e07589547245c919524fc9e8b4197
######################################################################################################################################################################################################################################################### 100.0%
==> Pouring portable-ruby-4.0.7.arm64_big_sur.bottle.tar.gz
==> Installation successful!
==> Homebrew has enabled anonymous aggregate formulae and cask analytics.
Read the analytics documentation (and how to opt-out) here:
https://docs.brew.sh/Analytics
No analytics data has been sent yet (nor will any be during this install run).
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
https://github.com/Homebrew/brew#donations
==> Next steps:
- Run these commands in your terminal to add Homebrew to your PATH:
echo >> /Users/suzuyu/.zprofile
echo 'eval "$(/opt/homebrew/bin/brew shellenv zsh)"' >> /Users/suzuyu/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv zsh)"
- Run brew help to get started
- Further documentation:
https://docs.brew.sh
suzuyu@macmini-m6 ~ %
表示されるインストール内容を確認し、案内に従って進める。パスワードを求められた場合は Mac のログインパスワードを入力する。入力中は文字が表示されない
今回の環境では、Homebrew インストーラが Command Line Tools for Xcode 27.0 を自動でインストールした。別途 xcode-select --install を実行する必要はなかった
Homebrew の PATH 設定と確認
インストール完了時の Next steps に従って設定する。Apple Silicon の標準インストール先は /opt/homebrew となる。zsh では下記を一度実行し、今後開くターミナルと現在のターミナルの両方で brew を使えるようにする
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
command -v brew
brew --version
brew --prefix
Homebrew の PATH・バージョン確認ログ
suzuyu@macmini-m6 ~ % echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
suzuyu@macmini-m6 ~ % eval "$(/opt/homebrew/bin/brew shellenv)"
suzuyu@macmini-m6 ~ % command -v brew
/opt/homebrew/bin/brew
suzuyu@macmini-m6 ~ % brew --version
Homebrew 7.0.9
suzuyu@macmini-m6 ~ % brew --prefix
/opt/homebrew
suzuyu@macmini-m6 ~ %
Homebrew インストール後の出力で出るものと同じのため、表示される Next steps の操作ですでに .zprofile へ同じ設定を追加した場合、上記の echo は省略する。
command -v brew が /opt/homebrew/bin/brew、brew --prefix が /opt/homebrew となり、バージョンが表示されることを確認する
今回の環境では、Homebrew 7.0.9、インストール先 /opt/homebrew を確認した
macOS ファイアウォールと接続元の制限
LAN 向けにサーバを起動する前に、今回の API の2ポートを下記の範囲に制限する。アプリ単位の許可には macOS ファイアウォール、接続元の制限には PF を使う
| 送信元 | 宛先 | プロトコル・ポート | 扱い |
|---|---|---|---|
192.168.129.0/24 |
192.168.129.6 |
TCP 11434 / 8000 | 許可 |
| 上記以外の IPv4 アドレス | 192.168.129.6 |
TCP 11434 / 8000 | 拒否 |
LAN 試験時は、サーバを 192.168.129.6 にバインドする。Ollama は許可範囲内で認証なしに利用できるため、信頼できる検証用 LAN を前提とする
アプリケーションファイアウォール
「システム設定 → ネットワーク → ファイアウォール」をオンにし、「オプション」の「すべての受信接続をブロック」はオフにする。Ollama / Splash の導入・起動後、実際に待ち受けるサーバの実行ファイルを確認し、受信接続を許可する。Apple の設定手順
PF のルールを作成する
購入直後で、VPN などが独自の PF 設定を管理していない環境向けの例。メイン設定の再読込は既存ルールに影響する場合があるため、既存設定がある場合は先に確認する。操作は Mac 本体のターミナルで行う
設定をバックアップし、専用のルールファイルを作成する。cp -n は既存のバックアップを上書きしない
sudo cp -p -n /etc/pf.conf /etc/pf.conf.before-local-llm
sudo tee /etc/pf.anchors/local.llm >/dev/null <<'EOF'
llm_host = "192.168.129.6"
llm_net = "192.168.129.0/24"
llm_ports = "{ 11434, 8000 }"
pass in inet proto tcp from $llm_net to $llm_host port $llm_ports flags S/SA keep state
block return in quick inet proto tcp from ! $llm_net to $llm_host port $llm_ports
EOF
PF ルールの作成ログ
suzuyu@macmini-m6 ~ % sudo cp -p -n /etc/pf.conf /etc/pf.conf.before-local-llm
Password:
suzuyu@macmini-m6 ~ % sudo tee /etc/pf.anchors/local.llm >/dev/null <<'EOF'
llm_host = "192.168.129.6"
llm_net = "192.168.129.0/24"
llm_ports = "{ 11434, 8000 }"
pass in inet proto tcp from $llm_net to $llm_host port $llm_ports flags S/SA keep state
block return in quick inet proto tcp from ! $llm_net to $llm_host port $llm_ports
EOF
suzuyu@macmini-m6 ~ % sudo nano /etc/pf.conf
suzuyu@macmini-m6 ~ % cat /etc/pf.conf
#
# Default PF configuration file.
#
# This file contains the main ruleset, which gets automatically loaded
# at startup. PF will not be automatically enabled, however. Instead,
# each component which utilizes PF is responsible for enabling and disabling
# PF via -E and -X as documented in pfctl(8). That will ensure that PF
# is disabled only when the last enable reference is released.
#
# Care must be taken to ensure that the main ruleset does not get flushed,
# as the nested anchors rely on the anchor point defined here. In addition,
# to the anchors loaded by this file, some system services would dynamically
# insert anchors into the main ruleset. These anchors will be added only when
# the system service is used and would removed on termination of the service.
#
# See pf.conf(5) for syntax.
#
#
# com.apple anchor point
#
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"
suzuyu@macmini-m6 ~ % cat /etc/pf.anchors/local.llm
llm_host = "192.168.129.6"
llm_net = "192.168.129.0/24"
llm_ports = "{ 11434, 8000 }"
pass in inet proto tcp from $llm_net to $llm_host port $llm_ports flags S/SA keep state
block return in quick inet proto tcp from ! $llm_net to $llm_host port $llm_ports
suzuyu@macmini-m6 ~ %
メイン設定から読み込む
sudo nano /etc/pf.conf
既存の Apple 用設定を残し、下記の local.llm を含む2行だけを追加する。anchor "local.llm" はフィルタ用の最初の anchor 行の直前(scrub-anchor・nat-anchor・rdr-anchor より後)、load anchor "local.llm" ... はファイル末尾に置く
下記は標準構成の該当部分の抜粋。ファイル全体を置き換えず、再実行時も同じ行を重複させない
anchor "local.llm"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"
load anchor "local.llm" from "/etc/pf.anchors/local.llm"
PF メイン設定の確認ログ
suzuyu@macmini-m6 ~ % cat /etc/pf.conf
#
# Default PF configuration file.
#
# This file contains the main ruleset, which gets automatically loaded
# at startup. PF will not be automatically enabled, however. Instead,
# each component which utilizes PF is responsible for enabling and disabling
# PF via -E and -X as documented in pfctl(8). That will ensure that PF
# is disabled only when the last enable reference is released.
#
# Care must be taken to ensure that the main ruleset does not get flushed,
# as the nested anchors rely on the anchor point defined here. In addition,
# to the anchors loaded by this file, some system services would dynamically
# insert anchors into the main ruleset. These anchors will be added only when
# the system service is used and would removed on termination of the service.
#
# See pf.conf(5) for syntax.
#
#
# com.apple anchor point
#
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
dummynet-anchor "com.apple/*"
anchor "local.llm"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"
load anchor "local.llm" from "/etc/pf.anchors/local.llm"
suzuyu@macmini-m6 ~ %
構文確認と適用
構文を確認し、直後の終了コードが 0 なら読み込みへ進む。エラーがあれば修正し、再確認する
sudo pfctl -nf /etc/pf.conf
echo $?
sudo pfctl -f /etc/pf.conf
echo $?
読込の終了コードも 0 なら、PF を有効化して状態とルールを確認する。-E は1回だけ実行し、出力される Token は設定を戻す際に使うため控える
sudo pfctl -E
sudo pfctl -s info
sudo pfctl -sr
sudo pfctl -a local.llm -sr
| 確認対象 | 成功時の表示 |
|---|---|
| PF の状態 | Status: Enabled |
| メインルール | anchor "local.llm" |
| 専用 anchor | TCP 11434 / 8000 の許可2件・拒否2件 |
今回の環境では上記を確認できた。実通信の確認は、後半の「別PC・サーバからの API 応答確認」で新しい接続を使って行う
pfctl の注意書きと ALTQ の通知について
pfctl: Use of -f option, could result in flushing of rules
present in the main ruleset added by the system at startup.
See /etc/pf.conf for further details.
-f でメインルールを読み込む際の一般的な注意書きで、この表示自体は構文エラーではない。-nf は構文確認のみで、実際のルールは変更しない
No ALTQ support in kernel
ALTQ related functions disabled
ALTQ による帯域制御・キュー制御がカーネルで利用できないという通知。今回の IP・ポートによる制限では ALTQ を使わないため、この表示自体は読込失敗を意味しない。成否は終了コードと読み込まれたルールで確認する
PF の適用・状態確認ログ
suzuyu@macmini-m6 ~ % sudo pfctl -f /etc/pf.conf
pfctl: Use of -f option, could result in flushing of rules
present in the main ruleset added by the system at startup.
See /etc/pf.conf for further details.
No ALTQ support in kernel
ALTQ related functions disabled
suzuyu@macmini-m6 ~ % echo $?
0
suzuyu@macmini-m6 ~ % sudo pfctl -E
No ALTQ support in kernel
ALTQ related functions disabled
pf enabled
Token : 923634586225060667
suzuyu@macmini-m6 ~ % sudo pfctl -s info
No ALTQ support in kernel
ALTQ related functions disabled
Status: Enabled for 0 days 00:00:05 Debug: Urgent
State Table Total Rate
current entries 0
searches 441 88.2/s
inserts 0 0.0/s
removals 0 0.0/s
Counters
match 379 75.8/s
bad-offset 0 0.0/s
fragment 0 0.0/s
short 0 0.0/s
normalize 0 0.0/s
memory 0 0.0/s
bad-timestamp 0 0.0/s
congestion 0 0.0/s
ip-option 0 0.0/s
proto-cksum 0 0.0/s
state-mismatch 0 0.0/s
state-insert 0 0.0/s
state-limit 0 0.0/s
src-limit 0 0.0/s
synproxy 0 0.0/s
dummynet 0 0.0/s
invalid-port 0 0.0/s
suzuyu@macmini-m6 ~ % sudo pfctl -sr
No ALTQ support in kernel
ALTQ related functions disabled
scrub-anchor "com.apple/*" all fragment reassemble
anchor "local.llm" all
anchor "com.apple/*" all
suzuyu@macmini-m6 ~ % sudo pfctl -a local.llm -sr
No ALTQ support in kernel
ALTQ related functions disabled
pass in inet proto tcp from 192.168.129.0/24 to 192.168.129.6 port = 11434 flags S/SA keep state
pass in inet proto tcp from 192.168.129.0/24 to 192.168.129.6 port = 8000 flags S/SA keep state
block return in quick inet proto tcp from ! 192.168.129.0/24 to 192.168.129.6 port = 11434
block return in quick inet proto tcp from ! 192.168.129.0/24 to 192.168.129.6 port = 8000
suzuyu@macmini-m6 ~ %
今回は再起動時の自動適用を設定しない。Mac を再起動した場合は、LAN 向けサーバの起動前に構文確認・読込・有効化と状態確認をやり直す。再起動していなければ -E は重ねず、状態確認のみ行う
設定を解除する場合は、後半の「利用後の停止と設定の切り戻し」を参照する
Ollama のインストール・起動・応答確認
公式ドキュメントでは macOS Sonoma 14 以降が必要で、Apple M シリーズでは CPU と GPU がサポートされている。macOS の要件
Homebrew でインストールする
事前準備で導入した Homebrew を使い、CLI 版の ollama をインストールする。2026-10-18 時点の配布版は0.40.6となる。Homebrew の配布情報、Ollama 0.40.6
まず配布バージョンを確認する
brew update
brew info ollama
Ollama の配布情報の確認ログ
suzuyu@macmini-m6 ~ % brew update
brew info ollama
==> Updating Homebrew...
==> Homebrew collects anonymous analytics.
Read the analytics documentation (and how to opt-out) here:
https://docs.brew.sh/Analytics
No analytics have been recorded yet (nor will be during this `brew` run).
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
https://github.com/Homebrew/brew#-donations
Updated 1 tap (homebrew/core).
==> New Formulae
bqlog: Cross-language logging library
==> ollama: stable 0.40.6 (bottled), HEAD
Create, run, and share large language models (LLMs)
https://ollama.com/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/o/ollama.rb
License: MIT
==> Dependencies
Required (1): mlx-c
==> Options
--HEAD
Install HEAD version
==> Caveats
To start ollama now and restart at login:
brew services start ollama
Or, if you don't want/need a background service you can just run:
OLLAMA_FLASH_ATTENTION="1" OLLAMA_KV_CACHE_TYPE="q8_0" /opt/homebrew/opt/ollama/bin/ollama serve
==> Downloading https://formulae.brew.sh/api/formula/ollama.json
==> Analytics
install: 54,285 (30 days), 143,032 (90 days), 639,496 (365 days)
install-on-request: 54,186 (30 days), 142,813 (90 days), 639,009 (365 days)
build-error: 160 (30 days)
suzuyu@macmini-m6 ~ %
配布版が0.40.6であることを確認してインストールする。将来の実行時に別の版になっている場合、このコマンドでは0.40.6を指定できないため、導入版と検証条件を見直して記録する
brew install ollama
brew list --versions ollama
command -v ollama
Ollama のインストールログ
suzuyu@macmini-m6 ~ % brew install ollama
==> Downloading bottle manifests
✔︎ Bottle Manifest ollama (0.40.6) Downloaded 50.4KB/ 50.4KB
==> Would install 1 formula:
ollama 0.40.6
==> Would install 11 dependencies for ollama:
ca-certificates
mpdecimal
openssl@4
readline
sqlite
xz
lz4
zstd
python@3.15
mlx
mlx-c
==> Do you want to proceed with the installation? [y/n]
==> Fetching downloads for: ollama
✔︎ Bottle Manifest ca-certificates (2026-09-25) Downloaded 8.1KB/ 8.1KB
✔︎ Bottle Manifest openssl@4 (4.0.3) Downloaded 19.5KB/ 19.5KB
✔︎ Bottle Manifest sqlite (3.54.1) Downloaded 22.6KB/ 22.6KB
✔︎ Bottle Manifest xz (5.8.4) Downloaded 14.6KB/ 14.6KB
✔︎ Bottle Manifest zstd (1.5.7_1) Downloaded 17.8KB/ 17.8KB
✔︎ Bottle Manifest lz4 (1.10.0) Downloaded 13.1KB/ 13.1KB
✔︎ Bottle Manifest mlx (0.32.4) Downloaded 36.4KB/ 36.4KB
✔︎ Bottle Manifest mpdecimal (4.0.1) Downloaded 16.6KB/ 16.6KB
✔︎ Bottle Manifest mlx-c (0.7.0_1) Downloaded 38.9KB/ 38.9KB
✔︎ Bottle Manifest readline (8.3.6) Downloaded 15.3KB/ 15.3KB
✔︎ Bottle Manifest python@3.15 (3.15.0) Downloaded 108.8KB/108.8KB
✔︎ Bottle ca-certificates (2026-09-25) Downloaded 112.2KB/112.2KB
✔︎ Bottle mpdecimal (4.0.1) Downloaded 185.8KB/185.8KB
✔︎ Bottle lz4 (1.10.0) Downloaded 286.9KB/286.9KB
✔︎ Bottle xz (5.8.4) Downloaded 788.8KB/788.8KB
✔︎ Bottle readline (8.3.6) Downloaded 757.0KB/757.0KB
✔︎ Bottle mlx-c (0.7.0_1) Downloaded 225.7KB/225.7KB
✔︎ Bottle zstd (1.5.7_1) Downloaded 795.2KB/795.2KB
✔︎ Bottle sqlite (3.54.1) Downloaded 2.5MB/ 2.5MB
✔︎ Bottle openssl@4 (4.0.3) Downloaded 11.2MB/ 11.2MB
✔︎ Bottle ollama (0.40.6) Downloaded 17.6MB/ 17.6MB
✔︎ Bottle python@3.15 (3.15.0) Downloaded 20.2MB/ 20.2MB
✔︎ Bottle mlx (0.32.4) Downloaded 76.9MB/ 76.9MB
==> Installing dependencies for ollama: ca-certificates, mpdecimal, openssl@4, readline, sqlite, xz, lz4, zstd, python@3.15, mlx and mlx-c
==> Installing ollama dependency: ca-certificates (2026-09-25)
==> Pouring ca-certificates--2026-09-25.all.bottle.tar.gz
🍺 /opt/homebrew/Cellar/ca-certificates/2026-09-25: 5 files, 204.4KB
==> Installing ollama dependency: mpdecimal (4.0.1)
==> Pouring mpdecimal--4.0.1.arm64_golden_gate.bottle.1.tar.gz
🍺 /opt/homebrew/Cellar/mpdecimal/4.0.1: 22 files, 627.4KB
==> Installing ollama dependency: openssl@4 (4.0.3)
==> Pouring openssl@4--4.0.3.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/openssl@4/4.0.3: 6,820 files, 23.8MB
==> Installing ollama dependency: readline (8.3.6)
==> Pouring readline--8.3.6.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/readline/8.3.6: 56 files, 2.6MB
==> Installing ollama dependency: sqlite (3.54.1)
==> Pouring sqlite--3.54.1.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/sqlite/3.54.1: 13 files, 5.4MB
==> Installing ollama dependency: xz (5.8.4)
==> Pouring xz--5.8.4.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/xz/5.8.4: 96 files, 2.7MB
==> Installing ollama dependency: lz4 (1.10.0)
==> Pouring lz4--1.10.0.arm64_golden_gate.bottle.3.tar.gz
🍺 /opt/homebrew/Cellar/lz4/1.10.0: 28 files, 774.1KB
==> Installing ollama dependency: zstd (1.5.7_1)
==> Pouring zstd--1.5.7_1.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/zstd/1.5.7_1: 32 files, 2.3MB
==> Installing ollama dependency: python@3.15 (3.15.0)
==> Pouring python@3.15--3.15.0.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/python@3.15/3.15.0: 3,983 files, 79MB
==> Installing ollama dependency: mlx (0.32.4)
==> Pouring mlx--0.32.4.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/mlx/0.32.4: 466 files, 240MB
==> Installing ollama dependency: mlx-c (0.7.0_1)
==> Pouring mlx-c--0.7.0_1.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/mlx-c/0.7.0_1: 40 files, 1MB
==> Installing ollama
==> Pouring ollama--0.40.6.arm64_golden_gate.bottle.tar.gz
🍺 /opt/homebrew/Cellar/ollama/0.40.6: 15 files, 54MB
==> Caveats
==> ollama
To start ollama now and restart at login:
brew services start ollama
Or, if you don't want/need a background service you can just run:
OLLAMA_FLASH_ATTENTION="1" OLLAMA_KV_CACHE_TYPE="q8_0" /opt/homebrew/opt/ollama/bin/ollama serve
suzuyu@macmini-m6 ~ % brew list --versions ollama
ollama 0.40.6
suzuyu@macmini-m6 ~ % command -v ollama
/opt/homebrew/bin/ollama
suzuyu@macmini-m6 ~ %
導入版が0.40.6、コマンドのパスが /opt/homebrew/bin/ollama であることを確認し、比較期間中の更新を防ぐ
brew pin ollama
pin は導入済みの版を保持する操作で、特定の旧版を取得するものではない。比較を終えて更新する際は brew unpin ollama で解除する
起動確認
Homebrew のサービスとして起動する。手動の ollama serve が動いている場合は、そのターミナルで Ctrl+C を押して終了してから実行する
brew services start ollama
brew services list
ollama が started となることを確認する。今回は sudo を付けず、ログインユーザーのサービスとして登録する。ターミナルを閉じても動作し、次回ログイン時にも起動する。OS 起動時からログイン前に動かす設定は今回の対象外となる。Homebrew services
続けて、クライアントとサーバのバージョン、待ち受け先を確認する。サービスの起動直後に接続できない場合は、起動を待って再確認する
ollama --version
curl --fail-with-body http://127.0.0.1:11434/api/version
lsof -nP -iTCP:11434 -sTCP:LISTEN
バージョンが0.40.6、待ち受け先が 127.0.0.1:11434 であることを確認する。サーバの再起動は brew services restart ollama、停止とログイン時の自動起動解除は brew services stop ollama で行う
Homebrew のサービスには、今回の配布情報で OLLAMA_FLASH_ATTENTION=1 と OLLAMA_KV_CACHE_TYPE=q8_0 が設定されている。比較時にはサービス設定とサーバログを確認し、実際の適用状況を記録する
% curl --fail-with-body http://127.0.0.1:11434/api/version
{"version":"0.40.6"}%
続けてモデル一覧を確認する
ollama ls
初回はモデルが未登録でも問題ない。サーバへの接続エラーが出ず、一覧を取得できることを確認する
Ollama の起動確認ログ
suzuyu@macmini-m6 ~ % command -v ollama
/opt/homebrew/bin/ollama
suzuyu@macmini-m6 ~ %
suzuyu@macmini-m6 ~ % brew pin ollama
suzuyu@macmini-m6 ~ %
suzuyu@macmini-m6 ~ % ollama --version
ollama version is 0.40.6
suzuyu@macmini-m6 ~ % curl --fail-with-body http://127.0.0.1:11434/api/version
{"version":"0.40.6"}%
suzuyu@macmini-m6 ~ % lsof -nP -iTCP:11434 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ollama 6835 suzuyu 4u IPv4 0xa5d1408b43cabbe0 0t0 TCP 127.0.0.1:11434 (LISTEN)
suzuyu@macmini-m6 ~ % ollama ls
NAME ID SIZE MODIFIED
suzuyu@macmini-m6 ~ %
gpt-oss 20B のダウンロードと起動
ダウンロード
下記でモデルをダウンロードする
ollama pull gpt-oss:20b
完了後、一覧を確認する
ollama ls
% ollama ls
NAME ID SIZE MODIFIED
gpt-oss:20b f38aa0c53da5 13 GB 51 seconds ago
モデル名に加えて ID とサイズも記録しておく。同じタグでも配布内容が更新されることがあるため、検証時点の情報を残す
gpt-oss 20B のダウンロードログ
suzuyu@macmini-m6 ~ % ollama pull gpt-oss:20b
pulling manifest
pulling 8ecb5d4ad617: 100% ▕███████████████████████████▏ 13 GB
pulling d8ba2f9a17b3: 100% ▕███████████████████████████▏ 18 B
pulling e103573196b6: 100% ▕███████████████████████████▏ 139 B
verifying sha256 digest
writing manifest
success
suzuyu@macmini-m6 ~ % ollama ls
NAME ID SIZE MODIFIED
gpt-oss:20b f38aa0c53da5 13 GB 51 seconds ago
suzuyu@macmini-m6 ~ %
コンテキスト長を8Kに設定する
検証用の作業ディレクトリを作成する。以降の設定・リクエスト・結果ファイルはここに保存する
mkdir -p ~/macmini-localllm/verification
cd ~/macmini-localllm/verification
OpenAI 互換 API ではコンテキスト長を直接指定できないため、8Kを設定したモデル名を作る。再学習ではなく、取得済みモデルに設定を付ける操作となる。設定方法
cat > Modelfile.gpt-oss-20b <<'EOF'
FROM gpt-oss:20b
PARAMETER num_ctx 8192
EOF
ollama create gpt-oss-20b-8k -f Modelfile.gpt-oss-20b
ollama show --modelfile gpt-oss-20b-8k
以下の実行では gpt-oss-20b-8k を使う
モデル作成ログと Modelfile 全文
suzuyu@macmini-m6 ~ % mkdir -p ~/macmini-localllm/verification
cd ~/macmini-localllm/verification
suzuyu@macmini-m6 verification % cat > Modelfile.gpt-oss-20b <<'EOF'
FROM gpt-oss:20b
PARAMETER num_ctx 8192
EOF
suzuyu@macmini-m6 verification % ollama create gpt-oss-20b-8k -f Modelfile.gpt-oss-20b
gathering model components
using existing layer sha256:8ecb5d4ad6172c63f9cbf1a07a04e525985a8a11a6e516da02b674c4010de716
using existing layer sha256:fa6710a93d78da62641e192361344be7a8c0a1c3737f139cf89f20ce1626b99c
using existing layer sha256:f60356777647e927149cbd4c0ec1314a90caba9400ad205ddc4ce47ed001c2d6
creating new layer sha256:e8fcfac0b1234eeb8c811092c47d0dfa6f9368b9171ba2abfcabed9e22ea9dc5
writing manifest
success
suzuyu@macmini-m6 verification % ollama show --modelfile gpt-oss-20b-8k
# Modelfile generated by "ollama show"
# To build a new Modelfile based on this, replace FROM with:
# FROM gpt-oss-20b-8k:latest
FROM /Users/suzuyu/.ollama/models/blobs/sha256-8ecb5d4ad6172c63f9cbf1a07a04e525985a8a11a6e516da02b674c4010de716
TEMPLATE """<|start|>system<|message|>You are ChatGPT, a large language model trained by OpenAI.
Knowledge cutoff: 2024-06
Current date: {{ currentDate }}
{{- if and .IsThinkSet .Think (ne .ThinkLevel "") }}
Reasoning: {{ .ThinkLevel }}
{{- else if or (not .IsThinkSet) (and .IsThinkSet .Think) }}
Reasoning: medium
{{- end }}
{{- $hasNonBuiltinTools := false }}
{{- if .Tools -}}
{{- $hasBrowserSearch := false }}
{{- $hasBrowserOpen := false }}
{{- $hasBrowserFind := false }}
{{- $hasPython := false }}
{{- range .Tools }}
{{- if eq .Function.Name "browser.search" -}}{{- $hasBrowserSearch = true -}}
{{- else if eq .Function.Name "browser.open" -}}{{- $hasBrowserOpen = true -}}
{{- else if eq .Function.Name "browser.find" -}}{{- $hasBrowserFind = true -}}
{{- else if eq .Function.Name "python" -}}{{- $hasPython = true -}}
{{- else }}{{ $hasNonBuiltinTools = true -}}
{{- end }}
{{- end }}
{{- if or $hasBrowserSearch $hasBrowserOpen $hasBrowserFind $hasPython }}
# Tools
{{- if or $hasBrowserSearch $hasBrowserOpen $hasBrowserFind }}
## browser
// Tool for browsing.
// The `cursor` appears in brackets before each browsing display: `[{cursor}]`.
// Cite information from the tool using the following format:
// `【{cursor}†L{line_start}(-L{line_end})?】`, for example: `【6†L9-L11】` or `【8†L3】`.
// Do not quote more than 10 words directly from the tool output.
// sources=web (default: web)
namespace browser {
{{- if $hasBrowserSearch }}
// Searches for information related to `query` and displays `topn` results.
type search = (_: {
query: string,
topn?: number, // default: 10
source?: string,
}) => any;
{{- end }}
{{- if $hasBrowserOpen }}
// Opens the link `id` from the page indicated by `cursor` starting at line number `loc`, showing `num_lines` lines.
// Valid link ids are displayed with the formatting: `【{id}†.*】`.
// If `cursor` is not provided, the most recent page is implied.
// If `id` is a string, it is treated as a fully qualified URL associated with `source`.
// If `loc` is not provided, the viewport will be positioned at the beginning of the document or centered on the most relevant passage, if available.
// Use this function without `id` to scroll to a new location of an opened page.
type open = (_: {
id?: number | string, // default: -1
cursor?: number, // default: -1
loc?: number, // default: -1
num_lines?: number, // default: -1
view_source?: boolean, // default: false
source?: string,
}) => any;
{{- end }}
{{- if $hasBrowserFind }}
// Finds exact matches of `pattern` in the current page, or the page given by `cursor`.
type find = (_: {
pattern: string,
cursor?: number, // default: -1
}) => any;
{{- end }}
} // namespace browser
{{- end }}{{/* end if has browser tools */}}
{{- if $hasPython }}
## python
Use this tool to execute Python code in your chain of thought. The code will not be shown to the user. This tool should be used for internal reasoning, but not for code that is intended to be visible to the user (e.g. when creating plots, tables, or files).
When you send a message containing Python code to python, it will be executed in a stateful Jupyter notebook environment. python will respond with the output of the execution or time out after 120.0 seconds. The drive at '/mnt/data' can be used to save and persist user files. Internet access for this session is UNKNOWN. Depends on the cluster.
{{- end }}{{/* end if hasPython */}}
{{- end }}{{/* end if has any built-in tools */}}
{{- end }}{{/* end if .Tools */}}
# Valid channels: analysis, commentary, final. Channel must be included for every message.{{ if $hasNonBuiltinTools }}
Calls to these tools must go to the commentary channel: 'functions'.
{{- end -}}<|end|>{{/* end of system */ -}}
{{- if or $hasNonBuiltinTools .System -}}
<|start|>developer<|message|>{{- if $hasNonBuiltinTools }}# Tools
## functions
namespace functions {
{{- range .Tools }}
{{- if not (or (eq .Function.Name "browser.search") (eq .Function.Name "browser.open") (eq .Function.Name "browser.find") (eq .Function.Name "python")) }}
{{if .Function.Description }}
// {{ .Function.Description }}
{{- end }}
{{- if and .Function.Parameters.Properties (gt (len .Function.Parameters.Properties) 0) }}
type {{ .Function.Name }} = (_: {
{{- range $name, $prop := .Function.Parameters.Properties }}
{{- if $prop.Description }}
// {{ $prop.Description }}
{{- end }}
{{ $name }}: {{ $prop | toTypeScriptType }},
{{- end }}
}) => any;
{{- else }}
type {{ .Function.Name }} = () => any;
{{- end }}
{{- end }}{{/* end if not browser tool */}}
{{- end }}{{/* end of range .Tools */}}
} // namespace functions
{{- end }}{{/* end if hasNonBuiltinTools */}}
{{- if .System}}
# Instructions
{{ .System }}
{{- end -}}
<|end|>
{{- end -}}
{{- /* Find the index of the last user message */ -}}
{{- $lastUserIdx := -1 }}
{{- $prefillingContent := false }}
{{- $prefillingThinkingOnly := false }}
{{- range $i, $msg := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 -}}
{{- if eq $msg.Role "user" }}
{{- $lastUserIdx = $i }}
{{- end -}}
{{- if and $last (eq $msg.Role "assistant") (gt (len $msg.Content) 0) }}
{{- $prefillingContent = true }}
{{- else if and $last (eq $msg.Role "assistant") (gt (len $msg.Thinking) 0) }}
{{- $prefillingThinkingOnly = true }}
{{- end }}
{{- end -}}
{{- /* Now render messages */ -}}
{{- range $i, $msg := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 -}}
{{- if (ne $msg.Role "system") -}}
{{- if eq $msg.Role "tool" -}}
{{- if or (eq $msg.ToolName "python") (eq $msg.ToolName "browser.search") (eq $msg.ToolName "browser.open") (eq $msg.ToolName "browser.find") -}}
<|start|>{{ $msg.ToolName }} to=assistant<|message|>{{ $msg.Content }}<|end|>
{{- else -}}
<|start|>functions.{{ $msg.ToolName }} to=assistant<|message|>{{ $msg.Content }}<|end|>
{{- end -}}
{{- else if eq $msg.Role "assistant" -}}
{{- if and $msg.Thinking (gt $i $lastUserIdx) -}}{{- /* Show thinking only after last user message */ -}}
<|start|>assistant<|channel|>analysis<|message|>{{ $msg.Thinking }}{{- if not $prefillingThinkingOnly -}}<|end|>{{- end -}}
{{- end -}}
{{- if gt (len $msg.Content) 0 -}}
<|start|>assistant<|channel|>final<|message|>{{ $msg.Content }}{{- if not $prefillingContent -}}<|end|>{{- end -}}
{{- end -}}
{{- if gt (len $msg.ToolCalls) 0 -}}
{{- range $j, $toolCall := $msg.ToolCalls -}}
{{- $isBuiltin := or (eq $toolCall.Function.Name "python") (eq $toolCall.Function.Name "browser.search") (eq $toolCall.Function.Name "browser.open") (eq $toolCall.Function.Name "browser.find") -}}
<|start|>assistant<|channel|>{{ if $isBuiltin }}analysis{{ else }}commentary{{ end }} to={{ if not $isBuiltin}}functions.{{end}}{{ $toolCall.Function.Name }} <|constrain|>json<|message|>{{ $toolCall.Function.Arguments }}<|call|>
{{- end -}}
{{- end -}}
{{- else if eq $msg.Role "user" -}}
<|start|>{{ $msg.Role }}<|message|>{{ $msg.Content }}<|end|>
{{- end }}
{{- else }}
{{- end }}
{{- end -}}
{{- if not (or $prefillingContent $prefillingThinkingOnly) -}}
<|start|>assistant
{{- end -}}"""
PARAMETER temperature 1
PARAMETER num_ctx 8192
LICENSE """
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License."""
suzuyu@macmini-m6 verification %
起動
下記で対話モードを開始する
ollama run gpt-oss-20b-8k
プロンプトが表示されたら、質問を入力する
日本語で回答してください。
ローカル LLM とは何かを、初めて使う人向けに3点で説明してください。
下記は実際に gpt-oss-20b が回答した結果
## ローカルLLM(Local LLM)とは?
1. **自分のコンピュータで動くAIモデル**
- インターネットに接続せず、PCやサーバーなど自分のハードウェア上で動作します。
- 例えば、メモリが16GB〜32GBのGPUがあれば、ローカルでLLMを走らせることが可能です。
2. **プライバシーとデータの安全性が高い**
- すべての入力・出力が自分の端末内で完結するため、外部へ情報が漏れる心配がありません。
- 個人情報や機密データを扱う際に、第三者にデータを送信するリスクを回避できます。
3. **高速応答とカスタマイズ性**
- ネットワーク遅延がなく、応答速度が高速です。
- モデルを自分のニーズに合わせて微調整(ファインチューニング)したり、独自のデータセットで学習させたりすることができます。
これらが「ローカルLLM」の主な特徴です。初めて使う際は、ハードウェアのスペックやモデルのサイズに合わせて設定を調整するとスムーズに利
用できます。
上記はモデルの回答原文。「外部へ情報が漏れる心配がない」「応答速度が高速」という断定は注意で、ローカル実行だけで情報漏えい防止や高速応答が保証されるわけではないので、内容には疑問あり
終了する場合は下記を入力する
/bye
モデルの取得と起動、対話モードの終了方法は Quickstart を参照
初回起動・回答・終了のログ
suzuyu@macmini-m6 verification % ollama run gpt-oss-20b-8k
>>> 日本語で回答してください。
... ローカル LLM とは何かを、初めて使う人向けに3点で説明してください。
Thinking...
The user wants explanation in Japanese: what is a local LLM? They ask to explain in 3 points for first-time users. So we need 3
bullet points in Japanese that explain what a local LLM is, targeted to someone new. Provide concise explanation, maybe mention
local LLM runs on local hardware, no internet, privacy, etc. Should be friendly. Should be Japanese. No other language. Should be
3 points. Probably numbered 1,2,3. Ensure correct grammar. Provide explanation. Let's produce.
...done thinking.
## ローカルLLM(Local LLM)とは?
1. **自分のコンピュータで動くAIモデル**
- インターネットに接続せず、PCやサーバーなど自分のハードウェア上で動作します。
- 例えば、メモリが16GB〜32GBのGPUがあれば、ローカルでLLMを走らせることが可能です。
2. **プライバシーとデータの安全性が高い**
- すべての入力・出力が自分の端末内で完結するため、外部へ情報が漏れる心配がありません。
- 個人情報や機密データを扱う際に、第三者にデータを送信するリスクを回避できます。
3. **高速応答とカスタマイズ性**
- ネットワーク遅延がなく、応答速度が高速です。
- モデルを自分のニーズに合わせて微調整(ファインチューニング)したり、独自のデータセットで学習させたりすることができます。
これらが「ローカルLLM」の主な特徴です。初めて使う際は、ハードウェアのスペックやモデルのサイズに合わせて設定を調整するとスムーズに利
用できます。
>>> /bye
suzuyu@macmini-m6 verification %
Ollama での動作試験
日本語で技術的な質問をする
技術的な質問もしてみて回答内容を確認してみる
ollama run gpt-oss-20b-8k
日本語で回答してください。
VXLAN と EVPN の役割の違いを、ネットワークエンジニア向けに説明してください。
最後に、それぞれの役割を表で整理してください。
Ollama + gpt-oss 20B の回答全文
## VXLAN と EVPN の役割の違い
| 項目 | VXLAN | EVPN |
|------|-------|------|
| **定義** | オーバーレイのデータプレーン(イーサネットフレームをIPパケットにカプセル化する技術) | BGPをベースとした制御プレーン(
MAC/IPの到達情報を配布するプロトコル) |
| **主な役割** | ① **ネットワークの分離**:VNI(Virtual Network Identifier)で論理セグメントを区別<br>② **拡張性**:数千~数万のセ
グメントをサポート(最大24ビット)<br>③ **スイッチングのトラフィックをIPネットワーク経由で転送** | ① **MAC/IP到達情報の配布**(
BGP‑EVPN Path AttributeでMAC/IPを広告)<br>② **MAC学習の最適化**(ARP/NDプローブやMACアドバタイズでMAC floodingを防止)<br>③
**L2/L3 VPN・VXLANの統合制御**(EVPN‑VE、EVPN‑IP、EVPN‑MAC) |
| **制御プレーン** | なし(静的設定やMPLS/OSPF/LDPのような別途制御プレーンが必要) | あり(BGPを利用) |
| **トラフィックフロー** | データプレーンのみ | データプレーン+制御プレーン |
| **主要な課題** | ① MACアドレステーブルの拡張性が低い(MAC flooding)<br>② L3VPNのサポートが限定的 | ① BGPの複雑さ<br>② 適切な
BGPフィルタリング・ポリシー設定が必要 |
| **代表的なユースケース** | データセンターファブリックでの単純なL2オーバーレイ、VM間の通信 | データセンターファブリックにおける
L2/L3 VPN、マルチテナント環境、VTEP間の冗長性・ロードバランシング |
| **拡張性** | 1VNI=1セグメントのため、セグメント数が増えるとVTEPが増加し管理が難しくなる | BGPを使うことで大規模なトポロジーでも集
中管理が容易 |
---
## さらに掘り下げてみると
| 観点 | VXLAN | EVPN |
|------|-------|------|
| **カプセル化方式** | 1レイヤ2フレームをVXLANヘッダ+IPヘッダ+UDPヘッダで包む | 同上(VXLANヘッダが必要) |
| **VNI(Virtual Network Identifier)** | 24ビット | 同上 |
| **ルーティング/ブリッジング** | L2オーバーレイのみ | L2 + L3 ルーティングを同一インフラで実現 |
| **冗長性** | 物理リンクの冗長性は別途IP/TCP/UDPの冗長化で対応 | BGPが冗長性を提供(EVPN‑VEでVRRP的機能) |
| **スケーラビリティ** | VTEP 1つにつき1VNIで限界がある | BGPルーティングでVTEP間の情報を効率的に配布 |
| **オープンスタンダード** | RFC7348(VXLAN) | RFC7432(EVPN) |
| **ベンダーサポート** | ほぼ全ベンダーで実装 | 大規模ネットワーク機器で主に実装 |
---
## まとめ
- **VXLAN** は「データプレーン」だけを担い、オーバーレイネットワークを作るためのカプセル化技術です。セグメントの数が増えると管理が煩
雑になり、MAC flooding の問題も生じます。
- **EVPN** は「制御プレーン」+「データプレーン」を統合したもので、BGP を利用して MAC/IP の到達情報を効率的に配布し、L2/L3 VPN の統
合管理を実現します。
- 大規模なデータセンターファブリックやマルチテナント環境では **VXLAN + EVPN** の組み合わせが主流です。VXLAN がトラフィックをカプセル
化し、EVPN がそのカプセル化されたトラフィックを制御・最適化します。
| 役割 | VXLAN | EVPN |
|------|-------|------|
| データプレーン | オーバーレイのイーサネットフレームをIPネットワーク上で転送 | VXLAN ヘッダ付きデータフローを制御 |
| 制御プレーン | なし(外部制御が必要) | BGP で MAC/IP 受信情報を広告・学習 |
| 分離/セグメント化 | VNI により論理セグメントを作成 | 同一 VNI で L2/L3 VPN を提供 |
| 拡張性 | 物理リンクや VTEP でスケール制限 | BGP の多発で大規模スケール対応 |
| MAC 学習 | 物理VTEP 内でのテーブル管理 | BGP‑MAC アドバタイズで動的学習 |
| 主要ユースケース | L2 オーバーレイ、簡易拡張 | L2/L3 VPN、マルチテナント、冗長化 |
このように、**VXLAN** は「転送」だけを担い、**EVPN** は「情報配布」と「制御」を担うことで、現代のデータセンターネットワークを効率的
に構築・運用できるようになっています。
回答内容の評価
以下は GPT-6 Astra (高) による回答評価。技術的な指摘には照合先の RFC・公式資料が併記されている。実機での動作確認とは別の AI による評価であり、全項目を読み込んで個別に評価した結果を示すものではない
| 確認項目 | 結果 |
|---|---|
| 日本語として読みやすいか | △:見出しと表で整理され、大意は読み取れる。ただし「BGP の多発」「MAC/IP 受信情報」など、不自然で意味が曖昧な表現がある |
| VXLAN と EVPN の役割を区別できているか | △:冒頭では VXLAN をデータプレーン、EVPN を BGP ベースの制御プレーンと説明できている。一方、後半で EVPN 自体を「データプレーン+制御プレーン」とし、VXLAN と組み合わせた構成全体との区別が曖昧になっている |
| 指定した表形式で回答できているか | ○:比較表を出力し、まとめにも役割を整理した表がある。ただし掲載ログにはセル途中の改行があり、Markdown の表として表示する場合は整形が必要 |
| 説明に誤りや補足が必要な箇所はあるか | あり:VTEP と VNI の関係、EVPN のカプセル化方式、制御プレーンの例、MAC/IP 広告と冗長化の用語などに修正が必要。技術説明としてそのまま採用するのは難しい |
主な修正点は下記のとおり。上の回答ログは評価対象として残し、補足を分けて記載する
- 「VTEP 1つにつき1VNI」は誤り:1つの VTEP で複数の VNI を扱えるため、VNI が増えると必ず VTEP も増えるという説明は不正確。Cisco の VXLAN 構成説明
- EVPN に VXLAN ヘッダが必須という説明は誤り:EVPN は MPLS でも利用できる。今回の EVPN-VXLAN 構成では、VXLAN がデータのカプセル化・転送、BGP EVPN が到達情報の配布を担う、と説明する方が明確。RFC 7432、RFC 8365 §5
- VXLAN の制御プレーンの例が不適切:OSPF はアンダーレイの IP 到達性を提供する用途で使えるが、EVPN と同じ MAC/IP 広告機能を担うものではない。VXLAN は EVPN なしの flood-and-learn 方式でも利用でき、「MPLS/OSPF/LDP が別途必要」という並べ方は役割を混同している。RFC 7348 §4
- MAC/IP 広告と冗長化の用語に混乱がある:MAC/IP 情報は EVPN の MAC/IP Advertisement route(Route Type 2)で広告する。「EVPN-VEでVRRP的機能」という説明では、EVPN のマルチホームや DF 選出などの仕組みを正しく説明できていない。RFC 7432 §7.2、同 §8
- フラッディングを防止できるという説明には条件が必要:制御プレーンでの MAC/IP 学習や ARP/ND 抑制により削減できるが、EVPN を使えばすべての BUM(Broadcast・Unknown Unicast・Multicast)転送がなくなるわけではない。RFC 7432 §10、RFC 8365 §8.3.3
今回の1回答では、日本語で応答し、指定した表形式を作ることは確認できた。
一方、もっともらしい用語や表の中に技術的な誤りが混在しており、ネットワーク設計や設定の判断に使うには一次資料との照合が必要となった。
この結果だけでモデル全体の品質や、API 試験での回答品質までは評価しているわけではない
GPU の利用状況を確認する
モデルがロードされている間に、別のターミナルで ollama ps を実行する
ollama ps
NAME ID SIZE PROCESSOR CONTEXT RUNNER UNTIL
gpt-oss-20b-8k:latest a1c927e2b35f 12 GB 100% GPU 8192 llamacpp 9 minutes from now
今回は PROCESSOR が 100% GPU となり、モデル全体が GPU にロードされていることを確認できた。これは GPU への配置を示す表示で、GPU 使用率が常時100%という意味ではない。GPU 利用状況の確認方法
一覧が空の場合は、ollama run gpt-oss-20b-8k で再度質問し、回答後すぐに ollama ps を実行する
メモリ使用量を確認する
macOS の「アクティビティモニタ」を開き、「メモリ」タブで使用状況を確認する
ollama run gpt-oss-20b-8k を実行して質問を始めると、実行前よりメモリプレッシャーのグラフが上昇した。撮影時点では緑色の範囲に収まっており、Mac 全体の使用済みメモリは24.36GB、スワップ使用領域は0バイトだった
使用済みメモリには macOS やほかのアプリも含まれるため、Ollama 単体の使用量とは区別する
Ollama を API 経由で呼び出す
比較では両方のサーバが対応する /v1/chat/completions を使用し、curl でチャットの応答を確認する。Ollama の API 互換性
前項で作成した verification ディレクトリで、リクエストをファイルに保存する
cat > ollama-request.json <<'EOF'
{
"model": "gpt-oss-20b-8k",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "low",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
curl --fail-with-body http://127.0.0.1:11434/v1/chat/completions \
-H 'Content-Type: application/json' \
--data-binary @ollama-request.json \
-o ollama-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
cat ollama-response.json
HTTP 200で、choices[0].message.content に回答本文があることを確認する。思考出力と回答本文を区別し、finish_reason が length なら出力上限に達した結果として記録する
gpt-oss の思考強度は low とする。対応する値は /api/show でも確認できる。思考設定
curl --fail-with-body http://127.0.0.1:11434/api/show \
-H 'Content-Type: application/json' \
-d '{"model":"gpt-oss-20b-8k"}' \
-o ollama-model-info.json
実環境での結果
HTTP 200で日本語の回答が返り、API 経由の応答を確認できた。今回の1回の完了時間は約7.68秒だった。ロードの有無を確認していないため、初期確認時の参考値として記録する
| 確認項目 | 結果 |
|---|---|
| HTTP ステータス | 200 |
| 応答完了までの時間 | 7.679472秒 |
| 終了理由 |
stop。出力上限による打ち切りではない |
| 日本語・3点の指定 | 日本語で3点に整理して回答 |
| 合計300字以内 | 未達。回答本文は Markdown 記号・空白・改行を含め455文字 |
| 技術説明の正確さ | 再起動とスケーリングの説明に補足が必要 |
回答本文とレスポンス全体は下記のログに記載する。内容には次の注意点があった (GPT-6 Astra での評価)
-
再起動の説明:単独の Pod でも、内部のコンテナは
restartPolicyに従って kubelet が再起動する。Pod 自体の再作成と、コンテナの再起動を区別する必要がある。Pod のライフサイクル -
自動スケールの説明:
spec.replicasは希望する Pod 数の指定で、負荷に応じた自動増減には HPA などを使う。「spec.replicasで自動スケール」という表現は、この違いが曖昧になっている。Deployment のレプリカ数、HPA
Ollama のローカル API 応答全文
suzuyu@macmini-m6 verification % >....
"model": "gpt-oss-20b-8k",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "low",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
curl --fail-with-body http://127.0.0.1:11434/v1/chat/completions \
-H 'Content-Type: application/json' \
--data-binary @ollama-request.json \
-o ollama-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
cat ollama-response.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1742 100 1403 100 339 182 44 0:00:07 0:00:07 --:--:-- 385
HTTP=200 total=7.679472s
{"id":"chatcmpl-420","object":"chat.completion","created":1792305076,"model":"gpt-oss-20b-8k","system_fingerprint":"fp_ollama","choices":[{"index":0,"message":{"role":"assistant","content":"**Kubernetes の Pod と Deployment の違い(300字以内)**\n\n1. **単体 vs. 管理単位** \n - *Pod* は 1 つまたは複数のコンテナをまとめた最小実行単位。 \n - *Deployment* は Pod の望ましい状態(レプリカ数、イメージバージョンなど)を宣言し、クラスタに自動で複製・更新を行う管理オブジェクト。\n\n2. **スケーリング・ローリングアップデート** \n - Pod は手動で作成・削除する必要があり、スケールアウトは手作業。 \n - Deployment は `spec.replicas` で自動スケール、`spec.strategy` でローリングアップデートを実現。\n\n3. **自己回復機能** \n - Pod がクラッシュすると自動再起動は行われない。 \n - Deployment は ReplicaSet を介し、失敗した Pod を検知して新規に再作成し、常に指定レプリカ数を維持する。","reasoning":"Need 3 points, 300 characters total? \"合計300字以内\" means total characters \u003c=300. Provide concise Japanese."},"finish_reason":"stop"}],"usage":{"prompt_tokens":95,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens":289,"total_tokens":384}}
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification % curl --fail-with-body http://127.0.0.1:11434/api/show \
-H 'Content-Type: application/json' \
-d '{"model":"gpt-oss-20b-8k"}' \
-o ollama-model-info.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 40787 0 40761 100 26 10.5M 7082 --:--:-- --:--:-- --:--:-- 12.9M
suzuyu@macmini-m6 verification %
Splash + Qwen3.8 のインストールと起動・応答確認
次に Splash と Qwen のインストールを実施する
前提条件
Splash は Apple M3 以降・macOS 26.4 以降が必要となる。OS を確認する
sw_vers
実行環境では下記の通り
% sw_vers
ProductName: macOS
ProductVersion: 27.0.1
BuildVersion: 26A434
Splash のインストール
事前準備で導入した Homebrew を使う。調査時点の公式 tap は1.3.2を配布している。インストール前に配布バージョンを確認する
brew tap incoai/tap
brew info incoai/tap/splash
配布版が 1.3.2 であることを確認してからインストールする。通常の brew install は実行時点の formula を使うため、将来も1.3.2が入ることを保証するコマンドではない。配布版が変わっている場合は、1.3.2 の公式リリース の配布手順を確認する
brew install incoai/tap/splash
brew list --versions splash
splash --version
splash serve --help
実機では brew list --versions splash と splash --version の両方で1.3.2を確認できた。splash serve --help も正常に表示され、今回使用する --max-context と --language-only を確認できた
インストール先は /opt/homebrew/Cellar/splash/1.3.2、Homebrew が表示した本体の容量は250.7MBだった。モデルは後続の手順で取得する
インストール後の案内に表示される UD-Q4_K_M は起動例であり、この時点でモデルが取得されたわけではない。今回は次の手順で UD-IQ4_XS を指定する
比較期間中の brew upgrade による更新を防ぐため、確認済みの版を pin する。pin は導入済みの版を保持する操作で、特定の旧版をインストールする機能ではない。Homebrew の pin
brew pin incoai/tap/splash
比較終了後、更新を再開する場合は brew unpin incoai/tap/splash で解除する
Splash のインストールログとヘルプ全文
suzuyu@macmini-m6 verification % sw_vers
ProductName: macOS
ProductVersion: 27.0.1
BuildVersion: 26A434
suzuyu@macmini-m6 verification % brew tap incoai/tap
==> Downloading Homebrew API data
✔︎ JSON API packages.arm64_golden_gate.jws.json Downloaded 15.8MB/ 15.8MB
==> Tapping incoai/tap
Cloning into '/opt/homebrew/Library/Taps/incoai/homebrew-tap'...
Tapped 1 formula (15 files, 33.8KB).
suzuyu@macmini-m6 verification % brew info incoai/tap/splash
==> incoai/tap/splash: stable 1.3.2 (bottled)
Local inference engine for Apple silicon, built around the model
https://github.com/incoai/splash
Not installed
From: https://github.com/incoai/homebrew-tap/blob/HEAD/Formula/splash.rb
Tap: incoai/tap
License: Apache-2.0
==> Requirements
Required: arm64 architecture ✔, macOS >= 26 ✔, Splashmacos ✔
==> Caveats
Serve a model:
splash serve --model unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M
suzuyu@macmini-m6 verification % brew install incoai/tap/splash
==> Auto-updating Homebrew...
Adjust how often this is run with `$HOMEBREW_AUTO_UPDATE_SECS` or disable with
`$HOMEBREW_NO_AUTO_UPDATE=1`. Hide these hints with `$HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==> Trusted formula incoai/tap/splash
==> Fetching downloads for: splash
✔︎ Bottle splash (1.3.2) Downloaded 74.5MB/ 74.5MB
==> Installing splash from incoai/tap
==> Pouring splash-1.3.2.arm64_tahoe.bottle.tar.gz
🍺 /opt/homebrew/Cellar/splash/1.3.2: 7,079 files, 250.7MB
==> Caveats
zsh completions have been installed to:
/opt/homebrew/share/zsh/site-functions
==> splash
Serve a model:
splash serve --model unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M
suzuyu@macmini-m6 verification % brew list --versions splash
splash 1.3.2
suzuyu@macmini-m6 verification % splash --version
Splash 1.3.2
suzuyu@macmini-m6 verification % splash serve --help
usage: splash serve [-h] --model OWNER/REPO[:VARIANT] [--revision REVISION] [--draft-model DRAFT] [--language-only] [--offline]
[--port PORT] [--host ADDRESS] [--allowed-host HOST] [--allowed-origin ORIGIN] [--api-key KEY] [--no-webui]
[--max-memory SIZE] [--max-context TOKENS] [--kv-format {int8,bf16}] [--state-format {fp32,bf16}]
[--idle-release DURATION] [--max-cache-disk SIZE] [--max-cache-disk-writes SIZE] [--persistent-cache]
[--cache-dir DIRECTORY] [--served-model-name NAME] [--announce-served-name] [--default-reasoning-effort EFFORT]
[--thinking-token-budget N] [--thinking-end-text TEXT] [--max-request-size SIZE] [--max-image-pixels PIXELS]
[--request-timeout DURATION] [--queue-size REQUESTS] [--decode-share SHARE] [--disable-ane] [--allow-idle-sleep]
Load an upstream model, automatically select its DFlash2 draft, and serve in the foreground.
options:
-h, --help show this help message and exit
model:
--model OWNER/REPO[:VARIANT]
upstream Hugging Face model, with a GGUF variant after ':' (e.g. :UD-Q4_K_M)
--revision REVISION model branch, tag or commit (default: the repository's default branch)
--draft-model DRAFT DFlash2 draft repository or local directory to use instead of the automatically selected one
--language-only skip vision preparation and loading; image and PDF input is refused
--offline start the installed model without contacting the Hugging Face Hub (as HF_HUB_OFFLINE=1)
network and access:
--port PORT HTTP port (default: SPLASH_PORT or 8000)
--host ADDRESS HTTP bind address (default: 127.0.0.1; 0.0.0.0 for all IPv4 interfaces); clients use an IP address, localhost
or a name given with --allowed-host
--allowed-host HOST additional HTTP Host name to accept, e.g. mymac.local; does not change the bind address (repeatable)
--allowed-origin ORIGIN
origin whose pages may call the API from a browser or webview, e.g. tauri://localhost; '*' for any
(repeatable)
--api-key KEY require this key as a bearer token or x-api-key on every route but the chat page, /health and /ready (default:
SPLASH_API_KEY)
--no-webui disable the chat page
memory and context:
--max-memory SIZE Metal memory budget, e.g. 28G; it can only lower the automatic budget (default: auto)
--max-context TOKENS context token limit, e.g. 100K (K = 1024 tokens); it can only lower the automatic limit, the model's native
window when memory holds it (default: auto)
--kv-format {int8,bf16}
target KV cache storage (default: int8); bf16 uses more memory
--state-format {fp32,bf16}
recurrent state storage (default: fp32); bf16 halves each state, so memory and the SSD cache hold about twice
as many
--idle-release DURATION
time without a request before the engine unwires its memory and frees the weights: seconds, or with an s, m or
h suffix, e.g. 30m (default: 10m); off keeps both, though POST /v1/memory/release still frees the weights
SSD cache:
--max-cache-disk SIZE
SSD quota for cached KV pages and states: auto follows the volume's free space up to 32G, 0 disables, or a
size such as 16G (default: auto)
--max-cache-disk-writes SIZE
the most the SSD cache writes a day, e.g. 200G; the state a conversation resumes from and KV pages pass it
(default: 0, no limit)
--persistent-cache keep the SSD cache across restarts
--cache-dir DIRECTORY
where --persistent-cache keeps its files; needs --persistent-cache (default: ~/Library/Caches/Splash/prefix-
cache)
API:
--served-model-name NAME
additional API model name (repeatable); responses report the loaded model ID unless --announce-served-name
--announce-served-name
report the first --served-model-name in API responses and list it first in /v1/models; /status keeps the
loaded model ID (needs --served-model-name)
--default-reasoning-effort EFFORT
effort of Chat and Responses requests that set none: none, minimal, low, medium, high, xhigh or max (default:
SPLASH_DEFAULT_REASONING_EFFORT, else the model template's)
--thinking-token-budget N
thinking tokens of a thinking request that sets no thinking_token_budget, after which its thinking closes
(default: SPLASH_THINKING_TOKEN_BUDGET, else no cap)
--thinking-end-text TEXT
text that ends thinking where a thinking budget runs out, such as a sentence that turns to the answer, then
</think> as its last token; at most 64 tokens (default: SPLASH_THINKING_END_TEXT, else </think> alone)
requests:
--max-request-size SIZE
maximum HTTP request body size, e.g. 128M (default: 128M); shared input budget is max(512M, twice this limit)
--max-image-pixels PIXELS
maximum resized pixels per image, 65536–4194304 (default: 4194304); bounds the vision scratch one image needs
--request-timeout DURATION
time before a queued or in-flight request expires with 504: seconds, or with an s, m or h suffix, e.g. 30m
(default: none)
--queue-size REQUESTS
requests admitted at once, running or waiting; more get 503 (default: 32)
--decode-share SHARE decode time owed per unit of prefill time while other requests decode (default: 0.5; 0 alternates one command
each)
--disable-ane prefill on the GPU alone (default: a dense model's long prompts also use the Neural Engine when that is
faster)
--allow-idle-sleep let the Mac sleep automatically while requests run (default: it stays awake until they finish; the display may
still sleep)
Examples:
splash serve --model unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_M
splash serve --model unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q4_K_M --max-context 128K
SIZE is bytes, or a number with K, M or G (1024-based), such as 28G.
DURATION is seconds, or a number with s, m or h, such as 30m.
After Ready, open http://127.0.0.1:PORT in a browser on this Mac, or
connect an installed agent; both reach the server on loopback, which
the default --host and 0.0.0.0 include. The startup summary and
/status report the effective context limit; a client may impose a
smaller one. Keep this terminal open; Ctrl+C stops serving.
suzuyu@macmini-m6 verification % brew pin incoai/tap/splash
suzuyu@macmini-m6 verification %
Ollama のモデルをアンロードする
ollama stop gpt-oss-20b-8k
ollama ps
一覧にモデルが残っていないことを確認する。他のモデルが表示される場合も、使用しているクライアントを止めてからアンロードする
Ollama のサービスは起動したまま、モデルをロードしない状態で Splash の試験へ進む
モデルの取得とサーバ起動
今回は起動ログを確認しやすく、比較後に Ctrl+C で停止できるよう、ターミナルで splash serve を実行する。Splash 1.3.2 の Homebrew formula にはサービス定義がないため、Ollama と同じ brew services start では起動しない。Homebrew formula
--model はリポジトリ名と量子化を指定する。下記では Qwen3.8-27B-UD-IQ4_XS.gguf が選択される
splash serve \
--model unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS \
--max-context 8K \
--language-only
初回に対象モデルと対応するドラフトモデルを取得する。Ready が表示されたら、このターミナルを開いたまま別のターミナルで API を確認する。ここではテキスト用途のため --language-only を指定している
起動ログのモデル・ドラフトのリビジョン、利用可能なコンテキスト長、メモリ計画を記録する。再現時は記録した対象モデルのコミットを --revision に指定できる。モデル保存とリビジョン
今回の32GB環境では、指定した UD-IQ4_XS で Ready まで到達し、8K・テキスト専用でサーバを起動できた
Splash のモデル取得・起動ログ
suzuyu@macmini-m6 ~ % splash serve \
--model unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS \
--max-context 8K \
--language-only
No GGUF is named for :UD-IQ4_XS alone; using Qwen3.8-27B-UD-IQ4_XS.gguf, the only one whose name ends in -UD-IQ4_XS.
Selected Qwen3.8-27B-UD-IQ4_XS.gguf from unsloth/Qwen3.8-27B-GGUF.
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
config.json: 100%|████████████████████████████████████████████████████████████████████████████████| 1.24k/1.24k [00:00<00:00, 2.51MB/s]
Fetching 1 file(s), 3.85 GB, from incoai/Qwen3.8-27B-DFlash2@015e795645c7; cached files are reused.
Fetching 2 files: 100%|█████████████████████████████████████████████████████████████████████████████████| 2/2 [03:36<00:00, 108.15s/it]
Download complete: : ██████████████████████████████████████████████████████████████████████████████████████████████| 3.34GB, 10.9MB/s Installing unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS as Qwen3.8-27B (gguf); draft incoai/Qwen3.8-27B-DFlash2; vision disabled..85GB, 33.5MB/s
Fetching 1 file(s), 14.25 GB, from unsloth/Qwen3.8-27B-GGUF@4ca720788d1e; cached files are reused.
Fetching 1 files: 100%|█████████████████████████████████████████████████████████████████████████████████| 1/1 [15:45<00:00, 945.69s/it]
Download complete: : ██████████████████████████████████████████████████████████████████████████████████████████████| 14.2GB, 11.4MB/s
Reconstruction complete: 100%|████████████████████████████████████████████████████████████████████████████| 14.3GB / 14.3GB, 29.9MB/s
Reconstruction complete: 100%|████████████████████████████████████████████████████████████████████████████| 3.85GB / 3.85GB, 33.5MB/s
Download complete: : ██████████████████████████████████████████████████████████████████████████████████████████████| 3.34GB, 10.9MB/s
16:12:07 Loading · unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS
16:12:07 Chat template · patched to render later system messages in place (the original template rejects them), generation prompt 5-7 tokens
16:12:10 Weights loaded in 3.09 s.
16:12:10 Kernel policy for GPU family 11 with 12 cores.
16:12:10 Setting up the Neural Engine FFN split (splash serve --disable-ane keeps the FFN on the GPU).
16:13:17 Neural Engine FFN split at share 0.76 for chunks of 512 rows or more: 30.9 ms per 2048-row FFN layer against 63.5 on the GPU alone, 1.0% from the GPU alone on the Neural Engine's part (calibrated in 67.2 s).
16:13:17 SSD cache: up to 32.0 GiB of temporary files in /var/folders/vm/x0l5wqcx7njgdqpfvbpp20n00000gn/T/ (its automatic quota, while its volume has room beyond the 10.0 GiB it keeps free); KV pages of 1040 KiB and states of 152 MiB, a state's write staging through 152 MiB of the memory plan; --max-cache-disk 0 turns it off.
16:14:15 Ready · unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS · context 8K · language only · http://127.0.0.1:8000
| 確認項目 | 起動ログの結果 |
|---|---|
| 対象ファイル | Qwen3.8-27B-UD-IQ4_XS.gguf |
| 対象モデルのリビジョン |
4ca720788d1e(ログの短縮表記) |
| ドラフトモデル |
incoai/Qwen3.8-27B-DFlash2、リビジョン 015e795645c7(短縮表記) |
| 取得対象の容量 | 対象14.25GB + ドラフト3.85GB = 約18.10GB(ログ表示値。関連ファイルやキャッシュは別) |
| コンテキスト・入力 | 8K、language only |
| 待ち受け先 | http://127.0.0.1:8000 |
| SSD キャッシュ | 一時ファイルの上限32.0GiB。実際に32GiB使用したことを示す値ではない |
冒頭の No GGUF is named... はファイル名の選択に関する案内で、その直後に目的の GGUF が選ばれている。HF Hub の未認証アクセスの警告も出たが、今回はダウンロードと起動が完了した。警告にある HF_TOKEN はモデル配布元への認証用で、Splash の API 認証キーとは別になる
重みのロードは3.09秒だったが、その後に Neural Engine の初期調整などが続いた。ダウンロード後の Loading(16:12:07)から Ready(16:14:15)までは約2分8秒で、3.09秒は起動全体の時間ではない
GPU 向け設定と Neural Engine の FFN 分担の初期調整もログで確認できた。回答中のメモリ使用状況と API 応答は、続く手順で確認する
日本語で技術的な質問をする
Ollama と同じ VXLAN / EVPN の質問を送り、日本語の読みやすさや説明の正確さ、表形式への対応を確認する
splash serve で Ready が表示されたら、Mac mini のブラウザで http://127.0.0.1:8000 を開く。サーバ用のターミナルは開いたままにする。Splash のチャット画面
会話履歴のない状態で、下記の質問を入力する
日本語で回答してください。
VXLAN と EVPN の役割の違いを、ネットワークエンジニア向けに説明してください。
最後に、それぞれの役割を表で整理してください。
下記が Splash での回答
Splash + Qwen3.8 の回答全文
ネットワークエンジニア向けに整理すると、結論から言うと、
- **VXLAN:Overlay の「データ面・カプセル化」**
- **EVPN:Ethernet サービスの「制御面・制御情報」**
という違いです。
現場では多くの場合、**EVPN over VXLAN** として一緒に使われ、**VXLANがデータ面、EVPNが制御面**を担います。
---
## 1. VXLAN の役割
VXLAN は、物理ネットワークの上に Overlay L2/L3 ネットワークを作るための **カプセル化プロトコル** です。
### 主な役割
- **VLANフレームを IP ネットワーク上で運ぶ**
- 802.1Q フレームを VXLAN ヘッダ付きでカプセル化
- 通常は UDP port 4789 を使う
- **Underlay の IP ネットワーク上を転送する**
- Underlay は通常、IPルーティング
- IGP / OSPF / BGP 等で VTEP 間経路を作る
- **VNI で Overlay を識別する**
- VNI は 24 bit
- 100万クラス以上の Overlay 空間を表現可能
- VLAN ID を直接使わず、Overlay 側の識別子として VNI を使う
- **VTEP 間で Overlay 通信を可能にする**
- VTEP は VXLAN のカプセル化/デカプセル化を行うポイント
### VXLAN 単体の特徴
VXLAN 自体には、強い制御面は備わっていません。
つまり、VXLAN 単体では次のような処理が必要になります。
- **BUM トラフィックの配布**
- Broadcast / Unknown unicast / Multicast
- **マルチキャストや PIM、Static、Ingress Replication 等との連携**
- **MAC address learning**
- Flooding して学習する場合
- **ARP / MAC 学習の最適化**
つまり、VXLAN は「フレームをどう運ぶか」のデータ面を提供しますが、
「どの VTEP が見えるか」「どの MAC がどこにいるか」「BUM はどう送るか」を賢く制御するのは別レイヤです。
---
## 2. EVPN の役割
EVPN は、BGP を制御面に使った Ethernet VPN であり、
**L2/L3 Ethernet サービスを制御するための制御面プロトコル**です。
### 主な役割
- **MAC 学習を制御面に委譲する**
- flooding による学習ではなく、BGP EVPN の MAC route で情報を共有
- **VTEP の発見**
- どの VTEP がどの Ethernet Segment / VNI に見えているか、BGP で制御
- **MAC / IP アドレスのルーティング制御**
- MAC route
- IP prefix route
- **Ethernet Segment 制御**
- ESI: Ethernet Segment Identifier
- 同一セグメント内の VTEP 群の識別
- **マルチテナント制御**
- RT / RD / Community 等で VNI、VRF、テナント分離を制御
- **BUM トラフィックの最適化**
- 従来は PIM マルチキャスト等に頼ることが多かったが
- EVPN なら **Head-End Replication** 等により、単播で BUM を配送可能
- **MAC Mobility / Loop Prevention**
- MAC が移動した際の制御
- 循環や誤学習の抑制に寄与する
### EVPN が扱う主要な Route Type
- **Type 1: Ethernet Segment route**
- ESI 情報
- Ethernet Segment 内の VTEP 情報
- **Type 2: MAC/IP route**
- MAC アドレスと IP アドレス
- MAC 学習情報
- **Type 3: INSET route**
- VNI 内でどの VTEP が見えているか
- BUM 配送の制御
- **Type 4: EVPN Multicast route**
- BUM を効率的に配送するための制御
---
## 3. VXLAN と EVPN の関係
実務で最も多いのが、**EVPN over VXLAN** です。
この場合の役割分担は、
- **VXLAN**
- フレームをカプセル化して Underlay IP 上に運ぶ
- VNI で Overlay を識別する
- **EVPN**
- MAC、IP、VTEP、Ethernet Segment などの制御情報を BGP で配布する
- BUM 配送を制御する
- 大規模マルチテナント環境での制御面を提供する
つまり、
> **VXLAN は「車体」、EVPN は「ナビと交通管理」**
> という比喩が近いと思います。
VXLAN 単体でも動作しますが、大規模クラウド、マルチテナント、データセンター Overlay では EVPN と組み合わせることで制御面の拡張性・安定性が上がります。
---
## 4. 具体例で見た違い
ある VNI 内で、未知宛てフレームやマルチキャストを配送する場合のイメージです。
### VXLAN 単体の場合
- BUM を配信するために、マルチキャストグループを使う
- PIM や static multicast と組み合わせる
- VTEP 間で flooding する必要がある場面もある
### EVPN over VXLAN の場合
- BGP EVPN で各 VTEP の存在を把握する
- Type 2 で MAC/IP 情報を交換する
- Type 3 で INSET を交換し、BUM を制御する
- Head-End Replication を使えば、PIM 不要で単播ベースにBUMを配送できる
したがって、
- **VXLAN:どのフレームをどうカプセル化してどこに運ぶか**
- **EVPN:どの VTEP が存在し、どの MAC/IP がどこにあり、BUMをどう制御するか**
という違いになります。
---
## 5. 役割の違いを表で整理
| 観点 | VXLAN | EVPN |
|---|---|---|
| 位置づけ | Overlay データ面プロトコル | Ethernet サービスの制御面プロトコル |
| 主な役割 | VLANフレームを IP ネットワーク上にカプセル化して転送 | MAC/IP/VTEP/Segment 情報を BGP で制御・配布 |
| 動作層 | L2/L3 Overlay のデータ面 | L2/L3 Ethernet サービスの制御面 |
| カプセル化 | 本体がカプセル化を行う | カプセル化自体は定義しない。VXLAN / MPLS / SRv6 等と組み合わせる |
| 識別子 | VNI で Overlay を識別 | RT / RD / VNI / ESI 等を用いてサービスやSegmentを制御 |
| Underlay 依存 | IPルーティングに依存。IGP/BGP 等で VTEP 間経路を作る | 制御面は BGP MP-BGP を使用。Underlay のルーティングとも連携 |
| MAC 学習 | flooding や ARP/ND の結果から学習する場合がある | BGP EVPN の MAC route で学習情報を共有 |
| BUM トラフィック | マルチキャスト、PIM、static、ingress replication 等と連携 | INSET / EVPN Multicast route / Head-End Replication で制御・最適化 |
| VTEP 発見 | 単体では static や multicast の制御に頼る場合がある | BGP で VTEP / INSET を動的に制御 |
| スケール | VNI 空間は広いが、制御面を外部に依存 | 大規模マルチテナント、DCI、データセンター Overlay に強い |
| 主な技術要素 | VTEP、VNI、VXLAN encapsulation、UDP 4789 | MP-BGP、Route Type 1–4、ESI、MAC/IP route、INSET |
| 相互関係 | EVPN のデータ面として使われることが多い | VXLAN の制御面として使われることが多い |
| 単体利用 | 可能。static / multicast 制御と組み合わせる | 可能。VXLAN / MPLS / SRv6 など複数のデータ面と組み合わせる |
| 典型利用例 | DC Overlay、マルチテナント L2/L3 延長 | EVPN over VXLAN、大規模マルチテナント、VXLAN の制御面として |
---
## 6. まとめ
一言でまとめると、
- **VXLAN** は、Overlay L2/L3 を物理ネットワーク上に作るための **データ面のカプセル化方式**
- **EVPN** は、その Overlay を制御するための **BGP ベースの制御面方式**
実務では、
> **EVPN over VXLAN**
> = VXLAN がデータ面、EVPN が制御面
という構成が中心です。
つまり、
- **VXLAN:Overlay の“運搬手段”**
- **EVPN:Overlay の“制御と最適化”**
と捉えておくと分かりやすいです。
回答内容の評価
以下は GPT-6 Astra (高) による回答評価となる。技術的な指摘には照合先の RFC・公式資料を併記する。実機での動作確認とは別の AI による評価であり、著者が全項目を独立に検証した結果を示すものではない
| 確認項目 | 結果 |
|---|---|
| 日本語として読みやすいか | ○:見出し・箇条書き・具体例で整理されており、流れを追いやすい。ただし、同じ結論の繰り返しが多く、やや冗長 |
| VXLAN と EVPN の役割を区別できているか | ○:EVPN-VXLAN 構成でのデータ面と制御面の役割分担を一貫して説明できている。詳細な経路種別の説明には誤りあり |
| 指定した表形式で回答できているか | ○:比較表で役割を整理できている。ただし、表の中にも誤った用語が含まれる |
| 説明に誤りや補足が必要な箇所はあるか | あり:Route Type 1・3・4 の名称と役割、VLAN タグの扱い、MAC 学習の説明などに修正が必要 |
特に Route Type の誤りは、ネットワークエンジニア向けの説明として見過ごせない。正しい対応は下記となる。RFC 7432 §7
| Route Type | 正しい名称 | 今回の回答の評価 |
|---|---|---|
| 1 | Ethernet Auto-Discovery | 「Ethernet Segment route」としており、Type 4 と混同 |
| 2 | MAC/IP Advertisement | MAC/IP 情報を広告するという説明は概ね正しい |
| 3 | Inclusive Multicast Ethernet Tag(IMET) | 「INSET」は誤り。BUM 配送先の把握に使うという方向性は正しい |
| 4 | Ethernet Segment | 「EVPN Multicast route」は誤り。同一 ES に接続する PE の発見や DF 選出に関わる |
そのほか、次の点を補足する
- VLAN タグ付きフレームに限定されない:VXLAN が運ぶのは Ethernet フレームであり、802.1Q タグが必須ではない。RFC 7348 では、別途設定がない限り、カプセル化時に内部の VLAN タグを除去する扱いとなる。RFC 7348 §6.1
- MAC 学習がすべて BGP に置き換わるわけではない:ローカル接続先の MAC 学習と、リモートへの BGP 広告・学習を区別する必要がある。ESI も VTEP 群そのものではなく Ethernet Segment を識別する。RFC 7432 §9、同 §3
- IP プレフィックス広告の説明には Type 5 を補足する:回答では IP prefix route に触れているが、対応する Route Type 5 の説明がない。RFC 9136 §3
今回の1回答では、Ollama + gpt-oss 20B より日本語の読みやすさと役割分担の説明は良好だった。一方で、専門用語と経路種別には明確な誤りがあり、そのまま設計・設定の根拠には使えない。モデル全体の優劣を、この1回答だけで判断するものではない
ここでは回答内容の動作確認を行う。Ollama の CLI と Splash のチャット画面では生成設定が揃っていないため、応答時間の比較は後述の共通 API 試験で行う
メモリ使用量を確認する
Ollama と同じく、アクティビティモニタの「メモリ」タブで確認する。Ollama のモデルはアンロードした状態とし、ほかのアプリの起動状況もなるべく揃える
Splash 実行時の撮影では、メモリプレッシャーは緑色の範囲に収まっていた。Mac 全体の使用済みメモリは25.50GB、スワップ使用領域は576KBだった
Ollama の撮影時と比べて使用済みメモリは約1.14GB多い。ただし、どちらも macOS やほかのアプリを含む Mac 全体の値であり、モデル単体の使用量の差ではない。この画像だけでは起動前からの増加量や、実行中のピークまでは分からない
API の動作確認
別のターミナルで作業ディレクトリに移動し、モデル一覧を確認する
cd ~/macmini-localllm/verification
curl --fail-with-body http://127.0.0.1:8000/v1/models
Ollama と同じ質問・出力上限を使う。今回は待ち時間を重視する設定として、Qwen 側は思考を無効にする
cat > splash-request.json <<'EOF'
{
"model": "unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "none",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
curl --fail-with-body http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
--data-binary @splash-request.json \
-o splash-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
cat splash-response.json
choices[0].message.content と finish_reason を確認する。reasoning_effort: none の扱いは Splash の思考設定 を参照
/v1/models で対象の UD-IQ4_XS、コンテキスト8192、テキスト入力のみであることを確認できた。チャット API も HTTP 200で回答が返った
Splash のローカル API 応答全文
suzuyu@macmini-m6 verification % curl --fail-with-body http://127.0.0.1:8000/v1/models
{"object":"list","data":[{"id":"unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS","object":"model","created":0,"owned_by":"splash","max_model_len":8192,"context_length":8192,"vision":false,"input_modalities":["text"]}],"models":[{"name":"unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS","description":"Splash resident model","release_date":""}]}%
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification % cat > splash-request.json <<'EOF'
{
"model": "unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "none",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
suzuyu@macmini-m6 verification % curl --fail-with-body http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
--data-binary @splash-request.json \
-o splash-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1993 100 1633 100 360 219 48 0:00:07 0:00:07 --:--:-- 477
HTTP=200 total=7.440603s
suzuyu@macmini-m6 verification % cat splash-response.json
{"id":"chatcmpl-68418aee3bad03571ba183fed54e670e","object":"chat.completion","created":1792308713,"model":"unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS","choices":[{"index":0,"message":{"role":"assistant","content":"Podはコンテナの最小実行単位で、同じストレージやネットワークを共有します。一方、DeploymentはPodの複製と更新を管理する上位リソースです。\n\n主な違いは以下の3点です。\n1. **管理対象**: Podは単一の実行体、Deploymentは複数のPodを管理します。\n2. **永続性**: Podは破損時に自動再作成されますが、DeploymentはPodの存在自体を保証します。\n3. **デプロイ機能**: Deploymentはロールバックや段階的更新などのライフサイクル管理が可能です。"},"finish_reason":"stop"}],"usage":{"prompt_tokens":39,"completion_tokens":121,"total_tokens":160,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}},"metrics":{"prefill":{"tokens":39},"decode":{"tokens":120},"request_latency":{"start_to_first_token_ms":313.237,"first_token_to_done_ms":7120.87,"wall_ms":7434.472,"ttft_ms":313.60199999999986,"queue_to_start_ms":0.36499999999983856,"stream_tokens_per_second":16.8518734368132},"cache":{"status":"miss","matched_tokens":0,"deduplicated_tokens":0,"lane":0},"speculation":{"drafted_tokens":399,"accepted_draft_tokens":63,"cycles":57}},"timings":{"prompt_n":39,"prompt_ms":313.237,"prompt_per_second":124.50636419069266,"predicted_n":121,"predicted_ms":7120.87,"predicted_per_second":16.8518734368132,"cache_n":0,"draft_n":399,"draft_n_accepted":63}}%
suzuyu@macmini-m6 verification %
| 確認項目 | 結果 |
|---|---|
| HTTP ステータス | 200 |
| 応答完了までの時間 | 7.440603秒 |
| 終了理由 |
stop。出力上限による打ち切りではない |
| 日本語・3点の指定 | 日本語で3点に整理して回答 |
| 合計300字以内 | 遵守。回答本文は Markdown 記号・空白・改行を含め254文字 |
| 思考トークン | reasoning_tokens: 0 |
| 技術説明の正確さ | Pod の自動再作成に関する説明に誤りあり |
回答は簡潔で、今回は文字数の指定にも対応できた。一方、2点目の「Podは破損時に自動再作成されます」は、単独の Pod の説明として誤り。Pod 自体が自動再作成するのではなく、Deployment 配下では ReplicaSet などのコントローラが代わりの Pod を作成する。コンテナの再起動とも区別が必要となる。Pod のライフサイクル
「DeploymentはPodの存在自体を保証します」も強すぎる表現で、希望するレプリカ数に近づけるよう管理する、と説明する方が正確。また、Deployment は1個の Pod を管理する構成にもできるため、単一か複数かだけが違いではない。Deployment のレプリカ数
レスポンスのサーバ側計測値は ttft_ms が約314ms、生成速度は約16.85 tokens/s、キャッシュは miss だった。今回は stream: false のため、この TTFT はクライアントで回答の表示が始まるまでの時間ではない
停止
Splash を起動したターミナルで Ctrl+C を押して終了する。
2構成の比較
今回は、Mac mini の32GB環境で起動し、日本語の質問とローカル API に応答できるかを初期確認した。ここまでの実行結果をまとめる
比較条件
| 項目 | Ollama + gpt-oss 20B | Splash + Qwen3.8 |
|---|---|---|
| 実行ツール | Ollama 0.40.6 | Splash 1.3.2 |
| モデル・量子化 | gpt-oss 20B / MXFP4 | Qwen3.8-27B / UD-IQ4_XS |
| コンテキスト上限 | 8192 | 8192 |
| VXLAN / EVPN の質問 | CLI で入力 | チャット画面で入力 |
| Kubernetes の質問 | ローカルの /v1/chat/completions
|
同左 |
| API の生成設定 |
temperature: 0.7、max_tokens: 2048、stream: false
|
同左 |
| API の思考設定 | low |
none |
2つのモデルは切り替えて実行し、API には同じ質問を1リクエストずつ送った。モデル・量子化・思考設定が異なるため、構成全体の使用感を確認するものとして扱う
初期確認の結果
| 確認項目 | Ollama + gpt-oss 20B | Splash + Qwen3.8 |
|---|---|---|
| 起動・日本語での応答 | 確認できた | 確認できた |
| VXLAN / EVPN の回答 | 表形式には対応。役割の説明に混乱があり、技術的な誤りもあった | 役割分担の説明は一貫していて読みやすいが、Route Type 1・3・4 に誤りがあった |
| Kubernetes の回答 | 日本語で3点に整理。再起動・スケーリングの説明に補足が必要 | 日本語で簡潔に3点に整理。Pod の自動再作成について誤りがあった |
| Kubernetes の300字以内の指定 | 未達:455文字 | 遵守:254文字 |
| ローカル API | HTTP 200、finish_reason: stop
|
HTTP 200、finish_reason: stop
|
| API 応答完了時間(各1回) | 約7.68秒 | 約7.44秒 |
| メモリプレッシャー(撮影時点) | 緑色 | 緑色 |
| Mac 全体の使用済みメモリ(撮影時点) | 24.36GB | 25.50GB |
| スワップ使用領域(撮影時点) | 0バイト | 576KB |
文字数は回答本文の Markdown 記号・空白・改行を含めて数えた。技術的な誤りと補足は、それぞれの動作確認の節に記載している
API 応答完了時間は curl の total で、送信から応答受信完了までの値となる。各1回の測定で、生成された回答の長さや思考設定も異なるため、約0.24秒の差から速度の優劣は判断しない。初期ロードやキャッシュの条件を揃えた性能測定は今回は行っていない
メモリはアクティビティモニタを撮影した時点の Mac 全体の値で、実行中のピークやモデル単体の使用量ではない
使ってみた所感
今回の32GB環境では、両構成ともモデルの起動、日本語での回答、ローカル API からの応答まで確認できた。撮影時点のメモリプレッシャーはどちらも緑色で、まずローカル LLM を試す構成として動かすことができた
回答の印象では、Splash + Qwen3.8 は VXLAN / EVPN の役割分担を整理できており、Kubernetes の質問でも300字以内の指定を守れた。一方で、両構成とも専門用語や動作の説明に誤りがあった。技術的な相談のたたき台には使えるが、設計や設定に採用する内容は公式資料と照合する必要がある
今回はここまでを初期確認とする。繰り返し測定による応答時間の比較、長文・同時リクエスト・長時間運用、画像入力、JSON Schema・関数呼び出しの評価は、実際の用途に合わせて別途検討する
別PC・サーバからの API 応答確認
検証構成
同じ Mac 内での比較を終えてから、LAN 内の別端末から応答確認する。ローカルツールとの連携は別途検討し、ここでは curl を使う
| 項目 | 設定 |
|---|---|
| API サーバ | Mac mini:192.168.129.6
|
| 接続元 | ROCKY01:Linux、192.168.129.12/24(ens192) |
| Ollama | http://192.168.129.6:11434/v1/chat/completions |
| Splash | http://192.168.129.6:8000/v1/chat/completions |
Mac mini に 192.168.129.6 が割り当てられていることを、システム設定の「ネットワーク」で確認する。IP を維持する場合は DHCP 予約またはネットワークに合った固定設定を使う
事前準備のファイアウォール設定を済ませ、PF が有効で local.llm のルールが読み込まれていることを確認する。今回の許可元は 192.168.129.0/24。アプリケーションファイアウォールでも、実際に起動するサーバの受信接続を許可する。宛先 IP の指定だけでは接続元の制限にはならない
以下のクライアント用コマンドは macOS / Linux のシェルを想定する。Windows では WSL などの bash 環境で実行する。前節の ollama-request.json と splash-request.json を接続元にも作成またはコピーし、そのディレクトリで実行する
Ollama の待ち受け先を変更する(Mac mini 側)
Splash が起動中なら Ctrl+C で終了する。Ollama はサービス設定のコピーに LAN IP を指定して起動する。これらの操作は、サービスを登録した同じユーザーで行う。待ち受け先の設定
通常のサービスが起動している状態で設定をコピーし、コピー側に OLLAMA_HOST を追加する。Homebrew が設定した Flash Attention などの値も引き継ぐ
今回の環境では、brew services start ollama の起動ログに表示されたラベルは sh.brew.ollama だった。コピー元のファイル名は sh.brew.ollama.plist とする。Homebrew の版や既存の登録状態で名前が異なる場合は、brew services list の File 列を確認する
brew services list
brew services info ollama
ls -l "$HOME/Library/LaunchAgents/sh.brew.ollama.plist"
サービスが未登録・停止中の場合は、先の起動確認の手順で通常のサービスを起動する。コピー元の存在を確認してから下記へ進む
mkdir -p "$HOME/.config/ollama"
cp "$HOME/Library/LaunchAgents/sh.brew.ollama.plist" "$HOME/.config/ollama/ollama-lan.plist"
plutil -replace EnvironmentVariables.OLLAMA_HOST -string "192.168.129.6:11434" "$HOME/.config/ollama/ollama-lan.plist"
plutil -lint "$HOME/.config/ollama/ollama-lan.plist"
構文確認が OK になったら、通常のサービスを停止し、LAN 用の設定で起動する
brew services stop ollama
brew services run ollama --file="$HOME/.config/ollama/ollama-lan.plist"
brew services list
run はログイン時の自動起動を登録せずにサービスを起動する。今回は再起動後に PF を再適用する手順のため、LAN 待ち受けを自動起動させない。再起動後に LAN 試験を再開する場合も、先に PF の有効化とルールを確認する。services run と --file
Mac mini 側で待ち受け先を確認する
lsof -nP -iTCP:11434 -sTCP:LISTEN
待ち受けが 192.168.129.6:11434 となったことを確認する。この設定中は 127.0.0.1:11434 では接続できない。Mac 内の CLI でも接続先を明示する
OLLAMA_HOST=192.168.129.6:11434 ollama ps
実機では plist の構文確認が OK となり、brew services run で起動できた。lsof でも 192.168.129.6:11434 (LISTEN) を確認した
この時点の ollama ps はヘッダーのみだった。サーバ再起動後でモデルがロードされていない状態のため、そのまま次の API 応答確認へ進む。ここまでで確認できたのは Mac mini 側の待ち受けで、別端末からの接続は次の手順で確認する
この直接接続には認証がない。接続できる端末は推論以外の管理 API も呼び出せるため、192.168.129.0/24 内の端末を信頼できる検証環境で行う。認証を付けて常用する構成は別途検討する
Ollama の LAN 待ち受け設定ログ
suzuyu@macmini-m6 verification % brew services start ollama
==> Successfully started `ollama` (label: sh.brew.ollama)
suzuyu@macmini-m6 verification % ls -l "$HOME/Library/LaunchAgents/homebrew.mxcl.ollama.plist"
ls: /Users/suzuyu/Library/LaunchAgents/homebrew.mxcl.ollama.plist: No such file or directory
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification % ls -l "$HOME/Library/LaunchAgents/sh.brew.ollama.plist"
-rw-r--r-- 1 suzuyu staff 1014 10月 18 17:33 /Users/suzuyu/Library/LaunchAgents/sh.brew.ollama.plist
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification % brew services list
Name Status User File
ollama started suzuyu ~/Library/LaunchAgents/sh.brew.ollama.plist
suzuyu@macmini-m6 verification % brew services info ollama
ollama (sh.brew.ollama)
Running: ✔
Loaded: ✔
Schedulable: ✘
User: suzuyu
PID: 8699
suzuyu@macmini-m6 verification % ls -l "$HOME/Library/LaunchAgents/sh.brew.ollama.plist"
-rw-r--r-- 1 suzuyu staff 1014 10月 18 17:33 /Users/suzuyu/Library/LaunchAgents/sh.brew.ollama.plist
suzuyu@macmini-m6 verification % mkdir -p "$HOME/.config/ollama"
suzuyu@macmini-m6 verification % cp "$HOME/Library/LaunchAgents/sh.brew.ollama.plist" "$HOME/.config/ollama/ollama-lan.plist"
suzuyu@macmini-m6 verification % plutil -replace EnvironmentVariables.OLLAMA_HOST -string "192.168.129.6:11434" "$HOME/.config/ollama/ollama-lan.plist"
suzuyu@macmini-m6 verification % plutil -lint "$HOME/.config/ollama/ollama-lan.plist"
/Users/suzuyu/.config/ollama/ollama-lan.plist: OK
suzuyu@macmini-m6 verification %
suzuyu@macmini-m6 verification % brew services stop ollama
Stopping `ollama`... (might take a while)
==> Successfully stopped `ollama` (label: sh.brew.ollama)
suzuyu@macmini-m6 verification % brew services run ollama --file="$HOME/.config/ollama/ollama-lan.plist"
==> Successfully ran `ollama` (label: sh.brew.ollama)
suzuyu@macmini-m6 verification % brew services list
Name Status User File
ollama started /opt/homebrew/opt/ollama/sh.brew.ollama.plist
suzuyu@macmini-m6 verification % lsof -nP -iTCP:11434 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ollama 8998 suzuyu 3u IPv4 0xc1fee6e3c24e661c 0t0 TCP 192.168.129.6:11434 (LISTEN)
suzuyu@macmini-m6 verification % OLLAMA_HOST=192.168.129.6:11434 ollama ps
NAME ID SIZE PROCESSOR CONTEXT RUNNER UNTIL
suzuyu@macmini-m6 verification %
Ollama を呼び出す(別PC・サーバ側)
まずモデル一覧を取得する
curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:11434/v1/models
ROCKY01(192.168.129.12)から、gpt-oss-20b-8k:latest と gpt-oss:20b の一覧を取得できた
続いて、接続元の PC・サーバで、同一 Mac 内で使ったものと同じリクエストファイルを作成する。以下のファイル作成と curl は同じディレクトリで実行する
cat > ollama-request.json <<'EOF'
{
"model": "gpt-oss-20b-8k",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "low",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
作成したファイルを使ってリクエストを送る
curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:11434/v1/chat/completions \
-H 'Content-Type: application/json' \
--data-binary @ollama-request.json \
-o ollama-lan-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
cat ollama-lan-response.json
HTTP 200と choices[0].message.content の回答を確認する。接続先は Mac mini の IP であり、接続元端末の localhost ではない
ROCKY01 から HTTP 200で日本語の回答を取得でき、finish_reason は stop だった
応答完了時間は12.547997秒だった。直前の ollama ps はモデル未ロードだったため、モデルのロード時間を含む可能性がある。ローカル試験の約7.68秒との差を、そのまま LAN の通信遅延とはみなさない。ここでは別端末から API で回答を取得できたことを確認結果とする
Ollama の LAN API 応答全文
ROCKY01 /home/suzuyu% ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:d0:ec:55 brd ff:ff:ff:ff:ff:ff
altname enp11s0
inet 192.168.129.12/24 brd 192.168.129.255 scope global dynamic noprefixroute ens192
valid_lft 84213sec preferred_lft 84213sec
inet6 fe80::20c:29ff:fed0:ec55/64 scope link noprefixroute
valid_lft forever preferred_lft forever
ROCKY01 /home/suzuyu% curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:11434/v1/models
{"object":"list","data":[{"id":"gpt-oss-20b-8k:latest","object":"model","created":1792302933,"owned_by":"library"},{"id":"gpt-oss:20b","object":"model","created":1792302782,"owned_by":"library"}]}
ROCKY01 /home/suzuyu% cat > ollama-request.json <<'EOF'
{
"model": "gpt-oss-20b-8k",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "low",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
ROCKY01 /home/suzuyu% curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:11434/v1/chat/completions \
-H 'Content-Type: application/json' \
--data-binary @ollama-request.json \
-o ollama-lan-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
cat ollama-lan-response.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1690 100 1351 100 339 107 27 0:00:12 0:00:12 --:--:-- 381
HTTP=200 total=12.547997s
{"id":"chatcmpl-247","object":"chat.completion","created":1792313251,"model":"gpt-oss-20b-8k","system_fingerprint":"fp_ollama","choices":[{"index":0,"message":{"role":"assistant","content":"**1. 管理単位の違い** \n- **Pod** は単一または複数コンテナをまとめた実行単位で、直接スケジューラに配置される。 \n- **Deployment** はPodのレプリカ数や更新戦略を管理するコントローラで、宣言的に希望状態を維持する。 \n\n**2. スケーリングとローリングアップデート** \n- Pod は手動で生成・削除し、更新は新しいPodを作って古いPodを削除する形。 \n- Deployment は `kubectl scale` や `spec.strategy` で自動的にレプリカ数を調整し、ローリングアップデートを安全に実施できる。 \n\n**3. 冗長性と耐障害性** \n- Pod だけだと障害時に単一障害点が残る。 \n- Deployment は複数のPodをクラスタに分散し、Podが失敗しても自動的に新しいPodを作成して可用性を保つ。","reasoning":"Need 3 points, 300 chars max, Japanese. 300 characters inclusive? So 300 characters. Provide concise. Let's craft ~280 chars."},"finish_reason":"stop"}],"usage":{"prompt_tokens":95,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens":287,"total_tokens":382}}
ROCKY01 /home/suzuyu%
Ollama をローカル待ち受けに戻す(Mac mini 側)
Splash の試験に移る前に、モデルをアンロードする
OLLAMA_HOST=192.168.129.6:11434 ollama stop gpt-oss-20b-8k
LAN 用のサービスを停止し、Homebrew 標準の設定で起動し直す。--file を付けず、ローカル待ち受けとログイン時の自動起動に戻す
brew services stop ollama
brew services start ollama
起動を待って、Mac mini 側で確認する
lsof -nP -iTCP:11434 -sTCP:LISTEN
ollama ps
待ち受けが 127.0.0.1:11434 に戻り、モデルがアンロードされていることを確認する
実機でも 127.0.0.1:11434 (LISTEN) への復帰を確認できた。ollama ps はヘッダーのみで、モデルがロードされていない状態となった。この状態で Splash の LAN 試験へ進む
Ollama のローカル待ち受け復帰ログ
suzuyu@macmini-m6 verification % OLLAMA_HOST=192.168.129.6:11434 ollama stop gpt-oss-20b-8k
suzuyu@macmini-m6 verification % brew services stop ollama
Stopping `ollama`... (might take a while)
==> Successfully stopped `ollama` (label: sh.brew.ollama)
suzuyu@macmini-m6 verification % brew services start ollama
==> Successfully started `ollama` (label: sh.brew.ollama)
suzuyu@macmini-m6 verification % lsof -nP -iTCP:11434 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ollama 9250 suzuyu 3u IPv4 0xcfa05c3eb2dfd786 0t0 TCP 127.0.0.1:11434 (LISTEN)
suzuyu@macmini-m6 verification % ollama ps
NAME ID SIZE PROCESSOR CONTEXT RUNNER UNTIL
suzuyu@macmini-m6 verification %
Splash を認証付きで起動する(Mac mini 側)
API キーをファイルに保存し、起動時に読み込む。初回のみ、32バイトの乱数を16進数64文字で生成する。-out などのオプションは最後のバイト数 32 より前に指定する。openssl rand
mkdir -p "$HOME/.config/splash"
(
umask 077
openssl rand -hex -out "$HOME/.config/splash/SPLASH_API_KEY" 32
)
新規ファイルは所有者のみ読み書きできる権限で作成される。再起動時は生成し直さず、保存済みのキーを読み込む。キーの生成コマンドを再実行するとファイルの値が変わるため、接続元にも同じキーを反映する
キーの読み込みに失敗した場合や内容が空の場合は、サーバを起動せずに終了する。下記は全体をまとめて実行する。丸括弧内のサブシェルで処理するため、エラー時も操作中のターミナルは終了しない。キー自体は画面に表示せず、記事・実行ログ・Git に含めない
(
if ! SPLASH_API_KEY="$(cat "$HOME/.config/splash/SPLASH_API_KEY")" || [ -z "$SPLASH_API_KEY" ]; then
printf '%s\n' 'API キーを読み込めないか、内容が空です。Splash は起動しません。' >&2
exit 1
fi
export SPLASH_API_KEY
splash serve \
--model unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS \
--host 192.168.129.6 \
--port 8000 \
--max-context 8K \
--language-only \
--no-webui
)
Ready の表示を待つ。SPLASH_API_KEY により API 認証が有効になる。今回は API のみ試すため、チャット画面は --no-webui で無効にする。/health や /ready などはキー認証の対象外となる。Splash のネットワーク・認証設定
Splash の認証付き起動ログ(変更前の実行記録)
以下はキー読み込みの失敗判定を追加する前の実行記録。起動時は上記の手順を使う
suzuyu@macmini-m6 verification % mkdir -p "$HOME/.config/splash"
(
umask 077
openssl rand -hex -out "$HOME/.config/splash/SPLASH_API_KEY" 32
)
suzuyu@macmini-m6 verification % export SPLASH_API_KEY="$(cat "$HOME/.config/splash/SPLASH_API_KEY")"
suzuyu@macmini-m6 verification % splash serve \
--model unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS \
--host 192.168.129.6 \
--port 8000 \
--max-context 8K \
--language-only \
--no-webui
Splash model unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS is already installed in /Users/suzuyu/Library/Application Support/Splash/models/.selections/834e3c6d41a9ee5bbe160f89275d30a18e1cca3874fb1159b752aa620232b8e1
17:58:43 Loading · unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS
17:58:44 Chat template · patched to render later system messages in place (the original template rejects them), generation prompt 5-7 tokens
17:58:49 Weights loaded in 4.99 s.
17:58:49 Kernel policy for GPU family 11 with 12 cores.
17:58:49 Setting up the Neural Engine FFN split (splash serve --disable-ane keeps the FFN on the GPU).
17:58:50 Neural Engine FFN split at share 0.76 for chunks of 512 rows or more, 1.0% from the GPU alone on the Neural Engine's part (set up as calibrated before in 1.1 s).
17:58:50 SSD cache: up to 32.0 GiB of temporary files in /var/folders/vm/x0l5wqcx7njgdqpfvbpp20n00000gn/T/ (its automatic quota, while its volume has room beyond the 10.0 GiB it keeps free); KV pages of 1040 KiB and states of 152 MiB, a state's write staging through 152 MiB of the memory plan; --max-cache-disk 0 turns it off.
17:58:57 Ready · unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS · context 8K · language only · http://192.168.129.6:8000
Splash を呼び出す(別PC・サーバ側)
Mac 側で生成したキーファイルを SFTP/SCP などで接続元の $HOME/.config/splash/SPLASH_API_KEY にコピーし、同じキーを読み込む。接続元で別のキーを生成しない。API キーは HTTP 通信を暗号化する仕組みではないため、この例は信頼できる検証用 LAN に限定する
chmod 600 "$HOME/.config/splash/SPLASH_API_KEY"
export SPLASH_API_KEY="$(cat "$HOME/.config/splash/SPLASH_API_KEY")"
curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:8000/v1/models \
-H "Authorization: Bearer $SPLASH_API_KEY"
実機では、Mac mini から SFTP でキーを転送し、ROCKY01 の所定の場所へ移動して権限を600に設定した。キー付きのリクエストで対象モデルの一覧を取得できた
チャット用のリクエストファイルも、接続元の ROCKY01 で作成する。ファイル作成と curl は同じディレクトリで実行する
cat > splash-request.json <<'EOF'
{
"model": "unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "none",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:8000/v1/chat/completions \
-H "Authorization: Bearer $SPLASH_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary @splash-request.json \
-o splash-lan-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
cat splash-lan-response.json
ROCKY01 から HTTP 200で日本語の回答を取得できた。finish_reason は stop、応答完了時間は8.937652秒だった
認証ヘッダーなしのリクエストが拒否されることも確認する
curl --connect-timeout 5 \
http://192.168.129.6:8000/v1/models \
-w '\nHTTP=%{http_code}\n'
認証ヘッダーを付けない /v1/models へのリクエストは HTTP 401 となり、invalid or missing API key・authentication_error が返った。キー付きの応答取得と、キーなしのアクセス拒否を確認できた
試験後は Mac 側の Splash を Ctrl+C で終了し、Mac と接続元の両方で下記を実行する
unset SPLASH_API_KEY
unset は環境変数を解除する操作で、保存したキーファイルは残る
再びローカル利用する場合は、前節の --host・--no-webui を付けない起動コマンドを使う
Splash のキー転送・LAN API 応答・認証確認ログ
suzuyu@macmini-m6 ~ % sftp 192.168.129.12
suzuyu@192.168.129.12's password:
Connected to 192.168.129.12.
sftp> put .config/splash/SPLASH_API_KEY
Uploading .config/splash/SPLASH_API_KEY to /home/suzuyu/SPLASH_API_KEY
SPLASH_API_KEY 100% 65 18.8KB/s 00:00
sftp> exit
suzuyu@macmini-m6 ~ % ssh 192.168.129.12
suzuyu@192.168.129.12's password:
Last login: Sun Oct 18 17:44:49 2026 from 192.168.129.6
ROCKY01 /home/suzuyu% mkdir -p "$HOME/.config/splash"
ROCKY01 /home/suzuyu% mv SPLASH_API_KEY .config/splash/
ROCKY01 /home/suzuyu% chmod 600 "$HOME/.config/splash/SPLASH_API_KEY"
export SPLASH_API_KEY="$(cat "$HOME/.config/splash/SPLASH_API_KEY")"
ROCKY01 /home/suzuyu% curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:8000/v1/models \
-H "Authorization: Bearer $SPLASH_API_KEY"
{"object":"list","data":[{"id":"unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS","object":"model","created":0,"owned_by":"splash","max_model_len":8192,"context_length":8192,"vision":false,"input_modalities":["text"]}],"models":[{"name":"unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS","description":"Splash resident model","release_date":""}]}%
ROCKY01 /home/suzuyu% cat > splash-request.json <<'EOF'
{
"model": "unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS",
"messages": [
{
"role": "user",
"content": "日本語で回答してください。Kubernetes の Pod と Deployment の違いを3点、合計300字以内で説明してください。"
}
],
"reasoning_effort": "none",
"temperature": 0.7,
"max_tokens": 2048,
"stream": false
}
EOF
ROCKY01 /home/suzuyu% curl --fail-with-body --connect-timeout 5 \
http://192.168.129.6:8000/v1/chat/completions \
-H "Authorization: Bearer $SPLASH_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary @splash-request.json \
-o splash-lan-response.json \
-w 'HTTP=%{http_code} total=%{time_total}s\n'
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2144 100 1784 100 360 199 40 0:00:09 0:00:08 0:00:01 453
HTTP=200 total=8.937652s
ROCKY01 /home/suzuyu% cat splash-lan-response.json
{"id":"chatcmpl-32eb3d15eeef9d5315da25daec8f5cb0","object":"chat.completion","created":1792314287,"model":"unsloth/Qwen3.8-27B-GGUF:UD-IQ4_XS","choices":[{"index":0,"message":{"role":"assistant","content":"Podはコンテナの最小実行単位であり、単一のグループ化されたコンテナ群を指します。一方、DeploymentはPodを管理・制御する抽象化オブジェクトです。\n\n1点目は役割の違いで、Podはアプリの実体ですが、Deploymentはそれらの複製や更新を統括する親です。2点目は持続性で、Podはノード故障時に失われますが、DeploymentはReplicaSetを介してPodを自動再作成し、可用性を維持します。3点目は運用性で、Deploymentはロールバックや段階的デプロイ(ロールリングアップデート)などの高度な更新戦略をネイティブにサポートし、安定した運用を可能にします。"},"finish_reason":"stop"}],"usage":{"prompt_tokens":39,"completion_tokens":147,"total_tokens":186,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}},"metrics":{"prefill":{"tokens":39},"decode":{"tokens":146},"request_latency":{"start_to_first_token_ms":311.202,"first_token_to_done_ms":8607.3,"wall_ms":8922.352,"ttft_ms":315.0520000000015,"queue_to_start_ms":3.8500000000015007,"stream_tokens_per_second":16.96234591567623},"cache":{"status":"miss","matched_tokens":0,"deduplicated_tokens":0,"lane":0},"speculation":{"drafted_tokens":483,"accepted_draft_tokens":77,"cycles":69}},"timings":{"prompt_n":39,"prompt_ms":311.202,"prompt_per_second":125.32053135905296,"predicted_n":147,"predicted_ms":8607.3,"predicted_per_second":16.96234591567623,"cache_n":0,"draft_n":483,"draft_n_accepted":77}}%
ROCKY01 /home/suzuyu% curl --connect-timeout 5 \
http://192.168.129.6:8000/v1/models \
-w '\nHTTP=%{http_code}\n'
{"error":{"message":"invalid or missing API key","type":"invalid_request_error","code":"authentication_error"}}
HTTP=401
ROCKY01 /home/suzuyu%
LAN 経由の API 動作確認結果
| 確認項目 | Ollama | Splash |
|---|---|---|
| 192.168.129.0/24 内からモデル一覧を取得 | 成功:Mac Mini の外のサーバ(ROCKY01(192.168.129.12))からモデル一覧を取得 | 成功:同じ ROCKY01 から API キー付きでモデル一覧を取得 |
| チャットの HTTP ステータス・回答本文 | HTTP 200、日本語の回答を取得、finish_reason: stop
|
HTTP 200、日本語の回答を取得、finish_reason: stop
|
| 認証なしのリクエスト | 認証なしの仕様 |
/v1/models で HTTP 401、authentication_error
|
| API 完了時間 (秒) | 12.547997(ロード時間を含む可能性あり) | 8.937652 |
範囲外からの接続拒否試験は未実施。今回は PF ルールの読み込みと、許可範囲内からの API 応答を確認した
導入後のディスク容量と差分を確認する
一通りの動作確認が終わったら、Mac mini 側で測定する。Splash は Ctrl+C で終了し、Ollama のモデルも停止してから実行する。ダウンロードしたモデルは残した状態とする
ollama stop gpt-oss-20b-8k
LC_ALL=C df -kP /System/Volumes/Data > "$HOME/local-llm-measurements/disk-after.txt"
cat "$HOME/local-llm-measurements/disk-after.txt"
awk '
NR == FNR { if (FNR == 2) before = $4; next }
FNR == 2 {
after = $4
printf "導入前の空き容量: %.2f GiB\n", before / 1048576
printf "導入後の空き容量: %.2f GiB\n", after / 1048576
printf "空き容量の減少分(導入前 − 導入後): %+.2f GiB\n", (before - after) / 1048576
}
' "$HOME/local-llm-measurements/disk-before.txt" "$HOME/local-llm-measurements/disk-after.txt"
df -kP の空き容量は KiB 単位のため、GiB に換算して表示する。差分が正なら空き容量が減少、負なら増加している
| 項目 | 測定結果 |
|---|---|
| 導入前の空き容量 | 400.48GiB |
| 導入後の空き容量(測定時点) | 366.45GiB |
| 空き容量の減少分 | 34.03GiB |
導入後のディスク容量と差分の測定ログ
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/disk3s5 482746452 71014400 384255016 16% /System/Volumes/Data
導入前の空き容量: 400.48 GiB
導入後の空き容量: 366.45 GiB
空き容量の減少分(導入前 − 導入後): +34.03 GiB
差分には Homebrew・依存関係・モデル・ダウンロードファイルに加え、測定間に発生した他の書き込みや削除、スワップ、APFS のスナップショットなどの影響も含まれる。今回の作業前後で空き容量がどれだけ変化したかの目安として扱う
Splash の実行中 SSD キャッシュは終了時に解放されるため、この差分には実行中の最大使用量は表れない。実行中の容量も確認する場合は、Splash を動かしている間に別ターミナルで df -h /System/Volumes/Data を実行し、停止後の値とは分けて記録する
利用後の停止と設定の切り戻し
停止方法をまとめている。モデルの削除と PF 設定の解除は、必要になった場合の参考手順として記載する。(未実施)
モデル・API サーバを停止する
| 対象 | 操作 | 停止後の状態 |
|---|---|---|
| Ollama のモデル | ollama stop gpt-oss-20b-8k |
モデルをメモリからアンロードする。API サーバは動き続ける |
| Ollama の API サーバ | brew services stop ollama |
サーバを停止し、ログイン時の自動起動も解除する |
| Splash | 起動したターミナルで Ctrl+C
|
サーバを終了し、モデルのメモリを解放する |
Ollama が LAN 待ち受け中の場合、モデルの停止は OLLAMA_HOST=192.168.129.6:11434 ollama stop gpt-oss-20b-8k とする。停止後も同じ Mac から利用する場合は、LAN 試験の「Ollama をローカル待ち受けに戻す」の手順を参照する
停止だけではダウンロード済みモデルは削除されない。モデル自体が不要になった場合の削除方法は下記となる。Ollama CLI Reference
Ollama のダウンロード済みモデルを削除する(必要な場合のみ)
ollama rm gpt-oss-20b-8k
ollama rm gpt-oss:20b
Ollama の API サーバが起動し、CLI から接続できる状態で実行する。再び使用する場合は、モデルのダウンロードと8K設定のモデル作成を行う
PF の接続制限を解除する(必要な場合のみ)
今回追加した接続制限が不要になった場合にだけ実行する。LAN 経由での API 利用を続ける場合は設定を維持する
設定を戻す場合は、先に LAN 向けの Ollama / Splash を停止する。/etc/pf.conf から今回追加した anchor "local.llm" と load anchor "local.llm" ... の2行だけを削除する。初回バックアップは差分確認用に残し、後から追加された他の設定を上書きしない
sudo nano /etc/pf.conf
sudo pfctl -nf /etc/pf.conf
構文確認後に再読込し、専用 anchor のルールを空にする
sudo pfctl -f /etc/pf.conf
sudo pfctl -a local.llm -F rules
最後に、事前準備の sudo pfctl -E 実行時に表示された Token : 数値 を指定し、今回の PF 有効化要求を解除する。Token は有効化要求を識別する管理用の番号で、今回のログでは 923634586225060667 だった
下記の 123456789 は自分の実行時に表示された Token に置き換える。再起動後に有効化し直した場合は、そのときの Token を使う
sudo pfctl -X 123456789
-X は指定した有効化要求だけを解除する。フィルタルールの削除は前の手順で行う。ほかのサービスによる有効化要求を残すため、PF 全体を無効化する pfctl -d は使わない
おわりに
Mac Mini M6 で実際にローカル LLM を動かして動作確認まで試せた
AI エージェントやツールからの利用などは今後試す予定



