Spring Boot + PostgreSQL + JS в Doсker

В этой статье покажу, как поднять в Docker-контейнере приложение, состоящее из трех частей:

  • Spring Boot REST API – бекэнд на встроенном Tomcat-сервере
  • База данных PostgreSQL
  • Фронтэнд на JavaScript (JQuery) на Nginx-сервере

Приложение можно скачать тут.

Оно состоит из одного контроллера и одного html и js-файла, позволяющего выводить и добавлять животных. Животные хранятся в базе.

Как выглядит приложение
Как выглядит приложение

Установка Docker

Чтобы развернуть приложение в Docker, для начала нужно установить Docker на компьютер. В Window 7 это проблематично, в Windows 10 и других ОС вполне возможно.

Чтобы убедиться, что Docker установлен, выполните из командной строки:

docker --version

Будет выведена версия Docker.

Теперь можно выполнять различные команды, которые билдят образы, запускают контейнеры и удаляют их все.

PostreSQL в контейнере

Одна из причин, почему Java-разработчику полезен Docker, это то, что можно не устанавливать различное дополнительное ПО. Например, базы данных различных версий. Ведь потом их придется удалять, остаются артефакты. Все это нежелательно, да и долго. С Docker проще.Например, чтобы испытать работу приложения с базой данных PostgreSQL 12 версии, можно не устанавливать ее на компьютер, а поднять в Docker-контейнере.

Для этого из командной строки запустим контейнер с базой:

docker run --name some-postgres --volume db-data:/var/lib/postgresql/data -e POSTGRES_PASSWORD=qqq -e POSTGRES_DB=vv -p 5434:5432 postgres:12-alpine

Теперь с базой можно соединиться по такому url:

jdbc:postgresql://localhost:5434/vv

Что и удается:

Соединение с PostgreSQL, запущенной в контейнере
Вкладка Databases в Intellij Idea Ultimate

Разберем строку выше. Она поднимает контейнер из образа PostgreSQL:

docker run ...параметры ....postgres:12-alpine

Параметры команды

postgres:12-alpine – это имя образа. Готовые образы лежат на Docker Hub, там есть разные версии базы. При запуске контейнера образ сначала загружается оттуда, если его нет на компьютере локально.

–name some-postgres – задает имя контейнера. Все контейнеры, которые есть локально, можно просмотреть с помощью команды docker ps –all.

-e POSTGRES_PASSWORD=qqq -e POSTGRES_DB=vv – переменные среды, задаются параметром -e. Эти параметры задают название базы, которая будет создана по умолчанию (vv) и пароль доступа для пользователя postgres (это имя тоже можно переписать с помощью переменной среды POSTGRES_USER, но мы не будем).

db-data – название volume, то есть того места на нашем диске, где реально будет храниться база из контейнера. Если не задавать volume, то при каждом новом запуске контейнера будет создаваться новый volume, и данные в базе для нас окажутся как бы не сохраненные. А с volume они будут сохраняться. Через двоеточие указан путь внутри контейнера, которому соответствует volume.

-p 5434:5432 – указан порт на локальной машине 5434, которому соответствует порт внутри контейнера 5432. То есть выставляем наружу  порт контейнера 5432, чтобы присоединяться к базе снаружи через порт нашего локального компьютера 5434 (выбран другой порт (не 5432) чтобы не было конфликтов с локально установленной базой, если она есть).

Таким образом, мы соединились с базой со вкладки Databases редактора Intellij Idea. Так же можно соединяться и из приложения, не устанавливая базу на компьютер, а запуская любую ее версию в контейнере.

Файл docker-compose.yml

Обычно приложение состоит из нескольких компонентов, которые зависят друг от друга, но могут быть запущены изолированно на разных машинах. Например, Spring Boot приложение, предоставляющее REST API, PostgreSQL и фронтэнд на сервере Nginx. Наш пример именно такой. Для такой связки удобно применить docker-compose – это и команда, и файл.

Файл docker-compose.yml представлен ниже:

version: '3.8'

services:
  postgres:
    build:
      context: services/postgres
      dockerfile: Dockerfile.development
    ports:
      - "5433:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_DB=vv
      - POSTGRES_PASSWORD=qqq
    volumes:
      - "db-data:/var/lib/postgresql/data"
  
  
  app:
    build:
      context: services/app
      dockerfile: Dockerfile.development
    environment:
      - PORT=8091
      - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres/vv
      - JS_URL=http://localhost
    image: 'my-java-application'
    ports:
      - 8091:8091
    depends_on:
      - postgres

  js:
    build:
      context: services/js
      dockerfile: Dockerfile.development
    image: 'my-js-app'
    ports:
        - 80:80   
        
volumes:
   db-data:

services – ключевое слово, под ним перечислены папки, в которых лежат три части нашего проекта: app, js, postgres.

docker-compose.yml со структурой папок
docker-compose.yml со структурой папок

docker-compose.yml лежит в корне иерархии рядом с папкой services, в которой перечислены сервисы – то есть компоненты, из который состоит наш проект.

Начнем с postgres.

Сервис Postgres: база данных PostgreSQL

Мы уже запускали postgres-контейнер из командной строки. Теперь все параметры вынесены из командной строки в файл docker-compose.yml – переменные среды, volume, порты. Копирую фрагмент этого файла:

postgres:
  build:
    context: services/postgres
    dockerfile: Dockerfile.development
  ports:
    - "5433:5432"
  environment:
    - POSTGRES_USER=postgres
    - POSTGRES_DB=vv
    - POSTGRES_PASSWORD=qqq
  volumes:
    - "db-data:/var/lib/postgresql/data"

Кстати, volume с именем db-data создается отдельной строкой в конце файла:

volumes: db-data:

Обратите внимание, что каждого сервиса указан Dockerfile (файл для сборки образа):

dockerfile: Dockerfile.development

для Postgres он лежит в папке services/postgres (см. скриншот выше). Его содержимое предельно кратко:

FROM postgres:12-alpine

Строка выше означает, что мы просто берем готовый образ postgres:12-alpine и ничего к нему не добавляем.

Кстати, порты, указанные в docker-compose.yml:

ports:
  - "5433:5432"

можно и не выставлять, ведь к базе будем подключаться не мы с localhost, а Spring Boot из соседнего контейнера. А запись выше именно для доступа с локальной машины.

Другие образы будут чуть сложнее.

Сервис App: Spring Boot приложение

Скопирую фрагмент из docker-compose.yml, касающийся приложения Spring Boot:

app:
  build:
    context: services/app
    dockerfile: Dockerfile.development
  command: java -jar ./app.jar
  environment:
    - PORT=8091
    - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres/vv
    - JS_URL=http://localhost
  image: 'my-java-app'
  ports:
    - 8091:8091
  depends_on:
    - postgres

Рассмотрим, из чего он состоит.

Порты
ports:
  - 8091:8091

Порты указаны так же, как в сервисе postgres, только теперь на указанном порту запущен не сервер базы данных, а http-сервер. Порту 8091 контейнера соответствует порт 8091 нашего компьютера.

Зависимость
depends_on: 
    - postgres

Тут сказано, что наш сервис зависит от сервиса postgres – это означает, что сначала запускается сервис postgres, а потом сервис app.

Имя образа

image: ‘my-java-app’ дает название образу.

Переменные среды

В environment перечислены переменные среды, к которым наше Spring Boot приложение имеет доступ. Мы их прописываем в application.yml приложения таким образом (через двоеточие стоит значение по умолчанию, которое используется в случае, если переменной окружения нет; у меня это просто значения для локального запуска без контейнера):

server:
  port: ${PORT:8091}
spring:
  datasource:
    url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost/mydb}
Доступ из одного контейнера в другой

Обратите внимание, что доступ из одного контейнера к другому происходит по имени сервиса. То есть к базе данных мы обращаемся не по localhost, а по postgres:

jdbc:postgresql://postgres/vv

Это значение мы указывали выше в docker-compose.yml в переменной среды SPRING_DATASOURCE_URL.

Dockerfile.development

Сервис app использует такой докер-файл:

FROM bellsoft/liberica-openjdk-alpine-musl:11.0.3
WORKDIR /usr/local/app
ADD docker-demo-0.0.1-SNAPSHOT.jar app.jar
CMD java -jar ./app.jar

Тут мы собираем образ на основе готового образа openjdk, задаем рабочую папку и копируем в нее наше приложение docker-demo-0.0.1-SNAPSHOT.jar (оно лежит рядом docker-файлом см. структуру папок выше – надо его сюда скопировать) под именем app.jar. А затем запускаем приложение с помощью команды:

java -jar ./app.jar

Наконец, рассмотрим последний компонент.

Сервис JS: JavaScript приложение

Тут все аналогично, только заданы другие порты и другое имя образа:

js:
  build:
    context: services/js
    dockerfile: Dockerfile.development
  image: 'my-js-app'
  ports:
      - 80:80
Dockerfile.development

Докер-файл у сервиса js такой:

FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY dist/ .

То есть мы собираем образ  на основе готового образа nginx:alpine. Затем копируем в папку сервера Nginx /usr/share/nginx/html файлы из папки dist (лежит рядом с докер-файлом, содержит html и js-файлы).

Как всё запустить: команда docker-compose

Наконец, из папки с файлом docker-compose.yml надо выполнить команду:

docker-compose up

Команда выше и построит образы, и запустит на их основе контейнеры.

Теперь в браузере по адресу

http://localhost

будет доступно наше приложение.

Чтобы остановить и удалить контейнеры, выполним команду:

docker-compose down

Если нужно удалить и volume с данными, делаем так:

docker-compose down --volume

Если вы сохраняли каких-то животных в приложении, то после этой команды при следующем запуске приложения их не будет.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *