Kibana Helm Chart で OpenSearch Service のデータを見る（VPC アクセスの場合）

VPC 内にある Elasticsearch クラスタ に対し、VPC 外部から Kibana にアクセスする場合、

- SSH トンネルを使用する
- プロキシを使用する
- VPN を使う

などの方法が挙げられます。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/opensearch-dashboards-vpc-cognito/

ただどの方法も構築、管理、運用のコストが大きいです。

そこでこの記事では、Kibana Helm Chart を利用し、EKS クラスタ内の Pod から Elasticsearch のデータにアクセスする方法を紹介します。なお、Helm Chart の管理には Argo CD を利用しているため、その前提の手順となっています。
## 環境

- Kubernetes version 1.21
- Platform version eks.7
- Elasticsearch 7.10
- Argo CD v2.3.3

OpenSearch Service で Elasticsearch を利用しています。
OpenSearch へ移行していてもほぼ同様の手順で導入可能かと思いますが未検証です。

## 構成
![kibana.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/a11ff2cb-3bc8-f3e2-a5db-94827fbc569f.png)

## 手順
はじめに EKS クラスタ内の Pod から Domain endpoint へのリーチャビリティがあることを確認します。

```
❯ curl https://vpc-xxx.ap-northeast-1.es.amazonaws.com
{
  "name" : "xxx",
  "cluster_name" : "xxx",
  "cluster_uuid" : "xxx",
  "version" : {
    "number" : "7.10.2",
    "build_flavor" : "oss",
    "build_type" : "tar",
    "build_hash" : "unknown",
    "build_date" : "2021-09-29T11:42:59.634166Z",
    "build_snapshot" : false,
    "lucene_version" : "8.7.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```

クラスタ名やバージョンを含む情報が返ってくれば問題ありません。
なお Domain endpoint はコンソールや AWS CLI から確認できます。
```
❯ aws opensearch describe-domain --domain-name xxx
{
    "DomainStatus": {
        ...
        "Endpoints": {
            "vpc": "vpc-xxx.ap-northeast-1.es.amazonaws.com" # これ
        },
        ...
    }
}
```
Helm Chart をインストールします。

https://github.com/elastic/helm-charts/tree/main/kibana

OpenSearch へ移行している場合はこちらもあります。

https://github.com/opensearch-project/helm-charts/tree/main/charts/opensearch-dashboards

Argo CD の Application として Chart を追加します。
Helm コマンドを使う場合は[こちら](https://github.com/elastic/helm-charts/tree/main/kibana#installing)をご確認ください。

```yaml
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: kibana-test
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://helm.elastic.co
    chart: kibana
    targetRevision: 7.10.2
    helm:
      values: |
        image: "docker.elastic.co/kibana/kibana-oss"
        imageTag: "7.10.2"
        imagePullPolicy: "IfNotPresent"
        elasticsearchHosts: "https://vpc-xxx.ap-northeast-1.es.amazonaws.com"
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
```

:::note warn
Kibana の Image に関する補足
デフォルトの Image だと下記エラーで Pod が起動しなかったため、OSS 版の Image を指定しています。

`Failed to parse mapping [_doc]: No handler for type [flattened] declared on field [config]`

理由ですが、恐らく次の通りかと思います。詳しい方がいたらコメントで教えて下さい。
https://github.com/elastic/kibana/issues/52324#issuecomment-592063291
この Issue で指摘されている通り、デフォルトの kibana では x-pack という有償オプションが有効になっているが、OpenSearch Service では無効であるため、この不整合によってエラーが起きた。OSS 版の Kibana であれば有償オプションが最初から付属していない[^kibana-oss]ため起動できる。
:::

[^kibana-oss]:https://discuss.elastic.co/t/elasticsearch-oss-image-and-x-pack-plugin/266169/3

Application を登録すると Pod や Service が作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/c4d244c9-49a4-9467-b912-cee770626b69.png)

ポートフォワードで Kibana の Web UI にアクセスします。
```
❯ k port-forward kibana-test-kibana-d4fc8cb48-xs7xl 8081:5601 -n default
Forwarding from 127.0.0.1:8081 -> 5601
Forwarding from [::1]:8081 -> 5601
```

正常ならこのような画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619762/06d895ff-055e-b805-05d5-64ae9484fd10.png)

Kibana Helm Chart には Ingress の有効化オプションがあります。annotation を付与することで AWS Load Balancer Controller[^aws-lb-controller] と組み合わせたり、Cognito で認証を挟むことができます。こうすることで VPC 外部からも安全にアクセスできます。
[^aws-lb-controller]:https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/

これらの詳細に入ると各 Operator の話になってしまうため、最終的なマニフェストを載せてこの記事は終わりにします。

```yaml
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: kibana-test
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://helm.elastic.co
    chart: kibana
    targetRevision: 7.10.2
    helm:
      values: |
        elasticsearchHosts: "https://vpc-xxx.ap-northeast-1.es.amazonaws.com"
        image: "docker.elastic.co/kibana/kibana-oss"
        imageTag: "7.10.2"
        imagePullPolicy: "IfNotPresent"
        ingress:
          enabled: "true"
          className: ""
          hosts:
            - host: "kibana-xxx.xxx.jp"
              paths:
                - path: "/*"
          pathtype: "ImplementationSpecific"
          annotations:
            kubernetes.io/ingress.class: "alb"
            alb.ingress.kubernetes.io/scheme: "internet-facing"
            alb.ingress.kubernetes.io/target-type: "ip"
            alb.ingress.kubernetes.io/backend-protocol: "HTTP"
            alb.ingress.kubernetes.io/healthcheck-protocol: "HTTP"
            alb.ingress.kubernetes.io/healthcheck-port: "5601"
            alb.ingress.kubernetes.io/healthcheck-path: "/api/status"
            alb.ingress.kubernetes.io/auth-type: "cognito"
            alb.ingress.kubernetes.io/auth-scope: "openid"
            alb.ingress.kubernetes.io/auth-session-timeout: "3600"
            alb.ingress.kubernetes.io/auth-session-cookie: "AWSELBAuthSessionCookie"
            alb.ingress.kubernetes.io/auth-on-unauthenticated-request: "authenticate"
            alb.ingress.kubernetes.io/certificate-arn: "arn:aws:acm:ap-northeast-1:xxx:certificate/xxx"
            alb.ingress.kubernetes.io/auth-idp-cognito: "{\"UserPoolArn\":\"arn:aws:cognito-idp:ap-northeast-1:xxx:userpool/ap-northeast-1_xxx\",\"UserPoolClientId\":\"xxx\",\"UserPoolDomain\":\"xxx\"}"
            external-dns.alpha.kubernetes.io/hostname: "kibana-xxx.xxx.jp"
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
```

記事を書くためにマニフェストを書き換えたので、もし動かなかったらコメントで教えて下さい。