はじめに
Kubernetes に限った話ではありませんが、操作したいことがあるのにコマンドを知らなかったり覚えていないせいで操作ができないことはありませんか?Kubernetes のコマンドは慣れると難しいものではありませんが、しばらく触っていないとすぐに忘れてしまうものです。このような時、操作したい内容をコマンドの代わりに自然言語で指示することができたら嬉しいですよね。最近は MCP が注目を浴びており、Kubernetes 用の MCP Server も公開されているので、AKS をコマンドレスで操作できないかどうか試してみました。
本記事の環境
- Kubernetes:Azure (AKS)
- MCP Client: GitHub Copilot Agent (Visual Studio Code)
- MCP Server: mcp-server-kubernetes
Kubernetes MCP Server で提供されている主な機能
mcp-server-kubernetes で提供されている機能の中で特に便利だと思われる機能は以下になります。
- リソース情報取得関連
- Pod, Service, Deployment, Node の詳細情報の取得
- Namespace の情報取得
- Pod のログ情報取得
- Kubernetes Cluster のイベント情報の取得
- リソース作成・削除関連
- Pod, Service, Deployment, Namespace の作成と削除
- ConfigMap の作成
- その他
- Helm Chart のインストールとアンインストール
- API Resource の一覧表示
環境セットアップ
以下では AKS, MCP Client, MCP Server のそれぞれのセットアップを行います。
AKS の準備
kubectl のインストール
以下続く AKS の準備の手順で Kubernetes を操作するためのクライアントツールである kubectl が必要になるため、インストールをして下さい。Linux の場合は以下のドキュメントを参考にしてインストールをすることができます。
AKS のデプロイ
AKS が準備されていない場合は、Azure ポータルや Azure CLI を使ってデプロイを行ってください。Azure CLI の場合は以下のコマンドでデプロイすることができます。
$ az group create --name <リソースグループ名> --location <リージョン名>
$ az aks create --resource-group <リソースグループ名> --name <クラスター名> --node-count 1 --generate-ssh-keys
AKS クラスターの認証情報取得
Azure CLI を使って AKS クラスターの認証情報を取得します。Azure CLI の場合は以下のコマンドを使って取得可能です。
$ az aks get-credentials --resource-group <リソースグループ名> --name <クラスター名>
コマンド実行後、以下のような標準出力がされれば認証情報は取得できています。
Merged <クラスター名> as current context in /home/<ユーザー名>/.kube/config
認証情報が取得できている場合、以下のように Pod を取得するコマンドを実行することができます。
$ kubectl get pod
No resources found in default namespace.
MCP Client の準備
GitHub Copilot Agent のセットアップ
GitHub Copilot Agent Mode は Visual Studio Code で利用可能です(2025 年 4 月現在)。Visual Studio Code で GitHub Copilot が利用できるようになっていると、以下の赤丸のようにプルダウンメニューで「Agent」を選択できるようになります。
MCP Server の準備
Helm のインストール
Kubernetes MCP Server の利用条件は以下になります。
- kubectl がインストールされ、PATH が通っていること
- コンテキストが設定された有効な kubeconfig ファイルがあること
- Kubernetesクラスターにアクセスできること
- Helm v3 がインストールされ、PATH が通っていること
kubectl のインストールや kubeconfig ファイルの設定は AKS の準備にて完了しているため、最後の利用条件となる Helm v3 のインストールを行ってください。Helm は Kubernetes のパッケージマネージャーであり、構成や運用に役立ちます。Helm のインストール手順は以下のドキュメントを参考にしてください。
Kubernetes MCP Server の準備
.vscode/mcp.json に以下の json を貼り付けてください。
{
"servers": {
"kubernetes": {
"command": "npx",
"args": ["mcp-server-kubernetes"]
}
}
}
貼り付け後 Kubernetes MCP Server のセットアップが始まります。数分待機した後、以下の赤丸のように工具のアイコンに数字が表示されればセットアップが完了です。(工具のアイコンが示す数字は GitHub Copilot Agent に登録されている MCP Tool の数です。)
工具のアイコンを押すと、以下のように登録されている MCP Server や MCP Tool が一覧として表示されます。
AKS のコマンドレス操作
AKS を操作するシナリオ
AKS の操作の主なシナリオである、以下のシナリオでコマンドレス操作が可能かどうかを試してみます。
- アプリケーションをデプロイする
- 既存のクラスター構成を取得する
- アプリケーションをアップグレードする
- トラブルシューティング
コマンドレス操作の事前準備
コントロールプレーンの kube-system や他の Namespace のリソースに影響を与えないように、コマンドレス操作用の Namespace を事前に作成します。
GitHub Copilot Agent に以下の指示をします。
クラスター内に新しい Namespace を作成してください。Namespace 名は command-less-ns で作成して下さい。
無事に Namespace を作成してくれました。
(コマンドレスの記事ではありますが、、) Namespace が作成されているかどうかをコマンドで確認してみると、実際に作成されていることがわかりました。
$ kubectl get namespace
NAME STATUS AGE
command-less-ns Active 54s
アプリケーションをデプロイする
Hello World! を出力するアプリのデプロイ
以下のようなプロンプトでアプリケーションをデプロイします。
Hello World! と表示するアプリケーションを command-less-ns の Namespace 内にデプロイしてください。
以下のような順番で動いてくれました。デプロイする Namespace の指示をして関係で、作成済みの Namespace を再度作成しようとしますが、作成できずに諦めてその後にアプリケーションを Deployment で作成してくれました。
コマンドで状況を確認すると、
$ kubectl get deployment -n command-less-ns
NAME READY UP-TO-DATE AVAILABLE AGE
hello-world-app 1/1 1 1 45s
hello-world-app にアクセスしたら以下のレスポンスが返ってきました。Hello World! ではなく Welcome to nginx! だったので、Nginx が持つデフォルトの index.html が返ってきているようです。
hell-world-app から返ってきたレスポンス
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
今回のアプリケーション作成の指示では具体的な指示をしませんでしたが、image を指定した Deployment のデプロイもできました。(bitnami/wordpress:latest など)
既存のクラスター構成を取得する
次に既存のクラスター構成を取得します。プロンプトは以下のものを使いました。
command-less-ns の Namespace 内にデプロイされている hello-world-app の構成を教えてください。
全て英語ですが、Deployment を describe した情報をもとに以下のような回答をしてくれました。
先ほどの回答は英語なのと情報量が多くてわかりづらかったため、以下のようなプロンプトを使って、回答は日本語で行い必要な構成のみ答えるように指示しました。
command-less-ns の Namespace 内にデプロイされている hello-world-app の構成について、イメージ名とリッスンしているポート番号を教えてください。また、回答は日本語で生成してください。
指示通りにイメージとリッスンしているポート番号のみ答えてくれました。GitHub Copilot Agent には LLM が組み込まれているので、このように、MCP Tool を実行して取得した情報の中から必要な情報を取り出して回答させることができます。
アプリケーションをアップグレードする
現状のアプリケーションは nginx:latest のイメージを使っており、既に最新のイメージを使っています。ここではアプリケーションのアップグレードをしたいのですが、要はイメージの変更ができることを確認できればよいので、今回は少し前のバージョンである nginx:1.27 に変更します。
指示では以下のプロンプトを利用しました。
command-less-ns の Namespace 内にデプロイされている hello-world-app の構成について、利用しているイメージを nginx:1.27 に変更してください。
update_deployment を使って無事にイメージの変更をしてくれました。
(コマンドレスの記事ですが、、) コマンドを使ってアプリケーションイメージのバージョンを確認すると、指示通り nginx:1.27 に変更されていました。
$ kubectl describe deployment hello-world-app -n command-less-ns | grep Image
Image: nginx:1.27
トラブルシューティング
Kubernetes クラスターの操作の目的としてトラブルシューティングはよくあることかと思います。トラブルシューティングではリソースのイベントの取得やポッドのログの取得はよくあることなので、それぞれ試してみます。
プロンプトは以下のものを使いました。
command-less-ns の Namespace 内にデプロイされている hello-world-app に関するリソースについて、イベントログを取得してください。
結果としてはイベントログは以下のように取得できました。Pod, Replicaset, Deployment, Service のログを一度に得ることができました。
ポッドのログ取得についても試してみます。プロンプトは以下のものを使いました。
command-less-ns の Namespace 内にデプロイされている hello-world-app のポッドについて、ログを取得してください。
ログについても取得できました。出力は英語ですが読んでみるとアプリの初期化プロセスとプローブからの HTTP リクエストが繰り返しあると書かれていました。
実際にポッドのログを (コマンドで) 確認してみると、確かにアプリの初期化プロセスとプローブからの HTTP リクエストの繰り返しの内容となっていました。
ログの詳細
$ kubectl logs hello-world-app-5567ddcdf5-glldh -n command-less-ns
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2025/04/30 23:29:39 [notice] 1#1: using the "epoll" event method
2025/04/30 23:29:39 [notice] 1#1: nginx/1.27.5
2025/04/30 23:29:39 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2025/04/30 23:29:39 [notice] 1#1: OS: Linux 5.15.0-1086-azure
2025/04/30 23:29:39 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2025/04/30 23:29:39 [notice] 1#1: start worker processes
2025/04/30 23:29:39 [notice] 1#1: start worker process 29
2025/04/30 23:29:39 [notice] 1#1: start worker process 30
10.224.0.4 - - [30/Apr/2025:23:29:41 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:29:42 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:29:47 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:29:52 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:29:57 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:02 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:07 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:12 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:17 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:22 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:27 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:32 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:37 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:42 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:47 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:52 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:30:57 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:02 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:07 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:12 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:17 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:22 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:27 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:32 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:37 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:42 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:47 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
10.224.0.4 - - [30/Apr/2025:23:31:52 +0000] "GET / HTTP/1.1" 200 615 "-" "kube-probe/1.31" "-"
...(以下略)...
現状 Kubernetes MCP Server でできないことの例
Kubernetes MCP Server を使っていくつかのシナリオで試してみたところ、一例として現状では以下のような課題があると感じました。
曖昧なプロンプトによる指示の難しさ
指示を具体的に記述しないと思うように動いてくれないので、曖昧な指示しかできない場合にはコマンドレス操作は現状難しそうと思いました。例えば今回の例では Namespace の指示を一つ取っても、プロンプトの冒頭で「command-less-ns の Namespace 内にデプロイされている hello-world-app の構成について」と明確に記述をしていました。
リソース作成の詳細設定の難しさ
Kubernetes MCP Server のドキュメントに書かれている通り、Pod, Service, Deployment などのリソースの作成はできますが、細かい設定が難しいと感じました。こちらの設定値は Service 作成時に表示される設定値になりますが、細かいパラメーターは設定されていません。そのため例えば Deployment を公開するためにセレクターを適切に設定したロードバランサータイプの Service の作成などは難しかったです。
マニフェストファイルを使ったデプロイ
マニフェストファイルを使ったデプロイの指示を行った場合、Kubernetes MCP Server が動いてくれるのではなく、マニフェストファイルを使ったデプロイコマンドが GitHub Copilot から生成されました。
参考:Microsoft Copilot in Azure の利用について
Kubernetes MCP Server とは文脈が外れますが、AKS では Azure ポータルで Copilot が利用することができます。これは、自然言語でやりたい操作を入力することで、Copilot がコマンドに変換してくれるものです。
Copilot はあくまでコマンド実行のサポートであり、MCP を使った時のようにコマンド実行の結果の整形はしてくれませんが、MCP とは違って Copilot では自然言語からコマンドに変換する形になるので、実質操作としての制限はありません。そのため、MCP では操作できないより複雑な操作を行いたい場合は Microsoft Copilot in Azure を利用するのが良いでしょう。
以下の赤丸の Copilot アイコンを押すことで、操作したい内容をコマンドに変換してくれます。
以下は Copilot によって実際に変換されたコマンドです。
以下は Copilot によって変換されたコマンドを実行した結果です。
おわりに
初めて Kubernetes MCP Server を触りましたが、コマンドレス (自然言語) で AKS の簡単な操作ができた感動しました。一方で、思ったよりも細かい指示ができなかったり、期待値通りの動きをさせるためのプロンプトの工夫は何度かする必要があったことが難しいと思ったポイントでした。Kubernetes に限らずだと思いますが、MCP Server を使うには Agent に寄り添ったプロンプトが一層重要だと思います。とはいえ、MCP はこれから更に発展していくことが期待できるので、指示の自由度の高さや挙動の精度についても改善されていくと考えられます。