EFK(Elastic Search, Fluentd, Kibana) のインストール

概要

EFKを用いてログの収集とUIを提供する。 必要な機能は、以下の通り。

  • コンテナのログの収集
  • 収集したログのUI

今回使うのは、EFK(Elastic Search, Fluentd, Kibana)とよばれるソフトウェアスタック。 FluentdでログをElasticSearchに送り、KibanaでElasticSearchのログを可視化する。 これらをKubernetesのリソースとして構築する。

準備

ロギングコンポーネントを配置するノードがk8s-monitoring-01k8s-monitoring-02なので、これらにlabelを付与する。

kubectl label node k8s-monitoring-01 role=monitoring
kubectl label node k8s-monitoring-02 role=monitoring

続いて、ロギング用ネームスペースの作成。

kubectl create ns kube-logging

Elastic Searchのインストール

Elastic SearchはStatefulSetとしてインストールする。 GithubにあるAddon公式のDockerを使ったデプロイを参考にすすめていく。

まず、StatefulSetに使用するPersistenetVolumeの作成を行う。

# elastic-pv.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-elastic0-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: 192.168.1.50
    path: /mnt/nfs/pv/elastic0
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-elastic1-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: 192.168.1.50
    path: /mnt/nfs/pv/elastic1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-elastic2-pv
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: 192.168.1.50
    path: /mnt/nfs/pv/elastic2

上記を読み込んでPVを作成。

kubectl apply -f elastic-pv.yml

作成が完了すると下記のようにPVがAvailableで表示される。

$ kubectl get pv
NAME              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
nfs-elastic0-pv   100Gi      RWO            Recycle          Available                                      1m
nfs-elastic1-pv   100Gi      RWO            Recycle          Available                                      1m
nfs-elastic2-pv   100Gi      RWO            Recycle          Available                                      1m

続いて、StatefulSetの設定ファイルを作成する。 大枠はaddonのものを使用し、イメージはElasticSearchの公式を用いる。

# elastic-statefulset.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch-logging
  namespace: kube-logging
  labels:
    k8s-app: elasticsearch-logging
    version: v6.6.0
    kubernetes.io/cluster-service: "true"
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      k8s-app: elasticsearch-logging
      version: v6.6.0
  template:
    metadata:
      labels:
        k8s-app: elasticsearch-logging
        version: v6.6.0
        kubernetes.io/cluster-service: "true"
    spec:
      nodeSelector:
        role: monitoring
      containers:
      - name: elasticsearch-logging
        image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.6.0
        resources:
            limits:
              cpu: 1000m
            requests:
              cpu: 100m
        ports:
        - containerPort: 9200
          name: db
          protocol: TCP
        - containerPort: 9300
          name: transport
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: elasticsearch-cluster
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.zen.ping.unicast.hosts
            value: "elasticsearch-logging-0.elasticsearch,elasticsearch-logging-1.elasticsearch,elasticsearch-logging-2.elasticsearch"
          - name: discovery.zen.minimum_master_nodes
            value: "2"
          - name: ES_JAVA_OPTS
            value: "-Xms1g -Xmx1g"
      initContainers:
      - image: alpine:3.6
        command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"]
        name: elasticsearch-logging-init-vm-map-count
        securityContext:
          privileged: true
      - image: alpine:3.6
        command: ["sh", "-c", "ulimit -n 65536"]
        name: elasticsearch-logging-init-ulimit
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 100Gi

変更点は以下の通り。

  • ServiceAccountに関する設定は今回必要ないので削除
  • デプロイ先をmonitoringノードのみにするためnodeSelectorを使用
  • イメージを公式のものに変更
  • データ用ディレクトリはPVを使うように変更
  • 必要な初期設定をenvとして追加している。メモリの割当などは環境に合わせて要件等

クラスタを構築する際にDNSを使っているので、この名前で解決できるようにHeadless Serviceを用いる。 詳しくは、公式を参照。 ざっくり、ClusterIPは割当せず、各PodへのアクセスへのDNSを設定するよとのこと。

# elastic-service.yml
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: kube-logging
  labels:
    k8s-app: elasticsearch-logging
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "Elasticsearch"
spec:
  selector:
    k8s-app: elasticsearch-logging
  clusterIP: None
  ports:
  - port: 9200
    name: db
  - port: 9300
    name: transport

実際に、上記の設定を読み込んでいく。 まず、Serviceの作成。

kubectl apply -f elastic-service.yml

作成されたサービスの確認。

$ kubectl get service
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP   None            <none>        9200/TCP,9300/TCP   11d

ClusterIPが割り当てられていないことがわかる。 続いて、Statefulsetのインストール。

kubectl apply -f elastic-statefulset.yml

デプロイされたPodの確認。

 $ kubectl get pod -o wide -l k8s-app=elasticsearch-logging
NAME                      READY     STATUS    RESTARTS   AGE       IP            NODE
elasticsearch-logging-0   1/1       Running   0          2m        10.244.5.14   k8s-monitoring-01
elasticsearch-logging-1   1/1       Running   0          1m        10.244.6.9    k8s-monitoring-02
elasticsearch-logging-2   1/1       Running   0          9s        10.244.5.15   k8s-monitoring-01

PVCの確認もしておく。

$ kubectl get pvc
NAME                           STATUS    VOLUME            CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-elasticsearch-logging-0   Bound     nfs-elastic2-pv   100Gi      RWO                           3m
data-elasticsearch-logging-1   Bound     nfs-elastic1-pv   100Gi      RWO                           2m
data-elasticsearch-logging-2   Bound     nfs-elastic0-pv   100Gi      RWO                           56s

以上で、ElasticSearchのインストールは終了なので、実際に動作しているかを確認する。

kubectl -n kube-logging port-forward elasticsearch-logging-0 9200:9200

他のターミナル画面でhttp://localhost:9200/_cluster/state?prettyにアクセスするとクラスタ内のノードがみれる。 以下は例。

$ curl 'http://localhost:9200/_cluster/state?pretty'
{
  "cluster_name" : "elasticsearch-cluster",
  "compressed_size_in_bytes" : 13960,
  "cluster_uuid" : "wsPB_NqrSrS7P2UvoriLKA",
  "version" : 123,
  "state_uuid" : "0xjuNhJYTESMEGPD8skLZA",
  "master_node" : "g7EyGIaDQKKNWhoa3-4gLA",
  "blocks" : { },
  "nodes" : {
    "JCDeUXEvSJ6dXbwmM3FkQg" : {
      "name" : "elasticsearch-logging-2",
      "ephemeral_id" : "n5HDGjdCQbmsrl0k1hu3cg",
      "transport_address" : "10.244.5.15:9300",
      "attributes" : { }
    },
    "gpYF9LvpR9yw_BqlBCKQ6Q" : {
      "name" : "elasticsearch-logging-1",
      "ephemeral_id" : "GkV5sG0CTcek7JWiRW1VOQ",
      "transport_address" : "10.244.6.9:9300",
      "attributes" : { }
    },
    "g7EyGIaDQKKNWhoa3-4gLA" : {
      "name" : "elasticsearch-logging-0",
      "ephemeral_id" : "ang23sJ5TDaxJoQv0TIscQ",
      "transport_address" : "10.244.5.14:9300",
      "attributes" : { }
    }
  },
  <snip>

Kibanaのインストール

ElasticSearchのインストールが完了したので、Kibanaをインストールする。 同様にAddonのファイルを参考にして作る。

今回インストールするのは、DeploymentとService。 Deploymentの設定ファイルから作っていく。

# kibana-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana-logging
  namespace: kube-logging
  labels:
    k8s-app: kibana-logging
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: kibana-logging
  template:
    metadata:
      labels:
        k8s-app: kibana-logging
    spec:
      nodeSelector:
        role: monitoring
      containers:
      - name: kibana-logging
        image: docker.elastic.co/kibana/kibana-oss:6.6.0
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        env:
          - name: ELASTICSEARCH_URL
            value: http://elasticsearch:9200
        ports:
        - containerPort: 5601
          name: ui
          protocol: TCP

変更点は以下の通り。

  • Addonの設定を削除
  • ノードセレクタでmonitoringノードを指定

続いて、Serviceの設定ファイルを作る。

# kibana-service.yml
apiVersion: v1
kind: Service
metadata:
  name: kibana-logging
  namespace: kube-logging
  labels:
    k8s-app: kibana-logging
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "Kibana"
spec:
  ports:
  - port: 5601
    protocol: TCP
    targetPort: ui
  selector:
    k8s-app: kibana-logging
  type: NodePort

変更点はtype。外部からアクセスできるようにNodePortに変更 上記をデプロイ。

kubectl apply -f ./kibana-deployment.yml
kubectl apply -f ./kibana-service.yml

デプロイしたら確認。Podから。

$ kubectl get pod -o wide
NAME                              READY     STATUS    RESTARTS   AGE       IP            NODE
kibana-logging-78d76f68c9-sdx4d   1/1       Running   0          1m        10.244.6.10   k8s-monitoring-02

サービスも確認。

$ kubectl get service
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
elasticsearch    ClusterIP   None            <none>        9200/TCP,9300/TCP   11d
kibana-logging   NodePort    10.98.101.139   <none>        5601:32339/TCP      5s

続いて、NodeのIPとNodeのPortを使ってアクセスする。上記の例だとポートは32339を使う。 実際にアクセスできると下記のようなKibanaのページが見れる。

Fluentdのインストール

公式を参考に作る。 ベージによると設定のファイルがGithubにあるので、ありがたく使わせていただく。 使用するのはRBACを含んだコンフィグファイル

ただし、いくつか変更を加える。

  • namespaceの名前
  • 認証の削除

上記変更を加えると下記の通り。

# fluentd.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd
  namespace: kube-logging

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd
roleRef:
  kind: ClusterRole
  name: fluentd
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd
  namespace: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-logging
  labels:
    k8s-app: fluentd-logging
    version: v1
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    matchLabels:
      k8s-app: fluentd-logging
  template:
    metadata:
      labels:
        k8s-app: fluentd-logging
        version: v1
        kubernetes.io/cluster-service: "true"
    spec:
      serviceAccount: fluentd
      serviceAccountName: fluentd
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:elasticsearch
        env:
          - name:  FLUENT_ELASTICSEARCH_HOST
            value: "elasticsearch"
          - name:  FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENT_UID
            value: "0"
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

デプロイする。

kubectl apply -f ./fluentd.yaml

作成されたPodの確認。

$ kubectl get pod -o wide
NAME                              READY     STATUS    RESTARTS   AGE       IP            NODE
elasticsearch-logging-0           1/1       Running   0          33m       10.244.5.14   k8s-monitoring-01
elasticsearch-logging-1           1/1       Running   0          32m       10.244.6.9    k8s-monitoring-02
elasticsearch-logging-2           1/1       Running   0          30m       10.244.5.15   k8s-monitoring-01
fluentd-7cl2p                     1/1       Running   0          30s       10.244.4.9    k8s-worker-02
fluentd-8kr77                     1/1       Running   0          30s       10.244.0.15   k8s-master-01
fluentd-bg5k4                     1/1       Running   0          30s       10.244.2.8    k8s-master-03
fluentd-htjnj                     1/1       Running   0          30s       10.244.8.10   k8s-lb-02
fluentd-rglz5                     1/1       Running   0          30s       10.244.7.16   k8s-lb-01
fluentd-rtl9s                     1/1       Running   0          30s       10.244.5.19   k8s-monitoring-01
fluentd-rxkbh                     1/1       Running   0          30s       10.244.6.13   k8s-monitoring-02
fluentd-szn79                     1/1       Running   0          30s       10.244.3.11   k8s-worker-01
fluentd-wwjkb                     1/1       Running   0          30s       10.244.1.6    k8s-master-02
kibana-logging-589cc9bbf7-kc9w9   1/1       Running   0          12m       10.244.5.16   k8s-monitoring-01

$ kubectl get daemonset
NAME      DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd   9         9         9         9            9           <none>          33s

実際にelasticsearchに接続されていると、Kibanaからログが見れるようになる。

Discoverをクリックして、index patternを作成する。

この画面でlogstash-*を入力して次へ。

次では、@timestampを時間のフィールドとして設定すると、次にまたDiscoverをクリックすると集められたログが以下のように見えるようになる。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください