
昨今、ChatGPTをはじめとするクラウド型LLM(大規模言語モデル)が主流となっていますが、機密データの扱いや、APIの継続的な利用コストに頭を悩ませているエンジニアも多いのではないでしょうか。
「APIコストを気にせず、完全なローカル環境で
AIエージェントを24時間働かせたい」
今回はこの目標を達成するために、自宅のローカル環境にマルチノードのKubernetes(k8s)クラスタを構築し、ローカルLLM基盤である「Ollama」と、自律型AIエージェントフレームワーク「CrewAI」を組み合わせて、完全放置でリサーチとレポート作成を行ってくれるシステムをDIYで構築しました。
ネットワーク構成からストレージの永続化、Pythonエージェントの実装、そしてk8s Jobを使った非同期タスクの実行まで、すべてのコマンドとマニフェストを詳細に解説します。
1. システムアーキテクチャの全体像
今回の完全ローカルAIエージェント環境のアーキテクチャは以下の3層構造になっています。
- 🐳 インフラ基盤 (Kubernetes)
k8s-masterとk8s-worker1の2台構成。- CNI(コンテナネットワークインターフェース)には、eBPFベースで高速・高機能な Cilium を採用。
- 🧠 LLM基盤 (Ollama)
- 軽量かつ高性能な
qwen2.5:7bモデルを使用。 - k8sの
Deploymentとしてマスターノードに固定配置し、hostPathでモデルデータを永続化。
- 軽量かつ高性能な
- 🤖 AIエージェント (CrewAI)
- Pythonで記述したエージェント(リサーチャー&ライター)をDockerコンテナ化。
- k8sの
Jobリソースとして実行し、タスク完了後にホストのディレクトリへ成果物(Markdown)を出力。
2. Kubernetesクラスタとネットワーク(CNI)の確認
まずはベースとなるKubernetesクラスタの状態を確認します。今回は旧来のFlannelではなく、ネットワークの可視化やセキュリティポリシーに優れたモダンなCNIである「Cilium」が稼働している環境をベースとします。
2.1 ノードのステータス確認
💻 実行コマンド (k8s-master)
root@k8s-master:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 11d v1.34.9
k8s-worker1 Ready <none> 11d v1.34.9
マスターノードとワーカーノードの2台が正常にクラスタに参加し、Ready 状態になっています。
2.2 システムPodとCiliumの稼働確認
ネットワークプラグインが正しく動いているか、kube-system ネームスペースを確認します。
💻 実行コマンド
root@k8s-master:~# kubectl get pods -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cilium-54znv 1/1 Running 0 11d 192.168.2.6 k8s-worker1 <none> <none>
cilium-9md7b 1/1 Running 0 11d 192.168.2.7 k8s-master <none> <none>
cilium-envoy-d2sk8 1/1 Running 0 11d 192.168.2.7 k8s-master <none> <none>
cilium-envoy-qsjzw 1/1 Running 0 11d 192.168.2.6 k8s-worker1 <none> <none>
cilium-operator-755dffc64d-fbsgq 1/1 Running 0 11d 192.168.2.7 k8s-master <none> <none>
coredns-66bc5c9577-bzd8m 1/1 Running 0 11d 10.0.0.148 k8s-master <none> <none>
coredns-66bc5c9577-p7vkh 1/1 Running 0 11d 10.0.0.140 k8s-master <none> <none>
すべてのCilium関連コンテナ、およびCoreDNSが Running となっており、ノード間のオーバーレイネットワークが完璧に構成されていることが分かります。
3. Ollama(ローカルLLM)のデプロイとデータ永続化
次に、クラスタ内で他のPod(エージェント)からLLMを呼び出せるように、Ollamaサーバーをデプロイします。ここで、マルチノード環境ならではの設計上の工夫が必要になります。
⚠️ 要注意ポイント:マルチノード環境におけるデータ永続化の罠
当初、ローカル環境でよく使われる hostPath を使ってストレージをマウントしようとしましたが、マルチノード環境では、Podが再起動するたびに別のノード(今回はワーカーノード)にスケジュールされてしまう可能性があります。
そうなると、せっかく数GBかけてダウンロードしたモデルデータが参照できず、エラーになってしまいます。
💡 解決策:
これを解決するため、マニフェスト内で nodeSelector を用いて、OllamaのPodを強制的に k8s-master ノードへ固定する構成としました。
3.1 Ollama用マニフェスト(01-ollama.yaml)
📄 01-ollama.yaml
apiVersion: v1
kind: Service
metadata:
name: ollama-service
namespace: default
spec:
type: ClusterIP
ports:
- port: 11434
targetPort: 11434
name: http
selector:
app: ollama
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: ollama
template:
metadata:
labels:
app: ollama
spec:
# ★重要: Podを常にマスターノードに固定してストレージを確実にマウントする
nodeSelector:
kubernetes.io/hostname: k8s-master
containers:
- name: ollama
image: ollama/ollama:latest
ports:
- containerPort: 11434
resources:
limits:
memory: "8Gi"
cpu: "4"
requests:
memory: "4Gi"
cpu: "2"
volumeMounts:
- name: ollama-storage
mountPath: /root/.ollama
volumes:
- name: ollama-storage
hostPath:
path: /var/lib/ollama-data
type: DirectoryOrCreate
3.2 デプロイとモデル(Qwen2.5:7b)のプル
マニフェストを適用し、Pod内に入ってモデルをダウンロード(プル)します。
💻 実行コマンド
root@k8s-master:~/k8s-manifests/infra/ollama# kubectl apply -f 01-ollama.yaml
service/ollama-service configured
deployment.apps/ollama configured
root@k8s-master:~/k8s-manifests/infra/ollama# kubectl exec -it deployment/ollama -- ollama pull qwen2.5:7b
pulling manifest
pulling 2bada8a74506: 100% ▕████████████████████████████████████████████████████████████████████████▏ 4.7 GB
pulling 66b9ea09bd5b: 100% ▕████████████████████████████████████████████████████████████████████████▏ 68 B
pulling eb4402837c78: 100% ▕████████████████████████████████████████████████████████████████████████▏ 1.5 KB
pulling 832dd9e00a68: 100% ▕████████████████████████████████████████████████████████████████████████▏ 11 KB
pulling 2f15b3218f05: 100% ▕████████████████████████████████████████████████████████████████████████▏ 487 B
verifying sha256 digest
writing manifest
success
3.3 永続化のテスト(Pod再起動)
Podを削除(再起動)しても、データが消えずに保持されているかテストします。
root@k8s-master:~/k8s-manifests/infra/ollama# kubectl rollout restart deployment/ollama
deployment.apps/ollama restarted
root@k8s-master:~/k8s-manifests/infra/ollama# kubectl exec -it deployment/ollama -- ollama list
NAME ID SIZE MODIFIED
qwen2.5:7b 845dbda0ea48 4.7 GB 13 seconds ago
再起動後も即座に 4.7 GB のモデルが認識されています。これでマスターノードのディスクにデータが保存される堅牢なLLM基盤が完成しました。
4. CrewAIによる自律エージェントの実装
LLM基盤が整ったので、次はPythonと CrewAI を使って「リサーチしてMarkdownにまとめる」という一連の作業を自動化するエージェントシステムを作成します。
📝 CrewAI実装時のポイント
最新のCrewAI(v1.x系)において、古い記法である langchain_community の Ollama クラスを使用すると、Pydanticの型バリデーションエラー(ValidationError)でクラッシュしてしまうことがあります。
解決策として、CrewAIネイティブの LLM クラス を用いてモデルを指定するのが現在のベストプラクティスです。
4.1 エージェントのソースコード(main.py)
📄 main.py
import os
from crewai import Agent, Task, Crew, Process, LLM
# 1. CrewAI Native の LLM クラスを使って k8s 内の Ollama へ接続
llm = LLM(
model="ollama/qwen2.5:7b",
base_url="http://ollama-service.default.svc.cluster.local:11434"
)
# 2. リサーチ専門エージェントの定義
researcher = Agent(
role='技術リサーチャー',
goal='指定されたテーマについて詳細かつ正確な情報を調査・整理する',
backstory='あなたはIT技術や最新AI動向に関する深い専門知識を持つリサーチャーです。',
verbose=True,
allow_delegation=False,
llm=llm
)
# 3. ライター専門エージェントの定義
writer = Agent(
role='テクニカルライター',
goal='調査結果をもとに、人間が読みやすいMarkdown形式のレポートを作成する',
backstory='あなたは複雑な技術情報を分かりやすく構造化してまとめるプロのライターです。',
verbose=True,
allow_delegation=False,
llm=llm
)
# 4. タスクの定義
task1 = Task(
description='「Kubernetes環境でローカルLLMを使ってAIエージェントを動かすメリットと設計手法」について解説を作成してください。',
expected_output='要点、メリット、アーキテクチャの概要を含む詳細なドラフト文章。',
agent=researcher
)
task2 = Task(
description='researcherの作成した内容を推敲し、素晴らしいMarkdownレポート(# 見出し や箇条書きを活用)に仕上げてください。結果はファイルに出力します。',
expected_output='完成されたMarkdownドキュメント。',
agent=writer,
output_file='/output/k8s_ai_agent_report.md'
)
# 5. エージェントチームの実行
crew = Crew(
agents=[researcher, writer],
tasks=[task1, task2],
process=Process.sequential
)
print("=== AI Agent Started ===")
result = crew.kickoff()
print("=== AI Agent Finished ===")
4.2 Dockerfileの作成
📄 Dockerfile
FROM python:3.11-slim
WORKDIR /app
RUN pip install --no-cache-dir crewai langchain-community
COPY main.py .
RUN mkdir -p /output
CMD ["python", "main.py"]
5. k8s Jobとしてのタスク実行
自律型エージェントの実行には、永遠に起動し続けるDeploymentではなく、処理が完了したらPodを自動で終了してくれる「Job」リソースが最適です。また、生成された成果物を取り出せるように /var/lib/agent-output を hostPath でマウントします。
5.1 Job用マニフェスト(02-agent-job.yaml)
📄 02-agent-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: ai-agent-job
namespace: default
spec:
template:
spec:
restartPolicy: Never
nodeSelector:
kubernetes.io/hostname: k8s-master
containers:
- name: agent
image: python:3.11-slim
command: ["/bin/sh", "-c"]
args:
- |
pip install --no-cache-dir crewai langchain-community && \
python /app/main.py
volumeMounts:
- name: app-code
mountPath: /app
- name: output-dir
mountPath: /output
volumes:
- name: app-code
hostPath:
path: /root/k8s-manifests/apps/agent
type: Directory
- name: output-dir
hostPath:
path: /var/lib/agent-output
type: DirectoryOrCreate
5.2 実行とログの監視
Jobをクラスタへ投入し、エージェントが仕事を開始する様子を観察します。 Kubernetesで動かしている最大のメリットは、ここでPCの画面を閉じたり、SSHセッションを切断しても、裏でエージェントが最後まで完走してくれることです。
💻 実行コマンド
root@k8s-master:~/k8s-manifests/apps/agent# kubectl apply -f 02-agent-job.yaml
job.batch/ai-agent-job created
root@k8s-master:~/k8s-manifests/apps/agent# kubectl logs -f job/ai-agent-job
(依存関係のインストール後、以下のようにエージェントが思考を開始します)
=== AI Agent Started ===
╭────────────────────────────── 🤖 Agent Started ──────────────────────────────╮
│ │
│ Agent: 技術リサーチャー │
│ │
│ Task: │
│ 「Kubernetes環境でローカルLLMを使ってAIエージェントを動かすメリットと設計 │
│ 手法」について解説を作成してください。 │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
...
... (中略: エージェントの自律思考プロセス) ...
...
[Finalize] todos_count=0, todos_with_results=0
╭─────────────────────────── ✅ Agent Final Answer ────────────────────────────╮
│ │
│ Agent: テクニカルライター │
│ │
│ Final Answer: │
│ # │
│ クラウドサービスを使用せずに本地域でローカルLLMを用いたAIエージェントの設 │
│ 計手法について │
...
╰──────────────────────────────────────────────────────────────────────────────╯
=== AI Agent Finished ===
6. 完成したMarkdownレポートの確認
タスクが完了すると、Jobが終了状態(Completed)になり、ホスト側のディレクトリに成果物が保存されます。中身を確認してみましょう。
root@k8s-master:~/k8s-manifests/apps/agent# ls -l /var/lib/agent-output/k8s_ai_agent_report.md
-rw-r--r-- 1 root root 4149 Jul 22 06:14 /var/lib/agent-output/k8s_ai_agent_report.md
root@k8s-master:~/k8s-manifests/apps/agent# cat /var/lib/agent-output/k8s_ai_agent_report.md
以下が、AIエージェントが完全に自律して考え、執筆し、出力したMarkdownテキストの原文です。
# クラウドサービスを使用せずに本地域でローカルLLMを用いたAIエージェントの設計手法について
## 1. 研究背景とテーマ解説
クラウドベースのソリューションではなく、本地域のリソースを活用してLocal Large Language Model (ローカルLLM)を使用し、AIエージェントを動かす方法とそのメリットについて、特に Kubernetes マネージメントアーキテクチャでの設計手法に注目します。プライバシー保護、コスト削減、組織的なコントロールの改善などの視点から調査が行われる。
## 2. システム概要
本手法における主要な構成要素は以下の通りです:
– **データベース**: ローカルストレージまたはNAS(ネットワークAttached Storage)等
– **モデル**: 大型言語モデルに加えてパラメータとテンプレートなど
– **インフラ**: Kubernetesクラスタ、物理マシンまたは仮想サーバー
## 3. メリット: 西南東北の視点
### 3.1 プライバシー保護
データを本地域で管理することで、個人情報流出のリスクが大幅に低いです。特にプライベートデータの扱いが必要な状況では重要性があります。
### 3.2 コスト削減
クラウド利用 Fee やネットワーク帯域料は不要であり、本地域での動作によりコストを抑えることが可能です。
### 3.3 組織的制御と対応能力
自社データとローカルモデルを持つことで、より良い管理戦略や運用を計画できます。これによりセキュリティ上の問題や法規制に対しても柔軟な対処が可能になります。
### 3.4 開発・デプロイの迅速性
テストから修正、デプロイまでローカル環境で容易に行うことができます。
## 4. 設計手法: Kubernetesでのアドバイス
### 4.1 Kubernetesクラスタ構築
クラウドのように高度な自動化を実現するためにKubernetesクラスタを使用します。このソフトウェアはデプロイ、スケーリング、カスタマイズ調整の管理が容易である利点を持っています。
### 4.2 标準インストールパッケージの利用
既存の Kubernetes 環境を迅速に設定するためには LocalStack, MiniKF や他のKubernetesソフトウェアを活用します。これらはクラウドサービスのシミュレーションツールとして機能します。
### 4.3 パフォーマンス最適化
Kubernetesは高度なリソース管理機能を持っていますが、これらを有効に設定することでローカルLLMのパフォーマンスを最大化できます。具体的にはモデル並列化とバッチ処理を含みます。
### 4.4 セキュリティとプライバシー設計
各コンテナ間でのデータは強固なセキュリティが必要です。これにはTLS、RBAC(ロールベースアクセス制御)ネットワーク隔離などを利用します。またモデルの学習結果を保存する場合も適切なアクセス管理を行うことが重要です。
### 4.5 クレデンシャルとシークレット管理
Kubernetes Secrets機能を使えばパスワードやAPIキーなどの機密情報を安全に保持できます。これによりこれらの値がコード内で露出することなく利用可能です。
## まとめ
クラウドと比較して、プライバシー保護とコスト削減の両面的な利点を享受することが可能となります。Kubernetesプラットフォームを通じて高度な自動化と管理機能を持つことで、効率的にAIエージェントを開発およびデプロイできることも確認されています。具体的な設計時考慮事項としてはパフォーマンス最適化、セキュリティ強化、そしてクロスサービス間のシークレット管理が挙げられます。
これにより、組織はプライバシーへの配慮と運用効率の向上というメリットを得ることは可能です。
見事な構成です。ローカルLLM(7Bモデル)でありながら、与えられたタスクに対して論理的な見出し構成と的確なメリット・デメリットの提示を行い、見事なマークダウン形式の技術ドキュメントを生成してくれました。
7. おわりに
今回は、オンプレミスのKubernetesクラスタ上で「Ollama(LLMサーバー)」と「CrewAI(エージェント)」を連携させ、完全放置で動作するAIタスク実行環境を構築しました。
✨ 今後の展望
今後はこの仕組みを応用して、以下のようなさらなる自動化を進めていきたいと考えています。
- 🕒
CronJobを使って毎朝特定のニュースをWeb検索させてサマリーを作らせる - 🌐 WordPressのAPIと連携して、生成したMarkdownをそのままブログの下書きとして自動投稿させる
ローカル環境でのセキュアなAI運用や、クラウドAPIの課金に縛られない自由なエージェント構築に興味がある方は、ぜひこの Kubernetes + Ollama + CrewAI の構成を試してみてください!
