m
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.log
|
||||||
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal file
@@ -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
|
||||||
79
progs/registry.mtail
Normal file
79
progs/registry.mtail
Normal file
@@ -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/<repo>/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/<repo>/blobs/uploads/<uuid> 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]++
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user