- О контейнерах
- Как работает Docker
- Образы
- Команды Docker
- Маппинг портов
- Dockerfile
- Cоздаем образ
- Теги
- Тома
- Слои
- Cеть в контейнерах
- Docker Compose
Контейнеры и контейнеризация
Что такое контейнеры
Контейнеры — это небольшие автономные исполняемые программные пакеты, которые содержат все необходимое для запуска программного обеспечения, включая сам программный код, среду выполнения (окружение), системные инструменты, библиотеки и настройки.
Контейнеры изолируют программное обеспечение от его окружения и обеспечивают единообразную работу несмотря на различия, например, между development- и staging-окружениями. Это означает, что становится возможно упаковать приложение вместе со всеми его зависимостями и библиотеками в изолированный пакет, который может быть запущен на любой машине с контейнерной средой исполнения, в том числе Docker.
Что такое контейнеризация
Соответственно, контейнеризация — это упаковка программного кода вместе с его зависимостями, чтобы код единообразно и последовательно выполнялся в любой инфраструктуре.
Этот подход обеспечивает простое развертывание приложений и их надежную работу при переносе из одной среды в другую.
Выгоды контейнеризации
- Согласованность: Приложения ведут себя одинаково независимо от того, где они запущены.
- Эффективность: Контейнеры легковесны и совместно используют ядро ОС хоста, потребляя меньше системных ресурсов, чем виртуальные машины.
- Масштабируемость: Легче масштабировать приложения по мере укрупнения.
- Изолированность: Каждый контейнер работает в изолированном окружении, что повышает безопасность и стабильность.
Что такое Docker
Docker — это открытая платформа, позволяющая: создавать, развертывать, запускать, обновлять и управлять контейнерными приложениями.
Почему Docker популярен: он оптимизирует процесс разработки, устраняет источник ссор из-за причин дефектов, возможности масштабирования, а также:
- Переносимость: Повышает портабельность, то есть приложения работают везде одинаково
- Масштабируемость: В зависимости от потребностей.
- Изолированность: Контейнеры инкапсулируют приложение и его зависимости.
Реализация идеи
Идея создателей в 2013 заключалась в легкой, портабельной и эффективной упаковке приложений для запуска в разных окружениях. Вдохновением послужила концепция упаковки товаров в морских контейнерах; по аналогии, приложения вместе с зависимостями упаковываются в стандартные контейнеры, которые можно легко развернуть в любом окружении.
Сегодня Docker поддерживается большим активным сообществом. Также он послужил толчком для развития смежных технологий типа Kubernetes, для оркестрации контейнеров.
Практика Docker
Прежде чем приступить, нужно установить Docker. Он доступен для Windows, macOS и Linux.
Установка
Следуйте инструкциям на официальном сайте.
После установки убедитесь, что Docker запущен:
docker — version
Архитектура Docker
Компоненты Docker
В Docker используется клиент-серверная архитектура с такими ключевыми компонентами:
- Docker Client: Инструмент командной строки (CLI) для работы пользователей с Docker. Он взаимодействует с Docker-демоном для выполнения команд.
- Docker Daemon (или Docker Engine): Технология контейнеризации с открытым исходным кодом, которая упаковывает приложения в контейнеры. Демон слушает запросы Docker API и обрабатывает их.
- containerd: Ключевой компонент, который управляет жизненным циклом контейнеров, включая запуск, остановку и управление процессами контейнеров.
- runc: Легкий инструмент CLI для создания и запуска контейнеров в соответствии со спецификацией Open Container Initiative (OCI).
- Docker Registry: Сервис, который хранит и предоставляет образы Docker. По умолчанию публичным реестром является Docker Hub, но можно использовать и другие реестры. Он работает как github, но в нем размещается не исходный код, а Docker-образы.
- Docker Networking: Сетевые возможности для контейнеров, позволяя им взаимодействовать друг с другом и внешним миром.
- Docker Volumes и Bind Mounts: Хранение и обмен данными между контейнерами и хост-системой.
- Docker Compose: Инструмент для определения и запуска мультиконтейнерных приложений с помощью YAML-файла.
Как Docker запускает приложения
- Сборка: Docker-клиент отправляет запрос на сборку Docker-демону, который создает образ, на основе инструкций в
Dockerfile
. - Ship: Образ хранится в реестре Docker (публичном или частном), откуда его можно загрузить.
- Run: Docker-клиент запрашивает демона Docker для создания и запуска контейнера на основе образа.
Образы Docker
Образ Docker — это легкий автономный исполняемый пакет, который содержит все необходимое для запуска программного обеспечения, включая код, среду выполнения, библиотеки, переменные окружения и файлы конфигурации.
Загрузка образа из Docker Hub (Pull)
Команда получения образа из Docker Hub:
docker pull hello-world
После получения образа его можно запустить командой docker run.
Разницу между образом и контейнером легко представить следующим образом: контейнер — это когда вы запускаете node app.js на своей машине из исходного кода, полученного с github, а образ — это ваша кодовая база на github.
Основные команды docker
1.) Проверка версии Docker
docker version
2) Просмотр общесистемной информации о Docker
docker info
3) Список всех образов Docker
docker images
4.) Список запущенных контейнеров
docker ps docker ps -a // List All Containers (Running and Stopped)
5.) Получение образа из реестра
docker pull node:20 // Here 20 specifies a specific version of the node we want to pull
6.) Создание и запуск нового контейнера из образа
В приведенном ниже примере мы развертываем сервер NGINX в detached-режиме (-d), соединяя порт хоста 8080 с портом контейнера 80.
docker run -d -p 8080:80 nginx
7.) Остановить запущенный контейнер и удалить его:
docker stop <container_id> docker rm <container_id> // Remove a Stopped Container
8.) Удалить образ
docker rmi <image_id>
9.) Сборка образа
docker build -t <your-image-name> .
10.) Загрузка своего образа в реестр
docker push <Name of the image> <Name of the repo>
Маппинг портов в Docker
Сопоставление (маппинг) портов в Docker — это сопоставление порта на хост-машине с портом в контейнере (как в примере 6 выше). Маппинг необходим для доступа к приложениям, работающим в контейнерах, извне Docker-хоста.
Как работает сопоставление портов
Представьте, что у вас веб-сервер, работающий внутри Docker-контейнера на порту 3000. По умолчанию этот порт доступен только в сети Docker, но не доступен с хост-машины или из внешней сети.
Чтобы сделать этот сервер доступным за пределами контейнера, вам нужно перенаправить порт с хоста в контейнер.
Пример:
docker run -p [HOST_PORT]:[CONTAINER_PORT] [IMAGE-NAME]
-p — флаг для сопоставления портов.
Dockerfile
Dockerfile
— это текстовый файл, содержащий инструкции по созданию образа. Каждая инструкция создает слой в образе, и эти слои кэшируются, для ускорения последующих сборок.
Ключевые инструкции в Dockerfile
- FROM: Устанавливает базовый образ для последующих инструкций.
- WORKDIR: Устанавливает рабочий каталог внутри контейнера.
- COPY: Копирует файлы из хост-системы в контейнер.
- RUN: Выполняет команду в контейнере.
- CMD: Указывает команду для выполнения при запуске контейнера.
- EXPOSE: Документирует, какие порты прослушивает контейнер.
.dockerignore
Файл .dockerignore
работает аналогично файлу .gitignore
. Он указывает, какие файлы и каталоги следует игнорировать при сборке образа Docker. Это помогает сохранить образ небольшим, не принимая ненужные файлы. Это уменьшает размер контекста сборки и экономит время сборки. Файлы из node_modules
, папки dist
и т. д.
Как создать образ
Рассмотрим докеризацию на примере простого приложения mongo-express typescript.
1.) Инициализация проекта
mkdir ts-express-app cd ts-express-app npm init -y npm install express mongoose dotenv npm install --save-dev typescript @types/node @types/express @types/mongoose ts-node tsc --init
2.) Создаем файл tsconfig.json
{ "compilerOptions": { "target": "ES6", "module": "commonjs", "outDir": "./dist", "rootDir": "./src", "strict": true } }
3.) Создаем файл src/index.ts
import express from 'express'; import mongoose from 'mongoose'; import dotenv from 'dotenv'; dotenv.config(); const app = express(); const PORT = 3000; const DB_URL = process.env.DATABASE_URL || ''; mongoose.connect(DB_URL, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('Connected to MongoDB')) .catch(err => console.error('Could not connect to MongoDB', err)); app.get('/', (req, res) => { res.send('Ram Ram bhai Sareya Ne'); }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
4.) Создаем файл package.json и файл .env
"scripts": { "start": "node dist/index.js", "build": "tsc" }
DATABASE_URL=mongodb://localhost:27017/ts-express-app
Теперь докеризируем.
5.) Создаем Dockerfile в корне проекта
# Use the Node.js 20 image as the base image FROM node:20 # Set the working directory inside the container WORKDIR /usr/src/app # Copy package.json and package-lock.json COPY package*.json ./ # Install dependencies RUN npm install # Copy the rest of the application code COPY . . # Build the TypeScript code RUN npm run build # Expose the port the app runs on EXPOSE 3000 # Command to run the app CMD ["npm", "start"]
6.) Создаем файл .dockerignore в корне проекта:
node_modules dist npm-debug.log
7.) Создаем образ
docker build -t ts-express-app .
Приведенная выше команда должна успешно выполниться и выдать результат, аналогичный показанному ниже:
8.) Запуск контейнера
После создания образа можно запустить из него контейнер следующей командой:
docker run -p 3000:3000 ts-express-app
Как уже сказано, вышеописанное запускает ваш контейнер и сопоставляет порт 3000 на вашей хост-машине с портом 3000 контейнера. Можно зайти на localhost:3000 и убедиться, что контейнер запущен.
Выше вы видите, что мой образ отображается в списке образов docker, а контейнер работает на порту 3000, который сопоставлен с портом 3000 машины. Потратьте немного времени, чтобы поиграть и проанализировать, почему.
Продвигаясь дальше, вы можете пробовать отправить (пушить) образ в реестр DockerHub с помощью простых команд —
9.) Передача образа в реестр Docker
docker login docker tag ts-express-app your-dockerhub-username/ts-express-app:latest docker push your-dockerhub-username/ts-express-app:latest
В поле выше введите имя пользователя dockerhub
вместо "your-dockerhub-username"
. Я использовал тег, который объясню позже.
Поздравляю вас с отправкой первого образа в DockerHub. Теперь, когда вы ознакомились с основами создания образов и запуска контейнеров, давайте углубимся в эту тему.
В production-режиме вы скрываете свой .env-файл, чтобы защитить свои секреты. Как же передать эти секреты в вашем .env в docker без использования .env-файла? Просто используйте флажок «-e«, который позволит вам передать переменные окружения в ваше приложение.
docker run -p 3000:3000 -e DATABASE_URL=mongodb://localhost:27017/ts-express-app ts-express-app
Теги Docker
Теги передают полезную информацию о версии/варианте образа. Теги позволяют идентифицировать и извлекать различные версии образа из реестра Docker. Они являются псевдонимами идентификатора образа, который часто выглядит так: f1477ec11d12
. Это просто способ обозначения образа. Аналогом является то, как теги в Git ссылаются на коммиты в истории.
Общий синтаксис тегов образов таков:
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
Если тег не указан, Docker по умолчанию использует тег latest
.
Когда используются теги:
1. При сборке образа используем следующую команду:
docker build -t username/image_name:tag_name .
2. Эксплицитное обозначение образа командой tag.
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
3. Управление версиями: Теги обычно используются для обозначения различных версий образа. Например, теги 1.0, 1.1, 2.0 и т. д., обозначающие основные или незначительные обновления версии.
4. Идентификация среды или этапа: Теги помогают различать development, staging и production — тегами dev
, staging
и prod
.
5. Обозначение архитектуры или платформы: Теги позволяют идентифицировать образы, созданные для различных архитектур и платформ, например amd64
, arm64
или windows
.
Разберемся в этом подробнее, используя тег в командах —
Создание образа с тегом:
docker build -t express-mongo-app:1.0 .
Получение конкретного тега:
docker pull node:14.18.0
Отметить существующий образ другим тегом:
docker tag express-mongo-app:1.0 express-mongo-app:latest
Управление версиями с помощью тегов:
Использование семантических версий (MAJOR.MINOR.PATCH) -…
docker build -t express-mongo-app:1.0.0 . docker tag express-mongo-app:1.0.0 express-mongo-app:1.0 docker tag express-mongo-app:1.0.0 express-mongo-app:latest
Команда Docker exec
Команда docker exec
позволяет выполнять команды внутри запущенного Docker-контейнера, для отладки, администрирования, создания папок/томов или проверки состояния контейнера.
Базовые команды docker exec
docker exec [OPTIONS] CONTAINER_ID|CONTAINER_NAME COMMAND [ARG…]
- OPTIONS: Различные опции, например -it для интерактивного режима.
- CONTAINER: Имя или ID контейнера.
- COMMAND: Команда, которую нужно запустить внутри контейнера.
- [ARG…]: Аргументы команды.
Откроется интерактивная оболочка Bash внутри запущенного контейнера. Вы можете запускать команды, просматривать файлы, создавать/удалять тома и выполнять скрипты в контейнере.
Опция -it
в docker exec
означает «-interactive -tty», что позволяет взаимодействовать с оболочкой контейнера.
Теперь разберемся в томах Docker.
Тома Docker
Тома — это файловые системы, которые устанавливаются на контейнеры, для хранения данных, генерируемых контейнером.
Как работает файловая система Docker
Контейнер выполняет программный стек, описанный в образе. Образы состоят из слоев только для чтения, файловая система Union File System. Когда мы запускаем новый контейнер, Docker добавляет слой чтения-записи поверх слоев образа, позволяя контейнеру функционировать как обычная файловая система в Linux. Таким образом, каждая модификация файла в контейнере создает функционирующую копию в слое чтения-записи. Однако когда контейнер останавливается или удаляется, слой чтения-записи исчезает.
Docker управляет томами, хранящимися в разделе файловой системы хоста — /var/lib/docker/volumes
в Linux. Эта часть файловой системы не должна изменяться процессами, не относящимися к Docker. В Docker тома являются наиболее эффективным способом хранения данных. Используя команду docker volume create, мы напрямую создаем том, или Docker делает это за нас при создании контейнера или сервиса.
Существует три основных типа томов:
- Именованный: (Named Volumes) Управляются Docker и хранятся в выделенном месте в файловой системе хоста.
- Bind: Монтирует каталог или файл с хост-машины в контейнер. На хост-системе они могут храниться в любом месте. Это могут быть важные системные папки или файлы. Они всегда могут быть изменены процессами, не относящимися к Docker, запущенными на хосте или в контейнере. В целом, это хуже вариант, чем тома.
- tmpfs: Монтирует временную файловую систему в контейнер, хранящийся в памяти хоста. Эти контейнеры никогда не записываются в файловую систему хост-системы; вместо этого они хранятся исключительно в памяти хост-системы. Ни на хосте, ни в контейнере они не хранятся на диске. Чувствительные или непостоянные данные могут храниться в таком контейнере в течение всего времени работы контейнера.
Именованные тома
Создаем именованный том следующей командой:
docker volume create my-ts-app-data
Теперь давайте смонтируем том в каталог внутри контейнера. Мы монтируем том my-ts-app-data
в каталог /app/data
внутри контейнера. Любые данные, записанные в /app/data
внутри контейнера, будут храниться в названном томе на хосте.
docker run -d -p 3000:3000 -e DATABASE_URL=mongodb://mongo:27017/ts-express-app -v my-ts-app-data:/data/db ts-express-app
Используем команду docker volume inspect "my-ts-app-data"
, чтобы посмотреть подробную информацию о томе, включая его расположение в файловой системе хоста.
Чтобы удалить том, если он больше не нужен, просто используйте команду «rm»:
docker volume rm my-ts-app-data
Операции с томами в docker
Теперь попробуем что-нибудь еще. Допустим, я хочу визуализировать свои данные в приложении-компасе на mongoDB. Как мне подключить его к моему тому в контейнере?
Запускаем mongo-контейнер локально, выполнив следующую команду —
docker run -p 27017:27017 -d mongo
Открываем свое приложение-компас на mongoDB и подключаемся к порту 27017. Создаем базу данных и коллекцию, вставляем в нее данные и сохраняем их.
Добавлена новая БД и случайные данные в нее.
Теперь уничтожьте контейнер, а затем перезапустите его. Откройте mongoDB compass и проверьте БД и данные, которые мы создали ранее. Что вы видите? Она исчезла, верно?
Как же мы сохраним данные? С помощью томов! Мы уже создали том с именем "my-ts-app-data"
, давайте использовать только его. Смонтируйте том в каталоге /data/db
контейнера mongo и запустите его с помощью следующей команды:
docker run -d -v my-ts-app-data:/data/db -p 27017:27017 mongo
Теперь повторите описанные выше шаги, создайте БД и добавьте в нее данные. Уничтожьте контейнер, перезапустите его и перепроверьте данные, которые вы ранее ввели. Вы увидите, что ваши данные сохранились.
Теперь погрузимся в другие темы по Docker. Как вы заметили выше, я использовал слово «слои» при объяснении томов. Но что такое слой? Рассмотрим далее. О bind mount и tmpfs mount поговорим позже.
Слои в Docker
Мы уже знаем, что билд Docker состоит из серии упорядоченных инструкций. Каждая инструкция в Dockerfile примерно соответствует слою, который также называется слоем образа. При создании нового контейнера из образа, поверх слоев образа добавляется новый слой, доступный для записи, что позволяет контейнеру вносить изменения без модификации нижележащих слоев образа.
Слои Docker
- Базовый слой: Это начальная точка образа Docker. Он содержит операционную систему, например Ubuntu, Alpine и т. д. и т. п. (все, что указано в dockerfile). Этот слой неизменяем и служит основой для последующих слоев.
- Промежуточные слои: Эти слои представляют собой инструкции в вашем Dockerfile, такие как RUN, COPY и ADD. Каждая инструкция создает новый слой поверх предыдущих. Промежуточные слои доступны только для чтения и кэшируются.
- Верхний слой для чтения/записи: Когда вы запускаете контейнер из образа, Docker добавляет слой с возможностью записи поверх слоев образа, доступных только для чтения. Это позволяет контейнеру вносить изменения без модификации нижележащего образа.
- Реюзабельные и расшаренные: Слои кэшируются и могут повторно использоваться в разных образах, что повышает эффективность создания и совместного использования образов. Если несколько образов созданы на основе одного и того же базового образа, или имеют общие инструкции, они могут параллельно использовать одни и те же слои, что сокращает объем памяти и ускоряет загрузку и сборку образов.
Как создаются слои
Слои создаются на основе инструкций, указанных в Dockerfile. Каждая инструкция в Dockerfile генерирует новый слой поверх предыдущих. Пример Dockerfile —
FROM ubuntu: 18.04 // This instruction creates a base layer by pulling the ubuntu: 18.04 image from the Docker registry. COPY . /app // This instruction creates a new layer on top of the base layer. It copies the entire contents of the build context (the directory containing the Dockerfile) into the /app directory inside the container. RUN make /app // This instruction creates another layer by running the make /app command inside the container. This command builds the application located in the /app directory. CMD python /app/app.py // This creates a new layer and specifies the default command to run when the container starts, which is python /app/app.py.
Мы знаем, что каждая инструкция создает новый слой поверх предыдущих, образуя как бы стопку слоев, которые и составляют конечный Docker-образ. Для большей наглядности:
Слой Docker
Кэширование слоев
Когда мы собираем образ с помощью Dockerfile, Docker последовательно обрабатывает каждую инструкцию и создает новый слой для каждой из них. Если слой образа не изменился, то билдер берет его из кэша сборки. Если же слой изменился с момента последней сборки, то этот слой и все следующие за ним слои должны быть пересобраны. Позвольте изобразить это на следующем примере —
Создание первого образа
Сборка образа во второй раз с небольшими изменениями в файле app.js
Как видите на скриншотах, на первом я отправил свое приложение Express в образ, и вижу, что каждый слой создан с нуля. Теперь я делаю небольшое изменение (просто добавил console.log('hi')
в файл app.js и теперь я пересобираю образ. Теперь во втором образе видно, что слои 2,3,4 кэшируются, поскольку в них нет изменений, но поскольку в файле произошло изменение, docker распознает это изменение, и поэтому слой 5 не кэшируется и собирается с нуля. А так как слой 5 изменен, все слои, построенные на его основе (после него), будут собраны с нуля.
Примечание: слой 1 на втором скриншоте все еще кэшируется, хотя на скриншоте не видно что он кэширован. На самом деле я не знаю почему. Но не волнуйтесь, он кэшируется.
Итак, теперь у вас есть довольно знания о докеризации приложений и о слоях.
Теперь поиграйтесь с томами. Попробуйте оптимизировать докерфайл, чтобы сократить количество слоев и максимально использовать кэшированные слои.
Сеть в Docker
Контейнеры Docker по умолчанию не могут общаться друг с другом. Поэтому Docker Networks позволяют контейнерам коммуницировать друг с другом и с внешним миром. Они обеспечивают изоляцию, безопасность и контроль над коммуникацией между контейнерами.
Типы сетей в Docker
Docker предоставляет несколько типов сетей:
1.Мостовая сеть (Bridge):
Тип сети по умолчанию для отдельных контейнеров. Мостовые сети создают программный мост между вашим хостом и контейнером. Контейнеры, подключенные к сети, могут общаться друг с другом, но они изолированы от тех, которые находится за пределами сети. Каждому контейнеру в сети присваивается собственный IP-адрес. Поскольку сеть соединена мостом с вашим хостом, контейнеры также могут общаться в вашей локальной сети и в Интернете. Однако они не будут отображаться как физические устройства в вашей локальной сети.
2. Сеть хоста (Host):
Контейнеры, использующие режим сети хоста, совместно используют сетевой стек вашего хоста без какой-либо изоляции. Им не выделяются собственные IP-адреса, а привязки портов будут публиковаться непосредственно к сетевому интерфейсу вашего хоста. Это означает, что процесс контейнера, который прослушивает порт 80, будет привязан к <ip_вашего_хоста>:80
3. Оверлейная сеть (Overlay):
Оверлейные сети — это распределенные сети, включающие несколько Docker-хостов. Сеть позволяет всем контейнерам, запущенным на любом из хостов, взаимодействовать друг с другом, не требуя поддержки маршрутизации на уровне ОС. Оверлейные сети реализуют сетевое взаимодействие для кластеров Docker Swarm, но также могут использоваться при запуске двух отдельных экземпляров Docker Engine с контейнерами, которые должны напрямую связываться друг с другом. Это позволяет создавать собственные окружения типа Swarm.
4. Macvlan-сеть:
Macvlan — еще одна расширенная опция, позволяющая контейнерам выглядеть как физические устройства в сети. Она работает путем присвоения каждому контейнеру в сети уникального MAC-адреса. Этот тип сети требует выделения одного из физических интерфейсов хоста для виртуальной сети. Более крупная сеть также должна быть соответствующим образом настроена для поддержки потенциально большого количества MAC-адресов, которые могут быть созданы активным хостом Docker, на котором запущено множество контейнеров.
5. ipvlan:
IPvLAN — это драйвер, который обеспечивает контроль над адресами IPv4 и IPv6, назначенными вашим контейнерам, а также маркировку и маршрутизацию VLAN уровней 2 и 3. Этот драйвер нужен при интеграции контейнерных сервисов с существующей физической сетью. Сети IPvLAN назначаются собственные интерфейсы, что дает преимущества в производительности по сравнению с мостовыми сетями.
6. Нет сети (None):
Контейнеры изолированы и не имеют сетевых интерфейсов.
Теперь попробуем заставить контейнеры общаться друг с другом.
Создайте сеть с помощью команды ниже (по умолчанию это мостовая сеть): —
(Примечание: когда вы запускаете контейнер без указания сети, он подключается к мостовой сети.)
docker network create my-first-network docker network ls
Запуск контейнеров в мостовой сети —
docker run -d --name c1 --network my-first-network nginx docker run -d --name c2 --network my-first-network nginx
Теперь соединим контейнер 1 с контейнером 2, зайдем внутрь c1 и пропингуем c2
docker exec -it c1 /bin/bash ping c2
Как видите, все работает. Теперь попробуем кое-что новое. Я запущу mongo в контейнере, а мое приложение Express в другом контейнере, а затем попытаюсь получить доступ к контейнеру mongo через мое приложение. Используйте тот же код приложения, который мы создали ранее, только в .env-файл поместите следующее —
DATABASE_URL=[container_name]://mongodb:27017/ts-express-app-db DATABASE_URL=mongodb://mongodb:27017/ts-express-app-db
docker run -d --name mongodb --network my-first-network -v my-ts-app-data:/data/db -p 27017:27017 mongo docker run -d -p 3000:3000 --network my-first-network -e DATABASE_URL='mongodb://mongodb:27017/ts-express-app-db' ts-express-app
Вы видите оба контейнера, теперь попробуйте добавить некоторые данные с помощью команды postman или curl в терминале. Вы увидите успешный ответ, что данные находятся в mongodb, можете зайти внутрь контейнера и выполнить следующие команды mongo.
docker exec -it mongodb mongo use mydatabase db.users.find().pretty()
Также можете проверить логи контейнера mongoDB, чтобы убедиться, что данные сохранены.
Удалить сеть —
docker network rm my-first-network # remove all unused network (networks that aren't connected to even a single container) docker network prune
Отключить контейнер от сети —
docker network disconnect my-first-network mongodb
Docker Compose
Docker Compose — инструмент, позволяющий описывать и запускать многоконтейнерные Docker-приложения. Он использует YAML-файл (docker-compose.yml
) для настройки и оркестрации сервисов, составляющих приложение, включая их зависимости, сети, тома и другие параметры конфигурации.
Пример файла docker-compose.yml для нашего приложения (которое мы использовали в примерах выше) —
version: '3.9' services: ts-express-app: build: . image: "express-mongo-ts-docker" container_name: ts-express-app ports: - "3000:3000" environment: - MONGO_URL=mongodb://mongodb:27017/ts-express-app-db depends_on: - mongodb mongodb: image: mongo container_name: mongodb volumes: - my-ts-app-data:/data/db ports: - "27017:27017" volumes: my-ts-app-data:
Объясню построчно этот файл.
1.) Version Declaration:
version: ‘3.9’: Указывает версию формата файла.
2.) Services:
Service (сервис или служба) — это единица развертывания, которая определяет, какой образ контейнера использовать. Я определил 2 сервиса в файле выше —
a.) ts-express-app
Этот сервис (служба) описывает наше приложение Express.
- build: .: Собирает образ Docker из Dockerfile в текущем каталоге.
- image: «express-mongo-ts-docker»: Именует образ как express-mongo-ts-docker. (Опционально)
- container_name: ts-express-app: Устанавливает имя контейнера как ts-express-app.
- ports: — «3000:3000»: Маппинг портов как обычно.
- environment: — MONGO_URL= mongodb ://mongodb :27017/ ts-express-app-db: Устанавливает переменную окружения MONGO_URL, используемую нашим приложением для подключения к MongoDB. Этот URL указывает на службу mongodb, определенную в файле compose ниже.
- depends_on: — mongodb: Обеспечивает запуск службы ts-express-app после службы mongodb.
b.) mongodb:
Этот сервис описывает базу данных MongoDB.
- image: mongo: Использует официальный образ MongoDB из Docker Hub.
- container_name: mongodb: Устанавливает имя контейнера на mongodb.
- volumes: — my-ts-app-data:/data/db: Монтирует том Docker my-ts-app-data в /data/db в контейнере, где MongoDB хранит свои данные. (как мы изучили выше)
- ports: — «27017:27017»: Маппинг портов стандартный
3.) Volumes
- my-ts-app-data: Здесь, в конце файла, мы определяем, какие тома мы используем.
Итак, раз уж мы изучили файл compose, скопируйте приведенный выше код и создайте файл docker-compose.yml
в корневом каталоге нашего приложения. Затем зайдите в терминал и выполните команду docker-compose up --build
и подождите, пока она соберет ваши образы и запустит контейнеры. Вы увидите сообщение об успешном выполнении, и теперь ваши контейнеры mongo и express app работают в одной сети, правильно подключенные к определенным нами томам, и готовы к использованию.
Для остановки приложения используйте команду — docker-compose down
.
Ниже показаны наши дашборды в Docker, где вы можете увидеть контейнер docker, запущенный внутри двух контейнеров (нашего приложения mongo и express). На третьем изображении я добавил некоторые данные с помощью POSTMAN, а затем получил их из нашей БД с помощью GET.
Кроме того, мы можем создать кастомную сеть и некоторые другие вещи. Если мы не определим сеть, как мы сделали это выше, Docker автоматически создаст дефолтную сеть для наших сервисов, обычно называется по имени директории проекта, в которой находится наш файл docker-compose.yml.
version: '3.8' services: ts-express-app: build: context: . dockerfile: Dockerfile image: express-mongo-ts-docker container_name: ts-express-app restart: unless-stopped ports: - "3000:3000" environment: - MONGO_URL=mongodb://mongodb:27017/ts-express-app-db - NODE_ENV=production depends_on: - mongodb networks: - ts-app-network mongodb: image: mongo container_name: mongodb restart: unless-stopped volumes: - my-ts-app-data:/data/db networks: - ts-app-network ports: - "27017:27017" volumes: my-ts-app-data: networks: ts-app-network: driver: bridge
Я также прикрепляю ссылку на github-репо нашего приложения в примерах, просто клонируйте его, установите библиотеки, создайте том с помощью docker-compose.yml, и запустите команду docker-compose up —build.