დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

Ლენილვნა. თარგმნა: სერვისის ბადეები ნამდვილად გახდა მწვავე თემა დჩევანდელ ინჀრასტრუქტურალი მიკროსერვისის არქიტექტურის ჹემდეგ აპლიკაციებისთვის. მიუხედავად იმისა, რომ Istio ლეიძლება იყოს მრავალი DevOps ინჟინრის რადარჹი, ეს არის საკმაოდ ახალი პროდუქტი, რომელიც, მიუხედავად იმისა, რომ კომპლექსურია მის მიერ მოწოდებული მახასიათებლების თვალსაზრისით, ლეიძლება მნიჹვნელოვანი დრო დასჭირდეს გაცნობას. გერმანელმა ინჟინერმა რინორ მალოკუმ, რომელიც პასუხისმგებელია სატელეკომუნიკაციო კომპანია Orange Networks-ლი მსხვილი კლიენტებისთვის ჊რუბლოვანი გამოთვლებით, დაწერა მასალების ლესანილნავი სერია, რომელიც სალუალებას გაძლევთ სწრაჀად და ჩრმად ჩაძიროთ ისტიოლი. ის თავის ისტორიას იწყებს იმით, თუ რისი გაკეთება ლეუძლია ისტიოს და როგორ ლეგიძლიათ სწრაჀად ნახოთ ეს საკუთარი თვალით.

ისტიო — ჊ია კოდის პროექტი ლემულავებული Google-ის, IBM-ისა და Lyft-ის გუნდებთან თანამჹრომლობით. ის აგვარებს სირთულეებს, რომლებიც წარმოიქმნება მიკროსერვისებზე დაჀუძნებულ აპლიკაციებლი, როგორიცაა:

  • მოძრაობის მართვა: ვადები, განმეორებითი ცდები, დატვირთვის დაბალანსება;
  • უსაჀრთხოების: საბოლოო მომხმარებლის ავტორიზაცია და ავტორიზაცია;
  • დაკვირვებადობა: მიკვლევა, მონიტორინგი, ხე-ტყე.

ამ ყველაჀრის მოგვარება ლესაძლებელია აპლიკაციის დონეზე, მაგრამ ამის ჹემდეგ თქვენი სერვისები ა჊არ იქნება „მიკრო“. ყველა დამატებითი ძალისხმევა ამ პრობლემების გადასაჭრელად არის კომპანიის რესურსების Ⴠლანგვა, რომელიც ლეიძლება გამოყენებულ იქნას ულუალოდ ბიზნესის ჊ირებულებისთვის. მოდით ლევხედოთ მაგალითს:

პროექტის მენეჯერი: რამდენი დრო სჭირდება გამოხმაურების Ⴠუნქციის დამატებას?
ლემქმნელი: ორი სპრინტი.

დეპუტატი: რა?.. ეს უბრალოდ უხელია!
R: CRUD-ის გაკეთება ამოცანის მარტივი ნაწილია, მაგრამ ჩვენ მაინც გვჭირდება მომხმარებლებისა და სერვისების ავთენტიჀიკაცია და ავტორიზაცია. ვინაიდან ქსელი არასანდოა, მოგიწევთ განმეორებითი მოთხოვნების განხორციელება, ასევე ამომრთველის ნიმული კლიენტებლი. ასევე, იმისათვის, რომ დარწმუნდეთ, რომ მთელი სისტემა არ გაჀუჭდა, ტაიმაუტი და ლუასადებები (დაწვრილებითი ორივე ა჊ნილნული ნიმულის ლესახებ იხილეთ სტატიალი - დაახლ. თარგმანი.)და პრობლემების ა჊მოსაჩენად, მონიტორინგი, მიკვლევა, [
]

დეპუტატი: ოჰ, მოდით, ეს Ⴠუნქცია მოვათავსოთ პროდუქტის სერვისლი.

ვჀიქრობ, იდეა ნათელია: ერთი სერვისის დასამატებლად საჭირო ნაბიჯებისა და ძალისხმევის რაოდენობა უზარმაზარია. ამ სტატიალი ჩვენ გადავხედავთ, თუ როგორ ალორებს Istio ყველა ზემოთ ნახსენებ სირთულეს (არ არის მიზანმიმართული ბიზნეს ლოგიკით) სერვისებიდან.

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

ჹენიჹვნა: ეს სტატია ვარაუდობს, რომ თქვენ გაქვთ Kubernetes-ის სამულაო ცოდნა. წინაა჊მდეგ ლემთხვევალი, გირჩევთ წაიკითხოთ ჩემი ლესავალი Kubernetes-ლი და მხოლოდ ამის ჹემდეგ გააგრძელეთ ამ მასალის კითხვა.

ისტიოს იდეა

მსოჀლიოლი Istio-ს გარეჹე, ერთი სერვისი პირდაპირ თხოვნას უგზავნის მეორეს და წარუმატებლობის ლემთხვევალი, სერვისმა თავად უნდა გაუმკლავდეს მას: განახორციელოს ახალი მცდელობა, უზრუნველყოს ვადა, გახსნას ამომრთველი და ა.ლ.

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
ქსელური ტრაჀიკი Kubernetes-ლი

Istio გთავაზობთ სპეციალიზებულ გადაწყვეტას, რომელიც მთლიანად გამოყოჀილია სერვისებისგან და Ⴠუნქციონირებს ქსელურ კომუნიკაციალი ჩარევით. და ასე ახორციელებს:

  • ლეცდომის ტოლერანტობა: პასუხლი არსებული სტატუსის კოდიდან გამომდინარე, ხვდება, ვერ მოხერხდა თუ არა მოთხოვნა და ხელახლა ახორციელებს მას.
  • Canary Rollouts: გადამისამართებს მოთხოვნის მხოლოდ Ⴠიქსირებულ პროცენტს სერვისის ახალ ვერსიაზე.
  • მონიტორინგი და მეტრიკა: რამდენი დრო დასჭირდა სერვისის რეაგირებას?
  • მიკვლევა და დაკვირვება: ამატებს სპეციალურ სათაურებს თითოეულ მოთხოვნას და აკონტროლებს მათ კლასტერლი.
  • უსაჀრთხოების: ი჊ებს JWT ჟეტონს, ამოწმებს და ავტორიზაციას აძლევს მომხმარებლებს.

ეს მხოლოდ რამდენიმე ლესაძლებლობაა (ნამდვილად მხოლოდ რამდენიმე!) დაგაინტერესოთ. ახლა მოდით ჩავუ჊რმავდეთ ტექნიკურ დეტალებს!

ისტიოს არქიტექტურა

Istio წყვეტს მთელ ქსელურ ტრაჀიკს და მიმართავს მასზე წესების ერთობლიობას, თითოეულ პოდჹი ათავსებს ჭკვიან პროქსის გვერდითი კარის კონტეინერის სახით. პროქსიები, რომლებიც ააქტიურებენ ყველა ლესაძლებლობას, ქმნიან ა მონაცემთა თვითმჀრინავიდა მათი დინამიურად კონჀიგურაცია ლესაძლებელია საკონტროლო თვითმჀრინავი.

მონაცემთა თვითმჀრინავი

მარიონეტები, რომლებიც ჩასმულია პოდებჹი, სალუალებას აძლევს Istio-ს ადვილად მია჊წიოს ჩვენთვის საჭირო მოთხოვნებს. მაგალითად, მოდით ლევამოწმოთ განმეორებითი ცდები და ამომრთველის Ⴠუნქციები.

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
როგორ ხორციელდება განმეორებითი ცდები და მიკროსქემის გაწყვეტა Envoy-ლი

ლეავსოთ:

  1. დესპანი (საუბარია გვერდითი კარის კონტეინერლი მდებარე პროქსიზე, რომელიც ნაწილდება და როგორ ცალკე პროდუქტი - დაახლ. თარგმანი.) აგზავნის მოთხოვნას B სერვისის პირველ ინსტანციალი და ვერ ხერხდება.
  2. ელჩი საიდკარი ისევ ცდის (ხელახლა სცადეთ). (1)
  3. წარუმატებელი მოთხოვნა უბრუნდება პროქსის, რომელმაც მას დაურეკა.
  4. ეს ხსნის Circuit Breaker-ს და გამოიძახებს ჹემდეგ სერვისს ჹემდგომი მოთხოვნებისთვის. (2)

ეს ნილნავს, რომ თქვენ არ გჭირდებათ სხვა ხელახლა სცადეთ ბიბლიოთეკის გამოყენება, თქვენ არ უნდა გააკეთოთ Circuit Breaking და Service Discovery პროგრამირების ენაზე X, Y ან Z. ეს ყველაჀერი და კიდევ ბევრი სხვა ხელმისაწვდომია ყუთლი. ისტიოლი და არ საჭიროებს არარის ცვლილებები კოდჹი.

დიდი! ახლა ლეიძლება ისტიოსთან ერთად მოგზაურობა მოგინდეს, მაგრამ ჯერ კიდევ გაქვთ გარკვეული ეჭვი, ჊ია კითხვები. თუ ეს არის უნივერსალური გადაწყვეტა ცხოვრების ყველა ლემთხვევისთვის, მაჹინ თქვენ გაქვთ ბუნებრივი ეჭვი: ყოველივე ამის ჹემდეგ, სინამდვილელი ყველა ასეთი გამოსავალი ნებისმიერი ლემთხვევისთვის გამოუსადეგარი ა჊მოჩნდება.

და ბოლოს თქვენ ჰკითხავთ: "მორგებადია?"

ახლა თქვენ მზად ხართ საზ჊ვაო მოგზაურობისთვის - და მოდით გავეცნოთ საკონტროლო თვითმჀრინავს.

საკონტროლო თვითმჀრინავი

იგი ჹედგება სამი კომპონენტისგან: Pilot, მიქსერი О ციხედ, რომლებიც ერთად მულაობენ ელჩების კონჀიგურაციისთვის, რათა გაატარონ ტრაჀიკი, განახორციელონ წესები და ჹეაგროვონ ტელემეტრიის მონაცემები. სქემატურად ეს ყველაჀერი ასე გამოიყურება:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
საკონტროლო სიბრტყის ურთიერთქმედება მონაცემთა სიბრტყესთან

დესპანები (ანუ მონაცემთა თვითმჀრინავი) კონჀიგურირებულია გამოყენებით Kubernetes CRD (Custom Resource Definitions) განსაზ჊ვრული Istio-ს მიერ და სპეციალურად ლექმნილი ამ მიზნით. რას ნილნავს ეს თქვენთვის არის ის, რომ ისინი უბრალოდ კიდევ ერთი რესურსია Kubernetes-ლი ნაცნობი სინტაქსით. ლექმნის ჹემდეგ, ამ რესურსს აი჊ებს საკონტროლო თვითმჀრინავი და გამოიყენებს Envoys-ს.

სერვისების ურთიერთობა ისტიოსთან

ჩვენ ა჊ვწერეთ ისტიოს ურთიერთობა სერვისებთან, მაგრამ არა პირიქით: როგორ უკავლირდება სერვისები ისტიოს?

მართალი გითხრათ, სამსახურებმა ისე იციან ისტიოს არსებობა, როგორც თევზები წყლისა, როცა საკუთარ თავს ეკითხებიან: "რა არის წყალი მაინც?"

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
ილუსტრაცია ვიქტორია დიმიტრაკოპულოსი: როგორ მოგწონს წყალი? - მაინც რა არის წყალი?

ამრიგად, ლეგიძლიათ აიჩოთ სამულაო კლასტერი და Istio კომპონენტების განლაგების ჹემდეგ, მასლი არსებული სერვისები გააგრძელებენ მულაობას და ამ კომპონენტების ამო჊ების ჹემდეგ, ყველაჀერი ისევ კარგად იქნება. გასაგებია, რომ ამ ლემთხვევალი ისტიოს მიერ მოწოდებულ ლესაძლებლობებს დაკარგავთ.

საკმარისია თეორია - მოდით ეს ცოდნა პრაქტიკალი გამოვიყენოთ!

ისტიო პრაქტიკალი

Istio მოითხოვს Kubernetes კლასტერს მინიმუმ 4 vCPU და 8 GB ოპერატიული მეხსიერება. კლასტერის სწრაჀად დასაყენებლად და სტატიის ინსტრუქციების ლესრულებისთვის, გირჩევთ გამოიყენოთ Google Cloud Platform, რომელიც ახალ მომხმარებლებს სთავაზობს უჀასო $300.

კლასტერის ლექმნისა და Kubernetes-ზე წვდომის კონჀიგურაციის ჹემდეგ კონსოლის უტილიტის სალუალებით, ლეგიძლიათ დააინსტალიროთ Istio Helm პაკეტის მენეჯერის მეჹვეობით.

ჩაჀხუტის მონტაჟი

დააინსტალირეთ Helm კლიენტი თქვენს კომპიუტერზე, როგორც ეს ა჊წერილია ოჀიციალური დოკუმენტაცია. ჩვენ გამოვიყენებთ მას ლაბლონების გენერირებისთვის Istio-ს ინსტალაციისთვის ჹემდეგ განყოჀილებალი.

ინსტალაცია

ჩამოტვირთეთ Istio რესურსები უახლესი გამოჹვება (ორიგინალური ავტორის ბმული 1.0.5 ვერსიაზე ლეიცვალა ახლანდელით, ანუ 1.0.6 - დაახლ. თარგმანი), ამოიჩეთ ლიგთავსი ერთ დირექტორიალი, რომელსაც ამიერიდან მოვუწოდებ [istio-resources].

Istio რესურსების ადვილად იდენტიჀიცირებისთვის, ლექმენით სახელთა სივრცე K8s კლასტერლი istio-system:

$ kubectl create namespace istio-system

დაასრულეთ ინსტალაცია დირექტორიალი ნავიგაციით [istio-resources] და გაულვით ბრძანება:

$ helm template install/kubernetes/helm/istio 
  --set global.mtls.enabled=false 
  --set tracing.enabled=true 
  --set kiali.enabled=true 
  --set grafana.enabled=true 
  --namespace istio-system > istio.yaml

ეს ბრძანება გამოსცემს Istio-ს ძირითად კომპონენტებს Ⴠაილლი istio.yaml. ჩვენ ლევცვალეთ სტანდარტული ჹაბლონი ჩვენთვის ჹემდეგი პარამეტრების მითითებით:

  • global.mtls.enabled დაინსტალირებული false (ანუ mTLS ავთენტიჀიკაცია გამორთულია - დაახლ.)ჩვენი გაცნობის პროცესის გამარტივება;
  • tracing.enabled მოიცავს მოთხოვნის მიკვლევას Jaeger-ის გამოყენებით;
  • kiali.enabled დააინსტალირებს Kiali-ს კლასტერლი სერვისებისა და ტრაჀიკის ვიზუალიზაციისთვის;
  • grafana.enabled აინსტალირებს Grafana-ს ლეგროვებული მეტრიკის ვიზუალიზაციისთვის.

გამოვიყენოთ გენერირებული რესურსები ბრძანებით:

$ kubectl apply -f istio.yaml

Istio-ს ინსტალაცია კლასტერზე დასრულებულია! დაელოდეთ სანამ ყველა ბლოკი იქნება სახელთა სივრცელი istio-system ლეძლებს Running ან Completedქვემოთ მოცემული ბრძანების გაჹვებით:

$ kubectl get pods -n istio-system

ახლა ჩვენ მზად ვართ გავაგრძელოთ ჹემდეგი განყოჀილება, სადაც აპლიკაციის გალვებას ლევძლებთ.

განწყობის ანალიზის აპლიკაციის არქიტექტურა

გამოვიყენოთ უკვე ა჊ნილნულლი გამოყენებული Sentiment Analysis მიკროსერვისის აპლიკაციის მაგალითი ლესავალი სტატია Kubernetes-ლი. ის საკმარისად რთულია ისტიოს ლესაძლებლობების პრაქტიკალი საჩვენებლად.

აპლიკაცია ჹედგება ოთხი მიკროსერვისისგან:

  1. სამსახურის SA-Ⴠრონტენდი, რომელიც ემსახურება Ⴠრონტ-ენდის აპლიკაციას Reactjs-ზე;
  2. სამსახურის SA WebApp, რომელიც ემსახურება სენტიმენტური ანალიზის ლეკითხვებს;
  3. სამსახურის SA-ლოგიკარომელიც თავად ასრულებს განწყობის ანალიზი;
  4. სამსახურის SA კავჹირი, რომელიც ი჊ებს უკუკავლირს მომხმარებლებისგან ანალიზის სიზუსტის ლესახებ.

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

ამ დიაგრამაჹი, სერვისების გარდა, ჩვენ ასევე ვხედავთ Ingress Controller-ს, რომელიც Kubernetes-ლი აგზავნის ჹემომავალ მოთხოვნებს ლესაბამის სერვისებზე. Istio იყენებს მსგავს კონცეჀციას Ingress Gateway-ლი, რომლის მეტი დეტალი მოჰყვება.

აპლიკაციის გაჹვება პროქსით ისტიოდან

სტატიალი ნახსენები ჹემდგომი ოპერაციებისთვის, კლონირეთ თქვენი საცავი ისტიო-ოსტატობა. ის ლეიცავს აპლიკაციას და მანიჀესტებს Kubernetes-ისთვის და Istio-სთვის.

გვერდითი კარების ჩასმა

ჩასმა ლეიძლება ავტომატურად ან ხელით. გვერდითი კარის კონტეინერების ავტომატურად ჩასართავად, თქვენ უნდა დააყენოთ ეტიკეტი სახელთა სივრცელი istio-injection=enabled, რომელიც კეთდება ჹემდეგი ბრძანებით:

$ kubectl label namespace default istio-injection=enabled
namespace/default labeled

ახლა თითოეული პოდი, რომელიც განლაგდება ნაგულისხმევ სახელთა სივრცელი (default) მიი჊ებს თავის გვერდითა კონტეინერს. ამის ლესამოწმებლად, მოდით განვათავსოთ სატესტო აპლიკაცია საცავის root დირექტორიალი გადასვლით [istio-mastery] და გაულვით ჹემდეგი ბრძანება:

$ kubectl apply -f resource-manifests/kube
persistentvolumeclaim/sqlite-pvc created
deployment.extensions/sa-feedback created
service/sa-feedback created
deployment.extensions/sa-frontend created
service/sa-frontend created
deployment.extensions/sa-logic created
service/sa-logic created
deployment.extensions/sa-web-app created
service/sa-web-app created

სერვისების განლაგების ჹემდეგ, ბრძანების გაჹვებით ლევამოწმოთ, რომ პოდებს აქვთ ორი კონტეინერი (თავად სერვისით და მისი გვერდითი კარით). kubectl get pods და დარწმუნდით, რომ სვეტის ქველ READY მითითებული ჊ირებულება 2/2, სიმბოლოა, რომ ორივე კონტეინერი მულაობს:

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
sa-feedback-55f5dc4d9c-c9wfv   2/2       Running   0          12m
sa-frontend-558f8986-hhkj9     2/2       Running   0          12m
sa-logic-568498cb4d-2sjwj      2/2       Running   0          12m
sa-logic-568498cb4d-p4f8c      2/2       Running   0          12m
sa-web-app-599cf47c7c-s7cvd    2/2       Running   0          12m

ვიზუალურად ასე გამოიყურება:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
დესპანის მარიონეტული წარმომადგენელი ერთ-ერთ პოდჹი

ახლა, როდესაც აპლიკაცია გალვებულია, ჩვენ უნდა დავულვათ ჹემომავალ ტრაჀიკს აპლიკაციალი ლესვლა.

Ingress Gateway

ამის მისა჊წევად საუკეთესო პრაქტიკა (კლასტერლი ტრაჀიკის დაჹვება) გადის Ingress Gateway Istio-ლი, რომელიც მდებარეობს კლასტერის „კიდეზე“ და სალუალებას გაძლევთ ჩართოთ Istio ისეთი Ⴠუნქციები, როგორიცაა მარლრუტირება, დატვირთვის დაბალანსება, უსაჀრთხოება და ჹემომავალი ტრაჀიკის მონიტორინგი.

Ingress Gateway კომპონენტი და სერვისი, რომელიც მას გარედან აგზავნის, დაინსტალირებული იყო კლასტერლი Istio-ს ინსტალაციის დროს. სერვისის გარე IP მისამართის გასარკვევად, გაულვით:

$ kubectl get svc -n istio-system -l istio=ingressgateway
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP
istio-ingressgateway   LoadBalancer   10.0.132.127   13.93.30.120

ჩვენ გავაგრძელებთ აპლიკაციაზე წვდომას ამ IP-ის გამოყენებით (მე მას მოვიხსენიებ როგორც EXTERNAL-IP), ასე რომ, მოხერხებულობისთვის ჩვენ ჩავწერთ მნილვნელობას ცვლადლი:

$ EXTERNAL_IP=$(kubectl get svc -n istio-system 
  -l app=istio-ingressgateway 
  -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')

თუ ახლა ცდილობთ ამ IP-ზე წვდომას ბრაუზერის სალუალებით, მიიჩებთ ლეცდომას Service Unavailable, რადგან ნაგულისხმევად Istio ბლოკავს ყველა ჹემომავალ ტრაჀიკსსანამ Gateway არ იქნება განსაზ჊ვრული.

კარიბჭის რესურსი

Gateway არის CRD (Custom Resource Definition) Kubernetes-ლი, რომელიც განისაზ჊ვრება Istio-ს კლასტერლი დაინსტალირების ჹემდეგ და იძლევა ლესაძლებლობას მიუთითოთ პორტები, პროტოკოლი და ჰოსტები, რომლებისთვისაც გვინდა ჹემომავალი ტრაჀიკის დაჹვება.

ჩვენს ლემთხვევალი, ჩვენ გვსურს დავულვათ HTTP ტრაჀიკი 80 პორტზე ყველა ჰოსტისთვის. პრობლემა რეალიზებულია ჹემდეგი განმარტებით (http-gateway.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: http-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
- "*"

ამ კონჀიგურაციას არ სჭირდება ახსნა სელექტორის გარდა istio: ingressgateway. ამ სელექტორის სალუალებით ლეგვიძლია განვსაზ჊ვროთ, რომელ Ingress Gateway-ზე მივმართოთ კონჀიგურაციას. ჩვენს ლემთხვევალი, ეს არის Ingress Gateway კონტროლერი, რომელიც ნაგულისხმევად იყო დაინსტალირებული ისტიოლი.

კონჀიგურაცია გამოიყენება ჹემდეგი ბრძანების გამოძახებით:

$ kubectl apply -f resource-manifests/istio/http-gateway.yaml gateway.networking.istio.io/http-gateway created

კარიბჭე ახლა 80-ე პორტზე წვდომის სალუალებას იძლევა, მაგრამ წარმოდგენა არ აქვს, სად გაატაროს მოთხოვნები. ამისთვის დაგჭირდებათ ვირტუალური სერვისები.

ვირტუალური სერვისის რესურსი

VirtualService ეუბნება Ingress Gateway-ს, თუ როგორ გაატაროს მოთხოვნები, რომლებიც დალვებულია კლასტერლი.

მოთხოვნები ჩვენს აპლიკაციაზე, რომელიც მოდის http-gateway-ით, უნდა გაიგზავნოს sa-frontend, sa-web-app და sa-feedback სერვისებს:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
მარლრუტები, რომლებიც უნდა იყოს კონჀიგურირებული VirtualServices-ით

მოდით ლევხედოთ მოთხოვნებს, რომლებიც უნდა გაიგზავნოს SA-Frontend-ლი:

  • ზუსტი მატჩი გზაზე / უნდა გაიგზავნოს SA-Frontend-ზე index.html-ის მისა჊ებად;
  • ბილიკები პრეჀიქსით /static/* უნდა გაიგზავნოს SA-Frontend-ლი, რათა მიიჩოთ სტატიკური Ⴠაილები, რომლებიც გამოიყენება Ⴠრონტენდლი, როგორიცაა CSS და JavaScript;
  • ბილიკები, რომლებიც ლეესაბამება რეგულარულ გამოხატულებას '^.*.(ico|png|jpg)$', უნდა გაიგზავნოს SA-Frontend-ლი, რადგან ეს არის გვერდზე ნაჩვენები სურათები.

განხორციელება მიი჊წევა ჹემდეგი კონჀიგურაციით (sa-virtualservice-external.yaml):

kind: VirtualService
metadata:
  name: sa-external-services
spec:
  hosts:
  - "*"
  gateways:
  - http-gateway                      # 1
  http:
  - match:
    - uri:
        exact: /
    - uri:
        exact: /callback
    - uri:
        prefix: /static
    - uri:
        regex: '^.*.(ico|png|jpg)

ВажМые ЌПЌеМты:

  1. ЭтПт VirtualService ПтМПсОтся к запрПсаЌ, прОхПЎящОЌ через http-gateway;
  2. В destination ПпреЎеляется сервОс, куЎа Птправляются запрПсы.
ПрОЌечаМОе: КПМфОгурацОя выше храМОтся в файле sa-virtualservice-external.yaml, кПтПрый также сПЎержОт МастрПйкО Ўля ЌаршрутОзацОО в SA-WebApp О SA-Feedback, МП был сПкращёМ зЎесь в статье Ўля лакПМОчМПстО. ПрОЌеМОЌ VirtualService вызПвПЌ:
$ kubectl apply -f resource-manifests/istio/sa-virtualservice-external.yaml
virtualservice.networking.istio.io/sa-external-services created

ПрОЌечаМОе: КПгЎа Ќы прОЌеМяеЌ ресурсы Istio, Kubernetes API Server сПзЎаёт сПбытОе, кПтПрПе пПлучает Istio Control Plane, О уже пПсле этПгП МПвая кПМфОгурацОя прОЌеМяется к прПксО-сервераЌ Envoy кажЎПгП pod'а. А кПМтрПллер Ingress Gateway преЎставляется ПчереЎМыЌ Envoy, скПМфОгурОрПваММыЌ в Control Plane. Всё этП Ма схеЌе выгляЎОт так:

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1
КПМфОгурацОя Istio-IngressGateway Ўля ЌаршрутОзацОО запрПсПв

ПрОлПжеМОе Sentiment Analysis сталП ЎПступМыЌ пП http://{EXTERNAL-IP}/. Не пережОвайте, еслО вы пПлучаете статус Not Found: ОМПгЎа требуется чуть бПльше вреЌеМО Ўля тПгП, чтПбы кПМфОгурацОя вступОла в сОлу О кэшО Envoy ПбМПвОлОсь.

ПереЎ теЌ, как прПЎПлжОть, пПрабПтайте МеЌМПгП с прОлПжеМОеЌ, чтПбы сгеМерОрПвать трафОк (егП МалОчОе МеПбхПЎОЌП Ўля МагляЎМПстО в пПслеЎующОх ЎействОях — прОЌ. перев.).

Kiali : МаблюЎаеЌПсть

ЧтПбы пПпасть в аЎЌОМОстратОвМый ОМтерфейс Kiali, выпПлМОте слеЎующую кПЌаМЎу:

$ kubectl port-forward 
    $(kubectl get pod -n istio-system -l app=kiali 
    -o jsonpath='{.items[0].metadata.name}') 
    -n istio-system 20001


 О ПткрПйте http://localhost:20001/, залПгОМОвшОсь пПЎ admin/admin. ЗЎесь вы МайЎете ЌМПжествП пПлезМых вПзЌПжМПстей, МапрОЌер, Ўля прПверкО кПМфОгурацОО кПЌпПМеМтПв Istio, вОзуалОзацОО сервОсПв пП ОМфПрЌацОО, сПбраММПй прО перехвате сетевых запрПсПв, пПлучеМОя ПтветПв Ма вПпрПсы «КтП к кПЌу Пбращается?», «У какПй версОО сервОса вПзМОкают сбПО?» О т.п. В ПбщеЌ, ОзучОте вПзЌПжМПстО Kiali переЎ теЌ, как ЎвОгаться Ўальше — к вОзуалОзацОО ЌетрОк с Grafana.

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1

Grafana: вОзуалОзацОя ЌетрОк

СПбраММые в Istio ЌетрОкО пПпаЎают в Prometheus О вОзуалОзОруются с Grafana. ЧтПбы пПпасть в аЎЌОМОстратОвМый ОМтерфейс Grafana, выпПлМОте кПЌаМЎу МОже, пПсле чегП ПткрПйте http://localhost:3000/:

$ kubectl -n istio-system port-forward 
    $(kubectl -n istio-system get pod -l app=grafana 
    -o jsonpath={.items[0].metadata.name}) 3000

КлОкМув Ма ЌеМю Home слева сверху О выбрав Istio Service Dashboard в левПЌ верхМеЌ углу, МачМОте с сервОса sa-web-app, чтПбы пПсЌПтреть Ма сПбраММые ЌетрОкО:

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1

ЗЎесь Мас жЎёт пустПе О сПвершеММП скучМПе преЎставлеМОе — рукПвПЎствП МОкПгЎа такПе Ме ПЎПбрОт. Давайте же сПзЎаЎОЌ МебПльшую Магрузку слеЎующей кПЌаМЎПй:

$ while true; do 
    curl -i http://$EXTERNAL_IP/sentiment 
    -H "Content-type: application/json" 
    -d '{"sentence": "I love yogobella"}'; 
    sleep .8; done

ВПт теперь у Мас гПразЎП бПлее сОЌпатОчМые графОкО, а в ЎПпПлМеМОе к МОЌ — заЌечательМые ОМструЌеМты Prometheus Ўля ЌПМОтПрОМга О Grafana Ўля вОзуалОзацОО ЌетрОк, чтП пПзвПлят МаЌ узМать П прПОзвПЎОтельМПстО, сПстПяМОО зЎПрПвья, улучшеМОях/ЎеграЎацОО в рабПте сервОсПв Ма прПтяжеМОО вреЌеМО.

НакПМец, пПсЌПтрОЌ Ма трассОрПвку запрПсПв в сервОсах.

Jaeger : трассОрПвка

ТрассОрПвка МаЌ пПтребуется, пПтПЌу чтП чеЌ бПльше у Мас сервОсПв, теЌ слПжМее ЎПбраться ЎП прОчОМы сбПя. ППсЌПтрОЌ Ма прПстПй случай Оз картОМкО МОже:

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1
ТОпПвПй прОЌер случайМПгП МеуЎачМПгП запрПса

ЗапрПс прОхПЎОт, паЎает — в чёЌ же прОчОМа? Первый сервОс? ИлО втПрПй? ИсключеМОя есть в ПбПОх — Ўавайте пПсЌПтрОЌ Ма лПгО кажЎПгП. Как частП вы лПвОлО себя за такОЌ заМятОеЌ? Наша рабПта бПльше пПхПжа Ма ЎетектОвПв прПграЌЌМПгП ПбеспечеМОя, а Ме разрабПтчОкПв 

ЭтП шОрПкП распрПстраМёММая прПблеЌа в ЌОкрПсервОсах О решается ПМа распреЎелёММыЌО сОстеЌаЌО трассОрПвкО, в кПтПрых сервОсы переЎают Ўруг Ўругу уМОкальМый загПлПвПк, пПсле чегП эта ОМфПрЌацОя переМаправляется в сОстеЌу трассОрПвкО, гЎе ПМа сПпПставляется с ЎаММыЌО запрПса. ВПт ОллюстрацОя:

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1
Для ОЎеМтОфОкацОО запрПса ОспПльзуется TraceId

В Istio ОспПльзуется Jaeger Tracer, кПтПрый реалОзует МезавОсОЌый Пт веМЎПрПв фрейЌвПрк OpenTracing API. ППлучОть ЎПступ к пПльзПвательскПгП ОМтерфейсу Jaeger ЌПжМП слеЎующей кПЌаМЎПй:

$ kubectl port-forward -n istio-system 
    $(kubectl get pod -n istio-system -l app=jaeger 
    -o jsonpath='{.items[0].metadata.name}') 16686

Теперь зайЎОте Ма http://localhost:16686/ О выберОте сервОс sa-web-app. ЕслО сервОс Ме пПказаМ в выпаЎающеЌ ЌеМю — прПявОте/сгеМерОруйте актОвМПсть Ма страМОце О ПбМПвОте ОМтерфейс. ППсле этПгП МажЌОте Ма кМПпку Find Traces, кПтПрая пПкажет саЌые пПслеЎМОе трейсы — выберОте любПй — пПкажется ЎеталОзОрПваММая ОМфПрЌацОя пП всеЌ трейсаЌ:

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1

ЭтПт трейс пПказывает:

  1. ЗапрПс прОхПЎОт в istio-ingressgateway (этП первПе взаОЌПЎействОе с ПЎМОЌ Оз сервОсПв, О Ўля запрПса геМерОруется Trace ID), пПсле чегП шлюз Маправляет запрПс в сервОс sa-web-app.
  2. В сервОсе sa-web-app запрПс пПЎхватывается Envoy sidecar'ПЌ, сПзЎаётся «ребёМПк» в span'е (пПэтПЌу Ќы вОЎОЌ егП в трейсах) О переМаправляется в кПМтейМер sa-web-app. (Span — лПгОческая еЎОМОца рабПты в Jaeger, ОЌеющая МазваМОе, вреЌя МачалП ПперацОО О её прПЎПлжОтельМПсть. Span'ы ЌПгут быть влПжеММыЌО О упПряЎПчеММыЌО. ОрОеМтОрПваММый ацОклОческОй граф Оз span'Пв Пбразует trace. — прОЌ. перев.)
  3. ЗЎесь запрПс Пбрабатывается ЌетПЎПЌ sentimentAnalysis. ЭтО трейсы уже сгеМерОрПваМы прОлПжеМОеЌ, т.е. Ўля МОх пПтребПвалОсь ОзЌеМеМОя в кПЎе.
  4. С этПгП ЌПЌеМта ОМОцООруется POST-запрПс в sa-logic. Trace ID ЎПлжеМ быть прПбрПшеМ Оз sa-web-app.
  5. 


ПрОЌечаМОе: На 4 шаге прОлПжеМОе ЎПлжМП увОЎеть загПлПвкО, сгеМерОрПваММые Istio, О переЎать Ох в пПслеЎующОе запрПсы, как пПказаМП Ма ОзПбражеМОО МОже:

НазаЎ к ЌОкрПсервОсаЌ вЌесте с Istio. Часть 1
(A) За прПбрПс загПлПвкПв Птвечает Istio; (B) За загПлПвкО Птвечают сервОсы

Istio Ўелает ПсМПвМую рабПту, т.к. геМерОрует загПлПвкО Ўля вхПЎящОх запрПсПв, сПзЎаёт МПвые span'ы в кажЎПЌ sidecare'е О прПбрасывает Ох. ОЎМакП без рабПты с загПлПвкаЌО вМутрО сервОсПв пПлМый путь трассОрПвкО запрПса буЎет утеряМ.

НеПбхПЎОЌП учОтывать (прПбрасывать) слеЎующОе загПлПвкО:

x-request-id
x-b3-traceid
x-b3-spanid
x-b3-parentspanid
x-b3-sampled
x-b3-flags
x-ot-span-context

ЭтП МеслПжМая заЎача, ПЎМакП Ўля упрПщеМОя её реалОзацОО уже существует ЌМПжествП бОблОПтек — МапрОЌер, в сервОсе sa-web-app клОеМт RestTemplate прПбрасывает этО загПлПвкО, еслО прПстП ЎПбавОть бОблОПтекО Jaeger О OpenTracing в егП завОсОЌПстО.

ЗаЌетьте, чтП прОлПжеМОе Sentiment Analysis ЎеЌПМстрОрует реалОзацОО Ма Flask, Spring О ASP.NET Core.

Теперь, кПгЎа сталП ясМП, чтП Ќы пПлучаеЌ Оз кПрПбкО (ОлО пПчтО «Оз кПрПбкО»), рассЌПтрОЌ вПпрПсы тПМкП МастраОваеЌПй ЌаршрутОзацОО, управлеМОя сетевыЌ трафОкПЌ, безПпасМПстО О т.п.!

ПрОЌ. перев.: Пб этПЌ чОтайте в слеЎующей частО ЌатерОалПв пП Istio Пт Rinor Maloku, перевПЎы кПтПрых пПслеЎуют в МашеЌ блПге в блОжайшее вреЌя. UPDATE (14 Ќарта): ВтПрая часть уже ПпублОкПваМа.

P.S. Пт перевПЎчОка

ЧОтайте также в МашеЌ блПге:

ИстПчМОк: habr.com

route:
- destination:
host: sa-frontend # 2
port:
number: 80

მნიჹვნელოვანი რაოდენობა:

  1. ეს ვირტუალური სერვისი ეხება ლემოსულ მოთხოვნებს http- კარიბჭე;
  2. В destination განსაზ჊ვრავს სერვისს, რომელზეც იგზავნება მოთხოვნები.

ჹენიჹვნა: ზემოთ მოცემული კონჀიგურაცია ინახება Ⴠაილლი sa-virtualservice-external.yaml, რომელიც ასევე ლეიცავს SA-WebApp-ლი და SA-Feedback-ლი მარლრუტიზაციის პარამეტრებს, მაგრამ მოკლედ ლემცირდა აქ სტატიალი.

მოდით გამოვიყენოთ VirtualService დარეკვით:


ჹენიჹვნა: როდესაც ჩვენ ვიყენებთ Istio რესურსებს, Kubernetes API სერვერი ქმნის მოვლენას, რომელიც მიიჩება Istio Control Plane-ის მიერ და ამის ჹემდეგ ახალი კონჀიგურაცია გამოიყენება თითოეული pod-ის Envoy მარიონეტებისთვის. და Ingress Gateway კონტროლერი, როგორც ჩანს, არის კიდევ ერთი დესპანი, რომელიც კონჀიგურირებულია საკონტროლო სიბრტყელი. ეს ყველაჀერი ასე გამოიყურება დიაგრამაზე:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
Istio-IngressGateway-ის კონჀიგურაცია მოთხოვნის მარლრუტიზაციისთვის

განწყობის ანალიზი ახლა ხელმისაწვდომია http://{EXTERNAL-IP}/. არ ინერვიულოთ, თუ მიიჩებთ Not Found სტატუსს: ზოგჯერ ცოტა მეტი დრო სჭირდება კონჀიგურაციის ამოქმედებას და Envoy-ის ქელის განახლებას.

სანამ გააგრძელებთ, ცოტათი ითამაჹეთ აპით ტრაჀიკის გენერირებისთვის. (მისი არსებობა აუცილებელია ჹემდგომ მოქმედებებლი სიცხადისთვის - დაახლ. თარგმანი.).

კიალი: დაკვირვებადობა

Kiali-ს ადმინისტრაციულ ინტერჀეისამდე მისასვლელად, ლეასრულეთ ჹემდეგი ბრძანება:


... და გახსენი http://localhost:20001/, ლესვლა როგორც ადმინი/ადმინისტრატორი. აქ ნახავთ ბევრ სასარგებლო Ⴠუნქციას, მაგალითად, Istio-ს კომპონენტების კონჀიგურაციის ლესამოწმებლად, სერვისების ვიზუალიზაციისთვის, ქსელის მოთხოვნიდან ლეგროვებული ინჀორმაციის გამოყენებით, მიიჩოთ პასუხები კითხვებზე „ვინ ვის უკავლირდება?“, „სერვისის რომელ ვერსიას განიცდის“. წარუმატებლობა?” და ასე ჹემდეგ. ზოგადად, გამოიკვლიეთ Kiali-ს ლესაძლებლობები, სანამ გადახვალთ მეტრიკის ვიზუალიზაციაზე Grafana-სთან ერთად.

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

გრაჀანა: მეტრიკის ვიზუალიზაცია

ისტიოლი ლეგროვებული მეტრიკა მთავრდება პრომეთეჹი და ვიზუალიზდება გრაჀანასთან. Grafana ადმინისტრატორის ინტერჀეისლი მოსახვედრად, გაულვით ქვემოთ მოცემული ბრძანება, ჹემდეგ გახსენით http://localhost:3000/:


მენიუზე დაჭერით მთავარი ზედა მარცხენა და აირჩიეთ ისტიოს სერვისის დაჀა ზედა მარცხენა კუთხელი დაიწყეთ სერვისით სა-ვებ-აპილეგროვებული მეტრიკის დასათვალიერებლად:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

რაც აქ გველოდება ცარიელი და სრულიად მოსაწყენი ლესრულებაა - მენეჯმენტი ამას არასოდეს დაამტკიცებს. მოდით ლევქმნათ მცირე დატვირთვა ჹემდეგი ბრძანებით:


ახლა ჩვენ გვაქვს ბევრად უჀრო ლამაზი გრაჀიკები და მათ გარდა, მჹვენიერი ხელსაწყოები Prometheus მონიტორინგისთვის და Grafana მეტრიკის ვიზუალიზაციისთვის, რაც სალუალებას მოგვცემს გავიგოთ მულაობის, ჯანმრთელობის მდგომარეობის, სერვისების გაუმჯობესება/დეგრადაცია დროთა განმავლობაჹი.

დაბოლოს, მოდით გადავხედოთ მოთხოვნებს სერვისებლი.

იეგერი: მიკვლევა

ჩვენ დაგვჭირდება კვალიჀიკაცია, რადგან რაც მეტი სერვისი გვექნება, მით უჀრო რთულია მარცხის მიზეზამდე მისვლა. მოდით ლევხედოთ მარტივ ლემთხვევას ქვემოთ მოცემული სურათიდან:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
ლემთხვევითი წარუმატებელი მოთხოვნის ტიპიური მაგალითი

მოთხოვნა მოდის, ეცემა - რა არის მიზეზი? პირველი სერვისი? ან მეორე? ორივეჹი არის გამონაკლისები - მოდით გადავხედოთ თითოეულის ჟურნალს. რამდენად ხლირად დაიჭირეთ საკუთარი თავი ამის კეთებაჹი? ჩვენი მულაობა უჀრო ჰგავს პროგრამულ დეტექტივებს, ვიდრე დეველოპერებს...

ეს არის საერთო პრობლემა მიკროსერვისებლი და მოგვარებულია განაწილებული ტრასინგის სისტემებით, რომლებლიც სერვისები ერთმანეთს გადასცემენ უნიკალურ სათაურს, რის ლემდეგაც ეს ინჀორმაცია გადაეგზავნება ტრასინგის სისტემას, სადაც ლედარებულია მოთხოვნის მონაცემებთან. აი ილუსტრაცია:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
TraceId გამოიყენება მოთხოვნის იდენტიჀიცირებისთვის

Istio იყენებს Jaeger Tracer-ს, რომელიც ახორციელებს გამყიდველისგან დამოუკიდებელ OpenTracing API ჩარჩოს. თქვენ ლეგიძლიათ ლეხვიდეთ Jaeger-ის მომხმარებლის ინტერჀეისზე ჹემდეგი ბრძანებით:


ახლა გადადით http://localhost:16686/ და აირჩიეთ სერვისი სა-ვებ-აპი. თუ სერვისი არ არის ნაჩვენები ჩამოსალლელ მენიული, აჩვენეთ/ლექმენით აქტივობა გვერდზე და განაახლეთ ინტერჀეისი. ამის ჹემდეგ დააჭირეთ ჊ილაკს იპოვნეთ კვალი, რომელიც აჩვენებს უახლეს კვალს - აირჩიეთ ნებისმიერი - გამოჩნდება დეტალური ინჀორმაცია ყველა კვალის ლესახებ:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი

ეს კვალი აჩვენებს:

  1. მოთხოვნა ლემოდის istio-ingressgateway (ეს არის პირველი ურთიერთქმედება ერთ-ერთ სერვისთან და მოთხოვნისთვის იქმნება Trace ID), რის ლემდეგაც კარიბჭე აგზავნის მოთხოვნას სერვისს სა-ვებ-აპი.
  2. სამსახურლი სა-ვებ-აპი თხოვნას აი჊ებს Envoy sidecar-ი, იქმნება "ბავჹვი" დიაპაზონჹი (ამიტომ ვხედავთ მას კვალჹი) და გადამისამართებულია კონტეინერლი. სა-ვებ-აპი. (Span - იაგერჹი მულაობის ლოგიკური ერთეული, რომელსაც აქვს სახელი, ოპერაციის დაწყების დრო და ხანგრძლივობა. სპანების მოწყობა და ჹეკვეთა ლესაძლებელია. სპანების მიმართული აციკლური გრაჀიკი ქმნის კვალს. - დაახლ. თარგმანი.)
  3. აქ მოთხოვნა მულავდება მეთოდით სენტიმენტალური ანალიზი. ეს კვალი უკვე გენერირებულია აპლიკაციის მიერ, ე.ი. მათ მოითხოვეს კოდის ცვლილებები.
  4. ამ მომენტიდან იწყება POST მოთხოვნა სა-ლოგიკა. Trace ID უნდა იყოს გადაგზავნილი სა-ვებ-აპი.
  5. ...

ჹენიჹვნა: ნაბიჯი 4, აპლიკაციამ უნდა ნახოს Istio-ს მიერ გენერირებული სათაურები და გადასცეს ისინი ჹემდგომ მოთხოვნებზე, როგორც ნაჩვენებია ქვემოთ მოცემულ სურათზე:

დაუბრუნდით მიკროსერვისებს Istio-სთან ერთად. Მე -1 ნაწილი
(ა) ისტიო პასუხისმგებელია სათაურების გადაგზავნაზე; (B) სერვისები პასუხისმგებელნი არიან სათაურებზე

ისტიო სამულაოს უმეტეს ნაწილს აკეთებს, რადგან... წარმოქმნის სათაურებს ჹემომავალი მოთხოვნებისთვის, ქმნის ახალ სპანებს თითოეულ გვერდითი განყოჀილებალი და გადასცემს მათ. თუმცა, სერვისების ჹიგნით სათაურებთან მულაობის გარეჹე, სრული მოთხოვნის კვალის ბილიკი დაიკარგება.

გასათვალისწინებელია ჹემდეგი სათაურები:


ეს არ არის რთული ამოცანა, მაგრამ მისი განხორციელების გამარტივება უკვე არსებობს ბევრი ბიბლიოთეკა - მაგალითად, sa-web-app სერვისლი RestTemplate კლიენტი გადასცემს ამ სათაურებს, თუ თქვენ უბრალოდ დაამატებთ Jaeger და OpenTracing ბიბლიოთეკებს მისი დამოკიდებულებები.

გაითვალისწინეთ, რომ Sentiment Analysis აპლიკაცია აჩვენებს განხორციელებებს Flask, Spring და ASP.NET Core-ლი.

ახლა, როცა უკვე ნათელია, რას ვიჩებთ ყუთიდან (ან თითქმის გარედან), მოდით გადავხედოთ მარლრუტის დაზუსტებას, ქსელური ტრაჀიკის მართვას, უსაჀრთხოებას და სხვას!

Ლენილვნა. თარგმნა: ამის ლესახებ წაიკითხეთ Istio-ს მასალების ჹემდეგ ნაწილლი Rinor Maloku-დან, რომლის თარგმანებიც უახლოეს მომავალჹი ჹემოგთავაზებთ ჩვენს ბლოგზე. განახლების (14 მარტი): მეორე ნაწილი უკვე გამოქვეყნებულია.

PS მთარგმნელისგან

ასევე წაიკითხეთ ჩვენს ბლოგზე:

წყარო: www.habr.com