diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..41f4a1a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3.9" + +services: + mtail: + image: ghcr.io/google/mtail:latest + container_name: mtail-registry + command: + - --progs=/etc/mtail + - --logs=/logs/registry.zinomedia.de.access.json.log + - --web_listen_addr=:3903 + - --vm_logs_runtime_errors=false + - --prometheus_emit_histograms=false + environment: + - TZ=Europe/Berlin + networks: + - web + volumes: + - ./progs:/etc/mtail:ro + - /opt/docker/docker-nginx/volumes/logs:/logs:ro + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:3903/metrics"] + interval: 30s + timeout: 5s + retries: 3 + +networks: + web: + external: true + name: web diff --git a/progs/registry.mtail b/progs/registry.mtail new file mode 100644 index 0000000..1b6bdb0 --- /dev/null +++ b/progs/registry.mtail @@ -0,0 +1,79 @@ +# Parses JSON access logs from NGINX fronting a Docker Registry. +# Emits Prometheus counters for pull/push bytes and seconds, plus request counts. + +# -------- Metrics +counter registry_requests_total by method, path_class, status, repo, user, node +counter registry_pull_blob_bytes_total by repo, user, node +counter registry_pull_blob_seconds_total by repo, user, node +counter registry_push_upload_bytes_total by repo, user, node +counter registry_push_upload_seconds_total by repo, user, node + +# -------- Field extraction (order-independent) +/\"method\":\"([A-Z]+)\"/ { method = $1 } +/\"path\":\"([^\"]+)\"/ { path = $1 } +/\"remote_address\":\"([^\"]*)\"/ { node = $1 } +/\"remote_user\":\"([^\"]*)\"/ { user = $1 } +/\"status\":([0-9]{3})/ { status = $1 } +/\"request_time\":([0-9.]+)/ { request_time = $1 } +/\"body_bytes_sent\":([0-9]+)/ { body_bytes_sent = $1 } +/\"upstream_range\":\"([^\"]*)\"/ { up_range = $1 } +/\"docker_upload_uuid\":\"([^\"]*)\"/ { upload_uuid = $1 } +/\"docker_content_digest\":\"([^\"]*)\"/ { digest = $1 } + +/\"path\":\"\/v2\/(.+?)\/(blobs|manifests|tags|_catalog)/ { + repo = $1 # supports namespaces like org/team/repo +} + +# Defaults per line +/$/ { + if repo == "" { repo = "unknown" } + if user == "" { user = "anonymous" } + if node == "" { node = "unknown" } +} + +# Helper: parse upstream_range "a-b" => bytes = b+1 +/\"upstream_range\":\"([0-9]+)-([0-9]+)\"/ { + uploaded_bytes = int($2) + 1 +} + +# -------- Classify & emit + +# Downloaded blob bytes/time: GET /v2//blobs/sha256:... +/\"method\":\"GET\".*\"\/v2\/.+\/blobs\/sha256:[a-f0-9]+\"/ { + registry_pull_blob_bytes_total[repo=repo, user=user, node=node] += body_bytes_sent + registry_pull_blob_seconds_total[repo=repo, user=user, node=node] += request_time + registry_requests_total[method=method, path_class="pull_blob", status=string(status), repo=repo, user=user, node=node]++ +} + +# Upload data transfer: PATCH /v2//blobs/uploads/ 202 +/\"method\":\"PATCH\".*\"\/v2\/.+\/blobs\/uploads\/[a-f0-9-]+\"/ { + if uploaded_bytes > 0 { + registry_push_upload_bytes_total[repo=repo, user=user, node=node] += uploaded_bytes + } + registry_push_upload_seconds_total[repo=repo, user=user, node=node] += request_time + registry_requests_total[method=method, path_class="push_upload", status=string(status), repo=repo, user=user, node=node]++ + uploaded_bytes = 0 +} + +# Upload session start/end (bookkeeping) +/\"method\":\"POST\".*\"\/v2\/.+\/blobs\/uploads\/\"/ { + registry_requests_total[method=method, path_class="push_start", status=string(status), repo=repo, user=user, node=node]++ +} +/\"method\":\"PUT\".*\"\/v2\/.+\/blobs\/uploads\/[a-f0-9-]+/ { + registry_requests_total[method=method, path_class="push_commit", status=string(status), repo=repo, user=user, node=node]++ +} + +# Manifest pulls +/\"method\":\"GET\".*\"\/v2\/.+\/manifests\/[^\"]+\"/ { + registry_requests_total[method=method, path_class="pull_manifest", status=string(status), repo=repo, user=user, node=node]++ +} + +# Manifest writes +/\"method\":\"PUT\".*\"\/v2\/.+\/manifests\/[^\"]+\"/ { + registry_requests_total[method=method, path_class="push_manifest", status=string(status), repo=repo, user=user, node=node]++ +} + +# Root auth probe +/\"path\":\"\/v2\/\"/ { + registry_requests_total[method=method, path_class="v2_root", status=string(status), repo="none", user=user, node=node]++ +}