KubernetesでElasticsearch5 + fluentd + Kibana5 のスタックを作る

  • 5
    Like
  • 0
    Comment

はじめに

Kubernetes初心者です。久しぶりにDockerを触ることになったので、組んでみました。

イメージの取得

以下のイメージを取得する。

application image
Elasticsearch docker.io/elasticsearch
Fluentd (td-agent) gcr.io/google_containers/fluentd-elasticsearch:1.20
Kibana docker.io/kibana

Elasticsearch, Kibanaはそれぞれ最新のものをpullした。Fluentdは1.20。(このバージョンで動作が確認できたため)

Elasticsearchの設定

ServiceとReplocation Controller (controller) を定義する。

Serviceの定義

serviceはL3ロードバランサーみたいなもので、podに対してアクセスを振り分ける機能を持つ。

es-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch-logging
  namespace: NAMESPACE
  labels:
    k8s-app: elasticsearch-logging
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "Elasticsearch"
spec:
  ports:
  - port: 9200
    protocol: TCP
    targetPort: db
  selector:
    k8s-app: elasticsearch-logging
  externalIPs:
  - 192.168.128.100

namespaceは適宜書き換えてください。
ちなみに、外部にサービスを公開したくないときは、externalIPsの箇所を削除してください。

replication controller の定義

Replication Controller は Pod 数を維持するための機能。(Podについては後述)

Replication Controller はテンプレートにより定義された Pod を、指定された数の分だけ作成する。そして対象の Pod 数を監視し、もしホストの障害等で Pod の数が減れば新たに作成し、逆に増えすぎた場合は削除することにより Pod を一定数に保つ。
Pod が1つしかないようなアプリケーションでも、Replication Controller を使うことが推奨されている。

マニフェストファイルは次の通り。

es-controller.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: elasticsearch-logging-v1
  namespace: NAMESPACE
  labels:
    k8s-app: elasticsearch-logging
    version: v1
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    k8s-app: elasticsearch-logging
    version: v1
  template:
    metadata:
      labels:
        k8s-app: elasticsearch-logging
        version: v1
        kubernetes.io/cluster-service: "true"
    spec:
      containers:
      - image: docker.io/elasticsearch
        name: elasticsearch-logging
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 100m
        ports:
        - containerPort: 9200
          name: db
          protocol: TCP
        - containerPort: 9300
          name: transport
          protocol: TCP
        volumeMounts:
        - name: es-persistent-storage
          mountPath: /data
      volumes:
      - name: es-persistent-storage
        emptyDir: {}

Elasticsearchを立ち上げる

kubectl create -f es-controller.yaml -f es-service.yaml

で立ち上がる。

削除するときは

kubectl create -f es-controller.yaml -f es-service.yaml
  • -f の後ろにマニフェストファイルを指定してあげるらしい。

Elasticsearchでエラーが出たとき

ElasticsearchをDocker上で動かしたとき、下記のエラーが出ることがある。

2016-12-07T07:10:30.448555000Z ERROR: bootstrap checks failed
2016-12-07T07:10:30.448717000Z max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

この場合、警告の通りにvm.max_map_countのパラメータを変更する必要がある。

vm.max_map_countは1つのプロセスで使えるメモリマップの数の上限らしい。

変更の手順は下記の通り。

  1. /etc/sysctl.confvm.max_map_count = 262144 を追加
  2. sysctl -p で反映
  3. sysctl -a | grep vm.max_map_countvm.max_map_count = 262144 になっていることを確認。

fluentdの設定

podとserviceを定義している。まとめているときに思ったが、replication controllerも定義してあげたほうがいいのだろうか。

また、複数のサーバーでクラスタを組んでKubernetesを運用している場合、fluentdを用いてKubernetesで管理するコンテナごとのログを集約したいという要求が見受けられる。

こういうときは、ノードごとにfluentdのコンテナを立ててあげる必要がある。(ノードごとのfluetndコンテナがElasticsearchにコンテナのログを送信するため)

Dockerfileの定義

コンテナ内部で自前で設定したtd-agent.conf(fluentdの設定ファイル)を動かしたい場合は、fluentdのコンテナのDockerfileを定義する。(不要ならば設定しなくてもよい)

gcr.io/google_containers/fluentd-elasticsearch:1.20 のイメージを使っている理由は、こいつに既に fluent-plugin-elasitcsearch が入っているため。

FROM gcr.io/google_containers/fluentd-elasticsearch:1.20

# Ensure there are enough file descriptors for running Fluentd.
RUN ulimit -n 65536

# Disable prompts from apt.
ENV DEBIAN_FRONTEND noninteractive

# Copying fluentd configuration file.
COPY td-agent.conf /etc/td-agent/td-agent.conf

# RUN /tmp/build.sh
ENV LD_PRELOAD /opt/td-agent/embedded/lib/libjemalloc.so

# Run the Fluentd service.
ENTRYPOINT ["td-agent"]

先程立てたElasticsearchにログを送りたい場合は、下記リンクの設定が参考になると思います。

Podの定義

Podとは何か、ということについてはKubernetes の学習 (2) ~ Pod の作成にうまく説明されている。

Podとは、いくつかのコンテナをグループ化したもの。Kubernetes はコンテナ単位では操作せず、 Pod 単位で作成、開始、停止、削除といった操作を行う。 そのため、1つのコンテナを作成したいときも、「コンテナが1つ含まれるPod」を作成することになる。

他に以下の特徴がある。

  • Pod 内のコンテナは、同一ホスト上に配備される
  • Pod 内のコンテナは、仮想NICやプロセステーブルを共有する

つまり、同じIPを使えたり、互いのプロセスが見えたりする。

fluent-es-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: fluentd-elasticsearch
  namespace: NAMESPACE
  labels:
    k8s-app: fluentd-logging
spec:
  containers:
  - name: fluentd-elasticsearch
    image: 192.168.128.100:5000/kohei-fluentd-elasticsearch:latest
    ports:
    - containerPort: 24224
    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

serviceの定義

serviceが何かについては前述したので、そこを参照。

fluent-es-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: fluentd-logging
  namespace: NAMESPACE
  labels:
    k8s-app: kibana-logging
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: "Fluentd"
spec:
  ports:
  - port: 24224
    protocol: TCP
    targetPort: ui
  selector:
    k8s-app: fluentd-logging
  externalIPs:
    - 192.168.128.100

fluentdを立ち上げる

どこかでコケることはないはず。

kubectl create -f fluent-es-service.yaml -f fluent-es-pod.yaml

削除するときは、下記の通り。

kubectl delete -f fluent-es-service.yaml -f fluent-es-pod.yaml

Kibanaの設定

Kibanaも同様に、serviceとcontrollerを定義する。

serviceの設定

kibana-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: kibana-logging
  namespace: NAMESPACE
  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
  externalIPs:
    - 192.168.128.100

replication controllerの設定

kibana-controller.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kibana-logging
  namespace: NAMESPACE
  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:
      containers:
      - name: kibana-logging
        image: docker.io/kibana
        resources:
          # keep request = limit to keep this container in guaranteed class
          limits:
            cpu: 100m
          requests:
            cpu: 100m
        env:
          - name: "ELASTICSEARCH_URL"
            value: "http://elasticsearch-logging:9200"
          - name: "KIBANA_BASE_URL"
            value: "/api/v1/proxy/namespaces/NAMESPACE/services/kibana-logging"
        ports:
        - containerPort: 5601
          name: ui
          protocol: TCP

注意すべき事項は、環境変数ELASTICSEARCH_URLにElasticsearchのURLを指定するくらいだろうか。

Kibanaを立ち上げる

前までと同様に

kubectl create -f kibana-controller.yaml -f kibana-service.yaml

削除するときも同様に

kubectl delete -f kibana-controller.yaml -f kibana-service.yaml

つまづいたところ

Dockerのlogging-driverの設定がjson-fileであれば、/var/lib/docker/containers/(コンテナID)/(コンテナID)-json.log以下にjson形式でコンテナのログが出力されるのだけど、
私が今回検証した環境では、コンテナのlogging driverがjournaldになっていたため、コンテナのログが /var/log/messages に出力されていた。

  • デフォルトだとjson-fileになっているはずなんだけど…。

ちなみに、Logging-driverの設定は次のようにして確認できる。

[root@localhost ~]# docker info | grep Logging
Logging Driver: journald

で、どうにかならないかなあと思っていたのだけど、更に残念なことに本稿執筆時点では、Kubernetes側からPod(コンテナ)のLogging-driverを設定するAPIは実装されていない模様。

追記
DockerのLogging Driverを変更する方法についてまとめました

といった感じで難もありますが、技術的には面白いものなので、今後とも追っていければと思っています。

参考資料