Containers
How to optimize log management for Amazon EKS with Amazon FSx for NetApp ONTAP
Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments. Among Cloud Native patterns, Containers, and Kubernetes are mainstream across multiple businesses. According to the Cloud Native Computing Foundation Annual Survey of 2022, 44% of respondents are already using containers for nearly all applications and business segments, and another 35% say containers are used for at least a few production applications.
The Kubernetes in the Wild 2023 report showed that in 2021, in a typical Kubernetes cluster, application workloads accounted for most of the Pods (59%). In contrast, the non-application workloads (system and auxiliary workloads) played a relatively smaller part. In 2022, this picture was reversed. Auxiliary workloads outnumbered application workloads (63% vs. 37%) as organizations increasingly adopted advanced Kubernetes platform technologies, such as security controls, service meshes, messaging systems, and observability tools. The total number of auxiliary workloads in a typical Kubernetes cluster grew by 211% YoY, while the total number of application workloads grew by 30% YoY. Optimizing auxiliary workloads infrastructure releases more resources to support business applications, improve performance and service levels, and keep operational costs under control.
Sidecar container sprawl
Sidecar containers are a common method to capture different log streams in a Kubernetes environment. They manage log files, store them in persistent storage, and push them to the respective observability application. Although they help keep application containers small and agents free, their use at scale contributes to the growth of the cluster resource utilization with auxiliary workloads. Moreover, the resources are often duplicated across Pods. If you have 100 Pods, then you may need 100 sidecar containers, each consuming compute and memory resources and adding to your overall usage.
Instead of a sidecar container you should consider using AWS Storage services for log file persistence. Amazon Elastic Block Store (Amazon EBS), Amazon Elastic File System (Amazon EFS), and Amazon FSx file services have Container Storage Interface (CSI) drivers to provide persistent volumes for Kubernetes containers. Volumes and shares can be provisioned and mounted through kubectl and eksctl, making it easier to automate and operate at scale. For storing log files, choosing file services (Amazon EFS or Amazon FSx) helps consolidate entities to manage. One volume can be shared as a persistent volume claim across each Kubernetes Namespace, and this reduces the need for provisioning a volume per sidecar container and the associated management operations.
In large-scale deployments, a storage volume per Namespace could still mean a lot of connectivity and operations to configure. To overcome this operational overhead, we would need a storage solution that allows us to attach the respective storage to multiple nodes and make its volume available across namespaces. This is where Amazon FSx for NetApp ONTAP provides capabilities to address the multi-attach and cross namespace challenges mentioned previously.
FSx for ONTAP is a fully managed ONTAP filesystem with the following capabilities.
- Kubernetes integration: NetApp Astra Trident CSI enables provisioning and operations through Kubernetes APIs for both block (iSCSI) and file (NFS and/or SMB) volumes.
- Capacity efficiency: native data duplication and compression reduces the amount of space needed to consolidate multiple log stream files by up to 70%.
- Intelligent data replication: space-saving snapshots and clones for ease of restore and environment generation, all managed through kubectl.
- Cost optimization: capacity tier with low-cost storage for continuous optimization.
- High availability options: deploy file systems in Single-Availability Zone (AZ) or Multi-AZ configurations for quick failover, and allow containers in different AZs to write to a single volume.
The NetApp Astra Trident driver extends these capabilities with the TridentVolumeReference resource feature. With this resource, a PersistentVolumeClaim (PVC) for a given Namespace can be mounted by containers in multiple Namespaces. The logs across multiple Namespaces can be written to the same shared volume, and then the log files can be read by a log aggregator engine in a dedicated Namespace. This solution reduces the operational overhead of deploying, configuring, and maintaining sidecar logging agents across the infrastructure. It also reduces the compute and memory overhead that each sidecar container would claim on multiple Pods, as well as the number of open connections and network traffic.
The multi-log problem
An application can have multiple log streams for different purposes, such as general stdout logs, access logs, and audit logs. Furthermore, different applications might create different logs streams of their own that suit the application’s observability needs.
Each log stream has its own format, frequency, and permissions configurations, but we still need to collect these logs and push them to our log aggregation engine. The main challenge is that these logs are located on disposable Pod storage and cannot be accessed or streamed in the same way as the default log streams from stdout/err.
In a common log architecture (as shown in the preceding figure), you use a DaemonSet that runs the collector agent to stream the logs to the central log agent. However, working with this architecture can prove challenging when each application can save different log types (format, write frequency, etc.). The operation team would have to know when each new log type is added and understand from the application developers how to handle and parse it.
There are a few ways to deal with this challenge. First would be using a sidecar container from the log collector engine to stream these additional logs files, and second would be to save these additional files in a PVC for persistency and then stream that into the log engine.
Solution A – Sidecar container streaming
A sidecar container runs as an additional application with a dedicated role and functionality inside the application Pod. In our case that would be a log collection agent to stream the additional log files from each application Pods directly into our log collection engine. This is achieved using a local mount shared by both the main container and the sidecar (marked pink in the following figure).
Upsides:
- Stream individual log files as needed by each application
Downsides:
- Resources used by the sidecars are duplicated, meaning if you have 100 Pods you need 100 sidecars, each consuming its own resources (RAM, CPU).
- Configuration, deployment, and maintenance of the sidecar component
- Open connections by tens or hundreds of sidecars to log aggregation engines
Solution B – Collect logs in PVC
Since PVC is a namespaced object, we can create a volume per namespace that holds all of the applications’ log streams in a single namespace, as shown in the following figure. We also need a single collector per namespace to stream the data from this namespaced volume to the target logging system. Therefore, we must configure it with the accessType “ReadWriteMany”.
Upsides:
- No sidecar containers, meaning a more centralized approach in log collection and shipment
- Low resource overhead, meaning one log agent per namespace
Downsides:
- PVC per namespace can be expensive and inefficient
- Need to manage storage operation and many volumes
Solution C – Sharing scalable volume across namespaces
This solution is an evolution of the previous one (Solution B), which uses a functionality unique to FSx for ONTAP that removes the need to manage multiple PVCs per namespace.
ONTAP’s CSI driver has the ability to share PVCs across namespaces (see this Astra Trident doc). It does this by using a CustomResourceDefinition (CRD) called TridentVolumeReference that enables the referencing of an existing PVC. With this we can consolidate all of our applications’ logs into a single volume, as well as run a single collector that has access to the same volume. In turn, we have not only reduced the number of volumes we need to manage, but also we’ve run a single collector that reduces the excess usage of compute resources in our cluster. Since FSx for ONTAP is a managed ONTAP solution, it can be used with your Amazon Elastic Kubernetes Service (Amazon ) cluster to achieve this architecture, as shown in the following figure.
Upsides:
- Single volume for all of our applications regardless of which namespace they’re running in
- No sidecar containers
- No excess compute resource consumption, meaning one logging agent per cluster
- Volume configuration can be managed by platform builders, where applications only need to reference that volume (using TridentVolumeReference)
Downsides:
- Careful configuration of mount paths for Pods to support multi-tenancy (such as a naming convention for path per namespace)
- Using objects other than the official CSI supported objects (TridentVolumeReference)
Getting Started
In this next section walk you through a quick-start implementation of this solution.
Prerequisites
The following prerequisites are required to complete this section:
- AWS Command Line Interface (AWS CLI): to interact with the AWS API
- kubectl: for interacting with the EKS cluster
- Helm: for Trident controller installation
Trident CSI EKS Add-on: to use the Trident CSI EKS Add-on, first you must subscribe to this add-on (note that subscribing and using the add-on doesn’t incur a cost, as it’s a free add-on to use under the Amazon EKS managed add-ons. To do that, head to this AWS Marketplace link and subscribe to that add-on, as shown in the following image.
Walkthrough
- Setting up your file system and Amazon EKS using Terraform
- EKS cluster version: 1.29
- FSx for ONTAP file system and storage virtual machine (SVM). You can read more about manually creating these resources in the FSx for ONTAP getting started guide.
- Configure Trident CSI backend to FSx for ONTAP.
- Create shared log collection infrastructure
- Create the shared PVC for the logs.
- Install Fluent-bit log collection so it mounts the shared PVC to Fluent Bit Pods.
- Deploy the sample application
- Create PVC to reference to the shared log PVC.
- Run sample application and log event.
- Verify log stream with Fluent Bit.
Create an EKS cluster and FSx for ONTAP file system using Terraform
In this section of the post, we create and configure an FSx for ONTAP file system with Amazon EKS and the Trident CSI driver for persistent storage. We can now run all the commands from the machine that we have installed in the Prerequisites section. Clone the sample repository from GitHub and create the relevant resources using the Terraform code in that repository:
This process can take 30–45 minutes to complete. When finished, the output of the command should look like the following:
Verify that the Trident CSI configured to use the FSx for ONTAP filesystem
Since the Terraform configuration created the StorageClass and the TridentBackendConfig for use, we need to verify that it was configured successfully. Run the following commands and verify that the output is similar to the response you’re getting from the cluster.
Create the shared log collection infrastructure
Now that we’ve finished installing the Trident CSI driver and connecting it to our FSx for ONTAP file system, we can create the log collection shared infrastructure and connect that to the shared PVC. For this post, we use Fluent-bit as the log collector.
- Create the “fluent” namespace, as well as the PersistentVolumeClaim that is shared across all namespaces (fluent and sample apps).
Note the annotation of “https://trident.netapp.io/shareToNamespace: ‘*’”, which tells the trident CSI driver to share this PVC to other namespaces.
2. Add the fluent-bit Helm repo and install the fluent-bit operator.
Note the configuration of the Fluent Bit under the “./manifests/log-collection/fluentbit-values.yaml”.
We’re configuring Fluent Bit to read the log files under “/var/log/*/*/*.log”. This path is where our sample-app Pods writes their log files. In addition, to make this walkthrough clear, we’ve configured the output to stdout so we can run kubectl logs on the fluent-bit Pods to get the log records (instead of configuring log system services such as Amazon CloudWatch or Amazon OpenSearch).
Deploy sample application
Our sample applications is deployed into two different namespaces where each “app” writes the date and time into a file, which is expected to be collected by the Fluent Bit deployment we deployed earlier.
The sample manifests are under “./manifests/sample-application/”. If you look at any of the files, then you can see that it first creates an object called TridentVolumeReference, which references the PVC created earlier in the fluent namespace. Then the PVC object has been created, so the sample application can use it. However, since the TridentVolumeReference was created earlier, it tells the Trident CSI driver to not create an actual PersistenVolume (PV) for that PVC, rather to point to the PV created in the fluent namespace
1.Create the sample application shared volume and volume reference.
2. At this point, our application outputs the date into path. We can now expect the logs of the Fluent Bit Pods to have the output records from both sample applications deployments.
Note that the records come from two different Pods running in two different namespaces (see “ns1” and “ns2” in the log record name).
Cleaning up
To clean all resources, first delete the deployments:
Then uninstall Fluent Bit by running the following:
Finally, delete all resources by running Terraform destroy.
Summary
In this post, we demonstrated how to use FSX for ONTAP as a centralized storage service to collect logs from multiple applications deployed on Amazon EKS. By creating a shared storage volume, applications in different Kubernetes namespaces could write log records to custom files, which were then collected and forwarded by Fluent Bit to remote logging services. This approach provides scalable, durable log storage and simplifies the log management process, offering flexibility to adapt to various logging requirements.