Déploiement du code bac à sable
La capacité d'exécution de code Python d'Aivory est fournie par un service bac à sable indépendant. Cette page décrit l'architecture sidecar, la signification de chaque configuration clé dans composer, les limites de sécurité et la façon de déployer bac à sable indépendamment sur une autre machine. Python bac à sable。
Architecture : contrôle sidecar + conteneur de session unique
Le bac à sable se compose de deux miroirs :
| Miroir | Rôle |
|---|---|
ghcr.io/hjxwz123/aivory-sandbox-sidecar | Service de contrôle (sidecar) : recevoir des demandes HTTP en arrière-plan, conduire Docker daemon à créer / récupérer des conteneurs de session |
ghcr.io/hjxwz123/aivory-sandbox | Miroir d’exécution : un conteneur verrouillé pour chaque session, qui exécute réellement le code utilisateur |
┌──────────┐ POST /sessions /exec /files ┌───────────────┐ docker exec ┌────────────────┐
│ app 后端 │ ────────────────────────────► │ sidecar 控制面 │ ────────────► │ 会话容器 │
└──────────┘ SANDBOX_BASE_URL └───────────────┘ │ aivory-sandbox │
└────────────────┘
Sidecar n'exécute pas le code de l'utilisateur lui-même. /var/run/docker.sock Docker Daemon, un conteneur pour chaque session. /workspace Entre plusieurs exécutions de la même session, les paquets installés par pip et les fichiers générés sont toujours en ligne avec l'interprète de code ChatGPT.
Le programme est basé sur l’hôte. /var/run/docker.sock Dans un conteneur sidecar, le conteneur de session est en fait créé par l'hôte daemon. Si votre environnement n'autorise pas l'hébergement d'un socket d'hôte, vous pouvez également fournir à sidecar un dind (docker:dind) daemon indépendant pour le coût d'une couche supplémentaire avec des coûts de stockage. De toute façon, le sidecar doit avoir accès à un daemon Docker, ce qui est une exigence rigide.
Le miroir d'exécution contient un stack de données (numpy, panda, sccipy, scikit-learn, matplotlib, seaborn, plotly, etc.), une bibliothèque de traitement de documents (python-pptx, python-docx, openpyxl, reportlab, weasyprint, etc.) et des polices Noto Sans CJK.
Bac à sable intégré : configuration zéro par défaut
Production de composite Il est liésandbox des services, docker compose up -d Une commande est tirée avec bac à sable, sans aucune configuration supplémentaire:
- ** Ne publiez aucun port hébergeur **: uniquement
apppar le privéinternalL’accès au réseau (SANDBOX_BASE_URL=http://sandbox:8000Les réseaux sociaux ne touchent jamais. - ** La clé API interne **:
app与sandboxLes deux services lisent le mêmeSANDBOX_API_KEY(avoué par défautaivory-bundled-sandboxParce que bac à sable n'est pas exposé à l'extérieur, cette valeur par défaut est disponible; mais chaque fois que vous avez l'intention d'exposer le port bac à sable à tout autre réseau, il doit être remplacé par une valeur aléatoire forte. - ** Démarrer le miroir. **:
SANDBOX_PULL_ON_START=1Laisser sidecar froid démarrer avant de tirer l'image du temps d'exécution, le contrôle de la santéstart_period: 120sC’est pour cette raison qu’il est réservé. - ** La douce dépendance **:
app不depends_onbac à sable. bac à sable n'est disponible que lorsque le code exécute des erreurs, le reste de l'application fonctionne normalement.
Composer les éléments clés individuellement
Ce qui suit est composésandbox Les variables environnementales du service sont répertoriées comme étant les valeurs par défaut pour produire compose :
| Variable | Valeur par défaut | Expliquer |
|---|---|---|
SANDBOX_API_KEY | aivory-bundled-sandbox | La clé d’identification de Bearer doit app Sidecar n'a pas de clé refusant de démarrer, l'évaluation utilise une comparaison de temps constante |
SANDBOX_IMAGE | ghcr.io/hjxwz123/aivory-sandbox:latest | Le miroir de chaque séance. |
SANDBOX_PULL_ON_START | 1 | Tirez l'image du temps d'exécution avant le démarrage pour vous assurer que la première exécution n'échoue pas |
SANDBOX_NETWORK | none | Réseau de conteneurs de session. none Réseau totalement désactivé ; bridge Connexion à Internet (en fonctionnement) pip install) |
SANDBOX_MEMORY | 2g | Limite de mémoire pour les conteneurs de session individuels |
SANDBOX_CPUS | 1 | Limite de la CPU pour un conteneur de session unique |
SANDBOX_MAX_SESSIONS | 16 | Limite du nombre de conteneurs de session survivants en même temps |
SANDBOX_EXEC_TIMEOUT_CAP_MS | 600000 | Durée maximale d’exécution d’une seule exécution (10 minutes) |
SANDBOX_IDLE_TTL_CAP_SECONDS | 86400 | Limite d'exploitation dur pour les fenêtres de recyclage gratuites (24 heures) |
SANDBOX_READ_ONLY_ROOTFS | 1 | Le système de fichiers racine des conteneurs de session ne lit que, le disque de protection remplit les attaques |
SANDBOX_WORKSPACE_SIZE | 512m | Seulement en mode lecture /workspace Il est possible d'écrire la taille de tmpfs |
SANDBOX_LOCAL_STORAGE_DIR | /var/lib/aivory/sandbox-archives | Catalogue d'archives d'espaces de travail locaux pour la pérennisation de la configuration zéro avec des volumes permanents |
SANDBOX_API_KEY : authentification partagée avec l’application
sidecar est exposé à la capacité de "Drive Host Docker", équivalent à l'hôte RCE, donc ** L’autorisation est obligatoire. **:key pour sidecar temporaire refuse directement le démarrage (la seule exception est la configuration explicite) SANDBOX_ALLOW_NO_AUTH=1 Il s’agit d’un environnement de développement local). app Services et sandbox Les services doivent configurer la même valeur pour générer une clé forte :
openssl rand -hex 24
SANDBOX_NETWORK : le défaut par défaut, le coût de la connexion
Définitivement none Le conteneur de session n’a pas de réseau, ni le code utilisateur. pip install Aucune demande ne peut être envoyée.Modifier bridge Le code post peut être connecté, mais le coût est clair:
Le bac à sable du réseau signifie que le code utilisateur peut envoyer les données de la session (les fichiers téléchargés, les résultats intermédiaires générés) à n'importe quelle adresse accidentelle, ou bien scanner activement et attaquer les services disponibles dans le conteneur bac à sable de votre réseau intérieur. none。
Limite des ressources : MEMORY / CPUS / MAX_SESSIONS
SANDBOX_MEMORY (avoué par défaut 2g) et SANDBOX_CPUS (avoué par défaut 1 limitation des conteneurs de session individuels; SANDBOX_MAX_SESSIONS (avoué par défaut 16 Limiter le nombre total de sessions survivantes en même temps.Considérez la capacité de l'hôte dans le pire des cas: MAX_SESSIONS x MEMORY est la quantité totale de mémoire que le conteneur de session peut occuper. SANDBOX_MAX_CONCURRENT_EXECS (par défaut 4) limiter le nombre d'exécutions globales simultanées, SANDBOX_PIDS_LIMIT (par défaut 256) Défense contre les bombes de fork.
Le contrôle sidecar lui-même est également limité à compose mem_limit: 1g / pids_limit: 512 L'archivage permet de sauvegarder jusqu'à 200 MiB d'espace de travail tar packs dans la mémoire (SANDBOX_MAX_ARCHIVE_BYTES), mem_limit Il est nécessaire de maintenir une quantité supérieure à cette valeur.
Deux « têtes dures » : EXEC_TIMEOUT_CAP_MS et IDLE_TTL_CAP_SECONDS
Ces deux variables sont ** Le plafond des opérations. ** Pour travailler avec les paramètres réglables de la console d'administration :
- L’exécution d’un délai supplémentaire d’exécution par l’administrateur en arrière-plan (
sandbox_exec_timeout_secElles seront retenues.SANDBOX_EXEC_TIMEOUT_CAP_MS(Dans les 600000ms par défaut, c’est-à-dire 10 minutes). - La fenêtre de récupération libre est configurée par l'administrateur (
sandbox_idle_ttl_secElles seront retenues.SANDBOX_IDLE_TTL_CAP_SECONDSDans les 86400s par défaut, c'est-à-dire 24 heures. Lorsque l'arrière-plan n'est pas lancé, Sidecar recycle les sessions gratuites pendant 30 minutes.
C’est-à-dire que l’administrateur peut faire des raccourcis sous le plafond, mais qu’il n’est pas toujours possible de maintenir le plafond fixé dans compose.
Protection du disque : READ_ONLY_ROOTFS / WORKSPACE_SIZE
SANDBOX_READ_ONLY_ROOTFS=1 (Ouvrir par défaut) Le système de fichiers racine du conteneur de session ne peut que lire, il n'y a que trois endroits où l'on peut écrire, et tous tmpfs avec une taille maximale:
| La voie | Petite source | Définitivement |
|---|---|---|
/workspace | SANDBOX_WORKSPACE_SIZE | 512m |
/tmp | SANDBOX_TMPFS_SIZE | 256m |
Utilisateur $HOME | SANDBOX_TMPFS_SIZE | 256m |
Par conséquent, il n'est pas possible de remplir le disque de l'hôte par une seule session, quelle que soit la façon dont les fichiers sont écrits. /workspace/uploads/ Le code produit est écrit. /workspace/outputs/ Tous sont soumis WORKSPACE_SIZE Restreindre le temps nécessaire pour un espace de travail plus grand (comme le traitement de grands ensembles de données) SANDBOX_WORKSPACE_SIZE Attention, les tmpfs occupent la mémoire.
SANDBOX_LOCAL_STORAGE_DIR : Permanence de l’archivage de l’espace de travail
Lorsque le conteneur de session est recyclé (excès de temps ou destruction explicite), le /workspace Les archives tar sont stockées ; la même conversation est automatiquement récupérée lors de la prochaine exécution du code. ** Conversation ID ** Par conséquent, même si le conteneur de session a été changé, le contenu de l'espace de travail peut également être recyclé.
SANDBOX_LOCAL_STORAGE_DIR Spécifiez le répertoire d'archives locales et composez-le sur le nom sandbox-archives Sur les volumes persistants, la persistance de configuration zéro n'est pas requise pour S3/OSS/MinIO.
- Cette variable ** Il peut être configuré à partir de variables environnementales. ** et n'accepte jamais les valeurs provenant de la requête ou de la console d'administration (sidecar s'exécute à la racine et détient docker.sock, permettant à distance de spécifier un chemin d'écriture équivaut à l'ouverture d'une fenêtre d'écriture d'hôte).
- L’archive locale n’est pas valable si elle est vide et la récupération est perdue (reaped = gone).
- ** Applicable uniquement aux points **: Les volumes Docker normaux ne sont pas partagés entre copies et le déploiement de copies multiples doit être remplacé par le backend S3/OSS.
- L’archivage est le meilleur effort : plus de 200 MiB d’espaces de travail sautent l’archivage et enregistrent les journaux, et l’échec de l’archivage/la récupération n’entraîne pas l’échec de la demande d’exécution.
Console d'administration
En plus des variables d'environnement compose, certaines actions peuvent être effectuées dans la console d'administration. Définir le site En ligne, il n’est pas nécessaire de redémarrer :
| Positionnement arrière | Rôle | Obligation |
|---|---|---|
Le délai d’exécution est limité (sandbox_exec_timeout_sec) | Durée maximale d'exécution de chaque code | 被 SANDBOX_EXEC_TIMEOUT_CAP_MS Régime |
Temps de récupération (sandbox_idle_ttl_sec) | Combien de temps après la récupération du conteneur ? | 被 SANDBOX_IDLE_TTL_CAP_SECONDS Régime |
Le stockage à l’arrière (storage_provider) | Où se trouvent les archives de la zone de travail ? | local (Résumé de l’objet du texte) s3 / aliyun_oss |
L’adresse et la clé du bac à sable sandbox_base_url / sandbox_api_key) | Bac à sable pour couvrir les variables environnementales | Les variables environnementales entrent en vigueur |
Choix du stockage s3 Remplir les endpoints, buckets et crédits. ** Services compatibles MinIO et S3 ** Le même choix s3 Lorsque vous remplissez l'endpoint défini, vous passez automatiquement à l'adresse path-style + SigV4 signature sans commutateur supplémentaire.
Frontières sécurisées
bac à sable est isolé au niveau du conteneur et chaque conteneur de session est rempli d'un ensemble de verrouillage :
- ** Fonctionne sans root. **,
--cap-drop ALL,--security-opt no-new-privileges。 - ** Impossible sans réseau ** (
--network none)。 - ** Lisez uniquement le système de fichiers. **+ une limite supérieure de tmpfs (voir ci-dessus), en plus de la limite de memory/cpu/pids/nofile.
- Exécution dans la même séance ** séries ** Parallèlement, la distribution globale est limitée; stdout/stderr est réduit à 32 KB; les fichiers de fichier de produit sont limités à 20 Mo, jusqu'à 20 Mo à la fois, pour un total de 50 Mo.
- Possibilité de : passer
SANDBOX_SECCOMP_PROFILEFixez le profil seccomp sur le conteneur de session (le parcours doit être lu dans le conteneur sidecar) pour restreindre encore la zone d'attaque du noyau.
L’architecture sidecar doit être capable d’exécuter Docker Daemon. /var/run/docker.sock L’équivalent root sur l’hébergeur : toute personne ayant accès au service sidecar est équivalente à l’hébergeur.
- ** N’exposez jamais les ports sidecar au réseau public ** Le déploiement intégré est déjà fait (pas de ports publiés); liaison de l'interface intranet ou VPN lors du déploiement indépendant.
SANDBOX_API_KEYIl doit être une valeur aléatoire forte (à l'exception de la valeur par défaut du réseau privé pour le stack intégré).- L'environnement de production recommande de placer un avant le socket nu. docker-socket-proxy Le conteneur crée/start/exec/kill/inspect avec l’image pull, les autres API sont rejetées, puis le sidecar est lancé.
DOCKER_HOST: tcp://socket-proxy:2375Il n’y a pas de socket.
Il s'agit d'un isolant de niveau conteneur, suffisant pour soutenir le déploiement d'un seul appareil, mais ** pas ** gVisor/microVM.Le protocole HTTP de sidecar est un contrat stable, et les déploiements avec des exigences de sécurité plus élevées peuvent remplacer l'exécution par gVisor, Firecracker, etc. app Aucune modification du côté n’est nécessaire.
Si Sidecar est redémarré, il sera détecté automatiquement. aivory.sandbox=1 Les conteneurs de stockage étiquetés, continuent de suivre et de recycler selon le TTL, ne laissent pas de conteneurs orphelins.
Déploiement indépendant : bac à sable dans une autre machine
L'exécution de code est une charge concentrée en CPU/mémoire et le démantèlement du bac à sable sur une machine indépendante permet d'éviter le pillage des ressources avec l'application principale. le service bac à sable dispose d'un entrepôt public et d'un miroir indépendants, qui peuvent être tirés sans nécessité de construire :
** Déploiement sur le bac à sable **
git clone https://github.com/hjxwz123/aivory-sandbox.git
cd aivory-sandbox
export OWNER=hjxwz123
export SANDBOX_API_KEY=$(openssl rand -hex 24)
printf 'SANDBOX_API_KEY=%s\n' "$SANDBOX_API_KEY" # 保存这个值
docker compose pull
docker compose up -d
Un compose indépendant affiche le sidecar sur l'hôte. 48217 Port (il reste 8000 dans le conteneur). vérification:
curl -H "Authorization: Bearer $SANDBOX_API_KEY" http://localhost:48217/healthz
retourner {ok, docker, image} C’est prêt.
** 2 – L’application se fait par le passé**
Dans l’application principale .env Modifier les deux variables et supprimer (ou arrêter de démarrer) les paramètres intégrés de la base sandbox Les services :
SANDBOX_BASE_URL=http://<沙箱机内网地址>:48217
SANDBOX_API_KEY=<第 1 步生成的同一个值>
Il est également possible de remplir les variables environnementales directement dans la console d'administration. sandbox_base_url / sandbox_api_key La valeur d’arrière-plan a la priorité sur les variables environnementales.
La clé de la clé de la clé de la clé (aivory-bundled-sandbox Ceci n'est acceptable que sous la condition « bac à sable sans port, accessible uniquement sur le réseau privé ». Une fois que bac à sable écoute le port de la carte réseau réelle, toute personne qui reçoit la clé peut conduire le Docker de l'hôte. openssl rand -hex 24 Créer une nouvelle clé ; utiliser un pare-feu / un groupe de sécurité48217 Limitez l’accès au serveur d’application principal et essayez d’utiliser des liens intranet ou VPN entre les deux machines.
Vérification de défaillance
| phénomène | Vérifier la direction |
|---|---|
| Le premier code a échoué. | L’écran ne s’est pas arrêté lors du démarrage à froid. docker compose logs sandbox L’inspection médicale est réservée à 120 personnes. start_period |
| Les examens sont toujours malsains. | Sidecar n'est pas disponible sur Docker Daemon /var/run/docker.sock L’hébergeur et l’hébergeur Daemon. |
| Sidecar déclenche le retrait | SANDBOX_API_KEY Sidecar est conçu pour démarrer sans clé. |
Dans le code pip installéchec | Définitivement SANDBOX_NETWORK=none Pas de réseau, évaluation des risques bridge |
| Le disque est rempli. | A frappé/workspace Les limites de tmpfs sont augmentées. SANDBOX_WORKSPACE_SIZE |
| Zone de travail perdue après le recyclage | SANDBOX_LOCAL_STORAGE_DIR Il n'est pas configuré ou le volume correspondant n'est pas suspendu; ou le fond storage_provider Vacuée |
| Les autres fonctions affectées | Il ne doit pas se produire : app Le bac à sable est une dépendance douce, seule la fonction d'exécution de code peut signaler des erreurs ; si l'ensemble est anormal, le problème est ailleurs. |