ブログ | NGINX

NGINX チュートリアル: マイクロサービスのデプロイと構成方法

ハビエル・エヴァンス サムネイル
ハビエル・エヴァンス
2023年3月7日公開

この投稿は、 2023 年 3 月の Microservices の概念を実践するのに役立つ 4 つのチュートリアルの 1 つです。 マイクロサービスの提供を開始する:

 

すべてのアプリには構成が必要ですが、マイクロサービスを構成する際の考慮事項は、モノリシック アプリの場合と同じではない可能性があります。両方のタイプのアプリに適用できるガイダンスについては、 12 要素アプリ要素 3 (環境に構成を保存する) を参照できますが、そのガイダンスはマイクロサービス アプリに適用できます。 特に、サービス構成を定義する方法、サービスに構成を提供する方法、およびサービスに依存する可能性のある他のサービスの構成値としてサービスを利用できるようにする方法を適応させることができます。

マイクロサービスに Factor 3 を適応させる方法 (具体的には、構成ファイル、データベース、サービス検出のベスト プラクティス) の概念を理解するには、弊社のブログの「マイクロサービス アプリを構成するためのベスト プラクティス」をお読みください。 この投稿は、その知識を実践するための素晴らしい方法です。

注記: このチュートリアルの目的は、マイクロサービスを本番環境にデプロイする正しい方法を示すことではなく、いくつかのコア概念を説明することです。 実際の「マイクロサービス」アーキテクチャを使用していますが、いくつか重要な注意点があります。

  • このチュートリアルでは、Kubernetes や Nomad などのコンテナ オーケストレーション フレームワークは使用しません。 これは、特定のフレームワークの詳細にこだわることなく、マイクロサービスの概念を学習できるようにするためです。 ここで紹介するパターンは、これらのフレームワークのいずれかを実行しているシステムに移植可能です。
  • サービスは、ソフトウェア エンジニアリングの厳密さよりも理解しやすさを重視して最適化されています。 重要なのは、コードの詳細ではなく、システムにおけるサービスの役割と通信パターンに注目することです。 詳細については、個々のサービスのREADMEファイルを参照してください。

チュートリアルの概要

このチュートリアルでは、Factor 3 の概念がマイクロサービス アプリにどのように適用されるかを説明します。 4 つのチャレンジでは、一般的なマイクロサービス構成パターンをいくつか検討し、それらのパターンを使用してサービスをデプロイおよび構成します。

  • チャレンジ 1チャレンジ 2では、マイクロサービス アプリの構成をどこに配置するかに関する最初のパターンを探ります。一般的な場所は 3 つあります。

    • アプリケーションコード
    • アプリケーションのデプロイメントスクリプト
    • デプロイメント スクリプトによってアクセスされる外部ソース
  • チャレンジ 3では、リバース プロキシとして NGINX を介してアプリを外部に公開し、Consul を使用してサービス検出を有効にするという、さらに 2 つのパターンを設定します。
  • チャレンジ 4では、最後のパターンを実装します。つまり、マイクロサービスのインスタンスを「ジョブ ランナー」として使用し、通常の機能とは異なる 1 回限りのアクション (この場合はデータベース移行のエミュレーション) を実行します。

このチュートリアルでは、次の 4 つのテクノロジを使用します。

  • メッセンジャー– このチュートリアル用に作成された、メッセージ保存機能を備えたシンプルなチャット API
  • NGINX オープンソースメッセンジャーサービスとシステム全体へのエントリ ポイント
  • Consul – 動的なサービスレジストリとキーバリューストア
  • RabbitMQ – サービスが非同期通信できるようにする人気のオープンソースメッセージブローカー

コンテナ内で NGINX オープンソースと Consul テンプレートが一緒に実行されていることを示すトポロジー図。 Consult テンプレートは Consult クライアントと通信します。 NGINX Open Source は、messenger_db にデータを保存し、Rabbit MQ と通信するメッセンジャー サービスのリバース プロキシです。

チュートリアルの概要については、このビデオをご覧ください。 手順はこの投稿と完全に一致しているわけではありませんが、概念を理解するのに役立ちます。

前提条件とセットアップ

前提条件

独自の環境でチュートリアルを完了するには、次のものが必要です。

  • Linux/Unix互換環境
  • Linux コマンドライン、JavaScript、 bashの基本的な知識 (ただし、すべてのコードとコマンドが提供され、説明されているため、限られた知識でも成功できます)
  • DockerDocker Compose
  • Node.js 19.x 以降

    • バージョン 19.x をテストしましたが、Node.js の新しいバージョンでも動作すると予想されます。
    • Node.js のインストールの詳細については、メッセンジャーサービス リポジトリのREADMEを参照してください。 コンテナで使用されるものとまったく同じ Node.js バージョンを取得するためにasdfをインストールすることもできます。
  • curl (ほとんどのシステムに既にインストールされています)
  • チュートリアルの概要に記載されている 4 つのテクノロジー: messenger (次のセクションでダウンロードします)、 NGINX Open SourceConsul 、およびRabbitMQ

設定

  1. ターミナル セッションを開始します (以降の手順では、これをアプリ ターミナルと呼びます)。
  2. ホーム ディレクトリにmicroservices-marchディレクトリを作成し、このチュートリアルの GitHub リポジトリをそこにクローンします。 (別のディレクトリ名を使用して、それに応じて手順を変更することもできます。)

    注記: チュートリアル全体を通して、コマンドをターミナルにコピーして貼り付けやすくするために、Linux コマンドラインのプロンプトは省略されています。 チルダ ( ~ ) はホームディレクトリを表します。

    mkdir ~/microservices-marchcd ~/microservices-march
    git clone https://github.com/microservices-march/platform.git --branch mm23-twelve-factor-start
    git clone https://github.com/microservices-march/messenger.git --branch mm23-twelve-factor-start
    
  3. プラットフォームリポジトリに変更し、Docker Compose を起動します。

    cd platformdocker compose up -d --build
    

    これにより、後続のチャレンジで使用される RabbitMQ と Consul の両方が起動します。

    • -dフラグは、コンテナが起動したら Docker Compose にコンテナからデタッチするように指示します (そうでない場合、コンテナはターミナルに接続されたままになります)。
    • --buildフラグは、起動時にすべてのイメージを再構築するように Docker Compose に指示します。 これにより、実行中のイメージは、ファイルへの潜在的な変更を通じて最新の状態に保たれます。
  4. メッセンジャーリポジトリに変更し、Docker Compose を起動します。

    cd ../messengerdocker compose up -d --build
    

    これにより、メッセンジャーサービス用の PostgreSQL データベースが起動します。このチュートリアルの残りの部分では、これをmessenger-databaseと呼びます。

課題1: アプリケーションレベルのマイクロサービス構成を定義する

このチャレンジでは、チュートリアルで取り上げる 3 つの場所のうち最初の場所であるアプリケーション レベルで構成を設定します。 (課題 2 では、 2 番目と 3 番目の場所、デプロイメント スクリプト、および外部ソースについて説明します。)

Twelve-Factor App では、アプリケーション レベルの構成は明確に除外されます。これは、このような構成は、異なるデプロイメント環境 (Twelve-Factor App ではdeploysと呼びます) 間で変更する必要がないためです。 ただし、完全性を期すために 3 つのタイプすべてを取り上げます。サービスを開発、構築、展開する際に各カテゴリを処理する方法は異なります。

メッセンジャーサービスは Node.js で記述されており、エントリポイントはメッセンジャーリポジトリのapp/index.mjsにあります。 ファイルのこの行:

app.use(express.json());

アプリケーションレベルの構成の例です。 Express フレームワークを構成して、 application/jsonタイプのリクエスト本体を JavaScript オブジェクトに逆シリアル化します。

このロジックはアプリケーション コードと密接に結合されており、Twelve-Factor App が「構成」とみなすものではありません。 しかし、ソフトウェアではすべては状況次第ですよね?

次の 2 つのセクションでは、この行を変更して、アプリケーション レベルの構成の 2 つの例を実装します。

例1

この例では、メッセンジャーサービスが受け入れるリクエスト本文の最大サイズを設定します。 このサイズ制限は、Express API ドキュメントで説明されているように、 express.json関数のlimit引数によって設定されます。 ここでは、上で説明した Express フレームワークの JSON ミドルウェアの構成に制限引数を追加します。

  1. 好みのテキスト エディターでapp/index.mjsを開き、次のコードを置き換えます。

    app.use(express.json())
    

    と:

    app.use(express.json({ limit: "20b" }));
    
  2. アプリ ターミナル (セットアップで使用したターミナル) で、アプリディレクトリに移動し、メッセンジャーサービスを開始します。

    cd app
    npm install
    node index.mjs
    messenger_service listening on port 4000
    
  3. 2 番目の別のターミナル セッション (後続の命令ではクライアント ターミナルと呼ばれます) を開始し、メッセンジャーサービスにPOST要求を送信します。 エラー メッセージは、リクエスト本文が手順 1 で設定された20 バイトの制限を下回っていたためリクエストが正常に処理されたが、JSON ペイロードの内容が正しくないことを示しています。

    curl -d '{ "text": "hello" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
    { "error": "Conversation must have 2 unique users" }
    
  4. 少し長めのメッセージ本文を送信します (これもクライアント端末で)。 出力はステップ 3 よりもはるかに多く、今回はリクエスト本文が 20 バイトを超えていることを示すエラー メッセージも含まれます。

    curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations...
    \”PayloadTooLargeError: request entity too large"
    

例2

この例では、単一のファイルで構成「スキーマ」全体を定義できるライブラリであるconvictを使用します。 また、12 要素アプリの要素 3 からの 2 つのガイドラインも示しています。

  • 環境変数に設定を保存する– アプリ コードにハードコードされるのではなく、環境変数 ( JSON_BODY_LIMIT ) を使用して最大本文サイズが設定されるようにアプリを変更します。
  • サービス構成を明確に定義します。これは、要因 3 をマイクロサービスに適用したものです。 この概念に馴染みがない場合は、弊社のブログの「マイクロサービス アプリを構成するためのベスト プラクティス」をぜひお読みください。

この例では、チャレンジ 2で活用する「配管」も設定します。チャレンジ 2 で作成するメッセンジャーデプロイメント スクリプトは、デプロイメント スクリプトで指定された構成の例として、ここでアプリ コードに挿入するJSON_BODY_LIMIT環境変数を設定します。

  1. convict構成ファイルapp/config/config.mjsを開き、 amqpportキーの後に次のキーを新しいキーとして追加します。

    jsonBodyLimit: {  doc: `The max size (with unit included) that will be parsed by the
            JSON middleware. Unit parsing is done by the
            https://www.npmjs.com/package/bytes library.
            ex: "100kb"`,
      format: String,
      default: null,
      env: "JSON_BODY_LIMIT",
    },
    

    以下の手順 3 でコマンド ラインで最大本文サイズを設定するためにJSON_BODY_LIMIT環境変数を使用すると、 convictライブラリによってその環境変数の解析が行われます。

    • 正しい環境変数から値を取得します
    • 変数の型をチェックします(文字列
    • アプリケーション内でjsonBodyLimitキーを使用してアクセスできるようにします
  2. app/index.mjsで以下を置き換えます:

    app.use(express.json({ limit: "20b" }));
    

    app.use(express.json({ limit: config.get("jsonBodyLimit") }));
    
  3. アプリターミナル (例 1の手順 2 でメッセンジャーサービスを開始した場所) で、 Ctrl + c を押してサービスを停止します。 次に、 JSON_BODY_LIMIT環境変数を使用して最大本文サイズを 27 バイトに設定し、再度起動します。

    ^cJSON_BODY_LIMIT=27b node index.mjs
    

    これは、ユースケースに適した場合に構成方法を変更する例です。つまり、Twelve-Factor App で推奨されているように、アプリ コードに値 (この場合はサイズ制限) をハードコーディングするのではなく、環境変数を使用して設定するように切り替えました。

    前述のように、チャレンジ 2では、コマンド ラインで設定するのではなく、メッセンジャーサービスのデプロイメント スクリプトを使用して環境変数を設定する場合、 JSON_BODY_LIMIT環境変数の使用が構成の 2 番目の場所の例になります。

  4. クライアント端末で、例 1手順 4curlコマンドを繰り返します (リクエスト本文を大きくします)。 サイズ制限を 27 バイトに増やしたため、リクエスト本文は制限を超えなくなり、リクエストは処理されたが JSON ペイロードの内容が正しくないことを示すエラー メッセージが表示されます。

    curl -d '{ "text": "hello, world" }' -H "Content-Type: application/json" -X POST http://localhost:4000/conversations{ "error": "Conversation must have 2 unique users" }
    

    必要に応じてクライアント端末を閉じることができます。 チュートリアルの残りの部分では、すべてのコマンドをアプリのターミナルで発行します。

  5. アプリ ターミナルで、 Ctrl + c を押してメッセンジャーサービスを停止します (上記の手順 3 でこのターミナルでサービスを停止して再起動しました)。

    ^c
    
  6. メッセンジャーデータベースを停止します。 プラットフォームリポジトリで定義されたインフラストラクチャ要素によってネットワークがまだ使用されているため、表示されるエラー メッセージは無視しても問題ありません。 メッセンジャーリポジトリのルートでこのコマンドを実行します。

    docker compose down
    ...failed to remove network mm_2023....
    

チャレンジ2: サービスのデプロイメント スクリプトを作成する

構成はコードから厳密に分離する必要があります (そうしないと、デプロイ間でどのように変化するのでしょうか)
– 12要素アプリの要素3より

一見すると、これは「ソース管理に構成をチェックインしない」と言っているように解釈されるかもしれません。 このチャレンジでは、このルールに違反しているように見えるかもしれませんが、実際にはルールを尊重しながら、マイクロサービス環境にとって重要な貴重なプロセス改善を提供する、マイクロサービス環境の共通パターンを実装します。

このチャレンジでは、マイクロサービスに構成を提供するインフラストラクチャ アズ コードとデプロイメント マニフェストの機能を模倣するデプロイメント スクリプトを作成し、外部の構成ソースを使用するようにスクリプトを変更しシークレットを設定してから、スクリプトを実行してサービスとそのインフラストラクチャをデプロイします。

メッセンジャーリポジトリに新しく作成されたインフラストラクチャディレクトリにデプロイメント スクリプトを作成します。 インフラストラクチャ(またはその名前のバリエーション) と呼ばれるディレクトリは、最新のマイクロサービス アーキテクチャで一般的なパターンであり、次のようなものを保存するために使用されます。

このパターンの利点は次のとおりです。

  • サービスの展開とサービス固有のインフラストラクチャ (データベースなど) の展開の所有権を、サービスを所有するチームに割り当てます。
  • チームは、これらの要素に対する変更が開発プロセス (コードレビュー、CI など) を通過することを保証できます。
  • チームは、外部のチームに作業を依頼することなく、サービスとそのサポート インフラストラクチャの展開方法を簡単に変更できます。

前述したように、このチュートリアルの目的は実際のシステムの設定方法を示すことではなく、このチャレンジで展開するスクリプトは実際の運用システムに似たものではありません。 むしろ、マイクロサービス関連のインフラストラクチャのデプロイメントを扱う際に、ツール固有の構成によって解決されるいくつかのコア概念と問題を示し、スクリプトを可能な限り最小限の特定のツールに抽象化します。

初期展開スクリプトを作成する

  1. アプリ ターミナルで、メッセンジャーリポジトリのルートにインフラストラクチャディレクトリを作成し、メッセンジャーサービスとmessenger-databaseのデプロイメント スクリプトを格納するファイルを作成します。 環境によっては、 chmodコマンドの前にsudoを付ける必要がある場合があります。

    mkdir infrastructurecd infrastructure
    touch messenger-deploy.sh
    chmod +x messenger-deploy.sh
    touch messenger-db-deploy.sh
    chmod +x messenger-db-deploy.sh
    
  2. 好みのテキスト エディターで、 messenger-deploy.shを開き、次のコードを追加して、メッセンジャーサービスの初期デプロイメント スクリプトを作成します。

    #!/bin/bashset -e
    
    JSON_BODY_LIMIT=20b
    
    docker run \
      --rm \
      -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
      messenger
    

このスクリプトは現時点では完成していませんが、いくつかの概念を示しています。

  • デプロイメント スクリプトにその構成を直接含めることで、環境変数に値を割り当てます。
  • docker runコマンドの-eフラグを使用して、実行時にコンテナに環境変数を挿入します。

この方法で環境変数の値を設定するのは冗長に思えるかもしれませんが、このデプロイメント スクリプトがどれほど複雑になっても、スクリプトの一番上をざっと見て、構成データがどのようにデプロイメントに提供されているかを理解できることを意味します。

さらに、実際のデプロイメント スクリプトではdocker runコマンドが明示的に呼び出されない場合もありますが、このサンプル スクリプトは、Kubernetes マニフェストのようなもので解決されるコアの問題を伝えることを目的としています。 Kubernetes のようなコンテナ オーケストレーション システムを使用する場合、デプロイメントによってコンテナが起動され、Kubernetes 構成ファイルから派生したアプリケーション構成がそのコンテナで使用できるようになります。 したがって、このサンプル デプロイメント ファイルは、Kubernetes マニフェストなどのフレームワーク固有のデプロイメント ファイルと同じ役割を果たすデプロイメント スクリプトの最小バージョンと考えることができます。

実際の開発環境では、このファイルをソース管理にチェックインし、コードレビューを行う場合があります。 これにより、チームの他のメンバーに設定についてコメントする機会が与えられ、誤った値の設定によって予期しない動作が発生するインシデントを回避するのに役立ちます。 たとえば、このスクリーンショットでは、チーム メンバーが、受信 JSON リクエスト本文の 20 バイトの制限 ( JSON_BODY_LIMITで設定) は低すぎることを正しく指摘しています。

メッセージ本文の制限が 20 バイトでは小さすぎるというコードレビューのスクリーンショット

外部ソースから構成値を照会するためのデプロイメント スクリプトの変更

チャレンジのこの部分では、マイクロサービスの構成の 3 番目の場所、つまり、デプロイ時にクエリされる外部ソースを設定します。 値を動的に登録し、デプロイメント時に外部ソースから取得することは、値をハードコーディングするよりもはるかに優れた方法です。ハードコーディングすると、常に更新する必要があり、障害が発生することがあります。 詳細については、弊社のブログの「マイクロサービス アプリを構成するためのベスト プラクティス」をご覧ください。

この時点で、メッセンジャーサービスに必要な補助サービスを提供するために、2 つのインフラストラクチャ コンポーネントがバックグラウンドで実行されています。

  1. 実際の展開ではプラットフォーム チームが所有する RabbitMQ (セットアップのステップ 3 で開始)
  2. 実際の展開でチームが所有するメッセンジャー データベース(セットアップの手順 4 で開始)

app/config/config.mjsメッセンジャーサービスのconvictスキーマは、これらの外部構成に対応する必要な環境変数を定義します。 このセクションでは、変数の値を一般にアクセス可能な場所に設定して構成を提供するこれら 2 つのコンポーネントを設定し、メッセンジャーサービスがデプロイ時に変数を照会できるようにします。

RabbitMQ とメッセンジャー データベースに必要な接続情報は、デプロイされたすべてのサービスがアクセスできる共通の場所であるConsul キー/値 (KV) ストアに登録されます。 Consul KV ストアは、このタイプのデータを保存する標準的な場所ではありませんが、このチュートリアルではわかりやすくするためにこれを使用します。

  1. 前のセクションの手順 2 で作成した、 infrastructure/messenger-deploy.shの内容を次の内容に置き換えます。

    #!/bin/bashset -e
    
    # This configuration requires a new commit to change
    NODE_ENV=production
    PORT=4000
    JSON_BODY_LIMIT=100kb
    
    # Postgres database configuration by pulling information from 
    # the system
    POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
    PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
    PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
    
    # RabbitMQ configuration by pulling from the system
    AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
    AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)
    
    docker run \
      --rm \
      -e NODE_ENV="${NODE_ENV}" \
      -e PORT="${PORT}" \
      -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
      -e PGUSER="${POSTGRES_USER}" \
      -e PGPORT="${PGPORT}" \
      -e PGHOST="${PGHOST}" \
      -e AMQPPORT="${AMQPPORT}" \
      -e AMQPHOST="${AMQPHOST}" \
      messenger
    

    このスクリプトは、次の 2 種類の構成の例を示しています。

    • デプロイメント スクリプトで直接指定された構成- デプロイメント環境 ( NODE_ENV ) とポート ( PORT ) を設定し、 JSON_BODY_LIMIT を20 バイトよりも現実的な値である 100 KB に変更します。
    • 外部ソースからクエリされた構成- Consul KV ストアからPOSTGRES_USERPGPORTPGHOSTAMQPHOST 、およびAMQPPORT環境変数の値を取得します。 次の 2 つの手順で、Consul KV ストアの環境変数の値を設定します。
  2. messenger-db-deploy.shを開き、以下を追加して、 messenger-databaseの初期デプロイメント スクリプトを作成します。

    #!/bin/bashset -e
    
    PORT=5432
    POSTGRES_USER=postgres
    
    docker run \
      -d \
      --rm \
      --name messenger-db \
      -v db-data:/var/lib/postgresql/data/pgdata \
      -e POSTGRES_USER="${POSTGRES_USER}" \
      -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
      -e PGPORT="${PORT}" \
      -e PGDATA=/var/lib/postgresql/data/pgdata \
      --network mm_2023 \
      postgres:15.1
    
    # Register details about the database with Consul
    curl -X PUT http://localhost:8500/v1/kv/messenger-db-port \
      -H "Content-Type: application/json" \
      -d "${PORT}"
    
    curl -X PUT http://localhost:8500/v1/kv/messenger-db-host \
      -H "Content-Type: application/json" \
      -d 'messenger-db' # This matches the "--name" flag above
                        # (the hostname)
    
    curl -X PUT http://localhost:8500/v1/kv/messenger-db-application-user \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_USER}"
    

    このスクリプトは、デプロイメント時にメッセンジャーサービスによって照会できる構成を定義するだけでなく、 「初期デプロイメント スクリプトの作成」のメッセンジャーサービスの初期スクリプトと同じ 2 つの概念を示しています。

    • デプロイメント スクリプトで特定の構成を直接指定します。この場合は、PostgreSQL データベースに実行するポートとデフォルト ユーザーのユーザー名を伝えます。
    • -eフラグを使用して Docker を実行し、実行時にコンテナに環境変数を挿入します。 また、実行中のコンテナの名前がmessenger-dbに設定され、これはセットアップの手順 2 でプラットフォームサービスを起動したときに作成した Docker ネットワーク内のデータベースのホスト名になります。
  3. 実際の展開では、通常、プラットフォーム チーム (または同様のチーム) が、メッセンジャーリポジトリのメッセンジャー データベースの場合と同様に、プラットフォームリポジトリ内の RabbitMQ などのサービスの展開とメンテナンスを処理します。 次に、プラットフォーム チームは、そのインフラストラクチャの場所が、それに依存するサービスによって検出可能であることを確認します。 チュートリアルの目的上、RabbitMQ の値を自分で設定します。

    curl -X PUT --silent --output /dev/null --show-error --fail \  -H "Content-Type: application/json" \
      -d "rabbitmq" \
      http://localhost:8500/v1/kv/amqp-host
    
    curl -X PUT --silent --output /dev/null --show-error --fail \
      -H "Content-Type: application/json" \
      -d "5672" \
      http://localhost:8500/v1/kv/amqp-port
    

    (RabbitMQ 変数の定義にamqpが使用されるのはなぜかと疑問に思うかもしれません。それは、 AMQPが RabbitMQ で使用されるプロトコルだからです。)

デプロイメントスクリプトにシークレットを設定する

メッセンジャーサービスのデプロイメント スクリプトで欠落している (重要な) データは 1 つだけです。それは、メッセンジャー データベースのパスワードです。

注記: シークレットの管理はこのチュートリアルの焦点ではないため、簡単にするためにシークレットはデプロイメント ファイルで定義されます。 実際の環境(開発、テスト、本番)では絶対にこれを行わないでください。大きなセキュリティ リスクが生じます

適切なシークレット管理について詳しくは、「Microservices March 2023」のユニット 2「Microservices Secrets Management 101」をご覧ください。 (ネタバレ: 秘密管理ツールは秘密を保存するための唯一の真に安全な方法です)。

  1. メッセンジャー データベースのパスワード シークレットを Consul KV ストアに保存するには、 infrastructure/messenger-db-deploy.shの内容を次のように置き換えます。

    #!/bin/bashset -e
    
    PORT=5432
    POSTGRES_USER=postgres
    # NOTE: Never do this in a real-world deployment. Store passwords
    # only in an encrypted secrets store.
    POSTGRES_PASSWORD=postgres
    
    docker run \
      --rm \
      --name messenger-db-primary \
      -d \
      -v db-data:/var/lib/postgresql/data/pgdata \
      -e POSTGRES_USER="${POSTGRES_USER}" \
      -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
      -e PGPORT="${PORT}" \
      -e PGDATA=/var/lib/postgresql/data/pgdata \
      --network mm_2023 \
      postgres:15.1
    
    echo "Register key messenger-db-port\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
      -H "Content-Type: application/json" \
      -d "${PORT}"
    
    echo "Register key messenger-db-host\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
      -H "Content-Type: application/json" \
      -d 'messenger-db-primary' # This matches the "--name" flag above
                                # which for our setup means the hostname
    
    echo "Register key messenger-db-application-user\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_USER}"
    
    echo "Register key messenger-db-password-never-do-this\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_PASSWORD}"
    
    printf "\nDone registering postgres details with Consul\n"
    
  2. Consul KV ストアからmessenger-databaseパスワード シークレットを取得するには、 infrastructure/messenger-deploy.shの内容を次のように置き換えます。

    #!/bin/bashset -e
    
    # This configuration requires a new commit to change
    NODE_ENV=production
    PORT=4000
    JSON_BODY_LIMIT=100kb
    
    # Postgres database configuration by pulling information from 
    # the system
    POSTGRES_USER=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-application-user?raw=true)
    PGPORT=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-port?raw=true)
    PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
    # NOTE: Never do this in a real-world deployment. Store passwords
    # only in an encrypted secrets store.
    PGPASSWORD=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true)
    
    # RabbitMQ configuration by pulling from the system
    AMQPHOST=$(curl -X GET http://localhost:8500/v1/kv/amqp-host?raw=true)
    AMQPPORT=$(curl -X GET http://localhost:8500/v1/kv/amqp-port?raw=true)
    
    docker run \
      --rm \
      -d \
      -e NODE_ENV="${NODE_ENV}" \
      -e PORT="${PORT}" \
      -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
      -e PGUSER="${POSTGRES_USER}" \
      -e PGPORT="${PGPORT}" \
      -e PGHOST="${PGHOST}" \
      -e PGPASSWORD="${PGPASSWORD}" \
      -e AMQPPORT="${AMQPPORT}" \
      -e AMQPHOST="${AMQPHOST}" \
      --network mm_2023 \
      messenger
    

デプロイメントスクリプトを実行する

  1. メッセンジャーリポジトリのアプリディレクトリに変更し、メッセンジャーサービスの Docker イメージをビルドします。

    cd ../appdocker build -t messenger .
    
  2. プラットフォームサービスに属するコンテナーのみが実行されていることを確認します。

    docker ps --format '{{.Names}}'consul-server
    consul-client
    rabbitmq
    
  3. メッセンジャーリポジトリのルートに変更し、メッセンジャー データベースメッセンジャーサービスをデプロイします。

    cd .../infrastructure/messenger-db-deploy.sh
    ./infrastructure/messenger-deploy.sh
    

    messenger-db-deploy.shスクリプトは、 messenger-databaseを起動し、適切な情報をシステム (この場合は Consul KV ストア) に登録します。

    次に、 messenger-deploy.shスクリプトがアプリケーションを起動し、 messenger-db-deploy.shによって登録された構成をシステム (この場合も、Consul KV ストア) から取得します。

    ヒント: コンテナの起動に失敗した場合は、デプロイメント スクリプト内のdocker runコマンドの 2 番目のパラメータ ( -d \行) を削除し、スクリプトを再度実行します。 その後、コンテナはフォアグラウンドで起動し、そのログがターミナルに表示され、問題が特定される可能性があります。 問題を解決したら、実際のコンテナがバックグラウンドで実行されるように-d \行を復元します。

  4. デプロイメントが成功したことを確認するために、アプリケーションに簡単なヘルスチェック リクエストを送信します。

    curl localhost:4000/healthcurl: (7) Failed to connect to localhost port 4000 after 11 ms: Connection refused
    

    あっ、失敗! 結局のところ、まだ重要な構成が 1 つ欠けており、メッセンジャーサービスがシステム全体に公開されていません。 mm_2023ネットワーク内では正常に動作していますが、そのネットワークにアクセスできるのは Docker 内からのみです。

  5. 次のチャレンジで新しいイメージを作成する準備として、実行中のコンテナを停止します。

    docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))
    

課題3: サービスを外部に公開する

実稼働環境では、通常、サービスを直接公開することはありません。 代わりに、一般的なマイクロサービス パターンに従い、メイン サービスの前にリバース プロキシ サービスを配置します。

このチャレンジでは、サービス検出 (新しいサービス情報の登録と、他のサービスがアクセスしたときにその情報を動的に更新する) を設定することで、メッセンジャーサービスを外部に公開します。 これを行うには、次のテクノロジを使用します。

  • 動的サービスレジストリであるConsulと、Consul データに基づいてファイルを動的に更新するツールである Consul テンプレート
  • NGINX Open Source は、コンテナ内で実行されるアプリケーションの複数の個別インスタンスで構成されるメッセンジャーサービスの単一のエントリ ポイントを公開するリバース プロキシおよびロード バランサーとして機能します。

サービス検出の詳細については、ブログの「マイクロサービス アプリを構成するためのベスト プラクティス」「サービスを構成として利用できるようにする」を参照してください。

領事館の設置

メッセンジャーリポジトリのapp/consul/index.mjsファイルには、起動時にメッセンジャーサービスを Consul に登録し、正常なシャットダウン時に登録を解除するために必要なすべてのコードが含まれています。 これは、新しくデプロイされたサービスを Consul のサービス レジストリに登録する、 registerという 1 つの関数を公開します。

  1. 好みのテキスト エディターでapp/index.mjsを開き、他のインポートステートメントの後に次のスニペットを追加して、 app/consul/index.mjsからregister関数をインポートします。

    import { register as registerConsul } from "./consul/index.mjs";
    

    次に、スクリプトの末尾にあるSERVER STARTセクションを次のように変更し、アプリケーションの起動後にregisterConsul()を呼び出すようにします。

    /* =================  SERVER START
    ================== */
    app.listen(port, async () => {
      console.log(`messenger_service listening on port ${port}`);
      registerConsul();
    });
    
    export default app;
    
  2. app/config/config.mjsconvictスキーマを開き、例 2の手順 1 で追加したjsonBodyLimitキーの後に次の構成値を追加します。

      consulServiceName: {    doc: "The name by which the service is registered in Consul. If not specified, the service is not registered",
        format: "*",
        default: null,
        env: "CONSUL_SERVICE_NAME",
      },
      consulHost: {
        doc: "The host where the Consul client runs",
        format: String,
        default: "consul-client",
        env: "CONSUL_HOST",
      },
      consulPort: {
        doc: "The port for the Consul client",
        format: "port",
        default: 8500,
        env: "CONSUL_PORT",
      },
    

    これにより、新しいサービスが登録される名前が設定され、Consul クライアントのホスト名とポートが定義されます。 次のステップでは、メッセンジャーサービスのデプロイメント スクリプトを変更して、この新しい Consul 接続とサービス登録情報を含めます。

  3. Infrastructure/messenger-deploy.shを開き、その内容を次の内容に置き換えて、前の手順で設定した Consul 接続とサービス登録情報をメッセンジャーサービス構成に含めます。

    #!/bin/bashset -e
    
    # This configuration requires a new commit to change
    NODE_ENV=production
    PORT=4000
    JSON_BODY_LIMIT=100kb
    
    CONSUL_SERVICE_NAME="messenger"
    
    # Consul host and port are included in each host since we
    # cannot query Consul until we know them
    CONSUL_HOST="${CONSUL_HOST}"
    CONSUL_PORT="${CONSUL_PORT}"
    
    # Postgres database configuration by pulling information from 
    # the system
    POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-application-user?raw=true")
    PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
    PGHOST=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-host?raw=true")
    # NOTE: Never do this in a real-world deployment. Store passwords
    # only in an encrypted secrets store.
    PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-password-never-do-this?raw=true")
    
    # RabbitMQ configuration by pulling from the system
    AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
    AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")
    
    docker run \
      --rm \
      -d \
      -e NODE_ENV="${NODE_ENV}" \
      -e PORT="${PORT}" \
      -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
      -e PGUSER="${POSTGRES_USER}" \
      -e PGPORT="${PGPORT}" \
      -e PGHOST="${PGHOST}" \
      -e PGPASSWORD="${PGPASSWORD}" \
      -e AMQPPORT="${AMQPPORT}" \
      -e AMQPHOST="${AMQPHOST}" \
      -e CONSUL_HOST="${CONSUL_HOST}" \
      -e CONSUL_PORT="${CONSUL_PORT}" \
      -e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
      --network mm_2023 \
      messenger
    

    注目すべき主な点は次のとおりです。

    • CONSUL_SERVICE_NAME環境変数は、メッセンジャーサービス インスタンスが Consul に登録されるときに使用する名前を指定します。
    • CONSUL_HOSTおよびCONSUL_PORT環境変数は、デプロイメント スクリプトが実行される場所で実行される Consul クライアント用です。

    注記: 実際の展開では、これはチーム間で合意する必要がある構成の例です。サービスはこの接続情報なしで Consul を照会できないため、Consul を担当するチームはすべての環境でCONSUL_HOSTおよびCONSUL_PORT環境変数を提供する必要があります。

  4. アプリ ターミナルで、アプリディレクトリに移動し、メッセンジャーサービスの実行中のインスタンスを停止し、Docker イメージを再構築して新しいサービス登録コードを組み込みます。

    cd appdocker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))
    docker build -t messenger .
    
  5. ブラウザでhttp://localhost:8500に移動して、Consul UI の動作を確認します (ただし、まだ何も面白いことは起きていません)。

  6. メッセンジャーリポジトリのルートで、デプロイメント スクリプトを実行して、メッセンジャーサービスのインスタンスを起動します。

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
    
  7. ブラウザの Consul UI で、ヘッダー バーの[サービス] をクリックして、単一のメッセンジャーサービスが実行されていることを確認します。

    Consul UI サービス タブには、Consul サービスと Messenger サービスのインスタンスがそれぞれ 1 つずつあります。

  8. メッセンジャーサービスのインスタンスをさらに起動するには、デプロイメント スクリプトをさらに数回実行します。 Consul UI で実行されていることを確認します。

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-deploy.sh
    

    Consul UI の「メッセンジャー」タブには 5 つのサービス インスタンスがあります

NGINXのセットアップ

次のステップでは、NGINX Open Source をリバース プロキシおよびロード バランサーとして追加し、着信トラフィックを実行中のすべてのメッセンジャーインスタンスにルーティングします。

  1. アプリのターミナルで、ディレクトリをメッセンジャーリポジトリのルートに変更し、 load-balancerというディレクトリと 3 つのファイルを作成します。

    mkdir load-balancercd load-balancer
    touch nginx.ctmpl
    touch consul-template-config.hcl
    touch Dockerfile
    

    Dockerfile は、 NGINX と Consul テンプレートが実行されるコンテナを定義します。 Consul テンプレートは、他の 2 つのファイルを使用して、サービス レジストリでメッセンジャーサービスが変更されたとき (サービス インスタンスが起動または停止したとき) に、NGINX アップストリームを動的に更新します。

  2. 手順 1 で作成したnginx.ctmplファイルを開き、次の NGINX 構成スニペットを追加します。これは、Consul テンプレートが NGINX アップストリーム グループを動的に更新するために使用します。

    upstream messenger_service {    {{- range service "messenger" }}
        server {{ .Address }}:{{ .Port }};
        {{- end }}
    }
    
    server {
        listen 8085;
        server_name localhost;
    
        location / {
            proxy_pass http://messenger_service;
            add_header Upstream-Host $upstream_addr;
        }
    }
    

    このスニペットは、Consul に登録されている各メッセンジャーサービス インスタンスの IP アドレスとポート番号を NGINX messenger_serviceアップストリーム グループに追加します。 NGINX は、受信リクエストを動的に定義されたアップストリーム サービス インスタンスのセットにプロキシします。

  3. 手順 1 で作成したconsul-template-config.hclファイルを開き、次の構成を追加します。

    consul {  address = "consul-client:8500"
    
      retry {
        enabled  = true
        attempts = 12
        backoff  = "250ms"
      }
    }
    template {
      source      = "/usr/templates/nginx.ctmpl"
      destination = "/etc/nginx/conf.d/default.conf"
      perms       = 0600
      command     = "if [ -e /var/run/nginx.pid ]; then nginx -s reload; else nginx; fi"
    }
    

    Consul テンプレートのこの構成は、ソーステンプレート (前の手順で作成された NGINX 構成スニペット) を再レンダリングし、指定された宛先に配置し、最後に指定されたコマンド(NGINX に構成を再読み込みするように指示する) を実行するように指示します。

    実際には、これは、Consul でサービス インスタンスが登録、更新、または登録解除されるたびに、新しいdefault.confファイルが作成されることを意味します。 その後、NGINX はダウンタイムなしで構成を再読み込みし、トラフィックを送信できる最新かつ正常なサーバー セット (メッセンジャーサービス インスタンス) を NGINX が確保できるようにします。

  4. 手順 1 で作成したDockerfileファイルを開き、次の内容を追加して、NGINX サービスを構築します。 (このチュートリアルではDockerfile を理解する必要はありませんが、便宜上、コードはインラインで文書化されています。)

    FROM nginx:1.23.1
    ARG CONSUL_TEMPLATE_VERSION=0.30.0
    
    # Set an environment variable for the location of the Consul
    # cluster. By default, it tries to resolve to consul-client:8500
    # which is the behavior if Consul is running as a container in the 
    # same host and linked to this NGINX container (with the alias 
    # consul, of course). But this environment variable can also be
    # overridden as the container starts if we want to resolve to
    # another address.
    
    ENV CONSUL_URL consul-client:8500
    
    # Download the specified version of Consul template
    ADD https://releases.hashicorp.com/consul-template/${CONSUL_TEMPLATE_VERSION}/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip /tmp
    
    RUN apt-get update \
      && apt-get install -y --no-install-recommends dumb-init unzip \
      && unzip /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip -d /usr/local/bin \
      && rm -rf /tmp/consul-template_${CONSUL_TEMPLATE_VERSION}_linux_amd64.zip
    
    COPY consul-template-config.hcl ./consul-template-config.hcl
    COPY nginx.ctmpl /usr/templates/nginx.ctmpl
    
    EXPOSE 8085
    
    STOPSIGNAL SIGQUIT
    
    CMD ["dumb-init", "consul-template", "-config=consul-template-config.hcl"]
    
  5. Docker イメージをビルドします。

    docker build -t messenger-lb .
    
  6. メッセンジャーディレクトリのルートに移動し、NGINX サービスのデプロイメント ファイルとしてmessenger-load-balancer-deploy.shという名前のファイルを作成します (チュートリアル全体でデプロイした他のサービスの場合と同様)。 環境によっては、 chmodコマンドの前にsudoを付ける必要がある場合があります。

    cd ..
    touch infrastructure/messenger-load-balancer-deploy.sh
    chmod +x infrastructure/messenger-load-balancer-deploy.sh
    
  7. messenger-load-balancer-deploy.shを開き、次の内容を追加します。

    #!/bin/bashset -e
    
    # Consul host and port are included in each host since we
    # cannot query Consul until we know them
    CONSUL_HOST="${CONSUL_HOST}"
    CONSUL_PORT="${CONSUL_PORT}"
    
    docker run \
      --rm \
      -d \
      --name messenger-lb \
      -e CONSUL_URL="${CONSUL_HOST}:${CONSUL_PORT}"  \
      -p 8085:8085 \
      --network mm_2023 \
      messenger-lb
    
  8. すべての準備が整ったので、NGINX サービスをデプロイします。

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-load-balancer-deploy.sh
    
  9. 外部からメッセンジャーサービスにアクセスできるかどうかを確認します。

    curl -X GET http://localhost:8085/health
    OK
    

    それは動作します! NGINX は、作成されたメッセンジャーサービスのすべてのインスタンス間で負荷分散を実行します。 これは、 X-Forwarded-Forヘッダーに、前のセクションのステップ 8の Consul UI と同じメッセンジャーサービスの IP アドレスが表示されているためわかります。

課題4: ジョブランナーとしてサービスを使用してデータベースを移行する

大規模なアプリケーションでは、多くの場合、データの変更などの 1 回限りのタスクを実行するために使用できる小さなワーカー プロセスを備えた「ジョブ ランナー」が使用されます (例として、 SidekiqCeleryがあります)。 これらのツールでは、多くの場合、 RedisRabbitMQなどの追加のサポート インフラストラクチャが必要になります。 この場合、メッセンジャーサービス自体を「ジョブ ランナー」として使用して、1 回限りのタスクを実行します。 これは、すでに非常に小さく、データベースやそれが依存するインフラストラクチャの他の部分と完全に対話でき、トラフィックを処理するアプリケーションとはまったく別に実行されているため、理にかなっています。

これを行うと、3 つの利点があります。

  1. ジョブランナー (実行されるスクリプトを含む) は、実稼働サービスとまったく同じチェックとレビューのプロセスを経ます。
  2. データベース ユーザーなどの構成値を簡単に変更して、本番環境の展開をより安全にすることができます。 たとえば、既存のテーブルへの書き込みとクエリのみを実行できる「低い権限」のユーザーで本番サービスを実行できます。 テーブルを作成および削除できる、より高い権限を持つユーザーとしてデータベース構造を変更するように、別のサービス インスタンスを構成できます。
  3. 一部のチームは、サービス本番トラフィックも処理しているインスタンスからジョブを実行します。 ジョブの問題がコンテナ内のアプリケーションが実行している他の機能に影響を与える可能性があるため、これは危険です。 そもそも、そのようなことを避けるのがマイクロサービスを行う理由ですよね?

このチャレンジでは、いくつかのデータベース構成値を変更し、メッセンジャーデータベースを新しい値を使用するように移行してパフォーマンスをテストすることで、アーティファクトを変更して新しい役割を果たす方法を探ります。

メッセンジャーデータベースの移行

実際の運用環境では、「アプリケーション ユーザー」と「移行ユーザー」という、異なる権限を持つ 2 つの異なるユーザーを作成する場合があります。 簡単にするために、この例では、デフォルト ユーザーをアプリケーション ユーザーとして使用し、スーパーユーザー権限を持つ移行ユーザーを作成します。 実際の状況では、各ユーザーの役割に基づいて、どの特定の最小限の権限が必要かを決定するために、より多くの時間を費やす価値があります。

  1. アプリターミナルで、スーパーユーザー権限を持つ新しい PostgreSQL ユーザーを作成します。

    echo "CREATE USER messenger_migrator WITH SUPERUSER PASSWORD 'migrator_password';" | docker exec -i messenger-db-primary psql -U postgres
    
  2. データベース デプロイメント スクリプト ( infrastructure/messenger-db-deploy.sh ) を開き、その内容を置き換えて新しいユーザーの資格情報を追加します。

    注記: もう一度繰り返しますが、実際のデプロイメントでは、データベース資格情報などのシークレットをデプロイメント スクリプト内やシークレット管理ツール以外の場所に置かないでください。 詳細については、ユニット 2 を参照してください。 マイクロサービスの秘密管理 101マイクロサービス 2023 年 3 月。

    #!/bin/bash
    set -e
    
    PORT=5432
    POSTGRES_USER=postgres
    # NOTE: Never do this in a real-world deployment. Store passwords
    # only in an encrypted secrets store.
    # Because we’re focusing on other concepts in this tutorial, we
    # set the password this way here for convenience.
    POSTGRES_PASSWORD=postgres
    
    # Migration user
    POSTGRES_MIGRATOR_USER=messenger_migrator
    # NOTE: As above, never do this in a real deployment.
    POSTGRES_MIGRATOR_PASSWORD=migrator_password
    
    docker run \
      --rm \
      --name messenger-db-primary \
      -d \
      -v db-data:/var/lib/postgresql/data/pgdata \
      -e POSTGRES_USER="${POSTGRES_USER}" \
      -e POSTGRES_PASSWORD="${POSTGRES_PASSWORD}" \
      -e PGPORT="${PORT}" \
      -e PGDATA=/var/lib/postgresql/data/pgdata \
      --network mm_2023 \
      postgres:15.1
    
    echo "Register key messenger-db-port\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-port \
      -H "Content-Type: application/json" \
      -d "${PORT}"
    
    echo "Register key messenger-db-host\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-host \
      -H "Content-Type: application/json" \
      -d 'messenger-db-primary' # This matches the "--name" flag above
                                # which for our setup means the hostname
    
    echo "Register key messenger-db-application-user\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-application-user \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_USER}"
    
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-password-never-do-this \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_PASSWORD}"
    
    echo "Register key messenger-db-application-user\n"
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-user \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_MIGRATOR_USER}"
    
    curl -X PUT --silent --output /dev/null --show-error --fail http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this \
      -H "Content-Type: application/json" \
      -d "${POSTGRES_MIGRATOR_PASSWORD}"
    
    printf "\nDone registering postgres details with Consul\n"
    

    この変更は、データベースのデプロイ後に Consul で設定されるユーザー セットに移行ユーザーを追加するだけです。

  3. インフラストラクチャディレクトリに、 messenger-db-migrator-deploy.shという新しいファイルを作成します (ここでも、 chmodコマンドの前にsudoを付ける必要があります)。

    touch infrastructure/messenger-db-migrator-deploy.shchmod +x infrastructure/messenger-db-migrator-deploy.sh
    
  4. messenger-db-migrator-deploy.shを開き、以下を追加します。

    #!/bin/bashset -e
    
    # This configuration requires a new commit to change
    NODE_ENV=production
    PORT=4000
    JSON_BODY_LIMIT=100kb
    
    CONSUL_SERVICE_NAME="messenger-migrator"
    
    # Consul host and port are included in each host since we
    # cannot query Consul until we know them
    CONSUL_HOST="${CONSUL_HOST}"
    CONSUL_PORT="${CONSUL_PORT}"
    
    # Get the migrator user name and password
    POSTGRES_USER=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-user?raw=true")
    PGPORT=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-port?raw=true")
    PGHOST=$(curl -X GET http://localhost:8500/v1/kv/messenger-db-host?raw=true)
    # NOTE: Never do this in a real-world deployment. Store passwords
    # only in an encrypted secrets store.
    PGPASSWORD=$(curl -X GET "http://localhost:8500/v1/kv/messenger-db-migrator-password-never-do-this?raw=true")
    
    # RabbitMQ configuration by pulling from the system
    AMQPHOST=$(curl -X GET "http://localhost:8500/v1/kv/amqp-host?raw=true")
    AMQPPORT=$(curl -X GET "http://localhost:8500/v1/kv/amqp-port?raw=true")
    
    docker run \--rm \
      -d \
      --name messenger-migrator \
      -e NODE_ENV="${NODE_ENV}" \
      -e PORT="${PORT}" \
      -e JSON_BODY_LIMIT="${JSON_BODY_LIMIT}" \
      -e PGUSER="${POSTGRES_USER}" \
      -e PGPORT="${PGPORT}" \
      -e PGHOST="${PGHOST}" \
      -e PGPASSWORD="${PGPASSWORD}" \
      -e AMQPPORT="${AMQPPORT}" \
      -e AMQPHOST="${AMQPHOST}" \
      -e CONSUL_HOST="${CONSUL_HOST}" \
      -e CONSUL_PORT="${CONSUL_PORT}" \
      -e CONSUL_SERVICE_NAME="${CONSUL_SERVICE_NAME}" \
      --network mm_2023 \
      messenger
    

    このスクリプトは、 Consul のセットアップ手順 3で作成した、最終的な形式のInfrastructure/messenger-deploy.shスクリプトと非常によく似ています。 主な違いは、 CONSUL_SERVICE_NAMEmessengerではなくmessenger-migratorであり、 PGUSER が上記の手順 1 で作成した「migrator」スーパーユーザーに対応していることです。

    CONSUL_SERVICE_NAMEmessenger-migratorであることが重要です。 messengerに設定されている場合、NGINX は自動的にこのサービスをローテーションに入れて API 呼び出しを受信しますが、トラフィックを処理することは意図されていません。

    このスクリプトは、移行の役割を持つ短命インスタンスをデプロイします。 これにより、移行に関する問題がメインのメッセンジャーサービス インスタンスによるトラフィックの処理に影響することがなくなります。

  5. PostgreSQL データベースを再デプロイします。 このチュートリアルではbashスクリプトを使用しているため、データベース サービスを停止して再起動する必要があります。 実稼働アプリケーションでは、通常、変更された要素のみを追加するために、インフラストラクチャ アズ コードスクリプトを実行します。

    docker stop messenger-db-primaryCONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-deploy.sh
    
  6. PostgreSQL データベース移行サービスをデプロイします。

    CONSUL_HOST=consul-client CONSUL_PORT=8500 ./infrastructure/messenger-db-migrator-deploy.sh
    
  7. インスタンスが期待どおりに実行されていることを確認します。

    docker ps --format "{{.Names}}"
    ...
    messenger-migrator
    

    また、Consul UI で、データベース移行サービスがmessenger-migratorとして Consul に正しく登録されていることを確認することもできます (この場合も、トラフィックを処理しないため、 messenger名では登録されません)。

    Consul UI サービス タブには、consul サービスと messenger-migrator サービスのインスタンスがそれぞれ 1 つずつ、messenger サービスのインスタンスが 4 つあります。

  8. さて、最後のステップとして、データベース移行スクリプトを実行します。 これらのスクリプトは実際のデータベース移行スクリプトとは似ていませんが、 messenger-migratorサービスを使用してデータベース固有のスクリプトを実行します。 データベースの移行が完了したら、 messenger-migratorサービスを停止します。

    docker exec -i -e PGDATABASE=postgres -e CREATE_DB_NAME=messenger messenger-migrator node scripts/create-db.mjsdocker exec -i messenger-migrator node scripts/create-schema.mjs
    docker exec -i messenger-migrator node scripts/create-seed-data.mjs
    docker stop messenger-migrator
    

メッセンジャー サービスを実際にテストする

メッセンジャーデータベースを最終形式に移行したので、メッセンジャーサービスの動作を確認できるようになりました。 これを行うには、NGINX サービスに対していくつかの基本的なcurlクエリを実行します ( NGINX のセットアップで NGINX をシステムのエントリ ポイントとして構成しました)。

次のコマンドの一部は、 jqライブラリを使用して JSON 出力をフォーマットします。 必要に応じてインストールすることも、必要に応じてコマンド ラインから省略することもできます。

  1. 会話を作成する:

    curl -d '{"participant_ids": [1, 2]}' -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations'{
      "conversation": { "id": "1", "inserted_at": "YYYY-MM-DDT06:41:59.000Z" }
    }
    
  2. ID 1 のユーザーから会話にメッセージを送信します。

    curl -d '{"content": "This is the first message"}' -H "User-Id: 1" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
      "message": {
        "id": "1",
        "content": "This is the first message",
        "index": 1,
        "user_id": 1,
        "username": "James Blanderphone",
        "conversation_id": 1,
        "inserted_at": "YYYY-MM-DDT06:42:15.000Z"
      }
    }
    
  3. 別のユーザー(ID 2)からのメッセージで返信します。

    curl -d '{"content": "This is the second message"}' -H "User-Id: 2" -H "Content-Type: application/json" -X POST 'http://localhost:8085/conversations/1/messages' | jq{
      "message": {
        "id": "2",
        "content": "This is the second message",
        "index": 2,
        "user_id": 2,
        "username": "Normalavian Ropetoter",
        "conversation_id": 1,
        "inserted_at": "YYYY-MM-DDT06:42:25.000Z"
      }
    }
    
  4. メッセージを取得します:

    curl -X GET 'http://localhost:8085/conversations/1/messages' | jq{
      "messages": [
        {
          "id": "1",
          "content": "This is the first message",
          "user_id": "1",
          "channel_id": "1",
          "index": "1",
          "inserted_at": "YYYY-MM-DDT06:42:15.000Z",
          "username": "James Blanderphone"
        },
        {
          "id": "2",
          "content": "This is the second message",
          "user_id": "2",
          "channel_id": "1",
          "index": "2",
          "inserted_at": "YYYY-MM-DDT06:42:25.000Z",
          "username": "Normalavian Ropetoter"
        }
      ]
    }
    

掃除

このチュートリアルでは、多数のコンテナーとイメージを作成しました。 保持したくない Docker コンテナとイメージを削除するには、次のコマンドを使用します。

  • 実行中の Docker コンテナを削除するには:

    docker rm $(docker stop $(docker ps -a -q --filter ancestor=messenger --format="{{.ID}}"))docker rm $(docker stop messenger-db-primary)
    docker rm $(docker stop messenger-lb)
    
  • プラットフォームサービスを削除するには:

    # From the platform repositorydocker compose down
    
  • チュートリアル全体で使用したすべての Docker イメージを削除するには:

    docker rmi messengerdocker rmi messenger-lb
    docker rmi postgres:15.1
    docker rmi hashicorp/consul:1.14.4
    docker rmi rabbitmq:3.11.4-management-alpine
    

次のステップ

「簡単な設定なのに、こんなに手間がかかりそう」と思われるかもしれませんが、その通りです。 マイクロサービスに重点を置いたアーキテクチャに移行するには、サービスをどのように構造化し、構成するかについて細心の注意を払う必要があります。 あらゆる複雑さにもかかわらず、着実な進歩を遂げました。

  • 他のチームが簡単に理解できる、マイクロサービスに重点を置いた構成を設定します。
  • 関連するさまざまなサービスのスケーリングと使用の両方の点で、ある程度柔軟になるようにマイクロサービス システムを設定します。

マイクロサービスに関する学習を継続するには、「Microservices March 2023」をご覧ください。 ユニット 2「マイクロサービス シークレット管理 101」では、マイクロサービス環境でのシークレット管理について、詳細かつユーザーフレンドリーな概要を説明します。


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 q。"