Initialize Tilt-based local dev setup

This commit is contained in:
2026-05-04 06:45:23 +01:00
commit f48fb40559
12 changed files with 629 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
# Local environment
.direnv/
.envrc
.env
.env.*
!.env.example
# Nix build outputs
result
result-*
# Tilt local state
.tiltbuild/
.tiltignore.local
# Kubernetes local config dumps
kubeconfig
*.kubeconfig
# Go build/test artifacts
bin/
dist/
*.test
*.out
coverage.out
# Editor and OS noise
.DS_Store
.idea/
.vscode/
*.swp
+29
View File
@@ -0,0 +1,29 @@
# AGENTS.md
## Repo Shape
- This repo is a minimal Nix + Kind + Tilt local Kubernetes setup, not a general Go monorepo yet.
- The only app is `services/hello-world`, a Go 1.22 HTTP service built by `services/hello-world/Dockerfile` and deployed by `k8s/hello-world.yaml`.
- `Tiltfile` is the wiring source of truth for dev orchestration; `flake.nix` is the source of truth for available CLI dependencies.
## Commands
- Use `nix run .#setup-kind` before Tilt if the cluster/context may not exist; it creates/selects the `kind-openstaticfish` context.
- Use `nix run .#tilt -- up` instead of calling a bare `tilt` from outside `nix develop`; the wrapper adds `kind`, `kubectl`, and `docker` to `PATH` so Tilt can load images into Kind.
- Use `nix run .#tilt -- down` to remove Tilt-managed resources.
- Use `nix develop` when running repeated manual commands; inside it, `setup-kind`, `tilt`, `kubectl`, `docker`, and `go` are available.
- Use `nix flake show` as the quick flake sanity check after editing `flake.nix`.
## Gotchas
- Nix flakes only see git-tracked or staged files. After adding files referenced by the flake, Tiltfile, Dockerfile, or manifests, run `git add <file>` or `git add -A` before `nix run` verification.
- `Tiltfile` intentionally allows only `kind-openstaticfish` via `allow_k8s_contexts`; do not switch it to a broad context unless explicitly requested.
- Do not re-add `default_registry('localhost:5000')` unless a local registry is also created; this setup relies on Tilt loading images directly into Kind.
- The hello-world Dockerfile intentionally copies only `go.mod`; there is no `go.sum` because the app currently has no external Go dependencies.
- Docker must be running and reachable by the user before `setup-kind` or Tilt image builds can work.
## Verification
- For infra config changes, run `nix flake show`.
- For cluster/Tilt changes, the focused path is `nix run .#setup-kind` then `nix run .#tilt -- up`, followed by `curl http://localhost:8080`.
- There is no test, lint, formatter, CI, or pre-commit config in this repo yet; do not invent commands.
+221
View File
@@ -0,0 +1,221 @@
# OpenStaticFish MicroServices
Basic local Kubernetes development setup using Nix, Kind, Tilt, Docker, and a small Go hello-world service.
## Requirements
- Nix with flakes enabled
- Docker running and reachable by your user
- Git
All project CLI dependencies are provided by `flake.nix`, including:
- `tilt`
- `kind`
- `kubectl`
- `docker`
- `helm`
- `go`
## Quick Start
Create or select the local Kind cluster:
```bash
nix run .#setup-kind
```
Start Tilt:
```bash
nix run .#tilt -- up
```
Open the Tilt UI:
```text
http://localhost:10350
```
Test the hello-world service:
```bash
curl http://localhost:8080
```
Stop Tilt resources:
```bash
nix run .#tilt -- down
```
## Dev Shell
You can also enter the Nix development shell and run commands directly:
```bash
nix develop
```
Inside the shell:
```bash
setup-kind
tilt up
tilt down
kubectl get pods
docker ps
```
## Project Commands
### Nix Commands
```bash
nix develop
```
Enter the dev shell with all dependencies on `PATH`.
```bash
nix run .#setup-kind
```
Create the `openstaticfish` Kind cluster if needed, select the `kind-openstaticfish` Kubernetes context, and print cluster info.
```bash
nix run .#tilt -- up
```
Run Tilt with the required runtime tools available, including `kind`, `kubectl`, and `docker`.
```bash
nix run .#tilt -- down
```
Tear down Kubernetes resources managed by Tilt.
```bash
nix flake show
```
Validate and display flake outputs.
### Tilt Commands
```bash
tilt up
```
Build images, deploy Kubernetes manifests, start file watching, and open the Tilt UI.
```bash
tilt down
```
Remove deployed Tilt resources from the current Kubernetes context.
```bash
tilt logs
```
Stream aggregated logs from Tilt resources.
```bash
tilt status
```
Show current resource status.
```bash
tilt ui
```
Open the Tilt web UI.
### Kubernetes Commands
```bash
kubectl config use-context kind-openstaticfish
```
Select the project Kind cluster.
```bash
kubectl cluster-info
```
Verify Kubernetes connectivity.
```bash
kubectl get pods
```
List running pods.
```bash
kubectl logs -f deployment/hello-world-deployment
```
Stream logs from the hello-world deployment.
```bash
kubectl get svc
```
List Kubernetes services.
### Docker Commands
```bash
docker ps
```
List running containers, including Kind node containers.
```bash
docker build -t hello-world ./services/hello-world
```
Build the hello-world image manually outside Tilt.
## Layout
```text
.
├── flake.nix
├── Tiltfile
├── k8s/
│ └── hello-world.yaml
├── scripts/
│ └── setup-kind
└── services/
└── hello-world/
├── Dockerfile
├── go.mod
└── main.go
```
## Notes
Nix flakes only see files that are tracked by Git. If `nix run` says a path is not visible to Nix, stage the file:
```bash
git add <file>
```
For this repo setup, staging all project files is usually enough:
```bash
git add -A
```
The Tiltfile allows only the `kind-openstaticfish` Kubernetes context to avoid accidentally deploying to another cluster.
If Tilt reports that it cannot connect to Kubernetes, rerun:
```bash
nix run .#setup-kind
```
Then restart Tilt.
+42
View File
@@ -0,0 +1,42 @@
# --- OpenStaticFish MicroServices Tiltfile ---
#
# Nix-managed dependencies:
# Run `nix develop` or use `nix run .#tilt -- up` to enter the dev shell
# with tilt, kubectl, docker, and all other deps available.
#
# Quick reference:
# nix run .#setup-kind - Create/select local Kind cluster
# tilt up - Start all services (live reload enabled)
# tilt down - Tear down all deployed resources
# tilt ui - Open the Tilt web UI (default: http://localhost:10350)
# tilt logs - Stream aggregated logs
# tilt status - Show resource status
# --- Global Settings ---
# Nix command wrapper:
# nix run .#setup-kind
#
# This creates/selects the supported local Kind context below.
allow_k8s_contexts('kind-openstaticfish')
# --- Hello World Service ---
# Builds the Docker image and deploys the manifest with live reload
docker_build('hello-world', './services/hello-world')
k8s_yaml('./k8s/hello-world.yaml')
# Watch source files for live reload
k8s_resource(
workload='hello-world-deployment',
port_forwards='8080:8080',
labels=['hello-world'],
)
# --- Local Resources ---
# Run a simple health check as a local resource
local_resource(
'health-check',
cmd='curl -sf http://localhost:8080/health || echo "Service not ready"',
resource_deps=['hello-world-deployment'],
labels=['utility'],
)
Generated
+61
View File
@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1777826146,
"narHash": "sha256-wQ/iN5Zp5VIa3ebBibijPnLyKhor+xEbDy4d0goa9Zs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "73c703c22422b8951895a960959dbbaca7296492",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+117
View File
@@ -0,0 +1,117 @@
{
description = "OpenStaticFish MicroServices - Tilt development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
setup-kind = pkgs.writeShellApplication {
name = "setup-kind";
runtimeInputs = with pkgs; [ docker kind kubectl ];
text = ''
exec bash ${./scripts/setup-kind} "$@"
'';
};
tilt-dev = pkgs.writeShellApplication {
name = "tilt-dev";
runtimeInputs = with pkgs; [ docker kind kubectl tilt ];
text = ''
exec tilt "$@"
'';
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
# --- Core Dependencies ---
tilt
kubectl
kubernetes-helm
kind
docker
docker-compose
# --- Language Toolchain ---
go
# --- Utilities ---
curl
jq
yq-go
];
shellHook = ''
echo "🐟 OpenStaticFish MicroServices Dev Shell"
echo "-----------------------------------------"
# --- Tilt Commands ---
# Create/select the local Kind cluster first:
# setup-kind
# nix run .#setup-kind
#
# Start the development environment:
# tilt up
# nix run .#tilt -- up
#
# Tear down resources:
# tilt down
#
# View Tilt UI:
# tilt ui
#
# Check Tilt version:
# tilt version
# --- Kubernetes Commands ---
# Check cluster connection:
# kubectl cluster-info
#
# Select the project Kind cluster:
# kubectl config use-context kind-openstaticfish
#
# List running pods:
# kubectl get pods
#
# View service logs:
# kubectl logs -f deployment/hello-world
# --- Docker Commands ---
# Build images manually:
# docker build -t hello-world ./services/hello-world
#
# List running containers:
# docker ps
echo ""
echo "Available commands:"
echo " setup-kind - Create/select local Kind cluster"
echo " tilt up - Start dev environment"
echo " tilt down - Stop dev environment"
echo " kubectl ... - Interact with k8s cluster"
echo " docker ... - Interact with containers"
echo ""
'';
};
# --- Command wrappers ---
# Run with: nix run .#setup-kind
# Run with: nix run .#tilt -- up
apps = {
setup-kind = {
type = "app";
program = "${setup-kind}/bin/setup-kind";
};
tilt = {
type = "app";
program = "${tilt-dev}/bin/tilt-dev";
};
};
}
);
}
+60
View File
@@ -0,0 +1,60 @@
# --- Hello World Kubernetes Manifests ---
# Managed by Tilt - run `tilt up` to deploy
# Tear down with: `tilt down`
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-world-deployment
labels:
app: hello-world
spec:
replicas: 1
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello-world
image: hello-world
ports:
- containerPort: 8080
env:
- name: PORT
value: "8080"
resources:
requests:
memory: "32Mi"
cpu: "50m"
limits:
memory: "64Mi"
cpu: "100m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 2
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 2
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: hello-world-service
spec:
selector:
app: hello-world
ports:
- protocol: TCP
port: 8080
targetPort: 8080
type: ClusterIP
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
cluster_name="openstaticfish"
if ! docker info >/dev/null 2>&1; then
echo "Docker is not running or is not reachable. Start Docker, then rerun setup-kind."
exit 1
fi
if kind get clusters | grep -qx "$cluster_name"; then
echo "Kind cluster '$cluster_name' already exists."
else
kind create cluster --name "$cluster_name"
fi
kubectl config use-context "kind-$cluster_name"
kubectl cluster-info
+1
View File
@@ -0,0 +1 @@
hello-world
+14
View File
@@ -0,0 +1,14 @@
# --- Hello World Service Dockerfile ---
# Multi-stage build for minimal image size
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod ./
COPY . .
RUN CGO_ENABLED=0 go build -o /hello-world .
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
COPY --from=builder /hello-world /hello-world
EXPOSE 8080
ENTRYPOINT ["/hello-world"]
+3
View File
@@ -0,0 +1,3 @@
module github.com/openstaticfish/microservices/hello-world
go 1.22
+32
View File
@@ -0,0 +1,32 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Hello from OpenStaticFish!",
"service": "hello-world",
})
})
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "ok")
})
log.Printf("hello-world listening on :%s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}