Kompilyatsiya qilingan taqsimlangan tizim konfiguratsiyasi

Men sizga taqsimlangan tizim konfiguratsiyasi bilan ishlashning bitta qiziqarli mexanizmini aytib bermoqchiman. Konfiguratsiya xavfsiz turlardan foydalangan holda to'g'ridan-to'g'ri kompilyatsiya qilingan tilda (Scala) taqdim etiladi. Ushbu post bunday konfiguratsiyaning namunasini taqdim etadi va kompilyatsiya qilingan konfiguratsiyani umumiy rivojlanish jarayoniga tatbiq etishning turli jihatlarini muhokama qiladi.

Kompilyatsiya qilingan taqsimlangan tizim konfiguratsiyasi

(english)

kirish

Ishonchli taqsimlangan tizimni yaratish barcha tugunlar boshqa tugunlar bilan sinxronlashtirilgan to'g'ri konfiguratsiyadan foydalanishini anglatadi. DevOps texnologiyalari (terraform, ansible yoki shunga o'xshash narsalar) odatda konfiguratsiya fayllarini avtomatik ravishda yaratish uchun ishlatiladi (ko'pincha har bir tugun uchun maxsus). Bundan tashqari, barcha aloqa tugunlari bir xil protokollardan (shu jumladan bir xil versiyadan) foydalanayotganiga ishonch hosil qilishni xohlaymiz. Aks holda, bizning taqsimlangan tizimimizga nomuvofiqlik kiritiladi. JVM dunyosida ushbu talabning natijasi shundan iboratki, kutubxonaning protokol xabarlarini o'z ichiga olgan bir xil versiyasi hamma joyda ishlatilishi kerak.

Taqsimlangan tizimni sinab ko'rish haqida nima deyish mumkin? Albatta, biz integratsiya testiga o'tishdan oldin barcha komponentlar birlik testlariga ega deb taxmin qilamiz. (Sinov natijalarini ish vaqtiga ekstrapolyatsiya qilishimiz uchun biz sinov bosqichida va ish vaqtida bir xil kutubxonalar to'plamini taqdim etishimiz kerak.)

Integratsiya testlari bilan ishlashda, odatda, barcha tugunlarda hamma joyda bir xil sinf yo'lidan foydalanish osonroq. Biz qilishimiz kerak bo'lgan narsa ish vaqtida bir xil sinf yo'lidan foydalanishni ta'minlashdir. (Turli sinf yo'llari bilan turli tugunlarni ishga tushirish mutlaqo mumkin bo'lsa-da, bu umumiy konfiguratsiyaga murakkablik va joylashtirish va integratsiya testlari bilan bog'liq qiyinchiliklarga olib keladi.) Ushbu xabarning maqsadlari uchun biz barcha tugunlar bir xil sinf yo'lidan foydalanadi deb taxmin qilamiz.

Konfiguratsiya ilova bilan birga rivojlanadi. Biz dastur evolyutsiyasining turli bosqichlarini aniqlash uchun versiyalardan foydalanamiz. Konfiguratsiyalarning turli versiyalarini ham aniqlash mantiqiy ko'rinadi. Va konfiguratsiyaning o'zini versiyani boshqarish tizimiga joylashtiring. Agar ishlab chiqarishda faqat bitta konfiguratsiya mavjud bo'lsa, biz shunchaki versiya raqamidan foydalanishimiz mumkin. Agar biz ko'plab ishlab chiqarish misollaridan foydalansak, bizga bir nechta kerak bo'ladi
konfiguratsiya filiallari va versiyaga qo'shimcha ravishda qo'shimcha yorliq (masalan, filial nomi). Shunday qilib, biz aniq konfiguratsiyani aniq aniqlashimiz mumkin. Har bir konfiguratsiya identifikatori taqsimlangan tugunlar, portlar, tashqi resurslar va kutubxona versiyalarining o'ziga xos kombinatsiyasiga noyob tarzda mos keladi. Ushbu postning maqsadlari uchun biz faqat bitta filial borligini taxmin qilamiz va biz konfiguratsiyani nuqta bilan ajratilgan uchta raqamdan foydalangan holda odatiy tarzda aniqlashimiz mumkin (1.2.3).

Zamonaviy sharoitlarda konfiguratsiya fayllari kamdan-kam hollarda qo'lda yaratiladi. Ko'pincha ular joylashtirish paytida hosil bo'ladi va endi tegilmaydi (shunday qilib hech narsani buzmang). Tabiiy savol tug'iladi: nega biz konfiguratsiyani saqlash uchun hali ham matn formatidan foydalanamiz? Konfiguratsiya uchun oddiy koddan foydalanish va kompilyatsiya vaqtini tekshirishdan foyda olish mumkin bo'lgan muqobil ko'rinadi.

Ushbu postda biz kompilyatsiya qilingan artefakt ichidagi konfiguratsiyani ifodalash g'oyasini o'rganamiz.

Kompilyatsiya qilingan konfiguratsiya

Ushbu bo'limda statik kompilyatsiya qilingan konfiguratsiyaga misol keltirilgan. Ikki oddiy xizmat amalga oshiriladi - echo xizmati va echo xizmati mijozi. Ushbu ikkita xizmat asosida ikkita tizim varianti yig'iladi. Bitta variantda ikkala xizmat ham bitta tugunda, boshqa variantda - turli tugunlarda joylashgan.

Odatda taqsimlangan tizim bir nechta tugunlarni o'z ichiga oladi. Ba'zi turdagi qiymatlar yordamida tugunlarni aniqlashingiz mumkin NodeId:

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

yoki

case class NodeId(hostName: String)

yoki hatto

object Singleton
type NodeId = Singleton.type

Tugunlar turli rollarni bajaradi, ular xizmatlarni ishga tushiradi va ular o'rtasida TCP/HTTP ulanishlari o'rnatilishi mumkin.

TCP ulanishini tavsiflash uchun bizga kamida port raqami kerak bo'ladi. Mijoz va server bir xil protokoldan foydalanishini ta'minlash uchun biz ushbu portda qo'llab-quvvatlanadigan protokolni ham aks ettirmoqchimiz. Biz ulanishni quyidagi sinfdan foydalanib tasvirlaymiz:

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

qayerda Port - shunchaki butun son Int qabul qilinadigan qiymatlar oralig'ini ko'rsatuvchi:

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

Tozalangan turlar

Kutubxonaga qarang Qayta ishlangan ΠΈ mening hisobot. Qisqasi, kutubxona kompilyatsiya vaqtida tekshiriladigan turlarga cheklovlar qo'shish imkonini beradi. Bunday holda, haqiqiy port raqami qiymatlari 16 bitli butun sonlardir. Kompilyatsiya qilingan konfiguratsiya uchun tozalangan kutubxonadan foydalanish majburiy emas, lekin kompilyatorning konfiguratsiyani tekshirish qobiliyatini yaxshilaydi.

HTTP (REST) ​​protokollari uchun port raqamiga qo'shimcha ravishda bizga xizmatga yo'l ham kerak bo'lishi mumkin:

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

Fantom turlari

Kompilyatsiya vaqtida protokolni aniqlash uchun biz sinfda ishlatilmaydigan turdagi parametrdan foydalanamiz. Ushbu qaror biz ish vaqtida protokol namunasidan foydalanmasligimiz bilan bog'liq, ammo biz kompilyatordan protokollarning mosligini tekshirishini xohlaymiz. Protokolni ko'rsatib, biz noto'g'ri xizmatni qaramlik sifatida o'tkaza olmaymiz.

Umumiy protokollardan biri Json seriyali REST API hisoblanadi:

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

qayerda RequestMessage - so'rov turi, ResponseMessage - javob turi.
Albatta, biz talab qiladigan tavsifning aniqligini ta'minlaydigan boshqa protokol tavsiflaridan foydalanishimiz mumkin.

Ushbu postning maqsadlari uchun biz protokolning soddalashtirilgan versiyasidan foydalanamiz:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

Bu erda so'rov url-ga qo'shilgan satr va javob HTTP javobining tanasida qaytarilgan satrdir.

Xizmat konfiguratsiyasi xizmat nomi, portlar va bog'liqliklar bilan tavsiflanadi. Ushbu elementlarni Scala-da bir necha usul bilan ifodalash mumkin (masalan, HList-s, algebraik ma'lumotlar turlari). Ushbu postning maqsadlari uchun biz kek naqshidan foydalanamiz va modullardan foydalanamiz traits. (Kek namunasi bu yondashuvning talab qilinadigan elementi emas. Bu shunchaki mumkin bo'lgan amalga oshirishdir.)

Xizmatlar orasidagi bog'liqliklar portlarni qaytaruvchi usullar sifatida ifodalanishi mumkin EndPointboshqa tugunlarning '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)
  }

Echo xizmatini yaratish uchun sizga port raqami va port aks-sado protokolini qoβ€˜llab-quvvatlashini koβ€˜rsatish kifoya. Muayyan portni ko'rsatmasligimiz mumkin, chunki... xususiyatlar amalga oshirilmasdan usullarni e'lon qilish imkonini beradi (mavhum usullar). Bunday holda, aniq konfiguratsiyani yaratishda kompilyator bizdan mavhum usulni amalga oshirishni va port raqamini taqdim etishimizni talab qiladi. Usulni amalga oshirganimiz sababli, ma'lum bir konfiguratsiyani yaratishda biz boshqa portni ko'rsatmasligimiz mumkin. Standart qiymatdan foydalaniladi.

Mijoz konfiguratsiyasida biz echo xizmatiga bog'liqlikni e'lon qilamiz:

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

Bog'liqlik eksport qilingan xizmat bilan bir xil turdagi echoService. Xususan, echo mijozida biz bir xil protokolni talab qilamiz. Shuning uchun, ikkita xizmatni ulashda biz hamma narsa to'g'ri ishlashiga ishonch hosil qilishimiz mumkin.

Xizmatlarni amalga oshirish

Xizmatni ishga tushirish va to'xtatish uchun funksiya talab qilinadi. (Xizmatni to'xtatish qobiliyati sinov uchun juda muhimdir.) Shunga qaramay, bunday xususiyatni amalga oshirishning bir nechta variantlari mavjud (masalan, biz konfiguratsiya turiga asoslangan turdagi sinflardan foydalanishimiz mumkin). Ushbu postning maqsadlari uchun biz tort naqshidan foydalanamiz. Biz xizmatni sinf yordamida taqdim etamiz cats.Resource, chunki Ushbu sinf allaqachon muammolar yuzaga kelganda resurslarni xavfsiz ravishda chiqarishni kafolatlaydigan vositalarni taqdim etadi. Resursni olish uchun biz konfiguratsiya va tayyor ish vaqti kontekstini taqdim etishimiz kerak. Xizmatni ishga tushirish funksiyasi quyidagicha ko'rinishi mumkin:

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

qayerda

  • Config β€” ushbu xizmat uchun konfiguratsiya turi
  • AddressResolver β€” boshqa tugunlarning manzillarini aniqlash imkonini beruvchi ish vaqti obyekti (pastga qarang)

va kutubxonaning boshqa turlari cats:

  • F[_] β€” effekt turi (eng oddiy holatda F[A] faqat funksiya bo'lishi mumkin () => A. Ushbu postda biz foydalanamiz cats.IO.)
  • Reader[A,B] - funktsiyaning ko'p yoki kamroq sinonimi A => B
  • cats.Resource - olinishi va chiqarilishi mumkin bo'lgan resurs
  • Timer β€” taymer (bir muncha vaqt uxlab qolish va vaqt oralig'ini o'lchash imkonini beradi)
  • ContextShift - analog ExecutionContext
  • Applicative β€” individual effektlarni (deyarli monad) birlashtirish imkonini beruvchi effekt turi sinfi. Murakkab dasturlarda undan foydalanish yaxshiroq ko'rinadi Monad/ConcurrentEffect.

Ushbu funktsiya imzosi yordamida biz bir nechta xizmatlarni amalga oshirishimiz mumkin. Masalan, hech narsa qilmaydigan xizmat:

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

(Sm. manba kodi, unda boshqa xizmatlar amalga oshiriladi - echo xizmati, echo mijozi
ΠΈ umr bo'yi boshqaruvchilar.)

Tugun - bu bir nechta xizmatlarni ishga tushirishi mumkin bo'lgan ob'ekt (resurslar zanjirining ishga tushirilishi Cake Pattern tomonidan ta'minlanadi):

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

E'tibor bering, biz ushbu tugun uchun zarur bo'lgan konfiguratsiyaning aniq turini ko'rsatmoqdamiz. Agar ma'lum bir xizmat tomonidan talab qilinadigan konfiguratsiya turlaridan birini belgilashni unutib qo'ysak, kompilyatsiya xatosi bo'ladi. Bundan tashqari, barcha kerakli ma'lumotlar bilan mos turdagi ob'ektni taqdim qilmagunimizcha, biz tugunni ishga tushira olmaymiz.

Xost nomini aniqlash

Masofaviy xostga ulanish uchun bizga haqiqiy IP manzil kerak. Manzil konfiguratsiyaning qolgan qismidan kechroq ma'lum bo'lishi mumkin. Shunday qilib, bizga tugun identifikatorini manzilga moslashtiradigan funksiya kerak:

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

Ushbu funktsiyani amalga oshirishning bir necha yo'li mavjud:

  1. Agar manzillar o'rnatishdan oldin bizga ma'lum bo'lsa, biz Scala kodini yaratishimiz mumkin
    manzillarni kiriting va keyin qurishni ishga tushiring. Bu testlarni kompilyatsiya qiladi va ishga tushiradi.
    Bunday holda, funktsiya statik ravishda ma'lum bo'ladi va kodda xaritalash sifatida ko'rsatilishi mumkin Map[NodeId, NodeAddress].
  2. Ba'zi hollarda, haqiqiy manzil faqat tugun boshlanganidan keyin ma'lum bo'ladi.
    Bunday holda, biz boshqa tugunlardan oldin ishlaydigan "kashfiyot xizmati" ni amalga oshirishimiz mumkin va barcha tugunlar ushbu xizmatda ro'yxatdan o'tadi va boshqa tugunlarning manzillarini so'raydi.
  3. Agar o'zgartirishimiz mumkin bo'lsa /etc/hosts, keyin siz oldindan belgilangan xost nomlaridan foydalanishingiz mumkin (masalan my-project-main-node ΠΈ echo-backend) va shunchaki bu nomlarni bog'lang
    tarqatish paytida IP manzillari bilan.

Ushbu postda biz ushbu holatlarni batafsil ko'rib chiqmaymiz. Bizning uchun
o'yinchoq misolida, barcha tugunlar bir xil IP manzilga ega bo'ladi - 127.0.0.1.

Keyinchalik, taqsimlangan tizimning ikkita variantini ko'rib chiqamiz:

  1. Barcha xizmatlarni bitta tugunga joylashtirish.
  2. Va echo xizmati va echo mijozini turli tugunlarda joylashtirish.

uchun konfiguratsiya bitta tugun:

Yagona tugun konfiguratsiyasi

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

Ob'ekt mijoz va serverning konfiguratsiyasini amalga oshiradi. Yashash vaqti konfiguratsiyasi intervaldan keyin ham qo'llaniladi lifetime dasturni tugatish. (Ctrl-C ham ishlaydi va barcha resurslarni bo'shatadi.)

dan tashkil topgan tizimni yaratish uchun bir xil konfiguratsiya va amalga oshirish xususiyatlaridan foydalanish mumkin ikkita alohida tugun:

Ikki tugun konfiguratsiyasi

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

Muhim! Xizmatlar qanday bog'langanligiga e'tibor bering. Biz bir tugun tomonidan amalga oshirilgan xizmatni boshqa tugunning qaramlik usulini amalga oshirish sifatida belgilaymiz. Tobelik turi kompilyator tomonidan tekshiriladi, chunki protokol turini o'z ichiga oladi. Ishga tushirilganda, qaramlik to'g'ri maqsadli tugun identifikatorini o'z ichiga oladi. Ushbu sxema tufayli biz port raqamini bir marta aniqlaymiz va har doim to'g'ri portga murojaat qilish kafolatlanadi.

Ikki tizim tugunini amalga oshirish

Ushbu konfiguratsiya uchun biz o'zgartirishlarsiz bir xil xizmat ilovalaridan foydalanamiz. Yagona farq shundaki, endi bizda turli xil xizmatlar to'plamini amalga oshiradigan ikkita ob'ekt mavjud:

  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
  }

Birinchi tugun serverni amalga oshiradi va faqat server konfiguratsiyasini talab qiladi. Ikkinchi tugun mijozni amalga oshiradi va konfiguratsiyaning boshqa qismini ishlatadi. Bundan tashqari, ikkala tugun ham umr bo'yi boshqarishga muhtoj. Server tuguni to'xtatilgunga qadar cheksiz ishlaydi SIGTERM'om, va mijoz tuguni bir muncha vaqt o'tgach tugaydi. Sm. ishga tushirish ilovasi.

Umumiy rivojlanish jarayoni

Keling, ushbu konfiguratsiya yondashuvi umumiy rivojlanish jarayoniga qanday ta'sir qilishini ko'rib chiqaylik.

Konfiguratsiya kodning qolgan qismi bilan birga kompilyatsiya qilinadi va artefakt (.jar) hosil bo'ladi. Konfiguratsiyani alohida artefaktga qo'yish mantiqiy ko'rinadi. Buning sababi shundaki, biz bir xil kod asosida bir nechta konfiguratsiyaga ega bo'lishimiz mumkin. Shunga qaramay, turli xil konfiguratsiya tarmoqlariga mos keladigan artefaktlarni yaratish mumkin. Kutubxonalarning ma'lum versiyalariga bog'liqliklar konfiguratsiya bilan birga saqlanadi va biz konfiguratsiyaning ushbu versiyasini o'rnatishga qaror qilganimizda, bu versiyalar abadiy saqlanadi.

Har qanday konfiguratsiya o'zgarishi kod o'zgarishiga aylanadi. Va shuning uchun har biri
o'zgarish odatdagi sifatni ta'minlash jarayoni bilan qoplanadi:

Xatolarni kuzatuvchisidagi chipta -> PR -> ko'rib chiqish -> tegishli filiallar bilan birlash ->
integratsiya -> joylashtirish

Kompilyatsiya qilingan konfiguratsiyani amalga oshirishning asosiy oqibatlari:

  1. Konfiguratsiya taqsimlangan tizimning barcha tugunlarida izchil bo'ladi. Barcha tugunlar bitta manbadan bir xil konfiguratsiyani olishlari sababli.

  2. Tugunlarning faqat bittasida konfiguratsiyani o'zgartirish muammoli. Shuning uchun, "konfiguratsiya o'zgarishi" ehtimoldan yiroq emas.

  3. Konfiguratsiyaga kichik o'zgarishlar kiritish qiyinroq bo'ladi.

  4. Ko'pgina konfiguratsiya o'zgarishlari umumiy ishlab chiqish jarayonining bir qismi sifatida amalga oshiriladi va ko'rib chiqilishi kerak.

Ishlab chiqarish konfiguratsiyasini saqlash uchun menga alohida ombor kerakmi? Ushbu konfiguratsiya parollar va biz kirishni cheklamoqchi bo'lgan boshqa maxfiy ma'lumotlarni o'z ichiga olishi mumkin. Shunga asoslanib, yakuniy konfiguratsiyani alohida omborda saqlash mantiqiy ko'rinadi. Siz konfiguratsiyani ikki qismga bo'lishingiz mumkin - biri umumiy foydalanish mumkin bo'lgan konfiguratsiya sozlamalarini va ikkinchisi cheklangan sozlamalarni o'z ichiga oladi. Bu ko'pchilik ishlab chiquvchilarga umumiy sozlamalarga kirish imkonini beradi. Standart qiymatlarni o'z ichiga olgan oraliq belgilar yordamida bu ajratish oson.

Mumkin bo'lgan o'zgarishlar

Keling, kompilyatsiya qilingan konfiguratsiyani ba'zi umumiy muqobillar bilan solishtirishga harakat qilaylik:

  1. Maqsadli mashinadagi matn fayli.
  2. Markazlashtirilgan kalit-qiymat do'koni (etcd/zookeeper).
  3. Jarayonni qaytadan boshlamasdan qayta sozlanishi/qayta ishga tushirilishi mumkin bo'lgan jarayon komponentlari.
  4. Konfiguratsiyani artefakt va versiya boshqaruvidan tashqarida saqlash.

Matn fayllari kichik o'zgarishlar nuqtai nazaridan sezilarli moslashuvchanlikni ta'minlaydi. Tizim ma'muri masofaviy tugunga kirishi, tegishli fayllarga o'zgartirishlar kiritishi va xizmatni qayta ishga tushirishi mumkin. Katta tizimlar uchun esa bunday moslashuvchanlik istalmagan bo'lishi mumkin. Kiritilgan o'zgarishlar boshqa tizimlarda hech qanday iz qoldirmaydi. O'zgarishlarni hech kim ko'rib chiqmaydi. O'zgarishlarni aynan kim va nima sababdan amalga oshirganini aniqlash qiyin. O'zgarishlar sinovdan o'tkazilmaydi. Agar tizim taqsimlangan bo'lsa, administrator boshqa tugunlarda tegishli o'zgartirishni unutishi mumkin.

(Shuni ham ta'kidlash kerakki, kompilyatsiya qilingan konfiguratsiyadan foydalanish kelajakda matnli fayllardan foydalanish imkoniyatini yopmaydi. Chiqish bilan bir xil turdagi tahlilchi va validatorni qo'shish kifoya qiladi. Config, va siz matnli fayllardan foydalanishingiz mumkin. Bundan darhol kelib chiqadiki, tuzilgan konfiguratsiyaga ega tizimning murakkabligi matnli fayllardan foydalanadigan tizimning murakkabligidan biroz kamroqdir, chunki matnli fayllar qo'shimcha kod talab qiladi.)

Markazlashtirilgan kalit-qiymat do'koni taqsimlangan dasturning meta parametrlarini tarqatish uchun yaxshi mexanizmdir. Konfiguratsiya parametrlari nima va faqat ma'lumotlar nima ekanligini hal qilishimiz kerak. Keling, bir funktsiyaga ega bo'lamiz C => A => B, va parametrlar C kamdan-kam hollarda o'zgaradi va ma'lumotlar A - tez-tez. Bunday holda, biz buni aytishimiz mumkin C - konfiguratsiya parametrlari va A - ma'lumotlar. Ko'rinishidan, konfiguratsiya parametrlari ma'lumotlardan farq qiladi, chunki ular odatda ma'lumotlarga qaraganda kamroq o'zgaradi. Bundan tashqari, ma'lumotlar odatda bitta manbadan (foydalanuvchidan) va konfiguratsiya parametrlari boshqasidan (tizim administratoridan) keladi.

Agar kamdan-kam o'zgaruvchan parametrlarni dasturni qayta ishga tushirmasdan yangilash kerak bo'lsa, bu ko'pincha dasturning murakkablashishiga olib kelishi mumkin, chunki biz qandaydir tarzda parametrlarni etkazib berishimiz, saqlashimiz, tahlil qilishimiz va tekshirishimiz va noto'g'ri qiymatlarni qayta ishlashimiz kerak bo'ladi. Shuning uchun, dasturning murakkabligini kamaytirish nuqtai nazaridan, dasturning ishlashi davomida o'zgarishi mumkin bo'lgan parametrlar sonini kamaytirish mantiqan to'g'ri keladi (yoki bunday parametrlarni umuman qo'llab-quvvatlamaydi).

Ushbu postning maqsadlari uchun biz statik va dinamik parametrlarni ajratamiz. Agar xizmat mantig'i dasturning ishlashi davomida parametrlarni o'zgartirishni talab qilsa, biz bunday parametrlarni dinamik deb ataymiz. Aks holda variantlar statik bo'lib, kompilyatsiya qilingan konfiguratsiya yordamida sozlanishi mumkin. Dinamik qayta konfiguratsiya uchun bizga operatsion tizim jarayonlari qanday qayta ishga tushirilganiga o'xshash yangi parametrlar bilan dastur qismlarini qayta ishga tushirish mexanizmi kerak bo'lishi mumkin. (Bizning fikrimizcha, real vaqt rejimida qayta konfiguratsiyadan qochish tavsiya etiladi, chunki bu tizimning murakkabligini oshiradi. Iloji bo'lsa, jarayonlarni qayta ishga tushirish uchun standart OT imkoniyatlaridan foydalangan ma'qul.)

Statik konfiguratsiyadan foydalanishning odamlarni dinamik qayta konfiguratsiya haqida o'ylashga majbur qiladigan muhim jihatlaridan biri bu konfiguratsiya yangilangandan so'ng tizimni qayta ishga tushirish vaqti (to'xtab qolish vaqti). Aslida, agar biz statik konfiguratsiyaga o'zgartirishlar kiritishimiz kerak bo'lsa, yangi qiymatlar kuchga kirishi uchun tizimni qayta ishga tushirishimiz kerak bo'ladi. To'xtab qolish muammosi turli tizimlar uchun jiddiylik darajasida farq qiladi. Ba'zi hollarda, yuk minimal bo'lgan vaqtda qayta ishga tushirishni rejalashtirishingiz mumkin. Agar siz uzluksiz xizmat ko'rsatishingiz kerak bo'lsa, siz amalga oshirishingiz mumkin AWS ELB ulanishini yo'qotish. Shu bilan birga, tizimni qayta ishga tushirishimiz kerak bo'lganda, biz ushbu tizimning parallel nusxasini ishga tushiramiz, balanslagichni unga o'tkazamiz va eski ulanishlar tugashini kutamiz. Barcha eski ulanishlar tugatilgandan so'ng, biz tizimning eski nusxasini o'chirib qo'yamiz.

Keling, konfiguratsiyani artefakt ichida yoki tashqarisida saqlash masalasini ko'rib chiqaylik. Agar konfiguratsiyani artefakt ichida saqlasak, hech bo'lmaganda artefaktni yig'ish paytida konfiguratsiyaning to'g'riligini tekshirish imkoniga ega bo'ldik. Agar konfiguratsiya boshqariladigan artefaktdan tashqarida bo'lsa, ushbu faylga kim va nima uchun o'zgarishlar kiritganini kuzatish qiyin. Bu qanchalik muhim? Bizning fikrimizcha, ko'plab ishlab chiqarish tizimlari uchun barqaror va yuqori sifatli konfiguratsiyaga ega bo'lish muhimdir.

Artefaktning versiyasi uning qachon yaratilganligini, u qanday qiymatlarni o'z ichiga olganligini, qanday funktsiyalar yoqilgan/o'chirilganligini va konfiguratsiyadagi har qanday o'zgarishlar uchun kim javobgar ekanligini aniqlash imkonini beradi. Albatta, konfiguratsiyani artefakt ichida saqlash biroz harakat talab qiladi, shuning uchun siz ongli qaror qabul qilishingiz kerak.

Tushkunlik va kamchiliklar

Men taklif etilayotgan texnologiyaning ijobiy va salbiy tomonlariga to'xtalib o'tmoqchiman.

Kanada PR

Quyida kompilyatsiya qilingan taqsimlangan tizim konfiguratsiyasining asosiy xususiyatlari ro'yxati keltirilgan:

  1. Statik konfiguratsiyani tekshirish. Bunga ishonch hosil qilish imkonini beradi
    konfiguratsiya to'g'ri.
  2. Keng konfiguratsiya tili. Odatda, boshqa konfiguratsiya usullari ko'pi bilan satr o'zgaruvchisini almashtirish bilan cheklanadi. Scala-dan foydalanganda konfiguratsiyani yaxshilash uchun til xususiyatlarining keng doirasi mavjud. Masalan, biz foydalanishimiz mumkin
    parametrlarni guruhlash uchun ob'ektlardan foydalangan holda standart qiymatlar uchun xususiyatlar, biz qo'shimcha doirada faqat bir marta e'lon qilingan (QURUQ) qiymatlarga murojaat qilishimiz mumkin. Siz to'g'ridan-to'g'ri konfiguratsiya ichida har qanday sinfni yaratishingiz mumkin (Seq, Map, maxsus sinflar).
  3. DSL. Scala DSL yaratishni osonlashtiradigan bir qator til xususiyatlariga ega. Ushbu xususiyatlardan foydalanish va maqsadli foydalanuvchilar guruhi uchun qulayroq bo'lgan konfiguratsiya tilini amalga oshirish mumkin, shunda konfiguratsiya hech bo'lmaganda domen mutaxassislari tomonidan o'qilishi mumkin. Mutaxassislar, masalan, konfiguratsiyani ko'rib chiqish jarayonida ishtirok etishlari mumkin.
  4. Tugunlar orasidagi yaxlitlik va sinxronlik. Butun taqsimlangan tizim konfiguratsiyasini bitta nuqtada saqlashning afzalliklaridan biri shundaki, barcha qiymatlar bir marta e'lon qilinadi va keyin ular kerak bo'lganda qayta ishlatiladi. Portlarni e'lon qilish uchun fantom turlaridan foydalanish tugunlarning barcha to'g'ri tizim konfiguratsiyalarida mos protokollardan foydalanishini ta'minlaydi. Tugunlar o'rtasida aniq majburiy bog'liqliklarning mavjudligi barcha xizmatlarning ulanganligini ta'minlaydi.
  5. Yuqori sifatli o'zgarishlar. Umumiy ishlab chiqish jarayonidan foydalangan holda konfiguratsiyaga o'zgartirishlar kiritish konfiguratsiya uchun ham yuqori sifat standartlariga erishish imkonini beradi.
  6. Bir vaqtning o'zida konfiguratsiyani yangilash. Konfiguratsiya o'zgarishlaridan so'ng avtomatik tizimni joylashtirish barcha tugunlarning yangilanishini ta'minlaydi.
  7. Ilovani soddalashtirish. Ilovaga tahlil qilish, konfiguratsiyani tekshirish yoki noto'g'ri qiymatlarni qayta ishlash kerak emas. Bu dasturning murakkabligini kamaytiradi. (Bizning misolimizda kuzatilgan ba'zi konfiguratsiya murakkabligi kompilyatsiya qilingan konfiguratsiyaning atributi emas, balki faqat kattaroq turdagi xavfsizlikni ta'minlash istagidan kelib chiqqan ongli qarordir.) Odatiy konfiguratsiyaga qaytish juda oson - etishmayotgan narsalarni amalga oshirish kifoya. qismlar. Shuning uchun, masalan, keraksiz qismlarni amalga oshirishni haqiqatan ham zarur bo'lgan vaqtga qoldirib, tuzilgan konfiguratsiyadan boshlashingiz mumkin.
  8. Tasdiqlangan konfiguratsiya. Konfiguratsiya o'zgarishlari boshqa har qanday o'zgarishlarning odatiy taqdiriga mos kelganligi sababli, biz olgan natija noyob versiyaga ega artefaktdir. Bu, masalan, agar kerak bo'lsa, konfiguratsiyaning oldingi versiyasiga qaytishga imkon beradi. Biz hatto bir yil oldingi konfiguratsiyadan foydalanishimiz mumkin va tizim xuddi shunday ishlaydi. Barqaror konfiguratsiya taqsimlangan tizimning taxminiy va ishonchliligini yaxshilaydi. Konfiguratsiya kompilyatsiya bosqichida o'rnatilganligi sababli, uni ishlab chiqarishda soxtalashtirish juda qiyin.
  9. Modullilik. Taklif etilayotgan ramka modulli bo'lib, modullar turli xil tizimlarni yaratish uchun turli usullar bilan birlashtirilishi mumkin. Xususan, tizimni bitta versiyada bitta tugunda, boshqasida bir nechta tugunlarda ishlashi uchun sozlashingiz mumkin. Tizimning ishlab chiqarish namunalari uchun bir nechta konfiguratsiyalarni yaratishingiz mumkin.
  10. Sinov. Shaxsiy xizmatlarni soxta ob'ektlar bilan almashtirish orqali siz tizimning sinov uchun qulay bo'lgan bir nechta versiyalarini olishingiz mumkin.
  11. Integratsiya testi. Butun taqsimlangan tizim uchun yagona konfiguratsiyaga ega bo'lish integratsiya testining bir qismi sifatida barcha komponentlarni boshqariladigan muhitda ishga tushirish imkonini beradi. Masalan, ba'zi tugunlarga kirish mumkin bo'lgan vaziyatga taqlid qilish oson.

Kamchiliklari va cheklovlari

Kompilyatsiya qilingan konfiguratsiya boshqa konfiguratsiya yondashuvlaridan farq qiladi va ba'zi ilovalar uchun mos kelmasligi mumkin. Quyida ba'zi kamchiliklar mavjud:

  1. Statik konfiguratsiya. Ba'zan siz barcha himoya mexanizmlarini chetlab o'tib, ishlab chiqarishdagi konfiguratsiyani tezda tuzatishingiz kerak. Ushbu yondashuv bilan bu yanada qiyin bo'lishi mumkin. Hech bo'lmaganda, kompilyatsiya va avtomatik joylashtirish hali ham talab qilinadi. Bu yondashuvning foydali xususiyati va ba'zi hollarda kamchilikdir.
  2. Konfiguratsiyani yaratish. Agar konfiguratsiya fayli avtomatik vosita tomonidan yaratilgan bo'lsa, qurish skriptini birlashtirish uchun qo'shimcha harakatlar talab qilinishi mumkin.
  3. Asboblar. Hozirgi vaqtda konfiguratsiya bilan ishlash uchun mo'ljallangan yordamchi dasturlar va texnikalar matnli fayllarga asoslangan. Bunday yordamchi dasturlar/texnikalarning hammasi ham kompilyatsiya qilingan konfiguratsiyada mavjud bo'lmaydi.
  4. Xulq-atvorni o'zgartirish kerak. Dasturchilar va DevOps matnli fayllarga o'rganib qolgan. Konfiguratsiyani tuzish g'oyasi biroz kutilmagan va g'ayrioddiy bo'lishi va rad etishga olib kelishi mumkin.
  5. Yuqori sifatli rivojlanish jarayoni talab qilinadi. Kompilyatsiya qilingan konfiguratsiyadan qulay foydalanish uchun dasturni yaratish va joylashtirish jarayonini to'liq avtomatlashtirish (CI/CD) zarur. Aks holda, bu juda noqulay bo'ladi.

Keling, ko'rib chiqilgan misolning tuzilgan konfiguratsiya g'oyasi bilan bog'liq bo'lmagan bir qator cheklovlariga ham to'xtalib o'tamiz:

  1. Agar biz tugun tomonidan foydalanilmaydigan konfiguratsiya ma'lumotlarini taqdim qilsak, kompilyator bizga etishmayotgan dasturni aniqlashga yordam bermaydi. Bu muammoni tort namunasidan voz kechish va qattiqroq turlardan foydalanish orqali hal qilish mumkin, masalan, HList yoki konfiguratsiyani ifodalash uchun algebraik ma'lumotlar turlari (holat sinflari).
  2. Konfiguratsiya faylida konfiguratsiyaning o'zi bilan bog'liq bo'lmagan qatorlar mavjud: (package, import,obyekt deklaratsiyasi; override defStandart qiymatlarga ega bo'lgan parametrlar uchun). Agar siz o'zingizning DSL-ni qo'llasangiz, bu qisman oldini olish mumkin. Bundan tashqari, konfiguratsiyaning boshqa turlari (masalan, XML) ham fayl tuzilishiga ma'lum cheklovlar qo'yadi.
  3. Ushbu postning maqsadlari uchun biz shunga o'xshash tugunlar klasterini dinamik qayta konfiguratsiyani ko'rib chiqmayapmiz.

xulosa

Ushbu postda biz Scala tipidagi tizimning ilg'or imkoniyatlaridan foydalangan holda manba kodida konfiguratsiyani ko'rsatish g'oyasini o'rganib chiqdik. Ushbu yondashuv turli xil ilovalarda xml yoki matn fayllariga asoslangan an'anaviy konfiguratsiya usullarini almashtirish sifatida ishlatilishi mumkin. Bizning misolimiz Scala-da amalga oshirilgan bo'lsa ham, xuddi shu g'oyalar boshqa kompilyatsiya qilingan tillarga (masalan, Kotlin, C#, Swift, ...) o'tkazilishi mumkin. Siz ushbu yondashuvni quyidagi loyihalardan birida sinab ko'rishingiz mumkin va agar u ishlamasa, etishmayotgan qismlarni qo'shib, matn fayliga o'ting.

Tabiiyki, kompilyatsiya qilingan konfiguratsiya yuqori sifatli ishlab chiqish jarayonini talab qiladi. Buning evaziga konfiguratsiyalarning yuqori sifati va ishonchliligi ta'minlanadi.

Ko'rib chiqilgan yondashuvni kengaytirish mumkin:

  1. Siz kompilyatsiya vaqtini tekshirish uchun makroslardan foydalanishingiz mumkin.
  2. Siz konfiguratsiyani oxirgi foydalanuvchilar uchun ochiq bo'ladigan tarzda taqdim etish uchun DSL-ni qo'llashingiz mumkin.
  3. Siz konfiguratsiyani avtomatik sozlash bilan dinamik resurslarni boshqarishni amalga oshirishingiz mumkin. Masalan, klasterdagi tugunlar sonini o'zgartirish (1) har bir tugun biroz boshqacha konfiguratsiyani olishini talab qiladi; (2) klaster menejeri yangi tugunlar haqida ma'lumot oldi.

Rahmatlar

Men Andrey Saksonov, Pavel Popov va Anton Nexayevga maqola loyihasini konstruktiv tanqid qilgani uchun minnatdorchilik bildiraman.

Manba: www.habr.com

a Izoh qo'shish