外部TCPサービスの利用

IstioのBookinfo例に基づいた簡単なシナリオについて説明します。

2018年2月6日 | Vadim Eisenberg

以前のブログ投稿外部Webサービスの利用では、HTTPS経由でメッシュ内のIstioアプリケーションが外部サービスを利用する方法について説明しました。この投稿では、TCP経由で外部サービスを利用する方法を示します。IstioのBookinfoサンプルアプリケーション(本の評価データがMySQLデータベースに保存されているバージョン)を使用します。このデータベースをクラスタの外部にデプロイし、ratingsマイクロサービスがそれを利用するように設定します。メッシュ内のアプリケーションが外部データベースにアクセスできるように、Service Entryを定義します。

外部評価データベースを使用したBookinfoサンプルアプリケーション

まず、本の評価データを保持するMySQLデータベースインスタンスをKubernetesクラスタの外部に設定します。次に、Bookinfoサンプルアプリケーションを修正して、このデータベースを使用します。

評価データ用のデータベースの設定

MySQLのインスタンスを設定します。任意のMySQLインスタンスを使用できます。私はCompose for MySQLを使用しました。MySQL ShellmysqlshをMySQLクライアントとして使用して、評価データを入力しました。

  1. MYSQL_DB_HOSTおよびMYSQL_DB_PORT環境変数を設定します

    $ export MYSQL_DB_HOST=<your MySQL database host>
    $ export MYSQL_DB_PORT=<your MySQL database port>
    

    デフォルトポートを使用するローカルMySQLデータベースの場合、値はそれぞれlocalhost3306です。

  2. データベースを初期化するには、以下のコマンドを実行し、プロンプトが表示されたらパスワードを入力します。このコマンドは、Compose for MySQLによってデフォルトで作成されたadminユーザーの資格情報を使用して実行されます。

    $ curl -s https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/src/mysql/mysqldb-init.sql | mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT
    

    または

    mysqlクライアントとローカルMySQLデータベースを使用する場合は、以下を実行します。

    $ curl -s https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/src/mysql/mysqldb-init.sql | mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT
    
  3. bookinfoというユーザーを作成し、test.ratingsテーブルに対するSELECT権限を付与します

    $ mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "CREATE USER 'bookinfo' IDENTIFIED BY '<password you choose>'; GRANT SELECT ON test.ratings to 'bookinfo';"
    

    または

    mysqlとローカルデータベースの場合、コマンドは次のとおりです。

    $ mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "CREATE USER 'bookinfo' IDENTIFIED BY '<password you choose>'; GRANT SELECT ON test.ratings to 'bookinfo';"
    

    ここでは最小権限の原則を適用します。これは、Bookinfoアプリケーションでadminユーザーを使用しないことを意味します。代わりに、最小限の権限を持つBookinfoアプリケーション専用のユーザーbookinfoを作成します。この場合、bookinfoユーザーは1つのテーブルに対してのみSELECT権限を持っています。

    ユーザー作成コマンドを実行した後、最後のコマンドの番号を確認してhistory -d <ユーザーを作成したコマンドの番号>を実行することで、bash履歴をクリアすることをお勧めします。新しく作成したユーザーのパスワードをbash履歴に保存したくありません。mysqlを使用している場合は、~/.mysql_historyファイルからも最後のコマンドを削除します。新しく作成されたユーザーのパスワード保護の詳細については、MySQLドキュメントを参照してください。

  4. 作成された評価を検査して、すべてが期待通りに機能していることを確認します

    $ mysqlsh --sql --ssl-mode=REQUIRED -u bookinfo -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "select * from test.ratings;"
    Enter password:
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      5 |
    |        2 |      4 |
    +----------+--------+
    

    または

    mysqlとローカルデータベースの場合

    $ mysql -u bookinfo -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "select * from test.ratings;"
    Enter password:
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      5 |
    |        2 |      4 |
    +----------+--------+
    
  5. データベースがBookinfoのratingsサービスで使用されていることを視覚的に確認するために、評価を一時的に1に設定します。

    $ mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "update test.ratings set rating=1; select * from test.ratings;"
    Enter password:
    
    Rows matched: 2  Changed: 2  Warnings: 0
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      1 |
    |        2 |      1 |
    +----------+--------+
    

    または

    mysqlとローカルデータベースの場合

    $ mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "update test.ratings set rating=1; select * from test.ratings;"
    Enter password:
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      1 |
    |        2 |      1 |
    +----------+--------+
    

    bookinfoユーザーにはtest.ratingsテーブルに対するUPDATE権限がないため、最後のコマンドではadminユーザー(およびローカルデータベースの場合はroot)を使用しました。

これで、データベースを使用するBookinfoアプリケーションのバージョンをデプロイする準備ができました。

Bookinfoアプリケーションの初期設定

外部データベースを使用するシナリオを示すために、IstioがインストールされているKubernetesクラスタから始めます。次に、Istio Bookinfoサンプルアプリケーションをデプロイし、デフォルトの宛先ルールを適用しIstioをデフォルトでイグレスをブロックするポリシーに変更します

このアプリケーションは、ratingsマイクロサービスを使用して本の評価(1から5の数値)を取得します。評価は、レビューごとに星として表示されます。ratingsマイクロサービスにはいくつかのバージョンがあります。一部はMongoDBを、他はMySQLをデータベースとして使用しています。

このブログ投稿の例のコマンドは、相互TLSが有効になっているかどうかに関係なく、Istio 0.8以降で動作します。

念のため、Bookinfoサンプルアプリケーションのアプリケーションのエンドツーエンドアーキテクチャを次に示します。

The original Bookinfo application
元のBookinfoアプリケーション

Bookinfoアプリケーションで評価データにデータベースを使用する

  1. MySQLデータベースを使用するratingsマイクロサービスのバージョン(Istioリリースアーカイブのsamples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yamlにある)のデプロイメント仕様を修正して、データベースインスタンスを使用するようにします。以下の行を編集します。

    - name: MYSQL_DB_HOST
      value: mysqldb
    - name: MYSQL_DB_PORT
      value: "3306"
    - name: MYSQL_DB_USER
      value: root
    - name: MYSQL_DB_PASSWORD
      value: password
    

    上記の断片の値を置き換え、データベースホスト、ポート、ユーザー、パスワードを指定します。Kubernetesでコンテナの環境変数でパスワードを扱う正しい方法は、シークレットを使用することです。この例的任务では、パスワードをデプロイメント仕様に直接記述しても構いません。しかし、本番環境では決して行わないでください! また、誰もが"password"をパスワードとして使用すべきではないことを認識しているものと仮定します…

  2. 修正した仕様を適用して、データベースを使用するratingsマイクロサービスのバージョンv2-mysqlをデプロイします。

    圧縮
    $ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml@
    deployment "ratings-v2-mysql" created
    
  3. reviewsサービス宛てのすべてのトラフィックをそのv3バージョンにルーティングします。これにより、reviewsサービスが常にratingsサービスを呼び出すことが保証されます。さらに、ratingsサービス宛てのすべてのトラフィックを、データベースを使用するratings v2-mysqlにルーティングします。

    2つの仮想サービスを追加することで、上記の両方のサービスのルーティングを指定します。これらの仮想サービスは、Istioリリースアーカイブのsamples/bookinfo/networking/virtual-service-ratings-mysql.yamlで指定されています。重要:以下のコマンドを実行する前に、デフォルトの宛先ルールを適用したことを確認してください。

    圧縮
    $ kubectl apply -f @samples/bookinfo/networking/virtual-service-ratings-mysql.yaml@
    

更新されたアーキテクチャを以下に示します。メッシュ内の青い矢印は、追加した仮想サービスに従って設定されたトラフィックを示しています。仮想サービスに従って、トラフィックはreviews v3ratings v2-mysqlに送信されます。

The Bookinfo application with ratings v2-mysql and an external MySQL database
ratings v2-mysqlと外部MySQLデータベースを使用したBookinfoアプリケーション

MySQLデータベースはIstioサービスメッシュの外、より正確にはKubernetesクラスタの外にあることに注意してください。サービスメッシュの境界は破線で示されています。

ウェブページへのアクセス

イングレスのIPアドレスとポートを決定した後、アプリケーションのウェブページにアクセスします。

問題が発生しました… 評価の星印の代わりに、「評価サービスは現在利用できません」というメッセージが各レビューの下に表示されます。

The Ratings service error messages
評価サービスのエラーメッセージ

外部Webサービスの利用と同様に、グレースフルサービス劣化が発生しています。これは良いことです。ratingsマイクロサービスのエラーのためにアプリケーションがクラッシュしませんでした。アプリケーションのウェブページには、本の情報、詳細、レビューが正しく表示されましたが、評価の星印はありませんでした。

外部Webサービスの利用と同じ問題、つまりKubernetesクラスタ外部のすべてのトラフィック(TCPとHTTPの両方)が、デフォルトでサイドカープロキシによってブロックされています。TCPに対してそのようなトラフィックを有効にするには、TCPのメッシュ外部サービスエントリを定義する必要があります。

外部MySQLインスタンスのメッシュ外部サービスエントリ

TCPメッシュ外部サービスエントリが役立ちます。

  1. MySQLデータベースインスタンスのIPアドレスを取得します。オプションとして、hostコマンドを使用できます。

    $ export MYSQL_DB_IP=$(host $MYSQL_DB_HOST | grep " has address " | cut -d" " -f4)
    

    ローカルデータベースの場合、クラスタからアクセス可能なマシンのIPアドレスを含むようにMYSQL_DB_IPを設定します。

  2. TCPメッシュ外部サービスエントリを定義します

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: mysql-external
    spec:
      hosts:
      - $MYSQL_DB_HOST
      addresses:
      - $MYSQL_DB_IP/32
      ports:
      - name: tcp
        number: $MYSQL_DB_PORT
        protocol: tcp
      location: MESH_EXTERNAL
    EOF
    
  3. 作成したサービスエントリを確認し、正しい値が含まれていることを確認します

    $ kubectl get serviceentry mysql-external -o yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
    ...
    

TCPサービスエントリの場合、エントリのポートのプロトコルとしてtcpを指定することに注意してください。また、外部サービスのIPアドレスをアドレスのリストに、サフィックス32を持つCIDRブロックとして指定する必要があることにも注意してください。

TCPサービスエントリについては、後述します。現時点では、追加したサービスエントリによって問題が解決されたことを確認します。ウェブページにアクセスして、星印が表示されるかどうかを確認します。

機能しました!アプリケーションのウェブページにアクセスすると、エラーなしで評価が表示されます

Book Ratings Displayed Correctly
正しく表示された本の評価

期待通りに、表示された両方のレビューに1つの星評価が表示されていることに注意してください。外部データベースが実際に使用されていることを視覚的に確認するために、評価を1つ星に変更しました。

HTTP/HTTPSのサービスエントリと同様に、kubectlを使用してTCPのサービスエントリを動的に削除および作成できます。

イグレスTCPトラフィック制御の動機

メッシュ内のIstioアプリケーションの中には、レガシーシステムなど、外部サービスにアクセスする必要があるものがあります。多くの場合、アクセスはHTTPまたはHTTPSプロトコル経由で行われません。他のTCPプロトコル(MongoDBワイヤプロトコルMySQLクライアント/サーバープロトコルなど、外部データベースと通信するためのデータベース固有のプロトコル)が使用されます。

次に、TCPトラフィックのサービスエントリについて詳しく説明します。

TCPトラフィックのサービスエントリ

特定のポートへのTCPトラフィックを有効にするサービスエントリでは、ポートのプロトコルとしてTCPを指定する必要があります。さらに、MongoDBワイヤプロトコルの場合、プロトコルはTCPではなくMONGOとして指定できます。

エントリのaddressesフィールドには、CIDR表記のIPブロックを使用する必要があります。TCPサービスエントリではhostsフィールドは無視されることに注意してください。

ホスト名で外部サービスへのTCPトラフィックを有効にするには、ホスト名のすべてのIPを指定する必要があります。各IPはCIDRブロックで指定する必要があります。

外部サービスのすべてのIPが常にわかっているわけではないことに注意してください。送信用TCPトラフィックを有効にするには、アプリケーションで使用されているIPのみを指定する必要があります。

CDNの場合など、外部サービスのIPが常に静的であるとは限りません。IPはほとんどの場合静的ですが、インフラストラクチャの変更などにより、時折変更される場合があります。このような場合、可能なIPの範囲がわかっている場合は、CIDRブロックで範囲を指定する必要があります。可能なIPの範囲が不明な場合は、TCPのサービスエントリを使用できず、外部サービスを直接呼び出す必要があります(サイドカープロキシをバイパスします)。

仮想マシンサポートとの関係

この投稿で説明されているシナリオは、仮想マシンを使用したBookinfoの例とは異なることに注意してください。そのシナリオでは、MySQLインスタンスは外部(クラスタ外)のマシン(ベアメタルまたはVM)で実行され、Istioサービスメッシュと統合されています。MySQLサービスはメッシュのファーストクラスシチズンとなり、Istioのすべての利点を持つ機能が適用されます。とりわけ、サービスはローカルクラスタドメイン名(例:mysqldb.vm.svc.cluster.local)でアドレス指定できるようになり、相互TLS認証によって通信を保護できます。このサービスにアクセスするためにサービスエントリを作成する必要はありませんが、サービスをIstioに登録する必要があります。このような統合を有効にするには、Istioコンポーネント(Envoyプロキシnode-agent_istio-agent_)をマシンにインストールし、Istioコントロールプレーン(PilotMixerCitadel)からアクセス可能にする必要があります。詳細については、Istio VM関連のタスクを参照してください。

私たちの場合、MySQLインスタンスは任意のマシンで実行したり、クラウドプロバイダーによってサービスとしてプロビジョニングしたりできます。マシンをIstioと統合する必要はありません。Istioコントロールプレーンはマシンからアクセス可能である必要はありません。サービスとしてのMySQLの場合、MySQLが実行されているマシンはアクセスできない可能性があり、必要なコンポーネントをインストールできない可能性があります。私たちの場合、MySQLインスタンスはグローバルドメイン名でアドレス指定できます。これは、使用しているアプリケーションがそのドメイン名を使用することを期待している場合に役立ちます。これは、期待されるドメイン名を消費アプリケーションの展開構成で変更できない場合に特に重要です。

クリーンアップ

  1. testデータベースとbookinfoユーザーを削除します。

    $ mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "drop database test; drop user bookinfo;"
    

    または

    mysqlとローカルデータベースの場合

    $ mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "drop database test; drop user bookinfo;"
    
  2. 仮想サービスを削除します。

    圧縮
    $ kubectl delete -f @samples/bookinfo/networking/virtual-service-ratings-mysql.yaml@
    Deleted config: virtual-service/default/reviews
    Deleted config: virtual-service/default/ratings
    
  3. ratings v2-mysqlをアンデプロイします。

    圧縮
    $ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml@
    deployment "ratings-v2-mysql" deleted
    
  4. サービスエントリを削除します。

    $ kubectl delete serviceentry mysql-external -n default
    Deleted config: serviceentry mysql-external
    

結論

このブログ投稿では、Istioサービスメッシュ内のマイクロサービスがTCP経由で外部サービスを消費する方法を示しました。デフォルトでは、Istioはクラスタ外のホストへのすべてのトラフィック(TCPとHTTP)をブロックします。TCPに対してそのようなトラフィックを有効にするには、サービスメッシュに対してTCPメッシュ外部サービスエントリを作成する必要があります。

この投稿を共有する