Dikompilasi Konfigurasi Sistem Distribusi

Aku kaya kanggo pitutur marang kowe siji mekanisme menarik kanggo nggarap konfigurasi saka sistem mbagekke. Konfigurasi dituduhake langsung ing basa kompilasi (Scala) nggunakake jinis aman. Kiriman iki menehi conto konfigurasi kasebut lan mbahas macem-macem aspek ngleksanakake konfigurasi sing dikompilasi menyang proses pangembangan sakabèhé.

Dikompilasi Konfigurasi Sistem Distribusi

(english)

Pambuka

Mbangun sistem distribusi sing dipercaya tegese kabeh simpul nggunakake konfigurasi sing bener, disinkronake karo simpul liyane. Teknologi DevOps (terraform, ansible utawa liya-liyane) biasane digunakake kanggo ngasilake file konfigurasi kanthi otomatis (asring spesifik kanggo saben simpul). Kita uga pengin mesthekake yen kabeh simpul komunikasi nggunakake protokol sing padha (kalebu versi sing padha). Yen ora, incompatibility bakal dibangun ing sistem sing disebarake. Ing jagad JVM, salah sawijining akibat saka syarat iki yaiku versi perpustakaan sing padha sing ngemot pesen protokol kudu digunakake ing endi wae.

Kepiye babagan nguji sistem sing disebarake? Mesthi, kita nganggep yen kabeh komponen duwe tes unit sadurunge pindhah menyang tes integrasi. (Supaya kita bisa ngekstrapolasi asil tes menyang runtime, kita uga kudu nyedhiyakake koleksi perpustakaan sing padha ing tahap tes lan nalika runtime.)

Nalika nggarap tes integrasi, asring luwih gampang nggunakake classpath sing padha ing endi wae ing kabeh simpul. Kabeh kudu kita tindakake iku mesthekake yen classpath padha digunakake ing runtime. (Nalika iku kabeh bisa kanggo mbukak kelenjar beda karo classpaths beda, iki nambah kerumitan konfigurasi sakabèhé lan kangelan karo penyebaran lan integrasi tes.) Kanggo tujuan kirim iki, kita assuming sing kabeh kelenjar bakal nggunakake classpath padha.

Konfigurasi evolves karo aplikasi. Kita nggunakake versi kanggo ngenali macem-macem tahapan evolusi program. Iku misale jek logis uga ngenali versi beda saka konfigurasi. Lan sijine konfigurasi dhewe ing sistem kontrol versi. Yen mung ana siji konfigurasi ing produksi, kita mung bisa nggunakake nomer versi. Yen kita nggunakake akeh conto produksi, mula kita butuh sawetara
cabang konfigurasi lan label tambahan saliyane versi (contone, jeneng cabang). Kanthi cara iki kita bisa ngerteni konfigurasi sing tepat. Saben pengenal konfigurasi cocog karo kombinasi tartamtu saka node sing disebarake, port, sumber daya eksternal, lan versi perpustakaan. Kanggo tujuan kirim iki kita bakal nganggep mung ana siji cabang lan kita bisa ngenali konfigurasi ing cara biasanipun nggunakake telung nomer dipisahake dening titik (1.2.3).

Ing lingkungan modern, file konfigurasi arang digawe kanthi manual. Luwih asring diasilake sajrone panyebaran lan ora disentuh maneh (supaya aja ngrusak apa-apa). Pitakonan alami muncul: kenapa kita isih nggunakake format teks kanggo nyimpen konfigurasi? Alternatif sing bisa ditindakake yaiku kemampuan kanggo nggunakake kode reguler kanggo konfigurasi lan entuk manfaat saka mriksa wektu kompilasi.

Ing kirim iki, kita bakal njelajah ide kanggo makili konfigurasi ing artefak sing dikompilasi.

Konfigurasi sing dikompilasi

Bagean iki menehi conto konfigurasi kompilasi statis. Rong layanan sing prasaja dileksanakake - layanan gema lan klien layanan gema. Adhedhasar loro layanan kasebut, rong opsi sistem dirakit. Ing siji opsi, loro layanan dumunung ing simpul sing padha, ing pilihan liyane - ing simpul beda.

Biasane sistem sing disebarake ngemot sawetara node. Sampeyan bisa ngenali simpul nggunakake nilai sawetara jinis NodeId:

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

utawa

case class NodeId(hostName: String)

utawa malah

object Singleton
type NodeId = Singleton.type

Node nindakake macem-macem peran, padha mbukak layanan lan sambungan TCP / HTTP bisa ditetepake ing antarane.

Kanggo njlèntrèhaké sambungan TCP kita kudu paling nomer port. Kita uga pengin nggambarake protokol sing didhukung ing port kasebut kanggo mesthekake yen klien lan server nggunakake protokol sing padha. Kita bakal njlèntrèhaké sambungan nggunakake kelas ing ngisor iki:

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

ngendi Port - mung integer Int nuduhake sawetara nilai sing bisa ditampa:

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

Jinis sing ditapis

Waca perpustakaan olahan и kawula laporan. Ing cendhak, perpustakaan ngidini sampeyan nambah watesan kanggo jinis sing dicenthang ing wektu kompilasi. Ing kasus iki, nilai nomer port sing bener yaiku integer 16-bit. Kanggo konfigurasi nyawiji, nggunakake perpustakaan olahan ora prentah, nanging mbenakake kemampuan compiler kanggo mriksa konfigurasi.

Kanggo protokol HTTP (REST), saliyane nomer port, kita uga mbutuhake dalan menyang layanan kasebut:

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

Tipe Phantom

Kanggo ngenali protokol ing wektu kompilasi, kita nggunakake parameter jinis sing ora digunakake ing kelas. Kaputusan iki amarga kasunyatan manawa kita ora nggunakake conto protokol nalika runtime, nanging kita pengin kompiler mriksa kompatibilitas protokol kasebut. Kanthi nemtokake protokol kasebut, kita ora bakal bisa ngliwati layanan sing ora cocog minangka ketergantungan.

Salah sawijining protokol umum yaiku REST API kanthi serialisasi Json:

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

ngendi RequestMessage - jinis panjalukan, ResponseMessage - jinis respon.
Mesthi, kita bisa nggunakake deskripsi protokol liyane sing nyedhiyakake akurasi deskripsi sing dibutuhake.

Kanggo tujuan kirim iki, kita bakal nggunakake versi protokol sing disederhanakake:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

Ing kene panyuwunan kasebut minangka senar sing ditambahake menyang url lan tanggapan kasebut minangka senar sing bali ing awak respon HTTP.

Konfigurasi layanan diterangake kanthi jeneng layanan, port, lan dependensi. Unsur kasebut bisa diwakili ing Scala kanthi sawetara cara (contone, HList-s, jinis data aljabar). Kanggo tujuan kirim iki, kita bakal nggunakake Pola Kue lan makili modul nggunakake trait' ov. (Pola Kue ora dadi unsur sing dibutuhake kanggo pendekatan iki. Iku mung siji implementasine.)

Ketergantungan antarane layanan bisa dituduhake minangka cara sing ngasilake port EndPointNode liyane:

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

Kanggo nggawe layanan gema, sampeyan mung butuh nomer port lan indikasi yen port kasebut ndhukung protokol echo. Kita bisa uga ora nemtokake port tartamtu, amarga ... sipat ngidini sampeyan ngumumake metode tanpa implementasine (metode abstrak). Ing kasus iki, nalika nggawe konfigurasi konkrit, compiler mbutuhake kita nyedhiyani implementasine saka cara abstrak lan nyedhiyani nomer port. Awit kita wis dipun ginakaken cara, nalika nggawe konfigurasi tartamtu, kita ora bisa nemtokake port beda. Nilai standar bakal digunakake.

Ing konfigurasi klien, kita nyatakake ketergantungan ing layanan gema:

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

Ketergantungan iku jinis sing padha karo layanan sing diekspor echoService. Utamane, ing klien echo kita mbutuhake protokol sing padha. Mulane, nalika nyambungake rong layanan, kita bisa mesthekake yen kabeh bakal bisa digunakake kanthi bener.

Implementasi layanan

Fungsi dibutuhake kanggo miwiti lan mungkasi layanan. (Kemampuan kanggo mungkasi layanan kritis kanggo testing.) Maneh, ana sawetara opsi kanggo ngleksanakake fitur kuwi (contone, kita bisa nggunakake kelas jinis adhedhasar jinis konfigurasi). Kanggo tujuan kirim iki, kita bakal nggunakake Pola Kue. Kita bakal makili layanan nggunakake kelas cats.Resource, amarga Kelas iki wis nyedhiyakake sarana kanggo njamin rilis sumber daya kanthi aman yen ana masalah. Kanggo entuk sumber daya, kita kudu nyedhiyakake konfigurasi lan konteks runtime sing wis siap. Fungsi wiwitan layanan bisa katon kaya iki:

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

ngendi

  • Config - jinis konfigurasi kanggo layanan iki
  • AddressResolver - obyek runtime sing ngidini sampeyan ngerteni alamat node liyane (ndeleng ngisor)

lan jinis liyane saka perpustakaan cats:

  • F[_] - jinis efek (ing kasus sing paling gampang F[A] mung bisa dadi fungsi () => A. Ing kirim iki kita bakal nggunakake cats.IO.)
  • Reader[A,B] - luwih utawa kurang sinonim karo fungsi A => B
  • cats.Resource - sumber daya sing bisa dipikolehi lan dirilis
  • Timer - timer (ngidini sampeyan turu sedhela lan ngukur interval wektu)
  • ContextShift -analog ExecutionContext
  • Applicative - kelas jinis efek sing ngijini sampeyan kanggo gabungke efek individu (meh monad). Ing aplikasi sing luwih rumit, luwih becik digunakake Monad/ConcurrentEffect.

Nggunakake teken fungsi iki kita bisa ngleksanakake sawetara layanan. Contone, layanan sing ora nindakake apa-apa:

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

(Cm. sumber, ing ngendi layanan liyane ditindakake - layanan kumandhang, klien echo
и pengontrol umur.)

A simpul minangka obyek sing bisa miwiti sawetara layanan (peluncuran sumber daya wis dijamin dening Pola Kue):

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

Wigati dimangerteni manawa kita nemtokake jinis konfigurasi sing dibutuhake kanggo simpul iki. Yen kita lali nemtokake salah sawijining jinis konfigurasi sing dibutuhake dening layanan tartamtu, bakal ana kesalahan kompilasi. Kajaba iku, kita ora bakal bisa miwiti simpul kajaba nyedhiyakake sawetara obyek saka jinis sing cocog karo kabeh data sing dibutuhake.

Resolusi Jeneng Host

Kanggo nyambung menyang host remot, kita butuh alamat IP nyata. Sampeyan bisa uga alamat bakal dikenal mengko saka liyane saka konfigurasi. Dadi, kita butuh fungsi sing menehi peta ID simpul menyang alamat:

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

Ana sawetara cara kanggo ngleksanakake fungsi iki:

  1. Yen alamat dadi dikenal kanggo kita sadurunge panyebaran, banjur kita bisa generate kode Scala karo
    alamat banjur mbukak mbangun. Iki bakal ngumpulake lan mbukak tes.
    Ing kasus iki, fungsi kasebut bakal dikenal kanthi statis lan bisa diwakili ing kode minangka pemetaan Map[NodeId, NodeAddress].
  2. Ing sawetara kasus, alamat nyata mung dikenal sawise simpul wis diwiwiti.
    Ing kasus iki, kita bisa ngleksanakake "layanan panemuan" sing mlaku sadurunge simpul liyane lan kabeh simpul bakal ndhaptar layanan iki lan njaluk alamat simpul liyane.
  3. Yen kita bisa ngowahi /etc/hosts, banjur sampeyan bisa nggunakake jeneng host sing wis ditemtokake (kaya my-project-main-node и echo-backend) lan mung nyambungake jeneng kasebut
    karo alamat IP sajrone panyebaran.

Ing kirim iki, kita ora bakal nimbang kasus kasebut kanthi luwih rinci. Kanggo kita
ing conto dolanan, kabeh simpul bakal duwe alamat IP sing padha - 127.0.0.1.

Sabanjure, kita nimbang rong opsi kanggo sistem sing disebarake:

  1. Nempatake kabeh layanan ing siji simpul.
  2. Lan hosting layanan gema lan klien echo ing macem-macem simpul.

Konfigurasi kanggo siji simpul:

Konfigurasi simpul tunggal

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

Obyek ngetrapake konfigurasi klien lan server. Konfigurasi wektu-kanggo-urip uga digunakake supaya sawise interval lifetime mungkasi program. (Ctrl-C uga dianggo lan mbebasake kabeh sumber daya kanthi bener.)

Set padha konfigurasi lan ciri implementasine bisa digunakake kanggo nggawe sistem dumadi saka rong simpul kapisah:

Konfigurasi rong node

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

Penting! Elinga carane layanan disambungake. Kita nemtokake layanan sing diimplementasikake dening siji simpul minangka implementasi metode dependensi simpul liyane. Jinis dependensi dicenthang dening compiler, amarga ngemot jinis protokol. Nalika mbukak, dependensi bakal ngemot ID simpul target sing bener. Thanks kanggo skema iki, kita nemtokake nomer port persis sapisan lan mesthi dijamin ngrujuk menyang port sing bener.

Implementasi rong node sistem

Kanggo konfigurasi iki, kita nggunakake implementasi layanan sing padha tanpa owah-owahan. Bentenipun mung saiki kita duwe rong obyek sing ngetrapake set layanan sing beda:

  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
  }

Node pisanan ngleksanakake server lan mung mbutuhake konfigurasi server. Simpul kapindho ngetrapake klien lan nggunakake bagean konfigurasi sing beda. Uga loro simpul mbutuhake manajemen umur. Node server mlaku tanpa wates nganti mandheg SIGTERM'om, lan simpul klien mungkasi sawise sawetara wektu. Cm. app ngluncurake.

Proses pangembangan umum

Ayo ndeleng carane pendekatan konfigurasi iki mengaruhi proses pembangunan sakabèhé.

Konfigurasi bakal disusun bebarengan karo liyane saka kode lan artefak (.jar) bakal kui. Iku misale jek nggawe pangertèn kanggo sijine konfigurasi ing artefak kapisah. Iki amarga kita bisa duwe sawetara konfigurasi adhedhasar kode padha. Maneh, bisa ngasilake artefak sing cocog karo cabang konfigurasi sing beda. Ketergantungan ing versi perpustakaan tartamtu disimpen bebarengan karo konfigurasi, lan versi iki disimpen ing salawas-lawase saben kita arep masang versi konfigurasi.

Sembarang owah-owahan konfigurasi dadi owah-owahan kode. Lan mulane, saben
owah-owahan bakal dijamin dening proses jaminan kualitas normal:

Tiket ing tracker bug -> PR -> review -> gabung karo cabang sing cocog ->
integrasi -> panyebaran

Konsekuensi utama kanggo ngetrapake konfigurasi sing dikompilasi yaiku:

  1. Konfigurasi bakal konsisten ing kabeh simpul sistem sing disebarake. Amarga kasunyatan manawa kabeh kelenjar nampa konfigurasi sing padha saka siji sumber.

  2. Masalah kanggo ngganti konfigurasi mung ing salah sawijining simpul. Mulane, "konfigurasi drift" ora mungkin.

  3. Dadi luwih angel kanggo nggawe owah-owahan cilik ing konfigurasi.

  4. Umume owah-owahan konfigurasi bakal kedadeyan minangka bagean saka proses pangembangan sakabèhé lan bakal ditinjau.

Apa aku butuh repositori sing kapisah kanggo nyimpen konfigurasi produksi? Konfigurasi iki bisa uga ngemot sandhi lan informasi sensitif liyane sing pengin kita matesi akses. Adhedhasar iki, misale jek nggawe pangertèn kanggo nyimpen konfigurasi final ing repositori kapisah. Sampeyan bisa pamisah konfigurasi dadi rong bagean-siji ngemot setelan konfigurasi sing bisa diakses umum lan siji ngemot setelan sing diwatesi. Iki bakal ngidini paling pangembang duwe akses menyang setelan umum. Pemisahan iki gampang digayuh nggunakake sipat penengah sing ngemot nilai standar.

Kemungkinan variasi

Coba mbandhingake konfigurasi sing dikompilasi karo sawetara alternatif umum:

  1. File teks ing mesin target.
  2. Toko nilai kunci terpusat (etcd/zookeeper).
  3. Komponen proses sing bisa dikonfigurasi maneh / diwiwiti maneh tanpa miwiti maneh proses kasebut.
  4. Nyimpen konfigurasi ing njaba artefak lan kontrol versi.

File teks nyedhiyakake keluwesan sing signifikan babagan owah-owahan cilik. Administrator sistem bisa mlebu menyang simpul remot, ngowahi file sing cocog lan miwiti maneh layanan kasebut. Nanging, kanggo sistem gedhe, keluwesan kasebut bisa uga ora dikarepake. Owah-owahan sing ditindakake ora ninggalake jejak ing sistem liyane. Ora ana sing nyemak owah-owahan. Pancen angel kanggo nemtokake sapa sing nggawe owah-owahan lan apa sebabe. Owah-owahan ora dites. Yen sistem disebarake, administrator bisa uga lali nggawe pangowahan sing cocog ing simpul liyane.

(Sampeyan uga kudu dicathet yen nggunakake konfigurasi sing dikompilasi ora nutup kemungkinan nggunakake file teks ing mangsa ngarep. Bakal cukup kanggo nambah parser lan validator sing ngasilake jinis sing padha karo output. Config, lan sampeyan bisa nggunakake file teks. Iku langsung nderek yen kerumitan sistem kanthi konfigurasi kompilasi rada kurang saka kerumitan sistem nggunakake file teks, amarga file teks mbutuhake kode tambahan.)

Toko kunci-nilai terpusat minangka mekanisme sing apik kanggo nyebarake parameter meta saka aplikasi sing disebarake. Kita kudu mutusake apa parameter konfigurasi lan apa mung data. Ayo kita duwe fungsi C => A => B, lan paramèter C arang owah-owahan, lan data A - asring. Ing kasus iki kita bisa ngomong sing C - paramèter konfigurasi, lan A - data. Katon yen paramèter konfigurasi beda karo data amarga umume owah-owahan kurang kerep tinimbang data. Uga, data biasane teka saka siji sumber (saka pangguna), lan paramèter konfigurasi saka liyane (saka administrator sistem).

Yen paramèter sing jarang diganti kudu dianyari tanpa miwiti maneh program, mula iki bisa nyebabake komplikasi program, amarga kita kudu ngirim paramèter, nyimpen, ngurai lan mriksa, lan ngolah nilai sing salah. Mulane, saka sudut pandang ngurangi kerumitan program, iku ndadekake pangertèn kanggo ngurangi jumlah paramèter sing bisa diganti sajrone operasi program (utawa ora ndhukung paramèter kasebut ing kabeh).

Kanggo tujuan kirim iki, kita bakal mbedakake antarane paramèter statis lan dinamis. Yen logika layanan mbutuhake owah-owahan paramèter sajrone operasi program, mula kita bakal nelpon paramèter kasebut dinamis. Yen ora, opsi kasebut statis lan bisa dikonfigurasi nggunakake konfigurasi sing dikompilasi. Kanggo konfigurasi ulang dinamis, kita bisa uga mbutuhake mekanisme kanggo miwiti maneh bagean program kanthi paramèter anyar, padha karo cara proses sistem operasi diwiwiti maneh. (Ing mratelakake panemume, disaranake supaya ora konfigurasi wektu nyata, amarga iki nambah kerumitan sistem. Yen bisa, luwih becik nggunakake kemampuan OS standar kanggo miwiti maneh proses.)

Siji aspek penting nggunakake konfigurasi statis sing ndadekake wong nganggep konfigurasi ulang dinamis yaiku wektu sing dibutuhake kanggo sistem urip maneh sawise nganyari konfigurasi (downtime). Nyatane, yen kita kudu ngowahi konfigurasi statis, kita kudu miwiti maneh sistem supaya nilai anyar bisa ditrapake. Masalah downtime beda-beda ing tingkat keruwetan kanggo macem-macem sistem. Ing sawetara kasus, sampeyan bisa gawe jadwal urip maneh nalika beban minimal. Yen sampeyan perlu kanggo nyedhiyani layanan terus, sampeyan bisa ngleksanakake AWS ELB sambungan draining. Ing wektu sing padha, nalika kita kudu urip maneh sistem, kita miwiti Kayata podo saka sistem iki, ngalih balancer menyang, lan ngenteni sambungan lawas rampung. Sawise kabeh sambungan lawas wis mungkasi, kita mateni conto lawas saka sistem.

Ayo saiki nimbang masalah nyimpen konfigurasi ing njero utawa njaba artefak. Yen kita nyimpen konfigurasi ing artefak, banjur paling kita duwe kesempatan kanggo verifikasi bener saka konfigurasi sak Déwan artefak. Yen konfigurasi ana ing njaba artefak sing dikontrol, angel dilacak sapa sing nggawe owahan ing file iki lan kenapa. Sepira pentinge? Miturut pendapat kita, kanggo akeh sistem produksi, penting kanggo nduwe konfigurasi sing stabil lan berkualitas.

Versi artefak ngidini sampeyan nemtokake kapan digawe, apa isine, fungsi apa sing diaktifake / dipateni, lan sapa sing tanggung jawab kanggo owah-owahan ing konfigurasi kasebut. Mesthi, nyimpen konfigurasi ing artefak mbutuhake sawetara gaweyan, supaya sampeyan kudu nggawe kaputusan informed.

Pros lan cons

Aku pengin manggon ing Pros lan cons saka teknologi ngajokaken.

Keuntungan

Ing ngisor iki minangka dhaptar fitur utama konfigurasi sistem distribusi sing dikompilasi:

  1. Priksa konfigurasi statis. Ngidini sampeyan manawa
    konfigurasi bener.
  2. Basa konfigurasi sugih. Biasane, cara konfigurasi liyane diwatesi kanggo substitusi variabel string paling akeh. Nalika nggunakake Scala, macem-macem fitur basa kasedhiya kanggo nambah konfigurasi sampeyan. Contone, kita bisa nggunakake
    sipat kanggo nilai gawan, nggunakake obyek kanggo paramèter klompok, kita bisa deleng vals ngumumaké mung sapisan (GARING) ing orane katrangan enclosing. Sampeyan bisa instantiate sembarang kelas langsung ing konfigurasi (Seq, Map, kelas khusus).
  3. DSL. Scala nduweni sawetara fitur basa sing nggawe DSL luwih gampang. Sampeyan bisa njupuk kauntungan saka fitur kasebut lan ngleksanakake basa konfigurasi sing luwih trep kanggo klompok target pangguna, supaya konfigurasi kasebut paling ora bisa diwaca dening ahli domain. Spesialis bisa, contone, melu ing proses review konfigurasi.
  4. Integritas lan sinkronisasi antarane node. Salah sawijining kaluwihan duwe konfigurasi kabeh sistem sing didistribusikake disimpen ing siji titik yaiku kabeh nilai diumumake persis sapisan lan banjur digunakake maneh ing ngendi wae sing dibutuhake. Nggunakake jinis phantom kanggo ngumumake port mesthekake yen kelenjar nggunakake protokol sing kompatibel ing kabeh konfigurasi sistem sing bener. Duwe dependensi wajib sing jelas ing antarane simpul mesthekake yen kabeh layanan disambungake.
  5. Owah-owahan kualitas dhuwur. Ngganti konfigurasi nggunakake proses pembangunan umum ndadekake iku bisa kanggo entuk standar kualitas dhuwur kanggo konfigurasi uga.
  6. Nganyari konfigurasi simultaneous. Penyebaran sistem otomatis sawise owah-owahan konfigurasi mesthekake yen kabeh simpul dianyari.
  7. Nyederhanakake aplikasi. Aplikasi ora mbutuhake parsing, mriksa konfigurasi, utawa nangani nilai sing salah. Iki nyuda kerumitan aplikasi. (Sawetara kerumitan konfigurasi diamati ing conto kita ora atribut saka konfigurasi nyawiji, nanging mung kaputusan sadar mimpin dening kepinginan kanggo nyedhiyani safety jinis luwih.) Iku cukup gampang kanggo bali menyang konfigurasi biasanipun - mung ngleksanakake ilang. bagean. Mulane, sampeyan bisa, contone, miwiti karo konfigurasi nyawiji, deferring implementasine saka bagean sing ora perlu nganti wektu nalika iku pancene perlu.
  8. Konfigurasi sing diverifikasi. Wiwit owah-owahan konfigurasi ngetutake nasibe owahan liyane, output sing dipikolehi minangka artefak kanthi versi unik. Iki ngidini kita, contone, bali menyang versi sadurungé saka konfigurasi yen perlu. Kita malah bisa nggunakake konfigurasi saka setahun kepungkur lan sistem bakal bisa persis padha. Konfigurasi stabil nambah prediksi lan linuwih saka sistem sing disebarake. Wiwit konfigurasi tetep ing tataran kompilasi, iku cukup angel kanggo palsu ing produksi.
  9. Modularitas. Rangka kerja sing diusulake yaiku modular lan modul bisa digabungake kanthi cara sing beda kanggo nggawe sistem sing beda. Ing tartamtu, sampeyan bisa ngatur sistem kanggo mbukak ing siji simpul ing siji pawujudan, lan ing sawetara kelenjar ing liyane. Sampeyan bisa nggawe sawetara konfigurasi kanggo kedadean produksi sistem.
  10. Testing. Kanthi ngganti layanan individu karo obyek mock, sampeyan bisa entuk sawetara versi sistem sing trep kanggo dites.
  11. Pengujian Integrasi. Duwe konfigurasi siji kanggo kabeh sistem mbagekke ndadekake iku bisa kanggo mbukak kabeh komponen ing lingkungan kontrol minangka bagéan saka testing integrasi. Iku gampang kanggo niru, contone, kahanan ing ngendi sawetara simpul bisa diakses.

Cacat lan watesan

Konfigurasi sing dikompilasi beda karo pendekatan konfigurasi liyane lan bisa uga ora cocog kanggo sawetara aplikasi. Ing ngisor iki sawetara kekurangan:

  1. Konfigurasi statis. Kadhangkala sampeyan kudu mbenerake konfigurasi kanthi cepet ing produksi, ngliwati kabeh mekanisme protèktif. Kanthi pendekatan iki bisa dadi luwih angel. Paling ora, kompilasi lan panyebaran otomatis isih dibutuhake. Iki minangka fitur migunani saka pendekatan lan kerugian ing sawetara kasus.
  2. Generasi konfigurasi. Ing kasus file konfigurasi kui dening alat otomatis, tambahan efforts uga dibutuhake kanggo nggabungake script mbangun.
  3. piranti. Saiki, utilitas lan teknik sing dirancang kanggo nggarap konfigurasi adhedhasar file teks. Ora kabeh keperluan / teknik kasebut bakal kasedhiya ing konfigurasi sing dikompilasi.
  4. Owah-owahan ing sikap dibutuhake. Pangembang lan DevOps wis biasa karo file teks. Gagasan banget kanggo nyusun konfigurasi bisa uga ora dikarepke lan ora biasa lan nyebabake penolakan.
  5. Proses pangembangan kualitas dhuwur dibutuhake. Kanggo nggunakake konfigurasi sing dikompilasi kanthi nyaman, otomatisasi lengkap proses mbangun lan nyebarake aplikasi (CI / CD) perlu. Yen ora, bakal cukup nyenengake.

Ayo kita uga mikirake sawetara watesan saka conto sing dianggep ora ana gandhengane karo ide konfigurasi sing dikompilasi:

  1. Yen kita nyedhiyakake informasi konfigurasi sing ora perlu sing ora digunakake dening simpul, mula kompiler ora bakal mbantu ndeteksi implementasine sing ilang. Masalah iki bisa ditanggulangi kanthi ninggalake Pola Kue lan nggunakake jinis sing luwih kaku, contone, HList utawa jinis data aljabar (kelas cilik) kanggo makili konfigurasi.
  2. Ana garis ing file konfigurasi sing ora ana hubungane karo konfigurasi kasebut: (package, import, pranyatan obyek; override def's kanggo paramèter sing duwe nilai standar). Iki bisa dihindari sebagian yen sampeyan ngetrapake DSL sampeyan dhewe. Kajaba iku, jinis konfigurasi liyane (contone, XML) uga ngetrapake watesan tartamtu ing struktur file.
  3. Kanggo tujuan kirim iki, kita ora nganggep konfigurasi ulang dinamis saka klompok simpul sing padha.

kesimpulan

Ing kirim iki, kita njelajah ide kanggo makili konfigurasi ing kode sumber nggunakake kemampuan canggih sistem jinis Scala. Pendekatan iki bisa digunakake ing macem-macem aplikasi minangka panggantos kanggo cara konfigurasi tradisional adhedhasar xml utawa file teks. Sanajan conto kita ditindakake ing Scala, gagasan sing padha bisa ditransfer menyang basa kompilasi liyane (kayata Kotlin, C#, Swift, ...). Sampeyan bisa nyoba pendekatan iki ing salah sawijining proyek ing ngisor iki, lan, yen ora bisa, pindhah menyang file teks, nambah bagean sing ilang.

Alami, konfigurasi sing dikompilasi mbutuhake proses pangembangan kualitas dhuwur. Ing bali, kualitas dhuwur lan linuwih konfigurasi wis mesthekake.

Pendekatan sing dianggep bisa ditambahi:

  1. Sampeyan bisa nggunakake makro kanggo nindakake mriksa wektu kompilasi.
  2. Sampeyan bisa ngetrapake DSL kanggo nampilake konfigurasi kanthi cara sing bisa diakses pangguna pungkasan.
  3. Sampeyan bisa ngetrapake manajemen sumber daya dinamis kanthi pangaturan konfigurasi otomatis. Contone, ngganti jumlah simpul ing kluster mbutuhake (1) saben simpul nampa konfigurasi sing rada beda; (2) manajer kluster nampa informasi babagan simpul anyar.

Matur suwun

Aku arep matur nuwun marang Andrei Saksonov, Pavel Popov lan Anton Nekhaev kanggo kritik sing mbangun babagan draf artikel kasebut.

Source: www.habr.com

Add a comment