You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
17 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Задания
### 1. Многоступенчатая сборка образов
В полученной вами директории `todo_app` находятся исходные коды Enterprise Quality™® системы для менеджмента напоминаний.
Она состоит из следующих частей:
- `TodoApi` - API-сервис, на платформе `.NET 7`.
- `todo_ui` - веб-интерфейс к этому сервису, Single-Page `JavaScript` приложение(`SPA`), написанное с использованием `Vue.js 3`.
- API-сервис использует `PostgreSQL` для хранения данных.
Необходимо будет контейнеризировать эту систему.
Сервис TodoApi имеет следующие шаги сборки:
- Во-первых, для его сборки необходим образ `mcr.microsoft.com/dotnet/sdk:7.0`
- Далее, из директории, в которой находятся исходные коды сервиса(см. `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` - в данном случае `mcr.microsoft.com/dotnet/aspnet:7.0`. \
Запуск осуществляется вызовом исполняемого файла `./TodoApi` из директории, которая хранит опубликованные \
бинарные файлы сервиса и его зависимостей.
`JavaScript` приложение собирается следующим образом:
- Прежде всего, необходим образ `NodeJS` - в данном случае подойдет `node:21`.
- Далее, из директории, в которой находятся исходные коды приложения(см. `todo_app/todo_ui`), \
нужно вызывать `npm install` - это команда скачает зависимости приложения.
- После этого, в этой же директории необходимо выполнить команду `npm run build`, в результате чего, \
в этой директории появится директория `wwwroot`, содержащая файл `index.html`, \
результирующий код на JS, а также его ресурсы, такие как картинки и файлы стилей(css).
Для запуска `JavaScript` приложения `NodeJS` не нужен, достаточно лишь любого веб-сервера, и в \
нашем случае, в API-сервисе такой имеется(он называется `Kestrel`).
Для хостинга JS-приложения в API-сервисе, необходимо всего лишь скопировать упомянутую выше директорию \
`wwwroot` внутрь результирующей директории API-сервиса(которая получается после выполнения `dotnet publish ...`).
#### Задачи.
1. Напишите `Dockerfile`, из которого можно было бы создать образ, который отвечал бы следующим требованиям:
- Образ должен основываться на образе `mcr.microsoft.com/dotnet/aspnet:7.0`
- Готовое приложение API-сервиса вместе с зависимостями должно находиться в директории `/app`
- `JavaScript` приложение должно находиться в директории `/app/wwwroot`
- При старте контейнера из такого образа, должен запускаться приложение API-сервиса.
Документация по многоступенчатой сборке образов находится тут: https://docs.docker.com/build/building/multi-stage/
2. Создайте образ из полученного `Dockerfile` и назовите его `todo-bundle`
3. Перед запуском самого приложения, необходимо запустить и подготовить `PostgreSQL` в сети `todo`.
Создайте именованную сеть типа `bridge`, в которой будет работать вся система, назовите ее `todo`.
Создайте том данных, в котором `PostgreSQL` будет хранить БД. Назовите его `todo_pgdata`.
Запустите именованный контейнер `todo_postgres` из образа `postgres:16`, при этом:
1. При запуске установите следующие переменные окружения контейнера в значение `todo`:
- `POSTGRES_DB` - имя базы данных, к которой будет обращаться API-сервис
- `POSTGRES_USER` - пользователь БД, через которого будет работать сервис
- `POSTGRES_PASSWORD` - пароль для пользователя
2. Установите значение переменной среды `PGDATA` в значение `/var/lib/postgresql/data` - здесь `PostgreSQL` будет хранить данные.
3. Примонтируйте том данных `todo_pgdata` в качестве директории `/var/lib/postgresql/data`.
4. Примонтируйте директорию `initdb` из `todo_app` в качестве директории `/docker-entrypoint-initdb.d` внутри контейнера. \
Там находится скрипт `init_db.sql`, который используется для инициализации базы данных при первом старте контейнера.
4. Запустите контейнер `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` - таким образом сервис будет выдавать больше логов.
5. Несмотря на то что 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` из этого докер-файла
6. Запустите в сети `todo` контейнер `todo_api` из образа `todo-api`, при этом:
- Не забудьте про переменную среды `ConnectionStrings__PostgreSQL`.
- Убедитесь что контейнер недоступен из внешней сети.
Запустите в сети `todo` контейнер `todo_ui` из образа `todo-ui`, при этом:
- Пробросьте порт контейнера 80(или любой другой, который вы использовали при \
написании конфигурации Nginx) на локальную машину(например, опять же на порт 8080)
Убедитесь что приложение доступно извне и работает.
7. Платформа `.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`
Пересоберите образ `todo-api`, и перезапустите контейнер `todo_api` на его основе, проверьте что всё работает.
### 2. Политики перезапуска и базовый мониторинг
1. Запустите `todo_ui`, `todo_api` и `todo_postgres` так, чтобы они перезапускались при ошибке или же \
при перезапуске демона Docker.
Перезапустите демон Docker, или же вообще, сделайте перезагрузку машины, \
и проверьте что контейнеры запустились при старте Docker.
2. Перезапустите контейнер `todo_postges`, так чтобы healthcheck:
- Запускался командой `pg_isready -U todo -d todo`
- Имел интервал 10 секунд
- Имел timeout 5 секунд
- Проверка имела бы до 5 попыток старта в случае неудачи
- Начиналась бы после 10 секунд после старта контейнера
### 3. Ограничение ресурсов
1. Перезапустите контейнеры `todo_ui`, `todo_api` и `todo_postgres`, так они имели ограничение по памяти в \
300, 500 и 800 мегабайт соответственно. Поэкспериментируйте с ограничениями и посмотрите что выходит, \
в случае их превышения.
2. Перезапустите контейнеры `todo_ui`, `todo_api` и `todo_postgres`, так они имели ограничение по ресурсам процессора в \
1, 1 и 2 соответственно
### 4. Docker Compose
1. Убедитесь что docker compose установлен, проверьте версию.
2. Напишите `docker-compose.yml` для контейнера на основе `busybox`, который бы печатал `Hello, World` при запуске. Запустите.
3. Напишите конфигурацию compose для приложения `cats_app`. \
Сделайте так, чтобы образ `cats_app` собирался, если его еще не существует. \
Запустите приложение через docker compose, используя эту конфигурацию. \
Убедитесь в том, что приложение работает.
4. Напишите конфигурацию compose для вышеописанной системы todo. При этом:
- Добавьте туда как healthcheck, так и ограничения ресурсов описанные выше
- Сделайте так чтобы контейнер `todo_api` зависел от `todo_postgres`, а `todo_ui` - от `todo_api`. \
Сделайте так, чтобы образы `todo-ui` и `todo-api` собирались, если они еще не существуют. \
Запустите систему через docker compose, и убедитесь что все работает.
5. Добавьте контейнер из образа `dpage/pgadmin4:7` в compose файл к вышеописанной системе, так чтобы он подключался к `PostgreSQL`\
и позволял смотреть данные приложения `todo`. \
При этом установите следующие переменные среды в контейнере в соответствующие значения:
- `PGADMIN_DEFAULT_EMAIL` = `todo@example.com`
- `PGADMIN_DEFAULT_PASSWORD` = `todo`
- `PGADMIN_CONFIG_SERVER_MODE` = `False`
### 5. Docker Swarm