Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Stack allows building and deployment of a system of related containerized applications as a single "stack". Transparently deploy to local Docker, Podman or to remote Kubernetes.

![Building and deploying a three-container todo app — front end, API and PostgreSQL — to local Docker with stack](./docs/images/quickstart.gif)

_An unedited recording of the [Docker quick start](#docker) below. Regenerate it with `./demo/record-quickstart.sh`._

## What is Stack good for?
Stack is useful for a wide category of software applications including those that have a web app component, back-end services and optionally a database: web systems.
Development of such a system ususally begins on a laptop where it's quick and easy to prototype concepts, try things out and iterate.
Expand Down Expand Up @@ -34,6 +38,8 @@ stack init \
--map-ports-to-host localhost-same

# create the deployment from the config
# (the parent directory must already exist)
mkdir -p ~/deployments
stack deploy --spec-file todo.yml --deployment-dir ~/deployments/todo-docker

# start / status / logs / stop
Expand Down Expand Up @@ -61,6 +67,8 @@ stack init \
--config REACT_APP_API_URL=https://example-todo.myexample.com/api/todos

# create the deployment from the config
# (the parent directory must already exist)
mkdir -p ~/deployments
stack deploy --spec-file todo.yml --deployment-dir ~/deployments/todo-k8s

# push image tags for this deployment to the image registry used by Kubernetes
Expand Down
143 changes: 143 additions & 0 deletions demo/quickstart.tape
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# VHS tape for the README quick-start animation.
#
# Renders docs/images/quickstart.gif — a real, unfaked run of the Docker quick
# start from README.md against the example todo stack.
#
# Run it via ./demo/record-quickstart.sh, which prepares the caches this tape
# assumes. Running `vhs demo/quickstart.tape` directly will still work, but the
# build and clone steps may take minutes instead of seconds.
#
# Requires: vhs, ttyd, ffmpeg, docker, and the `stack` CLI on PATH.

Output docs/images/quickstart.gif

Set Shell bash
Set FontSize 14
Set Width 1100
Set Height 640
Set Padding 18
Set Framerate 20
Set TypingSpeed 20ms
Set Theme "Catppuccin Mocha"

# --- off-camera setup ------------------------------------------------------
# Work in a scratch dir, with a scratch repo base dir, so the demo never
# collides with real clones or drops files in the repo. The `cd` and `export`
# apply to the recorded shell only; vhs resolves Output against its own cwd.
Hide
Type "cd /tmp/stack-demo && export STACK_REPO_BASE_DIR=/tmp/stack-demo/repos"
Enter
Type `PS1='\[\e[38;5;114m\]▸\[\e[0m\] '`
Enter
Type "clear"
Enter
Show

# --- clone and build -------------------------------------------------------
# Real clone (~2s) and a real container build (~8s, warm layer cache).
Type "stack fetch repo bozemanpass/example-todo-list"
Enter
Wait+Line@180s /^▸$/
Sleep 1s

# Show what the stack actually is before building it, so the rest of the
# recording has some context: three services, two built here, one stock
# postgres, and how requests reach them.
Type "stack chart --stack todo --format text"
Enter
Wait+Line@60s /^▸$/
Sleep 3s

# --quiet-build drops the docker build log, which is many screens of BuildKit
# step output that would swamp a short animation. It keeps the per-container
# progress lines. The README quick start deliberately shows the plain command;
# the flag is here only to keep the recording legible.
Type "stack prepare --stack todo --quiet-build"
Enter
Wait+Line@600s /^▸$/
Sleep 2s

Type "clear"
Enter

# --- confirm the build prerequisites are in place --------------------------
# Answers "did I already build the images?" without re-running a prepare that
# might take minutes — the question you have after stepping away from the build.
Type "stack check --stack todo"
Enter
Wait+Line@120s /^▸$/
Sleep 2s

# --- generate the deployment spec, then the deployment ---------------------
# init and deploy are near-instant and quiet, so pause a beat to let them be read.
Type "stack init --stack todo --output todo.yml --deploy-to compose --map-ports-to-host localhost-same"
Enter
Wait+Line@120s /^▸$/
Sleep 1s

# The whole generated spec is only 20 lines, so show all of it: the proxy
# routes, the host port mappings and the db volume are the interesting part.
Type "cat todo.yml"
Enter
Wait+Line@60s /^▸$/
Sleep 2500ms

Type "mkdir -p ~/deployments"
Enter
Wait+Line@60s /^▸$/

Type "stack deploy --spec-file todo.yml --deployment-dir ~/deployments/todo-demo"
Enter
Wait+Line@180s /^▸$/
Sleep 1500ms

Type "clear"
Enter

# --- run it ----------------------------------------------------------------
# ~12s: containers created, db comes up healthy, then the backend starts.
Type "stack manage --dir ~/deployments/todo-demo start"
Enter
Wait+Line@300s /^▸$/
Sleep 1500ms

Type "stack manage --dir ~/deployments/todo-demo status"
Enter
Wait+Line@120s /^▸$/
Sleep 2s

# --- prove the whole system works, not just that a port answers ------------
# The web app is served...
Type `curl -s -o /dev/null -w "frontend HTTP %{http_code}\n" localhost:3000`
Enter
Wait+Line@60s /^▸$/
Sleep 1500ms

# ...and the API is backed by a working database. The list starts empty
# because the recorder removes the deployment (and its db volume) each run.
Type "curl -s localhost:5000/ | jq -c ."
Enter
Wait+Line@60s /^▸$/
Sleep 2s

# The id comes back from postgres, so a successful round trip proves the
# backend and the database, not merely that the web server responded.
Type `curl -s -X POST localhost:5000/ \`
Enter
Type ` -H 'Content-Type: application/json' \`
Enter
Type ` -d '{"title":"Deploy without a PaaS"}' | jq -c '{id, title, completed}'`
Enter
Wait+Line@60s /^▸$/
Sleep 2500ms

Type `curl -s localhost:5000/ | jq -c '.[] | {id, title, completed}'`
Enter
Wait+Line@60s /^▸$/
Sleep 3s

# --- and tear down ---------------------------------------------------------
Type "stack manage --dir ~/deployments/todo-demo stop"
Enter
Wait+Line@180s /^▸$/
Sleep 3s
81 changes: 81 additions & 0 deletions demo/record-quickstart.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# Record docs/images/quickstart.gif from demo/quickstart.tape.
#
# The recording is a real run of the README quick start — no output is faked.
# To keep it short enough to work as a README animation, this script prepares
# state off camera so that each recorded command does genuine work but returns
# quickly:
#
# * STACK_REPO_BASE_DIR points at a scratch dir, so the recorded `stack fetch`
# performs a real clone. This also means your existing clones under
# ~/.config/stack/repos are never touched.
# * The todo image *tags* are removed, but Docker's build layer cache is left
# intact, so the recorded `stack prepare` runs a real build (~8s) rather
# than reporting "existing-image" or rebuilding from scratch (minutes).
#
# Requires: vhs, ttyd, ffmpeg, docker, and the `stack` CLI on PATH.

set -euo pipefail

cd "$(dirname "$0")/.."
REPO_ROOT="$PWD"
# Deliberately outside $HOME: `stack fetch` prints the absolute clone destination,
# so a scratch dir under $HOME would put the recorder's username in the recording.
SCRATCH="/tmp/stack-demo"
DEPLOYMENT="$HOME/deployments/todo-demo"

export STACK_REPO_BASE_DIR="$SCRATCH/repos"

missing=()
for tool in vhs ttyd ffmpeg docker stack; do
command -v "$tool" >/dev/null || missing+=("$tool")
done
if [ ${#missing[@]} -gt 0 ]; then
echo "error: not on PATH: ${missing[*]}" >&2
echo " ffmpeg, ttyd: sudo apt-get install -y ffmpeg ttyd" >&2
echo " vhs: https://github.com/charmbracelet/vhs/releases" >&2
exit 1
fi

echo "==> Cleaning previous demo state"
if [ -d "$DEPLOYMENT" ]; then
# Best effort — the deployment may already be stopped.
stack manage --dir "$DEPLOYMENT" stop || true
rm -rf "$DEPLOYMENT"
fi
rm -rf "$SCRATCH"
mkdir -p "$SCRATCH/repos"
# Note: ~/deployments is deliberately NOT created here. `stack deploy` requires
# the parent of --deployment-dir to already exist, and the tape creates it on
# camera so the recording matches the README.
rmdir "$HOME/deployments" 2>/dev/null || true

echo "==> Warming the build cache (off camera)"
cd "$SCRATCH"
stack fetch repo bozemanpass/example-todo-list
stack prepare --stack todo

echo "==> Dropping image tags so the recorded build is real but cached"
docker images --format '{{.Repository}}:{{.Tag}}' \
| grep -E '^bozemanpass/todo-' \
| xargs -r docker rmi >/dev/null 2>&1 || true

echo "==> Resetting the scratch clone so the recorded fetch is a real clone"
rm -rf "$SCRATCH/repos"
mkdir -p "$SCRATCH/repos"

echo "==> Recording"
cd "$REPO_ROOT"
vhs demo/quickstart.tape

echo "==> Cleaning up"
if [ -d "$DEPLOYMENT" ]; then
stack manage --dir "$DEPLOYMENT" stop || true
rm -rf "$DEPLOYMENT"
fi
rm -rf "$SCRATCH"

echo
echo "Wrote $REPO_ROOT/docs/images/quickstart.gif"
ls -lh "$REPO_ROOT/docs/images/quickstart.gif"
Binary file added docs/images/quickstart.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.