Konfigurasi compilable tina sistem disebarkeun

Dina tulisan ieu kami hoyong bagikeun cara anu pikaresepeun pikeun ngatur konfigurasi sistem anu disebarkeun.
Konfigurasi diwakilan langsung dina basa Scala dina cara anu aman. Hiji conto palaksanaan dijelaskeun dina detil. Rupa-rupa aspék proposal dibahas, kaasup pangaruh kana prosés pangwangunan sakabéh.

Konfigurasi compilable tina sistem disebarkeun

(dina basa Rusia)

perkenalan

Ngawangun sistem distribusi anu kuat butuh panggunaan konfigurasi anu leres sareng koheren dina sadaya titik. Solusi anu umum nyaéta ngagunakeun déskripsi panyebaran tékstual (terraform, ansible atanapi anu sami) sareng file konfigurasi otomatis dibangkitkeun (sering - dedicated kanggo unggal titik / peran). Urang ogé hoyong nganggo protokol anu sami tina vérsi anu sami dina unggal titik komunikasi (sabalikna urang bakal ngalaman masalah teu cocog). Di dunya JVM ieu ngandung harti yén sahenteuna perpustakaan olahtalatah kedah tina versi anu sami dina sadaya titik komunikasi.

Kumaha upami nguji sistem? Tangtosna, urang kedah gaduh tés unit pikeun sadaya komponén sateuacan sumping ka tés integrasi. Pikeun tiasa extrapolate hasil tés dina runtime, urang kedah pastikeun yén versi sadaya perpustakaan tetep idéntik dina duanana runtime jeung lingkungan nguji.

Nalika ngajalankeun tés integrasi, éta sering langkung gampang gaduh jalur kelas anu sami dina sadaya titik. Urang ngan perlu mastikeun yén classpath sarua dipaké dina deployment. (Kasebut nyaéta dimungkinkeun pikeun ngagunakeun classpaths béda dina titik béda, tapi leuwih hese ngagambarkeun konfigurasi ieu sareng neuleu nyebarkeun eta.) Ku kituna dina urutan tetep hal basajan urang ngan bakal mertimbangkeun classpaths idéntik dina sakabéh titik.

Konfigurasi condong mekar bareng jeung software. Urang biasana ngagunakeun versi pikeun ngaidentipikasi rupa
tahapan évolusi software. Sigana wajar pikeun nutupan konfigurasi dina manajemén versi sareng ngaidentipikasi konfigurasi anu béda sareng sababaraha labél. Lamun aya ngan hiji konfigurasi dina produksi, urang bisa make versi tunggal salaku identifier. Kadang urang tiasa gaduh sababaraha lingkungan produksi. Sareng pikeun tiap lingkungan urang peryogi cabang konfigurasi anu misah. Janten konfigurasi tiasa dilabélan ku cabang sareng versi pikeun ngaidentipikasi konfigurasi anu béda. Unggal labél cabang jeung versi pakait jeung kombinasi tunggal titik disebarkeun, palabuhan, sumberdaya éksternal, versi perpustakaan classpath on unggal titik. Di dieu urang ngan bakal nutupan cabang tunggal jeung nangtukeun konfigurasi ku tilu komponén versi decimal (1.2.3), dina cara nu sarua salaku artefak séjén.

Dina lingkungan modern, file konfigurasi henteu dirobih sacara manual deui. Biasana urang ngahasilkeun
file config dina waktos deployment na pernah noel aranjeunna saterusna. Janten tiasa naroskeun naha urang masih nganggo format téks pikeun file konfigurasi? Pilihan anu lumayan nyaéta nempatkeun konfigurasi di jero unit kompilasi sareng kauntungan tina validasi konfigurasi waktos kompilasi.

Dina tulisan ieu urang bakal nalungtik ide pikeun ngajaga konfigurasi dina artefak anu disusun.

Konfigurasi compilable

Dina bagian ieu urang bakal ngabahas conto konfigurasi statik. Dua jasa saderhana - jasa gema sareng klien jasa gema nuju dikonpigurasi sareng dilaksanakeun. Lajeng dua sistem disebarkeun béda jeung duanana jasa anu instantiated. Hiji pikeun konfigurasi titik tunggal sareng hiji deui pikeun konfigurasi dua titik.

Hiji sistem disebarkeun has diwangun ku sababaraha titik. Titik tiasa diidentifikasi nganggo sababaraha jinis:

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

atawa sakadar

case class NodeId(hostName: String)

atawa malah

object Singleton
type NodeId = Singleton.type

Titik-titik ieu ngalaksanakeun rupa-rupa kalungguhan, ngajalankeun sababaraha jasa sareng kedah tiasa komunikasi sareng titik-titik sanés ku cara sambungan TCP/HTTP.

Pikeun sambungan TCP sahenteuna jumlah port diperlukeun. Kami ogé hoyong mastikeun yén klien sareng server nyarioskeun protokol anu sami. Dina raraga model sambungan antara titik hayu urang dibewarakeun kelas handap:

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

di mana Port ngan hiji Int dina rentang anu diidinan:

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

Jenis refined

Tempo refined perpustakaan. Pondokna, ngamungkinkeun pikeun nambihan konstrain waktos kompilasi kana jinis anu sanés. Dina hal ieu Int ngan diidinan gaduh nilai 16-bit anu tiasa ngagambarkeun nomer port. Teu aya sarat pikeun ngagunakeun perpustakaan ieu pikeun pendekatan konfigurasi ieu. Ieu ngan sigana pas pisan.

Pikeun HTTP (REST) ​​​​urang ogé peryogi jalur jasa:

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

Jenis Phantom

Pikeun ngaidentipikasi protokol nalika kompilasi kami nganggo fitur Scala pikeun nyatakeun jinis argumen Protocol nu teu dipaké di kelas. Éta anu disebut tipe phantom. Dina runtime urang jarang butuh hiji conto identifier protokol, éta naha urang teu nyimpen eta. Salila kompilasi tipe phantom ieu méré kaamanan tipe tambahan. Urang teu bisa lulus port jeung protokol salah.

Salah sahiji protokol anu paling seueur dianggo nyaéta REST API sareng serialisasi Json:

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

di mana RequestMessage mangrupa tipe dasar pesen nu klien bisa ngirim ka server na ResponseMessage mangrupa pesen respon ti server. Tangtu, urang bisa nyieun déskripsi protokol séjén nu nangtukeun protokol komunikasi jeung precision dipikahoyong.

Pikeun kaperluan tulisan ieu kami bakal nganggo versi protokol anu langkung saderhana:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

Dina protokol ieu pesen pamundut ieu appended kana url jeung pesen respon balik salaku string polos.

Konfigurasi jasa tiasa dijelaskeun ku nami jasa, kumpulan palabuhan sareng sababaraha katergantungan. Aya sababaraha cara anu mungkin pikeun ngagambarkeun sadaya unsur ieu dina Scala (contona, HList, tipe data aljabar). Pikeun kaperluan tulisan ieu kami bakal nganggo Pola Kue sareng ngagambarkeun potongan anu tiasa digabungkeun (modul) salaku sipat. (Pola Kue teu sarat pikeun pendekatan konfigurasi compilable ieu. Ieu ngan hiji mungkin palaksanaan ide.)

Dependensi tiasa diwakilan nganggo Pola Kue salaku titik tungtung titik anu sanés:

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

Ladenan Echo ngan ukur peryogi port anu dikonpigurasi. Sarta kami nyatakeun yén port ieu ngarojong protokol gema. Catet yén urang henteu kedah netepkeun port khusus dina waktos ayeuna, sabab tret ngamungkinkeun deklarasi metode abstrak. Lamun urang ngagunakeun métode abstrak, compiler bakal merlukeun palaksanaan dina conto konfigurasi. Di dieu kami parantos nyayogikeun palaksanaan (8081) sarta eta bakal dipaké salaku nilai standar lamun urang skip eta dina konfigurasi beton.

Urang tiasa nyatakeun katergantungan dina konfigurasi klien jasa gema:

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

Gumantungna boga tipe sarua jeung echoService. Khususna, éta nungtut protokol anu sami. Lantaran kitu, urang tiasa yakin yén upami urang nyambungkeun dua katergantungan ieu, aranjeunna bakal tiasa dianggo leres.

Palaksanaan jasa

Hiji layanan perlu fungsi pikeun ngamimitian tur gracefully shutdown. (Kamampuhan pikeun shutdown layanan mangrupa kritis pikeun nguji.) Deui aya sababaraha pilihan pikeun ngahususkeun fungsi misalna pikeun config dibikeun (Contona, urang bisa make kelas tipe). Pikeun tulisan ieu kami bakal nganggo Pola Kue deui. Urang bisa ngagambarkeun layanan ngagunakeun cats.Resource nu geus nyadiakeun bracketing jeung release sumberdaya. Dina raraga acquire sumberdaya urang kudu nyadiakeun konfigurasi sarta sababaraha konteks runtime. Janten fungsi ngamimitian jasa sigana sapertos kieu:

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

di mana

  • Config - jinis konfigurasi anu diperyogikeun ku ngamimitian jasa ieu
  • AddressResolver - obyék runtime anu gaduh kamampuan pikeun kéngingkeun alamat nyata tina titik anu sanés (terus maca pikeun detil).

jenis séjén asalna ti cats:

  • F[_] - jinis pangaruh (Dina kasus pangbasajanna F[A] bisa ngan () => A. Dina tulisan ieu kami bakal nganggo cats.IO.)
  • Reader[A,B] - nyaeta leuwih atawa kirang sinonim pikeun fungsi A => B
  • cats.Resource - boga cara pikeun acquire jeung ngaleupaskeun
  • Timer - ngamungkinkeun pikeun bobo / ngukur waktos
  • ContextShift - analog tina ExecutionContext
  • Applicative - bungkus fungsi dina pangaruh (ampir monad a) (urang ahirna bisa ngaganti eta ku hal sejenna)

Ngagunakeun panganteur ieu urang bisa nerapkeun sababaraha layanan. Salaku conto, jasa anu henteu ngalakukeun nanaon:

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

(Tempo Kode sumber pikeun palaksanaan jasa anu sanés - layanan gema,
echo klien jeung Controllers hirupna.)

Titik mangrupikeun obyék tunggal anu ngajalankeun sababaraha jasa (ngamimitian ranté sumber diaktipkeun ku Pola Kue):

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

Catet yén dina node urang tangtukeun tipe pasti tina konfigurasi nu diperlukeun ku titik ieu. Compiler moal ngantep urang ngawangun obyék (Kue) kalayan jinis anu henteu cekap, sabab unggal ciri jasa nyatakeun konstrain dina Config ngetik. Ogé kami moal tiasa ngamimitian node tanpa nyayogikeun konfigurasi lengkep.

Resolusi alamat titik

Dina raraga nyieun sambungan urang peryogi alamat host nyata pikeun tiap titik. Bisa jadi dipikawanoh engké ti bagian séjén tina konfigurasi. Lantaran kitu, urang peryogi cara pikeun nyayogikeun pemetaan antara id titik sareng alamat anu saleresna. pemetaan ieu mangrupa fungsi:

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

Aya sababaraha cara anu mungkin pikeun ngalaksanakeun fungsi sapertos kitu.

  1. Lamun urang nyaho alamat sabenerna saméméh deployment, salila instantiation node sarwa, mangka urang bisa ngahasilkeun kode Scala jeung alamat nu sabenerna tur ngajalankeun ngawangun afterwards (anu ngalakukeun cék waktu compile lajeng ngajalankeun integrasi test suite). Dina hal ieu fungsi pemetaan urang dipikawanoh statik sarta bisa disederhanakeun kana hal kawas a Map[NodeId, NodeAddress].
  2. Kadang-kadang urang ménta alamat sabenerna ngan dina titik engké nalika titik sabenerna dimimitian, atawa urang teu boga alamat titik nu teu acan dimimitian. Dina hal ieu urang tiasa gaduh jasa panemuan anu dimimitian sateuacan sadaya titik sanés sareng unggal titik tiasa ngiklankeun alamatna dina jasa éta sareng ngalanggan katergantungan.
  3. Lamun urang bisa ngaropéa /etc/hosts, urang tiasa nganggo nami host anu tos disetel (sapertos my-project-main-node jeung echo-backend) sareng ngan ngahubungkeun nami ieu sareng alamat ip dina waktos panyebaran.

Dina tulisan ieu kami henteu nutupan kasus ieu dina langkung rinci. Nyatana dina conto kaulinan urang sadaya titik bakal gaduh alamat IP anu sami - 127.0.0.1.

Dina tulisan ieu kami bakal nganggap dua tata letak sistem anu disebarkeun:

  1. Tata perenah titik tunggal, dimana sadaya jasa disimpen dina titik tunggal.
  2. Tata perenah dua titik, dimana jasa sareng klien aya dina titik anu béda.

Konfigurasi pikeun a node tunggal perenah nyaéta kieu:

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

Di dieu urang nyieun hiji konfigurasi tunggal nu manjangan duanana konfigurasi server na klien. Ogé urang ngonpigurasikeun controller lifecycle nu normal bakal nungtungan klien tur server sanggeus lifetime interval pas.

Susunan palaksanaan sareng konfigurasi jasa anu sami tiasa dianggo pikeun nyiptakeun tata sistem sareng dua titik anu misah. Urang ngan perlu nyieun dua configs titik misah kalawan layanan luyu:

Konfigurasi dua titik

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

Tempo kumaha urang nangtukeun gumantungna. Kami nyebatkeun jasa anu disayogikeun ku titik sanés salaku kagumantungan titik ayeuna. Jinis kagumantungan dipariksa sabab ngandung tipe hantu anu ngajelaskeun protokol. Sareng dina runtime urang bakal gaduh id titik anu leres. Ieu salah sahiji aspék penting tina pendekatan konfigurasi diusulkeun. Eta nyadiakeun kami kalawan kamampuhan pikeun nyetél port ngan sakali tur pastikeun yén kami nujul kana port bener.

Palaksanaan dua titik

Pikeun konfigurasi ieu kami nganggo palaksanaan jasa anu sami. Taya parobahan pisan. Nanging, urang nyiptakeun dua palaksanaan titik anu béda-béda anu ngandung set jasa anu béda:

  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
  }

Titik kahiji implements server na eta ngan perlu config sisi server. Titik kadua implements klien tur perlu bagian séjén tina config. Duanana titik merlukeun sababaraha spésifikasi hirupna. Pikeun tujuan ieu titik jasa pos bakal boga hirupna taya wates nu bisa terminated maké SIGTERM, sedengkeun klien gema bakal nungtungan sanggeus durasi terhingga ngonpigurasi. Tempo éta aplikasi starter pikeun wincikanana.

Prosés pangwangunan sakabéh

Hayu urang tingali kumaha pendekatan ieu ngarobih cara urang damel sareng konfigurasi.

Konfigurasi salaku kode bakal disusun tur ngahasilkeun artefak. Sigana wajar mun misahkeun artefak konfigurasi tina artefak kode lianna. Sering urang tiasa gaduh seueur konfigurasi dina dasar kode anu sami. Sareng tangtosna, urang tiasa gaduh sababaraha versi tina sababaraha cabang konfigurasi. Dina konfigurasi urang bisa milih versi husus perpustakaan jeung ieu bakal tetep konstan iraha wae urang nyebarkeun konfigurasi ieu.

A robah konfigurasi jadi robah kode. Janten éta kedah katutupan ku prosés jaminan kualitas anu sami:

Tiket -> PR -> review -> ngahiji -> integrasi kontinyu -> deployment kontinyu

Aya konsekuensi handap tina pendekatan:

  1. Konfigurasi koheren pikeun conto sistem tinangtu. Sigana mah teu aya cara pikeun gaduh sambungan anu salah antara titik.
  2. Teu gampang pikeun ngarobah konfigurasi ngan dina hiji titik. Sigana teu masuk akal pikeun log in sareng ngarobih sababaraha file téks. Jadi drift konfigurasi janten kirang mungkin.
  3. Parobihan konfigurasi leutik henteu gampang dilakukeun.
  4. Seuseueurna parobahan konfigurasi bakal nuturkeun prosés pangwangunan anu sami, sareng éta bakal lulus sababaraha ulasan.

Naha urang peryogi gudang anu misah pikeun konfigurasi produksi? Konfigurasi produksi tiasa ngandung inpormasi sénsitip anu ku urang hoyong dijauhkeun tina jangkauan seueur jalma. Janten éta patut ngajaga gudang anu misah sareng aksés terbatas anu bakal ngandung konfigurasi produksi. Urang tiasa ngabagi konfigurasi kana dua bagian - hiji anu ngandung parameter produksi anu paling kabuka sareng hiji anu ngandung bagian rahasia tina konfigurasi. Ieu bakal ngamungkinkeun aksés ka sabagéan ageung pamekar ka seuseueurna parameter bari ngabatesan aksés kana hal-hal anu sénsitip. Ieu gampang keur ngalengkepan ieu ngagunakeun Tret panengah jeung nilai parameter standar.

variasi

Hayu urang tingali pro sareng kontra ngeunaan pendekatan anu diusulkeun dibandingkeun sareng téknik manajemén konfigurasi anu sanés.

Anu mimiti, urang bakal daptar sababaraha alternatif pikeun aspék béda tina cara anu diusulkeun pikeun ngatur konfigurasi:

  1. file téks dina mesin target.
  2. Panyimpenan nilai konci terpusat (sapertos etcd/zookeeper).
  3. Komponén subprosés anu tiasa dikonfigurasi deui / dibalikan deui tanpa ngamimitian deui prosés.
  4. Konfigurasi luar artefak jeung kontrol versi.

File téks masihan sababaraha kalenturan dina hal perbaikan ad-hoc. Administrator sistem tiasa login ka titik target, ngadamel perobihan sareng ngan saukur ngamimitian deui jasa. Ieu panginten henteu saé pikeun sistem anu langkung ageung. Taya jejak anu tinggaleun robahna. Parobahan teu ditinjau ku sapasang panon séjén. Bisa jadi hésé pikeun manggihan naon anu ngabalukarkeun parobahan. Teu acan diuji. Tina sudut pandang sistem anu disebarkeun, administrator ngan saukur tiasa hilap ngapdet konfigurasi dina salah sahiji titik anu sanés.

(Btw, upami ahirna aya kabutuhan pikeun ngamimitian nganggo file konfigurasi téks, urang ngan ukur kedah nambihan parser + validator anu tiasa ngahasilkeun anu sami. Config ngetik sareng éta cekap pikeun ngamimitian nganggo configs téks. Ieu ogé nunjukkeun yén pajeulitna konfigurasi waktos kompilasi sakedik langkung alit tibatan pajeulitna konfigurasi dumasar-téks, sabab dina versi dumasar-téks urang peryogi sababaraha kode tambahan.)

Panyimpenan konci-nilai terpusat mangrupikeun mékanisme anu hadé pikeun nyebarkeun parameter meta aplikasi. Di dieu urang kedah mikirkeun naon anu urang anggap salaku nilai konfigurasi sareng naon waé data. Dibéré fungsi C => A => B urang biasana nelepon jarang ngarobah nilai C "konfigurasi", bari remen robah data A - ngan input data. Konfigurasi kudu disadiakeun pikeun fungsi saméméhna ti data A. Dibikeun gagasan ieu urang bisa disebutkeun yen eta diperkirakeun frékuénsi parobahan naon bisa dipaké pikeun ngabedakeun data konfigurasi tina ngan data. Ogé data biasana asalna tina hiji sumber (pamaké) sareng konfigurasi asalna tina sumber anu béda (admin). Nguruskeun parameter anu tiasa dirobih saatos prosés inisialisasi nyababkeun paningkatan pajeulitna aplikasi. Pikeun parameter sapertos urang kedah nanganan mékanisme pangiriman, parsing sareng validasi, nanganan nilai anu salah. Lantaran kitu, pikeun ngirangan pajeulitna program, langkung saé urang ngirangan jumlah parameter anu tiasa robih dina waktos jalanna (atanapi ngaleungitkeun sadayana).

Tina sudut pandang tulisan ieu urang kedah ngabédakeun antara parameter statik sareng dinamis. Lamun logika jasa merlukeun parobahan langka sababaraha parameter dina runtime, mangka urang bisa nelepon aranjeunna parameter dinamis. Upami teu kitu, aranjeunna statik sareng tiasa dikonpigurasi nganggo pendekatan anu diusulkeun. Pikeun reconfiguration dinamis pendekatan séjén bisa jadi diperlukeun. Contona, bagian tina sistem bisa jadi restarted kalawan parameter konfigurasi anyar dina cara nu sarupa jeung restarting prosés misah tina sistem disebarkeun.
(Pendapat kuring anu hina nyaéta pikeun nyegah konfigurasi ulang runtime sabab ningkatkeun pajeulitna sistem.
Bisa jadi leuwih lugas mun ngan ngandelkeun rojongan OS prosés balikan deui. Padahal, éta moal salawasna mungkin.)

Hiji aspék penting tina ngagunakeun konfigurasi statik nu kadang ngajadikeun jalma mertimbangkeun konfigurasi dinamis (tanpa alesan séjén) nyaéta downtime jasa salila update konfigurasi. Mémang, upami urang kedah parobihan kana konfigurasi statik, urang kedah ngabalikan deui sistem supados nilai-nilai énggal janten épéktip. Sarat pikeun downtime beda-beda pikeun sistem anu béda, janten panginten henteu kritis. Upami éta kritis, maka urang kedah ngarencanakeun sateuacanna pikeun sistem balikan deui. Contona, urang bisa nerapkeun sambungan AWS ELB draining. Dina skenario ieu iraha wae urang kudu ngabalikan deui sistem, urang mimitian hiji conto anyar tina sistem dina paralel, lajeng pindah ELB ka dinya, bari ngantep sistem heubeul pikeun ngabéréskeun ngalayanan sambungan aya.

Kumaha upami ngajaga konfigurasi di jero artefak versi atanapi di luar? Ngajaga konfigurasi di jero artefak hartosna dina kalolobaan kasus yén konfigurasi ieu parantos ngalangkungan prosés jaminan kualitas anu sami sareng artefak sanés. Janten urang tiasa yakin yén konfigurasina kualitasna saé sareng tiasa dipercaya. Sabalikna konfigurasi dina file misah hartina euweuh ngambah saha jeung naha nyieun parobahan kana file éta. Ieu penting? Kami yakin yén pikeun sabagéan ageung sistem produksi langkung saé gaduh konfigurasi anu stabil sareng kualitas luhur.

Vérsi artefak ngamungkinkeun pikeun manggihan iraha eta dijieun, naon nilai eta ngandung, naon fitur diaktipkeun / ditumpurkeun, anu tanggung jawab pikeun nyieun unggal parobahan dina konfigurasi. Éta peryogi sababaraha usaha pikeun ngajaga konfigurasi di jero artefak sareng éta mangrupikeun pilihan desain.

Naros & kontra

Di dieu urang hoyong nyorot sababaraha kaunggulan sareng ngabahas sababaraha kalemahan pendekatan anu diusulkeun.

kaunggulan

Fitur tina konfigurasi compilable tina sistem disebarkeun lengkep:

  1. Pariksa statik tina konfigurasi. Hal ieu méré tingkat luhur kapercayaan, yén konfigurasi bener dibere konstrain tipe.
  2. Basa euyeub konfigurasi. Ilaharna, pendekatan konfigurasi anu sanés dugi ka paling substitusi variabel.
    Ngagunakeun Scala hiji bisa ngagunakeun rupa-rupa fitur basa pikeun nyieun konfigurasi hadé. Salaku conto, urang tiasa nganggo sipat pikeun nyayogikeun nilai standar, objék pikeun ngeset wengkuan anu béda, urang tiasa ningali vals diartikeun ngan sakali dina wengkuan luar (Gering). Ieu mungkin migunakeun runtuyan literal, atawa instansi tina kelas tangtu (Seq, Map, Jsb).
  3. DSL. Scala ngagaduhan dukungan anu santun pikeun panulis DSL. Hiji tiasa nganggo fitur ieu pikeun ngadegkeun basa konfigurasi nu leuwih merenah tur ramah-pamaké tungtung, ku kituna konfigurasi final sahenteuna bisa dibaca ku pamaké domain.
  4. Integritas jeung kohérénsi sakuliah titik. Salah sahiji kauntungan gaduh konfigurasi pikeun sistem anu disebarkeun di hiji tempat nyaéta yén sadaya nilai ditetepkeun sacara ketat sakali teras dianggo deui di sadaya tempat dimana urang peryogina. Ketik ogé deklarasi palabuhan aman mastikeun yén dina sagala konfigurasi anu leres, titik sistem bakal nyarios basa anu sami. Aya katergantungan eksplisit antara titik-titik anu matak sesah hilap nyayogikeun sababaraha jasa.
  5. Kualitas luhur parobahan. Pendekatan sakabéh ngaliwatan parobahan konfigurasi ngaliwatan prosés PR normal ngadegkeun standar kualitas luhur ogé dina konfigurasi.
  6. parobahan konfigurasi simultaneous. Iraha wae urang nyieun parobahan dina konfigurasi deployment otomatis ensures yén sakabéh titik keur diropéa.
  7. Nyederhanakeun aplikasi. Aplikasina henteu kedah nga-parse sareng ngesahkeun konfigurasi sareng nanganan nilai konfigurasi anu salah. Ieu simplifies sakabéh aplikasi. (Sababaraha kanaékan pajeulitna aya dina konfigurasi sorangan, tapi mangrupakeun trade-off sadar kana kaamanan.) Ieu geulis lugas mun balik deui ka konfigurasi biasa - ngan nambahan potongan leungit. Langkung gampang pikeun ngamimitian sareng konfigurasi anu disusun sareng nunda palaksanaan potongan tambahan pikeun sababaraha waktos engké.
  8. Konfigurasi versioned. Kusabab kanyataan yén parobahan konfigurasi nuturkeun prosés pangwangunan anu sami, salaku hasilna urang nampi artefak kalayan versi anu unik. Éta ngamungkinkeun urang ngalihkeun konfigurasi deui upami diperyogikeun. Urang malah tiasa nyebarkeun konfigurasi anu dianggo sataun katukang sareng éta bakal jalanna sami. Konfigurasi stabil ngaronjatkeun predictability jeung reliabilitas sistem disebarkeun. Konfigurasi dibenerkeun dina waktos kompilasi sareng teu tiasa gampang dirusak dina sistem produksi.
  9. Modularitas. Kerangka anu diusulkeun nyaéta modular sareng modul tiasa digabungkeun dina sababaraha cara pikeun
    ngarojong konfigurasi béda (setups / layouts). Khususna, mungkin waé gaduh perenah titik tunggal skala leutik sareng setélan multi node skala ageung. Ieu lumrah mun gaduh sababaraha layouts produksi.
  10. Nguji. Pikeun tujuan nguji hiji bisa nerapkeun layanan bohongan sarta ngagunakeun éta salaku kagumantungan dina tipe cara aman. Sababaraha tata perenah tés anu béda sareng sababaraha bagian anu diganti ku moyok tiasa dijaga sakaligus.
  11. Tés integrasi. Kadang dina sistem anu disebarkeun sesah ngajalankeun tés integrasi. Ngagunakeun pendekatan dijelaskeun pikeun ngetik konfigurasi aman tina sistem disebarkeun lengkep, urang tiasa ngajalankeun sagala bagian disebarkeun dina server tunggal dina cara controllable. Gampang niru kaayaan
    lamun salah sahiji layanan jadi teu sadia.

kalemahan

Pendekatan konfigurasi anu disusun béda sareng konfigurasi "normal" sareng éta henteu cocog sareng sadaya kabutuhan. Ieu sababaraha kalemahan tina konfigurasi anu disusun:

  1. Konfigurasi statik. Bisa jadi teu cocog pikeun sakabéh aplikasi. Dina sababaraha kasus aya kabutuhan gancang ngalereskeun konfigurasi dina produksi bypassing sadaya ukuran kaamanan. pendekatan ieu ngajadikeun eta leuwih hese. Kompilasi sareng redeployment diperyogikeun saatos ngadamel perobihan dina konfigurasi. Ieu mangrupikeun fitur sareng beban.
  2. Generasi konfigurasi. Nalika config dihasilkeun ku sababaraha alat automation pendekatan ieu merlukeun kompilasi saterusna (anu bisa dina gilirannana gagal). Éta peryogi usaha tambahan pikeun ngahijikeun léngkah tambahan ieu kana sistem ngawangun.
  3. Instrumén. Aya seueur alat anu dianggo ayeuna anu ngandelkeun konfigurasi dumasar téks. Sababaraha di antarana
    moal tiasa dianggo nalika konfigurasi disusun.
  4. A shift dina mindset diperlukeun. Pamekar sareng DevOps akrab sareng file konfigurasi téks. Gagasan pikeun nyusun konfigurasi sigana aneh pikeun aranjeunna.
  5. Saméméh ngenalkeun konfigurasi compilable prosés ngembangkeun software kualitas luhur diperlukeun.

Aya sababaraha watesan dina conto dilaksanakeun:

  1. Upami kami nyayogikeun konfigurasi tambahan anu henteu ditungtut ku palaksanaan titik, kompiler moal ngabantosan urang pikeun ngadeteksi palaksanaan anu henteu aya. Ieu bisa direngsekeun ku ngagunakeun HList atanapi ADTs (kelas kasus) pikeun konfigurasi titik tinimbang Tret jeung Pola Kue.
  2. Urang kedah nyayogikeun sababaraha boilerplate dina file config: (package, import, object deklarasi;
    override def's pikeun parameter nu boga nilai standar). Ieu tiasa sawaréh kajawab nganggo DSL.
  3. Dina postingan ieu kami henteu nutupan konfigurasi ulang dinamis tina klaster titik anu sami.

kacindekan

Dina tulisan ieu kami parantos ngabahas ide pikeun ngagambarkeun konfigurasi langsung dina kode sumber dina cara anu aman. Pendekatan ieu tiasa dianggo dina seueur aplikasi salaku gaganti xml- sareng konfigurasi dumasar-téks anu sanés. Sanaos conto urang parantos dilaksanakeun di Scala, éta ogé tiasa ditarjamahkeun kana basa anu tiasa dikompilasi (sapertos Kotlin, C #, Swift, jsb.). Hiji tiasa nyobian pendekatan ieu dina proyék énggal sareng, upami éta henteu pas, gentos kana cara anu lami.

Tangtu, konfigurasi compilable merlukeun prosés ngembangkeun kualitas luhur. Sabalikna eta janji nyadiakeun konfigurasi mantap kualitas sarua luhur.

Pendekatan ieu tiasa diperpanjang ku sababaraha cara:

  1. Hiji tiasa nganggo makro pikeun ngalakukeun validasi konfigurasi sareng gagal dina waktos kompilasi upami aya konstrain logika bisnis gagal.
  2. A DSL bisa dilaksanakeun pikeun ngagambarkeun konfigurasi dina cara ramah-pamaké domain.
  3. Manajemén sumberdaya dinamis sareng panyesuaian konfigurasi otomatis. Contona, nalika urang nyaluyukeun jumlah titik klaster urang meureun hoyong (1) titik pikeun ménta konfigurasi rada dirobah; (2) manajer klaster pikeun nampa info titik anyar.

hatur nuhun

Abdi hoyong ngucapkeun hatur nuhun ka Andrey Saksonov, Pavel Popov, Anton Nehaev pikeun masihan eupan balik inspirasi dina draf tulisan ieu anu ngabantosan kuring langkung jelas.

sumber: www.habr.com