Тархсан системийн эмхэтгэх боломжтой тохиргоо

Энэ нийтлэлд бид тархсан системийн тохиргоотой ажиллах сонирхолтой аргыг хуваалцахыг хүсч байна.
Тохиргоог шууд Scala хэл дээр аюулгүй хэлбэрээр харуулсан болно. Хэрэгжилтийн жишээг дэлгэрэнгүй тайлбарласан болно. Саналын янз бүрийн асуудлыг хэлэлцсэн бөгөөд үүнд ерөнхий хөгжлийн үйл явцад үзүүлэх нөлөөлөл багтсан болно.

Тархсан системийн эмхэтгэх боломжтой тохиргоо

(орос хэл дээр)

Оршил

Бат бөх тархсан системийг бий болгохын тулд бүх зангилаа дээр зөв, уялдаатай тохиргоог ашиглах шаардлагатай. Ердийн шийдэл бол байршуулалтын текстэн тайлбар (terraform, ansible эсвэл үүнтэй төстэй зүйл) болон автоматаар үүсгэгдсэн тохиргооны файлуудыг (ихэвчлэн зангилаа/ үүрэг тус бүрт зориулагдсан) ашиглах явдал юм. Бид мөн ижил хувилбаруудын ижил протоколуудыг харилцах зангилаа бүр дээр ашиглахыг хүсч байна (эсвэл бид үл нийцэх асуудалтай тулгарах болно). JVM ертөнцөд энэ нь дор хаяж мессежийн номын сан нь бүх харилцах зангилаа дээрх ижил хувилбартай байх ёстой гэсэн үг юм.

Системийг турших талаар юу хэлэх вэ? Мэдээжийн хэрэг, бид интеграцийн шалгалтанд орохын өмнө бүх бүрэлдэхүүн хэсгүүдийн нэгжийн туршилтыг хийх ёстой. Туршилтын үр дүнг ажлын цаг дээр экстраполяци хийх боломжтой байхын тулд бид бүх номын сангийн хувилбаруудыг ажиллах хугацаа болон туршилтын орчинд ижил байлгах ёстой.

Интеграцийн тестийг ажиллуулахдаа бүх зангилаанууд дээр ижил ангиллын замтай байх нь ихэвчлэн илүү хялбар байдаг. Бид зүгээр л байршуулахдаа ижил ангиллын замыг ашиглаж байгаа эсэхийг шалгах хэрэгтэй. (Өөр өөр зангилаанууд дээр өөр өөр ангиллын замыг ашиглах боломжтой боловч энэ тохиргоог дүрсэлж, зөв ​​байрлуулах нь илүү хэцүү байдаг.) ​​Тиймээс бүх зүйлийг энгийн байлгахын тулд бид зөвхөн бүх зангилаа дээрх ижил ангиллын замыг авч үзэх болно.

Програм хангамжтай хамт тохиргоо нь хөгжих хандлагатай байдаг. Бид янз бүрийн хувилбаруудыг тодорхойлохдоо ихэвчлэн ашигладаг
програм хангамжийн хувьслын үе шатууд. Хувилбарын удирдлагын хүрээнд тохиргоог хамруулж, зарим шошготой өөр тохиргоог тодорхойлох нь зүйтэй юм шиг санагдаж байна. Хэрэв үйлдвэрлэлд зөвхөн нэг тохиргоо байгаа бол бид танигч болгон нэг хувилбарыг ашиглаж болно. Заримдаа бид олон үйлдвэрлэлийн орчинтой байж болно. Мөн орчин бүрийн хувьд тохиргооны тусдаа салбар хэрэгтэй байж магадгүй юм. Тиймээс өөр өөр тохиргоог өвөрмөц байдлаар тодорхойлохын тулд тохиргоог салбар болон хувилбараар тэмдэглэж болно. Салбарын шошго, хувилбар бүр нь зангилаа тус бүрийн тархсан зангилаа, порт, гадаад нөөц, ангийн замын номын сангийн хувилбаруудын нэг хослолтой тохирч байна. Энд бид зөвхөн нэг салбарыг хамарч, бусад олдворуудын нэгэн адил гурван бүрэлдэхүүн хэсгийн аравтын бутархай хувилбараар (1.2.3) тохиргоог тодорхойлох болно.

Орчин үеийн орчинд тохиргооны файлуудыг гараар өөрчлөхөө больсон. Ихэвчлэн бид үүсгэдэг
тохиргооны файлуудыг байршуулах үед болон тэдэнд хэзээ ч бүү хүр дараа нь. Тиймээс бид яагаад тохиргооны файлд текст форматыг ашигласаар байгаа вэ? Боломжтой сонголт бол тохиргоог эмхэтгэлийн нэгж дотор байрлуулж, хөрвүүлэх хугацааны тохиргооны баталгаажуулалтаас ашиг хүртэх явдал юм.

Энэ нийтлэлд бид эмхэтгэсэн олдвор дахь тохиргоог хадгалах санааг авч үзэх болно.

Эмхэтгэх боломжтой тохиргоо

Энэ хэсэгт бид статик тохиргооны жишээг авч үзэх болно. Хоёр энгийн үйлчилгээ - цуурай үйлчилгээ болон цуурай үйлчилгээний үйлчлүүлэгчийг тохируулж, хэрэгжүүлж байна. Дараа нь хоёр үйлчилгээтэй хоёр өөр тархсан системийг үүсгэнэ. Нэг нь нэг зангилааны тохиргоонд, нөгөө нь хоёр зангилааны тохиргоонд зориулагдсан.

Ердийн тархсан систем нь хэд хэдэн зангилаанаас бүрдэнэ. Зангилааг зарим төрлийг ашиглан тодорхойлж болно:

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)

Phantom төрөл

Эмхэтгэх явцад протоколыг тодорхойлохын тулд бид төрлийн аргументыг зарлах Scala функцийг ашиглаж байна. Protocol Энэ нь ангид ашиглагддаггүй. Энэ нь ийм нэртэй юм хий үзэгдэл төрөл. Ажиллаж байх үед бидэнд протокол танигчийн жишээ ховор байдаг тул бид үүнийг хадгалдаггүй. Эмхэтгэлийн явцад энэ хийсвэр төрөл нь нэмэлт төрлийн аюулгүй байдлыг хангадаг. Бид портыг буруу протоколоор дамжуулж чадахгүй.

Хамгийн өргөн хэрэглэгддэг протоколуудын нэг бол Json цуваачлалтай REST API юм.

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

хаана RequestMessage нь үйлчлүүлэгчийн сервер рүү илгээж болох мессежийн үндсэн төрөл юм ResponseMessage серверээс ирсэн хариу мессеж юм. Мэдээжийн хэрэг, бид харилцааны протоколыг хүссэн нарийвчлалтайгаар тодорхойлсон бусад протоколын тайлбарыг үүсгэж болно.

Энэ нийтлэлийн зорилгоор бид протоколын энгийн хувилбарыг ашиглах болно:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

Энэ протоколд хүсэлтийн мессежийг url-д хавсаргасан бөгөөд хариу мессежийг энгийн мөр болгон буцаана.

Үйлчилгээний тохиргоог үйлчилгээний нэр, портуудын цуглуулга болон зарим хамаарлаар тодорхойлж болно. Эдгээр бүх элементүүдийг Scala-д хэрхэн дүрслэх хэд хэдэн боломжит арга байдаг (жишээлбэл, HList, алгебрийн өгөгдлийн төрлүүд). Энэ нийтлэлийн зорилгын үүднээс бид бялууны хэв маягийг ашиглаж, хосолсон хэсгүүдийг (модуль) шинж чанар болгон харуулах болно. (Бялууны загвар нь эмхэтгэх боломжтой тохиргооны аргад тавигдах шаардлага биш юм. Энэ бол санааг хэрэгжүүлэх боломжуудын нэг юм.)

Хамааралтай байдлыг бусад зангилааны төгсгөлийн цэг болгон Бялууны загварыг ашиглан төлөөлж болно:

  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)
  }

Цуурай үйлчилгээнд зөвхөн тохируулсан порт хэрэгтэй. Мөн бид энэ порт echo протоколыг дэмждэг гэдгийг мэдэгдэж байна. Шинж чанар нь хийсвэр аргуудын мэдэгдлийг зөвшөөрдөг тул бид одоогоор тодорхой портыг зааж өгөх шаардлагагүй гэдгийг анхаарна уу. Хэрэв бид хийсвэр аргуудыг ашиглавал хөрвүүлэгч нь тохиргооны жишээнд хэрэгжилтийг шаарддаг. Энд бид хэрэгжилтийг хангасан (8081) бөгөөд хэрэв бид үүнийг тодорхой тохиргоонд алгасвал өгөгдмөл утга болгон ашиглах болно.

Бид echo үйлчилгээний клиентийн тохиргоонд хамаарлыг зарлаж болно:

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

Хараат байдал нь echoService. Тодруулбал, ижил протоколыг шаардаж байна. Тиймээс, хэрэв бид эдгээр хоёр хамаарлыг холбовол тэдгээр нь зөв ажиллах болно гэдэгт итгэлтэй байж болно.

Үйлчилгээний хэрэгжилт

Үйлчилгээг эхлүүлэх, хаах функц хэрэгтэй. (Үйлчилгээг унтраах чадвар нь тест хийхэд маш чухал юм.) Өгөгдсөн тохиргоонд ийм функцийг зааж өгөх хэд хэдэн сонголт байдаг (жишээлбэл, бид төрлийн ангиллыг ашиглаж болно). Энэ нийтлэлд бид бялууны загварыг дахин ашиглах болно. Бид үйлчилгээг ашиглан төлөөлөх боломжтой 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](()))
  }

(Үзнэ үү Эх код бусад үйлчилгээний хэрэгжилтэд - цуурай үйлчилгээ,
echo үйлчлүүлэгч болон насан туршийн хянагч.)

Зангилаа нь хэд хэдэн үйлчилгээг ажиллуулдаг нэг объект юм (Бялууны загвараар нөөцийн сүлжээг эхлүүлэх боломжтой):

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

Зангилаанд бид энэ зангилаанд шаардлагатай тохиргооны төрлийг яг таг зааж өгөхийг анхаарна уу. Үйлчилгээний шинж чанар бүр нь хязгаарлалтыг зарладаг тул хөрвүүлэгч бидэнд хангалтгүй төрлийн объект (Бялуу) барихыг зөвшөөрөхгүй. 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.

Энэ нийтлэлд бид хоёр тархсан системийн зохион байгуулалтыг авч үзэх болно:

  1. Бүх үйлчилгээг нэг зангилаа дээр байрлуулсан нэг зангилааны байршил.
  2. Үйлчилгээ болон үйлчлүүлэгч өөр өөр цэг дээр байрлах хоёр зангилааны байршил.

А-д зориулсан тохиргоо нэг зангилаа зохион байгуулалт дараах байдалтай байна.

Нэг зангилааны тохиргоо

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 интервал өнгөрдөг.

Хоёр тусдаа зангилаа бүхий системийн зохион байгуулалтыг бий болгоход ижил үйлчилгээний хэрэгжилт, тохиргоог ашиглаж болно. Бид бүтээх л хэрэгтэй хоёр тусдаа зангилааны тохиргоо зохих үйлчилгээгээр:

Хоёр зангилааны тохиргоо

  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"
  }

Бид хамаарлыг хэрхэн тодорхойлохыг харна уу. Одоогийн зангилааны хамаарал гэж бид нөгөө зангилааны үзүүлсэн үйлчилгээг дурьдаж байна. Протоколыг дүрсэлсэн хийсвэр төрлийг агуулсан тул хамаарлын төрлийг шалгана. Мөн ажиллах үед бид зөв зангилааны дугаартай болно. Энэ нь санал болгож буй тохиргооны аргын чухал талуудын нэг юм. Энэ нь бидэнд портыг зөвхөн нэг удаа тохируулах боломжийг олгодог бөгөөд бид зөв портыг лавлаж байгаа эсэхийг шалгах боломжийг олгодог.

Хоёр зангилааны хэрэгжилт

Энэ тохиргооны хувьд бид яг ижил үйлчилгээний хэрэгжилтийг ашигладаг. Өөрчлөлт огт байхгүй. Гэсэн хэдий ч бид өөр өөр үйлчилгээг агуулсан хоёр өөр зангилааны хэрэгжилтийг бий болгодог:

  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
  }

Эхний зангилаа нь серверийг хэрэгжүүлдэг бөгөөд үүнд зөвхөн серверийн талын тохиргоо хэрэгтэй. Хоёр дахь зангилаа нь үйлчлүүлэгчийг хэрэгжүүлдэг бөгөөд тохиргооны өөр хэсэг хэрэгтэй. Хоёр зангилаа нь насан туршийн тодорхой үзүүлэлт шаарддаг. Энэхүү шуудангийн үйлчилгээний зангилаа нь хязгааргүй ашиглалтын хугацаатай байх бөгөөд үүнийг ашиглан дуусгавар болгож болно SIGTERM, харин echo клиент нь тохируулсан хязгаарлагдмал хугацааны дараа зогсох болно. -г үзнэ үү эхлүүлэх програм Дэлгэрэнгүй мэдээллийг.

Хөгжлийн ерөнхий үйл явц

Энэ арга нь тохиргоотой ажиллах арга барилыг хэрхэн өөрчлөхийг харцгаая.

Кодын тохиргоог эмхэтгэн олдворыг гаргана. Тохируулгын олдворыг бусад кодын олдворуудаас салгах нь үндэслэлтэй юм шиг санагддаг. Ихэнхдээ бид ижил кодын суурь дээр олон тооны тохиргоотой байж болно. Мэдээжийн хэрэг, бид янз бүрийн тохиргооны салбаруудын олон хувилбартай байж болно. Тохиргоонд бид номын сангийн тодорхой хувилбаруудыг сонгож болох ба энэ тохиргоог ашиглах бүрт энэ нь тогтмол хэвээр байх болно.

Тохиргооны өөрчлөлт нь кодын өөрчлөлт болно. Тиймээс энэ нь ижил чанарын баталгаажуулалтын процесст хамрагдах ёстой:

Тасалбар -> PR -> хянан үзэх -> нэгтгэх -> тасралтгүй нэгтгэх -> тасралтгүй байршуулах

Энэ хандлагын дараах үр дагавар бий.

  1. Тохиргоо нь тодорхой системийн жишээнд нийцтэй байна. Зангилааны хооронд буруу холболт хийх боломжгүй юм шиг санагддаг.
  2. Зөвхөн нэг зангилаа дотор тохиргоог өөрчлөх нь тийм ч хялбар биш юм. Нэвтрэх, зарим текст файлыг өөрчлөх нь үндэслэлгүй юм шиг санагдаж байна. Тиймээс тохиргооны шилжилт хийх боломж багасна.
  3. Тохиргооны жижиг өөрчлөлтийг хийхэд тийм ч хялбар биш юм.
  4. Ихэнх тохиргооны өөрчлөлтүүд нь ижил хөгжүүлэлтийн процессыг дагах бөгөөд энэ нь зарим хяналтыг давах болно.

Бидэнд үйлдвэрлэлийн тохиргоонд тусдаа агуулах хэрэгтэй юу? Үйлдвэрлэлийн тохиргоо нь бидний олон хүнд хүрэхгүй байхыг хүсдэг нууц мэдээллийг агуулж болзошгүй. Тиймээс үйлдвэрлэлийн тохиргоог агуулсан хязгаарлагдмал хандалттай тусдаа репозитор байлгах нь зүйтэй болов уу. Бид тохиргоог хоёр хэсэгт хувааж болно - нэг нь үйлдвэрлэлийн хамгийн нээлттэй параметрүүдийг агуулсан, нөгөө нь тохиргооны нууц хэсгийг агуулдаг. Энэ нь ихэнх хөгжүүлэгчид параметрийн дийлэнх хэсэгт хандах боломжийг олгохын зэрэгцээ үнэхээр эмзэг зүйлд хандах хандалтыг хязгаарлах болно. Өгөгдмөл параметрийн утгууд бүхий завсрын шинж чанаруудыг ашиглан үүнийг хийхэд хялбар байдаг.

Өөрчлөлт

Санал болгож буй аргын давуу болон сул талуудыг бусад тохиргооны менежментийн аргуудтай харьцуулахад харцгаая.

Юуны өмнө, бид тохиргоотой ажиллах санал болгож буй өөр өөр талуудын хэд хэдэн хувилбаруудыг жагсаах болно:

  1. Зорилтот машин дээрх текст файл.
  2. Төвлөрсөн түлхүүр-утга хадгалах сан (жишээ нь etcd/zookeeper).
  3. Процессыг дахин эхлүүлэхгүйгээр дахин тохируулах/дахин эхлүүлэх боломжтой дэд процессын бүрэлдэхүүн хэсгүүд.
  4. Олдвор болон хувилбарын хяналтын гаднах тохиргоо.

Текст файл нь түр зуурын засварын хувьд уян хатан байдлыг өгдөг. Системийн администратор зорилтот зангилаа руу нэвтэрч, өөрчлөлт хийж, үйлчилгээгээ дахин эхлүүлэх боломжтой. Энэ нь том системүүдийн хувьд тийм ч сайн биш байж магадгүй юм. Өөрчлөлтийн ард ямар ч ул мөр үлдэхгүй. Өөрчлөлтийг өөр хос нүдээр хянадаггүй. Өөрчлөлтөд юу нөлөөлснийг олоход хэцүү байж магадгүй юм. Үүнийг туршиж үзээгүй. Түгээмэл системийн үүднээс авч үзвэл администратор бусад зангилааны аль нэг дэх тохиргоогоо шинэчлэхээ мартаж болно.

(Btw, хэрэв эцэст нь текстийн тохиргооны файлуудыг ашиглаж эхлэх шаардлагатай бол бид зөвхөн ижил зүйлийг үүсгэж болох задлагч + баталгаажуулагчийг нэмэх хэрэгтэй болно. Config гэж бичвэл текстийн тохиргоог ашиглаж эхлэхэд хангалттай. Энэ нь хөрвүүлэх цагийн тохиргооны нарийн төвөгтэй байдал нь текст дээр суурилсан тохиргооны нарийн төвөгтэй байдлаас арай бага байгааг харуулж байна, учир нь текст дээр суурилсан хувилбарт бидэнд нэмэлт код хэрэгтэй болно.)

Түлхүүр утгын төвлөрсөн хадгалалт нь програмын мета параметрүүдийг түгээх сайн механизм юм. Энд бид тохиргооны үнэ цэнэ гэж юу вэ, зөвхөн өгөгдөл гэж юу болох талаар бодох хэрэгтэй. Функц өгсөн C => A => B Бид ихэвчлэн ховор өөрчлөгддөг утгууд гэж нэрлэдэг C "тохиргоо", байнга өөрчлөгддөг өгөгдөл A - зүгээр л өгөгдөл оруулах. Тохиргоог өгөгдлөөс өмнө функцэд өгөх ёстой A. Энэ санааг харгалзан бид тохиргооны өгөгдлийг зөвхөн өгөгдлөөс ялгахад ашиглаж болох өөрчлөлтийн давтамж гэж хэлж болно. Мөн өгөгдөл нь ихэвчлэн нэг эх сурвалжаас (хэрэглэгч) ирдэг ба тохиргоо нь өөр эх сурвалжаас (админ) ирдэг. Эхлэх процессын дараа өөрчлөх боломжтой параметрүүдийг шийдвэрлэх нь хэрэглээний нарийн төвөгтэй байдлыг нэмэгдүүлэхэд хүргэдэг. Ийм параметрийн хувьд бид тэдгээрийн хүргэх механизм, задлан шинжлэх, баталгаажуулалт, буруу утгыг зохицуулах шаардлагатай болно. Тиймээс, хөтөлбөрийн нарийн төвөгтэй байдлыг багасгахын тулд бид ажиллах үед өөрчлөгдөж болох параметрүүдийн тоог багасгах (эсвэл бүр бүрмөсөн устгах) нь дээр.

Энэ нийтлэлийн үүднээс бид статик болон динамик параметрүүдийг ялгах ёстой. Хэрэв үйлчилгээний логик нь ажиллах үед зарим параметрийн ховор өөрчлөлтийг шаарддаг бол бид тэдгээрийг динамик параметрүүд гэж нэрлэж болно. Үгүй бол тэдгээр нь статик бөгөөд санал болгож буй арга барилыг ашиглан тохируулж болно. Динамик дахин тохируулахын тулд өөр аргууд шаардлагатай байж болно. Жишээлбэл, системийн хэсгүүдийг шинэ тохиргооны параметрүүдээр дахин эхлүүлж, тархсан системийн тусдаа процессуудыг дахин эхлүүлж болно.
(Миний даруухан бодол бол системийн нарийн төвөгтэй байдлыг нэмэгдүүлдэг учраас ажиллах цагийн тохиргоог хийхээс зайлсхийх явдал юм.
Дахин эхлүүлэх үйл явцын үйлдлийн системийн дэмжлэгт найдах нь илүү хялбар байж болох юм. Гэсэн хэдий ч энэ нь үргэлж боломжгүй байж магадгүй юм.)

Хүмүүсийг динамик тохиргоог (өөр шалтгаангүйгээр) авч үзэхэд хүргэдэг статик тохиргоог ашиглах нэг чухал тал бол тохиргоог шинэчлэх үед үйлчилгээний зогсолт юм. Үнэн хэрэгтээ, хэрэв бид статик тохиргоонд өөрчлөлт оруулах шаардлагатай бол шинэ утгууд үр дүнтэй болохын тулд системийг дахин эхлүүлэх хэрэгтэй. Сул зогсолтын шаардлагууд нь өөр өөр системүүдэд өөр өөр байдаг тул энэ нь тийм ч чухал биш байж магадгүй юм. Хэрэв энэ нь маш чухал бол бид аливаа системийг дахин эхлүүлэхийг урьдчилан төлөвлөх ёстой. Жишээлбэл, бид хэрэгжүүлж болох юм AWS ELB холболтын ус зайлуулах. Энэ хувилбарт бид системийг дахин эхлүүлэх шаардлагатай үед системийн шинэ жишээг зэрэгцээ эхлүүлж, дараа нь ELB-г түүн рүү шилжүүлж, хуучин системд одоо байгаа холболтуудыг дуусгах боломжийг олгодог.

Тохиргоог хувилбартай олдвор дотор эсвэл гадна талд байлгах талаар юу хэлэх вэ? Тохиргоог олдвор дотор байлгах нь ихэнх тохиолдолд энэ тохиргоо нь бусад олдворуудын адил чанарын баталгаажуулалтын процессыг давсан гэсэн үг юм. Тиймээс тохиргоо нь сайн чанартай, найдвартай гэдэгт итгэлтэй байж болно. Эсрэгээр нь тусдаа файл дахь тохиргоо нь тухайн файлд хэн, яагаад өөрчлөлт оруулсан ул мөр байхгүй гэсэн үг юм. Энэ чухал уу? Ихэнх үйлдвэрлэлийн системүүдийн хувьд тогтвортой, өндөр чанартай тохиргоотой байх нь дээр гэж бид үзэж байна.

Олдворын хувилбар нь хэзээ бүтээгдсэн, ямар үнэ цэнийг агуулсан, ямар функцийг идэвхжүүлсэн/идэвхгүй болгосон, тохиргооны өөрчлөлт бүрийг хэн хариуцаж байсан зэргийг олж мэдэх боломжийг олгодог. Энэ нь олдвор дотор тохиргоог хадгалахын тулд бага зэрэг хүчин чармайлт шаардаж магадгүй бөгөөд энэ нь дизайны сонголт юм.

Давуу ба сул талууд

Энд бид санал болгож буй аргын зарим давуу талыг онцолж, зарим сул талуудыг хэлэлцэхийг хүсч байна.

Давуу тал

Бүрэн тархсан системийн эмхэтгэх боломжтой тохиргооны онцлогууд:

  1. Тохиргооны статик шалгалт. Энэ нь тухайн төрлийн хязгаарлалтыг харгалзан тохиргоо зөв байна гэсэн өндөр итгэлийг өгдөг.
  2. Тохиргооны баялаг хэл. Ихэвчлэн бусад тохиргооны аргууд нь хамгийн их хувьсах орлуулалтаар хязгаарлагддаг.
    Scala-г ашигласнаар тохиргоог илүү сайн болгохын тулд хэлний олон төрлийн функцийг ашиглаж болно. Жишээлбэл, бид үндсэн утгыг өгөхийн тулд шинж чанаруудыг, өөр хамрах хүрээг тогтоохын тулд объектуудыг ашиглаж болно. vals нь зөвхөн нэг удаа гаднах хүрээнд (ХУУРАЙ) тодорхойлогддог. Энэ нь шууд утга дараалал эсвэл тодорхой ангиудын тохиолдлуудыг ашиглах боломжтой (Seq, Map, гэх мэт).
  3. DSL. Scala нь DSL зохиолчдод хангалттай дэмжлэг үзүүлдэг. Эдгээр функцуудыг ашиглан илүү тохиромжтой, эцсийн хэрэглэгчдэд ээлтэй тохиргооны хэл бий болгох боломжтой бөгөөд ингэснээр эцсийн тохиргоог дор хаяж домэйн хэрэглэгчид унших боломжтой болно.
  4. Зангилаа хоорондын бүрэн бүтэн байдал, уялдаа холбоо. Бүхэл бүтэн тархсан системийн тохиргоог нэг дор байлгахын давуу талуудын нэг нь бүх утгыг нэг удаа хатуу тодорхойлж, дараа нь бидэнд хэрэгтэй бүх газарт дахин ашиглах явдал юм. Мөн аюулгүй портын мэдэгдлийг бичнэ үү. Бүх боломжит зөв тохиргоонд системийн зангилаанууд ижил хэлээр ярих болно. Зангилааны хооронд тодорхой хамаарал байдаг бөгөөд энэ нь зарим үйлчилгээг үзүүлэхээ мартахад хэцүү болгодог.
  5. Өөрчлөлтийн өндөр чанар. Тохиргооны өөрчлөлтийг ердийн PR процессоор дамжуулах ерөнхий хандлага нь тохиргоонд чанарын өндөр стандартыг бий болгодог.
  6. Тохиргоог нэгэн зэрэг өөрчлөх. Бид тохиргоонд ямар нэгэн өөрчлөлт хийх бүрд автоматаар байршуулах нь бүх зангилаа шинэчлэгдэж байгааг баталгаажуулдаг.
  7. Хэрэглээний хялбарчлал. Аппликешн нь тохиргоог задлан шинжлэх, баталгаажуулах, буруу тохиргооны утгыг зохицуулах шаардлагагүй. Энэ нь ерөнхий хэрэглээг хялбаршуулдаг. (Зарим нарийн төвөгтэй байдал нь тохиргоонд байгаа боловч энэ нь аюулгүй байдлын талаархи ухамсартай өөрчлөлт юм.) Энгийн тохиргоо руу буцах нь маш энгийн бөгөөд дутуу хэсгүүдийг нэмэхэд л хангалттай. Эмхэтгэсэн тохиргоог эхлүүлж, нэмэлт хэсгүүдийн хэрэгжилтийг хожим нь хойшлуулах нь илүү хялбар байдаг.
  8. Хувилбарын тохиргоо. Тохиргооны өөрчлөлтүүд нь ижил хөгжлийн процессыг дагаж мөрддөг тул үр дүнд нь бид өвөрмөц хувилбар бүхий олдворыг олж авдаг. Энэ нь шаардлагатай бол тохиргоог буцааж өөрчлөх боломжийг бидэнд олгодог. Бид бүр жилийн өмнө ашиглаж байсан тохиргоог байрлуулж болох бөгөөд энэ нь яг адилхан ажиллах болно. Тогтвортой тохиргоо нь тархсан системийн урьдчилан таамаглах, найдвартай байдлыг сайжруулдаг. Тохиргоог эмхэтгэх үед зассан бөгөөд үйлдвэрлэлийн системд амархан өөрчлөх боломжгүй.
  9. Модульчлал. Санал болгож буй хүрээ нь модульчлагдсан бөгөөд модулиудыг янз бүрийн аргаар нэгтгэж болно
    янз бүрийн тохиргоог дэмжих (тохиргоо/байршил). Ялангуяа жижиг хэмжээний нэг зангилааны байршил, том хэмжээний олон зангилааны тохиргоотой байх боломжтой. Олон тооны үйлдвэрлэлийн зохион байгуулалттай байх нь үндэслэлтэй юм.
  10. Туршилт хийх. Туршилтын зорилгоор хуурамч үйлчилгээг нэвтрүүлж, аюулгүй байдлаар хамаарал болгон ашиглаж болно. Янз бүрийн хэсгүүдийг хуурамчаар сольсон хэд хэдэн туршилтын схемийг нэгэн зэрэг хадгалах боломжтой.
  11. Интеграцийн туршилт. Заримдаа тархсан системд интеграцийн тестийг явуулахад хэцүү байдаг. Бүрэн тархсан системийн аюулгүй тохиргоог бичихийн тулд тайлбарласан аргыг ашигласнаар бид бүх тархсан хэсгүүдийг нэг сервер дээр удирдаж болохуйц байдлаар ажиллуулж чадна. Нөхцөл байдлыг дуурайхад хялбар байдаг
    үйлчилгээний аль нэг нь боломжгүй болсон үед.

Сул тал

Эмхэтгэсэн тохиргооны арга нь "ердийн" тохиргооноос ялгаатай бөгөөд бүх хэрэгцээнд нийцэхгүй байж магадгүй юм. Эмхэтгэсэн тохиргооны зарим сул талууд энд байна:

  1. Статик тохиргоо. Энэ нь бүх програмуудад тохиромжгүй байж магадгүй юм. Зарим тохиолдолд аюулгүй байдлын бүх арга хэмжээг тойрон үйлдвэрлэлд тохиргоог хурдан засах шаардлагатай байдаг. Энэ арга нь илүү хэцүү болгодог. Тохиргоонд ямар нэгэн өөрчлөлт хийсний дараа эмхэтгэх, дахин байршуулах шаардлагатай. Энэ бол онцлог, ачаалал аль аль нь юм.
  2. Тохиргоо үүсгэх. Зарим автоматжуулалтын хэрэгслээр тохиргоог үүсгэх үед энэ арга нь дараагийн эмхэтгэлийг шаарддаг (энэ нь эргээд бүтэлгүйтэж болзошгүй). Энэ нэмэлт алхамыг бүтээх системд нэгтгэхийн тулд нэмэлт хүчин чармайлт шаардаж магадгүй юм.
  3. Багаж хэрэгсэл. Өнөөдөр текст дээр суурилсан тохиргоонд тулгуурласан олон хэрэгсэл ашиглагдаж байна. Тэдний зарим нь
    тохиргоог эмхэтгэсэн үед хэрэгжихгүй.
  4. Сэтгэлгээний өөрчлөлт хэрэгтэй. Хөгжүүлэгчид болон DevOps нь текстийн тохиргооны файлуудыг мэддэг. Тохиргоог эмхэтгэх санаа нь тэдэнд хачирхалтай санагдаж магадгүй юм.
  5. Эмхэтгэх боломжтой тохиргоог нэвтрүүлэхээс өмнө өндөр чанартай програм хангамж боловсруулах процесс шаардлагатай.

Хэрэгжүүлсэн жишээнд зарим хязгаарлалтууд байдаг:

  1. Хэрэв бид зангилааны хэрэгжилтэд шаардлагагүй нэмэлт тохиргоог хийвэл хөрвүүлэгч байхгүй хэрэгжилтийг илрүүлэхэд бидэнд туслахгүй. Үүнийг ашиглан асуудлыг шийдэж болно HList эсвэл зангилааны тохиргоонд зориулсан ADTs (тохиолдлын ангиуд) нь шинж тэмдэг болон Бялууны хэв маягийн оронд.
  2. Бид тохиргооны файлд тодорхой хэмжээний танилцуулга өгөх ёстой: (package, import, object мэдэгдэл;
    override def's нь анхдагч утгатай параметрүүдийн хувьд). Үүнийг DSL ашиглан хэсэгчлэн шийдэж болно.
  3. Энэ нийтлэлд бид ижил төстэй зангилааны кластеруудын динамик дахин тохируулгыг авч үзэхгүй.

Дүгнэлт

Энэ нийтлэлд бид тохиргоог шууд эх кодонд аюулгүй хэлбэрээр илэрхийлэх санааг авч үзсэн. Энэхүү хандлагыг олон программуудад xml болон бусад текст дээр суурилсан тохиргоог орлуулах болгон ашиглаж болно. Бидний жишээг Скала дээр хэрэгжүүлсэн хэдий ч үүнийг хөрвүүлэх боломжтой бусад хэл рүү (Kotlin, C#, Swift гэх мэт) орчуулж болно. Энэ аргыг шинэ төсөл дээр туршиж үзэх боломжтой бөгөөд хэрэв энэ нь тохирохгүй байвал хуучин загвар руу шилжиж болно.

Мэдээжийн хэрэг, эмхэтгэх боломжтой тохиргоо нь өндөр чанартай боловсруулах процессыг шаарддаг. Үүний хариуд энэ нь адилхан өндөр чанартай, бат бөх тохиргоог хангахаа амлаж байна.

Энэ хандлагыг янз бүрийн аргаар өргөжүүлж болно:

  1. Тохиргооны баталгаажуулалтыг хийхийн тулд макро ашиглаж болох бөгөөд бизнесийн логик хязгаарлалтын алдаа гарсан тохиолдолд эмхэтгэх үед бүтэлгүйтэж болно.
  2. Домэйн хэрэглэгчдэд ээлтэй байдлаар тохиргоог илэрхийлэхийн тулд DSL-г хэрэгжүүлж болно.
  3. Автомат тохиргооны тохируулга бүхий динамик нөөцийн удирдлага. Жишээлбэл, бид кластерийн зангилааны тоог тохируулахдаа (1) зангилаанууд бага зэрэг өөрчлөгдсөн тохиргоог авахыг хүсч болно; (2) шинэ зангилааны мэдээллийг хүлээн авах кластер менежер.

баярлалаа

Энэ нийтлэлийн төслийг илүү ойлгомжтой болгоход тусалсан Андрей Саксонов, Павел Попов, Антон Нехаев нарт урам зориг өгсөнд баярлалаа гэж хэлмээр байна.

Эх сурвалж: www.habr.com