در این پست سکو  قصد داریم با استفاده از ابزار Maven یا Gradle، روند ایجاد کانتینر داکر برای اجرای برنامه‌ی جاوایی مبتنی بر Spring Boot رو توضیح بدیم و عبارت معروف Hello world رو با هم در خروجی ببینیم.

اما قبل از خوندن این پست، خوبه که با هر کدوم از مفاهیم زیر آشنا بشین:

  • برنامه‌ی Spring Boot:

spring boot یک فریم‌ورک برنامه‌نویسی و توسعه‌ی نرم‌افزار با جاواست که برای ایجاد برنامه‌های تحت وب و میکرو سرویس ازش استفاده می‌شه. 

spring boot کمک می‌کنه تمام مراحل راه‌اندازی، تنظیم و اجرای برنامه‌ها راحت‌تر و سریع‌تر انجام بشه.
اگه بخواین فقط از خود spring استفاده کنین، باید همه‌ی تنظیمات رو خودتون انجام بدین. 

  • داکر: 

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

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

ایمیج داکر هم دستورالعملیه برای اجرای یک فرآیند کانتینری (بسته‌بندی) شده، که تو این پست ما یک ایمیج رو برای اجرای برنامه‌ی ساده Spring Boot ایجاد می‌کنیم. 

اگه دوست دارین در مورد داکر بیش‌تر بدونین، خوندن این پست سکو رو بهتون پیشنهاد می‌کنیم. 

  • ساخت برنامه‌ی جاوا با استفاده از Gradle یا Maven:

برای هر زبان برنامه‌نویسی، build system های مختلفی به وجود اومده:
جاوا هم سه build system اصلی داره به نام‌های Ant، Maven و Gradle. برای آشنایی بیش‌تر با این سه build system به این لینک سر بزنین.

هدف اصلی build system ها، کامپایل کردن و اجرای خودکار کدهاست. کافیه برای اون‌ها لیستی از وظایف رو تعیین کنین تا به صورت خودکار اون وظایف رو انجام بدن. 

پیش‌نیازهای لازم

اگر از سیستم لینوکس استفاده نمی‌کنین، به یک سرور مجازی‌ نیاز دارین.
به وب‌سایت دانلود VirtualBox مراجعه کنین و ورژن مناسب VirtualBox رو برای سیستم‌‌تون نصب کنین و نگران اجرای اون نباشین.

دریافت ریپازیتوری این مثال از Git 

بعد از نصب تمام نرم‌افزارهای مورد نیاز، می‌تونین محتویات داخل ریپازیتوری رو از Git دانلود کرده و از حالت فشرده خارج کنین یا از دستور clone استفاده کنین: 

git clone https://github.com/shrikarvk/SpringBootOnDocker.git

. . .

حالا در ادامه توضیح میدیم که چطوری باید فایل‌های مورد نیاز تو این ریپازیتوری رو بسازین و بعد با ساخت یک کانتینر داکر، برنامه‌ی Spring Boot رو اجرا کنین.

۱. ورود به پوشه‌ی اصلی برنامه

با دستور cd وارد پوشه‌ای بشین که می‌خواین فایل‌های برنامه داخلش قرار بگیره:

cd  SpringBootOnDocker                       

۲. ایجاد build scriptها

 برای ایجاد کردن کدهای برنامه، یکی از دو ابزار Maven یا Gradle رو انتخاب کنین:

۲.۱. ایجاد build script با Maven:

اول باید build script رو تنظیم کنین.
کدهایی که برای کار با Maven نیاز دارین، تو این لینک وجود داره.
اگه با Maven زیاد آشنا نیستین، به لینک ایجاد پروژه‌های جاوا با Maven مراجعه کنین.

  • ایجاد پوشه‌های داخلی

توی مسیر پروژه، پوشه‌‌های داخلی زیر رو ایجاد کنین، مثلا:

mkdir -p src/main/java/helloDockerWorld
  • ایجاد فایل pom.xml

اگه از Maven بخواین استفاده کنین، در پوشه‌ی SpringBootOnDocker، یک فایل متنی با نام pom.xml ایجاد کنین و محتوای اون رو مطابق زیر پر کنین:

 <groupId>org.springframework</groupId>
    <artifactId>spring-boot-docker</artifactId>
    <version>0.1.0</version>
    <packaging>jar</packaging>
    <name>Spring Boot Docker</name>
    <description>Getting started with Spring Boot and Docker</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath />
    </parent>
    <properties>
        <docker.image.prefix>mydocker</docker.image.prefix>
        <java.version>1.8</java.version>
    </properties>

در کد بالا، artifact مورد نیاز برای ساخت داکر با groupId و version مشخص شده.
علاوه بر این به ایمیج داکری که قراره در مرحله‌ی بعد بسازیم هم prefix رو اضافه کردیم.
این پیکربندی مشخص می‌کنه که در مراحل بعدی ایمیجی ایجاد خواهد شد به نام mydocker/spring-boot-docker.

<plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>unpack</id>
                        <phase>package</phase>
                        <goals>
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>${project.groupId}</groupId>
                                    <artifactId>${project.artifactId}</artifactId>
                                    <version>${project.version}</version>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>

بخش بالا در pom.xml دارای پلاگین‌های مورد نیاز برای ساخت پروژه است:

  • پلاگین Spring Boot Maven، تمام فایل‌های jar موجود در مسیر کلاس رو جمع‌آوری می‌کنه.

و بعد یک فایل “spring-boot-docker-0.1.0.jar” قابل اجرایی رو می‌سازه که اجرا و حمل سرویس‌های شما رو راحت‌تر می‌کنه.

  • دنبال متدی می‌گرده به نام ()public static void main تا اون رو به عنوان کلاس قابل اجرا، flag گذاری ‌کنه.
  • همچنین، یکresolver داخلی داره که شماره‌ی version رو متناسب با وابستگی‌های Spring Boot تنظیم می‌کنه.

۲.۲. ایجاد build script با Gradle:

در این روش هم اول باید build script رو تنظیم کنین.
کدهایی که برای کار با Gradle نیاز دارین، تو این لینک وجود داره.
اگه با Gradle زیاد آشنا نیستین، به لینک ایجاد پروژه‌های جاوا با Gradle مراجعه کنین.

  • ایجاد پوشه‌های داخلی

مشابه روش قبلی در مسیر پروژه، پوشه‌‌های داخلی زیر رو ایجاد کنین، مثلا:

mkdir -p src/main/java/helloDockerWorld

اگه از Gradle بخواین استفاده کنین، در پوشه‌‌ی SpringBootOnDocker، یک فایل متنی با نام build.gradle ایجاد کنین و محتوای اون رو مطابق زیر پر کنین:

buildscript {
    repositories {
      maven {
        url "https://plugins.gradle.org/m2/"
      }
      mavenCentral()
    }
    dependencies {
        classpath('org.springframework.boot:spring-boot-gradle-plugin:2.0.5.RELEASE')
        classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0')
    }
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.palantir.docker'

group = 'mydocker'
bootJar {
    baseName = 'spring-boot-docker'
    version =  '0.1.0'
}
task unpack(type: Copy) {
    dependsOn bootJar
    from(zipTree(tasks.bootJar.outputs.files.singleFile))
    into("build/dependency")
}
docker {
    name "${project.group}/${bootJar.baseName}"
    copySpec.from(tasks.unpack.outputs).into("dependency")
    buildArgs(['DEPENDENCY': "dependency"])
}
repositories {
    mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") 
}

توی بلاک buildscript چیزهایی نوشته می‌شه که خود gradle بهشون نیاز داره.
مثلا برای اضافه کردن plugin ها، repository و dependency مربوط به آن توی این بلاک نوشته می‌شه.
در نهایت هم از apply plugin برای اضافه کردن plugin استفاده می‌شه.

در کد بالا پلاگین Spring Boot gradle رو داریم. این پلاگین در بخش dependencies اضافه شده و کارهای زیر رو انجام می‌ده: 

  • تمام فایل‌های jar موجود در مسیر کلاس رو جمع‌آوری می‌کنه.

و بعد یک فایل “spring-boot-docker-0.1.0.jar”  قابل اجرایی رو می‌سازه که اجرا و حمل سرویس‌های شما رو راحت‌تر می‌کنه.

  • این پلاگین دنبال متدی می‌گرده به نام  ()public static void main تا اون رو به عنوان کلاس قابل اجرا، flag گذاری کنه. 
  • همچنین یک resolver داخلی داره که شماره‌ی version رو متناسب با وابستگی‌های Spring Boot تنظیم می‌کنه. 

علاوه بر این، فایل پیکربندی build.gradle موارد دیگه‌ای رو هم مشخص می‌کنه: 

  • تسک unpack برای خارج کردن فایل jar از حالت فشرده.
  • نام ایمیج (یا tag)، با توجه به property های تعریف شده تو این فایل jar، تعریف می‌شه  که در این‌جا برابر می‌شه با: mydocker/spring-boot-docker    
  • محل فایل jar‌ ای که از حالت فشرده خارج شده.
  • یک آرگومان build برای داکر، که به فایل jar اشاره می‌کنه. 

. . .

حالا شما می‌تونین یک برنامه‌ی ساده‌ Spring Boot رو ایجاد کنین.

۳. ایجاد برنامه‌ی Spring Boot

شما می‌تونین در فایل src/main/java/helloDockerWorld/Application.java کلاسی با نام Application ایجاد کرده و کد زیر رو در اون قرار بدین:

package helloDockerWorld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {

    @RequestMapping("/")
    public String home() {
        return "Hello Docker World";
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

حالا کلاس Application.java رو با هم بررسی کنیم:

  • همون طور که در کد بالا می‌بینین، کلاس Application به شکل SpringBootApplication@ و RestController @، فلگ ‌گذاری شده.
    یعنی حالا Spring MVC می‌تونه برای مدیریت درخواست‌های وب ازش استفاده کنه.
  • در این کلاس، متدی به نام home با استفاده از RequestMapping@ برای دریافت درخواست‌های سرور روی آدرس / ، یعنی آدرس اصلی سایت قرار داده شده است.

در واقع بعد از اجرای این برنامه،  آدرس http://localhost:8080، مقدار Hello Docker World را به مرورگر بر می‌گردونه.

  •  متد ()main برای راه‌اندازی برنامه از متد ()SpringApplication.run استفاده می‌کنه. 

حالا می‌تونیم برنامه رو بدون کانتینر داکر (یعنی در سیستم عامل میزبان) اجرا کنیم.

۴. اجرای برنامه بدون کانتینر داکر (یعنی در سیستم‌عامل میزبان)

-اگر از Gradle استفاده می‌کنین، دستور زیر رو اجرا کنین تا jar ساخته بشه و برنامه رو در پورت 8080 اجرا کنین:

./gradlew build && java -jar build/libs/spring-boot-docker-0.1.0.jar

-اگر از Maven استفاده می‌کنین، دستور زیر رو اجرا کنین تا jar ساخته بشه و برنامه رو در پورت 8080 اجرا کنین:

./mvnw package && java -jar target/spring-boot-docker-0.1.0.jar

حالا برای مشاهده‌ی پیام "Hello Docker World" خود، به آدرس localhost: 8080 برین.

توجه: اگر می‌تونین پیام "Hello Docker World" رو ببینین، برنامه‌ی Spring Boot در Tomcat فعال و در حال اجرا است، اما هنوز کانتینری نشده است.

. . .

۵. کانتینری کردن این پروژه

از dockerfile برای مشخص کردن لایه‌های یک ایمیج داکر استفاده می‌شه و ما می‌خوایم تو این قسمت یک dockerfile رو برای این پروژه‌ی Spring Boot ایجاد کنیم. 

در پوشه‌ی اصلی پروژه، فایلی با نام Dockerfile بسازین و محتوای زیر رو توی اون قرار بدین:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","helloDockerWorld.Application"]
  • در کد بالا از یک ایمیج داکر به عنوان ایمیج پایه استفاده شده. بعد یک volume با نام tmp ایجاد کردیم که به عنوان پوشه‌ی کاری برای سرور tomcat واقع در spring boot در نظر گرفته می‌شه.
  • این dockerfile دارای پارامتری به نام DEPENDENCY است که به دایرکتوری‌ای اشاره می‌کنه که ما در اون دایرکتوری، فایل jar رو unpack کردیم.

این دایرکتوری شامل دو دایرکتوری BOOT-INF/lib (با وابستگی jar ها در آن)  و دایرکتوری BOOT-INF/classes  (با کلاس‌های برنامه در آن) است.

حالا بیاین ایمیج داکر رو بسازیم. قبل از ساخت ایمیج داکر، با اجرای دستور زیر مطمئن بشین که داکر در حال اجراست:

docker ps

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

. . .

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

۶. ساخت ایمیج داکر با Maven و Gradle

با توجه به دستورات زیر، یک ایمیج داکر به اسم mydocker/spring-boot-docker ایجاد می‌شه:

  • ساخت ایمیج با Maven
$ ./mvnw install dockerfile:build
  • ساخت ایمیج با Gradle
./gradlew build docker

. . .

۷. اجرای ایمیج داکر

$ docker run -p 8081:8080 -t mydocker/spring-boot-docker

حالا برنامه در آدرس http: // localhost: 8081 در دسترس است
(به این لینک که مراجعه کنین، پیغام "Hello Docker World" رو مشاهده می‌کنین).
در کد بالا  8081، پورت داکر و 8080، پورت Tomcat است. یعنی برنامه‌ی در حال اجرا روی پورت 8080 از tomcat، در پورت 8081 داکر در دسترس خواهد بود.

وقتی داکر در حال اجراست، شما می‌تونین با دستور زیر لیست کانتینرهای در حال کار رو مشاهده کنین:

$ docker ps
CONTAINER ID        IMAGE                                    
81c723d22865        mydocker/spring-boot-docker:latest

برای متوقف کردن داکر، می‌تونین از docker stop و شناسه‌ی کانتینر استفاده کنین: 

$ docker stop 81c723d22865
81c723d22865

خب تموم شد! شما تونستین کانتینر داکر رو برای اجرای برنامه‌ی Spring Boot ایجاد کنین.

منابع:
creating-a-docker-container-for-spring-boot-app
spring-boot-docker
spring-boot
build-a-java-app-with-gradle
maven-gradle