From 9f498642b27d1f8913d83177d0ad21322ab0df89 Mon Sep 17 00:00:00 2001 From: Dmitry Ignatiev Date: Tue, 14 Nov 2023 17:10:02 +0300 Subject: [PATCH] m2/08/tasks refactoring of p.1 --- .../tasks.md | 199 ++++++++++-------- 1 file changed, 107 insertions(+), 92 deletions(-) diff --git a/module2/08_docker_containerization_advanced/tasks.md b/module2/08_docker_containerization_advanced/tasks.md index f69139a..52026db 100644 --- a/module2/08_docker_containerization_advanced/tasks.md +++ b/module2/08_docker_containerization_advanced/tasks.md @@ -5,33 +5,46 @@ В полученной вами директории `todo_app` находятся исходные коды Enterprise Quality™® системы для менеджмента напоминаний. Она состоит из следующих частей: - - `TodoApi` - API-сервис, на платформе `.NET 7`. - - `todo_ui` - веб-интерфейс к этому сервису, Single-Page `JavaScript` приложение(`SPA`), написанное с использованием `Vue.js 3`. - - API-сервис использует `PostgreSQL` для хранения данных. + - `TodoApi` - API-сервис, на платформе `.NET 7`, + - `todo_ui` - веб-интерфейс к этому сервису, Single-Page `JavaScript` приложение(`SPA`), написанное с использованием `Vue.js 3`, + - `PostgreSQL` - API-сервис использует `PostgreSQL` для хранения данных. Необходимо будет контейнеризировать эту систему. -Сервис TodoApi имеет следующие шаги сборки: - - - Во-первых, для его сборки необходим .NET 7 SDK - - Далее, из директории, в которой находятся исходные коды сервиса(см. `todo_app/TodoApi`), \ - нужно сделать `dotnet restore TodoApi.csproj` - эта команда скачает зависимости сервиса. - - Следующим шагом идет непосредственно сборка сервиса. \ - Она производится командой `dotnet build TodoApi.csproj -c Release -o `, \ - в которой вместо `` необходимо подставить имя директории, которое будет \ - содержать бинарные файлы сервиса, например `/app/build` - - Последняя вещь, которую необходимо сделать - это вызывать команду \ - `dotnet publish TodoApi.csproj -c Release -o `, \ - которая опубликует результирующие бинарные файлы, а также зависимости сервиса \ - в ``(например в `/app/publish`). +#### 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 `, \ + в которой вместо `` необходимо подставить имя директории, которое будет \ + содержать бинарные файлы сервиса, например `/app/build` +- Последняя вещь, которую необходимо сделать - это вызывать команду \ + `dotnet publish TodoApi.csproj -c Release -o `, \ + которая опубликует результирующие бинарные файлы, а также зависимости сервиса \ + в ``(например в `/app/publish`). Для запуска процесса сервиса SDK не нужен, достаточно лишь соответствующего фреймворка \ `ASP.NET` - в данном случае версии `7.0`. \ Запуск осуществляется вызовом исполняемого файла `./TodoApi` из директории, которая хранит опубликованные \ бинарные файлы сервиса и его зависимостей. -`JavaScript` приложение собирается следующим образом: - +Фаза сборки `JavaScript` приложения состоит из следующих шагов: - Прежде всего, необходим `NodeJS` - в данном случае подойдет версия `21`. - Далее, из директории, в которой находятся исходные коды приложения(см. `todo_app/todo_ui`), \ нужно вызывать `npm install` - это команда скачает зависимости приложения. @@ -45,108 +58,110 @@ Для хостинга JS-приложения в API-сервисе, необходимо всего лишь скопировать упомянутую выше директорию \ `wwwroot` внутрь результирующей директории API-сервиса(которая получается после выполнения `dotnet publish ...`). -#### Задачи. +Создайте образ из полученного `Dockerfile` и назовите его `todo-bundle`. + +##### 1.2 Подготовка образа PostgreSQL -1. Напишите `Dockerfile`, из которого можно было бы создать образ, и который отвечал бы следующим требованиям: +Перед запуском самого приложения, необходимо запустить и подготовить `PostgreSQL` в сети `todo`. - - Фаза сборки API-сервиса должна использовать образ `mcr.microsoft.com/dotnet/sdk:7.0` - - Фаза сборки `JavaScript` приложения должна использовать образ `node:21` - - Результирующий образ должен основываться на `mcr.microsoft.com/dotnet/aspnet:7.0` - - Готовое приложение API-сервиса вместе с зависимостями должно находиться в директории `/app` - - `JavaScript` приложение должно находиться в директории `/app/wwwroot` - - При старте контейнера из такого образа, должен запускаться приложение API-сервиса. +Создайте именованную сеть типа `bridge`, в которой будет работать вся система, назовите ее `todo`. - Документация по многоступенчатой сборке образов находится тут: https://docs.docker.com/build/building/multi-stage/ +Создайте том данных, в котором `PostgreSQL` будет хранить БД. Назовите его `todo_pgdata`. -2. Создайте образ из полученного `Dockerfile` и назовите его `todo-bundle` +Запустите именованный контейнер `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`, который используется для инициализации базы данных при первом старте контейнера. -3. Перед запуском самого приложения, необходимо запустить и подготовить `PostgreSQL` в сети `todo`. +##### 1.3 Проверка работоспособности контейнеризованного приложения - Создайте именованную сеть типа `bridge`, в которой будет работать вся система, назовите ее `todo`. +Запустите контейнер `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. - Создайте том данных, в котором `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`, который используется для инициализации базы данных при первом старте контейнера. +При возникновении сложностей и необходимости отладки, вы можете также установить значение переменной \ +`ASPNETCORE_ENVIRONMENT` в `Development` - таким образом сервис будет выдавать больше логов. -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. +#### 2 Рефакторинг контейнеризованного приложения - Убедитесь, что приложение работает и доступно на выбранном вами порту на локальной машине. +##### 1.1 Декомпозиция образа `todo-bundle` - При возникновении сложностей и необходимости отладки, вы можете также установить значение переменной \ - `ASPNETCORE_ENVIRONMENT` в `Development` - таким образом сервис будет выдавать больше логов. +Несмотря на то что API-сервис поддерживает хостинг `JavaScript` приложения, в реальном(или скорее, идеальном) мире \ +никто так не делает. Над интерфейсом и API часто работают разные команды, у них может быть разный график работы, \ +разное версионирование приложений, и тем более разные репозитарии, и разный подход к разработке. -5. Несмотря на то что API-сервис поддерживает хостинг `JavaScript` приложения, в реальном(или скорее, идеальном) мире \ - никто так не делает. Над интерфейсом и API часто работают разные команды, у них может быть разный график работы, \ - разное версионирование приложений, и тем более разные репозитарии, и разный подход к разработке. +Кроме того, хотя `Kestrel` - хороший веб-сервер, все же раздача статических файлов - не основная его специализация. - Кроме того, хотя `Kestrel` - хороший веб-сервер, все же раздача статических файлов - не основная его специализация. +Поэтому, руководствуясь принципом разделения ответственности, вам необходимо разделить контейнеризацию \ +API-сервиса и `JavaScript` приложения. - Поэтому, руководствуясь принципом разделения ответственности, вам необходимо разделить контейнеризацию \ - API-сервиса и `JavaScript` приложения. +Остановите контейнер `todo_bundle`, удалите его и образ `todo-bundle` - Остановите контейнер `todo_bundle`, удалите его и образ `todo-bundle` +Модифицируйте `Dockerfile` для API-сервиса, таким образом, чтобы убрать из него все упоминание `JavaScript` приложения \ +и его этапов сборки. - Модифицируйте `Dockerfile` для API-сервиса, таким образом, чтобы убрать из него все упоминание `JavaScript` приложения \ - и его этапов сборки. +Создайте образ `todo-api` на основе нового докер-файла. - Создайте образ `todo-api` на основе нового докер-файла. +Напишите конфигурацию `Nginx` для использования его как в качестве обратного прокси для API-сервиса, так и для \ +раздачи файлов из `wwwroot`, получаемой после сборки `JavaScript` приложения. \ +При этом: +- Учтите, что все методы API-сервера имеют префикс `/api` +- Не забудьте о том, что приложение на `JS` является `SPA`, и веб-сервер должен перенаправлять все нераспознанные пути \ + на `index.html` (используйте директиву `try_files`) +- Содержимое `wwwroot` должно лежать в `/var/www/todo` - Напишите конфигурацию `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`. - Напишите `Dockerfile` для `JavaScript` приложения, взяв за основу образ `nginx`. \ - При сборке образа копируйте написанную вами конфигурацию Nginx в `/etc/nginx/nginx.conf`, \ - а результат сборки `JavaScript` приложения в `/var/www/todo`. +Создайте образ `todo-ui` из этого докер-файла. - Создайте образ `todo-ui` из этого докер-файла +##### 1.2 Проверка работоспособности контейнеризованного приложения -6. Запустите в сети `todo` контейнер `todo_api` из образа `todo-api`, при этом: - - Не забудьте про переменную среды `ConnectionStrings__PostgreSQL`. - - Убедитесь что контейнер недоступен из внешней сети. +Запустите в сети `todo` контейнер `todo_api` из образа `todo-api`, при этом: +- Не забудьте про переменную среды `ConnectionStrings__PostgreSQL`. +- Убедитесь что контейнер недоступен из внешней сети. - Запустите в сети `todo` контейнер `todo_ui` из образа `todo-ui`, при этом: - - Пробросьте порт контейнера 80(или любой другой, который вы использовали при \ - написании конфигурации Nginx) на локальную машину(например, опять же на порт 8080) +Запустите в сети `todo` контейнер `todo_ui` из образа `todo-ui`, при этом: +- Пробросьте порт контейнера 80(или любой другой, который вы использовали при \ + написании конфигурации Nginx) на локальную машину(например, опять же на порт 8080) - Убедитесь что приложение доступно извне и работает. +Убедитесь что приложение доступно извне и работает. -7. Платформа `.NET`, на самом деле, позволяет собирать приложения, отвязанные от "внешней" предустановленной \ +##### 1.3 Рефакторинг образа `todo-api` + +Платформа `.NET`, на самом деле, позволяет собирать приложения, отвязанные от "внешней" предустановленной \ среды выполнения. - Хотя рекомендованный способ докеризации приложений на `.NET` - отталкиваться от образа с `mcr.microsoft.com`, - иногда возникает необходимость именно в `self-contained` приложениях. +Хотя рекомендованный способ докеризации приложений на `.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.4 Проверка работоспособности контейнеризованного приложения - Модифицируйте `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` на его основе, проверьте что всё работает. - Пересоберите образ `todo-api`, и перезапустите контейнер `todo_api` на его основе, проверьте что всё работает. ### 2. Политики перезапуска и базовый мониторинг