diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 00000000..bc978e78 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,347 @@ +name: Docker Build + +on: + pull_request: + branches: [ main ] + paths-ignore: + - '**.md' + - '.github/**' + - '!.github/workflows/docker-build.yml' + - 'docs/**' + - '.gitignore' + - 'LICENSE' + +permissions: + contents: read + packages: write + pull-requests: read + id-token: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + detect-changes: + runs-on: self-hosted + outputs: + base_matrix: ${{ steps.set-matrix.outputs.base_matrix }} + clients_matrix: ${{ steps.set-matrix.outputs.clients_matrix }} + protocols_matrix: ${{ steps.set-matrix.outputs.protocols_matrix }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v45 + with: + since_last_remote_commit: true + + - name: List all changed files + env: + ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + for file in ${ALL_CHANGED_FILES}; do + echo "$file was changed" + done + + - name: Debug changed files outputs + env: + ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + echo "Debug outputs from changed-files action:" + echo "Changed files:" + echo "$ALL_CHANGED_FILES" + + - name: Generate build matrices + id: set-matrix + env: + ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} + run: | + # First find all Docker build contexts (directories containing Dockerfile) + DOCKER_DIRS=$(find . -name Dockerfile -exec dirname {} \; | sed 's|^./||' | sort) + echo "Found Docker directories:" + echo "$DOCKER_DIRS" + + # Get list of changed files + echo "Changed files in this commit:" + echo "$ALL_CHANGED_FILES" + + # For each changed file, find which Docker directory it belongs to + CHANGED_DIRS=$(for file in ${ALL_CHANGED_FILES}; do + echo "Checking file: $file" + echo "$DOCKER_DIRS" | while read -r docker_dir; do + if [[ "$file" =~ ^"$docker_dir"(/|$) ]]; then + echo "$docker_dir" + fi + done + done | sort -u) + echo "Changed directories:" + echo "$CHANGED_DIRS" + + # Create matrices using jq, filtering by directory structure + BASE=$(echo "$CHANGED_DIRS" | grep "^node-base$" || true) + CLIENTS=$(echo "$CHANGED_DIRS" | grep "^clients/" || true) + PROTOCOLS=$(echo "$CHANGED_DIRS" | grep "^[^/]\+/[^/]\+-[^/]\+$" || true) + + echo "Base dirs: $BASE" + echo "Client dirs: $CLIENTS" + echo "Protocol dirs: $PROTOCOLS" + + # Only output matrices if we have changes + if [[ -n "$BASE" ]]; then + echo "base_matrix=$(echo "$BASE" | jq -Rsc 'split("\n")[:-1] | {include: map({image_path: .})}')" >> $GITHUB_OUTPUT + else + echo 'base_matrix={"include":[]}' >> $GITHUB_OUTPUT + fi + + if [[ -n "$CLIENTS" ]]; then + echo "clients_matrix=$(echo "$CLIENTS" | jq -Rsc 'split("\n")[:-1] | {include: map({image_path: .})}')" >> $GITHUB_OUTPUT + else + echo 'clients_matrix={"include":[]}' >> $GITHUB_OUTPUT + fi + + if [[ -n "$PROTOCOLS" ]]; then + echo "protocols_matrix=$(echo "$PROTOCOLS" | jq -Rsc 'split("\n")[:-1] | {include: map({image_path: .})}')" >> $GITHUB_OUTPUT + else + echo 'protocols_matrix={"include":[]}' >> $GITHUB_OUTPUT + fi + + build-base: + needs: detect-changes + if: ${{ fromJson(needs.detect-changes.outputs.base_matrix).include[0] }} + runs-on: self-hosted + strategy: + matrix: ${{ fromJson(needs.detect-changes.outputs.base_matrix) }} + steps: + - uses: actions/checkout@v4 + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Generate version + id: version + run: | + SHA=$(git rev-parse --short HEAD) + echo "image_name=node-base" >> $GITHUB_OUTPUT + echo "image_tag=${SHA}" >> $GITHUB_OUTPUT + echo "sha=${SHA}" >> $GITHUB_OUTPUT + - name: Generate build contexts + id: build-contexts + run: | + # First, find all directories containing Dockerfiles to build our valid image list + VALID_IMAGES=$(find . -name Dockerfile -exec dirname {} \; | while read dir; do + # Convert directory path to image name (e.g., ./ethereum/ethereum-erigon -> ethereum-erigon) + basename "$dir" | tr '[:upper:]' '[:lower:]' + # For protocol directories, also add the protocol-client format + if [[ "$dir" =~ ^./[^/]+/[^/]+ ]]; then + echo "$dir" | sed 's|^./\([^/]\+\)/\([^/]\+\)|\1-\2|' | tr '[:upper:]' '[:lower:]' + fi + done | sort -u) + + # Now extract FROM directives and filter against our valid images + BUILD_CONTEXTS=$(grep -h "^FROM.*\${.*_IMAGE}" ./${{ matrix.image_path }}/Dockerfile | while read -r line; do + # Extract variable name from FROM line + var_name=$(echo "$line" | grep -o '\${[^}]*}' | tr -d '${}\n') + # Extract image name and remove any default tag + image=$(grep "^ARG ${var_name}=" ./${{ matrix.image_path }}/Dockerfile | cut -d'=' -f2 | cut -d':' -f1) + # Check if this image is in our valid image list + if echo "$VALID_IMAGES" | grep -q "^${image}$"; then + # Use short name for context key but specify it as a docker-image context with SHA tag + echo "${image}=docker-image://ghcr.io/blockjoy/${image}:${{ steps.version.outputs.sha }}" + fi + done | sort -u | tr '\n' ',' | sed 's/,$//') + echo "contexts=${BUILD_CONTEXTS}" >> $GITHUB_OUTPUT + echo "Generated build contexts: ${BUILD_CONTEXTS}" + echo "Valid images: ${VALID_IMAGES}" + echo "Current image path: '${{ matrix.image_path }}'" + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push base image + uses: docker/build-push-action@v5 + with: + context: ./${{ matrix.image_path }} + push: true + build-args: | + GRAFANA_LOKI_API_KEY=${{ secrets.GRAFANA_LOKI_API_KEY }} + GRAFANA_PROM_API_KEY=${{ secrets.GRAFANA_PROM_API_KEY }} + build-contexts: ${{ steps.build-contexts.outputs.contexts }} + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ghcr.io/blockjoy/${{ steps.version.outputs.image_name }}:${{ steps.version.outputs.image_tag }} + + build-clients: + needs: [detect-changes, build-base] + if: | + fromJson(needs.detect-changes.outputs.clients_matrix).include[0] && + (needs.build-base.result == 'success' || needs.build-base.result == 'skipped' || needs.build-base.result == null) + runs-on: self-hosted + strategy: + matrix: ${{ fromJson(needs.detect-changes.outputs.clients_matrix) }} + steps: + - uses: actions/checkout@v4 + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Generate version + id: version + run: | + SHA=$(git rev-parse --short HEAD) + CLIENT_NAME=$(basename ${{ matrix.image_path }}) + IMAGE_NAME="${CLIENT_NAME}" + + if [[ "${{ matrix.image_path }}" =~ /exec/ ]]; then + # Only extract version for execution clients + CLIENT_UPPER=$(echo "${CLIENT_NAME}" | tr '[:lower:]' '[:upper:]') + CLIENT_VERSION=$(grep -E "ENV ${CLIENT_UPPER}_VERSION=[[:space:]]*v?[0-9]+\.[0-9]+\.[0-9]+[-.a-zA-Z0-9]*" "${{ matrix.image_path }}/Dockerfile" | grep -oE "v?[0-9]+\.[0-9]+\.[0-9]+[-.a-zA-Z0-9]*") + if [[ ! -z "$CLIENT_VERSION" ]]; then + # Remove 'v' prefix and ensure version is Docker-compatible (only alphanumeric and . - _) + CLIENT_VERSION=${CLIENT_VERSION#v} + CLIENT_VERSION=$(echo "$CLIENT_VERSION" | tr -cd '[:alnum:].-') + IMAGE_TAG="${CLIENT_VERSION}-${SHA}" + else + IMAGE_TAG="${SHA}" + fi + else + IMAGE_TAG="${SHA}" + fi + echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT + echo "image_tag=${IMAGE_TAG}" >> $GITHUB_OUTPUT + echo "sha=${SHA}" >> $GITHUB_OUTPUT + - name: Generate build contexts + id: build-contexts + run: | + # First, find all directories containing Dockerfiles to build our valid image list + VALID_IMAGES=$(find . -name Dockerfile -exec dirname {} \; | while read dir; do + # Convert directory path to image name (e.g., ./ethereum/ethereum-erigon -> ethereum-erigon) + basename "$dir" | tr '[:upper:]' '[:lower:]' + # For protocol directories, also add the protocol-client format + if [[ "$dir" =~ ^./[^/]+/[^/]+ ]]; then + echo "$dir" | sed 's|^./\([^/]\+\)/\([^/]\+\)|\1-\2|' | tr '[:upper:]' '[:lower:]' + fi + done | sort -u) + + # Now extract FROM directives and filter against our valid images + BUILD_CONTEXTS=$(grep -h "^FROM.*\${.*_IMAGE}" ./${{ matrix.image_path }}/Dockerfile | while read -r line; do + # Extract variable name from FROM line + var_name=$(echo "$line" | grep -o '\${[^}]*}' | tr -d '${}\n') + # Extract image name and remove any default tag + image=$(grep "^ARG ${var_name}=" ./${{ matrix.image_path }}/Dockerfile | cut -d'=' -f2 | cut -d':' -f1) + # Check if this image is in our valid image list + if echo "$VALID_IMAGES" | grep -q "^${image}$"; then + # Use short name for context key but specify it as a docker-image context with SHA tag + echo "${image}=docker-image://ghcr.io/blockjoy/${image}:${{ steps.version.outputs.sha }}" + fi + done | sort -u | tr '\n' ',' | sed 's/,$//') + echo "contexts=${BUILD_CONTEXTS}" >> $GITHUB_OUTPUT + echo "Generated build contexts: ${BUILD_CONTEXTS}" + echo "Valid images: ${VALID_IMAGES}" + echo "Current image path: '${{ matrix.image_path }}'" + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push client images + uses: docker/build-push-action@v5 + with: + context: ./${{ matrix.image_path }} + push: true + build-args: | + GRAFANA_LOKI_BASICAUTH=${{ secrets.GRAFANA_LOKI_BASICAUTH }} + GRAFANA_PROM_BASICAUTH=${{ secrets.GRAFANA_PROM_BASICAUTH }} + CLOUDFLARE_API_KEY=${{ secrets.CLOUDFLARE_API_KEY }} + BASE_IMAGE=node-base:${{ steps.version.outputs.sha }} + build-contexts: ${{ steps.build-contexts.outputs.contexts }} + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ghcr.io/blockjoy/${{ steps.version.outputs.image_name }}:${{ steps.version.outputs.image_tag }} + + build-protocols: + needs: [detect-changes] + if: fromJson(needs.detect-changes.outputs.protocols_matrix).include[0] + runs-on: self-hosted + strategy: + matrix: ${{ fromJson(needs.detect-changes.outputs.protocols_matrix) }} + steps: + - uses: actions/checkout@v4 + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Generate version + id: version + run: | + SHA=$(git rev-parse --short HEAD) + if [[ -n "${{ matrix.image_path }}" ]]; then + # Only try to generate image name if matrix path is not empty + IMAGE_NAME=$(basename $(dirname "${{ matrix.image_path }}"))"-"$(basename "${{ matrix.image_path }}") + echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT + else + echo "::error ::No image path provided in matrix" + exit 1 + fi + echo "image_tag=${SHA}" >> $GITHUB_OUTPUT + echo "sha=${SHA}" >> $GITHUB_OUTPUT + - name: Generate build contexts + id: build-contexts + run: | + # First, find all directories containing Dockerfiles to build our valid image list + VALID_IMAGES=$(find . -name Dockerfile -exec dirname {} \; | while read dir; do + # Convert directory path to image name (e.g., ./ethereum/ethereum-erigon -> ethereum-erigon) + basename "$dir" | tr '[:upper:]' '[:lower:]' + # For protocol directories, also add the protocol-client format + if [[ "$dir" =~ ^./[^/]+/[^/]+ ]]; then + echo "$dir" | sed 's|^./\([^/]\+\)/\([^/]\+\)|\1-\2|' | tr '[:upper:]' '[:lower:]' + fi + done | sort -u) + + # Now extract FROM directives and filter against our valid images + BUILD_CONTEXTS=$(grep -h "^FROM.*\${.*_IMAGE}" ./${{ matrix.image_path }}/Dockerfile | while read -r line; do + # Extract variable name from FROM line + var_name=$(echo "$line" | grep -o '\${[^}]*}' | tr -d '${}\n') + # Extract image name and remove any default tag + image=$(grep "^ARG ${var_name}=" ./${{ matrix.image_path }}/Dockerfile | cut -d'=' -f2 | cut -d':' -f1) + # Check if this image is in our valid image list + if echo "$VALID_IMAGES" | grep -q "^${image}$"; then + # Use short name for context key but specify it as a docker-image context with SHA tag + echo "${image}=docker-image://ghcr.io/blockjoy/${image}:${{ steps.version.outputs.sha }}" + fi + done | sort -u | tr '\n' ',' | sed 's/,$//') + echo "contexts=${BUILD_CONTEXTS}" >> $GITHUB_OUTPUT + echo "Generated build contexts: ${BUILD_CONTEXTS}" + echo "Valid images: ${VALID_IMAGES}" + echo "Current image path: '${{ matrix.image_path }}'" + - name: Generate build args + id: build-args + run: | + # Extract all *_IMAGE args from Dockerfile with their specified tags + IMAGE_ARGS=$(grep "^ARG.*_IMAGE=" ./${{ matrix.image_path }}/Dockerfile | while read -r line; do + var_name=$(echo "$line" | cut -d'=' -f1 | cut -d' ' -f2) + # Use the full image reference from the Dockerfile, including its tag + image_ref=$(echo "$line" | cut -d'=' -f2) + echo "${var_name}=${image_ref}" + done | tr '\n' '\n') + echo "image_args<> $GITHUB_OUTPUT + echo "$IMAGE_ARGS" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push protocol images + uses: docker/build-push-action@v5 + with: + context: ./${{ matrix.image_path }} + push: true + build-args: | + GRAFANA_LOKI_BASICAUTH=${{ secrets.GRAFANA_LOKI_BASICAUTH }} + GRAFANA_PROM_BASICAUTH=${{ secrets.GRAFANA_PROM_BASICAUTH }} + CLOUDFLARE_API_KEY=${{ secrets.CLOUDFLARE_API_KEY }} + ${{ steps.build-args.outputs.image_args }} + build-contexts: ${{ steps.build-contexts.outputs.contexts }} + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ghcr.io/blockjoy/${{ steps.version.outputs.image_name }}:${{ steps.version.outputs.image_tag }} diff --git a/clients/consensus/lighthouse/Dockerfile b/clients/consensus/lighthouse/Dockerfile new file mode 100644 index 00000000..6fed12bb --- /dev/null +++ b/clients/consensus/lighthouse/Dockerfile @@ -0,0 +1,33 @@ +ARG BASE_IMAGE=node-base:latest +FROM ghcr.io/blockjoy/${BASE_IMAGE} + +ENV DEBIAN_FRONTEND=noninteractive + +ENV LIGHTHOUSE_VERSION=v5.3.0 +ENV CARGO_NET_GIT_FETCH_WITH_CLI=true + +# Install system dependencies including libclang +RUN apt-get update && \ + apt-get install -y \ + clang \ + libclang-dev \ + cmake \ + libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install Rust and required dependencies +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + . "$HOME/.cargo/env" && \ + rustup default stable && \ + rustup target add x86_64-unknown-linux-gnu + +# Build Lighthouse +WORKDIR /root +RUN git clone https://github.com/sigp/lighthouse.git && \ + cd lighthouse && \ + git checkout $LIGHTHOUSE_VERSION + +WORKDIR /root/lighthouse +RUN . "$HOME/.cargo/env" && cargo build --release --features portable && \ + mkdir -p /root/bin && \ + cp /root/lighthouse/target/release/lighthouse /root/bin/lighthouse diff --git a/clients/exec/erigon/Dockerfile b/clients/exec/erigon/Dockerfile new file mode 100644 index 00000000..058f69b6 --- /dev/null +++ b/clients/exec/erigon/Dockerfile @@ -0,0 +1,24 @@ +ARG BASE_IMAGE=node-base:latest +FROM ghcr.io/blockjoy/${BASE_IMAGE} as builder + +ENV DEBIAN_FRONTEND=noninteractive +ENV ERIGON_VERSION=v2.60.9 +ENV PATH="/usr/local/go/bin:${PATH}" +ENV GOROOT="/usr/local/go" + +RUN mkdir -p /root/bin + +# Clone and checkout in a separate layer +WORKDIR /root +RUN git clone --depth 1 --branch $ERIGON_VERSION https://github.com/erigontech/erigon.git + +# Build erigon with Go caching +WORKDIR /root/erigon +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + make erigon && \ + cp build/bin/erigon /root/bin/ + +# Create minimal final image +FROM ghcr.io/blockjoy/${BASE_IMAGE} +COPY --from=builder /root/bin/erigon /root/bin/erigon diff --git a/clients/exec/reth/Dockerfile b/clients/exec/reth/Dockerfile new file mode 100644 index 00000000..5c20eda5 --- /dev/null +++ b/clients/exec/reth/Dockerfile @@ -0,0 +1,39 @@ +ARG BASE_IMAGE=node-base:latest +FROM ghcr.io/blockjoy/${BASE_IMAGE} as builder + +ENV DEBIAN_FRONTEND=noninteractive +ENV RETH_VERSION=v1.1.4 +ENV CARGO_NET_GIT_FETCH_WITH_CLI=true + +# Install system dependencies in a separate layer +RUN apt-get update && \ + apt-get install -y \ + clang \ + libclang-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install Rust in a separate layer +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + . "$HOME/.cargo/env" && \ + rustup default stable && \ + rustup target add x86_64-unknown-linux-gnu + +# Clone and checkout in a separate layer +WORKDIR /root +RUN --mount=type=cache,target=/root/.cargo/git \ + --mount=type=cache,target=/root/.cargo/registry \ + git clone --depth 1 --branch $RETH_VERSION https://github.com/paradigmxyz/reth.git + +# Build reth with cargo caching +WORKDIR /root/reth +RUN --mount=type=cache,target=/root/.cargo/git \ + --mount=type=cache,target=/root/.cargo/registry \ + --mount=type=cache,target=/root/reth/target \ + . "$HOME/.cargo/env" && \ + cargo build --profile maxperf && \ + mkdir -p /root/bin && \ + cp target/maxperf/reth /root/bin/ + +# Create minimal final image +FROM ghcr.io/blockjoy/${BASE_IMAGE} +COPY --from=builder /root/bin/reth /root/bin/reth diff --git a/ethereum/ethereum-erigon/Caddyfile.template b/ethereum/ethereum-erigon/Caddyfile.template new file mode 100644 index 00000000..21e9d359 --- /dev/null +++ b/ethereum/ethereum-erigon/Caddyfile.template @@ -0,0 +1,41 @@ +{ + admin 127.0.0.1:2019 { + } + log { + output stdout + format json + level INFO + } + acme_dns cloudflare {$CLOUDFLARE_API_KEY} + auto_https disable_redirects + storage file_system {{ data_dir }} + email ops@blockjoy.com + servers { + metrics + } +} + +http://{{ hostname }}{{ tld }}, https://{{ hostname }}{{ tld }}, *.blkjy.io, http:// { + log { + output stdout + } + log_append upstream_host {rp.upstream.host} + log_append upstream_port {rp.upstream.port} + log_append upstream_requests {rp.upstream.requests} + log_append upstream_max_requests {rp.upstream.max_requests} + log_append upstream_fails {rp.upstream.fails} + log_append upstream_latency {rp.upstream.latency} + log_append upstream_latency_ms {rp.upstream.latency_ms} + log_append upstream_duration_ms {rp.upstream.duration_ms} + log_append duration_ms {rp.duration_ms} + @websockets { + header Connection *Upgrade* + header Upgrade websocket + } + reverse_proxy 127.0.0.1:{{ rpc_port }} { + header_up Host localhost + } + reverse_proxy @websockets 127.0.0.1:{{ ws_port }} { + header_up Host localhost + } +} diff --git a/ethereum/ethereum-erigon/Dockerfile b/ethereum/ethereum-erigon/Dockerfile new file mode 100644 index 00000000..65cc50ea --- /dev/null +++ b/ethereum/ethereum-erigon/Dockerfile @@ -0,0 +1,26 @@ +ARG ERIGON_IMAGE=ghcr.io/blockjoy/erigon:latest +ARG LIGHTHOUSE_IMAGE=ghcr.io/blockjoy/lighthouse:latest +ARG BASE_IMAGE=ghcr.io/blockjoy/node-base:latest + +FROM ${ERIGON_IMAGE} as erigon-build + +FROM ${LIGHTHOUSE_IMAGE} AS lighthouse-build + +FROM ${BASE_IMAGE} + +ARG CLOUDFLARE_API_KEY +ARG GRAFANA_LOKI_BASICAUTH +ARG GRAFANA_PROM_BASICAUTH + +ENV CLOUDFLARE_API_KEY=${CLOUDFLARE_API_KEY} +ENV GRAFANA_LOKI_BASICAUTH=${GRAFANA_LOKI_BASICAUTH} +ENV GRAFANA_PROM_BASICAUTH=${GRAFANA_PROM_BASICAUTH} + +RUN mkdir -p /root/bin +COPY --from=erigon-build /root/bin/erigon /root/bin/ +COPY --from=lighthouse-build /root/bin/lighthouse /root/bin/ + +COPY Caddyfile.template /var/lib/babel/templates/Caddyfile.template +COPY erigon.rhai /var/lib/babel/plugin/ +COPY main.rhai /var/lib/babel/plugin/ +COPY config-alloy.template /var/lib/babel/templates/config-alloy.template diff --git a/ethereum/ethereum-erigon/babel.yaml b/ethereum/ethereum-erigon/babel.yaml new file mode 100644 index 00000000..e761026c --- /dev/null +++ b/ethereum/ethereum-erigon/babel.yaml @@ -0,0 +1,67 @@ +version: 0.0.1 +container_uri: docker-daemon://ethereum-erigon:latest +sku_code: ETH-ERG +org_id: null +description: Ethereum Erigon node +visibility: public +protocol_key: ethereum +variants: + - key: erigon-mainnet-full + min_cpu: 16 + min_memory_mb: 65536 + min_disk_gb: 5000 + archive_pointers: + - pointer: !store_id ethereum-erigon-mainnet-full-v1 + - key: erigon-sepolia-full + min_cpu: 4 + min_memory_mb: 16000 + min_disk_gb: 2000 + archive_pointers: + - pointer: !store_id ethereum-erigon-sepolia-full-v1 + - key: erigon-mainnet-archive + min_cpu: 16 + min_memory_mb: 65536 + min_disk_gb: 5000 + archive_pointers: + - pointer: !store_id ethereum-erigon-mainnet-archive-v1 + - key: erigon-sepolia-archive + min_cpu: 4 + min_memory_mb: 16000 + min_disk_gb: 2000 + archive_pointers: + - pointer: !store_id ethereum-erigon-sepolia-archive-v1 +firewall_config: + default_in: deny + default_out: allow + rules: + - key: Allow incoming ports TCP + description: Allow TCP inbound traffic on port 80, 443 + protocol: tcp + direction: in + action: allow + ips: + - ip: 0.0.0.0/0 + name: allow all traffic + ports: + - port: 80 + name: HTTP port + - port: 443 + name: HTTPS port + - port: 3000 + name: Grafana port + - key: Allow P2P ports TCP/UDP + description: Allow inbound traffic on P2P ports + protocol: both + direction: in + action: allow + ips: + - ip: 0.0.0.0/0 + name: allow all traffic + ports: + - port: 30303 + name: P2P port 1 + - port: 30304 + name: P2P port 2 + - port: 42069 + name: Torrent port +properties: [] \ No newline at end of file diff --git a/ethereum/ethereum-erigon/config-alloy.template b/ethereum/ethereum-erigon/config-alloy.template new file mode 100644 index 00000000..9009bf13 --- /dev/null +++ b/ethereum/ethereum-erigon/config-alloy.template @@ -0,0 +1,206 @@ +logging { + level = "warn" +} + +discovery.relabel "metrics_grafana_agent" { + targets = [{ + __address__ = "127.0.0.1:12346", + }] + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } +} + +prometheus.scrape "metrics_grafana_agent" { + targets = discovery.relabel.metrics_grafana_agent.output + forward_to = [prometheus.remote_write.default.receiver] + job_name = "grafana_agent" + metrics_path = "/metrics" +} + +discovery.relabel "metrics_caddy" { + targets = [{ + __address__ = "127.0.0.1:2019", + }] + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } +} + +prometheus.scrape "metrics_caddy" { + targets = discovery.relabel.metrics_caddy.output + forward_to = [prometheus.remote_write.default.receiver] + job_name = "caddy" + metrics_path = "/metrics" +} + +discovery.relabel "metrics_{{ blockchain | lower | replace(from="-", to="_") }}" { + targets = [{ + __address__ = "127.0.0.1:{{ metrics_port }}", + }] + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } + + rule { + target_label = "protocol" + replacement = "{{ blockchain | lower | replace(from="-", to="_") }}" + } +} + +prometheus.scrape "metrics_{{ blockchain | lower | replace(from="-", to="_") }}" { + targets = discovery.relabel.metrics_{{ blockchain | lower | replace(from="-", to="_") }}.output + forward_to = [prometheus.remote_write.default.receiver] + job_name = "{{ blockchain | lower | replace(from="-", to="_") }}" + metrics_path = "{{ metrics_path }}" +} + +local.file_match "babel" { + path_targets = [{ + __address__ = "127.0.0.1", + __path__ = "/var/lib/babel/jobs/logs/*", + instance = "{{ hostname }}{{ tld }}", + job = "babel", + }] +} + +loki.source.file "logs_default_babel" { + targets = local.file_match.babel.targets + forward_to = [loki.write.logs_default.receiver] +} + + +discovery.relabel "logs_default_journal_scrape" { + targets = [] + + rule { + source_labels = ["__journal__systemd_unit"] + target_label = "unit" + } + + rule { + source_labels = ["__journal__boot_id"] + target_label = "boot_id" + } + + rule { + source_labels = ["__journal__transport"] + target_label = "transport" + } + + rule { + source_labels = ["__journal_priority_keyword"] + target_label = "level" + } +} + +loki.source.journal "logs_default_journal_scrape" { + max_age = "24h0m0s" + relabel_rules = discovery.relabel.logs_default_journal_scrape.rules + forward_to = [loki.write.logs_default.receiver,loki.write.logs_local.receiver] + labels = { + cluster = "prod-nodes", + instance = "{{ hostname }}{{ tld }}", + job = "systemd_logs", + } +} + +loki.write "logs_default" { + endpoint { + url = "https://logs-prod-012.grafana.net/loki/api/v1/push" + + basic_auth { + username = "760471" + password = "${GRAFANA_LOKI_BASICAUTH}" + } + } + external_labels = {} +} + +loki.write "logs_local" { + endpoint { + url = "http://localhost:9090/loki/api/v1/push" + } + external_labels = {} +} + +prometheus.exporter.unix "node_exporter" { + disable_collectors = ["ipvs", "btrfs", "infiniband", "xfs", "zfs"] + + filesystem { + fs_types_exclude = "^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|tmpfs|fusectl|hugetlbfs|iso9660|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$" + mount_points_exclude = "^/(dev|proc|run/credentials/.+|sys|var/lib/docker/.+)($|/)" + mount_timeout = "5s" + } + + netclass { + ignored_devices = "^(veth.*|cali.*|[a-f0-9]{15})$" + } + + netdev { + device_exclude = "^(veth.*|cali.*|[a-f0-9]{15})$" + } +} + +discovery.relabel "node_exporter" { + targets = prometheus.exporter.unix.node_exporter.targets + + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } + + rule { + target_label = "job" + replacement = "node_exporter" + } +} + +prometheus.scrape "node_exporter" { + targets = discovery.relabel.node_exporter.output + forward_to = [prometheus.relabel.node_exporter.receiver] + job_name = "node_exporter" +} + +prometheus.relabel "node_exporter" { + forward_to = [prometheus.remote_write.default.receiver] + + rule { + source_labels = ["__name__"] + regex = "node_scrape_collector_.+" + action = "drop" + } + + rule { + source_labels = ["__name__"] + regex = "node_arp_entries|node_boot_time_seconds|node_context_switches_total|node_cpu_seconds_total|node_disk_io_time_seconds_total|node_disk_io_time_weighted_seconds_total|node_disk_read_bytes_total|node_disk_read_time_seconds_total|node_disk_reads_completed_total|node_disk_write_time_seconds_total|node_disk_writes_completed_total|node_disk_written_bytes_total|node_filefd_allocated|node_filefd_maximum|node_filesystem_avail_bytes|node_filesystem_device_error|node_filesystem_files|node_filesystem_files_free|node_filesystem_readonly|node_filesystem_size_bytes|node_intr_total|node_load1|node_load15|node_load5|node_md_disks|node_md_disks_required|node_memory_Active_anon_bytes|node_memory_Active_bytes|node_memory_Active_file_bytes|node_memory_AnonHugePages_bytes|node_memory_AnonPages_bytes|node_memory_Bounce_bytes|node_memory_Buffers_bytes|node_memory_Cached_bytes|node_memory_CommitLimit_bytes|node_memory_Committed_AS_bytes|node_memory_DirectMap1G_bytes|node_memory_DirectMap2M_bytes|node_memory_DirectMap4k_bytes|node_memory_Dirty_bytes|node_memory_HugePages_Free|node_memory_HugePages_Rsvd|node_memory_HugePages_Surp|node_memory_HugePages_Total|node_memory_Hugepagesize_bytes|node_memory_Inactive_anon_bytes|node_memory_Inactive_bytes|node_memory_Inactive_file_bytes|node_memory_Mapped_bytes|node_memory_MemAvailable_bytes|node_memory_MemFree_bytes|node_memory_MemTotal_bytes|node_memory_SReclaimable_bytes|node_memory_SUnreclaim_bytes|node_memory_ShmemHugePages_bytes|node_memory_ShmemPmdMapped_bytes|node_memory_Shmem_bytes|node_memory_Slab_bytes|node_memory_SwapTotal_bytes|node_memory_VmallocChunk_bytes|node_memory_VmallocTotal_bytes|node_memory_VmallocUsed_bytes|node_memory_WritebackTmp_bytes|node_memory_Writeback_bytes|node_netstat_Icmp6_InErrors|node_netstat_Icmp6_InMsgs|node_netstat_Icmp6_OutMsgs|node_netstat_Icmp_InErrors|node_netstat_Icmp_InMsgs|node_netstat_Icmp_OutMsgs|node_netstat_IpExt_InOctets|node_netstat_IpExt_OutOctets|node_netstat_TcpExt_ListenDrops|node_netstat_TcpExt_ListenOverflows|node_netstat_TcpExt_TCPSynRetrans|node_netstat_Tcp_InErrs|node_netstat_Tcp_InSegs|node_netstat_Tcp_OutRsts|node_netstat_Tcp_OutSegs|node_netstat_Tcp_RetransSegs|node_netstat_Udp6_InDatagrams|node_netstat_Udp6_InErrors|node_netstat_Udp6_NoPorts|node_netstat_Udp6_OutDatagrams|node_netstat_Udp6_RcvbufErrors|node_netstat_Udp6_SndbufErrors|node_netstat_UdpLite_InErrors|node_netstat_Udp_InDatagrams|node_netstat_Udp_InErrors|node_netstat_Udp_NoPorts|node_netstat_Udp_OutDatagrams|node_netstat_Udp_RcvbufErrors|node_netstat_Udp_SndbufErrors|node_network_carrier|node_network_info|node_network_mtu_bytes|node_network_receive_bytes_total|node_network_receive_compressed_total|node_network_receive_drop_total|node_network_receive_errs_total|node_network_receive_fifo_total|node_network_receive_multicast_total|node_network_receive_packets_total|node_network_speed_bytes|node_network_transmit_bytes_total|node_network_transmit_compressed_total|node_network_transmit_drop_total|node_network_transmit_errs_total|node_network_transmit_fifo_total|node_network_transmit_multicast_total|node_network_transmit_packets_total|node_network_transmit_queue_length|node_network_up|node_nf_conntrack_entries|node_nf_conntrack_entries_limit|node_os_info|node_sockstat_FRAG6_inuse|node_sockstat_FRAG_inuse|node_sockstat_RAW6_inuse|node_sockstat_RAW_inuse|node_sockstat_TCP6_inuse|node_sockstat_TCP_alloc|node_sockstat_TCP_inuse|node_sockstat_TCP_mem|node_sockstat_TCP_mem_bytes|node_sockstat_TCP_orphan|node_sockstat_TCP_tw|node_sockstat_UDP6_inuse|node_sockstat_UDPLITE6_inuse|node_sockstat_UDPLITE_inuse|node_sockstat_UDP_inuse|node_sockstat_UDP_mem|node_sockstat_UDP_mem_bytes|node_sockstat_sockets_used|node_softnet_dropped_total|node_softnet_processed_total|node_softnet_times_squeezed_total|node_systemd_unit_state|node_textfile_scrape_error|node_time_zone_offset_seconds|node_timex_estimated_error_seconds|node_timex_maxerror_seconds|node_timex_offset_seconds|node_timex_sync_status|node_uname_info|node_vmstat_oom_kill|node_vmstat_pgfault|node_vmstat_pgmajfault|node_vmstat_pgpgin|node_vmstat_pgpgout|node_vmstat_pswpin|node_vmstat_pswpout|process_max_fds|process_open_fds" + action = "keep" + } +} + +prometheus.remote_write "default" { + external_labels = { + cluster = "prod-nodes", + } + + endpoint { + name = "prom-ep" + url = "https://prometheus-prod-24-prod-eu-west-2.grafana.net/api/prom/push" + + basic_auth { + username = "1322871" + password = "${GRAFANA_PROM_BASICAUTH}" + } + + queue_config { } + + metadata_config { } + } +} diff --git a/ethereum/ethereum-erigon/erigon.rhai b/ethereum/ethereum-erigon/erigon.rhai new file mode 100644 index 00000000..153dae00 --- /dev/null +++ b/ethereum/ethereum-erigon/erigon.rhai @@ -0,0 +1,39 @@ +fn base_config(metrics_port, rpc_port, ws_port, caddy_dir) { + #{ + config_files: [ + #{ + template: "/var/lib/babel/templates/config-alloy.template", + destination: "/etc/alloy/config.alloy", + params: #{ + hostname: node_env().node_name, + tld: ".n0des.xyz", + blockchain: node_env().node_protocol, + metrics_port: `${metrics_port}`, + metrics_path: "/debug/metrics/prometheus", + } + }, + #{ + template: "/var/lib/babel/templates/Caddyfile.template", + destination: "/etc/caddy/Caddyfile", + params: #{ + rpc_port: `${rpc_port}`, + ws_port: `${ws_port}`, + hostname: node_env().node_name, + node_ip: node_env().node_ip, + tld: ".n0des.xyz", + data_dir: `${caddy_dir}`, + } + } + ], + services: [ + #{ + name: "alloy", + run_sh: "/usr/bin/alloy run --server.http.listen-addr=127.0.0.1:12346 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy" + }, + #{ + name: "caddy", + run_sh: "/usr/bin/caddy run --config /etc/caddy/Caddyfile" + } + ] + } +} diff --git a/ethereum/ethereum-erigon/main.rhai b/ethereum/ethereum-erigon/main.rhai new file mode 100644 index 00000000..6119d70e --- /dev/null +++ b/ethereum/ethereum-erigon/main.rhai @@ -0,0 +1,145 @@ +import "base" as base; +import "erigon" as erigon; + +const CADDY_DIR = DATA_DRIVE_MOUNT_POINT + "/caddy"; +const RPC_PORT = 8545; +const WS_PORT = 8546; +const METRICS_PORT = 6161; + +let ERIGON_CONFIG = erigon::base_config(global::METRICS_PORT,global::RPC_PORT,global::WS_PORT,global::CADDY_DIR); + +const BASE_CONFIG = #{ + config_files: ERIGON_CONFIG.config_files+base::BASE_CONFIG.config_files, + services: ERIGON_CONFIG.services+base::BASE_CONFIG.services, +}; + +const VARIANTS = #{ + erigon_goerli_full: #{ + net: "goerli", + }, + erigon_sepolia_full: #{ + net: "sepolia", + }, + erigon_mainnet_full: #{ + net: "mainnet", + }, + erigon_goerli_archive: #{ + net: "goerli", + }, + erigon_sepolia_archive: #{ + net: "sepolia", + }, + erigon_mainnet_archive: #{ + net: "mainnet", + }, +}; + + +const API_HOST = `http://127.0.0.1:${global::RPC_PORT}`; + +const HOME = PROTOCOL_DATA_PATH + "/erigon"; + +const NET = VARIANTS[node_env().node_variant].net; + +const EXTRA_ERIGON_ARGS = if node_params().nodetype == "full" { + "--prune=htcr" +} else { + "" +}; + +const BABEL_VERSION = "0.9.0"; + +const PLUGIN_CONFIG = #{ + init: #{ + commands: [ + `mkdir -p ${global::HOME}`, + `mkdir -p ${global::CADDY_DIR}`, + ], + jobs: [ + #{ + name: "create_jwt", + run_sh: `export HOME=/root && openssl rand -hex 32 | tr -d "\n" > ${global::HOME}/jwt.hex`, + } + ] + }, + services: [ + #{ + name: "erigon", + run_sh: `export HOME=/root && /root/bin/erigon --datadir=${global::HOME} \ + --chain=${global::NET} --healthcheck=true --port=30303 --http.port=8545 --authrpc.port=8551 \ + --torrent.port=42069 --private.api.addr=127.0.0.1:9090 --http --ws --ws.port 8546 \ + --http.api=eth,debug,net,trace,web3,erigon --nat=none --authrpc.jwtsecret=${global::HOME}/jwt.hex \ + --torrent.download.rate=100mb --metrics --metrics.port=6666 --metrics.addr=127.0.0.1 \ + --http.vhosts localhost --downloader.disable.ipv6 --caplin.backfilling=true \ + --caplin.backfilling.blob=true --caplin.backfilling.blob.no-pruning=true --caplin.archive=true \ + --beacon.api=beacon,builder,config,debug,node,validator,lighthouse --internalcl \ + ${global::EXTRA_ERIGON_ARGS}`, + shutdown_timeout_secs: 120, + use_blockchain_data: true, + }, + ], + alternative_download: #{ + run_sh: "echo dummy" + }, + upload: #{ + exclude: [ + "caddy/**", + ] + } +}; + +fn application_status() { + let resp = run_jrpc(#{host: global::API_HOST, method: ""}); + + if resp.status_code == 200 { + "broadcasting" + } else { + "delinquent" + } +} + +fn address() { + parse_json(run_jrpc(global::API_HOST, "eth_coinbase")).result.to_string() +} + +fn height() { + let resp = run_jrpc(#{host: global::API_HOST, method: "eth_blockNumber"}); + if resp.status_code != 200 { + throw resp; + } + let h = parse_json(resp.body); + parse_int(sub_string(h.result,2),16) +} + +fn sync_status() { + let data1 = #{url: global::API_HOST+"/health", + method: "eth_getBlockByNumber", + headers: [ + ["X-ERIGON-HEALTHCHECK", "synced"], + ["X-ERIGON-HEALTHCHECK", "min_peer_count10"] + ] + }; + + let resp1 = run_rest(data1); + + let data2 = #{host: global::API_HOST, + method: "eth_getBlockByNumber", + params: ["latest", true], + headers: [["content-type", "application/json"]], + }; + + let t = run_jrpc(data2).expect(200); + + let block_timestamp = parse_hex(t.result.timestamp); + + let system_timestamp = run_sh (`date +%s`).unwrap(); + let current_timestamp = parse_int(system_timestamp); + + if resp1.status_code > 225 { + "syncing" + } else if (block_timestamp < current_timestamp - 100) { + "syncing" + } else { + "synced" + } +} diff --git a/ethereum/ethereum-reth/Caddyfile.template b/ethereum/ethereum-reth/Caddyfile.template new file mode 100644 index 00000000..aeedb66c --- /dev/null +++ b/ethereum/ethereum-reth/Caddyfile.template @@ -0,0 +1,41 @@ +{ + admin 127.0.0.1:2019 { + } + log { + output stdout + format json + level INFO + } + acme_dns cloudflare {$CLOUDFLARE_API_KEY} + auto_https disable_redirects + storage file_system {{ data_dir }} + email ops@blockjoy.com + servers { + metrics + } +} + +http://{{ hostname }}{{ tld }}, https://{{ hostname }}{{ tld }}, *.blkjy.io, http:// { + log { + output stdout + } + log_append upstream_host {rp.upstream.host} + log_append upstream_port {rp.upstream.port} + log_append upstream_requests {rp.upstream.requests} + log_append upstream_max_requests {rp.upstream.max_requests} + log_append upstream_fails {rp.upstream.fails} + log_append upstream_latency {rp.upstream.latency} + log_append upstream_latency_ms {rp.upstream.latency_ms} + log_append upstream_duration_ms {rp.upstream.duration_ms} + log_append duration_ms {rp.duration_ms} + @websockets { + header Connection *Upgrade* + header Upgrade websocket + } + reverse_proxy 127.0.0.1:{{ rpc_port }} { + header_up Host localhost + } + reverse_proxy @websockets 127.0.0.1:{{ ws_port }} { + header_up Host localhost + } +} \ No newline at end of file diff --git a/ethereum/ethereum-reth/Dockerfile b/ethereum/ethereum-reth/Dockerfile new file mode 100644 index 00000000..27edf0a8 --- /dev/null +++ b/ethereum/ethereum-reth/Dockerfile @@ -0,0 +1,26 @@ +ARG RETH_IMAGE=ghcr.io/blockjoy/reth:latest +ARG LIGHTHOUSE_IMAGE=ghcr.io/blockjoy/lighthouse:latest +ARG BASE_IMAGE=ghcr.io/blockjoy/node-base:latest + +FROM ${RETH_IMAGE} AS reth-build + +FROM ${LIGHTHOUSE_IMAGE} AS lighthouse-build + +FROM ${BASE_IMAGE} + +ARG CLOUDFLARE_API_KEY +ARG GRAFANA_LOKI_BASICAUTH +ARG GRAFANA_PROM_BASICAUTH + +ENV CLOUDFLARE_API_KEY=${CLOUDFLARE_API_KEY} +ENV GRAFANA_LOKI_BASICAUTH=${GRAFANA_LOKI_BASICAUTH} +ENV GRAFANA_PROM_BASICAUTH=${GRAFANA_PROM_BASICAUTH} + +RUN mkdir -p /root/bin +COPY --from=reth-build /root/bin/reth /root/bin/ +COPY --from=lighthouse-build /root/bin/lighthouse /root/bin/ + +COPY Caddyfile.template /var/lib/babel/templates/ +COPY reth.rhai /var/lib/babel/plugin/ +COPY main.rhai /var/lib/babel/plugin/ +COPY config-alloy.template /var/lib/babel/templates/config-alloy.template \ No newline at end of file diff --git a/ethereum/ethereum-reth/babel.yaml b/ethereum/ethereum-reth/babel.yaml new file mode 100644 index 00000000..0501bb18 --- /dev/null +++ b/ethereum/ethereum-reth/babel.yaml @@ -0,0 +1,67 @@ +version: 0.0.2 +container_uri: docker-daemon://ethereum-reth:latest +sku_code: ETH-RETH +org_id: null +description: Ethereum Reth node +visibility: public +protocol_key: ethereum +variants: + - key: reth-mainnet-full + min_cpu: 8 + min_memory_mb: 16000 + min_disk_gb: 1500 + archive_pointers: + - pointer: !store_id ethereum-reth-mainnet-full-v1 + - key: reth-sepolia-full + min_cpu: 4 + min_memory_mb: 8000 + min_disk_gb: 400 + archive_pointers: + - pointer: !store_id ethereum-reth-sepolia-full-v1 + - key: reth-mainnet-archive + min_cpu: 8 + min_memory_mb: 16000 + min_disk_gb: 4000 + archive_pointers: + - pointer: !store_id ethereum-reth-mainnet-archive-v1 + - key: reth-sepolia-archive + min_cpu: 4 + min_memory_mb: 8000 + min_disk_gb: 800 + archive_pointers: + - pointer: !store_id ethereum-reth-sepolia-archive-v1 +firewall_config: + default_in: deny + default_out: allow + rules: + - key: Allow incoming ports TCP + description: Allow TCP inbound traffic on port 80, 443 + protocol: tcp + direction: in + action: allow + ips: + - ip: 0.0.0.0/0 + name: allow all traffic + ports: + - port: 80 + name: HTTP port + - port: 443 + name: HTTPS port + - port: 3000 + name: Grafana port + - key: Allow P2P ports TCP/UDP + description: Allow inbound traffic on P2P ports + protocol: both + direction: in + action: allow + ips: + - ip: 0.0.0.0/0 + name: allow all traffic + ports: + - port: 30303 + name: P2P port + - port: 9000 + name: Metrics port + - port: 9001 + name: Metrics port +properties: [] diff --git a/ethereum/ethereum-reth/config-alloy.template b/ethereum/ethereum-reth/config-alloy.template new file mode 100644 index 00000000..78c5a80c --- /dev/null +++ b/ethereum/ethereum-reth/config-alloy.template @@ -0,0 +1,111 @@ +logging { + level = "warn" +} + +discovery.relabel "metrics_grafana_agent" { + targets = [{ + __address__ = "127.0.0.1:12346", + }] + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } +} + +prometheus.scrape "metrics_grafana_agent" { + targets = discovery.relabel.metrics_grafana_agent.output + forward_to = [prometheus.remote_write.default.receiver] + job_name = "grafana_agent" + metrics_path = "/metrics" +} + +discovery.relabel "metrics_caddy" { + targets = [{ + __address__ = "127.0.0.1:2019", + }] + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } +} + +prometheus.scrape "metrics_caddy" { + targets = discovery.relabel.metrics_caddy.output + forward_to = [prometheus.remote_write.default.receiver] + job_name = "caddy" + metrics_path = "/metrics" +} + +discovery.relabel "metrics_{{ blockchain | lower | replace(from="-", to="_") }}" { + targets = [{ + __address__ = "127.0.0.1:{{ metrics_port }}", + }] + + rule { + target_label = "instance" + replacement = "{{ hostname }}{{ tld }}" + } + + rule { + target_label = "protocol" + replacement = "{{ blockchain | lower | replace(from="-", to="_") }}" + } +} + +prometheus.scrape "metrics_{{ blockchain | lower | replace(from="-", to="_") }}" { + targets = discovery.relabel.metrics_{{ blockchain | lower | replace(from="-", to="_") }}.output + forward_to = [prometheus.remote_write.default.receiver] + job_name = "{{ blockchain | lower | replace(from="-", to="_") }}" + metrics_path = "/{{ blockchain | lower | replace(from="-", to="_") }}/metrics" +} + +local.file_match "babel" { + path_targets = [{ + __address__ = "127.0.0.1", + __path__ = "/var/lib/babel/jobs/logs/*", + instance = "{{ hostname }}{{ tld }}", + job = "babel", + }] +} + +loki.source.file "logs_default_babel" { + targets = local.file_match.babel.targets + forward_to = [loki.write.logs_default.receiver] +} + +discovery.relabel "logs_default_journal_scrape" { + targets = [] + + rule { + source_labels = ["__journal__systemd_unit"] + target_label = "unit" + } + + rule { + source_labels = ["__journal__boot_id"] + target_label = "boot_id" + } + + rule { + source_labels = ["__journal__transport"] + target_label = "transport" + } + + rule { + source_labels = ["__journal_priority_keyword"] + target_label = "level" + } +} + +prometheus.remote_write "default" { + endpoint { + url = "https://prometheus-prod-24-prod-eu-west-2.grafana.net/api/prom/push" + + basic_auth { + username = "1234567" + password = "${GRAFANA_PROM_BASICAUTH}" + } + } +} diff --git a/ethereum/ethereum-reth/main.rhai b/ethereum/ethereum-reth/main.rhai new file mode 100644 index 00000000..399ae1b5 --- /dev/null +++ b/ethereum/ethereum-reth/main.rhai @@ -0,0 +1,137 @@ +import "base" as base; +import "reth" as reth; + +const CADDY_DIR = node_env().data_mount_point + "/caddy"; +const RPC_PORT = 8545; +const WS_PORT = 8546; +const METRICS_PORT = 6665; + +let RETH_CONFIG = reth::base_config(global::METRICS_PORT,global::RPC_PORT,global::WS_PORT,global::CADDY_DIR); + +const BASE_CONFIG = #{ + config_files: RETH_CONFIG.config_files+base::BASE_CONFIG.config_files, + services: RETH_CONFIG.services+base::BASE_CONFIG.services, +}; + +const VARIANTS = #{ + "reth-sepolia-full": #{ + net: "sepolia", + url: "https://sepolia.beaconstate.info", + }, + "reth-mainnet-full": #{ + net: "mainnet", + url: "https://mainnet.checkpoint.sigp.io", + }, + "reth-sepolia-archive": #{ + net: "sepolia", + url: "https://sepolia.beaconstate.info", + }, + "reth-mainnet-archive": #{ + net: "mainnet", + url: "https://mainnet.checkpoint.sigp.io", + }, +}; + +const API_HOST = `http://127.0.0.1:${global::RPC_PORT}`; +const HOME = node_env().protocol_data_path + "/reth"; +const LIGHTHOUSE_DIR = node_env().protocol_data_path + "/lighthouse"; +const NET = VARIANTS[node_env().node_variant].net; +const CADDY_DIR = node_env().data_mount_point + "/caddy"; +const CHECKPOINT_URL = VARIANTS[node_env().node_variant].url; + +const RETH_EXTRA_ARGS = if node_env().node_variant.ends_with("-full") { + "--full" +} else { + "" +}; + +const BABEL_VERSION = "0.9.0"; + +const PLUGIN_CONFIG = #{ + init: #{ + commands: [ + `mkdir -p ${global::HOME}`, + `mkdir -p ${global::LIGHTHOUSE_DIR}`, + `mkdir -p ${global::CADDY_DIR}`, + ], + jobs: [ + #{ + name: "create_jwt", + run_sh: `export HOME=/root && openssl rand -hex 32 | tr -d "\n" > ${global::HOME}/jwt.hex`, + } + ] + }, + services: [ + #{ + name: "reth", + run_sh: `export HOME=/root && /root/bin/reth node \ + --datadir=${global::HOME} \ + --chain=${global::NET} \ + --http \ + --http.addr=127.0.0.1 \ + --http.port=${global::RPC_PORT} \ + --http.api=eth,net,web3,debug,trace,txpool \ + --ws \ + --ws.addr=127.0.0.1 \ + --ws.port=${global::WS_PORT} \ + --ws.api=eth,net,web3,debug,trace,txpool \ + --metrics=127.0.0.1:${global::METRICS_PORT} \ + --authrpc.jwtsecret=${global::HOME}/jwt.hex \ + ${global::RETH_EXTRA_ARGS}`, + shutdown_timeout_secs: 120, + use_blockchain_data: true, + log_timestamp: false, + }, + #{ + name: "lighthouse", + run_sh: `RUST_LOG=info /root/bin/lighthouse bn \ + --network ${global::NET} \ + --datadir ${global::LIGHTHOUSE_DIR} \ + --checkpoint-sync-url ${global::CHECKPOINT_URL} \ + --disable-deposit-contract-sync --http \ + --http-address 127.0.0.1 --http-port 5052 \ + --execution-endpoint http://127.0.0.1:8551 \ + --execution-jwt ${global::HOME}/jwt.hex \ + --metrics --metrics-port 9002 --prune-blobs false \ + --reconstruct-historic-states --block-cache-size 64 \ + --genesis-backfill --historic-state-cache-size 4 \ + --disable-backfill-rate-limiting`, + shutdown_timeout_secs: 120, + use_blockchain_data: true, + log_timestamp: false, + }, + ], + alternative_download: #{ + run_sh: "echo dummy" + }, + upload: #{ + exclude: [ + "jwt.hex", + ] + } +}; + +fn application_status() { + /// "uploading", "downloading", "initializing" statuses are automatically handled by BV if BABEL_CONFIG const is defined + let resp = run_jrpc(#{host: global::API_HOST, method: "/"}); + + if resp.status_code == 200 { + "broadcasting" + } else { + "delinquent" + } +} + +fn height() { + parse_hex(run_jrpc(#{host: global::API_HOST, method: "eth_blockNumber"}).expect(200).result) +} + + +fn sync_status() { + let resp = run_jrpc(#{host: global::API_HOST, method: "eth_syncing"}).expect(200); + if resp.result == false { + "synced" + } else { + "syncing" + } +} \ No newline at end of file diff --git a/ethereum/ethereum-reth/reth.rhai b/ethereum/ethereum-reth/reth.rhai new file mode 100644 index 00000000..d47ebfca --- /dev/null +++ b/ethereum/ethereum-reth/reth.rhai @@ -0,0 +1,39 @@ +fn base_config(metrics_port, rpc_port, ws_port, caddy_dir) { + #{ + config_files: [ + #{ + template: "/var/lib/babel/templates/config-alloy.template", + destination: "/etc/alloy/config.alloy", + params: #{ + hostname: node_env().node_name, + tld: ".n0des.xyz", + blockchain: node_env().node_protocol, + metrics_port: `${metrics_port}`, + metrics_path: "/metrics", + } + }, + #{ + template: "/var/lib/babel/templates/Caddyfile.template", + destination: "/etc/caddy/Caddyfile", + params: #{ + rpc_port: `${rpc_port}`, + ws_port: `${ws_port}`, + hostname: node_env().node_name, + node_ip: node_env().node_ip, + tld: ".n0des.xyz", + data_dir: `${caddy_dir}`, + } + } + ], + services: [ + #{ + name: "alloy", + run_sh: "/usr/bin/alloy run --server.http.listen-addr=127.0.0.1:12346 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy" + }, + #{ + name: "caddy", + run_sh: "/usr/bin/caddy run --config /etc/caddy/Caddyfile" + } + ] + } +} diff --git a/node-base/Dockerfile b/node-base/Dockerfile new file mode 100644 index 00000000..112bb9d0 --- /dev/null +++ b/node-base/Dockerfile @@ -0,0 +1,78 @@ +FROM debian:bookworm-slim@sha256:a165446a88794db4fec31e35e9441433f9552ae048fb1ed26df352d2b537cb96 + +ARG GRAFANA_LOKI_API_KEY +ARG GRAFANA_PROM_API_KEY + +ENV DEBIAN_FRONTEND=noninteractive +ENV GRAFANA_LOKI_API_KEY=${GRAFANA_LOKI_API_KEY} +ENV GRAFANA_PROM_API_KEY=${GRAFANA_PROM_API_KEY} + +ENV GO_VERSION=1.23.1 +ENV CADVISOR_VERSION=0.49.1 + +RUN apt-get update && apt-get install -y \ + wget \ + curl \ + gnupg \ + debian-keyring \ + debian-archive-keyring \ + apt-transport-https \ + software-properties-common \ + vim \ + psmisc \ + net-tools \ + nano \ + git \ + build-essential \ + ca-certificates \ + libc6 \ + libfontconfig1 \ + apt-utils + +RUN mkdir -p /etc/apt/keyrings/ && \ + wget --inet4-only --no-check-certificate -qO - http://apt.grafana.com/gpg.key | gpg --dearmor -o /etc/apt/keyrings/grafana.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] http://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list + +RUN curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg && \ + curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' > /etc/apt/sources.list.d/caddy-xcaddy.list + +RUN apt-get update && apt-get install -y xcaddy alloy grafana + +RUN wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz && \ + tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz && \ + rm go${GO_VERSION}.linux-amd64.tar.gz + +RUN wget https://packages.cloud.google.com/apt/doc/apt-key.gpg -qO /usr/share/keyrings/prometheus-archive-keyring.gpg && \ + curl -fsSL https://ftp-master.debian.org/keys/archive-key-11.asc | gpg --batch --yes --dearmor -o /usr/share/keyrings/debian-archive-keyring-11.gpg && \ + curl -fsSL https://ftp-master.debian.org/keys/archive-key-12.asc | gpg --batch --yes --dearmor -o /usr/share/keyrings/debian-archive-keyring-12.gpg && \ + curl -fsSL https://ftp-master.debian.org/keys/archive-key-12-security.asc | gpg --dearmor -o /usr/share/keyrings/debian-archive-keyring-12-security.gpg + +ENV PATH="/usr/local/go/bin:${PATH}" +ENV GOROOT="/usr/local/go" + +RUN cd / && \ + xcaddy build --with github.com/caddy-dns/cloudflare && \ + mv ./caddy /usr/bin/ && chmod +x /usr/bin/caddy && \ + mkdir -p /etc/caddy + +RUN wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && \ + sh /tmp/netdata-kickstart.sh --stable-channel --disable-telemetry --non-interactive --dont-start-it --native-only --disable-cloud + +RUN mkdir -p /var/lib/babel/templates && \ + mkdir -p /var/lib/babel/plugins + +RUN mkdir -p /etc/grafana /var/lib/grafana /usr/share/grafana/conf/provisioning/datasources \ + /usr/share/grafana/conf/provisioning/dashboards /var/lib/grafana/dashboards + +COPY config/grafana-datasource.yml /usr/share/grafana/conf/provisioning/datasources/datasource.yml +COPY config/grafana-dashboard.yml /usr/share/grafana/conf/provisioning/dashboards/dashboard.yml + +#COPY config/caddy-dashboard.json /var/lib/grafana/dashboards + +RUN apt-get autoremove -y && apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +COPY base.rhai /var/lib/babel/plugin/base.rhai +COPY templates/netdata-conf.template /var/lib/babel/templates/netdata-conf.template +COPY templates/netdata-stream-conf.template /var/lib/babel/templates/netdata-stream-conf.template +COPY templates/caddy-dashboard.template /var/lib/babel/templates/caddy-dashboard.template \ No newline at end of file diff --git a/node-base/Dockerfile.ubuntu b/node-base/Dockerfile.ubuntu new file mode 100644 index 00000000..9df6a6b4 --- /dev/null +++ b/node-base/Dockerfile.ubuntu @@ -0,0 +1,51 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + wget \ + curl \ + gnupg \ + debian-keyring \ + debian-archive-keyring \ + apt-transport-https \ + software-properties-common \ + vim \ + psmisc \ + net-tools \ + nano \ + git \ + build-essential + +RUN mkdir -p /etc/apt/keyrings/ && \ + wget --inet4-only --no-check-certificate -qO - http://apt.grafana.com/gpg.key | gpg --dearmor -o /etc/apt/keyrings/grafana.gpg && \ + echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] http://apt.grafana.com stable main" > /etc/apt/sources.list.d/grafana.list + +RUN curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg && \ + curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' > /etc/apt/sources.list.d/caddy-xcaddy.list + +RUN add-apt-repository -y universe && apt-get update + +RUN apt-get install -y alloy vim psmisc xcaddy + +RUN wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz && \ + tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz && \ + rm go1.23.1.linux-amd64.tar.gz + +ENV PATH="/usr/local/go/bin:${PATH}" +ENV GOROOT="/usr/local/go" + +RUN cd / && \ + xcaddy build --with github.com/caddy-dns/cloudflare && \ + mv ./caddy /usr/bin/ && chmod +x /usr/bin/caddy && \ + mkdir -p /etc/caddy + +RUN wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && \ + sh /tmp/netdata-kickstart.sh --stable-channel --disable-telemetry --non-interactive --dont-start-it --native-only --disable-cloud + +RUN mkdir -p /var/lib/babel/templates && \ + mkdir -p /var/lib/babel/plugins + +COPY base.rhai /var/lib/babel/plugin/base.rhai +COPY netdata-conf.template /var/lib/babel/templates/netdata-conf.template +COPY netdata-stream-conf.template /var/lib/babel/templates/netdata-stream-conf.template diff --git a/node-base/base.rhai b/node-base/base.rhai new file mode 100644 index 00000000..60f9df3b --- /dev/null +++ b/node-base/base.rhai @@ -0,0 +1,31 @@ +export const BASE_CONFIG = #{ + config_files: [ + #{ + template: "/var/lib/babel/templates/netdata-conf.template", + destination: "/etc/netdata/netdata.conf", + params: #{} + }, + #{ + template: "/var/lib/babel/templates/netdata-stream-conf.template", + destination: "/etc/netdata/stream.conf", + params: #{} + }, + #{ + template: "/var/lib/babel/templates/caddy-dashboard.template", + destination: "/var/lib/grafana/dashboards/caddy-dashboard.json", + params: #{ + node_name: node_env().node_name + }, + }, + ], + services : [ + #{ + name: "grafana", + run_sh: "GF_USERS_DEFAULT_ROLE=Viewer \ + GF_AUTH_ANONYMOUS_ENABLED=true \ + GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer \ + GF_ROLE_VIEWER_EDITORS_CAN_SAVE=false \ + grafana-server --homepath /usr/share/grafana --config /etc/grafana/grafana.ini" + }, + ] +} diff --git a/node-base/config/caddy-dashboard.json b/node-base/config/caddy-dashboard.json new file mode 100644 index 00000000..0292ece2 --- /dev/null +++ b/node-base/config/caddy-dashboard.json @@ -0,0 +1,2361 @@ +{ + "annotations": { + "list": [ + { + "$$hashKey": "object:1058", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Monitoring Caddy Server with Grafana (Prometheus + Loki)", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 1, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "panels": [], + "title": "Synthesis", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "P520024444597A5C0" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "srv0" + }, + "properties": [ + { + "id": "displayName", + "value": "Port: 80" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "srv1" + }, + "properties": [ + { + "id": "displayName", + "value": "Port: 443" + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 1 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "caddy_http_requests_total{instance=~\"$host\",handler=~\"$handler\"}", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{server}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Number of requests processed", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "P520024444597A5C0" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 1 + }, + "id": 21, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 50 + }, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "rate(caddy_http_requests_total{instance=~\"$host\",handler=~\"$handler\"}[5m])", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate of requests per second (avg 5m)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "P520024444597A5C0" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 10, + "y": 1 + }, + "id": 24, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 50 + }, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(rate(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}[5m])) by (handler)", + "format": "heatmap", + "hide": false, + "interval": "", + "legendFormat": "{{le}}", + "range": true, + "refId": "A" + } + ], + "title": "Average response time", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "P520024444597A5C0" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 15, + "y": 1 + }, + "id": 19, + "maxDataPoints": 10000, + "options": { + "colorMode": "background_solid", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "diff" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 50 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}) by (code)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "HTTP Status Errors", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "400", + "401", + "404", + "410", + "500" + ], + "reducer": "sum" + }, + "replaceFields": true + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 20, + "y": 1 + }, + "id": 23, + "options": { + "colorMode": "background_solid", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "diff" + ], + "fields": "", + "values": false + }, + "showPercentChange": true, + "text": { + "valueSize": 50 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(caddy_http_request_errors_total{instance=~\"$host\",handler=~\"$handler\"}) by (handler)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Middleware Errors", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "400", + "401", + "404", + "410", + "500" + ], + "reducer": "sum" + }, + "replaceFields": true + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 8, + "panels": [], + "title": "API REST Caddy", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 7 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "avg(rate(caddy_http_requests_total{instance=~\"$host\",handler=~\"$handler\"}[5m])*10) by (handler)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "HTTP(S) requests total", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "sum" + }, + "replaceFields": false + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 7 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "avg(avg_over_time(caddy_http_requests_in_flight{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (handler)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Requests In Flight", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "sum" + }, + "replaceFields": false + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 15, + "w": 14, + "x": 10, + "y": 7 + }, + "id": 22, + "maxDataPoints": 100, + "options": { + "calculate": false, + "calculation": { + "xBuckets": { + "mode": "size" + }, + "yBuckets": { + "mode": "size", + "scale": { + "type": "linear" + }, + "value": "" + } + }, + "cellGap": 2, + "cellValues": {}, + "color": { + "exponent": 0.5, + "fill": "#b4ff00", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Magma", + "steps": 128 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "showValue": "never", + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false, + "unit": "s" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(increase(caddy_http_request_duration_seconds_bucket{instance=~\"$host\", handler=~\"$handler\"}[5m])) by (le)", + "format": "heatmap", + "hide": false, + "interval": "", + "legendFormat": "{{le}}", + "range": true, + "refId": "A" + } + ], + "title": "Request Duration (heatmap)", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 40, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "Median response duration in seconds", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 12 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "histogram_quantile(0.5, sum by (le) (rate(caddy_http_response_duration_seconds_bucket[5m])))", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Median response duration in seconds", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 12 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "sum(rate(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}[5m])) by (handler)", + "interval": "", + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Request/Response duration seconds", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 40, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line+area" + } + }, + "displayName": "Median response size in bytes", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red" + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 17 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "histogram_quantile(0.5, sum by (le) (rate(caddy_http_response_size_bytes_bucket{instance=~\"$host\"}[5m])))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Median response size in bytes", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 17 + }, + "id": 17, + "maxDataPoints": 10000, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(irate(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (code)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Requests by Response Code", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "sum" + }, + "replaceFields": false + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 40, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 10, + "x": 0, + "y": 22 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.75, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "E" + } + ], + "title": "Request Duration (percentile)", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "Time", + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "histogram_quantile(0.5, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p50", + "histogram_quantile(0.75, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p75", + "histogram_quantile(0.90, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p90", + "histogram_quantile(0.95, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p95", + "histogram_quantile(0.99, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p99" + } + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "fixed" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "fieldMinMax": false, + "mappings": [], + "unit": "locale" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "^([2-2]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#aaffcab0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "^([3-3]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "^([4-4]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "^([5-5]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 10, + "y": 22 + }, + "id": 18, + "maxDataPoints": 10000, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "list", + "placement": "right", + "showLegend": true, + "values": [ + "value", + "percent" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "diff" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}) by (code)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "HTTP Status", + "transparent": true, + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 22 + }, + "id": 29, + "maxDataPoints": 10000, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(code) (caddy_http_request_duration_seconds_count{instance=~\"$host\", handler=~\"$handler\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{code}}", + "refId": "A", + "useBackend": false + } + ], + "title": "HTTP Status Errors", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "400", + "401", + "404", + "410", + "500" + ], + "reducer": "sum" + }, + "replaceFields": true + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 16, + "y": 27 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "avg(rate(caddy_http_request_errors_total{instance=~\"$host\",handler=~\"$handler\"}[5m])*10) by (handler)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "range": true, + "refId": "A" + } + ], + "title": "Middleware Errors", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 31 + }, + "id": 7, + "panels": [], + "title": "Logs Caddy", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 287 + } + ] + } + ] + }, + "gridPos": { + "h": 17, + "w": 18, + "x": 0, + "y": 32 + }, + "id": 26, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "detected_level" + } + ] + }, + "pluginVersion": "11.4.0-78571", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "editorMode": "code", + "expr": "{filename=\"$logfile\", instance=~\"$host\"} | pattern `<_>|` | line_format `{{.message}}` | json | line_format \"{{.status}}\"", + "queryType": "range", + "refId": "A" + } + ], + "title": "Rag logs in table", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [], + "keepTime": true, + "replace": true, + "source": "labels" + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "fieldMinMax": false, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Client IP" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Geo IP", + "url": "https://iplocation.io/ip/${__data.fields[\"Client IP\"]}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 202 + } + ] + } + ] + }, + "gridPos": { + "h": 17, + "w": 6, + "x": 18, + "y": 32 + }, + "id": 28, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Time" + } + ] + }, + "pluginVersion": "11.4.0-78571", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "editorMode": "builder", + "expr": "rate({filename=\"$logfile\"} | json | line_format `{{.status}}` [$__auto])", + "queryType": "range", + "refId": "A" + } + ], + "title": "Client IP", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [], + "keepTime": true, + "replace": true, + "source": "labels" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Status Count": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "Time": { + "aggregations": [], + "operation": "groupby" + }, + "request_client_ip": { + "aggregations": [], + "operation": "groupby" + }, + "status": { + "aggregations": [] + } + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "status\\status": true + }, + "includeByName": {}, + "indexByName": { + "200": 6, + "301": 5, + "302": 4, + "304": 3, + "404": 2, + "410": 1, + "500": 0, + "status\\status": 7 + }, + "renameByName": { + "Time": "", + "request_client_ip": "Client IP" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 33, + "w": 24, + "x": 0, + "y": 49 + }, + "id": 25, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": true, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "pluginVersion": "11.4.0-78029", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "editorMode": "code", + "expr": "{filename=\"$logfile\",instance=~\"$host\"} | pattern `<_>|` | line_format `{{.message}}` | json | line_format \"- [HTTP Status: {{.status}} - Duration: {{.duration}} - Client IP: {{.request_client_ip}} - Request: {{.request_uri}}]\"", + "queryType": "range", + "refId": "A" + } + ], + "title": "Raw logs", + "type": "logs" + } + ], + "preload": false, + "refresh": "", + "schemaVersion": 40, + "tags": [ + "Caddy" + ], + "templating": { + "list": [ + { + "current": { + "text": "grafanacloud-blockjoy-logs", + "value": "grafanacloud-logs" + }, + "hide": 2, + "includeAll": false, + "label": "Datasource Loki", + "name": "datasourceloki", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "/grafanacloud-blockjoy-logs/", + "type": "datasource" + }, + { + "current": { + "text": "/var/lib/babel/jobs/logs/caddy", + "value": "/var/lib/babel/jobs/logs/caddy" + }, + "datasource": { + "type": "loki", + "uid": "PA2B326B1CBC5D7D2" + }, + "definition": "", + "includeAll": false, + "label": "Log file", + "name": "logfile", + "options": [], + "query": { + "label": "filename", + "refId": "LokiVariableQueryEditor-VariableQuery", + "stream": "{instance=~\"$host\"}", + "type": 1 + }, + "refresh": 1, + "regex": "/caddy/", + "sort": 1, + "type": "query" + }, + { + "current": { + "text": "grafanacloud-blockjoy-prom", + "value": "grafanacloud-prom" + }, + "hide": 2, + "includeAll": false, + "label": "Datasource Prometheus", + "name": "datasourceprometheus", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "/grafanacloud-blockjoy-prom/", + "type": "datasource" + }, + { + "allValue": ".*", + "current": { + "text": "annually-whole-caribou.n0des.xyz", + "value": "annually-whole-caribou.n0des.xyz" + }, + "datasource": { + "type": "prometheus", + "uid": "P520024444597A5C0" + }, + "definition": "label_values(up{job=~\"caddy\"},instance)", + "includeAll": false, + "label": "Host", + "name": "host", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(up{job=~\"caddy\"},instance)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "P520024444597A5C0" + }, + "definition": "label_values({__name__=~\"^caddy.*\", handler=~\".*\"},handler)", + "includeAll": true, + "label": "Handler", + "name": "handler", + "options": [], + "query": { + "qryType": 1, + "query": "label_values({__name__=~\"^caddy.*\", handler=~\".*\"},handler)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Caddy Monitoring", + "uid": "bdh0qkohvn5s0a", + "version": 3, + "weekStart": "" +} diff --git a/node-base/config/grafana-dashboard.yml b/node-base/config/grafana-dashboard.yml new file mode 100644 index 00000000..e0a52503 --- /dev/null +++ b/node-base/config/grafana-dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 +providers: + - name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 + options: + path: /var/lib/grafana/dashboards + dashboards: + - file: /var/lib/grafana/dashboards/caddy-dashboard.json diff --git a/node-base/config/grafana-datasource.yml b/node-base/config/grafana-datasource.yml new file mode 100644 index 00000000..ae35edbe --- /dev/null +++ b/node-base/config/grafana-datasource.yml @@ -0,0 +1,20 @@ +apiVersion: 1 +datasources: + - name: grafanacloud-prom + type: prometheus + url: https://prometheus-prod-24-prod-eu-west-2.grafana.net/api/prom + access: server + isDefault: true + basicAuth: true + basicAuthUser: 1322871 + secureJsonData: + basicAuthPassword: "${GRAFANA_PROM_API_KEY}" + - name: grafanacloud-logs + type: loki + url: https://logs-prod-012.grafana.net + access: server + isDefault: false + basicAuth: true + basicAuthUser: 760471 + secureJsonData: + basicAuthPassword: "${GRAFANA_LOKI_API_KEY}" diff --git a/node-base/templates/caddy-dashboard.template b/node-base/templates/caddy-dashboard.template new file mode 100644 index 00000000..a3a2cd8d --- /dev/null +++ b/node-base/templates/caddy-dashboard.template @@ -0,0 +1,2366 @@ +{% raw %} +{ + "annotations": { + "list": [ + { + "$$hashKey": "object:1058", + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "Monitoring Caddy Server with Grafana (Prometheus + Loki)", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 1, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "panels": [], + "title": "Synthesis", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "srv0" + }, + "properties": [ + { + "id": "displayName", + "value": "Port: 80" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "srv1" + }, + "properties": [ + { + "id": "displayName", + "value": "Port: 443" + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 1 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "caddy_http_requests_total{instance=~\"$host\",handler=~\"$handler\"}", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{server}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Number of requests processed", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 1 + }, + "id": 21, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 50 + }, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "rate(caddy_http_requests_total{instance=~\"$host\",handler=~\"$handler\"}[5m])", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Rate of requests per second (avg 5m)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "text", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 10, + "y": 1 + }, + "id": 24, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 50 + }, + "textMode": "value", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(rate(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}[5m])) by (handler)", + "format": "heatmap", + "hide": false, + "interval": "", + "legendFormat": "{{le}}", + "range": true, + "refId": "A" + } + ], + "title": "Average response time", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 15, + "y": 1 + }, + "id": 19, + "maxDataPoints": 10000, + "options": { + "colorMode": "background_solid", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "diff" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": { + "valueSize": 50 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}) by (code)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "HTTP Status Errors", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "400", + "401", + "404", + "410", + "500" + ], + "reducer": "sum" + }, + "replaceFields": true + } + } + ], + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 20, + "y": 1 + }, + "id": 23, + "options": { + "colorMode": "background_solid", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "diff" + ], + "fields": "", + "values": false + }, + "showPercentChange": true, + "text": { + "valueSize": 50 + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(caddy_http_request_errors_total{instance=~\"$host\",handler=~\"$handler\"}) by (handler)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Middleware Errors", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "400", + "401", + "404", + "410", + "500" + ], + "reducer": "sum" + }, + "replaceFields": true + } + } + ], + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 8, + "panels": [], + "title": "API REST Caddy", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 7 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "avg(rate(caddy_http_requests_total{instance=~\"$host\",handler=~\"$handler\"}[5m])*10) by (handler)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "HTTP(S) requests total", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "sum" + }, + "replaceFields": false + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 7 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "avg(avg_over_time(caddy_http_requests_in_flight{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (handler)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Requests In Flight", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "sum" + }, + "replaceFields": false + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 15, + "w": 14, + "x": 10, + "y": 7 + }, + "id": 22, + "maxDataPoints": 100, + "options": { + "calculate": false, + "calculation": { + "xBuckets": { + "mode": "size" + }, + "yBuckets": { + "mode": "size", + "scale": { + "type": "linear" + }, + "value": "" + } + }, + "cellGap": 2, + "cellValues": {}, + "color": { + "exponent": 0.5, + "fill": "#b4ff00", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Magma", + "steps": 128 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "showValue": "never", + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false, + "unit": "s" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(increase(caddy_http_request_duration_seconds_bucket{instance=~\"$host\", handler=~\"$handler\"}[5m])) by (le)", + "format": "heatmap", + "hide": false, + "interval": "", + "legendFormat": "{{le}}", + "range": true, + "refId": "A" + } + ], + "title": "Request Duration (heatmap)", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 40, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "Median response duration in seconds", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 12 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "histogram_quantile(0.5, sum by (le) (rate(caddy_http_response_duration_seconds_bucket[5m])))", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Median response duration in seconds", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 12 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "sum(rate(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}[5m])) by (handler)", + "interval": "", + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Request/Response duration seconds", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 40, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "log" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line+area" + } + }, + "displayName": "Median response size in bytes", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red" + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 0, + "y": 17 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "expr": "histogram_quantile(0.5, sum by (le) (rate(caddy_http_response_size_bytes_bucket{instance=~\"$host\"}[5m])))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "title": "Median response size in bytes", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 5, + "x": 5, + "y": 17 + }, + "id": 17, + "maxDataPoints": 10000, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(irate(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (code)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "Requests by Response Code", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "reducer": "sum" + }, + "replaceFields": false + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 40, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 10, + "x": 0, + "y": 22 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.99, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.90, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.75, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.5, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\"$host\",handler=~\"$handler\"}[1m])) by (le))", + "format": "time_series", + "hide": false, + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "__auto", + "refId": "E" + } + ], + "title": "Request Duration (percentile)", + "transformations": [ + { + "id": "joinByField", + "options": { + "byField": "Time", + "mode": "outer" + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "histogram_quantile(0.5, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p50", + "histogram_quantile(0.75, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p75", + "histogram_quantile(0.90, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p90", + "histogram_quantile(0.95, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p95", + "histogram_quantile(0.99, sum(rate(caddy_http_request_duration_seconds_bucket{instance=~\".*\",handler=~\".*\"}[1m])) by (le))": "p99" + } + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "fixed" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "fieldMinMax": false, + "mappings": [], + "unit": "locale" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "^([2-2]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "#aaffcab0", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "^([3-3]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "^([4-4]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "^([5-5]\\d*)$" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 6, + "x": 10, + "y": 22 + }, + "id": 18, + "maxDataPoints": 10000, + "options": { + "displayLabels": [], + "legend": { + "displayMode": "list", + "placement": "right", + "showLegend": true, + "values": [ + "value", + "percent" + ] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "diff" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum(caddy_http_request_duration_seconds_count{instance=~\"$host\",handler=~\"$handler\"}) by (code)", + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "title": "HTTP Status", + "transparent": true, + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 22 + }, + "id": 29, + "maxDataPoints": 10000, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(code) (caddy_http_request_duration_seconds_count{instance=~\"$host\", handler=~\"$handler\"})", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{code}}", + "refId": "A", + "useBackend": false + } + ], + "title": "HTTP Status Errors", + "transformations": [ + { + "id": "calculateField", + "options": { + "mode": "reduceRow", + "reduce": { + "include": [ + "400", + "401", + "404", + "410", + "500" + ], + "reducer": "sum" + }, + "replaceFields": true + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-red", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsZero", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + }, + { + "matcher": { + "id": "byValue", + "options": { + "op": "gte", + "reducer": "allIsNull", + "value": 0 + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": true, + "tooltip": true, + "viz": false + } + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 16, + "y": 27 + }, + "id": 11, + "options": { + "legend": { + "calcs": [], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "11.3.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "avg(rate(caddy_http_request_errors_total{instance=~\"$host\",handler=~\"$handler\"}[5m])*10) by (handler)", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{handler}}", + "range": true, + "refId": "A" + } + ], + "title": "Middleware Errors", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 31 + }, + "id": 7, + "panels": [], + "title": "Logs Caddy", + "type": "row" + }, + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 287 + } + ] + } + ] + }, + "gridPos": { + "h": 17, + "w": 18, + "x": 0, + "y": 32 + }, + "id": 26, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "detected_level" + } + ] + }, + "pluginVersion": "11.4.0-78571", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "editorMode": "code", + "expr": "{filename=\"$logfile\", instance=~\"$host\"} | pattern `<_>|` | line_format `{{.message}}` | json | line_format \"{{.status}}\"", + "queryType": "range", + "refId": "A" + } + ], + "title": "Rag logs in table", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [], + "keepTime": true, + "replace": true, + "source": "labels" + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "center", + "cellOptions": { + "type": "auto" + }, + "filterable": true, + "inspect": false + }, + "fieldMinMax": false, + "links": [], + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Client IP" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Geo IP", + "url": "https://iplocation.io/ip/${__data.fields[\"Client IP\"]}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Time" + }, + "properties": [ + { + "id": "custom.width", + "value": 202 + } + ] + } + ] + }, + "gridPos": { + "h": 17, + "w": 6, + "x": 18, + "y": 32 + }, + "id": 28, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "enablePagination": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Time" + } + ] + }, + "pluginVersion": "11.4.0-78571", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "editorMode": "builder", + "expr": "rate({filename=\"$logfile\"} | json | line_format `{{.status}}` [$__auto])", + "queryType": "range", + "refId": "A" + } + ], + "title": "Client IP", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [], + "keepTime": true, + "replace": true, + "source": "labels" + } + }, + { + "id": "groupBy", + "options": { + "fields": { + "Status Count": { + "aggregations": [ + "count" + ], + "operation": "aggregate" + }, + "Time": { + "aggregations": [], + "operation": "groupby" + }, + "request_client_ip": { + "aggregations": [], + "operation": "groupby" + }, + "status": { + "aggregations": [] + } + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": { + "status\\status": true + }, + "includeByName": {}, + "indexByName": { + "200": 6, + "301": 5, + "302": 4, + "304": 3, + "404": 2, + "410": 1, + "500": 0, + "status\\status": 7 + }, + "renameByName": { + "Time": "", + "request_client_ip": "Client IP" + } + } + } + ], + "type": "table" + }, + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 33, + "w": 24, + "x": 0, + "y": 49 + }, + "id": 25, + "options": { + "dedupStrategy": "none", + "enableLogDetails": true, + "prettifyLogMessage": false, + "showCommonLabels": true, + "showLabels": false, + "showTime": true, + "sortOrder": "Descending", + "wrapLogMessage": false + }, + "pluginVersion": "11.4.0-78029", + "targets": [ + { + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "editorMode": "code", + "expr": "{filename=\"$logfile\",instance=~\"$host\"} | pattern `<_>|` | line_format `{{.message}}` | json | line_format \"- [HTTP Status: {{.status}} - Duration: {{.duration}} - Client IP: {{.request_client_ip}} - Request: {{.request_uri}}]\"", + "queryType": "range", + "refId": "A" + } + ], + "title": "Raw logs", + "type": "logs" + } + ], + "preload": false, + "refresh": "", + "schemaVersion": 40, + "tags": [ + "Caddy" + ], + "templating": { + "list": [ + { + "current": { + "text": "grafanacloud-blockjoy-logs", + "value": "grafanacloud-logs" + }, + "hide": 2, + "includeAll": false, + "label": "Datasource Loki", + "name": "datasourceloki", + "options": [], + "query": "loki", + "refresh": 1, + "regex": "/grafanacloud-blockjoy-logs/", + "type": "datasource" + }, + { + "current": { + "text": "/var/lib/babel/jobs/logs/caddy", + "value": "/var/lib/babel/jobs/logs/caddy" + }, + "datasource": { + "type": "loki", + "uid": "grafanacloud-logs" + }, + "definition": "", + "includeAll": false, + "label": "Log file", + "name": "logfile", + "options": [], + "query": { + "label": "filename", + "refId": "LokiVariableQueryEditor-VariableQuery", + "stream": "{instance=~\"$host\"}", + "type": 1 + }, + "refresh": 1, + "regex": "/caddy/", + "sort": 1, + "type": "query" + }, + { + "current": { + "text": "grafanacloud-blockjoy-prom", + "value": "grafanacloud-prom" + }, + "hide": 2, + "includeAll": false, + "label": "Datasource Prometheus", + "name": "datasourceprometheus", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "/grafanacloud-blockjoy-prom/", + "type": "datasource" + }, + { + "allValue": ".*", + "current": { +{% endraw %} + "text": "{{ node_name }}.n0des.xyz", + "value": "{{ node_name }}.n0des.xyz" +{% raw %} + }, + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "definition": "label_values(up{job=~\"caddy\"},instance)", + "hide": 2, + "includeAll": false, + "label": "Host", + "name": "host", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(up{job=~\"caddy\"},instance)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "type": "query" + }, + { + "allValue": ".*", + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "definition": "label_values({__name__=~\"^caddy.*\", handler=~\".*\"},handler)", + "includeAll": true, + "label": "Handler", + "name": "handler", + "options": [], + "query": { + "qryType": 1, + "query": "label_values({__name__=~\"^caddy.*\", handler=~\".*\"},handler)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Caddy Monitoring", + "uid": "bdh0qkohvn5s0a", + "version": 3, + "weekStart": "" +} +{% endraw %} diff --git a/node-base/templates/netdata-conf.template b/node-base/templates/netdata-conf.template new file mode 100644 index 00000000..85d1a726 --- /dev/null +++ b/node-base/templates/netdata-conf.template @@ -0,0 +1,9 @@ +[global] + run as user = root +[web] + mode = none +[db] + mode = ram + update every = 5 +[health] + enabled = no diff --git a/node-base/templates/netdata-stream-conf.template b/node-base/templates/netdata-stream-conf.template new file mode 100644 index 00000000..5375ef78 --- /dev/null +++ b/node-base/templates/netdata-stream-conf.template @@ -0,0 +1,10 @@ +[stream] + enabled = yes + destination = tcp:netdata.blkjy.io + api key = 894aa814-e672-4df1-9e8c-3bc919f1b905 + timeout seconds = 60 + default port = 19996 + send charts matching = * + buffer size bytes = 10485760 + reconnect delay seconds = 5 + initial clock resync iterations = 60 diff --git a/protocols.json b/protocols.json deleted file mode 100644 index 04d895c8..00000000 --- a/protocols.json +++ /dev/null @@ -1 +0,0 @@ -// No longer used!!! All protocols are now tracked via the proto-tracker API. \ No newline at end of file diff --git a/renovate.json b/renovate.json index 5db72dd6..4756f0e8 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,38 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:recommended" - ] + "config:base", + "docker:enableMajor" + ], + "enabledManagers": ["dockerfile", "github-actions"], + "dependencyDashboard": true, + "pinDigests": true, + "repositories": ["blockjoy/blockjoy-protocols"], + "packageRules": [ + { + "matchDatasources": ["docker"], + "matchUpdateTypes": ["major", "minor", "patch", "digest"], + "enabled": true + }, + { + "description": "All internal image updates", + "matchPaths": ["**/Dockerfile"], + "matchPackagePatterns": ["ghcr.io/blockjoy/*"], + "automerge": true, + "automergeType": "branch", + "groupName": "docker-dependencies" + } + ], + "regexManagers": [ + { + "fileMatch": ["^.+/Dockerfile$"], + "matchStrings": [ + "ARG .*_IMAGE=ghcr.io/blockjoy/(?[^:]+):(?[^\\s]+)" + ], + "datasourceTemplate": "docker", + "registryUrlTemplate": "https://ghcr.io" + } + ], + "prHourlyLimit": 0, + "prConcurrentLimit": 0 }