سناریوی کامل راه‌اندازی wordpress با داکر

docker + wordpress + mysql
docker + wordpress + mysql

یکی از سناریوهای پر کاربرد استفاده از داکر راه‌اندازی وب‌سایت با وردپرس می‌باشد. این سناریو‌ برای آموزش دستورات داکر نیز بسیار پرکاربرد است زیرا می‌توان با استفاده از آن بسیاری از موارد را بررسی و آموزش داد.

می‌خواهیم یک سایت با استفاده از وردپرس و دیتابیس mysql راه‌اندازی کنیم. همواره قبل از کار با داکر باید در مورد کاری که می‌‌خواهیم با آن انجام دهیم اطلاعات کافی داشته باشیم. البته این موضوع در مورد همه‌ی موارد دیگه نیز صادق است.

تشریح سناریوی مد نظر:

ما اینجا نیاز داریم که یک سرویس وردپرس و یک سرویس دیتابیس راه‌اندازی کنیم که برای کنترل بیشتر یک nginx هم به عنوان reverse proxy راه‌اندازی می‌کنیم تا بتوانیم تنظیمات مد نظر خودمون رو به صورت کامل و راحت اعمال کنیم.

پیاده‌سازی وردپرس با استفاده از داکر
پیاده‌سازی وردپرس با استفاده از داکر

خوب الان می‌دونیم دقیقا چه کانتینرهایی نیاز داریم و هر کدام از آنها قراره در این میان چه کاری را انجام دهند. بعد از این مرحله باید بهترین ایمیج‌هایی که می‌تواند اون نقش مد نظر ما را ایفا کند پیدا کنیم. همیشه ابتدا باید جستجو کنیم و اگر ایمیجی با مشخصات مد نظر خود پیدا نکردیم توسط داکرفایل آن را بسازیم. در برخی از موارد نیز ایمیج‌هایی نزدیک به نیاز خود پیدا می‌کنیم که می‌توانیم با استفاده از داکرفایل آنها را تغییر و مطابق با نیاز خودمون کنیم. معمولا شرکت‌ها و توسعه‌دهنده‌ها به صورت رسمی ایمیج‌های خود را ارائه می‌کنند. گاهی پیش می‌آید که نیاز است که تغییراتی در ایمیج‌ها ایجاد شود که این کار را با استفاده از داکرفایل انجام می‌دهیم. برخی از موارد نیز پیش می‌آید که ایمیج رسمی ارائه شده تمام نیاز شما را برطرف نمی‌کند از این رو نیاز است که تغییرات مد نظر خودتون رو به صورت کامل بر روی ایمیج انجام دهید یا کلا ایمیج را خودتون از ابتدا ایجاد کنید. در این سناریو هر ۳ ایمیج مورد نیاز ما به صورت رسمی وجود دارد و این ایمیج‌ها تمام نیاز ما را پوشش خواهند داد.

با دستور docker search ابتدا ایمیج‌های مورد نیاز خود را پیدا می‌کنیم.

docker search wordpress

پاسخ این دستور بسیار زیاد می‌باشد از این رو برای اینکه بتوانم جستجوی بهتری داشته باشم از آپشن فیلتر برای این دستور استفاده می‌کنم. برای من رسمی بود ایمیج خیلی اهمیت دارد از این رو با استفاده از فیلتر می‌خواهم ایمیج‌هایی که رسمی هستند را به من نشان دهد.

docker search --filter is-official=true wordpress

پاسخ این دستور تنها یک ایمیج است که به صورت رسمی می‌باشد که همان ایمیج مد نظر ما است. در مورد سایر ایمیج‌ها هم به همین منوال می‌تونیم رفتار کنیم. هر گاه ایمیج مد نظر خود را پیدا کردیم با استفاده از دستور docker pull می‌تونیم اون ایمیج را دانلود کنیم.

docker search --filter is-official=true
docker search --filter is-official=true

حالا با دستور pull ایمیج‌های خود را دانلود می‌کنیم. اگر در حین pull کردن ایمیج‌ها مشکل پیدا کردید و خطای 403 گرفتید به پست‌های قبلی یا این پست مراجعه کنید که طی آنها توضیح داده شده است چطور این مشکل رو رفع کنید.

با دستور زیر می‌تونید ایمیج‌های خود را pull کنید.

docker pull wordpress:latest
docker pull nginx:latest
docker pull mysql:5.7

با این دستور می‌توانید بررسی کنید که چه ایمیج‌هایی داخل سرور شما وجود دارد.

docker images
docker images
docker images

چند نکته‌ی مهم که به نظرم هنگام راه‌اندازی هر سرویس باید به آنها توجه کرد:

نکته‌ی اول: ترتیب راه‌اندازی کانتینرها یا پروسه‌ها در سرویس‌ می‌باشد. در اینجا سرویس به معنای محصول نهایی که در سناریوی ما راه‌اندازی سایت با وردپرس است می‌باشد. پروسه و یا کانتینرها هم شامل MySQL و WordPress و nginx می‌باشد.

نکته‌ی دوم: ارتباط بین کانتینرها به چه صورت است و هر کدام چه نیازمندی‌هایی دارند و چه خروجی از آن‌ها انتظار داریم. در سناریوی ما کانتینر WordPress به کانتینر MySQL وابسته می‌باشد و سرویس‌دهی نهایی ما نیز بر اساس خروجی کانتینر nginx می‌باشد و این کانتینر تمام درخواست‌های رسیده را به سمت کانتینر WordPress هدایت خواهد کرد.

نکته‌ی سوم: نکته‌ی مهم دیگه که خیلی حیاتی نیز می‌باشد دیتاهای به وجود آمده که حیات سیستم ما به آنها وابسته است و همواره باید آنها را به خوبی حفظ کنیم. در کل همواره باید دقت کنیم که کدام یک از کانتینرهای ما دارای دیتا یا state است که باید آن حفظ شود. کانفیگ‌ فایل‌ها نیز اطلاعاتی هستند که اگر در ایمیج اصلی وجود ندارد باید حتما حفظ شود. در سناریو‌ی ما کانتینر MySQL دارای اطلاعات مهم است که همواره از طریق کانتینر WordPress در آن ذخیره می‌شود و خود کانتینر WordPress دارای کانفیگ و اطلاعات مهمی است که همواره باید حفظ شود. کانتینر Nginx نیز دارای کانفیگ‌ فایل‌هایی است که دارای اهمیت است و باید آنها را حفظ کنیم. برای اینکه دیتای خودم را بتونم حفظ کنم اینجا از والیوم داکر استفاده می‌کنم.

نکته‌ی چهارم: یکی از مواردی که در سرویس‌دهی خیلی اهمیت دارد اینه‌ که با چه پورت‌هایی می‌خواهیم سرویس‌دهی کنیم و هر سرویس با چه پورتی به کجا سرویس‌دهی خواهد کرد. این موضوع اهمیت دارد که پورت‌های اضافی بر روی سرور باز نشود و هر پورت حوزه‌ی سرویس‌دهی آن به صورت کامل مشخص باشد. در سناریو‌ی ما تنها پورت‌های ۸۰ و ۴۴۳ کانتینر Nginx نیاز است تا برای همه باز باشد و به عموم سرویس‌دهی کند و مابقی کانتینرها باید به صورت داخلی به یکدیگر سرویس‌دهی کنند.

نکته‌ی پنجم: آماده‌سازی راه‌ حلی برای گرفتن پشتیبان از اطلاعات مهم و حیاتی سرویس‌ نهایی که در صورت بروز مشکل برای سرور اصلی بتوان با اطلاعات پشتیبان شده کل سرویس را از همان state نگهداری شده را‌ه‌اندازی کرد. در اینجا نیاز است ما از دیتابیس خود Dump بگیریم و از فایل‌های داخل کانتینر WordPress و کانفیگ‌های داخل Nginx پشتیبان تهیه‌ کنیم.

نکته‌ی ششم: ایجاد امنیت لازم برای سرویس‌ می‌باشد که باید در این راستا اقداماتی انجام شود که به مجموعه‌ی این اقدامات Hardening می‌گویند و خوب است که همواره قبل از هر گونه سرویس‌دهی در هر سرور Hardening صورت گیرد. در سناریو‌ی ما باید نهایت دقت شود که پورت اضافی باز نباشد و در داخل کانتینر MySQL و WordPress از پسوردهای پیچیده و رندم استفاده شود. در سرویس nginx نیز از ssl استفاده کنیم. البته این موارد از ابتدایی‌ترین موضوعات می‌باشد و حتما باید بیشتر از این موارد به امنیت حواسمان باشد.

نکته‌ی هفتم: نکته‌ی مهم دیگر اینکه حتما بعد از نهایی شدن تمام موارد تست‌های لازم انجام شود که این موضوع در سرویس‌های عملیاتی خیلی اهمیت دارد. ما در این سناریو تنها تست‌های راه‌اندازی ابتدایی سرویس را انجام خواهیم داد.

خوب به نظرم تمام موارد رو گفتیم و الان دیگه باید به ترتیب کانتینرها رو راه‌اندازی کنیم.

ایجاد شبکه برای ارتباطات کانتینرها با یکدیگر

docker network create --driver bridge --subnet=172.30.10.0/24 wp-net

با این دستور یک network با نام wp-net که دارای subnet IP به شماره‌ی 172.30.10.0/24 می‌باشد ایجاد کردیم.

با دستور زیر می‌توانیم لیست شبکه‌های داخل سرویس خود را مشاهده کنیم.

docker network ls

با استفاده از دستور زیر می‌توانیم جزئیات بیشتری از شبکه‌ای که ایجاد کردیم را مشاهده کنیم.

docker network inspect wp-net
docker network inspect wp-net
docker network inspect wp-net

در زمان ایجاد کانتینرها باید دقت کنیم که آنها را به این شبکه که ایجاد کردیم مرتبط کنیم.

ایجاد والیوم‌های مورد نیاز

برای هر سه کانتینر نیاز داریم که والیوم داشته باشیم اما در اینجا برای اینکه از دو روش ذخیره‌ی دیتا استفاده کنیم برای کانتینر MySQL و WordPress والیوم ایجاد می‌کنیم و برای کانتیر Nginx از مپ کردن دایرکتوری به کانفیگ‌ها استفاده می‌کنیم. البته می‌توانید والیوم‌ها را ایجاد نکنید و در هنگام راه‌اندازی کانتینر این والیوم‌ها ایجاد می شود.

با استفاده از این دستورات والیوم‌های مورد نیاز را با حجم و نام مشخص ایجاد می‌کنیم.

docker volume create --driver local --opt o=size=2048m --name wp-data
docker volume create --driver local --opt o=size=1024m --name db-data
docker volume create
docker volume create

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

docker volume ls
docker volume ls
docker volume ls

با استفاده از دستور زیر می‌توانیم جزئیات کامل والیوم‌های ایجاد شده را مشاهده کنیم.

docker inspect wp-data
docker inspect db-data
docker inspect volume
docker inspect volume

الان دو تا والیوم مورد نیاز را داریم و تنها نیاز به یک دایرکتوری داریم تا کانفیگ‌های nginx رو به آن مپ کنیم.

mkdir -p /home/ahmad/DockerMe/wp/nginx/conf.d
mkdir -p /home/ahmad/DockerMe/wp/nginx/cert
create directory
create directory

راه‌اندازی کانتینر MySQL

با دستور زیر کانتینر مربوط به MySQL را راه‌اندازی کرده و آن را به شبکه و والیومی که ساخته بودیم متصل می‌کنیم. لطفا به آپشن‌هایی که در این دستور استفاده شده است دقت کنید. سعی کردم از آپشن‌های زیادی استفاده کنم که تا حدودی نحوه‌ی استفاده از آپشن‌های دستور docker run را مرور کنیم.

نکته‌ی خیلی مهم: این دیتابیس و کلا این سناریو به صورت تستی و در یک سرور بدون دسترسی از بیرون راه‌اندازی شده است برای همین پسورد‌ها و یوزرها در زمانی که شما این مستند را مطالعه می‌کنید اعتباری نخواهند داشت اما اگر شما از این دستورات برای راه‌اندازی سرویس خود استفاده می‌کنید لطفا پسوردها و یوزرها و ... را تغییر دهید تا سرور شما دچار مخاطره نشود.

docker run -itd --name mysql --hostname mysql \
--network=wp-net --network-alias=db --ip=172.30.10.10 \
--restart=always --memory=512m \
--mount=source=db-data,target=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=EUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wordpress \
-e MYSQL_PASSWORD=EUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha \
mysql:5.7
docker run mysql container
docker run mysql container

با استفاده از این دستور لیست‌ کانتینرهای خود را بررسی کرده و وضعیت کانتینر MySQL را مشاهده می‌کنیم.

docker ps
docker ps
docker ps

با استفاده از این دستور می‌توانید میزان مصرف منابع این کانتینر را بررسی کنید.توجه کنید که من برای این کانتینر محدود استفاده از رم تا 512 را قرار داده بودم که در تصویر به صورت کامل مشخص است.

docker stats mysql
docker stats mysql
docker stats mysql

همواره تمام دستورات و مواردی که استفاده می‌کنیم رو با روشی باید تست و بررسی کنیم و حتما از صحت عملکرد اونها اطمینان حاصل کنیم. می‌خواهیم بررسی کنیم که دیتابیس wordpress داخل mysql به درستی ایجاد شده است. برای این کار از دستور زیر استفاده می‌کنیم.

docker exec -i mysql mysql -u root -pEUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha  <<< &quotshow databases&quot
docker exec commands
docker exec commands

راه‌اندازی کانتینر WordPress

با استفاده از دستور زیر کانتینر wordpress را راه‌اندازی می‌کنیم. نکته‌ی مهم اینکه باید مراقب باشیم که حتما این کانتینر را به درستی به MySQL لینک کنیم و این ارتباط به خوبی برقرار باشد.

docker run -itd --name wordpress --hostname wordpress \
--network=wp-net --network-alias=wp --ip=172.30.10.20 \
--restart=always --memory=1024m \
--mount=source=wp-data,target=/var/www/html/ \
-e WORDPRESS_DB_PASSWORD=EUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha \
-e WORDPRESS_DB_HOST=db:3306 \
--link mysql:db \
wordpress:latest
docker run wordpress container
docker run wordpress container

با استفاده از دستور زیر وضعیت کانتینر بعد از راه‌اندازی بررسی می‌کنیم.

docker ps
docker ps
docker ps

با استفاده از دستور زیر نیز بررسی می‌کنیم که wordpress چقدر منابع مصرف می‌کند. برای این کانتینر من محدودیت رم 1024 در نظر گرفتم.

docker stats --no-stream
docker stats --no-stream
docker stats --no-stream

بعد از راه‌اندازی کانتینر wordpress برای اینکه از صحت عملکرد آن اطمینان حاصل کنیم ابتدا با دستور زیر لاگ کانتینر را بررسی می‌کنیم و بعد از آن با دستور curl می‌توانیم یک درخواست http به سمت این کانتینر بفرستیم و بعد از دریافت پاسخ 200 ادامه دهیم.

docker logs -f wordpress 
curl -I -L 172.30.10.20
http test
http test

راه‌اندازی کانتینر Nginx:

ابتدا یک دامنه برای قرار دادن روی این سایت انتخاب کنید. من اینجا test.dockerme.ir استفاده می‌کنم. سپس کانتینر Nginx را راه‌اندازی کرده و موارد مربوط به کانفیگ آن را انجام خواهیم داد.

ابتدا با دستور زیر کانتینر nginx را راه‌اندازی می‌کنیم.

docker run -itd --name nginx --hostname nginx \
--network=wp-net --network-alias=web --ip=172.30.10.30 \
--restart=always --memory=512m \
--volume=/home/ahmad/DockerMe/wp/nginx/conf.d:/etc/nginx/conf.d \
--volume=/home/ahmad/DockerMe/wp/nginx/cert:/etc/nginx/cert \
--publish=80:80 --publish=443:443 \
--link wordpress:wp \
nginx:latest
docker run nginx container
docker run nginx container

وضعیت کانتینر‌های در حال کار را بررسی می‌کنیم.

docker ps
docker ps
docker ps

وضعیت مصرف منابع هر یک از کانتینر‌ها را بررسی می‌کنیم.

docker stats --no-stream
docker stats
docker stats

حالا هر کانفیگی داخل فایل conf.d داخل هاست خودمون قرار بدهیم داخل کانتینر لود شده و بر روی سرور nginx اعمال می‌شود. برای اینکه بتوانیم ssl هم استفاده کنیم یک دایرکتوری دیگه با نام cert قرار دادم که تمام certificateها را داخل ان قرار می‌دهیم و از مسیر etc/nginx/cert/ می‌توانیم آنها را در کانفیگ‌ فایل‌ها استفاده کنیم.

کانفیگ سرویس Nginx:

در حال حاضر ما نیاز داریم که یک reverse proxy داشته باشیم که proxy pass بکنه روی پورت ۸۰ کانتینر wordpress از این رو کانفیگ زیر را استفاده می‌کنیم. دقت کنید که این کانفیگ حتما باید در دایرکتوری conf.d قرار داده شود.

server {
  listen 80;
  server_name test.dockerme.ir;
    location / {
      proxy_pass            http://wordpress:80;
      proxy_set_header  Host              $http_host;   # required for docker client's sake
      proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
      proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
      proxy_set_header  X-Forwarded-Proto $scheme;
      add_header X-Powered-By &quotAhmad Rafiee | DockerMe.ir"
        }
 }

کانفیگ سرویس WordPress:

بعد از این مرحله اگر در browser خود دامنه‌ی test.dockerme.ir را وارد کنید به صفحه‌ی اول تنظیمات wordpress می‌رسید که برای نهایی شدن کار می‌بایست مراحل آن را طی کنید.

در این صفحه شما زبان مورد نظر خود را انتخاب می‌کنید.

در این صفحه شما اسم سایت و یوزر و پسورد ورود به سایت به همراه ایمیل خود را وارد می‌کنید. بعد از این مرحله Wordpress به صورت کامل نصب می‌شود.

بعد از این مرحله می‌توانید به پنل مدیریت wordpress وارد بشید.

در انتها هم سایت شما که به صورت کامل راه‌اندازی شده است.

خوب الان سایت ما به صورت کامل آماده است. نکته‌ی مهم اینکه دیتاهای ما که خیلی برای ما مهم است تمام و کمال از داخل کانتینر جدا شده و بر روی هاست قرار دارد. دو تا از کانتینرها توسط docker volume دیتای خود را بر روی دیسک نوشته‌اند و کانتینر nginx توسط مپ کرد دایرکتوری به کانتیتر این کار را انجام می‌دهد. نکته‌ی مهم اینکه هر زمان یکی از کانتینر‌های ما با مشکل مواجه شود به راحتی می‌توانیم آن را پاک کرده و کانتینر جدید را ایجاد نماییم زیرا تمام state ما داخل دیسک نگهداری شده است. پورت‌های اضافی اصلا از سرویس به بیرون منتقل نشده‌اند و تنها پورت‌هایی که در دسترس عموم قرار دارد ۸۰ و ۴۴۳ مربوط به nginx است که مخصوص سرویس‌دهی وب می‌باشد.

گرفتن Backup از دیتابیس:

گرفتن Dump از دیتابیس و قرار دادن بازه‌های تکرار برای آن یکی از مهمترین کارها بعد از راه‌اندازی سرویس دیتابیس می‌باشد که باید همواره با توجه به حساسیت هر دیتابیس این موضوع انجام شود. حالا دیتابیس ما به صورت کانتینر راه‌اندازی شده است. برای این کار هم می‌توان با دستور docker exec به کانتینر متصل شد و داخل آن دستورات Dump را اجرا کرد و یا اینکه از طریق ارتباط شبکه این کار را انجام داد. من روش دوم را معمولا استفاده می‌کنم. برای گرفتن dump از دستور زیر استفاده می‌شود.

mysqldump -u root -p --all-databases --single-transaction --quick  > full-backup-$(date +%F).sql 

از شما پسورد دیتابیس دریافت می‌شود که بعد از آن، dump از روی دیتابیس گرفته خواهد شد. این دستور برای مواقعی که داخل کانتینر و یا سرور دیتابیس باشیم کاربرد دارد که به صورت پیش‌فرض لوکال هاست در نظر گرفته می‌شود. برای مواقعی که از طریق شبکه این کار می‌خواهد انجام شود از دستور زیر استفاده کنید.

mysqldump -h IP_ADDRESS -u root -p --all-databases --single-transaction --quick > full-backup-$(date +%F).sql

نکته‌ی مهم: همواره پشتیبان‌های گرفته شده را از خود سرور بیرون بیاورید برای زمان‌هایی که سرور شما دچار مشکل می‌شود بتوانید کل سامانه رو مجدد بر روی سرور دیگری راه‌اندازی کنید.

برای اینکه بتونید بهترین کانفیگ‌ها رو داشته باشید همواره خوب و دقیق جستجو کنید. لینک‌های خوبی وجود دارد که به شما کمک می‌کند تا این کار را به خوبی انجام دهید. از این لینک می‌توانید استفاده کنید.

استفاده از docker compose:

همواره در اینگونه سرویس‌ها که شامل چند تا کانتینر می‌باشد و در یک سرور می‌خواهد راه‌اندازی شود بهتر است که از docker compose استفاده کنیم. با استفاده از این سایت شما می‌توانید دستورات docker run خود را وارد کرده و معادل کامپوز فایل آن را داشته باشید و بعد از راه‌اندازی آن بتوانید تمام سرویس خود را به صورت کامل راه‌اندازی کنید.

کامپوز فایل این سرویس:

با استفاده از سایت اشاره شده از روی داکر ران‌های مختلف داکر کامپوز را ایجاد می‌کنیم.

version: '3'
services:
    mysql:
        image: 'mysql:5.7'
        container_name: mysql
        restart: always
        environment:
            - MYSQL_ROOT_PASSWORD=EUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha
            - MYSQL_DATABASE=wordpress
            - MYSQL_USER=wordpress
            - MYSQL_PASSWORD=EUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha
    wordpress:
        image: 'wordpress:latest'      
        container_name: wordpress
        restart: always
        environment:
            - 'WORDPRESS_DB_HOST=mysql:3306'
            - WORDPRESS_DB_USER=wordpress
            - WORDPRESS_DB_PASSWORD=EUEBmxTYtgrXdsdsnfHJJwE9V9fKK7Anha
            - WORDPRESS_DB_NAME=wordpress
        links:
            - 'mysql:db'
    nginx:
        image: 'nginx:latest'
        container_name: nginx
        restart: always
        volumes:
            - '/home/ahmad/DockerMe/wp/nginx/conf.d:/etc/nginx/conf.d'
            - '/home/ahmad/DockerMe/wp/nginx/cert:/etc/nginx/cert'
        ports:
            - '80:80'
            - '443:443'
        links:
            - 'wordpress:wp'

همان‌طور که مشاهده می‌کنید تمام آپشن‌ها و کانفیگ‌ها داخل دستور docker run در اینجا به خوبی کنار یکدیگر قرار گرفته و به صورت کامل سرویس مد نظر را ایجاد می کند.

این موارد را در فایلی به نام docker-compose.yml داخل پوشه‌ی اصلی سرویس خود ذخیره می‌کنیم.
در اینجا هم می‌توانید اطلاعات بیشتری در مورد docker compose و موراد آن بدست بیاورید.

نحوه‌ی ران کردن کامپوز فایل:

با استفاده از دستور docker-compose up -d می‌توانید تمام کانتینرهای داخل فایل داکر کامپوز را راه‌اندازی کنید. همانطور که در تصویر زیر مشاهده می‌کنید کانتینرهای مورد نظر راه‌اندازی شده است.

docker-compose up -d
docker-compose up -d

با استفاده از دستور زیر می‌توانید همواره ۱۰۰ خط آخر لاگ کل سرویس که شامل ۳ تا کانتینر می‌شود را مشاهده کنید.

docker-compose logs -f --tail 100

با استفاده از دستور زیر هم می‌توانید وضعیت کنونی کانتیرها و کل سرویس را مشاهده کنید.

docker-compose ps

docker-compose ps
docker-compose ps

در مستندات بعدی نحوه‌ی نوشتن کامپوز فایل به صورت کامل توضیح داده خواهد شد.


آموزش داکر و پلتفرم به زبان فارسی
آموزش داکر و پلتفرم به زبان فارسی
https://dockerme.ir/