diff options
Diffstat (limited to 'docs/handbook/ofborg/deployment.md')
| -rw-r--r-- | docs/handbook/ofborg/deployment.md | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/docs/handbook/ofborg/deployment.md b/docs/handbook/ofborg/deployment.md new file mode 100644 index 0000000000..4a9497b0c3 --- /dev/null +++ b/docs/handbook/ofborg/deployment.md @@ -0,0 +1,413 @@ +# Tickborg — Deployment + +## Overview + +Tickborg can be deployed via **NixOS modules**, **Docker Compose**, or manual +systemd units. The preferred method is the NixOS module defined in +`service.nix`, which orchestrates all eight binaries as individual systemd +services. + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `service.nix` | NixOS module — systemd services | +| `docker-compose.yml` | Full-stack Docker Compose | +| `flake.nix` | Nix flake — package + dev shell | +| `example.config.json` | Reference configuration file | + +--- + +## NixOS Deployment + +### Module Structure (`service.nix`) + +```nix +{ config, pkgs, lib, ... }: +let + cfg = config.services.tickborg; + tickborg = cfg.package; +in +{ + options.services.tickborg = { + enable = lib.mkEnableOption "Enable tickborg CI services"; + + package = lib.mkOption { + type = lib.types.package; + description = "The tickborg package to use"; + }; + + configFile = lib.mkOption { + type = lib.types.path; + description = "Path to the tickborg config.json"; + }; + + logConfig = lib.mkOption { + type = lib.types.str; + default = "info"; + description = "RUST_LOG filter string"; + }; + + services = { + github-webhook-receiver = lib.mkEnableOption "webhook receiver"; + evaluation-filter = lib.mkEnableOption "evaluation filter"; + mass-rebuilder = lib.mkEnableOption "mass rebuilder (evaluation)"; + builder = lib.mkEnableOption "build executor"; + github-comment-filter = lib.mkEnableOption "comment filter"; + github-comment-poster = lib.mkEnableOption "comment poster"; + log-message-collector = lib.mkEnableOption "log collector"; + stats = lib.mkEnableOption "stats collector"; + }; + }; +} +``` + +### Per-Service Configuration + +Each service is toggled independently. A common template generates systemd +units: + +```nix +commonServiceConfig = binary: { + description = "tickborg ${binary}"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" "rabbitmq.service" ]; + wants = [ "network-online.target" ]; + + environment = { + RUST_LOG = cfg.logConfig; + RUST_LOG_JSON = "1"; + CONFIG_PATH = toString cfg.configFile; + }; + + serviceConfig = { + ExecStart = "${tickborg}/bin/${binary}"; + Restart = "always"; + RestartSec = "10s"; + DynamicUser = true; + + # Hardening + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + SystemCallFilter = [ "@system-service" "~@mount" ]; + }; +}; +``` + +### Applying the Module + +```nix +# In your NixOS configuration.nix or flake: +{ + imports = [ ./service.nix ]; + + services.tickborg = { + enable = true; + package = tickborg-pkg; + configFile = /etc/tickborg/config.json; + logConfig = "info,tickborg=debug"; + + services = { + github-webhook-receiver = true; + evaluation-filter = true; + mass-rebuilder = true; + builder = true; + github-comment-filter = true; + github-comment-poster = true; + log-message-collector = true; + stats = true; + }; + }; +} +``` + +### Service Management + +```bash +# View all tickborg services +systemctl list-units 'tickborg-*' + +# Restart a single service +systemctl restart tickborg-builder + +# View logs +journalctl -u tickborg-builder -f + +# Structured JSON logs (when RUST_LOG_JSON=1) +journalctl -u tickborg-builder -o cat | jq . +``` + +--- + +## Docker Compose Deployment + +### `docker-compose.yml` + +```yaml +services: + rabbitmq: + image: rabbitmq:3-management + ports: + - "5672:5672" + - "15672:15672" + environment: + RABBITMQ_DEFAULT_USER: tickborg + RABBITMQ_DEFAULT_PASS: tickborg + volumes: + - rabbitmq-data:/var/lib/rabbitmq + + webhook-receiver: + build: . + command: github-webhook-receiver + ports: + - "8080:8080" + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + depends_on: + - rabbitmq + + evaluation-filter: + build: . + command: evaluation-filter + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + depends_on: + - rabbitmq + + mass-rebuilder: + build: . + command: mass-rebuilder + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + - checkout-cache:/var/cache/tickborg + depends_on: + - rabbitmq + + builder: + build: . + command: builder + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + - checkout-cache:/var/cache/tickborg + depends_on: + - rabbitmq + + comment-filter: + build: . + command: github-comment-filter + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + depends_on: + - rabbitmq + + comment-poster: + build: . + command: github-comment-poster + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + depends_on: + - rabbitmq + + log-collector: + build: . + command: log-message-collector + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + - log-data:/var/log/tickborg + depends_on: + - rabbitmq + + stats: + build: . + command: stats + ports: + - "9090:9090" + environment: + CONFIG_PATH: /config/config.json + RUST_LOG: info + volumes: + - ./config:/config:ro + depends_on: + - rabbitmq + +volumes: + rabbitmq-data: + checkout-cache: + log-data: +``` + +### Running + +```bash +# Start all services +docker compose up -d + +# View webhook receiver logs +docker compose logs -f webhook-receiver + +# Scale builders +docker compose up -d --scale builder=3 + +# Stop everything +docker compose down +``` + +--- + +## Nix Flake + +### `flake.nix` Outputs + +```nix +{ + outputs = { self, nixpkgs, ... }: { + packages.x86_64-linux.default = /* tickborg cargo build */ ; + packages.x86_64-linux.tickborg = self.packages.x86_64-linux.default; + + devShells.x86_64-linux.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + cargo + rustc + clippy + rustfmt + pkg-config + openssl + ]; + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + }; + + nixosModules.default = import ./service.nix; + }; +} +``` + +### Building with Nix + +```bash +# Build the package +nix build + +# Enter dev shell +nix develop + +# Run directly +nix run .#tickborg -- github-webhook-receiver +``` + +--- + +## Environment Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `CONFIG_PATH` | `./config.json` | Path to configuration file | +| `RUST_LOG` | `info` | tracing filter directive | +| `RUST_LOG_JSON` | (unset) | Set to `1` for JSON-formatted logs | + +--- + +## Reverse Proxy + +The webhook receiver requires an HTTPS endpoint exposed to GitHub. Typical +setup with nginx: + +```nginx +server { + listen 443 ssl; + server_name ci.example.com; + + ssl_certificate /etc/letsencrypt/live/ci.example.com/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ci.example.com/privkey.pem; + + location /github-webhook { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # GitHub sends large payloads + client_max_body_size 25m; + } + + location /logs/ { + proxy_pass http://127.0.0.1:8081/; + } +} +``` + +--- + +## RabbitMQ Setup + +### Required Configuration + +```bash +# Create vhost +rabbitmqctl add_vhost tickborg + +# Create user +rabbitmqctl add_user tickborg <password> + +# Grant permissions +rabbitmqctl set_permissions -p tickborg tickborg ".*" ".*" ".*" +``` + +### Management UI + +Available at `http://localhost:15672` when using Docker Compose. Useful for +monitoring queue depths and consumer counts. + +--- + +## Health Checks + +Monitor these indicators: + +| Check | Healthy | Problem | +|-------|---------|---------| +| Queue depth `mass-rebuild-check-inputs` | < 50 | Evaluation filter slow/down | +| Queue depth `build-inputs-*` | < 20 | Builder slow/down | +| Consumer count per queue | ≥ 1 | No consumers (service down) | +| `stats` HTTP endpoint | 200 OK | Stats collector down | +| Webhook receiver `/health` | 200 OK | Webhook receiver down | + +### Systemd Watchdog + +Services configured with `Restart = "always"` will be automatically restarted +on crash. The 10-second `RestartSec` prevents restart loops on persistent +failures. |
