diff --git a/build/api.Dockerfile b/build/api.Dockerfile new file mode 100644 index 0000000..0dfa167 --- /dev/null +++ b/build/api.Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1.26-alpine AS build + +WORKDIR /src +COPY apps/api/go.mod apps/api/go.sum ./ +RUN go mod download + +COPY apps/api/ ./ +ARG APP_VERSION=dev +RUN CGO_ENABLED=0 go build \ + -trimpath \ + -ldflags "-s -w -X main.appVersion=${APP_VERSION}" \ + -o /out/nereus-api . + +FROM gcr.io/distroless/static:nonroot + +COPY --from=build /out/nereus-api /nereus-api +EXPOSE 8080 +ENTRYPOINT ["/nereus-api"] diff --git a/build/compose.mini.yaml b/build/compose.mini.yaml new file mode 100644 index 0000000..5bb125a --- /dev/null +++ b/build/compose.mini.yaml @@ -0,0 +1,33 @@ +name: nereus-web + +services: + web: + build: + context: .. + dockerfile: build/web.Dockerfile + args: + WEB_ASSET_PATH: ${WEB_ASSET_PATH:-apps/web} + environment: + CLUSTER_API_UPSTREAM: ${CLUSTER_API_UPSTREAM:?set CLUSTER_API_UPSTREAM to the cluster host and port} + read_only: true + tmpfs: + - /etc/nginx/conf.d:size=1m,uid=101,gid=101,mode=0755 + - /tmp:size=16m,mode=1777 + - /var/cache/nginx:size=16m,mode=0755 + security_opt: + - no-new-privileges:true + labels: + - traefik.enable=true + - traefik.http.routers.nereus-web.rule=Host(`${WEB_HOST:-nereus.fiwlabs.dev}`) + - traefik.http.routers.nereus-web.entrypoints=websecure + - traefik.http.routers.nereus-web.tls=true + - traefik.http.routers.nereus-web.tls.certresolver=porkbun + - traefik.http.services.nereus-web.loadbalancer.server.port=8080 + - traefik.docker.network=proxy + networks: + - proxy + restart: unless-stopped + +networks: + proxy: + external: true diff --git a/build/loadgen.Dockerfile b/build/loadgen.Dockerfile new file mode 100644 index 0000000..5e98bb3 --- /dev/null +++ b/build/loadgen.Dockerfile @@ -0,0 +1,16 @@ +FROM golang:1.26-alpine AS build + +WORKDIR /src +COPY apps/loadgen/go.mod ./ +RUN go mod download + +COPY apps/loadgen/ ./ +RUN CGO_ENABLED=0 go build \ + -trimpath \ + -ldflags "-s -w" \ + -o /out/nereus-loadgen . + +FROM gcr.io/distroless/static:nonroot + +COPY --from=build /out/nereus-loadgen /nereus-loadgen +ENTRYPOINT ["/nereus-loadgen"] diff --git a/build/otel-collector.local.yaml b/build/otel-collector.local.yaml new file mode 100644 index 0000000..e0331ff --- /dev/null +++ b/build/otel-collector.local.yaml @@ -0,0 +1,15 @@ +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + +exporters: + debug: + verbosity: basic + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [debug] diff --git a/build/web.Dockerfile b/build/web.Dockerfile new file mode 100644 index 0000000..31c0a80 --- /dev/null +++ b/build/web.Dockerfile @@ -0,0 +1,9 @@ +FROM nginxinc/nginx-unprivileged:1.31.4-alpine + +COPY build/web.conf.template /etc/nginx/templates/default.conf.template +ARG WEB_ASSET_PATH=apps/web +COPY ${WEB_ASSET_PATH}/index.html ${WEB_ASSET_PATH}/styles.css ${WEB_ASSET_PATH}/app.js /usr/share/nginx/html/ +COPY ${WEB_ASSET_PATH}/assets/ /usr/share/nginx/html/assets/ +COPY ${WEB_ASSET_PATH}/vendor/ /usr/share/nginx/html/vendor/ + +EXPOSE 8080 diff --git a/build/web.conf.template b/build/web.conf.template new file mode 100644 index 0000000..884f441 --- /dev/null +++ b/build/web.conf.template @@ -0,0 +1,33 @@ +server { + listen 8080; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + location /api/v1/ { + proxy_pass http://${CLUSTER_API_UPSTREAM}; + proxy_connect_timeout 1s; + proxy_read_timeout 5s; + proxy_send_timeout 5s; + } + + location = /healthz { + proxy_pass http://${CLUSTER_API_UPSTREAM}; + proxy_connect_timeout 1s; + proxy_read_timeout 5s; + proxy_send_timeout 5s; + } + + location = /readyz { + proxy_pass http://${CLUSTER_API_UPSTREAM}; + proxy_connect_timeout 1s; + proxy_read_timeout 5s; + proxy_send_timeout 5s; + } + +} diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..f628d71 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,66 @@ +name: nereus + +services: + postgres: + image: postgres:17-alpine + environment: + POSTGRES_DB: nereus + POSTGRES_USER: nereus + POSTGRES_HOST_AUTH_METHOD: trust + healthcheck: + test: ["CMD-SHELL", "pg_isready -U nereus -d nereus"] + interval: 2s + timeout: 3s + retries: 15 + volumes: + - postgres-data:/var/lib/postgresql/data + + otel-collector: + image: otel/opentelemetry-collector-contrib:0.159.0 + command: ["--config=/etc/otelcol-contrib/config.yaml"] + read_only: true + security_opt: + - no-new-privileges:true + volumes: + - ./build/otel-collector.local.yaml:/etc/otelcol-contrib/config.yaml:ro + + api: + build: + context: . + dockerfile: build/api.Dockerfile + args: + APP_VERSION: local + environment: + DATABASE_URL: postgres://nereus@postgres:5432/nereus?sslmode=disable + OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317 + LOG_LEVEL: info + depends_on: + postgres: + condition: service_healthy + otel-collector: + condition: service_started + ports: + - "${API_PORT:-18080}:8080" + read_only: true + tmpfs: + - /tmp:size=16m,mode=1777 + security_opt: + - no-new-privileges:true + + loadgen: + build: + context: . + dockerfile: build/loadgen.Dockerfile + environment: + TARGET_URL: http://api:8080 + RPS: "5" + depends_on: + - api + read_only: true + tmpfs: + - /tmp:size=16m,mode=1777 + security_opt: + - no-new-privileges:true + +volumes: + postgres-data: