外部TCPサービスの利用
IstioのBookinfo例に基づいた簡単なシナリオについて説明します。
以前のブログ投稿外部Webサービスの利用では、HTTPS経由でメッシュ内のIstioアプリケーションが外部サービスを利用する方法について説明しました。この投稿では、TCP経由で外部サービスを利用する方法を示します。IstioのBookinfoサンプルアプリケーション(本の評価データがMySQLデータベースに保存されているバージョン)を使用します。このデータベースをクラスタの外部にデプロイし、ratingsマイクロサービスがそれを利用するように設定します。メッシュ内のアプリケーションが外部データベースにアクセスできるように、Service Entryを定義します。
外部評価データベースを使用したBookinfoサンプルアプリケーション
まず、本の評価データを保持するMySQLデータベースインスタンスをKubernetesクラスタの外部に設定します。次に、Bookinfoサンプルアプリケーションを修正して、このデータベースを使用します。
評価データ用のデータベースの設定
MySQLのインスタンスを設定します。任意のMySQLインスタンスを使用できます。私はCompose for MySQLを使用しました。MySQL Shellのmysqlsh
をMySQLクライアントとして使用して、評価データを入力しました。
MYSQL_DB_HOST
およびMYSQL_DB_PORT
環境変数を設定します$ export MYSQL_DB_HOST=<your MySQL database host> $ export MYSQL_DB_PORT=<your MySQL database port>
デフォルトポートを使用するローカルMySQLデータベースの場合、値はそれぞれ
localhost
と3306
です。データベースを初期化するには、以下のコマンドを実行し、プロンプトが表示されたらパスワードを入力します。このコマンドは、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
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ドキュメントを参照してください。作成された評価を検査して、すべてが期待通りに機能していることを確認します
$ 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 | +----------+--------+
データベースが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サンプルアプリケーションのアプリケーションのエンドツーエンドアーキテクチャを次に示します。
Bookinfoアプリケーションで評価データにデータベースを使用する
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"
をパスワードとして使用すべきではないことを認識しているものと仮定します…修正した仕様を適用して、データベースを使用するratingsマイクロサービスのバージョンv2-mysqlをデプロイします。
$ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml@ deployment "ratings-v2-mysql" created
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 v3とratings v2-mysqlに送信されます。
MySQLデータベースはIstioサービスメッシュの外、より正確にはKubernetesクラスタの外にあることに注意してください。サービスメッシュの境界は破線で示されています。
ウェブページへのアクセス
イングレスのIPアドレスとポートを決定した後、アプリケーションのウェブページにアクセスします。
問題が発生しました… 評価の星印の代わりに、「評価サービスは現在利用できません」というメッセージが各レビューの下に表示されます。
外部Webサービスの利用と同様に、グレースフルサービス劣化が発生しています。これは良いことです。ratingsマイクロサービスのエラーのためにアプリケーションがクラッシュしませんでした。アプリケーションのウェブページには、本の情報、詳細、レビューが正しく表示されましたが、評価の星印はありませんでした。
外部Webサービスの利用と同じ問題、つまりKubernetesクラスタ外部のすべてのトラフィック(TCPとHTTPの両方)が、デフォルトでサイドカープロキシによってブロックされています。TCPに対してそのようなトラフィックを有効にするには、TCPのメッシュ外部サービスエントリを定義する必要があります。
外部MySQLインスタンスのメッシュ外部サービスエントリ
TCPメッシュ外部サービスエントリが役立ちます。
MySQLデータベースインスタンスのIPアドレスを取得します。オプションとして、hostコマンドを使用できます。
$ export MYSQL_DB_IP=$(host $MYSQL_DB_HOST | grep " has address " | cut -d" " -f4)
ローカルデータベースの場合、クラスタからアクセス可能なマシンのIPアドレスを含むように
MYSQL_DB_IP
を設定します。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
作成したサービスエントリを確認し、正しい値が含まれていることを確認します
$ kubectl get serviceentry mysql-external -o yaml apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: ...
TCPサービスエントリの場合、エントリのポートのプロトコルとしてtcp
を指定することに注意してください。また、外部サービスのIPアドレスをアドレスのリストに、サフィックス32
を持つCIDRブロックとして指定する必要があることにも注意してください。
TCPサービスエントリについては、後述します。現時点では、追加したサービスエントリによって問題が解決されたことを確認します。ウェブページにアクセスして、星印が表示されるかどうかを確認します。
機能しました!アプリケーションのウェブページにアクセスすると、エラーなしで評価が表示されます
期待通りに、表示された両方のレビューに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コントロールプレーン(Pilot、Mixer、Citadel)からアクセス可能にする必要があります。詳細については、Istio VM関連のタスクを参照してください。
私たちの場合、MySQLインスタンスは任意のマシンで実行したり、クラウドプロバイダーによってサービスとしてプロビジョニングしたりできます。マシンをIstioと統合する必要はありません。Istioコントロールプレーンはマシンからアクセス可能である必要はありません。サービスとしてのMySQLの場合、MySQLが実行されているマシンはアクセスできない可能性があり、必要なコンポーネントをインストールできない可能性があります。私たちの場合、MySQLインスタンスはグローバルドメイン名でアドレス指定できます。これは、使用しているアプリケーションがそのドメイン名を使用することを期待している場合に役立ちます。これは、期待されるドメイン名を消費アプリケーションの展開構成で変更できない場合に特に重要です。
クリーンアップ
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;"
仮想サービスを削除します。
$ kubectl delete -f @samples/bookinfo/networking/virtual-service-ratings-mysql.yaml@ Deleted config: virtual-service/default/reviews Deleted config: virtual-service/default/ratings
ratings v2-mysqlをアンデプロイします。
$ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml@ deployment "ratings-v2-mysql" deleted
サービスエントリを削除します。
$ kubectl delete serviceentry mysql-external -n default Deleted config: serviceentry mysql-external
結論
このブログ投稿では、Istioサービスメッシュ内のマイクロサービスがTCP経由で外部サービスを消費する方法を示しました。デフォルトでは、Istioはクラスタ外のホストへのすべてのトラフィック(TCPとHTTP)をブロックします。TCPに対してそのようなトラフィックを有効にするには、サービスメッシュに対してTCPメッシュ外部サービスエントリを作成する必要があります。