In-nases Terraform

In-nases Terraform
Ejja nenfasizzaw ftit nases, inklużi dawk relatati ma' loops, if dikjarazzjonijiet u tekniki ta' skjerament, kif ukoll kwistjonijiet aktar ġenerali li jaffettwaw lil Terraform b'mod ġenerali:

  • il-parametri count u for_each għandhom limitazzjonijiet;
  • jillimitaw żero skjeramenti ta' waqfien;
  • anke pjan tajjeb jista’ jfalli;
  • ir-refactoring jista' jkollu l-iżvantaġġi tiegħu;
  • il-koerenza differita hija konsistenti... mad-differiment.

Il-parametri count u for_each għandhom limitazzjonijiet

L-eżempji f'dan il-kapitolu jagħmlu użu estensiv mill-parametru tal-għadd u l-espressjoni for_each f'linji u loġika kondizzjonali. Huma jaħdmu tajjeb, iżda għandhom żewġ limitazzjonijiet importanti li trid tkun konxju minnhom.

  • Count u for_each ma jistgħux jirreferu għall-ebda varjabbli tal-output tar-riżorsi.
  • count u for_each ma jistgħux jintużaw fil-konfigurazzjoni tal-modulu.

count u for_each ma jistgħux jirreferu għall-ebda varjabbli tal-output tar-riżorsi

Immaġina li għandek bżonn tuża diversi servers EC2 u għal xi raġuni ma tridx tuża ASG. Il-kodiċi tiegħek jista' jkun bħal dan:

resource "aws_instance" "example_1" {
   count             = 3
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Ejja nħarsu lejhom waħda waħda.

Peress li l-parametru tal-għadd huwa ssettjat għal valur statiku, dan il-kodiċi jaħdem mingħajr problemi: meta tħaddem il-kmand tal-applika, se toħloq tliet servers EC2. Imma x'jiġri jekk ridt tuża server wieħed f'kull Żona ta' Disponibbiltà (AZ) fir-reġjun AWS attwali tiegħek? Jista' jkollok il-kodiċi tiegħek jgħabbi lista ta' żoni mis-sors tad-dejta aws_availability_zones u mbagħad iddur f'kull waħda u toħloq server EC2 fiha billi tuża l-parametru tal-għadd u l-aċċess għall-indiċi tal-firxa:

resource "aws_instance" "example_2" {
   count                   = length(data.aws_availability_zones.all.names)
   availability_zone   = data.aws_availability_zones.all.names[count.index]
   ami                     = "ami-0c55b159cbfafe1f0"
   instance_type       = "t2.micro"
}

data "aws_availability_zones" "all" {}

Dan il-kodiċi se jaħdem tajjeb ukoll, peress li l-parametru tal-għadd jista 'jirreferi sorsi tad-dejta mingħajr problemi. Imma x'jiġri jekk in-numru ta 'servers li għandek bżonn toħloq jiddependi fuq l-output ta' xi riżorsa? Biex turi dan, l-eħfef mod huwa li tuża r-riżorsa random_integer, li, kif tissuġġerixxi l-isem, tagħti numru sħiħ każwali:

resource "random_integer" "num_instances" {
  min = 1
  max = 3
}

Dan il-kodiċi jiġġenera numru każwali bejn 1 u 3. Ejja naraw x'jiġri jekk nippruvaw nużaw l-output ta 'din ir-riżorsa fil-parametru tal-għadd tar-riżorsa aws_instance:

resource "aws_instance" "example_3" {
   count             = random_integer.num_instances.result
   ami                = "ami-0c55b159cbfafe1f0"
   instance_type = "t2.micro"
}

Jekk tmexxi l-pjan terraform fuq dan il-kodiċi, ikollok l-iżball li ġej:

Error: Invalid count argument

   on main.tf line 30, in resource "aws_instance" "example_3":
   30: count = random_integer.num_instances.result

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.

Terraform jeħtieġ li l-għadd u l-for_each jiġu kkalkulati matul il-fażi tal-ippjanar, qabel ma jinħolqu jew jiġu modifikati kwalunkwe riżorsi. Dan ifisser li count u for_each jistgħu jirreferu għal letterali, varjabbli, sorsi ta 'dejta, u anke listi ta' riżorsi (sakemm it-tul tagħhom jista 'jiġi ddeterminat fil-ħin tal-iskedar), iżda mhux għal varjabbli ta' output tar-riżorsi kkalkulati.

count u for_each ma jistgħux jintużaw fil-konfigurazzjoni tal-modulu

Xi darba tista' tkun it-tentazzjoni li żżid parametru tal-għadd mal-konfigurazzjoni tal-modulu tiegħek:

module "count_example" {
     source = "../../../../modules/services/webserver-cluster"

     count = 3

     cluster_name = "terraform-up-and-running-example"
     server_port = 8080
     instance_type = "t2.micro"
}

Dan il-kodiċi jipprova juża l-għadd ġewwa modulu biex joħloq tliet kopji tar-riżorsa tal-webserver-cluster. Jew tista 'tkun trid tagħmel il-konnessjoni ta' modulu mhux obbligatorja bbażata fuq xi kundizzjoni Boolean billi tistabbilixxi l-parametru tal-għadd tiegħu għal 0. Dan jista 'jidher qisu kodiċi raġonevoli, imma int ikollok dan l-iżball meta tmexxi l-pjan terraform:

Error: Reserved argument name in module block

   on main.tf line 13, in module "count_example":
   13: count = 3

The name "count" is reserved for use in a future version of Terraform.

Sfortunatament, minn Terraform 0.12.6, l-użu ta' count jew for_each f'riżors ta' modulu mhuwiex appoġġjat. Skont in-noti tar-rilaxx Terraform 0.12 (http://bit.ly/3257bv4), HashiCorp qed tippjana li żżid din il-kapaċità fil-futur, għalhekk skont meta taqra dan il-ktieb, jista 'jkun diġà disponibbli. Biex issir taf żgur, aqra l-changelog Terraform hawn.

Limitazzjonijiet ta' Skjeramenti ta' Żero Waqfien

L-użu tal-blokk create_before_destroy flimkien ma 'ASG hija soluzzjoni kbira għall-ħolqien ta' skjeramenti mingħajr perijodi ta' waqfien, ħlief għal twissija waħda: ir-regoli tal-autoscaling mhumiex appoġġjati. Jew biex tkun aktar preċiża, dan jerġa 'jissettja d-daqs ASG lura għal min_size fuq kull skjerament, li tista' tkun problema jekk tkun qed tuża regoli ta 'autoscaling biex iżżid in-numru ta' servers li qed jaħdmu.

Pereżempju, il-modulu tal-webserver-cluster fih par ta 'riżorsi aws_autoscaling_schedule, li fid-9 am iżid in-numru ta' servers fil-cluster minn tnejn għal għaxra. Jekk tuża, ngħidu aħna, fil-11 a.m., l-ASG il-ġdid se jibda b'żewġ servers biss aktar milli għaxra u jibqa' hekk sad-9 a.m. l-għada.

Din il-limitazzjoni tista' tiġi evitata b'diversi modi.

  • Ibdel il-parametru ta' rikorrenza f'aws_autoscaling_schedule minn 0 9 * * * (“run fid-9 am”) għal xi ħaġa bħal 0-59 9-17 * * * (“run kull minuta mid-9 am sal-5 pm”). Jekk ASG diġà għandu għaxar servers, it-tħaddim ta 'din ir-regola ta' awtoscaling mill-ġdid ma jbiddel xejn, li huwa dak li rridu. Iżda jekk l-ASG ġie skjerat dan l-aħħar, din ir-regola tiżgura li f’massimu ta’ minuta n-numru tas-servers tiegħu jilħaq għaxra. Dan mhuwiex approċċ kompletament eleganti, u qabżiet kbar minn għaxra għal żewġ servers u lura jistgħu wkoll jikkawżaw problemi għall-utenti.
  • Oħloq skript personalizzat li juża l-AWS API biex tiddetermina n-numru ta’ servers attivi fl-ASG, sejjaħlu billi tuża sors ta’ dejta estern (ara “Sors ta’ Dejta Esterni” f’paġna 249), u ssettja l-parametru tal-kapaċità mixtieqa tal-ASG għall-valur ritornat minn l-iskrittura. Dan il-mod, kull istanza ASG ġdida dejjem se taħdem bl-istess kapaċità bħall-kodiċi Terraform eżistenti u tagħmilha aktar diffiċli biex tinżamm.

Naturalment, Terraform idealment ikollu appoġġ inkorporat għal skjeramenti mingħajr perijodi ta' waqfien, iżda minn Mejju 2019, it-tim ta' HashiCorp ma kellu l-ebda pjan li jżid din il-funzjonalità (dettalji - hawn).

Il-pjan korrett jista' jiġi implimentat mingħajr suċċess

Xi drabi l-kmand tal-pjan jipproduċi pjan ta' skjerament perfettament korrett, iżda l-kmand tal-applika jirritorna żball. Ipprova, pereżempju, żid ir-riżorsa aws_iam_user bl-istess isem li użajt għall-utent IAM li ħloqt aktar kmieni fil-Kapitolu 2:

resource "aws_iam_user" "existing_user" {
   # Подставьте сюда имя уже существующего пользователя IAM,
   # чтобы попрактиковаться в использовании команды terraform import
   name = "yevgeniy.brikman"
}

Issa, jekk tmexxi l-kmand tal-pjan, Terraform joħroġ pjan ta’ skjerament li jidher raġonevoli:

Terraform will perform the following actions:

   # aws_iam_user.existing_user will be created
   + resource "aws_iam_user" "existing_user" {
         + arn                  = (known after apply)
         + force_destroy   = false
         + id                    = (known after apply)
         + name               = "yevgeniy.brikman"
         + path                 = "/"
         + unique_id         = (known after apply)
      }

Plan: 1 to add, 0 to change, 0 to destroy.

Jekk tmexxi l-kmand tal-applikazzjoni, ikollok l-iżball li ġej:

Error: Error creating IAM User yevgeniy.brikman: EntityAlreadyExists:
User with name yevgeniy.brikman already exists.

   on main.tf line 10, in resource "aws_iam_user" "existing_user":
   10: resource "aws_iam_user" "existing_user" {

Il-problema, ovvjament, hija li diġà jeżisti utent IAM b'dak l-isem. U dan jista' jiġri mhux biss għall-utenti tal-IAM, iżda għal kważi kull riżorsa. Huwa possibbli li xi ħadd ħoloq din ir-riżorsa manwalment jew bl-użu tal-linja tal-kmand, iżda jew il-mod, it-tqabbil tal-IDs iwassal għal kunflitti. Hemm ħafna varjazzjonijiet ta' dan l-iżball li ħafna drabi jaqbdu b'sorpriża lil dawk li ġejjin ġodda għal Terraform.

Il-punt ewlieni huwa li l-kmand tal-pjan terraform iqis biss dawk ir-riżorsi li huma speċifikati fil-fajl tal-istat Terraform. Jekk ir-riżorsi jinħolqu b'xi mod ieħor (pereżempju, manwalment billi tikklikkja fil-console AWS), ma jispiċċawx fil-fajl tal-istat u għalhekk Terraform mhux se jqishom meta tesegwixxi l-kmand tal-pjan. Bħala riżultat, pjan li jidher korrett mal-ewwel daqqa t'għajn se jirriżulta li ma jirnexxix.

Hemm żewġ lezzjonijiet li wieħed jitgħallem minn dan.

  • Jekk diġà bdejt taħdem ma' Terraform, tużax xi ħaġa oħra. Jekk parti mill-infrastruttura tiegħek hija ġestita bl-użu ta' Terraform, ma tistax tibqa' timmodifikaha manwalment. Inkella, mhux biss tirriskja żbalji Terraform strambi, iżda wkoll tiċħad ħafna mill-benefiċċji ta 'IaC peress li l-kodiċi mhux se jibqa' rappreżentazzjoni preċiża tal-infrastruttura tiegħek.
  • Jekk diġà għandek xi infrastruttura, uża l-kmand tal-importazzjoni. Jekk qed tibda tuża Terraform b'infrastruttura eżistenti, tista 'żżidha mal-fajl tal-istat billi tuża l-kmand tal-importazzjoni terraform. B'dan il-mod Terraform ikun jaf x'infrastruttura trid tiġi ġestita. Il-kmand tal-importazzjoni jieħu żewġ argumenti. L-ewwel huwa l-indirizz tar-riżorsi fil-fajls tal-konfigurazzjoni tiegħek. Is-sintassi hawnhekk hija l-istess bħal għal links tar-riżorsi: _. (bħal aws_iam_user.existing_user). It-tieni argument huwa l-ID tar-riżorsa li għandha tiġi importata. Ejja ngħidu li l-ID tar-riżorsi aws_iam_user huwa l-isem tal-utent (per eżempju, yevgeniy.brikman), u l-ID tar-riżorsi aws_instance huwa l-ID tas-server EC2 (bħal i-190e22e5). Kif timporta riżorsa normalment tkun indikata fid-dokumentazzjoni fil-qiegħ tal-paġna tagħha.

    Hawn taħt hawn kmand tal-importazzjoni li jissinkronizza r-riżorsa aws_iam_user li żidt mal-konfigurazzjoni Terraform tiegħek flimkien mal-utent IAM fil-Kapitolu 2 (li jissostitwixxi ismek għal yevgeniy.brikman, ovvjament):

    $ terraform import aws_iam_user.existing_user yevgeniy.brikman

    Terraform se jsejjaħ l-AWS API biex isib l-utent IAM tiegħek u joħloq assoċjazzjoni tal-fajl tal-istat bejnu u r-riżorsa aws_iam_user.existing_user fil-konfigurazzjoni Terraform tiegħek. Minn issa 'l quddiem, meta tmexxi l-kmand tal-pjan, Terraform ikun jaf li l-utent IAM diġà jeżisti u mhux se jipprova jerġa' joħloq.

    Ta 'min jinnota li jekk diġà għandek ħafna riżorsi li trid timporta f'Terraform, tikteb il-kodiċi manwalment u timporta kull wieħed kull darba tista' tkun ta 'battikata. Allura ta 'min wieħed iħares lejn għodda bħal Terraforming (http://terraforming.dtan4.net/), li tista' awtomatikament timporta kodiċi u tiddikjara mill-kont AWS tiegħek.

    Ir-refactoring jista’ jkollu l-iżvantaġġi tiegħu

    Refactoring hija prattika komuni fl-ipprogrammar fejn tibdel l-istruttura interna tal-kodiċi filwaqt li tħalli l-imġieba esterna mhux mibdula. Dan biex jagħmel il-kodiċi aktar ċar, aktar pulit, u aktar faċli biex jinżamm. Ir-refactoring hija teknika indispensabbli li għandha tintuża regolarment. Imma meta niġu għal Terraform jew kwalunkwe għodda IaC oħra, trid tkun estremament attent dwar dak li tfisser "imġieba esterna" ta 'biċċa kodiċi, inkella jinqalgħu problemi mhux mistennija.

    Per eżempju, tip komuni ta 'refactoring huwa li tissostitwixxi l-ismijiet tal-varjabbli jew tal-funzjonijiet ma' oħrajn li jinftiehmu aktar. Ħafna IDEs għandhom appoġġ integrat għar-refactoring u jistgħu awtomatikament jibdlu l-isem tal-varjabbli u l-funzjonijiet matul il-proġett. F'lingwi ta' programmar għal skopijiet ġenerali, din hija proċedura trivjali li tista' ma taħsibx dwarha, iżda f'Terraform trid toqgħod attent ħafna b'dan, inkella tista' tesperjenza interruzzjonijiet.

    Pereżempju, il-modulu tal-webserver-cluster għandu input varjabbli cluster_name:

    variable "cluster_name" {
       description = "The name to use for all the cluster resources"
       type          = string
    }

    Immaġina li bdejt tuża dan il-modulu biex tuża mikroservizz imsejjaħ foo. Aktar tard, trid tibdel l-isem tas-servizz tiegħek għal bar. Din il-bidla tista’ tidher trivjali, iżda fir-realtà tista’ tikkawża tfixkil fis-servizz.

    Il-fatt hu li l-modulu tal-webserver-cluster juża l-varjabbli cluster_name f'numru ta 'riżorsi, inkluż il-parametru tal-isem ta' żewġ gruppi ta 'sigurtà u l-ALB:

    resource "aws_lb" "example" {
       name                    = var.cluster_name
       load_balancer_type = "application"
       subnets = data.aws_subnet_ids.default.ids
       security_groups      = [aws_security_group.alb.id]
    }

    Jekk tibdel il-parametru tal-isem fuq riżorsa, Terraform iħassar il-verżjoni l-qadima ta’ dik ir-riżorsa u toħloq waħda ġdida minflokha. Imma jekk dik ir-riżorsa tkun ALB, bejn it-tħassir tagħha u t-tniżżil ta' verżjoni ġdida, ma jkollokx mekkaniżmu biex tindirizza mill-ġdid it-traffiku lejn is-server tal-web tiegħek. Bl-istess mod, jekk grupp tas-sigurtà jitħassar, is-servers tiegħek jibdew jirrifjutaw kwalunkwe traffiku tan-netwerk sakemm jinħoloq grupp ġdid.

    Tip ieħor ta 'refactoring li jista' jkun interessat fih huwa li tbiddel l-ID Terraform. Ejja nieħdu r-riżorsa aws_security_group fil-modulu webserver-cluster bħala eżempju:

    resource "aws_security_group" "instance" {
      # (...)
    }

    L-identifikatur ta’ din ir-riżorsa jissejjaħ istanza. Immaġina li waqt ir-refactoring iddeċidejt li tibdelha għal isem aktar li jinftiehem (fl-opinjoni tiegħek) cluster_instance:

    resource "aws_security_group" "cluster_instance" {
       # (...)
    }

    X'se jiġri fl-aħħar? Dak id-dritt: tfixkil.

    Terraform jassoċja kull ID tar-riżorsi mal-ID tal-fornitur tas-sħab. Pereżempju, iam_user huwa assoċjat mal-ID tal-utent AWS IAM, u aws_instance huwa assoċjat mal-ID tas-server AWS EC2. Jekk tibdel l-ID tar-riżorsi (jiġifieri minn istanza għal cluster_instance, kif inhu l-każ ma' aws_security_group), għal Terraform jidher daqslikieku ħassejt ir-riżorsa l-antika u żiedet waħda ġdida. Jekk tapplika dawn il-bidliet, Terraform se jħassar il-grupp tas-sigurtà l-antik u joħloq wieħed ġdid, filwaqt li s-servers tiegħek jibdew jirrifjutaw kwalunkwe traffiku tan-netwerk.

    Hawn erba' lezzjonijiet ewlenin li għandek tieħu minn din id-diskussjoni.

    • Dejjem uża l-kmand tal-pjan. Jista 'jiżvela dawn l-isnags kollha. Irrevedi l-output tagħha bir-reqqa u agħti attenzjoni għal sitwazzjonijiet fejn Terraform qed tippjana li tħassar riżorsi li x'aktarx m'għandhomx jitħassru.
    • Oħloq qabel ma tħassar. Jekk trid tissostitwixxi riżorsa, aħseb sew jekk għandekx bżonn toħloq sostituzzjoni qabel tħassar l-oriġinal. Jekk it-tweġiba hija iva, create_before_destroy jista 'jgħin. L-istess riżultat jista 'jinkiseb manwalment billi twettaq żewġ passi: l-ewwel żid riżors ġdid mal-konfigurazzjoni u ħaddem il-kmand tal-applika, u mbagħad neħħi r-riżors il-qadim mill-konfigurazzjoni u uża mill-ġdid il-kmand tal-applika.
    • Il-bidla tal-identifikaturi teħtieġ bidla tal-istat. Jekk trid tibdel l-ID assoċjat ma 'riżorsa (per eżempju, semmi mill-ġdid aws_security_group minn istanza għal cluster_instance) mingħajr ma tħassar ir-riżors u toħloq verżjoni ġdida tagħha, trid taġġorna l-fajl tal-istat Terraform kif xieraq. Qatt tagħmel dan manwalment - uża l-kmand tal-istat terraform minflok. Meta tibdel l-isem tal-identifikaturi, għandek tħaddem il-kmand mv tal-istat terraform, li għandu s-sintassi li ġejja:
      terraform state mv <ORIGINAL_REFERENCE> <NEW_REFERENCE>

      ORIGINAL_REFERENCE hija espressjoni li tirreferi għar-riżorsa fil-forma attwali tagħha, u NEW_REFERENCE hija fejn trid tiċċaqlaqha. Pereżempju, meta tibdel l-isem tal-grupp aws_security_group minn istanza għal cluster_instance, għandek bżonn tmexxi l-kmand li ġej:

      $ terraform state mv 
         aws_security_group.instance 
         aws_security_group.cluster_instance

      Dan jgħid lil Terraform li l-istat li qabel kien assoċjat ma 'aws_security_group.instance issa għandu jkun assoċjat ma' aws_security_group.cluster_instance. Jekk wara li semmiet mill-ġdid u tħaddem dan il-pjan terraform tal-kmand ma juri l-ebda tibdil, allura għamilt kollox b'mod korrett.

    • Xi settings ma jistgħux jinbidlu. Il-parametri ta 'ħafna riżorsi ma jinbidlux. Jekk tipprova tibdelhom, Terraform iħassar ir-riżorsa l-antika u toħloq waħda ġdida minflokha. Kull paġna tar-riżorsi normalment tindika x'jiġri meta tbiddel setting partikolari, għalhekk kun żgur li tiċċekkja d-dokumentazzjoni. Dejjem uża l-kmand tal-pjan u ikkunsidra li tuża l-istrateġija create_before_destroy.

    Il-konsistenza differita hija konsistenti... mad-differiment

    Xi APIs tal-fornituri tal-cloud, bħal AWS, huma asinkroniċi u għandhom konsistenza mdewma. Asinkronija tfisser li l-interface jista 'minnufih jirritorna tweġiba mingħajr ma jistenna li titlesta l-azzjoni mitluba. Konsistenza mdewma tfisser li l-bidliet jistgħu jieħdu ż-żmien biex jiġu propagati fis-sistema kollha; waqt li dan qed jiġri, it-tweġibiet tiegħek jistgħu jkunu inkonsistenti u jiddependu fuq liema replika tas-sors tad-dejta qed tirrispondi għas-sejħiet tal-API tiegħek.

    Immaġina, pereżempju, li tagħmel sejħa API lill-AWS titlobha toħloq server EC2. L-API se jirritorna rispons "suċċess" (201 Maħluq) kważi istantanjament, mingħajr ma jistenna li s-server innifsu jinħoloq. Jekk tipprova tikkonnettja magħha mill-ewwel, kważi ċertament se tfalli minħabba li f'dak il-punt AWS għadu qed jinizjalizza r-riżorsi jew, alternattivament, is-server għadu ma qabadx. Barra minn hekk, jekk tagħmel sejħa oħra biex tikseb informazzjoni dwar dan is-server, tista' tirċievi żball (404 Not Found). Il-ħaġa hija li l-informazzjoni dwar dan is-server EC2 xorta tista 'tiġi propagata madwar l-AWS qabel ma ssir disponibbli kullimkien, ikollok tistenna ftit sekondi.

    Kull meta tuża API mhux sinkroniku b'konsistenza għażżien, trid terġa' tipprova perjodikament it-talba tiegħek sakemm l-azzjoni titlesta u tippropaga permezz tas-sistema. Sfortunatament, l-AWS SDK ma jipprovdi l-ebda għodda tajba għal dan, u l-proġett Terraform kien ibati minn ħafna bugs bħal 6813 (https://github.com/hashicorp/terraform/issues/6813):

    $ terraform apply
    aws_subnet.private-persistence.2: InvalidSubnetID.NotFound:
    The subnet ID 'subnet-xxxxxxx' does not exist

    Fi kliem ieħor, inti toħloq riżorsa (bħal subnet) u mbagħad tipprova tikseb xi informazzjoni dwarha (bħall-ID tas-subnet maħluqa ġdida), u Terraform ma tistax issibha. Ħafna minn dawn il-bugs (inkluż 6813) ġew irranġati, iżda għadhom joħorġu minn żmien għal żmien, speċjalment meta Terraform iżid appoġġ għal tip ġdid ta 'riżorsi. Dan huwa tedjanti, iżda f'ħafna każijiet ma jikkawża l-ebda ħsara. Meta tmexxi terraform applika mill-ġdid, kollox għandu jaħdem, peress li sa dan iż-żmien l-informazzjoni tkun diġà mifruxa mas-sistema kollha.

    Din is-silta hija ppreżentata mill-ktieb ta 'Evgeniy Brikman "Terraform: infrastruttura fil-livell tal-kodiċi".

Sors: www.habr.com

Żid kumment