خبرتنا في العمل مع البيانات في etcd Kubernetes-الكتلة مباشرة (بدون واجهة برمجة تطبيقات K8s)

في كثير من الأحيان ، يلجأ العملاء إلينا بطلب لتوفير الوصول إلى مجموعة Kubernetes لإمكانية الوصول إلى الخدمات داخل المجموعة: لكي تتمكن من الاتصال مباشرة ببعض قواعد البيانات أو الخدمات ، لربط التطبيق المحلي بالتطبيقات داخل المجموعة ...



على سبيل المثال ، هناك حاجة للاتصال من جهازك المحلي إلى الخدمة memcached.staging.svc.cluster.local. نحن نقدم هذه الفرصة مع VPN داخل الكتلة التي يتصل بها العميل. للقيام بذلك ، نعلن عن الشبكات الفرعية للقرون ، والخدمات ، وندفع DNS الكتلة إلى العميل. وبالتالي ، عندما يحاول العميل الاتصال بالخدمة memcached.staging.svc.cluster.local، ينتقل الطلب إلى DNS الخاص بالمجموعة ويتلقى استجابة لهذه الخدمة من شبكة خدمة المجموعة أو عنوان pod.

نقوم بتكوين مجموعات K8s باستخدام kubeadm ، حيث تكون الشبكة الفرعية للخدمة بشكل افتراضي 192.168.0.0/16وتكون شبكة pod 10.244.0.0/16. عادة كل شيء يعمل بشكل جيد ، ولكن هناك نقطتان:

  • 192.168.*.*غالبًا ما يتم استخدام الشبكة الفرعية في شبكات المكاتب للعملاء ، بل وفي كثير من الأحيان في الشبكات المنزلية للمطورين. ثم نحصل على صراعات: تعمل أجهزة التوجيه المنزلية على هذه الشبكة الفرعية وتقوم شبكة VPN بدفع هذه الشبكات الفرعية من الكتلة إلى العميل.
  • لدينا عدة مجموعات (الإنتاج ، المرحلة ، و / أو عدة مجموعات ديف). ثم في كل منها بشكل افتراضي ، ستكون هناك نفس الشبكات الفرعية للقرون والخدمات ، مما يخلق صعوبات كبيرة للعمل مع الخدمات في عدة مجموعات في وقت واحد.

لبعض الوقت الآن اعتمدنا ممارسة استخدام شبكات فرعية مختلفة للخدمات والجراب في إطار مشروع واحد - بشكل عام ، بحيث تكون جميع المجموعات مع شبكات مختلفة. ومع ذلك ، هناك عدد كبير من المجموعات قيد التشغيل لا أرغب في دحرجتها من الصفر ، لأنها تقوم بتشغيل العديد من الخدمات والتطبيقات ذات الحالة ، وما إلى ذلك.

ثم سألنا أنفسنا: كيف يمكنني تغيير الشبكة الفرعية في كتلة موجودة؟

البحث في القرارات


الممارسة الأكثر شيوعًا هي إعادة إنشاء جميع الخدمات باستخدام نوع ClusterIP. بدلاً من ذلك ، يمكنهم أيضًا نصحهم بما يلي:

تواجه العملية التالية مشكلة: بعد تكوين كل شيء ، تأتي الحواشي مع IP القديم كخادم أسماء DNS في /etc/resolv.conf.
نظرًا لأنني ما زلت لم أجد الحل ، اضطررت إلى إعادة تعيين المجموعة بالكامل باستخدام إعادة تعيين kubeadm وإعادة تشغيله مرة أخرى.

ولكن هذا لا يناسب الجميع ... فيما يلي ملاحظات تمهيدية أكثر تفصيلاً لقضيتنا:

  • المستخدمة من قبل الفانيلا.
  • هناك عناقيد في السحب وفي الحديد ؛
  • أود تجنب النشر المتكرر لجميع الخدمات في المجموعة ؛
  • هناك حاجة للقيام بكل شيء بحد أدنى من المشاكل ؛
  • إصدار Kubernetes هو 1.16.6 (ومع ذلك ، ستكون إجراءات أخرى مماثلة للإصدارات الأخرى) ؛
  • المهمة الرئيسية هي 192.168.0.0/16استبدالها بمجموعة منتشرة باستخدام kubeadm بشبكة فرعية للخدمة 172.24.0.0/16.

وقد حدث ذلك أنه لفترة طويلة كان من المثير للاهتمام بالنسبة لنا أن نرى ما وكيف يتم تخزينه في Kubernetes في وما إلى ذلك ، وما الذي يمكن القيام به به على الإطلاق ... لذا فكرنا: " لماذا لا نقوم فقط بتحديث البيانات في etcd عن طريق استبدال عناوين IP القديمة (الشبكة الفرعية) لأخرى جديدة ؟ "

البحث عن أدوات جاهزة للعمل مع البيانات في etcd ، لم نجد أي شيء يحل المهمة تمامًا. (بالمناسبة ، إذا كنت تعرف أي أدوات مساعدة للعمل مع البيانات مباشرة في etcd ، فإننا سنكون ممتنين للروابط.) ومع ذلك ، أصبح OpenShift etcdhelper نقطة انطلاق جيدة (بفضل مؤلفيها!) .

هذه الأداة قادرة على الاتصال إلى etcd باستخدام الشهادات وقراءة البيانات باستخدام الأوامر ls، get، dump.

أضف etcdhelper


الفكرة التالية منطقية: "ما الذي يمنع إضافة هذه الأداة المساعدة ، وإضافة القدرة على كتابة البيانات إلى etcd؟"

وقد تم ترجمته إلى نسخة معدلة من etcdhelper مع اثنين الجديدة changeServiceCIDRو الميزات changePodCIDR. يمكنك رؤية رمزها هنا .

ماذا تفعل الميزات الجديدة؟ خوارزمية changeServiceCIDR:

  • إنشاء deserializer.
  • تجميع تعبير عادي ليحل محل CIDR ؛
  • نمر عبر جميع الخدمات مع نوع ClusterIP في الكتلة:

    • فك شفرة القيمة من etcd إلى كائن Go ؛
    • باستخدام تعبير عادي ، استبدل أول وحدتي بايت من العنوان ؛
    • تعيين عنوان IP للخدمة من شبكة فرعية جديدة ؛
    • قم بإنشاء مُسلسل ، قم بتحويل كائن Go إلى protobuf ، اكتب بيانات جديدة إلى etcd.

الوظيفة changePodCIDRهي نفسها بشكل أساسي changeServiceCIDR- فقط بدلاً من تحرير مواصفات الخدمة نقوم بها للعقدة ونغيرها .spec.PodCIDRإلى شبكة فرعية جديدة.

ممارسة


تغيير خدمة CIDR


إن خطة تنفيذ المهمة بسيطة للغاية ، ولكنها تنطوي على وقت تعطل في وقت إعادة إنشاء جميع القرون في المجموعة. بعد وصف الخطوات الرئيسية ، سنشارك أيضًا أفكارنا حول كيفية تقليل هذه الخطوة البسيطة ، نظريًا.

الإجراءات التحضيرية:

  • تثبيت البرامج اللازمة وتجميع الخ.
  • النسخ الاحتياطي وما إلى ذلك /etc/kubernetes.

خطة عمل قصيرة لتغيير الخدمة CIDR:

  • تعديل قوائم apiserver ومدير التحكم
  • إعادة إصدار الشهادات ؛
  • تغيير خدمات ClusterIP في إلخ.
  • إعادة تشغيل جميع القرون في كتلة.

فيما يلي سلسلة كاملة من الإجراءات بالتفصيل.

1. تثبيت etcd-client لتفريغ البيانات:

apt install etcd-client

2. نجمع etcdhelper:

  • نضع golang:

    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تغيير المعلمة --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 والمفتاح القديم ، لأنه بدون هذا لن يتم إصدار شهادة جديدة:

    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. 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 

    ! , pod' /etc/resolv.conf CoreDNS (kube-dns), kube-proxy iptables . .
  9. ConfigMap' kube-system:

    kubectl -n kube-system edit cm kubelet-config-1.16

    clusterDNS IP- kube-dns: kubectl -n kube-system get svc kube-dns.

    kubectl -n kube-system edit cm kubeadm-config

    data.ClusterConfiguration.networking.serviceSubnet .
  10. kube-dns, kubelet :

    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. استبدل العنوان في جميع kubelets بأخرى ClusterDNSجديدة ، بينما ستستمر الخدمة القديمة في العمل في وقت واحد مع الجديدة.
  4. انتظر ظهور القرون التي بها تطبيقات إما بمفردها لأسباب طبيعية ، أو في وقت متفق عليه.
  5. حذف الخدمة kube-dns-tmpوتغييرها serviceSubnetCIDRلخدمة kube-dns.

ستعمل هذه الخطة على تقليل وقت التعطل إلى أقل من دقيقة واحدة - في الوقت الذي تتم فيه إزالة kube-dns-tmpالخدمة واستبدال الشبكة الفرعية للخدمة kube-dns.

تعديل podNetwork


في الوقت نفسه ، قررنا معرفة كيفية تعديل podNetwork باستخدام etcdhelper الناتج. تسلسل الإجراءات كما يلي:

  • نقوم بإصلاح التكوينات في kube-system؛
  • نقوم بإصلاح بيان kube-controller-manager ؛
  • نغير podCIDR مباشرة في إلخ.
  • إعادة تشغيل كافة عقد الكتلة.

الآن المزيد عن هذه الإجراءات:

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 مباشرة ، لأن هناك حالات عندما يكون تحرير كائنات Kubernetes في etcd هو الخيار الوحيد الممكن. (على سبيل المثال ، لا يمكنك فقط تغيير حقل الخدمة بدون توقف spec.clusterIP.)

مجموع


تتناول المقالة إمكانية العمل مع البيانات في etcd مباشرة ، أي تجاوز Kubernetes API. في بعض الأحيان يسمح لك هذا النهج بالقيام "بأشياء صعبة". تم اختبار العمليات الموضحة في النص على مجموعات K8s الحقيقية. ومع ذلك ، فإن حالة استعدادهم للاستخدام على نطاق واسع هي PoC (دليل على المفهوم) . لذلك ، إذا كنت ترغب في استخدام نسخة معدلة من الأداة المساعدة etcdhelper في مجموعاتك ، فقم بذلك على مسؤوليتك ومخاطرك.

ملاحظة


اقرأ أيضا في مدونتنا:


All Articles