24 KiB
Задания
1. Многоступенчатая сборка образов
В полученной вами директории todo_app
находятся исходные коды Enterprise Quality™® системы для менеджмента напоминаний.
Она состоит из следующих частей:
TodoApi
- API-сервис, на платформе.NET 7
,todo_ui
- веб-интерфейс к этому сервису, Single-PageJavaScript
приложение(SPA
), написанное с использованиемVue.js 3
,PostgreSQL
- API-сервис используетPostgreSQL
для хранения данных.
Необходимо будет контейнеризировать эту систему.
В этот раз, к сожалению, программисты не оставили никаких скриптов, но вы можете написать их сами, по ходу работы. Некоторые команды и их аргументы будут довольно длинные.
1.1 Контейнеризация приложения
1.1.1 Подготовка образа для TodoApi
и todo_ui
Используя подход к многоступенчатой сборке образов, напишите Dockerfile
, из которого можно было бы создать образ,
и который отвечал бы следующим требованиям:
- Фаза сборки API-сервиса должна использовать образ
mcr.microsoft.com/dotnet/sdk:7.0
- Фаза сборки
JavaScript
приложения должна использовать образnode:21
- Результирующий образ должен основываться на
mcr.microsoft.com/dotnet/aspnet:7.0
- Готовое приложение API-сервиса вместе с зависимостями должно находиться в директории
/app
JavaScript
приложение должно находиться в директории/app/wwwroot
- При старте контейнера из такого образа, должно запускаться приложение API-сервиса.
Документация по многоступенчатой сборке образов находится тут: https://docs.docker.com/build/building/multi-stage/.
Фаза сборки сервиса TodoApi
состоит из следующих шагов:
- Во-первых, для его сборки необходим .NET 7 SDK
- Далее, из директории, в которой находятся исходные коды сервиса(см.
todo_app/TodoApi
), нужно сделатьdotnet restore TodoApi.csproj
- эта команда скачает зависимости сервиса. - Следующим шагом идет непосредственно сборка сервиса.
Она производится командойdotnet build TodoApi.csproj -c Release -o <output_directory>
,
в которой вместо<output_directory>
необходимо подставить имя директории, которое будет содержать бинарные файлы сервиса, например/app/build
- Последняя вещь, которую необходимо сделать - это вызывать команду
dotnet publish TodoApi.csproj -c Release -o <publish_directory>
,
которая опубликует результирующие бинарные файлы, а также зависимости сервиса в<publish_directory>
(например в/app/publish
).
Для запуска процесса сервиса SDK не нужен, достаточно лишь соответствующего фреймворка ASP.NET
- в данном случае версии 7.0
. Запуск осуществляется вызовом исполняемого файла ./TodoApi
из директории, которая хранит опубликованные бинарные файлы сервиса и его зависимостей.
Фаза сборки JavaScript
приложения состоит из следующих шагов:
- Прежде всего, необходим
NodeJS
- в данном случае подойдет версия21
. - Далее, из директории, в которой находятся исходные коды приложения(см.
todo_app/todo_ui
), нужно вызыватьnpm install
- это команда скачает зависимости приложения. - После этого, в этой же директории необходимо выполнить команду
npm run build
, в результате чего, в этой директории появится директорияwwwroot
, содержащая файлindex.html
, результирующий код на JS, а также его ресурсы, такие как картинки и файлы стилей(css).
Перед выполнением этой команды, также можно установить переменную средыBASE_PATH
, в значение,
являющееся префиксом в пути URL, по которому приложение будет находится. По умолчанию,BASE_PATH
трактуется как имеющее значение/
, таким образом приложение должно раздаваться веб-сервером из корня сайта.
Если же вы хотите, чтобы приложение находилось например по адресуhttp://<домен>/todo/
, тогда необходимо
установитьBASE_PATH
в значение/todo/
соответственно. Для установки переменной среды, доступной во время
сборки образа, используйте директивуARG
внутри докер-файла(и соответственно опцию--build-arg
при вызовеdocker build
).
Для запуска JavaScript
приложения NodeJS
не нужен, достаточно лишь любого веб-сервера, и в
нашем случае, в API-сервисе такой имеется(он называется Kestrel
). Для хостинга JS-приложения в API-сервисе, необходимо всего лишь скопировать упомянутую выше директорию wwwroot
внутрь результирующей директории API-сервиса(которая получается после выполнения dotnet publish ...
).
Создайте образ из полученного Dockerfile
и назовите его todo-bundle
.
1.1.2 Подготовка образа PostgreSQL
Перед запуском самого приложения, необходимо запустить и подготовить PostgreSQL
в сети todo
.
Создайте именованную сеть типа bridge
, в которой будет работать вся система, назовите ее todo
.
Создайте том данных, в котором PostgreSQL
будет хранить БД. Назовите его todo_pgdata
.
Запустите именованный контейнер todo_postgres
из образа postgres:16
.
При этом:
- При запуске установите следующие переменные окружения контейнера в значение
todo
:POSTGRES_DB
- имя базы данных, к которой будет обращаться API-сервисPOSTGRES_USER
- пользователь БД, через которого будет работать сервисPOSTGRES_PASSWORD
- пароль для пользователя
- Установите значение переменной среды
PGDATA
в значение/var/lib/postgresql/data
- здесьPostgreSQL
будет хранить данные. - Примонтируйте том данных
todo_pgdata
в качестве директории/var/lib/postgresql/data
. - Примонтируйте директорию
initdb
изtodo_app
в качестве директории/docker-entrypoint-initdb.d
внутри контейнера. Там находится скриптinit_db.sql
, который используется для инициализации базы данных при первом старте контейнера.
1.1.3 Проверка работоспособности контейнеризованного приложения
Запустите контейнер todo_bundle
в сети todo
из созданного вами образа todo-bundle
, при этом:
- Установите значение переменной среды
ConnectionStrings__PostgreSQL
в контейнере равнойHost=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo
. Можете выбрать другойHost
в этой строке, если вы назвали контейнер с PostgreSQL по-другому. Аналогично - и другие параметры. - Пробросьте порт 80 на хост-систему(например на порт 8080).
API-сервис работает на 80м порту по умолчанию, но это можно изменить, установив значение переменной
ASPNETCORE_URLS
например вhttp://*:5000
- тогда внутри контейнера сервис будет слушать порт 5000.
Убедитесь, что приложение работает и доступно на выбранном вами порту на локальной машине.
При возникновении сложностей и необходимости отладки, вы можете также установить значение переменной ASPNETCORE_ENVIRONMENT
в Development
- таким образом сервис будет выдавать больше логов.
1.2 Рефакторинг контейнеризованного приложения
1.2.1 Декомпозиция образа todo-bundle
Несмотря на то что API-сервис поддерживает хостинг JavaScript
приложения, в реальном(или скорее, идеальном) мире никто так не делает. Над интерфейсом и API часто работают разные команды, у них может быть разный график работы, разное версионирование приложений, и тем более разные репозитарии, и разный подход к разработке.
Кроме того, хотя Kestrel
- хороший веб-сервер, все же раздача статических файлов - не основная его специализация.
Поэтому, руководствуясь принципом разделения ответственности, вам необходимо разделить контейнеризацию API-сервиса и JavaScript
приложения.
Остановите контейнер todo_bundle
, удалите его и образ todo-bundle
Модифицируйте Dockerfile
для API-сервиса, таким образом, чтобы убрать из него все упоминание JavaScript
приложения и его этапов сборки.
Создайте образ todo-api
на основе нового докер-файла.
Напишите конфигурацию Nginx
для использования его как в качестве обратного прокси для API-сервиса, так и для раздачи файлов из wwwroot
, получаемой после сборки JavaScript
приложения. При этом:
- Учтите, что все методы API-сервера имеют префикс
/api
- Не забудьте о том, что приложение на
JS
являетсяSPA
, и веб-сервер должен перенаправлять все нераспознанные пути наindex.html
(используйте директивуtry_files
) - Содержимое
wwwroot
должно лежать в/var/www/todo
Напишите Dockerfile
для JavaScript
приложения, взяв за основу образ nginx
.
При сборке образа копируйте написанную вами конфигурацию Nginx в /etc/nginx/nginx.conf
,
а результат сборки JavaScript
приложения в /var/www/todo
.
Создайте образ todo-ui
из этого докер-файла.
1.2.2 Проверка работоспособности контейнеризованного приложения
Запустите в сети todo
контейнер todo_api
из образа todo-api
, при этом:
- Не забудьте про переменную среды
ConnectionStrings__PostgreSQL
. - Убедитесь что контейнер недоступен из внешней сети.
Запустите в сети todo
контейнер todo_ui
из образа todo-ui
, при этом:
- Пробросьте порт контейнера 80(или любой другой, который вы использовали при написании конфигурации Nginx) на локальную машину(например, опять же на порт 8080)
Убедитесь что приложение доступно извне и работает.
1.2.3 Рефакторинг образа todo-api
Платформа .NET
, на самом деле, позволяет собирать приложения, отвязанные от "внешней" предустановленной среды выполнения.
Хотя рекомендованный способ докеризации приложений на .NET
- отталкиваться от образа с mcr.microsoft.com
,
иногда возникает необходимость именно в self-contained
приложениях.
Модифицируйте Dockerfile
для todo-api
, следующим образом:
- Поменяйте образ SDK для сборки на
mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim
- Замените команду публикации приложения на
dotnet publish TodoApi.csproj --self-contained -r linux-x64 -c Release -o /app/publish
(замените/app/publish
на директорию которую использовали вы, если она у вас отличается) - Поменяйте результирующий исходный образ на
debian:bullseye-slim
- В образе
debian:bullseye-slim
не хватаетICU
, необходимого для запуска полноценного.NET
приложения.
Установитеlibicu
в этом образе при сборке:apt-get update && apt-get install libicu67
1.2.4 Проверка работоспособности контейнеризованного приложения
Пересоберите образ todo-api
, и перезапустите контейнер todo_api
на его основе, проверьте что всё работает.
2. Политики перезапуска и базовый мониторинг
-
Запустите
todo_ui
,todo_api
иtodo_postgres
так, чтобы они перезапускались при ошибке или же при перезапуске демона Docker.Перезапустите демон Docker, или же вообще, сделайте перезагрузку машины, и проверьте что контейнеры запустились при старте Docker.
-
Перезапустите контейнер
todo_postges
, так чтобы healthcheck:- Запускался командой
pg_isready -U todo -d todo
- Имел интервал 10 секунд
- Имел timeout 5 секунд
- Проверка имела бы до 5 попыток старта в случае неудачи
- Начиналась бы после 10 секунд после старта контейнера
- Запускался командой
3. Ограничение ресурсов
-
Перезапустите контейнеры
todo_ui
,todo_api
иtodo_postgres
, так они имели ограничение по памяти в 300, 500 и 800 мегабайт соответственно.
Поэкспериментируйте с ограничениями и посмотрите что выходит, в случае их превышения. -
Перезапустите контейнеры
todo_ui
,todo_api
иtodo_postgres
, так они имели ограничение по ресурсам процессора в 1, 1 и 2 соответственно
4. Docker Compose
-
Убедитесь что docker compose установлен, проверьте версию.
-
Напишите
docker-compose.yml
для контейнера на основеbusybox
, который бы печаталHello, World
при запуске. Запустите. -
Напишите конфигурацию compose для приложения
cats_app
. Сделайте так, чтобы образcats_app
собирался, если его еще не существует. Запустите приложение через docker compose, используя эту конфигурацию. Убедитесь в том, что приложение работает. -
Напишите конфигурацию compose для вышеописанной системы todo. При этом:
- Добавьте туда как healthcheck, так и ограничения ресурсов описанные выше
- Сделайте так чтобы контейнер
todo_api
зависел отtodo_postgres
, аtodo_ui
- отtodo_api
. - Сделайте так, чтобы образы
todo-ui
иtodo-api
собирались, если они еще не существуют. Запустите систему через docker compose, и убедитесь что все работает.
-
Добавьте контейнер из образа
dpage/pgadmin4:7
в compose файл к вышеописанной системе, так чтобы он подключался кPostgreSQL
и позволял смотреть данные приложенияtodo
. При этом установите следующие переменные среды в контейнере в соответствующие значения:PGADMIN_DEFAULT_EMAIL
=todo@example.com
PGADMIN_DEFAULT_PASSWORD
=todo
PGADMIN_CONFIG_SERVER_MODE
=False
Убедитесь что консоль администрирования недоступна из интернета, и чтобы проверить ее работоспособность, можно, например,
прокинуть SSH-туннель от вашей машины, до машины с Docker, например таким образом:
ssh -L 5051:localhost:5050 stud@studN.myoffice.ru
.
где вместо 5050 - подставьте порт, на котором pgadmin доступен изнутри машиныstudN.myoffice.ru
.
После выполнения этой команды, pgadmin будет доступен на вашей машине, на http://localhost:5051Про SSH туннели подробнее можете почитать здесь:
https://unix.stackexchange.com/questions/115897/whats-ssh-port-forwarding-and-whats-the-difference-between-ssh-local-and-remot
5. Docker Swarm
-
Инициализируйте Docker в режим Swarm
-
Выведите на экран список нод Docker Swarm
-
Выведите токен для присоединения к Swarm в качестве worker
-
Создайте сеть Docker типа
overlay
с именемtodo_swarm
-
Создайте том данных
todo_swarm_pgdata
так, чтобы в него мог писать только 1 сервис одновременно -
Запустите образ
postgres:16
как сервис Swarm с именемtodo_postgres
, подключенный к сетиtodo_swarm
, с количеством реплик равному 1, при этом:- При запуске установите следующие переменные окружения контейнера в значение
todo
:POSTGRES_DB
- имя базы данных, к которой будет обращаться API-сервисPOSTGRES_USER
- пользователь БД, через которого будет работать сервисPOSTGRES_PASSWORD
- пароль для пользователя
- Установите значение переменной среды
PGDATA
в значение/var/lib/postgresql/data
- здесьPostgreSQL
будет хранить данные. - Примонтируйте том данных
todo_swarm_pgdata
в качестве директории/var/lib/postgresql/data
. - Примонтируйте директорию
initdb
изtodo_app
в качестве директории/docker-entrypoint-initdb.d
внутри контейнера.
Там находится скриптinit_db.sql
, который используется для инициализации базы данных при первом старте контейнера.
- При запуске установите следующие переменные окружения контейнера в значение
-
Запустите образ
todo-api
как сервис Swarm с именемtodo_api
, подключенный к сетиtodo_swarm
, с количеством реплик равному 1, при этом:- Установите переменную окружения
ConnectionStrings__PostgreSQL
в значениеHost=todo_postgres;Port=5432;Database=todo;Username=todo;Password=todo
- Установите переменную окружения
ASPNETCORE_URLS
вhttp://*:80
- Установите переменную окружения
-
Запустите образ
todo-ui
как сервис Swarm с именемtodo_ui
, подключенный к сетиtodo_swarm
, с количеством реплик равному 1, при этом:- Пробросьте порт 80 на внешний порт 8080
-
Убедитесь, что приложение доступно на порту 8080
-
Увеличьте количество реплик каждого из сервисов
todo_ui
иtodo_api
до 3. Убедитесь что приложение работает. -
Выведите листинг реплик сервисов
todo_api
иtodo_ui
-
Просмотрите логи сервиса
todo_api
. Убедитесь что запросы подхватываются разными репликами. -
Удалите все три сервиса
-
Напишите compose-файл для этих сервисов, который бы также запускал по 3 реплики
todo_ui
иtodo_api
и 1 репликуtodo_postgres
- Отталкивайтесь от ранее вами написанного файла для docker compose
-
Запустите docker stack, исходя из написанной вами конфигурации, и убедитесь в работоспособности системы