مؤخراً، نشرت مقالة حول استخدام Testcontainers لمحاكاة الاعتمادات الخارجية مثل قاعدة البيانات وذاكرة التخزين المؤقت لاختبارات تكامل الخلفية. كما شرحت تلك المقالة الطرق المختلفة لتشغيل اختبارات التكامل، وبناء بيئة العمل، ومزاياها وعيوبها.
في هذه المقالة، أود أن أظهر بديلًا آخر في حال كنت تستخدم GitHub Actions كمنصة CI الخاصة بك (أشهر حل CI/CD في الوقت الحالي). يُطلق على هذا البديل اسم حاويات الخدمة، وقد أدركت أن عددًا قليلًا من المطورين يبدو أنهم يعرفون عنها.
في هذا الدليل العملي، سأوضح كيفية إنشاء سير عمل GitHub Actions لاختبارات التكامل مع الاعتمادات الخارجية (MongoDB وRedis) باستخدام تطبيق Go التجريبي الذي أنشأناه في ذلك الدليل السابق. سنراجع أيضًا مزايا وعيوب حاويات خدمة GitHub.
متطلبات مسبقة
-
فهم أساسي لسير عمل GitHub Actions.
-
إلمام بحاويات Docker.
-
معرفة أساسية بأدوات Go.
فهرس المحتويات
ما هي حاويات الخدمة؟
حاويات الخدمة هي حاويات دوكر توفر وسيلة بسيطة وقابلة للنقل لاستضافة الاعتماديات مثل قواعد البيانات (MongoDB في مثالنا)، خدمات الويب، أو أنظمة التخزين المؤقت (Redis في مثالنا) التي تحتاجها تطبيقاتك ضمن سير العمل.
تركز هذه المقالة على اختبارات التكامل، ولكن هناك العديد من التطبيقات الأخرى الممكنة لحاويات الخدمة. على سبيل المثال، يمكنك أيضًا استخدامها لتشغيل الأدوات المساعدة المطلوبة من قبل سير العمل الخاص بك، مثل أدوات تحليل الكود، أدوات التحقق من الجودة، أو ماسحات الأمان.
لماذا لا نستخدم دوكر كومبوز؟
يبدو مشابهًا لـ الخدمات في دوكر كومبوز، أليس كذلك؟ حسنًا، لأن الأمر كذلك.
لكن بينما يمكنك تقنيًا استخدام Docker Compose داخل سير عمل GitHub Actions عن طريق تثبيت Docker Compose وتشغيل docker-compose up، توفر حاويات الخدمة نهجًا أكثر تكاملًا وسلاسة مصمم خصيصًا لبيئة GitHub Actions.
أيضًا، بينما هي مشابهة، فإنها تحل مشاكل مختلفة ولها أغراض عامة مختلفة:
-
يعتبر Docker Compose جيدًا عندما تحتاج إلى إدارة تطبيق متعدد الحاويات على جهازك المحلي أو خادم واحد. إنه الأنسب للبيئات الطويلة الأمد.
-
حاويات الخدمة عابرة وتوجد فقط لمدة تشغيل سير العمل، ويتم تعريفها مباشرة داخل ملف سير عمل GitHub Actions الخاص بك.
فقط ضع في اعتبارك أن مجموعة ميزات حاويات الخدمة (على الأقل حتى الآن) أكثر محدودية مقارنةً بـ Docker Compose، لذا كن مستعدًا لاكتشاف بعض الاختناقات المحتملة. سنغطي بعضًا منها في نهاية هذه المقالة.
وقت تشغيل الوظيفة
يمكنك تشغيل مهام GitHub مباشرة على جهاز التشغيل أو في حاوية Docker (عن طريق تحديد خاصية container). الخيار الثاني يبسط الوصول إلى خدماتك من خلال استخدام التسميات التي تحددها في قسم services.
لتشغيلها مباشرة على جهاز التشغيل:
.github/workflows/test.yaml
jobs:
integration-tests:
runs-on: ubuntu-24.04
services:
mongo:
image: mongodb/mongodb-community-server:7.0-ubi8
ports:
- 27017:27017
steps:
- run: |
echo "addr 127.0.0.1:27017"
أو يمكنك تشغيلها في حاوية (Chainguard Go Image في حالتنا):
jobs:
integration-tests:
runs-on: ubuntu-24.04
container: cgr.dev/chainguard/go:latest
services:
mongo:
image: mongodb/mongodb-community-server:7.0-ubi8
ports:
- 27017:27017
steps:
- run: |
echo "addr mongo:27017"
يمكنك أيضًا حذف منفذ المضيف، بحيث يتم تعيين منفذ الحاوية عشوائيًا إلى منفذ مجاني على المضيف. يمكنك بعد ذلك الوصول إلى المنفذ باستخدام المتغير.
فوائد حذف منفذ المضيف:
-
يتجنب صراعات المنافذ – على سبيل المثال، عندما تقوم بتشغيل العديد من الخدمات على نفس المضيف.
-
يعزز قابلية النقل – تصبح إعداداتك أقل اعتمادًا على بيئة المضيف المحددة.
jobs:
integration-tests:
runs-on: ubuntu-24.04
container: cgr.dev/chainguard/go:1.23
services:
mongo:
image: mongodb/mongodb-community-server:7.0-ubi8
ports:
- 27017/tcp
steps:
- run: |
echo "addr mongo:${{ job.services.mongo.ports['27017'] }}"
بالطبع، هناك مزايا وعيوب لكل نهج.
التشغيل في حاوية:
-
المزايا: الوصول المبسط إلى الشبكة (استخدام التسميات كأسماء مضيف)، والتعرض التلقائي للمنافذ ضمن شبكة الحاوية. كما أنك تحصل على عزل/أمان أفضل حيث يتم تشغيل الوظيفة في بيئة معزولة.
-
السلبيات: تكلفة ضمنية لتكنولوجيا الحاويات.
التشغيل على جهاز التشغيل:
-
الإيجابيات: قد تكون التكلفة أقل من تشغيل الوظيفة داخل حاوية.
-
السلبيات: يتطلب تعيين يدوي للمنافذ للوصول إلى حاوية الخدمة (باستخدام localhost:). هناك أيضًا عزل/أمان أقل، حيث يتم تشغيل الوظيفة مباشرة على جهاز التشغيل. هذا قد يؤثر على وظائف أخرى أو على جهاز التشغيل نفسه إذا حدث خطأ ما.
فحص الجاهزية
قبل تشغيل اختبارات التكامل التي تتصل بحاوياتك الموزعة، ستحتاج غالبًا إلى التأكد من أن الخدمات جاهزة. يمكنك القيام بذلك عن طريق تحديد خيارات إنشاء الحاويات مثل health-cmd.
هذا مهم جدًا – وإلا فقد لا تكون الخدمات جاهزة عندما تبدأ في الوصول إليها.
في حالة MongoDB و Redis، ستكون التالية:
services:
mongo:
image: mongodb/mongodb-community-server:7.0-ubi8
ports:
- 27017/27017
options: >-
--health-cmd "echo 'db.runCommand("ping").ok' | mongosh mongodb://localhost:27017/test --quiet"
--health-interval 5s
--health-timeout 10s
--health-retries 10
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 10s
--health-retries 10
في سجلات الإجراءات، يمكنك رؤية حالة الجاهزية:
سجلات الحاويات الخاصة
في مثالنا، نحن نستخدم صورًا عامة من Dockerhub، ولكن من الممكن استخدام صور خاصة من سجلاتك الخاصة أيضًا، مثل Amazon Elastic Container Registry (ECR)، و Google Artifact Registry، وما إلى ذلك.
تأكد من تخزين بيانات الاعتماد في Secrets ثم الإشارة إليها في قسم credentials.
services:
private_service:
image: ghcr.io/org/service_repo
credentials:
username: ${{ secrets.registry_username }}
password: ${{ secrets.registry_token }}
مشاركة البيانات بين الخدمات
يمكنك استخدام الأحجام لمشاركة البيانات بين الخدمات أو خطوات أخرى في وظيفة. يمكنك تحديد أحجام Docker المسماة، أو أحجام Docker المجهولة، أو عمليات الربط على المضيف. ولكن لا يمكن مباشرةً ربط كود المصدر كحجم حاوية. يمكنك الرجوع إلى هذه المناقشة المفتوحة لمزيد من السياق.
لتحديد حجم، تحدد مسار المصدر والمكان: <source>:<destinationPath>
المصدر <source>
هو اسم حجم أو مسار مطلق على آلة المضيف، والمكان <destinationPath>
هو مسار مطلق في الحاوية.
volumes:
- /src/dir:/dst/dir
تقدم وحدات التخزين في دوكر (وأيضًا في إجراءات GitHub باستخدام دوكر) تخزين بيانات دائم ومشاركة بين الحاويات أو خطوات الوظائف، مما يفصل البيانات عن صور الحاويات.
إعداد المشروع
قبل الغوص في الشيفرة المصدرية الكاملة، دعونا نقوم بإعداد مشروعنا لتشغيل اختبارات التكامل باستخدام حاويات خدمة GitHub.
-
إنشاء مستودع جديد على GitHub.
-
تهيئة وحدة Go باستخدام
go mod init
-
إنشاء تطبيق Go بسيط.
-
إضافة اختبارات تكامل في
integration_test.go
-
إنشاء دليل
.github/workflows
. -
إنشاء ملف باسم
integration-tests.yaml
داخل دليل.github/workflows
.
اختبارات تكامل Golang
الآن حيث يمكننا توفير تبعياتنا الخارجية، دعونا نلقي نظرة على كيفية تشغيل اختبارات التكامل لدينا في Go. سنقوم بذلك في قسم الخطوات في ملف سير العمل الخاص بنا.
سنقوم بتشغيل اختباراتنا في حاوية تستخدم صورة Chainguard Go. وهذا يعني أننا لا نحتاج إلى تثبيت/إعداد Go. إذا كنت ترغب في تشغيل اختباراتك مباشرة على آلة التشغيل، تحتاج إلى استخدام إجراء setup-go.
يمكنك العثور على الشيفرة المصدرية الكاملة مع الاختبارات وهذا سير العمل هنا.
.github/workflows/integration-tests.yaml
name: "integration-tests"
on:
workflow_dispatch:
push:
branches:
- main
jobs:
integration-tests:
runs-on: ubuntu-24.04
container: cgr.dev/chainguard/go:latest
env:
MONGO_URI: mongodb://mongo:27017
REDIS_URI: redis://redis:6379
services:
mongo:
image: mongodb/mongodb-community-server:7.0-ubi8
ports:
- 27017:27017
options: >-
--health-cmd "echo 'db.runCommand("ping").ok' | mongosh mongodb://localhost:27017/test --quiet"
--health-interval 5s
--health-timeout 10s
--health-retries 10
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 10s
--health-retries 10
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Download dependencies
run: go mod download
- name: Run Integration Tests
run: go test -tags=integration -timeout=120s -v ./...
لتلخيص ما يجري هنا:
-
نقوم بتشغيل مهمتنا في حاوية مع Go (حاوية)
-
نقوم بتشغيل خدمتين: MongoDB وRedis (خدمات)
-
نقوم بتكوين فحوصات الصحة للتأكد من أن خدماتنا “صحية” عندما نقوم بتشغيل الاختبارات (خيارات)
-
نقوم بإجراء فحص قياسي للشيفرة
-
ثم نقوم بتشغيل اختبارات Go
بمجرد الانتهاء من الإجراء (استغرق حوالي دقيقة واحدة في هذا المثال)، ستتوقف جميع الخدمات وتصبح يتيمة لذا لا داعي للقلق بشأن ذلك.
تجربة شخصية & القيود
لقد استخدمنا حاويات الخدمة لتشغيل اختبارات التكامل الخلفية في BINARLY لبعض الوقت، وكانت تعمل بشكل رائع. لكن إنشاء سير العمل الأولي استغرق بعض الوقت وواجهنا العقبات التالية:
-
لا يمكن تجاوز أو تشغيل أوامر مخصصة في حاوية خدمة الإجراء (كما تفعل في Docker Compose باستخدام خاصية command). فتح طلب سحب
- حل بديل: كان علينا العثور على حل لا يتطلب ذلك. في حالتنا، كنا محظوظين وتمكنا من القيام بنفس الشيء باستخدام متغيرات البيئة.
-
ليس من الممكن بشكل مباشر تحميل كود المصدر كحجم حاوية. نقاش مفتوح
- بينما تعتبر هذه بالفعل قيوداً كبيرة، يمكنك نسخ الكود من مستودعك إلى الدليل الذي قمت بتحميله بعد بدء حاوية الخدمة.
الاستنتاج
حاويات خدمة GitHub هي خيار رائع لإنشاء بيئة اختبار مؤقتة عن طريق تكوينها مباشرة في سير عمل GitHub الخاص بك. مع كون التكوين مشابهًا إلى حد ما لـ Docker Compose، من السهل تشغيل أي تطبيق محمول والتواصل معه في خط أنابيبك. وهذا يضمن أن تقوم مشغلات GitHub بإيقاف كل شيء عند الانتهاء.
إذا كنت تستخدم GitHub Actions، فإن هذه الطريقة تعمل بشكل ممتاز لأنها مصممة خصيصًا لبيئة GitHub Actions.
الموارد
-
اكتشف المزيد من المقالات على packagemain.tech
Source:
https://www.freecodecamp.org/news/how-to-run-integration-tests-with-github-service-containers/