Docker Compose آموزش سریع

اگه نمی‌‌دونید داکر چیه و جرا باید ازش استفاده کنیم، این پست رو بخونید.در این مقاله موارد زیر را با استفاده از جنگو و PostgreSQL توضیح می‌دهم:Docker ComposeDockerfiledocker-compose.ymlحتما توصیه می‌کنم بخش آخر، جمع بندی، را بخونید. از گیج شدن بعد خوندن این پست کاملا جلوگیری می‌کنه.What is Docker Compose?داکر Compose یک ابزار برای تعریف و اجرای اپ‌هایی هستش که از چند کانتینر مختلف استفاده می‌کنند.فرض کنید شما روی توسعه یک وب اپلیکشن توسط فریمورک Django با استفاده دیتابیس PostgreSQL کار می‌کنید. اگر بخواهید از داکر استفاده کنید، باید کانتینر ‌مربوط به ‌Django و PostgreSQL رو جداگانه کانفیگ و اجرا کنید. (‌اینجا فرض من اینه میدونید داکر چیه و چرا باید کانتیترهای جنگو و دیتابیس رو جدا اجرا کرد) اما با استفاده از داکر compose می‌تونید هر دو کانتینر رو به عنوان یک سرویس و با یک دستور اجرا کنید.موارد استفاده از Docker Composeتست خودکار (Automated testing environments)یکی از ویژگی‌های جالب docker compose پشتیبانی از اجرای تست‌های واحد و E2E، به صورت سریع و قابل تکراره و این تست‌ها در محیط‌های مخصوص به خودش انجام می‌شه.چندین محیط ایزوله شده در یک هاست (Single host deployments)داکر Compose از تداخل سرویس های مختلف جلوگیری می‌کنه. می‌تویند از یک سرویس کپی یا کانفینگ‌های مختلفی روی لوکال یا سرور داشته باشید.محیط توسعه (Development Environments)داکر Compose یک راه سریع و ساده برای شروع پروژه ها است چون به سرعت محیط‌های توسعه ایزوله جدید را ایجاد می‌کنه. نرم افزار تمام وابستگی‌های سرویس برنامه (دیتابیس‌ها، API های وب سرویس و غیره) را کانفیگ می‌کنه. این به شما امکان می‌ده با استفاده از یک فرمان، یک یا چند کانتینر برای هر وابستگی ایجاد و شروع کنید.Benefits of Docker Compose:راه اندازی سریع و ساده (Fast and simple configuration)به لطف اسکریپت های YAML و متغیرهای محلی، می‌توانید به راحتی سرویس‌های برنامه را کانفیگ یا تغییر دهید. ( اینکه YAML چیه رو در ادامه می‌خونید)ارتباطات امن (Secure internal communication)قابلیت حمل و پشتیبانی CI/CDاز آنجایی که همه سرویس‌ها در داخل فایل docker-compose تعریف شده‌اند، توسعه‌دهندگان می‌توانند به راحتی به کل کانفیگ دسترسی داشته باشند. با استفاده از فایل docker-compose ، در عرض چند دقیقه محیط توسعه آماده میشه که این به راه اندازی و فعال کردن CI/CD Pipeline کارآمد کمک می‌‌کنه.استفاده بهینه از منابع (Efficient use of resources)داکر Compose به شما امکان می‌ده چندین محیط ایزوله را روی یک هاست اجرا کنید. اجرای همه چیز روی یک سخت افزار به شما اجازه می‌ده منابع آزاد بیشتری داشته باشید.چجوری از Docker Compose استفاده کنیم؟۱. ایجاد یک Dockerfile . این فایل Image های داکر را می‌سازد که به عنوان پایه کانتینرها استفاده می شه. یک Dockerfile، اسکریپت با دستورالعمل‌هایی در مورد نحوه ساخت Docker Image است. این دستورالعمل‌ها در واقع گروهی از دستورات هستند که به طور خودکار در محیط داکر برای ساخت Docker Image خاص اجرا می‌شوند.۲. سرویس‌ها را در یک فایل Compose تعریف کنید. یک فایل جدید در دایرکتوری پروژه با نام docker-compose.yml ایجاد کنید و سرویس‌ها رو با استفاده از سینتکس YAML تعریف کنید.۳. با استفاده از دستور docker compose up کل برنامه اجرا می‌‌شه. ( images & containersl سرویسها اجرا می‌شوند.) در ادامه متوجه می‌شید در اکثر مواقع نیازی به انجام مرحله اول نداریم.آماده کردن فریمورک جنگو با استفاده از Docker Composeبرای اینکه یک مثال ببینیم ۳ مرحله docker compose چجوری اجرا میشه، جنگو و postgreSQL رو با استفاده docker compose کانفیگ می‌کنم. یک دایرکتوری با هر اسمی دلخواهی ایجاد کنید. از این دایرکتوری برای ایجاد سورس اولیه مورد نیاز برای ساختن image استفاده می‌کنیم.۱. فایلی با نام Dockerfile در داخل پوشه پروژه تون (‌دایرکتوری که مرحله قبل ساختید) ایجاد کنید. در لینوکس و مک با دستور touch Dockerfile اینکار انجام میشه.نکته مهم اینه که اسم فایل حتما و حتما باید ‌Dockerfile باشه. بدون هیچ فرمتی مثل txtاسکریپت زیر رو داخل Dockerfile بنویسید. # syntax=docker/dockerfile:1 FROM python:3.10.2 ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 WORKDIR /majid COPY . . RUN pip install -r requirements.txtاگر این پست رو بخونید متوجه می‌شید که Docker image را می‌شه براساس Docker image های دیگه ساخت. در واقع تعداد زیادی Base Image داریم که براساس اونها Image خودمون رو می‌سازیم. Base Image در واقع یک لینوکس کوچک هستش که روی هر کدامشان یک یا چند ابزار پایه با کمترین Dependencyها نصب شده اند.اولین دستورالعمل یک دستور FROM است که به داکر می‌گوید از چه Base image ای برای برنامه خود استفاده می‌کنم. اینجا Base image ما image رسمی پایتون هستش که از قبل دارای تمام ابزارها و بسته‌های مورد نیاز برای جنگو است. در این مورد از Python 3.10.2 استفاده می کنم.با استفاده از ENV متغیرهایی که نیاز داریم رو تعریف و مقداردهی می‌کنیم.متغیر PYTHONDONTWRITEBYTECODE=1 به این معنی هستش که پایتون فایلهای pyc. رو ایجاد نمی‌کنه.متغیر PYTHONUNBUFFERED=1 به این معنی هستش که وقتی کانتینر اجرا شد خروجی بافر نشه.دستور WORKDIR برای تنظیم یک دایرکتوری کاری پیش‌فرض هنگام اجرای بقیه دستورات استفاده می‌شه. این به Docker می گوید که از این مسیر به عنوان مکان پیش فرض برای همه دستورات بعدی استفاده کنه. که از این آدرس می‌شه به عنوان ریشه در دستورات بعدی استفاده کرد. ( اگر آدرس WORKDIR شما بصورت path/to/project/code باشد، می‌تونید تنها از code/ در دستورات بعدی استفاده کنید و این آدرس به آدرس اصلی پوشه روی ماشین شما اشاره می‌کنه) دستور COPY دو پارامتر می گیره: پارامتر اول به Docker آدرس مبدا را می‌گوید و پارامتر دوم به Docker می گوید که می خواهید فایل(ها) در کجا کپی شود. در این مورد، پارامتر اول ما < . > هستش. < . > در اینجا به پوشه فعلی ( پوشه ای خودمون در ابتدا ساختیم و داخلش Dockerfile رو قرار دادیم) اشاره می‌کنه. پارامتر دوم در واقع باید آدرس WORKDIR باشه که در اینجا < . > هستش که اینسری < . > به WORKDIR اشاره می‌کنه. ( توضیح دادم که از آدرس WORKDIR می‌تونیم به عنوان ریشه در آدرس دهی هامون استفاده کنیم)دستور بعدی فایل requirements.txt را نصب می‌کند.در حال حاضر ما یک image جدید بر اساس Python 3.10.2 داریم (البته هنوز ساخته نشده) و dependencies های مورد نیاز رو هم داخلش تعریف کردیم.ما دستورالعمل‌های کامل برای ایجاد یک تصویر سفارشی داریم اما هنوز آن را نساخته‌ایم. دستور انجام این کار بصورت زیر هستش. قبل اینکه دستور را بزنید باید داخل ترمینال با دستور cd وارد دایرکتوری که Dockerfile قرار داره، بشید. docker build -t django-image .در این دستور t - یک تگ به نام django-image به docker image انتساب می‌ده. اینکار برای خوانایی راحتتر انجام می‌شه. در آخر دستور بالا < . > بیانگر این است که Dockerfile در دایرکتوری فعلی هستش.در اینجا خروجی زیادی وجود خواهد داشت که من فقط دو خط اول و آخری را آورده ام.تا اینجا ما با استفاده از Base image: Python3.9 یک Image ساختیم. در این پست توضیح دادم که چجوری می‌شه یک کانتینر ساخت و اجراش کرد. در اینجا هم ما باید کانتینر بسازیم. می‌تونیم مشابه پست قبلی و کانتینرمون رو بسازیم. اما یادتون باشه که در اینصورت ما فقط یک کانتینر براساس Python3.9 و برای جنگو ایجاد کرده ایم. اگر بخواهیم کانتینر برای PostgreSQL رو هم ایجاد کنیم باید مشابه پست قبلی، اول Image مناسب رو از سایت داکرهاب pull کنیم و ..... اما Docker Compose اومده که ما بتونیم بصورت ساده تر و دقیقتر (‌و همینطور خواناتر) همه کانتینرهامون رو بسازیم.۲.فایل docker-compose.yam برای پروژه جنگو با دیتابیس PostgreSQLبرای ساخت و اجرای کانتیر یکسری دستورعمل نیاز هستش که بوسیله فایل docker-compose.yml این دستورالعمل هارو مشخص می‌کنیم. ابتدا یک فایل ایجاد کنید و نام آن را دقیقا برابر docker-compose قرار دهید. فرمت فایل هم می‌تواند yaml یا yml باشد.‌ ( docker-compose.yml ) این فایل دقیقا کنار Dockerfile باشد.اینکه فایل YAML چیه و چه دستوراتی می‌تونیم داخلش بنویسیم و همینطور سینتکس اون چجوری هستش رو حتما سرچ کنید. اینجا صرفا دستورات پایه‌ای و اصلی مورد نیاز برای وب اپلیکشین جنگو و postgrsql رو می‌نویسم.سپس داخل این فایل کدهای زیر را بنویسید:services: web: build: . command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - &quot8000:8000&quot environment: - POSTGRES_NAME=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres depends_on: - db در خط اول کلمه servces برای این است که مشخص کنیم میخواهیم که کدام سرویس‌ها (یا کانتینرها) را می‌خواهیم اجرا کنیم. در اینجا ما سرویس وب را می‌خواهیم اجرا کنیم و در خط دوم web را نوشتم.در خط بعدی build به Dockerfile اشاره می‌کنه. و چون ‌docker-compose.yml کنار داکرفایل هستش، می‌تونیم فقط از . استفاده کنیم ( آدرس دهی در لینوکس به این صورت هستش)در خط بعدی پورتی که میخواهیم جنگو ازشون استفاده کنه رو مشخص کردم. و با استفاده از command دستور اجرای جنگو رو نوشتم. در خط بعدی هم آدرس Docker Volume رو مشخص کردم.در ادامه متغیرهای محلی جنگو را مشخص کردم. اگر با جنگو آشنا باشید به خوبی می‌دانید معنی هرکدام چی هستش.سرویس web برای اجرا وابسته به سرویس db هستش و در خط آخر این رو مشخص کردم.در ادامه کدهای بالا کدهای زیر را هم بنویسید:db: image: postgres volumes: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgresدر خط اول سرویسی که میخواهیم اجرا بشه رو مشخص کردم.در خط بعدی مشخص کردم که سرویس db (کانتینر) بر اساس کدوم Docker Image باشه. در خط بعدی آدرس Docker Volume رو مشخص کردم ( این آدرسی است که درصورتی که دیتابیس رو روی لوکال نصب کنید، اطلاعات داخلش ذخیره خواهد شد. بطور سنتی ماهم از همون آدرس استفاده کردیم. شما می‌تونید هرآدرس دلخواهی استفاده کنید) . سپس متغیرهای محلی دیتابیس رو تعریف کردم. که اگر با جنگو و postgrsql آشنا باشید کاملا می‌دونید چه متغیرهایی هستند. ( هرچند از روی اسم شون هم مشخص هستش)در نهایت فایل docker-compose.yml شما باید بصورت زیر باشه:services: db: image: postgres volumes: - ./data/db:/var/lib/postgresql/data environment: - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres web: build: . ports: - &quot8000:8000&quot command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/majid environment: - POSTGRES_NAME=postgres - POSTGRES_USER=postgres - POSTGRES_PASSWORD=postgres depends_on: - db با استفاده از دستور زیر می‌توانید هر دو کانتینتری که در داخل فایل بالا مشخص کردیم رو همزمان بسازیم و اجرا کنیم ( میتونید داخل فایل بالا چندین سرویس (کانتینر) تعریف کنید):docker copmpose upاگر خروجی مشابه خروجی زیر داشتید یعنی همه چی درست نصب و اجرا شده:با استفاده از دستور docker container ls -a می‌تونید نام کانتینر مروبط به دیتابیس که ایجاد و اجرا شده رو پیدا کنید. سپس توسط دستور زیر به دیتابیس postgresql متصل بشید:docker exec -it container-name psql -U postgresمتصل شدن به دیتابیس PostgreSQLحتما الان سوال می‌پرسید که پس جنگو چیشد؟ چرا نمی‌تونیم آدرس ۰.۰.۰.۰:۸۰۰۰ رو روی مرورگر بدرستی ببینیم؟ دلیلش اینه که هنوز فریمورک جنگو رو اجرا نکردید!!!برای اینکار ۳ راه وجود داره: جنگو رو جداگانه خودتون اجرا کنید. ( البته باید در همون مسیری که فایل requirement.txt نصب شده اینکار را بکنید. چون جنگو در اون مسیر نصب شده)به جای docker compose up در اولین مرتبه که قصد شروع پروژه رو دارید از دستور زیر استفاده کنید:docker compose run web django-admin startproject composeexample .دقت کنید که تنها برای بار اول این دستور رو انجام بدید، در دفعات بعدی باید از دستور docker compose up استفاده کنید.این دستور به Compose میگه تا با استفاده از imageهای وب سرویس، دستور django-admin startproject Name را در یک کانتینر اجرا کند. و خب همانطور که داخل فایل docker-compose.yml توضیح دادم، image با استفاده از دستور build ساخته می‌شه ( که در فایل ما به Dockerfile اشاره می‌کنه)توصیه میکنم حتما از روی سایت داکر، دستور docker compose run رو مطالعه کنید تا فرقش رو با دستور docker compose up متوجه بشید. ( همانند دستورات docker create & docker run برای ایجاد و اجرای کانتینر)۳. بجای استفاده از Dockerfile از base image جنگو استفاده کینم.البته این راه رو توصیه نمیکنم. چون dependencyهای هر پروژه (requirements.txt) متفاوت هستش. حتی ممکنه شما قصد داشته باشید از ورژن خاصی از پایتون یا جنگو استفاده کنید. پس بهتره برای پروژه جنگو از base image استفاده نکنیم و تمام موارد مورد نیاز رو با استفاده از Dockerfile و requirements.txt مشخص کنیم و image مناسب رو بسازیم.در انتها بعد از اجرا کردن جنگو، میبایست تنظیمات پیشفرض جنگو رو متناسب با سرویس db که در docker-compose.yml تعریف کردید، تغییر بدید.# settings.py import os [...] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('POSTGRES_NAME'), 'USER': os.environ.get('POSTGRES_USER'), 'PASSWORD': os.environ.get('POSTGRES_PASSWORD'), 'HOST': 'db', 'PORT': 5432, } }متوقف کردن کانتینرها/سرویس‌هاتنها با استفاده از دستور docker compose down می‌تونید اینکار رو انجام بدید.آیا Dockerfile دوبار اجرا نشد؟؟اگر به نحوه اجرای دستورات docker compose up و docker build ( در مرحله ۲ توضیحش دادم )‌ دقت کرده باشید متوجه می‌شید که هنگام اجرای هر دو دستور Dockerfile هم اجرا می‌شه و داکر شروع میکنه به ساختن ‌‌imageای که توسط Dockerfile مشخص کردیم. ( البته در مثال ما چون image یکبار ساخته شده در مرحله بعدی با اینکه داکرفایل اجرا میشه اما image دیگه ساخته نمیشه)پس ما می‌تونیم وقتی هر دو فایل Dockerfile, docker-compose.yml را ایجاد کردیم، تنها دستور docker compose up رو بزنیم.چون هرکانتینر باید از روی یک image ساخته بشه و داخل فایل docker-compose.yml برای سرویس web به وسیله دستور build مشخص کردیم که کانتینر براساس image ای که مشخصاتش داحل Dockerfile هست ساخته بشه.جمع بندی مهمدر پروژه های تجاری و حتی پروژه های کوچک ما قطعا بیش از یک سرویس وجود داره. مفهوم docker compose برای این بوجود آمده تا بجای اینکه دونه دونه همه کانتینرها رو بسازیم، صرفا با یک دستور همه کانتینرها ایجاد بشن.پس با ساخت و نوشتن دستورالعملهای مناسب داخل فایل docker-compose.yml می‌تونیم براحتی همه سرویس ها رو اجرا کنیم.در صورتی که برای برخی سرویس ها مثل جنگو نیاز داشته باشیم که image مناسب رو خودمون بسازیم تا کانتینر بر اساس اون اجر بشه، تنها با استفاده از Dockerfile و استفاده از دستور build بجای image داخل docker-compose.yml می‌تونیم اینکار رو انجام بدیم. (پس Dockerfile برای هرپروژه ای لازم نیست)مثالی که اینجا زدم با استفاده از فریمورک جنگو و postgrSQL بود. شما می‌تونید هرسرویس دیگری را کاملا مشابه همین مثال با استفاده docker compose بالا بیارید. تنها تفاوتی که وجود خواهد داشت دستورالعمل های داخل docker-compose.yml و فایل Dockerfile ( اگر نیاز داشته باشید به این فایل) خواهد بود.نکته ای که باید از این پست متوجه بشید اینه که docker-compose.yml و Dockerfile چی هستند. (‌ البته من سینتکس و بادگیری نوشتن این دو فایل رو توضیح ندادم) و از همه مهتر عملکرد داکر کامپوز به چه صورت هستش.