Docker Alpine + ExpressJS + Angular

Это было для проекта, который я недавно сделал, который требовал, чтобы приложение панели отчетности обслуживалось из образа докера и работало внутри небольших маломощных устройств Linux.

Приложение должно было соответствовать следующим критериям:

  1. Его необходимо быстро воспроизвести для многих устройств.
  2. Он должен быть небольшим, места не так много
  3. Он должен иметь возможность подключаться к базе данных MariaDB в локальной сети.
  4. Некоторые поля можно настроить из базы данных JSON, которая доступна за пределами контейнера.

IMG_20190408_153056.jpg
Контейнер Docker, содержащий приложение ExpressJS, которое обслуживает приложение Angular.

Докер

1. Нужно быстро реплицировать на множество устройств

Docker существует уже довольно давно, выпущен на 2013он направлен на решение проблемы «Но это работает на моем компьютере».

Популярное использование докера — это объединение его с kubernetes для запуска нескольких микросервисов для обработки больших всплесков одновременных пользователей, а затем удаление их после всплеска для экономии ресурсов. Каждый микросервис — это крошечный сервер сам по себе.

Поскольку образы докеров по сути подобны резервной копии крошечного сервера, они всегда будут работать одинаково без особой настройки. Это упрощает копирование образа на любое новое устройство и запуск нового контейнера докеров с приложением.

Архитектурное решение

  1. Используйте Docker для контейнеризации приложения
  2. ExpressJS служит веб-сервером для внешнего приложения Angular.
  3. ExpressJS использует низкий дб для чтения/записи конфигураций в файл JSON в папке с именем data/.
  4. ExpressJS использует knex.js для запроса базы данных SQL.

Окончательная рабочая папка выглядит примерно так:

    backend/
        |- src/...
        |- dist/
            |- server.min.js
    frontend/
        |- src/...
        |- dist/
            |- public/
                |- main.min.js
                |- vendor.min.js
                |- index.html
    docker/
        |- data/
            |- local-db.json
        |- server.min.js
        |- package.json
        |- public/
            |- main.min.js
            |- vendor.min.js
            |- index.html
        |- Dockerfile
        |- .dockerignore

И интерфейс, и бэкенд сведены к минимуму, чтобы уменьшить пространство. Содержимое свернутого кода копируется в папку docker, чтобы мне было проще упаковать его в docker-образ.

Экспресс-сервер обслуживает общую папку (приложение Angular) по корневому маршруту. В то время как остальная часть API начинается с /api/ префикс. Это также означает, что Angular может использовать /api при вызове http, поэтому он всегда будет вызывать что-то в том же домене.

    this.http.post('/api'+ reportUrl);

Package.json из серверной части также включен, потому что мне нужно установить node_modules, необходимые для приложения ExpressJS. Вероятно, можно каким-то образом связать зависимости с помощью Webpack, но у меня было мало времени, и я не стал исследовать, как это сделать.

    import * as FileAsync from 'lowdb/adapters/FileAsync';
    const low = require('lowdb');
    
    low(new FileAsync(path)).then(db => {
        db.defaults({
            maxEntries: MAXENTRIES,
            configs: []
        })
        .write();
    });

Это запишет конфигурацию JSON по умолчанию в data/local-db.json.

Докерфайл

2. Он должен быть маленьким, места не так много

Распространенной практикой во время разработки является размещение всех исходных файлов в контейнере докеров и запуск полного сервера node.js для обслуживания вашего приложения. Это легко, быстро, безболезненно.

Но полный node.js на сервере Debian довольно большой — почти 650 МБ с самого начала. После установки нашего приложения ExpressJS и Angular он занял около 1,3 ГБ.

Поэтому мы решили использовать новую barebone-систему, основанную на Альпийский linux, который составляет огромные 50 МБ. Со всеми установленными файлами получилось около 300 МБ. Все еще намного меньше, чем полный образ сервера.

Полный список доступных базовых образов узлов: здесь.

Базовая ОС означает, что нам нужны дополнительные файлы для установки модулей узла.

    FROM node:10.15.3-alpine
    
    ENV JWT_SECRET 512FF7B2EMCKAHG24C
    ENV BCRYPT_SALT 12
    ENV PORT 8080
    ENV TOKEN_EXPIRES_IN 24h
    
    WORKDIR /usr/src/app
    COPY . .
    RUN apk --no-cache add --virtual native-deps \
        bash g++ gcc libgcc libstdc++ linux-headers make python && \
        npm install --quiet node-gyp forever -g &&\
        npm install --production --quiet
    EXPOSE 8080
    CMD forever server.min.js 8080

Там очень подробно документация о том, как написать Dockerfile, но, по сути, файл docker похож на сценарий bash, который последовательно запускает команды и, наконец, обслуживает приложение.

FROM node:10.15.3-alpine это то, как я выбираю изображение в качестве основы для контейнера.

Я устанавливаю переменные среды с помощью ENV команда. Который затем можно прочитать в приложении ExpressJS через process.env.

WORKDIR выбирает текущую папку, которую я хочу использовать в контейнере. Если каталога не существует, Docker создает их.

Я включаю несколько установок (gcc, python и т. д.), которые необходимы для сборки узла.

И вот, наконец, сервировка server.min.js с Навсегда.

Forever — это инструмент командной строки, который запускает сервер узлов как демон и не может быть случайно остановлен.

Докер Бег

3. Должна быть возможность подключения к базе данных MariaDB в локальной сети.

Теперь самое интересное — запустить приложение. Сначала мы должны построить его.

    $ docker build --tag node-app:alpine .

Сборка Docker считывает команду из файла Dockerfile и создает образ.

--tag позволяет мне назвать образ докера, чтобы его было легче найти позже.

После постройки, docker images показывает, что я действительно построил образ

    $ docker images
    
    REPOSITORY     TAG          IMAGE ID         CREATED          SIZE
    node-app       alpine       f4ca2e0425a0     3 days ago       354MB

Теперь, чтобы запустить образ докера

    $ docker run \
        --name node-app \
        --network host \
        --restart 'unless-stopped' \ 
        --detach \
        node-app:alpine

--name позволяет мне установить пользовательское имя. Если это не указано, докер создаст для вас имя, например, драгоценная трава или подобное.

--restart заставит работающий контейнер работать вечно, если его специально не остановить.

--detach заставит его работать в фоновом режиме, освобождая мой терминал для других вещей. Если вы не используете это, вам понадобится второй терминал, чтобы остановить ваш док-контейнер.

--network это ключевой вариант здесь. Одним из требований является то, что контейнер Docker должен иметь возможность подключаться к базе данных на локальном хосте. А с помощью опции host, мы можем сделать это. Существуют и другие методы создания сетей докеров и соединения контейнеров друг с другом. Вы можете прочитать больше здесь.

С помощью этой команды я теперь могу видеть, как приложение работает на localhost:8080. И мое приложение может успешно подключиться к базе данных. 🎉

giphy.gif

Объем докера

4. Некоторые поля можно настроить из базы данных JSON, которая доступна за пределами контейнера.

Контейнер Docker теперь должен работать без сбоев, пока я не обнаружу, что json local-db на самом деле не обновляется. И я не могу контролировать конфигурацию из-за пределов контейнера.

Это потому, что я упустил --volume опция во время запуска докера, которая позволяет мне монтировать папку в докер, которая затем может использоваться совместно между хост-компьютером и контейнером докера. Вы можете узнать больше о сохранении данных в докере здесь.

    $ docker run \
        --name node-app \
        --volume $(pwd)/data:/usr/src/app/data \
        --network host \
        --restart 'unless-stopped' \ 
        --detach \
        node-app:alpine

$(pwd) вставит текущий рабочий каталог в команду. Если бы я вызывал докер из другой папки, pwd был бы другим.

Здесь команда Volume монтирует папку данных в папку данных, которая существует внутри контейнера докеров. /usr/src/app/ папка. Если целевая папка не существует, --volume создаст его для меня.

Запустите это, и теперь я могу управлять конфигами извне.

Сохранение и загрузка изображений

Теперь, когда у меня есть изображение и работает. Мне нужно экспортировать изображение в формат, который я могу импортировать на разные устройства и запустить тот же экземпляр.

    $ docker save -o ./node-app-image.tar node-app:alpine

save Команда экспортирует текущее состояние образа в архив. Я удостоверяюсь, что не добавил никаких пользовательских конфигураций, потому что я не хочу, чтобы они сохранялись в образе.

    $ docker load -i ./node-app-image.tar

Затем я запускаю load команду после того, как я загрузил tarball в машину. Загрузка изображения будет включать изображение в docker images. И тогда я делаю run команда, как и раньше, и она должна найти то же самое node-app:alpine изображение, которое я сохранил.

Мысли

Докер действительно очень мощный. Это позволило разработчикам мгновенно и уверенно масштабировать приложения, потому что среда всегда будет одинаковой.

Тем не менее, я думаю, что с документацией по докеру будет немного проще иметь дело. Необходимо изучить такое огромное количество настроек, что неудивительно, что настройка докера — это отдельная профессия.

Как бы то ни было, изучать докер — это увлекательно. Дайте мне знать, если есть лучший способ!

giphy.gif

Ресурсы

[1] https://github.com/nodejs/докер-узел
[2] https://github.com/nodejs/docker-node/issues/282
[3] https://stackoverflow.com/questions/43316376/what-does-net-host-option-in-docker-command-really-do
[4]

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *