برنامه نویسی

نحوه Dockerize کردن یک برنامه Spring Boot با PostgreSQL

در این مقاله، نحوه استفاده از Docker با SpringBoot و PostgreSQL را به شما نشان خواهم داد، برای پیگیری باید درک اولیه ای از آنچه که وجود دارد داشته باشید. Docker، Maven نصب شده، تجربه با Spring Boot و یک IDE به انتخاب شما.

اطلاع : اگر قبلاً یک برنامه Spring Boot دارید، می توانید بخش های 1 و 2 را رد کرده و مستقیماً به بخش 3 بروید و آن را دنبال کنید.

در اینجا می توانید کد و سایر منابع مورد استفاده در این نمایش را بیابید (github.com)

1 – چه خواهیم ساخت؟

برای اهداف آزمایشی، ما یک برنامه مدیریت دستور العمل ساده با دو نهاد ایجاد خواهیم کرد: Chef و Recipe.

2 – یک اپلیکیشن فنری بوت ایجاد کنید

برای این کار، در صورت استفاده می‌توانید از افزونه Spring initializr یا Spring initializr استفاده کنید IntelliJ idea. و وابستگی های زیر را انتخاب کنید: Spring Web، PostgreSQL Driver و البته Spring Data JPA و حتما انتخاب کنید maven به عنوان مدیر پروژه

وب سایت بهار اولیه

پس از نصب پروژه، آن را استخراج کرده و با IDE مورد علاقه خود باز کنید.

همانطور که در بخش اول ذکر شد دو موجودیت ایجاد خواهیم کرد Chef و Recipe. به شرح زیر است:


@Entity
public class Chef {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "chef")
    private List<Recipe> recipes;

    public Chef() {
    }

    public Chef(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Chef(String name) {
        this.name = name;
    }

    // ADD GETTERS AND SETTER...
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

@Entity
public class Recipe {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(length = 100)
    private String name;
    private String description;

    @ManyToOne
    @JsonBackReference
    private Chef chef;

    public Recipe() {
    }

    public Recipe(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public Recipe(Long id, String name, String description) {
        this.id = id;
        this.name = name;
        this.description = description;
    }

    // GETTERS AND SETTERS...
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما همچنین نیاز به ایجاد ChefController، ChefService، ChefRepository و RecipeRepository.

سرویس آشپز: سه روش اساسی دارد: createChefWithRecipes که به عنوان پارامتر یک آشپز و لیستی از دستور العمل ها را در نظر می گیرد، getChefs برای دریافت لیست سرآشپزها و در نهایت getNumberOfChefs تعداد سرآشپزها را در پایگاه داده برمی گرداند.

@Service
public class ChefService {
    private ChefRepository chefRepository;
    private RecipeRepository recipeRepository;

    public ChefService(ChefRepository chefRepository, RecipeRepository recipeRepository) {
        this.chefRepository = chefRepository;
        this.recipeRepository = recipeRepository;
    }

    @Transactional
    public Chef createChefWithRecipes(Chef chef,List<Recipe> recipes){

        recipes.stream().forEach(recipe -> recipe.setChef(chef));

        this.chefRepository.save(chef);
        this.recipeRepository.saveAll(recipes);

        return chef;
    }

    public List<Chef> getChefs(){
        System.out.println(this.chefRepository.findAll());
        return this.chefRepository.findAll();
    }

    public Long getNumberOfChefs(){
        return this.chefRepository.count();
    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

@RestController
@RequestMapping("/api/chefs")
public class ChefController {

    private final ChefService chefService;

    public ChefController(ChefService chefService) {
        this.chefService = chefService;
    }

    @GetMapping
    public List<Chef> getAllChefs(){
        return chefService.getChefs();
    }

}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و در نهایت، چند ردیف اولیه به پایگاه داده خود اضافه می کنیم (اختیاری)، برای این کار یک کلاس اولیه داده ایجاد می کنیم و متد run را از رابط فرمان اجرا می کنیم.

@Component
public class DataInitializer implements CommandLineRunner {

    private final ChefService chefService;

    public DataInitializer(ChefService chefService) {
        this.chefService = chefService;
    }

    @Override
    public void run(String... args) throws Exception {

        if(chefService.getNumberOfChefs() > 1) {
            System.out.println("Chefs already initialized!");
            return;
        }

        Chef chef1 = new Chef("Gordon Ramsay");
        Chef chef2 = new Chef("Jamie Oliver");
        Chef chef3 = new Chef("Anthony Bourdain");

        List<Recipe> chef1Recipes = Arrays.asList(
                new Recipe("Beef Wellington", "A classic British..."),
                new Recipe("Scrambled Eggs", "A simple breakfast..."),
                new Recipe("Beef Burger", "A juicy burger made...")
        );

        List<Recipe> chef2Recipes = Arrays.asList(
                new Recipe("Spaghetti Carbonara", "A creamy pasta...")
                new Recipe("Roast Chicken", "A classic roastchicken"),
                new Recipe("Fish and Chips", "A traditional...")
        );

        chefService.createChefWithRecipes(chef1,chef1Recipes);
        chefService.createChefWithRecipes(chef2,chef2Recipes);
        chefService.createChefWithRecipes(chef3,new ArrayList<>());

    }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

3 – برنامه را در یک فایل JAR بسته بندی کنید

به سادگی دستور را اجرا کنید mvn package و بررسی کنید که یک فایل jar در زیر پوشه ایجاد شده باشد target.

mvn package -DskipTests

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توجه داشته باشید که استفاده کرده ایم -DskipTests گزینه ای برای رد شدن از آزمایش ها زیرا برنامه ما سعی می کند به پایگاه داده ای متصل شود که هنوز وجود ندارد.

فایل هدف پس از بسته بندی برنامه بوت فنری

فایل JAR تولید شده برنامه Spring Boot ما یک بایگانی اجرایی است که شامل تمام اجزا و وابستگی های لازم برای اجرای برنامه مانند کد کامپایل شده، وابستگی ها، سرور جاسازی شده و برخی منابع اضافی است.

ما به سادگی می توانیم برنامه خود را با استفاده از دستور اجرا کنیم java -jar target/recipe-management-0.0.1-SNAPSHOT.jar، این دقیقاً دستوری است که برای اجرای برنامه در داخل یک کانتینر به آن نیاز داریم.

4 – ایجاد تصویر برنامه (Dockerfile)

قسمت سرگرم کننده اینجاست!! اما ابتدا، فایل docker چیست؟

آ Dockerfile یک سند متنی است که شامل مجموعه‌ای از دستورالعمل‌ها برای ساختن یک تصویر است، این دستورالعمل‌ها می‌تواند استخراج و کپی کردن فایل‌ها یا اجرای دستورات باشد…

خوب، بیایید یکی را در دایرکتوری ریشه برنامه خود ایجاد کنیم، باید نامگذاری شود Dockerfile با “D” بزرگ (در واقع شما می توانید نام آن را هر چه می خواهید بگذارید، اما برای جلوگیری از سردرد در مراحل بعدی، اجازه دهید به این قرارداد احترام بگذاریم)

FROM openjdk:17
VOLUME /tmp
EXPOSE 8080
COPY target/recipe-management-0.0.1-SNAPSHOT.jar recipe.jar
ENTRYPOINT ["java","-jar","/recipe.jar"]

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • از openjdk:17 : هر تصویر داکر به یک تصویر پایه نیاز دارد که شامل سیستم عامل اصلی و سایر اجزای زمان اجرا باشد، و از آنجایی که ما یک برنامه Spring Boot داریم، OpenJDK:17 Java Runtime Environment (JRE).

  • VOLUME /tmp : (اختیاری) این دستورالعمل مشخص می کند که /tmp دایرکتوری در کانتینر docker به عنوان حجمی برای ذخیره فایل‌های موقت و داده‌های کش استفاده می‌شود… برای به اشتراک گذاشتن آن بین کانتینر داکر و سیستم فایل میزبان یا بین کانتینرها.

  • EXPOSE 8080 : این دستورالعمل به Docker اطلاع می دهد که کانتینر به پورت گوش می دهد 8080 در زمان اجرا

  • COPY target/recipe-management-0.0.1-SNAPSHOT.jar recipe.jar : این دستورالعمل کپی می کند JAR فایل به دایرکتوری کاری تصویر که به طور پیش فرض است /، کپی شده است JAR نام فایل به: recipe.jar. (ما می توانیم دایرکتوری کاری را با استفاده از آن تغییر دهیم WORKDIR دستورالعمل).

  • نقطه ورود [“java”,”-jar”,”/recipe.jar”]: برای قسمت اول ENTRYPOINT برای پیکربندی دستوری استفاده می‌شود که هنگام راه‌اندازی یک کانتینر اجرا می‌شود، و همانطور که ممکن است حدس بزنید قسمت دوم دستور واقعی را مشخص می‌کند که برنامه ما را اجرا می‌کند.

خوب، ما آماده ایم چند آزمایش انجام دهیم!! بیایید ابتدا با اجرای دستور یک تصویر بسازیم:

docker build . -t recipe:v1

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دستور بالا به داکر می گوید که تصویر را از فایل بسازد Dockerfile در دایرکتوری فعلی ما می توانیم به صورت اختیاری استفاده کنیم -t گزینه ای برای تعیین یک نام و یک برچسب برای تصویر ما.

اجرای دستور ساخت docker

اکنون تصویر خود را با نام و تگ با موفقیت ساخته ایم recipe:v1.

با اجرای دستور می توانیم به سادگی بررسی کنیم که تصویر ما وجود دارد: docker images:

لیست تصاویر داکر

5- ایجاد ظروف

در این بخش، با ایجاد یک کانتینر از برنامه خود و پیوند آن با a، همه چیز را کنار هم قرار می دهیم PostgreSQL ظرف پایگاه داده از آنجایی که برنامه ما به یک پایگاه داده نیاز دارد.

ظروف بوت فنری و postgresql

ما می‌توانیم به روش‌های مختلفی به این هدف برسیم، اما رایج‌ترین و واضح‌ترین آن استفاده از آن است Docker Compose.

Docker Compose به ما این امکان را می دهد که همه کانتینرهای برنامه خود را تعریف و پیکربندی کنیم، آنها را به هم پیوند دهیم و وابستگی های بین آنها را در یک فایل واحد مشخص کنیم: docker-compose.yml.

version: "3.8"

services:
  psql-db:
    image: postgres
    container_name: psql-db
    restart: always
    environment:
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=1234
      - POSTGRES_DB=recipe
    ports:
      - '5432:5432'

  recipe:
    container_name: recipe_app
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - '8081:8080'
    environment:
      - SPRING_DATASOURCE_URL=jdbc:postgresql://psql-db:5432/recipe
      - SPRING_DATASOURCE_USERNAME=admin
      - SPRING_DATASOURCE_PASSWORD=1234
      - SPRING_JPA_HIBERNATE_DDL_AUTO=update
    depends_on:
      - psql-db

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. ابتدا، ما را تعریف می کنیم yml نسخه

  2. اولین ظرف را ایجاد کنید psql-db با استفاده از تصویر رسمی Postgres image: postgres.

  3. ما به ظرف یک نام می دهیم (اختیاری) container_name: psql-db.

  4. را restart: always پارامتر تضمین می کند که در صورت خرابی کانتینر به طور خودکار راه اندازی مجدد می شود.

  5. را environment گزینه می تواند از یک ظرف به ظرف دیگر برای پایگاه داده Postgres ما متفاوت باشد، ما باید یک کاربر پایگاه داده، رمز عبور و نام داشته باشیم…

  6. را ports گزینه پورت داخلی کانتینر را نقشه می‌کشد (در سمت چپ 5432:) به پورت میزبان 5432. این اجازه می دهد تا سایر سرویس های در حال اجرا بر روی همان هاست به پایگاه داده Postgres متصل شوند psql-db کانتینری که از آدرس IP و پورت میزبان استفاده می کند 5432.

  7. برای recipe ظرفی که تغییرات کوچکی ایجاد کرده ایم، استفاده کردیم build به داکر بگوییم که یک تصویر جدید از ما بسازد Dockerfile.

  8. همچنین در بخش محیط، URL پایگاه داده، USERNAME و PASSWORD را مشخص کردیم. توجه داشته باشید که URL پایگاه داده حاوی نام است Postgres کانتینر و بندر آن: jdbc:postgresql://psql-db:5432/recipe.

  9. سرانجام، depends_on گزینه می گوید recipe ظرفی که نباید تا زمانی که شروع شود psql-db ظرف در حال اجراست

اکنون ما آماده ایم تا ظروف خود را با استفاده از آن ایجاد کنیم docker-compose.

(اگر از یک دستگاه لینوکس استفاده می کنید، باید این کار را انجام دهید Docker Compose را نصب کنید)

اگر شما docker-compose.yml فایل را در ریشه پروژه خود اجرا کنید:

docker-compose up 
# you can also use -d tag to start containers in the background.

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اجرای دستور docker-compose

از چند خط اول، واضح به نظر می رسد که داکر با موفقیت تصویر دستور غذا را بر اساس دستورالعمل های ارائه شده ساخته است Dockerfile، ما را نیز ایجاد کرد psql-db و recipe_app ظروف و متصل است psql-db به recipe_app ظرف

بیایید یک http درخواست کنید تا مطمئن شوید که همه چیز همانطور که انتظار می رود کار می کند!

نقطه پایانی http://localhost:8081/api/chefs است و توجه کنید که ما از پورت استفاده کردیم 8081.

درخواست http با استفاده از پستچی

وویلا! برنامه کانتینری شده است! اکنون می توانیم آن را با هر کسی که داکر در دستگاه خود دارد به اشتراک بگذاریم.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

همچنین ببینید
بستن
دکمه بازگشت به بالا