AKS 上的 Open-WebSearch 是什么?
本文介绍如何使用 KMCP 控制器在 Azure Kubernetes 服务(AKS)上部署 Open-WebSearch MCP 服务器。 Open-WebSearch 提供没有 API 密钥的多工程 Web 搜索和检索,因此生产部署需要仔细的注册表、网络和控制器配置。 这些步骤在将其扩展到 Azure 资源时重复使用本地 Kind 工作流,使所有内容都通过环境变量进行参数化。
长时间运行的 Azure 操作可能会在 CLI 中超时。 为了保持流的复原能力,az aks nodepool add --no-wait 这样的异步命令紧跟着 az aks nodepool wait --created,确保每个阶段在继续之前已完成。
设置环境
定义 AKS 预配后使用的环境变量。 Azure 特定的变量(资源组、群集名称、ACR 标识符)现在由create-aks-infrastructure.md 管理,本文假定它们已导出到当前 shell 中。 生成新资源(例如自定义图像)时,唯一名称仍会附加基于时间戳的HASH以避免冲突。
使用以下命令确保运行时变量准备就绪,同时确认 Azure 和 ACR值已由create-aks-infrastructure.md提供。
export HASH="${HASH:-$(date -u +"%y%m%d%H%M")}" # YYMMDDHHMM stamp
# Open-WebSearch image configuration
export MCP_IMAGE_NAME="${MCP_IMAGE_NAME:-open-websearch-mcp}"
export MCP_IMAGE_TAG="${MCP_IMAGE_TAG:-latest}"
export MCP_IMAGE_FULL="${MCP_IMAGE_FULL:-${ACR_LOGIN_SERVER}/${MCP_IMAGE_NAME}:${MCP_IMAGE_TAG}}"
# Repository clone targets
export OPENWEBSEARCH_REPO_URL="${OPENWEBSEARCH_REPO_URL:-https://github.com/Aas-ee/open-webSearch.git}"
export OPENWEBSEARCH_REPO_DIR="${OPENWEBSEARCH_REPO_DIR:-open-webSearch}"
# KMCP controller settings
export KMCP_NAMESPACE="${KMCP_NAMESPACE:-kmcp-system}"
export KMCP_CRDS_RELEASE_NAME="${KMCP_CRDS_RELEASE_NAME:-kmcp-crds}"
export KMCP_VERSION="${KMCP_VERSION:-}"
# MCP Kubernetes settings
export MCP_SERVER_NAMESPACE="${MCP_SERVER_NAMESPACE:-default}"
export SERVER_NAME="${SERVER_NAME:-open-websearch-mcp}"
export MCP_SERVICE_PORT="${MCP_SERVICE_PORT:-3000}"
export MCP_LOCAL_PORT="${MCP_LOCAL_PORT:-3000}"
# Application runtime behaviour
export ALLOWED_SEARCH_ENGINES="${ALLOWED_SEARCH_ENGINES:-bing,duckduckgo,brave,exa,baidu,csdn,juejin}"
export DEFAULT_SEARCH_ENGINE="${DEFAULT_SEARCH_ENGINE:-duckduckgo}"
export USE_PROXY="${USE_PROXY:-false}"
export PROXY_URL="${PROXY_URL:-}"
export RATE_LIMIT_QPS="${RATE_LIMIT_QPS:-5}"
export ENABLE_METRICS="${ENABLE_METRICS:-true}"
export METRICS_PORT="${METRICS_PORT:-9090}"
export PORT_FORWARD_PROBE_QUERY="${PORT_FORWARD_PROBE_QUERY:-healthcheck}"
# MCP protocol test parameters
export MCP_PROTOCOL_VERSION="${MCP_PROTOCOL_VERSION:-2024-10-22}"
export MCP_CLIENT_NAME="${MCP_CLIENT_NAME:-exec-doc}"
export MCP_CLIENT_VERSION="${MCP_CLIENT_VERSION:-0.0.1}"
export MCP_SEARCH_LIMIT="${MCP_SEARCH_LIMIT:-3}"
export MCP_SEARCH_ENGINES="${MCP_SEARCH_ENGINES:-[\"duckduckgo\",\"bing\"]}"
export MCP_ENDPOINT="${MCP_ENDPOINT:-http://localhost:${MCP_LOCAL_PORT}/mcp}"
export MCP_INIT_FILE="${MCP_INIT_FILE:-/tmp/mcp-init-request.json}"
export SEARCH_ENGINES_JSON="${SEARCH_ENGINES_JSON:-${MCP_SEARCH_ENGINES}}"
# Functional smoke test inputs
export TEST_SEARCH_QUERY="${TEST_SEARCH_QUERY:-open source vector database comparison}"
export TEST_FETCH_GITHUB_REPO="${TEST_FETCH_GITHUB_REPO:-Aas-ee/open-webSearch}"
先决条件
在继续部署之前,请确保具有以下工具和访问权限。 应已向 Azure(az login)进行身份验证,并为所选区域和 VM 大小提供足够的配额。 可选的本地工具支持快速生成验证。
- 具有所需 AKS 和 ACR 配额的 Azure 订阅
- 具有已登录会话的 Azure CLI (
az) - KMCP CLI(
kmcp)与所需控制器版本匹配 - 为 Azure 容器注册表推送配置的 Docker CLI
- 用于生成 Open-WebSearch 资产的 Node.js 18 或更高版本和 npm
- jq (可选)用于在验证期间分析 JSON 响应
command -v az >/dev/null || echo "Azure CLI missing"
command -v kmcp >/dev/null || echo "KMCP CLI missing"
command -v docker >/dev/null || echo "Docker CLI missing"
command -v node >/dev/null || echo "Node.js missing"
command -v npm >/dev/null || echo "npm missing"
command -v jq >/dev/null || echo "jq missing (optional)"
运行可执行文件指南, 创建 AKS 群集 以验证 Azure 访问权限、创建资源组、预配 Azure 容器注册表、部署 AKS 群集、添加任何可选节点池,并将注册表附加到群集。 本指南还会导出本文档使用的与 Azure 相关的环境变量。 报告完成后返回此处。
验证环境变量值
在预配资源之前检查有效配置,以避免意外的名称或区域。 进行调整后,重新运行此代码块以确认激活设置。
REQUIRED_AKS_VARS=(
HASH
RESOURCE_GROUP
AKS_CLUSTER_NAME
ACR_NAME
ACR_LOGIN_SERVER
)
VARS=(
"${REQUIRED_AKS_VARS[@]}"
MCP_IMAGE_FULL
OPENWEBSEARCH_REPO_URL
OPENWEBSEARCH_REPO_DIR
KMCP_NAMESPACE
KMCP_CRDS_RELEASE_NAME
KMCP_VERSION
MCP_SERVER_NAMESPACE
SERVER_NAME
MCP_SERVICE_PORT
MCP_LOCAL_PORT
ALLOWED_SEARCH_ENGINES
DEFAULT_SEARCH_ENGINE
USE_PROXY
PROXY_URL
RATE_LIMIT_QPS
ENABLE_METRICS
METRICS_PORT
PORT_FORWARD_PROBE_QUERY
TEST_SEARCH_QUERY
TEST_FETCH_GITHUB_REPO
MCP_PROTOCOL_VERSION
MCP_CLIENT_NAME
MCP_CLIENT_VERSION
MCP_SEARCH_LIMIT
MCP_SEARCH_ENGINES
SEARCH_ENGINES_JSON
MCP_ENDPOINT
)
for v in "${VARS[@]}"; do
printf "%s=%s\n" "$v" "${!v}"
done
Steps
每个步骤都包含用途、可执行命令和预期结果,以便你可以在继续之前验证进度。
安装 KMCP CRDs
使用已发布的 Helm 图表部署 KMCP CustomResourceDefinitions,以便群集了解 MCPServer 资源。
helm upgrade --install "${KMCP_CRDS_RELEASE_NAME}" \
oci://ghcr.io/kagent-dev/kmcp/helm/kmcp-crds \
--namespace "${KMCP_NAMESPACE}" \
--create-namespace
kubectl get crd | grep mcp || echo "MCP CRDs not detected"
安装 KMCP 控制器
安装或升级 KMCP 控制器,该控制器通过为 MCP 服务器创建部署和服务来协调 MCPServer 资源。
kmcp install ${KMCP_VERSION:+--version "${KMCP_VERSION}"} || true
kubectl get pods -n "${KMCP_NAMESPACE}"
克隆 Open-WebSearch 存储库
检索 Open-WebSearch 源以生成与要部署的当前分支或标记一致的容器映像。
if [ -d "${OPENWEBSEARCH_REPO_DIR}/.git" ]; then
git -C "${OPENWEBSEARCH_REPO_DIR}" fetch --all --prune
git -C "${OPENWEBSEARCH_REPO_DIR}" pull --ff-only
else
git clone "${OPENWEBSEARCH_REPO_URL}" "${OPENWEBSEARCH_REPO_DIR}"
fi
git -C "${OPENWEBSEARCH_REPO_DIR}" log -1 --oneline
生成 Open-WebSearch 资产
安装 Node.js 依赖项并生成 MCP 服务器入口点所需的可分发项目。
cd "${OPENWEBSEARCH_REPO_DIR}"
npm install
npm run build
node dist/index.js --help | head -n 20
cd -
生成容器映像
将编译的资产打包到为注册表标记的容器映像中。
cd "${OPENWEBSEARCH_REPO_DIR}"
docker build \
--tag "${MCP_IMAGE_FULL}" \
.
docker image inspect "${MCP_IMAGE_FULL}" >/dev/null
cd -
向 Azure 容器注册表推送映像
使用当前 Azure 会话(或服务主体)向 ACR 进行身份验证并发布容器映像。 如果 az 凭据已过期,或者注册表位于其他租户中,登录步骤将失败,请重新运行 az login 或使用 az acr login --expose-token,在这些情况下将短期令牌通过管道传递给 docker login。 如果要从自动化帐户生成,请确保服务主体在注册表上具有 AcrPush 角色。
if az acr login --name "${ACR_NAME}" && \
docker push "${MCP_IMAGE_FULL}" && \
az acr repository show-tags \
--name "${ACR_NAME}" \
--repository "${MCP_IMAGE_NAME}" \
--output tsv | grep "${MCP_IMAGE_TAG}"; then
echo "ACR push verified for ${MCP_IMAGE_FULL}"
else
cat <<ERR
Failed to authenticate with Azure Container Registry or push the image.
Remediation steps:
1. Re-run 'az login' (add '--tenant <TENANT_ID>' if required).
2. If direct login fails, run:
az acr login --name ${ACR_NAME} --expose-token --output tsv \
--query accessToken | \
docker login ${ACR_LOGIN_SERVER} \
--username 00000000-0000-0000-0000-000000000000 \
--password-stdin
3. Ensure the caller has AcrPush on the registry scope:
az role assignment list \
--scope "\$(az acr show --name ${ACR_NAME} --query id -o tsv)" \
--assignee <principal-id>
ERR
fi
创建 MCPServer 资源
定义 MCPServer 自定义资源,该资源指示 KMCP 使用配置的环境运行 Open-WebSearch MCP 服务器。
cat > mcpserver-${SERVER_NAME}.yaml <<EOF
apiVersion: kagent.dev/v1alpha1
kind: MCPServer
metadata:
name: ${SERVER_NAME}
namespace: ${MCP_SERVER_NAMESPACE}
spec:
transportType: http
httpTransport:
targetPort: ${MCP_SERVICE_PORT}
path: /
deployment:
image: ${MCP_IMAGE_FULL}
port: ${MCP_SERVICE_PORT}
env:
ALLOWED_SEARCH_ENGINES: "${ALLOWED_SEARCH_ENGINES}"
DEFAULT_SEARCH_ENGINE: "${DEFAULT_SEARCH_ENGINE}"
USE_PROXY: "${USE_PROXY}"
PROXY_URL: "${PROXY_URL}"
RATE_LIMIT_QPS: "${RATE_LIMIT_QPS}"
ENABLE_METRICS: "${ENABLE_METRICS}"
METRICS_PORT: "${METRICS_PORT}"
ENABLE_CORS: "true"
CORS_ORIGIN: "*"
EOF
kubectl apply -f mcpserver-${SERVER_NAME}.yaml
kubectl get mcpserver "${SERVER_NAME}" \
-n "${MCP_SERVER_NAMESPACE}" \
-o yaml | head -n 20
等待 MCPServer 就绪状态
等待 KMCP 创建服务和部署,确保至少有一个 Pod 在尝试端口转发或 MCP 会话测试之前准备就绪。 一旦至少有一个 "Pod" 报告准备就绪,即可继续操作。
echo "Waiting for Service ${SERVER_NAME}"
for i in $(seq 1 60); do
if kubectl get svc "${SERVER_NAME}" \
-n "${MCP_SERVER_NAMESPACE}" >/dev/null 2>&1; then
echo "Service detected on attempt ${i}"
break
fi
sleep 1
done
echo "Waiting for a Ready pod"
for i in $(seq 1 120); do
READY_CT=$(kubectl get pods \
-n "${MCP_SERVER_NAMESPACE}" \
-l app.kubernetes.io/name="${SERVER_NAME}" \
-o jsonpath='{range .items[*]}{range .status.conditions[*]}{@.type}{":"}{@.status}{"\n"}{end}{end}' \
2>/dev/null | grep -c 'Ready:True' || true)
if [ "${READY_CT}" -ge 1 ]; then
echo "Ready pod detected on attempt ${i}"
break
fi
sleep 1
done
端口转发 MCP 服务器
建立本地端口转发,以便从工作站安全地访问 MCP HTTP 终结点。
kubectl port-forward \
-n "${MCP_SERVER_NAMESPACE}" \
svc/"${SERVER_NAME}" \
"${MCP_LOCAL_PORT}:${MCP_SERVICE_PORT}" \
>/tmp/port-forward-${SERVER_NAME}.log 2>&1 &
export MCP_PORT_FORWARD_PID=$!
ATTEMPTS=30
for i in $(seq 1 "${ATTEMPTS}"); do
if ss -tnlp | grep -q ":${MCP_LOCAL_PORT}"; then
if curl -s --get \
--header 'Accept: application/json' \
--data-urlencode "engine=${DEFAULT_SEARCH_ENGINE}" \
--data-urlencode "query=${PORT_FORWARD_PROBE_QUERY}" \
"http://localhost:${MCP_LOCAL_PORT}/search" >/dev/null 2>&1; then
echo "Port-forward active on attempt ${i}"
break
fi
fi
sleep 1
done
初始化 MCP 会话
针对端口转发的终结点打开可流式传输的 HTTP MCP 会话,以获取后续 JSON-RPC 调用所需的会话标识符。
unset MCP_SESSION_ID
cat > "${MCP_INIT_FILE}" <<EOF
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"clientInfo": {
"name": "${MCP_CLIENT_NAME}",
"version": "${MCP_CLIENT_VERSION}"
},
"protocolVersion": "${MCP_PROTOCOL_VERSION}",
"capabilities": {}
}
}
EOF
INIT_HEADERS=$(mktemp)
curl -sS \
-D "${INIT_HEADERS}" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data-binary "@${MCP_INIT_FILE}" \
-o /tmp/mcp-init.json \
"${MCP_ENDPOINT}"
MCP_SESSION_ID=$(awk -F': ' 'tolower($1)=="mcp-session-id" {print $2}' "${INIT_HEADERS}" | tr -d '\r')
if [ -z "${MCP_SESSION_ID}" ]; then
echo "Failed to acquire MCP session identifier"
cat /tmp/mcp-init.json
else
export MCP_SESSION_ID
echo "MCP session established: ${MCP_SESSION_ID}"
fi
rm -f "${MCP_INIT_FILE}" "${INIT_HEADERS}"
通过 MCP 会话执行搜索
通过已建立的会话调用 MCP search 工具,并检查响应有效负载的前几行。
SESSION_STATUS=$(curl -s -o /dev/null -w '%{http_code}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "mcp-session-id: ${MCP_SESSION_ID}" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
"${MCP_ENDPOINT}")
if [ "${SESSION_STATUS}" = "200" ]; then
echo "MCP session verified"
else
echo "MCP session check failed with status ${SESSION_STATUS}"
fi
MCP_SEARCH_REQUEST=$(cat <<EOF
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"query": "${TEST_SEARCH_QUERY}",
"limit": ${MCP_SEARCH_LIMIT},
"engines": ${SEARCH_ENGINES_JSON}
}
}
}
EOF
)
curl -s \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "mcp-session-id: ${MCP_SESSION_ID}" \
-d "${MCP_SEARCH_REQUEST}" \
"${MCP_ENDPOINT}" |
awk '/^data:/{sub(/^data: /,"",$0); print}' |
jq -r '.result.content[0].text' |
sed 's/\\n/\n/g' |
head -n 40
清理(可选)
试验结束时删除本地端口转发和 Azure 资源。
kill "${MCP_PORT_FORWARD_PID}" 2>/dev/null || true
kubectl delete mcpserver "${SERVER_NAME}" -n "${MCP_SERVER_NAMESPACE}" || true
az group delete --name "${RESOURCE_GROUP}" --yes --no-wait || true
概要
Open-WebSearch MCP 服务器已生成、发布到 Azure 容器注册表,并通过 KMCP 控制器部署到 AKS。 端口转发和 MCP 会话测试已验证搜索工具端到端,确认注册表访问、控制器对帐和 HTTP 传输就绪情况。
后续步骤
- 添加自动化 MCP 协议测试,这些测试涵盖搜索以外的其他工具
- 集成 Azure Monitor 或使用 Prometheus 抓取指标和警报
- 配置网络策略和专用群集终结点以供生产使用
- 使用 CI 任务来扩展部署管道,以进行映像构建和 KMCP 应用
- 编写运行手册(缩放、日志审查、故障恢复)