So far so good: last part left us with several services exposed by caddy with impressive domain names like ticketchangelog.domain.com, but that is not why are we here for. Therefore, edit your Caddyfile with an axe: remove all those selected lines

44    @podzir host podzir.domain.com
45
46    handle @podzir {
47        reverse_proxy podzir
48    }
49
50    @spin host spin.domain.com
51
52    handle @spin {
53        reverse_proxy spin
54    }
55
56    @remotegis host remotegis.domain.com
57
58    handle @remotegis {
59        reverse_proxy remotegis
60    }

What is left can be found here

What have we done? Essentially - removed few modules that have no rest endpoints anyway except two that every module has: healtcheck and metrics. What are we going to do with those details? - measuring performance and health is, at least declaratively, very important.

We’ll make use of prometheus - we have added it to the stack to do just that: scrapes services for healthcheck / metrics informations and queries them - bare neccessity for every kind of cool-to-look-at-graphs-and-facts presentation or dashboard.

Last time that we have checked prometheus was up and running at https://prometheus.domain.com so let’s see if it is still there. Start the whole circus (if it is not already started) using docker compose up -d and open https://prometheus.domain.com/targets

If everything still works as expected, there should be only one target declared: prometheus itself. When we look at prometheus configuration (prometheus.yml, volume reco-prometheus wherever it may be found in your case) it is perfectly clear that prometheus is configured to look just at itself:

19# A scrape configuration containing exactly one endpoint to scrape:
20# Here it's Prometheus itself.
21scrape_configs:
22  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
23  - job_name: "prometheus"
24    static_configs:
25      - targets: ["prometheus:9090"]

Let’s change that. Open prometheus.yml in editor and add those lines:

27    - job_name: "allegro"
28    static_configs:
29        - targets: ["allegro:80"]
30        labels:
31            reco: demo
32
33    - job_name: "remotegis"
34    static_configs:
35        - targets: ["remotegis:80"]
36        labels:
37            reco: demo
38
39    - job_name: "spin"
40    static_configs:
41        - targets: ["spin:80"]
42        labels:
43            reco: demo
44
45    - job_name: "podzir"
46    static_configs:
47        - targets: ["podzir:80"]
48        labels:
49            reco: demo

Pay attention to indentation as this is just the part you have to add to indenation-sensitive yaml file or take complete file from here.

Restart prometheus with docker compose restart prometheus, wait for some time and refresh prometheus targets page. If you have configured everything successfuly there should be five targets now: prometheus and additional four services that we have just configured.

Open prometheus graph page at https://prometheus.domain.com/graph and enter aspnetcore_healthcheck_status as expression. You should see dozen or so lines with different labels (“job”, “name”, “reco”, “instance”). Those labels are there for distinction between different services, naturally.

We can move further this way, adding separate prometheus scrape jobs for each service that we add but we can be more lazy, of course: there are many ways of service discovery, depending on the service stack. We will add another one, custom built. Why another one? Well, it turns out that none of the existing ones fits exactly the way we need.

Our custom prometheus discovery service is also docker container so it will be another service that we need to add to our compose.yml. We need some configuration for our service discovery service and that is the file named description.yaml and it should be placed somewhere where docker container can read it. We can create another docker volume as usual, or we can (as we are going to do) use some of the already existing volumes - in this case reco-prometheus volume sounds completely satisfactory as it is already used by prometheus itself. Create file named description.yaml in your reco-prometheus volume containing those lines:

services:
  allegro:
  podzir:
  spin:
  ticketchangelog:
  remotegis:
  authenticatomatic:
    scheme: "https"

service_groups:
  # default service group. if not specified, reco will have prometheus targets for specified services
  default:
    - ticketchangelog
    - remotegis
    - podzir
    - spin
    - allegro
    - sgw
    - dardo
    - xlab
    - vin
    - core112
    - allegro-sip
    - system

  authenticatomatic:
    - authenticatomatic

tekelija:
  demo:
    target: "{{.Service}}:80"

standalone:
  authenticatomatic:
    target: "authenticatomatic.domain.com"
    group: "authenticatomatic"

(or get the file from here)

Update your compose.yml file with another service declaration:

214  prometheus-discovery:
215    image: registry.gitlab.com/tekelija/tekelija-prometheus-discovery:latest
216    volumes:
217      - reco-prometheus:/cfg:ro
218    ports:
219      - 9184:9184
220    hostname: prometheus-discovery

Execute another docker compose up -d and there should be endpoint visible, listening on localhost, port 9184. Execute simple get request at localhost:9184 and you should see something like this:

{
    "http": [
        {
            "targets": [
                "ticketchangelog:80"
            ],
            "labels": {
                "job": "tekelija",
                "reco": "demo",
                "service": "ticketchangelog"
            }
        },
        {
            "targets": [
                "remotegis:80"
            ],
            "labels": {
                "job": "tekelija",
                "reco": "demo",
                "service": "remotegis"
            }
        },
        ...
    ],
    "https": [
        {
            "targets": [
                "authenticatomatic.domain.com"
            ],
            "labels": {
                "job": "authenticatomatic",
                "service": "authenticatomatic"
            }
        }
    ]
}

Basically, discovery is rest web service that prometheus will query for target definitions, and json data returned as response pretty much looks like prometheus configuration that we have created manually just few minutes ago. Ok, now we have to tell prometheus where to look for target definitions. Update your prometheus.yml:

21scrape_configs:
22  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
23  - job_name: "prometheus"
24
25    # metrics_path defaults to '/metrics'
26    # scheme defaults to 'http'.
27
28    scheme: https
29
30    tls_config:
31      insecure_skip_verify: true
32
33    static_configs:
34      - targets: ["prometheus.domain.com"]
35
36  - job_name: "http"
37    scheme: "http"
38    scrape_interval: 5s
39    http_sd_configs:
40      - url: "http://prometheus-discovery:9184/?scheme=http"
41
42  - job_name: "https"
43    scheme: "https"
44    scrape_interval: 5s
45    tls_config:
46      insecure_skip_verify: true
47
48    http_sd_configs:
49      - url: "http://prometheus-discovery:9184/?scheme=https"

(complete file is here)

Restart prometheus using docker compose restart prometheus, wait for a minute or more and refresh prometheus targets page. You should be able to see something like this:

image

and query:

image

Cool, now we should have (almost) everything we need to display some graphs so stay tuned.