فایل های محلی هنگام انتقال یک برنامه به Kubernetes

فایل های محلی هنگام انتقال یک برنامه به Kubernetes

هنگام ساخت یک فرآیند CI/CD با استفاده از Kubernetes، گاهی اوقات مشکل ناسازگاری بین الزامات زیرساخت جدید و برنامه در حال انتقال به آن ایجاد می شود. به طور خاص، در مرحله ساخت برنامه مهم است که دریافت کنید یک تصویری که در آن استفاده خواهد شد از همه محیط ها و خوشه های پروژه این اصل زیربنای درستی است به گفته گوگل مدیریت کانتینر (بیش از یک بار در این مورد صحبت کرد و بخش فنی ما).

با این حال، در شرایطی که کد سایت از یک چارچوب آماده استفاده می کند، کسی را نخواهید دید که استفاده از آن محدودیت هایی را برای استفاده بیشتر از آن اعمال می کند. و در حالی که در یک "محیط عادی" به راحتی می توان با آن مقابله کرد، در Kubernetes این رفتار می تواند به یک مشکل تبدیل شود، به خصوص زمانی که برای اولین بار با آن روبرو می شوید. در حالی که یک ذهن مبتکر می تواند راه حل های زیرساختی ارائه دهد که در نگاه اول بدیهی یا حتی خوب به نظر می رسند ... مهم است که به یاد داشته باشید که اکثر موقعیت ها می توانند و باید از نظر معماری حل شود.

بیایید به راه‌حل‌های رایج راه‌حل برای ذخیره‌سازی فایل‌ها که می‌تواند منجر به عواقب ناخوشایند در هنگام کار کردن یک خوشه شود، نگاهی بیاندازیم، و همچنین به مسیر صحیح‌تری اشاره کنیم.

ذخیره سازی استاتیک

برای نشان دادن، یک برنامه وب را در نظر بگیرید که از نوعی ژنراتور استاتیک برای به دست آوردن مجموعه ای از تصاویر، سبک ها و موارد دیگر استفاده می کند. به عنوان مثال، فریم ورک Yii PHP دارای یک مدیر دارایی داخلی است که نام های دایرکتوری منحصر به فردی را تولید می کند. بر این اساس، خروجی مجموعه ای از مسیرها برای سایت استاتیک است که بدیهی است با یکدیگر تلاقی نمی کنند (این کار به چند دلیل انجام شد - به عنوان مثال، برای حذف موارد تکراری زمانی که چندین مؤلفه از یک منبع استفاده می کنند). بنابراین، اولین باری که به یک ماژول منبع وب دسترسی پیدا می‌کنید، فایل‌های استاتیک (در واقع، اغلب پیوندهای نمادین، اما در ادامه بیشتر در مورد آن توضیح داده می‌شوند) با یک فهرست ریشه مشترک و منحصر به فرد برای این استقرار شکل می‌گیرند و قرار می‌گیرند:

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

این از نظر یک خوشه به چه معناست؟

ساده ترین مثال

بیایید یک مورد نسبتاً رایج را در نظر بگیریم، زمانی که PHP قبل از nginx برای توزیع داده های ثابت و پردازش درخواست های ساده قرار می گیرد. آسانترین راه - گسترش با دو ظرف:

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 می توانید قرار دهید DaemonSet از کش کردن nginx، که نسخه هایی از دارایی ها را برای مدت زمان محدودی ذخیره می کند. این رفتار به راحتی با استفاده از آن قابل تنظیم است proxy_cache با عمق ذخیره سازی در روز یا گیگابایت فضای دیسک.

ترکیب این روش با فایل سیستم‌های توزیع‌شده ذکر شده در بالا، میدان بزرگی را برای تخیل فراهم می‌کند که تنها با بودجه و پتانسیل فنی کسانی که آن را اجرا و پشتیبانی می‌کنند محدود می‌شود. از روی تجربه، می توان گفت که هر چه سیستم ساده تر باشد، پایدارتر کار می کند. هنگامی که چنین لایه هایی اضافه می شوند، حفظ زیرساخت بسیار دشوارتر می شود و در عین حال زمان صرف شده برای تشخیص و بازیابی هر گونه خرابی افزایش می یابد.

توصیه

اگر اجرای گزینه های ذخیره سازی پیشنهادی نیز برای شما غیر قابل توجیه به نظر می رسد (پیچیده، گران...)، پس ارزش آن را دارد که از طرف دیگر به وضعیت نگاه کنید. یعنی، برای حفاری در معماری پروژه و مشکل را در کد حل کنید، به ساختار داده ایستا در تصویر گره خورده است، یک تعریف واضح از محتویات یا روش برای "گرم کردن" و/یا پیش کامپایل کردن دارایی ها در مرحله مونتاژ تصویر. به این ترتیب ما رفتار کاملاً قابل پیش بینی و مجموعه فایل های یکسانی را برای همه محیط ها و کپی های برنامه در حال اجرا دریافت می کنیم.

اگر به مثال خاص با چارچوب Yii برگردیم و به ساختار آن نپردازیم (که هدف مقاله نیست)، کافی است به دو رویکرد رایج اشاره کنیم:

  1. فرآیند ساخت تصویر را تغییر دهید تا دارایی ها را در یک مکان قابل پیش بینی قرار دهید. این در برنامه های افزودنی مانند پیشنهاد/اجرا شده است yii2-static-assets.
  2. تعریف هش های خاص برای دایرکتوری های دارایی، همانطور که در به عنوان مثال بحث شده است. این ارائه (از اسلاید شماره 35 شروع می شود). به هر حال، نویسنده گزارش در نهایت (و نه بی دلیل!) توصیه می کند که پس از مونتاژ دارایی ها در سرور ساخت، آنها را در یک ذخیره سازی مرکزی (مانند S3) آپلود کنید که در مقابل آن یک CDN قرار دهید.

دانلودها

مورد دیگری که قطعاً هنگام انتقال یک برنامه به خوشه Kubernetes مطرح می شود، ذخیره فایل های کاربر در سیستم فایل است. به عنوان مثال، ما دوباره یک برنامه PHP داریم که فایل ها را از طریق یک فرم آپلود می پذیرد، در حین کار با آنها کاری انجام می دهد و آنها را پس می فرستد.

در Kubernetes، مکانی که این فایل ها باید در آن قرار گیرند باید برای همه نسخه های مشابه برنامه مشترک باشد. بسته به پیچیدگی برنامه و نیاز به سازماندهی ماندگاری این فایل ها، گزینه های دستگاه مشترک ذکر شده در بالا ممکن است چنین مکانی باشند، اما همانطور که می بینیم، آنها معایب خود را دارند.

توصیه

یک راه حل این است با استفاده از فضای ذخیره سازی سازگار با S3 (حتی اگر نوعی دسته بندی خود میزبان مانند مینیو باشد). تغییر به S3 نیاز به تغییرات دارد در سطح کد، و اینکه چگونه محتوا در قسمت جلویی تحویل داده می شود، ما قبلاً دریافت کرده ایم писали.

جلسات کاربر

به طور جداگانه، شایان ذکر است که سازماندهی ذخیره سازی جلسات کاربر. اغلب اینها همچنین فایل‌هایی روی دیسک هستند، که در زمینه Kubernetes منجر به درخواست‌های مجوز دائمی از کاربر در صورتی که درخواست او در ظرف دیگری ختم شود، می‌شود.

مشکل تا حدی با روشن کردن حل می شود stickySessions در ورود (این ویژگی در همه کنترلرهای ورودی محبوب پشتیبانی می شود - برای جزئیات بیشتر، نگاه کنید بررسی ما)برای اتصال کاربر به یک pod خاص با برنامه:

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

اضافه کردن نظر