الملفات المحلية عند ترحيل تطبيق إلى Kubernetes

الملفات المحلية عند ترحيل تطبيق إلى Kubernetes

عند إنشاء عملية CI/CD باستخدام Kubernetes، تنشأ أحيانًا مشكلة عدم التوافق بين متطلبات البنية التحتية الجديدة والتطبيق الذي يتم نقله إليها. على وجه الخصوص، في مرحلة بناء التطبيق، من المهم الحصول عليه واحد الصورة التي سيتم استخدامها في جميع بيئات المشروع والمجموعات. هذا المبدأ يكمن وراء الصحيح وفقا لجوجل إدارة الحاويات (أكثر من مرة حول هذا الموضوع قال والقسم الفني لدينا).

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

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

تخزين ثابت

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

  • webroot/assets/2072c2df/css/…
  • webroot/assets/2072c2df/images/…
  • webroot/assets/2072c2df/js/…

ماذا يعني هذا من حيث الكتلة؟

أبسط مثال

لنأخذ حالة شائعة إلى حد ما، عندما يسبق nginx PHP لتوزيع البيانات الثابتة ومعالجة الطلبات البسيطة. أسهل طريقة - قابل للفتح مع حاويتين:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: site
spec:
  selector:
    matchLabels:
      component: backend
  template:
    metadata:
      labels:
        component: backend
    spec:
      volumes:
        - name: nginx-config
          configMap:
            name: nginx-configmap
      containers:
      - name: php
        image: own-image-with-php-backend:v1.0
        command: ["/usr/local/sbin/php-fpm","-F"]
        workingDir: /var/www
      - name: nginx
        image: nginx:1.16.0
        command: ["/usr/sbin/nginx", "-g", "daemon off;"]
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf

في شكل مبسط، يتلخص تكوين nginx في ما يلي:

apiVersion: v1
kind: ConfigMap
metadata:
  name: "nginx-configmap"
data:
  nginx.conf: |
    server {
        listen 80;
        server_name _;
        charset utf-8;
        root  /var/www;

        access_log /dev/stdout;
        error_log /dev/stderr;

        location / {
            index index.php;
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi_params;
        }
    }

عند دخولك إلى الموقع لأول مرة، تظهر الأصول في حاوية PHP. ولكن في حالة وجود حاويتين داخل جراب واحد، لا يعرف nginx شيئًا عن هذه الملفات الثابتة، والتي (حسب التكوين) يجب أن تُعطى لهم. نتيجة لذلك، سيرى العميل خطأ 404 لجميع طلبات ملفات CSS وJS. الحل الأبسط هنا هو تنظيم دليل مشترك للحاويات. الخيار البدائي - عام emptyDir:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: site
spec:
  selector:
    matchLabels:
      component: backend
  template:
    metadata:
      labels:
        component: backend
    spec:
      volumes:
        - name: assets
          emptyDir: {}
        - name: nginx-config
          configMap:
            name: nginx-configmap
      containers:
      - name: php
        image: own-image-with-php-backend:v1.0
        command: ["/usr/local/sbin/php-fpm","-F"]
        workingDir: /var/www
        volumeMounts:
        - name: assets
          mountPath: /var/www/assets
      - name: nginx
        image: nginx:1.16.0
        command: ["/usr/sbin/nginx", "-g", "daemon off;"]
        volumeMounts:
        - name: assets
          mountPath: /var/www/assets
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf

الآن يتم تقديم الملفات الثابتة التي تم إنشاؤها في الحاوية بواسطة nginx بشكل صحيح. لكن اسمحوا لي أن أذكرك أن هذا حل بدائي، مما يعني أنه بعيد عن المثالية وله الفروق الدقيقة وعيوبه، والتي سيتم مناقشتها أدناه.

تخزين أكثر تقدمًا

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

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

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

بشكل عام، والنتيجة هي الأخطاء مرة أخرى.

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

ما هي الحلول؟

  1. إذا سمحت الأجهزة والموارد، يمكنك استخدامها cephfs لتنظيم دليل يمكن الوصول إليه بشكل متساوٍ للاحتياجات الثابتة. الوثائق الرسمية توصي باستخدام محركات أقراص SSD، والنسخ المتماثل ثلاثة أضعاف على الأقل، والاتصال "السميك" المستقر بين عقد المجموعة.
  2. سيكون الخيار الأقل تطلبًا هو تنظيم خادم NFS. ومع ذلك، فأنت بحاجة إلى مراعاة الزيادة المحتملة في وقت الاستجابة لطلبات معالجة خادم الويب، وسوف يترك التسامح مع الخطأ الكثير مما هو مرغوب فيه. عواقب الفشل كارثية: فقدان الجبل يحكم على المجموعة بالموت تحت ضغط حمولة LA المندفعة إلى السماء.

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

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

توصية

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

إذا عدنا إلى المثال المحدد لإطار عمل Yii ولم نتعمق في بنيته (وهذا ليس غرض المقالة)، فيكفي أن نشير إلى نهجين شائعين:

  1. قم بتغيير عملية إنشاء الصورة لوضع الأصول في موقع يمكن التنبؤ به. تم اقتراح/تنفيذ هذا في ملحقات مثل yii2-static-assets.
  2. حدد تجزئات محددة لأدلة الأصول، كما تمت مناقشته في على سبيل المثال. هذا العرض (بدءا من الشريحة رقم 35). بالمناسبة، ينصح مؤلف التقرير في النهاية (وليس بدون سبب!) أنه بعد تجميع الأصول على خادم البناء، قم بتحميلها إلى وحدة تخزين مركزية (مثل S3)، وأمامها ضع CDN.

التحميلات

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

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

توصية

حل واحد هو باستخدام وحدة تخزين متوافقة مع S3 (حتى لو كانت فئة مستضافة ذاتيًا مثل minio). سيتطلب التبديل إلى S3 إجراء تغييرات على مستوى الكود، وكيف سيتم تسليم المحتوى على الواجهة الأمامية، لقد فعلنا ذلك بالفعل писали.

جلسات المستخدم

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

تم حل المشكلة جزئيًا عن طريق التشغيل stickySessions على الدخول (هذه الميزة مدعومة في جميع وحدات التحكم في الدخول الشائعة - لمزيد من التفاصيل، راجع مراجعتنا)لربط المستخدم بحجرة معينة مع التطبيق:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-test
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"

spec:
  rules:
  - host: stickyingress.example.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /

لكن هذا لن يقضي على مشاكل عمليات النشر المتكررة.

توصية

الطريقة الصحيحة هي نقل التطبيق إلى تخزين الجلسات في memcached وRedis والحلول المشابهة - بشكل عام، التخلي تماما عن خيارات الملف.

اختتام

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

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

PS

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

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

إضافة تعليق