How to integrate with the Canonical Observability Stack¶
gopkg-k8s exposes Prometheus metrics, forwards its structured logs,
and ships a Grafana dashboard and alert rules. Integrate its
metrics-endpoint, logging, and grafana-dashboard endpoints with
the Canonical Observability Stack (COS) to use them. For what each endpoint
carries and the metrics and alert rules the charm provides, see
Integrations.
These steps assume that gopkg-k8s and nginx-ingress-integrator
are deployed and integrated, as they are after the deployment steps of
Deploy and verify gopkg-k8s on Kubernetes and before its clean-up section. They
deploy the three COS charms into the same model, which is enough to see the
integrations work locally. A production deployment keeps COS in its own
model and integrates through cross-model offers; the COS documentation describes that layout.
Deploy the observability charms¶
juju deploy prometheus-k8s --channel=2/stable --trust
juju deploy loki-k8s --channel=2/stable --trust
juju deploy grafana-k8s --channel=2/stable --trust
Integrate the endpoints¶
juju integrate gopkg-k8s:metrics-endpoint prometheus-k8s:metrics-endpoint
juju integrate gopkg-k8s:logging loki-k8s:logging
juju integrate gopkg-k8s:grafana-dashboard grafana-k8s:grafana-dashboard
Wait until every application is active:
juju status --relations --watch 2s
The COS charms take a few minutes, and each restarts its own pod once after
it first reports active. If one of them stays blocked with Kubernetes
resources patch failed: Unauthorized for more than a couple of minutes, it
has hit a known race and will not recover by itself: follow
COS charm blocked on a Kubernetes patch to redeploy that charm, then
continue here.
Verify that Prometheus scrapes the service¶
Prometheus scrapes /metrics on the application port of every unit. Ask
its API for the up series of the application; a value of 1 means the
last scrape succeeded. Reach the API through the Kubernetes Service that Juju
maintains for prometheus-k8s. Its address is the one juju status
shows for the application, and it survives pod replacement, which the COS
charms trigger shortly after they first report active, when they set
resource limits on their own pods. The loop retries until the first scrape
completes and gives up after ten minutes:
export PROMETHEUS_IP=$(microk8s kubectl -n gopkg-k8s get service \
prometheus-k8s -o jsonpath='{.spec.clusterIP}')
echo "${PROMETHEUS_IP}"
timeout 600 bash -c '
until curl --silent --show-error --get \
"http://${PROMETHEUS_IP}:9090/api/v1/query" \
--data-urlencode "query=up{juju_application=\"gopkg-k8s\"}" \
| grep -F "\"1\"]"; do
sleep 10
done
' || { echo "Prometheus has not scraped gopkg-k8s successfully" >&2; false; }
echo prints the Service address, such as 10.152.183.148. The loop
prints nothing while it waits; when the scrape succeeds, its output is a
JSON document whose result entry ends in "1". If it gives up after
ten minutes, it prints the message on the last line instead: follow
Prometheus does not report the service as up before going on.
Generate traffic and view the dashboards¶
The dashboards show nothing until the service has handled requests. Send
two minutes of mixed traffic through ingress: package pages, go-get
queries, a package that does not exist, and health checks, so that every
panel has data. The loop stops by itself and leaves nothing behind; stop it
early with Ctrl-C:
export INGRESS_HOST=gopkg.example.com
SECONDS=0
while [ "${SECONDS}" -lt 120 ]; do
for path in /yaml.v2 "/yaml.v2?go-get=1" /mgo.v2 /check.v1 \
/does-not-exist.v9 /health-check; do
curl --silent --output /dev/null "http://${INGRESS_HOST}${path}" \
--resolve "${INGRESS_HOST}:80:127.0.0.1"
done
sleep 1
done
Package requests make the service look up references on GitHub, so the loop also exercises the upstream and cache panels.
Grafana has no route out of the VM, so fetch its administrator password and then, in a second terminal on the VM, forward its port to every interface of the VM:
juju run grafana-k8s/0 get-admin-password
microk8s kubectl -n gopkg-k8s port-forward --address 0.0.0.0 \
svc/grafana-k8s 3000:3000
In a browser on your workstation, open http://<vm-address>:3000, where
<vm-address> is the VM’s address from multipass info charm-dev on
the host, and log in as admin with that password. Under Dashboards,
gopkg Overview shows the traffic you just sent as request rate by route,
HTTP error rate, latency, upstream failure rate and refs cache hit ratio;
only its git upload-pack panel stays empty, because that needs a real
git clone. Go Operator is the framework’s dashboard. In
Explore, the Loki data source shows one
JSON log record per request other than a health check when filtered by
juju_application="gopkg-k8s". Press Ctrl-C in the second terminal to
stop the port forward; nothing else needs cleaning up.
Keep the metrics endpoint off the public hostname¶
By default the metrics endpoint shares the application port, so ingress
publishes it at /metrics on the public hostname. Move it to a port that
ingress does not route. The charm passes the new port to the service, which
opens a second listener for the metrics path only, and updates the scrape
job:
juju config gopkg-k8s metrics-port=9102
Confirm that Prometheus scrapes the new port, then that the public hostname
no longer serves metrics. The up series cannot tell the ports apart,
because the Prometheus charm rewrites its instance label to the Juju
topology, so ask the targets API instead: it lists the scrape URL and health
of every active target. The loop waits until the target whose URL ends in
:9102/metrics reports up:
timeout 600 bash -c '
until curl --silent --show-error \
"http://${PROMETHEUS_IP}:9090/api/v1/targets?state=active" \
| grep --only-matching "\"scrapeUrl\":\"[^\"]*:9102/metrics\"[^}]*\"health\":\"up\""; do
sleep 10
done
' || { echo "Prometheus has no healthy target on port 9102" >&2; false; }
export INGRESS_HOST=gopkg.example.com
curl --silent --output /dev/null --write-out '%{http_code}\n' \
http://${INGRESS_HOST}/metrics \
--resolve ${INGRESS_HOST}:80:127.0.0.1 | grep -Fx 404
The first command prints the matching part of the target entry, from its
scrape URL to "health":"up", or the message on its last line if no such
target appears within ten minutes (see Prometheus does not report the service as up).
The second prints 404, because the application answers its own not-found
page for that path.