Resources

Collecting and Monitoring Kubernetes Events with OpenTelemetry

September 25, 2024 by OpenObserve Team
K8s Events Receiver

Kubernetes events provide a real-time view of what's happening inside your clusters, helping you detect issues early, ensure compliance, and optimize performance. 

OpenTelemetry offers a powerful suite of tools to efficiently collect and monitor these events, enhancing your ability to manage and troubleshoot your Kubernetes deployments effectively.

How OpenTelemetry Enhances Kubernetes Event Monitoring

OpenTelemetry is an open-source observability framework that provides standardized tools for collecting, processing, and exporting telemetry data, including logs, metrics, and traces. 

When integrated with Kubernetes, OpenTelemetry enables you to:

  1. Streamline Data Collection:
    • Use the Kubernetes Objects Receiver to automatically collect events from your Kubernetes clusters. This receiver captures a wide range of events, ensuring you have comprehensive visibility into your cluster's activities.
  2. Transform and Enrich Data:
    • Utilize processors like the Attributes Processor and Transform Processor to enrich and format your event data. This processing ensures the data is structured and ready for analysis, making it easier to derive actionable insights.
  3. Flexible Export Options:
    • Export your processed event data to various backend systems for storage, visualization, and alerting. This flexibility allows you to choose the best tools for your specific monitoring needs, whether it’s a cloud-based observability platform or an on-premises solution.

In the next sections, we will dive deeper into understanding Kubernetes events, explore the functional components of OpenTelemetry related to Kubernetes, and provide detailed configuration steps to set up efficient event collection and monitoring. 

Understanding Kubernetes Events

To effectively monitor and manage your Kubernetes clusters, it's essential to understand the various events that occur within them. Kubernetes events are like the system's logbook, documenting significant occurrences that can affect your cluster's performance and reliability. 

In this section, we'll explore what Kubernetes events are, the different types of events you might encounter, and their critical role in operational reliability and decision-making.


What Are Kubernetes Events?

Kubernetes events are automated messages generated by the Kubernetes system or its components in response to significant actions or changes in the cluster. These events provide insights into the state and behavior of your cluster, helping you understand what's happening under the hood.

Why Events Matter

Events are crucial because they:

  • Provide Real-Time Insights: Offer immediate feedback on the state of your cluster, allowing for swift issue detection and resolution.
  • Enhance Debugging: Serve as a valuable resource for diagnosing problems by detailing the sequence of events leading up to an issue.
  • Support Compliance: Help track activities and changes within the cluster, aiding in compliance and auditing efforts.

Types of Kubernetes Events: From Failures to Node Issues

Kubernetes generates a wide variety of events, each indicating different types of activities or issues. Understanding these event types can help you quickly identify and respond to specific situations.

Common Event Types

  1. Failed Events:
    • Description: These occur when Kubernetes encounters errors that prevent an action from completing successfully.
    • Examples: Pod creation failures, container crashes, or failed deployments.
    • Importance: Indicate critical issues that need immediate attention to maintain system reliability.
  2. Evicted Events:
    • Description: Generated when a pod is removed from a node due to resource constraints.
    • Examples: Memory or CPU pressure leading to pod eviction.
    • Importance: Highlight resource management issues that could affect application performance.
  3. Failed Scheduling Events:
    • Description: Occur when the scheduler cannot find a suitable node to place a pod.
    • Examples: Lack of available resources or node affinity rules preventing pod placement.
    • Importance: Suggest capacity planning or configuration issues that need to be addressed.
  4. Volume Events:
    • Description: Related to issues with volume mounting or storage.
    • Examples: Failed volume mounts, persistent volume claims not bound.
    • Importance: Essential for ensuring data availability and integrity.
  5. Node Events:
    • Description: Pertaining to changes or issues with nodes.
    • Examples: Node not ready, node pressure, node rebooted.
    • Importance: Critical for maintaining the health and stability of the cluster infrastructure.

 

The Role of Kubernetes Events in Operational Excellence

Kubernetes events play a pivotal role in maintaining and improving your cluster's operational reliability, security, compliance, and strategic decision-making capabilities.

Enhancing Operational Reliability

  • Immediate Feedback: By monitoring events, you gain real-time insights into the cluster's state, enabling swift detection and resolution of issues.
  • Preventive Maintenance: Regularly analyzing events helps identify patterns and potential problem areas, allowing for proactive measures to prevent outages.

Boosting Security and Compliance

  • Tracking Activities: Events log critical activities and changes, providing a transparent view of what’s happening in the cluster.
  • Audit Trails: Maintain comprehensive audit trails to support compliance with industry regulations and internal policies.

Informing Strategic Decisions

  • Resource Optimization: Analyzing event data helps optimize resource allocation, ensuring efficient use of cluster resources.
  • Capacity Planning: Provides data-driven insights for future capacity planning and scaling decisions.

By understanding the various types of Kubernetes events and their significance, you can better manage your clusters, ensuring they run smoothly and efficiently. 

In the next section, we'll delve into how OpenTelemetry can help you collect and monitor these events, providing a standardized approach to telemetry data.

 

OpenTelemetry and Kubernetes Events

Collecting and monitoring Kubernetes events is essential, but to truly unlock their potential, you need a robust telemetry framework. OpenTelemetry offers the tools and standards necessary to efficiently capture, process, and analyze Kubernetes events, turning raw data into actionable insights. 

In this section, we'll introduce OpenTelemetry, explore its functional components related to Kubernetes, and outline the process for collecting Kubernetes events using the Kubernetes Objects Receiver.

 

Introducing OpenTelemetry: Your Telemetry Standard

OpenTelemetry is an open-source framework designed to standardize the collection and export of telemetry data, including logs, metrics, and traces. 

By providing a consistent set of APIs and libraries, OpenTelemetry simplifies the integration of telemetry data from diverse sources, ensuring comprehensive observability across your infrastructure.

Key Benefits of OpenTelemetry

  • Standardization: Ensures consistency in how telemetry data is collected and exported.
  • Flexibility: Supports multiple languages and platforms, making it adaptable to various environments.
  • Extensibility: Easily integrates with existing observability tools and platforms.

Functional Components of OpenTelemetry for Kubernetes

OpenTelemetry provides several components specifically designed to work with Kubernetes, each playing a crucial role in capturing and processing event data.

Attributes Processor

The Attributes Processor allows you to manipulate telemetry data by adding, modifying, or deleting attributes. This processing step ensures that your data is enriched with the necessary context, making it more informative and useful for analysis.

Example Configuration:

processors:
  attributes:
    actions:
      - key: k8s.pod.name
        action: insert
        value: my-pod

Kubeletstats Receiver

The Kubeletstats Receiver collects resource usage metrics from the Kubelet, providing insights into the performance and health of your nodes and pods. This receiver captures metrics such as CPU usage, memory usage, and disk I/O, which are crucial for performance monitoring.

Example Configuration:

receivers:
  kubeletstats:
    collection_interval: 10s
    endpoint: "https://kubelet-endpoint:10250"

Kubernetes Objects Receiver

The Kubernetes Objects Receiver is specifically designed to collect events from your Kubernetes clusters. This receiver captures a wide range of events, including pod failures, evictions, and node issues, ensuring comprehensive visibility into your cluster's activities.

Example Configuration:

receivers:
  k8s_objects:
    endpoint: "https://kubernetes.default.svc"
    auth_type: "serviceAccount"

Collecting Kubernetes Events with OpenTelemetry

Collecting Kubernetes events with OpenTelemetry involves configuring the Kubernetes Objects Receiver to capture event data and integrating it with other OpenTelemetry components for processing and export.

Step-by-Step Process

1. Set Up the Kubernetes Objects Receiver:

    • Define the receiver in your OpenTelemetry Collector configuration file.
    • Ensure that the receiver is properly authenticated to access the Kubernetes API.

Example Configuration:

receivers:
  k8s_objects:
    endpoint: "https://kubernetes.default.svc"
    auth_type: "serviceAccount"
    event_types: ["Normal", "Warning"]

2. Configure Data Processing:

    • Use processors like the Attributes Processor to enrich and format the collected event data.
    • This step ensures that your data is structured and ready for export.

Example Configuration:

processors:
  attributes:
    actions:
      - key: k8s.event.type
        action: insert
        value: "Warning"

3. Export the Data:

    • Define exporters to send the processed event data to your chosen observability platform. This could be OpenObserve, Prometheus, or any other compatible backend.

Example Configuration:

exporters:
  otlp:
    endpoint: "http://your-observability-platform:4317"
    compression: gzip

service:
  pipelines:
    logs:
      receivers: \[k8s_objects]
      processors: \[attributes]
      exporters: \[otlp]

By leveraging OpenTelemetry's powerful components and standardizing your telemetry data collection, you can gain deep insights into your Kubernetes environment. 

This setup not only helps in real-time monitoring but also provides valuable data for long-term analysis and strategic planning.

In the next section, we’ll dive into the detailed configuration steps for setting up the OpenTelemetry Collector in a Kubernetes cluster. 

We'll cover everything from creating a configuration map to deploying the collector, ensuring you have a seamless setup for monitoring Kubernetes events.

 

Configuring OpenTelemetry for Kubernetes Events

Setting up OpenTelemetry to collect and monitor Kubernetes events involves several steps, from preparing your Kubernetes cluster to configuring the OpenTelemetry Collector. 

This section will guide you through each step, ensuring a smooth and efficient setup process.  

 

Setting Up the OpenTelemetry Collector in a Kubernetes Cluster

The OpenTelemetry Collector acts as the central component for gathering, processing, and exporting telemetry data. 

To configure it within a Kubernetes environment, you'll need to create several Kubernetes resources. Let's break down the process.

Step 1: Create a Configuration Map

A Configuration Map (ConfigMap) is used to store the configuration file for the OpenTelemetry Collector. This file defines how the collector will receive, process, and export telemetry data.

Example ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: otel-collector-config
  namespace: default
data:
  otel-config.yml: |
    receivers:
      k8s_objects:
        endpoint: "https://kubernetes.default.svc"
        auth_type: "serviceAccount"
        event_types: ["Normal", "Warning"]

    processors:
      batch:
      attributes:
        actions:
          - key: k8s.event.type
            action: insert
            value: "Warning"

    exporters:
      otlp:
        endpoint: "http://your-observability-platform:4317"
        compression: gzip

    service:
      pipelines:
        logs:
          receivers: \[k8s_objects]
          processors: [batch, attributes]
          exporters: [logging, otlp]

Step 2: Create a Service Account and Cluster Role for RBAC

Role-Based Access Control (RBAC) ensures that the OpenTelemetry Collector has the necessary permissions to access Kubernetes event data. You’ll need to create a Service Account, a Cluster Role, and a Cluster Role Binding.

Service Account:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: otel-collector-sa
  namespace: default

Cluster Role:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: otel-collector-role
rules:
  - apiGroups: \[""]
    resources: \["events"]
    verbs: ["get", "list", "watch"]

Cluster Role Binding:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: otel-collector-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: otel-collector-role
subjects:
  - kind: ServiceAccount
    name: otel-collector-sa
    namespace: default

Step 3: Deploy the OpenTelemetry Collector

Finally, deploy the OpenTelemetry Collector using a Deployment manifest. This will ensure that the collector is running within your Kubernetes cluster, continuously collecting and processing event data.

Deployment Manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: otel-collector
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: otel-collector
  template:
    metadata:
      labels:
        app: otel-collector
    spec:
      serviceAccountName: otel-collector-sa
      containers:
        - name: otel-collector
          image: otel/opentelemetry-collector:latest
          volumeMounts:
            - name: otel-config
              mountPath: /etc/otel/config
              subPath: otel-config.yml
          env:
            - name: OTEL_CONFIG
              value: /etc/otel/config/otel-config.yml
      volumes:
        - name: otel-config
          configMap:
            name: otel-collector-config

Receiver Configuration for Kubernetes Event Collection

The Kubernetes Objects Receiver is the key component that captures Kubernetes events. In your configuration file, ensure it’s properly set up to collect the necessary event types.

Receiver Configuration:

receivers:
  k8s_objects:
    endpoint: "https://kubernetes.default.svc"
    auth_type: "serviceAccount"
    event_types: ["Normal", "Warning"]

By following these steps, you can set up the OpenTelemetry Collector to effectively gather and monitor Kubernetes events. This setup ensures that you have comprehensive visibility into your cluster’s activities, enabling real-time monitoring and proactive issue resolution.

In the next section, we’ll explore how to collect and format event data using the OpenTelemetry Transform Processor, providing practical examples and configurations. 

 

Collecting and Formatting Event Data

With the OpenTelemetry Collector set up, the next step is to ensure that the event data collected from Kubernetes is structured and formatted correctly for meaningful analysis. 

 

Enabling the Kubernetes Objects Receiver

The Kubernetes Objects Receiver is responsible for capturing the events generated within your Kubernetes clusters. Ensuring it is properly configured and enabled is crucial for effective event collection.

Configuring the Kubernetes Objects Receiver

The receiver configuration in your otel-config.yml file should specify the endpoint, authentication type, and the types of events you want to capture.

Example Configuration:

receivers:
  k8s_objects:
    endpoint: "https://kubernetes.default.svc"
    auth_type: "serviceAccount"
    event_types: ["Normal", "Warning"]

This configuration captures both normal and warning events, providing a comprehensive view of your cluster's activities.

 

Transforming Event Logs into Structured Data

Collected raw event logs can be unstructured and challenging to analyze. The OpenTelemetry Transform Processor helps convert these logs into structured data, making it easier to extract insights and take action.

Using the OpenTelemetry Transform Processor

The Transform Processor allows you to apply transformations to the telemetry data, such as adding, modifying, or deleting attributes. This step ensures your event data is enriched with the necessary context and formatted for analysis.

Example Configuration:

processors:
  transform:
    log_statements:
      - context: log
        statements:
          - set(attributes\["severity"], attributes\["k8s.event.type"])
          - set(attributes\["event_source"], attributes\["k8s.event.source"])

In this example, the severity of the event is set based on the Kubernetes event type, and the event source is captured as an attribute. 

This transformation enriches the event data, making it more useful for downstream analysis.

Formatting and Exporting Kubernetes Event Data

After transforming the data, the final step is to format it properly and export it to your chosen observability platform. 

This ensures that the data is readily accessible and can be visualized effectively.

Example Configuration for Formatting and Exporting

Here’s an example of how to configure the OpenTelemetry Collector to format and export Kubernetes event data:

Example Configuration:

service:
  pipelines:
    logs:
      receivers: \[k8s_objects]
      processors: [transform, batch]
      exporters: [logging, otlp]

exporters:
  logging:
    loglevel: debug

  otlp:
    endpoint: "http://your-observability-platform:4317"
    compression: gzip

This configuration sets up a pipeline that receives Kubernetes events, processes them using the Transform Processor and Batch Processor, and then exports the formatted data to a logging endpoint and an OTLP endpoint.

 

Practical Use Case: Visualization with OpenObserve

For a practical implementation, consider using OpenObserve (O2) as your observability platform. OpenObserve provides powerful visualization and alerting capabilities, allowing you to monitor Kubernetes events in real-time and set up proactive alerts.

Example OpenObserve Dashboard Configuration:

dashboards:
  - name: Kubernetes Events
    panels:
      - title: Event Counts
        type: graph
        targets:
          - expr: k8s_events_total
      - title: Event Details
        type: table
        targets:
          - expr: k8s_event_details

Example Alert Configuration:

alerts:
  - name: High Event Rate
    condition: k8s_events_total > 100
    actions:
      - notify: email
        to: ops-team@example.com
        message: "High rate of Kubernetes events detected."

By enabling the Kubernetes Objects Receiver and using the OpenTelemetry Transform Processor, you can ensure that the event data collected from your Kubernetes clusters is structured, enriched, and ready for analysis. 

Exporting this data to a powerful observability platform like OpenObserve allows you to visualize and monitor your Kubernetes events effectively, enhancing your ability to manage and optimize your clusters.

For more information and to get started with OpenObserve, visit our website, check out our GitHub repository, or sign up here

In the next section, we’ll explore how to set up monitoring and visualization platforms like SigNoz, providing step-by-step instructions to create dashboards and analyze event data

Monitoring and Visualization

Once you’ve set up OpenTelemetry to collect Kubernetes events, the next step is to visualize and monitor this data effectively. 

Visualization tools help transform raw data into meaningful insights, enabling proactive monitoring and quick decision-making. 

OpenObserve (O2) is an excellent choice for this purpose, offering powerful capabilities for creating dashboards and setting up alerts.

The Importance of Visualizing Kubernetes Events

Visualizing Kubernetes events is crucial for several reasons:

  1. Real-Time Monitoring:
    • Provides immediate visibility into the state of your cluster, allowing you to detect and respond to issues as they occur.
  2. Trend Analysis:
    • Helps identify patterns over time, which can inform capacity planning, resource optimization, and strategic decisions.
  3. Proactive Alerting:
    • Enables you to set up alerts for critical events, ensuring that your team is notified of potential issues before they escalate.

Setting Up Dashboards in OpenObserve

OpenObserve (O2) makes it straightforward to create detailed and interactive dashboards for monitoring Kubernetes events. Here’s how you can set up and configure your dashboards:

Example Dashboard Configuration

Creating a Dashboard:

  1. Define Panels:
    • Panels are the building blocks of your dashboard. Each panel visualizes a specific set of metrics or events.
  2. Configure Data Sources:
    • Connect the panels to the relevant data sources, ensuring they display the correct telemetry data.

Example Configuration:

dashboards:
  - name: Kubernetes Events
    panels:
      - title: Event Counts
        type: graph
        targets:
          - expr: k8s_events_total
          - legendFormat: "{{k8s.event.type}}"
          - title: Event Details
        type: table
        targets:
          - expr: k8s_event_details
          - columns:
            - text: "Event"
              value: "event"
            - text: "Severity"
              value: "severity"
            - text: "Source"
              value: "event_source"
            - text: "Message"
              value: "message"

Proactive Alerting with OpenObserve

Setting up alerts in OpenObserve ensures that your team is notified of critical events in real-time. Alerts can be configured based on specific thresholds or patterns detected in the event data.

Example Alert Configuration

Creating Alerts:

  1. Define Alert Conditions:
    • Specify the conditions under which an alert should be triggered, such as a high rate of warning events.
  2. Set Notification Channels:
    • Configure how and where the alerts should be sent, such as email, Slack, or other communication tools.

Example Configuration:

alerts:
  - name: High Event Rate
    condition: k8s_events_total > 100
    actions:
      - notify: email
        to: ops-team@example.com
        message: "High rate of Kubernetes events detected."
      - notify: slack
        channel: "#alerts"
        message: "High rate of Kubernetes events detected."

Practical Insights

By using OpenObserve, you gain access to advanced features that enhance your monitoring capabilities:

  1. Customizable Dashboards:
    • Tailor your dashboards to display the most relevant metrics and events for your team’s needs.
  2. Interactive Visualizations:
    • Use interactive graphs and tables to drill down into specific events and gain deeper insights.
  3. Scalability:
    • Handle large volumes of event data efficiently, ensuring that your monitoring setup can scale with your Kubernetes environment.

Visualizing and monitoring Kubernetes events with OpenObserve provides you with the tools needed to maintain high operational reliability and make informed decisions. By setting up detailed dashboards and proactive alerts, you can stay ahead of potential issues and ensure your clusters run smoothly.

For more information and to get started with OpenObserve, visit our website, check out our GitHub repository, or sign up here

In the next section, we’ll delve intobest practices and potential pitfalls to avoid. 

Best Practices for Deploying OpenTelemetry in Kubernetes

Deploying OpenTelemetry components within your Kubernetes environment involves several strategic decisions to ensure efficient and reliable telemetry data collection. 

This section will cover best practices for deployment, potential pitfalls to avoid, and how to enhance data correlation with custom resource attributes.

To maximize the effectiveness of your OpenTelemetry deployment, follow these best practices:

1. Deployment Patterns: Choosing the Right Mode

Selecting the appropriate deployment mode for the OpenTelemetry Collector is crucial. The two main deployment modes are:

  • DaemonSet: Deploys the collector as a daemon set, ensuring there is one collector per node. This is ideal for collecting node-level metrics and logs.
  • Deployment: Deploys the collector as a deployment, which can scale up or down based on load. This is suitable for collecting application-level telemetry data.

Example Configuration for DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: otel-collector-daemonset
  namespace: default
spec:
  selector:
    matchLabels:
      app: otel-collector
  template:
    metadata:
      labels:
        app: otel-collector
    spec:
      containers:
        - name: otel-collector
          image: otel/opentelemetry-collector:latest
          ports:
            - containerPort: 55680
          volumeMounts:
            - name: otel-config
              mountPath: /etc/otel/config
              subPath: otel-config.yml
          env:
            - name: OTEL_CONFIG
              value: /etc/otel/config/otel-config.yml
      volumes:
        - name: otel-config
          configMap:
            name: otel-collector-config

2. Avoiding Duplicate Events: Handling Single Replica Deployments

When deploying the OpenTelemetry Collector in deployment mode, using a single replica can lead to duplicate events. This happens because multiple instances of the collector might process the same events.

Best Practice:

  • Use a DaemonSet for node-level event collection to avoid duplicates.
  • If using a Deployment, ensure careful handling of state and idempotency in your processing logic.

Enhancing Data Correlation with Custom Resource Attributes

Custom resource attributes can significantly enhance the correlation and context of your telemetry data. By associating events with specific pods, nodes, or namespaces, you can gain deeper insights and improve troubleshooting efficiency.

1. Adding Custom Resource Attributes

Use the Attributes Processor in OpenTelemetry to add custom attributes to your telemetry data. This allows you to enrich the data with additional context, making it more informative and actionable.

Example Configuration:

processors:
  attributes:
    actions:
      - key: k8s.pod.name
        action: insert
        value: "my-pod"
      - key: k8s.namespace.name
        action: insert
        value: "default"

2. Associating Events with Pods and Nodes

By associating events with specific pods and nodes, you can track the impact of events on individual components within your cluster. This association helps in pinpointing issues and understanding the broader context of events.

Example Configuration:

processors:
  attributes:
    actions:
      - key: k8s.node.name
        action: insert
        value: "${k8s.node.name}"
      - key: k8s.pod.name
        action: insert
        value: "${k8s.pod.name}"



Practical Tips for Optimizing OpenTelemetry Deployment

  1. Resource Allocation:
    • Ensure the OpenTelemetry Collector has sufficient CPU and memory resources to handle the telemetry data volume.
  2. Load Balancing:
    • Use Kubernetes services to load balance telemetry data traffic, ensuring even distribution and preventing bottlenecks.
  3. Monitoring Collector Health:
    • Regularly monitor the health and performance of the OpenTelemetry Collector itself, using metrics and logs to detect and resolve issues proactively.

Deploying OpenTelemetry components within Kubernetes requires careful planning and execution. 

By following best practices for deployment, avoiding common pitfalls, and enhancing data correlation with custom attributes, you can create a robust and efficient telemetry system. This setup will provide comprehensive visibility into your Kubernetes environment, enabling proactive monitoring and informed decision-making.

In the next section, we’ll conclude with a summary of the benefits of using OpenTelemetry for Kubernetes event collection and monitoring, and discuss future trends in Kubernetes observability. 

 

Conclusion

Implementing OpenTelemetry for collecting and monitoring Kubernetes events provides you with invaluable insights into your cluster's operations. By leveraging the powerful capabilities of OpenObserve (O2), you can visualize and analyze these events in real-time, enabling proactive management and strategic decision-making. 

From setting up the OpenTelemetry Collector to creating detailed dashboards and alerts in OpenObserve, this guide has walked you through each step to ensure a robust and efficient telemetry system.

OpenTelemetry and OpenObserve together offer a comprehensive solution for enhancing the observability of your Kubernetes environments. By following best practices and optimizing your setup, you can maintain high operational reliability, improve security and compliance, and make data-driven decisions to scale and optimize your infrastructure.

For more detailed information and to start using OpenObserve, visit our website, check out our GitHub repository, or sign up here

Author:

authorImage

The OpenObserve Team comprises dedicated professionals committed to revolutionizing system observability through their innovative platform, OpenObserve. Dedicated to streamlining data observation and system monitoring, offering high performance and cost-effective solutions for diverse use cases.

OpenObserve Inc. © 2024