مشروع Docker عملي: تشغيل Spring Boot مع MySQL


تشغيل Docker مع Spring Boot وMySQL يجمع ما تعلمته عن الصور والشبكات والتخزين والإعدادات في مشروع واحد. سنجهّز تطبيق Spring Boot موجودًا للعمل داخل حاوية، ونربطه بقاعدة MySQL ضمن Compose، مع فصل بيانات القاعدة عن عمر الحاويات.
هذا الدرس يفترض أن لديك مشروع Maven صالحًا يستخدم Java 21 وSpring Web وSpring Data JPA وMySQL Driver. ليس مشروعًا كاملًا لواجهة CRUD، ولا يثبت اتصال قاعدة البيانات بمجرد إعادة نص من متحكم ويب. سنوضح كيف تراجع الاتصال من سجلات التطبيق وما يلزم لاختبار البيانات فعلًا.
ابدأ من مقدمة Docker للمبتدئين إذا لم تشغّل حاوية بعد. ملفات هذا المثال تعليمية للتشغيل المحلي، ولم تُبنَ أو تُشغَّل أثناء إعداد المقال. احتفظ بنسخة من مشروعك قبل تعديل ملفاته.
جهّز مشروع Spring Boot
تأكد من نجاح مشروعك خارج الحاوية أولًا. ينبغي أن يوجد pom.xml ومجلد src، وأن تتطابق نسخة Java في إعداد Maven مع صورة البناء. يمكن استخدام Spring Initializr لإنشاء الأساس، لكن اختيار النسخة والاعتماديات مسؤولية المشروع الذي تبدأ منه.
أضف متحكمًا بسيطًا داخل الحزمة التي يفحصها تطبيقك. المثال يستخدم الحزمة com.example.demo؛ غيّرها لتناسب مشروعك:
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@GetMapping("/api/message")
public String message() {
return "Hello from Geek4Arab";
}
}
يعطي المسار /api/message اختبارًا لطبقة HTTP. لا يكتب هذا المتحكم في MySQL، لذلك ستحتاج إلى كيان ومستودع واختبار قراءة وكتابة إن أردت إثبات دورة البيانات كاملة.
اكتب Dockerfile متعدد المراحل
ضع الملف بجوار pom.xml. سنفصل أدوات Maven عن صورة التشغيل النهائية:
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace
COPY pom.xml .
RUN mvn -B dependency:go-offline
COPY src ./src
RUN mvn -B clean package
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN groupadd --system app && useradd --system --gid app app
COPY --from=build --chown=app:app /workspace/target/*.jar /app/app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
تنتج المرحلة الأولى ملف JAR، وتنسخه الثانية مع بيئة Java اللازمة للتشغيل. لا نتجاوز الاختبارات باستخدام -DskipTests افتراضيًا. إذا احتاجت اختبارات مشروعك قاعدة بيانات أو خدمات أخرى، جهّز بيئة اختبار مناسبة بدل إخفاء الفشل.
يفترض النسخ وجود JAR واحد مطابق في target. بعض المشاريع تنتج أكثر من ملف أو وحدات Maven متعددة؛ استخدم اسمًا صريحًا أو عدّل مسار البناء حينها. كذلك فإن المشروع الذي يعتمد على ملفات Maven إضافية يحتاج نسخها قبل خطوة تنزيل الاعتماديات.
أضف .dockerignore:
.git
.env
target
*.log
.idea
استبعاد target صحيح هنا لأن Maven يبني داخل مرحلة البناء. إذا كنت تنسخ JAR مبنيًا على المضيف بدل ذلك، فلا تنقل هذا الاستبعاد دون تعديل الاستراتيجية.
جهّز إعدادات محلية خارج الصورة
أنشئ ملف .env داخل مجلد المشروع بقيم تدريبية، واستبدل كلمات المرور قبل الاستخدام:
MYSQL_DATABASE=g4a_demo
MYSQL_USER=g4a_app
MYSQL_PASSWORD=replace_with_local_training_password
MYSQL_ROOT_PASSWORD=replace_with_separate_root_password
لا ترفع الملف إلى Git. ضع ملف .env.example بلا أسرار لشرح المتغيرات لزملائك. يستخدم التطبيق مستخدمًا مستقلًا بدل root، ولا ننشر منفذ MySQL إلى المضيف في المثال.
شغّل Docker مع Spring Boot وMySQL عبر Compose
أنشئ compose.yaml في المجلد نفسه:
name: g4a-spring-training
services:
db:
image: mysql:8.4
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE:?set MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER:?set MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 --silent"]
interval: 5s
timeout: 3s
retries: 30
start_period: 30s
app:
build: .
ports:
- "127.0.0.1:8090:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/${MYSQL_DATABASE:?set MYSQL_DATABASE}
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER:?set MYSQL_USER}
SPRING_DATASOURCE_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD}
depends_on:
db:
condition: service_healthy
volumes:
mysql_data:
اسم db في عنوان JDBC هو اسم الخدمة داخل شبكة Compose. واستخدام localhost هنا سيشير إلى حاوية التطبيق نفسها، وليس قاعدة البيانات. يتوقف Compose عند غياب المتغيرات المطلوبة بدل تمرير قيم فارغة بصمت.
فحص mysqladmin ping يختبر استجابة خادم MySQL، ولا يثبت أن مستخدم التطبيق يملك الصلاحيات الصحيحة أو أن الجداول جاهزة. استخدم فحصًا إضافيًا مناسبًا إذا احتاج مشروعك التحقق من ذلك قبل استقبال الطلبات.
البناء والفحص الأول
نفّذ من مجلد المشروع:
docker compose config --quiet
docker compose up -d --build
docker compose ps
docker compose logs --tail 80 app
docker compose logs --tail 50 db
قد يستغرق البناء الأول وقتًا لتنزيل الصور واعتماديات Maven. بعد نجاح التشغيل افتح http://localhost:8090/api/message. ظهور النص يختبر مسار HTTP. ثم راجع سجل تهيئة DataSource، ونفّذ اختبارًا من تطبيقك يكتب سجلًا ويقرأه لإثبات اتصال JPA وقاعدة البيانات.
لا نضيف ddl-auto=create أو update كحل تلقائي. إدارة مخطط القاعدة تحتاج قرارًا واضحًا؛ استخدم ترحيلات مثل Flyway أو Liquibase عندما يتطلب مشروعك ذلك. قد يبدأ التطبيق دون أن تكون جداولك موجودة إذا لم يختبرها بعد.
كيف تحفظ البيانات وتحدّث التطبيق؟
وحدة mysql_data تحتفظ بملفات القاعدة خارج حاويتها. يمكنك إعادة بناء التطبيق بعد تعديل الشيفرة:
docker compose up -d --build app
ولإيقاف مختبر المشروع وإزالة حاوياته مع الاحتفاظ بالوحدة المسماة:
docker compose down
لا تضف -v إذا أردت الاحتفاظ بالبيانات. وتذكر أن متغيرات تهيئة MySQL تُستخدم عند تجهيز مجلد بيانات فارغ؛ تغيير كلمة المرور في .env بعد إنشاء القاعدة لا يغيّر حساباتها تلقائيًا. استخدم آلية إدارة المستخدمين في MySQL لتعديل الحسابات الموجودة.
تشخيص المشكلات الشائعة
رسالة Access denied توجّهك إلى المستخدم وكلمة المرور والصلاحيات وحالة تهيئة الوحدة. أما Communications link failure فقد يرتبط باسم الخدمة أو جاهزية الخادم أو الشبكة. ورسالة UnsupportedClassVersionError تشير غالبًا إلى اختلاف نسخة Java بين البناء والتشغيل.
إذا تعارض 8090، غيّر منفذ المضيف فقط واترك 8080 داخل التطبيق ما لم تغيّر إعداد Spring نفسه. وإذا فشل نسخ JAR، افحص مخرجات Maven وعدد الملفات المطابقة. اقرأ أول سبب فعلي في السجلات بدل تعديل عدة إعدادات معًا.
ما الذي يبقى قبل الإنتاج؟
يحتاج النشر الحقيقي إلى حماية النقل والأسرار، وتثبيت سياسة تحديث الصور، ومراقبة، وحدود موارد، ونسخ احتياطي قابل للاستعادة. كما يحتاج إلى إدارة ترحيلات القاعدة واختبار العودة إلى إصدار سابق. النجاح المحلي خطوة للتعلم، وليس شهادة جاهزية تشغيلية.
راجع مشروع Docker مع Spring Boot وMySQL عبر ثلاثة اختبارات مستقلة: هل بُني التطبيق؟ هل يستجيب HTTP؟ وهل تنجح قراءة وكتابة قاعدة البيانات وتبقى بعد إعادة الإنشاء؟ فصل هذه الأسئلة يجعل نتيجة كل اختبار واضحة ويمكن مراجعتها.
أسئلة شائعة
هل نجاح /api/message يثبت اتصال MySQL؟
لا. هذا المسار لا يستعلم من القاعدة. اختبر عملية قراءة وكتابة فعلية وراجع تهيئة DataSource.
هل نحتاج نشر 3306؟
لا يحتاجه التطبيق داخل شبكة Compose. انشر منفذًا محليًا فقط إذا احتجت أداة على المضيف وبإعداد مناسب.
هل تغيير .env يغير كلمة مرور قاعدة موجودة؟
لا يغيّر حسابات قاعدة سبق تهيئتها تلقائيًا. استخدم أدوات MySQL لإدارة الحسابات الموجودة.
هل down يحذف mysql_data؟
يحافظ عليها افتراضيًا في هذا المثال. لا تستخدم down -v إذا أردت استمرار البيانات.
المصادر والمراجعة التقنية
استُلهم ترتيب موضوعات السلسلة من ملاحظات Narayan Prasad (@code.withnarayan). الشرح العربي والأمثلة والرسوم هنا إعداد مستقل، مع الرجوع إلى الوثائق الرسمية لتصحيح وتوسيع المفاهيم.


