Add Dockerfile, compose.yaml, and an unbound example config (#66)

This adds a Dockerfile for unbound exporter, which we can publish in the future. An example docker compose.yml is included to demonstrate and test using it with unbound, along with a sample configuration file for unbound showing how to set up the remote-control.

The unbound example config file is based on the one inside the mvance/docker image that's used here.

A small integration test runs against the docker-compose setup to smoke-test unbound_exporter.
This commit is contained in:
Matthew McPherrin 2023-11-17 13:12:02 -05:00 committed by GitHub
parent cbed00787a
commit e54ee016b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 204 additions and 0 deletions

27
.github/workflows/integration.yaml vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: integration
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
jobs:
integration:
runs-on: [ubuntu-latest]
steps:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: "1.21.x"
- name: checkout
uses: actions/checkout@v4
- name: Start containers
run: docker compose up --build --detach
- name: run integration test
run: go test -v --tags=integration
- name: Stop containers
if: always()
run: docker compose down

20
Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.4-bookworm AS build
WORKDIR /go/src/app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY *.go .
ENV CGO_ENABLED=0
RUN GOOS=$TARGETOS GOARCH=$TARGETPLATFORM go build -v -o /go/bin/unbound_exporter ./...
FROM gcr.io/distroless/static-debian12
COPY --from=build /go/bin/unbound_exporter /
ENTRYPOINT ["/unbound_exporter"]

21
docker-compose.yml Normal file
View File

@ -0,0 +1,21 @@
services:
unbound_exporter:
build: .
command: [ "-unbound.host=unix:///var/run/socket/unbound.ctl" ]
volumes:
- socket:/var/run/socket:ro
ports:
- "9167:9167"
depends_on:
unbound:
condition: service_started
unbound:
image: "mvance/unbound:1.18.0"
volumes:
- socket:/var/run/socket:rw
- ./unbound-example.conf:/opt/unbound/etc/unbound/unbound.conf
ports:
- "1053:1053/udp"
- "1053:1053/tcp"
volumes:
socket:

52
integration_test.go Normal file
View File

@ -0,0 +1,52 @@
//go:build integration
package main
import (
"net/http"
"testing"
"github.com/prometheus/common/expfmt"
)
// TestIntegration checks that unbound_exporter is running, successfully
// scraping and exporting metrics.
//
// It assumes unbound_exporter is available on localhost:9167, and Unbound on
// localhost:1053, as is set up in the docker-compose.yml file.
//
// A typical invocation of this test would look like
//
// docker compose up --build -d
// go test --tags=integration
// docker compose down
func TestIntegration(t *testing.T) {
resp, err := http.Get("http://localhost:9167/metrics")
if err != nil {
t.Fatalf("Failed to fetch metrics from unbound_exporter: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("Expected a 200 OK from unbound_exporter, got: %v", resp.StatusCode)
}
parser := expfmt.TextParser{}
metrics, err := parser.TextToMetricFamilies(resp.Body)
if err != nil {
t.Fatalf("Failed to parse metrics from unbound_exporter: %v", err)
}
// unbound_up is 1 if we've successfully scraped metrics from it
unbound_up := metrics["unbound_up"].Metric[0].Gauge.GetValue()
if unbound_up != 1 {
t.Errorf("Expected unbound_up to be 1, not: %v", unbound_up)
}
// Check some expected metrics are present
for _, metric := range []string{"go_info", "unbound_queries_total", "unbound_response_time_seconds", "unbound_cache_hits_total"} {
if _, ok := metrics[metric]; !ok {
t.Errorf("Expected metric is missing: %s", metric)
}
}
}

84
unbound-example.conf Normal file
View File

@ -0,0 +1,84 @@
## This is an example Unbound configuration file
## This is needed to use unbound_exporter
remote-control:
control-enable: yes
control-interface: /var/run/socket/unbound.ctl
# The rest of this file is standard Unbound configuration
# There's nothing special here.
server:
cache-max-ttl: 86400
cache-min-ttl: 300
directory: "/opt/unbound/etc/unbound"
do-ip4: yes
do-ip6: no
do-tcp: yes
do-udp: yes
edns-buffer-size: 1232
interface: 0.0.0.0
port: 1053
prefer-ip6: no
rrset-roundrobin: yes
username: "_unbound"
log-local-actions: no
log-queries: no
log-replies: no
log-servfail: yes
logfile: /opt/unbound/etc/unbound/unbound.log
verbosity: 2
infra-cache-slabs: 4
incoming-num-tcp: 10
key-cache-slabs: 4
msg-cache-size: 142768128
msg-cache-slabs: 4
num-queries-per-thread: 4096
num-threads: 3
outgoing-range: 8192
rrset-cache-size: 285536256
rrset-cache-slabs: 4
minimal-responses: yes
prefetch: yes
prefetch-key: yes
serve-expired: yes
so-reuseport: yes
aggressive-nsec: yes
delay-close: 10000
do-daemonize: no
do-not-query-localhost: no
neg-cache-size: 4M
qname-minimisation: yes
access-control: 127.0.0.1/32 allow
access-control: 192.168.0.0/16 allow
access-control: 172.16.0.0/12 allow
access-control: 10.0.0.0/8 allow
access-control: fc00::/7 allow
access-control: ::1/128 allow
auto-trust-anchor-file: "var/root.key"
chroot: ""
deny-any: yes
harden-algo-downgrade: yes
harden-below-nxdomain: yes
harden-dnssec-stripped: yes
harden-glue: yes
harden-large-queries: yes
harden-referral-path: no
harden-short-bufsize: yes
hide-http-user-agent: no
hide-identity: yes
hide-version: yes
http-user-agent: "DNS"
identity: "DNS"
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
private-address: ::ffff:0:0/96
ratelimit: 1000
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
unwanted-reply-threshold: 10000
use-caps-for-id: yes
val-clean-additional: yes
include: /opt/unbound/etc/unbound/a-records.conf
include: /opt/unbound/etc/unbound/srv-records.conf