tree: 4561e15d35c9535f0c88903c7a8e71bedc00367b
  1. sandboxtypes/
  2. .gitignore
  3. cloud-init.yaml
  4. Dockerfile
  5. Dockerfile.gvisor
  6. Makefile
  7. metrics.go
  8. README.md
  9. sandbox.go
  10. sandbox_test.go
sandbox/README.md

Go Playground Sandbox Backend

This directory contains the source code and deployment configuration for the Go Playground sandbox backend.

The backend is responsible for executing untrusted Go code submitted by users. It uses gVisor (runsc) inside a Docker container on Container-Optimized OS (COS) VMs to ensure secure isolation.


Architecture Overview

The backend runs as a Managed Instance Group (MIG) of GCE VMs.

  • Host OS: Google Container-Optimized OS (COS).
  • Service: A systemd service (playsandbox.service) started on boot via cloud-init.
  • Runtime: The systemd service downloads gVisor (runsc), configures Docker to use it as a runtime, and starts the playground-sandbox container in privileged mode.
  • Execution: The sandbox container listens for compile/run requests from the frontend, spawns worker containers using the runsc runtime to execute the user code, and returns the output.

Local Development

You can build and run the sandbox backend locally for testing.

1. Build Docker Images

To build the sandbox server and gVisor runner images locally:

make docker TAG=local-test
make dockergvisor TAG=local-test

2. Run Locally

To run the sandbox server locally, mapped to port 8080:

make runlocal TAG=local-test

This starts the sandbox in dev mode. You can test it by sending a run request:

curl -v --data-binary @path/to/hello.go http://localhost:8080/run

Release Tagging Convention

When releasing new versions to production, follow this naming convention for both Docker tags and GCE resources to ensure traceability:

  • Format: cos-YYYYMMDD-HASH
    • YYYYMMDD: The current UTC date.
    • HASH: The 7-character short Git commit hash of the version being deployed (e.g., git rev-parse --short HEAD).
  • Example: cos-20260710-a67ec5c

Production Deployment (GCE)

Deployments are performed manually via the gcloud command. Always use a canary rollout when deploying changes to production.

Prerequisites

  1. Ensure your git working directory is clean.
  2. Get the short commit hash of HEAD:
    git rev-parse --short HEAD
    
    (We will use HASH as a placeholder below).

Step 1: Build and Push Production Images

Build and push the new images to the Google Container Registry (GCR) using the tagging convention:

make push TAG=cos-YYYYMMDD-HASH

Step 2: Generate the Cloud-Init Configuration

Generate the expanded cloud-init.yaml file, which embeds the new tag so the VMs pull the correct image on boot:

make cloud-init.yaml.expanded TAG=cos-YYYYMMDD-HASH

Step 3: Create a GCE Instance Template

Create a new instance template containing the updated metadata config. Replace <GCP_PROJECT> with your target GCP project:

gcloud compute instance-templates create play-sandbox-cos-YYYYMMDD-HASH \
    --project=<GCP_PROJECT> \
    --machine-type=e2-standard-8 \
    --network=golang \
    --no-address \
    --image-project=cos-cloud \
    --image-family=cos-stable \
    --metadata-from-file=user-data=cloud-init.yaml.expanded \
    --scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write

Step 4: Canary Rollout (Recommended)

Deploy the new template to 1 instance first to verify health under production load.

Replace <GCP_PROJECT>, <REGION>, and <MIG_NAME> with your GCE configuration. <LEGACY_TEMPLATE> is the template currently running in production.

gcloud compute instance-groups managed rolling-action start-update <MIG_NAME> \
    --project=<GCP_PROJECT> \
    --region=<REGION> \
    --version=template=<LEGACY_TEMPLATE> \
    --canary-version=template=play-sandbox-cos-YYYYMMDD-HASH,target-size=1 \
    --max-surge=3 \
    --max-unavailable=0

Note: For regional MIGs, max-surge must be at least equal to the number of zones (usually 3).

Step 5: Verify Canary Health

  1. Check the status of the instances in the MIG:
    gcloud compute instance-groups managed list-instances <MIG_NAME> \
        --region=<REGION> \
        --project=<GCP_PROJECT>
    
    Verify that the new instance is RUNNING and has ACTION=NONE.
  2. Check the serial console logs of the new instance to verify successful boot and service startup:
    gcloud compute instances get-serial-port-output <NEW_INSTANCE_NAME> \
        --zone=<ZONE> \
        --project=<GCP_PROJECT>
    
    Look for [ OK ] Started playsandbox.service. and ensure no repeated container restart loops are logged.
  3. Monitor production metrics/logs for any spike in timeouts or errors.

Step 6: Promote to 100%

Once the canary is verified healthy (typically after 1-2 hours of monitoring), promote the new template to 100% of the MIG:

gcloud compute instance-groups managed rolling-action start-update <MIG_NAME> \
    --project=<GCP_PROJECT> \
    --region=<REGION> \
    --version=template=play-sandbox-cos-YYYYMMDD-HASH \
    --max-surge=3 \
    --max-unavailable=0

Rollback Procedure

If critical issues are detected after a rollout, you can immediately revert the MIG to the previous stable template.

Run the rolling update command pointing to the last known-good template:

gcloud compute instance-groups managed rolling-action start-update <MIG_NAME> \
    --project=<GCP_PROJECT> \
    --region=<REGION> \
    --version=template=<LAST_KNOWN_GOOD_TEMPLATE> \
    --max-surge=3 \
    --max-unavailable=0

Verify the rollback progress using list-instances as described in the verification step.