Развертывание OpenClaw на экземплярах Cloud Run с помощью Terraform

1. Введение

Обзор

В этой практической работе вы развернете полностью персистентный экземпляр OpenClaw (фреймворка ИИ-агентов с открытым исходным кодом) в экземплярах Cloud Run с помощью декларативной инфраструктуры как кода (Terraform). Вы подготовите экземпляры Cloud Run, используя ресурс google_cloud_run_v2_instance от поставщика google, подключите Google Cloud Storage через Cloud Storage FUSE, чтобы сохранить состояние рабочей области агента, настроите безопасное управление учетными данными API с помощью Google Cloud Secret Manager и будете взаимодействовать со своим ИИ-агентом через встроенный веб-интерфейс OpenClaw, используя встроенный SSH экземпляров Cloud Run для безопасного подключения устройств.

Прежде чем начать, ознакомьтесь с документацией OpenClaw, чтобы узнать больше об архитектуре, инструментах и рабочих процессах агентов OpenClaw.

Что вам предстоит сделать

  • Определите всю инфраструктуру декларативно в Terraform (main.tf), включая API, сервисные аккаунты, привязки IAM, секреты, сегменты хранилища и экземпляры Cloud Run.
  • Разверните OpenClaw 2.0 или более поздней версии (ghcr.io/openclaw/openclaw:latest) с помощью google_cloud_run_v2_instance.
  • Автоматически генерировать и хранить учетные данные защищенного шлюза в Secret Manager.
  • Подключите сегмент Cloud Storage через Cloud Storage FUSE и синхронизируйте /home/node/.openclaw для обеспечения долговечности состояния, рабочей области и SQLite.
  • Используйте gcloud beta run instances ssh для аутентификации и одобрения запросов на подключение устройств в браузере в веб-интерфейсе OpenClaw Control.
  • (Необязательно.) Настройте канал обмена сообщениями (Telegram или WhatsApp).
  • (Необязательно.) Расширьте возможности агента, добавив в Cloud Storage собственные навыки.

Что вы узнаете

  • Как объявлять и развертывать экземпляры Cloud Run с помощью Terraform.
  • Как настроить точки подключения томов Cloud Storage FUSE в Terraform.
  • Как безопасно внедрять секреты Secret Manager в качестве переменных среды в экземпляры Cloud Run.
  • Как управлять аутентификацией устройств OpenClaw 2.0 с помощью встроенного SSH экземпляров Cloud Run
  • Как запускать долгоживущие постоянные рабочие нагрузки агентов в экземплярах Cloud Run.

2. Настройка и требования

Настройка проекта GCP

  1. Войдите в консоль Google Cloud.
  2. Создайте или выберите проект Google Cloud.
  3. Убедитесь, что для проекта Google Cloud включены платежные функции.

Открыть Cloud Shell

Откройте Google Cloud Shell, нажав на значок в верхней панели инструментов Cloud Console. В Cloud Shell предустановлены Terraform и Google Cloud SDK.

Как задать проект и регион

Задайте идентификатор проекта и регион в качестве переменных среды и настройте gcloud:

export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION=<YOUR_REGION>

gcloud config set project $PROJECT_ID
gcloud config get project

Установите бета-компоненты gcloud

Убедитесь, что компонент beta установлен (требуется для SSH-подключения к экземплярам Cloud Run и потоковой передачи журналов):

gcloud components install beta --quiet
gcloud components update

Проверка Terraform

Убедитесь, что Terraform установлен и соответствует требованиям к версии:

terraform version

Вы должны увидеть Terraform версии 1.5.0 или более поздней.

3. Как настроить интеграцию с мессенджерами (Telegram или WhatsApp) (необязательно)

Вы можете подключить OpenClaw к Telegram или WhatsApp. Выберите вариант А или вариант Б ниже.

Вариант А. Настройка бота Telegram

  1. Откройте Telegram и найдите @BotFather.
  2. Отправьте команду /newbot и следуйте инструкциям, чтобы указать название и имя пользователя бота.
  3. Скопируйте сгенерированный токен HTTP API (например, 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ).
  4. Найдите @userinfobot в Telegram, отправьте /start и скопируйте свой числовой идентификатор пользователя (например, 8035936176).

Эти значения нужно будет указать в качестве входных переменных Terraform в файле terraform.tfvars.

Вариант Б. Настройка WhatsApp

  1. Укажите свой номер телефона в WhatsApp в международном формате без пробелов и символов (например, +15551234567).
  2. OpenClaw подключается через белый список каналов WhatsApp.

4. Определение инфраструктуры в Terraform

Мы объявим всю нашу инфраструктуру в Terraform. В частности, вам нужно будет включить необходимые API Google Cloud, создать выделенный сервисный аккаунт и роли IAM, сгенерировать и сохранить секреты в Secret Manager, подготовить сегмент состояния Cloud Storage, сгенерировать конфигурацию openclaw.json и развернуть экземпляр Cloud Run.

1. Создание каталога Terraform

В Cloud Shell создайте каталог для файлов Terraform и перейдите в него:

mkdir openclaw-terraform && cd openclaw-terraform

2. Создать main.tf

Создайте файл с названием main.tf. В этой конфигурации для определения всех компонентов инфраструктуры используется поставщик Google:

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 8.8.0"
    }
    random = {
      source  = "hashicorp/random"
      version = ">= 3.5.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

# 1. Enable Required APIs
resource "google_project_service" "services" {
  for_each = toset([
    "run.googleapis.com",
    "secretmanager.googleapis.com",
    "storage.googleapis.com",
    "generativelanguage.googleapis.com",
    "compute.googleapis.com"
  ])
  project            = var.project_id
  service            = each.key
  disable_on_destroy = false
}

# 2. Dedicated Service Account
resource "google_service_account" "openclaw_sa" {
  account_id   = "openclaw-sa"
  display_name = "OpenClaw Service Account"
  depends_on   = [google_project_service.services]
}

# 3. Secret Manager: Gateway Password
resource "random_password" "gateway_password" {
  length  = 32
  special = false
}

resource "google_secret_manager_secret" "gateway_password" {
  secret_id = "openclaw-gateway-password"
  replication {
    auto {}
  }
  depends_on = [google_project_service.services]
}

resource "google_secret_manager_secret_version" "gateway_password" {
  secret      = google_secret_manager_secret.gateway_password.id
  secret_data = random_password.gateway_password.result
}

resource "google_secret_manager_secret_iam_member" "sa_gateway_password" {
  secret_id = google_secret_manager_secret.gateway_password.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${google_service_account.openclaw_sa.email}"
}

# 4. Secret Manager: Gemini API Key
resource "google_secret_manager_secret" "gemini_api_key" {
  secret_id = "gemini-api-key"
  replication {
    auto {}
  }
  depends_on = [google_project_service.services]
}

resource "google_secret_manager_secret_version" "gemini_api_key" {
  secret      = google_secret_manager_secret.gemini_api_key.id
  secret_data = var.gemini_api_key
}

resource "google_secret_manager_secret_iam_member" "sa_gemini_api_key" {
  secret_id = google_secret_manager_secret.gemini_api_key.id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${google_service_account.openclaw_sa.email}"
}

# 5. Optional Secret Manager: Telegram Bot Token
resource "google_secret_manager_secret" "telegram_bot_token" {
  count     = var.telegram_bot_token != "" ? 1 : 0
  secret_id = "telegram-bot-token"
  replication {
    auto {}
  }
  depends_on = [google_project_service.services]
}

resource "google_secret_manager_secret_version" "telegram_bot_token" {
  count       = var.telegram_bot_token != "" ? 1 : 0
  secret      = google_secret_manager_secret.telegram_bot_token[0].id
  secret_data = var.telegram_bot_token
}

resource "google_secret_manager_secret_iam_member" "sa_telegram_bot_token" {
  count     = var.telegram_bot_token != "" ? 1 : 0
  secret_id = google_secret_manager_secret.telegram_bot_token[0].id
  role      = "roles/secretmanager.secretAccessor"
  member    = "serviceAccount:${google_service_account.openclaw_sa.email}"
}

# 6. Cloud Storage Bucket for State Persistence
resource "google_storage_bucket" "openclaw_state" {
  name                        = "openclaw-state-${var.project_id}"
  location                    = var.region
  uniform_bucket_level_access = true
  force_destroy               = true
  depends_on                  = [google_project_service.services]
}

resource "google_storage_bucket_iam_member" "sa_storage" {
  bucket = google_storage_bucket.openclaw_state.name
  role   = "roles/storage.objectUser"
  member = "serviceAccount:${google_service_account.openclaw_sa.email}"
}

# 7. Upload openclaw.json to Cloud Storage Root
resource "google_storage_bucket_object" "openclaw_config" {
  name   = "openclaw.json"
  bucket = google_storage_bucket.openclaw_state.name
  content = jsonencode({
    gateway = {
      mode           = "local"
      port           = 18789
      trustedProxies = ["127.0.0.1", "::1", "169.254.0.0/16", "10.0.0.0/8"]
      bind           = "lan"
      auth = {
        password = "$${OPENCLAW_GATEWAY_PASSWORD}"
      }
      controlUi = {
        allowedOrigins = ["*"]
        enabled        = true
      }
    }
    memory = {
      search = {
        provider = "gemini"
      }
    }
    agents = {
      defaults = {
        model = {
          primary = "google/gemini-3.1-pro-preview"
        }
        sandbox = {
          mode = "off"
        }
      }
    }
    skills = {
      load = {
        extraDirs = ["/mnt/openclaw/skills"]
      }
    }
    channels = {
      telegram = {
        enabled        = var.telegram_bot_token != ""
        defaultAccount = "default"
        accounts = {
          default = {
            enabled   = var.telegram_bot_token != ""
            dmPolicy  = "allowlist"
            allowFrom = var.telegram_user_id != "" ? [var.telegram_user_id] : []
          }
        }
      }
    }
    plugins = {
      entries = {
        google   = { enabled = true }
        telegram = { enabled = var.telegram_bot_token != "" }
      }
    }
  })
}

# 8. Cloud Run Instance
resource "google_cloud_run_v2_instance" "openclaw" {
  name     = "openclaw-instance"
  location = var.region

  ingress              = "INGRESS_TRAFFIC_ALL"
  invoker_iam_disabled = true
  restart_policy       = "ON_FAILURE"
  service_account      = google_service_account.openclaw_sa.email

  containers {
    image = "ghcr.io/openclaw/openclaw:latest"
    args = [
      "/bin/sh",
      "-c",
      "cp /mnt/openclaw/openclaw.json /home/node/.openclaw/ && exec node /app/openclaw.mjs gateway"
    ]

    ports {
      container_port = 18789
    }

    resources {
      limits = {
        cpu    = "4"
        memory = "4Gi"
      }
    }

    env {
      name = "GEMINI_API_KEY"
      value_source {
        secret_key_ref {
          secret  = google_secret_manager_secret.gemini_api_key.secret_id
          version = "latest"
        }
      }
    }

    env {
      name = "OPENCLAW_GATEWAY_PASSWORD"
      value_source {
        secret_key_ref {
          secret  = google_secret_manager_secret.gateway_password.secret_id
          version = "latest"
        }
      }
    }

    dynamic "env" {
      for_each = toset(var.telegram_bot_token != "" ? ["telegram"] : [])
      content {
        name = "TELEGRAM_BOT_TOKEN"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.telegram_bot_token[0].secret_id
            version = "latest"
          }
        }
      }
    }

    volume_mounts {
      name       = "openclaw-storage"
      mount_path = "/mnt/openclaw"
    }
  }

  volumes {
    name = "openclaw-storage"
    gcs {
      bucket        = google_storage_bucket.openclaw_state.name
      read_only     = false
      mount_options = ["uid=1000", "gid=1000", "file-mode=0700", "dir-mode=0700"]
    }
  }

  depends_on = [
    google_project_service.services,
    google_storage_bucket_object.openclaw_config,
    google_secret_manager_secret_version.gateway_password,
    google_secret_manager_secret_version.gemini_api_key,
    google_secret_manager_secret_iam_member.sa_gateway_password,
    google_secret_manager_secret_iam_member.sa_gemini_api_key,
    google_storage_bucket_iam_member.sa_storage
  ]
}

3. Создать variables.tf

Создайте файл с названием variables.tf, чтобы определить входные переменные:

variable "project_id" {
  type        = string
  description = "Google Cloud Project ID"
}

variable "region" {
  type        = string
  default     = "us-west1"
  description = "Google Cloud Region for Cloud Run and Storage"
}

variable "gemini_api_key" {
  type        = string
  sensitive   = true
  description = "Gemini API Key from Google AI Studio"
}

variable "telegram_bot_token" {
  type        = string
  default     = ""
  sensitive   = true
  description = "(Optional) Telegram Bot HTTP API Token"
}

variable "telegram_user_id" {
  type        = string
  default     = ""
  description = "(Optional) Telegram User ID for allowlist policy"
}

4. Создать outputs.tf

Создайте файл с именем outputs.tf, чтобы получить URL развернутого экземпляра, сгенерированный пароль шлюза и название сегмента хранилища.

output "instance_url" {
  description = "Public URL for OpenClaw Cloud Run Instance"
  value       = google_cloud_run_v2_instance.openclaw.urls[0]
}

output "gateway_password" {
  description = "Generated OpenClaw Gateway Password"
  value       = random_password.gateway_password.result
  sensitive   = true
}

output "storage_bucket" {
  description = "Cloud Storage bucket persisting OpenClaw state"
  value       = google_storage_bucket.openclaw_state.name
}

5. Создать terraform.tfvars

Чтобы получить ключ Gemini API:

  1. Перейдите в Google AI Studio и войдите в аккаунт Google.
  2. Нажмите Создать ключ API и выберите облачный проект Google Cloud.
  3. Скопируйте сгенерированный ключ API.

Создайте файл terraform.tfvars с координатами проекта:

project_id     = "YOUR_PROJECT_ID"
region         = "YOUR_REGION"
gemini_api_key = "YOUR_GEMINI_API_KEY"

# Optional: Uncomment if configuring Telegram
# telegram_bot_token = "YOUR_TELEGRAM_BOT_TOKEN"
# telegram_user_id   = "YOUR_TELEGRAM_USER_ID"

5. Развертывание инфраструктуры с помощью Terraform

1. Инициализировать и применить

Инициализируйте рабочий каталог Terraform и скачайте плагины поставщика:

terraform init

Разверните инфраструктуру:

terraform apply

Когда появится запрос, проверьте запланированные ресурсы и введите yes для подтверждения.

2. Извлечь выходные данные развертывания

Экспортируйте URL экземпляра и сгенерированный пароль шлюза из состояния Terraform:

export INSTANCE_URL=$(terraform output -raw instance_url)
export GATEWAY_PASSWORD=$(terraform output -raw gateway_password)

echo "Instance URL: ${INSTANCE_URL}"
echo "Gateway Password: ${GATEWAY_PASSWORD}"

6. Взаимодействие через веб-интерфейс и аутентификация через Cloud Run SSH

В OpenClaw 2.0 и более поздних версиях применяется криптографическая аутентификация устройств. При подключении из нового браузера шлюз генерирует запрос на связывание, который вы одобряете непосредственно через встроенный SSH-клиент экземпляров Cloud Run.

1. Открыть интерфейс управления OpenClaw

  1. Откройте $INSTANCE_URL в браузере.
  2. Когда появится запрос на аутентификацию, введите пароль шлюза ($GATEWAY_PASSWORD).
  3. Поскольку это новый сеанс браузера, в интерфейсе OpenClaw Control появится запрос на подключение устройства с идентификатором запроса (например, b56143fc-1654-49da-a1d8-5d40479ddc66). Скопируйте этот идентификатор.

2. Подключение к экземпляру Cloud Run по SSH

В Cloud Shell запустите защищенную интерактивную оболочку в работающем экземпляре:

gcloud beta run instances ssh openclaw-instance --region=${REGION}

3. Одобрить подключение устройств

В сеансе SSH одобрите запрос браузера:

openclaw devices approve <REQUEST_ID> --password ${GATEWAY_PASSWORD}

4. Как задать запрос агенту (примеры)

После одобрения ваш браузер сразу же подключится к панели управления OpenClaw.

  1. Протестируйте агент OpenClaw, отправив запрос в интерфейсе чата, например "Hello".
  2. Чтобы протестировать длительный жизненный цикл экземпляра Cloud Run и постоянное хранилище, попросите агента OpenClaw выполнить запланированную автономную задачу. Например, вы можете попросить его отслеживать выпуски в хранилище GitHub и составлять дайджест:
    Check the GitHub repository openclaw/openclaw every hour for the next 24 hours. Track any new releases, merged pull requests, and breaking changes. Save a structured markdown digest titled `daily-release-digest.md` to your workspace, and notify me in this chat with a summary whenever a new release is detected.
    
  3. Поскольку состояние OpenClaw, файлы рабочей области и базы данных SQLite сохраняются непосредственно в Cloud Storage через Cloud Storage FUSE, запланированная задача, история разговоров и сгенерированные файлы дайджестов сохраняются при перезапуске экземпляра и обновлении контейнера.

7. Проверьте интеграцию каналов обмена сообщениями (необязательно)

Если вы настроили Telegram или WhatsApp, как описано выше, вы можете проверить доставку сообщений.

  1. Проверьте журналы экземпляра в Cloud Shell:
    gcloud beta run instances logs read openclaw-instance --region ${REGION} --limit 20
    
  2. Откройте Telegram или WhatsApp и отправьте сообщение, например /start или Hello OpenClaw!.
  3. Бот проверит ваш идентификатор пользователя по белому списку в openclaw.json и ответит с помощью Gemini.

8. Отслеживание и проверка журналов экземпляров

Экземпляры Cloud Run захватывают stdout и stderr из контейнера и передают их напрямую в Google Cloud Logging. Вы можете использовать Cloud Shell или Cloud Console, чтобы отслеживать трафик шлюза и проверять запуски инструментов агента в режиме реального времени.

1. Чтение журналов недавних экземпляров

Чтобы получить последние записи в журнале из экземпляра в Cloud Shell:

gcloud beta run instances logs read openclaw-instance \
  --region ${REGION} \
  --limit 50

2. Основные сигнатуры журналов, на которые следует обратить внимание

При наблюдении за работающим агентом обратите внимание на следующие ключевые события журнала выполнения:

  • [gateway] ready – подтверждает, что шлюз OpenClaw HTTP и WebSocket инициализирован и прослушивает порт 18789.
  • [gateway] agent model: ... – показывает активную модель Gemini (например, google/gemini-3.1-pro-preview или google/gemini-3.5-flash).
  • [agents/tool-policy] – инструменты, разрешенные или запрещенные вашими правилами.
  • [agent/embedded] отслеживает выполнение и вызов инструментов агентом.

3. Как проверить журналы в Google Cloud Console

Вы также можете просматривать и фильтровать журналы с помощью Logs Explorer в Cloud Console.

resource.type="cloud_run_instance"
resource.labels.instance_name="openclaw-instance"

9. Как расширить возможности агента с помощью навыков (необязательно)

OpenClaw поддерживает навыки – модульные пакеты возможностей, которые обучают агента определенным рабочим процессам, специализированным инструментам CLI и инструкциям для конкретных доменов.

Как работают навыки

Каждый навык – это каталог, содержащий файл SKILL.md. Он начинается с блока Front Matter в формате YAML, в котором указаны метаданные (name и description), а затем идут инструкции в формате Markdown:

---
name: summarize-logs
description: Summarize Cloud Run error logs into actionable bullet points.
---

# Log Summarizer Skill

When asked to analyze or summarize logs:
1. Parse error stack traces and group similar errors by frequency.
2. Identify root causes such as memory limits, timeouts, or permission errors.
3. Propose concrete remediation steps.
  • name – идентификатор навыка (его также можно вызвать напрямую в виде слеш-команды, например /summarize-logs).
  • description: указывает модели OpenClaw, когда автоматически вызывать этот навык в естественном разговоре.

Как добавить пользовательский навык в сегмент Cloud Storage

Поскольку сегмент Cloud Storage подключен к /mnt/openclaw и настроен в skills.load.extraDirs, навыки, размещенные в gs://${BUCKET_NAME}/skills/, загружаются автоматически и сохраняются при перезапуске экземпляра:

  1. Как узнать название сегмента
    export BUCKET_NAME=$(terraform output -raw storage_bucket)
    
  2. Создайте каталог местных навыков.
    mkdir -p my-skill
    cat << 'EOF' > my-skill/SKILL.md
    ---
    name: summarize-logs
    description: Summarize Cloud Run error logs into actionable bullet points.
    ---
    
    # Log Summarizer Skill
    
    When asked to analyze or summarize logs, group errors by frequency and suggest actionable fixes.
    EOF
    
  3. Загрузите навык в Cloud Storage:
    gcloud storage cp -r my-skill gs://${BUCKET_NAME}/skills/
    
  4. Как использовать навык в Chat
    • Вернитесь в веб-интерфейс OpenClaw Control.
    • Теперь вы можете отправлять агенту запросы, используя /summarize-logs или задавая вопросы на естественном языке, соответствующие описанию навыка.

10. Убирать

Чтобы избежать постоянных расходов на аккаунт Google Cloud за ресурсы, используемые в этой практической работе, вы можете уничтожить всю подготовленную инфраструктуру за один шаг с помощью Terraform:

  1. Перейдите в каталог openclaw-terraform:
    cd openclaw-terraform
    
  2. Удалить все ресурсы:
    terraform destroy
    
  3. Когда появится запрос, проверьте ресурсы, которые нужно удалить, и введите yes для подтверждения. Terraform удалит экземпляр Cloud Run, секреты Secret Manager, привязки правил IAM, сервисный аккаунт и сегмент Cloud Storage.

11. Заключение

Поздравляем! Вы успешно развернули безопасный полностью постоянный экземпляр OpenClaw в Cloud Run Instances с помощью декларативного Terraform, поддерживаемого сохранением состояния Cloud Storage, безопасностью учетных данных Secret Manager и собственным SSH Cloud Run Instances для безопасной аутентификации устройств.