انتقال للمقال
وقت القراءة: ≈ 15 دقيقة

ربط تطبيق Node.js بـ MySQL باستخدام Docker

السلام عليكم ورحمة الله وبركاته


المقدمة

في المقالات السابقة تعرفنا على الـ Volumes والـ Bind Mounts وكيفية حفظ البيانات بشكل دائم خارج الـ Container
وتعلمنا كيف أن حذف الـ Container لا يعني بالضرورة فقدان البيانات إذا كنا نستخدم Volume

الآن حان الوقت لنطبق ما تعلمناه في مثال عملي حقيقي
في هذه المقالة سنقوم بتشغيل قاعدة بيانات MySQL داخل Container
ثم سنبني تطبيق Node.js بسيط يتصل بقاعدة البيانات هذه ويقوم بتخزين واسترجاع البيانات منها
وسنستخدم Volume لحفظ بيانات قاعدة البيانات بشكل دائم حتى لو حذفنا الـ Container

الجميل في هذا المثال أنك لن تحتاج لتثبيت MySQL على جهازك الشخصي
الـ Docker سيتكفل بتشغيلها داخل Container وأنت تتعامل معها كأنها مثبتة على جهازك
وهذه من أجمل فوائد الـ Docker في بيئة التطوير

تحميل وتجهيز الـ MySQL Image

الـ MySQL لديها Image رسمي على Docker Hub يمكننا استخدامه مباشرة بدون ما نحتاج لكتابة Dockerfile خاص بها
سنستخدم الإصدار 9.6.0 من MySQL، أي أن الـ Image الذي سنستخدمه هو mysql:9.6.0

لكن قبل إنشاء الـ Container، هناك بعض الأشياء التي نحتاج لمعرفتها عن الـ Image الرسمي لـ MySQL

الـ Image الرسمي للـ MySQL يتطلب بعض الـ Environment Variables عند إنشاء الـ Container
وأهم هذه المتغيرات هو MYSQL_ROOT_PASSWORD الذي يحدد كلمة المرور لمستخدم root في MySQL
بدون هذا المتغير، الـ Container لن يعمل وسيعطينا خطأ

وهناك متغيرات أخرى اختيارية لكنها مفيدة جدًا:

  • MYSQL_DATABASE لإنشاء قاعدة بيانات تلقائيًا عند إنشاء الـ Container لأول مرة
  • MYSQL_USER لإنشاء مستخدم جديد غير root
  • MYSQL_PASSWORD لتحديد كلمة المرور لهذا المستخدم الجديد

نحن في هذا المثال سنستخدم MYSQL_ROOT_PASSWORD و MYSQL_DATABASE فقط لتبسيط الأمور

لتمرير هذه المتغيرات إلى الـ Container عند إنشائه، نستخدم خيار -e في أمر docker container run
الخيار -e يسمح لنا بتعريف Environment Variable داخل الـ Container
مثلًا -e MYSQL_ROOT_PASSWORD=tabarani-very-secret سيقوم بتعريف متغير env اسمه MYSQL_ROOT_PASSWORD بقيمة tabarani-very-secret داخل الـ Container


الآن عرفنا كل ما نحتاجه، لننشيء الـ Container من الـ MySQL
الآن نحتاج فقط لإنشاء Volume لربط البيانات التي سنخزنها في قاعدة البيانات MySQL به، حتى لا نفقدها عند حذف الـ Container

> docker volume create mysql-tabarani-db
mysql-tabarani-db

يستحسن أن يكون اسم الـ Volume واضحًا ويعبر عن المشروع أو الخدمة التي يستخدمها، لذلك اخترت mysql-tabarani-db كاسم لهذا الـ Volume
لنتخيل أن اسم المشروع هو Tabarani و هذا الـ Volume مخصص لقاعدة بيانات MySQL الخاصة بهذا المشروع


الآن لنحمل الـ Image الخاص بـ MySQL من الـ Docker Hub

> docker image pull mysql:9.6.0
9.6.0: Pulling from library/mysql
c07617e6f14b: Pulling fs layer
fe44c8bf49c1: Pull complete
4f37333d1be6: Pull complete
e5a384f12fc1: Pull complete
85e7dc27e1dd: Pull complete
7a3034072b44: Pull complete
74e9390a4418: Pull complete
c3c2157be11c: Pull complete
a5b1ba019080: Pull complete
93b95dea6553: Pull complete
Digest: sha256:db32c8ec843c042a728efb0ac7aa814d6f010eaac8923e20ae0a849d09c5baf8
Status: Downloaded newer image for mysql:9.6.0
docker.io/library/mysql:9.6.0

لنرى الـ Image الموجودة في جهازنا:

> docker image list
REPOSITORY          TAG       IMAGE ID       CREATED        SIZE
nodejs-docker-app   v3.0      380feaf700d6   7 hours ago    248MB
mysql               9.6.0     db32c8ec843c   45 hours ago   1.25GB

لدينا nodejs-docker-app الذي أنشأناه في المقالات السابقة، والآن لدينا أيضًا mysql:9.6.0 الذي قمنا بتحميله للتو

الآن لنقم بعمل inspect لهذا الـ mysql:9.6.0 لنتعرف على المزيد من التفاصيل حوله:

> docker image inspect db32c8ec843c
[
    {
      ...
        "Config": {
          ...
            "ExposedPorts": {
                "3306/tcp": {},
                "33060/tcp": {}
            },
            ...
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.19",
                "MYSQL_MAJOR=innovation",
                "MYSQL_VERSION=9.6.0-1.el9",
                "MYSQL_SHELL_VERSION=9.6.0-1.el9"
            ],
            ...
            "Volumes": {
                "/var/lib/mysql": {}
            },
            ...
        },
        ...
    }
]

لقد قمت بإخفاء الكثير من التفاصيل في نتيجة inspect لأنني أريدك فقط تركز على الأجزاء المهمة بالنسبة لنا في هذا المثال
أولًا، الـ Image يستخدم port رقم 3306 وهو الـ port الافتراضي لقاعدة بيانات MySQL
ثانيًا، الـ Image يحتوي على متغيرات env مكتوبة في الـ Dockerfile الخاص به
لكن هناك متغيرات أخرى مهمة مثل MYSQL_ROOT_PASSWORD و MYSQL_DATABASE غير مدونة في الـ Dockerfile لأن هذه المتغيرات يجب أن يحددها المستخدم عند إنشاء الـ Container بالتالي ليس لها قيم افتراضية في الـ Dockerfile
وهذه المعلومات ستحصل خلال قراءة الصفحة الرسمية لهذا الـ Image على Docker Hub
ثالثًا، الـ Image يحتوي على VOLUME يحدد أن مجلد /var/lib/mysql داخل الـ Container مخصص لتخزين بيانات MySQL
بالتالي نفهم أننا نحتاج لربط هذا المجلد بـ Volume خارجي لكي نحافظ على البيانات بشكل دائم ولا نعتمد على الـ Anonymous Volume

إنشاء الـ Container وربطه بالـ Volume

الآن سنقوم بإنشاء الـ Container من الـ Image الخاصة بـ MySQL وربطه بالـ Volume الذي أنشأناه سابقًا
الأمر سيكون طويل قليلًا لأننا سنمرر عدة خيارات عند إنشاء الـ Container، لكن لا تقلق سأشرح كل جزء منه بعد الأمر

> docker container run -d --rm --name mysql-container -p 3306:3306 -e MYSQL_ROOT_PASSWORD=tabarani-very-secret -e MYSQL_DATABASE=tabarani-app -v mysql-tabarani-db:/var/lib/mysql mysql:9.6.0
1a60ca4ea14c77ce61a8d3d40065083de1a9d30a0896f9708e13d407669140b8

حسنًا، سأشرح الأمر بالتفصيل الآن، أظن أنك إن ركزت فيه وقرأته بعناية ستفهم كل جزء منه بسهولة، لأن كل شيء شرحناه من قبل
على أي حال، إليك شرح كل خيار في الأمر:

  • -d لتشغيل الـ Container في الخلفية
  • --rm لحذف الـ Container تلقائيًا عند إيقافه
  • --name mysql-container لتسمية الـ Container باسم واضح
  • -p 3306:3306 لربط الـ port رقم 3306 على جهازنا بالـ port رقم 3306 داخل الـ Container، وهذا هو الـ port الافتراضي لـ MySQL الذي تم تحديده في الـ Image كما رأينا في نتيجة inspect
  • -e MYSQL_ROOT_PASSWORD=tabarani-very-secret لتحديد كلمة مرور مستخدم الـ root الخاص بـ MySQL
  • -e MYSQL_DATABASE=tabarani-app لإنشاء قاعدة بيانات بإسم tabarani-app تلقائيًا عند إنشاء الـ Container لأول مرة
  • -v mysql-tabarani-db:/var/lib/mysql لربط الـ Volume الذي أنشأناه mysql-tabarani-db بمجلد /var/lib/mysql داخل الـ Container حيث يتم تخزين بيانات MySQL
  • mysql:9.6.0 هو اسم الـ Image الذي نريد إنشاء الـ Container منه

لنتأكد أن الـ Container يعمل:

> docker container list
CONTAINER ID   IMAGE         COMMAND                  CREATED         STATUS         PORTS                               NAMES
1a60ca4ea14c   mysql:9.6.0   "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes   0.0.0.0:3306->3306/tcp, 33060/tcp   mysql-container

حسنًا، كما ترى أن الـ Container يعمل
وأن الـ port رقم 3306 مربوط ما بين جهازنا والـ Container
بالتالي أي تطبيق يعمل على جهازنا يمكنه الاتصال بـ MySQL عن طريق localhost:3306 كأن MySQL مثبتة على جهازنا مباشرة

التحقق من أن MySQL يعمل

لكي نقطع الشك باليقين ونتأكد فعلًا أن MySQL يعمل داخل الـ Container وأن قاعدة البيانات tabarani-app تم إنشاؤها بنجاح
سنقوم بالدخول إلى CLI الخاص بـ MySQL داخل الـ Container وننفذ بعض أوامر SQL لنتأكد أن كل شيء يعمل بشكل صحيح

سندخل إلى CLI الخاص بـ MySQL عن طريق تنفيذ أمر mysql داخل الـ Container:

> docker container exec -it mysql-container mysql -u root -p tabarani-very-secret
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 9.6.0 MySQL Community Server - GPL
...
mysql>

هنا باستخدام أمر docker container exec دخلنا إلى الـ Container الذي يعمل فيه MySQL
ثم قمنا بتنفيذ أمر mysql -u root -ptabarani-very-secret داخل الـ Container
وهذا الأمر يفتح لنا واجهة الأوامر الخاصة بـ MySQL حيث يمكننا تنفيذ أوامر SQL

ملحوظة: لاحظ أنه لا توجد مسافة بين -p وكلمة المرور tabarani-very-secret، هذه هي الطريقة لتمرير كلمة المرور في أمر mysql

لنرى قواعد البيانات الموجودة:

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| tabarani-app       |
+--------------------+
5 rows in set (0.013 sec)

لاحظ أن قاعدة البيانات tabarani-app تم إنشاؤها تلقائيًا كما حددنا في -e MYSQL_DATABASE=tabarani-app
أما باقي قواعد البيانات الظاهرة فهي تأتي مع MySQL بشكل افتراضي

حسنًا، كل شيء يعمل بشكل صحيح
لنخرج من الـ CLI الخاص بـ MySQL عن طريق تنفيذ الأمر exit:

mysql> exit
Bye

إنشاء تطبيق Node.js

الآن بعد أن تأكدنا أن MySQL يعمل داخل الـ Container
سنقوم بإنشاء تطبيق Node.js بسيط يتصل بقاعدة البيانات ويقوم بإضافة واسترجاع بيانات المنتجات من جدول يدعى products

لنقم بإنشاء مجلد جديد لمشروعنا:

> mkdir nodejs-mysql-app
> cd nodejs-mysql-app

ملف الـ package.json سيكون كالتالي:

{
  "name": "nodejs-mysql-app",
  "version": "1.0.0",
  "description": "Node.js app connected to MySQL in Docker",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^5.2.1",
    "mysql2": "^3.16.3"
  }
}

لدينا express لإنشاء الـ API ومكتبة mysql2 للتعامل مع قاعدة البيانات MySQL في Node.js

الآن ملف الـ app.js:

const express = require("express");
const mysql = require("mysql2/promise");

const app = express();
const port = process.env.PORT || 3000;

const dbConfig = {
  host: process.env.DB_HOST || "localhost",
  port: process.env.DB_PORT || 3306,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
};

let connection;

async function connectDB() {
  connection = await mysql.createConnection(dbConfig);
  await connection.execute(`
    CREATE TABLE IF NOT EXISTS products (
      id INT AUTO_INCREMENT PRIMARY KEY,
      name VARCHAR(255) NOT NULL,
      price DECIMAL(10, 2) NOT NULL
    )
  `);
  console.log("Connected to MySQL and products table is ready");
}

app.use(express.json());

app.get("/products", async (req, res) => {
  const [rows] = await connection.execute("SELECT * FROM products");
  res.json(rows);
});

app.post("/products", async (req, res) => {
  const { name, price } = req.body;
  const [result] = await connection.execute(
    "INSERT INTO products (name, price) VALUES (?, ?)",
    [name, price],
  );
  res.status(201).json({ id: result.insertId, name, price });
});

connectDB().then(() => {
  app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
  });
});

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

كل ما في الأمر أنه لدينا متغير يدعى dbConfig وهو مجرد object يحتوي على إعدادات الاتصال بقاعدة البيانات MySQL
وهذه الإعدادات تشمل host و port و user و password و database
لاحظ أن host هو localhost و port هو 3306
ولو لاحظت فأنني ربطنا الـ port رقم 3306 الموجود داخل الـ Container بالـ port رقم 3306 على جهازنا
بالتالي أي شيء يتصل بـ localhost:3306 على جهازنا سيتصل فعليًا بقاعدة البيانات MySQL داخل الـ Container
وباقي الإعدادات مثل user و password و database لم نحدد لها قيم افتراضية كنوع من الحماية، لذلك يجب أن نمررها من خلال Environment Variables في ملف الـ .env
ثم لدينا دالة connectDB تقوم بالاتصال بقاعدة البيانات وإنشاء جدول products إذا لم يكن موجودًا

ثم لدينا endpoint للـ GET /products يقوم بجلب جميع المنتجات من جدول products
و endpoint للـ POST /products يقوم بإضافة منتج جديد

وأخيرًا نقوم بالاتصال بقاعدة البيانات أولًا ثم ننشيء الـ Server

ملحوظة: لو كنت شخص جديد على Node.js و Express، فهذا لا يهم فنحن لا نحتاج لفهم كل تفاصيل الكود
فقط ركز على فكرة أننا لدينا MySQL يعمل داخل Docker Container ولدينا Server نريده أن يتصل بهذا الـ MySQL
سواء الـ Server مكتوب بـ Node.js أو PHP أو أي لغة أخرى لا يهم، المهم هو فكرة الاتصال بقاعدة البيانات داخل الـ Container


وأخيرًا ملف الـ .env لتحديد Environment Variables الخاصة بقاعدة البيانات:

# APP Port
PORT=3000

# Database configuration
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=tabarani-very-secret
DB_NAME=tabarani-app

ملحوظة: في هذا المثال لن نقوم بعمل image أو container لتطبيق الـ Node.js هذه المرة، بل سنشغل الـ Server مباشرة على جهازنا بشكل اعتيادي ونربطه بقاعدة البيانات التي تعمل داخل الـ Container
وهذا لتبسيط المثال لا أكثر ولكي لا نضطر إلى الدخول في مشاكل متعلقة بربط Container بـ Container آخر، لأن هذا موضوع سنغطيه في مقالات لاحقة نتكلم فيها عن Docker Networks و Docker Compose

الآن أصبح لدينا كل الملفات اللازمة لتشغيل الـ Server

تشغيل Server الـ Node.js وربطه بقاعدة البيانات MySQL داخل الـ Container

أولًا لا ننسى أن نقوم بتثبيت المكاتب الخاصة بالتطبيق:

> npm install

ثم لنشغل الـ Server:

> npm start

> nodejs-mysql-app@1.0.0 start
> node app.js

Connected to MySQL and products table is ready
Server is running on port 3000

كما تلاحظ الـ Server اتصل بقاعدة البيانات MySQL التي بداخل الـ Container بنجاح
وتم إنشاء جدول products أيضًا
لاحظ أن الـ Server احتل الـ Terminal الخاصة بنا في الـ Foreground لذا سنقوم بفتح نافذة Terminal جديدة لاختبار الـ API الخاص بنا واستكمال باقي الخطوات
ونترك الـ Server يعمل في النافذة الأولى كما هو


الآن لنقم باختبار الـ Server لنتأكد بشكل قاطع أن كل شيء يعمل بشكل صحيح
لنضف بعض المنتجات عن طريق POST /products:

> curl -X POST http://localhost:3000/products -H "Content-Type: application/json" -d '{"name": "Laptop", "price": 1200.00}'
{"id":1,"name":"Laptop","price":1200}

> curl -X POST http://localhost:3000/products -H "Content-Type: application/json" -d '{"name": "Phone", "price": 800.00}'
{"id":2,"name":"Phone","price":800}

> curl -X POST http://localhost:3000/products -H "Content-Type: application/json" -d '{"name": "Tablet", "price": 500.00}'
{"id":3,"name":"Tablet","price":500}

أضفنا ثلاثة منتجات بنجاح ولم نحصل على أي Exception لذا الأمور تسير بشكل جيد
لاحظ أن كل منتج حصل على id تلقائي بفضل AUTO_INCREMENT في جدول products

الآن لنجلب جميع المنتجات عن طريق GET /products:

> curl http://localhost:3000/products
[{"id":1,"name":"Laptop","price":1200},{"id":2,"name":"Phone","price":800},{"id":3,"name":"Tablet","price":500}]

كل المنتجات موجودة وتم تخزينها في قاعدة بيانات MySQL داخل الـ Container

التحقق من البيانات من داخل الـ MySQL مباشرة

لنتأكد أن البيانات موجودة فعلًا في MySQL داخل الـ Container
سندخل إلى CLI الخاص بـ MySQL داخل الـ Container مرة أخرى وننفذ أوامر SQL لعرض البيانات

> docker container exec -it mysql-container mysql -u root -ptabarani-very-secret
...
mysql>

لنختار قاعدة البيانات tabarani-app:

mysql> USE tabarani-app;
Database changed

الآن لنرى المنتجات الموجودة في جدول products:

mysql> SELECT * FROM products;
+----+-------+-------+
| id | name  | price |
+----+-------+-------+
|  1 | Laptop | 1200 |
|  2 | Phone  | 800  |
|  3 | Tablet | 500  |
+----+-------+-------+
3 rows in set (0.0001 sec)

البيانات موجودة في قاعدة البيانات وتم تخزينها بنجاح
هذه البيانات مخزنة في الـ Container في مجلد /var/lib/mysql
ولكن بما أننا ربطنا هذا المجلد بالـ Volume الذي يدعى mysql-tabarani-db
فالبيانات في الحقيقة مخزنة في الـ Volume وليس في الـ Container نفسه
والموجودة في الـ Container هي نسخة من البيانات الموجودة في الـ Volume
بالتالي حتى لو حذفنا الـ Container، البيانات ستظل موجودة في الـ Volume ويمكننا ربطها بأي Container آخر في المستقبل

لنخرج من الـ CLI الخاص بـ MySQL:

mysql> exit
Bye

التأكد من وجود البيانات حتى بعد حذف الـ Container الخاص بـ MySQL

الآن جاء الجزء المهم
لنرى هل الـ Volume سيحمي بياناتنا هذه المرة أم لا
نحن نعرف أن الإجابة نعم لأننا تعلمنا في مقالة حفظ البيانات في Docker باستخدام Volumes أن البيانات المخزنة في Volume لا تتأثر بحذف الـ Container
لكن لنختبر هذا عمليًا مرة أخرى في هذا المثال

أولًا لنوقف تطبيق Node.js بالضغط على Ctrl + C في النافذة التي يعمل فيها الـ Server
ثم لنوقف الـ Container الخاص بـ MySQL وبما أننا استخدمنا --rm فسيتم حذفه تلقائيًا:

> docker container stop mysql-container
mysql-container

هكذا تم حذف الـ Container الخاص بـ MySQL، لنتأكد أن الـ Volume ما زال موجودًا أم لا:

> docker volume list
DRIVER    VOLUME NAME
local     mysql-tabarani-db

الـ Volume الذي يحتوي على بيانات MySQL ما زال موجودًا
الآن لنعيد إنشاء Container جديد ونربطه بنفس الـ Volume:

> docker container run -d --rm --name mysql-container -p 3306:3306 -e MYSQL_ROOT_PASSWORD=tabarani-very-secret -e MYSQL_DATABASE=tabarani-app -v mysql-tabarani-db:/var/lib/mysql mysql:9.6.0
8cc1487ac9c305753fe818aed05380ab8e72414bdb9c0092681e214c124e5b80

لاحظ أن الأمر هو نفسه تمامًا أي نفس الـ Volume و نفس الـ port ونفس كل الإعدادات
الآن لنشغل الـ Server الخاص بالـ Node.js مرة أخرى:

> npm start

> nodejs-mysql-app@1.0.0 start
> node app.js

Connected to MySQL and products table is ready
Server is running on port 3000

الـ Server اتصل بقاعدة البيانات بنجاح
الآن اللحظة الحاسمة، هل البيانات ما زالت موجودة ؟
سنقوم بفتح نافذة Terminal جديدة وننفذ أمر curl لجلب المنتجات مرة أخرى:

> curl http://localhost:3000/products
[{"id":1,"name":"Laptop","price":1200},{"id":2,"name":"Phone","price":800},{"id":3,"name":"Tablet","price":500}]

البيانات ما زالت موجودة
رغم أننا حذفنا الـ Container بالكامل وأنشأنا واحد جديد
لكن البيانات لم تضيع لأنها محفوظة في الـ Volume وليس في الـ Container نفسه

لو أننا لم نستخدم الـ Volume وكتبنا الأمر بدون -v mysql-tabarani-db:/var/lib/mysql
لكانت كل البيانات اختفت عند حذف الـ Container
ولكانت الـ Anonymous Volume الذي تم إنشاؤه تلقائيًا قد تم حذفه مع حذف الـ Container بسبب استخدامنا لخيار --rm
وكنا سنفقد كل البيانات التي أضفناها في قاعدة البيانات MySQL داخل الـ Container

لنضف منتج جديد لنتأكد أن كل شيء يعمل بشكل طبيعي:

> curl -X POST http://localhost:3000/products -H "Content-Type: application/json" -d '{"name": "Headphones", "price": 150}'
{"id":4,"name":"Headphones","price":150}

> curl http://localhost:3000/products
[{"id":1,"name":"Laptop","price":1200},{"id":2,"name":"Phone","price":800},{"id":3,"name":"Tablet","price":500},{"id":4,"name":"Headphones","price":150}]

لاحظ أن الـ id للمنتج الجديد هو 4 وليس 1
لأن MySQL تتذكر آخر id تم استخدامه بفضل AUTO_INCREMENT
وهذا دليل آخر على أن البيانات القديمة ما زالت سليمة ولم يتم إعادة إنشاء الجدول من الصفر

حسنًا لنقم بإيقاف الـ Server مرة أخرى عن طريق Ctrl + C في النافذة التي يعمل فيها الـ Server
ونقم بحذف الـ Container الخاص بـ MySQL

> docker container stop mysql-container
mysql-container

ماذا لو لم نستخدم Volume ؟

ماذا لو نسينا استخدام Volume مع MySQL ؟
لقد عرفنا أن الـ Image الرسمية الخاصة بالـ MySQL يحتوي على أمر VOLUME في الـ Dockerfile الخاص به
وهذا يعني أن الـ Docker سينشئ Anonymous Volume تلقائيًا حتى لو لم نحدد -v عند إنشاء الـ Container

لكن كما تعلمنا في مقالة حفظ البيانات في Docker باستخدام Volumes، أن الـ Anonymous Volume ليس خيارًا جيدًا
لأن اسمه يكون hash عشوائي ولا يمكننا إعادة استخدامه بسهولة بين الـ Containers
وإذا استخدمنا --rm عند إنشاء الـ Container، فسيتم حذف الـ Anonymous Volume تلقائيًا مع حذف الـ Container

لذلك دائمًا استخدم Named Volume عند التعامل مع قواعد البيانات
هكذا تضمن أن بياناتك محفوظة بشكل آمن ويمكنك إعادة استخدامها مع أي Container في المستقبل

الخلاصة

في هذه المقالة قمنا بمثال عملي حقيقي يجمع بين ما تعلمناه في المقالات السابقة
شغلنا MySQL داخل Container بدون ما نحتاج لتثبيتها على جهازنا
واستخدمنا Volume لحفظ بيانات قاعدة البيانات بشكل دائم
ثم أنشأنا تطبيق Node.js بسيط يتصل بقاعدة البيانات ويتعامل معها

وتعلمنا أيضًا أهمية استخدام Named Volume مع قواعد البيانات لحماية البيانات من الضياع

لاحظ أننا في هذه المقالة لم نستخدم Dockerfile لتشغيل تطبيق الـ Node.js داخل Container، بل شغلناه مباشرة على جهازنا وربطناه بقاعدة البيانات داخل الـ Container
فعلت هذا لتبسيط المثال والتركيز على فكرة الاتصال بقاعدة البيانات داخل الـ Container
لأننا لو حولنا Server الخاص بالـ Node.js إلى Container آخر، كنا سنحتاج لربط Container بـ Container آخر وهذا موضوع سنغطيه في مقالات لاحقة عن Docker Networks و Docker Compose