كاليكو للتواصل في Kubernetes: التعرف على القليل من الخبرة



الغرض من هذا المقال هو تعريف القارئ على أساسيات الشبكات وإدارة سياسات الشبكة في Kubernetes ، بالإضافة إلى مكون إضافي من Calico يعمل على توسيع الميزات القياسية. على طول الطريق ، سيتم إظهار راحة التكوين وبعض الميزات مع أمثلة حقيقية من تجربة عمليتنا.

مقدمة سريعة لجهاز شبكة Kubernetes


لا يمكن تخيل كتلة Kubernetes بدون شبكة. لقد نشرنا بالفعل مواد على أساسياتها: " دليل مصور للشبكات في Kubernetes " و " مقدمة لسياسات شبكة Kubernetes لمتخصصي الأمن ".

في سياق هذه المقالة ، من المهم ملاحظة أن K8s ليست مسؤولة عن اتصال الشبكة بين الحاويات والعقد: يتم استخدام جميع أنواع الإضافات CNI (Container Networking Interface) لهذا الغرض. تحدثنا أيضا أكثر عن هذا المفهوم.

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

وخارج الصندوق ، يتم توفير NetworkPolicy API لتنظيم إدارة سياسة الشبكة في نظام Kubernetes. قد يحتوي هذا المورد ، الذي يمتد إلى مساحات الأسماء المحددة ، على قواعد لتقييد الوصول من تطبيق إلى آخر. كما يسمح لك بتكوين إمكانية الوصول بين برامج معينة أو بيئات (مساحات أسماء) أو مجموعات من عناوين IP:

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978 

ليس هذا هو المثال الأكثر بدائية من الوثائق الرسمية التي يمكن أن تثبط بشكل نهائي الرغبة في فهم منطق سياسات الشبكة. ومع ذلك ، لا نزال نحاول فهم المبادئ والأساليب الأساسية لمعالجة تدفقات حركة المرور باستخدام سياسات الشبكة ...

من المنطقي أن يكون هناك نوعان من حركة المرور: الواردة إلى pod (Ingress) والصادرة منها (Egress).



في الواقع ، تنقسم السياسة إلى هاتين الفئتين في اتجاه الحركة.

السمة المطلوبة التالية هي محدد ؛ الشخص الذي تنطبق عليه القاعدة. يمكن أن يكون قرنة (أو مجموعة من القرون) أو بيئة (أي مساحة اسم). تفاصيل مهمة: يجب أن يحتوي كلا النوعين من هذه الكائنات على تصنيف ( تسمية في مصطلحات Kubernetes) - هذه هي السياسات التي تعمل عليها.

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

  podSelector: {} ingress: [] policyTypes: - Ingress 

- في هذا المثال ، تغلق جميع حاضنات البيئة حركة المرور الواردة. يمكن تحقيق السلوك المعاكس بواسطة مثل هذا البناء:

  podSelector: {} ingress: - {} policyTypes: - Ingress 

على نحو مشابه للخارج:

  podSelector: {} policyTypes: - Egress 

- لتعطيله. وهنا ما يجب تضمينه:

  podSelector: {} egress: - {} policyTypes: - Egress 

بالعودة إلى اختيار مكون إضافي لـ CNI لمجموعة ، تجدر الإشارة إلى أنه ليس كل مكون إضافي للشبكة يدعم العمل مع NetworkPolicy . على سبيل المثال ، لا يعرف Flannel السابق ذكره كيفية تكوين سياسات الشبكة ، كما هو موضح صراحة في المستودع الرسمي. تم ذكر بديل أيضًا هناك - مشروع Calico Open Source ، الذي يمتد بشكل كبير واجهة برمجة تطبيقات Kubernetes القياسية من حيث سياسات الشبكة.



تلبية كاليكو: النظرية


يمكن استخدام المكون الإضافي Calico في التكامل مع Flannel (مشروع قناة ثانوي) أو من تلقاء نفسه ، بحيث يغطي كلاً من ميزات إدارة الاتصال وإتاحة الشبكة.

ما هي الميزات التي يوفرها حل محاصر K8s ومجموعة Calico API؟

هنا هو ما بنيت في NetworkPolicy:

  • السياسيون محدودون بالبيئة.
  • تنطبق السياسات على القرون الموسومة بعلامات.
  • يمكن تطبيق القواعد على القرون أو البيئات أو الشبكات الفرعية ؛
  • يمكن أن تحتوي القواعد على بروتوكولات أو تعليمات منفذ أو رمز رمزي.

وإليك كيفية قيام Calico بتوسيع هذه الميزات:

  • يمكن تطبيق السياسات على أي كائن: جراب أو حاوية أو جهاز ظاهري أو واجهة ؛
  • قد تحتوي القواعد على إجراء محدد (حظر ، إذن ، تسجيل) ؛
  • يمكن أن يكون الهدف أو مصدر القواعد منفذًا أو نطاق منفذ أو بروتوكولات أو سمات HTTP أو ICMP أو IP أو شبكة فرعية (4 أو 6 أجيال) أو أي محددات (العقد أو المضيفين أو البيئات) ؛
  • بالإضافة إلى ذلك ، يمكن التحكم في تدفق حركة المرور باستخدام إعدادات DNAT وسياسات إعادة توجيه حركة المرور.

يعود تاريخ GitHub الأول في مستودع Calico إلى يوليو 2016 ، وبعد عام من ذلك ، احتل المشروع موقعًا رائدًا في تنظيم اتصال شبكة Kubernetes - ويشار إلى ذلك ، على سبيل المثال ، بنتائج استطلاع أجرته The New Stack :



بدأت العديد من الحلول الكبيرة المدارة مع K8s ، مثل Amazon EKS و Azure AKS و Google GKE وغيرها ، في التوصية به للاستخدام.

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



يتطور المشروع بسرعة كبيرة ، وهو يدعم العمل في الحلول الشائعة المدارة K8s ، و OpenShift ، و OpenStack ، ومن الممكن استخدام Calico عند نشر كتلة باستخدام kops ، وهناك إشارات إلى بناء شبكات Service Mesh ( هنا مثال على استخدامه مع Istio).

الممارسة مع كاليكو


في الحالة العامة لاستخدام vanilla Kubernetes ، يتلخص تثبيت CNI في استخدام ملف calico.yaml تنزيله من الموقع الرسمي باستخدام kubectl apply -f .

كقاعدة عامة ، فإن الإصدار الحالي من البرنامج المساعد متوافق مع أحدث 2-3 إصدارات من Kubernetes: العمل في الإصدارات القديمة لم يتم اختباره ولا يضمن. وفقًا للمطورين ، تعمل Calico على Linux kernel أعلى 3.10 ضمن CentOS 7 أو Ubuntu 16 أو Debian 8 ، بالإضافة إلى iptables أو IPVS.

العزل داخل البيئة


لفهم عام ، فكر في حالة بسيطة لفهم كيف تختلف سياسات الشبكة في تدوين Calico عن السياسات القياسية وكيف أن نهج تجميع القواعد يبسط من قابليتها للقراءة ومرونة التكوين:



هناك تطبيقان للويب تم نشرهما في المجموعة: Node.js و PHP ، أحدهما يستخدم Redis. لمنع الوصول إلى Redis من PHP ، مع ترك الاتصال مع Node.js ، يكفي تطبيق السياسة التالية:

 kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: name: allow-redis-nodejs spec: podSelector: matchLabels: service: redis ingress: - from: - podSelector: matchLabels: service: nodejs ports: - protocol: TCP port: 6379 

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

المثال يستخدم apiVersion apiVersion "خارج الصندوق" ، لكن لا شيء يمنع استخدام المورد الذي يحمل نفس الاسم من تسليم Calico . بناء الجملة أكثر شمولاً ، لذلك تحتاج إلى إعادة كتابة القاعدة للحالة أعلاه في النموذج التالي:

 apiVersion: crd.projectcalico.org/v1 kind: NetworkPolicy metadata: name: allow-redis-nodejs spec: selector: service == 'redis' ingress: - action: Allow protocol: TCP source: selector: service == 'nodejs' destination: ports: - 6379 

تحتوي الإنشاءات المذكورة أعلاه للسماح لكل حركة المرور أو حظرها من خلال NetworkPolicy API المعتادة على هياكل ذات أقواس يصعب فهمها وتذكرها. في حالة كاليكو ، لتغيير منطق قاعدة جدار الحماية إلى العكس ، فقط قم بتغيير action: Allow action: Deny .

عزل البيئة


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



يتم وضع Prometheus ، كقاعدة عامة ، في بيئة خدمة منفصلة - في المثال ، سيكون مساحة اسم في النموذج التالي:

 apiVersion: v1 kind: Namespace metadata: labels: module: prometheus name: kube-prometheus 

حقل metadata.labels هنا لم يكن عرضيًا. كما ذكر أعلاه ، تعمل namespaceSelector (مثل podSelector ) على التسميات. لذلك ، للسماح بأخذ المقاييس من جميع القرون على منفذ معين ، سوف تضطر إلى إضافة بعض الملصقات (أو تأخذ من تلك الموجودة) ، ثم تطبيق التكوين مثل:

 apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-metrics-prom spec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: module: prometheus ports: - protocol: TCP port: 9100 

وإذا كنت تستخدم سياسات Calico ، فسيكون بناء الجملة:

 apiVersion: crd.projectcalico.org/v1 kind: NetworkPolicy metadata: name: allow-metrics-prom spec: ingress: - action: Allow protocol: TCP source: namespaceSelector: module == 'prometheus' destination: ports: - 9100 

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

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

استخدام كائنات Calico الاختيارية


اسمحوا لي أن أذكرك أنه من خلال مجموعة موسعة من واجهات برمجة التطبيقات Calico ، يمكنك التحكم في توافر العقد ، وليس على سبيل الحصر. في المثال التالي ، يؤدي استخدام GlobalNetworkPolicy إغلاق إمكانية تمرير طلبات ICMP في الكتلة (على سبيل المثال ، الأصوات من pod إلى عقدة أو بين pods أو من عقدة إلى pod IP):

 apiVersion: crd.projectcalico.org/v1 kind: GlobalNetworkPolicy metadata: name: block-icmp spec: order: 200 selector: all() types: - Ingress - Egress ingress: - action: Deny protocol: ICMP egress: - action: Deny protocol: ICMP 

في الحالة أعلاه ، لا تزال العقد العنقودية قادرة على "التواصل" مع بعضها البعض عبر بروتوكول ICMP. ويتم حل هذا السؤال عن طريق تطبيق HostEndpoint كيان HostEndpoint :

 apiVersion: crd.projectcalico.org/v1 kind: GlobalNetworkPolicy metadata: name: deny-icmp-kube-02 spec: selector: "role == 'k8s-node'" order: 0 ingress: - action: Allow protocol: ICMP egress: - action: Allow protocol: ICMP --- apiVersion: crd.projectcalico.org/v1 kind: HostEndpoint metadata: name: kube-02-eth0 labels: role: k8s-node spec: interfaceName: eth0 node: kube-02 expectedIPs: ["192.168.2.2"] 

حالة VPN


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



يتصل العملاء بشبكة VPN من خلال منفذ UDP القياسي 1194 ، وعند الاتصال ، يستقبلون الطرق المؤدية إلى الشبكات الفرعية العنقودية للقرون والخدمات. شبكات الدفع الفرعية بالكامل حتى لا تفقد الخدمات أثناء إعادة التشغيل وتغيير العنوان.

المنفذ في التكوين هو المعيار ، والذي يفرض بعض الفروق الدقيقة على عملية تكوين التطبيق ونقله إلى كتلة Kubernetes. على سبيل المثال ، في نفس AWS ، ظهر LoadBalancer لـ UDP حرفيًا في نهاية العام الماضي في قائمة محدودة من المناطق ، ولا يمكن استخدام NodePort نظرًا لإعادة توجيهه على جميع عقد نظام المجموعة ومن المستحيل قياس عدد مثيلات الخادم لتحمل الأخطاء. بالإضافة إلى ذلك ، يجب عليك تغيير نطاق المنفذ الافتراضي ...

نتيجة للبحث عن الحلول الممكنة ، تم اختيار ما يلي:

  1. تتم جدولة قرون VPN لكل مضيف في وضع hostNetwork ، أي على عنوان IP الفعلي.
  2. يتم نشر الخدمة من خلال ClusterIP . يرتفع المنفذ فعليًا على المضيف ، والذي يمكن الوصول إليه من الخارج مع بعض التحذيرات (التوفر المشروط لعنوان IP حقيقي).
  3. تعريف العقدة التي ارتفع عليها قرنة هو خارج نطاق قصتنا. لا يمكنني إلا أن أقول إنه يمكنك "تثبيت" الخدمة بشكل ثابت على المضيف أو كتابة خدمة جانبية صغيرة تراقب عنوان IP الحالي لخدمة VPN وتحرر سجلات DNS المسجلة مع العملاء - الذين لديهم ما يكفي من الخيال.

من وجهة نظر التوجيه ، يمكننا تحديد عميل VPN بشكل فريد من خلال عنوان IP الخاص به الصادر عن خادم VPN. فيما يلي مثال بدائي لتقييد الوصول إلى مثل هذا العميل على الخدمات ، وتوضيح على Redis المذكورة أعلاه:

 apiVersion: crd.projectcalico.org/v1 kind: HostEndpoint metadata: name: vpnclient-eth0 labels: role: vpnclient environment: production spec: interfaceName: "*" node: kube-02 expectedIPs: ["172.176.176.2"] --- apiVersion: crd.projectcalico.org/v1 kind: GlobalNetworkPolicy metadata: name: vpn-rules spec: selector: "role == 'vpnclient'" order: 0 applyOnForward: true preDNAT: true ingress: - action: Deny protocol: TCP destination: ports: [6379] - action: Allow protocol: UDP destination: ports: [53, 67] 

هنا ، يُمنع منعًا باتًا الاتصال بالمنفذ 6379 ، ولكن في الوقت نفسه ، يتم الحفاظ على خدمة DNS ، والتي غالباً ما تعاني عملها عند وضع القواعد. لأنه ، كما ذكر سابقًا ، عند ظهور محدد ، يتم تطبيق سياسة افتراضية باهظة عليه ، ما لم ينص على خلاف ذلك.

النتائج


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

قد لا تكون عملية عزل الكتل لمتطلبات الأمان ممكنة دائمًا ، وفي هذه الحالات يتم إنقاذ كاليكو (أو حل مشابه). يتم استخدام الأمثلة الواردة في هذه المقالة (مع القليل من التنقيح) في العديد من عمليات التثبيت لعملائنا في AWS.

PS


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

Source: https://habr.com/ru/post/ar485716/


All Articles