# Создание образа АПИ (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 с запущенным контейнером.