188 lines
5.1 KiB
Markdown
188 lines
5.1 KiB
Markdown
# Deployment
|
|
|
|
This repository owns the app-side deployment assets for `unrip`:
|
|
|
|
- application source
|
|
- Docker build inputs
|
|
- Kubernetes manifests under `deploy/k8s/base/`
|
|
- the Forgejo workflow under `.forgejo/workflows/deploy.yml`
|
|
|
|
The shared platform bootstrap lives in the separate infra/platform repository.
|
|
In the current local split, that repo is available as `../unrip3`.
|
|
|
|
## Ownership split
|
|
|
|
Platform repo responsibilities:
|
|
- Hetzner/OpenTofu provisioning
|
|
- cloud-init and k3s bootstrap
|
|
- shared registry
|
|
- Forgejo and the Forgejo runner
|
|
- cert-manager, Traefik, observability, and other cluster services
|
|
|
|
This repo responsibilities:
|
|
- app image build context
|
|
- app namespace manifests
|
|
- app runtime secret names
|
|
- app deployment workflow
|
|
|
|
## Deployment model
|
|
|
|
The intended production path is:
|
|
|
|
1. bootstrap the shared platform from the platform repo
|
|
2. create the `unrip` app secrets in the cluster
|
|
3. push this repo to Forgejo
|
|
4. let `.forgejo/workflows/deploy.yml` build and roll the app in-cluster
|
|
|
|
The image build happens inside Kubernetes via Kaniko. The operator pushes Git,
|
|
not Docker images.
|
|
|
|
## Platform prerequisite
|
|
|
|
Before deploying this repo, the platform repo should already have completed its
|
|
Hetzner bootstrap flow. From the current local split, the relevant operator docs
|
|
live in:
|
|
|
|
- `../unrip3/deploy/hetzner/README.md`
|
|
- `../unrip3/deploy/k8s/README.md`
|
|
- `../unrip3/docs/hetzner-self-hosted-ci-runbook.md`
|
|
|
|
After platform bootstrap, you should have:
|
|
|
|
- a reachable Forgejo instance
|
|
- a reachable shared registry
|
|
- a running Forgejo runner in the cluster
|
|
- `../unrip3/.state/hetzner/kubeconfig.yaml`
|
|
- `../unrip3/.state/hetzner/kubeconfig.incluster.yaml`
|
|
|
|
## One-time app namespace setup
|
|
|
|
Apply the namespace first:
|
|
|
|
```bash
|
|
kubectl apply -f deploy/k8s/base/namespace.yaml
|
|
```
|
|
|
|
Create the app runtime secret required by `near-intents-ingest`:
|
|
|
|
```bash
|
|
kubectl -n unrip create secret generic unrip-secrets \
|
|
--from-literal=NEAR_INTENTS_API_KEY=replace_me
|
|
```
|
|
|
|
Create the registry auth secret used both for image pulls and the in-cluster
|
|
Kaniko build job:
|
|
|
|
```bash
|
|
kubectl -n unrip create secret docker-registry unrip-registry-creds \
|
|
--docker-server=registry.<your-domain> \
|
|
--docker-username="$REGISTRY_USERNAME" \
|
|
--docker-password="$REGISTRY_PASSWORD"
|
|
```
|
|
|
|
Then apply the app manifests:
|
|
|
|
```bash
|
|
kubectl apply -k deploy/k8s/base
|
|
```
|
|
|
|
Notes:
|
|
|
|
- `deploy/k8s/base/unrip.yaml` references `unrip-secrets` and
|
|
`unrip-registry-creds`; they are not created by this repo.
|
|
- the checked-in image `ghcr.io/example/unrip:bootstrap` is only a placeholder.
|
|
The first real image tag is set by the Forgejo workflow or by a manual
|
|
`kubectl set image`.
|
|
|
|
## Required Forgejo repo settings
|
|
|
|
Required repo secret:
|
|
|
|
- `KUBECONFIG_B64`
|
|
|
|
Required repo variable:
|
|
|
|
- `REGISTRY_HOST`
|
|
|
|
Recommended repo variables when you do not want to rely on workflow defaults:
|
|
|
|
- `PROJECT_NAME`
|
|
- `PROJECT_NAMESPACE`
|
|
- `PROJECT_DEPLOYMENTS`
|
|
- `PROJECT_REGISTRY_SECRET_NAME`
|
|
|
|
Recommended values for this repo:
|
|
|
|
- `REGISTRY_HOST=registry.<your-domain>`
|
|
- `PROJECT_NAME=unrip`
|
|
- `PROJECT_NAMESPACE=unrip`
|
|
- `PROJECT_DEPLOYMENTS=near-intents-ingest,dummy-reactor,dummy-executor,dummy-consumer`
|
|
- `PROJECT_REGISTRY_SECRET_NAME=unrip-registry-creds`
|
|
|
|
`KUBECONFIG_B64` should be the base64-encoded contents of the in-cluster
|
|
kubeconfig produced by the platform repo, not the public workstation kubeconfig:
|
|
|
|
```bash
|
|
base64 -w0 ../unrip3/.state/hetzner/kubeconfig.incluster.yaml
|
|
```
|
|
|
|
The current workflow does not read `REGISTRY_USERNAME` or `REGISTRY_PASSWORD`
|
|
from Forgejo directly. Those values are still needed on the operator side when
|
|
creating the `unrip-registry-creds` Kubernetes secret.
|
|
|
|
## Seed the repo into Forgejo
|
|
|
|
Create the repository in Forgejo, add a `forgejo` remote for this repo, and
|
|
push `main`. For example:
|
|
|
|
```bash
|
|
git remote add forgejo https://git.<your-domain>/<owner>/$(basename "$PWD").git
|
|
git push forgejo HEAD:refs/heads/main
|
|
```
|
|
|
|
## Routine deploy flow
|
|
|
|
Once the repo exists in Forgejo and the repo settings above are configured:
|
|
|
|
```bash
|
|
git push forgejo main
|
|
```
|
|
|
|
On push to `main`, `.forgejo/workflows/deploy.yml` does the following:
|
|
|
|
1. loads kubeconfig from `KUBECONFIG_B64`
|
|
2. applies `deploy/k8s/base`
|
|
3. creates an in-cluster Kaniko Job in the `unrip` namespace
|
|
4. builds and pushes `REGISTRY_HOST/unrip:<git-sha>`
|
|
5. updates the four app deployments and waits for rollout
|
|
|
|
## Observe rollout
|
|
|
|
```bash
|
|
kubectl -n unrip get deploy,pods,pvc,job
|
|
kubectl -n unrip rollout status deploy/near-intents-ingest
|
|
kubectl -n unrip rollout status deploy/dummy-reactor
|
|
kubectl -n unrip rollout status deploy/dummy-executor
|
|
kubectl -n unrip rollout status deploy/dummy-consumer
|
|
```
|
|
|
|
Useful logs:
|
|
|
|
```bash
|
|
kubectl -n unrip logs deploy/near-intents-ingest -f
|
|
kubectl -n unrip logs deploy/dummy-reactor -f
|
|
kubectl -n unrip logs deploy/dummy-executor -f
|
|
kubectl -n unrip logs deploy/dummy-consumer -f
|
|
kubectl -n unrip logs job/redpanda-topic-bootstrap
|
|
```
|
|
|
|
## Local development
|
|
|
|
Local iteration remains separate from the production path:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
docker compose up -d --build
|
|
```
|
|
|
|
That path is for local testing. Production rollout is Forgejo + Kubernetes.
|