Ubuntu 22.04 + 8vCPU + 32GB RAM + 100GB SSD (不含业务数据)
sudo apt update && sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release jq unzip tar git wget
设置服务器 IP 变量(填写这台机器的真实内网/公网 IP,不要填 127.0.0.1)
export SERVER_IP=<this_machine_ip>
echo 'export SERVER_IP=<this_machine_ip>' >> ~/.bashrc
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
docker --version
docker compose version
cd ~
git clone <你的仓库地址> wenshu-platform
cd wenshu-platform
.env)cp .env.example .env
编辑 .env,只需要维护人工配置项(代码参数统一在 application.yml 默认值):
MYSQL_URL(唯一 MySQL 地址:本地 compose 用 mysql:3306,远程数据库填 <host>:<port>)MYSQL_USERMYSQL_PASSWORDMYSQL_ROOT_PASSWORD(仅本地启动 MySQL 容器时使用)SERVER_IP(必须;同机部署时只填这一项机器 IP,例如 192.168.1.10)POLARIS_POSTGRES_PASSWORDPOLARIS_CLIENT_SECRETOSS_ENDPOINTOSS_AKOSS_SKOSS_BUCKETSTOCK_SSH_CRYPTO_KEY(至少 32 位)K8S_KUBECONFIG_PATH(宿主机 kubeconfig 绝对路径,例如 /root/.kube/config)说明:
MYSQL_URL 这一个地址配置。MYSQL_URL 配成远程地址,后端会连接远程库。polaris(不需要手工在 FE 里执行 CREATE EXTERNAL CATALOG)。SERVER_IP 生成:http://<SERVER_IP>:8181(仅在你手工设置 POLARIS_HOST 时才覆盖)。POLARIS_CATALOG_OAUTH2_CREDENTIAL
2) 自动拼接 ${POLARIS_CLIENT_ID}:${POLARIS_CLIENT_SECRET}cd ~/wenshu-platform
chmod +x scripts/install-k8s-control-plane.sh scripts/install-k8s-operators.sh
# 写入 kubeconfig 路径(已存在则跳过)
grep -q '^K8S_KUBECONFIG_PATH=' .env || echo "K8S_KUBECONFIG_PATH=$HOME/.kube/config" >> .env
# 1) 安装单机 K8s 控制平面
bash scripts/install-k8s-control-plane.sh
# 2) 安装 Spark + StarRocks Operator
bash scripts/install-k8s-operators.sh
# 3) 生成永久 join 命令(用于后续“新增机器/纳管”)
JOIN_CMD="$(kubeadm token create --ttl 0 --print-join-command)"
sed -i "s|^KUBEADM_JOIN_COMMAND=.*|KUBEADM_JOIN_COMMAND=${JOIN_CMD}|" .env
# 4) 验证
kubectl get nodes -o wide
kubectl get crd sparkapplications.sparkoperator.k8s.io
kubectl get crd starrocksclusters.starrocks.com
kubectl get pods -n spark-operator
kubectl get pods -n starrocks
说明:
docker compose 容器。cd ~/wenshu-platform
mkdir -p data/mysql data/milvus/etcd data/milvus/minio data/milvus/milvus \
data/polaris/postgres data/huggingface
docker compose up -d --build
docker compose ps
说明:
docker compose up -d --build 默认会启动 ws-orchestrator-api 和 ws-orchestrator-controller,不需要单独再起。cd ~/wenshu-platform
# 等待 Polaris healthy
until [ "$(docker inspect -f '{{.State.Health.Status}}' ws-polaris 2>/dev/null)" = "healthy" ]; do
sleep 3
done
set -a
source .env
set +a
NET="$(basename "$PWD")_default"
get_token() {
curl -s -X POST http://localhost:8181/api/catalog/v1/oauth/tokens \
-H "Polaris-Realm: ${POLARIS_REALM:-POLARIS}" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=client_credentials&client_id=${POLARIS_CLIENT_ID:-root}&client_secret=${POLARIS_CLIENT_SECRET}&scope=PRINCIPAL_ROLE:ALL" \
| jq -r '.access_token // empty'
}
TOKEN="$(get_token)"
if [ -z "$TOKEN" ]; then
docker run --rm --network "$NET" \
--env="polaris.persistence.type=relational-jdbc" \
--env="quarkus.datasource.username=${POLARIS_POSTGRES_USER:-polaris}" \
--env="quarkus.datasource.password=${POLARIS_POSTGRES_PASSWORD}" \
--env="quarkus.datasource.jdbc.url=jdbc:postgresql://polaris-postgres:5432/${POLARIS_POSTGRES_DB:-polaris}" \
apache/polaris-admin-tool:1.3.0-incubating \
bootstrap -r "${POLARIS_REALM:-POLARIS}" -c "${POLARIS_REALM:-POLARIS},${POLARIS_CLIENT_ID:-root},${POLARIS_CLIENT_SECRET}"
TOKEN="$(get_token)"
fi
[ -n "$TOKEN" ] || { echo "Polaris 初始化失败"; exit 1; }
CATALOG_NAME="${POLARIS_DEFAULT_CATALOG:-demo_catalog}"
BASE_LOCATION="s3://${OSS_BUCKET}/${CATALOG_NAME}"
if curl -s http://localhost:8181/api/management/v1/catalogs \
-H "Authorization: Bearer ${TOKEN}" \
-H "Polaris-Realm: ${POLARIS_REALM:-POLARIS}" \
| jq -e --arg n "$CATALOG_NAME" '.catalogs[]? | select(.name == $n)' >/dev/null; then
echo "Catalog ${CATALOG_NAME} 已存在,跳过创建"
else
cat <<JSON | curl -s -X POST http://localhost:8181/api/management/v1/catalogs \
-H "Authorization: Bearer ${TOKEN}" \
-H "Polaris-Realm: ${POLARIS_REALM:-POLARIS}" \
-H "Content-Type: application/json" \
-d @- | jq .
{
"catalog": {
"type": "INTERNAL",
"name": "${CATALOG_NAME}",
"properties": {
"default-base-location": "${BASE_LOCATION}"
},
"storageConfigInfo": {
"storageType": "S3",
"allowedLocations": ["${BASE_LOCATION}"],
"region": "${OSS_REGION:-cn-hangzhou}",
"endpoint": "${OSS_ENDPOINT:-https://oss-cn-hangzhou.aliyuncs.com}",
"pathStyleAccess": false,
"stsUnavailable": true
}
}
}
JSON
fi
curl -s http://localhost:8181/api/management/v1/catalogs \
-H "Authorization: Bearer ${TOKEN}" \
-H "Polaris-Realm: ${POLARIS_REALM:-POLARIS}" | jq '.catalogs[].name'
说明:
polaris 会在“创建 StarRocks 集群并进入 READY 后”由 ws-orchestrator-controller 自动创建。SET CATALOG polaris,不会再每次重复建 catalog。创建 StarRocks 集群后,查看 controller 日志中是否出现 bootstrap 成功信息:
docker compose logs -f ws-orchestrator-controller
如果出现 Failed to load rest catalog,优先检查:
SERVER_IP 是否填写为这台机器对 K8s Pod 可达的 IP(不要填 127.0.0.1)。POLARIS_CLIENT_ID / POLARIS_CLIENT_SECRET(或 POLARIS_CATALOG_OAUTH2_CREDENTIAL)是否正确。.env 后是否已重启 ws-orchestrator-controller:docker compose up -d --build ws-orchestrator-controller
cd ~/wenshu-platform
set -a
source .env
set +a
echo "=== MySQL ==="
docker exec ws-mysql mysqladmin ping -h localhost -u"${MYSQL_USER}" -p"${MYSQL_PASSWORD}" --silent >/dev/null && echo "OK"
echo "=== Milvus ==="
curl -sf http://localhost:9091/healthz >/dev/null && echo "OK"
echo "=== TEI Embedding ==="
LEN=$(curl -s http://localhost:7997/v1/embeddings \
-H 'Content-Type: application/json' \
-d "{\"model\":\"${TEI_MODEL_ID:-intfloat/multilingual-e5-base}\",\"input\":\"passage: 测试文本\"}" | jq -r '.data[0].embedding | length')
echo "embedding_len=${LEN}"
[ "${LEN}" = "768" ] && echo "OK"
echo "=== Polaris ==="
CODE=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8181/api/catalog/v1/config)
echo "http=${CODE}"
([ "${CODE}" = "200" ] || [ "${CODE}" = "401" ]) && echo "OK"
echo "=== Backend ==="
CODE=$(curl -s -o /dev/null -w '%{http_code}' -X POST http://localhost:8080/api/auth/login -H 'Content-Type: application/json' -d '{}')
echo "http=${CODE}"
([ "${CODE}" = "200" ] || [ "${CODE}" = "400" ] || [ "${CODE}" = "422" ]) && echo "OK"
echo "=== Python Iceberg ==="
curl -sf http://localhost:8090/health >/dev/null && echo "OK"
echo "=== Frontend ==="
CODE=$(curl -s -o /dev/null -w '%{http_code}' http://localhost/)
echo "http=${CODE}"
[ "${CODE}" = "200" ] && echo "OK"
echo "=== Orchestrator API ==="
curl -sf http://localhost:18080/healthz >/dev/null && echo "OK"
cd ~/wenshu-platform
# 查看状态
docker compose ps
# 查看日志
docker compose logs -f backend
docker compose logs -f ws-orchestrator-api
docker compose logs -f ws-orchestrator-controller
# 重启单个服务
docker compose restart backend
# 停止(保留数据)
docker compose down
# 停止并删除数据(谨慎)
docker compose down -v
# 代码更新后重建
git pull
docker compose up -d --build