اسان جو تجربو etcd Kubernetes ڪلستر ۾ ڊيٽا سان ڪم ڪرڻ سڌو (K8s API کان سواء)

وڌ کان وڌ، ڪلائنٽ اسان کان پڇي رهيا آهن ڪبرنيٽس ڪلستر تائين رسائي فراهم ڪرڻ لاءِ ڪلسٽر اندر خدمتن تائين رسائي حاصل ڪرڻ لاءِ: ته جيئن اهي سڌو سنئون ڪجهه ڊيٽابيس يا خدمت سان ڳنڍي سگهن، مقامي ايپليڪيشن کي ڪلستر اندر ايپليڪيشنن سان ڳنڍڻ لاءِ...

اسان جو تجربو etcd Kubernetes ڪلستر ۾ ڊيٽا سان ڪم ڪرڻ سڌو (K8s API کان سواء)

مثال طور، توهان جي مقامي مشين مان هڪ خدمت سان ڳنڍڻ جي ضرورت آهي memcached.staging.svc.cluster.local. اسان هي صلاحيت مهيا ڪندا آهيون هڪ VPN استعمال ڪندي ڪلستر جي اندر جنهن سان ڪلائنٽ ڳنڍي ٿو. هن کي ڪرڻ لاءِ، اسان اعلان ڪريون ٿا سبنيٽس جي پوڊس، سروسز ۽ پش ڪلسٽر DNS ڪلائنٽ ڏانهن. اهڙيء طرح، جڏهن هڪ ڪلائنٽ خدمت سان ڳنڍڻ جي ڪوشش ڪري ٿو memcached.staging.svc.cluster.local, درخواست ڪلستر DNS ڏانهن وڃي ٿي ۽ جواب ۾ هن خدمت جو پتو ڪلستر سروس نيٽ ورڪ يا پوڊ ايڊريس کان وصول ڪري ٿو.

اسان kubeadm استعمال ڪندي K8s ڪلستر کي ترتيب ڏيو ٿا، جتي ڊفالٽ سروس سب نيٽ آهي 192.168.0.0/16، ۽ پوڊ جو نيٽ ورڪ آهي 10.244.0.0/16. عام طور تي هر شي چڱي طرح ڪم ڪري ٿو، پر اتي ڪجهه نقطا آهن:

  • سب نيٽ 192.168.*.* اڪثر ڪلائنٽ آفيس نيٽ ورڪن ۾ استعمال ٿيندا آهن، ۽ اڃا به وڌيڪ ڊولپر گهر نيٽ ورڪ ۾. ۽ پوءِ اسان تڪرارن سان ختم ڪريون ٿا: گهر جا روٽر هن سب نيٽ تي ڪم ڪن ٿا ۽ وي پي اين انهن سبنيٽس کي ڪلستر کان ڪلائنٽ ڏانهن ڌڪي ٿو.
  • اسان وٽ ڪيترائي ڪلستر آھن (پيداوار، اسٽيج ۽/يا ڪيترائي ديوي ڪلستر). پوءِ، ڊفالٽ طور، انهن سڀني وٽ پوڊس ۽ خدمتن لاءِ ساڳيا ذيلي نيٽ هوندا، جيڪي ڪيترن ئي ڪلسٽرن ۾ خدمتن سان گڏ ڪم ڪرڻ لاءِ وڏيون مشڪلاتون پيدا ڪن ٿا.

اسان گهڻو وقت اڳ هڪ ئي پروجيڪٽ ۾ خدمتن ۽ پوڊس لاءِ مختلف ذيلي نيٽ استعمال ڪرڻ جو رواج اختيار ڪيو آهي - عام طور تي، ته جيئن سڀني ڪلستر جا مختلف نيٽ ورڪ هجن. تنهن هوندي به، آپريشن ۾ ڪلسٽرن جو هڪ وڏو تعداد آهي، جنهن کي مان شروع کان رول اوور ڪرڻ نه چاهيندس، ڇاڪاڻ ته اهي ڪيتريون ئي خدمتون هلائيندا آهن، رياستي ايپليڪيشنون وغيره.

۽ پوءِ اسان پاڻ کان پڇيو: موجوده ڪلستر ۾ سبٽ ڪيئن بدلجي؟

فيصلن جي ڳولا

سڀ کان وڌيڪ عام رواج ٻيهر ٺاهڻ آهي سڀ ڪجهه ClusterIP قسم سان خدمتون. هڪ اختيار جي طور تي، صلاح ڪري سگهي ٿو ۽ هي:

ھيٺ ڏنل عمل ۾ ھڪڙو مسئلو آھي: سڀڪنھن شيء کي ترتيب ڏيڻ کان پوء، پوڊس /etc/resolv.conf ۾ DNS نالي سرور جي طور تي پراڻي IP سان گڏ ايندا آھن.
جيئن ته مون کي اڃا تائين حل نه مليو، مون کي پوري ڪلستر کي kubeadm ري سيٽ سان ري سيٽ ڪرڻو پيو ۽ ان کي ٻيهر شروع ڪيو.

پر اهو هر ڪنهن لاءِ مناسب ناهي... هتي اسان جي ڪيس لاءِ وڌيڪ تفصيلي تعارف آهن:

  • فلالين استعمال ڪيو ويندو آهي؛
  • بادلن ۾ ۽ هارڊويئر تي ڪلسٽر آهن.
  • مان ڪلستر ۾ سڀني خدمتن کي ٻيهر ترتيب ڏيڻ کان پاسو ڪرڻ چاهيندس؛
  • عام طور تي گهٽ ۾ گهٽ مسئلن سان سڀ ڪجهه ڪرڻ جي ضرورت آهي؛
  • Kubernetes نسخو 1.16.6 آهي (جڏهن ته، وڌيڪ قدم ٻين نسخن لاء ساڳيا هوندا)؛
  • بنيادي ڪم کي يقيني بڻائڻ آهي ته ڪلستر ۾ kubeadm استعمال ڪندي هڪ خدمت سبٽ سان 192.168.0.0/16، ان سان مٽايو 172.24.0.0/16.

۽ ائين ئي ٿيو آهي ته اسان گهڻو وقت کان اهو ڏسڻ ۾ دلچسپي وٺندا هئاسين ته ڪبرنيٽس وغيره ۾ ڇا ۽ ڪيئن ذخيرو ٿيل آهي، ان سان ڇا ڪري سگهجي ٿو... تنهنڪري اسان سوچيو: “ڇو نه صرف ڊيٽا کي تازه ڪاري ڪريو etcd ۾، پراڻن IP پتي (سب نيٽ) کي نئين سان تبديل ڪريو؟ »

وغيره ۾ ڊيٽا سان ڪم ڪرڻ لاءِ تيار ٿيل اوزارن جي ڳولا ڪرڻ تي، اسان کي ڪجھ به نه مليو جيڪو مسئلو مڪمل طور تي حل ڪري. (انهي سان، جيڪڏهن توهان ڄاڻو ٿا ڪنهن به استعمال جي باري ۾ ڊيٽا سان ڪم ڪرڻ لاء سڌو etcd ۾، اسان لنڪ جي تعريف ڪنداسين.) بهرحال، هڪ سٺو شروعاتي نقطو آهي وغيره مددگار OpenShift کان (ان جي ليکڪن جي مهرباني!).

هي افاديت سرٽيفڪيٽ استعمال ڪندي etcd سان ڳنڍي سگهي ٿي ۽ حڪمن کي استعمال ڪندي اتان کان ڊيٽا پڙهي سگهي ٿي ls, get, dump.

شامل ڪريو وغيره

ايندڙ سوچ منطقي آهي: "توهان کي هن افاديت کي شامل ڪرڻ کان ڇا روڪيو آهي وغيره وغيره ۾ ڊيٽا لکڻ جي صلاحيت شامل ڪندي؟"

اهو ٻن نون ڪمن سان etcdhelper جو تبديل ٿيل ورزن بڻجي ويو changeServiceCIDR и changePodCIDR. هن تي توهان ڪوڊ ڏسي سگهو ٿا هتي.

نيون خاصيتون ڇا ڪندا؟ الگورتھم changeServiceCIDR:

  • هڪ deserializer ٺاهيو؛
  • CIDR کي تبديل ڪرڻ لاءِ باقاعده اظهار گڏ ڪريو؛
  • اسان ڪلستر ۾ ClusterIP قسم سان سڀني خدمتن جي ذريعي وڃون ٿا:
    • اي ڊي ڊي مان قيمت کي ڊيڪوڊ ڪريو Go اعتراض ۾؛
    • باقاعده اظهار استعمال ڪندي اسان پتي جي پهرين ٻن بائيٽ کي تبديل ڪندا آهيون.
    • سروس کي تفويض ڪريو هڪ IP پتو نئين سب نيٽ کان؛
    • هڪ سيريلائزر ٺاهيو، گو اعتراض کي پروٽوبف ۾ تبديل ڪريو، نئين ڊيٽا وغيره کي لکو.

فعل changePodCIDR بنيادي طور تي هڪجهڙائي changeServiceCIDR - صرف خدمت جي وضاحت کي تبديل ڪرڻ جي بدران، اسان اهو ڪندا آهيون نوڊ ۽ تبديلي لاءِ .spec.PodCIDR هڪ نئين ذيلي نيٽ ورڪ ڏانهن.

مشق

تبديل ڪريو سروس CIDR

ڪم کي لاڳو ڪرڻ جو منصوبو تمام سادو آهي، پر ان ۾ ڪلستر ۾ سڀني پوڊس جي ٻيهر تخليق جي وقت ۾ وقت شامل آهي. مکيه مرحلن کي بيان ڪرڻ کان پوء، اسان پڻ خيالن جي حصيداري ڪنداسين ته ڪيئن، نظريي ۾، هن وقت جي گهٽتائي کي گهٽائي سگهجي ٿو.

تياري جا قدم:

  • ضروري سافٽ ويئر انسٽال ڪرڻ ۽ پيچ ٿيل وغيره هيلپر گڏ ڪرڻ؛
  • بيڪ اپ وغيره ۽ /etc/kubernetes.

سروس سي آءِ آر کي تبديل ڪرڻ لاءِ مختصر ايڪشن پلان:

  • apiserver ۽ ڪنٽرولر مينيجر کي تبديل ڪرڻ؛
  • سرٽيفڪيٽ جي ٻيهر جاري ڪرڻ؛
  • وغيره ۾ ClusterIP خدمتون تبديل ڪرڻ؛
  • ڪلستر ۾ سڀني پوڊس کي ٻيهر شروع ڪريو.

هيٺ ڏنل تفصيل سان عملن جو هڪ مڪمل سلسلو آهي.

1. انسٽال ڪريو etcd-client for data dump:

apt install etcd-client

2. تعمير وغيره مددگار:

  • گولنگ انسٽال ڪريو:
    GOPATH=/root/golang
    mkdir -p $GOPATH/local
    curl -sSL https://dl.google.com/go/go1.14.1.linux-amd64.tar.gz | tar -xzvC $GOPATH/local
    echo "export GOPATH="$GOPATH"" >> ~/.bashrc
    echo 'export GOROOT="$GOPATH/local/go"' >> ~/.bashrc
    echo 'export PATH="$PATH:$GOPATH/local/go/bin"' >> ~/.bashrc
  • اسان پاڻ لاء بچايو etcdhelper.go, ڊائون لوڊ انحصار، گڏ ڪريو:
    wget https://raw.githubusercontent.com/flant/examples/master/2020/04-etcdhelper/etcdhelper.go
    go get go.etcd.io/etcd/clientv3 k8s.io/kubectl/pkg/scheme k8s.io/apimachinery/pkg/runtime
    go build -o etcdhelper etcdhelper.go

3. بيڪ اپ وغيره ٺاهيو:

backup_dir=/root/backup
mkdir ${backup_dir}
cp -rL /etc/kubernetes ${backup_dir}
ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --key=/etc/kubernetes/pki/etcd/server.key --cert=/etc/kubernetes/pki/etcd/server.crt --endpoints https://192.168.199.100:2379 snapshot save ${backup_dir}/etcd.snapshot

4. Kubernetes ڪنٽرول جهاز جي ظاهر ۾ سروس سب نيٽ تبديل ڪريو. فائلن ۾ /etc/kubernetes/manifests/kube-apiserver.yaml и /etc/kubernetes/manifests/kube-controller-manager.yaml parameter کي تبديل ڪريو --service-cluster-ip-range نئين ذيلي نيٽ تي: 172.24.0.0/16 بدران بدران 192.168.0.0/16.

5. جيئن ته اسان سروس سب نيٽ تبديل ڪري رهيا آهيون جنهن تي kubeadm apiserver لاءِ سرٽيفڪيٽ جاري ڪري ٿو (بشمول)، انهن کي ٻيهر جاري ڪرڻ جي ضرورت آهي:

  1. اچو ته ڏسو ته ڪهڙن ڊومينز ۽ IP پتي لاءِ موجوده سرٽيفڪيٽ جاري ڪيو ويو آهي:
    openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt
    X509v3 Subject Alternative Name:
        DNS:dev-1-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:apiserver, IP Address:192.168.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
  2. اچو ته kubeadm لاءِ گهٽ ۾ گهٽ ترتيب تيار ڪريون:
    cat kubeadm-config.yaml
    apiVersion: kubeadm.k8s.io/v1beta1
    kind: ClusterConfiguration
    networking:
      podSubnet: "10.244.0.0/16"
      serviceSubnet: "172.24.0.0/16"
    apiServer:
      certSANs:
      - "192.168.199.100" # IP-адрес мастер узла
  3. اچو ته پراڻي crt ۽ key کي حذف ڪريون، ڇاڪاڻ ته ان کان سواء نئون سرٽيفڪيٽ جاري نه ڪيو ويندو:
    rm /etc/kubernetes/pki/apiserver.{key,crt}
  4. اچو ته API سرور لاءِ سرٽيفڪيٽ ٻيهر جاري ڪريون:
    kubeadm init phase certs apiserver --config=kubeadm-config.yaml
  5. اچو ته چيڪ ڪريون ته سرٽيفڪيٽ جاري ڪيو ويو نئين سب نيٽ لاءِ:
    openssl x509 -noout -ext subjectAltName </etc/kubernetes/pki/apiserver.crt
    X509v3 Subject Alternative Name:
        DNS:kube-2-master, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:172.24.0.1, IP Address:10.0.0.163, IP Address:192.168.199.100
  6. API سرور سرٽيفڪيٽ ٻيهر جاري ڪرڻ کان پوء، ان جي ڪنٽينر کي ٻيهر شروع ڪريو:
    docker ps | grep k8s_kube-apiserver | awk '{print $1}' | xargs docker restart
  7. اچو ته config regenerate for admin.conf:
    kubeadm alpha certs renew admin.conf
  8. اچو ته ڊيٽا کي ايڊٽ ڪريون etcd ۾:
    ./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-service-cidr 172.24.0.0/16 

    مهرباني ڪري ڏسو! هن وقت، ڊومين ريزوليوشن ڪلستر ۾ ڪم ڪرڻ بند ڪري ٿو، موجوده پوڊس ۾ /etc/resolv.conf پراڻي CoreDNS ايڊريس (kube-dns) رجسٽر ٿيل آهي، ۽ kube-proxy iptables ضابطن کي پراڻي سب نيٽ کان نئين ۾ تبديل ڪري ٿو. وڌيڪ مضمون ۾ اهو لکيل آهي ممڪن اختيارن جي باري ۾ گهٽ ۾ گهٽ وقت کي گھٽائڻ لاء.

  9. اچو ته درست ڪريون ConfigMap کي namespace ۾ kube-system:
    kubectl -n kube-system edit cm kubelet-config-1.16

    - هتي تبديل ڪريو clusterDNS kube-dns سروس جي نئين IP پتي تي: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    - اسان ان کي درست ڪنداسين data.ClusterConfiguration.networking.serviceSubnet هڪ نئين ذيلي نيٽ ورڪ ڏانهن.

  10. جيئن ته kube-dns ايڊريس تبديل ٿي چڪو آهي، اهو ضروري آهي ته kubelet config کي سڀني نوڊس تي تازه ڪاري ڪرڻ:
    kubeadm upgrade node phase kubelet-config && systemctl restart kubelet
  11. باقي اهو آهي ته ڪلستر ۾ سڀني پوڊس کي ٻيهر شروع ڪرڻ:
    kubectl get pods --no-headers=true --all-namespaces |sed -r 's/(S+)s+(S+).*/kubectl --namespace 1 delete pod 2/e'

گھٽ ۾ گھٽ وقت گھٽايو

دير جي وقت کي ڪيئن گھٽائڻ بابت سوچون:

  1. ڪنٽرول جهاز کي تبديل ڪرڻ کان پوء، هڪ نئين kube-dns سروس ٺاهيو، مثال طور، نالي سان kube-dns-tmp ۽ نئون پتو 172.24.0.10.
  2. ٺاهڻ if etcdhelper ۾، جيڪو kube-dns سروس کي تبديل نه ڪندو.
  3. ايڊريس کي سڀني ڪبيلن ۾ تبديل ڪريو ClusterDNS هڪ نئين تائين، جڏهن ته پراڻي سروس نئين سان گڏ ڪم ڪندي رهندي.
  4. انتظار ڪريو جيستائين ايپليڪيشنن سان گڏ پوڊ يا ته قدرتي سببن جي ڪري يا هڪ اتفاق ٿيل وقت تي پنهنجو پاڻ تي ڦري وڃن.
  5. خدمت کي ختم ڪريو kube-dns-tmp ۽ تبديلي serviceSubnetCIDR kube-dns سروس لاء.

هي منصوبو توهان کي اجازت ڏيندو ته گھٽ ۾ گھٽ وقت گھٽ ڪرڻ جو وقت ~ هڪ منٽ تائين - سروس ختم ڪرڻ جي مدت لاءِ kube-dns-tmp ۽ خدمت لاءِ ذيلي نيٽ تبديل ڪرڻ kube-dns.

ترميمي podNetwork

ساڳئي وقت، اسان اهو ڏسڻ جو فيصلو ڪيو ته ڪيئن بدلجي وڃي podNetwork جي نتيجي ۾ etcdhelper استعمال ڪندي. عملن جو سلسلو هن ريت آهي:

  • ۾ ٺاھ جوڙ کي درست ڪرڻ kube-system;
  • ڪيب-ڪنٽرولر-منيجر مينيفيسٽ کي درست ڪرڻ؛
  • تبديل ڪريو podCIDR سڌو etcd ۾؛
  • سڀني ڪلستر نوڊس کي ريبوٽ ڪريو.

هاڻي انهن عملن بابت وڌيڪ:

1. نالي جي جاءِ ۾ ConfigMap کي تبديل ڪريو kube-system:

kubectl -n kube-system edit cm kubeadm-config

- درست ڪرڻ data.ClusterConfiguration.networking.podSubnet هڪ نئين ذيلي نيٽ ورڪ ڏانهن 10.55.0.0/16.

kubectl -n kube-system edit cm kube-proxy

- درست ڪرڻ data.config.conf.clusterCIDR: 10.55.0.0/16.

2. سنڀاليندڙ مئنيجر مينيفيسٽ کي تبديل ڪريو:

vim /etc/kubernetes/manifests/kube-controller-manager.yaml

- درست ڪرڻ --cluster-cidr=10.55.0.0/16.

3. موجوده قدرن کي ڏسو .spec.podCIDR, .spec.podCIDRs, .InternalIP, .status.addresses سڀني ڪلستر نوڊس لاء:

kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'

[
  {
    "name": "kube-2-master",
    "podCIDR": "10.244.0.0/24",
    "podCIDRs": [
      "10.244.0.0/24"
    ],
    "InternalIP": "192.168.199.2"
  },
  {
    "name": "kube-2-master",
    "podCIDR": "10.244.0.0/24",
    "podCIDRs": [
      "10.244.0.0/24"
    ],
    "InternalIP": "10.0.1.239"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.244.1.0/24",
    "podCIDRs": [
      "10.244.1.0/24"
    ],
    "InternalIP": "192.168.199.222"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.244.1.0/24",
    "podCIDRs": [
      "10.244.1.0/24"
    ],
    "InternalIP": "10.0.4.73"
  }
]

4. تبديل ڪريو podCIDR سڌو سنئون etcd ۾ تبديليون ڪندي:

./etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt -cert /etc/kubernetes/pki/etcd/server.crt -key /etc/kubernetes/pki/etcd/server.key -endpoint https://127.0.0.1:2379 change-pod-cidr 10.55.0.0/16

5. اچو ته چيڪ ڪريون ته podCIDR واقعي تبديل ٿي چڪو آهي:

kubectl get no -o json | jq '[.items[] | {"name": .metadata.name, "podCIDR": .spec.podCIDR, "podCIDRs": .spec.podCIDRs, "InternalIP": (.status.addresses[] | select(.type == "InternalIP") | .address)}]'

[
  {
    "name": "kube-2-master",
    "podCIDR": "10.55.0.0/24",
    "podCIDRs": [
      "10.55.0.0/24"
    ],
    "InternalIP": "192.168.199.2"
  },
  {
    "name": "kube-2-master",
    "podCIDR": "10.55.0.0/24",
    "podCIDRs": [
      "10.55.0.0/24"
    ],
    "InternalIP": "10.0.1.239"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.55.1.0/24",
    "podCIDRs": [
      "10.55.1.0/24"
    ],
    "InternalIP": "192.168.199.222"
  },
  {
    "name": "kube-2-worker-01f438cf-579f9fd987-5l657",
    "podCIDR": "10.55.1.0/24",
    "podCIDRs": [
      "10.55.1.0/24"
    ],
    "InternalIP": "10.0.4.73"
  }
]

6. اچو ته هڪ هڪ ڪري سڀ ڪلسٽر نوڊس ريبوٽ ڪريون.

7. جيڪڏهن توهان گهٽ ۾ گهٽ هڪ نوڊ ڇڏيندا آهيو پراڻي podCIDR, پوء kube-controller-manager شروع ڪرڻ جي قابل نه هوندا، ۽ ڪلستر ۾ پوڊ شيڊول نه ٿي سگهندا.

حقيقت ۾، podCIDR تبديل ڪرڻ اڃا به آسان ٿي سگهي ٿو (مثال طور، پوء). پر اسان اهو سکڻ چاهيون ٿا ته سڌو سنئون etcd سان ڪيئن ڪم ڪجي، ڇاڪاڻ ته اهڙا ڪيس آهن جڏهن ڪبرنيٽس شيون وغيره کي ايڊٽ ڪري رهيا آهن. صرف ممڪن قسم. (مثال طور، توهان صرف سروس فيلڊ کي بغير وقت جي بغير تبديل نٿا ڪري سگهو spec.clusterIP.)

نتيجو

مضمون سڌو سنئون وغيره ۾ ڊيٽا سان ڪم ڪرڻ جي امڪان تي بحث ڪري ٿو، يعني. Kubernetes API کي پاس ڪرڻ. ڪڏهن ڪڏهن هي طريقو توهان کي "مشڪل شيون" ڪرڻ جي اجازت ڏئي ٿو. اسان حقيقي K8s ڪلستر تي متن ۾ ڏنل عملن کي آزمايو. تنهن هوندي به، وسيع استعمال لاء سندن تياري جي حيثيت آهي PoC (تصور جو ثبوت). تنهن ڪري، جيڪڏهن توهان پنهنجي ڪلستر تي etcdhelper افاديت جو تبديل ٿيل نسخو استعمال ڪرڻ چاهيو ٿا، ائين ڪريو پنهنجي خطري تي.

پي ايس

اسان جي بلاگ تي پڻ پڙهو:

جو ذريعو: www.habr.com

تبصرو شامل ڪريو