Samsett dreifð kerfisstilling

Mig langar að segja þér eitt áhugavert kerfi til að vinna með uppsetningu á dreifðu kerfi. Stillingin er sýnd beint á samsettu tungumáli (Scala) með öruggum gerðum. Þessi færsla gefur dæmi um slíka uppsetningu og fjallar um ýmsa þætti við að innleiða samansetta uppsetningu í heildarþróunarferlinu.

Samsett dreifð kerfisstilling

(Enska)

Inngangur

Að byggja upp áreiðanlegt dreifð kerfi þýðir að allir hnútar nota rétta uppsetningu, samstillt við aðra hnúta. DevOps tækni (terraform, ansible eða eitthvað svoleiðis) er venjulega notuð til að búa til sjálfkrafa stillingarskrár (oft sértækar fyrir hvern hnút). Við viljum líka vera viss um að allir samskiptahnútar noti eins samskiptareglur (þar á meðal sömu útgáfu). Annars verður ósamrýmanleiki innbyggður í dreifða kerfið okkar. Í JVM heiminum er ein afleiðing þessarar kröfu að sama útgáfa af bókasafninu sem inniheldur samskiptareglur verður að nota alls staðar.

Hvað með að prófa dreifð kerfi? Auðvitað gerum við ráð fyrir að allir íhlutir séu með einingapróf áður en við förum yfir í samþættingarpróf. (Til þess að við getum framreiknað prófunarniðurstöður yfir í keyrslutíma verðum við einnig að leggja fram eins safn af bókasöfnum á prófunarstigi og á keyrslutíma.)

Þegar unnið er með samþættingarpróf er oft auðveldara að nota sömu bekkjarstíginn alls staðar á öllum hnútum. Allt sem við þurfum að gera er að tryggja að sama bekkjarslóð sé notuð á keyrslutíma. (Þó að það sé alveg mögulegt að keyra mismunandi hnúta með mismunandi bekkjarslóðum, þá bætir þetta flókið við heildaruppsetninguna og erfiðleikum við uppsetningu og samþættingarpróf.) Í tilgangi þessarar færslu gerum við ráð fyrir að allir hnútar muni nota sömu flokksbrautina.

Stillingin þróast með forritinu. Við notum útgáfur til að bera kennsl á mismunandi stig forritsþróunar. Það virðist rökrétt að bera kennsl á mismunandi útgáfur af stillingum. Og settu stillingarnar sjálfar í útgáfustýringarkerfið. Ef það er aðeins ein uppsetning í framleiðslu, þá getum við einfaldlega notað útgáfunúmerið. Ef við notum mörg framleiðslutilvik, þá þurfum við nokkur
stillingargreinar og viðbótarmerki til viðbótar við útgáfuna (til dæmis heiti útibúsins). Þannig getum við greinilega greint nákvæma uppsetningu. Hvert skilgreiningaauðkenni samsvarar á einstakan hátt tiltekinni samsetningu dreifðra hnúta, gátta, ytri auðlinda og útgáfur bókasafns. Að því er varðar þessa færslu munum við gera ráð fyrir að það sé aðeins ein grein og við getum auðkennt stillinguna á venjulegan hátt með því að nota þrjár tölur aðskildar með punkti (1.2.3).

Í nútímaumhverfi eru stillingarskrár sjaldan búnar til handvirkt. Oftar myndast þau við dreifingu og eru ekki lengur snert (svo að ekki brjóta neitt). Eðlileg spurning vaknar: hvers vegna notum við enn textasnið til að geyma stillingar? Raunhæfur valkostur virðist vera hæfileikinn til að nota venjulegan kóða til að stilla upp og njóta góðs af samantektartíma.

Í þessari færslu munum við kanna hugmyndina um að tákna uppsetningu inni í samansettum gripi.

Samsett uppsetning

Þessi hluti gefur dæmi um kyrrstæða samsetta uppsetningu. Tvær einfaldar þjónustur eru innleiddar - bergmálsþjónustan og bergmálsþjónustuviðskiptavinurinn. Byggt á þessum tveimur þjónustum eru tveir kerfisvalkostir settir saman. Í einum valkosti eru báðar þjónusturnar staðsettar á sama hnút, í öðrum valkosti - á mismunandi hnútum.

Dreift kerfi inniheldur venjulega nokkra hnúta. Þú getur auðkennt hnúta með því að nota gildi af einhverri gerð NodeId:

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

eða

case class NodeId(hostName: String)

eða jafnvel

object Singleton
type NodeId = Singleton.type

Hnútar gegna ýmsum hlutverkum, þeir reka þjónustu og hægt er að koma á TCP/HTTP tengingum á milli þeirra.

Til að lýsa TCP tengingu þurfum við að minnsta kosti gáttarnúmer. Okkur langar líka að endurspegla samskiptareglur sem eru studdar á þeirri höfn til að tryggja að bæði biðlarinn og þjónninn noti sömu samskiptareglur. Við munum lýsa tengingunni með því að nota eftirfarandi flokk:

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

þar sem Port - bara heil tala Int sem gefur til kynna svið ásættanlegra gilda:

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

Hreinsaðar tegundir

Sjá bókasafn hreinsaður и mín skýrsla. Í stuttu máli, bókasafnið gerir þér kleift að bæta við takmörkunum við gerðir sem eru athugaðar við þýðingu. Í þessu tilviki eru gild gáttarnúmersgildi 16 bita heiltölur. Fyrir samsetta uppsetningu er ekki skylda að nota fíngerða bókasafnið, en það bætir getu þýðandans til að athuga stillingarnar.

Fyrir HTTP (REST) ​​samskiptareglur, auk gáttarnúmersins, gætum við einnig þurft slóðina að þjónustunni:

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

Phantom tegundir

Til að bera kennsl á samskiptareglur við þýðingu notum við tegundarbreytu sem er ekki notuð innan bekkjarins. Þessi ákvörðun er vegna þess að við notum ekki samskiptatilvik á keyrslutíma, en við viljum að þýðandinn athugi samhæfni samskiptareglur. Með því að tilgreina samskiptareglur getum við ekki framhjákvæmt óviðeigandi þjónustu sem ósjálfstæði.

Ein af algengu samskiptareglunum er REST API með Json serialization:

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

þar sem RequestMessage - tegund beiðni, ResponseMessage — gerð svars.
Auðvitað getum við notað aðrar samskiptalýsingar sem veita nákvæmni lýsingarinnar sem við krefjumst.

Í tilgangi þessarar færslu munum við nota einfaldaða útgáfu af samskiptareglunum:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

Hér er beiðnin strengur sem bætt er við slóðina og svarið er skilaða strengurinn í meginmáli HTTP svarsins.

Þjónustustillingunni er lýst með þjónustuheiti, gáttum og ósjálfstæði. Þessir þættir geta verið táknaðir í Scala á nokkra vegu (td, HList-s, algebruískar gagnategundir). Í tilgangi þessarar færslu munum við nota kökumynstrið og tákna einingar sem nota trait'ov. (Kökumynstrið er ekki nauðsynlegur þáttur í þessari nálgun. Það er einfaldlega ein möguleg útfærsla.)

Hægt er að tákna ósjálfstæði milli þjónustu sem aðferðir sem skila höfnum EndPointaf öðrum hnútum:

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

Til að búa til bergmálsþjónustu þarf allt sem þú þarft er gáttarnúmer og vísbending um að gáttin styðji bergmálssamskiptareglur. Við gætum ekki tilgreint ákveðna höfn vegna þess að... eiginleikar leyfa þér að lýsa yfir aðferðum án útfærslu (abstraktar aðferðir). Í þessu tilviki, þegar þú býrð til áþreifanlega uppsetningu, myndi þýðandinn krefjast þess að við leggjum fram útfærslu á abstraktaðferðinni og gefum upp gáttarnúmer. Þar sem við höfum innleitt aðferðina, þegar við búum til sérstaka stillingu, gætum við ekki tilgreint aðra höfn. Sjálfgefið gildi verður notað.

Í uppsetningu biðlara lýsum við yfir háð bergmálsþjónustunni:

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

Ósjálfstæði er af sömu tegund og útflutt þjónusta echoService. Sérstaklega, í echo biðlaranum, þurfum við sömu samskiptareglur. Þess vegna, þegar tvær þjónustur eru tengdar, getum við verið viss um að allt virki rétt.

Framkvæmd þjónustu

Aðgerð er nauðsynleg til að hefja og stöðva þjónustuna. (Hæfingin til að stöðva þjónustu er mikilvæg fyrir prófun.) Aftur eru nokkrir möguleikar til að útfæra slíkan eiginleika (til dæmis gætum við notað tegundaflokka byggða á stillingargerðinni). Í tilgangi þessarar færslu munum við nota kökumynstrið. Við munum tákna þjónustuna með því að nota bekk cats.Resource, vegna þess Þessi flokkur veitir nú þegar leiðir til að tryggja á öruggan hátt losun auðlinda ef vandamál koma upp. Til að fá tilföng þurfum við að útvega stillingar og tilbúið runtime samhengi. Uppsetningaraðgerðin getur litið svona út:

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

þar sem

  • Config — stillingargerð fyrir þessa þjónustu
  • AddressResolver - keyrsluhlutur sem gerir þér kleift að finna heimilisföng annarra hnúta (sjá hér að neðan)

og aðrar tegundir af bókasafninu cats:

  • F[_] — tegund áhrifa (í einfaldasta tilfelli F[A] gæti bara verið fall () => A. Í þessari færslu munum við nota cats.IO.)
  • Reader[A,B] - meira og minna samheiti við virkni A => B
  • cats.Resource - auðlind sem hægt er að fá og gefa út
  • Timer - teljari (gerir þér að sofna í smá stund og mæla tímabil)
  • ContextShift - hliðstæða ExecutionContext
  • Applicative — áhrifaflokkur sem gerir þér kleift að sameina einstök áhrif (næstum mónad). Í flóknari forritum virðist það betra að nota Monad/ConcurrentEffect.

Með því að nota þessa fallundirskrift getum við innleitt nokkrar þjónustur. Til dæmis, þjónusta sem gerir ekkert:

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

(Sentimetri. heimild, þar sem önnur þjónusta er innleidd - bergmálsþjónusta, echo viðskiptavinur
и líftíma stýringar.)

Hnútur er hlutur sem getur hleypt af stokkunum nokkrum þjónustum (kynning á keðju auðlinda er tryggð með kökumynstrinu):

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

Vinsamlegast athugaðu að við erum að tilgreina nákvæma gerð stillingar sem þarf fyrir þennan hnút. Ef við gleymum að tilgreina eina af stillingargerðunum sem tiltekin þjónusta krefst, þá verður söfnunarvilla. Einnig munum við ekki geta ræst hnút nema við gefum einhverjum hlut af viðeigandi gerð með öllum nauðsynlegum gögnum.

Upplausn hýsilheita

Til að tengjast ytri gestgjafa þurfum við alvöru IP tölu. Það er mögulegt að heimilisfangið verði þekkt seinna en restin af uppsetningunni. Svo við þurfum aðgerð sem varpar hnútauðkenninu á heimilisfang:

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

Það eru nokkrar leiðir til að útfæra þessa aðgerð:

  1. Ef heimilisföngin verða okkur kunn fyrir uppsetningu, þá getum við búið til Scala kóða með
    heimilisföng og keyra síðan bygginguna. Þetta mun safna saman og keyra próf.
    Í þessu tilviki verður aðgerðin þekkt á kyrrstöðu og hægt er að tákna hana í kóða sem kortlagningu Map[NodeId, NodeAddress].
  2. Í sumum tilfellum er raunverulegt heimilisfang aðeins þekkt eftir að hnúturinn hefur byrjað.
    Í þessu tilviki getum við innleitt „uppgötvunarþjónustu“ sem keyrir á undan öðrum hnútum og allir hnútar munu skrá sig hjá þessari þjónustu og biðja um heimilisföng annarra hnúta.
  3. Ef við getum breytt /etc/hosts, þá geturðu notað fyrirfram skilgreind hýsingarnöfn (eins og my-project-main-node и echo-backend) og einfaldlega tengja þessi nöfn
    með IP-tölum meðan á dreifingu stendur.

Í þessari færslu munum við ekki fjalla nánar um þessi mál. Fyrir okkar
í leikfangadæmi munu allir hnútar hafa sömu IP tölu - 127.0.0.1.

Næst skoðum við tvo valkosti fyrir dreift kerfi:

  1. Að setja alla þjónustu á einn hnút.
  2. Og hýsa bergmálsþjónustuna og bergmálsviðskiptavininn á mismunandi hnútum.

Stillingar fyrir einn hnút:

Stilling á einum hnút

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

Hluturinn útfærir uppsetningu bæði biðlarans og netþjónsins. Tími til að lifa stillingu er einnig notað þannig að eftir bilið lifetime slíta forritinu. (Ctrl-C virkar líka og losar öll tilföng rétt.)

Hægt er að nota sömu uppsetningar- og útfærslueiginleika til að búa til kerfi sem samanstendur af tveir aðskildir hnútar:

Tveggja hnúta stillingar

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

Mikilvægt! Taktu eftir hvernig þjónustan er tengd. Við tilgreinum þjónustu sem er útfærð af einum hnút sem útfærslu á ósjálfstæðisaðferð annars hnúts. Gerð ósjálfstæðis er athugað af þýðandanum, vegna þess að inniheldur samskiptaregluna. Þegar hún er keyrð mun ósjálfstæðin innihalda rétt markhnútauðkenni. Þökk sé þessu kerfi tilgreinum við gáttarnúmerið nákvæmlega einu sinni og er alltaf tryggt að vísa í rétta höfn.

Innleiðing tveggja kerfishnúta

Fyrir þessa uppsetningu notum við sömu þjónustuútfærslur án breytinga. Eini munurinn er sá að við höfum núna tvo hluti sem útfæra mismunandi þjónustuflokka:

  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
  }

Fyrsti hnúturinn útfærir netþjóninn og þarf aðeins stillingar miðlara. Annar hnúturinn útfærir biðlarann ​​og notar annan hluta af uppsetningunni. Einnig þurfa báðir hnútarnir ævistjórnun. Miðlarahnúturinn keyrir endalaust þar til hann er stöðvaður SIGTERM'om, og biðlarahnúturinn lýkur eftir nokkurn tíma. Cm. ræsiforrit.

Almennt þróunarferli

Við skulum sjá hvernig þessi stillingaraðferð hefur áhrif á heildarþróunarferlið.

Stillingin verður sett saman ásamt restinni af kóðanum og gripur (.jar) verður til. Það virðist vera skynsamlegt að setja uppsetninguna í sérstakan grip. Þetta er vegna þess að við getum haft margar stillingar byggðar á sama kóða. Aftur, það er hægt að búa til gripi sem samsvara mismunandi uppsetningargreinum. Ósjálfstæði á tilteknum útgáfum af bókasöfnum eru vistaðar ásamt uppsetningunni og þessar útgáfur eru vistaðar að eilífu í hvert sinn sem við ákveðum að nota þá útgáfu af uppsetningunni.

Allar stillingarbreytingar breytast í kóðabreytingar. Og þess vegna, hver
breytingin mun falla undir venjulegt gæðatryggingarferli:

Miði í villurekki -> PR -> endurskoðun -> sameinast viðeigandi útibúum ->
samþætting -> dreifing

Helstu afleiðingar þess að innleiða samsetta uppsetningu eru:

  1. Uppsetningin verður samkvæm á öllum hnútum dreifða kerfisins. Vegna þess að allir hnútar fá sömu stillingar frá einum uppruna.

  2. Það er vandkvæðum bundið að breyta stillingum í aðeins einum hnútanna. Þess vegna er „stillingarsvifa“ ólíklegt.

  3. Það verður erfiðara að gera litlar breytingar á uppsetningunni.

  4. Flestar stillingarbreytingar munu eiga sér stað sem hluti af heildarþróunarferlinu og verða endurskoðaðir.

Þarf ég sérstaka geymslu til að geyma framleiðslustillingarnar? Þessi uppsetning gæti innihaldið lykilorð og aðrar viðkvæmar upplýsingar sem við viljum takmarka aðgang að. Byggt á þessu virðist skynsamlegt að geyma endanlega uppsetningu í sérstakri geymslu. Þú getur skipt uppsetningunni í tvo hluta - einn sem inniheldur almenna aðgengilegar stillingar og einn sem inniheldur takmarkaðar stillingar. Þetta gerir flestum forriturum kleift að hafa aðgang að algengum stillingum. Auðvelt er að ná þessum aðskilnaði með því að nota millieiginleika sem innihalda sjálfgefin gildi.

Möguleg afbrigði

Við skulum reyna að bera saman samsettu uppsetninguna við nokkra algenga valkosti:

  1. Textaskrá á markvélinni.
  2. Miðstýrð verslun með lykilgildi (etcd/zookeeper).
  3. Vinnsluíhlutir sem hægt er að endurstilla/endurræsa án þess að endurræsa ferlið.
  4. Geymir stillingar utan grips og útgáfustýringar.

Textaskrár veita verulegan sveigjanleika hvað varðar litlar breytingar. Kerfisstjórinn getur skráð sig inn á ytri hnútinn, gert breytingar á viðeigandi skrám og endurræst þjónustuna. Fyrir stór kerfi gæti slíkur sveigjanleiki hins vegar ekki verið æskilegur. Breytingarnar sem gerðar voru skilja ekki eftir sig spor í öðrum kerfum. Enginn fer yfir breytingarnar. Erfitt er að ákveða hver nákvæmlega gerði breytingarnar og af hvaða ástæðu. Breytingar eru ekki prófaðar. Ef kerfinu er dreift, þá gæti stjórnandinn gleymt að gera samsvarandi breytingu á öðrum hnútum.

(Einnig skal tekið fram að notkun samsettrar stillingar lokar ekki möguleikanum á að nota textaskrár í framtíðinni. Það mun vera nóg að bæta við flokkun og löggildingaraðila sem framleiðir sömu tegund og úttak Config, og þú getur notað textaskrár. Það kemur strax í kjölfarið að flókið kerfi með samsettri uppsetningu er nokkru minna en flókið kerfi sem notar textaskrár, vegna þess að textaskrár þurfa viðbótarkóða.)

Miðstýrð lykilgildi geymsla er gott kerfi til að dreifa meta breytum dreifðs forrits. Við þurfum að ákveða hvað eru stillingarfæribreytur og hvað eru bara gögn. Leyfðu okkur að hafa hlutverk C => A => B, og breytur C breytist sjaldan, og gögn A - oft. Í þessu tilfelli getum við sagt það C - stillingarfæribreytur, og A - gögn. Svo virðist sem stillingarfæribreytur séu frábrugðnar gögnum að því leyti að þær breytast almennt sjaldnar en gögn. Einnig koma gögn venjulega frá einum uppruna (frá notandanum) og stillingarbreytur frá öðrum (frá kerfisstjóranum).

Ef sjaldan þarf að uppfæra breytur sem breytast án þess að endurræsa forritið, þá getur það oft leitt til flækjustigs forritsins, því við þurfum einhvern veginn að skila breytum, geyma, flokka og athuga og vinna úr röngum gildum. Þess vegna, frá því sjónarhorni að draga úr flækjustigi forritsins, er skynsamlegt að fækka færibreytum sem geta breyst við notkun forritsins (eða styðja alls ekki slíkar breytur).

Í tilgangi þessarar færslu munum við gera greinarmun á kyrrstæðum og kvikum breytum. Ef rökfræði þjónustunnar krefst þess að breyta breytum meðan á aðgerðinni stendur, þá munum við kalla slíkar breytur dynamic. Annars eru valmöguleikarnir kyrrstæðir og hægt er að stilla þær með því að nota samsettu uppsetninguna. Fyrir kraftmikla endurstillingu gætum við þurft kerfi til að endurræsa hluta af forritinu með nýjum breytum, svipað og hvernig stýrikerfisferlar eru endurræstir. (Að okkar mati er ráðlegt að forðast endurstillingu í rauntíma þar sem það eykur flókið kerfi. Ef mögulegt er er betra að nota staðlaða stýrikerfisgetu til að endurræsa ferla.)

Einn mikilvægur þáttur í notkun kyrrstæðrar stillingar sem fær fólk til að íhuga kraftmikla endurstillingu er tíminn sem það tekur kerfið að endurræsa eftir uppfærslu uppsetningar (niður í miðbæ). Reyndar, ef við þurfum að gera breytingar á kyrrstöðustillingunni, verðum við að endurræsa kerfið til að nýju gildin taki gildi. Niðurtímavandamálið er mismunandi í alvarleika fyrir mismunandi kerfi. Í sumum tilfellum geturðu tímasett endurræsingu á þeim tíma þegar álagið er í lágmarki. Ef þú þarft að veita stöðuga þjónustu geturðu innleitt AWS ELB tengitæmsla. Á sama tíma, þegar við þurfum að endurræsa kerfið, ræsum við samhliða tilvik af þessu kerfi, skiptum jafnvægisbúnaðinum yfir á það og bíðum eftir að gömlu tengingunum ljúki. Eftir að öllum gömlum tengingum hefur verið slitið, slökkvum við á gamla tilviki kerfisins.

Við skulum nú íhuga málið með að geyma uppsetninguna innan eða utan gripsins. Ef við geymum stillingarnar inni í grip, þá höfðum við að minnsta kosti tækifæri til að sannreyna réttmæti stillingarinnar við samsetningu gripsins. Ef uppsetningin er utan stjórnaðs grips er erfitt að fylgjast með hver gerði breytingar á þessari skrá og hvers vegna. Hversu mikilvægt er það? Að okkar mati er mikilvægt fyrir mörg framleiðslukerfi að hafa stöðuga og hágæða uppsetningu.

Útgáfan af gripi gerir þér kleift að ákvarða hvenær hann var búinn til, hvaða gildi hann inniheldur, hvaða aðgerðir eru virkar/óvirkar og hver ber ábyrgð á breytingum á uppsetningunni. Auðvitað krefst nokkurrar fyrirhafnar að geyma uppsetninguna inni í gripi, svo þú þarft að taka upplýsta ákvörðun.

Kostir og gallar

Mig langar að staldra við kosti og galla fyrirhugaðrar tækni.

Kostir

Hér að neðan er listi yfir helstu eiginleika samsettrar dreifðrar kerfisuppsetningar:

  1. Stöðug stillingarathugun. Leyfir þér að vera viss um það
    uppsetningin er rétt.
  2. Ríkt stillingartungumál. Venjulega eru aðrar stillingaraðferðir takmarkaðar við skiptingu strengjabreyta í mesta lagi. Þegar þú notar Scala er fjölbreytt úrval tungumálaeiginleika tiltækt til að bæta stillingar þínar. Við getum til dæmis notað
    eiginleikar fyrir sjálfgefin gildi, með því að nota hluti til að flokka færibreytur, getum við vísað til vals sem lýst er yfir einu sinni (DRY) í meðfylgjandi umfangi. Þú getur stofnað hvaða flokka sem er beint inni í uppsetningunni (Seq, Map, sérsniðin námskeið).
  3. DSL. Scala hefur fjölda tungumálaeiginleika sem gera það auðveldara að búa til DSL. Það er hægt að nýta sér þessa eiginleika og innleiða stillingartungumál sem er þægilegra fyrir markhóp notenda, þannig að uppsetningin sé að minnsta kosti læsileg af lénssérfræðingum. Sérfræðingar geta til dæmis tekið þátt í endurskoðunarferli stillinga.
  4. Heilindi og samstilling milli hnúta. Einn af kostunum við að hafa uppsetningu á heilu dreifðu kerfi geymd á einum stað er að öll gildi eru lýst nákvæmlega einu sinni og síðan endurnýtt hvar sem þeirra er þörf. Með því að nota phantom-gerðir til að lýsa yfir höfnum tryggir það að hnútar noti samhæfðar samskiptareglur í öllum réttum kerfisstillingum. Að hafa skýra lögboðna ósjálfstæði milli hnúta tryggir að öll þjónusta sé tengd.
  5. Hágæða breytingar. Með því að gera breytingar á uppsetningunni með sameiginlegu þróunarferli er hægt að ná háum gæðastöðlum fyrir uppsetninguna líka.
  6. Samtímis uppfærsla á stillingum. Sjálfvirk kerfisuppsetning eftir breytingar á stillingum tryggir að allir hnútar séu uppfærðir.
  7. Að einfalda forritið. Forritið þarf ekki þáttun, stillingarathugun eða meðhöndlun á röngum gildum. Þetta dregur úr flókinni umsókn. (Sumt af flóknu uppsetningarflækjunni sem sést í dæminu okkar er ekki eiginleiki samsettrar uppsetningar, heldur aðeins meðvituð ákvörðun sem knúin er áfram af lönguninni til að veita meira tegundaröryggi.) Það er frekar auðvelt að fara aftur í venjulega uppsetningu - útfærðu bara það sem vantar hlutar. Þess vegna geturðu til dæmis byrjað með samsettri uppsetningu, frestað innleiðingu óþarfa hluta þar til það er raunverulega þörf.
  8. Staðfest uppsetning. Þar sem stillingarbreytingar fylgja venjulegum örlögum allra annarra breytinga, er framleiðslan sem við fáum gripur með einstaka útgáfu. Þetta gerir okkur til dæmis kleift að fara aftur í fyrri útgáfu af uppsetningunni ef þörf krefur. Við getum meira að segja notað uppsetninguna fyrir ári síðan og kerfið mun virka nákvæmlega eins. Stöðug uppsetning bætir fyrirsjáanleika og áreiðanleika dreifðs kerfis. Þar sem uppsetningin er fast á safnstigi er frekar erfitt að falsa hana í framleiðslu.
  9. Modularity. Fyrirhuguð rammi er mát og hægt er að sameina einingarnar á mismunandi hátt til að búa til mismunandi kerfi. Sérstaklega geturðu stillt kerfið til að keyra á einum hnút í einni útfærslu og á mörgum hnútum í annarri. Þú getur búið til nokkrar stillingar fyrir framleiðslutilvik kerfisins.
  10. Prófanir. Með því að skipta út einstökum þjónustum fyrir sýndarhluti er hægt að fá nokkrar útgáfur af kerfinu sem henta vel til prófunar.
  11. Samþættingarpróf. Að hafa eina uppsetningu fyrir allt dreifða kerfið gerir það mögulegt að keyra alla íhluti í stýrðu umhverfi sem hluti af samþættingarprófun. Það er til dæmis auðvelt að líkja eftir aðstæðum þar sem sumir hnútar verða aðgengilegir.

Ókostir og takmarkanir

Samsett uppsetning er frábrugðin öðrum stillingaraðferðum og gæti ekki hentað sumum forritum. Hér að neðan eru nokkrir ókostir:

  1. Statísk stilling. Stundum þarftu að leiðrétta uppsetninguna fljótt í framleiðslu, framhjá öllum verndaraðferðum. Með þessari nálgun getur það verið erfiðara. Að minnsta kosti verður enn þörf á samantekt og sjálfvirkri uppsetningu. Þetta er bæði gagnlegur eiginleiki nálgunarinnar og ókostur í sumum tilfellum.
  2. Stillingar kynslóð. Ef stillingarskráin er búin til með sjálfvirku tóli, gæti þurft frekari viðleitni til að samþætta byggingarhandritið.
  3. Verkfæri. Eins og er eru tól og tækni sem eru hönnuð til að vinna með stillingar byggð á textaskrám. Ekki verða öll slík tól/tækni tiltæk í samsettri uppsetningu.
  4. Það þarf að breyta viðhorfum. Hönnuðir og DevOps eru vanir textaskrám. Sjálf hugmyndin um að setja saman stillingar getur verið nokkuð óvænt og óvenjuleg og valdið höfnun.
  5. Hágæða þróunarferli er krafist. Til að nota samansettu uppsetninguna á þægilegan hátt er full sjálfvirkni í ferlinu við að byggja upp og dreifa forritinu (CI/CD) nauðsynleg. Annars verður þetta frekar óþægilegt.

Við skulum líka dvelja við ýmsar takmarkanir á íhuguðu dæmi sem tengjast ekki hugmyndinni um samsetta uppsetningu:

  1. Ef við gefum upp óþarfa stillingarupplýsingar sem eru ekki notaðar af hnút, þá mun þýðandinn ekki hjálpa okkur að finna útfærsluna sem vantar. Þetta vandamál er hægt að leysa með því að yfirgefa kökumynstrið og nota stífari tegundir, til dæmis, HList eða algebrufræðilegar gagnategundir (tilvikaflokkar) til að tákna uppsetningu.
  2. Það eru línur í stillingarskránni sem eru ekki tengdar stillingunum sjálfum: (package, import,hlutyfirlýsingar; override def's fyrir færibreytur sem hafa sjálfgefin gildi). Þetta er hægt að forðast að hluta ef þú innleiðir þitt eigið DSL. Að auki setja aðrar gerðir af stillingum (til dæmis XML) einnig ákveðnar takmarkanir á skráargerðina.
  3. Í tilgangi þessarar færslu erum við ekki að íhuga kraftmikla endurstillingu á klasa af svipuðum hnútum.

Ályktun

Í þessari færslu könnuðum við hugmyndina um að tákna stillingar í frumkóða með því að nota háþróaða getu Scala gerð kerfisins. Þessa nálgun er hægt að nota í ýmsum forritum í stað hefðbundinna uppsetningaraðferða sem byggja á xml eða textaskrám. Jafnvel þó að dæmið okkar sé útfært í Scala er hægt að flytja sömu hugmyndir yfir á önnur samsett tungumál (eins og Kotlin, C#, Swift, ...). Þú getur prófað þessa nálgun í einu af eftirfarandi verkefnum og ef það virkar ekki skaltu fara yfir í textaskrána og bæta við þeim hlutum sem vantar.

Auðvitað krefst samsett uppsetning hágæða þróunarferlis. Í staðinn eru mikil gæði og áreiðanleiki stillinga tryggð.

Hægt er að útvíkka yfirhugaða nálgun:

  1. Þú getur notað fjölvi til að framkvæma athugun á samantektartíma.
  2. Þú getur innleitt DSL til að kynna stillingarnar á þann hátt sem er aðgengilegur fyrir notendur.
  3. Þú getur innleitt kraftmikla auðlindastjórnun með sjálfvirkri stillingaraðlögun. Til dæmis, að breyta fjölda hnúta í klasa krefst þess að (1) hver hnút fái aðeins mismunandi uppsetningu; (2) klasastjórinn fékk upplýsingar um nýja hnúta.

Viðurkenningar

Ég vil þakka Andrei Saksonov, Pavel Popov og Anton Nekhaev fyrir uppbyggilega gagnrýni þeirra á drög að greininni.

Heimild: www.habr.com

Bæta við athugasemd