Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

EC2 docker provider example

This example demonstrates running gopherstack as a backend (i.e. the "AWS API" lives in one process, your application / frontend / CI pipeline lives outside it) while the EC2 service is configured with the docker compute provider so RunInstances actually launches a real Docker container with a working sshd.

The launched container ships an OpenSSH daemon and the public key from the EC2 key pair the caller created is wired into /home/ec2-user/.ssh/authorized_keys, so callers can ssh ec2-user@<host> into a real shell.

Architecture

┌──────────────────────────────────────┐    ┌──────────────────────────┐
│ host (your laptop / CI runner)       │    │ docker daemon            │
│                                      │    │                          │
│  demo.sh ──► aws ec2 …                │    │  ┌────────────────────┐ │
│           ──► dig @127.0.0.1 -p 10053 │    │  │ gopherstack        │ │
│           ──► ssh -p 22000 ec2-user@… │    │  │  :8000 (HTTP API)  │ │
│                                      │    │  │  :10053 (DNS)      │ │
└────────┬─────────────────────────────┘    │  └─────┬──────────────┘ │
         │                                  │        │                 │
         │ host ports 8000 / 10053 / 22000+ │        ▼                 │
         └─────────────────────────────────►│  ┌────────────────────┐ │
                                            │  │ gopherstack-i-…    │ │
                                            │  │  (sshd, ec2-user)  │ │
                                            │  │  port 22 → host    │ │
                                            │  │       22000…22019  │ │
                                            │  └────────────────────┘ │
                                            └──────────────────────────┘

Key points:

  • demo.sh runs on the host, not inside the compose stack, so the same flow works for any frontend that talks to gopherstack over HTTP from a separate process.
  • gopherstack publishes three things on the docker host:
    • :8000 — the AWS-compatible HTTP API.
    • :10053/udp — the embedded DNS server, where the synthetic ec2-<id>.compute-1.amazonaws.com hostnames generated by the docker provider are registered. Frontends can resolve them with dig or by pointing their resolver at :10053.
    • 22000–22019 — a small SSH port range. The docker provider picks a free port from this range per launched instance and aws ec2 describe-instances exposes it as the gopherstack:ssh-port tag.

What the demo does

  1. aws ec2 create-key-pair --key-name ec2-docker-demo and saves the PEM.
  2. aws ec2 run-instances --image-id ami-… --instance-type t3.micro --key-name … to trigger a container launch through the docker compute provider.
  3. Polls aws ec2 describe-instances until State=running and the PublicDnsName plus the gopherstack:ssh-host / gopherstack:ssh-port tags are populated.
  4. dig @127.0.0.1 -p 10053 +short <PublicDnsName> to resolve the synthetic hostname through the embedded DNS server.
  5. ssh -p <ssh-port> -i …pem ec2-user@<resolved-ip> and runs echo "hello world from $(hostname)".
  6. aws ec2 terminate-instances … and delete-key-pair to clean up.

Run it

cd examples/ec2-docker
docker compose up -d --build
docker compose ps                  # wait for healthy
./demo.sh
docker compose down --remove-orphans

Expected output (truncated):

ec2-3b492e3c6823a.compute-1.amazonaws.com -> 127.0.0.1
sshd is up after 2s
hello world from i-3b492e3c6823a
=== SUCCESS: round-tripped through the docker-backed EC2 instance ===

demo.sh reads ENDPOINT, DNS_ADDR, DNS_PORT, KEY_NAME, KEY_FILE and AMI_ID from the environment if you want to point it at a remote gopherstack or change the key location.

How the frontend gets the SSH endpoint

DescribeInstances returns the AWS-style hostname and two gopherstack-specific tags so the integration is one HTTP call:

aws --endpoint-url http://localhost:8000 ec2 describe-instances \
  --instance-ids i-3b492e3c6823a \
  --query 'Reservations[0].Instances[0].{
    Dns:  PublicDnsName,
    Host: Tags[?Key==`gopherstack:ssh-host`]|[0].Value,
    Port: Tags[?Key==`gopherstack:ssh-port`]|[0].Value
  }'

You can either ssh by IP+port (the Host/Port tags) or by hostname+port after pointing your resolver at the embedded DNS:

# /etc/resolv.conf snippet on the calling machine:
#   nameserver 127.0.0.1
#   port 10053
ssh -p 22000 -i key.pem ec2-user@ec2-3b492e3c6823a.compute-1.amazonaws.com

Configuration knobs

The provider is configured via environment variables / CLI flags:

Flag Env var Default Description
--ec2-provider EC2_PROVIDER inmemory Set to docker to enable the docker compute provider.
--ec2-docker-image EC2_DOCKER_IMAGE amazonlinux:2 Docker image used for new instances.
--ec2-docker-network EC2_DOCKER_NETWORK (empty) Docker network the launched containers attach to (empty = default bridge).
--ec2-docker-ssh-host-ip EC2_DOCKER_SSH_HOST_IP 127.0.0.1 Host IP that mapped sshd ports bind to (use 0.0.0.0 to expose externally).
--ec2-docker-ssh-port-min EC2_DOCKER_SSH_PORT_MIN 0 Lower bound of the host TCP port range used to map sshd (0 = let Docker pick).
--ec2-docker-ssh-port-max EC2_DOCKER_SSH_PORT_MAX 0 Upper bound of the host TCP port range used to map sshd.
--dns-addr DNS_ADDR (empty) Bind address for the embedded DNS server (e.g. :10053). Empty = disabled.
--dns-resolve-ip DNS_RESOLVE_IP 127.0.0.1 IP address synthetic hostnames resolve to.