ElasticsearchとFilebeatを使用したKubernetesのロギング　Part 2 　〜Filebeatの構成編〜

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614998/cc3cd262-696e-cae6-f530-8c7ff6d966d7.png)

#はじめに
この記事では、Elasticsearchバックエンドにログを送信するために、KubernetesクラスターでDaemonSetとして実行するようにFilebeatを構成する方法について学習します。 FluentDまたはFluentBitの代わりに今回は、Filebeatを使用しています。Filebeatは、非常に軽量なユーティリティであり、Kubernetesをファーストクラスでサポートしているためであり、実稼働レベルのセットアップに最適です。 このブログ投稿は、2部構成のシリーズのPart2です。 [最初の投稿](https://qiita.com/MetricFire/items/ecd42c83f43f6611bbf1)では、ノードのデプロイメントアーキテクチャを実行し、KibanaとES-HQをデプロイしました。

#デプロイメントアーキテクチャ
Filebeatは、KubernetesクラスターでDaemonSetとして実行されます。 

- Loggingと呼ばれる別の名前空間にデプロイされます。
- ポッドは、マスターノードとワーカーノードの両方でスケジュールされます。
- マスターノードポッドは、監査とクラスター管理の目的でapi-serverログを転送します。
- クライアントノードポッドは、アプリケーションの可観測性のためにワークロード関連のログを転送します。

###FilebeatServiceAccountとClusterRoleの作成
次のマニフェストをデプロイして、Filebeatポッドに必要な権限を作成します。

```
apiVersion: v1
kind: Namespace
metadata:
  name: logging
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: filebeat
  namespace: logging
  labels:
    k8s-app: filebeat
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: filebeat
  namespace: logging
  labels:
    k8s-app: filebeat
rules:
- apiGroups: [""] # "" indicates the core API group
  resources:
  - namespaces
  - pods
  verbs:
  - get
  - watch
  - list
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: filebeat
  namespace: logging
subjects:
- kind: ServiceAccount
  name: filebeat
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: filebeat
  apiGroup: rbac.authorization.k8s.io
```

ClusterRoleのアクセス許可は、セキュリティの観点から可能な限り制限されていることを確認する必要があります。 このサービスアカウントに関連付けられているポッドのいずれかが侵害された場合、攻撃者はクラスター全体またはその中で実行されているアプリケーションにアクセスできなくなります。

‍###FilebeatConfigMapの作成
次のマニフェストを使用して、Filebeatポッドで使用されるConfigMapを作成します。

```
apiVersion: v1
kind: Namespace
metadata:
  name: logging
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config
  namespace: logging
  labels:
    k8s-app: filebeat
    kubernetes.io/cluster-service: "true"
data:
  filebeat.yml: |-
    filebeat.config:
    #  inputs:
    #    path: ${path.config}/inputs.d/*.yml
    #    reload.enabled: true
      modules:
        path: ${path.config}/modules.d/*.yml
        reload.enabled: true

    filebeat.autodiscover:
      providers:
        - type: kubernetes
          hints.enabled: true
          include_annotations: ["artifact.spinnaker.io/name","ad.datadoghq.com/tags"]
          include_labels: ["app.kubernetes.io/name"]
          labels.dedot: true
          annotations.dedot: true
          templates:
            - condition:
                equals:
                  kubernetes.namespace: myapp   #Set the namespace in which your app is running, can add multiple conditions in case of more than 1 namespace.
              config:
                - type: docker
                  containers.ids:
                    - "${data.kubernetes.container.id}"
                  multiline:
                    pattern: '^[A-Za-z ]+[0-9]{2} (?:[01]\d|2[0123]):(?:[012345]\d):(?:[012345]\d)'.   #Timestamp regex for the app logs. Change it as per format. 
                    negate: true
                    match: after
            - condition:
                equals:
                  kubernetes.namespace: elasticsearch
              config:
                - type: docker
                  containers.ids:
                    - "${data.kubernetes.container.id}"
                  multiline:
                    pattern: '^\[[0-9]{4}-[0-9]{2}-[0-9]{2}|^[0-9]{4}-[0-9]{2}-[0-9]{2}T'
                    negate: true
                    match: after
                    
    processors:
      - add_cloud_metadata: ~
      - drop_fields:
          when:
            has_fields: ['kubernetes.labels.app']
          fields:
            - 'kubernetes.labels.app'

    output.elasticsearch:
      hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}']
```

Filebeat ConfigMapの重要な概念：

- **hint.enabled**：これにより、Kubernetes用のFilebeatのヒントモジュールがアクティブになります。これを使用することで、ポッドアノテーションを使用して、構成をFilebeatポッドに直接渡すことができます。さまざまなマルチラインパターンやその他のさまざまなタイプの設定を指定できます。これについての詳細は[ここ](https://www.elastic.co/guide/en/beats/filebeat/current/configuration-autodiscover-hints.html)で読むことができます。

- **include_annotations**：これをtrueに設定すると、Filebeatは特定のログエントリのポッド注釈を保持できます。これらのアノテーションは、後でKibanaコンソールでログをフィルタリングするために使用できます。

- **include_labels**：これをtrueに設定すると、Filebeatは特定のログエントリのポッドラベルを保持できます。これらのラベルは、後でKibanaコンソールでログをフィルタリングするために使用できます。

- 特定の名前空間のログをフィルタリングして、それに応じてログエントリを処理することもできます。ここでは、dockerログプロセッサが使用されています。名前空間ごとに異なる複数行パターンを使用することもできます。

- Elasticsearchをストレージバックエンドとして使用しているため、出力はElasticsearchに設定されます。または、Redis、Logstash、Kafka、さらにはファイルを指すこともあります。これについての詳細は[こちら](https://www.elastic.co/guide/en/beats/filebeat/current/configuring-output.html)で読むことができます。

- クラウドメタデータプロセッサは、ログエントリにいくつかのホスト固有のフィールドを含みます。これは、特定のワーカーノードに固有のログをフィルタリングしようとするときに役立ちます。

###FilebeatDaemonSetのデプロイメント
以下のマニフェストを使用して、FilebeatDaemonSetをデプロイします。

```
apiVersion: v1
kind: Namespace
metadata:
  name: logging
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: filebeat
  namespace: logging
  labels:
    k8s-app: filebeat
spec:
  template:
    metadata:
      labels:
        k8s-app: filebeat
    spec:
      serviceAccountName: filebeat
      terminationGracePeriodSeconds: 30
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
      containers:
      - name: filebeat
        image: elastic/filebeat:6.5.4
        args: [
          "-c", "/usr/share/filebeat/filebeat.yml",
          "-e",
        ]
        env:
        - name: ELASTICSEARCH_HOST
          value: elasticsearch.elasticsearch
        - name: ELASTICSEARCH_PORT
          value: "9200"
        securityContext:
          runAsUser: 0
          # If using Red Hat OpenShift uncomment this:
          #privileged: true
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 100Mi
        volumeMounts:
        - name: config
          mountPath: /usr/share/filebeat/filebeat.yml
          readOnly: true
          subPath: filebeat.yml
        - name: inputs
          mountPath: /usr/share/filebeat/inputs.d
          readOnly: true
        - name: data
          mountPath: /usr/share/filebeat/data
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      volumes:
      - name: config
        configMap:
          defaultMode: 0600
          name: filebeat-config
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: inputs
        configMap:
          defaultMode: 0600
          name: filebeat-inputs
      # data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart
      - name: data
        hostPath:
          path: /var/lib/filebeat-data
          type: DirectoryOrCreate
---
```

ここで何が起こっているか見てみましょう：

- 各ポッドのログは/ var / log / docker / containersに書き込まれます。 このディレクトリをホストからFilebeatポッドにマウントすると、Filebeatは提供された構成に従ってログを処理します。

- この記事の[Part1](https://qiita.com/MetricFire/items/ecd42c83f43f6611bbf1)で作成されたElasticsearchクライアントサービスを参照するために、env varELASTICSEARCH_HOSTをelasticsearch.elasticsearchに設定しました。 すでにElasticsearchクラスターを実行している場合は、envvarがそれを指すように設定する必要があります。

- マニフェストの次の設定に注意してください。

```
...
      tolerations:
      - effect: NoSchedule
        key: node-role.kubernetes.io/master
...
```

これにより、FilebeatDaemonSetがマスターノードでもポッドをスケジュールするようになります。 Filebeat DaemonSetがデプロイされたら、ポッドが適切にスケジュールされているかどうかを確認できます。

```
root$ kubectl -n logging get pods  -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP            NODE                                         NOMINATED NODE   READINESS GATES
filebeat-4kchs   1/1     Running   0          6d    100.96.8.2    ip-10-10-30-206.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
filebeat-6nrpc   1/1     Running   0          6d    100.96.7.6    ip-10-10-29-252.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
filebeat-7qs2s   1/1     Running   0          6d    100.96.1.6    ip-10-10-30-161.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
filebeat-j5xz6   1/1     Running   0          6d    100.96.5.3    ip-10-10-28-186.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
filebeat-pskg5   1/1     Running   0          6d    100.96.64.4   ip-10-10-29-142.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
filebeat-vjdtg   1/1     Running   0          6d    100.96.65.3   ip-10-10-30-118.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;
filebeat-wm24j   1/1     Running   0          6d    100.96.0.4    ip-10-10-28-162.us-east-2.compute.internal   &lt;none&gt;           &lt;none&gt;

root$ kubectl -get nodes -o wide
NAME                                         STATUS   ROLES    AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                       KERNEL-VERSION   CONTAINER-RUNTIME
ip-10-10-28-162.us-east-2.compute.internal   Ready    master   6d    v1.14.8   10.10.28.162   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
ip-10-10-28-186.us-east-2.compute.internal   Ready    node     6d    v1.14.8   10.10.28.186   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
ip-10-10-29-142.us-east-2.compute.internal   Ready    master   6d    v1.14.8   10.10.29.142   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
ip-10-10-29-252.us-east-2.compute.internal   Ready    node     6d    v1.14.8   10.10.29.252   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
ip-10-10-30-118.us-east-2.compute.internal   Ready    master   6d    v1.14.8   10.10.30.118   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
ip-10-10-30-161.us-east-2.compute.internal   Ready    node     6d    v1.14.8   10.10.30.161   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
ip-10-10-30-206.us-east-2.compute.internal   Ready    node     6d    v1.14.8   10.10.30.206   &lt;none&gt;        Debian GNU/Linux 9 (stretch)   4.9.0-9-amd64    docker://18.6.3
```

ポッドの1つについてログを追跡すると、ポッドがElasticsearchに接続され、ファイルのハーベスタが開始されていることがはっきりとわかります。 以下のスニペットはこれを示しています：

```
2019-11-19T06:22:03.435Z	INFO	log/input.go:138	Configured paths: [/var/lib/docker/containers/c2b29f5e06eb8affb2cce7cf2501f6f824a2fd83418d09823faf4e74a5a51eb7/*.log]
2019-11-19T06:22:03.435Z	INFO	input/input.go:114	Starting input of type: docker; ID: 4134444498769889169 
2019-11-19T06:22:04.786Z	INFO	input/input.go:149	input ticker stopped
2019-11-19T06:22:04.786Z	INFO	input/input.go:167	Stopping Input: 4134444498769889169
2019-11-19T06:22:19.295Z	INFO	[monitoring]	log/log.go:144	Non-zero metrics in the last 30s	{"monitoring": {"metrics": {"beat":{"cpu":{"system":{"ticks":641680,"time":{"ms":16}},"total":{"ticks":2471920,"time":{"ms":180},"value":2471920},"user":{"ticks":1830240,"time":{"ms":164}}},"handles":{"limit":{"hard":1048576,"soft":1048576},"open":20},"info":{"ephemeral_id":"007e8090-7c62-4b44-97fb-e74e8177dc54","uptime":{"ms":549390018}},"memstats":{"gc_next":47281968,"memory_alloc":29021760,"memory_total":156062982472}},"filebeat":{"events":{"added":111,"done":111},"harvester":{"closed":2,"open_files":15,"running":13}},"libbeat":{"config":{"module":{"running":0}},"output":{"events":{"acked":108,"batches":15,"total":108},"read":{"bytes":69},"write":{"bytes":123536}},"pipeline":{"clients":1847,"events":{"active":0,"filtered":3,"published":108,"total":111},"queue":{"acked":108}}},"registrar":{"states":{"current":87,"update":111},"writes":{"success":18,"total":18}},"system":{"load":{"1":0.98,"15":1.71,"5":1.59,"norm":{"1":0.0613,"15":0.1069,"5":0.0994}}}}}}

2019-11-19T06:22:49.295Z	INFO	[monitoring]	log/log.go:144	Non-zero metrics in the last 30s	{"monitoring": {"metrics": {"beat":{"cpu":{"system":{"ticks":641720,"time":{"ms":44}},"total":{"ticks":2472030,"time":{"ms":116},"value":2472030},"user":{"ticks":1830310,"time":{"ms":72}}},"handles":{"limit":{"hard":1048576,"soft":1048576},"open":20},"info":{"ephemeral_id":"007e8090-7c62-4b44-97fb-e74e8177dc54","uptime":{"ms":549420018}},"memstats":{"gc_next":47281968,"memory_alloc":38715472,"memory_total":156072676184}},"filebeat":{"events":{"active":12,"added":218,"done":206},"harvester":{"open_files":15,"running":13}},"libbeat":{"config":{"module":{"running":0}},"output":{"events":{"acked":206,"batches":24,"total":206},"read":{"bytes":102},"write":{"bytes":269666}},"pipeline":{"clients":1847,"events":{"active":12,"published":218,"total":218},"queue":{"acked":206}}},"registrar":{"states":{"current":87,"update":206},"writes":{"success":24,"total":24}},"system":{"load":{"1":1.22,"15":1.7,"5":1.58,"norm":{"1":0.0763,"15":0.1063,"5":0.0988}}}}}}

2019-11-19T06:23:19.295Z	INFO	[monitoring]	log/log.go:144	Non-zero metrics in the last 30s	{"monitoring": {"metrics": {"beat":{"cpu":{"system":{"ticks":641750,"time":{"ms":28}},"total":{"ticks":2472110,"time":{"ms":72},"value":2472110},"user":{"ticks":1830360,"time":{"ms":44}}},"handles":{"limit":{"hard":1048576,"soft":1048576},"open":20},"info":{"ephemeral_id":"007e8090-7c62-4b44-97fb-e74e8177dc54","uptime":{"ms":549450017}},"memstats":{"gc_next":47281968,"memory_alloc":43140256,"memory_total":156077100968}},"filebeat":{"events":{"active":-12,"added":43,"done":55},"harvester":{"open_files":15,"running":13}},"libbeat":{"config":{"module":{"running":0}},"output":{"events":{"acked":55,"batches":12,"total":55},"read":{"bytes":51},"write":{"bytes":70798}},"pipeline":{"clients":1847,"events":{"active":0,"published":43,"total":43},"queue":{"acked":55}}},"registrar":{"states":{"current":87,"update":55},"writes":{"success":12,"total":12}},"system":{"load":{"1":0.99,"15":1.67,"5":1.49,"norm":{"1":0.0619,"15":0.1044,"5":0.0931}}}}}}

2019-11-19T06:23:25.261Z	INFO	log/harvester.go:255	Harvester started for file: /var/lib/docker/containers/ccb7dc75ecc755734f6befc4965b9fdae74d59810914101eadf63daa69eb62e2/ccb7dc75ecc755734f6befc4965b9fdae74d59810914101eadf63daa69eb62e2-json.log

2019-11-19T06:23:49.295Z	INFO	[monitoring]	log/log.go:144	Non-zero metrics in the last 30s	{"monitoring": {"metrics": {"beat":{"cpu":{"system":{"ticks":641780,"time":{"ms":28}},"total":{"ticks":2472310,"time":{"ms":196},"value":2472310},"user":{"ticks":1830530,"time":{"ms":168}}},"handles":{"limit":{"hard":1048576,"soft":1048576},"open":21},"info":{"ephemeral_id":"007e8090-7c62-4b44-97fb-e74e8177dc54","uptime":{"ms":549480018}},"memstats":{"gc_next":47789200,"memory_alloc":31372376,"memory_total":156086697176,"rss":-1064960}},"filebeat":{"events":{"active":16,"added":170,"done":154},"harvester":{"open_files":16,"running":14,"started":1}},"libbeat":{"config":{"module":{"running":0}},"output":{"events":{"acked":153,"batches":24,"total":153},"read":{"bytes":115},"write":{"bytes":207569}},"pipeline":{"clients":1847,"events":{"active":16,"filtered":1,"published":169,"total":170},"queue":{"acked":153}}},"registrar":{"states":{"current":87,"update":154},"writes":{"success":25,"total":25}},"system":{"load":{"1":0.87,"15":1.63,"5":1.41,"norm":{"1":0.0544,"15":0.1019,"5":0.0881}}}}}}
```

すべてのポッドを実行したら、Kibanaでfilebeat- *タイプのインデックスパターンを作成できます。 Filebeatインデックスには、通常、タイムスタンプが付けられます。 インデックスパターンを作成するとすぐに、検索可能なすべてのフィールドが表示され、インポートする必要があります。 最後に、アプリケーションログを検索し、必要に応じてダッシュボードを作成できます。 ログ処理が非常に簡単になり、メッセージを簡単に解析できるため、アプリケーションでJSONロガーを使用することを強くお勧めします。

#まとめ
これで、ログの設定は完了です。 提供されているすべての構成ファイルは実稼働環境でテストされており、簡単に展開できます。 ご不明な点がございましたら、お気軽にお問い合わせください。 

Elasticsearchがログ監視スペースを支配している一方で、時系列データの監視には[MetricFire](https://www.metricfire.com/japan/?utm_source=blog&utm_medium=Qiita&utm_campaign=Japan&utm_content=Kubernetes%20Logging%20with%20Filebeat%20and%20Elasticsearch%20Part%202)が最適です。[デモを予約](https://www.metricfire.com/demo-japan/?utm_source=blog&utm_medium=Qiita&utm_campaign=Japan&utm_content=Kubernetes%20Logging%20with%20Filebeat%20and%20Elasticsearch%20Part%202)して、自分に適した監視ソリューションについて直接お問い合わせください。

この記事は、ゲストブロガーのVaibhavThakurによって書かれました。