# النشر باستخدام Docker

يوفر استخدام Docker لنشر تطبيق Vapor الخاص بك عدة فوائد:

1. يمكن تشغيل تطبيقك المُحوّل إلى حاوية Docker بشكل موثوق باستخدام الأوامر نفسها على أي منصة تعمل بها خدمة Docker Daemon -- أي Linux (CentOS و Debian و Fedora و Ubuntu) و macOS و Windows.
2. يمكنك استخدام docker-compose أو ملفات Kubernetes manifests لتنسيق الخدمات المتعددة اللازمة للنشر الكامل (مثل Redis و Postgres و nginx وغيرها).
3. من السهل اختبار قدرة تطبيقك على التوسّع أفقيًا، حتى محليًا على جهاز التطوير الخاص بك.

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

عادةً ما تختلف عمليات النشر الأكثر تعقيدًا ومتانة باختلاف حل الاستضافة الذي تعتمده؛ فالعديد من الحلول الشائعة مثل AWS يوفّر دعمًا مدمجًا لـ Kubernetes وحلول قواعد بيانات مخصّصة مما يجعل من الصعب كتابة أفضل الممارسات بطريقة تنطبق على جميع عمليات النشر.

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

## الإعداد

ستحتاج إلى إعداد بيئة التطوير الخاصة بك لتشغيل Docker واكتساب فهم أساسي لملفات الموارد التي تُعِدّ حزم Docker.

### تثبيت Docker

ستحتاج إلى تثبيت Docker في بيئة التطوير الخاصة بك. يمكنك العثور على معلومات لأي منصة في قسم [المنصات المدعومة](https://docs.docker.com/install/#supported-platforms) من نظرة عامة على Docker Engine. إذا كنت تستخدم Mac OS، يمكنك الانتقال مباشرةً إلى صفحة تثبيت [Docker for Mac](https://docs.docker.com/docker-for-mac/install/).

### إنشاء القالب

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

1. ثبّت أو ابنِ Vapor Toolbox ([macOS](../install/macos.md#تثبيت-toolbox) أو [Linux](../install/linux.md#تثبيت-toolbox)).
2. أنشئ تطبيق Vapor جديدًا باستخدام `vapor new my-dockerized-app` واتّبع المطالبات لتفعيل الميزات ذات الصلة أو تعطيلها. ستؤثّر إجاباتك على هذه المطالبات في كيفية إنشاء ملفات موارد Docker.

## موارد Docker

من المفيد، سواء الآن أو في المستقبل القريب، أن تتعرّف على [نظرة عامة على Docker](https://docs.docker.com/engine/docker-overview/). ستشرح النظرة العامة بعض المصطلحات الأساسية التي يستخدمها هذا الدليل.

يحتوي قالب تطبيق Vapor على موردَين أساسيين خاصين بـ Docker: ملف **Dockerfile** وملف **docker-compose**.

### Dockerfile

يخبر ملف Dockerfile كيفية بناء صورة لتطبيقك المُحوّل إلى حاوية. تحتوي تلك الصورة على الملف التنفيذي لتطبيقك وجميع الاعتماديات اللازمة لتشغيله. يستحق [المرجع الكامل](https://docs.docker.com/engine/reference/builder/) أن يبقى مفتوحًا عندما تعمل على تخصيص ملف Dockerfile الخاص بك.

يتألف ملف Dockerfile المُنشأ لتطبيق Vapor الخاص بك من مرحلتين. تبني المرحلة الأولى تطبيقك وتُعِدّ منطقة تخزين مؤقت تحتوي على النتيجة. أما المرحلة الثانية فتُعِدّ أساسيات بيئة تشغيل آمنة، وتنقل كل ما في منطقة التخزين المؤقت إلى مكانه في الصورة النهائية، وتضبط نقطة دخول وأمرًا افتراضيَّين يُشغّلان تطبيقك في وضع الإنتاج على المنفذ الافتراضي (8080). يمكن تجاوز هذا الإعداد عند استخدام الصورة.

### ملف Docker Compose

يحدّد ملف Docker Compose الطريقة التي ينبغي بها لـ Docker بناء خدمات متعددة في علاقتها ببعضها البعض. يوفّر ملف Docker Compose في قالب تطبيق Vapor الوظائف اللازمة لنشر تطبيقك، ولكن إذا أردت معرفة المزيد فعليك الرجوع إلى [المرجع الكامل](https://docs.docker.com/compose/compose-file/) الذي يحتوي على تفاصيل حول جميع الخيارات المتاحة.

!!! note "ملاحظة"
    إذا كنت تخطّط في نهاية المطاف لاستخدام Kubernetes لتنسيق تطبيقك، فإن ملف Docker Compose ليس ذا صلة مباشرة. ومع ذلك، فإن ملفات Kubernetes manifests متشابهة من الناحية المفاهيمية، بل توجد مشاريع تهدف إلى [نقل ملفات Docker Compose](https://kubernetes.io/docs/tasks/configure-pod-container/translate-compose-kubernetes/) إلى Kubernetes manifests.

سيحدّد ملف Docker Compose في تطبيق Vapor الجديد خدمات لتشغيل تطبيقك، وتشغيل الترحيلات (migrations) أو التراجع عنها، وتشغيل قاعدة بيانات كطبقة استمرارية لتطبيقك. ستختلف التعريفات الدقيقة تبعًا لقاعدة البيانات التي اخترت استخدامها عند تشغيل `vapor new`.

لاحظ أن ملف Docker Compose الخاص بك يحتوي على بعض متغيرات البيئة المشتركة قرب أعلى الملف. (قد يكون لديك مجموعة مختلفة من المتغيرات الافتراضية تبعًا لما إذا كنت تستخدم Fluent أم لا، وأي مشغّل Fluent قيد الاستخدام إن كنت تستخدمه.)

```docker
x-shared_environment: &shared_environment
  LOG_LEVEL: ${LOG_LEVEL:-debug}
  DATABASE_HOST: db
  DATABASE_NAME: vapor_database
  DATABASE_USERNAME: vapor_username
  DATABASE_PASSWORD: vapor_password
```

سترى هذه المتغيرات مُدرجة في خدمات متعددة أدناه باستخدام صيغة مرجع YAML وهي `<<: *shared_environment`.

المتغيرات `DATABASE_HOST` و `DATABASE_NAME` و `DATABASE_USERNAME` و `DATABASE_PASSWORD` مُدرجة بقيم ثابتة في هذا المثال، بينما سيأخذ `LOG_LEVEL` قيمته من البيئة التي تُشغّل الخدمة أو سيعود إلى `'debug'` إذا لم يكن هذا المتغير مُعرّفًا.

!!! note "ملاحظة"
    إدراج اسم المستخدم وكلمة المرور بقيم ثابتة مقبول للتطوير المحلي، ولكن ينبغي عليك تخزين هذه المتغيرات في ملف أسرار (secrets) لنشر الإنتاج. إحدى طرق التعامل مع هذا في الإنتاج هي تصدير ملف الأسرار إلى البيئة التي تُشغّل عملية النشر واستخدام أسطر مثل التالي في ملف Docker Compose:

    ```
    DATABASE_USERNAME: ${DATABASE_USERNAME}
    ```

    هذا يمرّر متغير البيئة إلى الحاويات كما هو مُعرّف من قِبل المضيف.

أمور أخرى ينبغي الانتباه إليها:

- تُعرّف اعتماديات الخدمات بواسطة مصفوفات `depends_on`.
- تُكشف منافذ الخدمات للنظام الذي يُشغّل الخدمات باستخدام مصفوفات `ports` (بصيغة `<host_port>:<service_port>`).
- يُعرّف `DATABASE_HOST` بأنه `db`. هذا يعني أن تطبيقك سيصل إلى قاعدة البيانات عبر `http://db:5432`. يعمل هذا لأن Docker سيُشغّل شبكة تستخدمها خدماتك، وسيوجّه نظام DNS الداخلي على تلك الشبكة الاسم `db` إلى الخدمة المسمّاة `'db'`.
- يُتجاوز توجيه `CMD` في ملف Dockerfile في بعض الخدمات باستخدام مصفوفة `command`. لاحظ أن ما يُحدَّد بواسطة `command` يُشغَّل مقابل `ENTRYPOINT` في ملف Dockerfile.
- في وضع Swarm (المزيد حول هذا أدناه)، ستُعطى الخدمات افتراضيًا نسخة واحدة (instance)، ولكن خدمتَي `migrate` و `revert` مُعرّفتان بـ `deploy` `replicas: 0` بحيث لا تبدآن افتراضيًا عند تشغيل Swarm.

## البناء

يخبر ملف Docker Compose كيفية بناء تطبيقك (باستخدام ملف Dockerfile في المجلد الحالي) وكيفية تسمية الصورة الناتجة (`my-dockerized-app:latest`). الأخيرة هي في الواقع مزيج من اسم (`my-dockerized-app`) ووسم (`latest`) حيث تُستخدم الوسوم لإصدار صور Docker.

لبناء صورة Docker لتطبيقك، شغّل

```shell
docker compose build
```

من المجلد الجذر لمشروع تطبيقك (المجلد الذي يحتوي على `docker-compose.yml`).

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

عند الانتهاء، ستجد صورة تطبيقك عند تشغيل

```shell
docker image ls
```

## التشغيل

يمكن تشغيل حزمة خدماتك مباشرةً من ملف Docker Compose، أو يمكنك استخدام طبقة تنسيق مثل وضع Swarm أو Kubernetes.

### مستقل (Standalone)

أبسط طريقة لتشغيل تطبيقك هي بدؤه كحاوية مستقلة. سيستخدم Docker مصفوفات `depends_on` للتأكد من بدء أي خدمات معتمَد عليها أيضًا.

أولاً، نفّذ:

```shell
docker compose up app
```

ولاحظ أن كلتا الخدمتين `app` و `db` تبدآن.

يستمع تطبيقك على المنفذ 8080، وكما هو مُعرّف في ملف Docker Compose، يصبح متاحًا على جهاز التطوير الخاص بك عبر **http://localhost:8080**.

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

زُر `http://localhost:8080` وسترى `It works!`، لكن زُر `http://localhost:8080/todos` وستحصل على:

```
{"error":true,"reason":"Something went wrong."}
```

ألقِ نظرة على مخرجات السجلات في الطرفية التي شغّلت فيها `docker compose up app` وسترى:

```
[ ERROR ] relation "todos" does not exist
```

بالطبع! نحتاج إلى تشغيل الترحيلات على قاعدة البيانات. اضغط `Ctrl+C` لإيقاف تطبيقك. سنبدأ التطبيق مرة أخرى، لكن هذه المرة بـ:

```shell
docker compose up --detach app
```

الآن سيبدأ تطبيقك في وضع "منفصل" (في الخلفية). يمكنك التحقق من ذلك بتشغيل:

```shell
docker container ls
```

حيث سترى كلاً من قاعدة البيانات وتطبيقك يعملان في حاويات. يمكنك حتى التحقق من السجلات بتشغيل:

```shell
docker logs <container_id>
```

لتشغيل الترحيلات، نفّذ:

```shell
docker compose run migrate
```

بعد تشغيل الترحيلات، يمكنك زيارة `http://localhost:8080/todos` مرة أخرى، وستحصل على قائمة فارغة من المهام (todos) بدلاً من رسالة خطأ.

#### مستويات السجل (Log Levels)

تذكّر مما سبق أن متغير البيئة `LOG_LEVEL` في ملف Docker Compose سيُوَرَّث من البيئة التي تبدأ فيها الخدمة إن كان متاحًا.

يمكنك تشغيل خدماتك بـ

```shell
LOG_LEVEL=trace docker-compose up app
```

للحصول على تسجيل بمستوى `trace` (الأكثر تفصيلاً). يمكنك استخدام متغير البيئة هذا لضبط التسجيل إلى [أي مستوى متاح](../basics/logging.md#المستوى-level).

#### سجلات جميع الخدمات

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

```shell
docker-compose up app db
```

#### إيقاف الحاويات المستقلة

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

```shell
docker container stop <container_id>
```

لكن أسهل طريقة لإيقاف هذه الحاويات تحديدًا هي

```shell
docker-compose down
```

#### مسح قاعدة البيانات

يُعرّف ملف Docker Compose وحدة تخزين `db_data` للإبقاء على قاعدة بياناتك بين التشغيلات. هناك طريقتان لإعادة تعيين قاعدة بياناتك.

يمكنك إزالة وحدة التخزين `db_data` في الوقت نفسه الذي تُوقف فيه حاوياتك بـ

```shell
docker-compose down --volumes
```

يمكنك رؤية أي وحدات تخزين تُبقي البيانات حاليًا بـ `docker volume ls`. لاحظ أن اسم وحدة التخزين عادةً ما يكون له بادئة `my-dockerized-app_` أو `test_` تبعًا لما إذا كنت تعمل في وضع Swarm أم لا.

يمكنك إزالة وحدات التخزين هذه واحدة تلو الأخرى بـ، على سبيل المثال

```shell
docker volume rm my-dockerized-app_db_data
```

يمكنك أيضًا تنظيف جميع وحدات التخزين بـ

```shell
docker volume prune
```

فقط كن حذرًا حتى لا تحذف عن طريق الخطأ وحدة تخزين تحتوي على بيانات كنت تريد الاحتفاظ بها!

لن يسمح لك Docker بإزالة وحدات التخزين المستخدمة حاليًا بواسطة حاويات قيد التشغيل أو متوقّفة. يمكنك الحصول على قائمة بالحاويات قيد التشغيل بـ `docker container ls`، ويمكنك رؤية الحاويات المتوقّفة أيضًا بـ `docker container ls -a`.

### وضع Swarm

وضع Swarm واجهة سهلة الاستخدام عندما يكون لديك ملف Docker Compose جاهز وتريد اختبار كيفية توسّع تطبيقك أفقيًا. يمكنك قراءة كل ما يتعلق بوضع Swarm في الصفحات المتفرّعة من [النظرة العامة](https://docs.docker.com/engine/swarm/).

أول ما نحتاجه هو عقدة مدير (manager node) لـ Swarm الخاص بنا. شغّل

```shell
docker swarm init
```

بعد ذلك سنستخدم ملف Docker Compose الخاص بنا لتشغيل حزمة (stack) باسم `'test'` تحتوي على خدماتنا

```shell
docker stack deploy -c docker-compose.yml test
```

يمكننا رؤية كيف تعمل خدماتنا بـ

```shell
docker service ls
```

ينبغي أن تتوقع رؤية `1/1` نسخة لخدمتَي `app` و `db`، و `0/0` نسخة لخدمتَي `migrate` و `revert`.

نحتاج إلى استخدام أمر مختلف لتشغيل الترحيلات في وضع Swarm.

```shell
docker service scale --detach test_migrate=1
```

!!! note "ملاحظة"
    لقد طلبنا للتو من خدمة قصيرة العمر أن تتوسّع إلى نسخة واحدة. ستتوسّع بنجاح، وتعمل، ثم تخرج. ومع ذلك، سيتركها ذلك بـ `0/1` نسخة قيد التشغيل. هذا ليس بالأمر المهم إلى أن نرغب في تشغيل الترحيلات مرة أخرى، لكن لا يمكننا أن نطلب منها "التوسّع إلى نسخة واحدة" إذا كانت بالفعل عند تلك القيمة. من غرائب هذا الإعداد أنه في المرة القادمة التي نريد فيها تشغيل الترحيلات ضمن بيئة تشغيل Swarm نفسها، نحتاج أولاً إلى تقليص الخدمة إلى `0` ثم إعادة توسيعها إلى `1`.

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

إذا أردت تشغيل 5 نسخ من تطبيقك بشكل متزامن، نفّذ

```shell
docker service scale test_app=5
```

بالإضافة إلى مشاهدة Docker يوسّع تطبيقك، يمكنك التحقق من أن 5 نسخ تعمل فعلاً بالتحقق مرة أخرى من `docker service ls`.

يمكنك عرض (ومتابعة) سجلات تطبيقك بـ

```shell
docker service logs -f test_app
```

#### إيقاف خدمات Swarm

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

```shell
docker stack rm test
```

## عمليات نشر الإنتاج

كما ذُكر في الأعلى، لن يتطرّق هذا الدليل إلى تفاصيل كثيرة حول نشر تطبيقك المُحوّل إلى حاوية إلى الإنتاج، لأن الموضوع كبير ويتباين بشكل كبير تبعًا لخدمة الاستضافة (AWS و Azure وغيرها)، والأدوات (Terraform و Ansible وغيرها)، والتنسيق (Docker Swarm و Kubernetes وغيرها).

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

انسخ ملفات مشروعك إلى خادمك، واتصل بالخادم عبر SSH، وشغّل أمر `docker-compose` أو `docker stack deploy` لتشغيل الأمور عن بُعد.

بدلاً من ذلك، اضبط متغير البيئة المحلي `DOCKER_HOST` ليشير إلى خادمك وشغّل أوامر `docker` محليًا على جهازك. من المهم ملاحظة أنه مع هذا النهج، لا تحتاج إلى نسخ أي من ملفات مشروعك إلى الخادم _لكنك_ تحتاج إلى استضافة صورة Docker الخاصة بك في مكان يمكن لخادمك سحبها منه.
