# Создание образа АПИ (express.js). Настройка сети >Предполагается, что АПИ у вас уже написано (для C# создание образа рассмотрим на следующих занятиях) Нам нужна не простая рыба, а АПИ работающее с БД (чтобы задействовать контейнер `test_mysql` из прошлой лекции). Я накидал простенькое АПИ на основе [прошлогодней инструкции](https://github.com/kolei/PiRIS/blob/master/api/express01.md). Код лежит в каталоге [docker/api_js](../docker/api_js). Весь я его здесь описывать не буду, сосредоточусь только на важных моментах: 1. В файле `.sequelizerc` не забудьте указать порт, если он не стандартный ```js module.exports = { 'url': 'mysql://root:toor@127.0.0.1:3308/mydb' } ``` 1. Данные в базу накатываем командой `npx sequelize-cli db:seed:all` Если всё сделано правильно, то АПИ должен работать и возвращать список жанров: ![](../img/docker04.png) Так как у нас для разработки и боевого режима одна и та же база данных, то перед запуском контейнера нужо отменить миграцию командой: ``` npx sequelize-cli db:seed:undo ``` ## Создание образа При создании образа мы будем копировать в него наш проект, при этом мы должны помнить про безопасность и не хранить в образе пароли, плюс не нужно копировать каталог `node_modules`, так как в образе проект будет собираться заново. Для игнорирования файлов нужно добавить в каталог с проектом файл `.dockerignore`, в который добавить файл `.sequelizerc` и каталог `node_modules` (формат у файла такой же как у `.gitignore`) ``` .sequelizerc node_modules ``` В Dockerfile можно прописать только одну команду (`CMD`) для запуска приложения, а нам перед запуском нужно запустить миграцию. Для таких случаев в Dockerfile можно прописать не отдельную команду, а скрипт. Для этого служит команда `ENTRYPOINT` Создадим в каталоге проекта файл `entrypoint.sh`: ```sh #!/bin/sh # накатываем начальные данные npx sequelize-cli db:seed:all # запускаем сервер exec node index.js ``` Теперь пишем **Dockerfile**: ```Dockerfile # создаем образ на основе Node.js версии 20 на основе минималистичного linux-образа alpine FROM node:20-alpine # устанавливаем рабочую директорию WORKDIR /app # копируем package.json и package-lock.json COPY package*.json ./ # устанавливаем зависимости (как раз тут будет заполнен каталог node_modules) RUN npm install # копируем остальные файлы проекта COPY . . # открываем порт 3000 EXPOSE 3000 # запускаем сервер Express.js - вместо простого запуска будем использовать ENTRYPOINT # CMD ["node", "index.js"] # даем права на выполнение скрипту RUN chmod 777 ./entrypoint.sh # точка входа ENTRYPOINT [ "./entrypoint.sh" ] ``` Собираем образ ``` docker build -t test-api . ``` ## Запуск контейнера в сети До этого мы про сети не упоминали. Контейнеры, как вы знаете, работают в изолированном окружении, в том числе и в собственной сети, даже если мы явно никакую сеть не указывали. По-умолчанию используется режим сети `bridge`, когда для контейнера создается своя сеть (глубоко в тему сетей мы не полезем, упомяну только, что для разработки можно исользовать режим сети `host`, когда контейнер работает в сети хоста). Мы можем посмотреть текущий режим сети в Docker Desktop ![](../img/docker06.png) Нас, естественно, не устраивает, что у каждого контейнера своя сеть. Нам нужно запускать оба наших контейнера в одной сети, чтобы АПИ видело базу. Для создания сети нужно выполнить команду ``` docker network create test-network ``` * `docker network create`: консольная команда для создания сети * `test-network`: название создаваемой сети >Для начала этого достаточно. При желании можно указать тип создаваемой сети и её параметры Сеть создаётся один раз, то есть эту команду выполнять нужно только один раз при настройке. Апи мы запустим с использованием созданной сети, а как быть с базой? Можно перезапустить контейнер с указанием сети, а можно подключить уже запущенный контейнер к новой сети командой: ``` docker network connect test-network test_mysql ``` Теперь, когда все готово, запускаем контейнер с АПИ ```sh docker run --name test_api --network=test-network -d -p 3000:3000 -e NODE_ENV=production -e DATABASE_URL="mysql://root:toor@test_mysql/mydb" test-api ``` Тут у нас появились новые опции: * `--network=test-network`: при запуске контейнера присоединиться к существующей сети (если явно не указать, то будет создана своя сеть) * `-e NODE_ENV=production`: переменная среды окружения для Node.js, которая указывает приложению, что используется боевой режим (мы его используем для настройки доступа к базе данных) * `-e DATABASE_URL="mysql://root:toor@test_mysql/mydb"`: задаём строку подключения к базе данных. Обратите внимание, в качестве хоста указывается имя контейнера, т.е. нам не надо знать адрес контейнера с базой, в докере имя хоста можно использовать вместо IP-адреса. Ну и смена порта не нужна, так как внутри контейнера используется стандартный порт MySQL (`3306`) Если все сделали правильно, то контейнер с АПИ запустится, подключится к базе данных по внутренней сети и будет доступен по адресу `localhost:3000` Можно посмотреть логи работы в Docker Desktop ![](../img/docker07.png) У нас остался доступ к базе данных извне, а в реальных проектах его убирают и база становится доступна только для АПИ по внутренней сети. --- ## Задание на дом * Создать свой образ АПИ (если ваше АПИ написано на express.js, то сразу свой вариант, если нет, то собрать пример из этой лекции). Питонисты могут разработать свой контейнер, разницы с JS не много. * Создать сеть и подключить к ней базу данных * запустить контейнер c АПИ В репозитории опубликовать весь проект АПИ (не забываем про `.gitignore`), в `readme.md` написать команды для сборки образа и запуска контейнера и скриншот Docker Desktop с запущенным контейнером.