サービスメッシュを実現するIstioをEKS上で動かす - その2 EKSでサンプルを動かしてみる

山崎 雅斗
12

前回は、Minikubeを使ってKubernetesクラスタを作成し、それを使ってIstio上にサンプルアプリケーションをデプロイするというところまでやってみました。
今回はその続きとして、AWSのKubernetesマネージドサービスであるEKS上でBookinfoアプリケーションを動かすところをやっていきたいと思います。

シリーズ一覧

EKSクラスタの作成

今回は、eksctlを使ってEKSクラスタを作成していきます。

まずは、次のコマンドでeksctlをインストールします。

$ curl --silent --location "https://github.com/weaveworks/eksctl/releases/download/latest_release/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
$ sudo mv /tmp/eksctl /usr/local/bin

また、後ほど kubectl コマンドでEKSクラスタとやり取りができるように、次のコマンドでaws-iam-authenticatorも合わせてインストールしておきます(実行にはGoのビルド環境が必要です)。

$ go get -u -v github.com/kubernetes-sigs/aws-iam-authenticator/cmd/aws-iam-authenticator

準備ができたら、 eksctl コマンドでEKSクラスタを作成していきます。
今回は東京リージョンにクラスタを作成したいので、 --region オプションには ap-northeast-1 を指定します。

$ eksctl --region ap-northeast-1 create cluster --name istio-test-cluster

完了までは少し時間がかかりますが、最後に EKS cluster ... is ready と出力されれば作成は成功です。
クラスタが作成できたら、 kubectl コマンドがうまく実行できるか確認しておきます。
なお、 ~/.kube/config は自動的に更新されるため、特に手動で変更する必要はありません。

$ kubectl get nodes
NAME                                              STATUS   ROLES    AGE   VERSION
ip-10-7-162-203.ap-northeast-1.compute.internal   Ready    <none>   29d   v1.11.5
ip-10-7-163-236.ap-northeast-1.compute.internal   Ready    <none>   29d   v1.11.5

Istioのインストール

ここまででEKSクラスタの準備ができたので、次はIstioをインストールしていきます。

前回はデモ用の設定で簡単にインストールしましたが、プロダクション環境で使用する場合はKubernetes用パッケージマネージャのHelmを使ってインストールするのが推奨されています。
そのため、今回はHelmを使ってIstioをインストールしていきます。

また、Minikubeの時と同様、事前に必要なファイル群をダウンロードしておく必要があります。

$ curl -L https://git.io/getLatestIstio | ISTIO_VERSION=1.1.1 sh -
$ cd istio-1.1.1

事前に helm コマンドをインストールしておく必要があります。 helm コマンドは、Github Releasesからダウンロードできます。

まず、Helmを使うためにKubernetesクラスタへTillerをインストールします。
Tiller用のService Account作成、Tillerのインストール、という手順で進めていきます。

$ kubectl apply -f install/kubernetes/helm/helm-service-account.yaml
$ helm init --service-account tiller

次に、Istioをインストールするnamespaceを作成します。

$ kubectl create namespace istio-system

最後に、 helm install コマンドでIstioをインストールします。

$ helm install install/kubernetes/helm/istio --name istio --namespace istio-system

ここまででIstioのインストールは完了ですが、default namespaceにデプロイしたサービスにSidecar Proxyを自動で配置してくれるようにlabelをつけておきます。

$ kubectl label namespace default istio-injection=enabled

Bookinfoアプリケーションのデプロイ

アプリケーションのデプロイ手順は、基本的にMinikubeと同様の手順で行います。

$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

アプリケーションのデプロイができたら、うまくできているかどうかを確認するため実際にアクセスしてみます。
Minikubeと違い、EKSの場合はロードバランサが使えるので、ロードバランサに対してHTTPリクエストを発行する形となります。

ロードバランサはIstio用のnamespace(今回であれば istio-system )にServiceリソースとして登録されているので、下記コマンドで確認することができます。

$ kubectl get service istio-ingressgateway --namespace istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP                               PORT(S)                                                                                                                   AGE
istio-ingressgateway   LoadBalancer   172.20.188.72   xxxxxx.ap-northeast-1.elb.amazonaws.com   80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:31283/TCP,8060:31547/TCP,853:30670/TCP,15030:32670/TCP,15031:30958/TCP   7m

istio-ingressgateway の EXTERNAL-IP に対してリクエストを送ることで、Gatewayリソースの設定に応じてサービスメッシュ内のアプリケーションにアクセスできます。
前回見たように、Bookinfoアプリケーション用に作成したGatewayリソースはあらゆるホスト名にマッチするような設定となっています。Bookinfoアプリケーションは /productpage というパスでアクセスできるので、 xxxxx.ap-northeast-1.elb.amazonaws.com/productpage というURLにアクセスすることでBookinfoアプリケーション画面が表示されます。

bookinfo-application-eks
Bookinfoアプリケーション

トラフィックルーティングの設定

IstioのTraffic Managementの機能の概要を理解するため、簡単な設定を入れてみたいと思います。
今回設定する内容は次のとおりです。

  • デフォルトでは、reviewsサービスは3つのバージョンの内v2にルーティングされる
  • roundrobinユーザでログインした場合、reviews-v1 10%, reviews-v2 30%, reviews-v3 60% にルーティングされる
    • v1: 星はなく、文字だけで表示される
    • v2: 黒い星が表示されるようになる
    • v3: 赤い星が表示されるようになる

まず、reviewsサービスのそれぞれのバージョンへのルーティング設定ができるようにするため、DestinationRuleリソースを作成します。下記のmanifestを適用すると、reviewsサービスのバージョンが3つ(v1、v2、v3)定義されます。

# define service subsets to use in VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: 'reviews.default.svc.cluster.local'
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2
    - name: v3
      labels:
        version: v3
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL

DestinationRuleリソースの spec.subsets で、サービスのバージョンを定義することができます。こういったバージョンのことを、Istioでは subset と呼びます。
例えば下の定義では、ホスト名が reviews.default.svc.cluster.local のPodの中で、 version=v1 というlabelを持つものが、reviewsサービスのv1として定義されます。

host: 'reviews.default.svc.cluster.local'
subsets:
  - name: v1
    labels:
      version: v1

ルーティングの設定を行う

トラフィックのルーティング設定は、VirtualServiceリソースを使って設定します。ルーティングの設定は、 spec.http 以下に記述していきます。
ルーティングの設定は複数記述でき、上から順番に評価されていきます。マッチするものが見つかればそこで評価は終了するので、記述する順番にも気をつける必要があります。

今回はroundrobinユーザでログインしたときのみ3バージョンへルーティングさせたいので、まずその設定を書いていきます。
Bookinfoアプリケーションでは、ユーザ名が end-user というヘッダに格納されるような仕様となっています。なので、このヘッダを確認してroundrobinとマッチしたときにのみ適用されるような設定を書くことになります。

roundrobinユーザ用の設定は次のようなものになります。 matchend-user ヘッダの値が意図するものかどうかを確認し、マッチした場合は route の設定が適用されます。 route 以下にある weight が、各バージョンへルーティングを振り分ける重みとなっており、すべてを足して100になるように設定する必要があります。

  http:
    - match:
        - headers:
            end-user:
              exact: roundrobin
      route:
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v1
          weight: 10
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v2
          weight: 30
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v3
          weight: 60

最後は、roundrobinユーザ以外に適用される設定を書いていきます。特にマッチさせる条件がない場合は、 match を省くことでこれまでの条件に当てはまらなかったすべてのリクエストに適用させることができます。
roundrobinユーザ以外はreviews-v2にルーティングしたいので、つぎのような設定となります。

    - route:
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v2

これらを合わせた最終的なVirtualServiceリソースの設定は次のようになります。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - 'reviews.default.svc.cluster.local'
  gateways:
    - mesh
  http: # この http 以下に、ルーティングの設定を定義
    # match の条件に合致する通信は、 route 以下の定義に従ってルーティングされる
    - match:
        - headers:
            end-user:
              exact: roundrobin
      route:
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v1  # この subset は、 DestinationRule で定義しているもの
          weight: 10
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v2
          weight: 30
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v3
          weight: 60

    # match がないので、ここに到達したすべての通信にマッチする
    - route:
        - destination:
            host: reviews.default.svc.cluster.local
            subset: v2

このVirtualServiceリソースを適用すると、roundrobinユーザでログインした場合は指定した重みで複数のバージョンにルーティングされ、その他のユーザの場合はv2にしかアクセスされないことが確認できるようになります。

reviews-v2
ログインしない場合(v2のみにアクセスされる)
reviews-v3
roundrobinユーザでログインした場合、他のバージョンも表示されるようになる

mTLSの有効化

HelmでインストールしたIstioは、特に何もオプションを与えない限り、mTLSが無効になっています。
ここでは、Istioのインストール後にmTLSを有効にし、アプリケーションを正常に動かすための設定について見ていきます。

サービスメッシュ全体でmTLSを有効化する

サービスメッシュ全体でmTLSを有効にするには、 MeshPolicy リソースを作成します。

apiVersion: authentication.istio.io/v1alpha1
kind: MeshPolicy
metadata:
  name: default
spec:
  peers:
    - mtls: {}

これを適用すると、mTLSなしでサービスへリクエストを投げた場合は 503 が返ってくるようになります。

なお、上のように MeshPolicy リソースを使用するとサービスメッシュ全体でmTLSが有効になりますが、特定のnamespaceだけで有効にしたい場合は変わりに Policy リソースを使用します。この場合、PolicyリソースがあるnamespaceのみでmTLSが有効となります。

例えば、default namespaceのみでmTLSを有効にするには次のようなmanifestを適用します。

apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
  name: "default"
  namespace: "default"
spec:
  peers:
  - mtls: {}

メッシュ内のサービス間通信でmTLSを有効化する

次に、サービスごとにDestinationRule内でmTLSを有効にするための設定を作成する必要があります。
しかし、サービスごとに一つ一つ作成するのは面倒なので、ワイルドカードを使ってdefault namespaceの全サービスに適用させることにします。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: default
  namespace: default
spec:
  host: "*.local"   # *.localにマッチするすべてのホスト(サービス)が対象となる
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL

trafficPolicy.tls.modeISTIO_MUTUAL を設定すると、Istioが証明書や秘密鍵を勝手に管理してくれるようになります。これで、 特にサービスごとにDestionationRuleリソースを作成していない場合は このDestinationRuleリソースがデフォルトとして適用され、mTLSが有効になります。例えば、Bookinfoアプリケーションでは3つの異なるバージョンreviewsにルーティングさせるため、DestinationRuleリソースをすでに作成しています。そのため、今回作成したDestinationRuleリソースはreviewsサービスには適用されず、reviewsへのリクエストはmTLSが使われずに失敗してしまいます。

reviewsのように個別でDestinationRuleリソースを作成しているサービスは、そのDestinationRuleリソースにも上と同様 trafficPolicy.tls.modeISTIO_MUTUAL に設定する必要があります。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: 'reviews.default.svc.cluster.local'
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2
    - name: v3
      labels:
        version: v3
  # このように個別でmTLSの設定が必要となる
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL

メッシュ外のサービスからメッシュ内のサービスに接続する

mTLSを有効にしたサービスメッシュでは、メッシュ外のサービスからメッシュ内のサービスへのリクエストは 失敗します
現状でこれを防ぐ手立てはなく、必要な部分だけmTLSをオフにする等、認証のレベルを下げる他に対応策はないようです。

メッシュ内のサービスからメッシュ外のサービスに接続する

mTLSを有効にしたサービスメッシュからメッシュ外のサービスに接続すると、デフォルトでは失敗します。
正常に接続を行うには、メッシュ外のサービスに対して個別にmTLSを無効にする設定が必要となります。

例として、kube-apiserverに接続する時に必要となる設定は次のようになります。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: "api-server"
spec:
  host: "kubernetes.default.svc.cluster.local"
  trafficPolicy:
    tls:
      mode: DISABLE

このように、 trafficPolicy.tls.modeDISABLE にすることで、mTLSを無効にすることができます。

まとめ

今回は、EKS上にIstioをインストールし、Bookinfoアプリケーションをデプロイしました。Bookinfoアプリケーションを利用して、簡単なトラフィックルーティングの設定やmTLSの設定等を試してみました。
Istioのサービスメッシュ上にデプロイしたアプリケーションはメッシュ外とのサービスにももちろんアクセスできますが、デフォルトのままだとうまくいきません。次回はそのあたりを実際に試していきたいと思います。