Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Բարև Խաբրովսկի բնակիչներ։ Դասընթացի առաջին խմբում դասերը սկսվում են այսօր «PostgreSQL». Այս կապակցությամբ մենք ցանկանում ենք ձեզ պատմել, թե ինչպես է տեղի ունեցել այս դասընթացի բաց վեբինարը:

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

В հաջորդ բաց դաս մենք խոսեցինք SQL տվյալների բազաների առջև ծառացած մարտահրավերների մասին ամպերի և Kubernetes-ի դարաշրջանում: Միևնույն ժամանակ, մենք նայեցինք, թե ինչպես են SQL տվյալների բազաները հարմարվում և փոփոխվում այս մարտահրավերների ազդեցության տակ:

Վեբինարն անցկացվեց Վալերի Բեզրուկով, Google Cloud Practice Delivery Manager EPAM Systems-ում:

Երբ ծառերը փոքր էին...

Նախ, եկեք հիշենք, թե ինչպես սկսվեց DBMS-ի ընտրությունը անցյալ դարի վերջին: Այնուամենայնիվ, դա դժվար չի լինի, քանի որ այդ օրերին DBMS-ի ընտրությունը սկսվեց և ավարտվեց Oracle.

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

90-ականների վերջին և 2-ականների սկզբին, ըստ էության, ընտրություն չկար, երբ խոսքը վերաբերում էր արդյունաբերական մասշտաբային տվյալների բազաներին: Այո, կային IBM DBXNUMX, Sybase ու էլի մի քանի շտեմարաններ, որոնք գալիս-գնում էին, բայց ընդհանուր առմամբ Oracle-ի ֆոնին այդքան էլ նկատելի չէին։ Համապատասխանաբար, այն ժամանակների ինժեներների հմտությունները ինչ-որ կերպ կապված էին գոյություն ունեցող միակ ընտրության հետ։

Oracle DBA-ն պետք է կարողանար.

  • տեղադրել Oracle Server-ը բաշխման հավաքածուից;
  • կարգավորել Oracle սերվերը.

  • init.ora;
  • ունկնդիր.ora;

- ստեղծել:

  • սեղանի տարածքներ;
  • սխեման;
  • օգտվողներ;

- կրկնօրինակում և վերականգնում;
- իրականացնել մոնիտորինգ;
— զբաղվել ոչ օպտիմալ պահանջներով:

Միևնույն ժամանակ, Oracle DBA-ից հատուկ պահանջ չկար.

  • կարողանալ ընտրել օպտիմալ DBMS կամ տվյալների պահպանման և մշակման այլ տեխնոլոգիա.
  • ապահովել բարձր հասանելիություն և հորիզոնական մասշտաբայնություն (սա միշտ չէ, որ եղել է DBA-ի խնդիր);
  • առարկայի, ենթակառուցվածքների, կիրառական ճարտարապետության, ՕՀ-ի լավ իմացություն;
  • բեռնել և բեռնաթափել տվյալները, տեղափոխել տվյալները տարբեր DBMS-ների միջև:

Ընդհանրապես, եթե խոսենք այդ օրերի ընտրության մասին, ապա այն նման է 80-ականների վերջին խորհրդային խանութի ընտրությանը.

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Մեր ժամանակը

Այդ ժամանակից ի վեր, իհարկե, ծառերն աճել են, աշխարհը փոխվել է, և այն դարձել է այսպիսին.

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

DBMS շուկան նույնպես փոխվել է, ինչպես պարզ երևում է Gartner-ի վերջին զեկույցից.

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Եվ այստեղ պետք է նշել, որ ամպերը, որոնց ժողովրդականությունը գնալով աճում է, զբաղեցրել են իրենց տեղը։ Եթե ​​մենք կարդանք նույն Gartner զեկույցը, ապա կտեսնենք հետևյալ եզրակացությունները.

  1. Շատ հաճախորդներ դիմումները ամպ տեղափոխելու ճանապարհին են:
  2. Նոր տեխնոլոգիաները սկզբում հայտնվում են ամպի մեջ և փաստ չէ, որ դրանք երբևէ կտեղափոխվեն ոչ ամպային ենթակառուցվածք։
  3. Վճարովի գնագոյացման մոդելը սովորական է դարձել: Յուրաքանչյուր ոք ցանկանում է վճարել միայն այն, ինչ օգտագործում է, և դա նույնիսկ միտում չէ, այլ պարզապես փաստի հաստատում:

Հիմա ինչ?

Այսօր մենք բոլորս ամպի մեջ ենք: Իսկ մեզ մոտ ծագող հարցերը ընտրության հարցեր են։ Եվ դա հսկայական է, նույնիսկ եթե խոսենք միայն On-premises ձևաչափով DBMS տեխնոլոգիաների ընտրության մասին: Մենք նաև ունենք կառավարվող ծառայություններ և SaaS: Այսպիսով, ընտրությունը տարեցտարի միայն ավելի է դժվարանում։

Ընտրության հարցերի հետ մեկտեղ կան նաև սահմանափակող գործոններ:

  • գին. Շատ տեխնոլոգիաներ դեռևս փող են պահանջում.
  • հմտություններ. Եթե ​​մենք խոսում ենք ազատ ծրագրաշարի մասին, ապա առաջանում է հմտությունների հարցը, քանի որ ազատ ծրագրակազմը պահանջում է բավարար կոմպետենտություն այն մարդկանցից, ովքեր այն տեղակայում և շահագործում են.
  • ֆունկցիոնալ. Ոչ բոլոր ծառայությունները, որոնք հասանելի են ամպում և կառուցված, ասենք, նույնիսկ նույն Postgres-ում, ունեն նույն հնարավորությունները, ինչ Postgres On-premises-ը: Սա էական գործոն է, որը պետք է իմանալ և հասկանալ: Ավելին, այս գործոնը դառնում է ավելի կարևոր, քան մեկ DBMS-ի որոշ թաքնված հնարավորությունների իմացությունը:

Ինչ է սպասվում DA/DE-ից հիմա.

  • թեմայի և կիրառական ճարտարապետության լավ իմացություն;
  • համապատասխան DBMS տեխնոլոգիան ճիշտ ընտրելու ունակություն՝ հաշվի առնելով առաջադրանքը.
  • առկա սահմանափակումների համատեքստում ընտրված տեխնոլոգիայի իրականացման օպտիմալ մեթոդ ընտրելու ունակություն.
  • տվյալների փոխանցման և տեղափոխման ունակություն;
  • ընտրված լուծումներ իրականացնելու և գործարկելու ունակություն:

Ստորև բերված օրինակը հիմնված GCP-ի վրա ցույց է տալիս, թե ինչպես է աշխատում տվյալների հետ աշխատելու այս կամ այն ​​տեխնոլոգիայի ընտրությունը՝ կախված դրա կառուցվածքից.

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Խնդրում ենք նկատի ունենալ, որ PostgreSQL-ը ներառված չէ սխեմայի մեջ, և դա պայմանավորված է նրանով, որ այն թաքնված է տերմինաբանության տակ: Ամպային SQL. Եվ երբ հասնենք Cloud SQL-ին, մենք պետք է նորից ընտրություն կատարենք.

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Պետք է նշել, որ այս ընտրությունը միշտ չէ, որ պարզ է, ուստի հավելվածների մշակողները հաճախ առաջնորդվում են ինտուիցիայով:

Ընդամենը:

  1. Որքան առաջ եք գնում, այնքան ավելի հրատապ է դառնում ընտրության հարցը: Եվ նույնիսկ եթե նայեք միայն GCP-ին, կառավարվող ծառայություններին և SaaS-ին, ապա RDBMS-ի որոշ հիշատակումներ հայտնվում են միայն 4-րդ քայլում (և այնտեղ Spanner-ը մոտ է): Գումարած, 5-րդ քայլում հայտնվում է PostgreSQL-ի ընտրությունը, իսկ կողքին կան նաև MySQL և SQL Server, այսինքն. ամեն ինչ շատ է, բայց պետք է ընտրել.
  2. Չպետք է մոռանալ գայթակղությունների ֆոնին սահմանափակումների մասին։ Հիմնականում բոլորն ուզում են Բանալ, բայց դա թանկ է: Արդյունքում, տիպիկ հարցումը նման է հետևյալին. «Խնդրում եմ, մեզ դարձրեք «Բաց», բայց Cloud SQL-ի գնով դուք պրոֆեսիոնալներ եք»:

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Ինչ պետք է անեմ?

Չպնդելով, որ դա վերջնական ճշմարտություն է, ասենք հետևյալը.

Մենք պետք է փոխենք սովորելու մեր մոտեցումը.

  • իմաստ չունի ուսուցանել, ինչպես նախկինում ուսուցանվում էին DBA-ները.
  • մեկ ապրանքի իմացությունն այլևս բավարար չէ.
  • բայց մեկի մակարդակով տասնյակ իմանալն անհնար է։

Դուք պետք է իմանաք ոչ միայն և ոչ թե որքան է ապրանքը, այլև.

  • օգտագործման դեպք դրա կիրառման;
  • տեղակայման տարբեր մեթոդներ;
  • յուրաքանչյուր մեթոդի առավելություններն ու թերությունները;
  • նմանատիպ և այլընտրանքային ապրանքներ՝ տեղեկացված և օպտիմալ ընտրություն կատարելու համար և ոչ միշտ՝ հօգուտ ծանոթ արտադրանքի:

Դուք նաև պետք է կարողանաք տեղափոխել տվյալները և հասկանալ ETL-ի հետ ինտեգրման հիմնական սկզբունքները:

իրական դեպք

Ոչ վաղ անցյալում անհրաժեշտ էր բջջային հավելվածի համար ստեղծել backend: Մինչ դրա վրա աշխատանքները սկսվեցին, հետնախագիծն արդեն մշակված էր և պատրաստ էր իրականացման, և մշակող թիմը մոտ երկու տարի ծախսեց այս նախագծի վրա: Սահմանվել են հետևյալ խնդիրները.

  • կառուցել CI/CD;
  • վերանայել ճարտարապետությունը;
  • գործի դնել այդ ամենը:

Հավելվածն ինքնին միկրոծառայություններ էր, իսկ Python/Django կոդը մշակվել էր զրոյից և անմիջապես GCP-ում։ Ինչ վերաբերում է թիրախային լսարանին, ապա ենթադրվում էր, որ լինելու է երկու տարածաշրջան՝ ԱՄՆ և ԵՄ, իսկ երթևեկությունը բաշխվել է Global Load հավասարակշռողի միջոցով։ Բոլոր աշխատանքային բեռները և հաշվարկային ծանրաբեռնվածությունը գործարկվել են Google Kubernetes Engine-ում:

Ինչ վերաբերում է տվյալներին, ապա կար 3 կառուցվածք.

  • Ամպային պահեստավորում;
  • Տվյալների պահեստ;
  • Cloud SQL (PostgreSQL):

Ինչպես գոյատևել SQL տվյալների բազան 21-րդ դարում՝ ամպեր, Kubernetes և PostgreSQL multimaster

Կարելի է մտածել, թե ինչու է ընտրվել Cloud SQL-ը: Ճիշտն ասած, նման հարցը վերջին տարիներին ինչ-որ անհարմար դադար է առաջացրել. զգացողություն կա, որ մարդիկ ամաչել են հարաբերական տվյալների բազաներից, բայց, այնուամենայնիվ, շարունակում են ակտիվորեն օգտագործել դրանք ;-):

Ինչ վերաբերում է մեր գործին, Cloud SQL-ն ընտրվել է հետևյալ պատճառներով.

  1. Ինչպես նշվեց, հավելվածը մշակվել է Django-ի միջոցով, և այն ունի SQL տվյալների բազայից Python-ի օբյեկտներին (Django ORM) մշտական ​​տվյալները քարտեզագրելու մոդել:
  2. Շրջանակն ինքնին աջակցում էր DBMS-ների բավականին վերջավոր ցանկին.

  • PostgreSQL;
  • MariaDB;
  • MySQL
  • պատգամներ;
  • SQLite.

Համապատասխանաբար, PostgreSQL-ն ընտրվել է այս ցուցակից բավականին ինտուիտիվ (դե, դա Oracle-ի ընտրություն չէ, իսկապես):

Ինչ էր պակասում.

  • հավելվածը տեղակայվել է միայն 2 տարածաշրջանում, իսկ 3-րդը հայտնվել է պլաններում (Ասիա);
  • Տվյալների բազան գտնվում էր Հյուսիսային Ամերիկայի տարածաշրջանում (Այովա);
  • հաճախորդի կողմից մտահոգություններ կային հնարավորի վերաբերյալ մուտքի ուշացումներ Եվրոպայից և Ասիայից և ընդհատումներ ծառայության մեջ DBMS-ի պարապուրդի դեպքում.

Չնայած այն հանգամանքին, որ Django-ն ինքը կարող է զուգահեռ աշխատել մի քանի տվյալների շտեմարանների հետ և դրանք բաժանել կարդալու և գրելու, հավելվածում այդքան էլ գրել չկար (90%-ից ավելին կարդում է): Եվ ընդհանրապես, և ընդհանրապես, եթե հնարավոր լիներ անել Եվրոպայի և Ասիայի հիմնական բազայի ընթերցանության կրկնօրինակը, սա կլինի փոխզիջումային լուծում։ Դե, ի՞նչ կա դրա մեջ այդքան բարդ:

Դժվարությունն այն էր, որ հաճախորդը չէր ցանկանում հրաժարվել կառավարվող ծառայություններից և Cloud SQL-ից: Իսկ Cloud SQL-ի հնարավորությունները ներկայումս սահմանափակ են։ Cloud SQL-ն աջակցում է High Availability (HA) և Read Replica (RR), բայց նույն RR-ն աջակցվում է միայն մեկ տարածաշրջանում: Ամերիկյան տարածաշրջանում ստեղծելով տվյալների բազա՝ դուք չեք կարող կարդալ եվրոպական տարածաշրջանում՝ օգտագործելով Cloud SQL, թեև Postgres-ը ձեզ չի խանգարում դա անել: Google-ի աշխատակիցների հետ նամակագրությունը ոչ մի տեղ չտանեց և ավարտվեց խոստումներով՝ «մենք գիտենք խնդիրը և աշխատում ենք դրա վրա, մի օր հարցը կլուծվի»։

Եթե ​​համառոտ թվարկենք Cloud SQL-ի հնարավորությունները, ապա այն կունենա հետևյալ տեսքը.

1. Բարձր հասանելիություն (HA):

  • մեկ տարածաշրջանում;
  • սկավառակի կրկնօրինակման միջոցով;
  • PostgreSQL շարժիչները չեն օգտագործվում.
  • հնարավոր է ավտոմատ և ձեռքով կառավարում - failover/failback;
  • Միացնելիս DBMS-ն անհասանելի է մի քանի րոպեով:

2. Կարդացեք կրկնօրինակը (RR):

  • մեկ տարածաշրջանում;
  • տաք սպասման ռեժիմ;
  • PostgreSQL հոսքային կրկնօրինակում:

Բացի այդ, ինչպես ընդունված է, տեխնոլոգիա ընտրելիս միշտ բախվում ես որոշների հետ սահմանափակումներ:

  • հաճախորդը չէր ցանկանում ստեղծել իրեր և օգտագործել IaaS, բացառությամբ GKE-ի միջոցով.
  • հաճախորդը չի ցանկանում տեղադրել ինքնասպասարկում PostgreSQL/MySQL;
  • Դե, ընդհանուր առմամբ, Google Spanner-ը բավականին հարմար կլիներ, եթե չլիներ իր գնի համար, այնուամենայնիվ, Django ORM-ը չի կարող աշխատել դրա հետ, բայց դա լավ բան է:

Հաշվի առնելով ստեղծված իրավիճակը՝ հաճախորդը ստացել է հետևյալ հարցը. «Կարո՞ղ եք նման բան անել, որպեսզի այն լինի Google Spanner-ի նման, բայց նաև աշխատի Django ORM-ի հետ»:

Լուծման տարբերակ թիվ 0

Առաջին բանը, որ մտքովս անցավ.

  • մնալ CloudSQL-ում;
  • տարածաշրջանների միջև որևէ ձևով ներկառուցված կրկնօրինակում չի լինի.
  • փորձեք կրկնօրինակ կցել գոյություն ունեցող Cloud SQL-ին PostgreSQL-ի կողմից;
  • գործարկեք PostgreSQL օրինակ ինչ-որ տեղ և ինչ-որ կերպ, բայց գոնե մի դիպչեք վարպետին:

Ավաղ, պարզվեց, որ դա հնարավոր չէ անել, քանի որ չկա մուտք դեպի հոսթ (դա բոլորովին այլ նախագծում է) - pg_hba և այլն, ինչպես նաև չկա մուտք դեպի գերօգտագործող:

Լուծման տարբերակ թիվ 1

Հետագա մտորումներից և նախորդ հանգամանքները հաշվի առնելով՝ մտքի գնացքը որոշ չափով փոխվեց.

  • Մենք դեռ փորձում ենք մնալ CloudSQL-ի ներսում, բայց անցնում ենք MySQL-ին, քանի որ Cloud SQL by MySQL-ն ունի արտաքին վարպետ, որը.

— արտաքին MySQL-ի վստահված անձ է;
- կարծես MySQL օրինակ;
- հորինված է այլ ամպերից կամ ներքին տարածքներից տվյալների տեղափոխման համար:

Քանի որ MySQL-ի կրկնօրինակման կարգավորումը չի պահանջում մուտք դեպի հոսթ, սկզբունքորեն ամեն ինչ աշխատում էր, բայց դա շատ անկայուն էր և անհարմար: Եվ երբ մենք ավելի հեռուն գնացինք, դա բոլորովին սարսափելի դարձավ, քանի որ մենք ամբողջ կառույցը տեղակայեցինք տերրաֆորմով, և հանկարծ պարզվեց, որ արտաքին վարպետը չի աջակցվում տերրաֆորմով: Այո, Google-ն ունի CLI, բայց ինչ-ինչ պատճառներով ամեն ինչ աշխատում էր այստեղ մեկ-մեկ. երբեմն այն ստեղծվում է, երբեմն այն չի ստեղծվում: Թերևս այն պատճառով, որ CLI-ն ստեղծվել է արտաքին տվյալների միգրացիայի, այլ ոչ թե կրկնօրինակների համար:

Փաստորեն, այս պահին պարզ դարձավ, որ Cloud SQL-ն ընդհանրապես հարմար չէ։ Ինչպես ասում են՝ ամեն ինչ արեցինք։

Լուծման տարբերակ թիվ 2

Քանի որ հնարավոր չէր մնալ Cloud SQL շրջանակում, մենք փորձեցինք ձևակերպել պահանջներ փոխզիջումային լուծման համար: Պարզվեց, որ պահանջները հետևյալն են.

  • աշխատել Kubernetes-ում, Kubernetes-ի (DCS, ...) և GCP-ի (LB, ...) ռեսուրսների և հնարավորությունների առավելագույն օգտագործում;
  • բալաստի բացակայություն ամպի մեջ ավելորդ բաների մի փունջից, ինչպիսին է HA վստահված անձը;
  • PostgreSQL կամ MySQL հիմնական HA տարածաշրջանում գործարկելու ունակություն; այլ շրջաններում - HA հիմնական տարածաշրջանի RR-ից գումարած դրա պատճենը (հուսալիության համար);
  • մուլտի վարպետ (ես չէի ուզում կապվել նրա հետ, բայց դա այնքան էլ կարևոր չէր)

.
Այս պահանջների արդյունքում պհարմար DBMS և պարտադիր տարբերակներ:

  • MySQL Galera;
  • CockroachDB;
  • PostgreSQL գործիքներ

:
- pgpool-II;
- Հովանավոր:

MySQL Galera

MySQL Galera տեխնոլոգիան մշակվել է Codership-ի կողմից և հանդիսանում է պլագին InnoDB-ի համար: Առանձնահատկություններ:

  • բազմաբնույթ վարպետ;
  • համաժամանակյա վերարտադրություն;
  • ցանկացած հանգույցից կարդալ;
  • ցանկացած հանգույցի ձայնագրում;
  • ներկառուցված HA մեխանիզմ;
  • Կա Helm աղյուսակը Bitnami-ից:

cockroachDB

Ըստ նկարագրության՝ բանը բացարձակ ռումբ է և բաց կոդով նախագիծ է՝ գրված Go-ում։ Հիմնական մասնակիցը Cockroach Labs-ն է (հիմնադրվել է Google-ի մարդկանց կողմից): Այս հարաբերական DBMS-ն ի սկզբանե նախագծված էր բաշխված լինելու (շրջանակից դուրս հորիզոնական մասշտաբով) և սխալ հանդուրժող լինելու համար: Ընկերությունից դրա հեղինակները ուրվագծեցին «SQL ֆունկցիոնալության հարստությունը NoSQL լուծումներին ծանոթ հորիզոնական հասանելիության հետ համատեղելու նպատակը»:

Գեղեցիկ բոնուս է աջակցությունը հետգրես կապի արձանագրությանը:

pgpool

Սա PostgreSQL-ի հավելումն է, իրականում նոր կառույց, որը վերցնում է բոլոր կապերը և մշակում դրանք: Այն ունի իր սեփական բեռի հավասարակշռիչը և վերլուծիչը՝ լիցենզավորված BSD լիցենզիայի ներքո: Այն տալիս է լայն հնարավորություններ, բայց որոշ չափով սարսափելի տեսք ունի, քանի որ նոր էության առկայությունը կարող է լրացուցիչ արկածների աղբյուր դառնալ:

Հովանավոր

Սա վերջին բանն է, որին աչքս ընկավ, և, ինչպես պարզվեց, ոչ իզուր։ Patroni-ն բաց կոդով կոմունալ ծրագիր է, որն ըստ էության Python-ի դեյմոն է, որը թույլ է տալիս ավտոմատ կերպով պահպանել PostgreSQL կլաստերները՝ տարբեր տեսակի վերարտադրմամբ և դերերի ավտոմատ փոխարկումով: Բանը շատ հետաքրքիր է ստացվել, քանի որ այն լավ է ինտեգրվում խորանարդի հետ և չի ներկայացնում որևէ նոր սուբյեկտ։

Ի վերջո ի՞նչ ընտրեցիք։

Ընտրությունը հեշտ չէր.

  1. cockroachDB - կրակ, բայց մութ;
  2. MySQL Galera - նույնպես վատ չէ, այն օգտագործվում է շատ տեղերում, բայց MySQL;
  3. pgpool — շատ անհարկի սուբյեկտներ, այսպես ասած, ինտեգրում ամպի և K8-ի հետ;
  4. Հովանավոր - գերազանց ինտեգրում K8-ի հետ, առանց ավելորդ սուբյեկտների, լավ ինտեգրվում է GCP LB-ին:

Այսպիսով, ընտրությունն ընկավ Պատրոնի վրա։

Արդյունքները

Ժամանակն է ամփոփել համառոտ. Այո, ՏՏ ենթակառուցվածքների աշխարհը զգալիորեն փոխվել է, և սա դեռ սկիզբն է։ Եվ եթե նախկինում ամպերը այլ տեսակի ենթակառուցվածք էին, ապա այժմ ամեն ինչ այլ է։ Ավելին, ամպերում նորամուծություններն անընդհատ հայտնվում են, դրանք կհայտնվեն և, հնարավոր է, կհայտնվեն միայն ամպերում և միայն այդ ժամանակ ստարտափների ջանքերով կտեղափոխվեն On-premises։

Ինչ վերաբերում է SQL-ին, ապա SQL-ն կապրի: Սա նշանակում է, որ դուք պետք է իմանաք PostgreSQL-ն և MySQL-ը և կարողանաք աշխատել դրանց հետ, բայց ավելի կարևոր է դրանք ճիշտ օգտագործել կարողանալը:

Source: www.habr.com

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