Хочешь уверенно проходить IT-интервью?
![Готовься к IT-собеседованиям уверенно с AI-тренажёром T1!](https://media.proglib.io/banner/2025/01/28/t1.jpg)
Мы понимаем, как сложно подготовиться: стресс, алгоритмы, вопросы, от которых голова идёт кругом. Но с AI тренажёром всё гораздо проще.
💡 Почему Т1 тренажёр — это мастхэв?
- Получишь настоящую обратную связь: где затык, что подтянуть и как стать лучше
- Научишься не только решать задачи, но и объяснять своё решение так, чтобы интервьюер сказал: "Вау!".
- Освоишь все этапы собеседования, от вопросов по алгоритмам до диалога о твоих целях.
Зачем листать миллион туториалов? Просто зайди в Т1 тренажёр, потренируйся и уверенно удиви интервьюеров. Мы не обещаем лёгкой прогулки, но обещаем, что будешь готов!
Реклама. ООО «Смарт Гико», ИНН 7743264341. Erid 2VtzqwP8vqy
Файловая архитектура и инструменты
Используем Visual Code Studio в качестве редактора кода. Операционная система любая. Обязательно установите Docker.
Для начала создадим вот такую файловую архитектуру:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/66298f08e1ca83a977df741673345424.png)
По мере чтения статьи файлов у нас прибавится. Что мы создали: .env
будет содержать переменные среды окружения, в папке app будет контейнер с Node.js, в папке static
будет статика, а в Nginx — Nginx-конфигурация. Для начала этого хватит.
Docker и docker-compose.yml
У нас будет два docker-compose.yml
: dev
и production
. Начнем с dev-версии:
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
Здесь стоит обратить внимание на четыре вещи:
- Контейнеры
app
иnginx
связаны со статикой. Для Node.js папка со статикой будет ниже уровнем отapp.js
(главным файлом, чуть позже создадим), на одном уровне с папкой app. - Контейнер с базой данных содержит
${DB_USER}
и${DB_PASSWORD}
. Это переменные из .env-файла, мы его начнем заполнять спустя пару мгновений. - Контейнер с Pgadmin содержит такие строки:
PGADMIN_DEFAULT_EMAIL
иPGADMIN_DEFAULT_PASSWORD
. Вы можете там указать свою почту и свой пароль, он будет использоваться для входа в Pgadmin. - В контейнере с Node.js есть раздел
build
. Там естьtarget dev
. Просто пока обратите внимание.
Теперь перейдем к production-версии:
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
у нас другой target
– production
. И вот сейчас мы плавно переходим дальше…
Как Docker поймет, какой файл запускать
Идем в .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 с таким содержимым:
# 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
со следующим содержимым:
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
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/88a1bc9f1690d56ff21ee136fea2aa49.png)
После того как все будет готово, и в терминале появятся четыре заветных зеленых done можно продолжать. Теперь-то уже можно начать непосредственно работу с Node.js.
Работаем с Node.js в контейнере
Давайте перейдем в контейнер с Node.js командой:
docker-compose exec app sh
Если вы все сделали правильно, в терминале появится у строки ввода приписка opt/server
.
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/5ee6b92a19320dcb99676c25051a1e0b.png)
В этом режиме вы можете выполнять команды внутри контейнера. Чтобы выйти из контейнера, напишите команду:
exit
Теперь, давайте напишем немного Node.js кода (войдите в контейнер, если вышли). Сначала надо инициализировать проект командой:
npm init
Далее, давайте создадим файл app.js
внутри папки app
. Создаем его самым обычным способом (можете через терминал, как хотите). Когда файл появится в VS Code, вы можете проверить, появился ли он в контейнере, для этого достаточно написать команду:
ls
У нас должно быть сейчас 3 файла в контейнере: Dockerfile
, app.js
, package.json
.
После установим нужные нам пакеты командой:
npm install express nodemon
Поместим следующий код в app/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
:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/63b537b8ef900ae16a8afac725617663.png)
Команда start
:
nodemon ./app.js
Теперь можно запустить внутри контейнера команду:
npm run start
В консоль вы получите сообщение «Сервер запущен». Если перейдете в своем браузере по адресу http://localhost/, то увидите что-то подобное:
![У автора стоит плагин для Google Chrome JSON Viewer](https://media.proglib.io/posts/2022/09/24/b95a2bac9e506b07ca206106c61ab687.png)
Если создадите файл внутри папки app
с окончание js
или json
– nodemon
подхватит изменения и перезапустит проект. Аналогично, если вы просто поменяете содержимое какого-нибудь файла с данными расширениями.
Если отключите nodemon
командой ctrl + c, то по прошлому адресу можно увидеть сообщение от Nginx: 502 Bad Gateway. Если поместите в папку static
любой файл, он будет доступен по указанному маршруту. Например, static/file.txt
=> http://localhost/file.txt. Даже если nodemon не работает. Помним, за работу со статикой у нас отвечает Nginx.
Устанавливаем Linter для JavaScript
Ну а как без этого? Для начала, перейдите в VS Code в раздел с плагинами, скачайте и активируйте плагин Eslint:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/25f47bedac698a4c5fd5455860729762.png)
Вот теперь можно установить сам Eslint в проект командой:
npm init @eslint/config
Сначала выбираем To check syntax and find problems, после Common JS, на вопрос о фреймворке выбираем None of these, на вопрос про TypeScript отвечаем No. Платформу выбираем Node, галочку снимаем с Browser (все через пробел). Настройки сохранять будем в JSON, соглашаемся с установкой последней версии eslint и выбираем в конце npm.
Если у вас все получилось сделать правильно, будет работать подсветка:
![Возможно, придется перезапустить VS Code.](https://media.proglib.io/posts/2022/09/24/009c580e787933a25392b277d434a058.png)
Это все благодаря тому, что мы монтируем всю папку app
(вместе с node_modules) в dev-режиме. Осталось добавить команду для тестового (dev) запуска nodemon, который сначала будет парсить проект с помощью Eslit. Для этого добавьте еще две команды в package.json
:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/d54f51ab3485a5f4ffdb85751fe9719e.png)
dev:
nodemon ./app.js --exec \"npm run lint && node\"
lint:
eslint .
Сохраните и запустите внутри контейнера команду:
npm run dev
Допустите какую-нибудь ошибку в коде и сохраните файл:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/901f371885d147718dd83032f1ef739e.png)
Если получили ошибку, то вы все настроили верно. Поздравляю, теперь ваш код будет чище.
Подключаемся к базе данных
Выходим на финишную прямую: осталось лишь подключить 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()
})
Должно получиться как-то так:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/3c1d7182180cef61650b3ece042c2072.png)
Если вы сейчас сохраните и запустите проект, nodemon выбросит ошибку. А все потому, что у нас нет базы данных под названием api (если вы не меняли название). Для этого переходите по адресу http://localhost/pgadmin, и вводите данные из docker-compose.yml
для входа. Если вдруг получили ошибку от Nginx после авторизации, то просто нажмите F5.
Нам нужно создать новый сервер:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/de7fefc51a167e7c903e02d355ed1edd.png)
Во вкладке General введите любое название. Во вкладке Connection введите наши данные для подключения:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/11/14/ed4ff9ef2805380586d7070d78b6e2c2.png)
Базы api еще нет, поэтому вместо нее введите postgres
. Создайте новую базу под названием api
:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/ae21c09fb1bafa70ca97a3100c32af48.png)
Теперь запускайте nodemon. Поздравляем, вы подключились к базе данных:
![🐳🐘 Прочный фундамент для API: Docker + Node.js + Nginx + Postgres](https://media.proglib.io/posts/2022/09/24/be666dc394bd1cd77055df01b8eb2d90.png)
Финальная сборка
Так как мы написали все нужные команды, теперь можем поменять 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. Там можно найти весь код проекта.
Комментарии