# Repo map — Containers & Local Dev

Ground truth for the course. Every lesson anchors its concepts to these files. Paths are from
the repo root (`/Users/trinhdung98/Manabie/backend`). Line numbers drift — treat them as
"look near here," and re-grep if they've moved.

---

## 1. The image recipe — `local/Dockerfile.development`

The one Dockerfile that builds the image every backend service runs locally and in CI. It is a
**multi-stage** build ([Docker: multi-stage](https://docs.docker.com/build/building/multi-stage/)).

| Stage | Line | What it does |
|---|---|---|
| `ARG GO_VERSION=1.25.8` | 1 | Pinned Go version (build arg). |
| `curl-ready` | 4–5 | `debian:12.11` + `curl` — a tiny base to download tools. |
| `download-grpc-health-probe` | 9–12 | Fetches the gRPC health-check binary. |
| `download-modd` | 16–21 | Fetches **modd** — the live-reload runner (see below). |
| `no-op` | 28–35 | Base image with **no** `/server` binary; `ENTRYPOINT ["modd"]`. Used to deploy a placeholder fast, then hot-swap the real binary in. |
| `developer` | 40–59 | The real runtime image: copies `build/server`, `migrations`, `features`, `build/bdd.test`, `build/stub`, etc. `ENTRYPOINT ["/server"]`. |
| `j4-runner` | 62–69 | A separate small image for the `j4` tool. |
| `download-delve` + `developer-debug` | 73–84 | Adds the Delve debugger for `-m debug` mode (Part 2). |

**Key teaching points:**
- **One binary, many services.** The Go monorepo compiles to a single `build/server`; the
  image just copies it in (line 50). Which service runs is chosen by `command:` at runtime, not
  baked into the image.
- **Base image is `debian:12.11`** (not `scratch` / distroless) — because dev/CI need
  `bash`, `curl`, `netcat`, `cron` for debugging and BDD tests (line 44).
- **`modd`** is why the `no-op` stage exists: deploy a no-op container, then `docker cp` the
  freshly built binary in and let modd restart it — the fast local reload loop (Part 2).

Other Dockerfiles in `local/`: `Dockerfile.waitforit` (a sleep-forever gate container),
`Dockerfile.kafkaconnect` (Debezium/Kafka-Connect image). The repo has ~30 Dockerfiles total.

---

## 2. The local stack (docker-compose) — `local/docker-compose.*.yaml`

Five compose files, split by role. `local/run.bash` picks which to use.

| File | Role |
|---|---|
| `docker-compose.infra.yaml` | Infra deps: **`db`** (Postgres 15), `unleash`, NATS/Kafka, etc. |
| `docker-compose.infra.macos.yaml` | macOS variant (host-networking differences). |
| `docker-compose.service.yaml` | The ~25 **backend services** (`image: new-backend:locally`). |
| `docker-compose.service.macos.yaml` | macOS variant of the service stack. |
| `docker-compose.migration.yaml` | One-shot DB-migration containers (run before services). |
| `docker-compose.decrypt.yaml` | Sidecar that decrypts SOPS secrets for local use. |

### The `db` service — `docker-compose.infra.yaml:2–63`
The clearest Part-1 anchor for **every** Compose concept:
- **image** (line 3): `asia.gcr.io/.../postgres:15-bookworm-...` — pulled from a registry.
- **ports** (5–6): `"5432:5432"` — host:container port mapping (Lesson 3).
- **environment** (7–8): `POSTGRES_PASSWORD` (Lesson 3).
- **networks** (9): `local-manabie-network` — the shared bridge network; services reach the DB
  as hostname `db` (Lesson 3–4).
- **volumes** (11–13): a **named** volume `postgres_data` (data persists) **and** a **bind
  mount** of migrations from the host (Lesson 3).
- **healthcheck** (14–19): `pg_isready` — how Compose knows the DB is *ready*, not just
  *started* (Lesson 4).
- **command** (20–63): Postgres tuning flags incl. `wal_level=logical` — the CDC hook the
  Kafka/Postgres courses relied on.

### A service — `docker-compose.service.macos.yaml` (e.g. `fink`, lines 20–39)
- **image** (21): `new-backend:locally` — the image built from `Dockerfile.development`.
- **depends_on** (23–25): waits for `wait-for-it-service` to be **`service_healthy`**
  (Lesson 4 — startup ordering done right).
- **volumes** (28–31): bind-mount configs + service-credential JSON + SOPS-encrypted secrets.
- **command** (34–39): the args that select *which* service/job this container runs.

### The gate — `wait-for-it-service` (`docker-compose.service.macos.yaml:2–18`)
A single container whose **healthcheck** (`nc -z db 5432 && curl unleash/health`) gates every
service via `depends_on: service_healthy`. This is the repo's answer to "Compose doesn't wait
for readiness by default" ([Compose startup order](https://docs.docker.com/compose/how-tos/startup-order/)).

---

## 3. The orchestrator script — `local/run.bash`

The command the learner already types daily. Wraps `docker compose`.

- **modes** (`case "$COMMAND"` ~line 307): `start` / `stop` / `restart` / `bdd` / `cron`.
- **build + hot-swap** (~135–161): builds the Go binary, then `docker cp ../build/server
  local-$service-1:/server` (line 161) into the running container — the fast reload loop
  (no image rebuild).
- **migration mode** (~142–148): brings up `docker-compose.migration.yaml`, waits for it, then
  starts the service.
- CLI reference lives in `.claude/commands/run-local.md`.

---

## 4. The OTHER local stack (kind + Skaffold) — Part 2

The repo also runs the whole backend inside a **real Kubernetes cluster locally**, via
[kind](https://kind.sigs.k8s.io/) (Kubernetes-in-Docker) + [Skaffold](https://skaffold.dev/docs/).

- **`deployments/sk.bash`** — the entry point. Targets kube-context **`kind-kind`**, namespace
  **`local-manabie-backend`** (lines 14–15). Flags: `-s` (deploy), `-b` (build only), `-i`,
  `-d` (delete); args after `--` pass through to `skaffold`.
- **`skaffold.local.yaml`** — `apiVersion: skaffold/v4beta9`; `requires:` composes ~15 other
  `skaffold.*.yaml` files (emulator, backbone, backend, gateway, monitoring, …). `build.local`
  has `push:false`, `useDockerCLI:true`.
- **~27 `skaffold.*.yaml`** at repo root, one per stack area (`backend`, `gateway`, `airflow`,
  `monitoring`, `runner`, …).

**docker-compose stack vs kind+Skaffold stack** — two independent ways to run locally:

| | `local/run.bash` (compose) | `deployments/sk.bash` (kind+Skaffold) |
|---|---|---|
| Runtime | Docker Compose | real Kubernetes (kind) |
| Speed | fast, lightweight | slower, production-like |
| Deploys via | `docker compose` | Skaffold → Helm → kubectl |
| Best for | day-to-day service dev | testing K8s/Helm/Istio behaviour |

---

## 5. The env × org model — Part 2 (bridge to the Helm/K8s courses)

Config is a **matrix of environment × organisation**. Seen in the Helm values filenames, e.g.
`deployments/helm/backend/bob/`:

- **Envs:** `local`, `stag`, `uat`, `dorp` (disaster-recovery/prod), `prod`, `trial`.
- **Orgs (tenants):** `manabie`, `jprep`, `tokyo`, `aic`, `ga`, `renseikai`, `synersia`, …
- **Values files:** `{env}-{org}-values.yaml` — e.g. `local-manabie-values.yaml`,
  `prod-tokyo-values.yaml`, `stag-jprep-values.yaml`, `dorp-aic-values.yaml`.
- Each `(env, org)` pair maps to a Kubernetes **namespace** like `{env}-{org}-backend`
  (e.g. `local-manabie-backend`, matching `sk.bash`'s default).

This matrix is *the* organising principle of the whole `deployments/` domain — it recurs in
Helm (Course 3), CI/CD (Course 5), and Terraform (Course 7). Part 2 introduces it here.

---

## Anchors cheat-list (for quick citation)

| Concept | File:line |
|---|---|
| Multi-stage build, base image | `local/Dockerfile.development:1,4,28,40` |
| Copy the one binary in | `local/Dockerfile.development:50` |
| Live-reload entrypoint (modd) | `local/Dockerfile.development:35` |
| Port mapping | `local/docker-compose.infra.yaml:5-6` |
| Named + bind volume | `local/docker-compose.infra.yaml:11-13` |
| Healthcheck | `local/docker-compose.infra.yaml:14-19` |
| depends_on: service_healthy | `local/docker-compose.service.macos.yaml:23-25` |
| The service image | `local/docker-compose.service.macos.yaml:21` |
| Hot-swap binary | `local/run.bash:161` |
| kind context + namespace | `deployments/sk.bash:14-15` |
| Skaffold compose-of-configs | `skaffold.local.yaml:5-19` |
| env×org values | `deployments/helm/backend/bob/*-values.yaml` |
