Share This
Связаться со мной
Крути в низ
Categories
//Создаем API на Docker, Node.js, Nginx, Postgres

Создаем API на Docker, Node.js, Nginx, Postgres

sozdaem api na docker nodejs nginx postgres 75e6d61 - Создаем API на Docker, Node.js, Nginx, Postgres

Технический директор компании vverh.digital. JavaScript программист, любитель Kotlin и Swift. Как бы часто мы ни начинали новые проекты, каждый раз заложить фундамент трудней всего. Поэтому сегодня мы продемонстрируем универсальную сборку для бэкенда на Node.js c Postgres в Docker. И, конечно, обязательно будем отдавать статику через Nginx.

sozdaem api na docker nodejs nginx postgres 16ac971 - Создаем API на Docker, Node.js, Nginx, Postgres

Файловая архитектура и инструменты

Используем Visual Code Studio в качестве редактора кода. Операционная система любая. Обязательно установите Docker.

Для начала создадим вот такую файловую архитектуру:

sozdaem api na docker nodejs nginx postgres 157a98f - Создаем API на Docker, Node.js, Nginx, Postgres

По мере чтения статьи файлов у нас прибавится. Что мы создали: .env будет содержать переменные среды окружения, в папке app будет контейнер с Node.js, в папке static будет статика, а в Nginx — Nginx-конфигурация. Для начала этого хватит.

Docker и docker-compose.yml

У нас будет два docker-compose.yml: dev и production. Начнем с dev-версии:

docker-compose.dev.yml

         version: '3'  services:   # Контейнер с Node.js   app:     build:       context: ./app       target: dev     tty: true     working_dir: /opt/server     volumes:       - ./app:/opt/server       - ./static:/opt/static     env_file:       - .env     expose:       - '3000'     depends_on:       - db    # Контейнер с базой данных   db:     image: postgres:12-alpine     environment:       - POSTGRES_USER=${DB_USER}       - POSTGRES_PASSWORD=${DB_PASSWORD}     volumes:       - ./postgres:/var/lib/postgresql/data     expose:       - '5432'     restart: always    # Контейнер с nginx   nginx:     container_name: proxy_nginx     depends_on:       - app       - db       - pgadmin     image: nginx:latest     ports:       - '80:80'     volumes:       - ./nginx:/etc/nginx/conf.d       - ./static:/var/www/static     restart: always    # Контейнер с pgadmin   pgadmin:     container_name: pgadmin     depends_on:       - db     image: dpage/pgadmin4     environment:       PGADMIN_DEFAULT_EMAIL: info@proglib.io       PGADMIN_DEFAULT_PASSWORD: qwertyuiop     expose:       - '80'     restart: always     

Здесь стоит обратить внимание на четыре вещи:

  1. Контейнеры app и nginx связаны со статикой. Для Node.js папка со статикой будет ниже уровнем от app.js (главным файлом, чуть позже создадим), на одном уровне с папкой app.
  2. Контейнер с базой данных содержит ${DB_USER} и ${DB_PASSWORD}. Это переменные из .env-файла, мы его начнем заполнять спустя пару мгновений.
  3. Контейнер с Pgadmin содержит такие строки: PGADMIN_DEFAULT_EMAIL и PGADMIN_DEFAULT_PASSWORD. Вы можете там указать свою почту и свой пароль, он будет использоваться для входа в Pgadmin.
  4. В контейнере с Node.js есть раздел build. Там есть target dev. Просто пока обратите внимание.

Теперь перейдем к production-версии:

docker-compose.production.yml

         version: '3'  services:   # Контейнер с Node.js   app:     build:       context: ./app       target: production     tty: true     working_dir: /opt/server     volumes:       - ./app:/opt/server       - ./static:/opt/static       - /opt/server/node_modules/     env_file:       - .env     expose:       - '3000'     depends_on:       - db     command: npm run start    # Контейнер с базой данных   db:     image: postgres:12-alpine     environment:       - POSTGRES_USER=${DB_USER}       - POSTGRES_PASSWORD=${DB_PASSWORD}     volumes:       - ./postgres:/var/lib/postgresql/data     expose:       - '5432'     restart: always    # Контейнер с nginx   nginx:     container_name: proxy_nginx     depends_on:       - app       - db       - pgadmin     image: nginx:latest     ports:       - '80:80'     volumes:       - ./nginx:/etc/nginx/conf.d       - ./static:/var/www/static     restart: always    # Контейнер с pgadmin   pgadmin:     container_name: pgadmin     depends_on:       - db     image: dpage/pgadmin4     environment:       PGADMIN_DEFAULT_EMAIL: info@proglib.io       PGADMIN_DEFAULT_PASSWORD: qwertyuiop     expose:       - '80'     restart: always     

Здесь стоит отметить, что файлы отличаются между собой только настройками для app контейнера.

Во-первых, мы монтируем папку node_modules внутри контейнера специально, чтобы не было проблем между dev и production версией.

Во-вторых, мы исполняем команду npm run start, которая будет запускать наше приложение (позже ее напишем).

В-третьих, в разделе build у нас другой targetproduction. И вот сейчас мы плавно переходим дальше…

Как Docker поймет, какой файл запускать

Идем в .env файл и вставляем следующее содержимое:

.env

         # dev or production NODE_ENV=dev  DB_NAME=api DB_USER=postgres DB_PASSWORD=secret007 DB_HOST=db  COMPOSE_FILE=docker-compose.${NODE_ENV}.yml     

В самом начале в переменную NODE_ENV мы записываем в каком режиме мы будем сейчас работать: dev или production. В самом низу мы объединяем COMPOSE_FILE и NODE_ENV. Особо внимательные догадались, что будет происходить в зависимости от содержимого переменной NODE_ENV при команде:

         docker-compose up     

Будет использоваться тот или иной файл. То есть, если в NODE_ENV указана строка dev, мы активируем файл docker-compose.dev.yml. Если указана строка production, то мы активируем файл docker-compose.production.yml.

Также в файле есть другие переменные для базы данных. Можете поменять их содержимое, если хотите. Главное — помните: DB_HOST должен содержать в себе название контейнера с базой данных из docker-compose.yml.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека фронтендера» Интересно, перейти к каналу

Контейнер с Node.js

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

Для начала перейдем в папку app и создадим там файл Dockerfile с таким содержимым:

Dockerfile

         # dev FROM node:16.10.0-alpine AS dev RUN apk add --no-cache tzdata ENV TZ Europe/Moscow ENV NODE_PATH /opt/server/node_modules  WORKDIR /opt/server/  CMD [ "node" ]  # production FROM node:16.10.0-alpine AS production RUN apk add --no-cache tzdata ENV TZ Europe/Moscow ENV NODE_PATH /opt/server/node_modules  WORKDIR /opt/server/  COPY /*.json ./ RUN npm i  CMD ["sh", "-c", "npm run start"]     

Чтобы класс new Date() в Node.js возвращал корректное для вас время, можно поменять таймзону контейнера. Например, если хотите установить уральское время, вместо Europe/Moscow напишите Asia/Yekaterinburg.

Контейнер с Nginx

Прежде чем продолжить, надо еще добавить в папку nginx конфиг. Для этого внутри папки nginx создайте файл nginx.conf со следующим содержимым:

nginx.conf

         server {   root /var/www;   listen 80;   gzip on;   gzip_types text/plain application/xml text/css application/javascript;   gzip_min_length 1000;   # Проверку можно будет добавить в Express   client_max_body_size 0;    # C любовью   add_header X-Created-By "Proglib";    location / {     # Ищем файл в папке static (ее Docker собрал слизав у Node)     # Если ничего не нашли выбрасываем прокси     try_files /static/$uri $uri @nodeproxy;   }    location @nodeproxy {     proxy_redirect off;     proxy_set_header Host $host;     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     proxy_set_header X-Forwarded-Proto $scheme;     proxy_read_timeout 1m;     proxy_connect_timeout 1m;     # app это алиас для контейнера с Node.js     proxy_pass http://app:3000;   }    # А по этому маршруту проксируем все в Pgadmin   location /pgadmin {     proxy_set_header X-Script-Name /pgadmin;      proxy_pass http://pgadmin;     proxy_intercept_errors on;     error_page 503 = @nodeproxy;     error_page 502 = @nodeproxy;   } }     

Запускаем сборку

Вот теперь можно все запустить. Из основной папки стартуем нашу сборку командой:

         docker-compose up --build -d     

sozdaem api na docker nodejs nginx postgres ff28966 - Создаем API на Docker, Node.js, Nginx, Postgres

После того как все будет готово, и в терминале появятся четыре заветных зеленых done можно продолжать. Теперь-то уже можно начать непосредственно работу с Node.js.

Работаем с Node.js в контейнере

Давайте перейдем в контейнер с Node.js командой:

         docker-compose exec app sh     

Если вы все сделали правильно, в терминале появится у строки ввода приписка opt/server.

sozdaem api na docker nodejs nginx postgres c348d28 - Создаем API на Docker, Node.js, Nginx, Postgres

В этом режиме вы можете выполнять команды внутри контейнера. Чтобы выйти из контейнера, напишите команду:

         exit     

Теперь, давайте напишем немного Node.js кода (войдите в контейнер, если вышли). Сначала надо инициализировать проект командой:

         npm init     

Далее, давайте создадим файл app.js внутри папки app. Создаем его самым обычным способом (можете через терминал, как хотите). Когда файл появится в VS Code, вы можете проверить, появился ли он в контейнере, для этого достаточно написать команду:

         ls     

У нас должно быть сейчас 3 файла в контейнере: Dockerfile, app.js, package.json.

После установим нужные нам пакеты командой:

         npm install express nodemon      

Поместим следующий код в app/app.js:

app.js

         // Express const express = require('express') const app = express()  // Router const router = express.Router()  // Главная router.get('/', (_req, res) => {   res.status(200).json({     message: 'Hello World',   }) })  // Обработка всего остального router.get('/*', (_req, res) => {   res.status(400).json({     error: 'Запрос не может быть обработан, маршрут не найден'   }) })  // Routes app.use('/', router)  app.listen(3000, () => {   console.log('Сервер запущен') })     

И создадим команду start в package.json:

sozdaem api na docker nodejs nginx postgres 132466d - Создаем API на Docker, Node.js, Nginx, Postgres

Команда start:

         nodemon ./app.js     

Теперь можно запустить внутри контейнера команду:

         npm run start     

В консоль вы получите сообщение «Сервер запущен». Если перейдете в своем браузере по адресу http://localhost/, то увидите что-то подобное:

sozdaem api na docker nodejs nginx postgres 75f91c5 - Создаем API на Docker, Node.js, Nginx, Postgres

У автора стоит плагин для Google Chrome JSON Viewer

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

Если отключите nodemon командой ctrl + c, то по прошлому адресу можно увидеть сообщение от Nginx: 502 Bad Gateway. Если поместите в папку static любой файл, он будет доступен по указанному маршруту. Например, static/file.txt => http://localhost/file.txt. Даже если nodemon не работает. Помним, за работу со статикой у нас отвечает Nginx.

Устанавливаем Linter для JavaScript

Ну а как без этого? Для начала, перейдите в VS Code в раздел с плагинами, скачайте и активируйте плагин Eslint:

sozdaem api na docker nodejs nginx postgres 96f4630 - Создаем API на Docker, Node.js, Nginx, Postgres

Вот теперь можно установить сам Eslint в проект командой:

         npm init @eslint/config     

Сначала выбираем To check syntax and find problems, после Common JS, на вопрос о фреймворке выбираем None of these, на вопрос про TypeScript отвечаем No. Платформу выбираем Node, галочку снимаем с Browser (все через пробел). Настройки сохранять будем в JSON, соглашаемся с установкой последней версии eslint и выбираем в конце npm.

Если у вас все получилось сделать правильно, будет работать подсветка:

sozdaem api na docker nodejs nginx postgres 91413a8 - Создаем API на Docker, Node.js, Nginx, Postgres

Возможно, придется перезапустить VS Code.

Это все благодаря тому, что мы монтируем всю папку app (вместе с node_modules) в dev-режиме. Осталось добавить команду для тестового (dev) запуска nodemon, который сначала будет парсить проект с помощью Eslit. Для этого добавьте еще две команды в package.json:

sozdaem api na docker nodejs nginx postgres a27a4d6 - Создаем API на Docker, Node.js, Nginx, Postgres

dev:

         nodemon ./app.js --exec "npm run lint && node"      

lint:

         eslint .     

Сохраните и запустите внутри контейнера команду:

         npm run dev     

Допустите какую-нибудь ошибку в коде и сохраните файл:

sozdaem api na docker nodejs nginx postgres 988b90c - Создаем API на Docker, Node.js, Nginx, Postgres

Если получили ошибку, то вы все настроили верно. Поздравляю, теперь ваш код будет чище.

Подключаемся к базе данных

Выходим на финишную прямую: осталось лишь подключить Node.js к базе данных.

В контейнере устанавливаем пакет для соединения с базой данных:

         npm install pg     

Добавляем код в app/app.js:

         // DB const { Pool } = require('pg') const pool = new Pool({   user: process.env.DB_USER,   host: process.env.DB_HOST,   database: process.env.DB_NAME,   password: process.env.DB_PASSWORD,   port: 5432, })  pool.query('SELECT NOW()', (err, res) => {   console.log(err, res)   pool.end() })     

Должно получиться как-то так:

sozdaem api na docker nodejs nginx postgres 02d8202 - Создаем API на Docker, Node.js, Nginx, Postgres

Если вы сейчас сохраните и запустите проект, nodemon выбросит ошибку. А все потому, что у нас нет базы данных под названием api (если вы не меняли название). Для этого переходите по адресу http://localhost/pgadmin, и вводите данные из docker-compose.yml для входа. Если вдруг получили ошибку от Nginx после авторизации, то просто нажмите F5.

Нам нужно создать новый сервер:

sozdaem api na docker nodejs nginx postgres 999ca77 - Создаем API на Docker, Node.js, Nginx, Postgres

Во вкладке General введите любое название. Во вкладке Connection введите наши данные для подключения:

sozdaem api na docker nodejs nginx postgres c637b8b - Создаем API на Docker, Node.js, Nginx, Postgres

Базы api еще нет, поэтому вместо нее введите postgres. Создайте новую базу под названием api:

sozdaem api na docker nodejs nginx postgres fd20549 - Создаем API на Docker, Node.js, Nginx, Postgres

Теперь запускайте nodemon. Поздравляем, вы подключились к базе данных:

sozdaem api na docker nodejs nginx postgres 35af7d8 - Создаем API на Docker, Node.js, Nginx, Postgres

Финальная сборка

Так как мы написали все нужные команды, теперь можем поменять NODE_ENV в .env на production и запустить наш проект в другом режиме командой:

         docker-compose up --build -d     

В данный момент production сборка от dev отличается тем, что node_modules контейнер создает свои, и они больше не прокинуты через volumes между контейнером и ПК. Поэтому команда npm i (не забываем про разные таргеты) из Dockerfile установит node_modules только в контейнер. То есть, если вы удалите свои node_modules, то теперь на контейнер это не повлияет.

Поэтому эту команду можно использовать для того, чтобы быстро тянуть файлы с GitHub и запускать проект на сервере. Мы же не храним node_modules в git-репозиториях.

Как вернуться в dev

Для работы в dev-режиме вам просто потребуется вернуть NODE_ENV в dev режим, перезапустить проект через композ:

         docker-compose up --build -d     

Зайти в контейнер командой:

         docker-compose exec app sh     

Установить пакеты:

         npm install     

Запустить внутри команду:

         npm run dev     

Это обеспечит вам гибкость при разработке и разделит production от dev, позволит работать с линтером и подсветкой в VS Code. А также, разработка внутри контейнера обезопасит ваш компьютер от всяких вредоносных валварей.

***

Напоследок

Для работы с базой данных автор рекомендует Sequelize. Плюс, разделенные docker-compose.yml позволят вам создавать разные Nginx конфиги для production и dev. Например, в боевую сборку можно добавить образ gordonchan/auto-letsencrypt и открыть 443 порт в nginx для https.

И вот ссылка на репозиторий GitHub. Там можно найти весь код проекта.

Материалы по теме

  • Как запустить веб-приложение на Nginx в Docker 🐳👨🏽‍💻
  • «Контейнер дал течь»: проверьте безопасность Docker-образа 🐳
  • В чём разница между Docker и Kubernetes?

  • 3 views
  • 0 Comment

Leave a Reply

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Categories 05.

Связаться со мной
Close