Aspnetcore: IIS -> قفل ملف DLL لتطبيق .NET Core

تم إنشاؤها على ٧ يوليو ٢٠١٦  ·  114تعليقات  ·  مصدر: dotnet/aspnetcore

عندما أحاول الكتابة فوق ملف DLL للتطبيق .NET Core باستخدام FTP في الإنتاج ، يتم تأمين ملف خادم IIS ولا يمكن الكتابة فوقه.

لنشر الإصدار الجديد ، أحتاج إلى إيقاف التطبيق في IIS لتحرير القفل ، ثم الكتابة فوقه.
لا يمكن النشر بدون إيقاف التطبيق.

affected-medium area-servers bug servers-iis severity-nice-to-have

التعليق الأكثر فائدة

👍

عدم دعم إعادة التدوير المتداخلة هو تراجع.

ال 114 كومينتر

راجع app_offline.htm: https://docs.asp.net/en/latest/hosting/aspnet-core-module.html#asp -net-core-module-app-offline-htm

إذا انتهى بك الأمر إلى الرغبة في طريقة PowerShell للقيام بذلك ، فيمكنك استخدام أوامر cmdlets لمسؤول IIS: https://technet.microsoft.com/en-us/library/ee790599.aspx

Stop-WebAppPool -Name $appPoolName

... deploy ...

Start-WebAppPool -Name $appPoolName

شكرا للتوضيح Tratcher .
لست متأكدًا مما إذا كان هذا في خططك ولكن هذا عمل مع ASP.NET MVC السابق على ما أعتقد؟
هل تخطط لتنفيذ هذه الميزة أم لا؟

GuardRex لا يمكنني القيام بذلك لأنها بيئة استضافة مشتركة وليس لديها إذن لإيقاف AppPool

هل يمكنك جعله يعمل مع msdeploy.exe و Azure؟ إذا فهمت بشكل صحيح ، يحتاج موقع الويب إلى إعادة التشغيل لمنع قفل الملف. -enableRule:AppOffline يعمل ولكن الموقع بأكمله غير متصل لبضع دقائق وهي ليست تجربة مستخدم رائعة خاصةً لأننا ننشر عدة مرات في اليوم.

راجع أيضًا http://stackoverflow.com/q/40276582/14131

chuchuva ربما ، لكن كل السحر يأتي بتكلفة. الإصدارات السابقة من ASP.NET قامت ببعض النسخ الاحتياطية المعقدة للتغلب على مشاكل تأمين الملف.

سحر أم لا ، لقد عملت بشكل جيد. أفتقده الآن كيندا بعد الترحيل إلى ASP.NET core ...

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

👍

عدم دعم إعادة التدوير المتداخلة هو تراجع.

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

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

نود أن نعرف أيضا. لا يعمل وضع app_offline.htm داخل دليل التطبيقات.

لقد أدركت للتو هذه "الميزة" بعد نقل عدد من المواقع إلى AspNetCore. لا أصدق أنه من المقبول جعل مواقعك في وضع عدم الاتصال لبضع دقائق في كل مرة تريد النشر!

هذا سيء بما يكفي بالنسبة لي كقائد لفريق صغير - هل أولئك الذين يمارسون التكامل المستمر على نطاق واسع يتجنبون AspNetCore؟ لا توجد طريقة سيتمكنون من جعل موقعهم غير متصل بالإنترنت لدقائق كل ساعة لإعادة النشر!

هل تقوم بنشر استخدام FTP أو xcopying؟ أم أنك تستخدم webdeploy؟

أنا أنشر عبر webdeploy إلى IIS.

نحن نعمل حاليًا على حل هذه المشكلة عن طريق حذف web.config أولاً (قتل التطبيق فعليًا) ، ولكن هذا ليس حلاً مقبولًا حقًا على المدى الطويل.

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

تضمين التغريدة

أنا أنشر عبر webdeploy إلى IIS.

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

تضمين التغريدة

نحن نعمل حاليًا على حل هذه المشكلة عن طريق حذف web.config أولاً (قتل التطبيق فعليًا) ، ولكن هذا ليس حلاً مقبولًا حقًا على المدى الطويل.

هل هذا يعني أنك تقوم بنشر xcopy؟

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

شكرًا ديفيد - يبدو أفضل مما أفعله حاليًا (إيقاف الموقع وتجمع التطبيقات يدويًا في IIS). هل يمكنك الإشارة إلى بعض الموارد حتى أتمكن من التحقيق في الآثار المترتبة على هذه الأساليب؟

DaleMckeown بعض المعلومات حتى أجد مصدر الحقيقة https://github.com/Microsoft/vsts-tasks/issues/5259#issuecomment -346202503

davidfowl نحن نستخدم webdeploy للنشر إلى خادم الاختبار الخاص بنا (والذي لا يسبب لنا أي مشاكل أبدًا) ، ومن هناك نقوم بنسخ الملفات إلى خوادمنا المباشرة باستخدام robocopy

تضمين التغريدة

نحن نعمل حاليًا على حل هذه المشكلة بمجرد حذف web.config أولاً

عند إزالة web.config من النشر ، سيقوم IIS بخدمة الملفات الحساسة من النشر. يمكن للمهاجم ببساطة طلب الملفات باستمرار ليلًا ونهارًا حتى تفتح نافذة الثلاثينيات الخاصة بك.

تضمين التغريدة

باتباع نصيحة ajeckmans إذا كنت ترغب في الدخول في الأعشاب قليلاً باستخدام نهج PS للقيام بذلك ، يمكنك كتابة نص العملية لإسقاط AppPools واحدًا تلو الآخر للنشر عبر مزرعة ويب. للحصول على مثال حول كيفية القيام بذلك ، راجع https://github.com/guardrex/aspnetcore-iis-ps-publish ... ولكن انظر فقط إلى ذلك كمثال تجريبي وليس نصًا بجودة الإنتاج. لم ألعب بهذا منذ فترة ، لكن ينبغي (من الناحية النظرية) أن يستمر.

guardrex أنت على حق. نقوم بنسخ app_offline.htm إلى dir أولاً ، ثم نقوم بإعادة إحياء web.config ، والنسخ فوق التطبيق ، وإعادة إنشاء web.config وإزالة app_offline (كل ذلك باستخدام برنامج نصي ofc). لسوء الحظ ، فإن مجرد وضع ملف app_offline في دليل مواقع الويب لا يؤدي إلى كسر قفل ملفات dlls. نحتاج إلى إزالة web.config ، وهو إجراء لا نحتاج إلى القيام به مع تطبيقات asp.net الكاملة القديمة (مثل نماذج الويب القديمة وما إلى ذلك)

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

  1. أضف app_offline.htm .
  2. تأكد من أن الموقع يقدم app_offline.htm .
  3. اسحب web.config للخارج.
  4. طلب ملف حساس (على سبيل المثال ، http://localhost:<PORT>/<ASSEMBLY_NAME>.deps.json ... استبدال PORT و ASSEMBLY_NAME).
  5. يجب أن يتم تقديم ملف deps.json إذا كانت وحدة الملفات الثابتة تعمل بشكل صحيح.

لا أثق بمجرد إزالة الوحدة مع ...

<configuration> 
 <system.webServer> 
   <modules> 
     <remove name="StaticFileModule" /> 
   </modules> 
 </system.webServer> 
</configuration>

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

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

مناقشة لـ: نشر تغييرات IIS على موقع web.config (IISIntegration 158)
تحقق الرأس عند إعادة web.config إلى wwwroot (IISIntegration 164)

[تحرير] ربما يكون هذا هو الحل البديل (غير المدعوم): انقل web.config مرة أخرى إلى wwwroot ، ثم تابع ما تفعله. لا يزال ... نوعًا ما مخيف كسر التطبيق للحصول عليه في وضع عدم الاتصال بهذا الشكل.

هذا يجعلني أكثر حيرة بشأن ما يجب أن نفعله في هذه الحالة.

ما هي التوصية الحالية لنشر مواقع AspNetCore على IIS عبر نشر الويب بطريقة يمكن أن تتجنب تأمين الملفات؟

أود أن أعرف أيضا. لقد أصبح هذا عقبة رئيسية أمام اعتماد .net الأساسي في مؤسستي.

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

هل هناك أي حل بديل في .Net Core يمكنني من خلاله نشر Build / DLLs الجديد دون إيقاف App Pool؟

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

شكرا مقدما على أي مساعدة.

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

@ shahjay748 لا تحبس أنفاسك. إذا كنت سأعيد العملية هنا ، كنت سأضع التطبيق داخل حاوية وقمت فقط بوضع حاوية جديدة وتبديل حركة المرور وسحب الإصدار القديم لأسفل (أو شيء من هذا القبيل). يبدو أن الشيء الأكثر عقلانية الذي يجب القيام به ورؤيته. net الأساسية تدور حول طرق حديثة جديدة ، مجرد دفع إصدار جديد من التطبيق ببساطة عن طريق نسخ الملفات الجديدة يعتبر (وأنا أوافق) طريقة غامضة بعض الشيء للقيام بذلك.
أي من الـ OFC لا يساعدنا الذين ، في الوقت الحالي ، عالقون في العمليات القديمة :)

ما أفعله (لست متأكدًا مما إذا كانت طريقة مناسبة للقيام بذلك) ، هو تحميل الموقع إلى دليل آخر وتبديل المسارات في iis

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

إضافة ملاحظة جانبية سريعة لـ PowerShell إلى ذلك: يتم دائمًا تحرير القفل الموجود على مجموعة التطبيق ... لم أواجه مشكلة أبدًا ( حتى الآن! lol).

@ bpetersen1 ... نهج "التدريج" الذي تتبعه له تأثير جانبي

لا يزال هذا مزعجًا من الخارج ... لقد قررنا النظر في حل عامل الإرساء من أجل ترك IIS وراءنا.

سأقوم بتجربة حل. ترقب.

أواجه هذه المشكلة أيضًا باستخدام FTP.

ما هي نصيحة مايكروسوفت في هذا الشأن ؟!

أواجه هذه المشكلة أيضًا باستخدام FTP.

ما هي نصيحة مايكروسوفت في هذا الشأن ؟!

قم بإسقاط ملف appoffline.htm ، ثم ftp ثم قم بإزالته.

davidfowl نعم ، هذا ما أفعله الآن بملف دفعي. يستغرق IIS بضع ثوانٍ لتحرير القفل ثم نسخ الملفات.

شكرا - بدافع الاهتمام كيف تقوم بتشغيل الملف الدفعي؟

أيضا ارتباط إلى وثائق MIcrosoft على هذا التقدير ؛ لا يمكنني العثور عليه على جوجل. شكر.

@ niico100 بشكل أساسي ، يقوم الملف الدفعي بنسخ الملفات إلى دليل المشروع ، وأنا أقوم بإنشاء مجلد نسخ احتياطي أولاً قبل القيام بذلك. قبل نسخ الملفات ، ضع appoffline.htm في مجلد المشروع.
بسبب مشكلة قفل الملف ، ستؤدي عملية النسخ إلى إعادة المحاولة. أنا أستخدم ملف robcopy
https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy

إذا كان أي شخص مهتمًا ، يقوم البرنامج النصي التالي بتنزيل ملف النشر من appveyor ، وإيقاف الموقع ، ونسخ الملفات المطلوبة وإعادة الموقع احتياطيًا:
https://github.com/IsraelHikingMap/Site/blob/master/Scripts/Deploy.ps1
يتطلب تشغيله كمسؤول بالرغم من ذلك. نأمل أن يحل عامل الميناء كل هذا اللامعنى بمجرد أن نهاجر ...
يستخدم أوامر بوويرشيل Stop-WebAppPool و Start-WebAppPool .

بالنسبة للتنمية المحلية ، لم يعمل WebDeploy بالنسبة لي. حاولت استخدامه ضد IIS المحلي الخاص بي وسيظل يشكو من الملفات المقفلة. حاولت بعد ذلك استخدام الكوماندليتس أعلاه من أحداث إنشاء VS Pre / Post ، لكن هذا لم ينجح أيضًا ، ربما لأن بوويرشيل 32 بت لا يمكنه تعديل إعدادات IIS 64 بت؟ على أي حال ، ما يبدو أنه يعمل بشكل جيد هو الأحداث التالية قبل / بعد الإنشاء في مشروع ASP.Net Core الخاص بي:

حدث ما قبل الإنشاء:
echo "App Offline" /a > $(ProjectDir)app_offline.htm

حدث ما بعد البناء:
del $(ProjectDir)app_offline.htm

لقد اطلعت بإيجاز على الشكاوى هنا ، ويبدو أن هذا قد يكون ذا صلة. لقد نقلت عمليات نشر CI لنا من نشر الأخطبوط الذي "نجح للتو" بشكل عام ، إلى استخدام إصدارات azure devops مع مهام إدارة iis ، وما زلت تواجه مشكلات في نشر الملفات الجديدة لأنه حتى بعد إيقاف تطبيق الويب ، وإيقاف نطاق التطبيق ، لا تزال الملفات في استعمال.

من المحتمل أن يكون هذا واحدًا على الأقل من كل 3 عمليات نشر ولم يتم العثور على أي شيء يبدو أنه يجعل الموقف أفضل أو ينشر أكثر موثوقية.

Tratcherronnyek ويبدو أن لهما صلة البصائر التطبيق. بعد إضافة ملحق App Insights ، أصبح من الصعب جدًا تحديث تطبيق الويب DLL الخاص بي فجأة ، ولا توجد مشاكل قبل ذلك.

قد يكون هذا منطقيًا نظرًا لأن App Insights يجب أن يربط ملف التعريف بعملية التطبيق وملفات DLL.

ما زلت أواجه هذه المشكلة. أستخدم القاعدة nable -e

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

آخر وقت تشغيل أساسي لـ asp.net مثبت -> aspnetcore.dll 12.1.18263.2

أيه أفكار ؟ لا يزال خطأ؟

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

حسنًا ، أفهم ذلك ولكني أستخدم القاعدة -e nable: خيار

ما زلت أرى هذه المشكلة أيضًا. تشير ملفات مشروعي إلى Microsoft.NET.Sdk.Web ، لكنها لا تسقط ملف app_offline.htm تلقائيًا ، كما هو مذكور في المستندات.

اهلا ياجماعة. لا تزال هذه المشكلة المزعجة موجودة مع Core 2.1 مع أحدث إصدار من VS 2017. أستخدم appoffline ، ولا يزال ملف dll الرئيسي للمشروع دائمًا هو الاستخدام ويتطلب إيقافًا كاملاً للموقع أثناء النشر. هذا يجعل نشر الويب عديم الفائدة تقريبًا.

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

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

# Setup
Import-Module WebAdministration
# create 2 site root directories
$a = 'C:\inetpub\AspNetCoreSampleA'
$b = 'C:\inetpub\AspNetCoreSampleB'
$siteRoot = 'C:\inetpub\aspnetcoresample'
$siteName = 'AspNetCoreSample'
$poolName = "aspnetcore"
New-Item -Type Directory $a
New-Item -Type Directory $b
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $siteRoot -Target $a
# point the site root to the symlink
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot
# make sure it get's picked up
Restart-WebAppPool -Name $poolName

# this tells you the active side
Get-Item -Path $siteRoot | Select-Object -ExpandProperty target

# Flip the symlink
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
# at this point w3wp.exe still locks the current target folder until it's getting recycled
# Deploy new version to the symlink which is now pointing to the other side which should have no locks
robocopy \\myshare\myapp $siteRoot /mir
# recycle app pool, so it picks up the new files
Restart-WebAppPool -Name $poolName

# bonus point: rollback is easy
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
Restart-WebAppPool -Name $poolName

هنا الجوهر
https://gist.github.com/csharmath/b2af0f50700ce9fbdd8c5c3e582fd41b

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

هذا يشبه إلى حد ما النسخ الاحتياطي ، ولكن ليس بنسبة 100٪ حيث يوفر سيناريو xcopy أفضل بكثير وسلاسة.

حتى الآن يبدو أنه أفضل نهج يمكنني التفكير فيه إذا كنت بحاجة إلى البقاء متصلاً بالإنترنت أثناء الإصدار.

csharmath أو هناك عامل ميناء على ما أعتقد. لا يوجد ملف مقفل بهذه الطريقة ، ويمكنه إجراء تحديثات متدرجة عبر swarm / k8s / okd

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

متفق عليه csharmath ، ولكن في هذه المرحلة ، نظرًا لأن هذه كانت مشكلة غير مثبتة لمدة _ سنوات_ ، فقد يتكيف ويتبع الحلول التي تعمل بدلاً من انتظار معجزة ...

لقد استخدمت البرنامج النصي أدناه power shell وتم تعطل IIS وحتى أثر على تجمع التطبيقات الآخر ، ولا بد لي من إعادة تشغيل الجهاز لاستعادة كل شيء ... هذه العملية حصلت عليها من المستند الرسمي وكانت كارثة ، لذلك نصيحتي هي عدم استخدام app_offline.htm بشكل أفضل لاستخدام البرنامج النصي الذي يوقف تجمع التطبيقات وبدء تشغيله مرة أخرى ، واحد وجدته في هذا الموضوع https://github.com/IsraelHikingMap/Site/blob/master/Scripts/Deploy .ps1

$pathToApp = 'G:\prod_web_core'
$pathToAppOfflineHtml = 'G:\prod_web_core\app_offline.htm'
# Stop the AppPool 
New-Item -Path $pathToApp -Name app_offline.htm
# Provide script commands here to deploy the app
Copy-Item "G:\prod_web_core_temp\*" -Destination $pathToApp -Recurse -Force
# Restart the AppPool 
Remove-Item -Path $pathToAppOfflineHtml
Get-ChildItem -Path "G:\prod_web_core_temp" -Recurse | Remove-Item -Force

أنا مجبر على إيقاف تطبيق AppPool للنجاح في النشر ، فهل أي تقدم لإصلاح هذه المشكلة ؟!

لقد واجهنا للتو مشكلة قفل الملف هذه للمرة الأولى اليوم لأن dotnet core جديد بالنسبة لنا. لقد فوجئت بهذا. من المضحك أن السبب الأول الذي جعلني أتحول من Classic ASP إلى .NET Framework بينما كان لا يزال في مرحلة تجريبية (~ 2001) هو أنني أستطيع إجراء عمليات نشر ساخنة بدون قفل الملفات. كان رائعا! بعد 18 عامًا تقريبًا وأنا الآن أعود إلى حيث بدأت. حسنا، يمكنك كسب بعض، تخسر بعض.

لقد أشرت للتو إلى مشكلتي ... في حالتي نشر حلول Blazor Server Side ....
قبل يومين ، كان الأمر بمثابة كابوس ... لقد قمت بتطبيق app_offline.htm ، وقمت بإغلاق تجمعات التطبيقات ، وحتى أعدت تشغيل مثيلات IIS بأكملها لإلغاء تأمين الملفات. كانت الخطوة التالية هي إعادة تشغيل الخادم بالكامل ، لحسن الحظ بعد 5 دقائق ، تم فتح ملفات dlls.

2019 ، نفس المشكلة بالنسبة لي ... وتم إنشاء هذه المشكلة في عام 2016 ... ما زالت بلا حل؟

2020 ، نفس المشكلة بالنسبة لي. أكبر انحدار على الإطلاق. صافي 4.6 تقوم باستبدال ملفات dll والتطبيق يعيد تحميل الإصدار الجديد. . صافي النواة يقفل كل شيء.

bladefist أفضل رهان حتى اليوم هو استخدام حاويات Docker: /

شكراً kanadaj ولكن

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

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

kanadaj نعم ، نحن نستخدم موازن التحميل ويقوم بتحديث الخوادم خطيًا. لا توقف.

لقد حققنا النجاح مع

  • استخدام رابط رمزي لدليل التطبيق
  • انشر نسخة جديدة من الموقع إلى دليل مختلف
  • تحديث الارتباط الرمزي
  • أعد تشغيل تجمع التطبيقات باستخدام الأداة المساعدة appcmd التي تأتي مع IIS.

davidglassborow هذا حل مبتكر. شكر. لا يجب أن تكون هناك حاجة إلى حلول إبداعية.

من واقع خبرتي ، لم تكن الآلية القديمة موثوقة عندما كان الموقع قيد التحميل على أي حال ، وانتهى بنا الأمر إلى وضع وكلاء عكسيين على أي حال لمواقع حركة المرور العالية.

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

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

davidfowl الخطة التي حددتها لـ 5.0 تبدو رائعة. نحن نستخدم إطار العمل كما أنا متأكد من أن معظم الناس يفعلون.

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

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

لديك موازن تحميل. لا يمتلك الكثير من الأشخاص موازن تحميل ويقومون فقط باستبدال الملفات الموجودة على القرص في مكانها ويتوقعون السحر 😄

davidfowl صحيح لكن حركة مرور عالية + لا رطل = متهور

أيضًا ، المشكلة مع app_offline.htm هي أنه من خلال عملية ci / cd ، ليس لديك أي فكرة عن وقت توقف العملية. كان علينا إضافة أمر نوم ضخم بين إنشاء هذا الملف ودفع مكتبات DLL لأن عملية إيقاف التشغيل لدينا يمكن أن تستغرق من 1 ثانية إلى دقيقة واحدة. عندما تقوم بموازنة تحميل الكثير من الحالات ، تضيف فترات النوم هذه دورة نشر طويلة جدًا.

تحميل الملفات في الذاكرة يصلح كل ذلك بالرغم من ذلك.

davidfowl صحيح لكن حركة مرور عالية + لا رطل = متهور

👍

أيضًا ، المشكلة مع app_offline.htm هي أنه من خلال عملية ci / cd ، ليس لديك أي فكرة عن وقت توقف العملية. كان علينا إضافة أمر نوم ضخم بين إنشاء هذا الملف ودفع مكتبات DLL لأن عملية إيقاف التشغيل لدينا يمكن أن تستغرق من 1 ثانية إلى دقيقة واحدة. عندما تقوم بموازنة تحميل الكثير من الحالات ، تضيف فترات النوم هذه دورة نشر طويلة جدًا.

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

أيضًا ، المشكلة مع app_offline.htm هي أنه من خلال عملية ci / cd ، ليس لديك أي فكرة عن وقت توقف العملية.

حسنًا ، يستخدم ناشر VS فترات نوم قصيرة وعدة محاولات.

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

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

Socolin لماذا هذا؟

Socolin لماذا هذا؟

إذا فهمت جيدًا (أخبرني إذا كنت مخطئًا) ، فما تخطط له هو إزالة القفل ، ولكن لا يزال يتعين علينا إنشاء appoffline للإشارة إلى IIS لإعادة استخدام التطبيق بعد اكتمال الإنشاء؟

للقيام بذلك ، سيتعين علينا إنشاء ملف appoffline ، ثم حذفه. هل يمكن حذف هذا الملف فور إنشاء الملف؟ أم يجب أن ننتظر IIS ليكتشفها؟ إذا كان مطلوبًا الانتظار قليلاً ، فأنا أفضل الحصول على ملف مثل "apprestart" الذي سيستمع إليه IIS ، ثم احذفه بمجرد اكتشافه وبدء إعادة التدوير.


أيضا ، لدي سؤال آخر في ذهني. يتعلق الأمر بتحسين IDE أثناء البناء.

حاليًا ، أعمل في مشروع يستخدم التبعيات ، ولدينا مشروع لطبقة الويب ومشروع آخر للمنطق والبيانات. عندما أعمل في طبقة الويب (ASP.NET Core) أثناء الإنشاء ، أقوم بإنشاء ملف appoffline قبل الإنشاء ، ثم أحذفه بعد الإنشاء ويعمل.

ولكن عندما أعمل في إحدى التبعيات ، عندما أقوم ببنائها من Rider (أعتقد أنه يفعل الشيء نفسه على VS لأنه يستخدم msbuild) عندما أنظر إلى ما يفعله msbuild ، بمجرد اكتمال بناء المشروع ، dll مباشرة في دليل ويب / بن دون إعادة بناء مشروع ويب. ونظرًا لأن ملف dll مغلق ، فإنه يفشل بصمت ، ولا بد لي من إعادة تشغيل الخادم يدويًا أو تشغيل إنشاء مشروع الويب.

هل لديك أي حل لهذا؟
حتى الآن الحل الذي وجدته هو كتابة برنامج صغير يعمل في الخلفية يبحث عن / bin في مشروع الويب الخاص بي. عند تغيير ملف ، يقوم بإنشاء ملف appoffline في الدليل حيث يستمع IIS ، ثم نسخ الملفات المعدلة ، ثم حذف appoffline. لكن يبدو الأمر صعبًا بعض الشيء ، لكن حتى الآن هذه هي الطريقة الوحيدة التي وجدتها لأتمكن من البناء ، ثم اضغط على F5 واختبر تغييراتي.

للقيام بذلك ، سيتعين علينا إنشاء ملف appoffline ، ثم حذفه. هل يمكن حذف هذا الملف فور إنشاء الملف؟ أم يجب أن ننتظر IIS ليكتشفها؟ إذا كان مطلوبًا الانتظار قليلاً ، فأنا أفضل الحصول على ملف مثل "apprestart" الذي سيستمع إليه IIS ، ثم احذفه بمجرد اكتشافه وبدء إعادة التدوير.

كيف يعرف IIS عندما تنتهي من نسخ الملفات؟ كيف تتجنب الدولة الممزقة؟ تحتاج أساسًا إلى الإشارة إلى بدء المعاملة وإتمامها. يشير إنشاء الملف إلى البداية ويشير الحذف إلى النهاية.

هل لديك أي حل لهذا؟
حتى الآن الحل الذي وجدته هو كتابة برنامج صغير يعمل في الخلفية يبحث عن / bin في مشروع الويب الخاص بي. عند تغيير ملف ، يقوم بإنشاء ملف appoffline في الدليل حيث يستمع IIS ، ثم نسخ الملفات المعدلة ، ثم حذف appoffline. لكن يبدو الأمر صعبًا بعض الشيء ، لكن حتى الآن هذه هي الطريقة الوحيدة التي وجدتها لأتمكن من البناء ، ثم اضغط على F5 واختبر تغييراتي.

يعالج VS هذا عن طريق إنهاء العملية قبل بدء ملف جديد لأي شيء قد يؤثر على بناء هذا المشروع.

للقيام بذلك ، سيتعين علينا إنشاء ملف appoffline ، ثم حذفه. هل يمكن حذف هذا الملف فور إنشاء الملف؟ أم يجب أن ننتظر IIS ليكتشفها؟ إذا كان مطلوبًا الانتظار قليلاً ، فأنا أفضل الحصول على ملف مثل "apprestart" الذي سيستمع إليه IIS ، ثم احذفه بمجرد اكتشافه وبدء إعادة التدوير.

كيف يعرف IIS عندما تنتهي من نسخ الملفات؟ كيف تتجنب الدولة الممزقة؟ تحتاج أساسًا إلى الإشارة إلى بدء المعاملة وإتمامها. يشير إنشاء الملف إلى البداية ويشير الحذف إلى النهاية.

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

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

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

لقد نجحت في النشر الموضعي (https://flukefan.github.io/ZipDeploy/) ، بواسطة:

  1. إعادة تسمية ملفات التجميع (يبدو أنه يمكنك إعادة تسميتها على الرغم من أنه لا يمكنك الحذف / الكتابة فوقها) ؛
  2. نسخ في التجميعات الجديدة ؛
  3. المس تهيئة الويب لإعادة تشغيل التطبيق ؛
  4. احذف التجميعات القديمة المعاد تسميتها.

لا موازنة التحميل مثيل IIS واحد ؛ لا توجد تغييرات IIS. YMMV.

FlukeFan كيف تتجنب عمليات النشر الممزقة أم أن هذا لا يمثل مصدر قلق لك؟ بالتمزق ، أعني أن هناك نافذة حيث يقوم التطبيق القديم بتحميل مجموعة جديدة قبل إعادة تشغيل التطبيق.

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

عادةً ما أقوم بإيقاف تشغيل جميع خيارات إعادة التدوير التلقائية الأخرى ، وكذلك تعطيل المحذوفات المتداخلة في إعدادات تجمع تطبيقات IIS.

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

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

أفكار؟

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

لذلك أنا لا أتعامل مع هذه القضية الآن. https://github.com/FlukeFan/ZipDeploy/blob/master/ZipDeploy/ZipDeploy.cs#L55

أعتقد أنه يمكنني القيام بالأشياء بترتيب مختلف ، ولمس web.config أولاً ، وانتظر انتهاء جميع الطلبات "الأخرى" ، ثم أخيرًا قم بالاستبدال قبل انتهاء الطلب الأخير مباشرة (بينما يقوم IIS بإعداد قائمة انتظار الطلبات الجديدة لـ about-to- إعادة تدوير مجموعة التطبيقات) ، ولكن ما لدي يعمل الآن بالنسبة لي.

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

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

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

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

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

أفكار؟

هذا هو للتشغيل على خادم CI ، أقول؟ كيف ستدفع الثنائيات / الحزمة إلى الخادم الوجهة؟

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

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

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

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

قد تكون أداة dotnet العالمية هي الحل هنا.

هذا هو للتشغيل على خادم CI ، أقول؟ كيف ستدفع الثنائيات / الحزمة إلى الخادم الوجهة؟

لا أعتقد ذلك. من المحتمل أن يدعم FTP ونسخة مباشرة. أي شيء آخر غير ذلك هو على الأرجح بروتوكول احتكاري.

نأمل عندما تقول FTP ، فأنت تعني أيضًا بروتوكول نقل الملفات على ssh. هذه الأداة ستكون ذات قيمة كبيرة.

نأمل عندما تقول FTP ، فأنت تعني أيضًا بروتوكول نقل الملفات على ssh. هذه الأداة ستكون ذات قيمة كبيرة.

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

هل تقوم أيضًا باستخدام SSHing في أجهزة Windows لنشرها في IIS؟

davidfowl الرجاء إضافة دعم (الاشتراك)
https://github.com/dotnet/aspnetcore/issues/3719#issuecomment -473183712
https://github.com/dotnet/aspnetcore/issues/3793#issuecomment -335666414

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

علاوة على ذلك ، لن يسمح لنا ذلك بتحقيق صفر توقف.

لا توجد طريقة نستثمر فيها في ميزة مثل هذه مع الوحدة.

نأمل عندما تقول FTP ، فأنت تعني أيضًا بروتوكول نقل الملفات على ssh. هذه الأداة ستكون ذات قيمة كبيرة.

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

هل تقوم أيضًا باستخدام SSHing في أجهزة Windows لنشرها في IIS؟

نعم. نحن نستخدم CI / CD وننشر في كل من أنظمة Linux و windows ، لذلك كان من الأسهل بكثير تثبيت opensh على مربعات windows.

$ appPool = "افتراضي"
Stop-WebAppPool -Name $ appPool -Passthru
... نشر...

ابدأ-WebAppPool -Name $ appPool -Passthru
...نشر.....

أو

$ appPool = "افتراضي"
Stop-WebAppPool -Name $ appPool
while ((Get-WebAppPoolState -Name $ appPool) .Value -ne 'Stop') {
النوم 1 #second
}
... نشر...

Start-WebAppPool -Name $ appPool
while ((Get-WebAppPoolState -Name $ appPool) .Value -ne "Start") {
النوم 1 #second
}

...نشر.....

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

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

https://github.com/dotnet/aspnetcore/issues/3793#issuecomment -459870870

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

  • العميل -> الخادم ما هو المجلد / المشاركة غير النشطة؟
  • S: هنا
  • ج: حسن النشر / النسخ
  • S: تم تبديل الرابط الرمزي وإعادة تدويره
  • C: thx ، http get / healthcheck (اختياري)
  • C: آه لقد أفسدت ، رجاء التراجع
  • S: تبديل الارتباط الرمزي وإعادة التدوير (العودة إلى العمل)
  • ج: كرر الشطف حتى يعمل

لذلك أواجه أيضًا مشكلة في نشر الملفات المقفلة. في استضافة التطبيق. لقد حاولت إيقاف مجموعة التطبيقات ، لكن الملفات تظل مغلقة. لقد حاولت الضغط على app_offline.htm ولكن لم يحالفني الحظ أيضًا.
أيضًا إذا كنت تريد إدارة حذف ملف app_offline.htm ، فيمكنك القيام بذلك بسهولة عند بدء تشغيل التطبيق في program.cs. بهذه الطريقة نعرف متى يبدأ يصبح على الإنترنت.

المشكلة الحالية التي أواجهها هي أن ملفات dll معلقة وحتى إذا قمت بإعادة التسمية ثم نسخها فوق الملفات ، فإن العملية القديمة لا تزال تعمل مقابل الملفات المعاد تسميتها. لذلك عندما نحاول النشر مرة أخرى ، سيتعين علينا إعادة تسمية الملفات مرة أخرى. أعتقد أنه يمكننا استخدام تاريخ من نوع ما كبديل لاسم الملف.
نحن نستخدم dotnet core 3.1.4 لهذا على خادم IIS windows 2012 R2

foxjazz كيف تظل الملفات مقفلة إذا تم إيقاف تجمع التطبيقات / العملية؟ هل يمكنك التفصيل؟

foxjazz كيف تظل الملفات مقفلة إذا تم إيقاف تجمع التطبيقات / العملية؟ هل يمكنك التفصيل؟

image

image

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

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

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

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

ما سيكون لطيفًا حقًا هو أن يكون لديك طريق لإيقاف العملية.
api \ KillDeathKill
{
Program..KillProcess ()
}

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

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

عندما تقول app_offline.htm معطل أولاً. نعم كان هذا هو الحال. لدي مسار حالة يمكنني التحقق منه عبر طلب الحصول عليه ، وهو بالتأكيد غير متصل بالإنترنت. كما أوقف تجمع التطبيقات يدويًا. لكن عملية w3wp كانت لا تزال معلقة في الملف.

ثم لم تتوقف العملية

ثم لم تتوقف العملية

فلماذا يتم إيقاف تجمع التطبيقات وليس إيقاف العملية؟

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

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

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

منذ حوالي عام ، قمت بنشر معلومات حول إنشاء مجلدين جذر للموقع ولدينا رابط رمزي إلى أحدهما تم تكوينه لموقع IIS.

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

CJHarmath - لقد

إذا قمت بنشر تحديثات أكثر ، فقد أفكر في أداة نشر العميل ، ولكن في الحقيقة يمكنني فقط استخدام shell البعيد في خادم IIS وتنفيذ البرنامج النصي بوويرشيل لقلبه.
psexec.exe \\IIS_SERVER cmd /c "powershell -noninteractive -file C:\FlipSymLink.ps1"

# FlipSymLink.ps1

Import-Module WebAdministration # run as admin
# create 2 site root directories
$a = 'C:\Websites\MySiteA'
$b = 'C:\Websites\MySiteB'
$appRoot  = 'C:\Websites\MySite'
$appName  = 'MyAppName'
$siteName = 'MySiteName'
$poolName = "MyPoolName"
New-Item -Type Directory $a -ErrorAction SilentlyContinue
New-Item -Type Directory $b -ErrorAction SilentlyContinue

# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $appRoot -Target $a -Name A -ErrorAction SilentlyContinue
New-Item -Type SymbolicLink -Path $appRoot -Target $b -Name B -ErrorAction SilentlyContinue

# point the site root to the symlink
$currentPath = (Get-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath)
if ($currentPath -eq "$appRoot\A") {
    Write-Host "Switched to B"
    New-Item $b\active.txt
    Remove-Item $a\active.txt
    Set-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath -value $appRoot\B
} else {
    Write-Host "Switched to A"
    New-Item $a\active.txt
    Remove-Item $b\active.txt
    Set-ItemProperty "IIS:\Sites\$siteName\$appName" -name physicalPath -value $appRoot\A
}
Restart-WebAppPool -Name $poolName

psexec.exe \\IIS_SERVER cmd /c "powershell -noninteractive -file C:\FlipSymLink.ps1"

يمكن أيضًا تحويل هذا إلى نقطة نهاية JEA ، بحيث يمكنك تشغيل flip كمستخدم غير متقدم (على سبيل المثال ، من خطوة نشر خادم CI / CD)
Invoke-Command -ComputerName IIS_SERVER -ConfigurationName IIS-Flip -ScriptBlock { Switch-SymlinkTarget -SiteName MySite}
تم استخدام Switch بدلاً من Flip لأنه فعل معتمد.

تم استخدام Switch بدلاً من Flip لأنه فعل معتمد.

Swap هو الفعل المستخدم عند التعامل مع الفتحات - هل من المنطقي إعادة استخدامه هنا على ما يبدو؟

تواجه نفس المشكلة كما تم وصفها قبل 4 سنوات.
أي تحديث على هذا؟
لا أريد فتح سطح المكتب البعيد لتحميل ملف dll جديد.

عندما أحاول الكتابة فوق ملف DLL للتطبيق .NET Core باستخدام FTP في الإنتاج ، يتم تأمين ملف خادم IIS ولا يمكن الكتابة فوقه.

لنشر الإصدار الجديد ، أحتاج إلى إيقاف التطبيق في IIS (فتح RDP) لتحرير القفل ، ثم الكتابة فوقه.
لا يمكن النشر بدون إيقاف التطبيق.

أي حل لهذا؟ شكر

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

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

# Setup
Import-Module WebAdministration
# create 2 site root directories
$a = 'C:\inetpub\AspNetCoreSampleA'
$b = 'C:\inetpub\AspNetCoreSampleB'
$siteRoot = 'C:\inetpub\aspnetcoresample'
$siteName = 'AspNetCoreSample'
$poolName = "aspnetcore"
New-Item -Type Directory $a
New-Item -Type Directory $b
# create a symlink to targeting one side
New-Item -Type SymbolicLink -Path $siteRoot -Target $a
# point the site root to the symlink
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $siteRoot
# make sure it get's picked up
Restart-WebAppPool -Name $poolName

# this tells you the active side
Get-Item -Path $siteRoot | Select-Object -ExpandProperty target

# Flip the symlink
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
# at this point w3wp.exe still locks the current target folder until it's getting recycled
# Deploy new version to the symlink which is now pointing to the other side which should have no locks
robocopy \\myshare\myapp $siteRoot /mir
# recycle app pool, so it picks up the new files
Restart-WebAppPool -Name $poolName

# bonus point: rollback is easy
$current = (Get-Item -Path $siteRoot).Target
$newTarget = if ($current -eq $a) {$b} else {$a}
New-Item -Type SymbolicLink -Path $siteRoot -Target $newTarget -Force
Restart-WebAppPool -Name $poolName

هنا الجوهر
https://gist.github.com/csharmath/b2af0f50700ce9fbdd8c5c3e582fd41b

هذا شيء أعتقد أنني حصلت على مكامن الخلل. لقد جربت فقط مع ASP.NET ، لكنني أتخيل أنه سيعمل بنفس الطريقة مع وحدة الاستضافة لـ net core. يوقف التشغيل الأول الموقع قليلاً ، لذا قد ترغب في تعديل ذلك قليلاً.

param(
    [Parameter(Mandatory = $true)]
    [string] $iisRootPath,
    [Parameter(Mandatory = $true)]
    [string] $siteName,
    [Parameter(Mandatory = $true)]
    [string] $artifactPath,
    [Parameter(Mandatory = $false)]
    [string] $siteFolderName,
    [Parameter(Mandatory = $false)]
    [string] $appPoolName
)
Import-Module WebAdministration

#populate optional parameters
if (!($PSBoundParameters.ContainsKey('siteFolderName'))) { $siteFolderName = $siteName }
if (!($PSBoundParameters.ContainsKey('appPoolName'))) { $appPoolName = $siteName }

#set A, B and C paths
$a = "$iisRootPath\$siteFolderName" + 'A'
$b = "$iisRootPath\$siteFolderName" + 'B'
$c = "$iisRootPath\$siteFolderName"

#existence test
$cName = Get-Item -Path $c -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
$bName = Get-Item -Path $b -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue

#get the shudown timeout for the app pool
$shutdownTimeout = Get-WebConfigurationProperty -Filter 'system.web/httpRuntime' -PSPath "IIS:\Sites\$siteName" -Name shutdownTimeout | Select-Object -ExpandProperty Value

#create symlink, if symlink source is an existing directory, rename existing 
if($cName -eq $null) 
{
    Write-Output "Creating SymbolicLink $c -> $a" 
    New-Item -Type SymbolicLink -Path $c -Target $a
} 
else
{
    #directories don't have a target property
    $cTarget = Get-Item -Path $c | Select-Object -ExpandProperty target -ErrorAction SilentlyContinue

    #this is a directory, rename and create symlink
    if($cTarget -eq $null)
    {
        #restart AppPool first so no locked files
        #Write-Output "Restarting AppPool $appPoolName" 
        #Restart-WebAppPool -Name $appPoolName

        #stop AppPool first so no locked files
        Write-Output "Stopping AppPool $appPoolName" 
        Stop-WebAppPool -Name $appPoolName

        #sleep for shutdownTimeout
        Write-Output "Sleeping for $shutdownTimeout" 
        Start-Sleep -Seconds (([System.TimeSpan]::Parse("$shutdownTimeout").TotalSeconds) * 1.1)

        try {
             #if the rename fails, there are files in use. stop the script
            $newName = $siteFolderName + 'A'
            Write-Output "Renaming $c to $newName" 
            Rename-Item -Path $c -NewName $newName -ErrorAction Stop
        }
        catch {
            Write-Error -Message "Failed to rename $c to $newName due to files in use."

            #start AppPool 
            Write-Output "Starting AppPool $appPoolName due to failed folder rename. Consider trying again durring a time with reduced traffic." 
            Start-WebAppPool -Name $appPoolName

        }

        #create symlink
        Write-Output "Creating SymbolicLink $c -> $a" 
        New-Item -Type SymbolicLink -Path $c -Target $a

        #start AppPool 
        Write-Output "Starting AppPool $appPoolName" 
        Start-WebAppPool -Name $appPoolName

        #copy a to b to pick up directory permissions
        if($bName -eq $null) 
        {
            Write-Output "Copying Directory $a to $b"  
            robocopy $a $b /e /sec /sl
        }
    }
}

#existence test
$aName = Get-Item -Path $a -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue
$bName = Get-Item -Path $b -ErrorAction SilentlyContinue | Select-Object -ExpandProperty name -ErrorAction SilentlyContinue

#create if needed
if($aName -eq $null) 
{
    Write-Output "Creating Directory $a"  
    New-Item -Type Directory $a 
}
if($bName -eq $null) 
{
    Write-Output "Creating Directory $b"  
    New-Item -Type Directory $b 
}

#make sure site pointing to symlink
Write-Output "Pointing Website $siteName to Path $c" 
Set-ItemProperty "IIS:\Sites\$siteName" -name physicalPath -value $c

#restart so we know it's loading from symlink
Write-Output "Restarting AppPool $appPoolName" 
Restart-WebAppPool -Name $appPoolName

#sleep for shutdownTimeout 
Write-Output "Sleeping for $shutdownTimeout" 
Start-Sleep -Seconds ([System.TimeSpan]::Parse("$shutdownTimeout").TotalSeconds)

#get current symlink target and set new target
Get-Item -Path $c | Select-Object -ExpandProperty target
$current = (Get-Item -Path $c).Target
$newTarget = if ($current -eq $a) {$b} else {$a}

$fileOrDirectoryName = Get-Item $artifactPath | Select-Object -ExpandProperty name

#test for .zip
if(($fileOrDirectoryName).ToLower().EndsWith(".zip") -eq $true)
{
    #copy to temp location
    $guid = [System.Guid]::NewGuid()
    $tempPath = "$iisRootPath\temp-$guid"
    $tempDest = "$iisRootPath\temp-dest-$guid"

    Write-Output "Creating temp directory $tempPath" 
    New-Item -Type Directory $tempPath

    $artifactDirectory = [System.IO.Path]::GetDirectoryName($artifactPath)
    $artifactFile = [System.IO.Path]::GetFileName($artifactPath);

    #TODO: first robocopy arg needs to be a directory, not a file
    Write-Output "Copying archive from $artifactPath to $tempPath" 
    robocopy $artifactDirectory $tempPath $artifactFile

    #extract to temp destination (seems a failed extract can leave empty folders)
    Write-Output "Extracting archive to $tempDest" 
    Expand-Archive -Path "$tempPath\$fileOrDirectoryName" -DestinationPath $tempDest -Force

    #copy from temp destination to destination
    Write-Output "Copying files from $tempDest to $newTarget" 
    robocopy $tempDest $newTarget /e

    #delete temp directory
    Write-Output "Removing temp directory $tempPath" 
    Remove-Item -Path $tempPath -Recurse -Force

    #delete temp directory
    Write-Output "Removing temp directory $tempDest" 
    Remove-Item -Path $tempDest -Recurse -Force
}
else 
{
    #copy new files
    Write-Output "Copying files from $artifactPath to $newTarget" 
    robocopy $artifactPath $newTarget /e
}

#swap symlink
Write-Output "Pointing SymbolicLink $c -> $newTarget" 
New-Item -Type SymbolicLink -Path $c -Target $newTarget -Force

#restart so it loads from newly swapped symlink
Write-Output "Restarting AppPool $appPoolName" 
Restart-WebAppPool -Name $appPoolName

LucidObscurity ما هذا؟ كود كمد؟ أين يجب علي إضافة هذا الرمز؟
شكر

ماذا عن فكرة زيادة عدد المحاولات (المعلمة: -retryAttempts:X ) أو الفاصل الزمني لإعادة المحاولة (المعلمة: -retryInterval:XXXX ) للأمر msdeploy ، حتى تحرر العملية قفل الملف؟

أعتقد أن davidfowl على حق ، فالعملية لا تزال موجودة بطريقة ما لا مساعدة، لا تزال تنتظر العملية حتى المهلة. إذا قمت بإسقاط app_offline.htm وانتظرت 90 ثانية (إعداد المهلة الافتراضي لإيقاف تشغيل AppPool) ، وحاول مرة أخرى ، فسترى أنه يعمل.
بالنسبة لي على المستوى المحلي ، لا أريد الانتظار كل هذا الوقت لذلك قمت بتحديث app_offline.htm قبل
بالنسبة للخادم ، أستخدم msdeploy لإسقاط app_offline.htm ، ومزامنة الإصدار الجديد ، ثم إزالة app_offline.htm. باستخدام msdeploy ، يتم إعادة المحاولة إذا تم قفل الملفات ولكن في وقت ما استسلمت قبل 90 ثانية ، لذلك يجب أن أقوم بتشغيل النشر مرة أخرى ، ثم يعمل في المحاولة الثانية

أي تحديث حول هذا؟ متى سيكون هذا الإصدار الجديد جاهزًا؟

شكرا لإتصالك بنا.
نحن بصدد نقل هذه المشكلة إلى Next sprint planning المعلم للتقييم / التفكير المستقبلي. سنقوم بتقييم الطلب عندما نخطط للعمل من أجل المرحلة التالية. لمعرفة المزيد حول ما يمكن توقعه بعد ذلك وكيفية التعامل مع هذه المشكلة ، يمكنك قراءة المزيد حول عملية الفرز هنا .

لقد اكتشفت هذه المشكلة هذا الأسبوع ، بعد تحويل تطبيق .NET 4.5.2 إلى .NET 5.0. لدي طريقة أخرى للتغلب على هذا. لقد قمت بإنشاء خدمة Windows تعمل على خادم IIS. في تلك الخدمة لدي خادم HTTP بسيط يعمل على منفذ مخصص. يمكن لتطبيق الويب إجراء طلب لاحق لهذا الخادم لبدء الترقية. إنه يوقف تجمع التطبيقات ، ويتأكد من قتل جميع العمليات لهذا التطبيق ، وإنشاء نسخة احتياطية من الإصدار الحالي ، وإزالة الإصدار الحالي ، وتنزيل حزمة الترقية واستخراجها. ثم يبدأ مجمع التطبيقات مرة أخرى.

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

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات