التثبيت على Kubernetes

Yes

Whatever ENV you specify when you finally do you docker run on the image … will take.

And doing those things at every boot of the container is reasonable?

Some people like this pattern, I do not, that is why rails introduces some super fancy locking around migrations to ensure they never run concurrently when a cluster of 20 app try to run migrations at the same time.

Some people just boot the same image and run the migrations in some out-of-band process.

But each container should still rake assets:precompile at least once?

Depends on how do you host those.

If you have a NFS share of some sort (like AWS EFS) or if you upload those to some object storage service that can only be done on bootstrap.

شكرًا للجميع على جميع الردود المفيدة. لديّ بعض الأسئلة الإضافية حول كيفية القيام بذلك بشكل صحيح.

أنا أحاول إعداد ملف app.yaml بسيط لبدء التشغيل يحتوي فقط على المعلومات المطلوبة لبناء الحاوية. بعض المعلومات الواردة فيه مخصصة بوضوح لتشغيل الحاوية — مثل نقاط تحميل وحدات التخزين وتعيينات المنافذ. لكنني لست متأكدًا بشأن متغيرات البيئة. أعتقد أنني سأجربها ببساطة، لكن هل تُستخدم متغيرات البيئة هذه أثناء بناء الحاوية (يتم حقنها في Dockerfile بطريقة ما) أم فقط عند تشغيل الحاوية؟ إذا كانت الأخيرة، فسأحرص على أن تنتهي في ملف تكوين k8s المناسب.

ثانيًا، تحدث بعض الأشخاص هنا عن دفع الصورة إلى مستودع حاويات خاص. هل هذا ضروري؟ وبعبارة أخرى — هل تحتوي صورة البناء على أي معلومات سرية لا ينبغي نشرها في مستودع عام مثل Docker Hub؟ (ليس لدينا مستودع حاويات خاص بعد وأود تجنب إعداده.)

أخيرًا، هل هناك إعداد في app.yaml للتحكم في اسم الحاوية المنشأة؟ إنه أكثر من مجرد تحسين شكلي، لكن ذلك سيكون لطيفًا :slight_smile:.

شكرًا مسبقًا على المساعدة! (عذرًا عن إحياء موضوع قديم. هذا هو أول نتيجة تظهر في Google عند البحث عن كيفية تثبيت Discourse على Kubernetes.)

@Geoffrey_Challen يمكنك إنشاء صورة باستخدام مستودع Discourse والإضافات، ثم تثبيت حزم Ruby والاعتماديات الأخرى ودفع الصورة إلى سجل (مثل DockerHub). هذا المستودع سيكون مستقلًا عن البيئة ويمكن أن يكون عامًا (ما لم تتضمن إضافة خاصة أو ما شابه ذلك). يمكن استخدام هذه الصورة الأساسية في بيئات الاختبار والإنتاج وحتى في مشاريع مختلفة (إذا كانت تستخدم نفس الإضافات).

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

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

أنا غير متأكد من فهمي لهذا الجزء. ألا يحدث كل ذلك تلقائيًا داخل الحاوية حسب الحاجة؟ آمل أن أتمكن من مجرد دفع هذا إلى سحابتنا دون الحاجة أبدًا إلى الوصول إلى موجه الأوامر (shell) على الجهاز، تمامًا كما نادرًا ما أحتاج (إن حدث ذلك) إلى الدخول إلى حاوية ديسكورس Docker.

لا تحتاج إلى إدخال الحاوية بشكل صريح. ما أعنيه هو أنه لا يمكنك إنشاء صورة مُجمَّعة مسبقًا باستخدامها (مثلًا، مُولَّدة في خط أنابيب التكامل المستمر) واستخدامها كما هي، لأن هذا يجب تنفيذه على الجهاز المستهدف حيث توجد قاعدة البيانات (يمكن أتمتة ذلك، لكنني لم أقم بذلك في k8s، رغم أنني قمت بذلك باستخدام Ansible).

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

ولكن بغض النظر، ألا يقوم Discourse بتشغيل ترحيلات قاعدة البيانات أو خطوات الإعداد الأخرى عند بدء تشغيل الحاوية؟

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

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

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

يتضمن التثبيت القياسي nginx و Rails و Postgres و Redis داخل الحاوية. يستخدم وحدات تخزين خارجية لبيانات Postgres و Redis. ولا يتم تدميرها عند إعادة البناء أو الترقية.

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

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


بافتراض هذا السيناريو، يجب أن يبدو الإعداد شيئًا مثل هذا:

  • توفر الإصدارات الرسمية صورة Discourse الأساسية بالفعل.
    • سنحتاج أيضًا إلى حاوية لـ discourse_docker تأتي مع Docker خاص بها.
  • إنشاء سجل خاص داخل المجموعة.
  • إنشاء ConfigMap يحتوي على محتوى ملف app.yml.
  • تشغيل مهمة (Job) تنفذ ./launcher bootstrap باستخدام Docker المضمن على عقدة تعتمد على الآلات الافتراضية (بدون وصول إلى Docker socket)، ثم تعيد تسمية الصورة الناتجة وتدفعها إلى السجل الخاص (مع تسمية تعتمد على الطابع الزمني، وليس latest) (local_discourse ليس اسمًا جيدًا هنا)، وتقوم بتحديث النشر إلى التسمية الجديدة.
    • واو، هذا يتطلب الكثير من الأذونات لمهمة الترقية.

يعمل PostgreSQL داخل الحاوية. يحفظ البيانات خارج الحاوية، لكنه يعمل داخلها إذا استخدمت مجموعة قوالب التثبيت القياسية. ينطبق الأمر نفسه على Redis. أعتقد أن اللبس ينشأ عندما أقول “قاعدة البيانات تعمل داخل الحاوية”، فأنا أشير إلى خادم قاعدة البيانات، حتى لو كانت ملفات قاعدة البيانات موجودة خارج الحاوية. (لكن ملفات قاعدة البيانات لا “تعمل”، وهذا هو السبب في أنني أعتبر صياغتي واضحة — لكن من الواضح أنها ليست واضحة بما يكفي :slight_smile:.)

ملاحظة جانبية: في الواقع، لا يحفظ البيانات بالضرورة خارج الحاوية إلا إذا قمت بتكوين Docker لربط المجلد (bind mount). تمكنت من تخطي ذلك أثناء عملية التمهيد، على الرغم من أنه ربما لا يكون فكرة جيدة، لأن محتويات قاعدة البيانات لن تنجو من إعادة تشغيل الحاوية في تلك الحالة.

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

إليك ما أود أن أتمكن من فعله:

  • تشغيل ./launcher bootstrap محليًا لإنشاء صورة Discourse “شاملة” تتضمن جميع التبعيات: postgres و redis وما إلى ذلك.
  • نشر هذه الصورة على Kubernetes.
  • إعادة تشغيل ./launcher bootstrap لاحقًا لتحديث الصورة وإعادة النشر دون تدمير البيانات (طبعًا).

فهمي هو أن صورة Discourse الشاملة لا يجب أن تتطلب أي تبعيات لخدمات خارجية. ومع ذلك، لكي تبقى البيانات سليمة أثناء ترقية الحاويات، يجب أن تكون ملفات قاعدة بيانات postgres خارج الحاوية. هذا لا بأس به—يمكنني إنشاء حجم دائم (persistent volume) في k8s لها.

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

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

لاحظت أن @sam ذكر عدة مرات أنهم يعيدون نشر Discourse لعملائهم باستخدام سير عمل مشابه إلى حد كبير لما وصفته أعلاه. لكنني أشك في أن السبب في نجاح ذلك هو أن صور Discourse الخاصة بهم مُهيأة لاستخدام خادم قاعدة بيانات (وربما Redis أيضًا) يعمل على مجموعتهم—وهو ما منطقي لدعم عدة نشرات، لكنه ليس بالضبط ما أريد فعله. هذا يعني أن عملية التمهيد يمكنها تعديل قاعدة بيانات الإنتاج—أو ربما يتم تخطي خطوة ترحيل قاعدة البيانات تمامًا لأن ترقيات وتحويلات قاعدة البيانات تتم خارجيًا. @sam: هل يمكنك التأكيد؟

على أي حال، النتيجة بالنسبة لي هي أنني بحاجة إلى إيجاد طريقة لتشغيل ترحيلات قاعدة البيانات عند بدء تشغيل الحاوية، وليس أثناء ./launcher bootstrap. أعتقد أن إحدى الطرق للقيام بذلك هي:

  • بناء حاوية Discourse الشاملة محليًا باستخدام ./launcher bootstrap، مع استخدام نقطة تحميل حجم (volume mount) تشير إلى قاعدة بيانات محلية فارغة، نظرًا لأن هذه القاعدة لن تُستخدم لاحقًا. هذا سيضع كل شيء داخل الحاوية بشكل صحيح، لكنه لن ينهي عمل postgres.
  • إيجاد طريقة لتشغيل خطوة ترحيل قاعدة البيانات على قاعدة بيانات الإنتاج الفعلية—ربما باستخدام حاوية تهيئة (init container) في k8s؟
  • استبدال صورة Discourse القديمة بالصورة الجديدة

قد تكون مهتمًا بإعداد متعدد المواقع.

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

نصيحتي؟ قم بإعداد متعدد المواقع مع جدولة ثابتة على آلات افتراضية (VMs)، بعيدًا تمامًا عن مجموعتك. (أو استخدم نوع خدمة Service Type=ExternalName يشير إلى الآلة الافتراضية للحفاظ على نفس Ingress.)

حسناً… لقد تمكنت من اكتشاف طريقة واحدة للقيام بذلك. لستُ راضياً عنها بنسبة 100%، لكنها تعمل وقد تكون جذابة للآخرين الذين يحاولون نشر صورة Discourse ثنائية الحجم (تشمل postgres و redis وغيرها) في حاوية واحدة بسيطة على Kubernetes.

نهجي

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

للأسف، يتطلب الأمر launcher bootstrap إنشاء حاوية واستخدام Docker. لذا فإن تشغيل launcher داخل حاوية أخرى (على سبيل المثال، في حاوية تعمل على سحابتنا) يعني إما التعقيد مع إعداد Docker داخل Docker (وهو أمر ممكن، لكنه لا يُعتبر من أفضل الممارسات) أو كشف عملية تشغيل Docker الأساسية. لستُ متأكداً حتى من أن هذا النهج الثاني سيعمل، لأنني أعتقد أنه سيفسر نقطة تركيب المجلد بالنسبة لنظام الملفات المحلي للعقدة، بينما في سيناريونا نريد تركيب المجلد /shared على حجم Kubernetes دائم. ربما يعمل مسار Docker داخل Docker، لكنك ستواجه حينها نقطة تركيب ثلاثية غريبة من الحاوية المتداخلة إلى الحاوية الخارجية ومن هناك إلى حجم Kubernetes الدائم. هذا يبدو… غير حكيم.

ومع ذلك، فإن launcher bootstrap يقوم في جوهره بإنشاء ملف .yml كبير واحد من خلال معالجة قيمة templates في app.yml، ثم يمررها إلى صورة Discourse الأساسية عند انتهاء عملية التمهيد. لذا إذا تمكنا من استخراج ملف الإعدادات، يمكننا توليد الإعدادات على أي آلة، ثم نحتاج فقط إلى معرفة كيفية تمريرها إلى حاوية نبدأها في السحابة.

لذا، كملخص، إليك الخطوات التي سنقوم بها:

  1. توليد إعدادات التمهيد باستخدام launcher معدل.
  2. تمريرها إلى صورة Discourse الأساسية المعدلة التي ستقوم بالتمهيد (باستخدام pups) ثم تشغيل Discourse.

توليد إعدادات التمهيد

إليك التغيير المطلوب في launcher لدعم أمر dump الذي يكتب الإعدادات المدمجة إلى STDOUT:

run_dump() {
  set_template_info
  echo "$input"
}

(لاحظ أن هذا الأمر متاح في شعبتنا من discourse_docker.)

لذا فإن الخطوة الأولى هي استخدام أمر launcher dump الجديد المضاف أعلاه لإنشاء إعدادات التمهيد الخاصة بنا:

# استبدل اسم إعداد الحاوية الخاصة بك بـ app
./launcher dump app > bootstrap.yml

إنشاء الحاوية الأولية

بعد ذلك، نحتاج إلى حاوية تعرف كيفية تشغيل pups لتمهيد الحاوية قبل التشغيل عبر /sbin/boot. استخدمت ملف Dockerfile التالي لإجراء تغيير طفيف على صورة Discourse الأساسية:

FROM discourse/base:2.0.20191219-2109
COPY scripts/bootstrap.sh /
CMD bash bootstrap.sh

حيث يحتوي scripts/bootstrap.sh على:

cd /pups/ && /pups/bin/pups --stdin < /bootstrap/bootstrap.yml && /sbin/boot

لقد نشرت هذا كـ geoffreychallen:discourse_base:2.0.20191219-2109. (لاحظ أنه ربما يمكنك تحقيق نفس النتيجة عن طريق تعديل أمر التشغيل لصورة Docker الأساسية لـ Discourse، لكنني واجهت صعوبة في جعل ذلك يعمل مع إعادة التوجيه المطلوبة من shell لجعل pups يقرأ ملف الإعدادات.)

إعداد Kubernetes

الآن نحتاج إلى إعداد Kubernetes. يبدو إعدادي كالتالي:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: kotlin-forum-pvc
  namespace: ikp
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 64Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kotlin-forum-deployment
  namespace: ikp
spec:
  replicas: 0
  selector:
    matchLabels:
      app: kotlin-forum
  template:
    metadata:
      labels:
        app: kotlin-forum
    spec:
      volumes:
      - name: kotlin-forum
        persistentVolumeClaim:
          claimName: kotlin-forum-pvc
      - name: bootstrap
        configMap:
          name: kotlin-forum-bootstrap
      containers:
      - name: kotlin-forum
        image: geoffreychallen/discourse_base:2.0.20191219-2109
        imagePullPolicy: Always
        volumeMounts:
        - name: kotlin-forum
          mountPath: /shared/
        - name: bootstrap
          mountPath: /bootstrap/
        ports:
        - containerPort: 80
        env:
        - name: TZ
          value: "America/Chicago"
        - name: LANG
          value: en_US.UTF-8
        - name: RAILS_ENV
          value: production
        - name: UNICORN_WORKERS
          value: "3"
        - name: UNICORN_SIDEKIQS
          value: "1"
        - name: RUBY_GLOBAL_METHOD_CACHE_SIZE
          value: "131072"
        - name: RUBY_GC_HEAP_GROWTH_MAX_SLOTS
          value: "40000"
        - name: RUBY_GC_HEAP_INIT_SLOTS
          value: "400000"
        - name: RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR
          value: "1.5"
        - name: DISCOURSE_DB_SOCKET
          value: /var/run/postgresql
        - name: DISCOURSE_DEFAULT_LOCALE
          value: en
        - name: DISCOURSE_HOSTNAME
          value: kotlin-forum.cs.illinois.edu
        - name: DISCOURSE_DEVELOPER_EMAILS
          value: challen@illinois.edu
        - name: DISCOURSE_SMTP_ADDRESS
          value: outbound-relays.techservices.illinois.edu
        - name: DISCOURSE_SMTP_PORT
          value: "25"
---
apiVersion: v1
kind: Service
metadata:
  name: kotlin-forum
  namespace: ikp
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: 80
  selector:
    app: kotlin-forum
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: ikp
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  name: kotlin-forum-ingress
spec:
  rules:
  - host: kotlin-forum.cs.illinois.edu
    http:
      paths:
      - backend:
          serviceName: kotlin-forum
          servicePort: 80

سيبدو إعدادك مختلفاً. لاحظ أنني أقوم بإنهاء HTTPS في الأعلى، ومن هنا جاءت التعديلات على إعداد Ingress. كما أنني أحب وضع كل شيء في ملف واحد، وحذف الأجزاء التي لا تعمل أثناء التكرار، ثم السماح لـ Kubernetes بتخطي التكرارات في الأمر التالي kubectl create -f. لاحظ أيضاً أنني قمت بتعيين replicas: 0 حتى لا يبدأ النشر فوراً عند إعداده. والسبب في ذلك هو أن لدينا جزءاً إضافياً من الإعدادات يجب إكماله.

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

لاحظ أن لدينا خريطتين لحجمين يشيران إلى الحاوية: الأولى لـ postgres، مُعدة كحجم دائم سيبقى قائماً حتى بعد إعادة تشغيل الـ pod. والثانية هي خريطة إعدادات تم إنشاؤها كالتالي:

kubectl create configmap kotlin-forum-bootstrap --from-file=bootstrap.yml=<path/to/bootstrap.yml>

حيث يجب أن يتطابق kotlin-forum-bootstrap مع إعداد Kubernetes الخاص بك، و path/to/bootstrap.yml هو المسار إلى ملف bootstrap.yml الذي أنشأناه باستخدام launcher dump أعلاه.

بمجرد وضع configmap في مكانه، يجب أن تتمكن من توسيع النشر إلى نسخة واحدة ورؤية Discourse يبدأ في العمل ويشغل نفس عملية التمهيد التي كان من الممكن أن يقوم بها launcher bootstrap. يستغرق ذلك بضع دقائق. عند الانتهاء، سيتم تشغيل تثبيت Discourse.

أجزاء أخرى من الإعداد

بعض الملاحظات الأخرى التي صادفتها أثناء الوصول إلى هذا الإعداد (على الأقل حتى الآن) بشكل كامل:

  • يجب أن تقوم أي وكلاء وسيط (proxies) في الأعلى بتحويل رؤوس X-Forwarded، بما في ذلك X-Forwarded-For و X-Forwarded-Proto و X-Forwarded-Port. عدم القيام بذلك سيؤدي إلى أخطاء غريبة في المصادقة عند محاولة استخدام تسجيل الدخول عبر Google ومزودي تسجيل الدخول الآخرين على الأرجح.
  • يجب أن يكون مُتحكّم nginx Ingress مُعداً لتحويل الرؤوس عن طريق تعيين use-forwarded-headers في خريطة الإعدادات العامة. استغرق مني ذلك وقتاً طويلاً للحصول عليه بشكل صحيح، لأنني عدّلت عدة مرات خريطة الإعدادات الخاطئة، ثم توقعت أن تعيد حاويات ingress تشغيل نفسها عند تغيير خريطة الإعدادات. (لم تفعل ذلك.)

التحديث

لتحديث التثبيت المنشور، قم بتوليد ملف bootstrap.yml الجديد، وحدّث خريطة الإعدادات، ثم أعد تشغيل الحاوية (أسهل طريقة هي التوسيع إلى 0 ثم العودة إلى نسخة واحدة).

هذا يتسبب في توقف قصير للخدمة لأن التمهيد يحدث قبل بناء الحاوية. لكن هذا يبدو حتمياً في الحالات التي تحتاج فيها إلى تحديث الإعدادات و/أو تغيير الصورة الأساسية. يُوثّق launcher rebuild كـ stop; bootstrap; start، مما يعني أن عملية التمهيد ستسبب توقفًا للخدمة حتى لو تم تنفيذها باستخدام سكريبت launcher.

تعليقات

سيكون من الأسهل بكثير دعم نمط نشر Discourse في حاوية ثنائية الحجم إذا قام سكريبت launcher بفصل (أ) خطوات التمهيد التي يمكن تنفيذها دون اتصال وتؤثر فقط على الملفات في الحاوية، و (ب) خطوات التمهيد التي تعدل أو تحتاج إلى الوصول إلى قاعدة البيانات أو الحالة الأخرى خارج الحاوية بشكل أنظف. النهج الموصوف أعلاه محبط قليلاً لأنك ترى جميع أنواع التشويه في JS وتصغير الأصول وأشياء أخرى يمكن القيام بها مع النشر السابق… لكنها مختلطة جداً بأشياء أخرى (مثل ترحيل قاعدة البيانات) التي لا يمكن القيام بها دون الوصول إلى قاعدة البيانات. فكرتُ للحظة في إنشاء حاوية ستقوم فقط بتنفيذ الخطوات في templates/postgres.yml، لكنني لاحظت بعد ذلك أن ترحيل قاعدة البيانات يتم بواسطة قالب الويب، وفكرت في الإضافات، ثم استسلمت :slight_smile:.

مع فصل أفضل، يمكن أن يعمل إعادة النشر للحاويات الثنائية الحجم كالتالي:

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

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

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

نعم، سيكون ذلك رائعًا. لقد طلبت ذلك بالفعل، لكنني لا أعرف ما إذا كان سيتم ذلك ومتى.

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

هذا هو بالضبط ما أفعله في تثبيتات Kubernetes التي قمت بها. لا أستطيع أن أتخيل كيف أو لماذا استخدام k8s دون حاويات منفصلة للبيانات والويب (أو نوع آخر من قواعد بيانات PostgreSQL و Redis الخارجية - التثبيتات التي قمت بها للعملاء تستخدم موارد GCP لذلك).

أيضًا، هناك متغير بيئة يُسمى skip_post_migration_updates تحتاج إلى فهمه للترقيات الحقيقية بدون توقف. يتم وصفه هنا.