geoffwilliams@home:~$

ClickStack Homelab

I stumbled on ClickStack a couple of months ago. For a long time I’ve been looking for a logging solution for the homelab. I’ve played with Loki before but never got arround to onboarding all my lab systems.

If you want to know more about deploying and using ClickStack, The Manual is excellent.

Based on this doc, I was able to get up and running with the docker.io/clickhouse/clickstack-all-in-one container very quickly in docker on my laptop.

The all-in-one image bundles everything you need for a functioning log collector and frontend:

ClickStack into “production”

Disclaimer: this is for a homelab!

I found a permanent home for ClickStack on my “services” mini PC.

Podman Quadlet

I was able to run ClickStack as a first class homelab service quite easily with Podman Quadlet like I do for some of my other lab services, like this:

/etc/containers//systemd/clickstack-pod.kube

[Install]
WantedBy=default.target

[Unit]

[Kube]
Yaml=/etc/containers/systemd/clickstack-pod.yml

# ui/hyperdx
PublishPort=127.0.0.1:8080:8080

# clickhouse/db
PublishPort=127.0.0.1:8123:8123

# otel/grpc
PublishPort=127.0.0.1:4317:4317

# otel/http
PublishPort=127.0.0.1:4318:4318

/etc/containers//systemd/clickstack-pod.yml

apiVersion: v1
kind: Pod
metadata:
  annotations:
    io.kubernetes.cri-o.ContainerType/app: container
    io.kubernetes.cri-o.TTY/app: "false"
    io.podman.annotations.autoremove/app: "FALSE"
    io.podman.annotations.init/app: "FALSE"
    io.podman.annotations.privileged/app: "FALSE"
    io.podman.annotations.publish-all/app: "FALSE"
  labels:
    app: clickstack
  name: clickstack
spec:
  automountServiceAccountToken: false
  dnsPolicy: None
  containers:
  - image: docker.io/clickhouse/clickstack-all-in-one:2.15.1
    name: app
    env:
    - name: FRONTEND_URL
      value: "https://clickstack.infrastructure.asio"
    ports:
    - containerPort: 8123
      hostPort: 8123
    - containerPort: 8080
      hostPort: 8080      
    - containerPort: 4317
      hostPort: 4317
    - containerPort: 4318
      hostPort: 4318 
    resources: {}
    securityContext:
      capabilities:
        drop:
        - NET_ADMIN
        - CAP_MKNOD
        - CAP_AUDIT_WRITE
    volumeMounts:
    - mountPath: /data/db
      name: clickstack-data-vol
      subPath: db
    - mountPath: /var/lib/clickhouse
      name: clickstack-data-vol
      subPath: clickhouse
    - mountPath: /var/log/clickhouse-server
      name: clickstack-log-vol

  enableServiceLinks: false
  hostname: clickstack
  restartPolicy: Never
  volumes:
  - hostPath:
      path: /data/containers/clickstack/data
      type: Directory
    name: clickstack-data-vol
  - hostPath:
      path: /data/containers/clickstack/log
      type: Directory
    name: clickstack-log-vol
status: {}

Traefik frontend

With traefik already setup on the host, I only needed a single drop-in:

/etc/traefik/traefik.d/clickstack.infrastructure.asio.yml

# ansible managed

#
# HTTP
#
http:
  routers:
    clickstack-http:
      # HostSNI only for TCP - we get the real headers for http routes
      # as TLS already terminated and http call parsed
      rule: "Host(`clickstack.infrastructure.asio`)"

      tls: {}

      entryPoints:
      - infrastructure.https
      service: clickstack-8080

  services:
    clickstack-8080:
      loadBalancer:
        servers:
          - url: "http://localhost:8080"

#
# TCP
#
tcp:
  routers:
    otel-grpc:
      # always match - if not tls, there is no such thing as a header
      # so there is nothing to check against so just accept any value
      rule: "HostSNI(`*`)"
      entryPoints:
      - infrastructure.otel-grpc
      service: clickstack-4317
    otel-http:
      # always match - if not tls, there is no such thing as a header
      # so there is nothing to check against so just accept any value
      rule: "HostSNI(`*`)"
      entryPoints:
      - infrastructure.otel-http
      service: clickstack-4318


  services:
    clickstack-4317:
      loadBalancer:
        servers:
          - address: "localhost:4317"
    clickstack-4318:
      loadBalancer:
        servers:
          - address: "localhost:4318"

Setting up ClickStack

With these items setup, hitting the URL I configured in the browser got me straight to a set password dialog in HyperDX. Done.

Client setup

Now its time to send logs to ClickStack. I have a lone Windows machine on the network which is a source of constant pain, so this is the perfect starting point.

To collect logs from Windows:

  1. Install OTEL client for windows and install it as a windows service
  2. Install the OTEL contrib files for windows which are needed to collect the logs from Event Viewer
  3. Finally, configure the OTEL client in C:\Program Files\OpenTelemetry Collector\config.yaml. You will need a token for the ClickStack OTEL collector which you can find in the HyperDX UI by looking for Your Ingestion API Key:
# To limit exposure to denial of service attacks, change the host in endpoints below from 0.0.0.0 to a specific network interface.
# See https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/security-best-practices.md#safeguards-against-denial-of-service-attacks


receivers:
  windowseventlog/application:
     channel: application
  windowseventlog/system:
     channel: system


processors:
  batch:
  resource:
    attributes:
      - key: host.os
        value: windows
        action: insert


exporters:
  otlp/logs:
    endpoint: clickstack.infrastructure.asio:4317
    tls:
      insecure: true
    headers:
      authorization: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    compression: gzip

service:
  pipelines:
    logs:
      receivers:
        - windowseventlog/application
        - windowseventlog/system
      processors:
        - batch
      exporters:
        - otlp/logs

If everything worked, windows will start sending event logs to the OTEL collector previously exposed, and you will see events landing live in the UI:

clickstack working

Verdict

This is a very slick and easy to use stack. The hardest part of setting this up turned out to be the OTEL client on Windows. I think its safe to say if it works on Windows, I’ll be able to collect the rest of the logs I need no problem, although I’m sure kubernetes logs will be a pain.

For an enterprise grade logging solution, I’d be looking for a more scalable deployment architecture probably running on Kubernetes with a support contract but for my little homelab this is perfect.

Pretty sure both of those asks are available…

Post comment

Markdown is allowed, HTML is not. All comments are moderated.