تجربتنا مع البيانات في مجموعة Kubernetes etcd مباشرة (بدون K8s API)

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

تجربتنا مع البيانات في مجموعة Kubernetes etcd مباشرة (بدون K8s API)

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

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

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

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

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

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

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

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

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

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

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

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

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

إضافة الخالمساعد

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

لقد أصبح نسخة معدلة من etcdhelper بوظيفتين جديدتين changeServiceCIDR и changePodCIDR. عليها يمكنك رؤية الكود هنا.

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

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

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

ممارسة

تغيير خدمة CIDR

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

الخطوات التحضيرية:

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

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

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

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

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

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 تغيير المعلمة --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 

    تحذير! في هذه اللحظة، يتوقف تحليل النطاق عن العمل في المجموعة، كما هو الحال في القرون الموجودة /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 باستخدام ملف etcdhelper الناتج. تسلسل الإجراءات هو كما يلي:

  • إصلاح التكوينات في kube-system;
  • إصلاح بيان مدير وحدة تحكم kube؛
  • تغيير 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.)

مجموع

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

PS

اقرأ أيضًا على مدونتنا:

المصدر: www.habr.com

إضافة تعليق