Logs an Observability übertragen
Zuletzt aktualisiert am
Voraussetzungen
Abschnitt betitelt „Voraussetzungen“Bevor Sie Logs an Ihre STACKIT Observability-Instanz übertragen können, stellen Sie sicher, dass Sie Folgendes bereitstehen haben:
- Technische Zugangsdaten: Sie benötigen einen technischen
<USERNAME>und ein<PASSWORD>für Ihre spezifische Instanz. Wenn Sie diese noch nicht erstellt haben, folgen Sie den Anweisungen in unserer Authentifizierungsübersicht. - Log-Quelle: Sie müssen Logs haben, die von Ihrem Betriebssystem, Dienst oder Ihrer Anwendung generiert werden. Es kann jede Log-Bibliothek verwendet werden. Diese Logs können entweder in eine lokale Log-Datei oder in den Standard-Output (stdout) geschrieben werden.
Logs an STACKIT Observability übertragen
Abschnitt betitelt „Logs an STACKIT Observability übertragen“Sobald die Logs vorhanden sind, müssen sie gesammelt und an Ihre Observability-Instanz gesendet werden. STACKIT Observability unterstützt zwei Hauptprotokolle für die Log-Ingestion:
- OpenTelemetry (OTLP) Endpunkt: Der moderne Industriestandard.
- Loki-API-Endpunkt: Das native Grafana Loki-Protokoll.
Unten finden Sie Konfigurationsbeispiele für beide Ansätze.
Option 1: Logs über OpenTelemetry (OTLP) übertragen
Abschnitt betitelt „Option 1: Logs über OpenTelemetry (OTLP) übertragen“STACKIT Observability unterstützt das Ingestieren von OpenTelemetry-Logs über HTTP. Sie können den OpenTelemetry-HTTP-Exporter (otlphttp) konfigurieren, um Log-Daten an Ihre STACKIT Observability-Instanz zu senden. Zusätzlich wird die basicauth-Erweiterung benötigt, um die Authentifizierung per STACKIT-Zugriffstoken zu ermöglichen.
Stellen Sie sicher, dass diese Funktionen in dem von Ihnen verwendeten Agenten enthalten sind. Sie können den offiziellen OpenTelemetry Collector Contrib oder Grafana Alloy verwenden (welches im Hintergrund den OpenTelemetry Collector nutzt und OTLP-Komponenten vollständig unterstützt).
Beispiel: Setup mit OpenTelemetry Collector
Abschnitt betitelt „Beispiel: Setup mit OpenTelemetry Collector“Die folgende Konfigurationsdatei zeigt, wie Logs aus einer lokalen Datei gelesen, mit Ressourcen-Attributen angereichert und an den OTLP-Endpunkt übertragen werden. Sie können diese entsprechend Ihren Anforderungen erweitern.
OpenTelemetry Collector-Konfiguration
Abschnitt betitelt „OpenTelemetry Collector-Konfiguration“extensions: basicauth/stackit: client_auth: username: "<USERNAME>" password: "<PASSWORD>"
receivers: filelog: include: - /var/log/app/*.log start_at: end
processors: batch: send_batch_size: 1000 timeout: 10s
resource: attributes: - key: service.name value: custom_service action: insert - key: detected.level value: info action: insert
exporters: otlphttp/stackit: # WICHTIG: Kopieren Sie die "OTLP ingest"-URL aus dem STACKIT Portal, # aber Sie MÜSSEN das '/v1/logs'-Suffix am Ende entfernen! # Beispiel: [https://logs.stackit9.argus.eu01.stackit.cloud/instances/.../otlp](https://logs.stackit9.argus.eu01.stackit.cloud/instances/.../otlp) endpoint: "<OTLP_INGEST_URL_WITHOUT_V1_LOGS_SUFFIX>" auth: authenticator: basicauth/stackit
service: extensions: [basicauth/stackit] pipelines: logs: receivers: [filelog] processors: [batch, resource] exporters: [otlphttp/stackit]Docker Compose
Abschnitt betitelt „Docker Compose“Verwenden Sie diese Docker-compose-Datei, um den OpenTelemetry Collector in einem Docker-Container auszuführen. Sie mountet Ihre lokale Konfiguration und das Log-Verzeichnis in den Container.
services: otel-collector: image: otel/opentelemetry-collector-contrib:latest container_name: otel-collector volumes: - ./config.yaml:/etc/otelcol-contrib/config.yaml:ro - /var/log/app:/var/log/app:ro restart: unless-stoppedOption 2: Logs über Loki-API übertragen
Abschnitt betitelt „Option 2: Logs über Loki-API übertragen“Wenn Sie die Verwendung der nativen Loki-API bevorzugen, können Sie Agenten wie Grafana Alloy oder fluentbit mit Loki-Plugin verwenden. Unten finden Sie Beispielkonfigurationen dieser Tools.
Beispiel: Setup mit Grafana Alloy
Abschnitt betitelt „Beispiel: Setup mit Grafana Alloy“In diesem Beispiel sammeln wir Logs aus verschiedenen Quellen, z. B. von einer virtuellen Maschine. Grafana Alloy sammelt alle Logs und sendet sie an eine Observability-Loki-Instanz.
Grafana Alloy-Konfiguration
Abschnitt betitelt „Grafana Alloy-Konfiguration“local.file_match "node_logs" { path_targets = [{ // Monitor syslog to scrape node-logs __path__ = "/var/log/syslog", job = "node/syslog", node_name = sys.env("HOSTNAME"), cluster = "[cluster]", }]}
// loki.source.file reads log entries from files and forwards them to other loki.* components.// You can specify multiple loki.source.file components by giving them different labels.loki.source.file "node_logs" { targets = local.file_match.node_logs.targets forward_to = [loki.write.loki_instance.receiver]}
logging { level = "info" format = "json" write_to = [loki.write.loki_instance.receiver]}
loki.write "loki_instance" { endpoint { # Kopieren Sie die vollständige "Ingest"-URL aus dem STACKIT Portal url = "<LOKI_INGEST_URL_FROM_PORTAL>" basic_auth { username = "<USERNAME>" password = "<PASSWORD>" } retry_on_http_429 = true }}Docker Compose
Abschnitt betitelt „Docker Compose“Verwenden Sie diese Docker-compose-Datei, um Grafana Alloy in einem Docker-Container auszuführen. Die Alloy-Benutzeroberfläche und -API sind über Port 12345 erreichbar.
services: alloy: image: grafana/alloy:latest container_name: alloy command: - run - --server.http.listen-addr=0.0.0.0:12345 - --storage.path=/var/lib/alloy/data - /etc/alloy/config.alloy ports: - "12345:12345" volumes: - ./config.alloy:/etc/alloy/config.alloy:ro - /var/run/docker.sock:/var/run/docker.sock:ro - alloy-data:/var/lib/alloy/data restart: unless-stoppedvolumes: alloy-data:Beispiel: Setup mit fluentbit
Abschnitt betitelt „Beispiel: Setup mit fluentbit“Im zweiten Beispiel möchten wir Container-Logs erfassen und verwenden dafür fluentbit.
fluentbit-Konfiguration
Abschnitt betitelt „fluentbit-Konfiguration“[Output] Name loki Match * Tls on
# Extrahieren Sie die Domain aus Ihrer "Ingest"-URL im STACKIT Portal # Beispiel: logs.stackit9.argus.eu01.stackit.cloud Host <LOKI_HOST_FROM_PORTAL_URL>
# Extrahieren Sie den Pfad aus Ihrer "Ingest"-URL im STACKIT Portal # Beispiel: /instances/.../loki/api/v1/push Uri <LOKI_URI_FROM_PORTAL_URL>
Port 443 Labels job=fluent-bit,env=${FLUENT_ENV} Http_User <USERNAME> Http_Passwd <PASSWORD> Line_format json
[SERVICE] Parsers_File /fluent-bit/etc/parsers.conf # fluentbit benötigt einen Parser. Dieser sollte auf den Parser zeigen Flush 5 Daemon Off Log_Level debug
[FILTER] Name parser Match * Parser docker Key_name log
[INPUT] Name forward Listen 0.0.0.0 Port 24224Parsers-Konfiguration
Abschnitt betitelt „Parsers-Konfiguration“[PARSER] Name docker Format json Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L Time_Keep On Decode_Field_As json log log Decode_Field_As json level Decode_Field_As json ts Decode_Field_As json caller Decode_Field_As json msg msgDocker Compose
Abschnitt betitelt „Docker Compose“services: fluentbit: image: grafana/fluent-bit-plugin-loki:latest container_name: fluentbit_python_local volumes: - ./logging:/fluent-bit/etc # das Verzeichnis logging enthält parsers.conf und fluent-bit.conf ports: - "24224:24224" - "24224:24224/udp"
app: build: context: ./app dockerfile: ./docker/local/Dockerfile image: app container_name: app privileged: true volumes: - ./:/app ports: - "3000:3000" command: sh -c 'air' logging: driver: fluentd # damit fluentbit mit Docker funktioniert, wird dieser Treiber benötigtBeispiel: Setup mit Kubernetes und fluentbit
Abschnitt betitelt „Beispiel: Setup mit Kubernetes und fluentbit“Um Logs von einer oder mehreren Anwendungen an Loki zu senden, müssen Sie fluentbit in Ihrem Cluster installieren. Dadurch wird die Ausgabe direkt an Ihre STACKIT Observability-Instanz geleitet.
Sie können auch Logs an mehrere STACKIT Observability-Instanzen senden. Beachten Sie, dass die URL im fluentbit-Output-Plugin Ihre spezifische Instanz-ID enthalten muss.

Stellen Sie die folgenden Dateien in Ihrem Kubernetes-Cluster per Deployment bereit:
kind: NamespaceapiVersion: v1metadata: name: kube-loggingapiVersion: v1kind: ServiceAccountmetadata: name: fluent-bit namespace: kube-loggingapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: fluent-bit-readrules: - apiGroups: [""] resources: - namespaces - pods verbs: ["get", "list", "watch"]apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: fluent-bit-readroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: fluent-bit-readsubjects: - kind: ServiceAccount name: fluent-bit namespace: kube-loggingapiVersion: v1kind: Secretmetadata: name: fluent-bit-secrets namespace: kube-loggingtype: OpaquestringData: username: "<USERNAME>" password: "<PASSWORD>"apiVersion: v1kind: ConfigMapmetadata: name: fluent-bit-config namespace: kube-logging labels: k8s-app: fluent-bitdata: fluent-bit.conf: | [SERVICE] Daemon Off Flush 1 Log_Level info Parsers_File /fluent-bit/etc/parsers.conf Parsers_File /fluent-bit/etc/conf/custom_parsers.conf HTTP_Server On HTTP_Listen 0.0.0.0 HTTP_Port 2020 Health_Check On
[INPUT] Name tail Path /var/log/containers/*.log Parser cri Tag kube.* Mem_Buf_Limit 5MB Skip_Long_Lines On
[INPUT] Name systemd Tag host.* Systemd_Filter _SYSTEMD_UNIT=kubelet.service Read_From_Tail On
[FILTER] Name kubernetes Match kube.* Merge_Log On Keep_Log Off K8S-Logging.Parser On K8S-Logging.Exclude On
[OUTPUT] name loki match *
# Extract the domain from your STACKIT Portal "Ingest" URL # Example: logs.stackit9.argus.eu01.stackit.cloud host <LOKI_HOST_FROM_PORTAL_URL>
# Extract the path from your STACKIT Portal "Ingest" URL # Example: /instances/.../loki/api/v1/push uri <LOKI_URI_FROM_PORTAL_URL>
port 443 http_user ${FLUENT_USER} http_passwd ${FLUENT_PASS} tls on tls.verify on line_format json labels job=fluent-bit label_map_path /fluent-bit/etc/conf/labelmap.json
parsers.conf: | [PARSER] Name cri Format regex Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$ Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L%z
custom_parsers.conf: | [PARSER] Name docker Format json Time_Key time Time_Format %Y-%m-%dT%H:%M:%S.%L Time_Keep On Decode_Field_As json log log Decode_Field_As json level Decode_Field_As json ts Decode_Field_As json caller Decode_Field_As json msg msg
labelmap.json: |- { "kubernetes": { "container_name": "container", "host": "node", "labels": { "app": "app", "release": "release" }, "namespace_name": "namespace", "pod_name": "instance" }, "stream": "stream" }apiVersion: apps/v1kind: DaemonSetmetadata: name: fluent-bit namespace: kube-logging labels: app.kubernetes.io/name: fluent-bit app.kubernetes.io/instance: fluent-bit-lokispec: selector: matchLabels: k8s-app: fluent-bit-logging template: metadata: labels: k8s-app: fluent-bit-logging spec: serviceAccountName: fluent-bit terminationGracePeriodSeconds: 10 containers: - name: fluent-bit image: "fluent/fluent-bit:latest" imagePullPolicy: Always command: - /fluent-bit/bin/fluent-bit args: - --workdir=/fluent-bit/etc - --config=/fluent-bit/etc/conf/fluent-bit.conf ports: - name: http containerPort: 2020 protocol: TCP livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: /api/v1/health port: http volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: journal mountPath: /journal readOnly: true - name: fluent-bit-config mountPath: /fluent-bit/etc/conf env: - name: FLUENT_USER valueFrom: secretKeyRef: name: fluent-bit-secrets key: username - name: FLUENT_PASS valueFrom: secretKeyRef: name: fluent-bit-secrets key: password volumes: - name: varlog hostPath: path: /var/log - name: journal hostPath: path: /var/log/journal - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: fluent-bit-config configMap: name: fluent-bit-config tolerations: - key: node-role.kubernetes.io/master effect: NoScheduleErstellen Sie eine kleine Testanwendung, um Beispieldaten zu erzeugen. Für dieses Beispiel stellen wir einen NGINX-Webserver bereit:
apiVersion: v1kind: PersistentVolumeClaimmetadata: name: my-pvcspec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi # 1 Gigabyte Speicher anfordernapiVersion: apps/v1kind: Deploymentmetadata: name: nginx-deploymentspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: data mountPath: /usr/share/nginx/html resources: limits: memory: "256Mi" cpu: "200m" requests: memory: "128Mi" cpu: "100m" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 15 periodSeconds: 10 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 volumes: - name: data persistentVolumeClaim: claimName: my-pvcÜberprüfen Sie als Nächstes, ob Ihre bereitgestellten Pods erfolgreich ausgeführt werden:
$ kubectl get namespacesNAME STATUS AGEdefault Active 6dkube-logging Active 4d3hkube-node-lease Active 6dkube-public Active 6dkube-system Active 6d
$ kubectl get pods -n kube-loggingNAME READY STATUS RESTARTS AGEfluent-bit-c9b8d 1/1 Running 0 23h
$ kubectl get pods -n defaultNAME READY STATUS RESTARTS AGEnginx-deployment-58fc999d7b-56jrq 1/1 Running 0 9m2snginx-deployment-58fc999d7b-5gkwz 1/1 Running 0 9m2snginx-deployment-58fc999d7b-gxbfr 1/1 Running 0 9m2sSobald alles läuft, öffnen Sie Grafana und verwenden Sie die Loki-Datenquelle, um zu überprüfen, ob Ihre Logs ankommen:

Referenzen
Abschnitt betitelt „Referenzen“- fluentbit und Kubernetes
- fluentbit Loki Output
- fluentbit Helm-Charts (verwenden Sie fluent-bit-0.43.0 oder höher)
- fluentbit Docker-Image (verwenden Sie 2.2.2 oder höher)