IT system logs are essential in helping engineers to perform the following tasks::
- Troubleshooting application performance bottlenecks
- Determining the root cause of availability issues
- Debugging software code
- Investigating security breaches
- Planning infrastructure capacity
- Maintaining audit compliance requirements
- … and more.
Modern cloud-based, distributed applications depend heavily on container technology. These applications often rely on Kubernetes for container orchestration that yields seamless scaling and robust fault tolerance. With this shift in deployment infrastructure from bare metal to containers, logging and monitoring techniques have changed.
Storing logs in containers or virtual machines isn’t practical because both are ephemeral. Therefore, a different approach is necessary to capture logs from Kubernetes-hosted applications.
This article is part one of a guide that covers Kubernetes Logging fundamentals. We will introduce the Kubernetes logging architectures and explain the node-level logging patterns in detail.
Kubernetes Logging Basics
In a production Kubernetes environment, hundreds or even thousands of containers can exist in different states—running,, stopping, restarting, or terminating—at any given moment.
Kubernetes doesn’t provide a native solution for storing logs. However, it ships with some logging drivers to facilitate storing and aggregating logs. By default, Kubernetes uses the
json-file driver, which formats all log entries to JSON and internally caches them. The
journald logging drivers help writing to Linux log systems. Other logging drivers like FluentD, AWS CloudWatch, and GCP Logs facilitate writing logs to external log aggregator services.
Logs from Kubernetes system components
Kubernetes logs can come from the container orchestration system and from containerized applications.
Kubernetes system components include the Kubernetes Scheduler, kube-proxy, kubelet, and the container runtime. The Kubernetes scheduler and kube-proxy run inside a container and always write logs to the local
/var/log directory irrespective of the driver used. The kubelet and the container runtime write logs to the
systemd journal if it’s present or to the
/var/log directory if it’s not.
In a Kubernetes environment, the node or the cluster manages the application logs. In node-level logging, the Pod writes application logs to the node where it’s running. The container engine redirects any application log message to the
stderr streams. The node’s logging driver picks up the messages and writes them to the appropriate log file. If there’s no driver configured, logging defaults to
You can use the following command to check which logs use the default driver.
kubectl logs <pod_name>
Node-level logging, however, has a significant shortcoming: A Pod’s logs are lost if the node goes down or Kubernetes evicts that Pod.
Cluster-level logging involves a centralized logging service aggregating logs from all the nodes. This solves the problem of losing node-level logs by pushing all the node-level logs to a backend service. There are two ways to achieve cluster-level logging.
The first method uses a node-level agent configured as a DaemonSet. A DaemonSet is a Kubernetes feature where every node (or some of them) runs a copy of the Pod. In a later section of this guide, we will show how to use this approach for effective logging.
The second method uses a sidecar pattern, where every Pod contains a sidecar container that captures logs and sends those logs to an external service.
Capturing Logs Using a Node-level Logging Agent
A node-level logging agent is an always-on service that runs in a container in a separate Pod.
A node-level logging agent is useful when your environment matches the following conditions:
- There is a resource constraint in the nodes. A single node-level agent can capture logs from all the application Pods in that node, ensuring the resource consumption remains low.
- There is common log storage for all the Pods.
- There is a need for basic multi-tenant isolation without complex requirements. A separate resource allocation isn’t possible here since one logging agent caters to all application Pods.
Using a node-level logging agent has several advantages:
- It is easy to set up.
- Resource usage is low.
- Since the agent uses the same mechanism as the default logging configuration, the logs are available through the
- There’s no need to change anything in the application Pods.
Configuring the node-level logging agent in Kubernetes
The Kubernetes node-level agent uses the DaemonSet pattern. A DaemonSet runs a copy of a specific Pod in every cluster node (or some of its nodes). Every time you add a new node to the cluster, that Pod gets added to the new node as well. When you remove a node (or the cluster scales down), Kubernetes automatically garbage-collects that Pod. The DaemonSet feature allows you to run a Pod to collect logs from all the Pods in a node and send those to a log management service.
To capture logs from a specific directory, developers can use an open-source log collection agent like Fluentd. Fluentd has a GitHub repository that contains sample configurations for different logging backends.
Let’s assume that we want to send the DaemonSet Pod’s logs collected by Fluentd to an Elasticsearch backend. We would use this sample configuration from the GitHub repo.
The first step in deploying this configuration is to create a service account for the Fluentd logging agent. The code snippet below shows how to do this.
apiVersion: v1 kind: ServiceAccount metadata: name: fluentd-logger-daemonset namespace: logging labels: app: fluentd-logger-daemonset
Save the file as
fluentd_service_account.yaml, and run the command below to create the service account.
kubectl apply -f fluentd_service_account.yaml
Next, create the configuration for the DaemonSet. You can use the following configuration file to do this.
apiVersion: extensions/v1beta1 kind: DaemonSet metadata: name: fluentd namespace: kube-system labels: k8s-app: fluentd-elasticsearch-daemonset spec: template: metadata: labels: k8s-app: fluentd-elasticsearch-daemonset spec: containers: - name: fluentd image: fluent/fluentd-kubernetes-daemonset:elasticsearch env: - name: FLUENT\_ELASTICSEARCH\_HOST value: "<elasticsearch-host-url>" - name: FLUENT\_ELASTICSEARCH\_PORT value: "<elasticsearch-host-port>" volumeMounts: - name: logdir mountPath: /var/log volumes: - name: logdir hostPath: path: /var/log
The important part of this specification is the
kind attribute that specifies the Pod will run as a
spec template field is mandatory and defines the base image for creating the DaemonSet. This configuration uses the default
fluentd image as the base image. Like all Kubernetes configuration files, it’s necessary to specify the
apiVersion and the
metadata keys. The application container logs are mounted to the
You can save this configuration as
fluentd_daemonset_account.yaml and run the command below to create the Pod in all the cluster nodes.
kubectl apply -f fluentd-daemonset.yaml
This will initiate a node-level logging agent Pod in all your cluster nodes. It’s possible to run the Pod only on specific nodes by using the
nodeSelector parameter (under
spec.template.spec). Without this parameter, Kubeternetes creates the Pod in every node.
Troubleshooting containerized microservices can be tricky. Gone are the days when an engineer could log into each physical server and tail logs from the command shell. Compared to logs from other clustered applications, Kubernetes logs—both from the orchestrator and the application—are challenging to manage because Pods are ephemeral. That’s why you must collect logs from every node and send those to a central location outside the cluster for persistence and later analysis.
As we covered in this guide, a DaemonSet pattern allows quick and easy implementation of node-level logging agents in Kubernetes. You don’t have to make any changes to the application, and its resource usage is low. However, this solution doesn’t offer separate log storage or comprehensive multi-tenant isolation features for each application. For those functionalities, you would need to use a sidecar-based approach.
In Part Two of our guide, we will discuss the sidecar implementation, specifically its pros and cons, and common use cases.
Log Everything, Answer Anything – For Free
Falcon LogScale Community Edition (previously Humio) offers a free modern log management platform for the cloud. Leverage streaming data ingestion to achieve instant visibility across distributed systems and prevent and resolve incidents.
Falcon LogScale Community Edition, available instantly at no cost, includes the following:
- Ingest up to 16GB per day
- 7-day retention
- No credit card required
- Ongoing access with no trial period
- Index-free logging, real-time alerts and live dashboards
- Access our marketplace and packages, including guides to build new packages
- Learn and collaborate with an active community