ক্লাউড রান ইনস্ট্যান্সগুলিতে হার্মিস এজেন্ট কীভাবে স্থাপন করবেন

১. ভূমিকা

সংক্ষিপ্ত বিবরণ

এই ল্যাবে, আপনি ক্লাউড রান ইনস্ট্যান্সে নুস রিসার্চের তৈরি হার্মিস এজেন্টের একটি সম্পূর্ণ স্থায়ী ও সুরক্ষিত ইনস্ট্যান্স স্থাপন করবেন। আপনি হার্মিস ওয়েব ড্যাশবোর্ড ব্যবহার করে আপনার এআই এজেন্টের সাথে যোগাযোগ করবেন এবং গুগল ক্লাউড স্টোরেজ দিয়ে এর স্থায়ী ওয়ার্কস্পেসের ব্যাকআপ নেবেন।

হার্মিস একটি গেটওয়ে মোড সমর্থন করে যা একটি অটো-স্কেলিং ক্লাউড রান সার্ভিস হিসেবে চলতে পারে, তবে এটি একটি স্টেটফুল এজেন্ট হিসেবেও কাজ করে যা বুট করার সময় স্কিল স্ক্যান করে এবং ব্যাকগ্রাউন্ড এক্সিকিউশন পরিচালনা করে। ক্লাউড রান ইনস্ট্যান্সগুলো একটি দীর্ঘস্থায়ী, স্বতন্ত্রভাবে অ্যাড্রেসযোগ্য পরিবেশ প্রদান করে যা এই ধরনের ওয়ার্কলোডের জন্য পুরোপুরি উপযুক্ত।

আপনি যা করবেন

  • কন্টেইনারের অবস্থা এবং কনফিগারেশন সংরক্ষণের জন্য একটি ক্লাউড স্টোরেজ বাকেট প্রস্তুত করুন।
  • বুট ইনিশিয়ালাইজেশন পরিচালনার জন্য একটি কাস্টম পাইথন সুপারভাইজার ( run_hermes.py ) এবং একটি স্টার্টআপ স্ক্রিপ্ট ( start_hermes.sh ) তৈরি করুন।
  • gcloud beta run instances deploy ব্যবহার করে হার্মিস এজেন্ট স্থাপন করুন।
  • হার্মিস ড্যাশবোর্ডে প্রবেশ করুন এবং প্রমাণীকরণ করুন।

আপনি যা শিখবেন

  • ক্লাউড রান ইনস্ট্যান্সগুলিতে হার্মিস এজেন্ট কীভাবে স্থাপন করবেন
  • GCSFuse ব্যবহার করে কীভাবে ক্লাউড রান ইনস্ট্যান্সে ক্লাউড স্টোরেজ বাকেট মাউন্ট করবেন।
  • GCSFuse-এর ফাইল-লকিং সীমাবদ্ধতা এড়ানোর জন্য কীভাবে নিরাপদে SQLite এবং ক্ষণস্থায়ী ক্যাশে কনফিগার করবেন।

২. সেটআপ এবং প্রয়োজনীয়তা

জিসিপি প্রজেক্ট সেটআপ

  1. গুগল ক্লাউড কনসোলে সাইন ইন করুন।
  2. একটি গুগল ক্লাউড প্রজেক্ট তৈরি করুন বা নির্বাচন করুন।
  3. আপনার গুগল ক্লাউড প্রোজেক্টের জন্য বিলিং চালু আছে কিনা তা নিশ্চিত করুন।

ওপেন ক্লাউড শেল

ক্লাউড কনসোলের উপরের টুলবার থেকে গুগল ক্লাউড শেল সক্রিয় করুন।

প্রজেক্ট সেট করুন এবং gcloud বিটা ইনস্টল করুন

প্রথমে, আপনার প্রজেক্ট এবং অঞ্চলকে এনভায়রনমেন্ট ভেরিয়েবল হিসেবে সেট করুন।

export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION="us-west2"
export BUCKET_NAME="hermes-state-${PROJECT_ID}"

এবং gcloud-এর জন্য আপনার প্রজেক্টটি কনফিগার করুন।

gcloud config set project $PROJECT_ID

gcloud beta run instances জন্য beta কম্পোনেন্টটি ইনস্টল করা আছে কিনা তা নিশ্চিত করুন:

gcloud components install beta --quiet

এবং আপনার gcloud সংস্করণটি হালনাগাদ আছে।

gcloud components updates

প্রয়োজনীয় গুগল ক্লাউড এপিআই সক্রিয় করুন

ক্লাউড শেলে, ক্লাউড রান, ক্লাউড স্টোরেজ এবং সিক্রেট ম্যানেজার এপিআইগুলো সক্রিয় করুন:

gcloud services enable \
  run.googleapis.com \
  secretmanager.googleapis.com \
  storage.googleapis.com \
  compute.googleapis.com \
  aiplatform.googleapis.com

৩. ডেডিকেটেড সার্ভিস অ্যাকাউন্ট তৈরি করুন

ন্যূনতম বিশেষাধিকারের নীতি মেনে চলার জন্য, হার্মিস এজেন্টের জন্য একটি ডেডিকেটেড IAM সার্ভিস অ্যাকাউন্ট তৈরি করুন এবং ভার্টেক্স এআই মডেলগুলিকে আহ্বান করার জন্য এটিকে প্রয়োজনীয় অনুমতি প্রদান করুন:

export SERVICE_ACCOUNT_NAME="hermes-sa"
gcloud iam service-accounts create ${SERVICE_ACCOUNT_NAME} \
  --display-name="Hermes Service Account"

export SERVICE_ACCOUNT="${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT}" \
  --role="roles/aiplatform.user"

৪. সিক্রেট ম্যানেজারে ক্রেডেনশিয়াল সংরক্ষণ করুন

আমরা ড্যাশবোর্ড পাসওয়ার্ডের মতো সংবেদনশীল তথ্য গুগল ক্লাউড সিক্রেট ম্যানেজারে সংরক্ষণ করব, যাতে ক্লাউড রান বুট করার সময় সেগুলোকে নিরাপদে কন্টেইনারে যুক্ত করতে পারে।

আপনার ড্যাশবোর্ডের জন্য একটি সুরক্ষিত র‍্যান্ডম পাসওয়ার্ড তৈরি করুন এবং এটি সিক্রেট ম্যানেজারে সংরক্ষণ করুন:

export DASHBOARD_PASSWORD=$(openssl rand -hex 16)
echo "Generated Hermes Dashboard Password: ${DASHBOARD_PASSWORD}"

echo -n "${DASHBOARD_PASSWORD}" | gcloud secrets create hermes-dashboard-password \
  --data-file=- \
  --replication-policy="automatic"

gcloud secrets add-iam-policy-binding hermes-dashboard-password \
  --member="serviceAccount:${SERVICE_ACCOUNT}" \
  --role="roles/secretmanager.secretAccessor"

৫. ক্লাউড স্টোরেজ বাকেট ও কনফিগারেশন ফাইল প্রস্তুত করুন

হার্মিসের জন্য /opt/data হিসেবে মাউন্ট করা একটি স্থায়ী স্টোরেজ প্রয়োজন। আমরা একটি গুগল ক্লাউড স্টোরেজ (GCS) বাকেট ব্যবহার করব এবং ক্লাউড স্টোরেজ ভলিউম মাউন্ট ব্যবহার করে এটিকে মাউন্ট করব।

১. একটি ক্লাউড স্টোরেজ বাকেট তৈরি করুন

gcloud storage buckets create gs://${BUCKET_NAME} --location=${REGION}

# Grant the service account permissions to mount the bucket
gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
  --member="serviceAccount:${SERVICE_ACCOUNT}" \
  --role="roles/storage.objectAdmin"

২. config.yaml তৈরি করুন।

একটি config.yaml ফাইল তৈরি করুন। কনফিগারেশনটি সঠিকভাবে লোড হওয়া নিশ্চিত করতে _config_version: 12 অন্তর্ভুক্ত করতে ভুলবেন না:

_config_version: 12

model:
  default: "google/gemini-3.8-flash"
  provider: "vertex"

dashboard:
  enabled: true

database:
  journal_mode: delete

৩. সুপারভাইজার স্ক্রিপ্ট ( run_hermes.py ) তৈরি করুন।

ক্লাউড স্টোরেজ সেইসব নির্দিষ্ট ফাইল লকিং মেকানিজম সমর্থন করে না যা SQLite ডাটাবেস নিরাপদে চলার জন্য প্রয়োজন। ডাটাবেস করাপশন প্রতিরোধ করতে, আমাদের একটি কাস্টম "সুপারভাইজার" স্ক্রিপ্ট ( run_hermes.py ) প্রয়োজন। এই স্ক্রিপ্টটি এজেন্ট শুরু করার আগে হার্মিসকে কনফিগার করে, যাতে এটি তার অস্থায়ী ডাটাবেস লকগুলো ক্লাউড স্টোরেজের পরিবর্তে কন্টেইনারের লোকাল মেমরিতে সংরক্ষণ করে।

স্থানীয়ভাবে run_hermes.py তৈরি করুন:

import os
import shutil
import subprocess
import sys
import threading
import time

print(
    "=== INITIALIZING HERMES SUPERVISOR ===", flush=True
)

# 1. Local Directory Setup
# Creates temporary, local folders (in /tmp) for the agent's caches and working directories.
# See more below in comment section NOTE ON CLOUD STORAGE FUSE
home_dir = "/tmp/hermes_home"
hermes_dir = os.path.join(home_dir, ".hermes")
os.makedirs(hermes_dir, exist_ok=True)
os.makedirs("/tmp/logs", exist_ok=True)
os.makedirs("/tmp/skills", exist_ok=True)
os.makedirs("/tmp/uv_cache", exist_ok=True)
os.makedirs("/tmp/cache", exist_ok=True)
os.makedirs("/opt/data/workspace", exist_ok=True)
os.makedirs("/opt/data/.hermes", exist_ok=True)

# 2. State Restoration & Database Config
# Copies your existing configurations and chat history (state.db) from Cloud Storage into the local folders. 
# It also forces the SQLite database into TRUNCATE mode, a crucial step to prevent database corruption 
# when eventually saving back to Cloud Storage. See more in section 3. Note on Cloud Storage Fuse below
if os.path.exists("/opt/data/config.yaml"):
  shutil.copy("/opt/data/config.yaml", os.path.join(hermes_dir, "config.yaml"))
  print(f"Synced config.yaml -> {hermes_dir}/config.yaml", flush=True)
elif os.path.exists("/opt/data/.hermes/config.yaml"):
  shutil.copy("/opt/data/.hermes/config.yaml", os.path.join(hermes_dir, "config.yaml"))
  print(f"Synced config.yaml from .hermes -> {hermes_dir}/config.yaml", flush=True)

if os.path.exists("/opt/data/.env"):
  shutil.copy("/opt/data/.env", os.path.join(hermes_dir, ".env"))
  print(f"Synced .env -> {hermes_dir}/.env", flush=True)
elif os.path.exists("/opt/data/.hermes/.env"):
  shutil.copy("/opt/data/.hermes/.env", os.path.join(hermes_dir, ".env"))
  print(f"Synced .env from .hermes -> {hermes_dir}/.env", flush=True)

if os.path.exists("/opt/data/.hermes/state.db"):
  shutil.copy("/opt/data/.hermes/state.db", os.path.join(hermes_dir, "state.db"))
  print(f"Synced state.db -> {hermes_dir}/state.db (restored previous chats!)", flush=True)

# 3. Note on Cloud Storage Fuse
# Cloud Storage FUSE is optimized for object storage, but is not fully POSIX compliant.
# This means GCS lacks the byte-range file locking required by active caches and default SQLite (WAL mode) 
# which SQLite depends on to prevent data collisions.
# Without these locks, SQLite experiences database corruption and blocked I/O operations.
# To ensure stability, we route these active I/O processes to local container memory (/tmp).
# See section 5 Enable Autosave below on how /tmp is uploaded to Cloud Storage.
# Read more: https://cloud.google.com/storage/docs/cloud-storage-fuse/overview#differences-and-limitations
db_path = os.path.join(hermes_dir, "state.db")
try:
  import sqlite3
  conn = sqlite3.connect(db_path)
  conn.execute("PRAGMA journal_mode=TRUNCATE;")
  conn.close()
  print("Configured SQLite database to TRUNCATE mode for direct single-file persistence", flush=True)
except Exception as e:
  print(f"Warning: Failed to configure TRUNCATE mode: {e}", flush=True)

subprocess.run(["chmod", "-R", "777", "/tmp"], check=False)

# 4. Update system environment variables
# Hermes needs to know to look at the new local /tmp folders rather than defaulting to the mounted bucket.
env = dict(os.environ)
env["HOME"] = home_dir
env["HERMES_HOME"] = hermes_dir
env["PATH"] = "/opt/hermes/.venv/bin:/opt/hermes/bin:" + env.get("PATH", "")
env["PYTHONUNBUFFERED"] = "1"
env["HERMES_STATE_PATH"] = hermes_dir
env["HERMES_SKILLS_PATH"] = "/tmp/skills"
env["UV_CACHE_DIR"] = "/tmp/uv_cache"
env["XDG_CACHE_HOME"] = "/tmp/cache"
env["SQLITE_BUSY_TIMEOUT"] = "30000"
env["HERMES_ALLOW_ROOT_GATEWAY"] = "1"
env["HERMES_WORKSPACE"] = "/opt/data/workspace"
env["HERMES_WRITE_SAFE_ROOT"] = "/opt/data"

python_bin = "/opt/hermes/.venv/bin/python3"

# 5. Enable Autosave 
# Spawn a background worker thread to watch your local database and config files every 5 seconds.
# As you chat with your agent, this worker thread automatically copies the updated database content
# back to Cloud Storage to persist it.
def sync_to_gcs_loop():
  files_to_sync = ["state.db", "config.yaml", ".env"]
  last_mtimes = {}
  
  # Initialize last_mtimes
  for f in files_to_sync:
    path = os.path.join(hermes_dir, f)
    if os.path.exists(path):
      last_mtimes[f] = os.path.getmtime(path)
    else:
      last_mtimes[f] = 0
      
  while True:
    time.sleep(5)
    for f in files_to_sync:
      src_path = os.path.join(hermes_dir, f)
      if os.path.exists(src_path):
        try:
          mtime = os.path.getmtime(src_path)
          if mtime > last_mtimes.get(f, 0):
            dst_path = os.path.join("/opt/data/.hermes", f)
            shutil.copy2(src_path, dst_path)
            last_mtimes[f] = mtime
            print(f"Auto-saved {f} to GCS volume mount", flush=True)
        except Exception as e:
          print(f"Error auto-saving {f} to GCS: {e}", flush=True)

threading.Thread(target=sync_to_gcs_loop, daemon=True).start()


# 6. Launch the Hermes Gateway (the AI backend) and the Web Dashboard (the UI)
# These are launched as parallel processes, sending logs to Cloud Run via stdout & stderr
print("=== STARTING GATEWAY IN BACKGROUND ===", flush=True)
gw = subprocess.Popen(
    [python_bin, "-m", "hermes_cli.main", "gateway", "run"],
    env=env,
    cwd="/opt/data/workspace",
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=True,
    bufsize=1,
)

def stream_gw():
  for line in iter(gw.stdout.readline, ""):
    if line:
      print(f"[GATEWAY] {line.rstrip()}", flush=True)

threading.Thread(target=stream_gw, daemon=True).start()

print("=== STARTING DASHBOARD ON 0.0.0.0:8080 ===", flush=True)
sys.stdout.flush()

dash = subprocess.Popen(
    [
        python_bin,
        "-m",
        "hermes_cli.main",
        "dashboard",
        "--host",
        "0.0.0.0",
        "--port",
        "8080",
        "--skip-build",
    ],
    env=env,
    cwd="/opt/data/workspace",
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=True,
    bufsize=1,
)

for line in iter(dash.stdout.readline, ""):
  if line:
    print(f"[DASHBOARD] {line.rstrip()}", flush=True)

rc = dash.wait()
print(f"DASHBOARD EXITED WITH RETURN CODE: {rc}", flush=True)

while True:
  time.sleep(10)

৪. স্টার্টআপ স্ক্রিপ্ট ( start_hermes.sh ) তৈরি করুন।

স্থানীয়ভাবে start_hermes.sh তৈরি করুন।

#!/bin/sh
set -e
export PYTHONUNBUFFERED=1
exec python3 /opt/data/run_hermes.py

৫. ক্লাউড স্টোরেজে ফাইল আপলোড করুন

কনফিগারেশন ফাইলগুলো আপনার GCS বাকেটের রুটে কপি করুন:

gcloud storage cp config.yaml run_hermes.py start_hermes.sh gs://${BUCKET_NAME}/

৬. ক্লাউড রান ইনস্ট্যান্সগুলিতে হার্মিস স্থাপন করুন

আমরা কন্টেইনারটি ডেপ্লয় করার জন্য gcloud beta run instances deploy কমান্ডটি ব্যবহার করি। এই কমান্ডটিতে GCSFuse এবং কন্টেইনারের সীমাবদ্ধতা সংক্রান্ত জ্ঞাত সমস্যাগুলো সমাধানের জন্য নির্দিষ্ট কনফিগারেশন অন্তর্ভুক্ত রয়েছে।

আপনার সক্রিয় টার্মিনাল সেশনে এনভায়রনমেন্ট ভেরিয়েবলগুলো ( PROJECT_ID , REGION , BUCKET_NAME , SERVICE_ACCOUNT ) এক্সপোর্ট করা আছে কিনা, তা নিশ্চিত করুন।

ইনস্ট্যান্সটি স্থাপন করুন:

gcloud beta run instances deploy hermes-instance \
  --image nousresearch/hermes-agent:latest \
  --service-account ${SERVICE_ACCOUNT} \
  --command "/bin/sh" \
  --args "/opt/data/start_hermes.sh" \
  --port 8080 \
  --cpu 2 \
  --memory 4Gi \
  --ingress all \
  --no-invoker-iam-check \
  --add-volume name=hermes-storage,mount-path=/opt/data,type=cloud-storage,mount-options="uid=2000;gid=2000;file-mode=0777;dir-mode=0777;implicit-dirs",bucket=$BUCKET_NAME \
  --set-secrets "HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=hermes-dashboard-password:latest" \
  --set-env-vars "PYTHONUNBUFFERED=1,VERTEX_PROJECT_ID=$PROJECT_ID,VERTEX_LOCATION=global,HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin,HERMES_ALLOW_ROOT_GATEWAY=1,HERMES_WORKSPACE=/opt/data/workspace,HERMES_WRITE_SAFE_ROOT=/opt/data" \
  --region $REGION \
  --project $PROJECT_ID

উপরে অন্তর্ভুক্ত গুরুত্বপূর্ণ কনফিগারেশনগুলো হলো:

  • --service-account : নির্দিষ্ট hermes-sa সার্ভিস অ্যাকাউন্টটি সংযুক্ত করে।
  • সুপারভাইজার স্ক্রিপ্ট : start_hermes.sh কাস্টম পাইথন সুপারভাইজার run_hermes.py কল করে, যা SQLite লকিং সীমা এবং ক্যাশিং সংক্রান্ত সমস্যাগুলোকে GCS FUSE থেকে সরিয়ে স্থানীয় tmpfs-এ পাঠিয়ে দেয়।
  • --set-secrets : সিক্রেট ম্যানেজার থেকে সরাসরি ক্রেডেনশিয়ালগুলো এনভায়রনমেন্ট ভেরিয়েবলে যুক্ত করে।

৭. হার্মিস ওয়েব UI-এর মাধ্যমে সরাসরি যোগাযোগ করুন

একবার ডেপ্লয় করা হয়ে গেলে, আপনি তৈরি হওয়া .run.app URL-এ আপনার ড্যাশবোর্ড অ্যাক্সেস করতে পারবেন। অথেনটিকেশনের জন্য অনুরোধ করা হলে, ইউজারনেম হিসেবে admin এবং পাসওয়ার্ড হিসেবে আপনার ${DASHBOARD_PASSWORD} লিখুন।

আপনার এজেন্টের সাথে কথা বলুন

এজেন্টটি কাজ করছে কিনা তা নিশ্চিত করতে আপনি echo "hello" এর মতো কমান্ড ব্যবহার করে দেখতে পারেন।

স্থায়ী স্টোরেজ পরীক্ষা করুন

আপনি এজেন্টকে জিজ্ঞাসা করে আপনার গুগল ক্লাউড বাকেটে স্থায়ী স্টোরেজ পরীক্ষা করতে পারেন।

Write "hello world" to a file named hello.txt in your workspace.

তারপর আপনার শেল-এ, রান করে ফাইলটি লেখা হয়েছে কিনা তা যাচাই করতে পারেন।

gcloud storage cat gs://$BUCKET_NAME/workspace/hello.txt

সবশেষে, আপনার চ্যাট এবং ফাইলগুলি নতুন ক্লাউড রান ইনস্ট্যান্সগুলিতে সংরক্ষিত আছে কিনা তা যাচাই করতে (যেহেতু একটি ক্লাউড রান ইনস্ট্যান্স ডিফল্টরূপে স্বয়ংক্রিয় রিস্টার্ট পলিসি সহ ৭ দিন পর্যন্ত একটানা চলতে পারে), আপনি আগের মতোই gcloud beta run instances deploy কমান্ডটি পুনরায় চালাতে পারেন। তাহলে আপনি আপনার চ্যাট সেশনগুলি দেখতে পাবেন। এবং আপনি আপনার এজেন্টকে জিজ্ঞাসা করতে পারেন।

Read the contents of the file hello.txt in your workspace.

এবং আপনি "হ্যালো ওয়ার্ল্ড" দেখতে পাবেন।

৮. পরিষ্কার করা

এই কোডল্যাবে ব্যবহৃত রিসোর্সগুলির জন্য আপনার গুগল ক্লাউড অ্যাকাউন্টে চার্জ হওয়া এড়াতে:

  1. ক্লাউড রান ইনস্ট্যান্সটি মুছে ফেলুন :
    gcloud beta run instances delete hermes-instance --region ${REGION} --quiet
    
  2. সিক্রেট ম্যানেজারের গোপনীয় তথ্য মুছে ফেলুন :
    gcloud secrets delete hermes-dashboard-password --quiet
    
  3. ক্লাউড স্টোরেজ বাকেট মুছুন :
    gcloud storage rm -r gs://${BUCKET_NAME}
    
  4. ডেডিকেটেড সার্ভিস অ্যাকাউন্ট মুছুন :
    gcloud iam service-accounts delete ${SERVICE_ACCOUNT} --quiet
    

৯. উপসংহার

অভিনন্দন! আপনি ক্লাউড স্টোরেজ দ্বারা সমর্থিত ক্লাউড রান ইনস্ট্যান্সে হার্মিস এজেন্টের একটি সুরক্ষিত ও সম্পূর্ণ স্থায়ী ইনস্ট্যান্স সফলভাবে স্থাপন করেছেন!

আপনি যা শিখেছেন

  • ক্লাউড রান ইনস্ট্যান্সগুলিতে হার্মিস এজেন্ট কীভাবে স্থাপন করবেন
  • GCSFuse ব্যবহার করে কীভাবে ক্লাউড রান ইনস্ট্যান্সে ক্লাউড স্টোরেজ বাকেট মাউন্ট করবেন।
  • GCSFuse-এর ফাইল-লকিং সীমাবদ্ধতা এড়ানোর জন্য কীভাবে নিরাপদে SQLite এবং ক্ষণস্থায়ী ক্যাশে কনফিগার করবেন।