File size: 8,589 Bytes
fc93158 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | #!/usr/bin/env bash
# Deploy OpenClaw to Kubernetes.
#
# Secrets are generated in a temp directory and applied server-side.
# No secret material is ever written to the repo checkout.
#
# Usage:
# ./scripts/k8s/deploy.sh # Deploy (requires API key in env or secret already in cluster)
# ./scripts/k8s/deploy.sh --create-secret # Create or update the K8s Secret from env vars
# ./scripts/k8s/deploy.sh --show-token # Print the gateway token after deploy
# ./scripts/k8s/deploy.sh --delete # Tear down
#
# Environment:
# OPENCLAW_NAMESPACE Kubernetes namespace (default: openclaw)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MANIFESTS="$SCRIPT_DIR/manifests"
NS="${OPENCLAW_NAMESPACE:-openclaw}"
# Check prerequisites
for cmd in kubectl openssl; do
command -v "$cmd" &>/dev/null || { echo "Missing: $cmd" >&2; exit 1; }
done
kubectl cluster-info &>/dev/null || { echo "Cannot connect to cluster. Check kubeconfig." >&2; exit 1; }
# ---------------------------------------------------------------------------
# -h / --help
# ---------------------------------------------------------------------------
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
cat <<'HELP'
Usage: ./scripts/k8s/deploy.sh [OPTION]
(no args) Deploy OpenClaw (creates secret from env if needed)
--create-secret Create or update the K8s Secret from env vars without deploying
--show-token Print the gateway token after deploy or secret creation
--delete Delete the namespace and all resources
-h, --help Show this help
Environment:
Export at least one provider API key:
ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY
OPENCLAW_NAMESPACE Kubernetes namespace (default: openclaw)
HELP
exit 0
fi
SHOW_TOKEN=false
MODE="deploy"
while [[ $# -gt 0 ]]; do
case "$1" in
--create-secret)
MODE="create-secret"
;;
--delete)
MODE="delete"
;;
--show-token)
SHOW_TOKEN=true
;;
*)
echo "Unknown option: $1" >&2
echo "Run ./scripts/k8s/deploy.sh --help for usage." >&2
exit 1
;;
esac
shift
done
# ---------------------------------------------------------------------------
# --delete
# ---------------------------------------------------------------------------
if [[ "$MODE" == "delete" ]]; then
echo "Deleting namespace '$NS' and all resources..."
kubectl delete namespace "$NS" --ignore-not-found
echo "Done."
exit 0
fi
# ---------------------------------------------------------------------------
# Create and apply Secret to the cluster
# ---------------------------------------------------------------------------
_apply_secret() {
local TMP_DIR
local EXISTING_SECRET=false
local EXISTING_TOKEN=""
local ANTHROPIC_VALUE=""
local OPENAI_VALUE=""
local GEMINI_VALUE=""
local OPENROUTER_VALUE=""
local TOKEN
local SECRET_MANIFEST
TMP_DIR="$(mktemp -d)"
chmod 700 "$TMP_DIR"
trap 'rm -rf "$TMP_DIR"' EXIT
if kubectl get secret openclaw-secrets -n "$NS" &>/dev/null; then
EXISTING_SECRET=true
EXISTING_TOKEN="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d)"
ANTHROPIC_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.ANTHROPIC_API_KEY}' 2>/dev/null | base64 -d)"
OPENAI_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENAI_API_KEY}' 2>/dev/null | base64 -d)"
GEMINI_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.GEMINI_API_KEY}' 2>/dev/null | base64 -d)"
OPENROUTER_VALUE="$(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENROUTER_API_KEY}' 2>/dev/null | base64 -d)"
fi
TOKEN="${EXISTING_TOKEN:-$(openssl rand -hex 32)}"
ANTHROPIC_VALUE="${ANTHROPIC_API_KEY:-$ANTHROPIC_VALUE}"
OPENAI_VALUE="${OPENAI_API_KEY:-$OPENAI_VALUE}"
GEMINI_VALUE="${GEMINI_API_KEY:-$GEMINI_VALUE}"
OPENROUTER_VALUE="${OPENROUTER_API_KEY:-$OPENROUTER_VALUE}"
SECRET_MANIFEST="$TMP_DIR/secrets.yaml"
# Write secret material to temp files so kubectl handles encoding safely.
printf '%s' "$TOKEN" > "$TMP_DIR/OPENCLAW_GATEWAY_TOKEN"
printf '%s' "$ANTHROPIC_VALUE" > "$TMP_DIR/ANTHROPIC_API_KEY"
printf '%s' "$OPENAI_VALUE" > "$TMP_DIR/OPENAI_API_KEY"
printf '%s' "$GEMINI_VALUE" > "$TMP_DIR/GEMINI_API_KEY"
printf '%s' "$OPENROUTER_VALUE" > "$TMP_DIR/OPENROUTER_API_KEY"
chmod 600 \
"$TMP_DIR/OPENCLAW_GATEWAY_TOKEN" \
"$TMP_DIR/ANTHROPIC_API_KEY" \
"$TMP_DIR/OPENAI_API_KEY" \
"$TMP_DIR/GEMINI_API_KEY" \
"$TMP_DIR/OPENROUTER_API_KEY"
kubectl create secret generic openclaw-secrets \
-n "$NS" \
--from-file=OPENCLAW_GATEWAY_TOKEN="$TMP_DIR/OPENCLAW_GATEWAY_TOKEN" \
--from-file=ANTHROPIC_API_KEY="$TMP_DIR/ANTHROPIC_API_KEY" \
--from-file=OPENAI_API_KEY="$TMP_DIR/OPENAI_API_KEY" \
--from-file=GEMINI_API_KEY="$TMP_DIR/GEMINI_API_KEY" \
--from-file=OPENROUTER_API_KEY="$TMP_DIR/OPENROUTER_API_KEY" \
--dry-run=client \
-o yaml > "$SECRET_MANIFEST"
chmod 600 "$SECRET_MANIFEST"
kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
kubectl apply --server-side --field-manager=openclaw -f "$SECRET_MANIFEST" >/dev/null
# Clean up any annotation left by older client-side apply runs.
kubectl annotate secret openclaw-secrets -n "$NS" kubectl.kubernetes.io/last-applied-configuration- >/dev/null 2>&1 || true
rm -rf "$TMP_DIR"
trap - EXIT
if $EXISTING_SECRET; then
echo "Secret updated in namespace '$NS'. Existing gateway token preserved."
else
echo "Secret created in namespace '$NS'."
fi
if $SHOW_TOKEN; then
echo "Gateway token: $TOKEN"
else
echo "Gateway token stored in Secret only."
echo "Retrieve it with:"
echo " kubectl get secret openclaw-secrets -n $NS -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d && echo"
fi
}
# ---------------------------------------------------------------------------
# --create-secret
# ---------------------------------------------------------------------------
if [[ "$MODE" == "create-secret" ]]; then
HAS_KEY=false
for key in ANTHROPIC_API_KEY OPENAI_API_KEY GEMINI_API_KEY OPENROUTER_API_KEY; do
if [[ -n "${!key:-}" ]]; then
HAS_KEY=true
echo " Found $key in environment"
fi
done
if ! $HAS_KEY; then
echo "No API keys found in environment. Export at least one and re-run:"
echo " export <PROVIDER>_API_KEY=\"...\" (ANTHROPIC, GEMINI, OPENAI, or OPENROUTER)"
echo " ./scripts/k8s/deploy.sh --create-secret"
exit 1
fi
_apply_secret
echo ""
echo "Now run:"
echo " ./scripts/k8s/deploy.sh"
exit 0
fi
# ---------------------------------------------------------------------------
# Check that the secret exists in the cluster
# ---------------------------------------------------------------------------
if ! kubectl get secret openclaw-secrets -n "$NS" &>/dev/null; then
HAS_KEY=false
for key in ANTHROPIC_API_KEY OPENAI_API_KEY GEMINI_API_KEY OPENROUTER_API_KEY; do
[[ -n "${!key:-}" ]] && HAS_KEY=true
done
if $HAS_KEY; then
echo "Creating secret from environment..."
_apply_secret
echo ""
else
echo "No secret found and no API keys in environment."
echo ""
echo "Export at least one provider API key and re-run:"
echo " export <PROVIDER>_API_KEY=\"...\" (ANTHROPIC, GEMINI, OPENAI, or OPENROUTER)"
echo " ./scripts/k8s/deploy.sh"
exit 1
fi
fi
# ---------------------------------------------------------------------------
# Deploy
# ---------------------------------------------------------------------------
echo "Deploying to namespace '$NS'..."
kubectl create namespace "$NS" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
kubectl apply -k "$MANIFESTS" -n "$NS"
kubectl rollout restart deployment/openclaw -n "$NS" 2>/dev/null || true
echo ""
echo "Waiting for rollout..."
kubectl rollout status deployment/openclaw -n "$NS" --timeout=300s
echo ""
echo "Done. Access the gateway:"
echo " kubectl port-forward svc/openclaw 18789:18789 -n $NS"
echo " open http://localhost:18789"
echo ""
if $SHOW_TOKEN; then
echo "Gateway token (paste into Control UI):"
echo " $(kubectl get secret openclaw-secrets -n "$NS" -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d)"
echo ""
fi
echo "Retrieve the gateway token with:"
echo " kubectl get secret openclaw-secrets -n $NS -o jsonpath='{.data.OPENCLAW_GATEWAY_TOKEN}' | base64 -d && echo"
|