EfK(Elasticsearch+fluent-bit+Kibana)をHelmで構築する

EfK(Elasticsearch+fluent-bit+Kibana)が必要になったので構築した時の手順をメモ化する。
今更感は非常にあるが、Helmの手順はあまり見なかったので誰かの参考になれば幸いである。
なお、動けばいいレベルでの構築であり、ベストプラクティスなどには一切沿っていない点は注意。

## 検証環境
以下の環境・条件で検証する。
- EKS
- Ingress ControllerはKong Ingress Controllerを使用
- TLSはIngress終端とし、ES、KibanaはTLS化しない
- TLSの証明書は自己署名証明書とし、[cert-manager](https://cert-manager.io/docs/installation/)で発行する

作業するにあたり、`Ingress`で使用するドメインを以下で定義しておく。
```
export MYDOMAIN="aws.hogehoge.info"
```
また、リソースを展開するNamespaceを作成しておく。
```
kubectl create ns efk
```

## 自己署名証明書の準備
自己署名証明書を作成するために`Issuer`と`Certificate`を作成する。
```yaml
cat <<EOF > ./cert.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: selfsigned-issuer-efk
  namespace: efk
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: efk-ca
  namespace: efk
spec:
  commonName: "*.$MYDOMAIN"
  secretName: efk-ca
  issuerRef:
    name: selfsigned-issuer-efk
    kind: Issuer
EOF
kubectl apply -f ./cert.yaml
```
作成するとcert-managerがTLSの証明書を自動生成して`Secret`化してくれる。
`Secret`名は`Certificate`の`spec.secretName`で指定した名前となる。
```sh
$ kubectl get secret -n efk efk-ca
NAME     TYPE                DATA   AGE
efk-ca   kubernetes.io/tls   3      5h19m
```


## ElasticsearchとKibanaの構築
ElasticsearchとKibanaは[Elastic Cloud on Kubernetes(ECK)](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-quickstart.html)を使う。
まず最初にHelmでOperatorをインストールする。
```
helm repo add elastic https://helm.elastic.co
helm repo update
helm install elastic-operator elastic/eck-operator -n elastic-system --create-namespace
```
このタイミングではあくまでもOperatorのみで、Elasticsearchなどのサービスは作成されない。
次に[この辺](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-elasticsearch.html)を参考にElasticsearchを作成する。
Elasticsearchを展開する。
```yaml
cat <<EOF > ./es.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
  namespace: efk
spec:
  version: 8.13.4
  http:
    tls:
      selfSignedCertificate:
        disabled: true
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
EOF
kubectl apply -f ./es.yaml
```
[こちら](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-kibana.html)を参考にKibanaを展開する
```yaml
cat <<EOF > ./kibana.yaml
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
  namespace: efk
spec:
  version: 8.13.4
  http:
    tls:
      selfSignedCertificate:
        disabled: true
  count: 1
  elasticsearchRef:
    name: quickstart
EOF
kubectl apply -f ./kibana.yaml
```
ここまででElasticsearchとKinabaの展開は完了となる。
次にそれぞれのサービスにドメインでアクセスするために`Ingress`を作成する。
最初にElasticsearchの`Ingress`を作成する。
```yaml
cat << EOF > ./ing-es.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: quickstart-es
  namespace: efk
  annotations:
    konghq.com/protocols: "https"
spec:
  ingressClassName: kong
  rules:
  - host: es.$MYDOMAIN
    http:
      paths:
      - backend:
          service:
            name: quickstart-es-http
            port:
              number: 9200
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - es.$MYDOMAIN
    secretName: efk-ca
EOF
kubectl apply -f ./ing-es.yaml
```
`Ingress`でTLS終端するためにcert-managerで作ったTLSのSecret(`es-tls`)を指定し、`Ingress`のAnnotationでHTTPSのみ通すよう指定した(`konghq.com/protocols: "https"`)
他のIngress Controllerを使う場合はそのControllerの書き方に従うこと。
同様にKibanaの`Ingress`も作成する。
```yaml
cat << EOF > ./ing-kibana.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: quickstart-kibana
  namespace: efk
  annotations:
    konghq.com/protocols: "https"
spec:
  ingressClassName: kong
  rules:
  - host: kibana.$MYDOMAIN
    http:
      paths:
      - backend:
          service:
            name: quickstart-kb-http
            port:
              number: 5601
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - kibana.$MYDOMAIN
    secretName: efk-ca
EOF
kubectl apply -f ./ing-kibana.yaml
```
Ingressを展開したら設定したホスト名でアクセスできるようになっているため、早速アクセスする。
Elasticsearchのユーザ`elastic`の初期パスワードを取得する。
```sh
kubectl get secret -o yaml quickstart-es-elastic-user -o jsonpath={.data.elastic} -n efk | base64 -d
```
Kibanaにユーザ名：`elastic`、パスワード：`取得したパスワード`でログインするとログイン出来ることが確認できる。
![20240522125817.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/1bf8db73-cd1e-14a4-ce9f-255469f97d38.png)

Elasticsearchにも同様にブラウザでアクセスし、同じユーザ名、パスワードでログインすればログイン出来ることが確認できる。

![20240522130122.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/25f5d1a1-06e9-44e9-e8b3-b3dc6aa362c3.png)

## fluent-bitの構築
fluent-bitのHelmのvalues.yamlを抜き出す。
```sh
helm repo add fluent https://fluent.github.io/helm-charts
helm show values fluent/fluent-bit > fb-values.yaml
```
デフォルトではOUTPUTプラグインの設定箇所にElasticsearchに転送する設定が入っている。
これを編集する。
```txt:fb-values.yaml
    [OUTPUT]
        Name es
        Match kube.*
        Host quickstart-es-default
        Logstash_Format On
        Retry_Limit False
        HTTP_User elastic
        HTTP_Passwd ma8Av06F8nxxxxxxx
        Suppress_Type_Name On

    [OUTPUT]
        Name es
        Match host.*
        Host quickstart-es-default
        Logstash_Format On
        Logstash_Prefix node
        Retry_Limit False
        HTTP_User elastic
        HTTP_Passwd ma8Av06F8nxxxxxxx
        Suppress_Type_Name On
```
変更箇所は以下となる。
- `Host`: Elasticsearchのアドレス（ここでは`Service`経由でアクセスするよう設定）
- `HTTP_User`: 先ほどログインに使用したユーザ
- `HTTP_Passwd`: 先ほどログインに使用したパスワード
- `Suppress_Type_Name`: ES8以降はOnにするよう[説明があるので](https://docs.fluentbit.io/manual/pipeline/outputs/elasticsearch)Onにする

修正が終わったらvaluesを使ってfluent-bitをデプロイする。
```sh
helm upgrade -i fluent-bit fluent/fluent-bit -n efk -f ./fb-values.yaml
```


## 動作確認
テスト用のPodを起動する。
```
kubectl run nginx --image nginx -n default --port 80
```
出力を確認する。
```sh
$ kubectl logs -n default nginx
：（省略）
2024/05/22 06:23:51 [notice] 1#1: using the "epoll" event method
2024/05/22 06:23:51 [notice] 1#1: nginx/1.25.5
2024/05/22 06:23:51 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2024/05/22 06:23:51 [notice] 1#1: OS: Linux 5.10.215-203.850.amzn2.x86_64
2024/05/22 06:23:51 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/05/22 06:23:51 [notice] 1#1: start worker processes
2024/05/22 06:23:51 [notice] 1#1: start worker process 29
2024/05/22 06:23:51 [notice] 1#1: start worker process 30
```
このログがfluent-bitが拾ってElasticsearchに転送し、Kibanaから見れるか確認する。
まず、Kibanaにログインし、`Discover`->`Create data view`から
データビューを適当に作成する。
![20240522152136.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/1d21c36c-4adf-2b50-526d-7d3f2059b601.png)

作成後、先程起動したPodのログを検索してみる。

![20240522152919.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/64c8a9e8-4618-606d-29ed-ad3c8a7ec121.png)

ログが取れているので、動作は問題なさそうだ。
ということで、問題なく構築できた。