{"id":291,"date":"2018-11-26T18:47:29","date_gmt":"2018-11-26T15:47:29","guid":{"rendered":"https:\/\/artem.services\/?p=291"},"modified":"2019-02-26T18:18:50","modified_gmt":"2019-02-26T15:18:50","slug":"fluentd-kubernetes-graylog-gelf","status":"publish","type":"post","link":"https:\/\/artem.services\/?p=291","title":{"rendered":"Fluentd &#8212; Kubernetes (Graylog, GELF)"},"content":{"rendered":"<p><img loading=\"lazy\" class=\"size-full wp-image-314 aligncenter\" src=\"https:\/\/artem.services\/wp-content\/uploads\/2018\/11\/Fluentd-Logo.png\" alt=\"\" width=\"982\" height=\"340\" srcset=\"https:\/\/artem.services\/wp-content\/uploads\/2018\/11\/Fluentd-Logo.png 982w, https:\/\/artem.services\/wp-content\/uploads\/2018\/11\/Fluentd-Logo-300x104.png 300w, https:\/\/artem.services\/wp-content\/uploads\/2018\/11\/Fluentd-Logo-768x266.png 768w, https:\/\/artem.services\/wp-content\/uploads\/2018\/11\/Fluentd-Logo-954x330.png 954w\" sizes=\"(max-width: 982px) 100vw, 982px\" \/><\/p>\n<p>\u0417\u0430 \u043e\u0441\u043d\u043e\u0432\u0443 \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <strong>Docker<\/strong> \u043e\u0431\u0440\u0430\u0437 <strong>fluntd<\/strong><\/p>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 <strong>gem<\/strong>&#39;\u044b \u0434\u043b\u044f <strong>GELF<\/strong> \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 \u0438 <strong>Kubernetes<\/strong>&#39;\u0430, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0443\u043a\u0430\u0436\u0435\u043c, \u0447\u0442\u043e <strong>fluentd<\/strong> \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f <strong>root<\/strong>, \u0438\u043d\u0430\u0447\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430.<\/p>\n<h3>Dockerfile:<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nFROM fluent\/fluentd:latest\r\n\r\nUSER root\r\nWORKDIR \/home\/fluent\r\n\r\nRUN set -ex \\\r\n    &amp;&amp; apk add --no-cache --virtual .build-deps \\\r\n        build-base \\\r\n        ruby-dev \\\r\n    &amp;&amp; echo 'gem: --no-document' &gt;&gt; \/etc\/gemrc \\\r\n    &amp;&amp; gem install fluent-plugin-secure-forward \\\r\n    &amp;&amp; gem install fluent-plugin-record-reformer \\\r\n    &amp;&amp; gem install fluent-plugin-gelf-hs \\\r\n    &amp;&amp; gem install fluent-plugin-kubernetes_metadata_filter \\\r\n    &amp;&amp; gem install fluent-plugin-rewrite-tag-filter \\\r\n    &amp;&amp; apk del .build-deps \\\r\n    &amp;&amp; rm -rf \/tmp\/* \/var\/tmp\/* \/usr\/lib\/ruby\/gems\/*\/cache\/*.gem\r\n\r\n# Copy plugins\r\nCOPY plugins \/fluentd\/plugins\/\r\n\r\n# Environment variables\r\nENV FLUENTD_OPT=&quot;&quot;\r\nENV FLUENTD_CONF=&quot;fluent.conf&quot;\r\n\r\n# For Kubernetes. Start Fluentd by user root\r\nENV FLUENT_UID=&quot;0&quot;\r\n\r\n# Run Fluentd\r\nCMD fluentd -c \/fluentd\/etc\/$FLUENTD_CONF -p \/fluentd\/plugins $FLUENTD_OPT\r\n<\/pre>\n<h3><\/h3>\n<p><!--more--><\/p>\n<h3>plugins\/parser_kubernetes.rb:<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n#\r\n# Fluentd\r\n#\r\n#    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\r\n#    you may not use this file except in compliance with the License.\r\n#    You may obtain a copy of the License at\r\n#\r\n#        http:\/\/www.apache.org\/licenses\/LICENSE-2.0\r\n#\r\n#    Unless required by applicable law or agreed to in writing, software\r\n#    distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\r\n#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n#    See the License for the specific language governing permissions and\r\n#    limitations under the License.\r\n#\r\n\r\n# The following Fluentd parser plugin, aims to simplify the parsing of multiline\r\n# logs found in Kubernetes nodes. Since many log files shared the same format and\r\n# in order to simplify the configuration, this plugin provides a 'kubernetes' format\r\n# parser (built on top of MultilineParser).\r\n#\r\n# When tailing files, this 'kubernetes' format should be applied to the following\r\n# log file sources:\r\n#\r\n#  - \/var\/log\/kubelet.log\r\n#  - \/var\/log\/kube-proxy.log\r\n#  - \/var\/log\/kube-apiserver.log\r\n#  - \/var\/log\/kube-controller-manager.log\r\n#  - \/var\/log\/kube-scheduler.log\r\n#  - \/var\/log\/rescheduler.log\r\n#  - \/var\/log\/glbc.log\r\n#  - \/var\/log\/cluster-autoscaler.log\r\n#\r\n# Usage:\r\n#\r\n# ---- fluentd.conf ----\r\n#\r\n# &lt;source&gt;\r\n#    type tail\r\n#    format kubernetes\r\n#    path .\/kubelet.log\r\n#    read_from_head yes\r\n#    tag kubelet\r\n# &lt;\/source&gt;\r\n#\r\n# ----   EOF       ---\r\n\r\nrequire 'fluent\/parser'\r\n\r\nmodule Fluent\r\n  class KubernetesParser &lt; Fluent::TextParser::MultilineParser\r\n    Fluent::Plugin.register_parser(&quot;kubernetes&quot;, self)\r\n\r\n    CONF_FORMAT_FIRSTLINE = %q{\/^\\w\\d{4}\/}\r\n    CONF_FORMAT1 = %q{\/^(?&lt;severity&gt;\\w)(?&lt;time&gt;\\d{4} [^\\s]*)\\s+(?&lt;pid&gt;\\d+)\\s+(?&lt;source&gt;[^ \\]]+)\\] (?&lt;message&gt;.*)\/}\r\n    CONF_TIME_FORMAT = &quot;%m%d %H:%M:%S.%N&quot;\r\n\r\n    def configure(conf)\r\n      conf['format_firstline'] = CONF_FORMAT_FIRSTLINE\r\n      conf['format1'] = CONF_FORMAT1\r\n      conf['time_format'] = CONF_TIME_FORMAT\r\n      super\r\n    end\r\n  end\r\nend\r\n<\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u0431\u043e\u0440\u043a\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0437\u0430, \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 <strong>Kubernetes<\/strong>&#39;\u0430<\/p>\n<h3>rbac.yaml:<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napiVersion: v1\r\nkind: ServiceAccount\r\nmetadata:\r\n  name: kube-gelf\r\n  namespace: kube-system\r\n---\r\napiVersion: rbac.authorization.k8s.io\/v1beta1\r\nkind: ClusterRole\r\nmetadata:\r\n  annotations:\r\n    rbac.authorization.kubernetes.io\/autoupdate: &quot;true&quot;\r\n  name: kube-gelf-metadata-filter\r\nrules:\r\n- apiGroups:\r\n  - &quot;&quot;\r\n  resources:\r\n  - pods\r\n  - pods\/exec\r\n  - namespaces\r\n  verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;, &quot;create&quot;]\r\n---\r\napiVersion: rbac.authorization.k8s.io\/v1beta1\r\nkind: ClusterRoleBinding\r\nmetadata:\r\n  name: kube-gelf\r\nroleRef:\r\n  apiGroup: rbac.authorization.k8s.io\r\n  kind: ClusterRole\r\n  name: kube-gelf-metadata-filter\r\nsubjects:\r\n- kind: ServiceAccount\r\n  name: kube-gelf\r\n  namespace: kube-system\r\n<\/pre>\n<h3>configmap.yml:<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napiVersion: v1\r\nkind: ConfigMap\r\nmetadata:\r\n  name: kube-gelf\r\n  namespace: kube-system\r\n  labels:\r\n    app: kube-gelf\r\ndata:\r\n  GELF_HOST: &quot;graylog-svc.staging.svc.cluster.local&quot;\r\n  GELF_PORT: &quot;12201&quot;\r\n  GELF_PROTOCOL: &quot;udp&quot;\r\n  fluent.conf: |\r\n    &lt;match fluent.**&gt;\r\n      @type null\r\n    &lt;\/match&gt;\r\n\r\n    &lt;source&gt;\r\n      @type tail\r\n      path \/var\/log\/containers\/*.log\r\n      pos_file \/pos\/containers.pos\r\n      time_key time\r\n      time_format %Y-%m-%dT%H:%M:%S.%NZ\r\n      tag kubernetes.*\r\n      format json\r\n      read_from_head true\r\n    &lt;\/source&gt;\r\n\r\n    &lt;filter kubernetes.**&gt;\r\n      @type kubernetes_metadata\r\n      ca_file \/var\/run\/secrets\/kubernetes.io\/serviceaccount\/ca.crt\r\n      bearer_token_file \/var\/run\/secrets\/kubernetes.io\/serviceaccount\/token\r\n    &lt;\/filter&gt;\r\n\r\n    &lt;filter access&gt;\r\n      @type record_transformer\r\n      &lt;record&gt;\r\n        hostname &quot;#{ENV['NODENAME']}&quot;\r\n      &lt;\/record&gt;\r\n    &lt;\/filter&gt;\r\n\r\n    ### NOT COLLECT LOGS FROM ALL EXCEPT NAMESPACE STAGING ###\r\n\r\n    &lt;match **fluentd**&gt;\r\n      @type null\r\n    &lt;\/match&gt;\r\n\r\n    &lt;match **kube-system**&gt;\r\n      @type null\r\n    &lt;\/match&gt;\r\n\r\n    &lt;match **monitoring**&gt;\r\n      @type null\r\n    &lt;\/match&gt;\r\n\r\n    &lt;match **cert-manager**&gt;\r\n      @type null\r\n    &lt;\/match&gt;\r\n\r\n    &lt;match **efs-staging**&gt;\r\n      @type null\r\n    &lt;\/match&gt;\r\n\r\n    ##########################################################\r\n\r\n    &lt;match kubernetes.**&gt;\r\n      @type rewrite_tag_filter\r\n      &lt;rule&gt;\r\n        key $['kubernetes']['labels']['app']\r\n        pattern ^(.+)$\r\n        tag $1\r\n      &lt;\/rule&gt;\r\n    &lt;\/match&gt;\r\n\r\n    &lt;match **&gt;\r\n       @type copy\r\n       &lt;store&gt;\r\n         @type gelf\r\n         include_tag_key true\r\n         host &quot;#{ENV['GELF_HOST']}&quot;\r\n         port &quot;#{ENV['GELF_PORT']}&quot;\r\n         protocol &quot;#{ENV['GELF_PROTOCOL']}&quot;\r\n         flush_interval 10s\r\n         num_threads 2\r\n         use_record_host true\r\n         buffer_chunk_limit 4096K\r\n         buffer_queue_limit 512\r\n         max_retry_wait 300\r\n       &lt;\/store&gt;\r\n    &lt;\/match&gt;\r\n\r\n    # Valid log_level's are: fatal, error, warn, info, debug, trace\r\n    &lt;system&gt;\r\n      log_level warn\r\n    &lt;\/system&gt;\r\n<\/pre>\n<p>\u0412 \u0444\u0430\u0439\u043b\u0435 &quot;<strong>fluent.conf<\/strong>&quot; \u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u043b \u0432\u0441\u0435, \u043a\u0440\u043e\u043c\u0435 <strong>namespace staging<\/strong>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0443 \u043c\u0435\u043d\u044f \u043a\u0440\u0443\u0442\u044f\u0442\u0441\u044f \u0432\u0441\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b.<\/p>\n<h3>manifest.yml:<\/h3>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\napiVersion: apps\/v1\r\nkind: DaemonSet\r\nmetadata:\r\n  name: kube-gelf\r\n  namespace: kube-system\r\nspec:\r\n  updateStrategy:\r\n    type: RollingUpdate\r\n    rollingUpdate:\r\n      maxUnavailable: 1\r\n  selector:\r\n    matchLabels:\r\n      app: kube-gelf\r\n  template:\r\n    metadata:\r\n      labels:\r\n        app: kube-gelf\r\n    spec:\r\n      serviceAccountName: kube-gelf\r\n      dnsPolicy: ClusterFirst\r\n      containers:\r\n      - name: agent\r\n        image: my-image-repo:kube-gelf\r\n        imagePullPolicy: &quot;IfNotPresent&quot;\r\n        env:\r\n        - name: GELF_HOST\r\n          valueFrom:\r\n            configMapKeyRef:\r\n              name: kube-gelf\r\n              key: GELF_HOST\r\n        - name: GELF_PORT\r\n          valueFrom:\r\n            configMapKeyRef:\r\n              name: kube-gelf\r\n              key: GELF_PORT\r\n        - name: GELF_PROTOCOL\r\n          valueFrom:\r\n            configMapKeyRef:\r\n              name: kube-gelf\r\n              key: GELF_PROTOCOL\r\n        - name: NODENAME\r\n          valueFrom:\r\n            fieldRef:\r\n              fieldPath: spec.nodeName\r\n        volumeMounts:\r\n        - name: varlog\r\n          mountPath: \/var\/log\r\n          readOnly: true\r\n        - name: gelf-pos\r\n          mountPath: \/pos\r\n          readOnly: false\r\n        - name: varlibdockercontainers\r\n          mountPath: \/var\/lib\/docker\/containers\r\n          readOnly: true\r\n        - name: kube-gelf-config\r\n          mountPath: \/fluentd\/etc\/fluent.conf\r\n          subPath: fluent.conf\r\n      tolerations:\r\n      - key: node-role.kubernetes.io\/master\r\n        operator: Exists\r\n        effect: NoSchedule\r\n      volumes:\r\n      - name: varlog\r\n        hostPath:\r\n          path: \/var\/log\r\n      - name: varlibdockercontainers\r\n        hostPath:\r\n          path: \/var\/lib\/docker\/containers\r\n      - name: kube-gelf-config\r\n        configMap:\r\n          name: kube-gelf\r\n          items:\r\n          - key: fluent.conf\r\n            path: fluent.conf\r\n      - name: gelf-pos\r\n        hostPath:\r\n          path: \/var\/log\/pos\r\n<\/pre>\n<p>\u041d\u0435 \u0437\u0430\u0431\u044b\u0432\u0430\u0435\u043c \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0443\u0442\u044c \u0441 \u0441\u043e\u0431\u0440\u0430\u043d\u043e\u043c\u0443 \u043e\u0431\u0440\u0430\u0437\u0443.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0417\u0430 \u043e\u0441\u043d\u043e\u0432\u0443 \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 Docker \u043e\u0431\u0440\u0430\u0437 fluntd \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 gem&#39;\u044b \u0434\u043b\u044f GELF \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430 \u0438 Kubernetes&#39;\u0430, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0443\u043a\u0430\u0436\u0435\u043c, \u0447\u0442\u043e fluentd \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f root, \u0438\u043d\u0430\u0447\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0434\u043e\u0441\u0442\u0443\u043f\u0430. Dockerfile:<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2,20],"tags":[67,68,64,65,18,17],"_links":{"self":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/291"}],"collection":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=291"}],"version-history":[{"count":6,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/291\/revisions"}],"predecessor-version":[{"id":803,"href":"https:\/\/artem.services\/index.php?rest_route=\/wp\/v2\/posts\/291\/revisions\/803"}],"wp:attachment":[{"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=291"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=291"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/artem.services\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=291"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}