Elastic Cloud on Kubernetes (ECK)をLinode Kubernetes Engine (LKE) で構築してAkamaiのログを可視化する

# Elastic Cloud on Kubernetes (ECK)とは

Elastic Cloud on Kubernetes（ECK）は、ElasticsearchとKibanaのデプロイと管理をKubernetes上で効率化するオープンソースソフトウェアです。Kubernetesのネイティブな能力を活用して、Elastic Stackのスケーラビリティ、復元力、そしてフレキシビリティを最大化します。自動化された運用管理により、Elastic Stackの運用コストと複雑さを大幅に削減することができます。
今回はLinode Kubernetes Engine (LKE) を利用してECKを構築する方法を紹介します。

構成は以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/a9a0f1ef-0933-85d3-7275-6a7e10a89523.png)


## Linode Kubernetes Engine (LKE)の作成

Linode Cloud ManagerからLKEを作成し、kubeconfigをダウンロードします。

今回は Linode 8GB x 3台の構成としています。
尚、Linode 8GB 以下のスペックの場合リソース不足になり起動できませんのでご注意ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/c280765e-a73d-110d-71bb-b8e2dd9181f3.png)



kubectlを実行する端末でconfigをexportします。

```
export KUBECONFIG=./elasticcloud-kubeconfig.yaml
```

node一覧が表示されることを確認します。

```
kubectl get nodes

NAME                            STATUS   ROLES    AGE   VERSION
lke116066-172974-649c6380580d   Ready    <none>   42h   v1.26.3
lke116066-172974-649c6380c34c   Ready    <none>   94m   v1.26.3
lke116066-172974-649c63812a91   Ready    <none>   42h   v1.26.3
```

## cert-managerを構築する

Kibana、Elasticsearchで利用する証明書を発行します。

今回はDNS認証を利用します。またDNSにはAkamai Edge DNSを利用します。

手順に関しては以下を参考にします。

https://cert-manager.io/docs/installation/

https://cert-manager.io/docs/configuration/acme/dns01/akamai/


```yaml:cert.yaml
apiVersion: v1
kind: Secret
metadata:
  name: akamai-secret
type: Opaque
stringData:
  clientSecret: <Clinet Secret>
  accessToken: <Access Token>
  clientToken: <Clinet Token>
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-akamai-dns
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: example@mail.com
    privateKeySecretRef:
      name: letsencrypt-akamai-issuer-account-key
    solvers:
    - dns01:
        akamai:
          serviceConsumerDomain: <Hostname>
          clientTokenSecretRef:
            name: akamai-secret
            key: clientToken
          clientSecretSecretRef:
            name: akamai-secret
            key: clientSecret
          accessTokenSecretRef:
            name: akamai-secret
            key: accessToken
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: eck-zone
spec:
  secretName: akamai-crt-secret
  dnsNames:
  - 'elasticsearch.example.com'
  - 'kibana.example.com'
  issuerRef:
    name: letsencrypt-akamai-dns
    kind: Issuer
```

cert-managerをインストールします。

```terminal
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml
```

podのStatusがRunningであることを確認します。

```
kubectl get pods --namespace cert-manager

NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-579d48dff8-84nw9              1/1     Running   3          1m
cert-manager-cainjector-789955d9b7-jfskr   1/1     Running   3          1m
cert-manager-webhook-64869c4997-hnx6n      1/1     Running   0          1m
```

リソースを作成します。

```
kubectl apply -f cert.yaml
```

作成中のCertificateの進捗状況を確認します。
DNSのレコード作成と認証に数分時間がかかります。

```
kubectl describe certificate eck-zone

Name:         eck-zone
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  cert-manager.io/v1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2023-06-26T17:20:56Z
  Generation:          1
  Managed Fields:
    API Version:  cert-manager.io/v1
    Fields Type:  FieldsV1
~~~~~~~
```

認証が完了しない場合には、ログを確認して問題を特定します。

```
kubectl logs --namespace cert-manager cert-manager-74654c4948-qhzng
```

作成された証明書を確認します。

```
kubectl get certificate --all-namespaces


NAMESPACE   NAME           READY   SECRET              AGE
default     eck-zone   True    akamai-crt-secret   34h
```

以上で証明書の準備完了です。

## Elastic Cloud on Kubernetes (ECK)のインストール

以下の手順を参考に、ECKの構築を行います。

https://www.elastic.co/jp/downloads/elastic-cloud-kubernetes

ECKをインストールします。

```
kubectl create -f https://download.elastic.co/downloads/eck/2.8.0/crds.yaml
```

オペレーターをデプロイします。

```
kubectl apply -f https://download.elastic.co/downloads/eck/2.8.0/operator.yaml
```

### Elasticsearch clusterの構築

Elasticsearchを構築します。
今回は、Pod数を3として、ディスクを300GB付与、証明書の設定を行っています。

https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-elasticsearch.html#k8s-deploy-elasticsearch

```yaml:elasticsearch.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.8.1
  nodeSets:
  - name: master-nodes
    count: 3
    config:
      node.roles: ["master"]
      node.store.allow_mmap: false
  - name: data-nodes
    count: 3
    config:
      node.roles: ["data", "ingest", "ml", "transform"]
      node.store.allow_mmap: false
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 300Gi
  http:
    tls:
      certificate:
         secretName: akamai-crt-secret
```

作成したファイルを適用します。

```
kubectl apply -f elasticsearch.yaml
```

Podが起動できているか確認をします。

```
kubectl get pods --selector='elasticsearch.k8s.elastic.co/cluster-name=quickstart'

NAME                           READY   STATUS    RESTARTS   AGE
quickstart-es-data-nodes-0     1/1     Running   0          34m
quickstart-es-data-nodes-1     1/1     Running   0          34m
quickstart-es-data-nodes-2     1/1     Running   0          34m
quickstart-es-master-nodes-0   1/1     Running   0          34m
quickstart-es-master-nodes-1   1/1     Running   0          34m
quickstart-es-master-nodes-2   1/1     Running   0          34m
```

Linode NodeBalancerを付与して、外部からアクセスできるようにします。

```yaml:elasticsearch-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch-loadbalancer
spec:
  selector:
    common.k8s.elastic.co/type: elasticsearch
    elasticsearch.k8s.elastic.co/cluster-name: quickstart
  type: LoadBalancer
  ports:
    - name: http
      protocol: TCP
      port: 9200
      targetPort: 9200
```

作成した設定を展開します。

```
kubectl apply -f elasticsearch-service.yaml
```

ロードバランサーが作成されていることを確認します。

```
kubectl get svc elasticsearch-loadbalancer
NAME                         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)          AGE
elasticsearch-loadbalancer   LoadBalancer   10.128.69.96   xxx.xxx.xxx.xxx   9200:32334/TCP   46h
```

Akamai EdgeDNSにレコードを追加して名前解決できるようにします。

```
dig elasticsearch.example.com +noall +ans

; <<>> DiG 9.10.6 <<>> elasticsearch.example.com +noall +ans
;; global options: +cmd
elasticsearch.example.com 1748 IN	CNAME	xxx-xxx-xxx-xxx.ip.linodeusercontent.com.
xxx-xxx-xxx-xxx.ip.linodeusercontent.com. 86348 IN A xxx.xxx.xxx.xxx
```

### Kibanaの構築

以下のサイトを参考に、Kibanaを構築します。

Elasticsearchに関連付けを行い、証明書の設定も行います。

https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-kibana.html

```yaml:kibana.yaml
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 8.8.1
  count: 3
  elasticsearchRef:
    name: quickstart
  http:
    tls:
      certificate:
         secretName: akamai-crt-secret
```

作成したyamlを展開します。

```
kubectl apply -f kibana.yaml
```

kibanaが起動していることを確認します。

```
kubectl get kibana

NAME         HEALTH   NODES   VERSION   AGE
quickstart   green    3       8.8.1     41h
```

podのステータスが正常であることを確認します。

```
kubectl get pod --selector='kibana.k8s.elastic.co/name=quickstart'

NAME                             READY   STATUS    RESTARTS   AGE
quickstart-kb-694658999d-hmwzz   1/1     Running   0          41h
quickstart-kb-694658999d-vbtwv   1/1     Running   0          41h
quickstart-kb-694658999d-z6x8h   1/1     Running   0          41h
```

Linode NodeBalancerを付与して、外部からアクセスできるようにします。

```yaml:kibana-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: kibana-loadbalancer
spec:
  selector:
    common.k8s.elastic.co/type: kibana
    kibana.k8s.elastic.co/name: quickstart
  type: LoadBalancer
  ports:
    - name: http
      protocol: TCP
      port: 5601
      targetPort: 5601
```

作成したファイルを展開します。

```
kubectl apply -f kibana-service.yaml
```

ロードバランサーが作成されていることを確認します。

```
kubectl get svc kibana-loadbalancer
NAME                  TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)          AGE
kibana-loadbalancer   LoadBalancer   10.128.192.34   xxx.xxx.xxx.xxx   5601:30541/TCP   2d2h
```

Akamai EdgeDNSにレコードを追加して名前解決できるようにします。

```
dig kibana.example.com +noall +ans

; <<>> DiG 9.10.6 <<>> kibana.example.com +noall +ans
;; global options: +cmd
elasticsearch.example.com 1748 IN	CNAME	xxx-xxx-xxx-xxx.ip.linodeusercontent.com.
xxx-xxx-xxx-xxx.ip.linodeusercontent.com. 86348 IN A xxx.xxx.xxx.xxx
```


### Elasticsearchの設定追加

こちらの記事を参考に設定の追加を行います。

https://qiita.com/hideki_okamoto/items/6a2bcc9b1964c071eb12

KibanaとElasticsearchに接続する際のパスワードを変数に入れます。

```
export PASSWORD=`kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode; echo`
```

インデックスを作成します。
ElasticsearchのIndex Template、フィールドの型定義をしています。

```
curl -u elastic:$PASSWORD -X POST -H 'Content-Type: application/json' -d "$(curl -s https://raw.githubusercontent.com/isss802/stackscripts/main/elasticsearch-kibana/elasticsearch_index_template.json)" https://elasticsearch.example.com:9200/_index_template/logs-akamai.datastream2
```

パイプラインを３つ作成します。
ElasticsearchのIngest Pipelineで、送られてきたDS2のフィールドを一部加工するための定義です。
```
curl -u elastic:$PASSWORD -X PUT -H 'Content-Type: application/json' -d "$(curl -s https://raw.githubusercontent.com/isss802/stackscripts/main/elasticsearch-kibana/elasticsearch_ingest_pipeline.json)" https://elasticsearch.example.com:9200/_ingest/pipeline/datastream2-ingest-pipeline
```

```
curl -u elastic:$PASSWORD -X PUT -H 'Content-Type: application/json' -d "$(curl -s https://raw.githubusercontent.com/isss802/stackscripts/main/elasticsearch-kibana/elasticsearch_ingest_pipeline_breadcrumbs.json)" https://elasticsearch.example.com:9200/_ingest/pipeline/datastream2-ingest-pipeline-breadcrumbs
```


```
curl -u elastic:$PASSWORD -X PUT -H 'Content-Type: application/json' -d "$(curl -s https://raw.githubusercontent.com/isss802/stackscripts/main/elasticsearch-kibana/elasticsearch_ingest_pipeline_cmcd.json)" https://elasticsearch.example.com:9200/_ingest/pipeline/datastream2-ingest-pipeline-cmcd
```

以上で、elasticsearchの設定は完了です。

### Kibana追加設定

Kibanaのダッシュボードの設定を行うため、Saved ObjectsのファイルをPOSTします。

```
 curl -o kibana.ndjson https://raw.githubusercontent.com/hokamoto/stackscripts/main/elasticsearch-kibana/kibana_saved_objects.ndjson && curl -u elastic:$PASSWORD -X POST "https://elasticsearch.example.com:5601/api/saved_objects/_import" -H 'kbn-xsrf: reporting' --form file=@./kibana.ndjson
```

以上でKibanaの設定は完了です。

## DataStream2の設定

以下の記事を参考に、DataStream2の設定を行いログを確認します。

https://qiita.com/hideki_okamoto/items/6a2bcc9b1964c071eb12

ダッシュボードからグラフが確認できたら構築は完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/ec996b9c-6707-f2f8-d644-a43567ba68ca.png)


## Elasticsearchのディスク拡張

運用していくにつれてディスク拡張が必要になる場合があります。

今回は、試しに300GBから350GBにサイズを拡張してみます。

その場合はまず、storageの値を変更する必要があります。

```yaml:elasticsearch.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 8.8.1
  nodeSets:
  - name: master-nodes
    count: 3
    config:
      node.roles: ["master"]
      node.store.allow_mmap: false
  - name: data-nodes
    count: 3
    config:
      node.roles: ["data", "ingest", "ml", "transform"]
      node.store.allow_mmap: false
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            #容量追加 
            storage: 350Gi
  http:
    tls:
      certificate:
         secretName: akamai-crt-secret
```

設定を反映します。

```
kubectl apply -f elasticsearch.yaml
```

Capacityが増強されていることを確認します。

```
kubectl get pvc
NAME                                              STATUS   VOLUME                 CAPACITY   ACCESS MODES   STORAGECLASS                  AGE
elasticsearch-data-quickstart-es-data-nodes-0     Bound    pvc-d39ec344188f4155   350Gi      RWO            linode-block-storage-retain   41h
elasticsearch-data-quickstart-es-data-nodes-1     Bound    pvc-b68538a685fe4cbf   350Gi      RWO            linode-block-storage-retain   41h
elasticsearch-data-quickstart-es-data-nodes-2     Bound    pvc-4459f647a6d3415b   350Gi      RWO            linode-block-storage-retain   41h
elasticsearch-data-quickstart-es-master-nodes-0   Bound    pvc-e17373e2b5044f2b   10Gi       RWO            linode-block-storage-retain   41h
elasticsearch-data-quickstart-es-master-nodes-1   Bound    pvc-40bf519cece641df   10Gi       RWO            linode-block-storage-retain   41h
elasticsearch-data-quickstart-es-master-nodes-2   Bound    pvc-cc30b10ff06141ea   10Gi       RWO            linode-block-storage-retain   41h
```

対象のnodeを再起動したいのでdrainします。

```
kubectl drain lke116066-172974-649c63812a91 --ignore-daemonsets --delete-emptydir-data
```

Linode Cloud Managerから対象のインスタンスを再起動します。
再起動後にサービスに戻すため、uncordonします。

```
kubectl uncordon lke116066-172974-649c63812a91
```

resize2fsを利用してディスクを拡張する必要があるため、kubectl-node-shellを利用してnodeに接続します。

https://github.com/kvaps/kubectl-node-shell

```
kubectl get node
NAME                            STATUS   ROLES    AGE   VERSION
lke116066-172974-649c6380580d   Ready    <none>   40h   v1.26.3
lke116066-172974-649c6380c34c   Ready    <none>   13m   v1.26.3
lke116066-172974-649c63812a91   Ready    <none>   40h   v1.26.3
```

対象のNodeに接続します。

```
kubectl node-shell lke116066-172974-649c6380c34c
```

ディスクを確認します。

```
df -h
```

リサイズします。

```
resize2fs /dev/disk/by-id/scsi-0Linode_Volume_pvcd39ec344188f4155
```

ディスクが拡張できていれば作業完了です。

```
df -h /dev/disk/by-id/scsi-0Linode_Volume_pvc4459f647a6d3415b
Filesystem                                               Size  Used Avail Use% Mounted on
/dev/disk/by-id/scsi-0Linode_Volume_pvc4459f647a6d3415b  344G   21M  344G   1% /var/lib/kubelet/pods/bf2e34e2-cec6-4eed-aff0-f02164737084/volumes/kubernetes.io~csi/pvc-4459f647a6d3415b/mount
root@lke116066-172974-649c6380c34c:/#
```

podが偏り気になる際にはDeschedulerなどの利用を検討すると良いかと思います。

https://github.com/kubernetes-sigs/descheduler

# まとめ

Linode Kubernetes Engine (LKE)を利用することで拡張しやすいElastic Cloud on Kubernetes (ECK)を構築することができました。
ぜひ様々なシステムでLKEを活用していただければと思います。

# 関連記事

アカマイ・テクノロジーズ合同会社はQiitaでAkamai's cloud computing services関連など開発者向けの記事を掲載しております。

https://qiita.com/xenbase/items/52e2956a64cedc772bd9