werf 1.1 buraxılışı: bu gün inşaatçıya təkmilləşdirmələr və gələcək üçün planlar

werf 1.1 buraxılışı: bu gün inşaatçıya təkmilləşdirmələr və gələcək üçün planlar

werf proqramların yaradılması və Kubernetes-ə çatdırılması üçün açıq mənbəli GitOps CLI yardım proqramımızdır. Söz verildiyi kimi, v1.0 versiyasının buraxılışı werf-ə yeni funksiyaların əlavə edilməsinin və ənənəvi yanaşmaların yenidən nəzərdən keçirilməsinin başlanğıcını qeyd etdi. İndi biz inkişafda böyük addım və gələcək üçün təməl olan v1.1 buraxılışını təqdim etməkdən məmnunuq kollektor werf. Versiya hazırda mövcuddur kanal 1.1 e.

Buraxılışın əsasını yeni mərhələ saxlama arxitekturası və hər iki kollektorun işinin optimallaşdırılması (Stapel və Dockerfile üçün) təşkil edir. Yeni saxlama arxitekturası eyni hostda çoxlu hostlardan paylanmış birləşmələri və paralel montajları həyata keçirmək imkanını açır.

İşin optimallaşdırılması mərhələ imzalarının hesablanması mərhələsində lazımsız hesablamalardan qurtulmağı və fayl yoxlamalarının hesablanması mexanizmlərinin daha səmərəli olanlara dəyişdirilməsini əhatə edir. Bu optimallaşdırma werf-dən istifadə edərək layihənin qurulmasının orta vaxtını azaldır. Bütün mərhələlər keşdə mövcud olduqda boş qurulur mərhələlər - saxlama, indi həqiqətən sürətli. Əksər hallarda quruluşu yenidən başlatmaq 1 saniyədən az vaxt aparacaq! Bu, komandaların işi prosesində mərhələlərin yoxlanılması prosedurlarına da aiddir. werf deploy и werf run.

Həmçinin bu buraxılışda şəkilləri məzmuna görə etiketləmək strategiyası ortaya çıxdı - məzmuna əsaslanan etiketləmə, indi defolt olaraq aktivdir və yeganə tövsiyə olunur.

Gəlin werf v1.1-də əsas yeniliklərə daha yaxından nəzər salaq və eyni zamanda sizə gələcək planları haqqında danışaq.

werf v1.1-də nə dəyişdi?

Yeni mərhələ adlandırma formatı və keşdən mərhələləri seçmək üçün alqoritm

Yeni səhnə adı yaratma qaydası. İndi hər bir mərhələ quruluşu 2 hissədən ibarət unikal səhnə adı yaradır: imza (v1.0-da olduğu kimi) və unikal müvəqqəti identifikator.

Məsələn, tam səhnə şəklinin adı belə görünə bilər:

werf-stages-storage/myproject:d2c5ad3d2c9fcd9e57b50edd9cb26c32d156165eb355318cebc3412b-1582656767835

...və ya ümumiyyətlə:

werf-stages-storage/PROJECT:SIGNATURE-TIMESTAMP_MILLISEC

Burada:

  • SIGNATURE səhnə məzmununun identifikatorunu təmsil edən və Git-də bu məzmuna səbəb olan redaktələrin tarixindən asılı olan səhnə imzasıdır;
  • TIMESTAMP_MILLISEC yeni təsvirin qurulması zamanı yaradılan zəmanətli unikal şəkil identifikatorudur.

Keşdən mərhələlərin seçilməsi alqoritmi Git öhdəliyinin əlaqəsini yoxlamağa əsaslanır:

  1. Werf müəyyən mərhələnin imzasını hesablayır.
  2. В mərhələlər - saxlama Müəyyən bir imza üçün bir neçə mərhələ ola bilər. Werf imzaya uyğun gələn bütün mərhələləri seçir.
  3. Cari mərhələ Git ilə əlaqələndirilirsə (git-arxiv, Git yamaları ilə fərdi mərhələ: install, beforeSetup, setup; və ya git-latest-patch), sonra werf yalnız cari öhdəliyin əcdadı olan öhdəlik ilə əlaqəli olan mərhələləri seçir (bunun üçün qurulma çağırılır).
  4. Qalan uyğun mərhələlərdən biri seçilir - yaradılış tarixinə görə ən qədimi.

Fərqli Git filialları üçün bir mərhələ eyni imzaya malik ola bilər. Lakin werf, imzalar üst-üstə düşsə belə, müxtəlif filiallarla əlaqəli önbelleğin bu filiallar arasında istifadə edilməsinin qarşısını alacaq.

→ Sənədləşdirmə.

Səhnə yaddaşında mərhələlərin yaradılması və saxlanması üçün yeni alqoritm

Əgər keşdən mərhələləri seçərkən werf uyğun mərhələ tapmasa, o zaman yeni mərhələnin yığılması prosesi başlayır.

Qeyd edək ki, bir neçə proses (bir və ya bir neçə hostda) təxminən eyni vaxtda eyni mərhələni qurmağa başlaya bilər. Werf optimist bloklama alqoritmindən istifadə edir mərhələlər - saxlama təzə toplanmış şəkli saxlama anında mərhələlər - saxlama. Bu yolla, yeni mərhələ quruluşu hazır olduqda, werf blokları mərhələlər - saxlama və təzə toplanmış təsviri orada yalnız uyğun şəkil artıq mövcud olmadıqda saxlayır (imza və digər parametrlərə görə - keşdən mərhələ seçmək üçün yeni alqoritmə baxın).

Təzə yığılmış təsvirin unikal identifikatoru olacağına zəmanət verilir TIMESTAMP_MILLISEC (yeni mərhələ adlandırma formatına baxın). halda mərhələlər - saxlama uyğun bir şəkil tapılacaq, werf təzə tərtib edilmiş şəkli atacaq və təsviri keşdən istifadə edəcək.

Başqa sözlə: təsvirin qurulmasını başa çatdırmaq üçün ilk proses (ən sürətli olan) onu mərhələli yaddaşda saxlamaq hüququ əldə edəcək (və sonra bütün quruluşlar üçün istifadə ediləcək bu tək şəkildir). Yavaş qurulma prosesi heç vaxt daha sürətli prosesin cari mərhələnin qurma nəticələrini saxlamasına və növbəti quruluşa keçməsinə mane olmayacaq.

→ Sənədləşdirmə.

Təkmilləşdirilmiş Dockerfile qurucusu performansı

Hal-hazırda, Dockerfile-dən qurulmuş bir şəkil üçün mərhələlər xətti bir mərhələdən ibarətdir - dockerfile. İmza hesablanarkən faylların yoxlama məbləği hesablanır context, montaj zamanı istifadə olunacaq. Bu təkmilləşdirmədən əvvəl werf rekursiv olaraq bütün faylları gəzdi və hər bir faylın kontekstini və rejimini cəmləyərək yoxlama məbləği əldə etdi. V1.1-dən başlayaraq, werf Git repozitoriyasında saxlanılan hesablanmış yoxlama məbləğlərindən istifadə edə bilər.

Alqoritm buna əsaslanır git ls-ağacı. Alqoritmdə qeydlər nəzərə alınır .dockerignore və fayl ağacını yalnız lazım olduqda rekursiv şəkildə keçir. Beləliklə, biz fayl sistemini oxumaqdan və alqoritmin ölçüsündən asılılığını aradan qaldırdıq. context əhəmiyyətli deyil.

Alqoritm həmçinin izlənilməmiş faylları yoxlayır və zəruri hallarda yoxlama məbləğində onları nəzərə alır.

Faylları idxal edərkən təkmilləşdirilmiş performans

werf v1.1 versiyaları rsync serverindən istifadə etdikdə artefaktlardan və şəkillərdən faylların idxalı. Əvvəllər, idxal host sistemindən kataloq montajından istifadə etməklə iki addımda həyata keçirilirdi.

MacOS-da idxal performansı artıq Docker həcmləri ilə məhdudlaşmır və idxal Linux və Windows ilə eyni vaxtda tamamlanır.

Məzmun əsaslı etiketləmə

Werf v1.1 şəkil məzmunu ilə sözdə etiketləməni dəstəkləyir - məzmuna əsaslanan etiketləmə. Yaranan Docker şəkillərinin etiketləri həmin şəkillərin məzmunundan asılıdır.

Komandanı işləyərkən werf publish --tags-by-stages-signature və ya werf ci-env --tagging-strategy=stages-signature deyilən görüntüləri nəşr etdi səhnə imzası şəkil. Hər bir şəkil, bu təsvirin mərhələlərinin öz imzası ilə etiketlənir, bu, hər bir mərhələnin ayrı-ayrılıqda müntəzəm imzası ilə eyni qaydalara uyğun olaraq hesablanır, lakin təsvirin ümumi identifikatorudur.

Şəkil mərhələlərinin imzası aşağıdakılardan asılıdır:

  1. bu şəklin məzmunu;
  2. bu məzmuna səbəb olan Git dəyişikliklərinin tarixi.

Git repozitoriyası həmişə şəkil fayllarının məzmununu dəyişdirməyən dummy öhdəliklərə malikdir. Məsələn, yalnız şərhlər və ya birləşmə öhdəliyi ilə öhdəliklər və ya Git-də şəkilə idxal edilməyəcək faylları dəyişdirən öhdəliklər.

Məzmuna əsaslanan etiketləmədən istifadə edərkən, görüntünün məzmunu dəyişməsə belə, Kubernetes-də tətbiq podlarının lazımsız şəkildə yenidən işə salınması problemləri həll olunur. Yeri gəlmişkən, bu, bir tətbiqin bir çox mikroservislərinin bir Git deposunda saxlanmasına mane olan səbəblərdən biridir.

Həmçinin, məzmuna əsaslanan etiketləmə Git filiallarında etiketləmə ilə müqayisədə daha etibarlı etiketləmə üsuludur, çünki nəticədə yaranan şəkillərin məzmunu eyni filialın çoxsaylı öhdəliyinin yığılması üçün CI sistemində boru kəmərlərinin icrası ardıcıllığından asılı deyildir.

Vacibdir: indidən mərhələlər-imza - Mi yeganə tövsiyə olunan etiketləmə strategiyası. O, əmrdə standart olaraq istifadə olunacaq werf ci-env (açıq şəkildə fərqli etiketləmə sxemini göstərməsəniz).

→ Sənədləşdirmə. Bu xüsusiyyətə ayrıca bir nəşr də həsr olunacaq. YENİLƏNİB (3 aprel): Təfərrüatları olan məqalə nəşr olundu.

Giriş səviyyələri

İstifadəçinin indi çıxışa nəzarət etmək, giriş səviyyəsini təyin etmək və sazlama məlumatları ilə işləmək imkanı var. Seçimlər əlavə edildi --log-quiet, --log-verbose, --log-debug.

Varsayılan olaraq, çıxış minimum məlumatları ehtiva edir:

werf 1.1 buraxılışı: bu gün inşaatçıya təkmilləşdirmələr və gələcək üçün planlar

Ətraflı çıxışdan istifadə edərkən (--log-verbose) werf-in necə işlədiyini görə bilərsiniz:

werf 1.1 buraxılışı: bu gün inşaatçıya təkmilləşdirmələr və gələcək üçün planlar

Ətraflı çıxış (--log-debug), werf sazlama məlumatlarına əlavə olaraq, istifadə olunan kitabxanaların qeydlərini də ehtiva edir. Məsələn, Docker Registry ilə qarşılıqlı əlaqənin necə baş verdiyini görə bilərsiniz, həmçinin xeyli vaxt sərf olunan yerləri qeyd edə bilərsiniz:

werf 1.1 buraxılışı: bu gün inşaatçıya təkmilləşdirmələr və gələcək üçün planlar

Gələcək planlar

Diqqət! Aşağıda təsvir edilən seçimlər qeyd olunub v1.1 Onların bir çoxu yaxın gələcəkdə bu versiyada əlçatan olacaq. Yeniləmələr avtomatik yeniləmələr vasitəsilə gələcək multiwerf istifadə edərkən. Bu xüsusiyyətlər v1.1 funksiyalarının sabit hissəsinə təsir göstərmir, onların görünüşü mövcud konfiqurasiyalarda istifadəçinin əl ilə müdaxiləsini tələb etməyəcək.

Müxtəlif Docker Registry tətbiqləri üçün tam dəstək (YENİ)

  • Versiya: v1.1
  • Tarixlər: Mart
  • Problem

Məqsəd istifadəçinin werf-dən istifadə edərkən məhdudiyyətsiz fərdi tətbiqdən istifadə etməsidir.

Hal-hazırda, tam dəstəyə zəmanət verəcəyimiz aşağıdakı həllər dəstini müəyyən etdik:

  • Defolt (kitabxana/reyestr)*,
  • AWS ECR
  • Azure*,
  • Docker Hub
  • GCR*,
  • GitHub Paketləri
  • GitLab Registry*,
  • Liman*,
  • Quay.

Hal-hazırda werf tərəfindən tam dəstəklənən həllər ulduz işarəsi ilə qeyd olunur. Digərləri üçün dəstək var, lakin məhdudiyyətlərlə.

İki əsas problem müəyyən edilə bilər:

  • Bəzi həllər Docker Registry API istifadə edərək etiketlərin silinməsini dəstəkləmir və istifadəçilərin werf-in avtomatik təmizlənməsindən istifadə etməsinə mane olur. Bu, AWS ECR, Docker Hub və GitHub Paketləri üçün doğrudur.
  • Bəzi həllər sözdə yuvalanmış depoları (Docker Hub, GitHub Paketləri və Quay) dəstəkləmir və ya dəstəkləyir, lakin istifadəçi UI və ya API (AWS ECR) istifadə edərək onları əl ilə yaratmalıdır.

Həlllərin yerli API-lərindən istifadə edərək bu və digər problemləri həll edəcəyik. Bu vəzifə həm də onların hər biri üçün testlərlə werf əməliyyatının tam dövrəsini əhatə edir.

Paylanmış şəkil quruluşu (↑)

  • Versiya: v1.2 v1.1 (bu funksiyanın həyata keçirilməsi üçün prioritet artırılıb)
  • Tarixlər: Mart-Aprel Mart
  • Problem

Hazırda werf v1.0 və v1.1 şəkillərin qurulması və dərc edilməsi və tətbiqin Kubernetes-də yerləşdirilməsi əməliyyatları üçün yalnız bir xüsusi hostda istifadə edilə bilər.

werf-in paylanmış iş imkanlarını açmaq üçün, Kubernetes-də tətbiqlərin qurulması və yerləşdirilməsi bir neçə ixtiyari hostda işə salındıqda və bu hostlar quruluşlar (müvəqqəti qaçışçılar) arasında öz vəziyyətlərini saxlamadıqda, werf-dən istifadə etmək qabiliyyətini həyata keçirmək tələb olunur. səhnə mağazası kimi Docker Registry.

Əvvəllər werf layihəsi hələ də dapp adlananda onun belə bir imkanı var idi. Bununla belə, werf-də bu funksiyanı həyata keçirərkən nəzərə alınmalı olan bir sıra məsələlərlə qarşılaşdıq.

Qeyd. Bu xüsusiyyət kollektorun Kubernetes podlarının içərisində işləməsini tələb etmir, çünki Bunu etmək üçün yerli Docker serverindən asılılıqdan qurtulmalısınız (Kubernetes podunda yerli Docker serverinə giriş yoxdur, çünki proses özü konteynerdə işləyir və werf dəstəkləmir və dəstəkləməyəcəkdir. şəbəkə üzərindən Docker serveri ilə işləmək). Kubernetes-in işləməsi üçün dəstək ayrıca həyata keçiriləcək.

GitHub Actions üçün rəsmi dəstək (YENİ)

  • Versiya: v1.1
  • Tarixlər: Mart
  • Problem

werf sənədləri daxildir (bölmələr arayış и yol), həmçinin werf ilə işləmək üçün rəsmi GitHub Fəaliyyəti.

Bundan əlavə, o, werf-ə efemer qaçışçılar üzərində işləməyə imkan verəcək.

İstifadəçinin CI sistemi ilə qarşılıqlı əlaqəsinin mexanikası tətbiqi qurmaq/yaymaq üçün müəyyən hərəkətlərə başlamaq üçün çəkmə sorğularına etiketlər yerləşdirməyə əsaslanacaq.

werf ilə tətbiqlərin yerli inkişafı və yerləşdirilməsi (↓)

  • Versiya: v1.1
  • Tarixlər: yanvar-fevral aprel
  • Problem

Əsas məqsəd proqramların həm yerli, həm də istehsalatda, kompleks tədbirlər olmadan, qutudan kənarda yerləşdirilməsi üçün vahid vahid konfiqurasiyaya nail olmaqdır.

werf-dən həmçinin proqram kodunu redaktə etmək və sazlama üçün işləyən proqramdan dərhal rəy almaq rahat olacaq bir iş rejimi tələb olunur.

Yeni təmizləmə alqoritmi (YENİ)

  • Versiya: v1.1
  • Tarixlər: aprel
  • Problem

Prosedurda werf v1.1-in cari versiyasında cleanup Məzmun əsaslı etiketləmə sxemi üçün şəkillərin təmizlənməsi üçün heç bir müddəa yoxdur - bu şəkillər toplanacaq.

Həmçinin, werf-in cari versiyası (v1.0 və v1.1) etiketləmə sxemləri altında dərc edilmiş şəkillər üçün müxtəlif təmizləmə siyasətlərindən istifadə edir: Git filialı, Git tag və ya Git commit.

Git-də öhdəliyin tarixinə əsaslanan, bütün etiketləmə sxemləri üçün birləşdirilən şəkillərin təmizlənməsi üçün yeni alqoritm icad edilmişdir:

  • Hər git HEAD (filiallar və teqlər) üçün ən son N1 öhdəliyi ilə əlaqəli N2-dən çox olmayan şəkilləri saxlayın.
  • Hər git HEAD (filiallar və teqlər) üçün ən son N1 öhdəliyi ilə əlaqəli N2-dən çox olmayan mərhələ şəkillərini saxlayın.
  • İstənilən Kubernetes klaster resurslarında istifadə olunan bütün şəkilləri saxlayın (konfiqurasiya faylının bütün kube kontekstləri və ad boşluqları skan edilir; bu davranışı xüsusi seçimlərlə məhdudlaşdıra bilərsiniz).
  • Helm buraxılışlarında saxlanılan resurs konfiqurasiya manifestlərində istifadə olunan bütün şəkilləri saxlayın.
  • Şəkil git-dən heç bir HEAD ilə əlaqələndirilmədikdə (məsələn, müvafiq HEAD-in özü silindiyi üçün) və Kubernetes klasterində və Helm buraxılışlarında heç bir manifestdə istifadə edilmədikdə silinə bilər.

Paralel təsvirin qurulması (↓)

  • Versiya: v1.1
  • Tarixlər: yanvar-fevral aprel*

Werf-in cari versiyası təsvirdə təsvir olunan şəkilləri və artefaktları toplayır werf.yaml, ardıcıl olaraq. Şəkillərin və artefaktların müstəqil mərhələlərinin yığılması prosesini paralelləşdirmək, eləcə də rahat və informativ çıxış təmin etmək lazımdır.

* Qeyd: paylanmış montajın həyata keçirilməsi üçün artan prioritet səbəbindən son tarix dəyişdirilib, bu, daha çox üfüqi miqyaslama imkanları əlavə edəcək, həmçinin GitHub Fəaliyyətləri ilə werf-dən istifadə edəcəkdir. Paralel montaj bir layihəni birləşdirərkən şaquli miqyaslılığı təmin edən növbəti optimallaşdırma addımıdır.

Sükan 3-ə keçid (↓)

  • Versiya: v1.2
  • Tarixlər: fevral-mart may*

Yeni kod bazasına miqrasiya daxildir Sükan 3 və mövcud qurğuları köçürməyin sübut edilmiş, rahat yolu.

* Qeyd: Helm 3-ə keçid werf-ə əhəmiyyətli xüsusiyyətlər əlavə etməyəcək, çünki Helm 3-ün bütün əsas xüsusiyyətləri (3-yollu birləşmə və yivsiz) artıq werf-də tətbiq olunub. Üstəlik, werf var əlavə xüsusiyyətlər göstərilənlərə əlavə olaraq. Lakin bu keçid planlarımızda qalır və həyata keçiriləcək.

Kubernetes konfiqurasiyasını təsvir etmək üçün Jsonnet (↓)

  • Versiya: v1.2
  • Tarixlər: yanvar-fevral aprel-may

Werf Jsonnet formatında Kubernetes üçün konfiqurasiya təsvirlərini dəstəkləyəcək. Eyni zamanda, werf Helm ilə uyğun olaraq qalacaq və təsvir formatı seçimi olacaq.

Səbəb odur ki, Go şablonları, bir çox insanların fikrincə, yüksək giriş maneəsinə malikdir və bu şablonların kodunun başa düşülməsi də əziyyət çəkir.

Digər Kubernetes konfiqurasiya təsvir sistemlərinin (məsələn, Kustomize) tətbiqi imkanları da nəzərdən keçirilir.

Kubernetes daxilində işləmək (↓)

  • Versiya: v1.2
  • Tarixlər: aprel-may may-iyun

Məqsəd: Şəkillərin qurulduğundan və tətbiqin Kubernetesdəki qaçışçılardan istifadə edilərək çatdırıldığından əmin olun. Bunlar. Yeni şəkilləri birbaşa Kubernetes podlarından qurmaq, dərc etmək, təmizləmək və yerləşdirmək olar.

Bu qabiliyyəti həyata keçirmək üçün əvvəlcə paylanmış şəkilləri qurmağı bacarmalısınız (yuxarıdakı nöqtəyə baxın).

O, həmçinin Docker serveri olmadan (məsələn, Kaniko kimi qurma və ya istifadəçi məkanında qurma) qurucunun iş rejiminə dəstək tələb edir.

Werf Kubernetes-də təkcə Dockerfile ilə deyil, həm də artımlı yenidənqurma və Ansible ilə Stapel qurucusunu dəstəkləyəcək.

Açıq inkişafa doğru bir addım

Biz cəmiyyətimizi sevirik (Github, Teleqram) və biz istəyirik ki, getdikcə daha çox insan werf-i daha yaxşı etməyə kömək etsin, irəlilədiyimiz istiqaməti başa düşsün və inkişafda iştirak etsin.

Bu yaxınlarda keçid etmək qərara alındı GitHub layihə lövhələri komandamızın iş prosesini üzə çıxarmaq üçün. İndi siz dərhal planları, eləcə də aşağıdakı sahələrdə cari işləri görə bilərsiniz:

Problemlərlə bağlı çox iş görülüb:

  • Uyğun olmayanlar silindi.
  • Mövcud olanlar kifayət qədər detal və detallarla vahid formata gətirilir.
  • İdeya və təkliflərlə bağlı yeni məsələlər əlavə edildi.

v1.1 versiyasını necə aktivləşdirmək olar

Versiya hazırda mövcuddur kanal 1.1 e (kanallarda stabil и qaya kimi sabitləşmə baş verdikcə buraxılışlar görünəcək ea özü artıq istifadə üçün kifayət qədər sabitdir, çünki kanallardan keçdi alfa и beta). Aktivləşdirilib multiwerf vasitəsilə aşağıdakı şəkildə:

source $(multiwerf use 1.1 ea)
werf COMMAND ...

Nəticə

Stapel və Dockerfile qurucuları üçün yeni mərhələ saxlama arxitekturası və qurucu optimallaşdırmaları werf-də paylanmış və paralel quruluşların həyata keçirilməsi imkanlarını açır. Bu funksiyalar tezliklə eyni v1.1 buraxılışında görünəcək və avtomatik yeniləmə mexanizmi (istifadəçilər üçün) vasitəsilə avtomatik olaraq əlçatan olacaq. multiwerf).

Bu buraxılışda şəkil məzmununa əsaslanan etiketləmə strategiyası əlavə edilmişdir - məzmuna əsaslanan etiketləmə, bu standart strategiyaya çevrildi. Əsas əmr jurnalı da yenidən işlənmişdir: werf build, werf publish, werf deploy, werf dismiss, werf cleanup.

Növbəti mühüm addım paylanmış məclisləri əlavə etməkdir. Paylanmış quruluşlar v1.0-dan bəri paralel qurmalardan daha yüksək prioritetə ​​çevrilib, çünki onlar werf-ə daha çox dəyər verir: inşaatçıların şaquli miqyası və müxtəlif CI/CD sistemlərində efemer quruculara dəstək, həmçinin GitHub Actions üçün rəsmi dəstək vermək imkanı . Buna görə də paralel montajların icra müddətləri dəyişdirildi. Bununla belə, biz hər iki imkanın ən qısa zamanda həyata keçirilməsi üçün çalışırıq.

Xəbərləri izləyin! Bizi ziyarət etməyi unutmayın Githubproblem yaratmaq, mövcud olanı tapmaq və əlavə etmək, PR yaratmaq və ya sadəcə olaraq layihənin inkişafını izləmək.

PS

Bloqumuzda da oxuyun:

Mənbə: www.habr.com

Добавить комментарий