{"id":370,"date":"2026-01-02T05:37:00","date_gmt":"2026-01-02T05:37:00","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=370"},"modified":"2026-01-02T05:37:00","modified_gmt":"2026-01-02T05:37:00","slug":"setup-efk-stack-on-kubernetes","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=370","title":{"rendered":"How to Setup EFK Stack on Kubernetes: Step by Step Guides"},"content":{"rendered":"<p>In this <a href=\"https:\/\/devopscube.com\/kubernetes-tutorials-beginners\/\" rel=\"noreferrer noopener\">Kubernetes tutorial<\/a>, you will learn how to <strong>set up the EFK stack on a Kubernetes<\/strong> cluster for log streaming, log analysis, and log monitoring.<\/p>\n<p>Check out part 1 in this Kubernetes logging series, where we have covered <a href=\"https:\/\/devopscube.com\/kubernetes-logging-tutorial\/\" rel=\"noreferrer noopener\">Kubernetes logging fundamentals and patterns<\/a> for beginners.<\/p>\n<p>When running multiple applications and services on a Kubernetes cluster, it makes more sense to stream all of your application and Kubernetes cluster logs to one <strong>centralized logging infrastructure<\/strong> for easy log analysis.<\/p>\n<p>This beginner&#8217;s guide walks you through the key technical aspects of Kubernetes logging using the EFK stack.<\/p>\n<h2 id=\"what-is-efk-stack\">What is EFK Stack?<\/h2>\n<p>EFK stands for <strong>Elasticsearch, Fluent Bit, and Kibana<\/strong>. EFK is a popular and the best open-source choice for Kubernetes log aggregation and analysis.<\/p>\n<ol>\n<li><strong>Elasticsearch<\/strong> is a distributed, scalable search engine commonly used to search large volumes of log data. It is a NoSQL database based on the <a href=\"https:\/\/lucene.apache.org\/?ref=devopscube.com\" rel=\"noreferrer\">Apache Lucene<\/a> search engine (search library from Apache). Its primary work is to store logs and retrieve logs from Fluentbit.<\/li>\n<li><strong>Fluent Bit<\/strong> is a log shipper. It is a small but fast open-source tool that collects and forwards logs. It collects data from various sources, processes it, and then sends it to the location where the logs are stored and analyzed. It often sends data to services such as Elasticsearch, CloudWatch, Splunk, and Stackdriver.<\/li>\n<li><strong>Kibana<\/strong> is a UI tool for querying, data visualization, and dashboards. It is a query engine that allows you to explore your log data through a web interface, build visualizations for event logs, and query-specific to filter information for detecting issues. You can virtually build any type of dashboards using Kibana. <strong>Kibana Query Language (KQL<\/strong>) is used for querying Elasticsearch data. <\/li>\n<\/ol>\n<p>Also, Elasticsearch helps solve the problem of separating huge amounts of unstructured data and is in use by many organizations. Elasticsearch is commonly deployed alongside Kibana.<\/p>\n<blockquote><p><strong>Note:<\/strong> When it comes to Kubernetes, Fluent Bit is the best choice because it is a lightweight, fast tool. However, if you have a <strong>complex log-processing<\/strong> scenario, you can consider <strong>Fluentd<\/strong>.<\/p><\/blockquote>\n<p>Before we move to the setup, let us check the workflow of the EFK stack.<\/p>\n<h2 id=\"efk-stack-architecture\">EFK Stack Architecture<\/h2>\n<p>The following diagram shows the <strong>high-level architecture of the EFK stack<\/strong> we will build.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-61.png\" class=\"kg-image\" alt=\"EFK Setup Architecture\" loading=\"lazy\" width=\"2000\" height=\"1594\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/01\/image-61.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/01\/image-61.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/01\/image-61.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2026\/01\/image-61.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>EKF components get deployed as follows,<\/p>\n<ol>\n<li><strong>Fluentbit:- <\/strong>Deployed as a Daemonset, so an agent pod will run on each node to collect the logs from workloads.<\/li>\n<li><strong>Elasticsearch<\/strong>:- Deployed as a statefulset as it holds the log data. We also expose the service endpoint for Fluentbit and Kibana to connect to it.<\/li>\n<li><strong>Kibana<\/strong>:- Deployed as a deployment and connects to the Elasticsearch service endpoint.<\/li>\n<\/ol>\n<h2 id=\"prerequisites\">Prerequisites <\/h2>\n<p>Before beginning the installation, ensure you have the following <\/p>\n<ol>\n<li>A Kubernetes cluster<\/li>\n<li>Helm [Local Workstation]<\/li>\n<li>Kubectl [Local Workstation]<\/li>\n<\/ol>\n<p>Once you have a Kubernetes cluster, we can start the setup.<\/p>\n<h2 id=\"set-up-eck-operator-on-kubernetes\">Set up ECK Operator on Kubernetes<\/h2>\n<p>The first step is setting up <strong>Elasticsearch<\/strong> and <strong>Kibana<\/strong> on the cluster. <\/p>\n<p>Here, we use the <strong>ECK (Elastic Cloud on Kubernetes) Operator<\/strong> instead of the standard installation method to easily manage resources such as Elasticsearch and Kibana via Custom Resource Definitions.<\/p>\n<p>To install the ECK Operator, we even use the Helm chart.<\/p>\n<h3 id=\"step-1-add-elastic-helm-repositories\">Step 1: Add Elastic Helm Repositories <\/h3>\n<p>The first step is to add the Elastic Helm repositories to our local machine.<\/p>\n<pre><code class=\"language-bash\">helm repo add elastic https:\/\/helm.elastic.co\n\nhelm repo update<\/code><\/pre>\n<p>Once we have added and updated all the Elastic charts, we can begin the installation.<\/p>\n<h3 id=\"step-2-install-eck-operator\">Step 2: Install ECK Operator<\/h3>\n<p>To install the Elasticsearch operator, we need to choose the <code>elastic\/eck-operator<\/code> chart.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-36.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"2022\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-36.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-36.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/12\/image-36.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2025\/12\/image-36.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>In production, we do not deploy the Helm chart directly. Instead, we will make the necessary customizations based on the project requirements and then deploy.<\/p>\n<p>To do that, first download the chart or the original values file that contains the modifiable settings.<\/p>\n<p>To download the ECK operator Helm chart, run the following commands.<\/p>\n<pre><code class=\"language-bash\">helm pull elastic\/eck-operator --untar<\/code><\/pre>\n<p>The following is the directory structure of the ECK operator Helm chart.<\/p>\n<pre><code class=\"language-bash\">eck-operator\n\u251c\u2500\u2500 Chart.lock\n\u251c\u2500\u2500 Chart.yaml\n\u251c\u2500\u2500 LICENSE\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 charts\n\u2502   \u2514\u2500\u2500 eck-operator-crds\n\u2502       \u251c\u2500\u2500 Chart.yaml\n\u2502       \u251c\u2500\u2500 README.md\n\u2502       \u251c\u2500\u2500 templates\n\u2502       \u2502   \u251c\u2500\u2500 NOTES.txt\n\u2502       \u2502   \u251c\u2500\u2500 _helpers.tpl\n\u2502       \u2502   \u2514\u2500\u2500 all-crds.yaml\n\u2502       \u2514\u2500\u2500 values.yaml\n\u251c\u2500\u2500 profile-disable-automounting-api.yaml\n\u251c\u2500\u2500 profile-global.yaml\n\u251c\u2500\u2500 profile-istio.yaml\n\u251c\u2500\u2500 profile-restricted.yaml\n\u251c\u2500\u2500 profile-soft-multi-tenancy.yaml\n\u251c\u2500\u2500 templates\n\u2502   \u251c\u2500\u2500 NOTES.txt\n\u2502   \u251c\u2500\u2500 _helpers.tpl\n\u2502   \u251c\u2500\u2500 cluster-roles.yaml\n\u2502   \u251c\u2500\u2500 configmap.yaml\n\u2502   \u251c\u2500\u2500 managed-namespaces.yaml\n\u2502   \u251c\u2500\u2500 managed-ns-network-policy.yaml\n\u2502   \u251c\u2500\u2500 metrics-service.yaml\n\u2502   \u251c\u2500\u2500 operator-namespace.yaml\n\u2502   \u251c\u2500\u2500 operator-network-policy.yaml\n\u2502   \u251c\u2500\u2500 pdb.yaml\n\u2502   \u251c\u2500\u2500 podMonitor.yaml\n\u2502   \u251c\u2500\u2500 role-bindings.yaml\n\u2502   \u251c\u2500\u2500 service-account.yaml\n\u2502   \u251c\u2500\u2500 service-monitor.yaml\n\u2502   \u251c\u2500\u2500 statefulset.yaml\n\u2502   \u251c\u2500\u2500 validate-chart.yaml\n\u2502   \u2514\u2500\u2500 webhook.yaml\n\u2514\u2500\u2500 values.yaml<\/code><\/pre>\n<p>Here, you can see the original <code>values.yaml<\/code> file, but we will create a separate one based on the requirements.  <\/p>\n<p>Copy and paste the following contents into your terminal to create a custom values file.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF &gt; dev-es-values.yaml\ninstallCRDs: true\n\nreplicaCount: 1\n\nresources:\n  limits:\n    cpu: 500m\n    memory: 512Mi\n  requests:\n    cpu: 100m\n    memory: 200Mi\n\nwebhook:\n  enabled: true\n  failurePolicy: Ignore\n  manageCerts: true\n\nconfig:\n  logVerbosity: \"0\"\n  metrics:\n    port: 9090 \nEOF<\/code><\/pre>\n<p>Once the custom values file is ready, we can install the ECK operator using the following command.<\/p>\n<pre><code class=\"language-bash\">helm upgrade --install eck-operator elastic\/eck-operator \\\n  -n es-operator \\\n  --create-namespace \\\n  -f dev-es-values.yaml<\/code><\/pre>\n<p>Once installation is complete, we need to ensure the Elasticsearch operator pod is running without issues.<\/p>\n<h3 id=\"step-3-validate-the-elasticsearch-operator\">Step 3: Validate the Elasticsearch Operator <\/h3>\n<p>To ensure the Operator pod runs without issues, run the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n es-operator get po\n\nNAME                 READY   STATUS    RESTARTS      AGE\nelastic-operator-0   1\/1     Running   1 (13m ago)   14m<\/code><\/pre>\n<p>Since this is an operator, it has custom resources to manage the Elasticsearch pods.<\/p>\n<p>To list all the Custom Resource Definitions of the Elasticsearch, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds | grep elastic\n\nagents.agent.k8s.elastic.co                            2025-12-29T05:17:50Z\napmservers.apm.k8s.elastic.co                          2025-12-29T05:17:50Z\nbeats.beat.k8s.elastic.co                              2025-12-29T05:17:50Z\nelasticmapsservers.maps.k8s.elastic.co                 2025-12-29T05:17:50Z\nelasticsearchautoscalers.autoscaling.k8s.elastic.co    2025-12-29T05:17:50Z\nelasticsearches.elasticsearch.k8s.elastic.co           2025-12-29T05:17:50Z\nenterprisesearches.enterprisesearch.k8s.elastic.co     2025-12-29T05:17:50Z\nkibanas.kibana.k8s.elastic.co                          2025-12-29T05:17:50Z\nlogstashes.logstash.k8s.elastic.co                     2025-12-29T05:17:50Z\nstackconfigpolicies.stackconfigpolicy.k8s.elastic.co   2025-12-29T05:17:50Z<\/code><\/pre>\n<p>From this list, we need to use the <code>enterprisesearches.enterprisesearch.k8s.elastic.co<\/code> Custom Resource to create an Elasticsearch deployment.<\/p>\n<h3 id=\"step-4-deploy-elasticsearch\">Step 4: Deploy Elasticsearch<\/h3>\n<p>Our Elastic operator is ready, so we can create a manifest to create Elasticsearch.<\/p>\n<p>Use the following contents to create an Elasticsearch deployment. Since this is a demo, we use an optimized Elasticsearch manifest with a single node and minimal resources.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF &gt; elasticsearch.yaml\napiVersion: elasticsearch.k8s.elastic.co\/v1\nkind: Elasticsearch\nmetadata:\n  name: quickstart\n  namespace: es-operator\nspec:\n  version: 8.12.2\n  nodeSets:\n  - name: default\n    count: 1 # Optimized to 1 node for testing (production usually uses 3)\n    config:\n      node.roles: [\"master\", \"data\", \"ingest\"]\n      node.store.allow_mmap: false\n      xpack.ml.enabled: false \n      xpack.security.enabled: true\n      xpack.watcher.enabled: false \n    podTemplate:\n      spec:\n        containers:\n        - name: elasticsearch\n          resources:\n            requests:\n              memory: 1Gi\n              cpu: 200m \n            limits:\n              memory: 1.5Gi \n              cpu: 1000m\n          env:\n          - name: ES_JAVA_OPTS\n            value: \"-Xms512m -Xmx512m\" \n    volumeClaimTemplates:\n    - metadata:\n        name: elasticsearch-data\n      spec:\n        accessModes:\n        - ReadWriteOnce\n        resources:\n          requests:\n            storage: 2Gi\nEOF<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">For production, use minimum 3 nodes for the high availability as well as the appropriate resorces based on your project requirements.<\/div>\n<\/div>\n<p>To deploy this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl deploy -f elasticsearch.yaml<\/code><\/pre>\n<p>Once the Elasticsearch is deployed, we need to check whether the ElasticSearch pods are running without any issues.<\/p>\n<h3 id=\"step-5-validate-elasticsearch-deployment\">Step 5: Validate Elasticsearch Deployment<\/h3>\n<p>Once the deployment is completed, use the following command to list the Elasticsearch pods<\/p>\n<pre><code class=\"language-bash\">$ kubectl get pods -n es-operator -l common.k8s.elastic.co\/type=elasticsearch\n\nNAME                      READY   STATUS    RESTARTS   AGE\nquickstart-es-default-0   1\/1     Running   0          4m2s<\/code><\/pre>\n<p>Output show that the Pod is running without any issues.<\/p>\n<p>Since ElasticSearch is a custom resource, we can use that to list and see the status as well.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n es-operator get elasticsearch\n\nNAME         HEALTH   NODES   VERSION   PHASE   AGE\nquickstart   green    1       8.12.2    Ready   2m20s<\/code><\/pre>\n<p>Elasticsearch deployment is completed, so we need to install the Kibana dashboard to visualize the logs.<\/p>\n<h3 id=\"step-6-deploy-kibana\">Step 6: Deploy Kibana<\/h3>\n<p>As we deployed the Elasticsearch, we can use one of the Operator&#8217;s custom resource called <code>kibanas.kibana.k8s.elastic.co<\/code> to deploy Kibana.<\/p>\n<p>Use the following Kibana manifest to deploy the Kibana Pods.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF &gt; kibana.yaml\napiVersion: kibana.k8s.elastic.co\/v1\nkind: Kibana\nmetadata:\n  name: quickstart\n  namespace: es-operator\nspec:\n  version: 8.12.2\n  count: 1\n  elasticsearchRef:\n    name: quickstart\n  config:\n    server.publicBaseUrl: \"http:\/\/localhost:5601\"\n  podTemplate:\n    spec:\n      containers:\n      - name: kibana\n        resources:\n          requests:\n            memory: 512Mi\n            cpu: 200m\n          limits:\n            memory: 1Gi\n            cpu: 1000m\nEOF<\/code><\/pre>\n<p>To deploy Kibana, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f kibana.yaml<\/code><\/pre>\n<p>Once the Kibana installation is completed, we need to ensure that the Pods are running.<\/p>\n<h3 id=\"step-7-validating-kibana-deployment\">Step 7: Validating Kibana Deployment<\/h3>\n<p>Once the deployment is completed, use the following command to list the Kibana pods and services.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get pods,svc -n es-operator -l common.k8s.elastic.co\/type=kibana\n\nNAME                                 READY   STATUS    RESTARTS   AGE\npod\/quickstart-kb-7f694bf8db-pxlrs   1\/1     Running   0          11m\n\nNAME                         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE\nservice\/quickstart-kb-http   ClusterIP   10.96.138.121   &lt;none&gt;        5601\/TCP   11m<\/code><\/pre>\n<p>Output shows that the Kibana pod are running without any issues.<\/p>\n<p>Kibana also a custom resource so we can list in that way as well.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n es-operator get kibanas \n\nNAME         HEALTH   NODES   VERSION   AGE\nquickstart   green    1       8.12.2    12m<\/code><\/pre>\n<p>Now, both the Elasticsearch and the Kibana is ready. Since, Kibana is a dashboard, we need credentials to login.<\/p>\n<h3 id=\"step-8-generate-kibana-credentials\">Step 8: Generate Kibana Credentials <\/h3>\n<p>The default user name of Kibana is <code>elastic<\/code> and to generate password, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get secret quickstart-es-elastic-user -n es-operator -o go-template='{{.data.elastic | base64decode}}'\n\nYf7CCX9C4Gz9lNpnVrk8zh8Q%                      <\/code><\/pre>\n<p>Ignore the <code>%<\/code> when copy the password.<\/p>\n<h3 id=\"step-9-port-forward-kibana-service\">Step 9: Port Forward Kibana Service<\/h3>\n<p>To access the Kibana dashboard from our local machine, we need to perform port forwarding because Kibana will be deployed as a ClusterIP service.<\/p>\n<pre><code class=\"language-bash\">kubectl port-forward service\/quickstart-kb-http -n es-operator 5601\n<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">For production, deploy Kibana with a load balancer so that the load will be distributed and can be accessed with a domain name.<\/div>\n<\/div>\n<p>Now, we are ready for the Kibana login.<\/p>\n<h3 id=\"step-10-access-kibana-ui\">Step 10: Access Kibana UI<\/h3>\n<p>To access the UI, open any of the web browser and paste the URL <a href=\"https:\/\/localhost:5601\/?ref=devopscube.com\"><code>https:\/\/localhost:5601<\/code><\/a><\/p>\n<p>Once the login page is open, paste the default username and the password to enter into.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-53.png\" class=\"kg-image\" alt=\"The login page of the kibana dashboard.\" loading=\"lazy\" width=\"1036\" height=\"744\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-53.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-53.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-53.png 1036w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Once the login is completed, you will see a following page.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-54.png\" class=\"kg-image\" alt=\"The front page of the Kibana dashboard.\" loading=\"lazy\" width=\"1333\" height=\"801\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-54.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-54.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-54.png 1333w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Now, both the Elasticsearch and Kibana deployments ready, so we need to deploy the Fluentbit to collect logs.<\/p>\n<h2 id=\"setup-fluent-operator-on-kubernetes\">Setup Fluent Operator on Kubernetes<\/h2>\n<p>As like the Elastic operator, we are using an Operator for the FluentBit.<\/p>\n<p>Fluentbit is a light weight logging agent that collects logs from the workloads of each nodes.<\/p>\n<p>The collected logs will be send to the Elasticsearch for query and visualization. Elasticsearch needs to store the logs so is deployed as a StatefulSets and uses Persistent volumes as storage.<\/p>\n<p>To begin the installation, we need to add the Fluent Helm repository.<\/p>\n<h3 id=\"step-1-add-fluent-helm-repository\">Step 1: Add Fluent Helm repository<\/h3>\n<p>To add the Fluent Helm repo on our local machine, use the following command.<\/p>\n<pre><code class=\"language-bash\">helm repo add fluent https:\/\/fluent.github.io\/helm-charts\n\nhelm repo update<\/code><\/pre>\n<p>To install the Fluent operator, we use the <code>fluent\/fluent-operator<\/code> chart from the list.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-59.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"1228\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-59.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-59.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/12\/image-59.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2025\/12\/image-59.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Once all the charts are updated, we can create custom values.<\/p>\n<h3 id=\"step-2-create-custom-values-file\">Step 2: Create Custom Values File<\/h3>\n<p>Before create custom values file, we can pull the chart on the local machine.<\/p>\n<p>To pull the chart, use the following command.<\/p>\n<pre><code class=\"language-bash\">helm pull fluent\/fluent-operator --untar<\/code><\/pre>\n<p>Once the chart been download, you can see the following structure.<\/p>\n<pre><code class=\"language-bash\">fluent-operator\n\u251c\u2500\u2500 Chart.lock\n\u251c\u2500\u2500 Chart.yaml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 charts\n\u2502   \u251c\u2500\u2500 fluent-bit-crds\n\u2502   \u2502   \u251c\u2500\u2500 Chart.yaml\n\u2502   \u2502   \u251c\u2500\u2500 crds\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_clusterfilters.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_clusterfluentbitconfigs.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_clusterinputs.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_clustermultilineparsers.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_clusteroutputs.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_clusterparsers.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_collectors.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_filters.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_fluentbitconfigs.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_fluentbits.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_multilineparsers.yaml\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 fluentbit.fluent.io_outputs.yaml\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 fluentbit.fluent.io_parsers.yaml\n\u2502   \u2502   \u2514\u2500\u2500 values.yaml\n\u2502   \u2514\u2500\u2500 fluentd-crds\n\u2502       \u251c\u2500\u2500 Chart.yaml\n\u2502       \u251c\u2500\u2500 crds\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_clusterfilters.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_clusterfluentdconfigs.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_clusterinputs.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_clusteroutputs.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_filters.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_fluentdconfigs.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_fluentds.yaml\n\u2502       \u2502   \u251c\u2500\u2500 fluentd.fluent.io_inputs.yaml\n\u2502       \u2502   \u2514\u2500\u2500 fluentd.fluent.io_outputs.yaml\n\u2502       \u2514\u2500\u2500 values.yaml\n\u251c\u2500\u2500 templates\n\u2502   \u251c\u2500\u2500 NOTES.txt\n\u2502   \u251c\u2500\u2500 _helpers.tpl\n\u2502   \u251c\u2500\u2500 fluent-operator-clusterRole.yaml\n\u2502   \u251c\u2500\u2500 fluent-operator-clusterRoleBinding.yaml\n\u2502   \u251c\u2500\u2500 fluent-operator-deployment.yaml\n\u2502   \u251c\u2500\u2500 fluent-operator-service.yaml\n\u2502   \u251c\u2500\u2500 fluent-operator-servicemonitor.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterfilter-containerd.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterfilter-kubernetes.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterfilter-multiline.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterfilter-systemd.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterinput-metrics.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterinput-nodeExporterMetrics.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterinput-systemd.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-clusterinput-tail.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-containerd-config.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-fluentBit.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-fluentbit-edge.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-input-node-exporter-metrics-edge.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-input-prometheus-scrape-metrics-edge.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-lua-config.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-multilineParser-javaMultiline.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-elasticsearch.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-forward.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-kafka.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-loki.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-opensearch.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-opentelemetry.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-prometheus-exporter.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-prometheus-remote-write-edge.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-stackdriver.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-output-stdout.yaml\n\u2502   \u251c\u2500\u2500 fluentbit-servicemonitor.yaml\n\u2502   \u251c\u2500\u2500 fluentbitconfig-fluentBitConfig.yaml\n\u2502   \u251c\u2500\u2500 fluentbitconfig-fluentbitconfig-edge.yaml\n\u2502   \u251c\u2500\u2500 fluentd-clusterfluentdconfig.yaml\n\u2502   \u251c\u2500\u2500 fluentd-filter-kafka.yaml\n\u2502   \u251c\u2500\u2500 fluentd-fluentd.yaml\n\u2502   \u251c\u2500\u2500 fluentd-output-elasticsearch.yaml\n\u2502   \u251c\u2500\u2500 fluentd-output-kafka.yaml\n\u2502   \u251c\u2500\u2500 fluentd-output-opensearch.yaml\n\u2502   \u2514\u2500\u2500 serviceaccount.yaml\n\u2514\u2500\u2500 values.yaml<\/code><\/pre>\n<p>You can choose the required settings from the original values file and create a new custom one using the following contents.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF &gt; dev-fluent-values.yaml\nKubernetes: true\n\noperator:\n  enable: true\n  resources:\n    limits:\n      cpu: 50m\n      memory: 50Mi\n    requests:\n      cpu: 10m\n      memory: 20Mi\n\nfluentbit:\n  enable: true\n  resources:\n    limits:\n      cpu: 100m\n      memory: 100Mi\n    requests:\n      cpu: 10m\n      memory: 25Mi\n\nfluentd:\n  enable: false\nEOF<\/code><\/pre>\n<p>Once the custom values file is ready, we can install the Fluent Operator as well as the Pods.<\/p>\n<h3 id=\"step-3-install-fluent-operator-and-pods\">Step 3: Install Fluent Operator and Pods<\/h3>\n<p>Use the following command to install the Fluent operator and Fluentbit pods.<\/p>\n<pre><code class=\"language-bash\">helm upgrade --install fluent-operator fluent\/fluent-operator -f dev-fluent-values.yaml -n fluent --create-namespace<\/code><\/pre>\n<p>Once the installation is completed, check whether the Controller and the Fluentbit agents are running.<\/p>\n<h3 id=\"step-4-validate-the-fluent-operator-and-agents\">Step 4: Validate the Fluent Operator and Agents<\/h3>\n<p>To see the status of the Fluent operator and Fluentbit Pods, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n fluent get po,svc\n\nNAME                                   READY   STATUS    RESTARTS      AGE\npod\/fluent-bit-jtkdf                   1\/1     Running   1 (21m ago)   18h\npod\/fluent-bit-n6dtg                   1\/1     Running   1 (21m ago)   18h\npod\/fluent-bit-x2ndg                   1\/1     Running   1 (21m ago)   18h\npod\/fluent-operator-74f784fbb9-nf4gd   1\/1     Running   1 (21m ago)   18h\n\nNAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE\nservice\/fluent-bit        ClusterIP   10.96.151.168   &lt;none&gt;        2020\/TCP   18h\nservice\/fluent-operator   ClusterIP   10.96.71.8      &lt;none&gt;        8080\/TCP   18h<\/code><\/pre>\n<p>To see the Custom Resource Definitions of the Fluent, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crd | grep fluent\n\nclusterfilters.fluentbit.fluent.io                     2025-12-29T10:32:05Z\nclusterfilters.fluentd.fluent.io                       2025-12-29T10:32:08Z\nclusterfluentbitconfigs.fluentbit.fluent.io            2025-12-29T10:32:05Z\nclusterfluentdconfigs.fluentd.fluent.io                2025-12-29T10:32:08Z\nclusterinputs.fluentbit.fluent.io                      2025-12-29T10:32:05Z\nclusterinputs.fluentd.fluent.io                        2025-12-29T10:32:08Z\nclustermultilineparsers.fluentbit.fluent.io            2025-12-29T10:32:05Z\nclusteroutputs.fluentbit.fluent.io                     2025-12-29T10:32:05Z\nclusteroutputs.fluentd.fluent.io                       2025-12-29T10:32:08Z\nclusterparsers.fluentbit.fluent.io                     2025-12-29T10:32:05Z\ncollectors.fluentbit.fluent.io                         2025-12-29T10:32:06Z\nfilters.fluentbit.fluent.io                            2025-12-29T10:32:06Z\nfilters.fluentd.fluent.io                              2025-12-29T10:32:08Z\nfluentbitconfigs.fluentbit.fluent.io                   2025-12-29T10:32:06Z\nfluentbits.fluentbit.fluent.io                         2025-12-29T10:32:06Z\nfluentdconfigs.fluentd.fluent.io                       2025-12-29T10:32:08Z\nfluentds.fluentd.fluent.io                             2025-12-29T10:32:09Z\ninputs.fluentd.fluent.io                               2025-12-29T10:32:09Z\nmultilineparsers.fluentbit.fluent.io                   2025-12-29T10:32:07Z\noutputs.fluentbit.fluent.io                            2025-12-29T10:32:07Z\noutputs.fluentd.fluent.io                              2025-12-29T10:32:09Z\nparsers.fluentbit.fluent.io                            2025-12-29T10:32:07Z<\/code><\/pre>\n<p>Now, the FluentBit pods are ready to fetch the logs from the workloads.<\/p>\n<h3 id=\"step-5-create-cluster-output-custom-resource\">Step 5: Create Cluster Output Custom Resource<\/h3>\n<p>Now, we need to create cluster output custom resource to see the log output on the Fluentbit pods.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF &gt; fluent-stdout-output.yaml\napiVersion: fluentbit.fluent.io\/v1alpha2\nkind: ClusterOutput\nmetadata:\n  name: stdout\n  labels:\n    fluentbit.fluent.io\/enabled: \"true\"\nspec:\n  matchRegex: (?:kube|service)\\.(?:var\\.log\\.containers\\.counter.*)\n  stdout:\n    format: json\nEOF<\/code><\/pre>\n<p>Once the manifest is created, use the following command to deploy it.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f fluent-stdout-output.yaml<\/code><\/pre>\n<p>Once created the custom resource, we can list that using the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get clusteroutputs\n\nNAME     AGE\nstdout   3d4h<\/code><\/pre>\n<p>This ensures that the Custom Resource is created. To test the log collection, we will deploy a demo application.<\/p>\n<h2 id=\"verify-fluentd-setup\">Verify Fluentd Setup<\/h2>\n<p>To verify the Fluentd installation, let us start a pod that continuously generates logs.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF | kubectl apply -f -\napiVersion: v1\nkind: Pod\nmetadata:\n  name: counter\nspec:\n  containers:\n  - name: count\n    image: busybox\n    args:\n    - \/bin\/sh\n    - -c\n    - |\n      i=0\n      while true; do\n        echo \"Thanks for visiting devopscube! $i\"\n        i=$((i+1))\n        sleep 1\n      done\nEOF<\/code><\/pre>\n<p>Once it is deployed, the pod keeps on generate logs.<\/p>\n<h3 id=\"step-1-see-logs-from-pod\">Step 1: See Logs From Pod<\/h3>\n<p>Once we deployed the application, we can check the logs of the same pod to ensure that it generating the logs.<\/p>\n<pre><code class=\"language-bash\">$ kubectl logs counter \n\nThanks for visiting devopscube! \nThanks for visiting devopscube! \nThanks for visiting devopscube! \nThanks for visiting devopscube! <\/code><\/pre>\n<p>This ensures that the pod is properly generating the logs, so let us check the Fluentbit pods are collecting are not.<\/p>\n<h3 id=\"step-2-see-logs-through-fluentbit-pods\">Step 2: See Logs Through Fluentbit Pods<\/h3>\n<p>All the workload Pod&#8217;s logs will be stored in the same node on path <code>\/var\/log\/containers<\/code>.<\/p>\n<p>Fluentbit pods will be run as a DaemonSet, so each node has a Fluentbit pod. The agent from the same node will collect those logs from the static location.<\/p>\n<p>To check the logs of the Fluentbit pods, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl logs -n fluent -l app.kubernetes.io\/name=fluent-bit --tail=200 | grep \"counter\"\n\n[{\"date\":1767072959.964684,\"log\":\"2025-12-30T05:35:59.964536754Z stdout F Thanks for visiting devopscube! \",\"kubernetes\":{\"pod_name\":\"counter\",\"namespace_name\":\"default\",\"pod_ip\":\"10.244.1.6\",\"container_name\":\"count\",\"docker_id\":\"ec9dcdd990faf638f95a1ca724efb7ecc7da7f15ed34972eabc0c5fa2b015eee\",\"container_image\":\"docker.io\/library\/busybox:latest\"}}]\n[{\"date\":1767072960.966736,\"log\":\"2025-12-30T05:36:00.966605613Z stdout F Thanks for visiting devopscube! \",\"kubernetes\":{\"pod_name\":\"counter\",\"namespace_name\":\"default\",\"pod_ip\":\"10.244.1.6\",\"container_name\":\"count\",\"docker_id\":\"ec9dcdd990faf638f95a1ca724efb7ecc7da7f15ed34972eabc0c5fa2b015eee\",\"container_image\":\"docker.io\/library\/busybox:latest\"}}]<\/code><\/pre>\n<p>Here, you can see the same logs generated on the demo application.<\/p>\n<h3 id=\"step-3-configure-elasticsearch-for-kibana\">Step 3: Configure Elasticsearch for Kibana<\/h3>\n<p>Copy the Elasticsearch credentials into the Fluent Bit namespace for the Fluentbit to access the Elasticsearch.<\/p>\n<pre><code class=\"language-bash\">kubectl get secret quickstart-es-elastic-user -n es-operator -o yaml | sed 's\/namespace: es-operator\/namespace: fluent\/' | kubectl apply -f -<\/code><\/pre>\n<p>Create a secret for username as <code>elastic<\/code><\/p>\n<pre><code class=\"language-bash\">kubectl create secret generic elastic-username --from-literal=username=elastic -n fluent<\/code><\/pre>\n<p>Now, we need to create or modify the Cluster Output custom resource with the credentials.<\/p>\n<p>This tells that how the how the FluentBit can authenticate with the Elasticsearch.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt;EOF &gt; fluent-es-output.yaml\napiVersion: fluentbit.fluent.io\/v1alpha2\nkind: ClusterOutput\nmetadata:\n  name: es\n  labels:\n    fluentbit.fluent.io\/enabled: \"true\"\nspec:\n  matchRegex: (?:kube|service)\\.(?:var\\.log\\.containers\\.counter.*)\n  es:\n    host: quickstart-es-http.es-operator.svc\n    port: 9200\n    httpUser:\n      valueFrom:\n        secretKeyRef:\n          name: elastic-username\n          key: username\n    httpPassword:\n      valueFrom:\n        secretKeyRef:\n          name: quickstart-es-elastic-user\n          key: elastic\n    tls:\n      verify: false\n    index: fluent-bit\n    suppressTypeName: \"On\"\nEOF<\/code><\/pre>\n<p>To deploy this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f fluent-es-output.yaml<\/code><\/pre>\n<p>Once the resource is deployed, we need to restart the fluent pods.<\/p>\n<pre><code class=\"language-bash\">kubectl delete pod -n fluent -l app.kubernetes.io\/name=fluent-operator\nkubectl delete pod -n fluent -l app.kubernetes.io\/name=fluent-bit<\/code><\/pre>\n<p>To verify the Elasticsearch index, we need the credentials first.<\/p>\n<pre><code class=\"language-bash\">export ES_PASSWORD=$(kubectl get secret quickstart-es-elastic-user -n es-operator -o jsonpath='{.data.elastic}' | base64 --decode)<\/code><\/pre>\n<p>To check indices, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl run curl-test --rm -it --restart=Never --image=curlimages\/curl -- \\\n  -k -u elastic:$ES_PASSWORD https:\/\/quickstart-es-http.es-operator.svc:9200\/_cat\/indices?v<\/code><\/pre>\n<p>Now, the index is ready, so we can configure the Kibana dashboard to see the logs.<\/p>\n<h3 id=\"step-4-configure-kibana\">Step 4: Configure Kibana<\/h3>\n<p>Create a secret in the fluent namespace of the Elastic secret <\/p>\n<pre><code class=\"language-bash\">kubectl get secret quickstart-es-elastic-user -n es-operator -o json | \\\n  jq 'del(.metadata.namespace,.metadata.resourceVersion,.metadata.uid,.metadata.creationTimestamp)' | \\\n  kubectl apply -n fluent -f -<\/code><\/pre>\n<p>To see the logs in Kibana dashboard, you need to create a data view.<\/p>\n<p>Navigatew to Analytics -&gt; Discover and click &#8220;Create data view&#8221; to create a new one.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-55.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"942\" height=\"569\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-55.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-55.png 942w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Then give a name for the dataview and select the Index patten and save the view.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-56.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1439\" height=\"832\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-56.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-56.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-56.png 1439w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Once the dataview is created, you will be able to see the logs for the workload pods.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-57.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1909\" height=\"850\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-57.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-57.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/12\/image-57.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-57.png 1909w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Here, you can see the same logs that we get in the Fluentbit pods.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-58.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1515\" height=\"828\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-58.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-58.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-58.png 1515w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>This is how we can easily track the logs of our Kubernetes workload and is useful to identify the issues using the logs.<\/p>\n<h2 id=\"kubernetes-efk-best-practices\">Kubernetes EFK Best Practices<\/h2>\n<ol>\n<li><strong>Elasticsearch uses heap memory extensively<\/strong> for filtering and caching for better query performances, so ample memory should be available for Elasticsearch.\n<p>Giving more than half of total memory to Elasticsearch could also leave too less memory for OS functions which could in turn hamper Elasticsearch&#8217;s capabilities.<\/p>\n<p>So be mindful of this! A <strong>40-50% of total heap space to Elasticsearch is good enough<\/strong>.<\/li>\n<li><strong>Elasticsearch indices can fill up quickly<\/strong> so it&#8217;s important to clean up old indices regularly. Kubernetes cron jobs can help you do this regularly in an automated fashion.<\/li>\n<li>Having data replicated across multiple nodes can <strong>help in disaster recovery <\/strong>and also <strong>improve query performance<\/strong>. By default, replication factor in elasticsearch is set to 1.\n<p>Consider playing around with this values according to your use case. Having atleast 2 is a good practise.<\/li>\n<li>Data which is known to be accessed more frequently can be placed in different nodes with more resources allocated. This can be achieved by running a cronjob that moves the indices to different nodes at regular intervals.\n<p>Though this is an advance use case &#8211; it is good for a beginner to atleast have knowledge that something like this can be done.<\/li>\n<li>In Elasticsearch, you an archive indices to low cost cloud storage such as aws-s3 and restore when you need data from those indices.\n<p>This is a best practise if you need to <strong>conserve logs for audit and compliance<\/strong>.<\/li>\n<li>Having multiple nodes like master, data and client nodes with dedicated functionalities is good for high availability and fault tolerance. <\/li>\n<\/ol>\n<h2 id=\"fluentd-vs-fluent-bit\">Fluentd vs Fluent Bit<\/h2>\n<p>Most of them are still confused about choosing between Fluent and FluentBit. Here, I am providing a brief comparison to give you a better idea.<\/p>\n<p><!--kg-card-begin: html--><\/p>\n<table class=\"auto-wrap\" style=\"width: 100%;\">\n<thead>\n<tr style=\"background-color: #f2f2f2;\">\n<th style=\"border: 1px solid #ccc; padding: 12px;\">Aspect<\/th>\n<th style=\"border: 1px solid #ccc; padding: 12px;\">Fluent Bit<\/th>\n<th style=\"border: 1px solid #ccc; padding: 12px;\">Fluentd<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"border: 1px solid #ccc; padding: 12px;\"><strong>Language<\/strong><\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">C<\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">Ruby + C<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ccc; padding: 12px;\"><strong>Memory Footprint<\/strong><\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">~650 KB<\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">~40 MB<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ccc; padding: 12px;\"><strong>Plugins<\/strong><\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">100+<\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">700+<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ccc; padding: 12px;\"><strong>Best For<\/strong><\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">Edge collection, Kubernetes nodes<\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">Log aggregation, complex processing<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px solid #ccc; padding: 12px;\"><strong>Performance<\/strong><\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">High throughput, low latency<\/td>\n<td style=\"border: 1px solid #ccc; padding: 12px;\">Moderate throughput, feature-rich<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><!--kg-card-end: html--><\/p>\n<h2 id=\"beyond-efkfurther-research\">Beyond EFK &#8211; Further Research<\/h2>\n<p>This guide was just a small use case of setting up the Elastic stack on Kubernetes. Elastic stack has tons of other features which help in logging and monitoring solutions.<\/p>\n<p>For example, it can ship logs from virtual machines and managed services of various cloud providers. You can even ship logs from data engineering tools like Kafka into the Elastic Stack.<\/p>\n<p>The elastic stack has other powerful components worth looking into, such as:<\/p>\n<ol>\n<li><strong>Elastic Metrics<\/strong>: Ships metrics from multiple sources across your entire infrastructure and makes it available in elastic search and kibana.<\/li>\n<li><strong>APM<\/strong>: Expands elastic stack capabilities and lets you analyze where exactly an application is spending time quickly fixing issues in production.<\/li>\n<li><strong>Uptime<\/strong>: Helps in monitoring and analyzing availability issues across your apps and services before they start appearing in the production.<\/li>\n<\/ol>\n<p>Explore and research them!<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>In his <strong>Kubernetes EFK setup guide<\/strong>, we learned how to configure the logging infrastructure on Kubernetes.<\/p>\n<p>If you want to <a href=\"https:\/\/devopscube.com\/become-devops-engineer\/\" rel=\"noreferrer noopener\">become a DevOps engineer,<\/a> it is very important to understand all the concepts involved in the Kubernetes logging.<\/p>\n<p>In the next part of this series, we are going to explore Kibana dashboards and visualization options. <\/p>\n<p>In Kibana, it is a good practice to <strong>visualize data through graphs<\/strong> wherever possible as it gives a much more clear picture of your application state. So don&#8217;t forget to check out the next part of this series.<\/p>\n<p>Till then, keep learning and exploring.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/setup-efk-stack-on-kubernetes\/\" target=\"_blank\" rel=\"noopener noreferrer\">How to Setup EFK Stack on Kubernetes: Step by Step Guides \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/setup-efk-stack-on-kubernetes\/<\/p>\n","protected":false},"author":1,"featured_media":371,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-370","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/370","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=370"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/370\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/371"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}