Déployer OpenClaw sur des instances Cloud Run avec Terraform

1. Introduction

Présentation

Dans cet atelier, vous allez déployer une instance de production entièrement persistante d'OpenClaw, un framework d'agent IA Open Source, sur des instances Cloud Run à l'aide d'une infrastructure as code déclarative (Terraform). Vous allez provisionner des instances Cloud Run à l'aide de la ressource google_cloud_run_v2_instance du fournisseur google, installer Google Cloud Storage via Cloud Storage FUSE pour conserver l'état de l'espace de travail de l'agent, gérer les identifiants d'API de manière sécurisée à l'aide de Google Cloud Secret Manager et interagir avec votre agent IA via l'interface utilisateur Web intégrée d'OpenClaw à l'aide de SSH natif des instances Cloud Run pour une association d'appareils sécurisée.

Avant de commencer, vous pouvez explorer la documentation OpenClaw pour vous familiariser avec l'architecture, les outils et les workflows d'agent d'OpenClaw.

Objectifs de l'atelier

  • Définissez l'intégralité de votre infrastructure de manière déclarative dans Terraform (main.tf), y compris les API, les comptes de service, les liaisons IAM, les secrets, les buckets de stockage et les instances Cloud Run.
  • Déployez OpenClaw 2.0 ou version ultérieure (ghcr.io/openclaw/openclaw:latest) à l'aide de google_cloud_run_v2_instance.
  • Générez et stockez automatiquement des identifiants de passerelle sécurisée dans Secret Manager.
  • Installez un bucket Cloud Storage via Cloud Storage FUSE et synchronisez /home/node/.openclaw pour l'état durable, l'espace de travail et la persistance SQLite.
  • Utilisez gcloud beta run instances ssh pour authentifier et approuver les demandes d'association d'appareils du navigateur dans l'interface utilisateur Web de contrôle OpenClaw.
  • (Facultatif) Configurez un canal de messagerie (Telegram ou WhatsApp).
  • (Facultatif) Étendez les capacités de votre agent en ajoutant des compétences personnalisées à Cloud Storage.

Points abordés

  • Déclarer et déployer des instances Cloud Run à l'aide de Terraform
  • Découvrez comment configurer des montages de volume Cloud Storage FUSE dans Terraform.
  • Injecter de manière sécurisée des secrets Secret Manager en tant que variables d'environnement dans des instances Cloud Run
  • Gérer l'authentification des appareils OpenClaw 2.0 à l'aide de SSH natif pour les instances Cloud Run.
  • Découvrez comment exécuter des charges de travail d'agent persistantes et de longue durée sur des instances Cloud Run.

2. Préparation

Configuration du projet GCP

  1. Connectez-vous à la console Google Cloud.
  2. Créez ou sélectionnez un projet Google Cloud.
  3. Assurez-vous que la facturation est activée pour votre projet Google Cloud.

Ouvrir Cloud Shell

Activez Google Cloud Shell dans la barre d'outils supérieure de la console Cloud. Terraform et le SDK Google Cloud sont préinstallés dans Cloud Shell.

Définir le projet et la région

Définissez l'ID de votre projet et votre région en tant que variables d'environnement, puis configurez gcloud :

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

gcloud config set project $PROJECT_ID
gcloud config get project

Installer les composants gcloud beta

Assurez-vous que le composant beta est installé (requis pour SSH et le flux de journaux des instances Cloud Run) :

gcloud components install beta --quiet
gcloud components update

Vérifier Terraform

Vérifiez que Terraform est installé et qu'il répond aux exigences de version :

terraform version

Vous devriez voir Terraform v1.5.0 ou une version ultérieure.

3. (Facultatif) Configurer les intégrations de messagerie (Telegram ou WhatsApp)

Vous pouvez connecter OpenClaw à Telegram ou WhatsApp. Choisissez l'option A ou l'option B ci-dessous.

Option A : Configurer un bot Telegram

  1. Ouvrez Telegram et recherchez @BotFather.
  2. Envoyez la commande /newbot et suivez les instructions pour spécifier un nom de bot et un nom d'utilisateur.
  3. Copiez le jeton d'API HTTP généré (par exemple, 123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ).
  4. Recherchez @userinfobot sur Telegram, envoyez /start, puis copiez votre ID utilisateur numérique (par exemple, 8035936176).

Vous fournirez ces valeurs en tant que variables d'entrée Terraform dans votre fichier terraform.tfvars.

Option B : Configuration de WhatsApp

  1. Obtenez votre numéro de téléphone WhatsApp personnel au format international, sans espaces ni symboles (par exemple, +15551234567).
  2. OpenClaw se connecte via la règle de liste d'autorisation du canal WhatsApp.

4. Définir l'infrastructure dans Terraform

Nous allons déclarer l'intégralité de notre infrastructure dans Terraform. Cela inclut l'activation des API Google Cloud requises, la création du compte de service dédié et des rôles IAM, la génération et le stockage des secrets dans Secret Manager, le provisionnement du bucket d'état Cloud Storage, la génération de la configuration openclaw.json et le déploiement de l'instance Cloud Run.

1. Créer un répertoire Terraform

Dans Cloud Shell, créez un répertoire pour vos fichiers Terraform et accédez-y :

mkdir openclaw-terraform && cd openclaw-terraform

2. Créer main.tf

Créez un fichier nommé main.tf. Cette configuration utilise le fournisseur Google pour définir tous les composants d'infrastructure :

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. Créer variables.tf

Créez un fichier nommé variables.tf pour définir les variables d'entrée :

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. Créer outputs.tf

Créez un fichier nommé outputs.tf pour exposer l'URL de l'instance déployée, le mot de passe de la passerelle généré et le nom du bucket de stockage :

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. Créer terraform.tfvars

Pour obtenir une clé API Gemini :

  1. Accédez à Google AI Studio et connectez-vous avec votre compte Google.
  2. Cliquez sur Créer une clé API, puis sélectionnez votre projet Google Cloud.
  3. Copiez la clé API générée.

Créez un fichier nommé terraform.tfvars avec les coordonnées de votre projet :

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. Déployer une infrastructure avec Terraform

1. Initialiser et appliquer

Initialisez le répertoire de travail Terraform et téléchargez les plug-ins du fournisseur :

terraform init

Déployez l'infrastructure :

terraform apply

Lorsque vous y êtes invité, examinez les ressources planifiées et saisissez yes pour confirmer.

2. Extraire les sorties du déploiement

Exportez l'URL de l'instance et le mot de passe de la passerelle généré à partir de l'état 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. Interagir via l'UI Web et s'authentifier via Cloud Run SSH

OpenClaw 2.0 ou version ultérieure applique l'authentification cryptographique des appareils. Lors de la connexion à partir d'un nouveau navigateur, la passerelle génère une demande d'association que vous approuvez directement via le SSH natif des instances Cloud Run.

1. Ouvrir l'interface utilisateur de contrôle OpenClaw

  1. Ouvrez $INSTANCE_URL dans votre navigateur Web.
  2. Lorsque vous êtes invité à vous authentifier, saisissez le mot de passe de votre passerelle ($GATEWAY_PASSWORD).
  3. Comme il s'agit d'une nouvelle session de navigateur, l'interface utilisateur de contrôle OpenClaw affiche une invite d'association d'appareil en attente avec un ID de requête (par exemple : b56143fc-1654-49da-a1d8-5d40479ddc66). Copiez cet ID de requête.

2. Se connecter en SSH à l'instance Cloud Run

Dans Cloud Shell, démarrez un shell interactif sécurisé dans votre instance en cours d'exécution :

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

3. Approuver l'association d'appareils

Dans la session SSH, approuvez la requête de votre navigateur :

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

4. Envoyer un prompt à votre agent (instances en action)

Une fois la demande approuvée, votre navigateur se connecte immédiatement au tableau de bord de contrôle OpenClaw.

  1. Testez votre agent OpenClaw en envoyant une requête dans l'UI de chat, par exemple "Hello".
  2. Pour tester le cycle de vie de longue durée et le stockage persistant de l'instance Cloud Run, demandez à votre agent OpenClaw d'effectuer une tâche autonome planifiée. Par exemple, demandez-lui de surveiller un dépôt GitHub pour détecter les nouvelles versions et de rédiger un résumé :
    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. Comme l'état d'OpenClaw, les fichiers de l'espace de travail et les bases de données SQLite sont conservés directement dans Cloud Storage via Cloud Storage FUSE, la tâche planifiée, l'historique des conversations et les fichiers de résumé générés survivent aux redémarrages d'instance et aux mises à niveau de conteneur.

7. (Facultatif) Vérifier les intégrations de canaux de messagerie

Si vous avez configuré Telegram ou WhatsApp lors des étapes de configuration facultatives ci-dessus, vous pouvez vérifier la distribution des messages :

  1. Vérifiez les journaux de l'instance dans Cloud Shell :
    gcloud beta run instances logs read openclaw-instance --region ${REGION} --limit 20
    
  2. Ouvrez Telegram (ou WhatsApp) et envoyez un message (par exemple, /start ou Hello OpenClaw!).
  3. Le bot authentifiera votre ID utilisateur par rapport à la liste d'autorisation dans openclaw.json et répondra à l'aide de Gemini.

8. Surveiller et inspecter les journaux d'instance

Les instances Cloud Run capturent les flux stdout et stderr de votre conteneur et les transmettent directement à Google Cloud Logging. Vous pouvez utiliser Cloud Shell ou la console Cloud pour surveiller le trafic de la passerelle et inspecter les exécutions des outils de l'agent en temps réel.

1. Lire les journaux d'instance récents

Pour récupérer les entrées de journal récentes de l'instance dans Cloud Shell :

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

2. Signatures de journaux clés à observer

Lorsque vous observez votre agent en cours d'exécution, recherchez les événements de journal d'exécution clés suivants :

  • [gateway] ready : confirme que la passerelle HTTP et WebSocket OpenClaw a été initialisée et qu'elle écoute sur le port 18789.
  • [gateway] agent model: ... : affiche le modèle Gemini actif (par exemple, google/gemini-3.1-pro-preview ou google/gemini-3.5-flash).
  • [agents/tool-policy] : affiche les outils autorisés ou limités par votre règle.
  • [agent/embedded] : trace l'exécution et l'appel d'outils par l'agent.

3. Inspecter les journaux dans la console Google Cloud

Vous pouvez également afficher et filtrer les journaux à l'aide de l'Explorateur de journaux dans la console Cloud :

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

9. (Facultatif) Étendre votre agent avec des compétences

OpenClaw est compatible avec les compétences, des packages de fonctionnalités modulaires qui enseignent à votre agent des workflows spécifiques, des outils CLI spécialisés et des instructions spécifiques à un domaine.

Fonctionnement des compétences

Chaque compétence est un répertoire contenant un fichier SKILL.md. Il commence par un frontmatter YAML spécifiant les métadonnées (name et description), suivi d'instructions 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 : identifiant de la compétence (également appelable directement en tant que commande à barre oblique, par exemple /summarize-logs).
  • description : indique au modèle d'OpenClaw quand appeler automatiquement cette compétence dans une conversation naturelle.

Ajouter une compétence personnalisée à votre bucket Cloud Storage

Comme votre bucket Cloud Storage est installé sur /mnt/openclaw et configuré dans skills.load.extraDirs, les compétences placées sous gs://${BUCKET_NAME}/skills/ sont automatiquement chargées et conservées lors des redémarrages de l'instance :

  1. Récupérez le nom de votre bucket :
    export BUCKET_NAME=$(terraform output -raw storage_bucket)
    
  2. Créer un répertoire de compétences local :
    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. Importez la compétence dans Cloud Storage :
    gcloud storage cp -r my-skill gs://${BUCKET_NAME}/skills/
    
  4. Utiliser la compétence dans Chat :
    • Revenez à l'interface utilisateur Web OpenClaw Control.
    • Vous pouvez désormais envoyer des requêtes à votre agent à l'aide de /summarize-logs ou poser des questions en langage naturel correspondant à la description de la compétence.

10. Effectuer un nettoyage

Pour éviter que les ressources utilisées dans cet atelier de programmation soient facturées en permanence sur votre compte Google Cloud, vous pouvez détruire toute l'infrastructure provisionnée en une seule étape à l'aide de Terraform :

  1. Assurez-vous d'être dans le répertoire openclaw-terraform :
    cd openclaw-terraform
    
  2. Détruisez toutes les ressources :
    terraform destroy
    
  3. Lorsque vous y êtes invité, examinez les ressources à supprimer et saisissez yes pour confirmer. Terraform supprimera proprement l'instance Cloud Run, les secrets Secret Manager, les liaisons de stratégie IAM, le compte de service et le bucket Cloud Storage.

11. Conclusion

Félicitations ! Vous avez déployé une instance OpenClaw sécurisée et entièrement persistante sur des instances Cloud Run à l'aide de Terraform déclaratif, avec la persistance d'état Cloud Storage, la sécurité des identifiants Secret Manager et le SSH natif des instances Cloud Run pour une authentification sécurisée des appareils.