Kubernetes-en arazoak konpontzeko gida bisuala

Ohar. itzul.: Artikulu hau domeinu publikoan argitaratutako proiektuko materialen parte da ikasi8s, enpresek eta banakako administratzaileak trebatzea Kubernetesekin lan egiteko. Bertan, Daniele Polencic, proiektuaren kudeatzaileak, K8s klusterrean exekutatzen diren aplikazioekin arazo orokorrak izanez gero eman beharreko urratsei buruzko argibide bisualak partekatzen ditu.

Kubernetes-en arazoak konpontzeko gida bisuala

TL; DR: hona hemen Kubernetesen inplementazioa arakatzen lagunduko dizun diagrama bat:

Kubernetes-en arazoak konpontzeko gida bisuala

Kluster batean akatsak aurkitzeko eta konpontzeko fluxu-diagrama. Jatorrizkoa (ingelesez) helbidean dago eskuragarri PDF ΠΈ irudi gisa.

Aplikazio bat Kubernetes-en zabaltzean, normalean hiru osagai definitu behar dituzu:

  • Inplementazio - Aplikazioaren kopiak sortzeko errezeta moduko bat da, pods izenekoa;
  • zerbitzua β€” trafikoa poden artean banatzen duen barne karga-orekatzailea;
  • Ingress β€” trafikoa kanpoko mundutik Zerbitzura nola iritsiko den deskribatzea.

Hona hemen laburpen grafiko azkar bat:

1) Kubernetesen, aplikazioek kanpoko munduaren trafikoa jasotzen dute karga-orekatzaileen bi geruzen bidez: barnekoa eta kanpokoa.

Kubernetes-en arazoak konpontzeko gida bisuala

2) Barne-balantzaileari Zerbitzua deitzen zaio, kanpokoari Ingress.

Kubernetes-en arazoak konpontzeko gida bisuala

3) Inplementazioak lekak sortzen ditu eta haiek kontrolatzen ditu (ez dira eskuz sortzen).

Kubernetes-en arazoak konpontzeko gida bisuala

Demagun aplikazio sinple bat zabaldu nahi duzula Kaixo Mundua. Horren YAML konfigurazioa honela izango da:

apiVersion: apps/v1
kind: Deployment # <<<
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
      labels:
        any-name: my-app
    spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service # <<<
metadata:
  name: my-service
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    name: app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress # <<<
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
        serviceName: app
        servicePort: 80
      path: /

Definizioa nahiko luzea da eta erraz nahastea da osagaiak nola erlazionatzen diren.

Adibidez:

  • Noiz erabili behar duzu 80 ataka eta noiz erabili 8080?
  • Zerbitzu bakoitzerako ataka berri bat sortu behar al dut gatazkarik ez izateko?
  • Garrantzi al dute etiketen izenak? Berdinak izan behar al dute nonahi?

Arazketan zentratu aurretik, gogora dezagun hiru osagaiak nola erlazionatzen diren elkarren artean. Has gaitezen Inplementazioa eta Zerbitzuarekin.

Hedapenaren eta Zerbitzuaren arteko harremana

Harrituta geratuko zara, baina Inplementazioa eta Zerbitzua ez daude inola ere konektatuta. Horren ordez, Zerbitzuak zuzenean Pods-era seinalatzen du, Inplementazioa saihestuz.

Horrela, Pods eta Zerbitzuak elkarren artean nola erlazionatzen diren interesatzen zaigu. Gogoratu beharreko hiru gauza:

  1. Hautatzailea (selector) Zerbitzurako gutxienez Pod etiketa batekin bat etorri behar da.
  2. targetPort bat etorri behar da containerPort Pod barruan edukiontzia.
  3. port Zerbitzua edozer izan daiteke. Zerbitzu ezberdinek ataka bera erabil dezakete IP helbide desberdinak dituztelako.

Hurrengo eskemak aurreko guztia grafikoki adierazten du:

1) Imajinatu zerbitzuak trafikoa pod jakin batera bideratzen duela:

Kubernetes-en arazoak konpontzeko gida bisuala

2) Pod bat sortzean, zehaztu behar duzu containerPort ontzi bakoitzeko ontzi bakoitzeko:

Kubernetes-en arazoak konpontzeko gida bisuala

3) Zerbitzu bat sortzean, zehaztu behar duzu port ΠΈ targetPort. Baina zein erabiltzen da edukiontzira konektatzeko?

Kubernetes-en arazoak konpontzeko gida bisuala

4) Via targetPort. Bat etorri behar da containerPort.

Kubernetes-en arazoak konpontzeko gida bisuala

5) Demagun 3000. ataka edukiontzian irekita dagoela.Ondoren balioa targetPort berdina izan beharko luke.

Kubernetes-en arazoak konpontzeko gida bisuala

YAML fitxategian, etiketak eta ports / targetPort bat etorri behar da:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
     labels:  # <<<
        any-name: my-app  # <<<
   spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
       - containerPort: 8080  # <<<
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - port: 80
   targetPort: 8080  # <<<
 selector:  # <<<
    any-name: my-app  # <<<

Zer gertatzen da etiketarekin track: canary Hedapena atalaren goialdean? Bat etorri behar al du?

Etiketa hau inplementazio espezifikoa da eta zerbitzuak ez du erabiltzen trafikoa bideratzeko. Beste era batera esanda, kendu edo beste balio bat esleitu daiteke.

Zer esan hautatzailea matchLabels?

Beti bat egin behar du Pod-en etiketekin, Deployment-ek lekak jarraitzeko erabiltzen baitu.

Demagun aldaketa zuzenak egin dituzula. Nola egiaztatu?

Pod etiketa egiazta dezakezu komando honekin:

kubectl get pods --show-labels

Edo, lekak hainbat aplikaziotakoak badira:

kubectl get pods --selector any-name=my-app --show-labels

non any-name=my-app etiketa bat da any-name: my-app.

Zailtasunik geratzen al da?

Podarekin konekta zaitezke! Horretarako komandoa erabili behar duzu port-forward kubectl-en. Zerbitzura konektatzeko eta konexioa egiaztatzeko aukera ematen du.

kubectl port-forward service/<service name> 3000:80

hemen:

  • service/<service name> β€” zerbitzuaren izena; gure kasuan hala da my-service;
  • 3000 da ordenagailuan ireki behar den ataka;
  • 80 - eremuan zehaztutako portua port zerbitzua.

Konexioa ezarri bazen, ezarpenak zuzenak dira.

Konexioak huts egiten badu, etiketarekin arazo bat dago edo atakak ez datoz bat.

Zerbitzuaren eta Sarreraren arteko harremana

Aplikaziorako sarbidea emateko hurrengo urratsa Ingress konfiguratzea da. Ingress-ek zerbitzu bat nola aurkitzen jakin behar du, gero gailuak aurkitu eta haietara trafikoa bideratu. Ingress-ek behar den zerbitzua aurkitzen du izenaren eta ataka irekiaren arabera.

Sarrera eta Zerbitzuaren deskribapenean, bi parametrok bat etorri behar dute:

  1. servicePort Ingress-en parametroarekin bat etorri behar da port Zerbitzuan;
  2. serviceName in Ingress eremuarekin bat etorri behar da name Zerbitzuan.

Ondorengo diagramak portuen konexioak laburbiltzen ditu:

1) Dagoeneko dakizuenez, Zerbitzuak jakin bat entzuten du port:

Kubernetes-en arazoak konpontzeko gida bisuala

2) Ingress izeneko parametro bat du servicePort:

Kubernetes-en arazoak konpontzeko gida bisuala

3) Parametro hau (servicePort) beti bat etorri behar da port Zerbitzuaren definizioan:

Kubernetes-en arazoak konpontzeko gida bisuala

4) Zerbitzuan 80 ataka zehazten bada, beharrezkoa da hori servicePort 80ren berdina ere izan zen:

Kubernetes-en arazoak konpontzeko gida bisuala

Praktikan, lerro hauei arreta jarri behar diezu:

apiVersion: v1
kind: Service
metadata:
 name: my-service  # <<<
spec:
  ports:
 - port: 80  # <<<
   targetPort: 8080
  selector:
    any-name: my-app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
       serviceName: my-service  # <<<
       servicePort: 80  # <<<
     path: /

Nola egiaztatu Ingress martxan dagoen?

Metodoarekin erabil dezakezu kubectl port-forward, baina zerbitzuaren ordez Ingress kontrolagailura konektatu behar duzu.

Lehenik eta behin, Ingress kontrolagailuarekin podaren izena aurkitu behar duzu:

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Bilatu Ingress pod-a (beste izen-espazio batean egon daiteke) eta exekutatu komandoa describeataka zenbakiak jakiteko:

kubectl describe pod nginx-ingress-controller-6fc5bcc 
--namespace kube-system 
 | grep Ports
Ports:         80/TCP, 443/TCP, 18080/TCP

Azkenik, konektatu gailura:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Orain zure ordenagailuko 3000 atakara eskaera bat bidaltzen duzun bakoitzean, Ingress kontrolagailuarekin pod-eko 80 atakara birbidaliko da. Joanez http://localhost:3000, aplikazioak sortutako orria ikusi beharko zenuke.

Portuen laburpena

Gogora dezagun berriro zein ataka eta etiketa bat etorri behar duten:

  1. Zerbitzuaren definizioko hautatzaileak pod-aren etiketarekin bat etorri behar du;
  2. targetPort definizioan Zerbitzuak bat etorri behar du containerPort leka baten barruan edukiontzia;
  3. port definizioan Zerbitzua edozer izan daiteke. Zerbitzu ezberdinek ataka bera erabil dezakete IP helbide desberdinak dituztelako;
  4. servicePort Sarrerak bat etorri behar du port Zerbitzuaren definizioan;
  5. Zerbitzuaren izenak eremuarekin bat etorri behar du serviceName Sarreran.

Zoritxarrez, ez da nahikoa YAML konfigurazio bat behar bezala egituratzen jakitea.

Zer gertatzen da gauzak gaizki doazenean?

Baliteke poda ez abiatzea edo huts egitea.

Kubernetes-en aplikazio-arazoak diagnostikatzeko 3 urrats

Zure inplementazioa arakatzen hasi aurretik, Kubernetes-ek nola funtzionatzen duen ondo ulertu behar duzu.

K8s-en deskargatutako aplikazio bakoitzak hiru osagai dituenez, ordena jakin batean arazketa egin behar da, behetik hasita.

  1. Lehenik, lekak funtzionatzen ari direla ziurtatu behar duzu, gero...
  2. Egiaztatu zerbitzuak trafikoa hornitzen duen podei, eta gero...
  3. Egiaztatu Ingress behar bezala konfiguratuta dagoen.

Irudikapen bisuala:

1) Behetik hasi beharko zenuke arazoak bilatzen. Lehenik eta behin, egiaztatu lekak egoerak dituztela Ready ΠΈ Running:

Kubernetes-en arazoak konpontzeko gida bisuala

2) Lekak prest badaude (Ready), jakin beharko zenuke zerbitzuak ontzien artean trafikoa banatzen duen ala ez:

Kubernetes-en arazoak konpontzeko gida bisuala

3) Azkenik, zerbitzuaren eta Sarreraren arteko lotura aztertu behar duzu:

Kubernetes-en arazoak konpontzeko gida bisuala

1. Leken diagnostikoa

Kasu gehienetan arazoa podarekin lotuta dago. Ziurtatu lekak honela zerrendatuta daudela Ready ΠΈ Running. Hau egiaztatu dezakezu komandoa erabiliz:

kubectl get pods
NAME                    READY STATUS            RESTARTS  AGE
app1                    0/1   ImagePullBackOff  0         47h
app2                    0/1   Error             0         47h
app3-76f9fcd46b-xbv4k   1/1   Running           1         47h

Goiko komandoaren irteeran, azken poda honela agertzen da Running ΠΈ Ready, ordea, ez da hori beste bietan gertatzen.

Nola ulertu zer gertatu den gaizki?

Podak diagnostikatzeko lau komando erabilgarriak daude:

  1. kubectl logs <имя pod'а> ontzi bateko erregistroak ateratzeko aukera ematen du;
  2. kubectl describe pod <имя pod'а> podarekin lotutako gertaeren zerrenda ikusteko aukera ematen du;
  3. kubectl get pod <имя pod'а> Kubernetes-en gordetako pod baten YAML konfigurazioa lortzeko aukera ematen du;
  4. kubectl exec -ti <имя pod'а> bash komando-shell interaktibo bat abiarazteko aukera ematen dizu pod edukiontzietako batean

Zein aukeratu behar duzu?

Kontua da ez dagoela agindu unibertsala. Hauen konbinazio bat erabili behar da.

Lekaren arazo tipikoak

Bi motatako akatsak daude: abiarazte-akatsak eta exekuzio-akatsak.

Abiarazteko erroreak:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • InvalidImageName

Exekutatzeko akatsak:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

Akats batzuk beste batzuk baino ohikoagoak dira. Hona hemen akats ohikoenetako batzuk eta nola konpondu.

ImagePullBackOff

Errore hau Kubernetes-ek ontzi-ontzietako baten irudirik lortu ezin duenean gertatzen da. Hona hemen horretarako hiru arrazoi ohikoenak:

  1. Irudiaren izena okerra da - adibidez, akats bat egin duzu bertan, edo irudia ez da existitzen;
  2. Irudirako existitzen ez den etiketa bat zehaztu zen;
  3. Irudia erregistro pribatu batean gordetzen da eta Kubernetes-ek ez du baimenik bertara sartzeko.

Lehenengo bi arrazoiak erraz ezabatzen dira - zuzen ezazu irudiaren izena eta etiketa. Azken honen kasuan, itxitako erregistrorako kredentzialak sartu behar dituzu Sekretuan eta estekak gehitu behar dituzu podetan. Kubernetesen dokumentazioan adibide bat dago hau nola egin daitekeen.

Crash Loop Back Off

Kubenetes akats bat botatzen du CrashLoopBackOff, edukiontzia ezin bada hasi. Hau normalean gertatzen da:

  1. Aplikazioan abiaraztea eragozten duen akats bat dago;
  2. edukiontzi gaizki konfiguratuta;
  3. Liveness probak gehiegitan huts egin du.

Edukiontzitik erregistroetara iristen saiatu behar duzu porrotaren arrazoia ezagutzeko. Erregistroetara sartzea zaila bada edukiontzia azkarregi berrabiarazten delako, komando hau erabil dezakezu:

kubectl logs <pod-name> --previous

Edukiontziaren aurreko enkarnazioko errore-mezuak bistaratzen ditu.

RunContainerError

Errore hau edukiontzia abiarazten ez denean gertatzen da. Aplikazioa martxan jarri aurreko momentuari dagokio. Normalean ezarpen okerrek eragiten dute, adibidez:

  • existitzen ez den bolumen bat muntatzen saiatzea, esate baterako, ConfigMap edo Secrets;
  • irakurtzeko soilik den bolumen bat irakurtzeko-idazketa gisa muntatzeko saiakera bat.

Taldea oso egokia da horrelako akatsak aztertzeko kubectl describe pod <pod-name>.

Lekak Zain dauden egoeran daude

Sortu ondoren, leka egoeran geratzen da Pending.

Zergatik gertatzen da hau?

Hona hemen arrazoi posibleak (planifikatzaileak ondo funtzionatzen duela suposatzen dut):

  1. Klusterrak ez du baliabide nahikorik, hala nola prozesatzeko potentzia eta memoria, poda exekutatzeko.
  2. Objektua izen-espazio egokian instalatuta dago ResourceQuota eta pod bat sortzeak izen-espazioa kuota gainditzea eragingo du.
  3. Podra Zain dago loturik PersistentVolumeClaim.

Kasu honetan, komandoa erabiltzea gomendatzen da kubectl describe eta begiratu atala Events:

kubectl describe pod <pod name>

Honekin lotutako akatsen kasuan ResourceQuotas, komandoa erabiliz clusterren erregistroak ikustea gomendatzen da

kubectl get events --sort-by=.metadata.creationTimestamp

Lekak ez daude Prest

Pod gisa zerrendatzen bada Running, baina ez dago egoera batean Ready, prest dagoen egiaztatzea esan nahi du (presttasun zunda) huts egiten du.

Hori gertatzen denean, pod-a ez da zerbitzura konektatzen eta ez da trafikorik joaten. Prestakuntza probaren porrota aplikazioan dauden arazoek eragiten dute. Kasu honetan, errorea aurkitzeko, atala aztertu behar duzu Events komandoaren irteeran kubectl describe.

2. Zerbitzu-diagnostikoak

Lekak gisa zerrendatzen badira Running ΠΈ Ready, baina oraindik ez dago aplikazioaren erantzunik, zerbitzuaren ezarpenak egiaztatu behar dituzu.

Zerbitzuak arduratzen dira trafikoa podetara bideratzeaz, haien etiketen arabera. Hori dela eta, egin behar duzun lehenengo gauza zerbitzuarekin zenbat pods funtzionatzen duten egiaztatzea da. Horretarako, zerbitzuko amaierako puntuak egiaztatu ditzakezu:

kubectl describe service <service-name> | grep Endpoints

Amaiera inprimakiaren balio pare bat da <IP-адрСс:ΠΏΠΎΡ€Ρ‚>, eta gutxienez halako bikote bat egon behar da irteeran (hau da, gutxienez pod batek zerbitzuarekin funtzionatzen du).

atala bada Endpoins hutsik, bi aukera daude posible:

  1. ez dago etiketa zuzena duten podsik (aholkua: egiaztatu izen-espazioa ondo hautatuta dagoen);
  2. Errore bat dago hautatzaileko zerbitzu-etiketetan.

Amaiera-puntuen zerrenda ikusten baduzu baina oraindik ezin baduzu aplikaziora sartu, orduan erruduna akats bat da targetPort zerbitzuaren deskribapenean.

Nola egiaztatu zerbitzuaren funtzionaltasuna?

Zerbitzu mota edozein dela ere, komandoa erabil dezakezu kubectl port-forward harekin konektatzeko:

kubectl port-forward service/<service-name> 3000:80

hemen:

  • <service-name> β€” zerbitzuaren izena;
  • 3000 da ordenagailuan irekitzen duzun ataka;
  • 80 - portua zerbitzu aldean.

3. Sarrera-diagnostikoak

Honaino irakurri baduzu, orduan:

  • lekak gisa zerrendatzen dira Running ΠΈ Ready;
  • zerbitzuak arrakastaz banatzen du trafikoa poden artean.

Hala ere, oraindik ezin zara aplikaziora iritsi.

Horrek esan nahi du Ingress kontrolagailua behar bezala ez dagoela behar bezala konfiguratuta. Ingress kontrolatzailea klusterreko hirugarrenen osagaia denez, motaren arabera arazketa-metodo desberdinak daude.

Baina Ingress konfiguratzeko tresna bereziak erabiltzera jo baino lehen, zerbait oso erraza egin dezakezu. Sarrera erabilerak serviceName ΠΈ servicePort zerbitzura konektatzeko. Ondo konfiguratuta dauden egiaztatu behar duzu. Hau egin dezakezu komandoa erabiliz:

kubectl describe ingress <ingress-name>

Zutabea bada Backend hutsik, konfigurazio-errore bat izateko probabilitate handia dago. Backend-ak lekuan badaude, baina aplikazioa oraindik ez badago eskuragarri, arazoa honako hau izan daiteke:

  • Sartu irisgarritasun-ezarpenak Internet publikotik;
  • kluster erabilerraztasun-ezarpenak Internet publikotik.

Azpiegituraren arazoak identifikatu ditzakezu zuzenean Ingress pod-era konektatuz. Horretarako, bilatu lehenik Ingress Controller poda (baliteke izen-espazio ezberdin batean egotea):

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Erabili komandoa describeataka ezartzeko:

kubectl describe pod nginx-ingress-controller-6fc5bcc
--namespace kube-system 
 | grep Ports

Azkenik, konektatu gailura:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Orain ordenagailuko 3000 atakarako eskaera guztiak pod-eko 80 atakara birbideratuko dira.

Orain funtzionatzen al du?

  • Baiezkoa bada, arazoa azpiegiturarena da. Trafikoa klusterera nola bideratzen den jakin behar da.
  • Hala ez bada, arazoa Ingress kontrolagailuarekin dago.

Ezin baduzu Ingress kontrolagailua funtzionatzea lortu, arazketa egin beharko duzu.

Ingress kontrolagailu mota asko daude. Ezagunenak Nginx, HAProxy, Traefik, etab. (Duden soluzioei buruzko informazio gehiago lortzeko, ikus gure berrikuspena - gutxi gorabehera. itzul.) Arazoak konpontzeko gidaliburua kontsultatu beharko zenuke dagokion kontrolagailuaren dokumentazioan. Zeren Sarrera Nginx Ingress kontrolagailu ezagunena da, artikuluan aholku batzuk sartu ditugu horrekin lotutako arazoak konpontzeko.

Ingress Nginx kontrolagailua araztea

Ingress-nginx proiektuak ofizial bat du kubectl-erako plugina. Taldea kubectl ingress-nginx hauetarako erabil daiteke:

  • erregistroak, backendak, ziurtagiriak eta abar aztertzea;
  • Ingress-ekin konexioak;
  • egungo konfigurazioa aztertzen.

Hiru komando hauek lagunduko dizute horretan:

  • kubectl ingress-nginx lint β€” txekeak nginx.conf;
  • kubectl ingress-nginx backend β€” backend-a aztertzen du (en antzekoa kubectl describe ingress <ingress-name>);
  • kubectl ingress-nginx logs - erregistroak egiaztatzen ditu.

Kontuan izan kasu batzuetan Ingress kontrolagailurako izen-espazio zuzena zehaztu behar duzula bandera erabiliz --namespace <name>.

Laburpena

Kubernetes-en arazoak konpontzea zaila izan daiteke nondik hasi ez badakizu. Beti arazoa behetik gora jorratu behar duzu: hasi podsekin, eta, gero, zerbitzura eta Ingressera pasa. Artikulu honetan deskribatutako arazketa-teknikak beste objektu batzuetan aplika daitezke, hala nola:

  • Idle Jobs eta CronJobs;
  • StatefulSets eta DaemonSets.

Nire esker ona adierazten dut Gergely Risko, Daniel Weibel ΠΈ Charles Christyraj iruzkin eta gehigarri baliotsuetarako.

PS itzultzailetik

Irakurri ere gure blogean:

Iturria: www.habr.com

Gehitu iruzkin berria