ECKの外部接続をEKSとGKEで試してみる



## はじめに

[ECK](https://www.elastic.co/jp/elastic-cloud-kubernetes)というElasticsearchをKubernetes上にデプロイできるECK Operatorを使って、Elasticsearchクラスターを構築しKibanaを外部公開するまでを試してみた。

また、Kubernetesがクラウド間の差分を吸収してくれるというのを耳にしたことがあったので、GKE（GCP）とEKS（AWS）上で同じKubernetesマニフェストで実際にECKが動くのか試してみた。

目的としては、Kubernetesを触ったことがなかったので、学習目的で触ってみたかったのと、近々Elasticsearchを使う機会がありそうなため。



## 要約

- Kubernetesクラスター作成含め、コマンド計4回でElasticsearchクラスターが構築できた。
- GKE、EKSで同じマニフェストでElasticsearchクラスタを作成して外部接続できた。
- Type: LoadBalanecerを指定すると、若干性質が異なるLBが作成される。(GKEはネットワークロードバランサーが、EKSはCLBが作成される)
- Kubernetesの学習目的であれば、1クラスターが無料で使えるGKEを選んだほうがよさそう。

## 構成図
![eck_architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420612/0f7bc537-8f99-c61a-80f4-028bd8fce8e2.png)

- Node2台構成で、Elasticsearch用Podを2台、Kibana用Podを1台配置
- LoadBalancerでは、SSLオフロードせず各Podの同一ポートにバランシングする
- 各PodでSSL終端する（ECK側で持っている自己証明書を使用）

## 構築手順

### 事前準備(共通)

- kubernetesのCLI操作ツールである[kubectl](https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/)をインストールする。 
 ※[シェルの自動補完]([https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/#%E3%82%B7%E3%82%A7%E3%83%AB%E3%81%AE%E8%87%AA%E5%8B%95%E8%A3%9C%E5%AE%8C%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B](https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/#シェルの自動補完を有効にする))を有効にしておくと非常に便利

### ECK on GKE

#### 事前準備

- CLIでGCPリソースを操作できるように、[CloudSDK](https://cloud.google.com/sdk/docs/downloads-interactive?hl=ja)をインストールする

#### GKE Cluster作成

- 以下のコマンドを実行する
  - eck-learnというクラスター名でデータノードとしてEC2（n1-standard-1）を2台が作成される
  - 作成に3分程度かかる

  ```cmd
  gcloud container clusters create eck-learn \
      --zone=asia-northeast1-a \
      --cluster-version=latest \
      --enable-ip-alias \
      --num-nodes=2
  ```

- kubectlにてnode状態を確認する。（status: ReadyになっていればOK）

  ```cmd
  $ kubectl get nodes
  NAME                                       STATUS   ROLES    AGE   VERSION
  gke-eck-learn-default-pool-feb7e9ae-23k1   Ready    <none>   47h   v1.16.11-gke.5
  gke-eck-learn-default-pool-feb7e9ae-x7zl   Ready    <none>   47h   v1.16.11-gke.5
  ```

  

#### ECK作成

※ [こちら](https://www.elastic.co/guide/en/cloud-on-Kubernetes/current/Kubernetes-quickstart.html)を参考に実施

##### ECK operatorをインストール

- ECK operatorをapplyする

  ```cmd
  $ kubectl apply -f https://download.elastic.co/downloads/eck/1.1.2/all-in-one.yaml
  ```

- kubectl get svc にて状態を確認する（elastic-webhook-serverが表示されればOK）

  ```cmd
  $ kubectl get svc -n elastic-system
  NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
  elastic-webhook-server   ClusterIP   10.0.0.38    <none>        443/TCP   110s
  ```

##### ElasticsearchをDeploy

- manifestを作成する
    - 接続元を制限したい場合は「loadBalancerSourceRanges」に許可IPを入力する
    
```yaml:eck-elascticsearch.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 7.8.0
  nodeSets:
    - name: default
      count: 2
      config:
        node.master: true
        node.data: true
        node.ingest: true
        node.store.allow_mmap: false

  http:
    service:
      spec:
        type: LoadBalancer
        # Ref: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-services.html
        # https://kubernetes.io/docs/concepts/services-networking/service
        loadBalancerSourceRanges:
          - "xxx.xxx.xxx.xxx/32"
```

- kubectl applyを実行

  ```cmd
  $ kubectl apply -f eck-elascticsearch.yaml
  ```

- kubectl get pod にてpod状態を確認する（RunningになればOK）

  ```cmd
  $ kubectl get pod
  NAME                      READY   STATUS    RESTARTS   AGE
  quickstart-es-default-0   1/1     Running   0          63s
  ```

- kubectl get svc にてservice状態を確認する

  - `quickstart-es-http`にEXTERNAL-IPが振られていればOK
  - GKEの場合は、EXTERNAL-IPがLBに振られたグローバルIPになる

  ```cmd
  $ kubectl get svc
  NAME                      TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
  kubernetes                ClusterIP      10.0.0.1      <none>        443/TCP          14m
  quickstart-es-default     ClusterIP      None          <none>        <none>           2m40s
  quickstart-es-http        LoadBalancer   10.0.12.244   xxx.xxx.xxx.xxx   9200:30834/TCP   2m41s
  quickstart-es-transport   ClusterIP      None          <none>        9300/TCP         2m41s
  ```

- Elasticsearchに接続するためのパスワードを取得する

  ```cmd
  $ PASSWORD=$(kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode)
  ```

- Elasticsearchに接続する（以下のような出力が返ればOK）  
  ※ xxx.xxx.xxx.xxxは`kubectl get svc`で出力される`quickstart-es-http`の`EXTERNAL-IP`を入力する

  ```cmd
  $ curl -u "elastic:$PASSWORD" -k https://xxx.xxx.xxx.xxx:9200
  {
    "name" : "quickstart-es-default-0",
    "cluster_name" : "quickstart",
    "cluster_uuid" : "p_wTfmIdQZ2Wt6Maq3wEDg",
    "version" : {
      "number" : "7.8.0",
      "build_flavor" : "default",
      "build_type" : "docker",
      "build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
      "build_date" : "2020-06-14T19:35:50.234439Z",
      "build_snapshot" : false,
      "lucene_version" : "8.5.1",
      "minimum_wire_compatibility_version" : "6.8.0",
      "minimum_index_compatibility_version" : "6.0.0-beta1"
    },
    "tagline" : "You Know, for Search"
  }
  ```

  

##### KibanaをDeploy

- manifestを作成する
    - 接続元を制限したい場合は「loadBalancerSourceRanges」に許可IPを入力する

```yaml:eck-kibana.yaml
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 7.8.0
  count: 1
  elasticsearchRef:
    name: quickstart
  http:
    service:
      spec:
        type: LoadBalancer
        # Ref: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-services.html
        # https://kubernetes.io/docs/concepts/services-networking/service
        loadBalancerSourceRanges:
          - "xxx.xxx.xxx.xxx/32"
```

- kubectl applyを実行

  ```cmd
  $ kubectl apply -f eck-kibana.yaml
  ```

- kubectl get pod にてpod状態を確認する（RunningになればOK）

  ```cmd
  $ kubectl get pod
  NAME                            READY   STATUS    RESTARTS   AGE
  quickstart-es-default-0         1/1     Running   0          46h
  quickstart-kb-c69c8645d-2hwx4   1/1     Running   0          118s
  ```

- kubectl get svc にてservice状態を確認する

  - `quickstart-kb-http`にEXTERNAL-IPが振られていればOK
  - GKEの場合は、EXTERNAL-IPがLBに振られたグローバルIPになる

  ```cmd
  $ kubectl get svc
  NAME                      TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
  kubernetes                ClusterIP      10.0.0.1      <none>        443/TCP          47h
  quickstart-es-default     ClusterIP      None          <none>        <none>           46h
  quickstart-es-http        LoadBalancer   10.0.12.244   xxx.xxx.xxx.xxx   9200:30834/TCP   46h
  quickstart-es-transport   ClusterIP      None          <none>        9300/TCP         46h
  quickstart-kb-http        LoadBalancer   10.0.13.197   xxx.xxx.xxx.xxx   5601:30579/TCP   88s
  ```

- Kibanaに接続する

  - ブラウザに「https://xxx.xxx.xxx.xxx:5601」を入力

  - xxx.xxx.xxx.xxxは`kubectl get svc`で出力される`quickstart-kb-http`の`EXTERNAL-IP`を入力する

  - 自己証明書のため、ブラウザで警告が出るが「アクセスする」を選ぶ

  - 以下のログイン画面がでれば接続成功  
    ※ UsernameとPasswordは、Elasticserach接続時に使用したもの（Username: elastic, Password: xxxxxxxxxxxxxxx）と同じ

    ![image-20200723054829076.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420612/93c65a39-ef21-b18a-0967-ac12c3bacaf8.png)

### ECK on EKS

#### 事前準備

- CLIでAWSリソースを操作できるように、[AWS CLI Version2](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)をインストールする。※eksctlを使うためにはversion2が必要
- EKSをCLI操作するために、[eksctl](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html)をインストールする。

#### EKS Cluster作成

- 以下のコマンドを実行する

    - eck-learnというクラスター名でデータノードとしてEC2（t3.small）を2台が作成される
    - EC2以外にもVPCやサブネットやルートテーブルといったネットワーク関連リソースが `eksctl~` というtag付きで作成される
    - 作成に15分程度かかる
    - EKS on EC2で作成する。（EKS on Fargate だとECK作成時に失敗する）
 
  ```cmd
  $ eksctl create cluster \
      --vpc-cidr 10.0.0.0/16 \
      --vpc-nat-mode HighlyAvailable \
      --name eck-learn \
      --nodegroup-name eck-learn-ng \
      --node-type t3.small \
      --nodes 2
  ```

- kubectlにてnode状態を確認する。（status: ReadyになっていればOK）

  ```cmd
  $ kubectl get nodes
  
  NAME                                                 STATUS   ROLES    AGE   VERSION
  ip-192-168-155-85.ap-northeast-1.compute.internal    Ready    <none>   18m   v1.16.8-eks-e16311
  ip-192-168-177-141.ap-northeast-1.compute.internal   Ready    <none>   17m   v1.16.8-eks-e16311
  ```

#### ECK作成

以降は、ECK on GKEと同じ手順で作成できる

## ざっくり比較（GKE vs EKS）

- 起動時間
  - GKE:2~3分
  - EKS:15分
- Kubernetes version
  - GKE: かなり細かく指定が可能  
    `gcloud container get-server-config --zone asia-northeast1-a`で東京リージョンで使えるversionが表示可能
  - EKS: 各マイナーバージョンごとに一つを選ぶ  
    参考：[利用可能な Amazon EKS Kubernetes バージョン](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/kubernetes-versions.html)

- 料金
    - 課金モデルは同じ
        - Kubernetesクラスターに対する時間課金（現時点で両者とも0.10USD/hour）と
        - ワーカーノード実行に必要なAWSリソースに対する課金
    - GKEは1つ目のクラスター（ゾーンクラスター）は無料で利用可能
    - 参考：
        - [GKE料金](https://cloud.google.com/kubernetes-engine/pricing/?hl=ja)
        - [EKS料金](https://aws.amazon.com/jp/eks/pricing/)

- Type: LoadBalanecer指定した場合に作成されるリソース
    - EKSの場合
        - CLBが作成される（リバースプロキシ）
        - 外からの接続先はCLBのエンドポイントになる（固定化されたIPは払い出されない）
        - loadBalancerSourceRanges に設定した内容はSecurityGroupとしてCLBにアタッチされる
    - GKEの場合
        - ネットワークロードバランサーが作成される（パススルーのロードバランサー）
        - 外からの接続先としてグローバルIPが払い出される
        - loadBalancerSourceRanges に設定した内容は各ワーカーノードのFW設定として入る（つまり許可外のIPもLBは通過する）


## まとめ

- Kibana接続まで実質コマンド4,5回でできるので、普通にElascticクラスターを構築する場合と比較するとかなり楽
- Kubernetesクラスター作成以降（ECKの作成）は、両クラウドともに同じ手順でできたため、「Kubernetesがクラウド間の差分を吸収してくれる」というのは実感できた。
- ただ、同じマニフェストでも作成されるリソースはクラウドによって違う（当然といえば当然）ので、クラウド側のサービス仕様の理解は必要。（今回の場合はType: LoadBalanecerの部分）
- Kubernetesの学習目的であれば、1クラスターが無料で使えるGKEを選んだほうがよさそう。