.. | ||
exercise-app | ||
training-app | ||
database.sql | ||
docker-compose.yaml | ||
kind-windows-amd64.exe | ||
nginx.conf | ||
README.md |
Лабораторная работа 3.
Задание
Цель: изучение современных технологий контейнеризации.
Задачи:
- Установить средство контейнеризации docker.
- Изучить применение и принципы docker.
- Изучить утилиту docker-compose и структуру файла docker-compose.yml.
- Развернуть не менее 3х различных сервисов при помощи docker-compose.
- Оформить отчёт в формате Markdown и создать Pull Request в git-репозитории.
Как запустить лабораторную работу
В директории с файлом характеристик docker-compose.yaml выполнить команду:
docker-compose -f docker-compose.yaml up
Описание лабораторной работы
Создание базы данных
Каждый сервис реализует CRUD-операции, поэтому были выбраны следующие сущности, соответствующие теме диплома: упражнение и тренировка. Эти сущности связаны отношением один ко многим. Созданные таблицы базы данных:
-- Создание таблицы тренировок
CREATE TABLE t_training (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description VARCHAR(255) NOT NULL
);
-- Создание таблицы упражнений
CREATE TABLE t_exercise (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description VARCHAR(255) NOT NULL,
id_training INTEGER,
FOREIGN KEY (id_training) REFERENCES t_training(id)
);
Также в файле docker-compose.yaml
создадим соответствующий сервис:
#database
postgresql:
#configuration
image: postgres:latest
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: admin
POSTGRES_USER: admin
POSTGRES_DB: traininarium
volumes:
- ./database.sql:/docker-entrypoint-initdb.d/database.sql
restart: always
networks:
- mynetwork
image: postgres:latest
указывает, что мы хотим использовать последнюю версию образаPostgreSQL
.ports: - 5432:5432
пробрасывает порт 5432 контейнера (стандартный порт PostgreSQL) на порт 5432 хоста, чтобы можно было подключаться к базе данных с внешнего устройства.environment
определяет переменные окружения, которые будут использоваться контейнером. В данном случае, устанавливаются значения для переменныхPOSTGRES_PASSWORD
,POSTGRES_USER
иPOSTGRES_DB
.volumes
указывает путь к файлуdatabase.sql
в текущей директории, который будет использоваться для инициализации базы данных при запуске контейнера.restart: always
гарантирует, что контейнер будет перезапущен автоматически, если он остановится или перезагрузится.networks
определяет сеть, к которой будет присоединен контейнер. В данном случае, контейнер будет присоединен к сети с именемmynetwork
.
В итоге будет создан контейнер с базой данных PostgreSQL
, настроенной с указанными переменными окружения, проброшенным портом и файлом database.sql
для инициализации базы данных.
Создание микросервиса Упражнения
После реализации необходимых элементов микросервиса, таких как: controller, model, modelDTO, repository и service, создадим конфигурационный файл Dockerfile, в котором пропишем следующее:
FROM openjdk:17-jdk
WORKDIR /app
COPY ./exercise-app/build/libs/exercise-app-0.0.1-SNAPSHOT.jar /app/exercise-app-0.0.1-SNAPSHOT.jar
EXPOSE 8081
CMD ["java", "-jar", "exercise-app-0.0.1-SNAPSHOT.jar"]
В данном файле мы указываем базовый образ, который будет использован для создания контейнера (OpenJDK версии 17 с установленным JDK); устанавливаем рабочую директорию внутри контейнера, где будут размещены файлы приложения; указываем, что приложение внутри контейнера будет слушать на порту 8081 и определяем команду, которая будет выполнена при запуске контейнера, а именно запуск приложения Java из файла exercise-app-0.0.1-SNAPSHOT.jar
внутри контейнера.
Также в файле docker-compose.yaml
создадим соответствующий сервис:
exercise-service:
build:
context: .
dockerfile: ./exercise-app/Dockerfile
ports:
- 8081:8081
environment:
DATASOURCE_URL: jdbc:postgresql://postgresql:5432/traininarium
DATASOURCE_USERNAME: admin
DATASOURCE_PASSWORD: admin
restart: always
#wait build database
depends_on:
- postgresql
networks:
- mynetwork
exercise-service
является именем сервиса, который будет создан и запущен в контейнере.context: .
указывает, что контекстом для сборки Docker-образа является текущая директория.dockerfile: ./exercise-app/Dockerfile
указывает путь кDockerfile
, который будет использоваться для сборки образа.ports
определяет проброс портов между хостом и контейнером.8081:8081
указывает, что порт 8081 на хосте будет проброшен на порт 8081 внутри контейнера.environment
определяет переменные окружения, которые будут доступны внутри контейнера.DATASOURCE_URL: jdbc:postgresql://postgresql:5432/traininarium
определяет URL для подключения к базе данныхPostgreSQL
.DATASOURCE_USERNAME: admin
определяет имя пользователя для подключения к базе данных.DATASOURCE_PASSWORD: admin
определяет пароль для подключения к базе данных.restart: always
указывает, что контейнер будет автоматически перезапущен в случае его остановки. depends_on определяет зависимость данного сервиса от другого сервиса.postgresql
указывает, что данный сервис зависит от сервиса с именемpostgresql
.mynetwork
указывает, что контейнер будет присоединен к сети с именемmynetwork
.
Создание микросервиса Тренировки
Аналогично реализуем controller, model, modelDTO, repository и service для микросервиса Тренировки, но дополняя кодом отправки запроса к сервису Упражнений. Пример запроса к сервису Упражнений:
public List<TrainingDto> findAllTraining() throws Exception {
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> entity = new HttpEntity<>(new HttpHeaders());
List<TrainingDto> trainingDtos = new ArrayList<>();
for (Training training : trainingRepository.findAll()) {
TrainingDto trainingDto = new TrainingDto();
trainingDto.setId(training.getId());
trainingDto.setName(training.getName());
trainingDto.setDescription(training.getDescription());
ResponseEntity<String> response = restTemplate.exchange(
"http://" + exercise_service_host + "/exercise/training/" + trainingDto.getId(), HttpMethod.GET, entity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
List<ExerciseDto> exerciseDtos;
try {
exerciseDtos = objectMapper.readValue(responseBody, ArrayList.class);
trainingDto.setExercises(exerciseDtos);
} catch (JsonProcessingException e) {
throw new Exception("Не удалось десериализовать тело запроса в объект");
}
} else {
throw new Exception("Ошибка получения объекта");
}
trainingDtos.add(trainingDto);
}
return trainingDtos;
}
Теперь создадим конфигурационный файл Dockerfile, в котором пропишем следующее:
FROM openjdk:17-jdk
WORKDIR /app
COPY ./training-app/build/libs/training-app-0.0.1-SNAPSHOT.jar /app/training-app-0.0.1-SNAPSHOT.jar
EXPOSE 8082
CMD ["java", "-jar", "training-app-0.0.1-SNAPSHOT.jar"]
В данном файле мы указываем базовый образ, который будет использован для создания контейнера (OpenJDK версии 17 с установленным JDK); устанавливаем рабочую директорию внутри контейнера, где будут размещены файлы приложения; указываем, что приложение внутри контейнера будет слушать на порту 8082; копируем файл training-app-0.0.1-SNAPSHOT.jar
из локальной директории ./training-app/build/libs/
внутрь контейнера в папку /app
и определяем команду, которая будет выполнена при запуске контейнера, а именно запуск приложения Java из файла training-app-0.0.1-SNAPSHOT.jar
внутри контейнера.
Также в файле docker-compose.yaml
создадим соответствующий сервис:
training-service:
build:
context: .
dockerfile: ./training-app/Dockerfile
ports:
- 8082:8082
environment:
EXERCISE_SERVICE_HOST: exercise-service:8081
DATASOURCE_URL: jdbc:postgresql://postgresql:5432/traininarium
DATASOURCE_USERNAME: admin
DATASOURCE_PASSWORD: admin
restart: always
#wait build database
depends_on:
- postgresql
networks:
- mynetwork
training-service
является именем сервиса, который будет создан и запущен в контейнере.context: .
указывает, что контекстом для сборки Docker-образа является текущая директория.dockerfile: ./training-app/Dockerfile
указывает путь кDockerfile
, который будет использоваться для сборки образа.8082:8082
указывает, что порт 8082 на хосте будет проброшен на порт 8082 внутри контейнера.environment
определяет переменные окружения, которые будут доступны внутри контейнера.EXERCISE_SERVICE_HOST: exercise-service:8081
определяет хост и порт для подключения к сервисуexercise-service
.DATASOURCE_URL: jdbc:postgresql://postgresql:5432/traininarium
определяет URL для подключения к базе данныхPostgreSQL
.DATASOURCE_USERNAME: admin
определяет имя пользователя для подключения к базе данных.DATASOURCE_PASSWORD: admin
определяет пароль для подключения к базе данных.restart: always
указывает, что контейнер будет автоматически перезапущен в случае его остановки.depends_on
определяет зависимость данного сервиса от другого сервиса, данный сервис зависит от сервиса с именемpostgresql
.mynetwork
указывает, что контейнер будет присоединен к сети с именемmynetwork
.
Реализация gateway при помощи nginx
Для того, чтобы использовать Nginx для обработки HTTP-запросов и маршрутизации их к соответствующим сервисам, создадим файл конфигурации Nginx:
events {
worker_connections 1024;
}
http {
upstream exercise-service {
server exercise-service:8081;
}
upstream training-service {
server training-service:8082;
}
server {
listen 80;
listen [::]:80;
server_name localhost;
location /exercise-service/ {
proxy_pass http://exercise-service/;
}
location /training-service/ {
proxy_pass http://training-service/;
}
}
}
events
определяет настройки событий для сервера Nginx.worker_connections 1024
указывает максимальное количество одновременных соединений, которые могут быть обработаны сервером.
http
определяет настройки HTTP-сервера Nginx.upstream
определяет группу серверов, которые могут обрабатывать запросы.exercise-service
определяет группу серверов с именемexercise-service
, в которой находится только один серверexercise-service:8081
.training-service
определяет группу серверов с именемtraining-service
, в которой находится только один серверtraining-service:8082
.server
определяет настройки для конкретного виртуального сервера.listen 80
указывает на порт, на котором сервер будет слушать входящие HTTP-запросы.listen [::]:80
указывает на IPv6-адрес и порт, на котором сервер будет слушать входящие HTTP-запросы.server_name localhost
указывает имя сервера.location /exercise-service/
определяет местоположение для обработки запросов, которые начинаются с/exercise-service/
.proxy_pass http://exercise-service/
указывает, что все запросы, начинающиеся с/exercise-service/
, должны быть перенаправлены на группу серверовexercise-service
.location /training-service/
определяет местоположение для обработки запросов, которые начинаются с/training-service/
.proxy_pass http://training-service/
указывает, что все запросы, начинающиеся с/training-service/
, должны быть перенаправлены на группу серверовtraining-service
.
Таким образом, при запуске сервера Nginx с использованием этого конфигурационного файла, сервер будет слушать входящие HTTP-запросы на порту 80 и маршрутизировать запросы, начинающиеся с /exercise-service/
, на группу серверов exercise-service
, а запросы, начинающиеся с /training-service/
, на группу серверов training-service
.
Далее в файле docker-compose.yaml
создадим соответствующий сервис:
nginx:
#configuration
image: nginx:latest
ports:
- 80:80
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
restart: always
depends_on:
- training-service
- exercise-service
networks:
- mynetwork
-
nginx
- это название сервиса, который будет запущен в контейнере. -
image: nginx:latest
указывает на использование последней версии образа Nginx из Docker Hub. -
80:80
пробрасывает порт 80 контейнера на порт 80 хостовой машины. -
volumes
определяет привязку тома между контейнером и хостовой машиной../nginx.conf:/etc/nginx/nginx.conf
привязывает файлnginx.conf
из текущего рабочего каталога хостовой машины к файлуnginx.conf
внутри контейнера Nginx. Это позволяет настроить Nginx с помощью внешнего файла конфигурации.
-
restart: always
указывает, что контейнер должен быть автоматически перезапущен при его остановке или падении. -
depends_on
указывает на зависимость этого сервиса от других сервисов.training-service
указывает, что контейнер Nginx должен быть запущен после контейнераtraining-service
.exercise-service
указывает, что контейнер Nginx должен быть запущен после контейнераexercise-service
.
-
networks
определяет сети, к которым будет присоединен контейнер.mynetwork
добавляет контейнер в сеть с именемmynetwork
.
Видео