طَرد | نوجيت |
---|---|
FluentDocker | |
اختبار مايكروسوفت | |
اختبار XUnit |
تتيح هذه المكتبة تفاعلات docker
docker-compose
باستخدام Fluent API . وهو مدعوم على أنظمة التشغيل Linux وWindows وMac. كما أن لديها دعمًا للتفاعلات docker-machine
الإرساء.
نموذج لاستخدام واجهة برمجة التطبيقات Fluent
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
يؤدي هذا إلى تشغيل postgres وينتظر أن يصبح جاهزًا. لاستخدام الإنشاء، ما عليك سوى القيام بذلك على النحو التالي:
ملاحظة: استخدم AssumeComposeVersion(ComposeVersion.V2) لاستخدام سلوك V2، ولا يزال الإعداد الافتراضي هو V1 (سيتم تغييره إلى الإعداد الافتراضي إلى V2 في وقت لاحق من هذا العام)
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
// @formatter:off
using ( var svc = new Builder ( )
. UseContainer ( )
. UseCompose ( )
. FromFile ( file )
. RemoveOrphans ( )
. WaitForHttp ( "wordpress" , "http://localhost:8000/wp-admin/install.php" )
. Build ( ) . Start ( ) )
// @formatter:on
{
// We now have a running WordPress with a MySql database
var installPage = await "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
Assert . AreEqual ( 1 , svc . Hosts . Count ) ; // The host used by compose
Assert . AreEqual ( 2 , svc . Containers . Count ) ; // We can access each individual container
Assert . AreEqual ( 2 , svc . Images . Count ) ; // And the images used.
}
:bulb ملاحظة لمستخدمي Linux: يتطلب Docker Sudo افتراضيًا وتتوقع المكتبة افتراضيًا أن المستخدم المنفذ لا يحتاج إلى القيام بـ sudo من أجل التحدث إلى برنامج docker الخفي. يمكن العثور على مزيد من الوصف في فصل Talking to Docker Daemon .
تعمل واجهة برمجة التطبيقات (API) بطلاقة على إنشاء خدمة واحدة أو أكثر. وقد تكون كل خدمة مركبة أو فردية. ولذلك فمن الممكن، على سبيل المثال، تشغيل العديد من الخدمات القائمة على إنشاء عامل إرساء وإدارة كل منها كخدمة واحدة أو البحث في جميع الخدمات الأساسية واستخدامها في كل خدمة إنشاء عامل إرساء . ومن الممكن أيضًا استخدام الخدمات مباشرةً، على سبيل المثال
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
using ( var svc = new DockerComposeCompositeService ( DockerHost , new DockerComposeConfig
{
ComposeFilePath = new List < string > { file } , ForceRecreate = true , RemoveOrphans = true ,
StopOnDispose = true
} ) )
{
svc . Start ( ) ;
// We now have a running WordPress with a MySql database
var installPage = await $ "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
}
يقوم المثال أعلاه بإنشاء خدمة إنشاء عامل إرساء من ملف إنشاء واحد. عند التخلص من الخدمة، يتم إيقاف جميع الخدمات الأساسية تلقائيًا.
المكتبة مدعومة بـ .NET full 4.51 Framework والإصدارات الأحدث، .NET Standard 1.6, 2.0. وهي مقسمة إلى ثلاث طبقات رقيقة، يمكن الوصول إلى كل طبقة:
غالبية طرق الخدمة هي طرق تمديد وليست مدمجة في الخدمة نفسها، مما يجعلها خفيفة الوزن وقابلة للتخصيص. نظرًا لأنه يمكن الوصول إلى كل شيء، فمن السهل على سبيل المثال إضافة طريقة الامتدادات لخدمة تستخدم أوامر الطبقة 1 لتوفير الوظائف.
أنا أرحب بالمساهمة، على الرغم من عدم وجود إرشادات خاصة بالمساهمة حتى الآن، تأكد من الالتزام بـ .editorconfig عند إجراء طلبات السحب. وإلا فإن البناء سوف تفشل. سأقوم بالتحديث بمبادئ توجيهية حقيقية عاجلاً أم آجلاً هذا العام.
تحتاج جميع الأوامر إلى DockerUri
للعمل معها. إنه Uri لبرنامج عامل الإرساء، سواء محليًا أو عن بعد. يمكن أن يكون قابلاً للاكتشاف أو مشفرًا. يمكن اكتشاف DockerUri
المحلي عن طريق
var hosts = new Hosts ( ) . Discover ( ) ;
var _docker = hosts . FirstOrDefault ( x => x . IsNative ) ?? hosts . FirstOrDefault ( x => x . Name == "default" ) ;
سيتحقق المثال الذي تم اقتصاصه من المضيفين الأصليين أو المضيفين الأصليين لـ docker beta، إذا لم يكن الأمر كذلك، فاختر جهاز الإرساء "الافتراضي" كمضيف. إذا كنت تستخدم docker-machine ولم يكن هناك أي جهاز موجود أو لم يتم تشغيله، فمن السهل إنشاء/بدء تشغيل جهاز docker-machine على سبيل المثال "test-machine".Create(1024,20000000,1)
. سيؤدي هذا إلى إنشاء جهاز إرساء يسمى "جهاز اختبار" بذاكرة وصول عشوائي (RAM) سعة 1 جيجابايت وقرص سعة 20 جيجابايت واستخدام وحدة معالجة مركزية واحدة.
أصبح من الممكن الآن استخدام Uri للتواصل باستخدام الأوامر. على سبيل المثال للحصول على إصدار ثنائيات عامل الإرساء للعميل والخادم:
var result = _docker . Host . Version ( _docker . Certificates ) ;
Debug . WriteLine ( result . Data ) ; // Will Print the Client and Server Version and API Versions respectively.
تقوم جميع الأوامر بإرجاع CommandResponse بحيث يكون من الممكن التحقق من عامل النجاح عن طريق response.Success
. إذا تم إرجاع أي بيانات مرتبطة بالأمر في خاصية response.Data
.
بعد ذلك يكون الأمر بسيطًا كما هو موضح أدناه للبدء والإيقاف، بما في ذلك حذف حاوية باستخدام الأوامر. أدناه تبدأ حاوية وتقوم بعمل PS عليها ثم تقوم بحذفها.
var id = _docker . Host . Run ( "nginx:latest" , null , _docker . Certificates ) . Data ;
var ps = _docker . Host . Ps ( null , _docker . Certificates ) . Data ;
_docker . Host . RemoveContainer ( id , true , true , null , _docker . Certificates ) ;
عند التشغيل على Windows، يمكن للمرء اختيار تشغيل Linux أو Windows Container. استخدم LinuxDaemon
أو WindowsDaemon
للتحكم في البرنامج الخفي الذي تريد التحدث إليه.
_docker . LinuxDaemon ( ) ; // ensures that it will talk to linux daemon, if windows daemon it will switch
تقوم بعض الأوامر بإرجاع دفق من البيانات عندما تكون الأحداث أو السجلات مطلوبة باستخدام دفق مستمر. يمكن استخدام التدفقات في مهام الخلفية ودعم CancellationToken
. المثال أدناه ذيول السجل.
using ( var logs = _docker . Host . Logs ( id , _docker . Certificates ) )
{
while ( ! logs . IsFinished )
{
var line = logs . TryRead ( 5000 ) ; // Do a read with timeout
if ( null == line )
{
break ;
}
Debug . WriteLine ( line ) ;
}
}
توجد طرق مساعدة للأوامر. وهي تأتي في أشكال مختلفة مثل الشبكات وما إلى ذلك. على سبيل المثال عند قراءة سجل حتى النهاية:
using ( var logs = _docker . Host . Logs ( id , _docker . Certificates ) )
{
foreach ( var line in logs . ReadToEnd ( ) )
{
Debug . WriteLine ( line ) ;
}
}
أعلى طبقة في هذه المكتبة هي واجهة برمجة التطبيقات (API) بطلاقة حيث يمكنك تحديد الأجهزة والصور والحاويات والتحكم فيها. على سبيل المثال، لإعداد موازن التحميل مع خادمي العقدة Nodejs للقراءة من خادم redis، يمكن أن يبدو هذا الشكل (يتم إنشاء صورة العقدة خصيصًا إذا لم يتم العثور عليها في المستودع).
var fullPath = ( TemplateString ) @"${TEMP}/fluentdockertest/${RND}" ;
var nginx = Path . Combine ( fullPath , "nginx.conf" ) ;
Directory . CreateDirectory ( fullPath ) ;
typeof ( NsResolver ) . ResourceExtract ( fullPath , "index.js" ) ;
using ( var services = new Builder ( )
// Define custom node image to be used
. DefineImage ( "mariotoffia/nodetest" ) . ReuseIfAlreadyExists ( )
. From ( "ubuntu" )
. Maintainer ( "Mario Toffia <[email protected]>" )
. Run ( "apt-get update &&" ,
"apt-get -y install curl &&" ,
"curl -sL https://deb.nodesource.com/setup | sudo bash - &&" ,
"apt-get -y install python build-essential nodejs" )
. Run ( "npm install -g nodemon" )
. Add ( "emb:Ductus.FluentDockerTest/Ductus.FluentDockerTest.MultiContainerTestFiles/package.txt" ,
"/tmp/package.json" )
. Run ( "cd /tmp && npm install" )
. Run ( "mkdir -p /src && cp -a /tmp/node_modules /src/" )
. UseWorkDir ( "/src" )
. Add ( "index.js" , "/src" )
. ExposePorts ( 8080 )
. Command ( "nodemon" , "/src/index.js" ) . Builder ( )
// Redis Db Backend
. UseContainer ( ) . WithName ( "redis" ) . UseImage ( "redis" ) . Builder ( )
// Node server 1 & 2
. UseContainer ( ) . WithName ( "node1" ) . UseImage ( "mariotoffia/nodetest" ) . Link ( "redis" ) . Builder ( )
. UseContainer ( ) . WithName ( "node2" ) . UseImage ( "mariotoffia/nodetest" ) . Link ( "redis" ) . Builder ( )
// Nginx as load balancer
. UseContainer ( ) . WithName ( "nginx" ) . UseImage ( "nginx" ) . Link ( "node1" , "node2" )
. CopyOnStart ( nginx , "/etc/nginx/nginx.conf" )
. ExposePort ( 80 ) . Builder ( )
. Build ( ) . Start ( ) )
{
Assert . AreEqual ( 4 , services . Containers . Count ) ;
var ep = services . Containers . First ( x => x . Name == "nginx" ) . ToHostExposedEndpoint ( "80/tcp" ) ;
Assert . IsNotNull ( ep ) ;
var round1 = $ "http:// { ep . Address } : { ep . Port } " . Wget ( ) ;
Assert . AreEqual ( "This page has been viewed 1 times!" , round1 ) ;
var round2 = $ "http:// { ep . Address } : { ep . Port } " . Wget ( ) ;
Assert . AreEqual ( "This page has been viewed 2 times!" , round2 ) ;
}
يعرّف المثال أعلاه ملف Dockerfile ، ويبنيه، لصورة العقدة. ثم يستخدم الفانيليا redis وnginx. إذا كنت تريد فقط استخدام ملف Dockerfile موجود، فيمكن القيام بذلك على هذا النحو.
using ( var services = new Builder ( )
. DefineImage ( "mariotoffia/nodetest" ) . ReuseIfAlreadyExists ( )
. FromFile ( "/tmp/Dockerfile" )
. Build ( ) . Start ( ) )
{
// Container either build to reused if found in registry and started here.
}
تدعم واجهة برمجة التطبيقات (API) بطلاقة تحديد جهاز الإرساء إلى مجموعة من مثيلات الإرساء. يحتوي على دعم مدمج، على سبيل المثال، انتظار منفذ معين أو عملية داخل الحاوية قبل اكتمال Build()
وبالتالي يمكن استخدامه بأمان ضمن عبارة الاستخدام. إذا كانت هناك إدارة محددة بشأن مهلات الانتظار وما إلى ذلك، فيمكنك دائمًا إنشاء الحاوية وبدء تشغيلها واستخدام طرق التمديد للقيام بالانتظار على الحاوية نفسها.
لإنشاء حاوية فقط قم بحذف البداية. على سبيل المثال:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( ) )
{
Assert . AreEqual ( ServiceRunningState . Stopped , container . State ) ;
}
يقوم هذا المثال بإنشاء حاوية تحتوي على postgres، وتكوين متغير بيئة واحد. ضمن بيان الاستخدام، من الممكن بدء تشغيل IContainerService
. وبالتالي يتم تغليف كل حاوية مبنية في IContainerService
. من الممكن أيضًا استخدام IHostService.GetContainers(...)
للحصول على الحاويات التي تم إنشاؤها وتشغيلها والخروج منها. من الممكن أيضًا من IHostService
الحصول على جميع الصور الموجودة في المستودع المحلي لإنشاء حاويات منها.
عندما تريد تشغيل حاوية واحدة، استخدم طريقة بدء خدمة الحاوية أو بطلاقة. على سبيل المثال:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( ) ;
Assert . AreEqual ( ServiceRunningState . Running , container . State ) ;
Assert . IsTrue ( config . Config . Env . Any ( x => x == "POSTGRES_PASSWORD=mysecretpassword" ) ) ;
}
افتراضيًا، يتم إيقاف الحاوية وحذفها عند تشغيل طريقة التخلص، ومن أجل الاحتفاظ بالحاوية في الأرشيف، استخدم KeepContainer()
على واجهة برمجة التطبيقات بطلاقة. عند استدعاء Dispose()
سيتم إيقافه ولكن لن يتم حذفه. ومن الممكن أيضًا الاحتفاظ به قيد التشغيل بعد التخلص منه أيضًا.
من الممكن كشف المنافذ بشكل صريح أو عشوائي. وفي كلتا الحالتين من الممكن حل IP (في حالة الجهاز) والمنفذ (في حالة المنفذ العشوائي) لاستخدامه في التعليمات البرمجية. على سبيل المثال:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 40001 , 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var endpoint = container . ToHostExposedEndpoint ( "5432/tcp" ) ;
Assert . AreEqual ( 40001 , endpoint . Port ) ;
}
نقوم هنا بتعيين منفذ الحاوية 5432 لاستضافة المنفذ 40001 بشكل صريح. لاحظ استخدام container.ToHostExposedEndpoint(...)
. هذا هو الحل دائمًا لعنوان IP العامل والمنفذ للتواصل مع حاوية عامل الإرساء. من الممكن أيضًا تعيين منفذ عشوائي، أي السماح لـ Docker باختيار منفذ متاح. على سبيل المثال:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var endpoint = container . ToHostExposedEndpoint ( "5432/tcp" ) ;
Assert . AreNotEqual ( 0 , endpoint . Port ) ;
}
والفرق الوحيد هنا هو أنه يتم استخدام وسيطة واحدة فقط عند استخدام ExposePort(...)
لتكوين الحاوية. وينطبق نفس الاستخدام على خلاف ذلك، وبالتالي يكون شفافًا بالنسبة للكود.
لمعرفة متى يتم تشغيل خدمة معينة قبل البدء في الاتصال بها على سبيل المثال. من الممكن الانتظار حتى يتم فتح منفذ معين. على سبيل المثال:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
في المثال أعلاه ننتظر حتى يتم فتح منفذ الحاوية 5432 خلال 30 ثانية. إذا فشلت، فسيتم طرح استثناء وبالتالي سيتم التخلص من الحاوية وإزالتها (نظرًا لعدم وجود أي تكوين للاحتفاظ بالحاوية وما إلى ذلك).
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ , "127.0.0.1" )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
في بعض الأحيان لا يكون من الممكن الوصول مباشرة إلى الحاوية، عن طريق IP والمنفذ المحلي، بدلاً من ذلك، على سبيل المثال، تحتوي الحاوية على منفذ مكشوف على واجهة الاسترجاع ( 127.0.0.1 ) وهذه هي الطريقة الوحيدة للوصول إلى الحاوية من البرنامج. يفرض المثال أعلاه أن يكون العنوان 127.0.0.1 ولكنه لا يزال يحل المنفذ المضيف. افتراضيًا، يستخدم FluentDocker فحص الشبكة على الحاوية لتحديد تكوين الشبكة.
في وقت ما لا يكفي مجرد انتظار المنفذ. في بعض الأحيان يكون انتظار عملية الحاوية أكثر أهمية. لذلك توجد طريقة انتظار العملية في واجهة برمجة التطبيقات (API) بطلاقة بالإضافة إلى طريقة تمديد على كائن الحاوية. على سبيل المثال:
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForProcess ( "postgres" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
Assert . AreEqual ( ServiceRunningState . Running , config . State . ToServiceState ( ) ) ;
}
في المثال أعلاه، سيعيد Build()
التحكم عند بدء عملية "postgres" داخل الحاوية.
من أجل الاستفادة من الحاويات، من الضروري أحيانًا تحميل وحدات التخزين الموجودة في الحاوية على المضيف أو مجرد النسخ من الحاوية أو إليها. اعتمادًا على ما إذا كنت تقوم بتشغيل جهاز أو عامل إرساء، فإن تعيين الحجم أصلاً يحتوي على القيد الذي يجب أن يكون قابلاً للوصول إليه من الجهاز الظاهري.
حالة الاستخدام العادي هي أن يكون خادم الويب، على سبيل المثال، يقدم محتوى على حاوية عامل إرساء ويقوم المستخدم بتحرير الملفات على نظام الملفات المضيف. في مثل هذا السيناريو، من الضروري تركيب وحدة تخزين حاوية عامل إرساء على المضيف. على سبيل المثال:
const string html = "<html><head>Hello World</head><body><h1>Hello world</h1></body></html>" ;
var hostPath = ( TemplateString ) @"${TEMP}/fluentdockertest/${RND}" ;
Directory . CreateDirectory ( hostPath ) ;
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "nginx:latest" )
. ExposePort ( 80 )
. Mount ( hostPath , "/usr/share/nginx/html" , MountType . ReadOnly )
. Build ( )
. Start ( )
. WaitForPort ( "80/tcp" , 30000 /*30s*/ ) )
{
File . WriteAllText ( Path . Combine ( hostPath , "hello.html" ) , html ) ;
var response = $ "http:// { container . ToHostExposedEndpoint ( "80/tcp" ) } /hello.html" . Wget ( ) ;
Assert . AreEqual ( html , response ) ;
}
في المثال أعلاه، يتم تشغيل حاوية nginx وتثبيت "/usr/share/nginx/html" على مسار مضيف (عشوائي، في الدليل المؤقت). يتم نسخ ملف HTML إلى مسار المضيف وعندما يتم وصول HTTP إلى حاوية nginx docker، يتم تقديم نفس الملف.
في بعض الأحيان يكون من الضروري نسخ الملفات من وإلى الحاوية. على سبيل المثال، قم بنسخ ملف التكوين، وقم بتكوينه ونسخه مرة أخرى. السيناريو الأكثر شيوعًا هو نسخ ملف التكوين إلى الحاوية، قبل بدء تشغيله مباشرةً. يقوم مثال الحاوية المتعددة بنسخ ملف تكوين nginx قبل بدء تشغيله مباشرةً. وبالتالي فمن الممكن تجنب إنشاء ملف Dockerfile وصورة يدويًا لمثل هذه المهمة البسيطة. بدلاً من ذلك، ما عليك سوى استخدام صورة رسمية أو مخصصة، ونسخ التكوين وتشغيله.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( )
. CopyFrom ( "/etc/conf.d" , fullPath ) )
{
var files = Directory . EnumerateFiles ( Path . Combine ( fullPath , "conf.d" ) ) . ToArray ( ) ;
Assert . IsTrue ( files . Any ( x => x . EndsWith ( "pg-restore" ) ) ) ;
Assert . IsTrue ( files . Any ( x => x . EndsWith ( "postgresql" ) ) ) ;
}
المثال أعلاه ينسخ دليلاً إلى مسار مضيف (fullPath) من حاوية قيد التشغيل. لاحظ استخدام طريقة الامتداد هنا، وبالتالي عدم استخدام واجهة برمجة التطبيقات (API) بطلاقة (نظرًا لأن CopyFrom يأتي بعد Start()). إذا كنت تريد نسخ الملفات من الحاوية قبل البدء مباشرة، فاستخدم Fluent API بدلاً من ذلك.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. CopyOnStart ( "/etc/conf.d" , fullPath )
. Build ( )
. Start ( ) )
{
}
يوضح المثال أدناه سيناريو أكثر شيوعًا حيث يتم نسخ الملفات إلى الحاوية. يستخدم هذا المثال طريقة الامتداد بدلاً من إصدار واجهة برمجة التطبيقات (API) بطلاقة. يأخذ لقطة مختلفة قبل النسخ ثم بعد النسخ مباشرة. وفي الأخير يوجد hello.html.
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( )
. WaitForProcess ( "postgres" , 30000 /*30s*/ )
. Diff ( out before )
. CopyTo ( "/bin" , fullPath ) )
{
var after = container . Diff ( ) ;
Assert . IsFalse ( before . Any ( x => x . Item == "/bin/hello.html" ) ) ;
Assert . IsTrue ( after . Any ( x => x . Item == "/bin/hello.html" ) ) ;
}
في بعض الأحيان يكون من المفيد نسخ الملفات في IContainerService.Dispose()
(قبل توقف الحاوية مباشرة). لذلك توجد واجهة برمجة تطبيقات (API) بطلاقة للتأكد من أنها ستفعل ذلك.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. CopyOnDispose ( "/etc/conf.d" , fullPath )
. Build ( )
. Start ( ) )
{
}
من أجل تحليل الحاوية، توجد طريقة تمديد التصدير وطرق API بطلاقة. وأبرزها إمكانية تصدير حاوية عند التخلص من IContainerService
.
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExportOnDispose ( fullPath )
. Build ( )
. Start ( ) )
{
}
سيؤدي هذا إلى تصدير حاوية (ملف tar) على المضيف (fullPath). إذا كنت تفضل تفجيرها (بدون قطران)، فاستخدم طريقة ExportExplodedOnDispose
بدلاً من ذلك. بالطبع يمكنك تصدير الحاوية في أي وقت باستخدام طريقة الامتداد الموجودة على الحاوية.
هناك خدعة مفيدة عندما يتعلق الأمر باختبار الوحدة وهي تصدير حالة الحاوية عند فشل اختبار الوحدة لسبب ما، وبالتالي توجد واجهة برمجة تطبيقات Fluent سيتم تصديرها عند استيفاء شرط Lambda معين. على سبيل المثال:
var failure = false ;
using ( new Builder ( ) . UseContainer ( )
. UseImage ( "kiasaki/alpine-postgres" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExportOnDispose ( fullPath , svc => failure )
. Build ( )
. Start ( ) )
{
failure = true ;
}
سيقوم هذا المقتطف بتصدير الحاوية عندما تقوم عبارة الاستخدام بالتخلص من الحاوية حيث تم تعيين متغير الفشل على true ويتم استخدامه في تعبير ExportOnDispose
.
يمكن تمديد جميع الخدمات باستخدام الخطافات. في ExportOnDispose(path, lambda)
يقوم بتثبيت خطاف عند تعيين حالة الخدمة لتنفيذ lambda عندما تكون الحالة Removing
. من الممكن تثبيت وإزالة الخطافات أثناء الطيران. إذا تم تسجيل عدة خطافات على نفس مثيل الخدمة، بنفس ServiceRunningState
، فسيتم تنفيذها بترتيب التثبيت.
تعتبر الخطافات جيدة بشكل خاص إذا كنت تريد تنفيذ شيء ما عندما تكون الحالة على وشك التعيين (أو التنفيذ) على الخدمة مثل Starting
. تستفيد واجهة Fluent API من تلك الميزات في بعض المواقف مثل نسخ الملفات والتصدير وما إلى ذلك.
يدعم FluentDocker جميع أوامر شبكة الإرساء. يمكنه اكتشاف الشبكات بواسطة _docker.NetworkLs()
حيث يكتشف جميع الشبكات وبعض المعلمات البسيطة المحددة في NetworkRow
. يمكنه أيضًا الفحص للحصول على معلومات أعمق حول الشبكة (مثل الحاويات الموجودة في الشبكة وتكوين Ipam) بواسطة _docker.NetworkInspect(network:"networkId")
.
لإنشاء شبكة جديدة، استخدم _docker.NetworkCreate("name_of_network")
. من الممكن أيضًا توفير NetworkCreateParams
حيث يمكن تخصيص كل شيء مثل إنشاء شبكة متراكبة وتغيير تكوين Ipam. لحذف شبكة، ما عليك سوى استخدام _docker.NetworkRm(network:"networkId")
.
لاحظ أن الشبكات لا يتم حذفها في حالة وجود أي حاويات ملحقة بها!
عند إنشاء شبكة، من الممكن وضع حاوية واحدة أو أكثر فيها باستخدام _docker.NetworkConnect("containerId","networkId")
. لاحظ أن الحاويات قد تكون في عدة شبكات في وقت واحد، وبالتالي يمكنها توكيل الطلب بين الشبكات المعزولة. لفصل حاوية عن الشبكة، ما عليك سوى إجراء _docker.NetworkDisconnect("containerId","networkId")
.
يقوم النموذج التالي بتشغيل حاوية وإنشاء شبكة جديدة وتوصيل الحاوية قيد التشغيل بالشبكة. ثم يقوم بفصل الحاوية وحذفها وحذف الشبكة.
var cmd = _docker . Run ( "postgres:9.6-alpine" , new ContainerCreateParams
{
PortMappings = new [ ] { "40001:5432" } ,
Environment = new [ ] { "POSTGRES_PASSWORD=mysecretpassword" }
} , _certificates ) ;
var container = cmd . Data ;
var network = string . Empty ;
var created = _docker . NetworkCreate ( "test-network" ) ;
if ( created . Success )
network = created . Data [ 0 ] ;
_docker . NetworkConnect ( container , network ) ;
// Container is now running and has address in the newly created 'test-network'
_docker . NetworkDisconnect ( container , id , true /*force*/ ) ;
_docker . RemoveContainer ( container , true , true ) ;
// Now it is possible to delete the network since it has been disconnected from the network
_docker . NetworkRm ( network : network ) ;
من الممكن أيضًا استخدام منشئ يتقن إنشاء شبكات إرساء جديدة أو إعادة استخدام شبكات الإرساء الحالية. ويمكن بعد ذلك الرجوع إليها أثناء بناء الحاويات . من الممكن بناء أكثر من شبكة إرساء واحدة وربط حاوية بأكثر من شبكة في نفس الوقت.
using ( var nw = new Builder ( ) . UseNetwork ( "test-network" ) )
{
using (
var container =
new DockerBuilder ( )
. WithImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePorts ( "5432" )
. UseNetwork ( nw )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( ) )
{
container . Create ( ) . Start ( ) ;
}
}
يقوم مقتطف الكود أعلاه بإنشاء شبكة جديدة تسمى شبكة الاختبار ثم يقوم بإنشاء حاوية متصلة بشبكة الاختبار . عندما يتم استدعاء Dispose()
على nw فإنه سيتم إزالة الشبكة. من الممكن أيضًا إجراء تعيينات حاوية IP ثابتة داخل الشبكة بواسطة UseIpV4
أو UseIpV6
. على سبيل المثال:
using ( var nw = Fd . UseNetwork ( "unit-test-nw" )
. UseSubnet ( "10.18.0.0/16" ) . Build ( ) )
{
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePort ( 5432 )
. UseNetwork ( nw )
. UseIpV4 ( "10.18.0.22" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var ip = container . GetConfiguration ( ) . NetworkSettings . Networks [ "unit-test-nw" ] . IPAddress ;
Assert . AreEqual ( "10.18.0.22" , ip ) ;
}
}
ينشئ المثال أعلاه وحدة شبكة جديدة test-nw بنطاق IP 10.18.0.0/16 . يتم استخدامه في الحاوية الجديدة. تم تعيين عنوان IP للحاوية على 10.18.0.22 وهو ثابت بسبب أمر UseIpV4
.
يدعم FluentDocker إدارة حجم عامل الإرساء من خلال الأوامر ومن واجهة برمجة التطبيقات (API) بطلاقة. لذلك، من الممكن التحكم بشكل كامل في الأحجام المستخدمة في الحاوية، مثل ما إذا كان سيتم التخلص منها أو إعادة استخدامها أو السائق الذي سيتم استخدامه وما إلى ذلك.
var volume = _docker . VolumeCreate ( "test-volume" , "local" , opts : {
{ "type" , "nfs" } ,
{ "o=addr" , "192.168.1.1,rw" } ,
{ "device" , ":/path/to/dir" }
} ) ;
var cfg = _docker . VolumeInspect ( _certificates , "test-volume" ) ;
_docker . VolumeRm ( force : true , id : "test-volume" ) ;
يقوم المقتطف أعلاه بإنشاء وحدة تخزين جديدة باسم حجم الاختبار وهي من نوع NFS . ثم يقوم بعد ذلك بفحص وحدة التخزين التي تم إنشاؤها للتو، وأخيرًا يقوم بحذف وحدة التخزين.
من الممكن أيضًا استخدام واجهة برمجة التطبيقات (API) بطلاقة لإنشاء وحدات التخزين أو استخدامها. ويمكن بعد ذلك استخدامها عند بناء حاوية. يعد هذا مفيدًا بشكل خاص عندما يكون إنشاء وحدات التخزين خاصًا أو يحتاج إلى التحكم مدى الحياة.
using ( var vol = new Builder ( ) . UseVolume ( "test-volume" ) . RemoveOnDispose ( ) . Build ( ) )
{
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. MountVolume ( vol , "/var/lib/postgresql/data" , MountType . ReadWrite )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( ) ;
Assert . AreEqual ( 1 , config . Mounts . Length ) ;
Assert . AreEqual ( "test-volume" , config . Mounts [ 0 ] . Name ) ;
}
}
يقوم النموذج أعلاه بإنشاء وحدة تخزين جديدة تسمى حجم الاختبار ومن المقرر حذفها عند استدعاء Dispose()
على IVolumeService
. يتم إنشاء الحاوية وربط وحدة التخزين التي تم إنشاؤها حديثًا إلى /var/lib/postgresql/data كوضع وصول للقراءة/الكتابة . نظرًا لأن الحاوية تقع ضمن نطاق عبارة using
الخاصة بوحدة التخزين، فإن عمرها يمتد على عمر الحاوية بالكامل ثم يتم حذفها.
يدعم FluentDocker الاتصال بآلية حدث عامل الإرساء للاستماع إلى الأحداث التي يرسلها.
using ( var events = Fd . Native ( ) . Events ( ) )
{
using (
var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
FdEvent e ;
while ( ( e = events . TryRead ( 3000 ) ) != null )
{
if ( e . Type == EventType . Container && e . Action == EventAction . Start )
break ;
}
}
}
مستمع الأحداث عالمي ويمكنه التعامل مع العديد من أنواع EventAction
.
على سبيل المثال
اعتمادًا على الإجراء، قد يختلف نوع الحدث مثل ContainerKillEvent
لـ EventAction.Kill
. جميع الأحداث مستمدة من FdEvent
. وهذا يعني أن جميع الخصائص المشتركة موجودة في الحدث الأساسي والخصائص الصريحة موجودة في الحدث المشتق.
على سبيل المثال، يحتوي ´ContainerKillEvent` على الخصائص التالية:
public sealed class ContainerKillActor : EventActor
{
/// <summary>
/// The image name and label such as "alpine:latest".
/// </summary>
public string Image { get ; set ; }
/// <summary>
/// Name of the container.
/// </summary>
public string Name { get ; set ; }
/// <summary>
/// The signal that the container has been signalled.
/// </summary>
public string Signal { get ; set ; }
}
يمكن استخدام حلقة الأحداث هذه لالتقاط الأحداث وتشغيل مثيلات IService
التي تم إنشاء مثيل لها. أو إذا كنت بحاجة إلى الرد على سبيل المثال، تتم إضافة شبكة أو حذفها.
في الإطار الكامل يستخدم التسجيل المطول باستخدام System.Diagnostics.Debugger.Log
. بالنسبة لـ .net core فإنه يستخدم Microsoft.Extensions.Logging.ILog
القياسي لتسجيل الدخول. كلاهما يستخدم فئة Ductus.FluentDocker وبالتالي قد يتم تكوينهما للمشاركة في التسجيل أم لا وتكوين وجهات تسجيل مختلفة.
في .net core، يمكنك توفير مقطع التسجيل في ملف تكوين التطبيق.
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Ductus.FluentDocker": "None"
}
}
}
يرجى مراجعة https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 لمزيد من المعلومات. للحصول على الإطار الكامل، يرجى مراجعة ملف XML المطلوب في appconfig للحصول على الإطار الكامل الموضح في https://docs.microsoft.com/en-us/dotnet/framework/wcf/diagnostics/tracing/configuring-tracing.
هناك طريقة سريعة لتعطيل/تمكين التسجيل عبر ( Ductus.FluentDocker.Services
) Logging.Enabled()
أو Logging.Disabled()
. سيؤدي هذا إلى تمكين/تعطيل التسجيل بالقوة.
من الممكن تجاوز الآلية الافتراضية لـ FluentDocker التي تحل عنوان IP الخاص بالحاوية من منظور العملاء، على سبيل المثال WaitForPort
. يمكن تجاوز هذا على أساس ContainerBuilder
.
النموذج أدناه، يتجاوز السلوك الافتراضي . عندما يُرجع null
يبدأ الحل الافتراضي .
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePort ( 5432 )
. UseCustomResolver ( (
ports , portAndProto , dockerUri ) =>
{
if ( null == ports || string . IsNullOrEmpty ( portAndProto ) )
return null ;
if ( ! ports . TryGetValue ( portAndProto , out var endpoints ) )
return null ;
if ( null == endpoints || endpoints . Length == 0 )
return null ;
if ( CommandExtensions . IsNative ( ) )
return endpoints [ 0 ] ;
if ( CommandExtensions . IsEmulatedNative ( ) )
return CommandExtensions . IsDockerDnsAvailable ( )
? new IPEndPoint ( CommandExtensions . EmulatedNativeAddress ( ) , endpoints [ 0 ] . Port )
: new IPEndPoint ( IPAddress . Loopback , endpoints [ 0 ] . Port ) ;
if ( Equals ( endpoints [ 0 ] . Address , IPAddress . Any ) && null != dockerUri )
return new IPEndPoint ( IPAddress . Parse ( dockerUri . Host ) , endpoints [ 0 ] . Port ) ;
return endpoints [ 0 ] ;
} )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( )
. Start ( ) )
{
var state = container . GetConfiguration ( true /*force*/ ) . State . ToServiceState ( ) ;
Assert . AreEqual ( ServiceRunningState . Running , state ) ;
}
هناك دعم محدود لاستخدام FluentAPI للتحدث إلى برنامج عامل إرساء بعيد دون استخدام جهاز عامل إرساء. يتم ذلك إما عن طريق إنشاء مثيل DockerHostService
يدويًا أو استخدام FromUri
على HostBuilder
.
using ( var container = Fd . UseHost ( ) .
FromUri ( Settings . DockerUri , isWindowsHost : true ) .
UseContainer ( ) .
Build ( ) )
{
}
يتصل النموذج أعلاه بـ DockerUri
مخصص من أحد الإعدادات وهو عبارة عن برنامج خفي لرسو السفن في حاوية Windows.
FromUri
الذي يستخدم DockerUri
لإنشاء IHostService
. هذا uri تعسفي. كما أنه يدعم خصائص أخرى ( انظر أدناه ). public HostBuilder FromUri (
DockerUri uri ,
string name = null ,
bool isNative = true ,
bool stopWhenDisposed = false ,
bool isWindowsHost = false ,
string certificatePath = null ) { /*...*/ }
وسوف يستخدم الإعدادات الافتراضية "المعقولة" على جميع المعلمات. في معظم الحالات يكون uri كافيًا. على سبيل المثال، إذا لم يتم توفير مسار الشهادة ، فسوف يحاول الحصول عليها من البيئة DOCKER_CERT_PATH . إذا لم يتم العثور عليه في البيئة، فسيتم تعيينه افتراضيًا على لا شيء.
UseHost
الذي يأخذ تطبيق IHostService
الذي تم إنشاء مثيل له. تدعم المكتبة docker-compose لاستخدام ملفات الإنشاء الموجودة لتقديم الخدمات وإدارة عمر هذه الملفات.
ستحتوي العينة التالية على ملف إنشاء يقوم بتشغيل MySql و WordPress . لذلك، ستحتوي خدمة الإنشاء الفردي على خدمتين للحاويتين أسفلها. افتراضيًا، سيتم إيقاف الخدمات والتنظيف عند استدعاء Dispose()
. يمكن تجاوز ذلك بواسطة KeepContainers()
في التكوين بطلاقة .
version : ' 3.3 '
services :
db :
image : mysql:5.7
volumes :
- db_data:/var/lib/mysql
restart : always
environment :
MYSQL_ROOT_PASSWORD : somewordpress
MYSQL_DATABASE : wordpress
MYSQL_USER : wordpress
MYSQL_PASSWORD : wordpress
wordpress :
depends_on :
- db
image : wordpress:latest
ports :
- " 8000:80 "
restart : always
environment :
WORDPRESS_DB_HOST : db:3306
WORDPRESS_DB_USER : wordpress
WORDPRESS_DB_PASSWORD : wordpress
volumes :
db_data :
الملف أعلاه هو ملف إنشاء عامل الإرساء لتركيب الخدمة الكاملة.
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
using ( var svc = new Builder ( )
. UseContainer ( )
. UseCompose ( )
. FromFile ( file )
. RemoveOrphans ( )
. Build ( ) . Start ( ) )
{
var installPage = await "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
Assert . AreEqual ( 1 , svc . Hosts . Count ) ;
Assert . AreEqual ( 2 , svc . Containers . Count ) ;
Assert . AreEqual ( 2 , svc . Images . Count ) ;
Assert . AreEqual ( 5 , svc . Services . Count ) ;
}
يقوم المقتطف أعلاه بتكوين خدمة إنشاء عامل الإرساء بسلاسة ويستدعي صفحة التثبيت للتحقق من أن WordPress يعمل بالفعل.
من الممكن أيضًا القيام بجميع العمليات التي تدعمها حاوية واحدة مثل عمليات النسخ والتصدير والانتظار. على سبيل المثال:
var file = Path . Combine ( Directory . GetCurrentDirectory ( ) ,
( TemplateString ) "Resources/ComposeTests/WordPress/docker-compose.yml" ) ;
// @formatter:off
using ( new Builder ( )
. UseContainer ( )
. UseCompose ( )
. FromFile ( file )
. RemoveOrphans ( )
. WaitForHttp ( "wordpress" , "http://localhost:8000/wp-admin/install.php" , continuation : ( resp , cnt ) =>
resp . Body . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ? 0 : 500 )
. Build ( ) . Start ( ) )
// @formatter:on
{
// Since we have waited - this shall now always work.
var installPage = await "http://localhost:8000/wp-admin/install.php" . Wget ( ) ;
Assert . IsTrue ( installPage . IndexOf ( "https://wordpress.org/" , StringComparison . Ordinal ) != - 1 ) ;
}
يقوم المقتطف أعلاه بتشغيل مشروع إنشاء عامل ميناء WordPress والتحقق من عنوان URL http://localhost:8000/wp-admin/install.php ويعيد قيمة معينة في النص (في هذه الحالة "https://wordpress.org" /"). إذا لم يكن الأمر كذلك، فسترجع 500 وستنتظر الدالة WaitForHttp
500 مللي ثانية قبل الاستدعاء مرة أخرى. يعمل هذا أيضًا مع أي لامدا مخصصة، فقط استخدم WaitFor
بدلاً من ذلك. وبالتالي فمن الممكن على سبيل المثال الاستعلام عن قاعدة بيانات قبل المتابعة داخل نطاق الاستخدام.
بالنسبة لمستخدمي Linux وMac، هناك عدة خيارات لكيفية المصادقة تجاه المقبس. لا يدعم FluentDocker أي sudo أو Sudo بدون أي كلمة مرور (تمت إضافة المستخدم كـ NOPASSWD في /etc/sudoer)، أو sudo بكلمة مرور. الافتراضي هو أن FluentDocker يتوقع أن يكون قادرًا على التحدث دون أي sudo . الخيارات عالمية ولكن يمكن تغييرها في وقت التشغيل.
SudoMechanism . None . SetSudo ( ) ; // This is the default
SudoMechanism . Password . SetSudo ( "<my-sudo-password>" ) ;
SudoMechanism . NoPassword . SetSudo ( ) ;
إذا كنت ترغب في إيقاف تشغيل Sudo للتواصل مع برنامج عامل الإرساء، فيمكنك اتباع البرنامج التعليمي الخاص بعامل الإرساء والقيام بالخطوة الأخيرة لإضافة المستخدم الخاص بك إلى مجموعة عامل الإرساء.
يدعم FluentDocker الاتصال ببرامج الإرساء البعيدة. تدعم واجهة برمجة التطبيقات (API) بطلاقة على سبيل المثال
new Builder ( ) . UseHost ( ) . UseMachine ( ) . WithName ( "remote-daemon" )
حيث يتطلب ذلك إدخالاً تم إعداده مسبقًا في سجل جهاز الإرساء . من الممكن أيضًا تحديد إجماليات تسجيل جهاز الإرساء المستندة إلى SSH للاتصال بالبرنامج الخفي البعيد.
using (
var container =
new Builder ( ) . UseHost ( )
. UseSsh ( "192.168.1.27" ) . WithName ( "remote-daemon" )
. WithSshUser ( "solo" ) . WithSshKeyPath ( "${E_LOCALAPPDATA}/lxss/home/martoffi/.ssh/id_rsa" )
. UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( ) )
{
Assert . AreEqual ( ServiceRunningState . Stopped , container . State ) ;
}
سيؤدي هذا المثال إلى إنشاء إدخال تسجيل جديد لجهاز الإرساء باسم Remote-daemon يستخدم SSH بعنوان IP 192.168.1.27 ومستخدم SSH منفردًا . إذا تم العثور بالفعل على إدخال باسم Remote-daemon، فسيتم إعادة استخدام هذا الإدخال. ثم يحصل على IHostService بالشهادات الصحيحة وعنوان URL للبرنامج الخفي البعيد. وبالتالي، من الممكن بعد ذلك إنشاء حاوية إرساء على البرنامج الخفي البعيد، وفي هذه الحالة تكون صورة postgres . عندما يتخلص من الحاوية، كالعادة، فإنه يحذفها من وحدة الإرساء البعيدة. تتأكد IHostService من التقاط جميع الشهادات اللازمة لمصادقة الاتصال.
ينتج المثال أعلاه إدخال تسجيل جهاز الإرساء هذا.
C:Usersmartoffi>docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
remote-daemon * generic Running tcp://192.168.1.27:2376 v18.06.1-ce
من أجل استخدام UseSsh(...)
يجب إعداد نفق SSH بدون كلمة مرور. بالإضافة إلى ذلك، يجب السماح للمستخدم الذي يستخدم النفق بالوصول إلى برنامج عامل الإرساء الخفي.
اتبع هذا البرنامج التعليمي حول كيفية إعداد نفق SSH والتأكد من أن المستخدم يمكنه الوصول إلى برنامج عامل الإرساء الخفي.
قم بشكل أساسي بإنشاء مفتاح rsa جديد لاستخدامه مع نفق SSH باستخدام ssh-keygen -t rsa
ثم انسخه إلى المضيف البعيد بواسطة ssh-copy-id {username}@{host}
.
قم بتحرير /etc/sudoers كما هو محدد في البرنامج التعليمي الثاني.
عند الانتهاء من ذلك، يمكنك الآن الوصول إلى برنامج عامل الإرساء البعيد عن طريق برنامج التشغيل العام أو واجهة برمجة التطبيقات المحددة أعلاه. للقيام بنفس الشيء يدويًا كما هو محدد في المثال، سيبدو الأمر على هذا النحو.
C:Usersmartoffi>docker-machine.exe create --driver generic --generic-ip-address=192.168.1.27 --generic-ssh-key="%localappdata%/lxss/home/martoffi/.ssh/id_rsa" --generic-ssh-user=solo remote-daemon
Running pre-create checks...
Creating machine...
(remote-daemon) Importing SSH key...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with ubuntu(systemd)...
Installing Docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine.exe env remote-daemon
الآن تم إنشاء إدخال التسجيل، ومن الممكن ضبط بيئة عامل الإرساء الطرفي.
C:Usersmartoffi>docker-machine.exe env remote-daemon
SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://192.168.1.24:2376
SET DOCKER_CERT_PATH=C:Usersmartoffi.dockermachinemachinesremote-daemon
SET DOCKER_MACHINE_NAME=remote-daemon
SET COMPOSE_CONVERT_WINDOWS_PATHS=true
REM Run this command to configure your shell:
REM @FOR /f "tokens=*" %i IN ('docker-machine.exe env remote-daemon') DO @%i
قم بتشغيل هذا لجعل عميل عامل الإرساء يستخدم برنامج عامل الإرساء البعيد.
@FOR /f "tokens=*" %i IN ('docker-machine.exe env remote-daemon') DO @%i
سيتم الآن تنفيذ جميع الأوامر التي تستخدم ثنائي docker
على برنامج عامل الإرساء البعيد.
عند الإنشاء والاستعلام، عبر جهاز، جهاز إرساء Hyper-v، يجب رفع مستوى العملية نظرًا لأن Hyper-V لن يستجيب لاستدعاءات واجهة برمجة التطبيقات (API) في وضع المستخدم القياسي.
من الممكن تحديد فحص صحي لحاوية الإرساء للإبلاغ عن حالة الحاوية بناءً على هذا النشاط. يستخدم المثال التالي فحصًا صحيًا للتأكد من خروج الحاوية أم لا. من الممكن التحقق من التكوين (تأكد من فرض التحديث) والحالة التي يبلغ عنها فحص الصحة.
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:latest" , force : true )
. HealthCheck ( "exit" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
var config = container . GetConfiguration ( true ) ;
AreEqual ( HealthState . Starting , config . State . Health . Status ) ;
}
من الممكن عبر Fluent API و ContainerCreateParams
تحديد الحد الأقصى لحاوية الإرساء على سبيل المثال للحد من عدد الملفات المفتوحة وما إلى ذلك. على سبيل المثال، يمكن أن يبدو استخدام Fluent API بهذا الشكل عند تقييد عدد الملفات المفتوحة إلى 2048 (سواء الناعمة أو الصلبة) .
using (
var container =
Fd . UseContainer ( )
. UseImage ( "postgres:latest" , force : true )
. UseUlimit ( Ulimit . NoFile , 2048 , 2048 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. Build ( )
. Start ( ) )
{
// Do stuff
}
يحتوي هذا الريبو على ثلاث حزم nuget، واحدة للوصول بطلاقة، وواحدة للفئات الأساسية لاختبار ms وأخرى للفئات الأساسية xunit لاستخدامها أثناء الاختبار. على سبيل المثال، في اختبار الوحدة، من الممكن تشغيل حاوية postgres والانتظار حتى يتم تشغيل قاعدة البيانات.
public class PostgresXUnitTests : IClassFixture < PostgresTestBase >
{
[ Fact ]
public void Test ( )
{
// We now have a running postgres
// and a valid connection string to use.
}
}
using (
var container =
new DockerBuilder ( )
. WithImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. ExposePorts ( "5432" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ )
. Build ( ) )
{
container . Create ( ) . Start ( ) ;
}
من الممكن أيضًا إعادة استخدام الفئات الأساسية المجردة، على سبيل المثال قاعدة اختبار postgres لتبسيط وإجراء اختبار Unittest نظيف تجاه الحاوية.
[ TestClass ]
public class PostgresMsTests : PostgresTestBase
{
[ TestMethod ]
public void Test ( )
{
// We now have a running postgres
// and a valid connection string to use.
}
}
يسمح FluentDockerTestBase
بتجاوزات بسيطة للقيام بأي اختبار مخصص مدعوم من عامل الإرساء بسهولة. ما عليك سوى إنشاء فئة اختبار واشتقاقها من FluentDockerTestBase
وتجاوز الطرق المناسبة. على سبيل المثال.
protected override DockerBuilder Build ( )
{
return new DockerBuilder ( )
. WithImage ( "kiasaki/alpine-postgres" )
. WithEnvironment ( $ "POSTGRES_PASSWORD= { PostgresPassword } " )
. ExposePorts ( "5432" )
. WaitForPort ( "5432/tcp" , 30000 /*30s*/ ) ;
}
سيؤدي هذا إلى إنشاء مُنشئ باستخدام docker image postgres:latest وتعيين سلسلة بيئة واحدة، كما سيكشف أيضًا عن منفذ postgres db 5432 للمضيف حتى يتمكن الشخص من الاتصال بقاعدة البيانات داخل الحاوية. وأخيرًا، سينتظر المنفذ 5432. وهذا يضمن تشغيل قاعدة البيانات وتمهيدها بشكل صحيح. إذا تم ضبط المهلة، في هذا المثال، على 30 ثانية، فسيتم طرح استثناء وسيتم إيقاف الحاوية وإزالتها. لاحظ أن المنفذ المضيف ليس 5432! استخدم Container.GetHostPort("5432/tcp")
للحصول على المنفذ المضيف. يمكن استرداد عنوان IP الخاص بالمضيف بواسطة خاصية Container.Host
وبالتالي يجب استخدامه عند الاتصال بالتطبيق الموجود في الحاوية.
إذا كانت هناك حاجة إلى رد اتصال عند سحب الحاوية وإنشائها وبدء تشغيلها بنجاح.
protected override void OnContainerInitialized ( )
{
ConnectionString = string . Format ( PostgresConnectionString , Container . Host ,
Container . GetHostPort ( "5432/tcp" ) , PostgresUser ,
PostgresPassword , PostgresDb ) ;
}
يعرض هذا المثال سلسلة اتصال مناسبة لقاعدة بيانات postgresql داخل الحاوية التي تم إنشاؤها حديثًا. يمكن استخدام هذا للاتصال باستخدام Npgsql أو EF7 أو NHibernate أو Marten أو أدوات أخرى متوافقة. لن يتم استدعاء هذه الطريقة في حالة سحب الصورة من مستودع عامل الإرساء أو تعذر إنشاء/بدء تشغيل الحاوية.
إذا كان ربط الحاوية قبل إيقاف التشغيل مطلوبًا لتجاوزه.
protected virtual void OnContainerTearDown ( )
{
// Do stuff before container is shut down.
}
لاحظ أنه إذا لم يتم تسمية الحاوية، وإذا لم يتم التخلص منها بشكل صحيح، فستستمر حاوية الإرساء في العمل ويجب إزالتها يدويًا. هذه ميزة وليست خطأ لأنك قد ترغب في تشغيل عدة حاويات في الاختبار الخاص بك. تدير فئة DockerContainer
معرف مثيل الحاوية وبالتالي تتفاعل معها فقط وليس مع أي حاوية أخرى.
عند إنشاء/بدء حاوية جديدة، سيتم أولاً التحقق من المستودع المحلي إذا كانت صورة الحاوية موجودة بالفعل وسيتم تنزيلها إذا لم يتم العثور عليها. قد يستغرق هذا بعض الوقت، ولا يوجد سوى سجل التصحيح، إذا تم تمكينه، فمن الممكن مراقبة عملية التنزيل.
عندما يحدث استثناء لم تتم معالجته وينتهي تطبيق FailFast بسرعة، فلن يتم استدعاء الجملة finally
. وبالتالي فإن فشل WaitForPort
داخل عبارة using
لن يؤدي إلى التخلص من خدمة الحاوية. ولذلك فإن الحاوية لا تزال قيد التشغيل. لإصلاح هذه المشكلة، قم بإجراء محاولة عالمية...للحصول على واحدة أو حقنها محليًا على سبيل المثال
try
{
using ( var container =
new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=postgres" )
. WaitForPort ( "5777/tcp" , 10000 ) // Fail here since 5777 is not valid port
. Build ( ) )
{
container . Start ( ) ; // FluentDockerException is thrown here since WaitForPort is executed
}
} catch { throw ; }
ولكن هذا يحدث فقط عندما يتم إنهاء التطبيق بسبب FluentDockerException
الذي تم طرحه في WaitForPort
، وإلا فإنه سيتم التخلص من الحاوية بشكل صحيح وبالتالي ليست هناك حاجة إلى try...catch
.
يمكن أيضًا حل هذه المشكلة باستخدام وظائف Fd.Build
( راجع استخدام ملحقات Builder لمزيد من المعلومات).
الفئة Fd
هي فئة ثابتة توفر طرقًا ملائمة لبناء وتشغيل الحاويات الفردية والمركبة. لبناء حاوية فقط استخدم:
var build = Fd . Build ( c => c . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , TimeSpan . FromSeconds ( 30 ) ) ) ;
// This is the equivalent of
var build = new Builder ( ) . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , TimeSpan . FromSeconds ( 30 ) ) ;
يمكن بعد ذلك استخدام هذا لبدء الحاويات ضمن بند using
الآمن الذي يضمن التخلص منه حتى لو لم يتم اكتشاف الاستثناء.
build . Container ( svc =>
{
var config = svc . GetConfiguration ( ) ;
// Do stuff...
} ) ;
بعد تنفيذ طريقة Container
، يتم إيقاف الحاوية وإزالتها في هذه الحالة. هذا هو ما يعادل
// This is equivalent of
try
{
using ( var svc = build . Build ( ) )
{
svc . Start ( ) ;
var config = svc . GetConfiguration ( ) ;
// Do stuff...
}
}
catch
{
Log ( .. . ) ;
throw ;
}
من الممكن أيضًا الجمع بين المُنشئ والتشغيل، على سبيل المثال عبر:
Fd . Container ( c => c . UseContainer ( )
. UseImage ( "postgres:9.6-alpine" )
. ExposePort ( 5432 )
. WithEnvironment ( "POSTGRES_PASSWORD=mysecretpassword" )
. WaitForPort ( "5432/tcp" , TimeSpan . FromSeconds ( 30 ) ) ,
svc =>
{
var config = svc . GetConfiguration ( ) ;
// Do stuff...
} ) ;
المثال أعلاه سوف يقوم بإنشاء الحاوية، وبدءها، وإيقافها، وأخيرًا حذفها. حتى لو تم طرح Exception
، فسيتم Disposed
. بالطبع من الممكن استخدام الحاوية المركبة باستخدام طرق الامتداد composite
كما هو الحال مع container
.