本文へスキップ
Starone

ケイパビリティ

負荷と部分的な障害の下でも正しく動作し続けるバックエンドシステム

バックエンドの真価は、依存先がタイムアウトしたとき、メッセージが二重に届いたとき、トラフィックが3倍になったときに問われます。私たちはそうした状況を前提にAPI、サービス、データストアを設計します。明示的なコントラクト、冪等な操作、上限のあるキュー、そしてワークロードに合わせて選んだストレージです。

概要

バックエンドエンジニアリングとは、システムの状態を保持し、そのルールを強制するサービス、API、データストアを設計することです。難しいのは、ほとんどの場合ハッピーパスではありません。2つのリクエストが同じレコードを変更する並行処理下での挙動、呼び出しが成功したのか、失敗したのか、まだ実行中なのか分からない部分的な障害下での挙動、そして小さなテーブルでは高速だったクエリが大きなテーブルではフルスキャンになる成長下での挙動です。

システムが複数のプロセスにまたがると、すべてのコンポーネントの間にネットワークが入り、分散トランザクションをコストなしに得ることはできません。呼び出しは結果を明かさないままタイムアウトし、メッセージは少なくとも1回配信され、レプリカには遅延が生じます。そのとき正しさを支えるのは、意図して適用するパターンです。冪等性キー、バックオフとジッターを伴うリトライ、サーキットブレーカー、トランザクショナルアウトボックス、補償アクションを伴うSaga、そしてどのデータに強整合性が必要で、どのデータは収束すればよいのかという明示的な判断です。

私たちは、JavaとSpring Boot、Go、Node.js、Pythonでバックエンドを構築し、PostgreSQL、MySQL、MongoDB、Redis、Kafka、OpenSearchを、目の前の課題に応じて選定します。アーキテクチャはアクセスパターン、整合性の要件、障害モードから出発し、分散させる具体的な理由が生じるまではモジュラーモノリスを優先します。

エンジニアリング領域

技術領域

01

APIアーキテクチャ

HTTP上のRESTは、キャッシュが可能で広く理解されており、ゲートウェイで保護しやすいため、公開APIやパートナー向けAPIに適しています。GraphQLは、相互に関連するドメインに対して多様なデータ要件を持つクライアントに適し、gRPCは、厳密なコントラクト、ストリーミング、低オーバーヘッドが求められる内部のサービス間呼び出しに適しています。コントラクトはOpenAPIまたはProtocol Buffersで先に記述し、破壊的変更はCIで検出します。

  • OpenAPIコントラクトによるREST
  • Persisted Queryと複雑度制限を備えたGraphQL
  • gRPCとProtocol Buffers
  • APIゲートウェイとリクエストルーティング
  • カーソルベースのページネーション
  • 追加のみによるバージョニングと非推奨化ポリシー
  • 署名付きペイロードによるWebhook
02

認証、認可、トラフィック制御

認証は呼び出し元が誰であるかを確定するもので、ユーザーにはOAuth 2.0とOpenID Connectを、サービスには相互TLSまたはワークロードアイデンティティを用います。認可は、すべてのリクエストでオブジェクトレベルまで検証します。あるテナントの有効なトークンで、別のテナントのレコードが読めることは決してあってはならないためです。レート制限とクォータはキャパシティを保護するもので、クライアント単位、テナント単位、エンドポイント単位で適用します。

  • OAuth 2.0とOpenID Connect
  • 有効期間の短いJWTとトークンイントロスペクション
  • サービス間の相互TLS
  • OPAなどのポリシーエンジンによるRBACとABAC
  • トークンバケットとスライディングウィンドウによるレート制限
  • ロードシェディングとリクエストの優先度付け
03

非同期処理とイベントストリーミング

リクエスト内で完了する必要のない処理はキューやストリームに移し、バーストを吸収するとともに、低速な依存先を切り離します。Kafkaは、キー単位の順序を保証する、永続的でパーティション分割されたリプレイ可能なログを提供します。RabbitMQなどのキューブローカーは、メッセージ単位のルーティングと確認応答を提供します。配信は少なくとも1回であるため、コンシューマーは冪等に実装します。

  • Apache Kafkaのトピック、パーティション、コンシューマーグループ
  • RabbitMQとクラウドのキューサービス
  • 軽量なパイプライン向けのRedis Streams
  • デッドレターキューとリプレイ用ツール
  • AvroまたはProtobufを用いたスキーマレジストリ
  • バックプレッシャーとコンシューマーラグの制御
04

レジリエンスと整合性のパターン

すべてのリモート呼び出しに、タイムアウト、指数バックオフとジッターを伴う回数上限付きのリトライポリシー、そして障害の起きている依存先への呼び出しを止めるサーキットブレーカーを設けます。副作用を伴う操作は冪等性キーを受け付け、リトライによって課金、出荷、通知が二重に行われることを防ぎます。複数サービスにまたがるワークフローには、2フェーズコミットではなくSagaとアウトボックスパターンを用います。

  • 冪等性キーと重複排除ストア
  • 指数バックオフとジッターを伴うリトライ
  • サーキットブレーカーとバルクヘッド
  • 変更データキャプチャ(CDC)を用いたトランザクショナルアウトボックス
  • Saga:オーケストレーションとコレオグラフィ
  • 楽観的ロックとバージョンカラム
05

データストアとキャッシュ

リレーショナルデータベースを既定のSystem of Record(記録の正本)とし、その他のストアは特定のアクセスパターンによって正当化される場合に追加します。Redisは、分散キャッシュ、レート制限用カウンター、有効期限付きロック、セッションストレージを提供します。検索エンジンやベクトルインデックスは、System of Recordから生成する派生ビューであり、信頼できる情報源として扱うことは決してありません。

  • PostgreSQLとMySQL
  • MongoDB
  • Redis
  • ElasticsearchとOpenSearch
  • pgvector、Qdrant、Milvusによるベクトル検索
  • キャッシュアサイド、ライトスルー、TTLの各戦略
  • リードレプリカ、パーティショニング、シャーディング
06

言語とランタイムエンジニアリング

JavaとSpring Bootは、成熟したエコシステム、強い型付け、そして大規模なブロッキングI/Oを扱う仮想スレッドの恩恵を受ける、ドメインの比重が大きい大規模システムに選びます。Goは、予測可能なレイテンシと小さな静的バイナリが求められるネットワークサービスやインフラコンポーネントに適しています。Node.jsはI/OバウンドなAPIや、フロントエンドとTypeScriptを共有するチームに、Pythonはデータ集約型のサービスや機械学習サービスに適しています。

  • JavaとSpring Boot
  • Go
  • TypeScriptとNestJSによるNode.js
  • FastAPIによるPython
  • コネクションプーリングと、スレッドまたはイベントループのチューニング
  • プロファイリング、ヒープ解析、GCチューニング

流行ではなくワークロードでデータベースを選ぶ

適切なデータベースは、ワークロードの4つの性質から導かれます。アクセスパターン、ビジネスルールが要求する整合性、書き込み量、そしてクエリの形です。製品を選ぶ前に、主要なクエリを想定頻度、カーディナリティ、レイテンシバジェットとともに書き出すと、議論の大半は不要になります。中規模のデータに対して複数行のトランザクション、結合、アドホックなクエリが必要なシステムはPostgreSQLまたはMySQLが適しており、ほとんどの業務アプリケーションがこれに当てはまります。

特化型のストアは、リレーショナルデータベースが苦手とするパターンがあって初めて採用に値します。そして、どのストアも運用上の負担を増やします。バックアップ、アップグレード、監視、フェイルオーバー、そして同期を保たなければならないデータの第2のコピーです。JSONB、全文検索、パーティショニング、pgvectorを備えたPostgreSQLは、2つ目のシステムが正当化されるまでの広い範囲をカバーできるため、立証責任は追加する側にあります。

  • PostgreSQLまたはMySQL:トランザクションを扱うSystem of Record、リレーショナルな整合性、読み書きが混在するワークロード、レプリカを通じたレポーティング
  • MongoDB:可変的な構造を持ち、ドキュメント全体として読み書きされる集約。ドキュメントをまたぐトランザクションが例外的な場合
  • Redis:キャッシュ、カウンター、セッション、リーダーボード、短命な協調処理のためのサブミリ秒のキーアクセス。永続化はあくまで補助的な機能として扱う
  • ElasticsearchまたはOpenSearch:転置インデックス上での全文検索の関連度、ファセット、ログ分析。非同期に投入し、リフレッシュの遅延を許容する
  • ベクトルデータベース:HNSWやIVFインデックスによる埋め込みベクトルの近似最近傍探索。再現率と引き換えにレイテンシとメモリを抑える
  • Kafka:システム間をつなぐ、順序付きでリプレイ可能なイベントログ。トランスポートでありバッファであって、クエリエンジンではない

アーキテクチャ

イベント駆動アーキテクチャ

プロデューサー

  1. 01

    コマンド

    サービスがリクエストを処理

  2. 02

    ローカルトランザクション

    状態とアウトボックス行を同時にコミット

  3. 03

    アウトボックスリレー

    ポーリングかログ追跡で取得し発行

  4. 04

    発行

    スキーマバージョン付きのキー付きイベント

ブローカー

  1. 01

    トピック

    イベント種別または集約ごとに1つ

  2. 02

    パーティション

    キー単位で順序を保証

  3. 03

    保持

    過去のオフセットから再生可能

  4. 04

    デッドレターキュー

    失敗し続けるイベントを保持

コンシューマー

  1. 01

    コンシューマーグループ

    パーティションをまたいでスケールアウト

  2. 02

    冪等ハンドラー

    適用前にイベントIDで重複排除

  3. 03

    バックオフ付きリトライ

    一時的な障害に限定

  4. 04

    オフセットコミット

    副作用の成功後に実行

  5. 05

    状態の投影

    リードモデルとキャッシュを更新

プロデューサーはトランザクショナルアウトボックスを通じてイベントを発行し、ブローカーはキー単位で順序を保ち、コンシューマーは各イベントを冪等に適用します。処理できないイベントはパーティションを塞がず、デッドレターキューに退避されます。

サービス間の整合性:アウトボックス、Saga、冪等性

二重書き込みの問題は、サービスが自身のデータベースを更新し、かつメッセージを発行するあらゆる場面で現れます。2つの操作が別々であれば、その間でクラッシュが起きたとき、データベースは変更されたのにイベントが発行されない、あるいはその逆の状態が残ります。トランザクショナルアウトボックスはこの隙間をなくします。イベントを状態変更と同じローカルトランザクションの中でアウトボックステーブルに挿入し、その後リレー(一般的にはDebeziumによる変更データキャプチャ)がKafkaへ発行します。配信は少なくとも1回となるため、コンシューマーは重複排除を行わなければなりません。

在庫の引き当て、決済、出荷の作成のように複数のサービスにまたがるビジネスプロセスでは、全体を1つのACIDトランザクションで保持することはできません。Sagaは、これをローカルトランザクションの連なりとしてモデル化し、それぞれに、意味的に取り消すための補償アクションを持たせます。オーケストレーションは、一連の手順を1つのコーディネーターに置く方式で、Temporalのようなワークフローエンジンやデータベース上のステートマシンで構築し、プロセスを可視化してテスト可能にします。コレオグラフィは、サービスが互いのイベントに反応する方式で、短いフローでは結合を減らせますが、長いフローでは全体の把握が難しくなります。

冪等性はリトライを安全にします。クライアントは変更を伴うリクエストごとに一意のキーを送信し、サーバーはそのキーを結果とともに、効果を生む処理と同じトランザクション内で保存し、同じキーを再び受け取ったときには保存済みの結果を返します。呼び出しチェーンを通じて伝播させるデッドラインと、リトライストームを防ぐリトライバジェットを組み合わせることで、副作用を重複させることなくタイムアウトから回復できます。

同期呼び出し、キュー、イベントストリーム

同期呼び出しは、呼び出し側の可用性とレイテンシを呼び出し先のそれに結合させます。リクエストの経路が5つのサービスをまたぐなら、その可用性は5つすべての積になります。それでも、決済を承認する前の残高確認のように、呼び出し側が処理を続けるために答えを必要とする場合には、同期呼び出しが適切な選択です。通知、プロジェクション、検索インデックスの更新、分析など、レスポンスの後に行えるものはすべて、イベントで駆動するほうが適しています。

キューとストリームの違いは、何を保持するかにあります。キューは各メッセージを1つのコンシューマーに渡し、確認応答を受けると削除するため、メッセージ単位でリトライするタスク分配に適しています。Kafkaのトピックは、消費されたかどうかにかかわらず設定した期間ログを保持するため、新しいコンシューマーが履歴をリプレイでき、複数のコンシューマーグループが独立して読み取れ、パーティション内では順序が保証されます。したがって、パーティションキーは順序と並列度の両方を決めるものであり、その選択は設計判断です。通常は、アカウントや注文といった集約の識別子を用います。

結果整合性は、技術的な判断であると同時にプロダクト上の判断でもあります。イベントから更新されるリードモデルは書き込み側より遅れるため、UIは自身の書き込みを書き込み元から読むか、保留中の状態を表示するか、遅延を許容するかのいずれかを選ぶ必要があります。私たちはプロジェクションごとに想定される遅延を文書化し、コンシューマーラグがそれを超えたときにアラートを発報します。

誤ったデータを返さない分散キャッシュ

キャッシュはデータの第2のコピーであり、それ自体の整合性の問題を抱えます。TTL付きのキャッシュアサイドが、正しく動作する最もシンプルなパターンです。Redisから読み、ミスした場合はデータベースにフォールバックし、結果を有効期限付きで書き込み、元の行が変更されたらキーを削除します。書き込み時は更新するより削除するほうが安全です。更新の場合、並行する書き込みによって古い値が残ってしまうことがあるためです。

障害モードは予測可能であり、明示的な対処が必要です。スタンピードは、人気のあるキーが失効し、多数のリクエストが一斉に再計算を行うときに発生し、リクエストの集約(コアレッシング)、確率的な早期リフレッシュ、短時間のロックで対処します。ホットキーは1つのシャードに負荷を集中させ、キー空間に上限がなければ、エビクションポリシーとキーのカーディナリティを計画していない限りメモリを使い果たします。また、キャッシュが利用できないときも、サービスは遅くなることはあっても正しく動作し続けなければなりません。

エンジニアリングの進め方

私たちのアプローチ

私たちは、データと障害モードを起点に外側へ向かってバックエンドを設計し、可動部品の数を要件が許すかぎり少なく保ちます。

  1. 01

    ドメインとその不変条件のモデリング

    集約、トランザクションとして守らなければならないルール、そして結果整合性を許容できる境界を特定します。後からサービスを分割できる場所を決めるのは、チームの好みではなくこの境界です。

  2. 02

    ワークロードの特性の把握

    アクセスパターン、読み書きの比率、データ量、成長率、レイテンシ目標を操作ごとに書き出します。これがスキーマ設計、インデックス設計、データストアの選定、キャパシティプランニングの根拠になります。

  3. 03

    コントラクトを先に定義

    OpenAPI、GraphQL、Protobufのスキーマとイベントスキーマを、エラーモデル、ページネーション、冪等性の挙動、バージョニングのルールを含めて、実装の前に合意します。利用側は、同じコントラクトから生成したモックに対して開発を進めます。

  4. 04

    障害対応を組み込んだ実装

    タイムアウト、リトライ、サーキットブレーカー、アウトボックスによる発行、冪等なコンシューマーは、後からの堅牢化フェーズではなく、最初の実装に含めます。スキーマ変更にはexpand-and-contract方式のマイグレーションを用い、新旧のコードが並行して動作できるようにします。

  5. 05

    現実的な条件でのテスト

    インテグレーションテストはコンテナ上の実際のデータベースとブローカーに対して実行し、コントラクトテストでサービス境界を守り、負荷テストには本番相当のデータ量を用います。フォールトインジェクションにより、依存先が遅い場合、利用できない場合、重複を返す場合の挙動を検証します。

  6. 06

    サービスレベル目標に基づく運用

    各サービスに、可用性とレイテンシの目標、4つのゴールデンシグナルのダッシュボード、エラーバジェットのバーンレートに連動したアラート、既知の障害モードに対するランブックを用意します。

トレードオフ

設計を左右する判断

アーキテクチャを確定する前に、お客様のチームとともに検討する問いです。

REST、GraphQL、gRPCのどれを使うか
HTTPキャッシュ、ツール群、幅広いクライアントサポートが重要な公開APIやパートナー向けAPIにはRESTを使います。多数のクライアント画面が、相互に関連するグラフの異なるプロジェクションを必要とし、クエリコストの制限、N+1クエリを防ぐバッチ処理、フィールドレベルの認可に投資できる場合はGraphQLを使います。内部サービス間では、型付きのコントラクト、デッドライン、ストリーミングのためにgRPCを使います。ブラウザからはgRPC-Webプロキシが必要になるため、外部向けインターフェースとして適切であることはまれです。
モジュラーモノリスか、マイクロサービスか
1つのチームがドメインを所有している場合は、モジュラーモノリスから始めます。境界を強制し、スキーマを分離したモジュールにより、1つのデプロイとローカルトランザクションのままで、設計上の利点の大半が得られます。モジュールに独立したスケーリング、異なるリリースサイクル、異なるランタイム、障害の分離が必要になったときに、サービスとして切り出します。マイクロサービスはプロセス内呼び出しをネットワーク呼び出しに置き換えるものであり、その採用を正当化するのは組織上・運用上の必要性であって、コードの整理ではありません。
Kafka、メッセージキュー、Redis Streamsのどれを使うか
イベントを保持してリプレイする必要があり、複数の独立したグループが消費し、高スループットでキー単位の順序が求められる場合はKafkaを選びます。メッセージ単位の確認応答、遅延、ルーティングを伴うタスク分配で、履歴が不要な場合は、RabbitMQまたはマネージドのキューを選びます。Redis Streamsは、すでにRedisが導入されており、フェイルオーバー時にわずかな時間幅のデータが失われることを許容できるなら、中程度までのデータ量には十分です。
どこに強整合性が必要で、どこなら結果整合性を許容できるか
残高、在庫の減算、一意性のように、一瞬たりとも破られてはならない不変条件は、1つのサービスの1つのデータベーストランザクション内に収めます。検索インデックス、リードモデル、通知、分析といった派生データには結果整合性を受け入れます。不変条件が2つのサービスにまたがっているなら、それは境界の位置が誤っていることを示しています。
2つ目のデータベースを追加すべきか
計測済みのアクセスパターンが、適切なインデックス、パーティショニング、レプリカを施したプライマリのストアでは満たせない場合に限ります。検索の関連度、高次元の類似検索、単純なキーに対する非常に高い書き込みスループットは、正当な理由です。追加するストアごとに、同期の経路(通常は変更データキャプチャ)と、System of Recordから再構築するための計画が必要になります。
バックエンドにはどの言語とフレームワークを使うべきか
第一にチームの運用経験、第二にワークロードで選びます。JavaとSpring Bootは複雑なトランザクションを扱うドメインと大規模なチームに、Goはメモリと起動時間の制約が厳しい高並行のネットワークサービスに適しています。Node.jsはI/OバウンドなAPIに適しますが、イベントループ上でのCPU負荷の高い処理には向きません。Pythonはデータやモデルのコードに近いサービスに適しています。組織で使うランタイムを2つか3つに絞ることで、ライブラリ、パイプライン、オンコールの知識を共有し続けられます。

本番対応

本番運用に耐える条件

セキュリティの確保

リクエストはゲートウェイで認証し、サービス内でオブジェクトレベルまで改めて認可します。テナントの識別情報はリクエストパラメータではなく、検証済みのクレームで受け渡します。入力はコントラクトに照らして検証し、クエリはパラメータ化し、シークレットはローテーションを伴うシークレットマネージャーから取得し、サービス間通信には相互TLSを用います。監査ログは、誰がどのリソースに何をしたかを記録し、追記のみで書き込みます。

スケーリング

サービスはステートレスで、ロードバランサーの背後で水平にスケールし、状態はデータベース、キャッシュ、ブローカーに置きます。通常はデータベースが限界となるため、スケーリングは順を追って進めます。インデックスとクエリプラン、コネクションプーリング、キャッシュ、リードレプリカ、テーブルパーティショニングの順に進め、シャーディングは単一のプライマリで書き込みを吸収しきれなくなった場合に限ります。キューがバーストを吸収し、並行数の上限設定とロードシェディングにより、過負荷のサービスでも受け付けたリクエストには応答し続けられます。

監視

OpenTelemetryにより、HTTP、gRPC、メッセージの境界をまたぐ分散トレースを取得します。トレースコンテキストはメッセージヘッダーで運ぶため、非同期のホップも可視のまま保たれます。メトリクスは、エンドポイントごとのレート、エラー、所要時間、飽和度に加え、コンシューマーラグ、プール使用率、キャッシュヒット率を対象とします。ログは構造化し、相関IDを付与し、個人データとシークレットは含めません。

デプロイ

サービスはコンテナイメージとして、テスト、静的解析、依存関係とイメージのスキャンを実行するパイプラインを通じて出荷します。その後、ローリングリリースまたはカナリアリリースで段階的にデプロイし、エラー率やレイテンシのリグレッションを検知すると自動でロールバックします。データベースマイグレーションは後方互換とし、expand、migrate、contractの手順に従ってコードとは別に適用します。新しい挙動の公開範囲は、デプロイとは独立してフィーチャーフラグで制御します。

システム連携

外部システムにはアダプターを介して接続し、そのデータモデルと障害時の挙動をコアドメインから切り離します。受信するWebhookは署名を検証して永続化し、速やかに応答を返したうえで、重複排除を行いながら非同期に処理します。送信するWebhookには署名を付け、バックオフを伴ってリトライします。レガシーシステムとは、データベースへの共有アクセスではなく、変更データキャプチャまたは腐敗防止層を通じて統合します。

使用技術

ワークロードとお客様の既存環境に合わせて選定します。

  • Java and Spring Boot
  • Go
  • Node.js
  • Python and FastAPI
  • PostgreSQL
  • MySQL
  • MongoDB
  • Redis
  • Apache Kafka
  • RabbitMQ
  • OpenSearch
  • gRPC
  • GraphQL
  • OpenTelemetry

ゼロから本番へ

エンジニアリングライフサイクル

  1. 01

    アイデア

  2. 02

    ディスカバリー

  3. 03

    システムアーキテクチャ

  4. 04

    UX・プロダクトデザイン

  5. 05

    アプリケーション開発

  6. 06

    AI・データレイヤー

  7. 07

    クラウドインフラ

  8. 08

    セキュリティ

  9. 09

    QA・パフォーマンス

  10. 10

    デプロイ

  11. 11

    オブザーバビリティ

  12. 12

    継続的な進化

FAQ

よくあるご質問

冪等性とは何ですか。なぜ重要なのですか

冪等な操作とは、1回実行しても複数回実行しても同じ結果になる操作です。分散システムでは、タイムアウトからはリクエストが処理されたかどうかが分からず、安全な対応はリトライしかないため、冪等性が重要になります。APIの冪等性キーとメッセージコンシューマーでの重複排除により、そのリトライは無害になります。

Kafkaでexactly-once配信は実現できますか

Kafkaは冪等なプロデューサーとトランザクションを提供しており、Kafka内で完結する読み取り・処理・書き込みのサイクルについてはexactly-onceのセマンティクスが得られます。しかし、コンシューマーが外部のデータベースに書き込んだり、別のAPIを呼び出したりした時点で、保証はat-least-once(少なくとも1回)になります。そのためコンシューマーは冪等でなければならず、一般的には、処理済みメッセージの識別子を状態変更と同じトランザクション内で記録します。

APIのバージョニングはどのように行うべきですか

バージョンを切るよりも、進化させることを優先します。オプションのフィールドや新しいエンドポイントを追加し、既存フィールドの意味は決して変えず、クライアントには未知のフィールドを無視させます。破壊的変更が避けられない場合は、パスまたはヘッダーで新しいメジャーバージョンを公開して両方を並行稼働させ、非推奨化のスケジュールを告知し、旧バージョンの残存利用状況を計測したうえで廃止します。

分散キャッシュを追加する価値があるのはどのような場合ですか

プロファイリングによって読み取り負荷や高コストな計算がボトルネックであることが示され、かつデータが定められた範囲の古さを許容できる場合です。キャッシュは無効化の問題と新たな障害モードを持ち込むため、クエリやインデックスの最適化の代わりではなく、その後に検討すべきものです。キャッシュする項目のそれぞれに、所有者、TTL、無効化のトリガーが必要です。

バックエンドをモノリスからサービス群へ段階的に移行できますか

はい、ストラングラーパターンを用いれば可能です。モノリスの前段に置いたルーティング層が、選択したパスを新しいサービスへ振り分け、境界づけられたコンテキストを1つずつ移行します。移行期間中は、変更データキャプチャまたはイベントによってデータの同期を保ちます。最初の一歩は通常、モノリス内部でモジュール境界を強制することです。プロセス内で分離できないモジュールは、ネットワークを挟んでも分離できないためです。

本番環境で動くシステムが必要な方へ

構築しようとしているものをお聞かせください。エンジニアが内容を拝見し、2営業日以内にご返信します。