1. The overall solution of unified log management

Application and system logs provide insight into what is happening within the Kubernetes cluster and are useful for debugging problems and monitoring cluster activity. For most applications, there will be some kind of logging mechanism. Therefore, most container engines are also designed to support some kind of logging mechanism. The simplest and most acceptable method of logging for containerized applications is to write log content to standard output and standard error stream.

However, the native functionality provided by the container engine or runtime is usually insufficient to support a complete logging solution. For example, if a container crashes, a Pod is expelled, or a Node dies, application stakeholders may still need access to the application’s logs. Therefore, logs should have separate storage and lifecycle independent of Node, Pod, or container, a concept known as cluster-level logging. Cluster-level logging requires a separate back end to store, analyze, and query logs. Kubernetes itself does not provide a native storage solution for log data, but many existing logging solutions can be integrated into the Kubernetes cluster. In Kubernetes, there are three levels of logging:

  • Based on the log

  • Node level logs

  • Cluster level logging architecture

1.1 Basic Logs

Kubernetes basic logging is to output log data to the standard output stream. You can use the kubectl logs command to obtain container log information. If there are multiple containers in the Pod, you can specify which container’s logs to access by appending the container name to the command. For example, if you have a Pod named Nexus3-F5b7fc55C-hq5v7 under the devops namespace in the Kubernetes cluster, you can retrieve logs by using the following command:


     
  1. $ kubectl logs nexus3-f5b7fc55c-hq5v7 --namespace=devops

Copy the code

1.2 Node level Logs

Everything that container applications write to stdout and Stderr is handled and redirected by the container engine. For example, the Docker container engine redirects these two streams to the logging driver, which in Kubernetes is configured to write files in JSON format. The Docker JSON logging driver treats each line as a separate message. When using the Docker logging driver, multi-line messages are not supported and therefore need to be processed at the logging agent level or higher.

By default, kubectl keeps a terminated container and its logs if the container is restarted. If a Pod is expelled from Node, all corresponding containers in the Pod are expelled, along with their logs. An important consideration in logging at the Node level is to implement log rotation so that logs do not consume all available storage on Node. Kubernetes is not currently responsible for rotation logging and the deployment tool should build a solution to solve this problem.

There are two types of system components in Kubernetes: components running in containers and components not running in containers. Such as:

  • The Kubernetes scheduler and kube-Proxy run in the container.

  • Kubelet and container runtimes, such as Docker, do not run in containers.

On machines with Systemd, kubelet and container runtime write journaId. If systemd does not exist, they write.log files to the /var/log directory. The system components in the container always bypass the default logging mechanism and write to the /var/log directory using the GOLg log library. You can find conventions in logging for which components record severity in the development document.

Similar to container logs, system component logs in the /var/log directory should be rotated. These logs are configured to be rotated daily by Logrotate or when the size exceeds 100MB.

1.3 Cluster-level log Architecture

Kubernetes itself does not provide a native solution for cluster level logging, but there are several common approaches that can be taken:

  • Use node-level logging agents that run on each Node;

  • Include a Sidecar for logging in the application Pod.

  • Push logs directly from within the application to the back end.

All things considered, this article uses cluster level logging by including Node level logging agents on each Node. A logging agent exposes logs or pushes logs to a specialized tool on the back end. Typically, logging-Agent is a container that has access to the log files of all application containers on the Node.

Because logging must be run on every Node, it is often used as a copy of DaemonSet, or as a dedicated native process on a manifest Pod or Node. However, the latter two methods will be abandoned in the future. Using Node level logging agents is the most common and popular approach for Kubernetes clustering, as it creates only one agent per Node and does not require any changes to the applications running on the nodes. However, Node level logging only applies to application standard output and standard error.

Kubernetes itself does not specify a logging agent, but two alternative logging agents are available packaged with Kubernetes versions: Stackdriver and Elasticsearch with Google Cloud platform, both of which use custom configured FluentD as agent on Node. In this solution, Fluentd is used for logging-agent, Elasticsearch is used for Logging Backend, and Grafana is used for front-end display. Fluentd is used as logging-agent to collect logs and push them to the backend Elasticsearch. Grafana takes the logs from Elasticsearch and displays them uniformly.

2. Install the unified log management component

In this paper, the use of Node logging agent aspects of Kubernetes unified log management, related tools are used:

  • Logging-agent: The logging agent is used to fetch log information from the container, using Fluentd.

  • Logging-backend: The log Backend processes logs pushed by the log Logging agent. The log Backend uses Elasticsearch.

  • Log display: Log display is used to display unified log information to users, using Kibana.

Elasticsearch add-on is available in Kubernetes. This component includes Elasticsearch, Fluentd, and Kibana. Elasticsearch is a search engine that stores logs and allows queries. Fluentd gets the log message from Kubernetes and sends it to Elasticsearch. Kibana is a graphical interface for viewing and querying logs stored in Elasticsearch. Requirements for the environment before installation and deployment are as follows:

2.1 Installing and Deploying Elasticsearch

Elasticsearch is an open source search and data analysis engine based on Apache Lucene(TM). Elasticsearch is developed in Java and uses Lucene as its core for all indexing and searching functions. It aims to make full-text search easy by hiding Lucene’s complexity with a simple RESTful API. Elasticsearch is more than just Lucene and full text search, it also provides the following capabilities:

  • Distributed real-time file storage where every field is indexed and searchable;

  • Distributed real-time analytical search engine;

  • Scalable to hundreds of servers, processing petabytes of structured or unstructured data.

Elasticsearch contains multiple indexes, and each Index can contain multiple types. Each of these different types can store multiple documents, and each Document has multiple properties. Like a database in a traditional relational database, an index is a place to store relational documents. Elasticsearch uses a standard RESTful API and JSON. In addition, it has built and maintained clients in many other languages, such as Java, Python,.NET, and PHP.

The YAML configuration file for Elasticsearch defines a ServiceAccount named ElasticSearch-Logging and grants it access to namespaces, services, and endpoints. Deploy Elasticsearch as StatefulSet.


     
  1. # RBAC authn and authz

  2. apiVersion: v1

  3. kind: ServiceAccount

  4. metadata:

  5.  name: elasticsearch-logging

  6.  namespace: kube-system

  7.  labels:

  8.    k8s-app: elasticsearch-logging

  9.    kubernetes.io/cluster-service: "true"

  10.    addonmanager.kubernetes.io/mode: Reconcile

  11. ---

  12. kind: ClusterRole

  13. apiVersion: rbac.authorization.k8s.io/v1

  14. metadata:

  15.  name: elasticsearch-logging

  16.  labels:

  17.    k8s-app: elasticsearch-logging

  18.    kubernetes.io/cluster-service: "true"

  19.    addonmanager.kubernetes.io/mode: Reconcile

  20. rules:

  21. - apiGroups:

  22.  - ""

  23.  resources:

  24.  - "services"

  25.  - "namespaces"

  26.  - "endpoints"

  27.  verbs:

  28.  - "get"

  29. ---

  30. kind: ClusterRoleBinding

  31. apiVersion: rbac.authorization.k8s.io/v1

  32. metadata:

  33.  namespace: kube-system

  34.  name: elasticsearch-logging

  35.  labels:

  36.    k8s-app: elasticsearch-logging

  37.    kubernetes.io/cluster-service: "true"

  38.    addonmanager.kubernetes.io/mode: Reconcile

  39. subjects:

  40. - kind: ServiceAccount

  41.  name: elasticsearch-logging

  42.  namespace: kube-system

  43.  apiGroup: ""

  44. roleRef:

  45.  kind: ClusterRole

  46.  name: elasticsearch-logging

  47.  apiGroup: ""

  48. ---

  49. # Elasticsearch deployment itself

  50. apiVersion: apps/v1

  51. kind: StatefulSet

  52. metadata:

  53.  name: elasticsearch-logging

  54.  namespace: kube-system

  55.  labels:

  56.    k8s-app: elasticsearch-logging

  57. Version: v6.2.5

  58.    kubernetes.io/cluster-service: "true"

  59.    addonmanager.kubernetes.io/mode: Reconcile

  60. spec:

  61.  serviceName: elasticsearch-logging

  62.  replicas: 2

  63.  selector:

  64.    matchLabels:

  65.      k8s-app: elasticsearch-logging

  66. Version: v6.2.5

  67.  template:

  68.    metadata:

  69.      labels:

  70.        k8s-app: elasticsearch-logging

  71. Version: v6.2.5

  72.        kubernetes.io/cluster-service: "true"

  73.    spec:

  74.      serviceAccountName: elasticsearch-logging

  75.      containers:

  76. - image: k8s. GCR. IO/elasticsearch: v6.2.5

  77.        name: elasticsearch-logging

  78.        resources:

  79.          # need more cpu upon initialization, therefore burstable class

  80.          limits:

  81.            cpu: 1000m

  82.          requests:

  83.            cpu: 100m

  84.        ports:

  85.        - containerPort: 9200

  86.          name: db

  87.          protocol: TCP

  88.        - containerPort: 9300

  89.          name: transport

  90.          protocol: TCP

  91.        volumeMounts:

  92.        - name: elasticsearch-logging

  93.          mountPath: /data

  94.        env:

  95.        - name: "NAMESPACE"

  96.          valueFrom:

  97.            fieldRef:

  98.              fieldPath: metadata.namespace

  99.      volumes:

  100.      - name: elasticsearch-logging

  101.        emptyDir: {}

  102.      # Elasticsearch requires vm.max_map_count to be at least 262144.

  103.      # If your OS already sets up this number to a higher value, feel free

  104.      # to remove this init container.

  105.      initContainers:

  106. - image: alpine: 3.6

  107.        command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]

  108.        name: elasticsearch-logging-init

  109.        securityContext:

  110.          privileged: true

Copy the code

To deploy Elasticsearch, run the following command:


     
  1. $ kubectl create -f {path}/es-statefulset.yaml

Copy the code

Port 9200 is exposed for Elasticsearch proxy service.


     
  1. apiVersion: v1

  2. kind: Service

  3. metadata:  

  4.  name: elasticsearch-logging  

  5.  namespace: kube-system  

  6.  labels:    

  7.    k8s-app: elasticsearch-logging    

  8.    kubernetes.io/cluster-service: "true"    

  9.    addonmanager.kubernetes.io/mode: Reconcile    

  10.    kubernetes.io/name: "Elasticsearch"

  11. spec:  

  12.  ports:  

  13.  - port: 9200    

  14.    protocol: TCP    

  15.    targetPort: db  

  16.  selector:    

  17.    k8s-app: elasticsearch-logging

Copy the code

Run the following command to deploy the Elasticsearch proxy service:


     
  1. $ kubectl create -f {path}/es-service.yaml

Copy the code

2.2 Installing and Deploying Fluentd

Fluentd is an open source data collector that can uniformly collect and consume data to better use and understand data. Fluentd structures the data as JSON to handle log data uniformly, including collection, filtering, caching, and output. Fluentd is a plug-in architecture, including input plug-ins, output plug-ins, filtering plug-ins, parsing plug-ins, formatting plug-ins, caching plug-ins, and storage plug-ins. You can expand and better use Fluentd through plug-ins.

The overall processing process of Fluentd is as follows: Obtain data through Input plug-in, filter, parse, format, and cache data through Engine, and Output data to a specific terminal through Output plug-in.

In this article, Fluentd acts as a logging-agent to collect logs and push the collected days to the backend Elasticsearch. For Kubernetes, DaemonSet ensures that all (or some) nodes will run a Pod copy. So Fluentd was deployed as DaemonSet, which will generate a POD on each node to read logs generated by kubelet, container runtime, and container and send them to Elasticsearch. To make Fluentd can work, each Node must mark beta. Kubernetes. IO/Fluentd – ds – ready = true.

Here is Fluentd’s ConfigMap configuration file, which defines the log data sources Fluentd gets and outputs those log data to Elasticsearch.


     
  1. kind: ConfigMap

  2. apiVersion: v1

  3. metadata:

  4. Name: fluentd - es - config - v0.1.4

  5.  namespace: kube-system

  6.  labels:

  7.    addonmanager.kubernetes.io/mode: Reconcile

  8. data:

  9.  system.conf: |-

  10.    <system>

  11.      root_dir /tmp/fluentd-buffers/

  12.    </system>

  13.  containers.input.conf: |-

  14.    <source>

  15.      @id fluentd-containers.log

  16.      @type tail

  17.      path /var/log/containers/*.log

  18.      pos_file /var/log/es-containers.log.pos

  19.      time_format %Y-%m-%dT%H:%M:%S.%NZ

  20.      tag raw.kubernetes.*

  21.      read_from_head true

  22.      <parse>

  23.        @type multi_format

  24.        <pattern>

  25.          format json

  26.          time_key time

  27.          time_format %Y-%m-%dT%H:%M:%S.%NZ

  28.        </pattern>

  29.        <pattern>

  30. format /^(? <time>.+) (? <stream>stdout|stderr) [^ ]* (? <log>.*)$/

  31.          time_format %Y-%m-%dT%H:%M:%S.%N%:z

  32.        </pattern>

  33.      </parse>

  34.    </source>

  35.    # Detect exceptions in the log output and forward them as one log entry.

  36.    <match raw.kubernetes.**>

  37.      @id raw.kubernetes

  38.      @type detect_exceptions

  39.      remove_tag_prefix raw

  40.      message log

  41.      stream stream

  42.      multiline_flush_interval 5

  43.      max_bytes 500000

  44.      max_lines 1000

  45.    </match>

  46.  output.conf: |-

  47.    # Enriches records with Kubernetes metadata

  48.    <filter kubernetes.**>

  49.      @type kubernetes_metadata

  50.    </filter>

  51.    <match **>

  52.      @id elasticsearch

  53.      @type elasticsearch

  54.      @log_level info

  55.      include_tag_key true

  56.      host elasticsearch-logging

  57.      port 9200

  58.      logstash_format true

  59.      <buffer>

  60.        @type file

  61.        path /var/log/fluentd-buffers/kubernetes.system.buffer

  62.        flush_mode interval

  63.        retry_type exponential_backoff

  64.        flush_thread_count 2

  65.        flush_interval 5s

  66.        retry_forever

  67.        retry_max_interval 30

  68.        chunk_limit_size 2M

  69.        queue_limit_length 8

  70.        overflow_action block

  71.      </buffer>

  72.    </match>

Copy the code

Run the following command to create Fluentd ConfigMap:


     
  1. $ kubectl create -f {path}/fluentd-es-configmap.yaml

Copy the code

The YAML configuration file for Fluentd itself is as follows:


     
  1. apiVersion: v1

  2. kind: ServiceAccount

  3. metadata:

  4.  name: fluentd-es

  5.  namespace: kube-system

  6.  labels:

  7.    k8s-app: fluentd-es

  8.    kubernetes.io/cluster-service: "true"

  9.    addonmanager.kubernetes.io/mode: Reconcile

  10. ---

  11. kind: ClusterRole

  12. apiVersion: rbac.authorization.k8s.io/v1

  13. metadata:

  14.  name: fluentd-es

  15.  labels:

  16.    k8s-app: fluentd-es

  17.    kubernetes.io/cluster-service: "true"

  18.    addonmanager.kubernetes.io/mode: Reconcile

  19. rules:

  20. - apiGroups:

  21.  - ""

  22.  resources:

  23.  - "namespaces"

  24.  - "pods"

  25.  verbs:

  26.  - "get"

  27.  - "watch"

  28.  - "list"

  29. ---

  30. kind: ClusterRoleBinding

  31. apiVersion: rbac.authorization.k8s.io/v1

  32. metadata:

  33.  name: fluentd-es

  34.  labels:

  35.    k8s-app: fluentd-es

  36.    kubernetes.io/cluster-service: "true"

  37.    addonmanager.kubernetes.io/mode: Reconcile

  38. subjects:

  39. - kind: ServiceAccount

  40.  name: fluentd-es

  41.  namespace: kube-system

  42.  apiGroup: ""

  43. roleRef:

  44.  kind: ClusterRole

  45.  name: fluentd-es

  46.  apiGroup: ""

  47. ---

  48. apiVersion: apps/v1

  49. kind: DaemonSet

  50. metadata:

  51. Name: fluentd - es - v2.2.0

  52.  namespace: kube-system

  53.  labels:

  54.    k8s-app: fluentd-es

  55. Version: v2.2.0

  56.    kubernetes.io/cluster-service: "true"

  57.    addonmanager.kubernetes.io/mode: Reconcile

  58. spec:

  59.  selector:

  60.    matchLabels:

  61.      k8s-app: fluentd-es

  62. Version: v2.2.0

  63.  template:

  64.    metadata:

  65.      labels:

  66.        k8s-app: fluentd-es

  67.        kubernetes.io/cluster-service: "true"

  68. Version: v2.2.0

  69.      # This annotation ensures that fluentd does not get evicted if the node

  70.      # supports critical pod annotation based priority scheme.

  71.      # Note that this does not guarantee admission on the nodes (#40573).

  72.      annotations:

  73.        scheduler.alpha.kubernetes.io/critical-pod: ''

  74.        seccomp.security.alpha.kubernetes.io/pod: 'docker/default'

  75.    spec:

  76.      priorityClassName: system-node-critical

  77.      serviceAccountName: fluentd-es

  78.      containers:

  79.      - name: fluentd-es

  80. Image: k8s. GCR. IO/fluentd - elasticsearch: v2.2.0

  81.        env:

  82.        - name: FLUENTD_ARGS

  83.          value: --no-supervisor -q

  84.        resources:

  85.          limits:

  86.            memory: 500Mi

  87.          requests:

  88.            cpu: 100m

  89.            memory: 200Mi

  90.        volumeMounts:

  91.        - name: varlog

  92.          mountPath: /var/log

  93.        - name: varlibdockercontainers

  94.          mountPath: /var/lib/docker/containers

  95.          readOnly: true

  96.        - name: config-volume

  97.          mountPath: /etc/fluent/config.d

  98.      nodeSelector:

  99.        beta.kubernetes.io/fluentd-ds-ready: "true"

  100.      terminationGracePeriodSeconds: 30

  101.      volumes:

  102.      - name: varlog

  103.        hostPath:

  104.          path: /var/log

  105.      - name: varlibdockercontainers

  106.        hostPath:

  107.          path: /var/lib/docker/containers

  108.      - name: config-volume

  109.        configMap:

  110. Name: fluentd - es - config - v0.1.4

Copy the code

Run the following command to deploy Fluentd:


     
  1. $ kubectl create -f {path}/fluentd-es-ds.yaml

Copy the code

2.3 Installing and Deploying Kibana

Kibana is an open source analytics and visualization platform designed to work with Elasticsearch. With Kibana, you can search, view, and interact with Elasticsearch data. You can analyze and visualize data using various charts, tables, and maps. The YAML deployed for Kibana is as follows. Specify the Elasticsearch service to obtain log data by using the environment variable ELASTICSEARCH_URL. http://elasticsearch-logging:9200, ElasticSearch. Cattle-logging is the name of elasticSearch’s proxy service in Kubernetes. In the Fluented configuration file, there are some key instructions:

  • The source directive determines the input source.

  • The match directive determines the output target.

  • The filter directive identifies the event processing pipeline.

  • The system directive sets system-wide configurations.

  • The label directive groups output and filters for internal routing

  • The @include directive contains other files.


     
  1. apiVersion: apps/v1

  2. kind: Deployment

  3. metadata:

  4.  name: kibana-logging

  5.  namespace: kube-system

  6.  labels:

  7.    k8s-app: kibana-logging

  8.    kubernetes.io/cluster-service: "true"

  9.    addonmanager.kubernetes.io/mode: Reconcile

  10. spec:

  11.  replicas: 1

  12.  selector:

  13.    matchLabels:

  14.      k8s-app: kibana-logging

  15.  template:

  16.    metadata:

  17.      labels:

  18.        k8s-app: kibana-logging

  19.      annotations:

  20.        seccomp.security.alpha.kubernetes.io/pod: 'docker/default'

  21.    spec:

  22.      containers:

  23.      - name: kibana-logging

  24. Image: docker. Elastic. Co/kibana/kibana - oss: 6.2.4

  25.        resources:

  26.          # need more cpu upon initialization, therefore burstable class

  27.          limits:

  28.            cpu: 1000m

  29.          requests:

  30.            cpu: 100m

  31.        env:

  32.          - name: ELASTICSEARCH_URL

  33.            value: http://elasticsearch-logging:9200

  34.        ports:

  35.        - containerPort: 5601

  36.          name: ui

  37.          protocol: TCP

Copy the code

Run the following command to deploy the Kibana proxy service:


     
  1. $ kubectl create -f {path}/kibana-deployment.yaml

Copy the code

The following proxy service YAML configuration file for Kibana. The proxy service type is NodePort.


     
  1. apiVersion: v1

  2. kind: Service

  3. metadata:

  4.  name: kibana-logging

  5.  namespace: kube-system

  6.  labels:

  7.    k8s-app: kibana-logging

  8.    kubernetes.io/cluster-service: "true"

  9.    addonmanager.kubernetes.io/mode: Reconcile

  10.    kubernetes.io/name: "Kibana"

  11. spec:

  12.  type: NodePort

  13.  ports:

  14.  - port: 5601

  15.    protocol: TCP

  16.    targetPort: ui

  17.  selector:

  18.    k8s-app: kibana-logging

Copy the code

Run the following command to deploy the Kibana proxy service:


     
  1. $ kubectl create -f {path}/kibana-service.yaml

Copy the code

3. Log data display

Run the following command to obtain the exposed ports of Kibana:


     
  1. $ kubectl get svc --namespace=kube-system

Copy the code

As you can see from the output, the exposed port of Kibana is 30471, so you can access Kibana outside the Kubernetes cluster by going to http://{NodeIP}:30471.

Click “Discover” to see the log information retrieved from the container in real time:

About the author: Ji Xiangyuan, product manager of Beijing Shenzhou Aerospace Software Technology Co., LTD. The copyright of this article belongs to the author.

The resources

1. Logging Architecture

https://kubernetes.io/docs/concepts/cluster-administration/logging/

Kubernetes Logging with Fluentd

https://docs.fluentd.org/v0.12/articles/kubernetes-fluentd

3. Quickstart Guide

https://docs.fluentd.org/v0.12/articles/quickstart

Fluentd-elasticsearch (fluentd-ElasticSearch)

https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch

5. Elasticsearch address:

https://www.elastic.co/products/elasticsearch

6. What is Fluentd? Address:

https://www.fluentd.org/architecture

7. Configuration File Syntax

https://docs.fluentd.org/v1.0/articles/config-file

8. A Practical Introduction to Elasticsearch

https://www.elastic.co/blog/a-practical-introduction-to-elasticsearch