−25%

על Windows בתשלום שנתי, עד 31.10. לחבילות

EQVPS
התחלה

VPS לתצוגות מקדימות של סוכני קוד: כל ענף שהסוכן דוחף מקבל כתובת משלו

סוכן קוד סיים ענף ואומר שהוא עובד. תנו לו שרת שבו אפשר לבדוק את זה: כל ענף נבנה לקונטיינר ונפתח בתת-דומיין HTTPS משלו מאחורי סיסמה, ומפתח ה-SSH של הסוכן יכול רק להפעיל ולעצור תצוגות מקדימות.

סוכן קוד אומר שהפיצ'ר עובד. אולי זה נכון. הבדיקות שכתב עוברות, וזה מוכיח פחות ממה שכולם היו רוצים. מה שאתם באמת רוצים הוא לפתוח את זה בדפדפן לפני שזה מתקרב למשתמשים, ובלי לבקש מהסוכן לגעת בייצור.

סביבות תצוגה מקדימה פותרות את זה, ולא צריך לשם כך פלטפורמה. VPS אחד עם IPv4 משלו, ‏Docker, ‏Caddy ושני סקריפטים קצרים נותנים לכל ענף כתובת משלו כמו https://feature-login-form.preview.example.com, מאחורי סיסמה, ונבנים מחדש בכל push. הסוכן מקבל מפתח SSH שיכול להפעיל ולעצור תצוגות מקדימות ושום דבר אחר.

בנינו את זה על שרת בדיקות ועברנו על כל המחזור לפני שכתבנו: יצירה, פתיחה, push נוסף, פירוק, ניסיון לנצל את המפתח לרעה.

מה צריך

  • VPS עם IPv4 ציבורי. על AI-Agent-IP נכנסות כמה תצוגות מקדימות בבת אחת.
  • רשומת DNS מסוג wildcard: *.preview.example.com ← כתובת ה-IP של השרת.
  • מאגר האפליקציה שלכם עם Dockerfile שמגיש בפורט 8080. כל שפה מתאימה.
  • Ubuntu 24.04 או Debian 12/13 עם apt install -y docker.io caddy git.

שבטו את המאגר פעם אחת אל /srv/app עם מפתח פריסה לקריאה בלבד.

שני הסקריפטים

/usr/local/bin/preview-up בונה ענף ומעלה אותו לאוויר:

#!/usr/bin/env bash
# preview-up <branch> — build the branch and open it at https://<name>.preview.example.com
set -euo pipefail
BRANCH="$1"
NAME=$(printf '%s' "$BRANCH" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-40)
DIR=/srv/previews/$NAME
git -C /srv/app fetch --quiet origin "$BRANCH"
rm -rf "$DIR"; git -C /srv/app worktree prune
git -C /srv/app worktree add --quiet --force --detach "$DIR" FETCH_HEAD
docker build --quiet -t "preview-$NAME" "$DIR" >/dev/null
docker rm -f "preview-$NAME" >/dev/null 2>&1 || true
docker run -d --name "preview-$NAME" --restart unless-stopped --memory 512m -p 127.0.0.1::8080 "preview-$NAME" >/dev/null
PORT=$(docker port "preview-$NAME" 8080/tcp | head -1 | awk -F: '{print $NF}')
cat > "/etc/caddy/previews/$NAME.caddy" <<CADDY
$NAME.preview.example.com {
	import preview_common
	reverse_proxy 127.0.0.1:$PORT
}
CADDY
systemctl reload caddy
echo "https://$NAME.preview.example.com"

/usr/local/bin/preview-down מסיר אותו:

#!/usr/bin/env bash
# preview-down <branch> — remove the branch preview
set -euo pipefail
NAME=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-40)
docker rm -f "preview-$NAME" >/dev/null 2>&1 || true
docker image rm "preview-$NAME" >/dev/null 2>&1 || true
rm -f "/etc/caddy/previews/$NAME.caddy"; rm -rf "/srv/previews/$NAME"; git -C /srv/app worktree prune
systemctl reload caddy

כמה בחירות כאן מכוונות. הקונטיינרים מפרסמים רק על 127.0.0.1 עם פורט אקראי, כך ששום דבר לא מגיע אליהם אלא דרך Caddy. זה חשוב, כי פורטים ש-Docker מפרסם עוקפים את ufw. לכל תצוגה מקדימה מגבלת זיכרון של 512 MB, כדי שענף אחד שיצא משליטה לא יפיל את האחרים. והרצה חוזרת של אותו ענף בונה אותו מחדש במקום: push, הרצת up, רענון.

‏Caddy: קובץ אחד לכל תצוגה מקדימה, סיסמה אחת לכולן

mkdir -p /etc/caddy/previews /srv/previews
chmod +x /usr/local/bin/preview-up /usr/local/bin/preview-down
caddy hash-password --plaintext 'choose-a-review-password'

הכניסו את ה-hash אל /etc/caddy/Caddyfile:

(preview_common) {
	basicauth {
		reviewer PASTE-THE-HASH-HERE
	}
}
import /etc/caddy/previews/*.caddy

ואז systemctl reload caddy. עכשיו כל תצוגה מקדימה נמצאת מאחורי אותה התחברות, כך שענף חצי גמור עם פלט דיבאג לא עומד חשוף באינטרנט. בגרסאות חדשות של Caddy ההנחיה נכתבת גם basic_auth; ‏basicauth עדיין עובד בשתיהן.

המפתח של הסוכן: up ו-down, לא יותר

הסוכן צריך להפעיל תצוגות מקדימות, לא להיות הבעלים של השרת. צרו משתמש deploy וסקריפט שער שמקבל בדיוק שתי פקודות:

#!/usr/bin/env bash
# /usr/local/bin/preview-gate — the only thing the agent's key may run
set -euo pipefail
read -r ACTION BRANCH EXTRA <<< "${SSH_ORIGINAL_COMMAND:-}"
[[ -z "${EXTRA:-}" && "${BRANCH:-}" =~ ^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$ && "$BRANCH" != *..* ]] || { echo "usage: up|down <branch>" >&2; exit 2; }
case "$ACTION" in
  up)   exec sudo /usr/local/bin/preview-up "$BRANCH" ;;
  down) exec sudo /usr/local/bin/preview-down "$BRANCH" ;;
  *)    echo "usage: up|down <branch>" >&2; exit 2 ;;
esac
useradd -m -s /bin/bash deploy
echo 'deploy ALL=(root) NOPASSWD: /usr/local/bin/preview-up, /usr/local/bin/preview-down' > /etc/sudoers.d/preview
chmod 440 /etc/sudoers.d/preview && visudo -cf /etc/sudoers.d/preview
chmod 755 /usr/local/bin/preview-gate

ב-/home/deploy/.ssh/authorized_keys, קשרו את המפתח הציבורי של הסוכן לסקריפט הזה:

restrict,command="/usr/local/bin/preview-gate" ssh-ed25519 AAAA... coding-agent

מהמכונה שבה הסוכן עובד:

ssh deploy@preview.example.com up feature/login-form
# https://feature-login-form.preview.example.com
ssh deploy@preview.example.com down feature/login-form

בבדיקה שלנו up בנה ופרסם את הענף, ‏up שני אחרי commit חדש הגיש את הגרסה החדשה, ו-down הסיר את הקונטיינר ואת האתר. ניסיונות של cat /etc/shadow, ‏up x; id, שמות ענפים כמו -x או a/../b, או התחברות אינטראקטיבית עם המפתח קיבלו כולם רק usage: up|down <branch>. שם ענף חייב להתחיל באות או בספרה ואינו יכול להכיל ...

חיבור לסוכן

ב-Claude Code או ב-Codex מספיקה שורה אחת בקובץ ההנחיות של הפרויקט:

אחרי push של ענף, הרץ ssh deploy@preview.example.com up <branch> והכנס את הכתובת שהודפסה לסיכום שלך. הרץ down כשהענף מוזג או ננטש.

אם ה-CI שלכם כבר רץ בכל push, קראו לאותה פקודה משם והרחיקו את המפתח לגמרי מסביבת הסוכן. זו האפשרות המחמירה יותר, ואנחנו היינו בוחרים בה לכל דבר שמעבר לפרויקט אישי.

למה לשים לב

  • דיסק. כל בנייה משאירה שכבות אימג'. ‏cron שבועי עם docker image prune -f שומר על זה בשליטה; ‏down כבר מוחק את האימג' של התצוגה המקדימה עצמה.
  • ענפים שאף אחד לא סוגר. סוכנים פותחים יותר ענפים מבני אדם. משימת cron שמונה קונטיינרים preview-* ישנים משבוע ומריצה עליהם preview-down חוסכת לכם עשרים תצוגות מקדימות נשכחות.
  • סודות. תצוגות מקדימות צריכות לרוץ עם פרטי גישה לבדיקות. סיסמת ה-basic auth מרחיקה זרים, אבל היא לא הופכת לגיטימי לחבר תצוגה מקדימה למסד הנתונים של הייצור.
  • זמן בנייה. אימג'ים נבנים על השרת, כך שבנייה כבדה מתחרה על CPU עם התצוגות המקדימות שרצות. אם בנייה לוקחת דקות, בנו ב-CI ומשכו את האימג'.

הסוכן עצמו לא חייב לגור על השרת הזה. אם הוא גר עליו, ראו VPS ל-Claude Code, וכדי לתת לו להזמין ולתחזק את השרת בעצמו, חברו אותו לשרת ה-MCP של EQVPS. אותה מכונה יכולה לארח גם את שער ה-MCP של הצוות. להרצת קוד לא אמין בלי שום שרת יש ארגזי חול, והתוכניות עם IPv4 משלהן מתחילות ב-$8 לחודש.

שאלות נפוצות

למה לא לתת לסוכן לפרוס ישר לייצור?

כי „הבדיקות עוברות” ו„זה עובד” אינם אותו דבר, והסוכן צריך להיות האחרון שמחליט על כך. תצוגה מקדימה נותנת לאדם משהו ללחוץ עליו לפני שמשהו מגיע למשתמשים, במחיר של פקודה נוספת אחת.

איזו תוכנית מתאימה?

תלוי כמה תצוגות מקדימות רצות במקביל. ‏AI-Agent-IP ‏($15 לחודש, 4 vCPU, ‏4 GB RAM, דיסק 40 GB) מחזיק בנוחות כמה אפליקציות ווב קטנות עם מגבלת 512 MB לכל אחת. החלק הכבד ביותר הוא בניית Docker, אז אם האימג'ים גדולים, עקבו אחרי הדיסק והזיכרון.

אני צריך תעודת wildcard?

לא. ‏Caddy משיג בעצמו תעודה נפרדת לכל תת-דומיין של תצוגה מקדימה. רק רשומת ה-DNS היא wildcard. ‏Let's Encrypt מאפשר כ-50 תעודות חדשות לדומיין רשום בשבוע, די והותר אלא אם הסוכן יוצר מאות ענפים.

הסוכן יכול להרוס את השרת עם מפתח ה-SSH שלו?

לא עם המפתח הזה. הוא מוגבל לפקודה אחת שמקבלת רק „up <ענף>” או „down <ענף>” עם שם ענף בטוח. גישת shell, ארגומנטים נוספים והעברת פורטים נדחים.

זה עובד על תוכנית NAT?

לא. תצוגות מקדימות חייבות להיות נגישות בפורטים 80 ו-443 בתת-דומיינים משלהן, וזה דורש IPv4 ציבורי. תוכניות NAT מעבירות רק את פורט ה-SSH.

תגובות

אין עדיין תגובות. היו הראשונים.

השאירו תגובה

התגובות עוברות מודרציה לפני שהן מופיעות.