Docker: заметки веб-разработчика. Итерация третья

b7djotv_rf_w4fh7wxkpeykxj6g.jpeg


Привет, друзья! Продолжаю делиться с вами заметками о Docker.

Заметки состоят из 4 частей: 2 теоретических и 2 практических. Если быть более конкретным:

В этой части мы разработаем простое приложение, состоящее из трех сервисов и базы данных, а в заключительной — «контейнеризуем» его.

Репозиторий с кодом приложения.

Если вам это интересно, прошу под кат.


Подготовка и настройка проекта

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

Также предполагается, что на вашей машине установлен Docker и Node.js.

Хорошо, если на вашей машине установлен Yarn и вы имеете опыт работы с React, Vue, Node.js, PostgreSQL и sh или bash (все это опционально).

Как я сказал, наше приложение будет состоять из трех сервисов:


  • клиента на React.js;
  • админки на Vue.js;
  • сервера (API) на Node.js.

В качестве базы данных мы будем использовать PostgreSQL, а для взаимодействия с ней — Prisma.

Функционал нашего приложения будет следующим:


  • в админке задаются настройки для приветствия, темы и базового размера шрифта;
  • эти настройки записываются в БД и применяются на клиенте;
  • на клиенте реализована «тудушка»;
  • задачи записываются в БД;
  • все это обслуживается сервером.

Создаем директорию для проекта, переходим в нее и создаем еще парочку директорий:

mkdir docker-test

cd !$ # docker-test

mkdir services sh uploads

В директории services будут находиться наши сервисы, в директории sh — скрипты для терминала, директорию uploads мы использовать не будем, но обычно в ней хранятся различные файлы, загружаемые админом или пользователями.

Переходим в директорию services, создаем директорию для API, генерируем шаблон клиента с помощью Create React App и шаблон админки с помощью Vue CLI:

cd services

mkdir api

yarn create react-app client
# or
npx create-react-app client

yarn create vue-app admin
# or
npx vue create admin

Начнем с API.


API

Переходим в директорию api, инициализируем Node.js-проект и устанавливаем зависимости:

cd api

yarn init -yp
# or
npm init -y

# производственные зависимости
yarn add express cors
# зависимости для разработки
yarn add -D nodemon prisma


  • express — Node.js-фреймворк для разработки веб-серверов;
  • cors — утилита для работы с CORS;
  • nodemon — утилита для запуска сервера для разработки;
  • prisma — ядро (core) ORM, которое мы будем использовать для взаимодействия с postgres.

Инициализируем prisma:

npx prisma init

Это приводит к генерации директории prisma, а также файлов prisma/schema.prisma и .env.

Определяем генератор, источник данных и модели в файле prisma/schema.prisma:

// https://pris.ly/d/prisma-schema
generator client {
  provider      = "prisma-client-js"
  // это нужно для контейнера
  binaryTargets = ["native"]
}

datasource db {
  provider = "postgresql"
  // путь к БД извлекается из переменной среды окружения `DATABASE_URL`
  url      = env("DATABASE_URL")
}

// модель для настроек
model Settings {
  id             Int      @id @default(autoincrement())
  created_at     DateTime @default(now())
  updated_at     DateTime @updatedAt
  greetings      String
  theme          String
  base_font_size String
}

// модель для задачи
model Todo {
  id         Int      @id @default(autoincrement())
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
  text       String
  done       Boolean
}

Определяем путь к БД в файле .env:

DATABASE_URL=postgresql://postgres:postgres@localhost:5432/mydb?schema=public

Здесь:


  • postgres — имя пользователя и пароль;
  • localhost — хост, на котором запущен сервер postgres;
  • 5432 — порт, на котором запущен сервер postgres;
  • mydb — название БД.

Определяем команду для запуска контейнера postgres в файле sh/db (без расширения):

docker run --rm --name postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=mydb -dp 5432:5432 -v $HOME/docker/volumes/postgres:/var/lib/postgresql/data postgres

Обратите внимание: если вы работаете на Mac, вам потребуется предоставить самому себе разрешение на выполнение кода из файла sh/db. Это можно сделать так:

# мы находимся в директории `sh`
chmod +x db
# or
sudo chmod +x db

Находясь в корневой директории проекта, открываем терминал и выполняем команду:

sh/db

Происходит загрузка образа postgres из Docker Hub и запуск контейнера под названием postgres.

Обратите внимание: иногда может возникнуть ошибка, связанная с тем, что порт 5432 занят другим процессом. В этом случае необходимо найти PID данного процесса и «убить» его. На Mac это делается так:

# получаем `PID` процесса, запущенного на порту `5432`
sudo lsof -i :5432
# предположим, что `PID` имеет значение `103`
# "убиваем" процесс
sudo kill 103

Убедиться в запуске контейнера можно, выполнив команду docker ps:


image-loader.svg

Или запустив Docker Desktop:


image-loader.svg

Или в разделе Individual Containers расширения Docker для VSCode:


image-loader.svg

Выполняем миграцию:

# мы находимся в директории `api`
# migrate dev - миграция для разработки
# --name init - название миграции
npx prisma migrate dev --name init

Это приводит к генерации файла prisma/migrations/[Date]-init/migration.sql, подключению к БД, созданию в ней таблиц, установке и настройке @prisma/client.

Создаем файл prisma/seed.js с кодом для заполнения БД начальными данными:

import Prisma from '@prisma/client'
const { PrismaClient } = Prisma

// инициализируем клиента
const prisma = new PrismaClient()

// начальные настройки
const initialSettings = {
  greetings: 'Welcome to Docker Test App',
  theme: 'light',
  base_font_size: '16px'
}

// начальные задачи
const initialTodos = [
  {
    text: 'Eat',
    done: true
  },
  {
    text: 'Code',
    done: true
  },
  {
    text: 'Sleep',
    done: false
  },
  {
    text: 'Repeat',
    done: false
  }
]

async function main() {
  try {
    // если таблица настроек является пустой
    if (!(await prisma.settings.findFirst())) {
      await prisma.settings.create({ data: initialSettings })
    }
    // если таблица задач является пустой
    if (!(await (await prisma.todo.findMany()).length)) {
      await prisma.todo.createMany({ data: initialTodos })
    }
    console.log('Database has been successfully seeded 
    
            

© Habrahabr.ru