Skip to navigationSkip to main contentSkip to footerScaleway Docs HomepageAsk our AI
Ask our AI

How to send logs from your Kubernetes cluster to your Cockpit

Follow this procedure to send application logs from your Kubernetes cluster to your Cockpit. You can use Helm directly, or deploy the Helm chart with Terraform or OpenTofu.

You will use the k8s-monitoring Helm chart, which installs an Alloy Daemon set to export the logs of your Kubernetes cluster to your Cockpit.

Before you start

To complete the actions presented below, you must have:

  • A Scaleway account logged into the console
  • Owner status or IAM permissions allowing you to perform actions in the intended Organization
  • Created a custom data source of type Logs
  • Created a Cockpit token in the same region as the data source of the logs
  • A running Kubernetes cluster containing your deployed application
  • Created an API key and retrieved your API secret key
  • Installed Helm
Important

Sending logs for Scaleway resources or personal data using an external path is a billable feature. In addition, any data that you push yourself is billed, even if you send data from Scaleway products. See the product pricing page for more information.

Configure the Helm chart

Create a values.yml file to configure your Helm chart to send logs from your Kubernetes cluster to Cockpit, using the following example.

The example Helm chart can be configured to send both logs and metrics to your Cockpit. In this article, we are interested in the logs part only. For information about setting up metrics collection, see the sending metrics from your Kubernetes cluster to Cockpit article.

Ensure that you replace:

  • CHANGE_ME_CLUSTER_NAME with the name of your Scaleway Kubernetes cluster
  • CHANGE_ME_LOGS_PUSH_URL with the URL of your custom log data source (you can find it under the "API URL" section in the Data sources tab of the Scaleway console)
  • CHANGE_ME_TOKEN (under cockpit-logs) with your Cockpit token
# Grafana k8s-monitoring Helm chart values for Scaleway Cockpit

cluster:
  name: CHANGE_ME_CLUSTER_NAME

global:
  scrapeInterval: 60s

destinations: 
  cockpit-metrics:
    type: prometheus

    metrics:
      enabled: true
    logs:
      enabled: false
    traces:
      enabled: false

    url: CHANGE_ME_METRICS_PUSH_URL/api/v1/push
    tenantId: CHANGE_ME_TOKEN

    queueConfig:
      sampleAgeLimit: 1h

  cockpit-logs:
    type: loki

    metrics:
      enabled: false
    logs:
      enabled: true
    traces:
      enabled: false

    url: CHANGE_ME_LOGS_PUSH_URL/loki/api/v1/push
    tenantId: CHANGE_ME_TOKEN

clusterMetrics:
  enabled: true
  destinations: ["cockpit-metrics"]
  collector: metrics-collector

  nodeLabels:
    nodePools: true
    regions: true
    availabilityZone: true
    instanceType: true

  # Already included in Scaleway data source
  controlPlane:
    enabled: false

  kubelet:
    enabled: true
    metricsTuning:
      useDefaultAllowList: false
      useIntegrationAllowList: false
      includeMetrics:
        # Volume statistics metrics required by Grafana dashboards
        - kubelet_volume_stats_capacity_bytes
        - kubelet_volume_stats_inodes
        - kubelet_volume_stats_inodes_used
        - kubelet_volume_stats_used_bytes

  kubeletResource:
    enabled: true
    # Adjustments to the scraped metrics to filter the amount of data sent to storage
    metricsTuning:
      useDefaultAllowList: false
      useIntegrationAllowList: false
      includeMetrics:
        - node_cpu_usage_seconds_total
        - node_memory_working_set_bytes

  kubeletProbes:
    enabled: false

  cadvisor:
    enabled: true
    metricsTuning:
      useDefaultAllowList: false
      includeMetrics:
        # Container metrics required by Grafana dashboards
        - container_cpu_cfs_throttled_seconds_total
        - container_cpu_usage_seconds_total
        - container_memory_working_set_bytes
        - container_network_receive_bytes_total
        - container_network_receive_errors_total
        - container_network_receive_packets_dropped_total
        - container_network_receive_packets_total
        - container_network_transmit_bytes_total
        - container_network_transmit_errors_total
        - container_network_transmit_packets_dropped_total
        - container_network_transmit_packets_total
        - container_oom_events_total
        # Machine metrics
        - machine_cpu_cores
        - machine_memory_bytes

  # Already included in Scaleway data source
  apiServer:
    enabled: false

  kubeControllerManager:
    enabled: false

  kubeDNS:
    enabled: false

  kubeProxy:
    enabled: false

  kubeScheduler:
    enabled: false

  kube-state-metrics:
    enabled: true
    metricsTuning:
      useDefaultAllowList: false
      includeMetrics:
        # Kubernetes state metrics required by Grafana dashboards
        - kube_configmap_info
        - kube_daemonset_labels
        - kube_deployment_labels
        - kube_deployment_status_replicas_available
        - kube_deployment_status_replicas_unavailable
        - kube_endpoint_info
        - kube_hpa_labels
        - kube_ingress_info
        - kube_namespace_created
        - kube_namespace_labels
        - kube_networkpolicy_labels
        - kube_node_info
        - kube_persistentvolumeclaim_info
        - kube_pod_container_info
        - kube_pod_container_resource_limits
        - kube_pod_container_resource_requests
        - kube_pod_container_status_last_terminated_exitcode
        - kube_pod_container_status_last_terminated_reason
        - kube_pod_container_status_ready
        - kube_pod_container_status_restarts_total
        - kube_pod_container_status_running
        - kube_pod_container_status_terminated
        - kube_pod_container_status_waiting
        - kube_pod_info
        - kube_pod_status_phase
        - kube_pod_status_qos_class
        - kube_pod_status_reason
        - kube_secret_info
        - kube_service_info
        - kube_statefulset_labels

hostMetrics:
  enabled: true
  destinations: ["cockpit-metrics"]
  collector: metrics-collector

  linuxHosts:
    enabled: true
    # Adjustments to the scraped metrics to filter the amount of data sent to storage
    metricsTuning:
      useDefaultAllowList: false
      useIntegrationAllowList: false
      includeMetrics:
        # Node system metrics required by Grafana dashboards
        - node_context_switches_total
        - node_cpu_core_throttles_total
        - node_cpu_seconds_total
        - node_disk_io_now
        - node_disk_read_bytes_total
        - node_disk_reads_completed_total
        - node_disk_writes_completed_total
        - node_disk_written_bytes_total
        - node_filefd_allocated
        - node_filefd_maximum
        - node_filesystem_avail_bytes
        - node_filesystem_device_error
        - node_filesystem_files
        - node_filesystem_files_free
        - node_filesystem_size_bytes
        - node_intr_total
        - node_load1
        - node_load15
        - node_load5
        - node_uname_info
        - node_memory_Buffers_bytes
        - node_memory_Cached_bytes
        - node_memory_MemAvailable_bytes
        - node_memory_MemFree_bytes
        - node_memory_MemTotal_bytes
        - node_memory_SwapFree_bytes
        - node_memory_SwapTotal_bytes
        - node_netstat_Tcp_CurrEstab
        - node_network_receive_bytes_total
        - node_network_receive_drop_total
        - node_network_receive_errs_total
        - node_network_receive_packets_total
        - node_network_transmit_bytes_total
        - node_network_transmit_drop_total
        - node_network_transmit_errs_total
        - node_network_transmit_packets_total
        - node_nf_conntrack_entries
        - node_nf_conntrack_entries_limit
        - node_time_seconds
        - node_boot_time_seconds
        - node_timex_estimated_error_seconds
        - node_timex_maxerror_seconds

  windowsHosts:
    enabled: false

clusterEvents:
  enabled: true
  # We can use source : kubernetes-events instead
  # extraLogProcessingStages: |-
  #   stage.static_labels {
  #     values = {
  #       log_source = "clusterEvents",
  #     }
  #   }
  # labelsToKeep:
  # - job
  # - level
  # - namespace
  # - node
  # - source
  # - reason
  # - log_source
  destinations: ["cockpit-logs"]
  collector: events-collector

nodeLogs:
  enabled: true
  # We can use source : journal instead
  # extraLogProcessingStages: |-
  #   stage.static_labels {
  #     values = {
  #       log_source = "nodeLogs",
  #     }
  #   }
  # labelsToKeep:
  #  - instance
  #  - job
  #  - level
  #  - name
  #  - unit
  #  - service.name
  #  - source
  #  - log_source
  destinations: ["cockpit-logs"]
  collector: logs-collector

podLogsViaLoki:
  enabled: true
  staticLabels:
    source: "podLogs"
  # Add an indexed label containing the podname from structured metadata for ease of filtering in dashboards
  extraLogProcessingStages: |-
    stage.labels {
      values = {
        pod = "pod",
      }
    }
  destinations: ["cockpit-logs"]
  volumeGatherSettings:
    onlyGatherNewLogLines: true
  # Filter pods at discovery level - only keep pods with the annotation `cockpit/logs=true`
  extraDiscoveryRules: |
    rule {
      source_labels = ["__meta_kubernetes_pod_annotation_cockpit_logs"]
      action = "keep"
      regex = "true"
    }

  # Copy Kubernetes Pod labels to log labels
  labels:
    app_kubernetes_io_name: app.kubernetes.io/name
    container: container
    instance: instance
    job: job
    level: level
    namespace: namespace
    service_name: service.name
    service_namespace: service.namespace
    deployment_environment: deployment.environment
    deployment_environment_name: deployment.environment.name
    k8s_namespace_name: k8s.namespace.name
    k8s_deployment_name: k8s.deployment.name
    k8s_statefulset_name: k8s.statefulset.name
    k8s_daemonset_name: k8s.daemonset.name
    k8s_cronjob_name: k8s.cronjob.name
    k8s_job_name: k8s.job.name
    k8s_node_name: k8s.node.name
    source: source
    pod: pod

  collector: logs-collector

podLogsViaKubernetesApi:
  enabled: false

applicationObservability:
  enabled: false

autoInstrumentation:
  enabled: false

annotationAutodiscovery:
  enabled: true
  destinations: ["cockpit-metrics"]
  annotations:
    # -- Annotation for enabling scraping for this service or pod. Value should be either "true" or "false"
    # @section -- Annotations
    scrape: "cockpit/metrics"
    # -- Annotation for overriding the job label
    # @section -- Annotations
    job: "cockpit/job"
    # -- Annotation for overriding the instance label
    # @section -- Annotations
    instance: "cockpit/instance"
    # -- Annotation for selecting the specific container to scrape
    # @section -- Annotations
    metricsContainer: "cockpit/metrics.container"
    # -- Annotation for setting or overriding the metrics path. If not set, it defaults to /metrics
    # @section -- Annotations
    metricsPath: "cockpit/metrics.path"
    # -- Annotation for setting the metrics port by name
    # @section -- Annotations
    metricsPortName: "cockpit/metrics.portName"
    # -- Annotation for setting the metrics port by number
    # @section -- Annotations
    metricsPortNumber: "cockpit/metrics.portNumber"
    # -- Annotation for setting the metrics scheme, default: http
    # @section -- Annotations
    metricsScheme: "cockpit/metrics.scheme"
    # -- Annotation for setting `__param_<key>` parameters when scraping
    # Example: `cockpit/metrics.param_key: "value"`
    # @section -- Annotations
    metricsParam: "cockpit/metrics.param"
    # -- Annotation for overriding the scrape interval for this service or pod. Value should be a duration like "15s, 1m"
    # Overrides metrics.autoDiscover.scrapeInterval
    # @section -- Annotations
    metricsScrapeInterval: "cockpit/metrics.scrapeInterval"
    # -- Annotation for overriding the scrape timeout for this service or pod. Value should be a duration like "15s, 1m"
    # Overrides metrics.autoDiscover.scrapeTimeout
    # @section -- Annotations
    metricsScrapeTimeout: "cockpit/metrics.scrapeTimeout"
  pods:
    staticLabels:
      metric_source: "podsAnnotationAutodiscovery"
  services:
    staticLabels:
      metric_source: "servicesAnnotationAutodiscovery"
  collector: metrics-collector

prometheusOperatorObjects:
  enabled: true
  destinations: ["cockpit-metrics"]
  probes:
    enabled: true
    labelSelector: |-
      match_expression {
        key = "cockpit/metrics"
        operator = "In"
        values = ["true", "on", "yes", "1"]
      }
    extraMetricProcessingRules: |-
      rule {
        target_label = "metric_source"
        replacement = "promObjectProbes"
        source_labels = []
      }

  podMonitors:
    enabled: true
    labelSelector: |-
      match_expression {
        key = "cockpit/metrics"
        operator = "In"
        values = ["true", "on", "yes", "1"]
      }
    extraMetricProcessingRules: |-
      rule {
        target_label = "metric_source"
        replacement = "promObjectPodMonitors"
        source_labels = []
      }

  serviceMonitors:
    enabled: true
    labelSelector: |-
      match_expression {
        key = "cockpit/metrics"
        operator = "In"
        values = ["true", "on", "yes", "1"]
      }
    extraMetricProcessingRules: |-
      rule {
        target_label = "metric_source"
        replacement = "promObjectServiceMonitors"
        source_labels = []
      }
  collector: metrics-collector

profiling:
  enabled: false

profilesReceiver:
  enabled: false

integrations:
  destinations: []
  collector: metrics-collector

selfReporting:
  enabled: false

collectors:
  metrics-collector:
    presets: [clustered, statefulset]
  logs-collector:
    presets: [filesystem-log-reader, daemonset]
  events-collector:
    presets: [singleton]

collectorCommon:
  alloy:
    alloy:
      logging:
        level: info
        format: logfmt
      # Easier debug, but consumes more resources
      liveDebugging:
        enabled: false
      enableReporting: false

telemetryServices:
  kube-state-metrics:
    deploy: true
  node-exporter:
    deploy: true
  windows-exporter:
    deploy: false
  kepler:
    deploy: false
  opencost:
    deploy: false

alloy-operator:
  deploy: true

extraObjects: []
Tip

The PodLogs feature automatically discovers and collects logs from all pods. The example configuration only keeps pods with the annotation cockpit/logs=true. If you want to exclude certain types of pods, you can add drop rules in the extraDiscoveryRules section.

Send Kubernetes logs using Helm chart

Once you have configured your values.yml file, you can use Helm to deploy the log-forwarding configuration to your Kubernetes cluster.

Before installing the Helm chart, ensure that your kubectl tool is properly connected to your Kubernetes cluster. kubectl is the command-line tool for interacting with Kubernetes clusters.

  1. Connect kubectl to your Kubernetes cluster.

  2. Run the following commands to install the k8s-monitoring Helm chart.

    Ensure that you replace:

    • /your-path/to/values.yml with the correct path where your values.yml file is stored

    • name-of-your-choice-for-your-log-ingester with a clear name (e.g., alloy-logs-ingester)

      helm repo add grafana https://grafana.github.io/helm-charts
      helm repo update
      helm install -f /your-path/to/values.yml name-of-your-choice-for-your-log-ingester grafana/k8s-monitoring --version 4.5.0

      The -f flag specifies the path to your values.yml file, which contains the configuration for the Helm chart.

      Helm installs the k8s-monitoring chart, which includes the Alloy DaemonSet configured to collect logs from your Kubernetes cluster.

      The DaemonSet ensures that a pod is running on each node in your cluster, which collects logs and forwards them to the specified Loki endpoint in your Cockpit.

  3. (Optional) Run the following command to check the status of the release and ensure it was installed:

    helm list

Send Kubernetes logs using Helm chart with Terraform/OpenTofu

You can also use Terraform/OpenTofu to manage and deploy Helm charts, providing you with more automation and consistency to manage your Kubernetes resources.

  1. Create a provider.tf file and paste the following template to set up the Helm Terraform/OpenTofu provider.

    Ensure that you replace:

    • your_k8s_cluster_host with the URL of your Kubernetes API server
    • your_k8s_cluster_token with the authentication token to access the cluster
    • your_k8s_cluster_ca_certificate with the CA certificate of the cluster
    provider "helm" {
      kubernetes = {
        host = "your_k8s_cluster_host"
        token = "your_k8s_cluster_token"
        cluster_ca_certificate = base64decode("your_k8s_cluster_ca_certificate")
      }
    }
  2. Create a maint.tf file and paste the following template to create a Helm release resource.

    Make sure that you replace:

    • /your-path/to/values.yml with the actual path to your values file
    • name-of-your-log-ingester with your chosen name of your log ingester (e.g., alloy-logs-ingester)
    resource "helm_release" "alloy" {
      name = "name-of-your-log-ingester"
      repository = "https://grafana.github.io/helm-charts"
      chart = "k8s-monitoring"
      version = "4.5.0"
    
      namespace = "log-ingester"
      create_namespace = true
      values = [file("/your-path/to/values.yml")]
    }
  3. Save your changes.

  4. Run terraform init to initialize your Terraform/OpenTofu configuration and download any necessary providers.

  5. Run terraform apply to apply your configuration.

  6. Type yes when prompted to confirm the actions.

Explore your logs in Cockpit

  1. In the Scaleway console side menu, go to Monitoring > Cockpit. The Overview page opens.
  2. Click Access Grafana to open your preconfigured dashboards in Grafana. You are redirected to the Grafana website.
  3. Log in to your Grafana account.
  4. Click Explore in the Grafana main menu.
  5. Select your custom data source in the search dropdown.
  6. In the Labels filter dropdown, select the cluster label and in the Value dropdown, select your cluster.
  7. (Optional) Click the Clock icon and filter by time range.
  8. Click Run query to see your logs. An output similar to the following should display.

To learn how to set up log collection using the Scaleway CLI, see:

For detailed information about how to set up a complete monitoring stack (logs and metrics) using Terraform, see:

Still need help?

Create a support ticket
No Results