diff --git a/.env b/.env.example similarity index 100% rename from .env rename to .env.example diff --git a/.gitignore b/.gitignore index 4f4d87b..6f1a9f1 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,8 @@ Thumbs.db .nx/cache .nx/workspace-data + +.env.prod +.env + +.unikraft diff --git a/bun.lockb b/bun.lockb index aff0b2e..558ccf8 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docker-compose.yml b/docker-compose.yml index b3a0c35..de4e7a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,9 +22,11 @@ services: - '8082:8082' - '9092:9092' - '19644:9644' + volumes: + - redpanda:/var/lib/redpanda/data restate: - image: docker.io/restatedev/restate:1.0 + image: docker.io/restatedev/restate:1.0.2 ports: - '8080:8080' - '9070:9070' @@ -32,5 +34,6 @@ services: - 'RESTATE_CONFIG=/etc/config/restate.toml' volumes: - ./infra/restate/config.toml:/etc/config/restate.toml + - restate:/var/lib/restate extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/infra/postgres/Dockerfile b/infra/postgres/Dockerfile new file mode 100644 index 0000000..c1d7ed7 --- /dev/null +++ b/infra/postgres/Dockerfile @@ -0,0 +1,15 @@ +FROM postgres:16.4-alpine3.19 AS base + +FROM base AS ext-build +RUN apk add --no-cache gcc make musl-dev llvm15-dev clang15 unzip +ADD https://github.com/kraftcloud/pg_ukc_scaletozero/archive/refs/heads/stable.zip /tmp/src.zip +RUN mkdir /src && \ + unzip /tmp/src.zip -d /tmp/extract && \ + mv /tmp/extract/pg_ukc_scaletozero-*/* /src +RUN cd /src && \ + make && \ + make install DESTDIR=/out + +FROM base +COPY --from=ext-build /out/usr/local/lib/postgresql/pg_ukc_scaletozero.so /usr/local/lib/postgresql/pg_ukc_scaletozero.so +ADD wrapper.sh /usr/local/bin/wrapper.sh diff --git a/infra/postgres/Kraftfile b/infra/postgres/Kraftfile new file mode 100644 index 0000000..bd319e0 --- /dev/null +++ b/infra/postgres/Kraftfile @@ -0,0 +1,14 @@ +spec: v0.6 + +name: postgres + +runtime: base-compat:latest + +labels: + cloud.unikraft.v1.instances/scale_to_zero.policy: "idle" + cloud.unikraft.v1.instances/scale_to_zero.stateful: "true" + cloud.unikraft.v1.instances/scale_to_zero.cooldown_time_ms: 1000 + +rootfs: ./Dockerfile + +cmd: ["wrapper.sh", "docker-entrypoint.sh", "postgres", "-c", "shared_preload_libraries='pg_ukc_scaletozero'"] diff --git a/infra/postgres/deploy.sh b/infra/postgres/deploy.sh old mode 100644 new mode 100755 index e69de29..1bbe8d0 --- a/infra/postgres/deploy.sh +++ b/infra/postgres/deploy.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh +kraft cloud volume create --name ftgo-postgres --size 256 + +kraft cloud deploy \ + --name ftgo-postgres \ + -M 1024 \ + -p 5432:5432/tls \ + -v ftgo-postgres:/var/lib/postgresql \ + -e POSTGRES_DATABASE=$DATABASE_NAME \ + -e POSTGRES_USER=$DATABASE_USER \ + -e POSTGRES_PASSWORD=$DATABASE_PASSWORD \ + . diff --git a/infra/postgres/project.json b/infra/postgres/project.json new file mode 100644 index 0000000..6de9bf3 --- /dev/null +++ b/infra/postgres/project.json @@ -0,0 +1,17 @@ +{ + "name": "postgres", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "infra/postgres", + "tags": ["infra"], + "targets": { + "deploy": { + "executor": "nx:run-commands", + "inputs": ["{projectRoot}/**"], + "options": { + "cwd": "{projectRoot}", + "command": "./deploy.sh" + } + } + } +} diff --git a/infra/postgres/wrapper.sh b/infra/postgres/wrapper.sh new file mode 100755 index 0000000..ca3894a --- /dev/null +++ b/infra/postgres/wrapper.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -e +# Set defaults from offical dockerfile +if [ -z "$LANG" ]; then + export LANG=en_US.utf8 +fi +export PG_MAJOR=16 +export PG_VERSION=16.2 + +if [ -z "$PGDATA" ]; then + export PGDATA=/var/lib/postgresql/data +fi + +exec "$@" diff --git a/infra/redpanda/Dockerfile b/infra/redpanda/Dockerfile new file mode 100644 index 0000000..df06831 --- /dev/null +++ b/infra/redpanda/Dockerfile @@ -0,0 +1 @@ +FROM docker.redpanda.com/redpandadata/redpanda:v24.2.2 diff --git a/infra/redpanda/Kraftfile b/infra/redpanda/Kraftfile index e69de29..3ebee37 100644 --- a/infra/redpanda/Kraftfile +++ b/infra/redpanda/Kraftfile @@ -0,0 +1,9 @@ +spec: v0.6 + +name: redpanda + +runtime: base-compat:latest + +rootfs: ./Dockerfile + +cmd: ["/usr/bin/redpanda"] diff --git a/infra/redpanda/deploy.sh b/infra/redpanda/deploy.sh new file mode 100755 index 0000000..047f8cb --- /dev/null +++ b/infra/redpanda/deploy.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh +kraft cloud volume create --name ftgo-redpanda --size 256 + +kraft cloud deploy \ + --name ftgo-redpanda \ + -M 2048 \ + -p 8081:8081/tls \ + -p 8082:8082/tls \ + -p 9092:9092/tls \ + -p 19644:9644/tls \ + -v ftgo-redpanda:/var/lib/redpanda/data \ + . diff --git a/infra/redpanda/project.json b/infra/redpanda/project.json new file mode 100644 index 0000000..6c21257 --- /dev/null +++ b/infra/redpanda/project.json @@ -0,0 +1,17 @@ +{ + "name": "redpanda", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "infra/redpanda", + "tags": ["infra"], + "targets": { + "deploy": { + "executor": "nx:run-commands", + "inputs": ["{projectRoot}/**"], + "options": { + "cwd": "{projectRoot}", + "command": "./deploy.sh" + } + } + } +} diff --git a/infra/restate/Dockerfile b/infra/restate/Dockerfile new file mode 100644 index 0000000..b8841c8 --- /dev/null +++ b/infra/restate/Dockerfile @@ -0,0 +1,2 @@ +FROM docker.io/restatedev/restate:1.0.2 +COPY ./config.prod.toml /etc/config/restate.toml diff --git a/infra/restate/Kraftfile b/infra/restate/Kraftfile index e69de29..2285464 100644 --- a/infra/restate/Kraftfile +++ b/infra/restate/Kraftfile @@ -0,0 +1,9 @@ +spec: v0.6 + +name: restate + +runtime: base:latest + +rootfs: ./Dockerfile + +cmd: ["/usr/local/bin/restate-server"] diff --git a/infra/restate/config.prod.toml b/infra/restate/config.prod.toml index 4b2470c..f106b64 100644 --- a/infra/restate/config.prod.toml +++ b/infra/restate/config.prod.toml @@ -1,7 +1,16 @@ +base-dir = "/var/lib/restate" +# Unikraft instances are limited to 31 threads +rocksdb-high-priority-bg-threads = 1 +storage-high-priority-bg-threads = 1 +storage-low-priority-bg-threads = 1 +default-thread-pool-size = 1 +rocksdb-bg-threads = 1 +bootstrap-num-partitions = 1 + [worker.invoker.retry-policy] initial-interval = "100ms" max-attempts = 5 [[ingress.kafka-clusters]] name = "default" -brokers = ["PLAINTEXT://:9092"] +brokers = ["PLAINTEXT://ftgo-redpanda.internal:9092"] diff --git a/infra/restate/config.toml b/infra/restate/config.toml index d7ecd89..226edc2 100644 --- a/infra/restate/config.toml +++ b/infra/restate/config.toml @@ -1,3 +1,5 @@ +base-dir = "/var/lib/restate" + [worker.invoker.retry-policy] initial-interval = "50ms" max-attempts = 3 diff --git a/infra/restate/deploy.sh b/infra/restate/deploy.sh old mode 100644 new mode 100755 index e69de29..50577ec --- a/infra/restate/deploy.sh +++ b/infra/restate/deploy.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +kraft cloud volume create --name ftgo-restate --size 256 +# TODO: need redpanda broker host +kraft cloud deploy --name ftgo-restate -M 512 -v ftgo-restate:/var/lib/restate -e RESTATE_CONFIG=/etc/config/restate.toml -p 8080:8080/tls -p 9070:9070/tls . diff --git a/infra/restate/project.json b/infra/restate/project.json new file mode 100644 index 0000000..b5e449c --- /dev/null +++ b/infra/restate/project.json @@ -0,0 +1,18 @@ +{ + "name": "restate", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "sourceRoot": "infra/restate", + "tags": ["infra"], + "targets": { + "deploy": { + "executor": "nx:run-commands", + "inputs": ["{projectRoot}/**"], + "dependsOn": [], + "options": { + "cwd": "{projectRoot}", + "command": "./deploy.sh" + } + } + } +} diff --git a/nx.json b/nx.json index f78b6c6..9e11a2a 100644 --- a/nx.json +++ b/nx.json @@ -1,5 +1,8 @@ { "$schema": "./node_modules/nx/schemas/nx-schema.json", + "cli": { + "packageManager": "bun" + }, "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": [ diff --git a/package.json b/package.json index 8700c56..be2e269 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,33 @@ "name": "@ftgo/source", "version": "0.0.0", "license": "MIT", - "scripts": {}, "private": true, "type": "module", + "scripts": { + "postinstall": "deepkit-type-install" + }, + "dependencies": { + "@deepkit/app": "^1.0.1-alpha.153", + "@deepkit/broker": "^1.0.1-alpha.153", + "@deepkit/bson": "^1.0.1-alpha.153", + "@deepkit/core": "^1.0.1-alpha.147", + "@deepkit/event": "^1.0.1-alpha.153", + "@deepkit/filesystem": "^1.0.1-alpha.147", + "@deepkit/framework": "^1.0.1-alpha.153", + "@deepkit/http": "^1.0.1-alpha.153", + "@deepkit/injector": "^1.0.1-alpha.153", + "@deepkit/orm": "^1.0.1-alpha.153", + "@deepkit/postgres": "^1.0.1-alpha.153", + "@deepkit/sql": "^1.0.1-alpha.153", + "@deepkit/type": "^1.0.1-alpha.153", + "deepkit-restate": "^0.0.37", + "kafkajs": "2.2.4", + "tslib": "2.6.3" + }, "devDependencies": { "@deepkit/type-compiler": "^1.0.1-alpha.150", "@deepkit/vite": "^1.0.1-alpha.150", + "@nx-tools/nx-container": "^6.0.1", "@nx/eslint": "19.5.7", "@nx/eslint-plugin": "19.5.7", "@nx/js": "19.5.7", @@ -30,35 +51,15 @@ "prettier": "^3.3.3", "typescript": "^5.5.4", "vite": "^5.4.0", - "vitest": "^2.0.5" + "vitest": "^2.0.5", + "zx": "^8.1.4" }, "patchedDependencies": { "typescript@5.5.4": "patches/typescript@5.5.4.patch" }, - "dependencies": { - "@deepkit/app": "^1.0.1-alpha.153", - "@deepkit/broker": "^1.0.1-alpha.153", - "@deepkit/bson": "^1.0.1-alpha.153", - "@deepkit/core": "^1.0.1-alpha.147", - "@deepkit/event": "^1.0.1-alpha.153", - "@deepkit/filesystem": "^1.0.1-alpha.147", - "@deepkit/framework": "^1.0.1-alpha.153", - "@deepkit/http": "^1.0.1-alpha.153", - "@deepkit/injector": "^1.0.1-alpha.153", - "@deepkit/orm": "^1.0.1-alpha.153", - "@deepkit/postgres": "^1.0.1-alpha.153", - "@deepkit/sql": "^1.0.1-alpha.153", - "@deepkit/type": "^1.0.1-alpha.153", - "deepkit-restate": "^0.0.37", - "kafkajs": "2.2.4", - "tslib": "2.6.3" - }, + "trustedDependencies": ["nx"], "engines": { "node": "22.6.0", "bun": "1.1.22" - }, - "trustedDependencies": [ - "@deepkit/type-compiler", - "nx" - ] + } } diff --git a/scripts/deploy-kraft-service.mjs b/scripts/deploy-kraft-service.mjs new file mode 100644 index 0000000..1bc45bf --- /dev/null +++ b/scripts/deploy-kraft-service.mjs @@ -0,0 +1,19 @@ +import { $, cd } from 'zx'; + +const [name, projectRoot] = process.argv.slice(2); + +// $.verbose = false; + +cd(projectRoot); + +const port = process.env.RESTATE_SERVER_PORT || '9080'; + +const svcName = `ftgo-${name}`; + +// try { +// await $`kraft cloud service create --name ftgo-${name} -s "${svcName}" 443:${port}/http+tls`; +// } catch {} + +const envVars = Object.entries(process.env).reduce((acc, [key, value]) => acc + ' ' + `-e ${key}=${value}`, ''); + +await $`kraft cloud deploy --rollout remove --restart on-failure --scale-to-zero on --scale-to-zero-cooldown 1000ms --strategy overwrite -M 512 -p ${port}:${port}/http+tls -e RESTATE_SERVER_HOST=${svcName}.internal ${envVars} --name ${svcName} .`;