Боли: дайджест зелёный при расколе версий; разные skills/current; нет строки аудита — нет отката. Ещё: webhook и multi-AZ, тенанты и Doctor, rsync-матрица, блог.
Матрица: широковещательный webhook против канареечного среза
Статья про webhook и шлюзы ставит нотификации в центр; здесь главный рычаг — доля трафика и версионный срез skill, а дайджест лишь сигнал внутри пробы. Так вы снижаете риск ложноположительного зелёного статуса при расколе версий между AZ.
| Угол | Главный рычаг | Что даёт | Риск |
|---|---|---|---|
| Webhook-first | Дайджесты, overlap токенов | Видимость ретраев. | Не доказывает semver на диске. |
| Канарея | Веса LB, шаги долей | Малый blast-radius. | Без снимка весов нет отката. |
| Срез skill | /var/db/openclaw/skills/<semver> + lock |
Граница тень/прод. | Расход skills.d ломает тенантов. |
| Merge проб | Один /readyz: Doctor, очередь, дайджест |
Один JSON для LB. | Пять URL скрывают частичный деград. |
Многоузловая публикация OpenClaw до сдвига долей
Публикация — контракт шлюзов: openclaw.lock, фрагменты из гайда по тенантам, дерево skill по semver, маршрут merge-пробы. Цепочка: тень → зелёный Doctor/проба → вес LB → зеркало → аудит. Порядок: тег на writer, pull канареи, стабильные после гейта; воркеры не опережают шлюз. На трёх узлах clustervps сценарий честно воспроизводится без «одного ноутбука с правами sudo».
#!/usr/bin/env bash
set -euo pipefail
cd /usr/local/share/openclaw-infra
/usr/bin/git fetch --tags origin
/usr/bin/git checkout "refs/tags/${PROMOTE_TAG}"
/usr/bin/shasum -a 256 openclaw.lock | /usr/bin/tee /tmp/lock.sha
/usr/local/bin/openclaw version --json | /usr/bin/tee /tmp/version.json
/usr/bin/diff -q openclaw.lock <(ssh canary-gw "cat /usr/local/share/openclaw-infra/openclaw.lock")
Пустой diff разрешает трогать веса; непустой — стоп: вы на пороге раскола AZ.
Доли трафика: канареечные гейты на multi-AZ шлюзах
Старт ~5% сессий на шлюз с skills/next; держите ≥двух интервалов пробы; смотрите p95 и очередь вне шума webhook. Шаг 10–20% только при зелёном Doctor и приемлемом дайджесте.
Фрагменты конфигурации по тенантам и граница среза
Зеркалируйте /etc/openclaw/tenants/<t>/skills.d/ до смены next; фрагменты — границы инструментов и квот, секреты — в /var/db/openclaw/secrets/<t> с ACL.
# На каждом шлюзе после checkout тега sudo install -d -o root -g wheel /etc/openclaw/tenants/acme/skills.d sudo /usr/local/bin/openclaw config lint --tenant acme sudo /bin/ln -sfn "/var/db/openclaw/skills/1.4.2" /var/db/openclaw/skills/next sudo launchctl kickstart -k system/com.openclaw.gateway
Канареечный YAML на next, прод на current до аудита.
Объединение проб: Doctor, очереди, дайджест и semver skill
Один URL: semver на диске = lock, Doctor тенантов с трафиком, очередь в SLO; дайджест webhook — поле JSON, не главный рычаг промоушена (в отличие от broadcast-only).
#!/usr/bin/env bash
set -euo pipefail
TENANT_CANARY="${TENANT_CANARY:-acme}"
SKILL_PATH="/var/db/openclaw/skills/current"
/usr/bin/readlink "${SKILL_PATH}" | /usr/bin/tee /tmp/skill_path.txt
/usr/local/bin/openclaw doctor --tenant "${TENANT_CANARY}" --json >/tmp/doctor.json
/usr/bin/curl -fsS --max-time 3 "http://127.0.0.1:9099/v1/webhook-digest" -o /tmp/digest.json
/usr/bin/python3 - <<'PY'
import hashlib, json, pathlib
blob = pathlib.Path("/tmp/doctor.json").read_bytes() + pathlib.Path("/tmp/digest.json").read_bytes()
print(json.dumps({"probe_sha256": hashlib.sha256(blob).hexdigest(),"skill_resolved": pathlib.Path("/tmp/skill_path.txt").read_text().strip()}))
PY
degraded при жёлтом Doctor приемлемом semver; hard fail при расхождении с lock — не промоутить по webhook.
Откат: веса, symlink и launchd одним дыханием
Вместе: снимок весов LB + symlink current на старый semver; иначе раскол. Затем kickstart и сверка JSON с архивом аудита. См. rsync-матрицу под артефакты.
#!/usr/bin/env bash
set -euo pipefail
/usr/bin/scp stable-gw:/var/db/openclaw/audit/last_good_weights.json /tmp/weights.json
./lb_restore_weights.sh /tmp/weights.json
sudo /bin/ln -sfn "/var/db/openclaw/skills/${ROLLBACK_SEMVER}" /var/db/openclaw/skills/current
sudo launchctl kickstart -k system/com.openclaw.gateway
/usr/bin/curl -fsS http://127.0.0.1:8088/readyz | /usr/bin/jq .
Аудит: append-only JSONL для grep и комплаенса
На шаг доли — строка в promotions.jsonl: actor, ticket, from/to semver, веса, probe_sha256; реплика в объектное хранилище.
/usr/bin/printf '%s\n' \
"{"ts":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","actor":"${USER}","ticket":"${TICKET}","from":"1.4.1","to":"1.4.2","weights":"${WEIGHT_BLOB}","probe_sha256":"${PROBE_SHA}"}" \
| /usr/bin/tee -a /var/db/openclaw/audit/promotions.jsonl
Ответы по jq, не по Slack.
Коротко
Дайджест — поле пробы, не единственный гейт. Один тенант — sticky на канареечный пул. Минимум три Mac: два стабильных AZ + канарея до зеркала.
Шлюз на каждую AZ
Добавьте Mac mini M4 под канарею и merge-пробу: тарифы, справка SSH, покупка.