├── .DS_Store ├── yaml ├── .DS_Store ├── fluentd-es-ds.yaml └── fluentd-es-configmap.yaml ├── Image ├── .DS_Store ├── fluent.conf ├── Gemfile ├── run.sh ├── clean-apt ├── clean-install └── Dockerfile └── README.MD /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengdidi/k8s-fluentd-elasticsearch/HEAD/.DS_Store -------------------------------------------------------------------------------- /yaml/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengdidi/k8s-fluentd-elasticsearch/HEAD/yaml/.DS_Store -------------------------------------------------------------------------------- /Image/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fengdidi/k8s-fluentd-elasticsearch/HEAD/Image/.DS_Store -------------------------------------------------------------------------------- /Image/fluent.conf: -------------------------------------------------------------------------------- 1 | # This is the root config file, which only includes components of the actual configuration 2 | 3 | # Do not collect fluentd's own logs to avoid infinite loops. 4 | 5 | @type null 6 | 7 | 8 | @include /etc/fluent/config.d/*.conf -------------------------------------------------------------------------------- /Image/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'fluentd', '<=1.1.0' 4 | gem 'activesupport', '~>5.1.4' 5 | gem 'fluent-plugin-kubernetes_metadata_filter', '~>1.0.0' 6 | gem 'fluent-plugin-elasticsearch', '~>2.4.1' 7 | gem 'fluent-plugin-systemd', '~>0.3.1' 8 | gem 'fluent-plugin-detect-exceptions', '~>0.0.9' 9 | gem 'fluent-plugin-prometheus', '~>0.3.0' 10 | gem 'fluent-plugin-multi-format-parser', '~>1.0.0' 11 | gem 'oj', '~>3.3.1.0' -------------------------------------------------------------------------------- /Image/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # These steps must be executed once the host /var and /lib volumes have 18 | # been mounted, and therefore cannot be done in the docker build stage. 19 | 20 | # For systems without journald 21 | mkdir -p /var/log/journal 22 | 23 | /usr/local/bin/fluentd $@ -------------------------------------------------------------------------------- /Image/clean-apt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # A script encapsulating a common Dockerimage pattern for installing packages 18 | # and then cleaning up the unnecessary install artifacts. 19 | # e.g. clean-install iptables ebtables conntrack 20 | 21 | set -o errexit 22 | 23 | apt-get clean -y 24 | rm -rf \ 25 | /var/cache/debconf/* \ 26 | /var/lib/apt/lists/* \ 27 | /var/log/* \ 28 | /tmp/* \ 29 | /var/tmp/* -------------------------------------------------------------------------------- /Image/clean-install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # A script encapsulating a common Dockerimage pattern for installing packages 18 | # and then cleaning up the unnecessary install artifacts. 19 | # e.g. clean-install iptables ebtables conntrack 20 | 21 | set -o errexit 22 | 23 | if [ $# = 0 ]; then 24 | echo >&2 "No packages specified" 25 | exit 1 26 | fi 27 | 28 | apt-get update 29 | apt-get install -y --no-install-recommends $@ 30 | clean-apt -------------------------------------------------------------------------------- /Image/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Kubernetes Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This Dockerfile will build an image that is configured 16 | # to run Fluentd with an Elasticsearch plug-in and the 17 | # provided configuration file. 18 | # The image acts as an executable for the binary /usr/sbin/td-agent. 19 | # Note that fluentd is run with root permssion to allow access to 20 | # log files with root only access under /var/log/containers/* 21 | 22 | FROM debian:stretch-slim 23 | 24 | ARG DEBIAN_FRONTEND=noninteractive 25 | 26 | COPY clean-apt /usr/bin 27 | COPY clean-install /usr/bin 28 | COPY Gemfile /Gemfile 29 | 30 | # 1. Install & configure dependencies. 31 | # 2. Install fluentd via ruby. 32 | # 3. Remove build dependencies. 33 | # 4. Cleanup leftover caches & files. 34 | RUN BUILD_DEPS="make gcc g++ libc6-dev ruby-dev libffi-dev" \ 35 | #authorize the scripts, modify by fengdidi@gmail.com 36 | && chmod +x /usr/bin/clean-install \ 37 | && chmod +x /usr/bin/clean-apt \ 38 | && clean-install $BUILD_DEPS \ 39 | ca-certificates \ 40 | libjemalloc1 \ 41 | ruby \ 42 | && echo 'gem: --no-document' >> /etc/gemrc \ 43 | && gem install --file Gemfile \ 44 | && apt-get purge -y --auto-remove \ 45 | -o APT::AutoRemove::RecommendsImportant=false \ 46 | $BUILD_DEPS \ 47 | && clean-apt \ 48 | # Ensure fluent has enough file descriptors 49 | && ulimit -n 65536 50 | 51 | # Copy the Fluentd configuration file for logging Docker container logs. 52 | COPY fluent.conf /etc/fluent/fluent.conf 53 | COPY run.sh /run.sh 54 | 55 | #authorize the scripts, modify by fengdidi@gmail.com 56 | RUN chmod +x /run.sh 57 | 58 | # Expose prometheus metrics. 59 | EXPOSE 80 60 | 61 | ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1 62 | 63 | # Start Fluentd to pick up our config that watches Docker container logs. 64 | CMD /run.sh $FLUENTD_ARGS -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ## K8s-fluentd-elasticsearch 2 | 3 | For Chinese documentation, please visit [Here](https://www.jianshu.com/p/92a4c11e77ba) 4 | 5 | This is a custom fluentd image with elasticsearch plugin based on Kubernetes's official implementation. 6 | 7 | Fluentd is deployed as a DaemonSet which spawns a pod on each node that reads logs, generated by kubelet, container runtime and containers and sends them to Elasticsearch. 8 | 9 | Note: in order for Fluentd to work, every Kubernetes node must be labeled with beta.kubernetes.io/fluentd-ds-ready=true, as otherwise the Fluentd DaemonSet will ignore them. 10 | 11 | Differences between my custom fluentd-elasticseach image and the official one: 12 | * The Elasticseach address and port are injected into the container by using environment variables. 13 | * Modify Dockerfile to solve some permission problems. 14 | * The offical implementation also deploys the elasticseach on the kubernetes cluster. But in my company, we deploy elasticsearch cluster outside of kubernetes. We think this is a better idea because Elasticseach is a database that is stateful. Since our company uses kubernetes as a micro serice platform, we don't want to deploy anything stateful on kubernetes cluster. 15 | 16 | logs are collected by this image: 17 | * containers log: /var/log/containers/*.log 18 | * docker log: /var/log/docker.log 19 | * kubelet log: /var/log/kubelet.log 20 | * kube-proxy log: /var/log/kube-proxy.log 21 | * kube-apiserver log: /var/log/kube-apiserver.log 22 | * kube-controller-manager log: /var/log/kube-controller-manager.log 23 | * kube-scheduler log: /var/log/kube-scheduler.log 24 | For more details, please check yaml/fluentd-es-configmap.yaml 25 | 26 | ## How to use 27 | Before you deploy this log collector on your Kubernetes node, you have to label your node by this command: 28 | ``` 29 | kubectl label nodes nodename beta.kubernetes.io/fluentd-ds-ready=true 30 | ``` 31 | Then, you need to build the image by yourself. Simply go into the Image directory, and run: 32 | ``` 33 | docker build -t dockerhub.fengdidi:5000/fengdidi/fluentd-elasticsearch:1801 . 34 | docker push dockerhub.fengdidi:5000/fengdidi/fluentd-elasticsearch:1801 35 | ``` 36 | dockerhub.fengdidi:5000/fengdidi/fluentd-elasticsearch:1801 is the image name and tag, use your own name and tag when you build by yourself. 37 | After you build the image, go to the yaml directory, create the config map on your kubernetes cluster. 38 | ``` 39 | kubectl create -f fluentd-es-configmap.yaml 40 | ``` 41 | Then modify fluentd-es-ds.yaml, change the image name to the one you built before. Assign the Elasticseach address and port. 42 | The Elasticseach address and port are injected into the container by using environment variables. After finishing your configuration, you can run the fluentd DaemonSet by using the command below: 43 | ``` 44 | kubectl create -f fluentd-es-ds.yaml 45 | ``` -------------------------------------------------------------------------------- /yaml/fluentd-es-ds.yaml: -------------------------------------------------------------------------------- 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: extensions/v1beta1 49 | kind: DaemonSet 50 | metadata: 51 | name: fluentd-es-v2.0.4 52 | namespace: kube-system 53 | labels: 54 | k8s-app: fluentd-es 55 | version: v2.0.4 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.0.4 63 | template: 64 | metadata: 65 | labels: 66 | k8s-app: fluentd-es 67 | kubernetes.io/cluster-service: "true" 68 | version: v2.0.4 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 | spec: 75 | priorityClassName: system-node-critical 76 | serviceAccountName: fluentd-es 77 | containers: 78 | - name: fluentd-es 79 | image: dockerhub.icbc:5000/icbc/fluentd-elasticsearch:1802 80 | env: 81 | - name: FLUENTD_ARGS 82 | value: --no-supervisor -q 83 | # elasticseach ip or host 84 | - name: ES_HOST 85 | value: localhost 86 | # elasticseach port 87 | - name: ES_PORT 88 | value: 9200 89 | resources: 90 | limits: 91 | memory: 500Mi 92 | requests: 93 | cpu: 100m 94 | memory: 200Mi 95 | volumeMounts: 96 | - name: varlog 97 | mountPath: /var/log 98 | - name: varlibdockercontainers 99 | mountPath: /var/lib/docker/containers 100 | readOnly: true 101 | - name: config-volume 102 | mountPath: /etc/fluent/config.d 103 | nodeSelector: 104 | beta.kubernetes.io/fluentd-ds-ready: "true" 105 | terminationGracePeriodSeconds: 30 106 | volumes: 107 | - name: varlog 108 | hostPath: 109 | path: /var/log 110 | - name: varlibdockercontainers 111 | hostPath: 112 | path: /var/lib/docker/containers 113 | - name: config-volume 114 | configMap: 115 | name: fluentd-es-config-v0.1.4 -------------------------------------------------------------------------------- /yaml/fluentd-es-configmap.yaml: -------------------------------------------------------------------------------- 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 | 11 | root_dir /tmp/fluentd-buffers/ 12 | 13 | containers.input.conf: |- 14 | # This configuration file for Fluentd / td-agent is used 15 | # to watch changes to Docker log files. The kubelet creates symlinks that 16 | # capture the pod name, namespace, container name & Docker container ID 17 | # to the docker logs for pods in the /var/log/containers directory on the host. 18 | # If running this fluentd configuration in a Docker container, the /var/log 19 | # directory should be mounted in the container. 20 | # 21 | # These logs are then submitted to Elasticsearch which assumes the 22 | # installation of the fluent-plugin-elasticsearch & the 23 | # fluent-plugin-kubernetes_metadata_filter plugins. 24 | # See https://github.com/uken/fluent-plugin-elasticsearch & 25 | # https://github.com/fabric8io/fluent-plugin-kubernetes_metadata_filter for 26 | # more information about the plugins. 27 | # 28 | # Example 29 | # ======= 30 | # A line in the Docker log file might look like this JSON: 31 | # 32 | # {"log":"2014/09/25 21:15:03 Got request with path wombat\n", 33 | # "stream":"stderr", 34 | # "time":"2014-09-25T21:15:03.499185026Z"} 35 | # 36 | # The time_format specification below makes sure we properly 37 | # parse the time format produced by Docker. This will be 38 | # submitted to Elasticsearch and should appear like: 39 | # $ curl 'http://elasticsearch-logging:9200/_search?pretty' 40 | # ... 41 | # { 42 | # "_index" : "logstash-2014.09.25", 43 | # "_type" : "fluentd", 44 | # "_id" : "VBrbor2QTuGpsQyTCdfzqA", 45 | # "_score" : 1.0, 46 | # "_source":{"log":"2014/09/25 22:45:50 Got request with path wombat\n", 47 | # "stream":"stderr","tag":"docker.container.all", 48 | # "@timestamp":"2014-09-25T22:45:50+00:00"} 49 | # }, 50 | # ... 51 | # 52 | # The Kubernetes fluentd plugin is used to write the Kubernetes metadata to the log 53 | # record & add labels to the log record if properly configured. This enables users 54 | # to filter & search logs on any metadata. 55 | # For example a Docker container's logs might be in the directory: 56 | # 57 | # /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b 58 | # 59 | # and in the file: 60 | # 61 | # 997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log 62 | # 63 | # where 997599971ee6... is the Docker ID of the running container. 64 | # The Kubernetes kubelet makes a symbolic link to this file on the host machine 65 | # in the /var/log/containers directory which includes the pod name and the Kubernetes 66 | # container name: 67 | # 68 | # synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log 69 | # -> 70 | # /var/lib/docker/containers/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b/997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b-json.log 71 | # 72 | # The /var/log directory on the host is mapped to the /var/log directory in the container 73 | # running this instance of Fluentd and we end up collecting the file: 74 | # 75 | # /var/log/containers/synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log 76 | # 77 | # This results in the tag: 78 | # 79 | # var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log 80 | # 81 | # The Kubernetes fluentd plugin is used to extract the namespace, pod name & container name 82 | # which are added to the log message as a kubernetes field object & the Docker container ID 83 | # is also added under the docker field object. 84 | # The final tag is: 85 | # 86 | # kubernetes.var.log.containers.synthetic-logger-0.25lps-pod_default_synth-lgr-997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b.log 87 | # 88 | # And the final log record look like: 89 | # 90 | # { 91 | # "log":"2014/09/25 21:15:03 Got request with path wombat\n", 92 | # "stream":"stderr", 93 | # "time":"2014-09-25T21:15:03.499185026Z", 94 | # "kubernetes": { 95 | # "namespace": "default", 96 | # "pod_name": "synthetic-logger-0.25lps-pod", 97 | # "container_name": "synth-lgr" 98 | # }, 99 | # "docker": { 100 | # "container_id": "997599971ee6366d4a5920d25b79286ad45ff37a74494f262e3bc98d909d0a7b" 101 | # } 102 | # } 103 | # 104 | # This makes it easier for users to search for logs by pod name or by 105 | # the name of the Kubernetes container regardless of how many times the 106 | # Kubernetes pod has been restarted (resulting in a several Docker container IDs). 107 | # Json Log Example: 108 | # {"log":"[info:2016-02-16T16:04:05.930-08:00] Some log text here\n","stream":"stdout","time":"2016-02-17T00:04:05.931087621Z"} 109 | # CRI Log Example: 110 | # 2016-02-17T00:04:05.931087621Z stdout F [info:2016-02-16T16:04:05.930-08:00] Some log text here 111 | 112 | @id fluentd-containers.log 113 | @type tail 114 | path /var/log/containers/*.log 115 | pos_file /var/log/es-containers.log.pos 116 | time_format %Y-%m-%dT%H:%M:%S.%NZ 117 | tag raw.kubernetes.* 118 | read_from_head true 119 | 120 | @type multi_format 121 | 122 | format json 123 | time_key time 124 | time_format %Y-%m-%dT%H:%M:%S.%NZ 125 | 126 | 127 | format /^(? 130 | 131 | 132 | # Detect exceptions in the log output and forward them as one log entry. 133 | 134 | @id raw.kubernetes 135 | @type detect_exceptions 136 | remove_tag_prefix raw 137 | message log 138 | stream stream 139 | multiline_flush_interval 5 140 | max_bytes 500000 141 | max_lines 1000 142 | 143 | system.input.conf: |- 144 | # Example: 145 | # 2015-12-21 23:17:22,066 [salt.state ][INFO ] Completed state [net.ipv4.ip_forward] at time 23:17:22.066081 146 | 147 | @id minion 148 | @type tail 149 | format /^(?