Kubernetes Pod وسائل تک کیسے رسائی حاصل کریں۔

Kubernetes Pod وسائل تک کیسے رسائی حاصل کریں۔توحید کی طرف سے انعام

Kubernetes کے ساتھ شروعات کرتے وقت، کنٹینر کے وسائل کو ترتیب دینے کے بارے میں بھول جانا عام بات ہے۔ اس مقام پر، یہ یقینی بنانے کے لیے کافی ہے کہ Docker امیج کام کرتا ہے اور اسے Kubernetes کلسٹر میں تعینات کیا جا سکتا ہے۔

لیکن بعد میں ایپلیکیشن کو دیگر ایپلی کیشنز کے ساتھ پروڈکشن کلسٹر میں تعینات کرنے کی ضرورت ہے۔ ایسا کرنے کے لیے، آپ کو کنٹینر کے لیے وسائل مختص کرنے کی ضرورت ہے اور اس بات کو یقینی بنانا ہوگا کہ ایپلیکیشن کو چلانے اور چلانے کے لیے ان میں سے کافی ہیں، اور یہ کہ دیگر چلنے والی ایپلیکیشنز کو مسائل کا سامنا نہیں کرنا پڑے گا۔

ٹیم Mail.ru سے Kubernetes aaS کنٹینر وسائل (CPU اور MEM)، درخواستوں اور وسائل کی حدود کے بارے میں ایک مضمون کا ترجمہ کیا۔ آپ ان ترتیبات کے فوائد جانیں گے اور اگر آپ انہیں سیٹ نہیں کرتے ہیں تو کیا ہوتا ہے۔

کمپیوٹنگ کے وسائل

ہمارے پاس درج ذیل اکائیوں کے ساتھ دو قسم کے وسائل ہیں:

  • سنٹرل پروسیسنگ یونٹ (سی پی یو) - کور؛
  • میموری (MEM) - بائٹس۔

وسائل ہر کنٹینر کے لیے مخصوص ہیں۔ درج ذیل Pod YAML فائل میں، آپ کو ایک ریسورس سیکشن نظر آئے گا جس میں مطلوبہ اور محدود وسائل شامل ہیں:

  • درخواست کردہ پوڈ وسائل = تمام کنٹینرز کے درخواست کردہ وسائل کا مجموعہ؛
  • Pod Resource Limit = تمام Pod Resource Limits کا مجموعہ۔

apiVersion: v1
kind: Pod
metadata:
  name: backend-pod-name
  labels:
    application: backend
spec:
  containers:
    — name: main-container
      image: my-backend
      tag: v1
      ports:
      — containerPort: 8080
      resources:
        requests:
          cpu: 0.2 # REQUESTED CPU: 200m cores
          memory: "1Gi" # REQUESTED MEM: 1Gi
        limits:
          cpu: 1 # MAX CPU USAGE: 1 core
          memory: "1Gi" # MAX MEM USAGE:  1Gi
    — name: other-container
      image: other-app
      tag: v1
      ports:
      — containerPort: 8000
      resources:
        requests:
          cpu: "200m" # REQUESTED CPU: 200m cores
          memory: "0.5Gi" # REQUESTED MEM: 0.5Gi
        limits:
          cpu: 1 # MAX CPU USAGE: 1 core
          memory: "1Gi" # MAX MEM USAGE:  1Gi

درخواست کردہ اور محدود وسائل کی مثال

فیلڈ resources.requested تفصیلات سے Pod ان عناصر میں سے ایک ہے جو مطلوبہ نوڈ کو تلاش کرنے کے لیے استعمال ہوتا ہے۔ آپ پہلے ہی اس کے لیے پوڈ کی تعیناتی کی منصوبہ بندی کر سکتے ہیں۔ آپ کو ایک مناسب نوڈ کیسے ملتا ہے؟

Kubernetes کئی اجزاء پر مشتمل ہوتا ہے، بشمول ایک ماسٹر نوڈ یا ماسٹر نوڈ (Kubernetes کنٹرول پلین)۔ ماسٹر نوڈ کے کئی عمل ہوتے ہیں: kube-apiserver، kube-controller-manager اور kube-scheduler۔

کیوب-شیڈیولر کا عمل نئے بنائے گئے پوڈز کا جائزہ لینے اور ممکنہ ورکر نوڈس کو تلاش کرنے کے لیے ذمہ دار ہے جو تمام پوڈ کی درخواستوں سے مماثل ہوں، بشمول درخواست کردہ وسائل کی تعداد۔ کیوب-شیڈیولر کے ذریعہ پائے جانے والے نوڈس کی فہرست کی درجہ بندی کی گئی ہے۔ پوڈ سب سے زیادہ سکور کے ساتھ نوڈ پر شیڈول کیا جاتا ہے.

Kubernetes Pod وسائل تک کیسے رسائی حاصل کریں۔جامنی رنگ کی پھلی کہاں رکھی جائے گی؟

تصویر میں آپ دیکھ سکتے ہیں کہ کیوب-شیڈیولر کو ایک نیا جامنی رنگ کا پوڈ شیڈول کرنا چاہیے۔ Kubernetes کلسٹر دو نوڈس پر مشتمل ہے: A اور B۔ جیسا کہ آپ دیکھ سکتے ہیں، کیوب-شیڈیولر نوڈ A پر پوڈ کو شیڈول نہیں کر سکتا - دستیاب (غیر درخواست کردہ) وسائل جامنی پوڈ کی درخواستوں سے میل نہیں کھاتے ہیں۔ لہذا، جامنی پوڈ کے ذریعہ درخواست کردہ 1 GB میموری نوڈ A پر فٹ نہیں ہوگی، کیونکہ دستیاب میموری 0,5 GB ہے۔ لیکن نوڈ بی کے پاس کافی وسائل ہیں۔ نتیجے کے طور پر، kube-scheduler فیصلہ کرتا ہے کہ جامنی رنگ کی Pod کی منزل نوڈ B ہے۔

اب ہم جانتے ہیں کہ درخواست کردہ وسائل پوڈ کو چلانے کے لیے نوڈ کے انتخاب کو کیسے متاثر کرتے ہیں۔ لیکن معمولی وسائل کا کیا اثر ہے؟

وسائل کی حد ایک حد ہے جسے CPU/MEM عبور نہیں کر سکتا۔ تاہم، سی پی یو کا وسیلہ لچکدار ہے، اس لیے وہ کنٹینرز جو اپنی سی پی یو کی حد تک پہنچ جاتے ہیں، پوڈ کے باہر نکلنے کا سبب نہیں بنیں گے۔ اس کے بجائے، CPU تھروٹلنگ شروع ہو جائے گی۔ اگر MEM استعمال کی حد تک پہنچ جاتی ہے، تو کنٹینر کو OOM-Killer کی وجہ سے روک دیا جائے گا اور اگر RestartPolicy سیٹنگ سے اجازت ہو تو اسے دوبارہ شروع کیا جائے گا۔

تفصیل سے درخواست کی گئی اور زیادہ سے زیادہ وسائل

Kubernetes Pod وسائل تک کیسے رسائی حاصل کریں۔Docker اور Kubernetes کے درمیان وسائل کی مواصلت

وسائل کی درخواستیں اور وسائل کی حدود کیسے کام کرتی ہیں اس کی وضاحت کرنے کا بہترین طریقہ Kubernetes اور Docker کے درمیان تعلق کو متعارف کرانا ہے۔ اوپر کی تصویر میں آپ دیکھ سکتے ہیں کہ کبرنیٹس فیلڈز اور ڈوکر اسٹارٹ اپ جھنڈوں کا آپس میں کیا تعلق ہے۔

یادداشت: درخواست اور حد

containers:
...
 resources:
   requests:
     memory: "0.5Gi"
   limits:
     memory: "1Gi"

جیسا کہ اوپر ذکر کیا گیا ہے، میموری بائٹس میں ماپا جاتا ہے. کی بنیاد پر Kubernetes دستاویزات، ہم میموری کو ایک نمبر کے طور پر بیان کرسکتے ہیں۔ عام طور پر یہ ایک عدد عدد ہوتا ہے، مثال کے طور پر 2678 - یعنی 2678 بائٹس۔ آپ لاحقے بھی استعمال کر سکتے ہیں۔ G и Gi، اہم بات یہ یاد رکھنا ہے کہ وہ برابر نہیں ہیں۔ پہلا اعشاریہ ہے اور دوسرا بائنری ہے۔ k8s دستاویزات میں مذکور مثال کی طرح: 128974848, 129e6, 129M, 123Mi - وہ عملی طور پر برابر ہیں۔

Kubernetes آپشن limits.memory پرچم سے میل کھاتا ہے۔ --memory ڈوکر سے کی صورت میں request.memory Docker کے لیے کوئی تیر نہیں ہے کیونکہ Docker اس فیلڈ کو استعمال نہیں کرتا ہے۔ آپ پوچھ سکتے ہیں، کیا یہ بھی ضروری ہے؟ ہاں ضرورت ہے۔ جیسا کہ میں نے پہلے کہا، میدان کبرنیٹس کے لیے اہمیت رکھتا ہے۔ اس سے حاصل کردہ معلومات کی بنیاد پر، کیوب-شیڈیولر فیصلہ کرتا ہے کہ کس نوڈ پر پوڈ کو شیڈول کرنا ہے۔

اگر آپ درخواست کے لیے ناکافی میموری سیٹ کرتے ہیں تو کیا ہوتا ہے؟

اگر کنٹینر درخواست کردہ میموری کی حد تک پہنچ گیا ہے، تو Pod کو Pods کے ایک گروپ میں رکھا جاتا ہے جو نوڈ میں کافی میموری نہ ہونے پر رک جاتا ہے۔

اگر آپ میموری کی حد بہت کم سیٹ کرتے ہیں تو کیا ہوتا ہے؟

اگر کنٹینر میموری کی حد سے زیادہ ہے، تو اسے OOM-Killed کی وجہ سے ختم کر دیا جائے گا۔ اور اگر ممکن ہو تو RestartPolicy کی بنیاد پر دوبارہ شروع کرے گا جہاں پہلے سے طے شدہ قدر ہے۔ Always.

اگر آپ درخواست کردہ میموری کی وضاحت نہیں کرتے ہیں تو کیا ہوگا؟

Kubernetes حد کی قدر لے گا اور اسے ڈیفالٹ ویلیو کے طور پر سیٹ کرے گا۔

اگر آپ میموری کی حد متعین نہیں کرتے ہیں تو کیا ہو سکتا ہے؟

کنٹینر پر کوئی پابندی نہیں ہے؛ یہ جتنی چاہے میموری استعمال کر سکتا ہے۔ اگر وہ نوڈ کی تمام دستیاب میموری استعمال کرنا شروع کر دیتا ہے، تو OOM اسے مار ڈالے گا۔ اگر ممکن ہو تو RestartPolicy کی بنیاد پر کنٹینر کو دوبارہ شروع کیا جائے گا۔

اگر آپ میموری کی حدود متعین نہیں کرتے ہیں تو کیا ہوگا؟

یہ بدترین صورت حال ہے: شیڈولر نہیں جانتا کہ کنٹینر کو کتنے وسائل درکار ہیں، اور یہ نوڈ پر سنگین مسائل پیدا کر سکتا ہے۔ اس صورت میں، نام کی جگہ (LimitRange کے ذریعہ مقرر کردہ) پر ڈیفالٹ حدود رکھنا اچھا ہوگا۔ کوئی طے شدہ حدود نہیں ہیں - Pod کی کوئی حد نہیں ہے، یہ جتنی چاہے میموری استعمال کر سکتا ہے۔

اگر درخواست کردہ میموری نوڈ کی پیشکش سے زیادہ ہے، تو Pod کو شیڈول نہیں کیا جائے گا۔ یہ یاد رکھنا ضروری ہے۔ Requests.memory - کم از کم قیمت نہیں۔ یہ کنٹینر کو مسلسل چلانے کے لیے کافی میموری کی مقدار کی تفصیل ہے۔

عام طور پر اس کے لیے ایک ہی قدر مقرر کرنے کی سفارش کی جاتی ہے۔ request.memory и limit.memory. یہ یقینی بناتا ہے کہ Kubernetes کسی نوڈ پر Pod کو شیڈول نہیں کرے گا جس میں Pod کو چلانے کے لیے کافی میموری ہے لیکن اسے چلانے کے لیے کافی نہیں۔ ذہن میں رکھیں: Kubernetes Pod منصوبہ بندی صرف اکاؤنٹ میں لیتا ہے requests.memoryاور limits.memory اکاؤنٹ میں نہیں لیتا ہے.

سی پی یو: درخواست اور حد

containers:
...
 resources:
   requests:
     cpu: 1
   limits:
     cpu: "1200m"

سی پی یو کے ساتھ سب کچھ تھوڑا زیادہ پیچیدہ ہے۔ Kubernetes اور Docker کے درمیان تعلقات کی تصویر پر واپس آکر، آپ اسے دیکھ سکتے ہیں۔ request.cpu سے مساوی --cpu-shares، جبکہ limit.cpu پرچم سے میل کھاتا ہے۔ cpus ڈوکر میں

Kubernetes جس CPU کی درخواست کرتا ہے اسے 1024 سے ضرب دیا جاتا ہے، CPU سائیکلوں کا تناسب۔ اگر آپ 1 مکمل کور کی درخواست کرنا چاہتے ہیں تو آپ کو شامل کرنا ہوگا۔ cpu: 1جیسا کہ اوپر دکھایا گیا ہے.

مکمل کرنل (تناسب = 1024) کی درخواست کرنے کا مطلب یہ نہیں ہے کہ آپ کا کنٹینر اسے وصول کرے گا۔ اگر آپ کی میزبان مشین میں صرف ایک کور ہے اور آپ ایک سے زیادہ کنٹینر چلا رہے ہیں، تو تمام کنٹینرز کو ان کے درمیان دستیاب CPU کا اشتراک کرنا چاہیے۔ یہ کیسے ہوتا ہے؟ آئیے تصویر کو دیکھتے ہیں۔

Kubernetes Pod وسائل تک کیسے رسائی حاصل کریں۔
سی پی یو کی درخواست - سنگل کور سسٹم

آئیے تصور کریں کہ آپ کے پاس کنٹینرز چلانے والا سنگل کور ہوسٹ سسٹم ہے۔ ماں (Kubernetes) نے ایک پائی (CPU) پکائی اور اسے بچوں (کنٹینرز) کے درمیان تقسیم کرنا چاہتی ہے۔ تین بچے پوری پائی چاہتے ہیں (تناسب = 1024)، دوسرے بچے کو آدھی پائی (512) چاہیے۔ ماں منصفانہ بننا چاہتی ہے اور ایک سادہ حساب لگاتی ہے۔

# Сколько пирогов хотят дети?
# 3 ребенка хотят по целому пирогу и еще один хочет половину пирога
cakesNumberKidsWant = (3 * 1) + (1 * 0.5) = 3.5
# Выражение получается так:
3 (ребенка/контейнера) * 1 (целый пирог/полное ядро) + 1 (ребенок/контейнер) * 0.5 (половина пирога/половина ядра)
# Сколько пирогов испечено?
availableCakesNumber = 1
# Сколько пирога (максимально) дети реально могут получить?
newMaxRequest = 1 / 3.5 =~ 28%

حساب کی بنیاد پر، تین بچوں کو کور کا 28% ملے گا، پورے کور کو نہیں۔ چوتھے بچے کو مکمل دانا کا 14% ملے گا، آدھا نہیں۔ لیکن اگر آپ کے پاس ملٹی کور سسٹم ہے تو چیزیں مختلف ہوں گی۔

Kubernetes Pod وسائل تک کیسے رسائی حاصل کریں۔
سی پی یو کی درخواست - ملٹی کور (4) سسٹم

اوپر کی تصویر میں آپ دیکھ سکتے ہیں کہ تین بچے پوری پائی چاہتے ہیں، اور ایک آدھا چاہتا ہے۔ چونکہ ماں نے چار پائی پکائی ہیں، اس کے ہر بچے کو جتنے چاہیں ملیں گے۔ ملٹی کور سسٹم میں، پروسیسر کے وسائل تمام دستیاب پروسیسر کوروں میں تقسیم کیے جاتے ہیں۔ اگر ایک کنٹینر ایک مکمل CPU کور سے کم تک محدود ہے، تو پھر بھی اسے 100% پر استعمال کر سکتا ہے۔

مندرجہ بالا حسابات کو یہ سمجھنے کے لیے آسان بنایا گیا ہے کہ CPU کو کنٹینرز میں کیسے تقسیم کیا جاتا ہے۔ بلاشبہ، خود کنٹینرز کے علاوہ، دیگر عمل بھی ہیں جو CPU وسائل کو بھی استعمال کرتے ہیں۔ جب ایک کنٹینر میں عمل غیر فعال ہوتے ہیں، تو دوسرے اس کے وسائل کو استعمال کرسکتے ہیں۔ CPU: "200m" سے مساوی CPU: 0,2جس کا مطلب ہے ایک کور کا تقریباً 20%۔

اب بات کرتے ہیں۔ limit.cpu. Kubernetes کی حد کو 100 سے ضرب دیا جاتا ہے۔ نتیجہ یہ ہے کہ کنٹینر ہر 100 µs (cpu-period).

limit.cpu ڈاکر پرچم سے میل کھاتا ہے۔ --cpus. یہ پرانے کا نیا مجموعہ ہے۔ --cpu-period и --cpu-quota. اسے ترتیب دے کر، ہم اس بات کی نشاندہی کرتے ہیں کہ کنٹینر تھروٹلنگ شروع ہونے سے پہلے کتنے دستیاب CPU وسائل کو زیادہ سے زیادہ استعمال کر سکتا ہے:

  • cpus - مجموعہ cpu-period и cpu-quota. cpus = 1.5 ترتیب کے برابر cpu-period = 100000 и cpu-quota = 150000;
  • سی پی یو مدت --.مدت n CPU CFS شیڈولر, پہلے سے طے شدہ 100 مائیکرو سیکنڈز؛
  • سی پی یو کوٹہ - اندر مائیکرو سیکنڈز کی تعداد cpu-period، جو کنٹینر سے جڑا ہوا ہے۔

اگر آپ ناکافی درخواست کردہ CPU انسٹال کرتے ہیں تو کیا ہوتا ہے؟

اگر کنٹینر کو انسٹال ہونے سے زیادہ کی ضرورت ہے، تو یہ دوسرے پروسیس سے CPU چوری کر لے گا۔

اگر آپ CPU کی حد بہت کم سیٹ کرتے ہیں تو کیا ہوتا ہے؟

چونکہ CPU وسیلہ سایڈست ہے، اس لیے تھروٹلنگ آن ہو جائے گی۔

اگر آپ CPU کی درخواست کی وضاحت نہیں کرتے ہیں تو کیا ہوگا؟

میموری کی طرح، درخواست کی قیمت حد کے برابر ہے۔

اگر آپ CPU کی حد متعین نہیں کرتے ہیں تو کیا ہوگا؟

کنٹینر اتنا سی پی یو استعمال کرے گا جتنا اسے ضرورت ہے۔ اگر نام کی جگہ میں ڈیفالٹ CPU پالیسی (LimitRange) کی وضاحت کی گئی ہے، تو یہ حد کنٹینر کے لیے بھی استعمال ہوتی ہے۔

اگر آپ درخواست یا CPU کی حد متعین نہیں کرتے ہیں تو کیا ہوگا؟

جیسا کہ میموری کے ساتھ، یہ بدترین صورت حال ہے۔ شیڈولر نہیں جانتا کہ آپ کے کنٹینر کو کتنے وسائل کی ضرورت ہے، اور یہ نوڈ پر سنگین مسائل کا سبب بن سکتا ہے۔ اس سے بچنے کے لیے، آپ کو نام کی جگہوں (LimitRange) کے لیے پہلے سے طے شدہ حدود مقرر کرنے کی ضرورت ہے۔

یاد رکھیں: اگر آپ نوڈس سے زیادہ CPU کی درخواست کرتے ہیں، تو Pod کو شیڈول نہیں کیا جائے گا۔ Requests.cpu - کم از کم قیمت نہیں، بلکہ ایک قدر جو Pod کو شروع کرنے اور ناکامیوں کے بغیر کام کرنے کے لیے کافی ہے۔ اگر ایپلیکیشن پیچیدہ حساب کتاب نہیں کرتی ہے، تو بہترین آپشن انسٹال کرنا ہے۔ request.cpu <= 1 اور ضرورت کے مطابق زیادہ سے زیادہ نقلیں لانچ کریں۔

مطلوبہ وسائل یا وسائل کی حد کی مثالی مقدار

ہم نے کمپیوٹنگ وسائل کی محدودیت کے بارے میں سیکھا۔ اب اس سوال کا جواب دینے کا وقت آگیا ہے: "میرے پوڈ کو بغیر کسی پریشانی کے ایپلیکیشن چلانے کے لیے کتنے وسائل درکار ہیں؟ مثالی رقم کیا ہے؟

بدقسمتی سے، ان سوالات کے کوئی واضح جواب نہیں ہیں۔ اگر آپ نہیں جانتے کہ آپ کی ایپلی کیشن کیسے کام کرتی ہے یا اسے کتنی سی پی یو یا میموری کی ضرورت ہے، تو بہترین آپشن یہ ہے کہ ایپلیکیشن کو بہت زیادہ میموری اور سی پی یو دیا جائے اور پھر کارکردگی کے ٹیسٹ چلائیں۔

کارکردگی کے ٹیسٹ کے علاوہ، ایک ہفتے تک مانیٹرنگ میں ایپلیکیشن کے رویے کی نگرانی کریں۔ اگر گراف اس بات کی نشاندہی کرتے ہیں کہ آپ کی درخواست آپ کی درخواست سے کم وسائل استعمال کر رہی ہے، تو آپ درخواست کردہ CPU یا میموری کی مقدار کو کم کر سکتے ہیں۔

مثال کے طور پر یہ دیکھیں گرافانا ڈیش بورڈ. یہ درخواست کردہ وسائل یا وسائل کی حد اور موجودہ وسائل کے استعمال کے درمیان فرق دکھاتا ہے۔

حاصل يہ ہوا

وسائل کی درخواست اور محدود کرنے سے آپ کے Kubernetes کلسٹر کو صحت مند رکھنے میں مدد ملتی ہے۔ مناسب حد کی ترتیب لاگت کو کم کرتی ہے اور ایپلیکیشنز کو ہر وقت چلتی رہتی ہے۔

مختصر میں، ذہن میں رکھنے کے لئے چند چیزیں ہیں:

  1. درخواست کردہ وسائل ایک کنفیگریشن ہے جو شروع کے وقت (جب Kubernetes ایپلیکیشن کی میزبانی کرنے کا ارادہ رکھتی ہے) پر غور کیا جاتا ہے۔ اس کے برعکس، رن ٹائم پر وسائل کو محدود کرنا اہم ہے- جب ایپلیکیشن پہلے سے ہی نوڈ پر چل رہی ہو۔
  2. میموری کے مقابلے میں، CPU ایک ریگولیٹڈ وسیلہ ہے۔ اگر کافی CPU نہیں ہے تو، آپ کا Pod بند نہیں ہوگا اور تھروٹلنگ میکانزم آن ہو جائے گا۔
  3. درخواست کردہ وسائل اور وسائل کی حد کم از کم اور زیادہ سے زیادہ اقدار نہیں ہیں! درخواست کردہ وسائل کی وضاحت کرکے، آپ اس بات کو یقینی بناتے ہیں کہ ایپلیکیشن بغیر کسی پریشانی کے چلے گی۔
  4. میموری کی درخواست کو میموری کی حد کے برابر سیٹ کرنا ایک اچھا عمل ہے۔
  5. ٹھیک ہے انسٹال کرنے کی درخواست کی گئی۔ CPU <=1، اگر ایپلیکیشن پیچیدہ حسابات انجام نہیں دیتی ہے۔
  6. اگر آپ نوڈ پر دستیاب وسائل سے زیادہ وسائل کی درخواست کرتے ہیں، تو Pod کو اس نوڈ پر کبھی بھی شیڈول نہیں کیا جائے گا۔
  7. مطلوبہ وسائل/وسائل کی حدوں کی صحیح مقدار کا تعین کرنے کے لیے، لوڈ ٹیسٹنگ اور مانیٹرنگ کا استعمال کریں۔

مجھے امید ہے کہ یہ مضمون آپ کو وسائل کی حد کے بنیادی تصور کو سمجھنے میں مدد کرے گا۔ اور آپ اس علم کو اپنے کام میں لاگو کر سکیں گے۔

گڈ لک!

اور کیا پڑھنا ہے:

  1. SRE مشاہدہ: نام کی جگہیں اور میٹرک ڈھانچہ.
  2. Kubernetes کے لیے 90+ مفید ٹولز: تعیناتی، انتظام، نگرانی، حفاظت اور مزید.
  3. ٹیلیگرام میں ہمارا چینل Around Kubernetes.

ماخذ: www.habr.com

نیا تبصرہ شامل کریں