Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Привет, друзья! Продолжаю делиться с вами заметками о Docker
.
Заметки состоят из 4 частей: 2 теоретических и 2 практических. Если быть более конкретным:
- первая часть посвящена
Docker
,Docker CLI
иDockerfile
; - во второй части рассказывается о
Docker Compose
.
В этой части мы разработаем простое приложение, состоящее из трех сервисов и базы данных, а в заключительной — "контейнеризуем" его.
Репозиторий с кодом приложения.
Если вам это интересно, прошу под кат.
Подготовка и настройка проекта
Предполагается, что вы хотя бы вкратце ознакомились с содержанием предыдущих частей или изучали другие материалы, посвященные работе с 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
:
Или запустив Docker Desktop
:
Или в разделе Individual Containers
расширения Docker
для VSCode
:
Выполняем миграцию:
# мы находимся в директории `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