یک برنامه با چند کانتینر (Multi container apps)

یک برنامه با چند کانتینر (Multi container apps)

یک برنامه با چند کانتینر (Multi container apps)

تا اینجا با برنامه هایی کار کردیم که روی یک کانتینر اجرا می شدند. ولی حالا می خواهیم MySQL را به صف تکنولوژی های برنامه مان اضافه کنیم. در این شرایط سوال زیر ایجاد می شود:

MySQL قرار است کجا اجرا شود؟ برنامه مان و MySQL را روی یک کانتینر ایجاد کنیم یا روی کانتینرهای مستقلی اجرای شان کنیم؟

به طور کلی،

 هر کانتینر باید یک کار انجام بدهد و آن کار را به خوبی انجام بدهد.

برای این قاعده ی کلی، دلایل زیر را داریم:

  • این احتمال وجود دارد که بخواهید برنامه را گسترش دهید و لازم باشد front end های مختلفی داشته باشید که با APIها و دیتابیس ها به صورت های مختلفی ارتباط داشته باشند.
  • جداسازی کانتینرها به شما این اجازه را می دهد تا بتوانید، نسخه ها را جداگانه و به صورت مجزا مدیریت کنید.
  • شما ممکن است بخواهید از کانتینری برای دیتابیس لوکال استفاده کنید و با استفاده از سرویس مدیریت شده ای، برای دیتابیس محصول هم استفاده کنید. شما نمی خواهید engine دیتابیس و برنامه را با هم جابجا کنید.
  • راه اندازی چندین پردازه ای، نیازمند یک مدیر پردازه (Process Manager) است. با توجه به این که کانتینر فقط یک پردازه را شروع می کند، این کار میتواند فرآیند شروع کار و پایان کار (Startup / Shutdown) کانتینر را پیچیده کند.

و البته دلایل بسیار زیاد دیگری هم وجود دارد. در نتیجه ما برنامه مان را طوری تغییر می دهیم که به صورت زیر کار کند.

شبکه کردن کانتینر (container networking)

یادتان هست که گفتیم کانتینرها، به صورت پیش فرض، در محیط های ایزوله اجرا می شوند و از بقیه ی Processها یا کانتینرهای روی همان ماشین چیزی نمی دانند؟

خب پس چطور به کانتینرها اجازه بدهیم با هم صحبت کنند؟ پاسخ، شبکه کردن (Networking) است. برای این کار، شما لازم نیست یک کارشناس شبکه باشید (هووورااا!).

این قاعده را در یادتان نگه دارید که:

اگر دو کانتینر روی یک شبکه ی مشترک باشند، می توانند با هم صحبت کنند. و اگر روی یک شبکه ی مشترک نباشند، نمی توانند.

شروع MySQL

دو راه برای قراردادن یک کانتینر روی یک شبکه وجود دارد.

  1. اتصال آن در زمان شروع کار.
  2. اتصال یک کانتینر موجود به شبکه.

در این بخش از دوره، ما ابتدا شبکه را ایجاد خواهیم کرد و سپس در زمان بالا آمدن (startup) کانتینر MySQL، آن را به شبکه متصل می کنیم.

  1. ایجاد شبکه
Docker network create todo-app

    2. راه اندازی یک کانتینر MySQL و اتصال آن به شبکه. همچنین ما تعدادی متغیر محیطی (Environment Variables) هم برای آن تعریف می کنیم که کانتینر برای راه اندازی یک دیتابیس از آن استفاده خواهد کرد. توجه داشته باشید، متغیرهای محیطی را می توانید در بخش MySQL Docker Hub listing ببینید.

docker run -d \
    --network todo-app --network-alias mysql \
     -v todo-mysql-data:/var/lib/mysql \
     -e MYSQL_ROOT_PASSWORD=secret \
     -e MYSQL_DATABASE=todos \
    mysql:5.7

اگر از PowerShell استفاده می کنید دستورهای بالا را به صورت زیر وارد کنید.

docker run -d `
    --network todo-app --network-alias mysql `
     -v todo-mysql-data:/var/lib/mysql `
     -e MYSQL_ROOT_PASSWORD=secret `
     -e MYSQL_DATABASE=todos `
    mysql:5.7

همانطور که می بینید ما پرچم network-alias– را هم مشخص کردیم. کمی بعد تر به این موضوع برمی گردیم.

✔نکته
توجه کردید که اینجا از حافظه ای (Volume) با نام todo-mysql-data استفاده کردیم. و آن را روی آدرس var/lib/mysql/ سوار کردیم. این آدرس همان جایی هست که MYSQL اطلاعاتش را ذخیره می کند. این درحالی است که، ما هرگز یک دستور docker volume create اجرا نکردیم.

داکر متوجه می شود که ما می خواهیم از یک named volume استفاده کنیم و در نتیجه به صورت خودکار برایمان آن را می سازد.

     3. برای اینکه مطمئن شویم که دیتابیس بالاست، به دیتابیس وصل شوید و از ارتباط با آن اطمینان حاصل کنید.

Docker exec -it <mysql-container-id> mysql -p

وقتی درخواست رمز عبور ظاهر شد، secret را تایپ کنید. حالا MySQL از ما میخواهد که دستورات مان را برای کار با آن وارد کنیم. حالا دستوری را وارد می کنیم که لیست دیتابیس های موجود را نمایش دهد تا مطمئن شویم دیتابیس todos هم داخل آن لیست موجود است.

Mysql> SHOW DATABASES;

باید خروجی زیر را برای دستور بالا ببینید.

+--------------------+
 | Database           |
 +--------------------+
 | information_schema |
 | mysql              |
 | performance_schema |
 | sys                |
 | todos              |
 +--------------------+
 5 rows in set (0.00 sec)

هوووررررااا ما دیتابیس todos را داریم و برای استفاده آماده است.

اتصال به MySQL

حالا که می دانیم MySQL بالا و درحال اجراست، برویم تا از آن استفاده کنیم! اما سوال اینجاست... چطوری؟ اگر ما کانتینر دیگری را روی همین شبکه ای که MySQL هم روی آن هست اجرا کنیم چطوری می توانیم کانتینرهای دیگر را پیدا کنیم؟ یادتان هست، هر کانتینر آدرس IP خودش را داشت.

برای درک بهتر این موضوع، ما قصد داریم از روی nicolaka/netshoot یک کانتینر بسازیم که همراه خودش ابزار های مفیدی برای خطایابی و ایرادیابی در مشکلات شبکه دارد.

1- یک کانتینر را از روی image مربوط به nicolaka/netshoot می سازیم. مطمئن شوید که به همان شبکه ی قبلی متصل شود.

Docker run -it –network todo-app nicolaka/netshoot

2- درون کانتینر، ما قصد داریم از دستور dig استفاده کنیم که یک ابزار کاربردی DNS است. ما قصد داریم دنبال آدرس IP کانتینری بگردیم که hostname آن mysql است.

Dig mysql

و خروجی مشابه زیر را خواهیم گرفت.

; <<>> DiG 9.14.1 <<>> mysql
 ;; global options: +cmd
 ;; Got answer:
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

 ;; QUESTION SECTION:
 ;mysql.                                             IN        A

 ;; ANSWER SECTION:
 mysql.                                   600     IN        A         172.23.0.2

 ;; Query time: 0 msec
 ;; SERVER: 127.0.0.11#53(127.0.0.11)
 ;; WHEN: Tue Oct 01 23:47:24 UTC 2019
 ;; MSG SIZE  rcvd: 44

در بخش “ANSWER SECTION”، شما یک آدرس از نوع A برای ردیف mysql مشاهده میکنید که به IP با آدرس 172.23.0.2 رسیده است (آدرس IP ای که شما خواهید دید مقادیر دیگری خواهد داشت!). درحالی که mysql معمولا یک hostname معتبر نیست، داکر توانست از آن به آدرس IP کانتینری برسد که همان نام مستعار (alias) شبکه را داشت. (یادتان هست پرچم network-alias– را قبل تر استفاده کرده بودیم؟)

معنی این چیست؟

برنامه ی ما به سادگی نیاز دارد تا به میزبانی با نام mysql متصل شود و با دیتابیس صحبت کند. از این ساده تر نمی شد!

برنامه تان را با MySQL اجرا کنید.

برنامه ی todo برای مشخص کردن تنظیمات اتصال MySQL از تعداد زیادی متغیر محیطی برای تنظیمات پشتیبانی می کند. که عبارتند از:

  • MYSQL_HOST: hostname برای اجرای MySQL سرور.
  • MYSQL_USER: نام کاربری ای که برای اتصال به MYSQL مورد استفاده قرار می گیرد.
  • MYSQL_PASSWORD: رمز عبوری که برای اتصال به MYSQL استفاده می شود.
  • MYSQL_DB: دیتابیسی که می خواهیم به آن وصل شویم.

تنظیمات اتصال را می توانید از طریق متغیرهای محیطی (Env Vars) تنظیم کنید.

درحالی که استفاده از متغیرهای محیطی برای اتصال به دیتابیس در زمان توسعه به طور عمومی مشکلی ندارد، ولی در زمان اجرای برنامه ی نهایی (Production) به شدت خطرناک خواهد بود و به هیچ وجه توصیه نمی شود. در بخش پایانی این دوره درباره ی اینکه چرا نباید برای اطلاعات حساس از ENV استفاده کنید، مطلبی را ارائه خواهم داد.

راهکار امن تر، این است که از Secret Support ای که توسط Orchestration Framework ارائه شده است استفاده کنید. در بیشتر موارد، این اطلاعات حساس بصورت فایل در کانتینر درحال اجرا، Mount شده اند. 

در آینده، برنامه های زیادی را خواهید دید که مانند همین MySQL image و برنامه ی Todo، از متغیرهای محلی با پسوند FILE_، که برای اشاره به فایلی شامل مقادیر حساس هست، پشتیبانی می کنند.

برای مثال، تنظیم متغیر MYSQL_PASSWORD_FILE باعث خواهد شد تا برنامه از محتویات فایل ارجاع داده شده، به عنوان پسورد اتصال به دیتابیس استفاده کند. توجه داشته باشید که داکر، کاری برای پشتیبانی کردن از این متغیر ها انجام نمی دهد، برنامه ی شما باید دنبال این متغیر بگردد و محتویات فایل را بخواند.

بعد از همه ی این توضیحات، وقت آن رسیده است که کانتینر توسعه مان را آماده و راه اندازی کنیم.

1- در همان زمانی که کانتینر به شبکه ای که برای برنامه مان در نظر گرفته ایم متصل می شود، ما هر یک از متغیر های محیطی گفته شده در بالا را نیز مشخص خواهیم کرد.

Docker run -dp 3000:3000 \
            -w /app -v “$(pwd):/app” \
            --network todo-app \
            -e MYSQL_HOST=mysql \
            -e MYSQL_USER=root \
            -e MYSQL_PASSWORD=secret \
            -e MYSQL_DB=todos \
Node:12-alpine \
            Sh -c “yarn install && yarn run dev”

اگر از PowerShell استفاده می کنید دستورهای بالا را مشابه زیر وارد نمایید.

docker run -dp 3000:3000 `
   -w /app -v "$(pwd):/app" `
   --network todo-app `
   -e MYSQL_HOST=mysql `
   -e MYSQL_USER=root `
   -e MYSQL_PASSWORD=secret `
   -e MYSQL_DB=todos `
   node:12-alpine `
   sh -c "yarn install && yarn run dev"

2- اگر به لاگ های کانتینر نگاهی بیندازیم (docker logs <container-id>)، ما باید پیامی را ببینیم که نشان دهنده ی استفاده از دیتابیس است.

# Previous log messages omitted
 $ nodemon src/index.js
 [nodemon] 1.19.2
 [nodemon] to restart at any time, enter `rs`
 [nodemon] watching dir(s): *.*
 [nodemon] starting `node src/index.js`
 Connected to mysql db at host mysql
 Listening on port 3000

3- برنامه را در مرورگرتان باز کنید و تعدادی مورد را به لیست وظایف تان اضافه کنید.

4- به دیتابیس mysql متصل و مطمئن شوید که اطلاعات، درحال ثبت شدن در دیتابیس هستند. یاد آوردی: پسورد دیتابیس secret است.

docker exec -it <mysql-container-id> mysql -p todos

و در خط فرمان MySQL دستور زیر را اجرا کنید:

select * from todo_items;

و چیزی مشابه خروجی زیر خواهید دید:

+--------------------------------------+--------------------+-----------+
 | id                                   | name               | completed |
 +--------------------------------------+--------------------+-----------+
 | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
 | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
 +--------------------------------------+--------------------+-----------+

البته کاملا مشخص است اطلاعاتی که شما در دیتابیس می بینید با این ها متفاوت است. شما باید بتوانید اطلاعاتی که وارد کرده اید را مشاهده کنید.

اگر نگاهی به کانتینرهای درحال اجرا بیندازید (از طریق Docker Dashboard یا Docker CLI) دو کانتینر را می بیند که درحال اجرا هستند. اما، چیزی در این باره نمی بینیم که این دو برای یک برنامه، کنار هم قرار گرفته اند. به زودی یاد می گیریم که چطوری می توانیم این کار را بهتر و حرفه ای تر انجام بدهیم.

خلاصه

در این مرحله، ما برنامه ای داریم که اطلاعات خودش را داخل دیتابیسی در کانتینر مستقلی ذخیره می کند. ما یک مقدار درمورد شبکه کردن کانتینرها یاد گرفتیم.

ممکن است الان احساس کنید در حال غرق شدن زیر حجم زیادی از کارهایی که برای اجرای این برنامه باید انجام بدهید، هستید. ما باید یک شبکه درست می کردیم، کانتینرها را راه می انداختیم، تمامی متغیر های محیطی را تعریف می کردیم، Port هایی را تعریف می کردیم و کارهای متعدد دیگری!!!

اینها کارهای زیادی است که به خاطر سپردنشان یا انتقال آن ها به دیگران، کار خیلی سختی خواهد بود.

در بخش بعدی درباره ی Docker Compose صحبت خواهیم کرد. با Docker Compose، می توانیم به راحتی برنامه مان و کارهایی که برای راه اندازی آن لازم است انجام دهیم را با دیگران به اشتراک بگذاریم تا آن ها بتوانند با یک دستور ساده، برنامه ی ما را راه اندازی کنند.

دوره در دست تالیف است ... rocket
نظرات
اگر login نکردی برامون ایمیلت رو بنویس: