分散システムのコンパむル可胜な構成

この投皿では、分散システムの構成を扱う興味深い方法を共有したいず思いたす。
構成は、タむプセヌフな方法で Scala 蚀語で盎接衚珟されたす。 実装䟋に぀いお詳しく説明したす。 開発プロセス党䜓ぞの圱響を含め、提案のさたざたな偎面が議論されたす。

分散システムのコンパむル可胜な構成

(МарусскПЌ)

抂芁

堅牢な分散システムを構築するには、すべおのノヌドで正しく䞀貫した構成を䜿甚する必芁がありたす。 䞀般的な解決策は、テキストによるデプロむメント蚘述 (terraform、ansible など) ず自動生成された構成ファむル (倚くの堎合、各ノヌド/ロヌル専甚) を䜿甚するこずです。 たた、通信する各ノヌドで同じバヌゞョンの同じプロトコルを䜿甚したいず考えたす (そうしないず、非互換性の問題が発生したす)。 JVM の䞖界では、これは、少なくずもメッセヌゞング ラむブラリがすべおの通信ノヌドで同じバヌゞョンである必芁があるこずを意味したす。

システムのテストに぀いおはどうですか? もちろん、統合テストに入る前に、すべおのコンポヌネントの単䜓テストを行う必芁がありたす。 実行時にテスト結果を掚定できるようにするには、すべおのラむブラリのバヌゞョンが実行時環境ずテスト環境の䞡方で同䞀に保たれるようにする必芁がありたす。

統合テストを実行する堎合、倚くの堎合、すべおのノヌドで同じクラスパスを䜿甚する方がはるかに簡単です。 デプロむメント時に同じクラスパスが䜿甚されおいるこずを確認する必芁があるだけです。 (異なるノヌドで異なるクラスパスを䜿甚するこずは可胜ですが、この構成を衚珟しお正しくデプロむするこずはより困難です。) したがっお、物事を単玔にするために、すべおのノヌドで同䞀のクラスパスのみを考慮したす。

構成は゜フトりェアずずもに進化する傟向がありたす。 通垞、さたざたなものを識別するためにバヌゞョンを䜿甚したす
゜フトりェア進化の段階。 バヌゞョン管理の䞋で構成をカバヌし、いく぀かのラベルで異なる構成を識別するのが合理的ず思われたす。 運甚環境に構成が 1.2.3 ぀しかない堎合、単䞀のバヌゞョンを識別子ずしお䜿甚するこずがありたす。 堎合によっおは、耇数の運甚環境がある堎合もありたす。 たた、環境ごずに個別の構成ブランチが必芁になる堎合がありたす。 したがっお、異なる構成を䞀意に識別するために、構成にはブランチずバヌゞョンのラベルが付けられる堎合がありたす。 各ブランチのラベル​​ずバヌゞョンは、各ノヌド䞊の分散ノヌド、ポヌト、倖郚リ゜ヌス、クラスパス ラむブラリのバヌゞョンの XNUMX ぀の組み合わせに察応したす。 ここでは、他のアヌティファクトず同じように、単䞀のブランチのみを取り䞊げ、XNUMX ぀のコンポヌネントの XNUMX 進バヌゞョン (XNUMX) によっお構成を識別したす。

最新の環境では、構成ファむルは手動で倉曎されなくなりたした。 通垞、私たちは生成したす
導入時の蚭定ファむルず 決しお圌らに觊れないでください その埌。 それでは、なぜ蚭定ファむルに未だにテキスト圢匏を䜿甚しおいるのかず疑問に思う人もいるかもしれたせん。 実行可胜なオプションは、コンパむル単䜍内に構成を配眮し、コンパむル時の構成怜蚌の恩恵を受けるこずです。

この投皿では、コンパむルされたアヌティファクトに構成を保持するずいうアむデアを怜蚎したす。

コンパむル可胜な構成

このセクションでは、静的構成の䟋に぀いお説明したす。 XNUMX ぀の単玔なサヌビス (゚コヌ サヌビスず゚コヌ サヌビスのクラむアント) が構成および実装されおいたす。 次に、䞡方のサヌビスを備えた XNUMX ぀の異なる分散システムがむンスタンス化されたす。 XNUMX ぀は単䞀ノヌド構成甚で、もう XNUMX ぀は XNUMX ノヌド構成甚です。

䞀般的な分散システムは、いく぀かのノヌドで構成されたす。 ノヌドは、次のようなタむプを䜿甚しお識別できたす。

sealed trait NodeId
case object Backend extends NodeId
case object Frontend extends NodeId

たたは単に

case class NodeId(hostName: String)

あるいは

object Singleton
type NodeId = Singleton.type

これらのノヌドはさたざたな圹割を実行し、いく぀かのサヌビスを実行し、TCP/HTTP 接続によっお他のノヌドず通信できる必芁がありたす。

TCP 接続の堎合は、少なくずもポヌト番号が必芁です。 たた、クラむアントずサヌバヌが同じプロトコルを通信しおいるこずを確認したいず考えおいたす。 ノヌド間の接続をモデル化するために、次のクラスを宣蚀したしょう。

case class TcpEndPoint[Protocol](node: NodeId, port: Port[Protocol])

コラボレヌ Port ただの Int 蚱容範囲内:

type PortNumber = Refined[Int, Closed[_0, W.`65535`.T]]

掗緎されたタむプ

芋る 掗緎された 図曞通。 ぀たり、他の型にコンパむル時間の制玄を远加できるようになりたす。 この堎合 Int ポヌト番号を衚すこずができる 16 ビット倀のみを持぀こずが蚱可されたす。 この構成アプロヌチでは、このラむブラリを䜿甚する必芁はありたせん。 ずおもよくフィットしおいるようです。

HTTP (REST) の堎合、サヌビスのパスも必芁になる堎合がありたす。

type UrlPathPrefix = Refined[String, MatchesRegex[W.`"[a-zA-Z_0-9/]*"`.T]]
case class PortWithPrefix[Protocol](portNumber: PortNumber, pathPrefix: UrlPathPrefix)

ファントムタむプ

コンパむル䞭にプロトコルを識別するために、型匕数を宣蚀する Scala 機胜を䜿甚しおいたす。 Protocol 授業では䜿わないものです。 いわゆる ファントムタむプ。 実行時にプロトコル識別子のむンスタンスが必芁になるこずはほずんどないため、プロトコル識別子を保存したせん。 コンパむル䞭に、このファントム型は远加の型安党性を提䟛したす。 䞍正なプロトコルではポヌトを枡すこずができたせん。

最も広く䜿甚されおいるプロトコルの XNUMX ぀は、Json シリアル化を䜿甚した REST API です。

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

コラボレヌ RequestMessage クラむアントがサヌバヌに送信できるメッセヌゞの基本タむプです。 ResponseMessage サヌバヌからの応答メッセヌゞです。 もちろん、必芁な粟床で通信プロトコルを指定する他のプロトコル蚘述を䜜成するこずもできたす。

この蚘事では、より単玔なバヌゞョンのプロトコルを䜿甚したす。

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

このプロトコルでは、リク゚スト メッセヌゞが URL に远加され、レスポンス メッセヌゞがプレヌン文字列ずしお返されたす。

サヌビス構成は、サヌビス名、ポヌトのコレクション、およびいく぀かの䟝存関係によっお説明できたす。 Scala でこれらすべおの芁玠を衚珟する方法はいく぀かありたす (たずえば、 HList、代数デヌタ型。 この投皿の目的のために、Cake パタヌンを䜿甚し、組み合わせ可胜なピヌス (モゞュヌル) を特性ずしお衚したす。 (Cake パタヌンは、このコンパむル可胜な構成アプロヌチの芁件ではありたせん。これは、アむデアの XNUMX ぀の可胜な実装にすぎたせん。)

䟝存関係は、他のノヌドの゚ンドポむントずしお Cake パタヌンを䜿甚しお衚すこずができたす。

  type EchoProtocol[A] = SimpleHttpGetRest[A, A]

  trait EchoConfig[A] extends ServiceConfig {
    def portNumber: PortNumber = 8081
    def echoPort: PortWithPrefix[EchoProtocol[A]] = PortWithPrefix[EchoProtocol[A]](portNumber, "echo")
    def echoService: HttpSimpleGetEndPoint[NodeId, EchoProtocol[A]] = providedSimpleService(echoPort)
  }

゚コヌ サヌビスにはポヌトの蚭定のみが必芁です。 そしお、このポヌトが゚コヌ プロトコルをサポヌトしおいるこずを宣蚀したす。 トレむトでは抜象メ゜ッドの宣蚀が蚱可されおいるため、珟時点では特定のポヌトを指定する必芁がないこずに泚意しおください。 抜象メ゜ッドを䜿甚する堎合、コンパむラは構成むンスタンスでの実装を必芁ずしたす。 ここでは実装を提䟛したした (8081) であり、具䜓的な蚭定でスキップした堎合はデフォルト倀ずしお䜿甚されたす。

゚コヌ サヌビス クラむアントの構成で䟝存関係を宣蚀できたす。

  trait EchoClientConfig[A] {
    def testMessage: String = "test"
    def pollInterval: FiniteDuration
    def echoServiceDependency: HttpSimpleGetEndPoint[_, EchoProtocol[A]]
  }

䟝存関係のタむプは echoService。 特に、同じプロトコルが必芁です。 したがっお、これら XNUMX ぀の䟝存関係を接続するず、正しく動䜜するこずが保蚌されたす。

サヌビスの実装

サヌビスには、開始および正垞にシャットダりンするための機胜が必芁です。 (サヌビスをシャットダりンできるこずは、テストには重芁です。) ここでも、特定の構成に察しおそのような関数を指定するオプションがいく぀かありたす (たずえば、型クラスを䜿甚できたす)。 この投皿では、もう䞀床 Cake Pattern を䜿甚したす。 次を䜿甚しおサヌビスを衚すこずができたす cats.Resource これはすでにブラケティングずリ゜ヌスの解攟を提䟛したす。 リ゜ヌスを取埗するには、構成ず実行時コンテキストを提䟛する必芁がありたす。 したがっお、サヌビス開始関数は次のようになりたす。

  type ResourceReader[F[_], Config, A] = Reader[Config, Resource[F, A]]

  trait ServiceImpl[F[_]] {
    type Config
    def resource(
      implicit
      resolver: AddressResolver[F],
      timer: Timer[F],
      contextShift: ContextShift[F],
      ec: ExecutionContext,
      applicative: Applicative[F]
    ): ResourceReader[F, Config, Unit]
  }

コラボレヌ

  • Config — このサヌビススタヌタヌに必芁な構成のタむプ
  • AddressResolver — 他のノヌドの実際のアドレスを取埗する機胜を持぀ランタむム オブゞェクト (詳现に぀いおは読み続けおください)。

他のタむプはから来たす cats:

  • F[_] — ゚フェクトのタむプ (最も単玔なケヌスの堎合) F[A] ただかもしれない () => A。 この投皿では䜿甚したす cats.IO.)
  • Reader[A,B] — 倚かれ少なかれ関数の同矩語です A => B
  • cats.Resource — 取埗および解攟する方法がある
  • Timer — 睡眠/時間を枬定するこずができたす
  • ContextShift - アナログ ExecutionContext
  • Applicative — 実際の関数のラッパヌ (ほがモナド) (最終的には他のものに眮き換える可胜性がありたす)

このむンタヌフェむスを䜿甚するず、いく぀かのサヌビスを実装できたす。 たずえば、䜕もしないサヌビス:

  trait ZeroServiceImpl[F[_]] extends ServiceImpl[F] {
    type Config <: Any
    def resource(...): ResourceReader[F, Config, Unit] =
      Reader(_ => Resource.pure[F, Unit](()))
  }

その他のオプションは ゜ヌスコヌド 他のサヌビス実装の堎合 — ゚コヌサヌビス,
゚コヌクラむアント および ラむフタむムコントロヌラヌ.)

ノヌドは、いく぀かのサヌビスを実行する単䞀のオブゞェクトです (リ゜ヌスのチェヌンの開始は Cake Pattern によっお有効になりたす)。

object SingleNodeImpl extends ZeroServiceImpl[IO]
  with EchoServiceService
  with EchoClientService
  with FiniteDurationLifecycleServiceImpl
{
  type Config = EchoConfig[String] with EchoClientConfig[String] with FiniteDurationLifecycleConfig
}

ノヌドでは、このノヌドに必芁な構成の正確なタむプを指定しおいるこずに泚意しおください。 各サヌビス特性が制玄を宣蚀しおいるため、コンパむラは䞍十分な型でオブゞェクト (Cake) を構築できたせん。 Config タむプ。 たた、完党な構成を提䟛しないずノヌドを起動できたせん。

ノヌドアドレス解決

接続を確立するには、各ノヌドの実際のホスト アドレスが必芁です。 これは、構成の他の郚分よりも埌で刀明する可胜性がありたす。 したがっお、ノヌド ID ずその実際のアドレスの間のマッピングを提䟛する方法が必芁です。 このマッピングは関数です。

case class NodeAddress[NodeId](host: Uri.Host)
trait AddressResolver[F[_]] {
  def resolve[NodeId](nodeId: NodeId): F[NodeAddress[NodeId]]
}

このような関数を実装するにはいく぀かの方法がありたす。

  1. デプロむメント前、぀たりノヌドホストのむンスタンス化䞭に実際のアドレスがわかっおいる堎合は、実際のアドレスを䜿甚しお Scala コヌドを生成し、その埌ビルドを実行できたす (これにより、コンパむル時のチェックが実行され、その埌、統合テスト スむヌトが実行されたす)。 この堎合、マッピング関数は静的に知られおおり、次のように単玔化できたす。 Map[NodeId, NodeAddress].
  2. 堎合によっおは、ノヌドが実際に開始される埌の時点でのみ実際のアドレスを取埗するこずも、ただ開始されおいないノヌドのアドレスを取埗しおいないこずもありたす。 この堎合、他のすべおのノヌドよりも先に開始される怜出サヌビスがあり、各ノヌドがそのサヌビス内で自分のアドレスをアドバタむズし、䟝存関係にサブスクラむブする可胜性がありたす。
  3. 修正できれば /etc/hosts、事前定矩されたホスト名を䜿甚できたす (䟋: my-project-main-node および echo-backend、展開時にこの名前を IP アドレスに関連付けるだけです。

この投皿では、これらのケヌスに぀いおは詳しく説明したせん。 実際、私たちのおもちゃの䟋では、すべおのノヌドが同じ IP アドレスを持ちたす。 127.0.0.1.

この投皿では、XNUMX ぀の分散システム レむアりトに぀いお怜蚎したす。

  1. 単䞀ノヌド レむアりト。すべおのサヌビスが単䞀ノヌドに配眮されたす。
  2. サヌビスずクラむアントが異なるノヌド䞊にある XNUMX ノヌド レむアりト。

の構成 シングルノヌド レむアりトは次のずおりです。

シングルノヌド構成

object SingleNodeConfig extends EchoConfig[String] 
  with EchoClientConfig[String] with FiniteDurationLifecycleConfig
{
  case object Singleton // identifier of the single node 
  // configuration of server
  type NodeId = Singleton.type
  def nodeId = Singleton

  /** Type safe service port specification. */
  override def portNumber: PortNumber = 8088

  // configuration of client

  /** We'll use the service provided by the same host. */
  def echoServiceDependency = echoService

  override def testMessage: UrlPathElement = "hello"

  def pollInterval: FiniteDuration = 1.second

  // lifecycle controller configuration
  def lifetime: FiniteDuration = 10500.milliseconds // additional 0.5 seconds so that there are 10 requests, not 9.
}

ここでは、サヌバヌずクラむアントの䞡方の構成を拡匵する単䞀の構成を䜜成したす。 たた、通垞はクラむアントずサヌバヌを終了するラむフサむクル コントロヌラヌを構成したす。 lifetime むンタヌバルが経過したす。

同じサヌビスの実装ず構成のセットを䜿甚しお、XNUMX ぀の別個のノヌドを含むシステムのレむアりトを䜜成できたす。 あずは䜜成するだけです XNUMX ぀の別々のノヌド構成 適切なサヌビスを利甚しお:

XNUMXノヌド構成

  object NodeServerConfig extends EchoConfig[String] with SigTermLifecycleConfig
  {
    type NodeId = NodeIdImpl

    def nodeId = NodeServer

    override def portNumber: PortNumber = 8080
  }

  object NodeClientConfig extends EchoClientConfig[String] with FiniteDurationLifecycleConfig
  {
    // NB! dependency specification
    def echoServiceDependency = NodeServerConfig.echoService

    def pollInterval: FiniteDuration = 1.second

    def lifetime: FiniteDuration = 10500.milliseconds // additional 0.5 seconds so that there are 10 request, not 9.

    def testMessage: String = "dolly"
  }

䟝存関係を指定する方法を確認しおください。 珟圚のノヌドの䟝存関係ずしお、他のノヌドが提䟛するサヌビスに぀いお蚀及したす。 䟝存関係のタむプは、プロトコルを蚘述するファントム タむプが含たれおいるためチェックされたす。 そしお実行時には正しいノヌド ID が埗られたす。 これは、提案された構成アプロヌチの重芁な偎面の XNUMX ぀です。 これにより、ポヌトを XNUMX 回だけ蚭定しお、正しいポヌトを参照しおいるこずを確認できるようになりたす。

XNUMX ぀のノヌドの実装

この構成では、たったく同じサヌビス実装を䜿甚したす。 たったく倉化はありたせん。 ただし、異なるサヌビス セットを含む XNUMX ぀の異なるノヌド実装を䜜成したす。

  object TwoJvmNodeServerImpl extends ZeroServiceImpl[IO] with EchoServiceService with SigIntLifecycleServiceImpl {
    type Config = EchoConfig[String] with SigTermLifecycleConfig
  }

  object TwoJvmNodeClientImpl extends ZeroServiceImpl[IO] with EchoClientService with FiniteDurationLifecycleServiceImpl {
    type Config = EchoClientConfig[String] with FiniteDurationLifecycleConfig
  }

最初のノヌドはサヌバヌを実装しおおり、サヌバヌ偎の構成のみが必芁です。 XNUMX 番目のノヌドはクラむアントを実装しおおり、構成の別の郚分が必芁です。 どちらのノヌドにも、䜕らかのラむフタむム仕様が必芁です。 このポストサヌビスの目的のために、ノヌドは無限の存続期間を持ち、次を䜿甚しお終了できたす。 SIGTERM䞀方、゚コヌ クラむアントは、蚭定された有限の期間が経過するず終了したす。 を参照しおください。 スタヌタヌアプリケヌション 詳现に぀いおは。

党䜓的な開発プロセス

このアプロヌチによっお構成の䜜業方法がどのように倉わるかを芋おみたしょう。

コヌドずしおの構成がコンパむルされ、アヌティファクトが生成されたす。 構成アヌティファクトを他のコヌドアヌティファクトから分離するのが合理的ず思われたす。 倚くの堎合、同じコヌド ベヌス䞊に倚数の構成を含めるこずができたす。 そしおもちろん、さたざたな構成ブランチの耇数のバヌゞョンを持぀こずができたす。 構成では、ラむブラリの特定のバヌゞョンを遞択できたす。これは、この構成をデプロむするたびに䞀定に保たれたす。

構成の倉曎はコヌドの倉曎になりたす。 したがっお、同じ品質保蚌プロセスでカバヌされる必芁がありたす。

チケット -> PR -> レビュヌ -> マヌゞ -> 継続的むンテグレヌション -> 継続的デプロむメント

このアプロヌチには次のような圱響がありたす。

  1. 構成は、特定のシステムのむンスタンスに察しお䞀貫しおいたす。 ノヌド間の接続が間違っおいるこずはないようです。
  2. XNUMX ぀のノヌドだけで構成を倉曎するのは簡単ではありたせん。 ログむンしおテキストファむルを倉曎するのは無理があるようです。 したがっお、構成のドリフトは起こりにくくなりたす。
  3. 小さな構成倉曎は簡単ではありたせん。
  4. 構成倉曎のほずんどは同じ開発プロセスに埓い、ある皋床のレビュヌに合栌したす。

運甚環境蚭定甚に別のリポゞトリが必芁ですか? 実皌働構成には、倚くの人が觊れないようにしたい機密情報が含たれおいる堎合がありたす。 したがっお、実皌働構成を含む、アクセスが制限された別のリポゞトリを保持する䟡倀があるかもしれたせん。 蚭定を XNUMX ぀の郚分に分割する堎合がありたす。XNUMX ぀は本番環境の最もオヌプンなパラメヌタを含み、もう XNUMX ぀は蚭定の秘密郚分を含みたす。 これにより、ほずんどの開発者は倧郚分のパラメヌタにアクセスできるようになりたすが、本圓に機密性の高いものぞのアクセスは制限されたす。 デフォルトのパラメヌタ倀を持぀䞭間特性を䜿甚するず、これを簡単に実珟できたす。

バリ゚ヌション

他の構成管理手法ず比范しお、提案されたアプロヌチの長所ず短所を芋おみたしょう。

たず最初に、提案されおいる構成凊理方法のさたざたな偎面に察する代替案をいく぀かリストしたす。

  1. タヌゲット マシン䞊のテキスト ファむル。
  2. 䞀元化されたキヌず倀のストレヌゞ ( etcd/zookeeper).
  3. プロセスを再起動せずに再構成/再起動できるサブプロセス コンポヌネント。
  4. アヌティファクトおよびバヌゞョン管理倖の構成。

テキスト ファむルを䜿甚するず、アドホックな修正に関しおある皋床の柔軟性が埗られたす。 システム管理者はタヌゲット ノヌドにログむンし、倉曎を加えおサヌビスを再起動するだけで枈みたす。 これは、倧芏暡なシステムではあたり良くない可胜性がありたす。 倉曎の痕跡は残りたせん。 倉曎は別の目で確認されたせん。 䜕が倉化を匕き起こしたのかを芋぀けるのは難しいかもしれたせん。 テストはされおいたせん。 分散システムの芳点から芋るず、管理者は他のノヌドの XNUMX ぀で構成を曎新するこずを単玔に忘れる可胜性がありたす。

(ずころで、最終的にテキスト構成ファむルの䜿甚を開始する必芁がある堎合は、同じものを生成できるパヌサヌずバリデヌタヌを远加するだけで枈みたす。 Config ず入力するだけで、テキスト構成の䜿甚を開始できたす。 これは、テキストベヌスのバヌゞョンでは远加のコヌドが必芁なため、コンパむル時の構成の耇雑さがテキストベヌスの構成の耇雑さよりもわずかに小さいこずも瀺しおいたす)。

䞀元化されたキヌず倀のストレヌゞは、アプリケヌションのメタ パラメヌタヌを配垃するための優れたメカニズムです。 ここで、䜕が構成倀であるか、䜕が単なるデヌタであるかを考える必芁がありたす。 䞎えられた関数 C => A => B 私たちは通垞、めったに倉化しない倀を「倀」ず呌びたす。 C 「構成」、頻繁に倉曎されるデヌタ A - デヌタを入力するだけです。 蚭定はデヌタよりも前に関数に提䟛する必芁がありたす A。 この考えを螏たえるず、構成デヌタず単なるデヌタを区別するために䜿甚できるのは、予想される倉曎の頻床であるず蚀えたす。 たた、通垞、デヌタは XNUMX ぀の゜ヌス (ナヌザヌ) から取埗され、構成は別の゜ヌス (管理者) から取埗されたす。 初期化プロセス埌に倉曎できるパラメヌタを凊理するず、アプリケヌションの耇雑さが増加したす。 このようなパラメヌタの堎合は、その配信メカニズム、解析ず怜蚌、䞍正な倀の凊理を凊理する必芁がありたす。 したがっお、プログラムの耇雑さを軜枛するには、実行時に倉曎できるパラメヌタヌの数を枛らす (たたは完党に削陀する) 方が良いでしょう。

この投皿の芳点からは、静的パラメヌタヌず動的パラメヌタヌを区別する必芁がありたす。 サヌビス ロゞックで実行時に䞀郚のパラメヌタヌをたれに倉曎する必芁がある堎合、それらを動的パラメヌタヌず呌ぶこずがありたす。 それ以倖の堎合、それらは静的であり、提案されたアプロヌチを䜿甚しお構成できたす。 動的再構成には、他のアプロヌチが必芁になる堎合がありたす。 たずえば、分散システムの個別のプロセスを再起動するのず同様の方法で、システムの䞀郚が新しい構成パラメヌタを䜿甚しお再起動される堎合がありたす。
(私の謙虚な意芋は、システムの耇雑さが増すため、実行時の再構成は避けるべきです。
プロセスを再起動するための OS サポヌトに䟝存する方が簡単かもしれたせん。 ただし、垞に可胜であるずは限りたせん。)

静的構成を䜿甚する堎合、(他の理由がないのに) 動的構成を考慮するこずがある重芁な偎面の XNUMX ぀は、構成曎新䞭のサヌビスのダりンタむムです。 実際、静的構成を倉曎する必芁がある堎合は、新しい倀が有効になるようにシステムを再起動する必芁がありたす。 ダりンタむムの芁件はシステムによっお異なるため、それほど重芁ではない可胜性がありたす。 それが重芁な堎合は、システムの再起動を事前に蚈画する必芁がありたす。 たずえば、次のように実装できたす。 AWS ELB 接続のドレむン。 このシナリオでは、システムを再起動する必芁があるずきはい぀でも、システムの新しいむンスタンスを䞊行しお起動し、ELB をそれに切り替えたす。その間、叀いシステムが既存の接続のサヌビスを完了できるようにしたす。

構成をバヌゞョン管理されたアヌティファクトの内郚たたは倖郚に保持する堎合はどうすればよいでしょうか? 構成をアヌティファクト内に保持するずいうこずは、ほずんどの堎合、この構成が他のアヌティファクトず同じ品質保蚌プロセスに合栌しおいるこずを意味したす。 したがっお、構成が高品質で信頌できるものであるず確信できるかもしれたせん。 逆に、別のファむルに構成を䜜成するず、そのファむルに誰が、なぜ倉曎を加えたかの痕跡が残らないこずになりたす。 これは重芁ですか? 私たちは、ほずんどの実皌働システムでは、安定した高品質の構成を䜿甚するこずが望たしいず考えおいたす。

アヌティファクトのバヌゞョンにより、アヌティファクトがい぀䜜成されたか、どのような倀が含たれおいるか、どの機胜が有効/無効になっおいるか、構成の各倉曎を誰が行ったかを知るこずができたす。 アヌティファクト内に構成を保持するにはある皋床の努力が必芁になる堎合がありたすが、これは蚭蚈䞊の遞択ずなりたす。

長所短所

ここでは、提案されたアプロヌチのいく぀かの利点を匷調し、いく぀かの欠点に぀いお説明したいず思いたす。

Advantages

完党な分散システムのコンパむル可胜な構成の特城:

  1. 構成の静的チェック。 これにより、型制玄が䞎えられた堎合に構成が正しいずいう高いレベルの信頌が埗られたす。
  2. 豊富な蚭定蚀語。 通垞、他の構成アプロヌチは最倧でも倉数眮換に限定されたす。
    Scala を䜿甚するず、幅広い蚀語機胜を䜿甚しお構成を改善できたす。 たずえば、特性を䜿甚しおデフォルト倀を提䟛したり、オブゞェクトを䜿甚しお異なるスコヌプを蚭定したりできたす。 val■ 倖偎のスコヌプ (DRY) で XNUMX 回だけ定矩されたす。 リテラル シヌケンスたたは特定のクラスのむンスタンスを䜿甚するこずができたす (Seq, Mapなど。
  3. DSL。 Scala は DSL ラむタヌを適切にサポヌトしおいたす。 これらの機胜を䜿甚しお、より䟿利で゚ンドナヌザヌに優しい構成蚀語を確立し、最終構成が少なくずもドメむン ナヌザヌに読み取れるようにするこずができたす。
  4. ノヌド間の敎合性ず䞀貫性。 分散システム党䜓の構成を XNUMX か所で行う利点の XNUMX ぀は、すべおの倀が厳密に䞀床定矩され、必芁なすべおの堎所で再利甚できるこずです。 たた、タむプ セヌフ ポヌト宣蚀により、考えられるすべおの正しい構成でシステムのノヌドが同じ蚀語を話すこずが保蚌されたす。 ノヌド間には明瀺的な䟝存関係があるため、䞀郚のサヌビスの提䟛を忘れにくくなりたす。
  5. 高品質な倉曎。 通垞の PR プロセスを通じお構成倉曎を枡すずいう党䜓的なアプロヌチにより、構成においおも高い品質基準が確立されたす。
  6. 同時構成倉曎。 構成に倉曎を加えるたびに、自動デプロむメントによっおすべおのノヌドが確実に曎新されたす。
  7. アプリケヌションの簡玠化。 アプリケヌションは構成を解析しお怜蚌したり、間違った構成倀を凊理したりする必芁がありたせん。 これにより、アプリケヌション党䜓が簡玠化されたす。 (構成自䜓が倚少耇雑になりたすが、これは安党性ずの意識的なトレヌドオフです。) 通垞の構成に戻すのは非垞に簡単で、䞍足しおいる郚分を远加するだけです。 コンパむルされた構成から開始し、远加郚分の実装を埌で延期する方が簡単です。
  8. バヌゞョン管理された構成。 構成の倉曎は同じ開発プロセスに埓うため、結果ずしお固有のバヌゞョンのアヌティファクトが埗られたす。 これにより、必芁に応じお蚭定を元に戻すこずができたす。 XNUMX 幎前に䜿甚された構成をデプロむするこずもでき、たったく同じように機胜したす。 安定した構成により、分散システムの予枬可胜性ず信頌性が向䞊したす。 構成はコンパむル時に固定され、運甚システムでは簡単に改ざんできたせん。
  9. モゞュヌル性。 提案されたフレヌムワヌクはモゞュヌル匏であり、モゞュヌルはさたざたな方法で組み合わせるこずができたす。
    さたざたな構成 (セットアップ/レむアりト) をサポヌトしたす。 特に、小芏暡なシングルノヌドのレむアりトず倧芏暡なマルチノヌドの蚭定が可胜です。 耇数の運甚レむアりトを䜿甚するこずは合理的です。
  10. テスト䞭。 テスト目的では、モック サヌビスを実装し、それを型安党な方法で䟝存関係ずしお䜿甚する堎合がありたす。 さたざたな郚分をモックに眮き換えた、いく぀かの異なるテスト レむアりトを同時に維持できたす。
  11. 統合テスト。 分散システムでは、統合テストの実行が難しい堎合がありたす。 完党な分散システムのタむプ セヌフ構成に説明したアプロヌチを䜿甚するず、すべおの分散郚分を単䞀のサヌバヌ䞊で制埡可胜な方法で実行できたす。 状況を真䌌するのは簡単です
    いずれかのサヌビスが利甚できなくなったずき。

デメリット

コンパむルされた構成アプロヌチは「通垞の」構成ずは異なるため、すべおのニヌズに適合するずは限りたせん。 コンパむルされた構成の欠点のいく぀かを次に瀺したす。

  1. 静的構成。 すべおのアプリケヌションに適しおいるわけではないかもしれたせん。 堎合によっおは、すべおの安党察策を回避しお、運甚環境の構成を迅速に修正する必芁がありたす。 このアプロヌチでは䜜業がより困難になりたす。 構成を倉曎した埌は、コンパむルず再デプロむメントが必芁です。 これが特城でもあり、負担でもありたす。
  2. 構成の生成。 構成が䜕らかの自動化ツヌルによっお生成される堎合、このアプロヌチではその埌のコンパむルが必芁になりたす (結果的に倱敗する可胜性がありたす)。 この远加手順をビルド システムに統合するには、远加の䜜業が必芁になる堎合がありたす。
  3. 楜噚。 珟圚、テキストベヌスの構成に䟝存するツヌルが数倚く䜿甚されおいたす。 それらのいく぀か
    構成がコンパむルされるずきは適甚されたせん。
  4. 考え方の転換が必芁です。 開発者ず DevOps はテキスト構成ファむルに粟通しおいたす。 圌らにずっお、蚭定をコンパむルするずいう考えは奇劙に芋えるかもしれたせん。
  5. コンパむル可胜な構成を導入する前に、高品質の゜フトりェア開発プロセスが必芁です。

実装された䟋にはいく぀かの制限がありたす。

  1. ノヌド実装に芁求されおいない远加の構成を提䟛した堎合、コンパむラヌは欠萜しおいる実装を怜出するのに圹立ちたせん。 これは次の方法で解決できたす。 HList たたは、トレむトず Cake Pattern の代わりにノヌド構成甚の ADT (ケヌス クラス)。
  2. 蚭定ファむルに定型文を指定する必芁がありたす:(package, import, object 宣蚀;
    override defはデフォルト倀を持぀パラメヌタを衚したす)。 これは、DSL を䜿甚しお郚分的に察凊できる可胜性がありたす。
  3. この投皿では、同様のノヌドのクラスタヌの動的再構成に぀いおは説明したせん。

たずめ

この投皿では、タむプセヌフな方法で゜ヌス コヌド内で構成を盎接衚すずいうアむデアに぀いお説明したした。 このアプロヌチは、XML やその他のテキストベヌスの構成の代わりずしお、倚くのアプリケヌションで䜿甚できたす。 この䟋は Scala で実装されおいたすが、他のコンパむル可胜な蚀語 (Kotlin、C#、Swift など) に翻蚳するこずもできたす。 新しいプロゞェクトでこのアプロヌチを詊しお、うたく適合しない堎合は、昔ながらの方法に切り替えるこずもできたす。

もちろん、コンパむル可胜な構成には高品質の開発プロセスが必芁です。 その代わりに、同様に高品質で堅牢な構成を提䟛するこずを玄束したす。

このアプロヌチはさたざたな方法で拡匵できたす。

  1. マクロを䜿甚しお構成怜蚌を実行し、ビゞネス ロゞック制玄が倱敗した堎合にはコンパむル時に倱敗する可胜性がありたす。
  2. DSL を実装するず、ドメむン ナヌザヌにわかりやすい方法で構成を衚珟できたす。
  3. 自動構成調敎による動的なリ゜ヌス管理。 たずえば、クラスタヌ ノヌドの数を調敎する堎合、(1) ノヌドの構成をわずかに倉曎する必芁があるかもしれたせん。 (2) クラスタヌマネヌゞャヌが新しいノヌド情報を受信したす。

感謝

この投皿の草皿に察しお、私がより明確にするのに圹立぀むンスピレヌションに満ちたフィヌドバックを䞎えおくれた Andrey Saksonov、Pavel Popov、Anton Nehaev に感謝したいず思いたす。

出所 habr.com