Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion static-exporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ local static_exporter = import 'github.com/grafana/jsonnet-libs/static-expoter/m
}
```

## Using shell based implementation

The original implementation using the Apache web server contains quite a few dependencies that might be tricky to keep updated. An optional [busybox] based implementation can be used by supplying the `shell_exporter` flag:

```jsonnet
static_exporter.new('team-holiday-exporter', shell_exporter=true)
```

This variant uses a [distroless] image, runs as non-root and comes with a health check.

[distroless]:https://github.com/GoogleContainerTools/distroless
[busybox]:https://busybox.net/

## Updating httpd.conf

There is a default httpd.conf that was added to this library.
Expand All @@ -61,4 +74,4 @@ If there is a downstream change that requires updating this config file, run the
</IfModule>
```

This change adds a Header to the requests that enables Prometheus 3.x to scrape the static exporter.
This change adds a Header to the requests that enables Prometheus 3.x to scrape the static exporter.
105 changes: 99 additions & 6 deletions static-exporter/main.libsonnet
Original file line number Diff line number Diff line change
@@ -1,31 +1,124 @@
local k = import 'ksonnet-util/kausal.libsonnet';

{
new(name, image='httpd:2.4-alpine')::
new(name, image=null, shell_exporter=false, port=null)::
local _image =
if image == null
then (
if shell_exporter
then 'gcr.io/distroless/static-debian12:debug'
else 'httpd:2.4-alpine'
)
else image
;
local _port = if port == null
then (
if shell_exporter
then 8080
else 80
)
else port;

{
name:: name,
data:: { metrics: '' },
data:: {
metrics: '',
[if shell_exporter then 'handler']: |||
METRICS_FILE="/data/metrics"

handle_request() {
local request_line
read -r request_line

# Parse HTTP method and path
local method=$(echo "$request_line" | cut -d' ' -f1)
local path=$(echo "$request_line" | cut -d' ' -f2)

# Read and discard headers
while IFS= read -r line && [ "$line" != $'\r' ]; do
:
done

if [[ "$path" == "/metrics" ]]; then
# Serve Prometheus metrics
echo "HTTP/1.1 200 OK"
echo "Connection: close"
echo "Content-Type: text/plain; version=0.0.4; charset=utf-8"
echo "Content-Length: $(wc -c < "$METRICS_FILE")"
echo ""
cat "$METRICS_FILE"
elif [[ "$path" == "/health" ]]; then
# Health check endpoint
echo "HTTP/1.1 200 OK"
echo "Connection: close"
echo "Content-Type: text/plain"
echo "Content-Length: 3"
echo ""
echo "OK"
else
# 404 for other paths
echo "HTTP/1.1 404 Not Found"
echo "Connection: close"
echo "Content-Type: text/plain"
echo "Content-Length: 10"
echo ""
echo "Not Found"
fi
}

handle_request
|||,


},

local configMap = k.core.v1.configMap,
configmap:
configMap.new(name, self.data),

local container = k.core.v1.container,
container::
container.new('static-exporter', image)
container.new('static-exporter', _image)
+ container.withPorts([
k.core.v1.containerPort.newNamed(name='http-metrics', containerPort=80),
k.core.v1.containerPort.newNamed(name='http-metrics', containerPort=_port),
])
+ k.util.resourcesRequests('10m', '10Mi')
+ (
if shell_exporter
then
container.withCommand([
'sh',
'-eu',
'-c',
|||
# handler is created in a new file
mkdir -p "%(bin_dir)s"
echo '#!'$(which sh) > "%(bin_dir)s/handler"
cat /data/handler >> "%(bin_dir)s/handler"
chmod +x %(bin_dir)s/handler

# run nc, which forks each handler in its own process
exec nc -p %(port)d -l -k -e "%(bin_dir)s/handler" 0.0.0.0
||| % {
port: _port,
bin_dir: '/home/nonroot/bin',
},
]) +
container.securityContext.withRunAsUser(65532) +
container.securityContext.withRunAsGroup(65532) +
container.readinessProbe.httpGet.withPath('/health') +
container.readinessProbe.httpGet.withPort('http-metrics')
else {}
)
,

local deployment = k.apps.v1.deployment,
local volumeMount = k.core.v1.volumeMount,
deployment:
deployment.new(name, replicas=1, containers=[self.container])
+ k.util.configMapVolumeMount(self.configmap, '/usr/local/apache2/htdocs'),
+ k.util.configMapVolumeMount(self.configmap, if shell_exporter then '/data' else '/usr/local/apache2/htdocs'),
}
+ self.withHttpConfig()
+ (if shell_exporter then {} else self.withHttpConfig())
,

withData(data):: { data: data },
Expand Down
Loading