داکرایز کردن و دیپلوی وب اپلیکیشن Spring Bootیی روی فندق

این مقاله بیشتر از اینکه یه مقاله‌ی تخصصی از یه متخصص اسپرینگ و داکر باشه، داستان آخر هفته‌ایه که به کلنجار رفتن با داکر و پلتفرم فندق گذشت. قضیه از اینجا شروع شد که قبلا برای پایان‌نامه‌ام یه وب اپلیکیشن با اسپرینگ بوت (و دیتابیس PostgreSQL) درست کرده بودم و از VPSها برای هاستش استفاده می‌کردم. با توجه به اینکه هزینه‌های VPS خیلی زیاده، همیشه به مشکل میخوردم. چند وقت پیش توی توییتر دیدم یه پلتفرم Paas به اسم فندق توی ایران درست شده که سرویس رایگان هم داره. واسه همین کنجکاو شدم تا ببینم میشه ازش برای کارم استفاده کنم یا نه.

برای شروع فکرکنم بهتر باشه اول یکم در مورد کلمه‌های توی این مقاله مثل داکرایز، فندق، اسپرینگ بوت و ... صحبت کنیم.

داکر (Docker): خانم پیرو توی بلاگش داکر رو اینجوری توضیح داده "داکر ابزاری‌ست برای ساخت، ارسال و اجرای آسان اپلیکیشن. یکی از مهم‌ترین مفاهیم در داکر، کانتینر است. کانتینر با استفاده از مجازی‌سازی در لایه سیستم‌ عامل و ایزوله کردن منابع به این امر کمک می‌کنه. میشه گفت کانتینر و ماشین مجازی تقریبا شبیه هم عمل می‌کنند با این تفاوت که کانتینر در لایه سیستم‌عامل و ماشین مجازی در لایه سخت‌افزار این ایزوله‌سازی را انجام می‌دهند که همین باعث تفاوت‌هایی در عملکرد و پرفورمنسشان می‌شود."

داکرایز کردن (Dockerizing): به فرآیند تغییر برنامه بصورتی که بتونه توی کانتینر داکر اجرا بشه، داکرایز کردن میگن.

داکر هاب (Docker Hub): یه خدمت از شرکت داکر هست تا شرکت‌ها و برنامه‌نویس‌ها بتونن ایمیج‌هاشون رو با هم تیمیشون یا بصورت عمومی به اشتراک بذارن.

فندق: توی سایتشون نوشته " فندق یه PaaS است که به شما این امکان رو میده که سرویس‌های خودتون رو بر روی سرورهای ابری مستقر کنید و نگران مدیریت سرورها و سرویس‌هایی که نیاز دارید نباشید." البته اگر اشتباه نکنم به اینجور پلتفرم‌ها Container as a Service هم میگن.

اسپرینگ بوت (Spring Boot): اسپرینگ بوت یه پروژه‌ای هست که بر روی فریمورک اسپرینگ نوشته شده و کمک میکنه مراحل راه‌اندازی، تنظیمات و اجرای وب اپلیکیشن ساده‌تر و سریعتر انجام بشه. اگر بخواید فقط از خود اسپرینگ استفاده کنید، باید تمام تنظیمات رو خودتون انجام بدید.

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

داکرایز کردن وب اپلیکیشن اسپرینگ بوت

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

۱- با استفاده از maven یه پکیج از وب اپلیکیشن ساختم. وقتی کار maven تموم شد، یه فایل با پسوند war در پوشه‌ی target پروژه ساخته شد.

۲- توی فولدر پروژه یه فایل با اسم Dockerfile ساختم و دستورات زیر رو داخلش نوشتم. اینارو از یه پروژه‌ی آماده کپی کردم و تغییر دادم.

FROM openjdk:8-jdk-alpine
ADD target/crowdsourcing.war target/crowdsourcing.war
EXPOSE 8082
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","target/crowdsourcing.war"]

خط اول میگه که این ایمیج براساس یه ایمیج دیگه ساخته بشه که روش JDK 8 نصب هست. خط سوم war ساخته شده توسط maven رو به ایمیج اضافه میکنه. خط پنجم پورت 8082 رو باز میکنه تا از بیرون کانتینر بشه به سایت دسترسی داشت. در نهایت خط آخر مشخص میکنه وقتی کانتینری از این ایمیج ساخته شد، داخلش فایل war با پروفایل prod اجرا بشه.

۳- اول مطمئن بشید داکر رو بدرستی روی سیستم نصب کردید. خود سایت داکر نحوه‌ی نصب داکر روی سیستم عامل‌های مختلف رو نوشته، بطور مثال این لینک آموزش نصب داکر روی اوبونتو هست. ترمینال رو باز کنید و به مسیر پروژه برید. با دستور زیر براساس Dockerfileیی که ساخته بودم، ایمیج‌ درست شد.

$ docker build .

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

Successfully built d4ef33685011

رشته‌ی "d4ef33685011" شناسه‌ی ایمیج ساخته شده هست، بعدا هرجا خواستم از این ایمیج استفاده کنم با این شناسه میتونم بهش اشاره کنم.

۴- برای تست ایمیج دستور زیر رو اجرا کردم تا متوجه بشم که آیا کانتینر به درستی از روی ایمیج اجرا میشه یا نه؟!

$ docker run d4ef33685011

کانتینر اجرا شد ولی چون پروژه نیاز به دیتابیس PostgreSQL داشت، بعد از اجرا شدن خطا داد و کانتینر بسته شد.

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

$ docker ps

۵- برای دیتابیس PostgreSQL دنبال ایمیج آماده گشتم که اینجارو پیدا کردم، داکیومنت نحوه‌ی استفاده‌اش رو هم نوشتن. چون ایمیج توی داکر هاب هست با این دستور زیر یه کانتینر postgres با تنظیماتی که نیاز داشتم ساختم:

$ docker run --name mypostgres -e POSTGRES_PASSWORD=123456 -e POSTGRES_DB=crowdsourcing -d postgres

این ایمیج "postgres" پورت 5432 رو expose میکنه و بقیه کانتینرها اگر بهش لینک بشن، میتونن از این پورت به دیتابیس متصل بشن. توی connection stringم از شناسه کانتینر برای اتصال به دیتابیس استفاده کردم، مثل زیر:

jdbc:postgresql://mypostgres:5432/crowdsourcing

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

-p 8081:5432

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

۶- حالا با دستور زیر دوباره کانتینتر وب اپلیکیشن رو اجرا کردم. توی دستور زیر کانتینر وب اپلیکیشن رو به کانتینر postgres لینک کردم تا مشکل دسترسی به دیتابیس حل بشه.

$ docker run --link mypostgres d4ef33685011

نتیجه رضایت بخش بود و لاگ‌ها نشون دادن وب اپلیکیشن به درستی اجرا و به دیتابیس متصل شده.

پوش کردن ایمیج روی داکر هاب

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

 $ docker build -t <hub-user>/<repo-name>[:<tag>] 

یا اینکه هر موقع نیاز داشتید، یه ایمیج لوکالتون رو دوباره اینجوری Tagگذاری کنید.

 $ docker tag <existing-image> <hub-user>/<repo-name>[:<tag>] 

من از روش دوم استفاده کردم، فرض کنید اسم اکانتم در داکر هاب abbasoveissi هست و اسم ریپو رو webcorwd گذاشتم. شناسه ایمیج هم که d4ef33685011 بود. با دستور زیر ایمیج تگ‌دار میشه.

 $ docker tag d4ef33685011 abbasoveissi/webcrowd:v1

بعد Tag گذاشتن، با دستور زیر به داکر هاب پوش کردمش.

$ docker push abbasoveissi/webcrowd:v1

سایت داکر هاب رو چک کردم و مطمئن شدم عملیات پوش کردن با موفقیت انجام شده. ایمیج postgres هم که از قبل روی داکر هاب ساخته شده بود و نیاز نبود در رابطه با اون کاری انجام بدم.

دیپلوی وب اپلیکیشن روی فندق

به بخش نهایی رسیدیم. مثل داکر هاب، توی فندق هم یه اکانت ساختم (موقع ثبت‌نام یه namespace پرسیده میشه که بعدا توی آدرس سرویس تاثیر داره). پلن رایگانش اینجوری هست که میذاره دو تا کانتینر داشته باشید که رم‌هاشون ۲۰۰ مگ هست. توضیحات بیشتر رو میتونید توی سایتش بخونید.

فندق سه مدل سرویس داره، خودشون اینجوری توضیح دادن:

سرویس خارجی (ExternalService)- اگر تحت شرایطی بخواهید به یک سرویس بیرون از محیط namespace دسترسی داشته باشید می توانید از سرویس های خارجی استفاده کنید. مثلا سرویس Front از پروژه شما که قرار است از بیرون مشاهده شود باید از نوع سرویس خارجی باشد.

سرویس داخلی (InternalService)- این سرویس ها تنها از داخل namespace قابل دسترسی هستند و فقط سرویس هایی که داخلی این فضا نام باشند می توانند با سرویس های داخلی ارتباط برقرار کنند و هیچ راهی برای برقراری ارتباط با این سرویس ها از خارج از namespace وجود ندارد.

سرویس‌های مدیریت شده (ManagedService)- برخی از سرویس ها مانند MySql یا Postgresql و خیلی موارد دیگر٬ بسیار پر‌کاربرد هستند٬ لذا هسته فندق به صورت خودکار در صورت درخواست کاربر این سرویس ها را می‌سازد تا دیگر شما درگیر تنظیمات آن نشوید و سرعت روند کاری کاهش پیدا نکند. ما به این سرویس ها Managed Service یا سرویس مدیریت شده می‌گوییم.

برای ساخت سرویس ابتدا CLI فندق رو از روی آموزشش نصب کردم و بعد با دستور زیر توی ترمینال لاگین کردم.

$  fandogh  login

اجرا شدن وب اپلیکیشنم به دو تا سرویس نیاز داره:

۱- سرویس خارجی برای وب اپلیکیشن که کاربرها بتونن از طریق اینترنت ازش استفاده کنن

۲- سرویس مدیریت شده برای دیتابیس PostgreSQL

برای ساخت سرویس توی فندق باید مانیفست درست کرد، فندق از تنظیمات توی مانیفست برای ساخت سرویس‌ها استفاده می‌کنه.

برای سرویس خارجی مانیفست زیر رو درست کردم و توی یه فایل با پسوند yaml ذخیره کردم. برخلاف Dockerfile، اسم این فایل مهم نیست.

kind: ExternalService
name: web
spec:
 image: abbasoveissi/webcrowd:v1
 image_pull_policy: Always
 image_pull_secret: "mydockerhub"
 replicas: 1
 port: 8082
 allow_http: true
 env:
    - name: _JAVA_OPTIONS
    value: -Dspring.profiles.active=dev -Xmx160M -Xms130M
 resources:
 memory: 400Mi

توی داکیومنت فندق، تک تک آیتم‌های مانیفست توضیح داده شده، فقط چندتا نکته سریع بگم:

  • چون ریپوی من توی داکر هاب خصوصی هست، برای اینکه فندق بهش دسترسی داشته باشه باید از secretها استفاده می‌کردم. یدونه به اسم mydockerhub ساختم و اسمشو توی مانیفست نوشتم. فندق از طریق این secret میتونه به ریپوی خصوصیم دسترسی داشته باشه و ایمیج رو ازش بگیره. قبلا با دستور زیر secret رو ساختم
 fandogh  secret create --name mydockerhub -t docker-registry -f server=registry.hub.docker.com -f username=JohnKane -f password=J0hnKane 
  • از طریق مانیفست میشه متغیر محیطی ست کرد. مثلا من متغیر _JAVA_OPTIONS رو ست کردم تا هم پروفایلی که وب اپلیکیشن باهاش اجرا میشه رو تغییر بدم و هم مقدار استفاده از رم رو با Xmx و Xms کنترل کنم.
  • با استفاده از port میشه مشخص کرد درخواست‌هایی که به پورت 80 یا 443 سرویستون میرسه به چه پورتی از کانتینر منتقل بشه.
  • توی خط آخر مشخص کردم که میخوام این سرویس ۴۰۰مگ رم استفاده کنه (باید سرویس غیر رایگان باشه وگرنه خطا میده)، دلیلشم اینه نتونستم با کمتر از این مقدار رم، وب اپلیکیشن رو بالا بیارم. به دلیل کم بودن رم، سرویس بسته میشد.

با دستور زیر توی ترمینال، سرویس رو در فندق ساختم.

 fandogh service apply -f manifestname.yaml

آدرس سرویس از ترکیب اسم سرویس و namespace درست میشه. مثل زیر:

service_name-namespace.fandogh.cloud

آدرس سرویس من این شکلی شد:

http://web-abbas.fandogh.cloud

هر درخواستی به آدرس بالا بره، به پورت 8082 کانتینر منتقل میشه.

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

$ fandogh service logs

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

fandogh managed-service deploy postgresql 10.4 -c service_name=myservicename  -c adminer_enabled=true -c postgres_password=123456

البته قبلا connection string رو توی وب اپلیکیشن به عبارت زیر تغییر داده بودم.

jdbc:postgresql://myservicename/dbname

و تماااام!

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

ببخشید دیگه مقاله خیلی طولانی شد ولی امیدوارم که هم بعدا بدرد کسی بخوره و هم خودم در آینده اگر خواستم باز از اینکارها کنم، کمتر نیاز باشه براش سرچ کنم. در آخر از دوستم علیرضا و پشتیبانی فندق برای کمک‌هاشون در حل گیر‌ها تشکر میکنم :)